ruote-maestrodev 2.2.1

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 (265) hide show
  1. data/CHANGELOG.txt +290 -0
  2. data/CREDITS.txt +99 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.rdoc +88 -0
  5. data/Rakefile +108 -0
  6. data/TODO.txt +488 -0
  7. data/lib/ruote.rb +7 -0
  8. data/lib/ruote/context.rb +194 -0
  9. data/lib/ruote/engine.rb +1062 -0
  10. data/lib/ruote/engine/process_error.rb +122 -0
  11. data/lib/ruote/engine/process_status.rb +448 -0
  12. data/lib/ruote/exp/command.rb +87 -0
  13. data/lib/ruote/exp/commanded.rb +69 -0
  14. data/lib/ruote/exp/condition.rb +227 -0
  15. data/lib/ruote/exp/fe_add_branches.rb +138 -0
  16. data/lib/ruote/exp/fe_apply.rb +154 -0
  17. data/lib/ruote/exp/fe_cancel_process.rb +78 -0
  18. data/lib/ruote/exp/fe_command.rb +156 -0
  19. data/lib/ruote/exp/fe_concurrence.rb +321 -0
  20. data/lib/ruote/exp/fe_concurrent_iterator.rb +219 -0
  21. data/lib/ruote/exp/fe_cron.rb +141 -0
  22. data/lib/ruote/exp/fe_cursor.rb +324 -0
  23. data/lib/ruote/exp/fe_define.rb +112 -0
  24. data/lib/ruote/exp/fe_echo.rb +60 -0
  25. data/lib/ruote/exp/fe_equals.rb +115 -0
  26. data/lib/ruote/exp/fe_error.rb +82 -0
  27. data/lib/ruote/exp/fe_filter.rb +648 -0
  28. data/lib/ruote/exp/fe_forget.rb +88 -0
  29. data/lib/ruote/exp/fe_given.rb +154 -0
  30. data/lib/ruote/exp/fe_if.rb +127 -0
  31. data/lib/ruote/exp/fe_inc.rb +205 -0
  32. data/lib/ruote/exp/fe_iterator.rb +234 -0
  33. data/lib/ruote/exp/fe_let.rb +75 -0
  34. data/lib/ruote/exp/fe_listen.rb +304 -0
  35. data/lib/ruote/exp/fe_lose.rb +110 -0
  36. data/lib/ruote/exp/fe_noop.rb +45 -0
  37. data/lib/ruote/exp/fe_once.rb +215 -0
  38. data/lib/ruote/exp/fe_participant.rb +287 -0
  39. data/lib/ruote/exp/fe_read.rb +69 -0
  40. data/lib/ruote/exp/fe_redo.rb +82 -0
  41. data/lib/ruote/exp/fe_ref.rb +152 -0
  42. data/lib/ruote/exp/fe_registerp.rb +110 -0
  43. data/lib/ruote/exp/fe_reserve.rb +126 -0
  44. data/lib/ruote/exp/fe_restore.rb +102 -0
  45. data/lib/ruote/exp/fe_save.rb +72 -0
  46. data/lib/ruote/exp/fe_sequence.rb +59 -0
  47. data/lib/ruote/exp/fe_set.rb +154 -0
  48. data/lib/ruote/exp/fe_subprocess.rb +211 -0
  49. data/lib/ruote/exp/fe_that.rb +92 -0
  50. data/lib/ruote/exp/fe_undo.rb +67 -0
  51. data/lib/ruote/exp/fe_unregisterp.rb +69 -0
  52. data/lib/ruote/exp/fe_wait.rb +95 -0
  53. data/lib/ruote/exp/flowexpression.rb +886 -0
  54. data/lib/ruote/exp/iterator.rb +81 -0
  55. data/lib/ruote/exp/merge.rb +118 -0
  56. data/lib/ruote/exp/ro_attributes.rb +212 -0
  57. data/lib/ruote/exp/ro_filters.rb +136 -0
  58. data/lib/ruote/exp/ro_persist.rb +154 -0
  59. data/lib/ruote/exp/ro_variables.rb +189 -0
  60. data/lib/ruote/exp/ro_vf.rb +68 -0
  61. data/lib/ruote/fei.rb +260 -0
  62. data/lib/ruote/id/mnemo_wfid_generator.rb +43 -0
  63. data/lib/ruote/id/wfid_generator.rb +81 -0
  64. data/lib/ruote/log/default_history.rb +122 -0
  65. data/lib/ruote/log/pretty.rb +176 -0
  66. data/lib/ruote/log/storage_history.rb +159 -0
  67. data/lib/ruote/log/test_logger.rb +208 -0
  68. data/lib/ruote/log/wait_logger.rb +64 -0
  69. data/lib/ruote/part/block_participant.rb +137 -0
  70. data/lib/ruote/part/code_participant.rb +81 -0
  71. data/lib/ruote/part/engine_participant.rb +189 -0
  72. data/lib/ruote/part/local_participant.rb +138 -0
  73. data/lib/ruote/part/no_op_participant.rb +60 -0
  74. data/lib/ruote/part/null_participant.rb +54 -0
  75. data/lib/ruote/part/rev_participant.rb +169 -0
  76. data/lib/ruote/part/smtp_participant.rb +116 -0
  77. data/lib/ruote/part/storage_participant.rb +392 -0
  78. data/lib/ruote/part/template.rb +84 -0
  79. data/lib/ruote/participant.rb +7 -0
  80. data/lib/ruote/reader.rb +278 -0
  81. data/lib/ruote/reader/json.rb +49 -0
  82. data/lib/ruote/reader/radial.rb +290 -0
  83. data/lib/ruote/reader/ruby_dsl.rb +186 -0
  84. data/lib/ruote/reader/xml.rb +99 -0
  85. data/lib/ruote/receiver/base.rb +212 -0
  86. data/lib/ruote/storage/base.rb +364 -0
  87. data/lib/ruote/storage/composite_storage.rb +121 -0
  88. data/lib/ruote/storage/fs_storage.rb +139 -0
  89. data/lib/ruote/storage/hash_storage.rb +211 -0
  90. data/lib/ruote/svc/dispatch_pool.rb +158 -0
  91. data/lib/ruote/svc/dollar_sub.rb +298 -0
  92. data/lib/ruote/svc/error_handler.rb +138 -0
  93. data/lib/ruote/svc/expression_map.rb +97 -0
  94. data/lib/ruote/svc/participant_list.rb +397 -0
  95. data/lib/ruote/svc/tracker.rb +172 -0
  96. data/lib/ruote/svc/treechecker.rb +141 -0
  97. data/lib/ruote/tree_dot.rb +85 -0
  98. data/lib/ruote/util/filter.rb +525 -0
  99. data/lib/ruote/util/hashdot.rb +79 -0
  100. data/lib/ruote/util/look.rb +128 -0
  101. data/lib/ruote/util/lookup.rb +127 -0
  102. data/lib/ruote/util/misc.rb +167 -0
  103. data/lib/ruote/util/ometa.rb +71 -0
  104. data/lib/ruote/util/serializer.rb +103 -0
  105. data/lib/ruote/util/subprocess.rb +88 -0
  106. data/lib/ruote/util/time.rb +100 -0
  107. data/lib/ruote/util/tree.rb +58 -0
  108. data/lib/ruote/version.rb +29 -0
  109. data/lib/ruote/worker.rb +386 -0
  110. data/lib/ruote/workitem.rb +394 -0
  111. data/phil.txt +14 -0
  112. data/ruote.gemspec +44 -0
  113. data/test/bm/ci.rb +55 -0
  114. data/test/bm/ici.rb +71 -0
  115. data/test/bm/juuman.rb +54 -0
  116. data/test/bm/launch_bench.rb +37 -0
  117. data/test/bm/load_26c.rb +97 -0
  118. data/test/bm/mega.rb +64 -0
  119. data/test/bm/seq_thousand.rb +31 -0
  120. data/test/bm/t.rb +35 -0
  121. data/test/functional/base.rb +247 -0
  122. data/test/functional/concurrent_base.rb +98 -0
  123. data/test/functional/crunner.rb +31 -0
  124. data/test/functional/ct_0_concurrence.rb +65 -0
  125. data/test/functional/ct_1_iterator.rb +67 -0
  126. data/test/functional/ct_2_cancel.rb +81 -0
  127. data/test/functional/eft_0_process_definition.rb +65 -0
  128. data/test/functional/eft_10_cancel_process.rb +46 -0
  129. data/test/functional/eft_11_wait.rb +109 -0
  130. data/test/functional/eft_12_listen.rb +500 -0
  131. data/test/functional/eft_13_iterator.rb +342 -0
  132. data/test/functional/eft_14_cursor.rb +456 -0
  133. data/test/functional/eft_15_loop.rb +69 -0
  134. data/test/functional/eft_16_if.rb +183 -0
  135. data/test/functional/eft_17_equals.rb +55 -0
  136. data/test/functional/eft_18_concurrent_iterator.rb +410 -0
  137. data/test/functional/eft_19_reserve.rb +136 -0
  138. data/test/functional/eft_1_echo.rb +68 -0
  139. data/test/functional/eft_20_save.rb +116 -0
  140. data/test/functional/eft_21_restore.rb +61 -0
  141. data/test/functional/eft_22_noop.rb +28 -0
  142. data/test/functional/eft_23_apply.rb +168 -0
  143. data/test/functional/eft_24_add_branches.rb +98 -0
  144. data/test/functional/eft_25_command.rb +28 -0
  145. data/test/functional/eft_26_error.rb +77 -0
  146. data/test/functional/eft_27_inc.rb +280 -0
  147. data/test/functional/eft_28_once.rb +135 -0
  148. data/test/functional/eft_29_cron.rb +64 -0
  149. data/test/functional/eft_2_sequence.rb +58 -0
  150. data/test/functional/eft_30_ref.rb +155 -0
  151. data/test/functional/eft_31_registerp.rb +130 -0
  152. data/test/functional/eft_32_lose.rb +93 -0
  153. data/test/functional/eft_33_let.rb +31 -0
  154. data/test/functional/eft_34_given.rb +123 -0
  155. data/test/functional/eft_35_filter.rb +375 -0
  156. data/test/functional/eft_36_read.rb +95 -0
  157. data/test/functional/eft_3_participant.rb +149 -0
  158. data/test/functional/eft_4_set.rb +296 -0
  159. data/test/functional/eft_5_subprocess.rb +163 -0
  160. data/test/functional/eft_6_concurrence.rb +304 -0
  161. data/test/functional/eft_7_forget.rb +61 -0
  162. data/test/functional/eft_8_undo.rb +114 -0
  163. data/test/functional/eft_9_redo.rb +138 -0
  164. data/test/functional/ft_0_worker.rb +65 -0
  165. data/test/functional/ft_10_dollar.rb +304 -0
  166. data/test/functional/ft_11_recursion.rb +109 -0
  167. data/test/functional/ft_12_launchitem.rb +43 -0
  168. data/test/functional/ft_13_variables.rb +151 -0
  169. data/test/functional/ft_14_re_apply.rb +324 -0
  170. data/test/functional/ft_15_timeout.rb +226 -0
  171. data/test/functional/ft_16_participant_params.rb +98 -0
  172. data/test/functional/ft_17_conditional.rb +102 -0
  173. data/test/functional/ft_18_kill.rb +138 -0
  174. data/test/functional/ft_19_participant_code.rb +67 -0
  175. data/test/functional/ft_1_process_status.rb +796 -0
  176. data/test/functional/ft_20_storage_participant.rb +543 -0
  177. data/test/functional/ft_21_forget.rb +153 -0
  178. data/test/functional/ft_22_process_definitions.rb +90 -0
  179. data/test/functional/ft_23_load_defs.rb +79 -0
  180. data/test/functional/ft_24_block_participant.rb +235 -0
  181. data/test/functional/ft_25_receiver.rb +207 -0
  182. data/test/functional/ft_26_participant_rtimeout.rb +179 -0
  183. data/test/functional/ft_27_var_indirection.rb +128 -0
  184. data/test/functional/ft_28_null_noop_participants.rb +51 -0
  185. data/test/functional/ft_29_part_template.rb +60 -0
  186. data/test/functional/ft_2_errors.rb +380 -0
  187. data/test/functional/ft_30_smtp_participant.rb +122 -0
  188. data/test/functional/ft_31_part_blocking.rb +72 -0
  189. data/test/functional/ft_33_participant_subprocess_priority.rb +32 -0
  190. data/test/functional/ft_34_cursor_rewind.rb +101 -0
  191. data/test/functional/ft_35_add_service.rb +56 -0
  192. data/test/functional/ft_36_storage_history.rb +150 -0
  193. data/test/functional/ft_37_default_history.rb +109 -0
  194. data/test/functional/ft_38_participant_more.rb +193 -0
  195. data/test/functional/ft_39_wait_for.rb +136 -0
  196. data/test/functional/ft_3_participant_registration.rb +574 -0
  197. data/test/functional/ft_40_wait_logger.rb +62 -0
  198. data/test/functional/ft_41_participants.rb +91 -0
  199. data/test/functional/ft_42_storage_copy.rb +71 -0
  200. data/test/functional/ft_43_participant_on_reply.rb +87 -0
  201. data/test/functional/ft_44_var_participant.rb +35 -0
  202. data/test/functional/ft_45_participant_accept.rb +64 -0
  203. data/test/functional/ft_46_launch_single.rb +83 -0
  204. data/test/functional/ft_47_wfid_generator.rb +54 -0
  205. data/test/functional/ft_48_lose.rb +112 -0
  206. data/test/functional/ft_49_engine_on_error.rb +201 -0
  207. data/test/functional/ft_4_cancel.rb +132 -0
  208. data/test/functional/ft_50_engine_config.rb +22 -0
  209. data/test/functional/ft_51_misc.rb +67 -0
  210. data/test/functional/ft_52_case.rb +134 -0
  211. data/test/functional/ft_53_engine_on_terminate.rb +95 -0
  212. data/test/functional/ft_54_patterns.rb +104 -0
  213. data/test/functional/ft_55_engine_participant.rb +303 -0
  214. data/test/functional/ft_56_filter_attribute.rb +259 -0
  215. data/test/functional/ft_57_rev_participant.rb +252 -0
  216. data/test/functional/ft_58_workitem.rb +69 -0
  217. data/test/functional/ft_59_pause.rb +343 -0
  218. data/test/functional/ft_5_on_error.rb +384 -0
  219. data/test/functional/ft_60_code_participant.rb +45 -0
  220. data/test/functional/ft_61_trailing_fields.rb +34 -0
  221. data/test/functional/ft_62_exp_name_and_dollar_substitution.rb +35 -0
  222. data/test/functional/ft_6_on_cancel.rb +221 -0
  223. data/test/functional/ft_7_tags.rb +177 -0
  224. data/test/functional/ft_8_participant_consumption.rb +124 -0
  225. data/test/functional/ft_9_subprocesses.rb +146 -0
  226. data/test/functional/restart_base.rb +34 -0
  227. data/test/functional/rt_0_wait.rb +55 -0
  228. data/test/functional/rt_1_listen.rb +56 -0
  229. data/test/functional/rt_2_errors.rb +56 -0
  230. data/test/functional/rt_3_once.rb +70 -0
  231. data/test/functional/rt_4_cron.rb +64 -0
  232. data/test/functional/rt_5_timeout.rb +60 -0
  233. data/test/functional/rtest.rb +8 -0
  234. data/test/functional/storage_helper.rb +93 -0
  235. data/test/functional/test.rb +44 -0
  236. data/test/functional/vertical.rb +46 -0
  237. data/test/path_helper.rb +15 -0
  238. data/test/test.rb +13 -0
  239. data/test/test_helper.rb +28 -0
  240. data/test/unit/storage.rb +428 -0
  241. data/test/unit/storages.rb +37 -0
  242. data/test/unit/test.rb +28 -0
  243. data/test/unit/ut_0_ruby_reader.rb +223 -0
  244. data/test/unit/ut_11_lookup.rb +122 -0
  245. data/test/unit/ut_13_serializer.rb +65 -0
  246. data/test/unit/ut_14_is_uri.rb +28 -0
  247. data/test/unit/ut_15_util.rb +57 -0
  248. data/test/unit/ut_16_reader.rb +225 -0
  249. data/test/unit/ut_18_engine.rb +47 -0
  250. data/test/unit/ut_19_part_template.rb +86 -0
  251. data/test/unit/ut_1_fei.rb +165 -0
  252. data/test/unit/ut_20_composite_storage.rb +74 -0
  253. data/test/unit/ut_21_svc_participant_list.rb +46 -0
  254. data/test/unit/ut_22_filter.rb +1094 -0
  255. data/test/unit/ut_23_svc_tracker.rb +48 -0
  256. data/test/unit/ut_24_radial_reader.rb +332 -0
  257. data/test/unit/ut_25_merge.rb +113 -0
  258. data/test/unit/ut_3_wait_logger.rb +39 -0
  259. data/test/unit/ut_4_expmap.rb +20 -0
  260. data/test/unit/ut_5_tree.rb +54 -0
  261. data/test/unit/ut_6_condition.rb +303 -0
  262. data/test/unit/ut_7_workitem.rb +99 -0
  263. data/test/unit/ut_8_tree_to_dot.rb +72 -0
  264. data/test/unit/ut_9_xml_reader.rb +61 -0
  265. metadata +504 -0
