Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

has_many and :after_add / :before_add => callback for << and create methods

I am reading Rails 3 Way book and got confused at the point:

:after_add => callback Called after a record is added to the collection via the << method. Is not triggered by the collection’s create method

As I understand book.chapters.create(title: 'First Chapter') won't invoke before_add callback but actually it is calling.

class Book < ActiveRecord::Base
  attr_accessible :title
  has_many :chapters, :before_add => :add_chapter

  private
    def add_chapter(chapter)
      logger.error('chapter added to book')
    end
end

class Chapter < ActiveRecord::Base
  belongs_to :book
  attr_accessible :title
end

In Console(minified)

 > b = Book.first
  Book Load (0.1ms)  SELECT "books".* FROM "books" LIMIT 1
 > b.chapters.create(title: 'Last Chapter')
  begin transaction
chapter added to book
  INSERT INTO "chapters" ....
  commit transaction

Here you can see that after_add callback is invoke for create.

Am I misunderstood something?

Edit

b.chapters.new(title: 'New Chapter')
b.chapters.build(title: 'New Chapter')

also invokes callback

like image 343
Amit Patel Avatar asked Sep 20 '12 11:09

Amit Patel


1 Answers

The before_add and after_add callbacks are triggered when an item is added to a collection. It has nothing to do with whether or not a record is saved to the database. (A slight exception is to be made here for has_and_belongs_to_many and has_many :through relations, where adding it to the collection is immediately reflected to the database by ActiveRecord internally).

As soon as you add a new record to the collection, the callbacks will fire. The before_add will be called before the element is added to the collection, and the after_add after.

This example below might give you a better understanding.

# id: integer
class Author < ActiveRecord::Base
  has_many :books, before_add: :bef, after_add: aft

  def bef
    puts "Before adding, author ##{id} has #{books.size} books"
  end

  def aft
    puts "After adding, author ##{id} has #{books.size} books"
  end
end

# id integer
# author_id: integer
class Book < ActiveRecord::Base
  belongs_to :author
  after_save: :saved

  def saved
    puts "The book is now saved!"
  end
end

> book = Book.new

> author = Author.first

> author.books << book
'Before adding, author #1 has 3 books'
'After adding, author #1 has 4 books'

> author.save
'The book is now saved'
like image 185
Pelle Avatar answered Sep 19 '22 02:09

Pelle