flor 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+