1

ステータスのテーブルがあり、それぞれに name 属性があります。現在、私はできる:

FooStatus.find_by_name("bar")

そして、それは結構です。しかし、私ができるかどうか疑問に思っています:

FooStatus.bar

だから私はこのアプローチを持っています:

class FooStatus < ActiveRecord::Base
  def self.method_missing(meth, *args, &block)
    if self.allowed_statuses.include?(meth.to_s.titleize)
      self.where("name = ?", meth.to_s.titleize).first
    else
      super(meth, *args, &block)
    end
  end

  def self.allowed_statuses
    self.pluck(:name)
  end
end

上記のコードは機能しますが、次の奇妙な動作につながります。

FooStatus.respond_to?(:bar) => false
FooStatus.bar => #<FooStatus name: 'bar'>

それは良くないのですが、respond_to? を実装しようとすると、再帰の問題が発生します。

class FooStatus < ActiveRecord::Base
  def self.method_missing(meth, *args, &block)
    if self.allowed_statuses.include?(meth.to_s.titleize)
      self.where("name = ?", meth.to_s.titleize).first
    else
      super(meth, *args, &block)
    end
  end

  def self.allowed_statuses
    self.pluck(:name)
  end

  def self.respond_to?(meth, include_private = false)
    if self.allowed_statuses.include?(meth.to_s.titleize)
      true
    else
      super(meth)
    end
  end
end

そして、それは私を取得します:

FooStatus.bar => ThreadError: deadlock; recursive locking

method_missing と Respond_to を連携させるためのアイデアはありますか?

4

3 に答える 3

1

私があなたのアプローチをお勧めするかどうかはわかりません...私にはあまりにも魔法のようで、「destroy」という名前のステータスまたは合法的に呼び出したい他の方法があるとどうなるか心配です(またはそのRailsの呼び出しは、あなたが気付いていない内部的なものです)。

しかし...メソッドが欠落しているのではなく、クラスを拡張し、allowed_statusesをループしてメソッドを作成することにより、メソッドを自動的に定義する方がよいと思います。これはrespond_toになりますか?仕事。また、他の場所でまだ定義されていないことを確認することもできます...

于 2012-11-30T20:15:57.843 に答える
1

Philip Hallstrom の提案に同意します。クラスのビルド時に allowed_statuses がわかっている場合は、リストをループしてメソッドを明示的に定義します。

%w(foo bar baz).each do |status|
  define_singleton_method(status) do
    where("name = ?", status.titleize).first
  end
end

…または、コードの他の場所でステータスのリストが必要な場合:

ALLOWED_STATUSES = %w(foo bar baz).freeze
ALLOWED_STATUSES.each do |status|
  define_singleton_method(status) do
    where("name = ?", status.titleize).first
  end
end

より明確で、短く、将来の破損や奇妙なうさぎの穴が発生する可能性がはるかに低くなり、現在のような ActiveRecord と競合します。

method_missing とその仲間を使って本当にすばらしいことを行うことができますが、メタプログラミングを行うときに最初に使用する方法ではありません。通常、可能な場合は明示的な方が適切です。

また、組み込みメソッドとの競合の作成に関する Philip の懸念にも同意します。ステータスのリストをハードコーディングすることで、行き過ぎを防ぐことができますが、そのリストが拡大または変更される可能性がある場合は、FooStatus.named_bar代わりに次のような規則を検討することもできます。FooStatus.bar

于 2012-11-30T22:01:25.027 に答える
0

スコープを使用します。

class FooStatus < ActiveRecord::Base
  scope :bar, where(:name => "bar")

  # etc
end

FooStatus.barこれで、ActiveRelationオブジェクトを返す方法を実行できます。これが単一のインスタンスを返すことを期待する場合は、またはFooStatus.bar.first多くの場合、またはをスコープの最後にFooStatus.bar.all置くことができます。その場合、ファインダーと同じものが返されます。.first.all

入力が一定でない場合(常に「バー」とは限らない)、ラムダを使用してスコープを定義することもできます。 このガイドのセクション13.1に例があります

于 2012-11-30T20:16:14.357 に答える