1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
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
}
|