openwferu 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/{README → README.txt} +16 -13
  2. data/bin/validate-workflow.rb +46 -22
  3. data/examples/README.txt +8 -0
  4. data/examples/homeworkreview.rb +66 -0
  5. data/examples/quotereporter.rb +154 -0
  6. data/lib/{openwferu.rb → openwfe.rb} +6 -8
  7. data/lib/{ru → openwfe}/contextual.rb +11 -3
  8. data/lib/{ru → openwfe/engine}/engine.rb +50 -36
  9. data/lib/{ru/participant.rb → openwfe/engine/file_persisted_engine.rb} +21 -22
  10. data/lib/openwfe/expool/expressionpool.rb +534 -0
  11. data/lib/openwfe/expool/expstorage.rb +184 -0
  12. data/lib/openwfe/expool/journalexpstorage.rb +312 -0
  13. data/lib/openwfe/expool/yamlexpstorage.rb +127 -0
  14. data/lib/{ru → openwfe/expressions}/environment.rb +19 -14
  15. data/lib/{ru → openwfe/expressions}/expressionmap.rb +48 -21
  16. data/lib/{ru → openwfe/expressions}/fe_concurrence.rb +111 -35
  17. data/lib/openwfe/expressions/fe_cursor.rb +236 -0
  18. data/lib/{ru → openwfe/expressions}/fe_define.rb +5 -5
  19. data/lib/openwfe/expressions/fe_fqv.rb +99 -0
  20. data/lib/openwfe/expressions/fe_iterator.rb +182 -0
  21. data/lib/{ru/fe_misc.rb → openwfe/expressions/fe_losfor.rb} +14 -56
  22. data/lib/openwfe/expressions/fe_misc.rb +102 -0
  23. data/lib/{ru → openwfe/expressions}/fe_participant.rb +25 -14
  24. data/lib/{ru → openwfe/expressions}/fe_raw.rb +39 -75
  25. data/lib/{ru/fe_base.rb → openwfe/expressions/fe_sequence.rb} +40 -35
  26. data/lib/{ru → openwfe/expressions}/fe_subprocess.rb +30 -14
  27. data/lib/{ru → openwfe/expressions}/fe_time.rb +59 -31
  28. data/lib/{ru → openwfe/expressions}/fe_utils.rb +42 -26
  29. data/lib/{ru → openwfe/expressions}/fe_value.rb +20 -14
  30. data/lib/openwfe/expressions/flowexpression.rb +434 -0
  31. data/lib/openwfe/expressions/raw_prog.rb +391 -0
  32. data/lib/openwfe/expressions/raw_xml.rb +128 -0
  33. data/lib/openwfe/flowexpressionid.rb +148 -0
  34. data/lib/{ru → openwfe}/logging.rb +10 -6
  35. data/lib/{osocket.rb → openwfe/osocket.rb} +36 -35
  36. data/lib/{otime.rb → openwfe/otime.rb} +71 -21
  37. data/lib/openwfe/participants/atomparticipants.rb +144 -0
  38. data/lib/openwfe/participants/enoparticipant.rb +73 -0
  39. data/lib/openwfe/participants/participant.rb +85 -0
  40. data/lib/{ru → openwfe/participants}/participantmap.rb +40 -12
  41. data/lib/{ru → openwfe/participants}/participants.rb +41 -12
  42. data/lib/openwfe/participants/soapparticipants.rb +96 -0
  43. data/lib/{controlclient.rb → openwfe/rest/controlclient.rb} +12 -13
  44. data/lib/{definitions.rb → openwfe/rest/definitions.rb} +3 -3
  45. data/lib/{exception.rb → openwfe/rest/exception.rb} +3 -3
  46. data/lib/{restclient.rb → openwfe/rest/restclient.rb} +13 -22
  47. data/lib/{worklistclient.rb → openwfe/rest/worklistclient.rb} +33 -46
  48. data/lib/openwfe/rest/xmlcodec.rb +575 -0
  49. data/lib/{ru → openwfe}/rudefinitions.rb +32 -4
  50. data/lib/{ru → openwfe}/service.rb +20 -8
  51. data/lib/openwfe/storage/yamlfilestorage.rb +159 -0
  52. data/lib/{ru → openwfe/util}/dollar.rb +10 -8
  53. data/lib/openwfe/util/lru_cache.rb +149 -0
  54. data/lib/{ru → openwfe/util}/scheduler.rb +18 -10
  55. data/lib/{ru → openwfe/util}/schedulers.rb +7 -7
  56. data/lib/{utils.rb → openwfe/utils.rb} +93 -9
  57. data/lib/openwfe/workitem.rb +366 -0
  58. data/lib/openwfe/worklist/worklists.rb +175 -0
  59. data/test/README.txt +27 -0
  60. data/test/atomtest.rb +99 -0
  61. data/test/crontest.rb +58 -0
  62. data/test/dollartest.rb +3 -3
  63. data/test/feitest.rb +42 -14
  64. data/test/file_persistence_test.rb +93 -0
  65. data/test/flowtestbase.rb +72 -26
  66. data/test/ft_0.rb +1 -97
  67. data/test/ft_0b_sequence.rb +33 -0
  68. data/test/ft_0c_testname.rb +29 -0
  69. data/test/ft_10_loop.rb +48 -0
  70. data/test/ft_11_ppd.rb +292 -0
  71. data/test/ft_12_blockparticipant.rb +45 -0
  72. data/test/ft_13_eno.rb +51 -0
  73. data/test/ft_14_subprocess.rb +90 -0
  74. data/test/ft_14b_subprocess.rb +40 -0
  75. data/test/ft_15_iterator.rb +70 -0
  76. data/test/ft_16_fqv.rb +57 -0
  77. data/test/ft_1_unset.rb +25 -1
  78. data/test/ft_2_concurrence.rb +10 -5
  79. data/test/ft_3_equals.rb +35 -1
  80. data/test/ft_4_misc.rb +16 -1
  81. data/test/ft_5_time.rb +26 -1
  82. data/test/ft_6_lambda.rb +2 -1
  83. data/test/{ft_7_losfor.rb → ft_7_lose.rb} +41 -35
  84. data/test/ft_8_forget.rb +46 -0
  85. data/test/ft_9_cursor.rb +94 -0
  86. data/test/journal_persistence_test.rb +147 -0
  87. data/test/misctest.rb +13 -9
  88. data/test/rake_ptest.rb +18 -0
  89. data/test/rake_qtest.rb +43 -0
  90. data/test/{fulltest.rb → rake_test.rb} +2 -2
  91. data/test/raw_prog_test.rb +236 -0
  92. data/test/rest_test.rb +189 -0
  93. data/test/rutest_utils.rb +1 -1
  94. data/test/timetest.rb +42 -34
  95. metadata +125 -82
  96. data/lib/codec.rb +0 -573
  97. data/lib/flowexpressionid.rb +0 -139
  98. data/lib/ru/expressionpool.rb +0 -382
  99. data/lib/ru/expressionstorage.rb +0 -99
  100. data/lib/ru/flowexpression.rb +0 -272
  101. data/lib/ru/ruutils.rb +0 -70
  102. data/lib/test.rb +0 -222
  103. data/lib/workitem.rb +0 -249
  104. data/test/quicktest.rb +0 -21
