ruote 2.1.11 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (217) hide show
  1. data/CHANGELOG.txt +60 -0
  2. data/CREDITS.txt +22 -4
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +6 -7
  5. data/Rakefile +58 -59
  6. data/TODO.txt +137 -65
  7. data/couch_url.txt +1 -0
  8. data/jruby_issue.txt +32 -0
  9. data/lib/ruote.rb +1 -1
  10. data/lib/ruote/context.rb +12 -10
  11. data/lib/ruote/engine.rb +280 -145
  12. data/lib/ruote/engine/process_error.rb +5 -5
  13. data/lib/ruote/engine/process_status.rb +47 -28
  14. data/lib/ruote/exp/command.rb +7 -10
  15. data/lib/ruote/exp/commanded.rb +2 -2
  16. data/lib/ruote/exp/condition.rb +130 -43
  17. data/lib/ruote/exp/fe_add_branches.rb +2 -2
  18. data/lib/ruote/exp/fe_apply.rb +1 -1
  19. data/lib/ruote/exp/fe_cancel_process.rb +3 -3
  20. data/lib/ruote/exp/fe_command.rb +3 -3
  21. data/lib/ruote/exp/fe_concurrence.rb +4 -4
  22. data/lib/ruote/exp/fe_concurrent_iterator.rb +17 -5
  23. data/lib/ruote/exp/fe_cron.rb +3 -3
  24. data/lib/ruote/exp/fe_cursor.rb +5 -5
  25. data/lib/ruote/exp/fe_define.rb +3 -3
  26. data/lib/ruote/exp/fe_echo.rb +3 -3
  27. data/lib/ruote/exp/fe_equals.rb +2 -2
  28. data/lib/ruote/exp/fe_error.rb +2 -2
  29. data/lib/ruote/exp/fe_filter.rb +519 -0
  30. data/lib/ruote/exp/fe_forget.rb +9 -2
  31. data/lib/ruote/exp/fe_given.rb +154 -0
  32. data/lib/ruote/exp/fe_if.rb +16 -13
  33. data/lib/ruote/exp/fe_inc.rb +3 -3
  34. data/lib/ruote/exp/fe_iterator.rb +4 -4
  35. data/lib/ruote/exp/fe_let.rb +75 -0
  36. data/lib/ruote/exp/fe_listen.rb +68 -12
  37. data/lib/ruote/exp/fe_lose.rb +110 -0
  38. data/lib/ruote/exp/fe_noop.rb +1 -1
  39. data/lib/ruote/exp/{fe_when.rb → fe_once.rb} +25 -21
  40. data/lib/ruote/exp/fe_participant.rb +14 -17
  41. data/lib/ruote/exp/fe_redo.rb +10 -6
  42. data/lib/ruote/exp/fe_ref.rb +1 -1
  43. data/lib/ruote/exp/fe_registerp.rb +112 -0
  44. data/lib/ruote/exp/fe_reserve.rb +3 -3
  45. data/lib/ruote/exp/fe_restore.rb +2 -2
  46. data/lib/ruote/exp/fe_save.rb +2 -2
  47. data/lib/ruote/exp/fe_sequence.rb +3 -4
  48. data/lib/ruote/exp/fe_set.rb +16 -7
  49. data/lib/ruote/exp/fe_subprocess.rb +23 -1
  50. data/lib/ruote/exp/fe_that.rb +92 -0
  51. data/lib/ruote/exp/fe_undo.rb +3 -3
  52. data/lib/ruote/exp/fe_unregisterp.rb +71 -0
  53. data/lib/ruote/exp/fe_wait.rb +2 -2
  54. data/lib/ruote/exp/flowexpression.rb +153 -78
  55. data/lib/ruote/exp/iterator.rb +2 -2
  56. data/lib/ruote/exp/merge.rb +2 -2
  57. data/lib/ruote/exp/ro_attributes.rb +14 -12
  58. data/lib/ruote/exp/ro_filters.rb +136 -0
  59. data/lib/ruote/exp/ro_persist.rb +51 -35
  60. data/lib/ruote/exp/ro_variables.rb +18 -27
  61. data/lib/ruote/fei.rb +73 -33
  62. data/lib/ruote/id/mnemo_wfid_generator.rb +1 -1
  63. data/lib/ruote/id/wfid_generator.rb +11 -4
  64. data/lib/ruote/log/default_history.rb +122 -0
  65. data/lib/ruote/log/pretty.rb +36 -8
  66. data/lib/ruote/log/storage_history.rb +37 -5
  67. data/lib/ruote/log/test_logger.rb +26 -24
  68. data/lib/ruote/log/wait_logger.rb +5 -3
  69. data/lib/ruote/part/block_participant.rb +22 -11
  70. data/lib/ruote/part/engine_participant.rb +6 -7
  71. data/lib/ruote/part/local_participant.rb +6 -12
  72. data/lib/ruote/part/no_op_participant.rb +4 -4
  73. data/lib/ruote/part/null_participant.rb +4 -4
  74. data/lib/ruote/part/smtp_participant.rb +4 -4
  75. data/lib/ruote/part/storage_participant.rb +40 -20
  76. data/lib/ruote/part/template.rb +4 -4
  77. data/lib/ruote/participant.rb +0 -1
  78. data/lib/ruote/{parser.rb → reader.rb} +30 -25
  79. data/lib/ruote/{parser → reader}/ruby_dsl.rb +28 -11
  80. data/lib/ruote/{parser → reader}/xml.rb +6 -5
  81. data/lib/ruote/receiver/base.rb +35 -13
  82. data/lib/ruote/storage/base.rb +20 -18
  83. data/lib/ruote/storage/composite_storage.rb +10 -10
  84. data/lib/ruote/storage/fs_storage.rb +17 -10
  85. data/lib/ruote/storage/hash_storage.rb +29 -18
  86. data/lib/ruote/svc/dispatch_pool.rb +41 -14
  87. data/lib/ruote/svc/dollar_sub.rb +50 -17
  88. data/lib/ruote/svc/error_handler.rb +19 -11
  89. data/lib/ruote/svc/expression_map.rb +4 -4
  90. data/lib/ruote/svc/participant_list.rb +105 -100
  91. data/lib/ruote/svc/tracker.rb +58 -18
  92. data/lib/ruote/svc/treechecker.rb +51 -24
  93. data/lib/ruote/tree_dot.rb +4 -4
  94. data/lib/ruote/util/filter.rb +440 -0
  95. data/lib/ruote/util/hashdot.rb +4 -4
  96. data/lib/ruote/util/look.rb +2 -6
  97. data/lib/ruote/util/lookup.rb +9 -7
  98. data/lib/ruote/util/misc.rb +40 -8
  99. data/lib/ruote/util/ometa.rb +1 -1
  100. data/lib/ruote/util/serializer.rb +4 -4
  101. data/lib/ruote/util/subprocess.rb +29 -9
  102. data/lib/ruote/util/time.rb +4 -4
  103. data/lib/ruote/util/tree.rb +3 -3
  104. data/lib/ruote/version.rb +2 -2
  105. data/lib/ruote/worker.rb +55 -32
  106. data/lib/ruote/workitem.rb +64 -11
  107. data/ruote.gemspec +31 -302
  108. data/test/bm/launch_bench.rb +37 -0
  109. data/test/functional/base.rb +60 -18
  110. data/test/functional/concurrent_base.rb +2 -2
  111. data/test/functional/ct_0_concurrence.rb +1 -1
  112. data/test/functional/ct_1_iterator.rb +1 -1
  113. data/test/functional/ct_2_cancel.rb +1 -1
  114. data/test/functional/eft_0_process_definition.rb +2 -2
  115. data/test/functional/eft_10_cancel_process.rb +1 -1
  116. data/test/functional/eft_11_wait.rb +19 -11
  117. data/test/functional/eft_12_listen.rb +79 -13
  118. data/test/functional/eft_13_iterator.rb +13 -10
  119. data/test/functional/eft_14_cursor.rb +98 -9
  120. data/test/functional/eft_15_loop.rb +6 -4
  121. data/test/functional/eft_16_if.rb +12 -0
  122. data/test/functional/eft_18_concurrent_iterator.rb +31 -32
  123. data/test/functional/eft_19_reserve.rb +4 -4
  124. data/test/functional/eft_1_echo.rb +9 -0
  125. data/test/functional/eft_20_save.rb +4 -4
  126. data/test/functional/{eft_28_when.rb → eft_28_once.rb} +33 -7
  127. data/test/functional/eft_30_ref.rb +17 -2
  128. data/test/functional/eft_31_registerp.rb +130 -0
  129. data/test/functional/eft_32_lose.rb +93 -0
  130. data/test/functional/eft_33_let.rb +31 -0
  131. data/test/functional/eft_34_given.rb +123 -0
  132. data/test/functional/eft_35_filter.rb +269 -0
  133. data/test/functional/eft_3_participant.rb +4 -6
  134. data/test/functional/eft_4_set.rb +16 -2
  135. data/test/functional/eft_5_subprocess.rb +2 -4
  136. data/test/functional/eft_6_concurrence.rb +29 -29
  137. data/test/functional/eft_8_undo.rb +39 -3
  138. data/test/functional/eft_9_redo.rb +94 -2
  139. data/test/functional/ft_10_dollar.rb +81 -2
  140. data/test/functional/ft_11_recursion.rb +13 -17
  141. data/test/functional/ft_12_launchitem.rb +9 -5
  142. data/test/functional/ft_13_variables.rb +7 -9
  143. data/test/functional/ft_14_re_apply.rb +6 -9
  144. data/test/functional/ft_15_timeout.rb +18 -18
  145. data/test/functional/ft_16_participant_params.rb +1 -3
  146. data/test/functional/ft_17_conditional.rb +25 -2
  147. data/test/functional/ft_18_kill.rb +65 -12
  148. data/test/functional/ft_1_process_status.rb +147 -71
  149. data/test/functional/ft_20_storage_participant.rb +0 -1
  150. data/test/functional/ft_21_forget.rb +82 -1
  151. data/test/functional/{ft_24_block_participants.rb → ft_24_block_participant.rb} +42 -11
  152. data/test/functional/ft_25_receiver.rb +47 -17
  153. data/test/functional/{ft_26_participant_timeout.rb → ft_26_participant_rtimeout.rb} +56 -19
  154. data/test/functional/ft_29_part_template.rb +6 -5
  155. data/test/functional/ft_2_errors.rb +21 -37
  156. data/test/functional/ft_30_smtp_participant.rb +1 -1
  157. data/test/functional/ft_31_part_blocking.rb +8 -6
  158. data/test/functional/ft_34_cursor_rewind.rb +13 -10
  159. data/test/functional/ft_35_add_service.rb +1 -1
  160. data/test/functional/ft_36_storage_history.rb +24 -1
  161. data/test/functional/ft_37_default_history.rb +109 -0
  162. data/test/functional/ft_38_participant_more.rb +10 -10
  163. data/test/functional/ft_39_wait_for.rb +12 -9
  164. data/test/functional/ft_3_participant_registration.rb +111 -32
  165. data/test/functional/ft_40_wait_logger.rb +2 -1
  166. data/test/functional/ft_41_participants.rb +30 -4
  167. data/test/functional/ft_43_participant_on_reply.rb +6 -23
  168. data/test/functional/ft_45_participant_accept.rb +4 -4
  169. data/test/functional/ft_46_launch_single.rb +36 -2
  170. data/test/functional/ft_47_wfid_generator.rb +54 -0
  171. data/test/functional/ft_48_lose.rb +112 -0
  172. data/test/functional/ft_49_engine_on_error.rb +201 -0
  173. data/test/functional/ft_4_cancel.rb +66 -6
  174. data/test/functional/ft_50_engine_config.rb +22 -0
  175. data/test/functional/ft_51_misc.rb +67 -0
  176. data/test/functional/ft_52_case.rb +134 -0
  177. data/test/functional/ft_53_engine_on_terminate.rb +95 -0
  178. data/test/functional/ft_54_patterns.rb +104 -0
  179. data/test/functional/{ft_37_engine_participant.rb → ft_55_engine_participant.rb} +4 -5
  180. data/test/functional/ft_56_filter_attribute.rb +259 -0
  181. data/test/functional/ft_5_on_error.rb +77 -30
  182. data/test/functional/ft_6_on_cancel.rb +66 -11
  183. data/test/functional/ft_7_tags.rb +94 -5
  184. data/test/functional/ft_8_participant_consumption.rb +36 -5
  185. data/test/functional/ft_9_subprocesses.rb +10 -10
  186. data/test/functional/rt_1_listen.rb +3 -3
  187. data/test/functional/{rt_3_when.rb → rt_3_once.rb} +4 -4
  188. data/test/functional/storage_helper.rb +15 -13
  189. data/test/functional/test.rb +1 -3
  190. data/test/test_helper.rb +0 -8
  191. data/test/unit/storage.rb +154 -10
  192. data/test/unit/{ut_0_ruby_parser.rb → ut_0_ruby_reader.rb} +61 -11
  193. data/test/unit/ut_11_lookup.rb +7 -0
  194. data/test/unit/ut_13_serializer.rb +1 -1
  195. data/test/unit/ut_15_util.rb +23 -0
  196. data/test/unit/{ut_16_parser.rb → ut_16_reader.rb} +11 -13
  197. data/test/unit/ut_1_fei.rb +57 -10
  198. data/test/unit/ut_20_composite_storage.rb +25 -11
  199. data/test/unit/ut_21_participant_list.rb +47 -0
  200. data/test/unit/ut_22_filter.rb +903 -0
  201. data/test/unit/ut_3_wait_logger.rb +2 -6
  202. data/test/unit/ut_6_condition.rb +164 -17
  203. data/test/unit/ut_7_workitem.rb +28 -0
  204. data/test/unit/ut_8_tree_to_dot.rb +1 -1
  205. data/test/unit/{ut_9_xml_parser.rb → ut_9_xml_reader.rb} +5 -5
  206. metadata +108 -84
  207. data/.gitignore +0 -4
  208. data/examples/barley.rb +0 -391
  209. data/examples/flickr_report.rb +0 -107
  210. data/examples/pong.rb +0 -37
  211. data/examples/ruote_quickstart.rb +0 -43
  212. data/examples/web_first_page.rb +0 -68
  213. data/lib/ruote/part/hash_participant.rb +0 -91
  214. data/test/README.rdoc +0 -15
  215. data/test/functional/crunner.sh +0 -19
  216. data/test/pdef.xml +0 -7
  217. data/test/unit/ut_2_wfidgen.rb +0 -21
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@ module Ruote
30
30
  #
