flor 0.10.0 → 0.11.0

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.
@@ -1,29 +1,28 @@
1
- #--
2
- # Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
- #
22
- # Made in Japan.
23
- #++
24
-
25
1
 
26
2
  class Flor::Pro::Apply < Flor::Procedure
3
+ #
4
+ # Applies a function.
5
+ #
6
+ # ```
7
+ # sequence
8
+ # define sum a b
9
+ # +
10
+ # a
11
+ # b
12
+ # apply sum 1 2
13
+ # ```
14
+ #
15
+ # It is usually used implicitely, as in
16
+ # ```
17
+ # sequence
18
+ # define sum a b
19
+ # +
20
+ # a
21
+ # b
22
+ # sum 1 2
23
+ # ```
24
+ # where flor figures out by itself it has to use this "apply" procedure
25
+ # to call the function.
27
26
 
28
27
  name 'apply'
29
28
 
@@ -82,39 +82,37 @@ class Flor::Pro::Case < Flor::Procedure
82
82
  # ot = tree; return if ot[1].size < 2
83
83
  # t = Flor.dup(ot)
84
84
  #
85
- # nchildren = [ t[1].first ]
85
+ # nchildren = []
86
86
  # mode = :array
87
87
  #
88
- # t[1][1..-1].each do |ct|
88
+ # #t[1][1..-1].each do |ct|
89
+ # non_att_children.each do |ct|
89
90
  #
90
- # if mode == :array
91
+ # if nchildren.empty? || mode == :clause
92
+ # nchildren << ct
93
+ # mode = :array
94
+ # next
95
+ # end
91
96
  #
92
- # if ct[0].is_a?(String)
93
- # puts "---"
94
- # dct0 = deref(ct[0])
95
- # hct0 = toheap(ct, dct0)
96
- # p dct0
97
- # p hct0
98
- # puts "/---"
99
- # end
97
+ # ct0, ct1, ct2 = ct
100
98
  #
101
- # if (Flor.is_tree?(ct[0]) || ct[0] == 'else') && ct[1].any?
102
- # nchildren << (ct[0] == 'else' ? [ 'else', [], ct[2] ] : ct[0])
103
- # if ct[1].size == 1
104
- # nchildren << ct[1].first
105
- # else # ct[1].size > 1
106
- # sequence = [ 'sequence', ct[1], ct[1].first[2] ]
107
- # nchildren << sequence
108
- # end
109
- # else
110
- # nchildren << ct
111
- # mode = :clause
99
+ # if (Flor.is_tree?(ct0) || ct0 == 'else') && ct1.any?
100
+ # nchildren << (ct0 == 'else' ? [ 'else', [], ct2 ] : ct0)
101
+ # if ct1.size == 1
102
+ # nchildren << ct1.first
103
+ # else # ct1.size > 1
104
+ # sequence = [ 'sequence', ct1, ct1.first[2] ]
105
+ # nchildren << sequence
112
106
  # end
113
- #
114
- # else # mode == :clause
115
- #
107
+ # #elsif ct0.is_a?(String) && ct1.is_a?(Array) && ct1.any?
108
+ # # p ct
109
+ # # dct0 = deref(ct0)
110
+ # # hct0 = reheap(ct, dct0)
111
+ # # p dct0
112
+ # # p hct0
113
+ # else
116
114
  # nchildren << ct
117
- # mode = :array
115
+ # mode = :clause
118
116
  # end
119
117
  # end
120
118
  #
@@ -1,5 +1,15 @@
1
1
 
2
2
  class Flor::Pro::Fail < Flor::Procedure
3
+ #
4
+ # Explicitely raises an error.
5
+ #
6
+ # ```
7
+ # fail "not enough water in the tank"
8
+ # # or
9
+ # error "not enough water in the tank"
10
+ # ```
11
+ #
12
+ # (I prefer "fail" because it's a verb over "error", pick the one you like)
3
13
 
4
14
  names 'fail', 'error'
5
15
 
data/lib/flor/pcore/if.rb CHANGED
@@ -2,10 +2,45 @@
2
2
  class Flor::Pro::If < Flor::Procedure
3
3
  #
4
4
  # The classical "if" (and its "unless" sidequick)
5
+ #
6
+ # ```
7
+ # if
8
+ # > f.age 3
9
+ # set f.designation 'child' # then
10
+ # set f.designation 'baby' # else
11
+ #
12
+ # if (f.age > 3)
13
+ # sequence # then
14
+ # set f.designation 'child'
15
+ # sequence # else
16
+ # set f.designation 'baby'
17
+ # order_baby_food
18
+ # ```
19
+ #
20
+ # Warning, the direct children are relevant. In the following snip,
21
+ # `order_child_seat` is considered the "else" part of the `if`
22
+ # ```
23
+ # if (f.age > 3)
24
+ # set f.designation 'child'
25
+ # order_child_seat
26
+ # ```
27
+ #
28
+ # ## postfix `if` and `unless`
29
+ #
30
+ # The flor parser will automatically turn
31
+ # ```
32
+ # task 'bob' if a > b
33
+ # ```
34
+ # into the syntax tree that would result from
35
+ # ```
36
+ # if
37
+ # a > b
38
+ # task 'bob'
39
+ # ```
5
40
 
