ruote 2.2.0 → 2.3.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 (305) hide show
  1. data/CHANGELOG.txt +166 -1
  2. data/CREDITS.txt +36 -17
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +1 -7
  5. data/Rakefile +38 -29
  6. data/TODO.txt +93 -52
  7. data/lib/ruote-fs.rb +3 -0
  8. data/lib/ruote.rb +5 -1
  9. data/lib/ruote/context.rb +140 -35
  10. data/lib/ruote/dashboard.rb +1247 -0
  11. data/lib/ruote/{engine → dboard}/process_error.rb +22 -2
  12. data/lib/ruote/dboard/process_status.rb +587 -0
  13. data/lib/ruote/engine.rb +6 -871
  14. data/lib/ruote/exp/command.rb +7 -2
  15. data/lib/ruote/exp/commanded.rb +2 -2
  16. data/lib/ruote/exp/condition.rb +38 -13
  17. data/lib/ruote/exp/fe_add_branches.rb +1 -1
  18. data/lib/ruote/exp/fe_apply.rb +1 -1
  19. data/lib/ruote/exp/fe_await.rb +357 -0
  20. data/lib/ruote/exp/fe_cancel_process.rb +17 -3
  21. data/lib/ruote/exp/fe_command.rb +8 -4
  22. data/lib/ruote/exp/fe_concurrence.rb +218 -18
  23. data/lib/ruote/exp/fe_concurrent_iterator.rb +71 -10
  24. data/lib/ruote/exp/fe_cron.rb +3 -10
  25. data/lib/ruote/exp/fe_cursor.rb +14 -4
  26. data/lib/ruote/exp/fe_define.rb +3 -1
  27. data/lib/ruote/exp/fe_echo.rb +1 -1
  28. data/lib/ruote/exp/fe_equals.rb +1 -1
  29. data/lib/ruote/exp/fe_error.rb +1 -1
  30. data/lib/ruote/exp/fe_filter.rb +163 -4
  31. data/lib/ruote/exp/fe_forget.rb +21 -4
  32. data/lib/ruote/exp/fe_given.rb +1 -1
  33. data/lib/ruote/exp/fe_if.rb +1 -1
  34. data/lib/ruote/exp/fe_inc.rb +102 -35
  35. data/lib/ruote/exp/fe_iterator.rb +47 -12
  36. data/lib/ruote/exp/fe_listen.rb +96 -11
  37. data/lib/ruote/exp/fe_lose.rb +31 -4
  38. data/lib/ruote/exp/fe_noop.rb +1 -1
  39. data/lib/ruote/exp/fe_on_error.rb +109 -0
  40. data/lib/ruote/exp/fe_once.rb +10 -19
  41. data/lib/ruote/exp/fe_participant.rb +90 -28
  42. data/lib/ruote/exp/fe_read.rb +69 -0
  43. data/lib/ruote/exp/fe_redo.rb +3 -2
  44. data/lib/ruote/exp/fe_ref.rb +57 -27
  45. data/lib/ruote/exp/fe_registerp.rb +1 -3
  46. data/lib/ruote/exp/fe_reserve.rb +1 -1
  47. data/lib/ruote/exp/fe_restore.rb +6 -6
  48. data/lib/ruote/exp/fe_save.rb +12 -19
  49. data/lib/ruote/exp/fe_sequence.rb +38 -2
  50. data/lib/ruote/exp/fe_set.rb +143 -40
  51. data/lib/ruote/exp/{fe_let.rb → fe_stall.rb} +7 -38
  52. data/lib/ruote/exp/fe_subprocess.rb +8 -2
  53. data/lib/ruote/exp/fe_that.rb +1 -1
  54. data/lib/ruote/exp/fe_undo.rb +40 -4
  55. data/lib/ruote/exp/fe_unregisterp.rb +1 -3
  56. data/lib/ruote/exp/fe_wait.rb +12 -25
  57. data/lib/ruote/exp/{flowexpression.rb → flow_expression.rb} +375 -229
  58. data/lib/ruote/exp/iterator.rb +2 -2
  59. data/lib/ruote/exp/merge.rb +78 -17
  60. data/lib/ruote/exp/ro_attributes.rb +46 -36
  61. data/lib/ruote/exp/ro_filters.rb +34 -8
  62. data/lib/ruote/exp/ro_on_x.rb +431 -0
  63. data/lib/ruote/exp/ro_persist.rb +19 -7
  64. data/lib/ruote/exp/ro_timers.rb +123 -0
  65. data/lib/ruote/exp/ro_variables.rb +90 -29
  66. data/lib/ruote/fei.rb +57 -3
  67. data/lib/ruote/fs.rb +3 -0
  68. data/lib/ruote/id/mnemo_wfid_generator.rb +30 -7
  69. data/lib/ruote/id/wfid_generator.rb +17 -38
  70. data/lib/ruote/log/default_history.rb +23 -9
  71. data/lib/ruote/log/fancy_printing.rb +265 -0
  72. data/lib/ruote/log/storage_history.rb +23 -13
  73. data/lib/ruote/log/wait_logger.rb +224 -17
  74. data/lib/ruote/observer.rb +82 -0
  75. data/lib/ruote/part/block_participant.rb +65 -28
  76. data/lib/ruote/part/code_participant.rb +81 -0
  77. data/lib/ruote/part/engine_participant.rb +7 -2
  78. data/lib/ruote/part/local_participant.rb +221 -21
  79. data/lib/ruote/part/no_op_participant.rb +1 -1
  80. data/lib/ruote/part/null_participant.rb +1 -1
  81. data/lib/ruote/part/participant.rb +50 -0
  82. data/lib/ruote/part/rev_participant.rb +178 -0
  83. data/lib/ruote/part/smtp_participant.rb +2 -2
  84. data/lib/ruote/part/storage_participant.rb +228 -60
  85. data/lib/ruote/part/template.rb +1 -1
  86. data/lib/ruote/participant.rb +2 -0
  87. data/lib/ruote/reader.rb +205 -68
  88. data/lib/ruote/reader/json.rb +49 -0
  89. data/lib/ruote/reader/radial.rb +303 -0
  90. data/lib/ruote/reader/ruby_dsl.rb +44 -9
  91. data/lib/ruote/reader/xml.rb +11 -8
  92. data/lib/ruote/receiver/base.rb +98 -45
  93. data/lib/ruote/storage/base.rb +104 -35
  94. data/lib/ruote/storage/composite_storage.rb +50 -60
  95. data/lib/ruote/storage/fs_storage.rb +25 -34
  96. data/lib/ruote/storage/hash_storage.rb +38 -36
  97. data/lib/ruote/svc/dispatch_pool.rb +104 -35
  98. data/lib/ruote/svc/dollar_sub.rb +10 -8
  99. data/lib/ruote/svc/error_handler.rb +108 -52
  100. data/lib/ruote/svc/expression_map.rb +3 -3
  101. data/lib/ruote/svc/participant_list.rb +160 -55
  102. data/lib/ruote/svc/tracker.rb +31 -31
  103. data/lib/ruote/svc/treechecker.rb +28 -16
  104. data/lib/ruote/tree_dot.rb +1 -1
  105. data/lib/ruote/util/deep.rb +143 -0
  106. data/lib/ruote/util/filter.rb +125 -18
  107. data/lib/ruote/util/hashdot.rb +15 -13
  108. data/lib/ruote/util/look.rb +1 -1
  109. data/lib/ruote/util/lookup.rb +60 -22
  110. data/lib/ruote/util/misc.rb +63 -18
  111. data/lib/ruote/util/mpatch.rb +53 -0
  112. data/lib/ruote/util/ometa.rb +1 -2
  113. data/lib/ruote/util/process_observer.rb +177 -0
  114. data/lib/ruote/util/subprocess.rb +1 -1
  115. data/lib/ruote/util/time.rb +2 -2
  116. data/lib/ruote/util/tree.rb +64 -2
  117. data/lib/ruote/version.rb +3 -2
  118. data/lib/ruote/worker.rb +421 -92
  119. data/lib/ruote/workitem.rb +157 -22
  120. data/ruote.gemspec +15 -9
  121. data/test/bm/ci.rb +0 -2
  122. data/test/bm/ici.rb +0 -2
  123. data/test/bm/load_26c.rb +0 -3
  124. data/test/bm/mega.rb +0 -2
  125. data/test/functional/base.rb +57 -43
  126. data/test/functional/concurrent_base.rb +16 -13
  127. data/test/functional/ct_0_concurrence.rb +7 -11
  128. data/test/functional/ct_1_iterator.rb +9 -11
  129. data/test/functional/ct_2_cancel.rb +28 -17
  130. data/test/functional/eft_0_flow_expression.rb +35 -0
  131. data/test/functional/eft_10_cancel_process.rb +1 -1
  132. data/test/functional/eft_11_wait.rb +13 -13
  133. data/test/functional/eft_12_listen.rb +199 -66
  134. data/test/functional/eft_13_iterator.rb +95 -29
  135. data/test/functional/eft_14_cursor.rb +74 -24
  136. data/test/functional/eft_15_loop.rb +7 -7
  137. data/test/functional/eft_16_if.rb +1 -1
  138. data/test/functional/eft_17_equals.rb +1 -1
  139. data/test/functional/eft_18_concurrent_iterator.rb +156 -68
  140. data/test/functional/eft_19_reserve.rb +15 -15
  141. data/test/functional/eft_1_echo.rb +1 -1
  142. data/test/functional/eft_20_save.rb +51 -9
  143. data/test/functional/eft_21_restore.rb +1 -1
  144. data/test/functional/eft_22_noop.rb +1 -1
  145. data/test/functional/eft_23_apply.rb +1 -1
  146. data/test/functional/eft_24_add_branches.rb +7 -8
  147. data/test/functional/eft_25_command.rb +1 -1
  148. data/test/functional/eft_26_error.rb +11 -11
  149. data/test/functional/eft_27_inc.rb +111 -67
  150. data/test/functional/eft_28_once.rb +16 -16
  151. data/test/functional/eft_29_cron.rb +9 -9
  152. data/test/functional/eft_2_sequence.rb +23 -4
  153. data/test/functional/eft_30_ref.rb +36 -24
  154. data/test/functional/eft_31_registerp.rb +24 -24
  155. data/test/functional/eft_32_lose.rb +46 -20
  156. data/test/functional/eft_34_given.rb +1 -1
  157. data/test/functional/eft_35_filter.rb +161 -7
  158. data/test/functional/eft_36_read.rb +97 -0
  159. data/test/functional/{eft_0_process_definition.rb → eft_37_process_definition.rb} +4 -4
  160. data/test/functional/eft_38_on_error.rb +195 -0
  161. data/test/functional/eft_39_stall.rb +35 -0
  162. data/test/functional/eft_3_participant.rb +77 -22
  163. data/test/functional/eft_40_await.rb +297 -0
  164. data/test/functional/eft_4_set.rb +110 -11
  165. data/test/functional/eft_5_subprocess.rb +27 -5
  166. data/test/functional/eft_6_concurrence.rb +299 -60
  167. data/test/functional/eft_7_forget.rb +24 -22
  168. data/test/functional/eft_8_undo.rb +52 -15
  169. data/test/functional/eft_9_redo.rb +18 -20
  170. data/test/functional/ft_0_worker.rb +122 -13
  171. data/test/functional/ft_10_dollar.rb +77 -16
  172. data/test/functional/ft_11_recursion.rb +9 -9
  173. data/test/functional/ft_12_launchitem.rb +7 -9
  174. data/test/functional/ft_13_variables.rb +125 -22
  175. data/test/functional/ft_14_re_apply.rb +112 -56
  176. data/test/functional/ft_15_timeout.rb +64 -33
  177. data/test/functional/ft_16_participant_params.rb +59 -6
  178. data/test/functional/ft_17_conditional.rb +68 -2
  179. data/test/functional/ft_18_kill.rb +48 -30
  180. data/test/functional/ft_19_participant_code.rb +67 -0
  181. data/test/functional/ft_1_process_status.rb +222 -150
  182. data/test/functional/ft_20_storage_participant.rb +445 -44
  183. data/test/functional/ft_21_forget.rb +21 -26
  184. data/test/functional/ft_22_process_definitions.rb +8 -6
  185. data/test/functional/ft_23_load_defs.rb +29 -5
  186. data/test/functional/ft_24_block_participant.rb +199 -20
  187. data/test/functional/ft_25_receiver.rb +98 -46
  188. data/test/functional/ft_26_participant_rtimeout.rb +34 -26
  189. data/test/functional/ft_27_var_indirection.rb +40 -5
  190. data/test/functional/ft_28_null_noop_participants.rb +5 -5
  191. data/test/functional/ft_29_part_template.rb +2 -2
  192. data/test/functional/ft_2_errors.rb +106 -74
  193. data/test/functional/ft_30_smtp_participant.rb +7 -7
  194. data/test/functional/ft_31_part_blocking.rb +11 -11
  195. data/test/functional/ft_32_scope.rb +50 -0
  196. data/test/functional/ft_33_participant_subprocess_priority.rb +3 -3
  197. data/test/functional/ft_34_cursor_rewind.rb +14 -14
  198. data/test/functional/ft_35_add_service.rb +67 -9
  199. data/test/functional/ft_36_storage_history.rb +92 -24
  200. data/test/functional/ft_37_default_history.rb +35 -23
  201. data/test/functional/ft_38_participant_more.rb +189 -32
  202. data/test/functional/ft_39_wait_for.rb +25 -25
  203. data/test/functional/ft_3_participant_registration.rb +235 -107
  204. data/test/functional/ft_40_wait_logger.rb +105 -18
  205. data/test/functional/ft_41_participants.rb +13 -12
  206. data/test/functional/ft_42_storage_copy.rb +12 -12
  207. data/test/functional/ft_43_participant_on_reply.rb +85 -11
  208. data/test/functional/ft_44_var_participant.rb +5 -5
  209. data/test/functional/ft_45_participant_accept.rb +3 -3
  210. data/test/functional/ft_46_launch_single.rb +17 -17
  211. data/test/functional/ft_47_wfids.rb +41 -0
  212. data/test/functional/ft_48_lose.rb +19 -25
  213. data/test/functional/ft_49_engine_on_error.rb +54 -70
  214. data/test/functional/ft_4_cancel.rb +84 -26
  215. data/test/functional/ft_50_engine_config.rb +4 -4
  216. data/test/functional/ft_51_misc.rb +12 -12
  217. data/test/functional/ft_52_case.rb +17 -17
  218. data/test/functional/ft_53_engine_on_terminate.rb +18 -21
  219. data/test/functional/ft_54_patterns.rb +18 -16
  220. data/test/functional/ft_55_engine_participant.rb +55 -55
  221. data/test/functional/ft_56_filter_attribute.rb +90 -52
  222. data/test/functional/ft_57_rev_participant.rb +252 -0
  223. data/test/functional/ft_58_workitem.rb +150 -0
  224. data/test/functional/ft_59_pause.rb +329 -0
  225. data/test/functional/ft_5_on_error.rb +430 -77
  226. data/test/functional/ft_60_code_participant.rb +65 -0
  227. data/test/functional/ft_61_trailing_fields.rb +34 -0
  228. data/test/functional/ft_62_exp_name_and_dollar_substitution.rb +35 -0
  229. data/test/functional/ft_63_participants_221.rb +458 -0
  230. data/test/functional/ft_64_stash.rb +41 -0
  231. data/test/functional/ft_65_timers.rb +313 -0
  232. data/test/functional/ft_66_flank.rb +133 -0
  233. data/test/functional/ft_67_radial_misc.rb +34 -0
  234. data/test/functional/ft_68_reput.rb +72 -0
  235. data/test/functional/ft_69_worker_info.rb +56 -0
  236. data/test/functional/ft_6_on_cancel.rb +189 -36
  237. data/test/functional/ft_70_take_and_discard_attributes.rb +94 -0
  238. data/test/functional/ft_71_retries.rb +144 -0
  239. data/test/functional/ft_72_on_terminate.rb +60 -0
  240. data/test/functional/ft_73_raise_msg.rb +107 -0
  241. data/test/functional/ft_74_respark.rb +106 -0
  242. data/test/functional/ft_75_context.rb +66 -0
  243. data/test/functional/ft_76_observer.rb +53 -0
  244. data/test/functional/ft_77_process_observer.rb +157 -0
  245. data/test/functional/ft_78_part_participant.rb +37 -0
  246. data/test/functional/ft_7_tags.rb +238 -50
  247. data/test/functional/ft_8_participant_consumption.rb +27 -21
  248. data/test/functional/ft_9_subprocesses.rb +48 -18
  249. data/test/functional/restart_base.rb +4 -6
  250. data/test/functional/rt_0_wait.rb +10 -10
  251. data/test/functional/rt_1_listen.rb +6 -6
  252. data/test/functional/rt_2_errors.rb +12 -12
  253. data/test/functional/rt_3_once.rb +17 -12
  254. data/test/functional/rt_4_cron.rb +17 -17
  255. data/test/functional/rt_5_timeout.rb +13 -13
  256. data/test/functional/signals.rb +103 -0
  257. data/test/functional/storage.rb +730 -0
  258. data/test/functional/storage_helper.rb +48 -35
  259. data/test/functional/test.rb +6 -2
  260. data/test/misc/idle.rb +21 -0
  261. data/test/misc/light.rb +29 -0
  262. data/test/path_helper.rb +1 -1
  263. data/test/test.rb +2 -5
  264. data/test/test_helper.rb +13 -0
  265. data/test/unit/test.rb +1 -4
  266. data/test/unit/ut_0_ruby_reader.rb +25 -9
  267. data/test/unit/ut_10_participants.rb +47 -0
  268. data/test/unit/ut_11_lookup.rb +59 -2
  269. data/test/unit/ut_12_wait_logger.rb +123 -0
  270. data/test/unit/ut_14_is_uri.rb +1 -1
  271. data/test/unit/ut_15_util.rb +1 -1
  272. data/test/unit/ut_16_reader.rb +136 -14
  273. data/test/unit/ut_17_merge.rb +155 -0
  274. data/test/unit/ut_19_part_template.rb +1 -1
  275. data/test/unit/ut_1_fei.rb +11 -2
  276. data/test/unit/ut_20_composite_storage.rb +27 -1
  277. data/test/unit/{ut_21_participant_list.rb → ut_21_svc_participant_list.rb} +2 -3
  278. data/test/unit/ut_22_filter.rb +231 -10
  279. data/test/unit/ut_23_svc_tracker.rb +48 -0
  280. data/test/unit/ut_24_radial_reader.rb +458 -0
  281. data/test/unit/ut_25_process_status.rb +143 -0
  282. data/test/unit/ut_26_deep.rb +131 -0
  283. data/test/unit/ut_2_dashboard.rb +114 -0
  284. data/test/unit/ut_3_worker.rb +54 -0
  285. data/test/unit/ut_4_expmap.rb +1 -1
  286. data/test/unit/ut_5_tree.rb +23 -23
  287. data/test/unit/ut_6_condition.rb +71 -29
  288. data/test/unit/ut_7_workitem.rb +18 -4
  289. data/test/unit/ut_8_tree_to_dot.rb +1 -1
  290. data/test/unit/ut_9_xml_reader.rb +1 -1
  291. metadata +142 -63
  292. data/jruby_issue.txt +0 -32
  293. data/lib/ruote/engine/process_status.rb +0 -403
  294. data/lib/ruote/log/pretty.rb +0 -165
  295. data/lib/ruote/log/test_logger.rb +0 -204
  296. data/lib/ruote/util/serializer.rb +0 -103
  297. data/phil.txt +0 -14
  298. data/test/functional/eft_33_let.rb +0 -31
  299. data/test/functional/ft_19_alias.rb +0 -33
  300. data/test/functional/ft_47_wfid_generator.rb +0 -54
  301. data/test/unit/storage.rb +0 -403
  302. data/test/unit/storages.rb +0 -37
  303. data/test/unit/ut_13_serializer.rb +0 -65
  304. data/test/unit/ut_18_engine.rb +0 -47
  305. data/test/unit/ut_3_wait_logger.rb +0 -39
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, 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,28 +37,18 @@ module Ruote
37
37
  def initialize(context)