31
31
  class ProcessError
32
32
 
33
- def initialize (h)
33
+ def initialize(h)
34
34
  @h = h
35
35
  end
36
36
 
@@ -65,7 +65,7 @@ module Ruote
65
65
  # A shortcut for modifying the tree of an expression when it has had
66
66
  # an error upon being applied.
67
67
  #
68
- def tree= (t)
68
+ def tree=(t)
69
69
  @h['msg']['tree'] = t
70
70
  end
71
71
 
@@ -83,7 +83,7 @@ module Ruote
83
83
  # Exposes the workitem fields directly.
84
84
  #
85
85
  def fields
86
- @h['msg']['workitem']['fields']
86
+ @h['msg']['workitem'] && @h['msg']['workitem']['fields']
87
87
  end
88
88
 
89
89
  # Returns an instance of Ruote::Workitem
@@ -94,7 +94,7 @@ module Ruote
94
94
 
95
95
  protected
96
96
 
97
- def to_dot (opts)
97
+ def to_dot(opts)
98
98
 
99
99
  i = fei.to_storage_id
100
100
  label = "error : #{message.gsub(/"/, "'")}"
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,10 @@ module Ruote
38
38
  #
39
39
  attr_reader :expressions
40
40
 
41
+ # Returns the expression at the root of the process instance.
42
+ #
43
+ attr_reader :root_expression
44
+
41
45
  # An array of the workitems currently in the storage participant for this
