Skip to content

spacegangster/super-emitter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SuperEmitter

Event handling and binding

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

Code weirds

# 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')

How it can change your life:

# 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()

Demo

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.

Docs

Binding events

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_pack, eye_pack, nose_pack ]

Emitting events

# 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_ml, delay_ms, noradrenaline])
    # square brackets used to denote arguments from event name

Receiving events

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

Event handling is data now

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

Merging event tables

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 table1, table2) # -> will produce:
[ [ 'nose', [ [ 'smell-cheese', [ 'look_around'
                                  'search_food' ] ] ] ]
  [ 'eye' , [ [ 'sought-food' , [ 'grab_item'
                                  'chew_item'   ] ] ] ] ]

License

MIT

About

Declarative event handling and binding

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors