git.s-ol.nu watch-cad / master state.moon
master

Tree @master (Download .tar.gz)

state.moon @masterraw · history · blame

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
}