42
46
  # process instance.
43
47
  #
@@ -55,7 +59,7 @@ module Ruote
55
59
  #
56
60
  attr_reader :schedules
57
61
 
58
- def initialize (context, expressions, stored_workitems, errors, schedules)
62
+ def initialize(context, expressions, stored_workitems, errors, schedules)
59
63
 
60
64
  @expressions = expressions.collect { |e|
61
65
  Ruote::Exp::FlowExpression.from_h(context, e) }
@@ -67,16 +71,8 @@ module Ruote
67
71
 
68
72
  @errors = errors.sort! { |a, b| a.fei.expid <=> b.fei.expid }
69
73
  @schedules = schedules.sort! { |a, b| a['owner'].sid <=> b['owner'].sid }
70
- end
71
-
72
- # Returns the expression at the root of the process instance.
73
- #
74
- def root_expression
75
-
76
- #@expressions.find { |e| e.fei.expid == '0' && e.fei.sub_wfid == nil }
77
- # vanilla implementation
78
74
 
79
- root_expressions.first
75
+ @root_expression = root_expressions.first
80
76
  end
81
77
 
82
78
  # Returns a list of all the expressions that have no parent expression.
@@ -87,7 +83,7 @@ module Ruote
87
83
  roots = @expressions.select { |e| e.h.parent_id == nil }
