super-emitter

Declarative event binding library

SuperEmitter

If you are writing your own GUI components, or you are just tired of event-binding boilerplate, this package is what you need. Out of the box:

  • Declarative event binding, with concise format, very clear about event source, name and its handlers
  • Event handling logic is now a data structure (!!), not code
  • Event handling inheritance
  • Event handling composition
  • Light-weight event emission (no built-in bubble/capture), but with arguments
# I use different parentheses styles to separate method calls from function calls:  
obj.method()
(function1 (function2 arg1)arg2)
# I don't use brackets on methods, if I pass anonymous multiline functions. 
$jquery_element.on 'click'(e) ->
  console.log('wow')
# Your old boilerplate to bind handlers to events: 
class EventSpaghetti
  constructor: ->
    $some_jquery.on 'click'(e) ->
      task1(e)
      task2(e)
      task3(e)
 
    @non_jquery_emitter.on 'server_event'(e) ->
      if (event_is_valid e)
        @update_view(e)
 
new EventSpaghetti();
 
# Your new boilerplate 
class Component extends SuperEmitter
  event_table: [ 
    [ $some_jquery        [ [ 'click'       [ task1,
                                                  task2,
                                                  task3          ]
    [ 'non_jquery_emitter'[ [ 'server_event'[ event_is_valid,
                                                  'update_view'  ]
  ]
new Component().bind_events()

Clone the repo, open demo/index.html in browser, move your mouse, click on blocks, look at console output.

Read the demo/app.coffee it's very short.

Ask me.

Create emitter class, describe its emitters, events and reactions in event_table format:

# [ [ emitter_name, [ [ event_name, [ reactions... ] ] ] ] ] 
# let me show it trough simple composition 
# I will write a class descibing a brain of an ancient human, 
# That should be able to handle various events. 
 
class Brain extends SuperEmitter
  # Events 
  event_table: [
    [ 'ear' [ [ 'snake_heard'     [ 'emit_adrenaline'
                                        'look_around'        ]
    [ 'eye' [ [ 'food_spotted'    [ 'emit_noradrenaline'
                                        'hunt'
                                        'emit_endorphins'    ]
                [ 'predator_spotted'[ 'emit_cortisol'
                                        'emit_adrenaline'
                                        'run'                ]
    [ 'nose'[ [ 'food_smelled'    [ 'look_around'        ]
                [ 'blood_smelled'   [ 'emit_adrenaline'
                                        'look_around'        ]
  ]
 
  # constructor and instance members 
  constructor = ->
    @ear  = new Ear()
    @eye  = new Eye()
    @nose = new Nose()
 
 
  # methods: 
  emit_adrenaline: ->
  emit_cortisol: ->
  emit_endorphins: ->
  hunt: ->
  look_around: ->
 
# The event table can be decomposed as following 
flee_reactions = [ 'emit_cortisol''emit_adrenaline''run' ]
hunt_reactions = [ 'emit_noradrenaline''hunt''emit_endorphins' ]
seek_reactions = [ 'look_around' ]
watch_outs     = [ 'emit_adrenaline''look_around' ]
 
ear_events  = [ [ 'snake_heard'     watch_outs     ]
eye_events  = [ [ 'food_spotted'    hunt_reactions ]
                [ 'predator_spotted'flee_reactions ]
nose_events = [ [ 'food_smelled'    seek_reactions
                  'blood_smelled'   watch_outs     ]
 
ear_pack  = [ 'ear' ear_events  ]
eye_pack  = [ 'eye' eye_events  ]
nose_pack = [ 'nose'nose_events ]
 
Brain::event_table = [ ear_packeye_packnose_pack ]
 
# Use in a method 
class Brain extends SuperEmitter
  # ... event table and stuff ... 
  # simplest form 
  emit_adrenaline: ->
    @emit('adrenaline')
 
  # or with arguments 
  emit_adrenaline: (dose_ml = 0.02, delay_ms = 500, noradrenaline = false) ->
    @emit('adrenaline'[dose_mldelay_msnoradrenaline])
    # square brackets used to denote arguments from event name 
class Brain extends SuperEmitter
  # the ones called without args 
  receive_adrenaline: ->
    console.log arguments.length # -> 0 
 
  # the ones called with args 
  receive_adrenaline: (dose_ml, delay_ms, noradrenaline) ->
    console.log arguments

And you can do with it anything you can do with data. Equality, cloning and merging.

There is a class_tools module, which contains merge_events function. It is a pure function merges two or more event tables into one. New items will be added, repeating ones will be removed.

table1 = [ [ 'nose'[ [ 'smell-cheese'[ 'look_around' ]
           [ 'eye' [ [ 'sought-food' [ 'grab_item'   ]
 
table2 = [ [ 'nose'[ [ 'smell-cheese'[ 'search_food' ]
           [ 'eye' [ [ 'sought-food' [ 'grab_item'
                                           'chew_item'   ]
 
(class_tools.merge_events table1table2) # -> will produce: 
[ [ 'nose'[ [ 'smell-cheese'[ 'look_around'
                                  'search_food' ]
  [ 'eye' [ [ 'sought-food' [ 'grab_item'
                                  'chew_item'   ]