flor 0.18.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +29 -0
  3. data/CREDITS.md +1 -0
  4. data/LICENSE.txt +1 -1
  5. data/Makefile +1 -1
  6. data/README.md +16 -2
  7. data/flor.gemspec +1 -2
  8. data/lib/flor.rb +3 -2
  9. data/lib/flor/colours.rb +5 -3
  10. data/lib/flor/conf.rb +1 -0
  11. data/lib/flor/core.rb +9 -8
  12. data/lib/flor/core/executor.rb +20 -16
  13. data/lib/flor/core/node.rb +1 -13
  14. data/lib/flor/core/procedure.rb +10 -1
  15. data/lib/flor/core/texecutor.rb +35 -3
  16. data/lib/flor/djan.rb +1 -0
  17. data/lib/flor/errors.rb +1 -0
  18. data/lib/flor/flor.rb +172 -26
  19. data/lib/flor/id.rb +5 -1
  20. data/lib/flor/log.rb +14 -8
  21. data/lib/flor/migrations/0001_tables.rb +1 -0
  22. data/lib/flor/migrations/0002_cunit_and_munit.rb +1 -0
  23. data/lib/flor/migrations/0003_timer_onid_bnid.rb +1 -0
  24. data/lib/flor/migrations/0004_trap_bnid.rb +1 -0
  25. data/lib/flor/migrations/0005_pointer_content.rb +1 -0
  26. data/lib/flor/parser.rb +19 -11
  27. data/lib/flor/pcore/_apply.rb +1 -0
  28. data/lib/flor/pcore/_arr.rb +1 -0
  29. data/lib/flor/pcore/_atom.rb +1 -0
  30. data/lib/flor/pcore/_att.rb +1 -0
  31. data/lib/flor/pcore/_coll.rb +1 -0
  32. data/lib/flor/pcore/_dmute.rb +1 -0
  33. data/lib/flor/pcore/_dol.rb +1 -0
  34. data/lib/flor/pcore/_dqs.rb +1 -0
  35. data/lib/flor/pcore/_dump.rb +1 -0
  36. data/lib/flor/pcore/_err.rb +1 -0
  37. data/lib/flor/pcore/_head.rb +1 -0
  38. data/lib/flor/pcore/_obj.rb +1 -0
  39. data/lib/flor/pcore/_pat_.rb +1 -0
  40. data/lib/flor/pcore/_pat_arr.rb +1 -0
  41. data/lib/flor/pcore/_pat_guard.rb +1 -0
  42. data/lib/flor/pcore/_pat_obj.rb +1 -0
  43. data/lib/flor/pcore/_pat_or.rb +1 -0
  44. data/lib/flor/pcore/_pat_regex.rb +1 -0
  45. data/lib/flor/pcore/_ref.rb +1 -0
  46. data/lib/flor/pcore/_rxs.rb +1 -0
  47. data/lib/flor/pcore/_skip.rb +1 -0
  48. data/lib/flor/pcore/_val.rb +1 -0
  49. data/lib/flor/pcore/all.rb +1 -0
  50. data/lib/flor/pcore/andor.rb +1 -0
  51. data/lib/flor/pcore/any.rb +1 -0
  52. data/lib/flor/pcore/apply.rb +1 -0
  53. data/lib/flor/pcore/arith.rb +1 -0
  54. data/lib/flor/pcore/array_qmark.rb +1 -0
  55. data/lib/flor/pcore/break.rb +1 -0
  56. data/lib/flor/pcore/case.rb +1 -0
  57. data/lib/flor/pcore/cmp.rb +1 -0
  58. data/lib/flor/pcore/collect.rb +2 -1
  59. data/lib/flor/pcore/cond.rb +1 -0
  60. data/lib/flor/pcore/cursor.rb +45 -3
  61. data/lib/flor/pcore/define.rb +1 -0
  62. data/lib/flor/pcore/detect.rb +1 -0
  63. data/lib/flor/pcore/do_return.rb +1 -0
  64. data/lib/flor/pcore/each.rb +1 -0
  65. data/lib/flor/pcore/echo.rb +1 -0
  66. data/lib/flor/pcore/empty.rb +1 -0
  67. data/lib/flor/pcore/fail.rb +1 -0
  68. data/lib/flor/pcore/filter.rb +1 -0
  69. data/lib/flor/pcore/find.rb +1 -0
  70. data/lib/flor/pcore/flatten.rb +1 -0
  71. data/lib/flor/pcore/for_each.rb +1 -0
  72. data/lib/flor/pcore/if.rb +1 -0
  73. data/lib/flor/pcore/includes.rb +1 -0
  74. data/lib/flor/pcore/inject.rb +1 -0
  75. data/lib/flor/pcore/iterator.rb +1 -0
  76. data/lib/flor/pcore/keys.rb +1 -0
  77. data/lib/flor/pcore/length.rb +1 -0
  78. data/lib/flor/pcore/loop.rb +1 -0
  79. data/lib/flor/pcore/map.rb +1 -0
  80. data/lib/flor/pcore/match.rb +1 -0
  81. data/lib/flor/pcore/matchr.rb +1 -0
  82. data/lib/flor/pcore/max.rb +1 -0
  83. data/lib/flor/pcore/merge.rb +1 -0
  84. data/lib/flor/pcore/move.rb +1 -0
  85. data/lib/flor/pcore/noeval.rb +1 -0
  86. data/lib/flor/pcore/noret.rb +1 -0
  87. data/lib/flor/pcore/not.rb +1 -0
  88. data/lib/flor/pcore/on.rb +4 -3
  89. data/lib/flor/pcore/on_cancel.rb +1 -0
  90. data/lib/flor/pcore/on_error.rb +1 -0
  91. data/lib/flor/pcore/push.rb +1 -0
  92. data/lib/flor/pcore/rand.rb +1 -0
  93. data/lib/flor/pcore/range.rb +1 -0
  94. data/lib/flor/pcore/reduce.rb +1 -0
  95. data/lib/flor/pcore/return.rb +1 -0
  96. data/lib/flor/pcore/reverse.rb +1 -0
  97. data/lib/flor/pcore/select.rb +1 -0
  98. data/lib/flor/pcore/sequence.rb +1 -0
  99. data/lib/flor/pcore/set.rb +1 -0
  100. data/lib/flor/pcore/shuffle.rb +1 -0
  101. data/lib/flor/pcore/slice.rb +1 -0
  102. data/lib/flor/pcore/sort.rb +1 -0
  103. data/lib/flor/pcore/sort_by.rb +1 -0
  104. data/lib/flor/pcore/split.rb +1 -0
  105. data/lib/flor/pcore/stall.rb +1 -0
  106. data/lib/flor/pcore/strings.rb +1 -0
  107. data/lib/flor/pcore/timestamp.rb +1 -0
  108. data/lib/flor/pcore/to_array.rb +1 -0
  109. data/lib/flor/pcore/twig.rb +1 -0
  110. data/lib/flor/pcore/type_of.rb +1 -0
  111. data/lib/flor/pcore/until.rb +1 -0
  112. data/lib/flor/punit/abort.rb +50 -0
  113. data/lib/flor/punit/c_collect.rb +1 -0
  114. data/lib/flor/punit/c_each.rb +19 -0
  115. data/lib/flor/punit/c_for_each.rb +2 -1
  116. data/lib/flor/punit/c_iterator.rb +2 -1
  117. data/lib/flor/punit/c_map.rb +1 -0
  118. data/lib/flor/punit/cancel.rb +1 -0
  119. data/lib/flor/punit/concurrence.rb +7 -5
  120. data/lib/flor/punit/cron.rb +1 -0
  121. data/lib/flor/punit/do_trap.rb +1 -0
  122. data/lib/flor/punit/every.rb +1 -0
  123. data/lib/flor/punit/graft.rb +1 -0
  124. data/lib/flor/punit/{m_ram.rb → m_receive_and_merge.rb} +5 -4
  125. data/lib/flor/punit/on_timeout.rb +1 -0
  126. data/lib/flor/punit/part.rb +1 -0
  127. data/lib/flor/punit/schedule.rb +1 -0
  128. data/lib/flor/punit/signal.rb +1 -0
  129. data/lib/flor/punit/sleep.rb +1 -0
  130. data/lib/flor/punit/task.rb +1 -0
  131. data/lib/flor/punit/trace.rb +1 -0
  132. data/lib/flor/punit/trap.rb +10 -1
  133. data/lib/flor/to_string.rb +1 -0
  134. data/lib/flor/tools/env.rb +1 -0
  135. data/lib/flor/tools/firb.rb +33 -0
  136. data/lib/flor/tools/shell.rb +13 -3
  137. data/lib/flor/tools/shell_out.rb +1 -0
  138. data/lib/flor/tt.rb +98 -0
  139. data/lib/flor/unit.rb +3 -0
  140. data/lib/flor/unit/caller.rb +158 -23
  141. data/lib/flor/unit/caller_jruby.rb +133 -0
  142. data/lib/flor/unit/dump.rb +36 -0
  143. data/lib/flor/unit/executor.rb +19 -13
  144. data/lib/flor/unit/ganger.rb +9 -12
  145. data/lib/flor/unit/gangers.rb +125 -0
  146. data/lib/flor/unit/hloader.rb +34 -7
  147. data/lib/flor/unit/hook.rb +3 -0
  148. data/lib/flor/unit/hooker.rb +32 -15
  149. data/lib/flor/unit/journal.rb +23 -0
  150. data/lib/flor/unit/loader.rb +142 -15
  151. data/lib/flor/unit/logger.rb +35 -7
  152. data/lib/flor/unit/models.rb +8 -1
  153. data/lib/flor/unit/models/execution.rb +51 -0
  154. data/lib/flor/unit/models/message.rb +6 -0
  155. data/lib/flor/unit/models/pointer.rb +21 -1
  156. data/lib/flor/unit/models/timer.rb +1 -0
  157. data/lib/flor/unit/models/trace.rb +1 -0
  158. data/lib/flor/unit/models/trap.rb +3 -2
  159. data/lib/flor/unit/scheduler.rb +51 -36
  160. data/lib/flor/unit/spooler.rb +1 -0
  161. data/lib/flor/unit/storage.rb +113 -84
  162. data/lib/flor/unit/taskers.rb +70 -1
  163. data/lib/flor/unit/waiter.rb +22 -17
  164. data/lib/flor/unit/wlist.rb +19 -8
  165. metadata +16 -11
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flor
4
+
5
+ class Caller
6
+
7
+ def split_cmd(cmd)
8
+
9
+ #cmd.split(/ +/)
10
+ # too naive
11
+
12
+ #Raabro.pp(Flor::Caller::CmdParser.parse(cmd, debug: 3), colours: true)
13
+ Flor::Caller::CmdParser.parse(cmd)
14
+ end
15
+
16
+ class ProcessStatus
17
+
18
+ attr_reader :process, :exitstatus
19
+
20
+ def initialize(process, exitstatus)
21
+
22
+ @process = process
23
+ @exitstatus = exitstatus
24
+ end
25
+
26
+ def pid; @process.pid; rescue; nil; end
27
+ end
28
+
29
+ def spawn(conf, data)
30
+
31
+ t0 = Time.now
32
+
33
+ cmd = conf['cmd']
34
+ henv, *acmd = split_cmd(cmd)
35
+
36
+ to = Fugit.parse(conf['timeout'] || '14s')
37
+ to = to.is_a?(Fugit::Duration) ? to.to_sec : 14
38
+ to = 0 if to < 0 # no timeout
39
+
40
+ builder = java.lang.ProcessBuilder.new(*acmd)
41
+ henv.each { |k, v| builder.environment.put(k, v) }
42
+
43
+ process = builder.start
44
+ pid = process.respond_to?(:pid) ? process.pid : nil
45
+
46
+ o = process.outputStream.to_io
47
+ i = process.inputStream.to_io
48
+ f = process.errorStream.to_io
49
+
50
+ o.write(data)
51
+ o.close
52
+
53
+ ex = timeout(to) { process.waitFor }
54
+
55
+ status = ProcessStatus.new(process, ex)
56
+
57
+ fail SpawnNonZeroExitError.new(conf, { to: to, t0: t0 }, status, i, f) \
58
+ if status.exitstatus != 0
59
+
60
+ [ i.read, status ]
61
+
62
+ rescue => err
63
+
64
+ if pid
65
+ Process.detach(pid)
66
+ (Process.kill(9, pid) rescue nil) unless Flor.no?(conf['on_error_kill'])
67
+ else
68
+ process.destroy rescue nil
69
+ end
70
+
71
+ raise err if err.is_a?(SpawnError)
72
+ raise WrappedSpawnError.new(conf, { to: to, t0: t0, pid: pid }, err)
73
+
74
+ ensure
75
+
76
+ [ i, o, f ].each { |x| x.close rescue nil }
77
+ end
78
+
79
+ module CmdParser include Raabro
80
+
81
+ # parsing
82
+
83
+ def separator(i); rex(nil, i, /[ ]+/); end
84
+
85
+ def dqstring(i); rex(:string, i, /"(\\"|[^"])*"/); end
86
+ def sqstring(i); rex(:string, i, /'(\\'|[^'])*'/); end
87
+
88
+ def word(i); rex(:word, i, /[^ "']+/); end
89
+ def item(i); alt(nil, i, :word, :sqstring, :dqstring); end
90
+ def cmd(i); jseq(:cmd, i, :item, :separator); end
91
+
92
+ def equal(i); rex(nil, i, /[ ]*=[ ]*/); end
93
+ def vval(i); alt(:vval, i, :word, :sqstring, :dqstring); end
94
+ def vkey(i); rex(:vkey, i, /[a-zA-Z_][a-zA-Z_0-9]*/); end
95
+ def var(i); seq(:var, i, :vkey, :equal, :vval, :separator); end
96
+
97
+ def cmdline(i); seq(:cmdline, i, :var, '*', :cmd) end
98
+
99
+ # rewriting
100
+
101
+ def rewrite_word(t); t.string; end
102
+
103
+ def rewrite_string(t)
104
+ s = t.string[1..-2]
105
+ s == 'sleep' ? t.string : s # ah, the awful thing :-(
106
+ end
107
+ #
108
+ # because of `ruby -e "sleep"` :-( is there another way?
109
+
110
+ def rewrite_cmd(t)
111
+
112
+ t.subgather(nil).collect { |tt| rewrite(tt) }
113
+ end
114
+
115
+ def rewrite_vars(ts)
116
+
117
+ ts.inject({}) { |h, t|
118
+ k = t.lookup(:vkey).string
119
+ v = t.lookup(:vval).string; v = v[1..-2] unless t.lookup(:word)
120
+ h[k] = v
121
+ h }
122
+ end
123
+
124
+ def rewrite_cmdline(t)
125
+
126
+ #Raabro.pp(t, colours: true)
127
+ [ rewrite_vars(t.subgather(:var)),
128
+ *t.lookup(:cmd).subgather(nil).collect { |tt| rewrite(tt) } ]
129
+ end
130
+ end
131
+ end
132
+ end
133
+
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flor
4
+
5
+ class << self
6
+
7
+ # See Scheduler#dump for the details
8
+ #
9
+ def dump(db_or_unit_or_uri, io=nil, opts=nil, &block)
10
+
11
+ derive_unit(db_or_unit_or_uri)
12
+ .dump(io, opts, &block)
13
+ end
14
+
15
+ # See Scheduler#load for the details
16
+ #
17
+ def load(db_or_unit_or_uri, string_or_io, opts={}, &block)
18
+
19
+ derive_unit(db_or_unit_or_uri)
20
+ .load(string_or_io, opts, &block)
21
+ end
22
+
23
+ protected
24
+
25
+ def derive_unit(db_or_unit_or_uri)
26
+
27
+ case o = db_or_unit_or_uri
28
+ when Flor::Unit then o
29
+ when Sequel::Database then Flor::Unit.new(sto_db: o)
30
+ when String then Flor::Unit.new(sto_uri: o)
31
+ else fail ArgumentError.new("cannot derive flor Unit out of #{o.inspect}")
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor
3
4
 
@@ -108,15 +109,13 @@ module Flor
108
109
  rescue Exception => exc
109
110
 
110
111
  # TODO eventually, have a dump dir
112
+
111
113
  fn =
112
- [
113
- 'flor',
114
- @unit.conf['env'], @unit.identifier, @exid,
115
- 'r' + counter('runs').to_s
116
- ].collect(&:to_s).join('_') + '.dump'
114
+ [ 'flor', @unit.conf['env'], @unit.identifier, @exid,
115
+ 'r' + counter('runs').to_s ].collect(&:to_s).join('_') + '.dump'
117
116
 
118
117
  @unit.logger.error(
119
- "#{self.class}#do_run()", exc, "(dumping to #{fn})")
118
+ "#{self.class}#do_run()", exc, "(dumping to #{fn} ...)")
120
119
 
121
120
  File.open(fn, 'wb') do |f|
122
121
  f.puts(Flor.to_pretty_s({
@@ -133,19 +132,26 @@ module Flor
133
132
  f.puts(on_do_run_exc(exc))
134
133
  end
135
134
 
135
+ @unit.logger.error(
136
+ "#{self.class}#do_run()", exc, "(dumped to #{fn})")
137
+
136
138
  #puts on_do_run_exc(exc)
137
139
  # dump notification above
138
140
  end
139
141
 
140
142
  def task(message)
141
143
 
142
- return error_reply(
143
- node(message['nid']),
144
- message,
145
- "don't know how to apply #{message['tasker'].inspect}"
146
- ) if message['routed'] == false
147
- #
148
- # use an error message similar to what the core executor would emit
144
+ if message['routed'] == false
145
+
146
+ t = message['tasker']
147
+ n = node(message['nid'])
148
+
149
+ msg = n['heat0'] != t ?
150
+ "tasker #{t.inspect} not found" :
151
+ "don't know how to apply #{t.inspect}"
152
+
153
+ return error_reply(n, message, msg)
154
+ end
149
155
 
150
156
  @unit.ganger.task(self, message)
151
157
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor
3
4
 
@@ -53,6 +54,9 @@ module Flor
53
54
  (@unit.loader.tasker(domain, 'ganger', message) ||
54
55
  @unit.loader.tasker(domain, 'tasker', message))) ||
55
56
  @unit.loader.tasker(domain, tname, message)
57
+ #puts "=" * 80
58
+ #pp tconf
59
+ #puts "=" * 80
56
60
 
57
61
  fail ArgumentError.new(
58
62
  "tasker #{tname.inspect} not found"
@@ -66,11 +70,15 @@ module Flor
66
70
  tconf = tconf.find { |h| points.include?(h['point']) }
67
71
  end
68
72
 
73
+ fail ArgumentError.new(
74
+ "tconf #{tconf.inspect} not a hash"
75
+ ) unless tconf.is_a?(Hash)
76
+
69
77
  message['tconf'] = tconf unless tconf['include_tconf'] == false
70
78
 
71
79
  message['vars'] = gather_vars(executor, tconf, message)
72
80
 
73
- m = dup_message(message)
81
+ m = Flor.dup_message(message)
74
82
  #
75
83
  # the tasker gets a copy of the message (and it can play with it
76
84
  # to its heart content), meanwhile the message is handed to the
@@ -92,17 +100,6 @@ module Flor
92
100
 
93
101
  protected
94
102
 
95
- def dup_message(m)
96
-
97
- tc = m.delete('tconf')
98
- m1 = Flor.dup(m)
99
- m1['tconf'] = tc.inject({}) { |h, (k, v)|
100
- h[k] = k == 'class' ? v : Flor.dup(v); h } \
101
- if tc
102
-
103
- m1
104
- end
105
-
106
103
  def var_match(k, filter)
107
104
 
108
105
  filter.each { |f| return true if (f.is_a?(String) ? k == f : f.match(k)) }
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flor
4
+
5
+ # A ModuleGanger accepts a `module:` conf entry that points to a Ruby
6
+ # module. The tasker implementations are searched for among the classes
7
+ # in the given module.
8
+ #
9
+ # Among the tasker classes (classes that respond to on_task, on_detask, ...)
10
+ # it selects the first tasker that matches the tasker name.
11
+ #
12
+ class ModuleGanger
13
+
14
+ def initialize(service, conf, message)
15
+
16
+ @service = service
17
+ @conf = conf
18
+ @message = message
19
+ end
20
+
21
+ def task
22
+
23
+ tas = @message['tasker']
24
+ clas = list_tasker_classes
25
+ cla = clas.find { |c| tasker_name(c) == tas }
26
+
27
+ return [ Flor.dup_and_merge(@message, 'routed' => false) ] \
28
+ unless cla
29
+
30
+ call_tasker(cla)
31
+ end
32
+
33
+ alias detask task
34
+
35
+ protected
36
+
37
+ def list_tasker_classes
38
+
39
+ mod_name = @conf['module']
40
+
41
+ fail ArgumentError.new('ganger module: configuration entry missing') \
42
+ unless mod_name
43
+
44
+ mod = Flor.const_lookup(mod_name) rescue nil
45
+
46
+ fail ArgumentError.new("ganger cannot find module #{mod_name.inspect}") \
47
+ unless mod
48
+
49
+ list_classes(mod, [])
50
+ end
51
+
52
+ def list_classes(start, r)
53
+
54
+ # place leave classes on top if possible
55
+ # within a level, sort alphabetically
56
+
57
+ clas = start.constants.collect { |co| start.const_get(co) }
58
+ clas, mods = clas.partition { |c| c.is_a?(Class) }
59
+
60
+ mods.each { |m| list_classes(m, r) }
61
+ r.concat(clas.select { |c| tasker?(c) }.sort_by { |c| c.name })
62
+
63
+ r
64
+ end
65
+
66
+ TASKER_METHODS = [
67
+ :on, :on_message,
68
+ :task, :on_task,
69
+ :detask, :on_detask, :cancel, :on_cancel
70
+ ].freeze
71
+
72
+ def tasker?(cla)
73
+
74
+ return true if (TASKER_METHODS & cla.public_instance_methods).any?
75
+ return true if (TASKER_METHODS & cla.public_methods).any?
76
+ false
77
+ end
78
+
79
+ def tasker_name(cla)
80
+
81
+ if cla.public_instance_methods.include?(:tasker_name)
82
+
83
+ unless cla.respond_to?(:_ganged)
84
+ class << cla
85
+ attr_accessor :_ganged
86
+ end
87
+ cla._ganged = cla.allocate
88
+ end
89
+
90
+ call_tasker_name(cla._ganged)
91
+
92
+ elsif cla.public_methods.include?(:tasker_name)
93
+
94
+ call_tasker_name(cla)
95
+
96
+ else
97
+
98
+ cla.name.split('::').last.gsub(/Tasker\z/, '')
99
+ .gsub(/([a-z])([A-Z])/) { |_| $1 + '_' + $2.downcase }
100
+ .gsub(/([A-Z])/) { |c| c.downcase }
101
+ end
102
+ end
103
+
104
+ def call_tasker_name(o)
105
+
106
+ case i = o.method(:tasker_name).arity
107
+ when 1 then o.tasker_name(@message)
108
+ when 2 then o.tasker_name(@conf, @message)
109
+ when 3 then o.tasker_name(@service, @conf, @message)
110
+ when -1 then o.tasker_name(
111
+ service: @service, conf: @conf, message: @message)
112
+ else o.tasker_name
113
+ end
114
+ end
115
+
116
+ def call_tasker(c)
117
+
118
+ cnf = @conf.merge('class' => c)
119
+
120
+ @service.unit.caller
121
+ .call(@service, cnf, @message)
122
+ end
123
+ end
124
+ end
125
+
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor
3
4
 
4
5
  # A loader which keeps everything in a Hash, while the traditional/default
5
6
  # Flor::Loader reads from a file tree.
6
7
  #
7
- class HashLoader
8
+ class HashLoader < CoreLoader
8
9
 
9
10
  # NB: tasker configuration entries start with "loa_", like for Flor::Loader
10
11
 
@@ -27,9 +28,6 @@ module Flor
27
28
  self.environment = (@unit.conf['lod_environment'] || {})
28
29
  end
29
30
 
30
- def shutdown
31
- end
32
-
33
31
  HCATS = {
34
32
  'v' => 'variables',
35
33
  'var' => 'variables',
@@ -53,9 +51,14 @@ module Flor
53
51
  path = path.to_s
54
52
  path = path + '.' if c == 'hooks' && path.length > 0 && path[-1, 1] != '.'
55
53
 
56
- value = block ?
57
- block_to_class(c, block) :
58
- Flor.to_string_keyed_hash(value)
54
+ value =
55
+ if block
56
+ block_to_class(c, block)
57
+ elsif value.is_a?(Proc)
58
+ block_to_class(c, value)
59
+ else
60
+ Flor.to_string_keyed_hash(value)
61
+ end
59
62
 
60
63
  e = (@environment[c] ||= [])
61
64
  e << [ *split(path), value ]
@@ -132,6 +135,30 @@ module Flor
132
135
  .collect { |h| Flor::Hook.new(@unit, exid, h) }
133
136
  end
134
137
 
138
+ def domains(start=nil)
139
+
140
+ start ||= ''
141
+
142
+ @environment.values
143
+ .flatten(1)
144
+ .collect { |x| x.first }
145
+ .select { |dm| dm && dm.length > 0 }
146
+ .select { |dm| Flor.sub_domain?(start, dm) }
147
+ .uniq
148
+ .sort
149
+ end
150
+
151
+ def definitions(start=nil)
152
+
153
+ start ||= ''
154
+
155
+ @environment.values
156
+ .flatten(1)
157
+ .collect { |x| x[0, 2].join('.') }
158
+ .select { |fl| Flor.sub_domain?(start, fl) }
159
+ .sort
160
+ end
161
+
135
162
  protected
136
163
 
137
164
  def recompose(h)