38
38
 
39
39
  @context = context
40
-
41
- if @context.worker
42
- #
43
- # this is a worker context, DO log
44
- #
45
- @context.worker.subscribe(:all, self)
46
- #else
47
- #
48
- # this is not a worker context, no notifications. BUT
49
- # honour calls to add_tracker/remove_tracker
50
- #
51
- end
52
40
  end
53
41
 
54
- # The worker passes all the messages it has to process to the tracker via
55
- # this method.
42
+ # The context calls this method for each successfully processed msg
43
+ # in the worker.
56
44
  #
57
- def notify(msg)
45
+ def on_msg(message)
46
+
47
+ m_error = message['error']
48
+ m_wfid = message['wfid'] || (message['fei']['wfid'] rescue nil)
49
+ m_action = message['action']
58
50
 
59
- m_error = msg['error']
60
- m_wfid = msg['wfid'] || (msg['fei']['wfid'] rescue nil)
61
- m_action = msg['action']
51
+ msg = m_action == 'error_intercepted' ? message['msg'] : message
62
52
 
63
53
  @context.storage.get_trackers['trackers'].each do |tracker_id, tracker|
64
54
 
@@ -70,9 +60,7 @@ module Ruote
70
60
  next if t_wfid && t_wfid != m_wfid
71
61
  next if t_action && t_action != m_action
