ruote 2.1.11 → 2.2.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.
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