88
84
 
89
85
  roots = roots.inject({}) { |h, e|
90
- h["#{e.h.fei['expid']}__#{e.h.fei['sub_wfid']}"] = e; h
86
+ h["#{e.h.fei['expid']}__#{e.h.fei['subid']}"] = e; h
91
87
  }
92
88
 
93
89
  roots.keys.sort.collect { |k| roots[k] }
@@ -96,7 +92,7 @@ module Ruote
96
92
  # Given an expression id, returns the root (top ancestor) for its
97
93
  # expression.
98
94
  #
99
- def root_expression_for (fei)
95
+ def root_expression_for(fei)
100
96
 
101
97
  sfei = Ruote.sid(fei)
102
98
 
@@ -112,7 +108,7 @@ module Ruote
112
108
  #
113
109
  def variables
114
110
 
115
- root_expression.variables
111
+ @root_expression && @root_expression.variables
116
112
  end
117
113
 
118
114
  # Returns a hash fei => variable_hash containing all the variable bindings
@@ -131,7 +127,7 @@ module Ruote
131
127
  #
132
128
  def tags
133
129
 
134
- variables.select { |k, v| FlowExpressionId.is_a_fei?(v) }
130
+ Hash[variables.select { |k, v| FlowExpressionId.is_a_fei?(v) }]
135
131
  end