72
62
 
73
- next unless does_match?(msg, tracker['conditions'])
74
-
75
- msg = msg['msg'] if m_action == 'error_intercepted'
63
+ next unless does_match?(message, tracker['conditions'])
76
64
 
77
65
  if tracker_id == 'on_error' || tracker_id == 'on_terminate'
78
66
 
@@ -84,7 +72,7 @@ module Ruote
84
72
 
85
73
  # prepare and emit/put 'reaction' message
86
74
 
87
- m = tracker['msg']
75
+ m = Ruote.fulldup(tracker['msg'])
88
76
 
89
77
  action = m.delete('action')
90
78
 
@@ -93,7 +81,9 @@ module Ruote
93
81
 
94
82
  m['workitem'] = msg['workitem'] if m['workitem'] == 'replace'
95
83
 
96
- if tracker_id == 'on_error' && m_action == 'error_intercepted'
84
+ if t_action == 'error_intercepted'
85
+ m['workitem']['fields']['__error__'] = m_error
86
+ elsif tracker_id == 'on_error' && m_action == 'error_intercepted'
97
87
  m['workitem']['fields']['__error__'] = m_error
98
88
  elsif tracker_id == 'on_terminate' && m_action == 'terminated'
99
89
  m['workitem']['fields']['__terminate__'] = { 'wfid' => m_wfid }
