Skip to content

Instantly share code, notes, and snippets.

@threepointone
Created July 13, 2012 08:27
Show Gist options
  • Save threepointone/3103649 to your computer and use it in GitHub Desktop.
Save threepointone/3103649 to your computer and use it in GitHub Desktop.
attributes for objects (ala YUI)
match = require 'minimatch'
_ = require 'underscore'
class Events
on: (evt, handler = _.i, scope = @) ->
@_events or= []
spec =
evt: evt
handler: handler
scope: scope
enable:=> @_events = _.union @_events, spec;spec
disable:=> @_events = _.without @_events, spec;spec
@_events.push spec
spec
off: (evt, handler, scope) ->
if evt.indexOf('*') >= 0 then throw 'no wildcards in .off(), use .disable() instead'
@_events or= []
for spec in @_events
matches = evt is spec.evt
if handler
matches = matches and handler is spec.handler
if scope
matches = matches and scope is spec.scope
if matches
spec.disable()
@
fire: (evt, args...) ->
if evt.indexOf('*') >= 0 then throw "#{evt} no wildcards in .fire()"
@_events or= []
for spec in @_events
if match evt, spec.evt
spec.handler.apply spec.scope, [{evt, _evt:spec.evt}].concat args
@
class Attr extends Events
# default spec for an attribute
defaults =
lazy: false
setter: (value) -> value
getter: (value) -> value
value: null
valueFn: -> null # this overrides value
validate: (value) -> true
readOnly: false
# private
_value: null
_init: false
set: (key, value) ->
@describe key unless @_attrs?[key] # if key is not an attrib yet, make a blank stub
attr = @_attrs[key].startup()
if attr.readOnly
@fire 'attr:fail'
throw "attr:#{key} is read only"
if attr.validate.call @, value
attr._value = attr.setter.call @, value
@fire 'set', key, attr._value, value
else
@fire 'attr:fail'
throw "attr:#{key} did not validate for value #{value}"
@
get: (key) ->
@describe key unless @_attrs?[key]
attr = @_attrs[key].startup()
value = attr.getter.call @, attr._value
@fire 'get', key, value
value
describe: (name, spec = {}) ->
@_attrs or= {} # private
attr = @_attrs[name] = _.defaults spec, defaults
attr.startup = =>
if !attr._init
attr._value = if attr.valueFn then attr.valueFn.apply @ else attr.value
attr._init = true
@fire 'attr:init', name, attr._value
attr
attr.startup() unless attr.lazy
@fire 'attr:describe', name, spec
# x = new Attr
# x.describe 'a',
# lazy: true
# value: 2
# valueFn: -> 3
# validate: (value) -> value%8 is 0
# setter: (value) -> value*2
# getter: (value) -> value.toString()
# # readOnly: true
# # value still isn't set because lazy: true
# console.log x.get 'a'
# # value is now set
# # can initialize automatically
# x.set 'b', 5
# console.log x.get 'c'
# console.log 'a', x.get 'a'
# x.set 'a', 16
# console.log 'a', x.get 'a'
# # simple handler
# _h = (e) ->
# console.log "matched #{e._evt} for evt #{e.evt}"
# # x = new Events
# x.on 'a:*', _h
# alpha = x.on '*', _h
# x.on '*:b', _h
# x.on '*:b:*', _h
# # fire a bunch of events
# x.fire 'a'
# x.fire 'a:b'
# x.fire 'a:b:c'
# x.fire 'l:m:n'
# console.log 'disabling * listener'
# alpha.disable()
# x.fire 'l:m:n'
# alpha.enable()
# x.fire 'l:m:n'
@threepointone
Copy link
Author

[working on removing the minimatch dependency]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment