flor 0.16.1 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/CREDITS.md +1 -0
- data/Makefile +1 -1
- data/README.md +82 -6
- data/lib/flor.rb +1 -1
- data/lib/flor/conf.rb +19 -6
- data/lib/flor/core/executor.rb +45 -16
- data/lib/flor/core/node.rb +4 -4
- data/lib/flor/core/procedure.rb +40 -0
- data/lib/flor/djan.rb +5 -2
- data/lib/flor/flor.rb +92 -7
- data/lib/flor/id.rb +19 -0
- data/lib/flor/migrations/0001_tables.rb +6 -6
- data/lib/flor/migrations/0005_pointer_content.rb +20 -0
- data/lib/flor/pcore/_apply.rb +103 -57
- data/lib/flor/pcore/_att.rb +15 -1
- data/lib/flor/pcore/_ref.rb +2 -1
- data/lib/flor/pcore/arith.rb +46 -9
- data/lib/flor/pcore/break.rb +1 -1
- data/lib/flor/pcore/case.rb +41 -0
- data/lib/flor/pcore/collect.rb +1 -1
- data/lib/flor/pcore/cursor.rb +1 -1
- data/lib/flor/pcore/define.rb +32 -6
- data/lib/flor/pcore/iterator.rb +12 -0
- data/lib/flor/pcore/on_cancel.rb +1 -1
- data/lib/flor/pcore/set.rb +14 -4
- data/lib/flor/punit/{ccollect.rb → c_collect.rb} +2 -2
- data/lib/flor/punit/c_each.rb +11 -0
- data/lib/flor/punit/c_for_each.rb +41 -0
- data/lib/flor/punit/c_iterator.rb +160 -0
- data/lib/flor/punit/c_map.rb +43 -0
- data/lib/flor/punit/concurrence.rb +43 -200
- data/lib/flor/punit/graft.rb +3 -2
- data/lib/flor/punit/m_ram.rb +281 -0
- data/lib/flor/unit.rb +1 -0
- data/lib/flor/unit/caller.rb +6 -1
- data/lib/flor/unit/executor.rb +17 -4
- data/lib/flor/unit/ganger.rb +12 -1
- data/lib/flor/unit/hloader.rb +251 -0
- data/lib/flor/unit/hook.rb +74 -15
- data/lib/flor/unit/hooker.rb +9 -12
- data/lib/flor/unit/loader.rb +41 -17
- data/lib/flor/unit/models.rb +54 -18
- data/lib/flor/unit/models/execution.rb +15 -4
- data/lib/flor/unit/models/pointer.rb +11 -0
- data/lib/flor/unit/scheduler.rb +126 -30
- data/lib/flor/unit/spooler.rb +5 -3
- data/lib/flor/unit/storage.rb +40 -13
- data/lib/flor/unit/waiter.rb +165 -26
- data/lib/flor/unit/wlist.rb +98 -5
- metadata +10 -4
- data/lib/flor/punit/cmap.rb +0 -112
data/lib/flor/punit/graft.rb
CHANGED
@@ -64,8 +64,9 @@ class Flor::Pro::Graft < Flor::Procedure
|
|
64
64
|
|
65
65
|
# graft subtree into parent node
|
66
66
|
|
67
|
-
parent_tree = lookup_tree(parent)
|
68
|
-
|
67
|
+
if parent_tree = lookup_tree(parent)
|
68
|
+
parent_tree[1][child_id] = tree
|
69
|
+
end
|
69
70
|
|
70
71
|
# re-apply self with subtree
|
71
72
|
|
@@ -0,0 +1,281 @@
|
|
1
|
+
|
2
|
+
# Module extracted out of "concurrence", deals with receivers and mergers.
|
3
|
+
#
|
4
|
+
# Should it deal with remainder?
|
5
|
+
#
|
6
|
+
module Flor::Pro::ReceiveAndMerge
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def receive_from_branch
|
11
|
+
|
12
|
+
(@node['payloads'] ||= {})[from] = @message['payload']
|
13
|
+
|
14
|
+
apply_receiver
|
15
|
+
end
|
16
|
+
|
17
|
+
def apply_receiver
|
18
|
+
|
19
|
+
@node['receiver'] ||= determine_receiver
|
20
|
+
|
21
|
+
if @node['receiver'].is_a?(String)
|
22
|
+
apply_receiver_method
|
23
|
+
else
|
24
|
+
apply_receiver_function
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def apply_receiver_method
|
29
|
+
|
30
|
+
ret = send('rm__' + @node['receiver'])
|
31
|
+
msg = { 'payload' => { 'ret' => ret } }
|
32
|
+
|
33
|
+
receive_from_receiver(msg)
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply_receiver_function
|
37
|
+
|
38
|
+
(@node['on_receive_queue'] ||= []) << from
|
39
|
+
|
40
|
+
dequeue_receiver_function
|
41
|
+
end
|
42
|
+
|
43
|
+
def dequeue_receiver_function
|
44
|
+
|
45
|
+
if @node['on_receive_nids']
|
46
|
+
[]
|
47
|
+
elsif f = (@node['on_receive_queue'] || []).shift
|
48
|
+
ms = apply(@node['receiver'], receiver_args(f), tree[2])
|
49
|
+
@node['on_receive_nids'] = [ ms.first['nid'], f ]
|
50
|
+
ms
|
51
|
+
else
|
52
|
+
[]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def receiver_args(from)
|
57
|
+
|
58
|
+
rs = Flor.dup(@node['payloads'])
|
59
|
+
|
60
|
+
[ [ 'reply', rs[from] ],
|
61
|
+
[ 'from', from ],
|
62
|
+
[ 'replies', rs ],
|
63
|
+
[ 'branch_count', @node['branch_count'] ],
|
64
|
+
[ 'over', !! @node['over'] ] ]
|
65
|
+
end
|
66
|
+
|
67
|
+
def receive_from_receiver(msg=message)
|
68
|
+
|
69
|
+
ret = msg['payload']['ret']
|
70
|
+
over = @node['over']
|
71
|
+
|
72
|
+
if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
|
73
|
+
over = over || ret['done']
|
74
|
+
from = @node['on_receive_nids'][1]
|
75
|
+
@node['payloads'][from] = ret['payload']
|
76
|
+
else
|
77
|
+
over = over || ret
|
78
|
+
end
|
79
|
+
|
80
|
+
@node['on_receive_nids'] = nil
|
81
|
+
|
82
|
+
just_over = over && ! @node['over']
|
83
|
+
|
84
|
+
@node['over'] ||= just_over
|
85
|
+
|
86
|
+
if just_over
|
87
|
+
apply_merger
|
88
|
+
elsif ! over
|
89
|
+
[] # wait for more branches
|
90
|
+
else
|
91
|
+
receive_from_merger(nil)
|
92
|
+
end +
|
93
|
+
dequeue_receiver_function
|
94
|
+
end
|
95
|
+
|
96
|
+
def apply_merger
|
97
|
+
|
98
|
+
if (m = determine_merger).is_a?(Hash)
|
99
|
+
apply_merger_method(m)
|
100
|
+
else
|
101
|
+
apply_merger_function(m)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def apply_merger_method(h)
|
106
|
+
|
107
|
+
o = h[:order] || h['order']
|
108
|
+
m = h[:merger] || h['merger']
|
109
|
+
|
110
|
+
payloads = send("mom__#{o}", h, Flor.dup(@node['payloads']))
|
111
|
+
payload = send("mmm__#{m}", h, payloads)
|
112
|
+
msg = { 'payload' => payload }
|
113
|
+
|
114
|
+
receive_from_merger(msg)
|
115
|
+
end
|
116
|
+
|
117
|
+
def apply_merger_function(func_tree)
|
118
|
+
|
119
|
+
@node['merging'] = true
|
120
|
+
|
121
|
+
apply(func_tree, merger_args, tree[2])
|
122
|
+
end
|
123
|
+
|
124
|
+
def merger_args
|
125
|
+
|
126
|
+
rs = Flor.dup(@node['payloads'])
|
127
|
+
|
128
|
+
[ [ 'rets', rs.inject({}) { |h, (k, v)| h[k] = v['ret']; h } ],
|
129
|
+
[ 'replies', rs ],
|
130
|
+
[ 'branch_count', @node['branch_count'] ] ]
|
131
|
+
end
|
132
|
+
|
133
|
+
def receive_from_merger(msg=message)
|
134
|
+
|
135
|
+
pl = msg ? msg['payload'] : {}
|
136
|
+
ret = pl['ret']
|
137
|
+
|
138
|
+
pl = ret['payload'] \
|
139
|
+
if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
|
140
|
+
|
141
|
+
# TODO somehow, what if done is false, should we un-over the concurrence?
|
142
|
+
|
143
|
+
@node['merged_payload'] = pl \
|
144
|
+
if msg && ! @node.has_key?('merged_payload')
|
145
|
+
|
146
|
+
rem = determine_remainder
|
147
|
+
|
148
|
+
cancel_children(rem) + reply_to_parent(rem)
|
149
|
+
end
|
150
|
+
|
151
|
+
def rm__default_receive
|
152
|
+
|
153
|
+
@node['payloads'].size >= branch_count
|
154
|
+
end
|
155
|
+
|
156
|
+
def rm__expect_integer_receive
|
157
|
+
|
158
|
+
@node['payloads'].size >= att(:expect)
|
159
|
+
end
|
160
|
+
|
161
|
+
def determine_receiver
|
162
|
+
|
163
|
+
ex = att(:expect)
|
164
|
+
|
165
|
+
return 'expect_integer_receive' if ex && ex.is_a?(Integer) && ex > 0
|
166
|
+
|
167
|
+
att(:on_receive, :receiver) || 'default_receive'
|
168
|
+
end
|
169
|
+
|
170
|
+
# order:
|
171
|
+
# * first, last: first or last to reply
|
172
|
+
# * top/north/head, bottom/south/tail: position in concurrence, in collection
|
173
|
+
#
|
174
|
+
# merge:
|
175
|
+
# * deep
|
176
|
+
# * mix/plain
|
177
|
+
# * override
|
178
|
+
# * ignore
|
179
|
+
# * stack
|
180
|
+
|
181
|
+
STACK_REX = /\Astack(?::([-_a-zA-Z0-9]+))?\z/
|
182
|
+
|
183
|
+
TRANSLATORS = { STACK_REX => 'k', /\Atail\z/ => 'a' }
|
184
|
+
|
185
|
+
MORDERS = {
|
186
|
+
'f' => :first, 'l' => :last, /[tnh]/ => :north, /[bsa]/ => :south }
|
187
|
+
MMERGERS = {
|
188
|
+
'd' => :deep, /[mp]/ => :mix, 'o' => :override, 'i' => :ignore,
|
189
|
+
'k' => :stack }
|
190
|
+
|
191
|
+
def default_merger
|
192
|
+
|
193
|
+
{ order: :first, merger: :deep }
|
194
|
+
end
|
195
|
+
|
196
|
+
def determine_merger
|
197
|
+
|
198
|
+
m = att(:on_merge, :merger, :merge)
|
199
|
+
h = default_merger
|
200
|
+
|
201
|
+
return h if m == nil
|
202
|
+
return m unless m.is_a?(String)
|
203
|
+
|
204
|
+
mm = m.split(/[-\s_]+/)
|
205
|
+
mm = mm[0].chars if mm.size == 1 && mm[0].size < 3
|
206
|
+
#
|
207
|
+
d = mm
|
208
|
+
.collect { |s| TRANSLATORS.inject(s) { |r, (k, v)| r.match(k) ? v : r } }
|
209
|
+
.collect { |s| s[0, 1] }.join
|
210
|
+
|
211
|
+
MORDERS.each do |rex, order|
|
212
|
+
if d.match(rex); h[:order] = order; break; end
|
213
|
+
end
|
214
|
+
MMERGERS.each do |rex, merger|
|
215
|
+
if d.match(rex); h[:merger] = merger; break; end
|
216
|
+
end
|
217
|
+
|
218
|
+
h[:key] = m.match(STACK_REX)[1] \
|
219
|
+
if h[:merger] == :stack
|
220
|
+
|
221
|
+
h
|
222
|
+
end
|
223
|
+
|
224
|
+
def mom__first(h, payloads)
|
225
|
+
mom__last(h, payloads).reverse
|
226
|
+
end
|
227
|
+
def mom__last(_, payloads)
|
228
|
+
payloads.values
|
229
|
+
end
|
230
|
+
def mom__north(h, payloads)
|
231
|
+
mom__south(h, payloads).reverse
|
232
|
+
end
|
233
|
+
def mom__south(_, payloads)
|
234
|
+
payloads.sort_by { |k, _| k }.collect(&:last)
|
235
|
+
end
|
236
|
+
|
237
|
+
def mmm__deep(_, ordered_payloads)
|
238
|
+
ordered_payloads.inject { |h, pl| Flor.deep_merge!(h, pl) }
|
239
|
+
end
|
240
|
+
def mmm__mix(_, ordered_payloads)
|
241
|
+
ordered_payloads.inject { |h, pl| h.merge!(pl) }
|
242
|
+
end
|
243
|
+
def mmm__override(_, ordered_payloads)
|
244
|
+
ordered_payloads.last
|
245
|
+
end
|
246
|
+
def mmm__ignore(_, _)
|
247
|
+
node_payload.copy
|
248
|
+
end
|
249
|
+
def mmm__stack(h, ordered_payloads)
|
250
|
+
k = h[:key] || 'ret'
|
251
|
+
node_payload.copy.merge!(k => ordered_payloads.reverse)
|
252
|
+
end
|
253
|
+
|
254
|
+
def determine_remainder
|
255
|
+
|
256
|
+
att(:remaining, :rem) || 'cancel'
|
257
|
+
end
|
258
|
+
|
259
|
+
def cancel_children(rem)
|
260
|
+
|
261
|
+
(rem && rem != 'forget') ? wrap_cancel_children : []
|
262
|
+
end
|
263
|
+
|
264
|
+
def reply_to_parent(rem)
|
265
|
+
|
266
|
+
return [] \
|
267
|
+
if @node['replied']
|
268
|
+
return [] \
|
269
|
+
if @node['payloads'].size < branch_count && ( ! rem || rem == 'wait')
|
270
|
+
|
271
|
+
@node['replied'] = true
|
272
|
+
|
273
|
+
wrap_reply('payload' => post_merge)
|
274
|
+
end
|
275
|
+
|
276
|
+
def post_merge
|
277
|
+
|
278
|
+
@node['merged_payload']
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
data/lib/flor/unit.rb
CHANGED
data/lib/flor/unit/caller.rb
CHANGED
@@ -55,7 +55,12 @@ module Flor
|
|
55
55
|
#
|
56
56
|
# initialize
|
57
57
|
|
58
|
-
k =
|
58
|
+
k =
|
59
|
+
case com = conf['class'] || conf['module']
|
60
|
+
when String then Flor.const_lookup(com)
|
61
|
+
when Class then com
|
62
|
+
else fail ArgumentError.new("don't know how to call #{com.inspect}")
|
63
|
+
end
|
59
64
|
|
60
65
|
o =
|
61
66
|
if k.class == Module
|
data/lib/flor/unit/executor.rb
CHANGED
@@ -16,12 +16,11 @@ module Flor
|
|
16
16
|
unit.storage.fetch_traps(@exid),
|
17
17
|
unit.storage.load_execution(@exid))
|
18
18
|
|
19
|
-
@messages =
|
20
|
-
|
19
|
+
@messages = messages
|
20
|
+
.collect { |m|
|
21
21
|
Flor::Storage
|
22
22
|
.from_blob(m[:content])
|
23
|
-
.tap { |mm| mm['mid'] = m[:id].to_i }
|
24
|
-
}
|
23
|
+
.tap { |mm| mm['mid'] = m[:id].to_i } }
|
25
24
|
@consumed = []
|
26
25
|
|
27
26
|
@alive = true
|
@@ -48,6 +47,11 @@ module Flor
|
|
48
47
|
|
49
48
|
protected
|
50
49
|
|
50
|
+
CLOSING_POINTS = %w[ task terminated ceased ]
|
51
|
+
#
|
52
|
+
# point for messages that, after consumption, are conserved
|
53
|
+
# in the execution's "closing_messages" array
|
54
|
+
|
51
55
|
def do_run
|
52
56
|
|
53
57
|
@unit.logger.log_run_start(self)
|
@@ -84,6 +88,10 @@ module Flor
|
|
84
88
|
|
85
89
|
@alive = false
|
86
90
|
|
91
|
+
@execution.merge!(
|
92
|
+
closing_messages: @consumed.select { |m|
|
93
|
+
CLOSING_POINTS.include?(m['point']) })
|
94
|
+
|
87
95
|
@unit.storage.put_execution(@execution)
|
88
96
|
@unit.storage.consume(@consumed)
|
89
97
|
|
@@ -170,6 +178,11 @@ module Flor
|
|
170
178
|
[ m ]
|
171
179
|
end
|
172
180
|
|
181
|
+
def add(message)
|
182
|
+
|
183
|
+
apply(@execution['nodes'][message['nid']], message)
|
184
|
+
end
|
185
|
+
|
173
186
|
def on_do_run_exc(e)
|
174
187
|
|
175
188
|
io = StringIO.new
|
data/lib/flor/unit/ganger.rb
CHANGED
@@ -62,7 +62,7 @@ module Flor
|
|
62
62
|
|
63
63
|
message['vars'] = gather_vars(executor, tconf, message)
|
64
64
|
|
65
|
-
m =
|
65
|
+
m = dup_message(message)
|
66
66
|
#
|
67
67
|
# the tasker gets a copy of the message (and it can play with it
|
68
68
|
# to its heart content), meanwhile the message is handed to the
|
@@ -81,6 +81,17 @@ module Flor
|
|
81
81
|
|
82
82
|
protected
|
83
83
|
|
84
|
+
def dup_message(m)
|
85
|
+
|
86
|
+
tc = m.delete('tconf')
|
87
|
+
m1 = Flor.dup(m)
|
88
|
+
m1['tconf'] = tc.inject({}) { |h, (k, v)|
|
89
|
+
h[k] = k == 'class' ? v : Flor.dup(v); h } \
|
90
|
+
if tc
|
91
|
+
|
92
|
+
m1
|
93
|
+
end
|
94
|
+
|
84
95
|
def var_match(k, filter)
|
85
96
|
|
86
97
|
filter.each { |f| return true if (f.is_a?(String) ? k == f : f.match(k)) }
|
@@ -0,0 +1,251 @@
|
|
1
|
+
|
2
|
+
module Flor
|
3
|
+
|
4
|
+
# A loader which keeps everything in a Hash, while the traditional/default
|
5
|
+
# Flor::Loader reads from a file tree.
|
6
|
+
#
|
7
|
+
class HashLoader
|
8
|
+
|
9
|
+
# NB: tasker configuration entries start with "loa_", like for Flor::Loader
|
10
|
+
|
11
|
+
attr_reader :environment
|
12
|
+
|
13
|
+
def environment=(h)
|
14
|
+
|
15
|
+
@environment = Flor.to_string_keyed_hash(h)
|
16
|
+
.inject({}) { |r, (cat, v)|
|
17
|
+
r[cat] = recompose(v)
|
18
|
+
r }
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(unit)
|
22
|
+
|
23
|
+
@unit = unit
|
24
|
+
|
25
|
+
class << @unit; include Flor::HashLoader::UnitAdders; end
|
26
|
+
|
27
|
+
self.environment = (@unit.conf['lod_environment'] || {})
|
28
|
+
end
|
29
|
+
|
30
|
+
def shutdown
|
31
|
+
end
|
32
|
+
|
33
|
+
HCATS = {
|
34
|
+
'v' => 'variables',
|
35
|
+
'var' => 'variables',
|
36
|
+
'variable' => 'variables',
|
37
|
+
'flo' => 'libraries',
|
38
|
+
'flow' => 'libraries',
|
39
|
+
'lib' => 'libraries',
|
40
|
+
'library' => 'libraries',
|
41
|
+
'sub' => 'sublibraries',
|
42
|
+
'sublib' => 'sublibraries',
|
43
|
+
'sublibrary' => 'sublibraries',
|
44
|
+
'tasker' => 'taskers',
|
45
|
+
'hook' => 'hooks' }
|
46
|
+
CATS =
|
47
|
+
HCATS.values.uniq
|
48
|
+
|
49
|
+
def add(cat, path, value, &block)
|
50
|
+
|
51
|
+
c = recat(cat)
|
52
|
+
|
53
|
+
path = path.to_s
|
54
|
+
path = path + '.' if c == 'hooks' && path.length > 0 && path[-1, 1] != '.'
|
55
|
+
|
56
|
+
value = block ?
|
57
|
+
block_to_class(c, block) :
|
58
|
+
Flor.to_string_keyed_hash(value)
|
59
|
+
|
60
|
+
e = (@environment[c] ||= [])
|
61
|
+
e << [ *split(path), value ]
|
62
|
+
e.sort_by! { |pa, _, _| pa.count('.') }
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove(cat, path)
|
66
|
+
|
67
|
+
c = recat(cat)
|
68
|
+
|
69
|
+
e = @environment[c]
|
70
|
+
return [] unless e
|
71
|
+
|
72
|
+
pa, ke = split(path)
|
73
|
+
|
74
|
+
e.reject! { |epa, eke, _| epa == pa && eke == ke }
|
75
|
+
end
|
76
|
+
|
77
|
+
def variables(domain)
|
78
|
+
|
79
|
+
entries('variables', domain)
|
80
|
+
.inject({}) { |h, (_, k, v)| h[k] = v; h }
|
81
|
+
end
|
82
|
+
|
83
|
+
#def procedures(path)
|
84
|
+
#
|
85
|
+
# # TODO
|
86
|
+
# # TODO work with Flor.load_procedures
|
87
|
+
#end
|
88
|
+
|
89
|
+
# If found, returns [ source_path, path ]
|
90
|
+
#
|
91
|
+
def library(domain, name=nil, opts={})
|
92
|
+
|
93
|
+
path, key = split(domain, name)
|
94
|
+
|
95
|
+
libs = entries('libraries', path)
|
96
|
+
if opts[:subflows] # used by "graft"/"import"
|
97
|
+
libs += entries('sublibraries', path)
|
98
|
+
libs = libs.sort_by { |pa, _, _| pa.count('.') }
|
99
|
+
end
|
100
|
+
|
101
|
+
libs
|
102
|
+
.each { |pa, ke, va|
|
103
|
+
next unless ke == key
|
104
|
+
return [ [ pa, ke ].join('.'), va ] }
|
105
|
+
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def tasker(domain, name, message={})
|
110
|
+
|
111
|
+
path, key = split(domain, name)
|
112
|
+
|
113
|
+
entries('taskers', path)
|
114
|
+
.reverse # FIXME
|
115
|
+
.each { |pa, ke, va|
|
116
|
+
next unless ke == key
|
117
|
+
return to_conf_h('taskers', pa, va, message) }
|
118
|
+
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def hooks(domain)
|
123
|
+
|
124
|
+
entries('hooks', domain)
|
125
|
+
.collect { |_, _, va| to_conf_h('hooks', domain, va, {}) }
|
126
|
+
.flatten(1)
|
127
|
+
end
|
128
|
+
|
129
|
+
def load_hooks(exid)
|
130
|
+
|
131
|
+
hooks(Flor.domain(exid))
|
132
|
+
.collect { |h| Flor::Hook.new(@unit, exid, h) }
|
133
|
+
end
|
134
|
+
|
135
|
+
protected
|
136
|
+
|
137
|
+
def recompose(h)
|
138
|
+
|
139
|
+
deflate(h, {}, nil)
|
140
|
+
.sort_by { |k, _| k.count('.') }
|
141
|
+
.collect { |k, v| [ *split(k), v ] }
|
142
|
+
end
|
143
|
+
|
144
|
+
def deflate(h, out, path)
|
145
|
+
|
146
|
+
h
|
147
|
+
.inject(out) { |r, (k, v)|
|
148
|
+
pathk = path ? [ path, k ].join('.') : k
|
149
|
+
if v.is_a?(Hash)
|
150
|
+
deflate(v, r, pathk)
|
151
|
+
else
|
152
|
+
r[pathk] = v
|
153
|
+
end
|
154
|
+
r }
|
155
|
+
end
|
156
|
+
|
157
|
+
def recat(cat)
|
158
|
+
|
159
|
+
c = cat.to_s
|
160
|
+
c = HCATS[c] || c
|
161
|
+
|
162
|
+
fail ArgumentError.new("unknown category #{cat.to_s.inspect}") \
|
163
|
+
unless CATS.include?(c)
|
164
|
+
|
165
|
+
c
|
166
|
+
end
|
167
|
+
|
168
|
+
def split(path, key=nil)
|
169
|
+
|
170
|
+
return [ path, key ] if key
|
171
|
+
|
172
|
+
i = path.rindex('.') || 0
|
173
|
+
|
174
|
+
[ path[0, i], path[(i == 0 ? i : i + 1)..-1] ]
|
175
|
+
end
|
176
|
+
|
177
|
+
def entries(cat, domain)
|
178
|
+
|
179
|
+
(@environment[cat.to_s] || [])
|
180
|
+
.select { |path, _, _| Flor.sub_domain?(path, domain) }
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_conf_h(cat, path, value, context)
|
184
|
+
|
185
|
+
is_array = value.is_a?(Array)
|
186
|
+
|
187
|
+
a = (is_array ? value : [ value ])
|
188
|
+
.collect { |v|
|
189
|
+
h =
|
190
|
+
case v
|
191
|
+
when String then eval(path, v, context)
|
192
|
+
when Class then { 'class' => v }
|
193
|
+
when Hash then v
|
194
|
+
else { 'instance' => v }
|
195
|
+
end
|
196
|
+
h['_path'] = path
|
197
|
+
h['root'] = nil
|
198
|
+
h }
|
199
|
+
|
200
|
+
is_array ? a : a.first
|
201
|
+
end
|
202
|
+
|
203
|
+
def eval(path, code, context)
|
204
|
+
|
205
|
+
code = Flor::ConfExecutor.load(code)
|
206
|
+
|
207
|
+
Flor::ConfExecutor.interpret(path, code, context)
|
208
|
+
end
|
209
|
+
|
210
|
+
def block_to_class(cat, block)
|
211
|
+
|
212
|
+
c =
|
213
|
+
cat == 'taskers' ?
|
214
|
+
Class.new(Flor::BasicTasker) :
|
215
|
+
Class.new
|
216
|
+
|
217
|
+
class << c; attr_accessor :source_location; end
|
218
|
+
c.source_location = block.source_location
|
219
|
+
c.send(:define_method, :on_message, &block)
|
220
|
+
|
221
|
+
c
|
222
|
+
end
|
223
|
+
|
224
|
+
module UnitAdders
|
225
|
+
|
226
|
+
def add_variable(path, value)
|
227
|
+
self.loader.add(:variable, path, value)
|
228
|
+
end
|
229
|
+
def add_library(path, value)
|
230
|
+
self.loader.add(:library, path, value)
|
231
|
+
end
|
232
|
+
def add_sublibrary(path, value)
|
233
|
+
self.loader.add(:sublibrary, path, value)
|
234
|
+
end
|
235
|
+
def add_tasker(path, value=nil, &block)
|
236
|
+
self.loader.add(:tasker, path, value, &block)
|
237
|
+
end
|
238
|
+
def add_hook(path, value=nil, &block)
|
239
|
+
self.loader.add(:hook, path, value, &block)
|
240
|
+
end
|
241
|
+
|
242
|
+
alias add_var add_variable
|
243
|
+
alias add_lib add_library
|
244
|
+
alias add_sub add_sublibrary
|
245
|
+
alias add_sub_lib add_sublibrary
|
246
|
+
|
247
|
+
# to remove: unit.loader.remove(:tasker, path)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|