136
132
 
137
133
  # Returns a hash tagname => array of feis of all the tags set in the process
@@ -163,7 +159,8 @@ module Ruote
163
159
  #
164
160
  def definition_name
165
161
 
166
- root_expression.attribute('name') || root_expression.attribute_text
162
+ @root_expression && (
163
+ @root_expression.attribute('name') || @root_expression.attribute_text)
167
164
  end
168
165
 
169
166
  # For a process
@@ -177,7 +174,7 @@ module Ruote
177
174
  #
178
175
  def definition_revision
179
176
 
180
- root_expression.attribute('revision')
177
+ @root_expression && @root_expression.attribute('revision')
181
178
  end
182
179
 
183
180
  # Returns the 'position' of the process.
@@ -198,9 +195,16 @@ module Ruote
198
195
  def position
199
196
 
200
197
  workitems.collect { |wi|
198
+
201
199
  r = [ wi.fei.sid, wi.participant_name ]
202
- params = wi.fields['params'].dup
200
+
201
+ params = (wi.fields['params'] || {}).dup
203
202
  params.delete('ref')
203
+
204
+ if err = errors.find { |e| e.fei == wi.fei }
205
+ params['error'] = err.message
206
+ end
207
+
204
208
  r << params
205
209
  r
206
210
  }
@@ -245,14 +249,14 @@ module Ruote
245
249
  #
246
250
  def original_tree
247
251
 
248
- root_expression.original_tree
252
+ @root_expression && @root_expression.original_tree
249
253
  end
250
254
 
251
255
  # Returns a Time instance indicating when the process instance was launched.
252
256
  #
253
257
  def launched_time
254
258
 
255
- root_expression.created_time
259
+ @root_expression && @root_expression.created_time
256
260
  end
257
261
 
258
262
  def to_s
@@ -267,18 +271,33 @@ module Ruote
267
271
 
268
272
  def inspect
269
273
 
274
+ vars = variables rescue nil
275
+ avars = all_variables.inject({}) { |h, (k, v)| h[Ruote.sid(k)] = v; h }
276
+
270
277
  s = [ "== #{self.class} ==" ]
271
278
  s << " expressions : #{@expressions.size}"
272
279
  @expressions.each do |e|