6
41
  names %w[ if unless ife unlesse ]
7
42
  #
8
- # removing "ife" and "unlesse" leas to
43
+ # removing "ife" and "unlesse" leads to
9
44
  # LoadError: cannot load such file -- sequel/adapters/
10
45
  # when running spec/unit/ and spec/punit/
11
46
  # weird...
@@ -0,0 +1,32 @@
1
+
2
+ class Flor::Pro::Twig < Flor::Procedure
3
+
4
+ name 'twig'
5
+
6
+ def pre_execute
7
+
8
+ unatt_unkeyed_children
9
+ stringify_first_child
10
+ end
11
+
12
+ def receive_first
13
+
14
+ nac = non_att_children
15
+
16
+ if nac.size == 1
17
+ reply('ret' => nac.first)
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def receive_non_att
24
+
25
+ t = non_att_children.last
26
+
27
+ set_value(payload['ret'], t)
28
+
29
+ reply
30
+ end
31
+ end
32
+
@@ -6,7 +6,7 @@ class Flor::Pro::Val < Flor::Procedure
6
6
  def execute
7
7
 
8
8
  heat = @node['heat']
9
- heat = nil if heat == [ '_proc', 'val', -1 ]
9
+ heat = nil if heat == [ '_proc', 'val', -1 ] || heat[0] == '_nul'
10
10
 
11
11
  payload['ret'] = heat
12
12
 
@@ -1,5 +1,43 @@
1
1
 
2
2
  class Flor::Pro::Graft < Flor::Procedure
3
+ #
4
+ # Graft a subtree into the current flo
5
+ #
6
+ # Given
7
+ # ```
8
+ # # sub0.flo
9
+ # sequence
10
+ # task 'a'
11
+ # task 'b'
12
+ # ```
13
+ # and
14
+ # ```
15
+ # # sub1.flo
16
+ # sequence
17
+ # task 'c'
18
+ # task 'd'
19
+ # ```
20
+ # then
21
+ # ```
22
+ # # main.flo
23
+ # concurrence
24
+ # graft 'sub0.flo'
25
+ # graft 'sub1' # suffix can be omitted
26
+ # graft 'sub0'
27
+ # #
28
+ # # which is thus equivalent to
29
+ # #
30
+ # concurrence
31
+ # sequence
32
+ # task 'a'
33
+ # task 'b'
34
+ # sequence
35
+ # task 'c'
36
+ # task 'd'
37
+ # sequence
38
+ # task 'a'
39
+ # task 'b'
40
+ # ```
3
41
 
4
42
  names 'graft', 'import'
5
43
 
@@ -14,7 +52,7 @@ class Flor::Pro::Graft < Flor::Procedure
14
52
  # look up subtree
15
53
 
16
54
  sub =
17
- att('tree', 'subtree', 'flow', 'subflow', nil)
55
+ att('tree', 'subtree', 'flow', 'subflow', 'twig', nil)
18
56
  source_path, source =
19
57
  @executor.unit.loader.library(domain, sub, subflows: true)
20
58
 
data/lib/flor/unit.rb CHANGED
@@ -5,7 +5,9 @@ require 'sequel/extensions/migration'
5
5
  require 'fugit'
6
6
 
7
7
  require 'flor'
8
+ require 'flor/unit/hook'
8
9
  require 'flor/unit/hooker'
10
+ require 'flor/unit/runner'
9
11
  require 'flor/unit/wlist'
10
12
  require 'flor/unit/logger'
11
13
  require 'flor/unit/journal'
@@ -16,6 +18,7 @@ require 'flor/unit/scheduler'
16
18
  require 'flor/unit/models'
17
19
  require 'flor/unit/loader'
18
20
  require 'flor/unit/ganger'
21
+ require 'flor/unit/spooler'
19
22
  require 'flor/unit/taskers'
20
23
 
21
24
  Flor.load_procedures('punit')
@@ -12,6 +12,7 @@ module Flor
12
12
 
13
13
  super(
14
14
  unit,
15
+ unit.loader.load_hooks(@exid),
15
16
  unit.storage.fetch_traps(@exid),
16
17
  unit.storage.load_execution(@exid))
17
18
 
@@ -42,26 +42,26 @@ module Flor
42
42
  "tasker #{tname.inspect} not found"
43
43
  ) unless tconf
44
44
 
