【问题标题】:Ruby define_methodRuby 定义方法
【发布时间】:2012-10-21 12:07:34
【问题描述】:

我必须通过以下测试:

def test_can_find_by_arbitrary_fields
  assert @library.respond_to? :find_by_artist
  assert !@library.respond_to?(:find_by_bitrate)
  @library.add_song({ :artist => 'Green Day',
                     :name => 'American Idiot',
                     :bitrate => 192 })
  assert @library.respond_to?(:find_by_bitrate)
end

我不知道该怎么做。

我试过了:

def respond_to?(method)
     if self.public_methods.include? method
         true
     elsif (method == :find_by_bitrate)
         define_method :find_by_bitrate, ->(default = nrb)  { @songs.select |a| a[:bitrate] == nrb }       
         false
     else
         false
end

但它说“define_method 未定义”。有什么方法可以定义find_by_bitrate 方法吗?

【问题讨论】:

  • 您确定要重新定义respond_to?吗?我怀疑这不是你想要做的。至少你几乎肯定不想在respond_to? 中定义一个方法。
  • 并非如此。但我想不出在第一次未找到后插入 find_by_bitrate 的任何其他方式。
  • 但是respond_to? 在尝试调用对象上的方法时不会被调用,因此除非您的实际应用代码调用respond_to?,否则它不会定义find_by_bitrate。您使用的是 Ruby 1.9 吗?

标签: ruby metaprogramming


【解决方案1】:

您可以在 method_missing 中第一次调用方法时定义方法。

您是否应该进行一些辩论,但它比respond_to? 更好。

class Foo
  def method_missing(sym)
    puts "Method missing; defining."
    self.class.send(:define_method, sym) do
      puts "Called #{sym}."
    end
  end
end

完整性检查:

f = Foo.new
=> #<Foo:0x007fa6aa09d3c0>
f.wat
=> Method wat missing; defining.
f.wat
=> Called wat.
f2 = Foo.new
=> Called wat.

【讨论】:

    【解决方案2】:

    我认为您不应该重新定义 respond_to? 方法。测试的重点是(可能)@library 对象应该定义一个find_by_artist 方法并且没有find_by_bitrate,直到您添加一首具有比特率的歌曲。 IE。 add_song 方法应该定义方法 find_by_bitrate 当它看到一首具有比特率的歌曲时(?)。

    另外,define_method 是 Class 的私有方法。上面,您试图从实例方法中调用它。请参阅“Ruby: define_method vs. def”,还有更多关于这些内容的信息。

    【讨论】:

      【解决方案3】:

      缺少很多信息来正确回答这个问题。该测试表明即使@library 为空,也始终定义find_by_artist,但是只有当库包含具有此类方法的记录时,其他属性(例如:比特率)上才有可用的动态方法。

      在任何情况下都不应该重新定义respond_to?。有一个明确的钩子方法来回答respond_to?对于动态方法:Object#respond_to_missing?。

      因此,让您的测试通过的一种简单方法是确保 @library 对象有一个具体的方法 #find_by_artist 和一个响应钩子来检查它的任何元素是否具有请求的属性。如果我假设@library 是一个集合对象Library,它会枚举@songs 中的歌曲

      class Library
        def find_by_artist artist
          @songs.select { |song| song['artist'] == artist }
        end
      
        def method_missing meth, arg
          m = /^find_by_(.+)$/.match meth.to_s
          return super unless attr = m && m[1]
      
          @songs.select { |song| song[attr] ==  arg }
        end
      
        def respond_to_missing? meth, include_private
          m = /^find_by_(.+)$/.match meth.to_s
          return super unless attr = m && m[1]
      
          @songs.any? { |song| song.has_key? attr }
        end
      end
      

      这在那个respond_to中有性能问题吗?现在会搜索所有歌曲。可以通过保留一组包含在 @songs 中的所有属性并在添加/更新/删除集合中元素的方法中对其进行更新来进行优化。

      【讨论】:

        猜你喜欢
        • 2019-12-25
        • 2012-03-13
        • 2010-10-15
        • 2014-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-22
        相关资源
        最近更新 更多