273
- s << " #{e.fei.to_storage_id} : #{e}"
280
+ s << " #{e.fei.to_storage_id}"
281
+ s << " | #{e.name}"
282
+ s << " | #{e.attributes.inspect}"
283
+ s << " `-parent--> #{e.h.parent_id ? e.parent_id.to_storage_id : 'nil'}"
274
284
  end
285
+ s << " schedules : #{@schedules.size}"
286
+ s << " stored workitems : #{@stored_workitems.size}"
287
+ s << " variables : #{vars.inspect}"
288
+ s << " all_variables : #{avars.inspect}"
275
289
  s << " errors : #{@errors.size}"
276
290
  @errors.each do |e|
291
+ s << " ***"
277
292
  s << " #{e.fei.to_storage_id} :" if e.fei
278
- s << " #{e.inspect}"
293
+ s << " action : #{e.action}"
294
+ s << " message : #{e.message}"
295
+ s << " trace :"
296
+ e.trace.split("\n").each do |line|
297
+ s << " #{line}"
298
+ end
299
+ s << " fields : #{e.fields.inspect}"
279
300
  end
280
- s << " schedules : #{@schedules.size}"
281
- s << " stored workitems : #{@stored_workitems.size}"
282
301
 
283
302
  s.join("\n") + "\n"
284
303
  end
@@ -286,7 +305,7 @@ module Ruote
286
305
  # Returns a 'dot' representation of the process. A graph describing
287
306
  # the tree of flow expressions that compose the process.
288
307
  #
289
- def to_dot (opts={})
308
+ def to_dot(opts={})
290
309
 
291
310
  s = [ "digraph \"process wfid #{wfid}\" {" ]
292
311
  @expressions.each { |e| s.push(*e.send(:to_dot, opts)) }
@@ -347,7 +366,7 @@ module Ruote
347
366
 
348
367
  protected
349
368
 
350
- def original_tree_from_parent (e)
369
+ def original_tree_from_parent(e)
351
370
 
352
371
  parent = @expressions.find { |exp| exp.fei == e.parent_id }
353
372
 
@@ -355,14 +374,14 @@ module Ruote
355
374
  end
356
375
  end
357
376
 
358
- def self.decompose_tree (t, pos='0', h={})
377
+ def self.decompose_tree(t, pos='0', h={})
359
378
 
360
379
  h[pos] = t[0, 2]
361
380
  t[2].each_with_index { |c, i| decompose_tree(c, "#{pos}_#{i}", h) }
362
381
  h
363
382
  end
364
383
 
365
- def self.recompose_tree (h, pos='0')
384
+ def self.recompose_tree(h, pos='0')
366
385
 
367
386
  t = h[pos]
368
387
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@ module Ruote::Exp
37
37
 
38
38
  # TODO : :ignore_workitem / :disallow => 'workitem' thing ?
39
39
 
40
- def get_command (workitem)
40
+ def get_command(workitem)
41
41
 
42
42
  command, step = workitem['fields'].delete(F_COMMAND)
43
43
  command, step = lookup_attribute_command(workitem) unless command
@@ -57,30 +57,27 @@ module Ruote::Exp
57
57
  [ command, step ]
58
58
  end
59
59
 
60
- def set_command (workitem, command, step=nil)
60
+ def set_command(workitem, command, step=nil)
61
61
 
62
62
  workitem['fields'][F_COMMAND] = [ command, step ]
63
63
  end
64
64
 
65
- def lookup_attribute_command (workitem)
65
+ def lookup_attribute_command(workitem)
66
66
 
67
67
  lookup_att_com('if', workitem) || lookup_att_com('unless', workitem)
68
68
  end
69
69
 
70
- def lookup_att_com (dir, workitem)
70
+ def lookup_att_com(dir, workitem)
71
71
 
72
72
  ATT_COMMANDS.each do |com|
73
73
 
74
- c =
75
- attribute("#{com}_#{dir}", workitem) ||
76
- attribute("#{com}-#{dir}", workitem)
74
+ c = attribute("#{com}_#{dir}", workitem)
77
75
 
78
76
  next unless c
79
77
 
80
78
  c = Condition.true?(c)
81
79
 
82
- return [ com, nil ] \
83
- if (dir == 'if' && c) || (dir == 'unless' && ( ! c))
80
+ return [ com, nil ] if (dir == 'if' && c) || (dir == 'unless' && ( ! c))
84
81
  end
85
82
 