@@ -1,6 +1,6 @@
1
1
  #
2
2
  #--
3
- # Copyright (c) 2006, John Mettraux, OpenWFE.org
3
+ # Copyright (c) 2006-2007, John Mettraux, OpenWFE.org
4
4
  # All rights reserved.
5
5
  #
6
6
  # Redistribution and use in source and binary forms, with or without
@@ -39,17 +39,17 @@
39
39
  # John Mettraux at openwfe.org
40
40
  #
41
41
 
42
- require 'workitem'
43
- require 'flowexpressionid'
44
- require 'ru/flowexpression'
45
- require 'ru/fe_utils'
42
+ require 'openwfe/workitem'
43
+ require 'openwfe/flowexpressionid'
44
+ require 'openwfe/expressions/flowexpression'
45
+ require 'openwfe/expressions/fe_utils'
46
46
 
47
47
 
48
48
  #
49
49
  # expressions like 'set' and 'unset' and their utility methods
50
50
  #
51
51
 
52
- module OpenWFEru
52
+ module OpenWFE
53
53
 
54
54
  class SetValueExpression < FlowExpression
55
55
 
@@ -57,7 +57,7 @@ module OpenWFEru
57
57
 
58
58
  if @children.length < 1
59
59
  workitem.attributes[FIELD_RESULT] = \