@@ -112,6 +102,9 @@ module Ruote
112
102
  #
113
103
  def add_tracker(wfid, action, id, conditions, msg)
114
104
 
105
+ conditions =
106
+ conditions && conditions.remap { |(k, v), h| h[k] = Array(v) }
107
+
115
108
  doc = @context.storage.get_trackers
116
109
 
117
110
  doc['trackers'][id] =
@@ -123,7 +116,7 @@ module Ruote
123
116
 
124
117
  r = @context.storage.put(doc)
125
118
 
126
- add_tracker(wfid, action, id, msg) if r
119
+ add_tracker(wfid, action, id, conditions, msg) if r
127
120
  # the put failed, have to redo the work
128
121
  end
129
122
 
@@ -150,13 +143,20 @@ module Ruote
150
143
 
151
144
  conditions.each do |k, v|
152
145
 
153
- val = msg[k]
154
- v = Ruote.regex_or_s(v)
146
+ return false unless Array(v).find do |vv|
147
+
148
+ # the Array(v) is for backward compatibility, although newer
149
+ # track conditions are already stored as arrays.
150
+
151
+ vv = Ruote.regex_or_s(vv)
152
+
153
+ val = case k
154
+ when 'class' then msg['error']['class']
155
+ when 'message' then msg['error']['message']
156
+ else msg[k]
157
+ end
155
158
 