86
83
  nil
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -36,7 +36,7 @@ module Ruote::Exp
36
36
 
37
37
  include CommandMixin
38
38
 
39
- def reply (workitem)
39
+ def reply(workitem)
40
40
 
41
41
  workitem = h.command_workitem || workitem
42
42
  h.command_workitem = nil
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -25,12 +25,32 @@
25
25
 
26
26
  module Ruote::Exp
27
27
 
28
+ #
29
+ # A few helper methods for evaluating :if and :unless expression
30
+ # attributes in process definitions.
31
+ #
28
32
  module Condition
29
33
 
30
- SET_REGEX = /(\S*?)( +is)?( +not)?( +set)$/
31
- COMPARISON_REGEX = /(.*?) *(==|!=|>=|<=|>|<|=~) *(.*)/
34
+ #
35
+ # A runtime error for unusable comparison strings.
36
+ #
37
+ class ConditionError < RuntimeError
32
38
 
33
- def self.apply? (sif, sunless)
39
+ def initialize(code)
40
+ super(
41
+ "couldn't interpret >#{code}<, " +
42
+ "if it comes from a ${xx} construct, please use ${\"xx} or ${'yy}")
43
+ end
44
+ end
45
+
46
+ REGEXES = {
47
+ 'evl_set' => /^(.+?)( +is)?( +not)?( +set)$/,
48
+ 'evl_null' => /^(.+?)( +is)?( +not)?( +null)$/,
49
+ 'evl_empty' => /^(.+[\]}"'])( +is)?( +not)?( +empty)$/,
50
+ 'evl_in' => /^(.+?)( +is)?( +not)?( +in +)(\[.*\]|\{.*\})$/
51
+ }
52
+
53
+ def self.apply?(sif, sunless)
34
54
 
35
55
  return (true?(sif)) if sif
36
56
  return ( ! true?(sunless)) if sunless
@@ -38,77 +58,144 @@ module Ruote::Exp
38
58
  true
39
59
  end
40
60
 
41
- # TODO : rconditional
42
- # is it really necessary ? there is already ${r:xxx}
43
-
44
- def self.true? (conditional)
61
+ # Returns true if the given conditional string evaluates to true.
62
+ #
63
+ def self.true?(conditional)
45
64
 
46
- conditional = unescape(conditional)
65
+ conditional = unescape(conditional.to_s)
47
66
 
48
- if m = SET_REGEX.match(conditional)
49
- eval_is(m)
50
- elsif m = COMPARISON_REGEX.match(conditional)
51
- compare(m)
52
- else
53
- to_b(conditional)
67
+ REGEXES.each do |method, regex|
68
+ if m = regex.match(conditional)
69
+ return self.send(method, m)
70
+ end
54
71
  end
72
+
73
+ evl(conditional) ? true : false
74
+
75
+ rescue ArgumentError => ae
76
+
77
+ raise ConditionError.new(conditional)
78
+ end
79
+
80
+ # Evaluates the given [conditional] code string and returns the
81
+ # result.
82
+ #
83
+ # Note : this is not a full Ruby evaluation !
84
+ #
85
+ def self.eval(code)
86
+
87
+ evl(code)
88
+
89
+ rescue ArgumentError => ae
90
+
91
+ raise ConditionError.new(code)
55
92
  end
56
93
 
57
94
  protected
58
95
 
59
- def self.eval_is (match)
96
+ def self.parse(conditional)
60
97
 
61
- match = match[1..-2].select { |e| e != nil }
98
+ Rufus::TreeChecker.parse(conditional)
62
99
 
63
- negative = match.find { |m| m == ' not' }
100
+ rescue NoMethodError => nme
64
101
 
65
- first = match.first.strip
66
- is_set = first != '' && first != 'is'
102
+ raise NoMethodError.new(
103
+ "/!\\ please upgrade your rufus-treechecker gem /!\\"
104
+ )
67
105
 
68
- negative ? (not is_set) : is_set
106
+ rescue => e
107
+
108
+ [ :false ]
69
109
  end
70
110
 
71
- def self.unescape (s)
111
+ def self.unescape(s)
72
112
 
73
- s ? s.to_s.gsub('&amp;', '&').gsub('&gt;', '>').gsub('&lt;', '<') : nil
113
+ s.gsub('&amp;', '&').gsub('&gt;', '>').gsub('&lt;', '<')
74
114
  end
