如何在调用类的任何其他方法时调用类的方法(Ruby)

时间:2014-01-16 03:59:15

标签: ruby class

在Ruby中,有没有办法在调用类的任何其他方法时调用方法? 例如,

class Car
    def repair
        puts "Repaired!"
    end
    def drive
        # content
    end
    def checkup
        # content
    end
end

在这个例子中,如果我在Car的实例上调用任何方法,我应该总是调用repair方法。我如何在Ruby中做到这一点?

注意:我也想要在内置方法中调用repair,例如Carinstance.class应该调用repair也是。

2 个答案:

答案 0 :(得分:7)

我假设您希望在每个Car#repair的其他实例方法返回后调用Car。我看到你已经添加了一个要求,其他方法也会调用repair。我最后添加了一些关于将其扩展为包含内置实例方法的评论。

我采用的方法是使用BasicObject#method_missing

class Car
  def repair
    puts "Repaired!"
  end

  def drive
    puts "Drive!"
  end

  def checkup
    puts "Checkup!"
  end

  def method_missing(m, *args)
    if @@ims.key?(m)
      ret = send(@@ims[m], *args)
      repair
      ret
    else
      super
    end
  end

  @@ims = instance_methods(false).each_with_object({}) do |m,h|
    next if (m == :repair || m == :method_missing)
    saved_name = "_#{m}"
    alias_method saved_name, m
    h[m] = saved_name
    remove_method(m)
  end            
end

car = Car.new

car.repair
Repaired!

car.drive
Drive!
Repaired!

car.checkup #
Checkup!
Repaired!

car.wash # => in `method_missing': undefined method `wash'...

解析类Car后,在构造完所有实例方法之后,执行以下操作,我用一个例子解释:

instance_methods(false) # => [:repair, :drive, :checkup, :method_missing]  

each_with_object({})创建一个哈希(最初为空),由块变量h引用(稍后将详细介绍)。

next if (m == :repair || m == :method_missing)

导致:repair:method_missing被跳过。

m => :drive时,以下三个语句有效地将:drive重命名为:_drive,并将:drive" => "_drive"添加到哈希h

each_with_object返回

@@ims = {:drive=>"_drive", :checkup=>"_checkup"}

现在

instance_methods(false) # => [:repair, :method_missing, :_drive, :_checkup]

由于不再使用方法:driveCar.new.drive会调用method_missing(:drive)。后者发现@@ims有一个键:drive,因此它使用send来调用:_drive,调用:repair并返回:_drive的返回值}。如果method_missing传递的方法不是@@ims的密钥,则会调用super并引发异常。

在一个现已删除的编辑中,我建议要包含内置实例方法,只需将instance_methods(false)更改为instance_methods,但警告可能出现意外的副作用。 @Kal指出无法删除内置实例方法,因此该方法不起作用。这也是一样的 - 不应该以这种方式混淆Ruby。我显然没有测试我的断言。耻辱!

答案 1 :(得分:1)

class Car
  def self.default_method
      instance_methods(true).each do |meth|
        alias_method meth, :repair
      end
  end
  def initialize
    self.class.default_method
  end

  def repair
    puts "Repaired!"
  end
  def drive
    # content
  end
  def checkup
    # content
  end
end

car = Car.new

car.drive # => Repaired!
car.checkup # => Repaired!
car.class # => Repaired!

请注意,重新定义内置方法会产生一些警告:

# => untitled 5:6: warning: redefining `object_id' may cause serious problems
# => untitled 5:6: warning: redefining `__send__' may cause serious problems

编辑:糟糕,我发布的内容太快,没有发现问题。它调用repair,但不调用原始方法。我知道这似乎太容易了!我想我对这个问题不够深入。 :-)(注意:我认为Cary的方法非常聪明,它确实适用于您自己的方法,但看起来他使用内置方法达到了死胡同,无论如何,它改变方法的方式你真的不应该尝试使用内置方法。)