156
- if v.is_a?(String)
157
- return false unless val && v == val
158
- else
159
- return false unless val && v.match(val)
159
+ val && (vv.is_a?(String) ? (vv == val) : vv.match(val))
160
160
  end
161
161
  end
162
162
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, 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
37
37
 
38
38
  def initialize(context)
39
39
 
40
- (context['use_ruby_treechecker'] == false) and return
40
+ return if context['use_ruby_treechecker'] == false
41
41
 
42
42
  checker = Rufus::TreeChecker.new do
43
43
 
@@ -50,7 +50,6 @@ module Ruote
50
50
 
51
51
  #exclude_raise # no raise or throw
52
52
 
53
- exclude_def # no method definition
54
53
  exclude_eval # no eval, module_eval or instance_eval
55
54
  exclude_backquotes # no `rm -fR the/kitchen/sink`
56
55
  exclude_alias # no alias or aliast_method
@@ -70,18 +69,16 @@ module Ruote
70
69
  exclude_call_to :instance_variable_get, :instance_variable_set
71
70
  end
72
71
 
73
- # the checker used when reading process definitions
74
-
75
- @def_checker = checker.clone # and not dup
76
- @def_checker.add_rules do
77
- exclude_raise # no raise or throw
72
+ stricter_checker = checker.clone
73
+ stricter_checker.add_rules do
74
+ exclude_def # no method definition
75
+ exclude_raise # no raise or throw
78
76
  end
