Ruby on rails 在实例方法中动态定义实例方法

Ruby on rails 在实例方法中动态定义实例方法,ruby-on-rails,ruby,user-defined-functions,Ruby On Rails,Ruby,User Defined Functions,我有几个类,每个类定义各种统计信息 class MonthlyStat attr_accessor :cost, :size_in_meters end class DailyStat attr_accessor :cost, :weight end 我想为这些对象的集合创建一个装饰器/演示器,使我能够轻松访问每个集合的聚合信息,例如: class YearDecorator attr_accessor :objs def self.[]= *objs new obj

我有几个类,每个类定义各种统计信息

class MonthlyStat
  attr_accessor :cost, :size_in_meters
end

class DailyStat
  attr_accessor :cost, :weight
end
我想为这些对象的集合创建一个装饰器/演示器,使我能够轻松访问每个集合的聚合信息,例如:

class YearDecorator
  attr_accessor :objs
  def self.[]= *objs
    new objs
  end
  def initialize objs
    self.objs = objs
    define_helpers
  end

  def define_helpers
    if o=objs.first # assume all objects are the same
      o.instance_methods.each do |method_name|
        # sums :cost, :size_in_meters, :weight etc
        define_method "yearly_#{method_name}_sum" do
          objs.inject(0){|o,sum| sum += o.send(method_name)}
        end
      end
    end
  end
end

YearDecorator[mstat1, mstat2].yearly_cost_sum
不幸的是,define方法在实例方法中不可用

将此替换为:

class << self
  define_method "yearly_#{method_name}_sum" do
    objs.inject(0){|o,sum| sum += o.send(method_name)}
  end
end

class我很好奇你用
o.instance\u方法得到了什么。这是一个类级别的方法,通常不适用于对象的实例,我可以告诉您,这就是您在这里要处理的

无论如何,您可能正在寻找
method\u missing
,它将在您第一次调用它时动态定义该方法,并允许您将
:define\u method
发送到对象的类。您不需要每次实例化新对象时都重新定义相同的实例方法,因此
method\u missing
将允许您仅在尚未定义调用的方法时在运行时更改类

由于您希望其他类中的方法名包含一些模式(即,
yearly\u base\u sum
将对应于
base
方法),因此我建议编写一个方法,如果找到匹配模式,则返回匹配模式。注意:这不需要在另一个类上列出方法-当您的一个对象不知道如何响应您发送的消息时,您仍然应该依赖内置的
NoMethodError
。这使您的API更加灵活,并且在您的stats类也可能在运行时被修改的情况下非常有用

def method_missing(name, *args, &block)
  method_name = matching_method_name(name)
  if method_name
    self.class.send :define_method, name do |*args|
      objs.inject(0) {|obj, sum| sum + obj.send(method_name)}
    end
    send name, *args, &block
  else
    super(name, *args, &block)
  end
end

def matching_method_name(name)
  # ... this part's up to you
end

我很好奇你从
o.instance\u方法中得到了什么。这是一个类级别的方法,通常不适用于对象的实例,我可以告诉您,这就是您在这里要处理的

无论如何,您可能正在寻找
method\u missing
,它将在您第一次调用它时动态定义该方法,并允许您将
:define\u method
发送到对象的类。您不需要每次实例化新对象时都重新定义相同的实例方法,因此
method\u missing
将允许您仅在尚未定义调用的方法时在运行时更改类

由于您希望其他类中的方法名包含一些模式(即,
yearly\u base\u sum
将对应于
base
方法),因此我建议编写一个方法,如果找到匹配模式,则返回匹配模式。注意:这不需要在另一个类上列出方法-当您的一个对象不知道如何响应您发送的消息时,您仍然应该依赖内置的
NoMethodError
。这使您的API更加灵活,并且在您的stats类也可能在运行时被修改的情况下非常有用

def method_missing(name, *args, &block)
  method_name = matching_method_name(name)
  if method_name
    self.class.send :define_method, name do |*args|
      objs.inject(0) {|obj, sum| sum + obj.send(method_name)}
    end
    send name, *args, &block
  else
    super(name, *args, &block)
  end
end

def matching_method_name(name)
  # ... this part's up to you
end
(编辑:我知道你现在想做什么。)

嗯,我尝试了与您可能尝试过的相同的方法,但最终不得不使用
eval

class Foo
  METHOD_NAMES = [:foo]

  def def_foo
    METHOD_NAMES.each { |method_name|
      eval <<-EOF
        def self.#{method_name}
          \"#{method_name}\".capitalize
        end
      EOF
    }
  end
end

foo=Foo.new

foo.def_foo
p foo.foo # => "Foo"

f2 = Foo.new
p f2.foo # => "undefined method 'foo'..."
class-Foo
方法_name=[:foo]
迪福
方法_名称。每个{|方法_名称|
eval“未定义的方法‘foo’…”
我自己承认这不是最优雅的解决方案(甚至可能不是最惯用的),但我在过去也遇到过类似的情况,其中最直接的方法是
eval
(编辑:我明白你现在想做的。)

嗯,我尝试了与您可能尝试过的相同的方法,但最终不得不使用
eval

class Foo
  METHOD_NAMES = [:foo]

  def def_foo
    METHOD_NAMES.each { |method_name|
      eval <<-EOF
        def self.#{method_name}
          \"#{method_name}\".capitalize
        end
      EOF
    }
  end
end

foo=Foo.new

foo.def_foo
p foo.foo # => "Foo"

f2 = Foo.new
p f2.foo # => "undefined method 'foo'..."
class-Foo
方法_name=[:foo]
迪福
方法_名称。每个{|方法_名称|
eval“未定义的方法‘foo’…”

我自己也承认这不是最优雅的解决方案(甚至可能不是最惯用的)但我在过去也遇到过类似的情况,最直接的方法是
eval

问题是我希望这些方法只应用于一个实例,而上面的方法将在未来所有实例上创建方法,对吗?所以当我传递几个月的状态时,我会动态创建:year\u size\u sum。如果我随后传入DailyStats(没有大小方法),year\u size\u sum方法仍然是定义的,因为它是在类级别定义的。不,这个
method\u missing
是在
YearDecorator
类上定义的,它定义的方法也保留在那里。如果我没有说清楚的话,很抱歉。啊,明白了。不幸的是,这会在将来的实例中导致方法污染。我认为我是赞成的bably尝试这样做时脱离了Ruby/Rails,太痛苦了。这很公平。如果您对更好的解释感兴趣,可以很好地概括一下在与您类似的情况下使用
method\u missing
。问题是我希望这些方法只应用于单个实例,而上述方法将在一个实例上创建方法那么,当我传入几个月的状态时,我会动态创建:year\u size\u sum。如果我传入DailyStats(没有大小方法),year\u size\u sum方法仍然是定义的,因为它是在类级别定义的。不,这个
method\u missing
是在
YearDecorator
类上定义的,它定义的方法也保留在那里。如果我没有说清楚的话,很抱歉。啊,明白了。不幸的是,这会在将来的实例中导致方法污染。我认为我是赞成的bably尝试这样做时脱离了Ruby/Rails,太痛苦了。这很公平。如果你对更好的解释感兴趣,这是一个很好的概述,介绍了在与你类似的情况下使用
方法\u missing
。请参阅我上面的评论,认为此方法遇到了相同的问题。它对我有效。
类签出此要点:,它可能会我希望动态创建的方法只应用于实例,