No commit activity in last 3 years
No release in over 3 years
No additional description yet.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

has_many_callbacks¶ ↑

Adds additional callback hooks to ActiveRecord has_many relations.

Motivation¶ ↑

Callback hooks are a common pattern in software development, used when modeling event driven behavior.

Rails has_many relations provide hooks to child events, but they have several shortcomings. This gem provides additional callbacks, that are built on ActiveRecord model callbacks and work in the same way.

The main goal is ensuring the code that expresses the relation parent behavior when handling events triggered by child objects is kept in the parent code base, where it belongs. If the parent has a lot of relations, this prevents that class’ code to be scattered across all the child class definitions.

Usage¶ ↑

Just add a reference to Gemfile:

gem install "has_many_callbacks"

As an example, consider the following classes:

class TodoList < ActiveRecord::Base
  # name, completed
  has_many :tasks
end

class Task < ActiveRecord::Base
  # name, completed
  belongs_to :todo_list
end

Imagine that you want a To-Do list to be marked completed if it’s not empty and all the tasks in it are completed.

class TodoList < ActiveRecord::Base
  has_many :tasks, :inverse_of => :todo_list,
           :after_save => lambda { |todo_list, task|
             todo_list.completed = !task.completed ? false : todo_list.tasks.where(:completed => false).count == 0
             todo_list.save! if todo_list.completed_changed?
           },
           :after_destroy => lambda { |todo_list, task|
             todo_list.completed = (todo_list.tasks.any? and todo_list.tasks.where(:completed => false).count == 0)
             todo_list.save! if todo_list.completed_changed?
           }

end

Please note that the inverse_of option is required. Three callbacks are available, after_create, after_save and after_destroy.

You can also pass callbacks as symbols refering to the instance methods to call:

class TodoList < ActiveRecord::Base
  has_many :tasks,
           :inverse_of => :todo_list,
           :after_save => :handle_task_save,
           :after_destroy => :handle_task_destroy

  private

  def handle_task_save(task)
    self.completed = !task.completed ? false : tasks.where(:completed => false).count == 0
    save! if completed_changed?
  end

  def handle_task_destroy(task)
    self.completed = (tasks.any? and tasks.where(:completed => false).count == 0)
    save! if completed_changed?
  end
end

License¶ ↑

This software is available under the MIT License. See MIT-LICENSE for details.