79
- @def_checker.freeze
80
77
 
81
- # the checker used when dealing with BlockParticipant code
78
+ # the checker used when reading process definitions
82
79
 
83
- @blo_checker = checker.clone # and not dup
84
- @blo_checker.freeze
80
+ @def_checker = stricter_checker.clone # and not dup
81
+ @def_checker.freeze
85
82
 
86
83
  ## the checker used when dealing with conditionals
87
84
  #
@@ -100,12 +97,22 @@ module Ruote
100
97
 
101
98
  # the checker used when dealing with code in $(ruby:xxx}
102
99
 
103
- @dol_checker = checker.clone # and not dup
104
- @dol_checker.add_rules do
105
- exclude_raise # no raise or throw
106
- end
100
+ @dol_checker = stricter_checker.clone # and not dup
107
101
  @dol_checker.freeze
108
102
 
103
+ # the checker used when dealing with BlockParticipant code
104
+
105
+ @blo_checker = checker.clone # and not dup
106
+ @blo_checker.add_rules do
107
+ exclude_def # no method definition
108
+ end
109
+ @blo_checker.freeze
110
+
111
+ # the checker used for CodeParticipant
112
+
113
+ @cod_checker = checker.clone # and not dup
114
+ @cod_checker.freeze
115
+
109
116
  freeze
110
117
  # preventing further modifications
111
118
  end
@@ -124,6 +131,11 @@ module Ruote
124
131
 
125
132
  @dol_checker.check(ruby_code) if @dol_checker
126
133
  end
134
+
135
+ def code_check(ruby_code)
136
+
137
+ @cod_checker.check(ruby_code) if @cod_checker
138
+ end
127
139
  end
128
140
  end
