Project

hollerback

0.0
No commit activity in last 3 years
No release in over 3 years
Simple DSL-like callback pattern for Ruby classes.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.11
>= 0
~> 10.0
~> 3.0
 Project Readme

Hollerback

Build Status Gem Version

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.message}"
    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

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/delner/hollerback.

License

The gem is available as open source under the terms of the MIT License.