web-dev-qa-db-ja.com

Rails:ActiveRecordアソシエーションメソッドのオーバーライド

ActiveRecordアソシエーションによって提供されるメソッドの1つをオーバーライドする方法はありますか?

たとえば、次の典型的なポリモーフィックhas_many:throughassociationがあるとします。

class Story < ActiveRecord::Base
    has_many :taggings, :as => :taggable
    has_many :tags, :through => :taggings, :order => :name
end


class Tag < ActiveRecord::Base
    has_many :taggings, :dependent => :destroy
    has_many :stories, :through => :taggings, :source => :taggable, :source_type => "Story"
end

ご存知かもしれませんが、これにより、tags、tags <<、tags =、tags.empty?などの関連するメソッドがストーリーモデルに多数追加されます。

これらのメソッドの1つをオーバーライドするにはどうすればよいですか?具体的には、tags <<メソッドです。通常のクラスメソッドをオーバーライドするのは非常に簡単ですが、関連付けメソッドをオーバーライドする方法に関する情報が見つからないようです。次のようなことをする

def tags<< *new_tags
    #do stuff
end

呼び出されると構文エラーが発生するため、明らかにそれほど単純ではありません。

34
seaneshbaugh

has_manyでブロックを使用して、メソッドとの関連付けを拡張できます。コメント「ブロックを使用して関連付けを拡張する」を参照してください ここ
既存のメソッドをオーバーライドすることもできますが、それが良い考えかどうかはわかりません。

  has_many :tags, :through => :taggings, :order => :name do
    def << (value)
      "overriden" #your code here
      super value
    end     
  end
56
Voyta

Rails 3.2でモデル自体にアクセスする場合は、proxy_association.ownerを使用する必要があります

例:

class Author < ActiveRecord::Base
  has_many :books do
    def << (book)
      proxy_association.owner.add_book(book)
    end
  end

  def add_book (book)
    # do your thing here.
  end
end

ドキュメント を参照してください

18
Paul Oliver

<<メソッドを持つオブジェクトを返すには、tagsメソッドを定義する必要があります。

このようにすることもできますが、私は本当にお勧めしません。 ActiveRecordが使用するものを置き換えようとするよりも、モデルに必要なことを実行するメソッドを追加する方がはるかに良いでしょう。

これは基本的にデフォルトのtagsメソッドを実行し、結果のオブジェクトに<<メソッドを追加して、そのオブジェクトを返します。実行するたびに新しいメソッドが作成されるため、これは少しリソースを消費する可能性があります

def tags_with_append
  collection = tags_without_append
  def collection.<< (*arguments)
    ...
  end
  collection
end
# defines the method 'tags' by aliasing 'tags_with_append'
alias_method_chain :tags, :append  
0

署名にはdef tags.<<(*new_tags)が必要だと思います。これは機能するはずです。または、複数のメソッドをオーバーライドする必要がある場合は、同等で少しクリーンな次のものが必要です。

class << tags
  def <<(*new_tags)
    # rawr!
  end
end
0
x1a4

これはあなたの場合には役に立たないかもしれませんが、これを調べている他の人には役立つかもしれません。

アソシエーションコールバック: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

ドキュメントからの例:

class Project
  has_and_belongs_to_many :developers, :after_add => :evaluate_velocity

  def evaluate_velocity(developer)
    ...
  end
end

アソシエーション拡張機能も参照してください。

class Account < ActiveRecord::Base
  has_many :people do
    def find_or_create_by_name(name)
      first_name, last_name = name.split(" ", 2)
      find_or_create_by_first_name_and_last_name(first_name, last_name)
    end
  end
end

person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name  # => "Heinemeier Hansson"
0
Rimian

Railsガイド 追加されたメソッドを直接オーバーライドすることに関するドキュメント。

<<のオーバーライドに関するOPの問題は、おそらくこれに対する唯一の例外であり、次のようになります トップアンサー 。ただし、has_one=割り当てメソッドまたはゲッターメソッドでは機能しません。

0
lulalala

私が使用する方法は、関連付けを拡張することです。ここで「数量」属性を処理する方法を確認できます: https://Gist.github.com/1399762

それは基本的にあなたがただすることを可能にします

has_many : tags, :through => : taggings, extend => QuantityAssociation

メソッドをオーバーライドすることによって何を達成したいのかを正確に知らなければ、同じことができるかどうかを知ることは困難です。

0
BigFive