45
- message['tconf'] = tconf \
46
- unless tconf['on_task']['include_tconf'] == false
47
-
48
- message['vars'] = gather_vars(executor, tconf, message)
49
-
50
- cot = tconf['on_task']
45
+ if tconf.is_a?(Array)
46
+ tconf =
47
+ tconf.find { |h| h['point'] == message['point'] } ||
48
+ tconf.find { |h| h['point'] == nil }
49
+ tconf ||=
50
+ tconf.find { |h| h['point'] == 'cancel' } \
51
+ if message['point'] == 'detask'
52
+ end
51
53
 
52
- return ruby_task(message, tconf) if cot['require'] || cot['class']
53
- return cmd_task(message, tconf) if cot['cmd']
54
+ message['tconf'] = tconf unless tconf['include_tconf'] == false
54
55
 
55
- fail ArgumentError.new(
56
- "don't know how to use tasker at #{tconf['_path']}"
57
- )
58
- end
59
-
60
- def cancel(tasker_name, fei)
56
+ message['vars'] = gather_vars(executor, tconf, message)
61
57
 
62
- # TODO use on_cancel || on_task
58
+ r = @unit.runner.run(self, tconf, message)
63
59
 
64
- fail NotImplementedError
60
+ if is_a_message_array?(r)
61
+ r
62
+ else
63
+ []
64
+ end
65
65
  end
66
66
 
67
67
  def return(message)
@@ -78,53 +78,6 @@ module Flor
78
78
  o.first['point'].is_a?(String)
79
79
  end
80
80
 
81
- def ruby_task(message, tconf)
82
-
83
- root = File.dirname(tconf['_path'])
84
-
85
- Array(tconf['on_task']['require'])
86
- .each { |pa|
87
- fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
88
- require(File.join(root, pa)) }
89
- Array(tconf['on_task']['load'])
90
- .each { |pa|
91
- fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
92
- load(File.join(root, pa)) }
93
-
94
- k = tconf['on_task']['class']
95
- k = Flor.const_lookup(k)
96
-
97
- ka = k.instance_method(:initialize).arity
98
-
99
- m =
100
- message['point'] == 'detask' ?
101
- :cancel :
102
- :task
103
-
104
- r =
105
- if ka == 2
106
- k.new(self, tconf).send(m, message)
107
- else # ka == 3
108
- k.new(self, tconf, message).send(m)
109
- end
110
-
111
- # if the tasker returns something intelligible, use it
112
- # else reply with "nothing further to do" (empty message array)
113
-
114
- if is_a_message_array?(r)
115
- r
116
- else
117
- []
118
- end
119
- end
120
-
121
- def cmd_task(message, tconf)
122
-
123
- fail NotImplementedError
124
-
125
- []
126
- end
127
-
128
81
  def var_match(k, filter)
129
82
 
130
83
  filter.each { |f| return true if (f.is_a?(String) ? k == f : f.match(k)) }
@@ -155,17 +108,15 @@ module Flor
155
108
 
156
109
  def gather_vars(executor, tconf, message)
157
110
 
158
- ot = tconf['on_task']
159
-
160
111
  # try to return before calling executor.vars(nid) which my be costly...
161
112
 
162
- return nil if (ot.keys & %w[ include_vars exclude_vars ]).empty?
113
+ return nil if (tconf.keys & %w[ include_vars exclude_vars ]).empty?
163
114
  # default behaviour, don't pass variables to taskers
164
115
 
165
- iv = expand_filter(ot['include_vars'])
116
+ iv = expand_filter(tconf['include_vars'])
166
117
  return nil if iv == false
167
118
 
168
- ev = expand_filter(ot['exclude_vars'])
119
+ ev = expand_filter(tconf['exclude_vars'])
169
120
  return {} if ev == true
170
121
 
171
122
  vars = executor.vars(message['nid'])
@@ -0,0 +1,32 @@
1
+
2
+ module Flor
3
+
4
+ class Hook
5
+
6
+ def initialize(unit, exid, h)
7
+
8
+ @unit = unit
9
+ @exid = exid
10
+ @h = h
11
+ end
12
+
13
+ def to_hook
14
+
15
+ opts = {}
16
+ opts[:consumed] = @h['consumed']
17
+ opts[:point] = Flor.h_fetch_a(@h, 'points', 'point', nil)
18
+ opts[:heap] = Flor.h_fetch_a(@h, 'heaps', 'heap', nil)
19
+ opts[:heat] = Flor.h_fetch_a(@h, 'heats', 'heat', nil)
20
+ #opts[:name] = data['names']
21
+
22
+ [ "hook#{object_id}", opts, self, nil ]
23
+ end
24
+
25
+
26
+ def notify(executor, message)
27
+
28
+ @unit.runner.run(executor, @h, message)
29
+ end
30
+ end
31
+ end
32
+