Seh
Structured event handler. Pure ruby event handling similar to w3c dom events. Check out examples/epic_battle.rb
Why Seh?
Seh was developed as a dependency of my ruby game engine. I couldn't find another ruby event library with the features I wanted.
You might want to use Seh if:
- You need events/hooks/triggers/slots+signals
- You need a synchronous system, because an event must be fully resolved before things can continue
- An object/target/actor/thing might be affected by other things it doesn't know about
- Your objects have underlying hierarchical / graph / observer relationships
- You're hoping for complex emergent behaviors
You probably don't want to use Seh if:
- You need distributed or asynchronous events/hooks/triggers/slots+signals
- You're thinking "gee, this seems complicated, I really just need simple hooks" -> Seh is probably over-engineering for you
Features
Seh is driven by Seh::Event objects targeting objects of type Seh::EventTarget. Events are typed using Seh::EventType (which is mostly used implicitly).
In most event systems, such as w3c dom events in a web browser, events are handled by child nodes before the event bubbles to ancestor nodes. In Seh, each EventTarget which sees the Event may add callbacks to the event, before any callbacks on the event are executed. After each EventTarget has been visited, the Event executes its callbacks. This allows an ancestor object to affect an outcome on a descendant.
Major Features
-
EventTargetis a mixin allowing any object to be the target of anEvent - Create an
Event, add some targets - objects which mixinEventTarget- and dispatch the event. EachEventis one-use-only -
Eventhas a list of types, e.g. :snow, :bad_weather, :sunshine; Types may be any object which defines equality == - Bind callbacks to an
EventTarget, e.g.my_target.bind(:snow) { "It's snowing!" } - Bind target callbacks using a type boolean expression, e.g.
my_target.bind( Seh::or :hurricane, Seh::and(:rain, :sunshine, Seh::not(:cold)) ) { "It's either a hurricane, or sunny and raining and not cold." } - Disconnect callback binds,
my_bind = my_target.bind(:only_needed) { "for awhile" }; my_bind.disconnect -
EventTarget#observersdefaults to[], and can be overridden to create an object graph. EachEventTargetrecursively reachable byEventTarget#observersreceives the event as if it was a top-level target. -
Eventmakes it easy for events to "inherit" from one another, so that you may develop a rich event hierarchy on your application side. Note that there's no real ruby class inheritance, e.g. inexamples/event/, theEvent::damage()method callsEvent::hostile(), making each damage event a hostile event. -
Eventinherits fromOpenStructto easily define attributes on the event - Event stages provide fine-grained control over callbacks
Understanding Event Stages & Callbacks
See Seh::Event#dispatch. When #dispach is called:
- determine the full set of targets affected by this event
- run callbacks on targets which match this event's types
- run stage callbacks contained in this event; typically targets will append stage callbacks to this event using Event#bind, #start, #finish
- Callback execution order: (1) start callbacks; (2) stage callbacks - in the order stages were added to the event; (3) finish callbacks
target = Seh::EventTarget::Default.new # Default includes EventTarget
target.bind(:fireball) do |event|
puts "1"
event.finish { puts "4" }
end
event = Seh::Event.new
event.type :fireball
event.target target
event.start { puts "2" }
event.add_stage :burn_enemy
event.bind(:burn_enemy) { puts "3" }
event.dispatchThe output of the above code is:
1
2
3
4Roadmap
I'm working on efficiency, so tens of thousands of events can be used each second in a game engine. There's a toy benchmark in 'rake benchmark'.
Release Notes
- v0.3.0 - updated api, significantly expanded test coverage, documentation, examples, released under the BSD license.
- v0.1.0 - an older version of the API that's not backwards-compatible
License
Seh is released under the BSD license.