1

我有以下必须通过的测试:

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方法吗?

4

3 回答 3

3

您可以在第一次调用方法时定义方法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.
于 2012-10-31T23:36:49.663 回答
1

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

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

于 2012-10-31T22:06:10.867 回答
1

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

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

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

于 2012-10-31T23:25:00.053 回答