75
115
 
76
- def self.to_b (o)
116
+ COMPARATORS = %w[ == > < != >= <= ].collect { |c| c.to_sym }
77
117
 
78
- o = o.strip if o.is_a?(String)
118
+ def self.evl(tree)
79
119
 
80
- not(o == nil || o == false || o == 'false' || o == '')
81
- end
120
+ return evl(parse(tree)) if tree.is_a?(String)
121
+
122
+ return nil if tree == []
123
+
124
+ return tree.last if tree.first == :str
125
+ return tree.last if tree.first == :lit
126
+ return tree.last.to_s if tree.first == :const
127
+ return nil if tree == [ :nil ]
128
+ return true if tree == [ :true ]
129
+ return false if tree == [ :false ]
82
130
 
83
- def self.compare (m)
131
+ return ( ! evl(tree.last)) if tree.first == :not
84
132
 
85
- return (m[1].=~(Regexp.new(m[3])) != nil) if m[2] == '=~'
133
+ return evl(tree[1]) && evl(tree[2]) if tree[0] == :and
134
+ return evl(tree[1]) || evl(tree[2]) if tree[0] == :or
86
135
 
87
- a = narrow_to_f(m[1])
88
- b = narrow_to_f(m[3])
136
+ return tree[1..-1].collect { |e| evl(e) } if tree[0] == :array
137
+ return Hash.[](*tree[1..-1].collect { |e| evl(e) }) if tree[0] == :hash
89
138
 
90
- if a.class != b.class
91
- a = m[1]
92
- b = m[3]
139
+ if tree[0] == :match3
140
+ return evl(tree[2]) =~ evl(tree[1])
93
141
  end
142
+ if tree[0] == :call && tree[2] == :=~
143
+ return evl(tree[1]) =~ Regexp.new(evl(tree.last.last).to_s)
144
+ end
145
+
146
+ if tree[0] == :call && COMPARATORS.include?(tree[2])
147
+ return evl(tree[1]).send(tree[2], evl(tree.last.last))
148
+ end
149
+
150
+ return flatten(tree) if tree[0] == :call
151
+
152
+ raise ArgumentError
153
+
154
+ #require 'ruby2ruby'
155
+ #Ruby2Ruby.new.process(Sexp.from_array(tree))
156
+ # returns the raw Ruby as a String
157
+ # it's nice but "Loan/Grant" becomes "(Loan / Grant)"
158
+ end
159
+
160
+ KEYWORDS = %w[ call const arglist ].collect { |w| w.to_sym }
161
+
162
+ def self.flatten(tree)
94
163
 
95
- #a.send(m[2], b)
96
- # ruby 1.8.x doesn't like that one
164
+ (tree.flatten - KEYWORDS).collect { |e| e.nil? ? ' ' : e.to_s }.join.strip
165
+ end
166
+
167
+ def self.evl_set(match)
168
+
169
+ set = evl(match[1])
170
+ set = set != nil && set != ''
171
+ set = false if match[1].match(/is$/) && match[2].nil?
172
+
173
+ match[3].nil? ? set : ( ! set)
174
+ end
97
175
 
98
- a = strip(a)
99
- b = strip(b)
176
+ def self.evl_empty(match)
177
+
178
+ object = evl(match[1])
179
+
180
+ empty = if object.respond_to?(:empty?)
181
+ object.empty?
182
+ elsif object.nil?
183
+ true
184
+ else
185
+ false
186
+ end
100
187
 
101
- m[2] == '!=' ? ( ! a.send('==', b)) : a.send(m[2], b)
188
+ ( ! match[3].nil? ^ empty)
102
189
  end
103
190
 
104
- def self.narrow_to_f (s)
191
+ def self.evl_null(match)
105
192
 
106
- Float(s) rescue s
193
+ ( ! match[3].nil? ^ evl(match[1]).nil?)
107
194
  end
108
195
 
109
- def self.strip (s)
196
+ def self.evl_in(match)
110
197
 
111
- s.respond_to?(:strip) ? s.strip : s
198
+ ( ! match[3].nil? ^ evl(match[5]).include?(evl(match[1]))) rescue false
112
199
  end
113
200
  end
114
201
  end