HasGuardedHandlers
HasGuardedHandlers allows an object's API to provide flexible handler registration, storage and matching to arbitrary events.
Installation
gem install has-guarded-handlers
Usage
require 'has_guarded_handlers'
class A
include HasGuardedHandlers
end
a = A.new
a.register_handler :event, :type => :foo do |event|
puts "Handled the event of type #{event.type} with value #{event.value}"
end
Event = Class.new Struct.new(:type, :value)
a.trigger_handler :event, Event.new(:foo, 'bar')
Register a handler for a particular named channel:
a.register_handler(:event) { ... }
a.register_handler(:event, :type => :foo) { ... }
a.trigger_handler :event, :foo
Register a global handler for all channels:
a.register_handler { ... }
a.register_handler(nil, :type => :foo) { ... }
a.trigger_handler :event, :foo
Register a temporary handler, which is deleted once triggered:
a.register_tmp_handler(:event) { ... }
a.trigger_handler :event, :foo
Handlers are triggered in order of priority, followed by order of declaration. By default, all handlers are registered with priority 0, and are thus executed in the order declared:
a.register_handler { ... }
a.register_handler { ... }
...
a.trigger_handler :event, :foo
You may specify a handler priority in order to change this order. Higher priority is executed first:
a.register_handler(:event) { ... }
a.register_handler_with_priority(:event, 10) { ... }
...
a.trigger_handler :event, :foo
You may specify a priority for a temporary handler:
a.register_handler_with_options(:event, {:tmp => true, :priority => 10}, :foo => :bar) { ... }
Handler chaining
Each handler can control whether subsequent handlers should be executed by throwing :pass
or :halt
.
To explicitly pass to the next handler, throw :pass
in your handler:
a.register_handler(:event) { do_stuff; throw :pass }
a.register_handler(:event) { ... }
a.trigger_handler :event, :foo
or indeed explicitly halt the handler chain by throwing :halt
in the handler:
a.register_handler(:event) { do_stuff; throw :halt }
a.register_handler(:event) { ... }
a.trigger_handler :event, :foo
If nothing is thrown in the event handler, the handler chain will be halted by default, so subsequent handlers will not be executed.
a.register_handler(:event) { do_stuff; }
a.register_handler(:event) { ... }
a.trigger_handler :event, :foo
By triggering the event in broadcast mode, the handler chain will continue by default.
a.register_handler(:event) { do_stuff; }
a.register_handler(:event) { ... }
a.trigger_handler :event, :foo, broadcast: true
What are guards?
Guards are a concept borrowed from Erlang. They help to better compartmentalise handlers.
There are a number of guard types and one bit of special syntax. Guards act like AND statements. Each condition must be met if the handler is to be used.
message :chat?, :body
The different types of guards are:
register_handler Foo
register_handler :chat?
register_handler :body => 'exit'
register_handler :body => /exit/
register_handler :name => [:gone, :fobidden]
register_handler [:[], :name] => :gone
register_handler proc { |m| m.id % 3 == 0 }
register_handler [{:body => 'foo'}, {:body => 'baz'}]
Links:
Note on Patches/Pull Requests
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with rakefile, version, or history.
- If you want to have your own version, that is fine but bump version in a commit by itself so I can ignore when I pull
- Send me a pull request. Bonus points for topic branches.
Copyright
Copyright (c) 2011 Ben Langfeld, Jeff Smick. MIT licence (see LICENSE.md for details).