60
- OpenWFEru::lookup_value(self, workitem)
60
+ OpenWFE::lookup_value(self, workitem)
61
61
  reply(workitem)
62
62
  return
63
63
  end
@@ -69,7 +69,9 @@ module OpenWFEru
69
69
  return
70
70
  end
71
71
 
72
- workitem.attributes[FIELD_RESULT] = child.to_s
72
+ #workitem.attributes[FIELD_RESULT] = child.to_s
73
+ workitem.attributes[FIELD_RESULT] = \
74
+ OpenWFE::fetch_text_content(self, workitem)
73
75
 
74
76
  reply(workitem)
75
77
  end
@@ -98,7 +100,7 @@ module OpenWFEru
98
100
 
99
101
  def handle_child (child, workitem)
100
102
 
101
- child = get_expression_pool().fetch(child)
103
+ child, _fei = get_expression_pool().fetch(child)
102
104
 
103
105
  if child.is_definition?
104
106
  fei = get_expression_pool().evaluate(child, workitem)
@@ -118,7 +120,7 @@ module OpenWFEru
118
120
  # return child
119
121
  # end
120
122
  # end
121
- # return OpenWFEru::lookup_value(self, workitem)
123
+ # return OpenWFE::lookup_value(self, workitem)
122
124
  #end
123
125
  end
124
126
 
@@ -148,14 +150,14 @@ module OpenWFEru
148
150
 
149
151
  def apply (workitem)
150
152
 
151
- value_a = OpenWFEru::lookup_value(self, workitem)
152
- value_b = OpenWFEru::lookup_value(self, workitem, 'other')
153
+ value_a = OpenWFE::lookup_value(self, workitem)
154
+ value_b = OpenWFE::lookup_value(self, workitem, 'other')
153
155
 
154
156
  result = compare(value_a, value_b)
155
157
 
156
158
  ldebug { "apply() result is '#{result}' #{@fei.to_debug_s}" }
157
159
 
158
- OpenWFEru::set_result(workitem, result)
160
+ OpenWFE::set_result(workitem, result)
159
161
 
160
162
  reply_to_parent(workitem)
161
163
  end
@@ -175,12 +177,14 @@ module OpenWFEru
175
177
  protected
176
178
 
177
179
  def compare (a, b)
180
+ #ldebug { "compare() #{fei.to_debug_s}" }
181
+ #ldebug { "compare() '#{a}' == '#{b}'" }
178
182
  return a == b
179
183
  end
180
184
  end
181
185
 
182
186
  #
183
- # <if/>
187
+ # <if/>
184
188
  #
185
189
  class IfExpression < FlowExpression
186
190
 
@@ -209,6 +213,8 @@ module OpenWFEru
209
213
 
210
214
  @condition_replied = true
211
215
 
216
+ store_itself()
217
+
212
218
  if result
213
219
  apply_consequence(1, workitem)
214
220
  else
