Problem mit Setter-Override bei ActiveRecord

Dies ist nicht gerade eine Frage, sondern ein Bericht darüber, wie ich ein Problem mit gelöst habewrite_attribute Wenn das Attribut ein Objekt ist, auf RailsActive Record. Ich hoffe, dass dies für andere hilfreich sein kann, die sich dem gleichen Problem gegenübersehen.

Lassen Sie mich mit einem Beispiel erklären. Angenommen, Sie haben zwei Klassen,Book undAuthor:

class Book < ActiveRecord::Base
  belongs_to :author
end

class Author < ActiveRecord::Base
  has_many :books
end

Sehr einfach. Aber aus irgendeinem Grund müssen Sie die überschreibenauthor= Methode einBook. Als Neuling bei Rails bin ich dem Vorschlag von Sam Ruby zur agilen Webentwicklung mit Rails gefolgt: useattribute_writer private Methode. Mein erster Versuch war also:

class Book < ActiveRecord::Base
  belongs_to :author

  def author=(author)
    author = Author.find_or_initialize_by_name(author) if author.is_a? String
    self.write_attribute(:author, author)
  end
end

Das geht leider nicht. Das bekomme ich von der Konsole:

>> book = Book.new(:name => "Alice's Adventures in Wonderland", :pub_year => 1865)
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author = "Lewis Carroll"
=> "Lewis Carroll"
>> book
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author
=> nil

Es scheint, dass Rails nicht erkennt, dass es sich um ein Objekt handelt, und nichts macht: Nach der Zuschreibung ist der Autor immer noch null! Natürlich könnte ich es versuchenwrite_attribute(:author_id, author.id), aber es hilft nicht, wenn der Autor noch nicht gespeichert ist (es hat noch keine ID!) und ich muss die Objekte zusammen speichern (Autor muss nur gespeichert werden, wenn das Buch gültig ist).

Nachdem ich viel nach einer Lösung gesucht hatte (und viele andere Dinge vergeblich ausprobiert hatte), fand ich diese Meldung:http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/4fe057494c6e23e8, also endlich konnte ich etwas Arbeitscode haben:

class Book < ActiveRecord::Base
  belongs_to :author

  def author_with_lookup=(author)
    author = Author.find_or_initialize_by_name(author) if author.is_a? String
    self.author_without_lookup = author
  end
  alias_method_chain :author=, :lookup
end

Diesmal war die Konsole nett zu mir:

>> book = Book.new(:name => "Alice's Adventures in Wonderland", :pub_year => 1865)
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author = "Lewis Carroll"=> "Lewis Carroll"
>> book
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author
=> #<Author id: nil, name: "Lewis Carroll", created_at: nil, updated_at: nil>

Der Trick hier ist deralias_method_chain, das einen Abfangjäger erzeugt (in diesem Fallauthor_with_lookup) und einen alternativen Namen zum alten Setter (author_without_lookup). Ich gebe zu, dass es einige Zeit gedauert hat, diese Regelung zu verstehen, und ich wäre froh, wenn jemand sie im Detail erklären würde, aber was mich überraschte, war der Mangel an Informationen über diese Art von Problem. Ich muss viel googeln, um nur einen Beitrag zu finden, der nach dem Titel anfangs nichts mit dem Problem zu tun hatte. Ich bin neu bei Rails, was denkst du, Jungs: Ist das eine schlechte Praxis?

Antworten auf die Frage(3)

Ihre Antwort auf die Frage