Hollerback
For Ruby 2+
Introduction
Hollerback adds the callback pattern to your application, allowing you to easily implement DSL-like event handling to your application.
note = NoteApi.get_note("Grocery List") do |on|
on.found do |note|
puts "Found an existing note!"
end
on.not_found do |name|
puts "Note is missing! Creating a new one."
Note.create!(name: name)
end
on.error do |error|
puts "Failed to retrieve note!"
end
end
Installation
If you're not using Bundler...
Install the gem via:
gem install hollerback
Then require it into your application with:
require 'hollerback'
If you're using Bundler...
Add the gem to your Gemfile:
gem 'hollerback'
And then bundle install to install the gem and its dependencies.
Usage
Enable the callback pattern by including the Hollerback module into the class/module you want to trigger callbacks.
class NoteApi
include Hollerback
def get_note(name)
# ...
end
end
Then add a &block argument to the function you want to be callback enabled.
class NoteApi
include Hollerback
def get_note(name, &block)
# ...
end
end
And use hollerback_for to get a set of callbacks you can invoke:
class NoteApi
include Hollerback
def get_note(name, &block)
hollerback_for(block) do |callbacks|
# ...
end
end
end
Then trigger callbacks as you like, passing any arguments you need.
class NoteApi
include Hollerback
def get_note(name, &block)
# Creates Callbacks object from the block
hollerback_for(block) do |callbacks|
begin
# Retrieves a HTTP response
response = make_note_request(name: name)
# Invoke Callbacks
when response.status
case 200
callbacks.respond_with(:found, response.body)
case 404
callbacks.respond_with(:not_found, name)
end
rescue => e
callbacks.respond_with(:error, e)
end
end
end
end
And finally use your newly callback-enabled function with your callback DSL:
def write_note(name, content)
note = NoteApi.new.get_note("Grocery List") do |on|
on.found do |note_json|
Note.from_json(note_json)
end
on.not_found do |name|
Note.create!(name: name)
end
on.error do |error|
raise "Failed to retrieve note! Reason: #{error.}"
end
end
note.append(content)
end
Features
#hollerback_for
Converts an anonymous callback block into a Hollerback::Callbacks object that you can invoke callbacks from. Can be called as an instance or class method from any class that includes Hollerback.
class NoteApi
include Hollerback
def self.get_note(name, &block)
hollerback_for(block) do |callbacks|
# ...
end
end
def get_note(name, &block)
hollerback_for(block) do |callbacks|
# ...
end
end
end
This is the equivalent of:
def get_note(name, &block)
callbacks = Hollerback::Callbacks.new(block)
end
If you override the behavior of Hollerback::Callbacks in a subclass, you can use it as your callbacks object instead:
class NoteCallbacks < Hollerback::Callbacks
# ...
end
class NoteApi
include Hollerback
def get_note(name, &block)
hollerback_for(block, callback_class: NoteCallbacks) do |callbacks|
# ...
end
end
end
#respond_with
Triggers a callback, passing any arguments along. If the callback isn't defined, it raises a NoMethodError.
callbacks_block = Proc.new do |on|
on.no_args { "No args." }
on.with_args { |a, b| "#{a}, #{b}" }
on.with_arg_list { |*args| args }
on.with_arg_block { |&block| block.call }
end
callbacks = Hollerback::Callbacks.new(callbacks_block)
callbacks.respond_with(:no_args)
# => "No args."
callbacks.respond_with(:with_args, 1, 2)
# => "1, 2"
callbacks.respond_with(:with_arg_list, *[1,2,3])
# => [1,2,3]
callbacks.respond_with(:with_arg_block, &(Proc.new { "Block called." }))
# => "Block called."
callbacks.respond_with(:some_nonexisting_callback)
# => NoMethodError: No callback 'some_nonexisting_callback' is defined.
#try_respond_with
Triggers a callback like respond_with, passing any arguments along. If the callback isn't defined, it returns nil.
callbacks_block = Proc.new do |on|
on.no_args { "No args." }
end
callbacks = Hollerback::Callbacks.new(callbacks_block)
callbacks.try_respond_with(:no_args)
# => "No args."
callbacks.try_respond_with(:some_nonexisting_callback)
# => nil
Testing
If you're writing RSpec tests around code that uses callbacks, you can mock callbacks using the rspec-hollerback-mocks gem.
it { expect(NoteApi).to receive(:get_note).with(name).and_callback(:found, note) }
Check out the rspec-hollerback-mocks gem to learn more.
Development
Install dependencies using bundle install. Run tests using bundle exec rspec
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/delner/rspec-hollerback-mocks.
License
The gem is available as open source under the terms of the MIT License.