Rails: переопределение метода ассоциации ActiveRecord - PullRequest
33 голосов
/ 23 мая 2010

Есть ли способ переопределить один из методов, предоставляемых ассоциацией ActiveRecord?

Скажем, например, у меня есть следующий типичный полиморфный has_many: через ассоциацию:

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

Как вы, вероятно, знаете, это добавляет целый ряд связанных методов в модель Story, таких как теги, теги <<, tags =, tags.empty? И т. Д. </p>

Как мне переопределить один из этих методов? В частности, теги << метод. Довольно просто переопределить обычные методы класса, но я не могу найти никакой информации о том, как переопределить методы ассоциации. Делать что-то вроде </p>

def tags<< *new_tags
    #do stuff
end

выдает синтаксическую ошибку при вызове, поэтому, очевидно, не все так просто.

Ответы [ 7 ]

55 голосов
/ 23 мая 2010

Вы можете использовать блок с has_many, чтобы расширить связь с методами. См. Комментарий «Используйте блок для расширения ваших ассоциаций» здесь .
Переопределение существующих методов также работает, однако не знаю, хорошая ли это идея.

  has_many :tags, :through => :taggings, :order => :name do
    def << (value)
      "overriden" #your code here
      super value
    end     
  end
18 голосов
/ 25 января 2013

Если вы хотите получить доступ к самой модели в 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

См. документацию

0 голосов
/ 18 октября 2016

Rails направляет документы о прямой переопределении добавленных методов.

Проблема ОП с переопределением <<, вероятно, является единственным исключением из этого, за которым следует верхний ответ . Но это не сработает для has_one = метода присваивания или методов получения.

0 голосов
/ 19 декабря 2012

Это может быть не полезно в вашем случае, но может быть полезно для других, изучающих это.

Обратные вызовы ассоциации: 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 голосов
/ 29 ноября 2011

Метод, который я использую, состоит в расширении ассоциации. Вы можете увидеть, как я обрабатываю атрибуты количества здесь: https://gist.github.com/1399762

Это в основном позволяет просто сделать

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

Не зная точно, чего вы надеетесь достичь путем переопределения методов, трудно понять, можете ли вы сделать то же самое.

0 голосов
/ 23 мая 2010

Вы должны будете определить метод тегов для возврата объекта, у которого есть метод <<.

Вы могли бы сделать это так, но я действительно не рекомендовал бы это. Вам было бы намного лучше просто добавить в модель метод, который делает то, что вы хотите, чем пытаться заменить то, что использует ActiveRecord.

По сути, он запускает метод по умолчанию tags, который добавляет метод << к результирующему объекту и возвращает этот объект. Это может быть немного ресурсоемким, потому что он создает новый метод каждый раз, когда вы запускаете его </p>

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 голосов
/ 23 мая 2010

Я думаю, что вы хотели def tags.<<(*new_tags) для подписи, которая должна работать, или следующее, что эквивалентно и немного чище, если вам нужно переопределить несколько методов.

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