129
141
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, 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
@@ -0,0 +1,143 @@
1
+ #--
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@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
+
26
+ module Ruote
27
+
28
+ #--
29
+ # methods that go deep inside a nested structures of arrays and hashes
30
+ # (JSON output maybe).
31
+ #++
32
+
33
+ # Given a hash and a key, deletes all the entries with that key, in child
34
+ # hashes too.
35
+ #
36
+ # Note: this method is not related to the "dot notation" methods in this
37
+ # lookup.rb file.
38
+ #
39
+ # == example
40
+ #
41
+ # h = { 'a' => 1, 'b' => { 'a' => 2 } }
42
+ # Ruote.deep_delete(h, 'a')
43
+ # # => { 'b' => {} }
44
+ #
45
+ def self.deep_delete(h, key)
46
+
47
+ h.delete(key)
48
+
49
+ h.each { |k, v| deep_delete(v, key) if v.is_a?(Hash) }
50
+ end
51
+
52
+ class << self
53
+ alias delete_all deep_delete
54
+ end
55
+
56
+ # Dives into a nested structure of hashes and arrays to find match hash keys.
57
+ #
58
+ # The method expects a block with 3 or 4 arguments.
59
+ #
60
+ # 3 arguments: collection, key and value
61
+ # 4 arguments: parent collection, collection, key and value
62
+ #
63
+ # Warning: .deep_mutate forces hash keys to be strings. It's a JSON world.
64
+ #
65
+ # == example
66
+ #
67
+ # h = {
68
+ # 'a' => 0,
69
+ # 'b' => 1,
70
+ # 'c' => { 'a' => 2, 'b' => { 'a' => 3 } },
71
+ # 'd' => [ { 'a' => 0 }, { 'b' => 4 } ] }
72
+ #
73
+ # Ruote.deep_mutate(h, 'a') do |coll, k, v|
74
+ # coll['a'] = 10
75
+ # end
76
+ #
77
+ # h # =>
78
+ # { 'a' => 10,
79
+ # 'b' => 1,
80
+ # 'c' => { 'a' => 10, 'b' => { 'a' => 10 } },
81
+ # 'd' => [ { 'a' => 10 }, { 'b' => 4 } ] }
82
+ #
83
+ # == variations
84
+ #
85
+ # Instead of a single key, it's OK to pass an array of keys:
86
+ #
87
+ # Ruote.deep_mutate(a, [ 'a', 'b' ]) do |coll, k, v|
88
+ # # ...
89
+ # end
90
+ #
91
+ # Regular expressions are made to match:
92
+ #
93
+ # Ruote.deep_mutate(a, [ 'a', /^a\./ ]) do |coll, k, v|
94
+ # # ...
95
+ # end
96
+ #
97
+ # A single regular expression is OK:
98
+ #
99
+ # Ruote.deep_mutate(a, /^user\./) do |coll, k, v|
100
+ # # ...
101
+ # end
102
+ #
103
+ def self.deep_mutate(coll, key_or_keys, parent=nil, &block)
104
+
105
+ keys = key_or_keys.is_a?(Array) ? key_or_keys : [ key_or_keys ]
106
+
107
+ if coll.is_a?(Hash)
108
+
109
+ coll.dup.each do |k, v|
110
+
111
+ # ensure that all keys are strings
112
+
113
+ unless k.is_a?(String)
114
+ coll.delete(k)
115
+ k = k.to_s
116
+ coll[k] = v
117
+ end
118
+
119
+ # call the mutation blocks for each match
120
+
121
+ if keys.find { |kk| kk.is_a?(Regexp) ? kk.match(k) : kk == k }
122
+
123
+ if block.arity > 3
124
+ block.call(parent, coll, k, v)
125
+ else
126
+ block.call(coll, k, v)
127
+ end
128
+
129
+ elsif v.is_a?(Array) || v.is_a?(Hash)
130
+
131
+ deep_mutate(v, keys, coll, &block)
132
+ end
133
+ end
134
+
135
+ elsif coll.is_a?(Array)
136
+
137
+ coll.each { |e| deep_mutate(e, keys, coll, &block) }
138
+
139
+ #else # nothing
140
+ end
141
+ end
142
+ end
143
+
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, 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
@@ -23,7 +23,6 @@
23
23
  #++
24
24
 
25
25
  require 'ruote/util/misc'
26
- require 'ruote/util/lookup'
27
26
 
28
27
 
29
28
  module Ruote
@@ -52,6 +51,50 @@ module Ruote
52
51
  "not a filter : #{filter}"
53
52
  ) unless filter.is_a?(Array)
54
53
 
54
+ filters = or_split(filter)
55
+
56
+ result = nil
57
+
58
+ filters.each do |fl|
59
+
60
+ result = begin
61
+ do_filter(fl, hash, options)
62
+ rescue ValidationError => err
63
+ err
64
+ end
65
+
66
+ return result if result.is_a?(Hash)
67
+ # success
68
+ end
69
+
70
+ raise(result) if result.is_a?(ValidationError)
71
+
72
+ result
73
+ end
74
+
75
+ # Used by Ruote.filter
76
+ #
77
+ def self.or_split(filter)
78
+
79
+ return filter if filter.first.is_a?(Array)
80
+ return [ filter ] if filter.empty? or ( ! filter.include?('or'))
81
+
82
+ # [ {}, 'or', {}, {}, 'or', {} ]
83
+
84
+ filter.inject([ [] ]) do |result, fl|
85
+ if fl.is_a?(Hash)
86
+ result.last << fl
87
+ else
88
+ result << []
89
+ end
90
+ result
91
+ end
92
+ end
93
+
94
+ # Used by Ruote.filter
95
+ #
96
+ def self.do_filter(filter, hash, options)
97
+
55
98
  hash = Rufus::Json.dup(hash)
56
99
 
57
100
  hash['~'] = Rufus::Json.dup(hash)
@@ -64,6 +107,7 @@ module Ruote
64
107
 
65
108
  hash.delete('~')
66
109
  hash.delete('~~')
110
+ hash.delete('~~~')
67
111
  # remove the 'originals'
68
112
 
69
113
  if deviations.empty?
@@ -82,10 +126,13 @@ module Ruote
82
126
  class RuleSession
83
127
 
84
128
  SKIP = %w[ and or fields field f ]
129
+ BOOLEANS = %w[ and or ]
85
130
  NUMBER_CLASSES = [ Fixnum, Float ]
86
131
  BOOLEAN_CLASSES = [ TrueClass, FalseClass ]
87
132
  TILDE = /^~/
88
133
  RTILDE = /^\^~/
134
+ COMMA_SPLIT = / *, */
135
+ PIPE_SPLIT = / *\| */
89
136
 
90
137
  def initialize(hash, rule)
91
138
 
@@ -112,17 +159,24 @@ module Ruote
112
159
 
113
160
  # now only keep the keys that match our regexp
114
161
 
