No commit activity in last 3 years
No release in over 3 years
Provides a way to define callbacks of one ActiveRecord model into an associated one
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

< 10.0.0, >= 9.0.0
< 13.0.0, >= 10.3.0
< 4.0.0, >= 3.1.0
< 1.4.0, >= 1.3.0

Runtime

< 6.0.0, >= 5.0.0
< 6.0.0, >= 5.0.0
 Project Readme

AssociationCallbacks

Provides a way to define callbacks of one ActiveRecord model into an associated one.

Example

First, two simple Article and Comment models:

class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :article
end

Then, you often need to denormalize Comment stuff into Post or vice versa. Here is the standard way to denormalize last_comment_at on posts:

class Comment < ActiveRecord::Base
  belongs_to :article

  after_create :update_post_last_comment_at
  after_destroy :update_post_last_comment_at

  def update_post_last_comment_at
    post.update_attributes!(last_comment_at: post.comments.order('created_at').last.try(:created_at))
  end
end

But, there is a problem here: we define Post denormalization callbacks into Comment model. IMHO, this is the wrong place. Post denormalization should be in Post model in order to have less relationship between models.

Here is how to do it with association_callabacks:

class Post < ActiveRecord::Base
  has_many :comments

  after_create :update_last_comment_at, source: :comments
  after_destroy :update_last_comment_at, source: :comments

  def update_last_comment_at
    update_attributes!(last_comment_at: comments.order('created_at').last.try(:created_at))
  end
end

You just have to specify :source option to your callbacks, and that's all! Note that :source option must be an existing association name.

Note that association callbacks methods can take associated record as argument, above code can be:

class Post < ActiveRecord::Base
  has_many :comments

  after_create :update_last_comment_at

  def update_last_comment_at(comment)
    update_attributes!(last_comment_at: comment.created_at)
  end
end

Association callbacks can also be defined as block:

class Post < ActiveRecord::Base
  has_many :comments

  after_destroy source: :comments do |post|
    post.decrement!(:comments_count)
  end
end

Another solution is to use ActiveModel Observers, but for a better project comprehension, I really prefer placing denormalization directly into model. Observers are more designed for emails notifications, cache sweepers, etc.

Installation

Just add this into your Gemfile:

gem 'association_callabacks'

Then, just run bundle install.

Executing test suite

This project is fully tested with Rspec 3. Just run bundle exec rake (after a bundle install).