unpack = unpack or table.unpack class Once new: (@value) => @is_once: (val) -> return false unless val return false unless 'table' == type val val.__class == @@ is_once = (val) -> (Once.is_once val) and val.value is_live = (val) -> (not Once.is_once val) and val class Cursor @__base.__index = do old_index = @__base.__index (k) => if v = old_index[k] return v if 'string' == type k return @get_nested k new: (@state, @path='') => set: (value) => @state.values[@path] = value value get: (value) => @state.values[@path] init: (value) => old = @get! if old != nil old else @set value drive: (live_or_once) => if val = is_once live_or_once @init val else @set live_or_once get_nested: (name) => if #@path > 0 name = "#{@path}.#{name}" Cursor @state, name __call: => @get! __tostring: => @path __eq: (other) => @path == other.path @is_cursor: (val) -> return false unless val return false unless 'table' == type val val.__class == @@ class State @mt: { __index: (t, k) -> if v = @[k] return v with v = setmetatable {}, @@mt rawset t, k, v } -- initialize state under 'key' with 'default' unless already set init: (key, default) => if val = is_once default default = val rawset @, key, (rawget @, key) or default new: => @reset! reset: => @values = setmetatable {}, { __index: (k) => if Cursor.is_cursor k k = tostring k rawget @, k __newindex: (k, v) => if Cursor.is_cursor k k = tostring k rawset @, k, v } @root = Cursor @ { :is_once :is_live :Once :Cursor :State }