@@ -0,0 +1,141 @@
1
+ #--
2
+ # Copyright (c) 2005-2011, 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
+ require 'rufus/treechecker'
27
+ require 'fileutils'
28
+
29
+
30
+ module Ruote
31
+
32
+ #
33
+ # The TreeChecker service is used to check incoming external ruby code
34
+ # and raise a security error if it contains potentially evil code.
35
+ #
36
+ class TreeChecker
37
+
38
+ def initialize(context)
39
+
40
+ return if context['use_ruby_treechecker'] == false
41
+
42
+ checker = Rufus::TreeChecker.new do
43
+
44
+ exclude_fvccall :abort, :exit, :exit!
45
+ exclude_fvccall :system, :fork, :syscall, :trap, :require, :load
46
+ exclude_fvccall :at_exit
47
+
48
+ #exclude_call_to :class
49
+ exclude_fvcall :private, :public, :protected
50
+
51
+ #exclude_raise # no raise or throw
52
+
53
+ exclude_eval # no eval, module_eval or instance_eval
54
+ exclude_backquotes # no `rm -fR the/kitchen/sink`
55
+ exclude_alias # no alias or aliast_method
56
+ exclude_global_vars # $vars are off limits
57
+ exclude_module_tinkering # no module opening
58
+
59
+ exclude_rebinding Kernel # no 'k = Kernel'
60
+
61
+ exclude_access_to(
62
+ IO, File, FileUtils, Process, Signal, Thread, ThreadGroup)
63
+
64
+ #exclude_class_tinkering :except => Ruote::ProcessDefinition
65
+ #
66
+ # excludes defining/opening any class except
67
+ # Ruote::ProcessDefinition
68
+
69
+ exclude_call_to :instance_variable_get, :instance_variable_set
70
+ end
71
+
72
+ stricter_checker = checker.clone
73
+ stricter_checker.add_rules do
74
+ exclude_def # no method definition
75
+ exclude_raise # no raise or throw
76
+ end
77
+
78
+ # the checker used when reading process definitions
79
+
80
+ @def_checker = stricter_checker.clone # and not dup
81
+ @def_checker.freeze
82
+
83
+ ## the checker used when dealing with conditionals
84
+ #
85
+ #@con_checker = checker.clone # and not dup
86
+ #@con_checker.add_rules do
87
+ # exclude_raise # no raise or throw
88
+ # at_root do
89
+ # exclude_head [ :block ] # preventing 'a < b; do_sthing_evil()'
90
+ # exclude_head [ :lasgn ] # preventing 'a = 3'
91
+ # end
92
+ #end
93
+ #@con_checker.freeze
94
+ #
95
+ # lib/ruote/exp/condition.rb doesn't use this treechecker
96
+ # kept (commented out) for 'documentation'
97
+
98
+ # the checker used when dealing with code in $(ruby:xxx}
99
+
100
+ @dol_checker = stricter_checker.clone # and not dup
101
+ @dol_checker.freeze
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
+
116
+ freeze
117
+ # preventing further modifications
118
+ end
119
+
120
+ def definition_check(ruby_code)
121
+
122
+ @def_checker.check(ruby_code) if @def_checker
123
+ end
124
+
125
+ def block_check(ruby_code)
126
+
127
+ @blo_checker.check(ruby_code) if @blo_checker
128
+ end
129
+
130
+ def dollar_check(ruby_code)
131
+
132
+ @dol_checker.check(ruby_code) if @dol_checker
133
+ end
134
+
135
+ def code_check(ruby_code)
136
+
137
+ @cod_checker.check(ruby_code) if @cod_checker
138
+ end
139
+ end
140
+ end
141
+
@@ -0,0 +1,85 @@
1
+ #--
2
+ # Copyright (c) 2005-2011, 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
+ # Turns a process definition tree to a graphviz dot representation.
29
+ #
30
+ # http://www.graphviz.org
31
+ #
32
+ def self.tree_to_dot(tree, name='ruote process definition')
33
+
34
+ s = "digraph \"#{name}\" {\n"
35
+ s << branch_to_dot('0', tree).join("\n")
36
+ s << "\n}\n"
37
+ end
38
+
39
+ protected
40
+
41
+ def self.branch_to_dot(expid, exp)
42
+
43
+ [
44
+ " \"#{expid}\" "+
45
+ "[ label = \"#{exp[0]} #{exp[1].inspect.gsub("\"", "'")}\" ];"
46
+ ] +
47
+ children_to_dot(expid, exp)
48
+ end
49
+
50
+ def self.children_to_dot(expid, exp)
51
+
52
+ exp_name = exp[0]
53
+ child_count = exp[2].size
54
+
55
+ i = -1
56
+
57
+ a = exp[2].collect do |child|
58
+ i += 1
59
+ branch_to_dot("#{expid}_#{i}", child)
60
+ end
61
+
62
+ if child_count > 0 # there are children
63
+
64
+ if %w[ concurrence if ].include?(exp_name)
65
+
66
+ (0..child_count - 1).each do |i|
67
+ a << " \"#{expid}\" -> \"#{expid}_#{i}\";"
68
+ a << " \"#{expid}_#{i}\" -> \"#{expid}\";"
69
+ end
70
+
71
+ else
72
+
73
+ a << " \"#{expid}\" -> \"#{expid}_0\";"
74
+ a << " \"#{expid}_#{child_count -1}\" -> \"#{expid}\";"
75
+
76
+ (0..child_count - 2).each do |i|
77
+ a << " \"#{expid}_#{i}\" -> \"#{expid}_#{i + 1}\";"
78
+ end
79
+ end
80
+ end
81
+
82
+ a
83
+ end
84
+ end
85
+
@@ -0,0 +1,525 @@
1
+ #--
2
+ # Copyright (c) 2005-2011, 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
+ require 'ruote/util/misc'
26
+ require 'ruote/util/lookup'
27
+
28
+
29
+ module Ruote
30
+
31
+ #
32
+ # An error class for validation errors gathered during filtering.
33
+ #
34
+ class ValidationError < StandardError
35
+
36
+ attr_reader :deviations
37
+
38
+ def initialize(deviations)
39
+ @deviations = deviations
40
+ super("validation failed with #{@deviations.size} deviation(s)")
41
+ end
42
+ end
43
+
44
+ # Given a filter (a list of rules) and a hash (probably workitem fields)
45
+ # performs the validations / transformations dictated by the rules.
46
+ #
47
+ # See the Ruote::Exp::FilterExpression for more information.
48
+ #
49
+ def self.filter(filter, hash, options={})
50
+
51
+ raise ArgumentError.new(
52
+ "not a filter : #{filter}"
53
+ ) unless filter.is_a?(Array)
54
+
55
+ filters = or_split(filter)
56
+
57
+ result = nil
58
+
59
+ filters.each do |fl|
60
+
61
+ result = begin
62
+ do_filter(fl, hash, options)
63
+ rescue ValidationError => err
64
+ err
65
+ end
66
+
67
+ return result if result.is_a?(Hash)
68
+ # success
69
+ end
70
+
71
+ raise(result) if result.is_a?(ValidationError)
72
+
73
+ result
74
+ end
75
+
76
+ # Used by Ruote.filter
77
+ #
78
+ def self.or_split(filter)
79
+
80
+ return filter if filter.first.is_a?(Array)
81
+ return [ filter ] if filter.empty? or ( ! filter.include?('or'))
82
+
83
+ # [ {}, 'or', {}, {}, 'or', {} ]
84
+
85
+ filter.inject([ [] ]) do |result, fl|
86
+ if fl.is_a?(Hash)
87
+ result.last << fl
88
+ else
89
+ result << []
90
+ end
91
+ result
92
+ end
93
+ end
94
+
95
+ # Used by Ruote.filter
96
+ #
97
+ def self.do_filter(filter, hash, options)
98
+
99
+ hash = Rufus::Json.dup(hash)
100
+
101
+ hash['~'] = Rufus::Json.dup(hash)
102
+ hash['~~'] = Rufus::Json.dup(options[:double_tilde] || hash)
103
+ # the 'originals'
104
+
105
+ deviations = filter.collect { |rule|
106
+ RuleSession.new(hash, rule).run
107
+ }.flatten(1)
108
+
109
+ hash.delete('~')
110
+ hash.delete('~~')
111
+ # remove the 'originals'
112
+
113
+ if deviations.empty?
114
+ hash
115
+ elsif options[:no_raise]
116
+ deviations
117
+ else
118
+ raise ValidationError.new(deviations)
119
+ end
120
+ end
121
+
122
+ # :nodoc:
123
+ #
124
+ # The class used to run a rule (a line of a filter).
125
+ #
126
+ class RuleSession
127
+
128
+ SKIP = %w[ and or fields field f ]
129
+ BOOLEANS = %w[ and or ]
130
+ NUMBER_CLASSES = [ Fixnum, Float ]
131
+ BOOLEAN_CLASSES = [ TrueClass, FalseClass ]
132
+ TILDE = /^~/
133
+ RTILDE = /^\^~/
134
+ COMMA_SPLIT = / *, */
135
+ PIPE_SPLIT = / *\| */
136
+
137
+ def initialize(hash, rule)
138
+
139
+ @hash = hash
140
+ @rule = rule
141
+
142
+ fl = @rule['fields'] || @rule['field'] || @rule['f']
143
+
144
+ raise ArgumentError.new(
145
+ "filter is missing a 'fields', 'field' or 'f' arg at #{@rule.inspect}"
146
+ ) unless fl
147
+
148
+ if fl.is_a?(String)
149
+ fl = fl.gsub(/!/, '\.') if REGEX_IN_STRING.match(fl)
150
+ fl = Ruote.regex_or_s(fl)
151
+ end
152
+
153
+ @fields = if fl.is_a?(Regexp)
154
+
155
+ # when restoring, you look at the old keys, not the current ones
156
+
157
+ keys = Ruote.flatten_keys(@rule['restore'] ? @hash['~~'] : @hash)
158
+ keys = keys.reject { |k| TILDE.match(k) } unless RTILDE.match(fl.source)
159
+
160
+ # now only keep the keys that match our regexp
161
+
162
+ keys.inject([]) { |a, k|
163
+ if m = fl.match(k)
164
+ a << [ k, Ruote.lookup(@hash, k), m[1..-1] ]
165
+ end
166
+ a
167
+ }
168
+
169
+ elsif fl.is_a?(String) and PIPE_SPLIT.match(fl)
170
+
171
+ fields = fl.split(PIPE_SPLIT).collect { |field|
172
+ val = Ruote.lookup(@hash, field)
173
+ val.nil? ? nil : [ field, val, nil ]
174
+ }.compact
175
+
176
+ fields.empty? ? [ [ fl, nil, nil ] ] : fields
177
+ # if no fields where found, place fake fl field to force failure
178
+
179
+ else
180
+
181
+ (fl.is_a?(Array) ? fl : fl.to_s.split(COMMA_SPLIT)).collect { |field|
182
+ [ field, Ruote.lookup(@hash, field), nil ]
183
+ }
184
+ end
185
+ end
186
+
187
+ def run
188
+
189
+ keys = @rule.keys - SKIP
190
+ validation = (@rule.keys & BOOLEANS).empty?
191
+
192
+ if validation and @fields.empty? and keys.empty?
193
+ fl = @rule['fields'] || @rule['field'] || @rule['f']
194
+ return [ [ @rule, fl, nil ] ] # validation break
195
+ end
196
+
197
+ @fields.collect { |field, value, matches|
198
+
199
+ valid = nil
200
+
201
+ if keys.empty?
202
+
203
+ valid = (value != nil)
204
+
205
+ else
206
+
207
+ keys.each do |k|
208
+
209
+ v = @rule[k]
210
+
211
+ m = "_#{k}"
212
+ next unless self.respond_to?(m)
213
+
214
+ r = self.send(m, field, value, matches, k, v)
215
+
216
+ valid = false if r == false
217
+ end
218
+ end
219
+
220
+ raise_or_and(valid, field, value)
221
+
222
+ }.compact
223
+ end
224
+
225
+ protected
226
+
227
+ def _remove(field, value, matches, m, v)
228
+
229
+ Ruote.unset(@hash, field)
230
+
231
+ nil
232
+ end
233
+ alias _rm _remove
234
+ alias _delete _remove
235
+ alias _del _remove
236
+
237
+ def _set(field, value, matches, m, v)
238
+
239
+ Ruote.set(@hash, field, Rufus::Json.dup(v))
240
+
241
+ nil
242
+ end
243
+ alias _s _set
244
+
245
+ def adjust_target(target, matches)
246
+
247
+ target.gsub(/\\\d+/) { |digit| matches[digit.to_i - 1] rescue '' }
248
+ end
249
+
250
+ def _copy_to(field, value, matches, m, v)
251
+
252
+ v = adjust_target(v, matches)
253
+
254
+ Ruote.set(@hash, v, Rufus::Json.dup(value))
255
+ Ruote.unset(@hash, field) if m == 'move_to' or m == 'mv_to'
256
+
257
+ nil
258
+ end
259
+ alias _cp_to _copy_to
260
+ alias _move_to _copy_to
261
+ alias _mv_to _copy_to
262
+
263
+
264
+ def _copy_from(field, value, matches, m, v)
265
+
266
+ Ruote.set(@hash, field, Rufus::Json.dup(Ruote.lookup(@hash, v)))
267
+ Ruote.unset(@hash, v) if m == 'move_from' or m == 'mv_from'
268
+
269
+ nil
270
+ end
271
+ alias _cp_from _copy_from
272
+ alias _move_from _copy_from
273
+ alias _mv_from _copy_from
274
+
275
+ # Used by both _merge_to and _merge_from
276
+ #
277
+ def do_merge(field, target, value)
278
+
279
+ value = Rufus::Json.dup(value)
280
+
281
+ if target.is_a?(Array)
282
+ target.push(value)
283
+ elsif value.is_a?(Hash)
284
+ target.merge!(value)
285
+ else # deal with non Hash
286
+ target[field.split('.').last] = value
287
+ end
288
+
289
+ target.delete('~')
290
+ target.delete('~~')
291
+ end
292
+
293
+ def _merge_to(field, value, matches, m, v)
294
+
295
+ target = Ruote.lookup(@hash, v)
296
+
297
+ return unless target.respond_to?(:merge!) or target.is_a?(Array)
298
+
299
+ do_merge(field, target, value)
300
+
301
+ Ruote.unset(@hash, field) if m == 'migrate_to' or m == 'mi_to'
302
+
303
+ nil
304
+ end
305
+ alias _mg_to _merge_to
306
+ alias _push_to _merge_to
307
+ alias _pu_to _merge_to
308
+ alias _migrate_to _merge_to
309
+ alias _mi_to _merge_to
310
+
311
+ def _merge_from(field, value, matches, m, v)
312
+
313
+ return unless value.respond_to?(:merge!) or value.is_a?(Array)
314
+
315
+ do_merge(v, value, Ruote.lookup(@hash, v))
316
+
317
+ Ruote.unset(@hash, v) if v != '.' and m.match(/^mi(grate)?_from$/)
318
+
319
+ nil
320
+ end
321
+ alias _mg_from _merge_from
322
+ alias _push_from _merge_from
323
+ alias _pu_from _merge_from
324
+ alias _migrate_from _merge_from
325
+ alias _mi_from _merge_from
326
+
327
+ def _restore(field, value, matches, m, v)
328
+
329
+ prefix = v == true ? '~~' : v.to_s
330
+
331
+ Ruote.set(@hash, field, Ruote.lookup(@hash, "#{prefix}.#{field}"))
332
+
333
+ nil
334
+ end
335
+ alias _restore_from _restore
336
+ alias _rs _restore
337
+
338
+ def _size(field, value, matches, m, v)
339
+
340
+ v = v.is_a?(String) ? v.split(',').collect { |i| i.to_i } : Array(v)
341
+
342
+ if value.respond_to?(:size)
343
+ (v.first ? value.size >= v.first : true) and
344
+ (v.last ? value.size <= v.last : true)
345
+ else
346
+ false
347
+ end
348
+ end
349
+ alias _sz _size
350
+
351
+ def _empty(field, value, matches, m, v)
352
+
353
+ # 'empty' => '30%' could be fun ;-)
354
+
355
+ (value.respond_to?(:empty?) ? value.empty? : false) == v
356
+ end
357
+ alias _e _empty
358
+
359
+ def _in(field, value, matches, m, v)
360
+
361
+ (v.is_a?(Array) ?
362
+ v :
363
+ v.to_s.split(',').collect { |e| e.strip }
364
+ ).include?(value)
365
+ end
366
+ alias _i _in
367
+
368
+ def _has(field, value, matches, m, v)
369
+
370
+ v = v.is_a?(Array) ? v : v.to_s.split(',').collect { |e| e.strip }
371
+
372
+ if value.is_a?(Hash)
373
+ (value.keys & v) == v
374
+ elsif value.is_a?(Array)
375
+ (value & v) == v
376
+ else
377
+ false
378
+ end
379
+ end
380
+ alias _h _has
381
+
382
+ def _includes(field, value, matches, m, v)
383
+
384
+ case value
385
+ when Array then value.include?(v)
386
+ when Hash then value.values.include?(v)
387
+ else false
388
+ end
389
+ end
390
+
391
+ def _type(field, value, matches, m, v)
392
+
393
+ of_type?(value, v)
394
+ end
395
+ alias _t _type
396
+
397
+ TYPE_SPLITTER = /^(?: *, *)?([^,<]+(?:<.+>)?)(.*)$/
398
+
399
+ def split_type(type)
400
+
401
+ result = []
402
+
403
+ loop do
404
+ m = TYPE_SPLITTER.match(type)
405
+ break unless m
406
+ result << m[1]
407
+ type = m[2]
408
+ end
409
+
410
+ result
411
+ end
412
+
413
+ def of_type?(value, types)
414
+
415
+ types = types.is_a?(Array) ? types : split_type(types)
416
+
417
+ types.inject(false) do |valid, type|
418
+
419
+ valid ||= case type
420
+ when 'null', 'nil'
421
+ value == nil
422
+ when 'string'
423
+ value.class == String
424
+ when 'number'
425
+ NUMBER_CLASSES.include?(value.class)
426
+ when /^(array|object|hash)<(.*)>$/
427
+ children_of_type?(value, $~[2])
428
+ when 'object', 'hash'
429
+ value.class == Hash
430
+ when 'array'
431
+ value.class == Array
432
+ when 'boolean', 'bool'
433
+ BOOLEAN_CLASSES.include?(value.class)
434
+ else
435
+ raise ArgumentError.new("unknown type '#{type}'")
436
+ end
437
+ end
438
+ end
439
+
440
+ def children_of_type?(values, types)
441
+
442
+ return false unless values.is_a?(Array) or values.is_a?(Hash)
443
+
444
+ values = values.is_a?(Array) ? values : values.values
445
+
446
+ values.each { |v| of_type?(v, types) or return(false) }
447
+
448
+ true
449
+ end
450
+
451
+ def _match(field, value, matches, m, v)
452
+
453
+ value.nil? ? false : value.to_s.match(v) != nil
454
+ end
455
+ alias _m _match
456
+
457
+ def _smatch(field, value, matches, m, v)
458
+
459
+ value.is_a?(String) ? value.match(v) != nil : false
460
+ end
461
+ alias _sm _smatch
462
+
463
+ def _is(field, value, matches, m, v)
464
+
465
+ value == v
466
+ end
467
+
468
+ def _valid(field, value, matches, m, v)
469
+
470
+ v.to_s == 'true'
471
+ end
472
+ alias _v _valid
473
+
474
+ def raise_or_and(valid, field, value)
475
+
476
+ # dealing with :and and :or...
477
+
478
+ if valid == false
479
+
480
+ if o = @rule['or']
481
+ Ruote.set(@hash, field, Rufus::Json.dup(o))
482
+ elsif @rule['and'].nil?
483
+ return [ @rule, field, value ] # validation break
484
+ end
485
+
486
+ elsif a = @rule['and']
487
+
488
+ Ruote.set(@hash, field, Rufus::Json.dup(a))
489
+
490
+ elsif value.nil? and o = (@rule['or'] || @rule['default'])
491
+
492
+ Ruote.set(@hash, field, Rufus::Json.dup(o))
493
+ end
494
+
495
+ nil
496
+ end
497
+ end
498
+
499
+ # Ruote.flatten_keys({ 'a' => 'b', 'c' => [ 1, 2, 3 ] })
500
+ # # =>
501
+ # [ 'a', 'c', 'c.0', 'c.1', 'c.2' ]
502
+ #
503
+ def self.flatten_keys(o, prefix='', accu=[])
504
+
505
+ if o.is_a?(Array)
506
+
507
+ o.each_with_index do |elt, i|
508
+ pre = "#{prefix}#{i}"
509
+ accu << pre
510
+ flatten_keys(elt, pre + '.', accu)
511
+ end
512
+
513
+ elsif o.is_a?(Hash)
514
+
515
+ o.keys.sort.each do |key|
516
+ pre = "#{prefix}#{key}"
517
+ accu << pre
518
+ flatten_keys(o[key], pre + '.', accu)
519
+ end
520
+ end
521
+
522
+ accu
523
+ end
524
+ end
525
+