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?いかなる場合でも再定義すべきではありません。reply_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 に答える