115
- keys.inject([]) { |a, k|
116
- if m = fl.match(k)
117
- a << [ k, Ruote.lookup(@hash, k), m[1..-1] ]
118
- end
119
- a
162
+ keys.each_with_object([]) { |k, a|
163
+ m = fl.match(k)
164
+ a << [ k, Ruote.lookup(@hash, k), m[1..-1] ] if m
120
165
  }
121
166
 
167
+ elsif fl.is_a?(String) and PIPE_SPLIT.match(fl)
168
+
169
+ fields = fl.split(PIPE_SPLIT).collect { |field|
170
+ val = Ruote.lookup(@hash, field)
171
+ val.nil? ? nil : [ field, val, nil ]
172
+ }.compact
173
+
174
+ fields.empty? ? [ [ fl, nil, nil ] ] : fields
175
+ # if no fields where found, place fake fl field to force failure
176
+
122
177
  else
123
178
 
124
- (fl.is_a?(Array) ? fl : fl.to_s.split(',')).collect { |field|
125
- field = field.strip
179
+ (fl.is_a?(Array) ? fl : fl.to_s.split(COMMA_SPLIT)).collect { |field|
126
180
  [ field, Ruote.lookup(@hash, field), nil ]
127
181
  }
128
182
  end
@@ -130,20 +184,35 @@ module Ruote
130
184
 
131
185
  def run
132
186
 
187
+ keys = @rule.keys - SKIP
188
+ validation = (@rule.keys & BOOLEANS).empty?
189
+
190
+ if validation and @fields.empty? and keys.empty?
191
+ fl = @rule['fields'] || @rule['field'] || @rule['f']
192
+ return [ [ @rule, fl, nil ] ] # validation break
193
+ end
194
+
133
195
  @fields.collect { |field, value, matches|
134
196
 
135
197
  valid = nil
136
198
 
137
- @rule.each do |k, v|
199
+ if keys.empty?
200
+
201
+ valid = (value != nil)
202
+
203
+ else
138
204
 
139
- next if SKIP.include?(k)
205
+ keys.each do |k|
140
206
 
141
- m = "_#{k}"
142
- next unless self.respond_to?(m)
207
+ v = @rule[k]
143
208
 
144
- r = self.send(m, field, value, matches, k, v)
209
+ m = "_#{k}"
210
+ next unless self.respond_to?(m)
145
211
 
146
- valid = false if r == false
212
+ r = self.send(m, field, value, matches, k, v)
213
+
214
+ valid = false if r == false
215
+ end
147
216
  end
148
217
 
149
218
  raise_or_and(valid, field, value)
@@ -264,6 +333,30 @@ module Ruote
264
333
  alias _restore_from _restore
265
334
  alias _rs _restore
266
335
 
336
+ def _take(field, value, matches, m, v)
337
+
338
+ unless @hash.has_key?('~~~')
339
+
340
+ @hash['~~~'] = @hash.keys.select { |k|
341
+ ! k.match(/^\~+$/)
342
+ }.each_with_object({}) { |k, h|
343
+ h[k] = @hash.delete(k)
344
+ }
345
+
346
+ @hash.merge!(@hash['~~'])
347
+ @hash.merge!(@hash['~~~']) if m == 'discard' && v != 'all'
348
+ end
349
+
350
+ if m == 'take'
351
+ @hash[field] = @hash['~~~'][field]
352
+ elsif v != 'all'
353
+ @hash.delete(field)
354
+ end
355
+
356
+ nil
357
+ end
358
+ alias _discard _take
359
+
267
360
  def _size(field, value, matches, m, v)
268
361
 
269
362
  v = v.is_a?(String) ? v.split(',').collect { |i| i.to_i } : Array(v)
@@ -279,7 +372,9 @@ module Ruote
279
372
 
280
373
  def _empty(field, value, matches, m, v)
281
374
 
282
- value.respond_to?(:empty?) ? value.empty? : false
375
+ # 'empty' => '30%' could be fun ;-)
376
+
377
+ (value.respond_to?(:empty?) ? value.empty? : false) == v
283
378
  end
284
379
  alias _e _empty
285
380
 
@@ -306,6 +401,15 @@ module Ruote
306
401
  end
307
402
  alias _h _has
308
403
 
404
+ def _includes(field, value, matches, m, v)
405
+
406
+ case value
407
+ when Array then value.include?(v)
408
+ when Hash then value.values.include?(v)
409
+ else false
410
+ end
411
+ end
412
+
309
413
  def _type(field, value, matches, m, v)
310
414
 
311
415
  of_type?(value, v)
@@ -352,8 +456,6 @@ module Ruote
352
456
  else
353
457
  raise ArgumentError.new("unknown type '#{type}'")
354
458
  end
355
-
356
- valid
357
459
  end
358
460
  end
359
461
 
@@ -380,6 +482,11 @@ module Ruote
380
482
  end
381
483
  alias _sm _smatch
382
484
 
485
+ def _is(field, value, matches, m, v)
486
+
487
+ value == v
488
+ end
489
+
383
490
  def _valid(field, value, matches, m, v)
384
491
 
385
492
  v.to_s == 'true'