@@ -0,0 +1,434 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2006-2007, John Mettraux, OpenWFE.org
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # . Redistributions of source code must retain the above copyright notice, this
10
+ # list of conditions and the following disclaimer.
11
+ #
12
+ # . Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ #
16
+ # . Neither the name of the "OpenWFE" nor the names of its contributors may be
17
+ # used to endorse or promote products derived from this software without
18
+ # specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+ #++
32
+ #
33
+ # $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $
34
+ #
35
+
36
+ #
37
+ # "made in Japan"
38
+ #
39
+ # John Mettraux at openwfe.org
40
+ #
41
+
42
+ require 'openwfe/utils'
43
+ require 'openwfe/logging'
44
+ require 'openwfe/contextual'
45
+ require 'openwfe/rudefinitions'
46
+ require 'openwfe/util/dollar'
47
+
48
+
49
+ module OpenWFE
50
+
51
+ #
52
+ # FlowExpression
53
+ #
54
+
55
+ #
56
+ # The base class for all OpenWFE flow expression classes.
57
+ # It gathers all the methods for attributes and variable lookup.
58
+ #
59
+ class FlowExpression
60
+ include Contextual, Logging, OwfeServiceLocator
61
+
62
+ attr_accessor \
63
+ :fei, \
64
+ :parent_id, \
65
+ :environment_id, \
66
+ :attributes, \
67
+ :children, \
68
+ :apply_time
69
+
70
+
71
+ def initialize (fei, parent_id, env_id, app_context, attributes)
72
+
73
+ super()
74
+ #
75
+ # very necessary as this class includes the MonitorMixin
76
+
77
+ @fei = fei
78
+ @parent_id = parent_id
79
+ @environment_id = env_id
80
+ @application_context = app_context
81
+ @attributes = attributes
82
+
83
+ @apply_time = nil
84
+
85
+ #ldebug do
86
+ # "initialize()\n"+
87
+ # "self : #{@fei}\n"+
88
+ # "parent : #{@parent_id}"
89
+ #end
90
+ end
91
+
92
+ #
93
+ # the two most important methods for flow expressions
94
+
95
+ #
96
+ # this default implementation immediately replies to the
97
+ # parent expression
98
+ #
99
+ def apply (workitem)
100
+ get_parent().reply(workitem) if @parent_id
101
+ end
102
+
103
+ #
104
+ # this default implementation immediately replies to the
105
+ # parent expression
106
+ #
107
+ def reply (workitem)
108
+ reply_to_parent(workitem)
109
+ end
110
+
111
+ #
112
+ # Triggers the reply to the parent expression (of course, via the
113
+ # expression pool).
114
+ # Expressions do call this method when their job is done and the flow
115
+ # should resume without them.
116
+ #
117
+ def reply_to_parent (workitem)
118
+ get_expression_pool.reply_to_parent(self, workitem)
119
+ end
120
+
121
+ #
122
+ # a default implementation for cancel :
123
+ # cancels all the children
124
+ # Attempts to return an InFlowWorkItem
125
+ #
126
+ def cancel ()
127
+
128
+ return nil if not @children
129
+
130
+ inflowitem = nil
131
+
132
+ @children.each do |child|
133
+
134
+ next if child.kind_of? String
135
+
136
+ i = get_expression_pool().cancel(child)
137
+ inflowitem = i if not inflowitem
138
+ end
139
+
140
+ return inflowitem
141
+ end
142
+
143
+ #
144
+ # some convenience methods
145
+
146
+ #
147
+ # Returns the parent expression (not as a FlowExpressionId but directly
148
+ # as the FlowExpression instance it is).
149
+ #
150
+ def get_parent ()
151
+ parent, parent_fei = get_expression_pool().fetch(@parent_id)
152
+ return parent
153
+ end
154
+
155
+ #
156
+ # Stores itself in the expression pool.
157
+ # It's very important for expressions in persisted context to save
158
+ # themselves as soon as their state changed.
159
+ # Else this information would be lost at engine restart or
160
+ # simply if the expression got swapped out of memory and reloaded later.
161
+ #
162
+ def store_itself ()
163
+ ldebug { "store_itself() for #{@fei.to_debug_s}" }
164
+ #ldebug { "store_itself() \n#{OpenWFE::caller_to_s(0, 6)}" }
165
+ get_expression_pool().update(self)
166
+ end
167
+
168
+ #
169
+ # Returns the environment instance this expression uses.
170
+ # An environment is a container (a scope) for variables in the process
171
+ # definition.
172
+ # Environments themselves are FlowExpression instances.
173
+ #
174
+ def get_environment ()
175
+ return nil if not @environment_id
176
+ env, fei = get_expression_pool().fetch(@environment_id)
177
+ return env
178
+ end
179
+
180
+ #
181
+ # Returns true if the expression's environment was generated
182
+ # for itself (usually DefineExpression do have such envs)
183
+ #
184
+ def owns_its_environment? ()
185
+
186
+ #ldebug { "owns_its_environment?() #{@fei.to_debug_s}" }
187
+
188
+ return false if not @environment_id
189
+
190
+ ei = @fei.dup()
191
+ vi = @environment_id.dup()
192
+
193
+ ei.expression_name = "neutral"
194
+ vi.expression_name = "neutral"
195
+
196
+ #ldebug do
197
+ # "owns_its_environment?()\n"+
198
+ # " exp #{ei.to_debug_s}\n"+
199
+ # " env #{vi.to_debug_s}"
200
+ #end
201
+
202
+ return ei == vi
203
+ end
204
+
205
+ #
206
+ # Sets a variable in the current environment. Is usually
207
+ # called by the 'set' expression.
208
+ #
209
+ # The variable name may be prefixed by / to indicate process level scope
210
+ # or by // to indicate engine level (global) scope.
211
+ #
212
+ def set_variable (varname, value)
213
+ get_environment()[varname] = value
214
+ end
215
+
216
+ #
217
+ # Looks up the value of a variable in the current environment.
218
+ # If not found locally will lookup at the process level and even
219
+ # further in the engine scope.
220
+ #
221
+ # The variable name may be prefixed by / to indicate process level scope
222
+ # or by // to indicate engine level (global) scope.
223
+ #
224
+ def lookup_variable (varname)
225
+ return get_environment()[varname]
226
+ end
227
+
228
+ #
229
+ # Unsets a variable in the current environment.
230
+ #
231
+ # The variable name may be prefixed by / to indicate process level scope
232
+ # or by // to indicate engine level (global) scope.
233
+ #
234
+ def delete_variable (varname)
235
+ get_environment().delete(varname)
236
+ end
237
+
238
+ #
239
+ # Looks up the value for an attribute of this expression.
240
+ #
241
+ # if the expression is
242
+ #
243
+ # <participant ref="toto" />
244
+ #
245
+ # then
246
+ #
247
+ # participant_expression.lookup_attribute("toto", wi)
248
+ #
249
+ # will yield "toto"
250
+ #
251
+ # The various methods for looking up attributes do perform dollar
252
+ # variable substitution.
253
+ # It's ok to pass a Symbol for the attribute name.
254
+ #
255
+ def lookup_attribute (attname, workitem, default=nil)
256
+
257
+ #attname = attname.to_s
258
+ attname = symbol_to_attname(attname) \
259
+ if attname.kind_of? Symbol
260
+
261
+ #ldebug { "lookup_attribute() '#{attname}' in #{@fei.to_debug_s}" }
262
+
263
+ text = @attributes[attname]
264
+
265
+ return default if not text
266
+ return OpenWFE::dosub(text, self, workitem)
267
+ end
268
+
269
+ #
270
+ # A convenience method for looking up a boolean value.
271
+ # It's ok to pass a Symbol for the attribute name.
272
+ #
273
+ def lookup_boolean_attribute (attname, workitem, default=false)
274
+ value = lookup_attribute(attname, workitem)
275
+ return default if not value
276
+ return value.downcase == 'true'
277
+ end
278
+
279
+ #
280
+ # Returns a hash of all the FlowExpression attributes with their
281
+ # values having undergone dollar variable substitution.
282
+ # If the attributes parameter is set (to an Array instance) then
283
+ # only the attributes named in that list will be looked up.
284
+ #
285
+ # It's ok to pass an array of Symbol instances for the attributes
286
+ # parameter.
287
+ #
288
+ def lookup_attributes (workitem, _attributes=nil)
289
+
290
+ result = {}
291
+
292
+ return result if not @attributes
293
+
294
+ _attributes = @attributes.keys if not _attributes
295
+
296
+ _attributes.each do |k|
297
+ k = k.to_s
298
+ v = @attributes[k]
299
+ result[k] = OpenWFE::dosub(v, self, workitem)
300
+ #ldebug do
301
+ # "lookup_attributes() added '#{k}' -> '#{result[k]}'"
302
+ #end
303
+ end
304
+
305
+ return result
306
+ end
307
+
308
+ def lookup_comma_list_attribute (attname, workitem, default=nil)
309
+
310
+ a = lookup_attribute(attname, workitem, default)
311
+
312
+ return nil if not a
313
+
314
+ result = []
315
+ a.split(',').each do |elt|
316
+ elt = elt.strip
317
+ result << elt if elt.length > 0
318
+ end
319
+ return result
320
+ end
321
+
322
+ #
323
+ # creates a new environment just for this expression
324
+ #
325
+ def new_environment ()
326
+
327
+ ldebug { "new_environment() for #{@fei.to_debug_s}" }
328
+
329
+ @environment_id = @fei.dup
330
+ @environment_id.expression_name = EN_ENVIRONMENT
331
+
332
+ parent_fei = nil
333
+ parent = nil
334
+
335
+ parent, _fei = get_expression_pool().fetch(@parent_id) \
336
+ if @parent_id
337
+
338
+ parent_fei = parent.environment_id if parent
339
+
340
+ env = Environment\
341
+ .new(@environment_id, parent_fei, nil, @application_context, nil)
342
+
343
+ ldebug { "new_environment() is #{env.fei.to_debug_s}" }
344
+
345
+ env.store_itself()
346
+ end
347
+
348
+ #
349
+ # takes care of removing all the children
350
+ #
351
+ def clean_children
352
+ @children.each do |children_fei|
353
+ get_expression_pool.remove(children_fei)
354
+ end
355
+ end
356
+
357
+ #
358
+ # currently only used by dollar.rb and its ${r:some_ruby_code},
359
+ # returns the binding in this flow expression
360
+ #
361
+ def get_binding
362
+ return binding()
363
+ end
364
+
365
+ #
366
+ # Used like the classical Ruby synchronize, but as the OpenWFE
367
+ # expression pool manages its own set of monitores, it's one of those
368
+ # monitors that is used. But the synchronize code looks like the class
369
+ # just included the MonitorMixin. No hassle.
370
+ #
371
+ def synchronize
372
+ get_expression_pool.get_monitor(@fei).synchronize do
373
+ yield
374
+ end
375
+ end
376
+
377
+ #
378
+ # some eye candy
379
+ #
380
+ def to_s
381
+ s = "* #{@fei.to_debug_s}"
382
+
383
+ if @parent_id
384
+ s << "\n `--p--> #{@parent_id.to_debug_s}"
385
+ end
386
+
387
+ if @environment_id
388
+ s << "\n `--e--> #{@environment_id.to_debug_s}"
389
+ end
390
+
391
+ if @children
392
+ @children.each do |c|
393
+ sc = if c.kind_of?(OpenWFE::FlowExpressionId)
394
+ c.to_debug_s
395
+ else
396
+ ">#{c.to_s}<"
397
+ end
398
+ s << "\n `--c--> #{sc}"
399
+ end
400
+ end
401
+
402
+ return s
403
+ end
404
+
405
+ protected
406
+
407
+ def symbol_to_attname (s)
408
+ attname = s.to_s
409
+ attname = OpenWFE::to_dash(attname)
410
+ return attname
411
+ end
412
+ end
413
+
414
+ #
415
+ # A parent class for CursorExpression and IteratorExpression.
416
+ # Takes care of removing templates before replying to the parent
417
+ # expression.
418
+ #
419
+ class WithTemplateExpression < FlowExpression
420
+
421
+ #
422
+ # this overriden method takes care of removing all the children
423
+ # (templates) before replying to its parent.
424
+ #
425
+ def reply_to_parent (workitem)
426
+ @children.each do |child|
427
+ get_expression_pool.remove(child)
428
+ end
429
+ super(workitem)
430
+ end
431
+ end
432
+
433
+ end
434
+