CallbacksAttachable

Attach callbacks to every ruby object including this mixin. Trigger all callbacks attached under the same namespace and detach callback when you no longer need them.

Installation

Add this line to your application's Gemfile:

gem 'callbacks_attachable'

And then execute:

$ bundle

Or install it yourself as:

$ gem install callbacks_attachable

Usage

require 'callbacks_attachable'

class CallbacksHolder
    include CallbacksAttachable
end

Callbacks attached to the class

Attach callbacks to the class:

callback = CallbacksHolder.on(:event) do |method|
    puts self.__send__(method)
end

Callbacks attached to the class are executed in the context of it, i.e. self points to the class.

Trigger all :event callbacks with:

CallbacksHolder.trigger(:event, :name) # => puts 'CallbacksHolder'

The second and further arguments given to .trigger are passed down to the callback.

Callbacks can be executed in the context of a different context by passing the context option to .trigger:

object = Object.new
CallbacksHolder.trigger(:event, :__id__, context: object)
# => puts the result of object.__id__

To detach a callback call .off with the event namespace and the callback handle returned by .on:

CallbacksHolder.off(:event, callback)
CallbacksHolder.trigger(:event, :name)  # => nothing will happen

If you want to execute a callback just a single time attach it with .once_on:

CallbacksHolder.once_on(:singularity) { puts 'callback called!' }
CallbacksHolder.trigger(:singularity) # => puts 'callback called!' and immediately
                                      #    detaches the callback
CallbacksHolder.trigger(:singularity) # => does nothing

To detach a callback when a condition is met use .until_true_on. The callback will be detached if it has a truthy (!) return value.

counter = 0
CallbacksHolder.until_true_on(:count_to_two) do
    puts counter
    counter >= 2
end

CallbacksHolder.trigger(:count_to_two) # => puts 0
CallbacksHolder.trigger(:count_to_two) # => puts 1
CallbacksHolder.trigger(:count_to_two) # => puts 2 and detaches the callback
CallbacksHolder.trigger(:count_to_two) # => does nothing

Callbacks attached to an instance

All above mentioned methods on the class level also exist for each instance of the class. Callbacks are executed if the instance they are attached to or their class calls #trigger.

Callbacks are called in their lexical scope. So, self inside and outside the callback is the same object. The context of a callback cannot be changed with the context option of the #trigger method as it is possible for class callbacks.

callbacks_holder1 = CallbacksHolder.new
callbacks_holder2 = CallbacksHolder.new

callback1 = callbacks_holder1.on(:event) { puts 'This is #1!' }
callback2 = callbacks_holder2.on(:event) { puts 'This is #2!' }

callbacks_holder1.trigger(:event) # => puts 'This is #1!'
callbacks_holder2.trigger(:event) # => puts 'This is #2!'

CallbacksHolder.trigger(:event) # => puts 'This is #1!' and 'This is #2!'

callbacks_holder1.off(:event, callback)
callbacks_holder2.off(:event, callback)

The two methods #once_on and #until_true_on are available for instances, too, and work as you'd expect.