Class: Ruote::Exp::FlowExpression

Inherits:
Object
  • Object
show all
Includes:
WithH, WithMeta
Defined in:
lib/ruote/exp/ro_vf.rb,
lib/ruote/exp/ro_filters.rb,
lib/ruote/exp/ro_persist.rb,
lib/ruote/exp/ro_variables.rb,
lib/ruote/exp/ro_attributes.rb,
lib/ruote/exp/flowexpression.rb

Overview

Ruote is a process definition interpreter. It doesn’t directly “read” process definitions, it relies on a parser/generator to produce “abstract syntax trees” that look like

[ expression_name, { ... attributes ... }, [ children_expressions ] ]

The nodes (and leaves) in the trees are expressions. This is the base class for all expressions.

The most visible expressions are “define”, “sequence” and “participant”. Think :

pdef = Ruote.process_definition do
  sequence do
    participant :ref => 'customer'
    participant :ref => 'accounting'
    participant :ref => 'logistics'
  end
end

Each node is an expression…

Constant Summary collapse

COMMON_ATT_KEYS =
%w[
if unless forget timeout on_error on_cancel on_timeout ]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from WithMeta

#class_def, included

Methods included from WithH

included

Constructor Details

#initialize(context, h) ⇒ FlowExpression

Returns a new instance of FlowExpression.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ruote/exp/flowexpression.rb', line 93

def initialize(context, h)

  @context = context

  @msg = nil
    # contains generally the msg the expression got instantiated for

  self.h = h

  h._id ||= Ruote.to_storage_id(h.fei)
  h['type'] ||= 'expressions'
  h.name ||= self.class.expression_names.first
  h.children ||= []
  h.applied_workitem['fei'] = h.fei
  h.created_time ||= Ruote.now_to_utc_s

  h.on_cancel ||= attribute(:on_cancel)
  h.on_error ||= attribute(:on_error)
  h.on_timeout ||= attribute(:on_timeout)
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



83
84
85
# File 'lib/ruote/exp/flowexpression.rb', line 83

def context
  @context
end

#errorObject

Mostly used when the expression is returned via Ruote::Engine#ps(wfid) or Ruote::Engine#processes(). If an error occurred for this flow expression, #ps will set this error field so that it yields the ProcessError.

So, for short, usually, this attribute yields nil.



91
92
93
# File 'lib/ruote/exp/flowexpression.rb', line 91

def error
  @error
end

#hObject

Returns the value of attribute h.



69
70
71
# File 'lib/ruote/exp/flowexpression.rb', line 69

def h
  @h
end

Class Method Details

.do_action(context, msg) ⇒ Object

Called by the worker when it has something to do for a FlowExpression.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/ruote/exp/flowexpression.rb', line 190

def self.do_action(context, msg)

  fei = msg['fei']
  action = msg['action']

  #p msg unless fei

  if action == 'reply' && fei['engine_id'] != context.engine_id
    #
    # the reply has to go to another engine, let's locate the
    # 'engine participant' and give it the workitem/reply
    #
    # see ft_37 for a test/example

    engine_participant =
      context.plist.lookup(fei['engine_id'], msg['workitem'])

    raise(
      "no EngineParticipant found under name '#{fei['engine_id']}'"
    ) unless engine_participant

    engine_participant.reply(fei, msg['workitem'])
    return
  end

  # normal case

  fexp = nil

  3.times do
    fexp = fetch(context, msg['fei'])
    break if fexp
    sleep 0.028
  end
    # this retry system is only useful with ruote-couch

  fexp.send("do_#{action}", msg) if fexp
end

.fetch(context, fei) ⇒ Object

Fetches an expression from the storage and readies it for service.



163
164
165
166
167
168
169
170
# File 'lib/ruote/exp/flowexpression.rb', line 163

def self.fetch(context, fei)

  return nil if fei.nil?

  fexp = context.storage.get('expressions', Ruote.to_storage_id(fei))

  fexp ? from_h(context, fexp) : nil
end

.from_h(context, h) ⇒ Object

Instantiates expression back from hash.



154
155
156
157
158
159
# File 'lib/ruote/exp/flowexpression.rb', line 154

def self.from_h(context, h)

  exp_class = context.expmap.expression_class(h['name'])

  exp_class.new(context, h)
end

.names(*exp_names) ⇒ Object

Keeping track of names and aliases for the expression



178
179
180
181
182
# File 'lib/ruote/exp/flowexpression.rb', line 178

def self.names(*exp_names)

  exp_names = exp_names.collect { |n| n.to_s }
  meta_def(:expression_names) { exp_names }
end

Instance Method Details

#ancestor?(fei) ⇒ Boolean

Returns true if the given fei points to an expression in the parent chain of this expression.

Returns:

  • (Boolean)


579
580
581
582
583
584
585
586
587
# File 'lib/ruote/exp/flowexpression.rb', line 579

def ancestor?(fei)

  fei = fei.to_h if fei.respond_to?(:to_h)

  return false unless h.parent_id
  return true if h.parent_id == fei

  parent.ancestor?(fei)
end

#att(key, values, opts = {}) ⇒ Object

Returns the value for attribute ‘key’, this value should be present in the array list ‘values’. If not, the default value is returned. By default, the default value is the first element of ‘values’.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/ruote/exp/ro_attributes.rb', line 75

def att(key, values, opts={})

  default = opts[:default] || values.first

  val = attribute(key)
  val = val.to_s if val

  #raise(
  #  ArgumentError.new("attribute '#{key}' missing in #{tree}")
  #) if opts[:mandatory] && val == nil
  #raise(
  #  ArgumentError.new("attribute '#{key}' has invalid value in #{tree}")
  #) if opts[:enforce] && (not values.include?(val))

  values.include?(val) ? val : default
end

#attribute(n, workitem = h.applied_workitem, options = {}) ⇒ Object

Looks up the value for attribute n.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ruote/exp/ro_attributes.rb', line 48

def attribute(n, workitem=h.applied_workitem, options={})

  n = n.to_s

  default = options[:default]
  escape = options[:escape]
  string = options[:to_s] || options[:string]

  v = attributes[n]

  v = if v == nil
    default
  elsif escape
    v
  else
    @context.dollar_sub.s(v, self, workitem)
  end

  v = v.to_s if v and string

  v
end

#attribute_text(workitem = h.applied_workitem) ⇒ Object

Given something like

sequence do
  participant 'alpha'
end

in the context of the participant expression

attribute_text()

will yield ‘alpha’.

Note : an empty text returns ”, not the nil value.



153
154
155
156
157
158
# File 'lib/ruote/exp/ro_attributes.rb', line 153

def attribute_text(workitem=h.applied_workitem)

  text = attributes.keys.find { |k| attributes[k] == nil }

  @context.dollar_sub.s(text.to_s, self, workitem)
end

#attributesObject

Returns the attributes of this expression (like { ‘ref’ => ‘toto’ } or { ‘timeout’ => ‘2d’ }.



696
697
698
699
# File 'lib/ruote/exp/flowexpression.rb', line 696

def attributes

  tree[1]
end

#cancel(flavour) ⇒ Object

This default implementation cancels all the [registered] children of this expression.



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/ruote/exp/flowexpression.rb', line 439

def cancel(flavour)

  return reply_to_parent(h.applied_workitem) if h.children.empty?
    #
    # there are no children, nothing to cancel, let's just reply to
    # the parent expression

  do_persist || return
    #
    # before firing the cancel message to the children
    #
    # if the do_persist returns false, it means it failed, implying this
    # expression is stale, let's return, thus discarding this cancel message

  children.each do |cfei|
    #
    # let's send a cancel message to each of the children
    #
    # maybe some of them are gone or have not yet been applied, anyway,
    # the message are sent

    @context.storage.put_msg(
      'cancel',
      'fei' => cfei,
      'parent_id' => h.fei, # indicating that this is a "cancel child"
      'flavour' => flavour)
  end

  #if ! children.find { |i| Ruote::Exp::FlowExpression.fetch(@context, i) }
  #  #
  #  # since none of the children could be found in the storage right now,
  #  # it could mean that all children are already done or it could mean
  #  # that they are not yet applied...
  #  #
  #  # just to be sure let's send a new cancel message to this expression
  #  #
  #  # it's very important, since if there is no child to cancel the parent
  #  # the flow might get stuck here
  #  @context.storage.put_msg(
  #    'cancel',
  #    'fei' => h.fei,
  #    'flavour' => flavour)
  #end
end

#compile_atts(opts = {}) ⇒ Object

Returns a Hash containing all attributes set for an expression with their values resolved.



116
117
118
119
120
121
122
# File 'lib/ruote/exp/ro_attributes.rb', line 116

def compile_atts(opts={})

  attributes.keys.inject({}) { |r, k|
    r[k] = attribute(k, h.applied_workitem, opts)
    r
  }
end

#compile_variablesObject

Returns a fresh hash of all the variables visible from this expression.

This is used mainly when forgetting an expression.



44
45
46
47
48
49
50
# File 'lib/ruote/exp/ro_variables.rb', line 44

def compile_variables

  vars = h.parent_id ? parent.compile_variables : {}
  vars.merge!(h.variables) if h.variables

  vars
end

#do_apply(msg) ⇒ Object

Called by the worker when it has just created this FlowExpression and wants to apply it.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/ruote/exp/flowexpression.rb', line 232

def do_apply(msg)

  @msg = Ruote.fulldup(msg)

  if not Condition.apply?(attribute(:if), attribute(:unless))

    return reply_to_parent(h.applied_workitem)
  end

  if attribute(:forget).to_s == 'true'

    pi = h.parent_id
    wi = Ruote.fulldup(h.applied_workitem)

    h.variables = compile_variables
    h.parent_id = nil
    h.forgotten = true

    @context.storage.put_msg('reply', 'fei' => pi, 'workitem' => wi) if pi
      # reply to parent immediately (if there is a parent)

  elsif attribute(:lose).to_s == 'true'

    h.lost = true
  end

  filter

  consider_tag
  consider_timeout

  apply
end

#do_cancel(msg) ⇒ Object

The raw handling of messages passed to expressions (the fine handling is done in the #cancel method).



388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/ruote/exp/flowexpression.rb', line 388

def do_cancel(msg)

  @msg = Ruote.fulldup(msg)

  flavour = msg['flavour']

  return if h.state == 'cancelling' && flavour != 'kill'
    # cancel on cancel gets discarded

  return if h.state == 'failed' && flavour == 'timeout'
    # do not timeout expressions that are "in error" (failed)

  @msg = Ruote.fulldup(msg)

  h.state = case flavour
    when 'kill' then 'dying'
    when 'timeout' then 'timing_out'
    else 'cancelling'
  end

  h.applied_workitem['fields']['__timed_out__'] = [
    h.fei, Ruote.now_to_utc_s, tree.first, compile_atts
  ] if h.state == 'timing_out'

  if h.state == 'cancelling'

    if t = msg['on_cancel']

      h.on_cancel = t

    elsif hra = msg['re_apply']

      hra = {} if hra == true

      h.on_re_apply = hra['tree'] || tree

      if fs = hra['fields']
        h.applied_workitem['fields'] = fs
      end
      if mfs = hra['merge_in_fields']
        h.applied_workitem['fields'].merge!(mfs)
      end
    end
  end

  cancel(flavour)
end

#do_fail(msg) ⇒ Object

Called when handling an on_error, will place itself in a ‘failing’ state and cancel the children (when the reply from the children comes back, the on_reply will get triggered).



488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/ruote/exp/flowexpression.rb', line 488

def do_fail(msg)

  @msg = Ruote.fulldup(msg)

  @h['state'] = 'failing'
  @h['applied_workitem'] = msg['workitem']

  if h.children.size < 1
    reply_to_parent(@h['applied_workitem'])
  else
    persist_or_raise
    h.children.each { |i| @context.storage.put_msg('cancel', 'fei' => i) }
  end
end

#do_pause(msg) ⇒ Object

Expression received a “pause” message. Will put the expression in the “paused” state and then pass the message to the children.

If the expression is in a non-nil state (failed, timed_out, …), the message will be ignored.



509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/ruote/exp/flowexpression.rb', line 509

def do_pause(msg)

  return if h.state != nil

  h['state'] = 'paused'

  do_persist || return

  h.children.each { |i|
    @context.storage.put_msg('pause', 'fei' => i)
  } unless msg['breakpoint']
end

#do_persistObject



96
97
98
99
# File 'lib/ruote/exp/ro_persist.rb', line 96

def do_persist

  do_p(true)
end

#do_reply(msg) ⇒ Object Also known as: do_receive

Wraps #reply (does the administrative part of the reply work).



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/ruote/exp/flowexpression.rb', line 337

def do_reply(msg)

  @msg = Ruote.fulldup(msg)
    # keeping the message, for 'retry' in collision cases

  workitem = msg['workitem']
  fei = workitem['fei']

  if ut = msg['updated_tree']
    ct = tree.dup
    ct.last[Ruote::FlowExpressionId.child_id(fei)] = ut
    update_tree(ct)
  end

  h.children.delete(fei)
    # accept without any check ?

  if h.state == 'paused'

    (h['paused_replies'] ||= []) << msg

    do_persist

  elsif h.state != nil # failing or timing out ...

    if h.children.size < 1
      reply_to_parent(workitem)
    else
      persist_or_raise # for the updated h.children
    end

  else # vanilla reply

    reply(workitem)
  end
end

#do_resume(msg) ⇒ Object

Will “unpause” the expression (if it was paused), and trigger any ‘paused_replies’ (replies that came while the expression was paused).



525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/ruote/exp/flowexpression.rb', line 525

def do_resume(msg)

  return if h.state != 'paused'

  h['state'] = nil
  replies = h.delete('paused_replies') || []

  do_persist || return

  h.children.each { |i| @context.storage.put_msg('resume', 'fei' => i) }
    # resume children

  replies.each { |m| @context.storage.put_msg(m.delete('action'), m) }
    # trigger replies
end

#do_unpersistObject



101
102
103
104
# File 'lib/ruote/exp/ro_persist.rb', line 101

def do_unpersist

  do_p(false)
end

#expand_atts(opts = {}) ⇒ Object

Like compile_atts, but the keys are expanded as well.

Useful for things like

set "f:${v:field_name}" => "${v:that_variable}"


130
131
132
133
134
135
136
137
# File 'lib/ruote/exp/ro_attributes.rb', line 130

def expand_atts(opts={})

  attributes.keys.inject({}) { |r, k|
    kk = @context.dollar_sub.s(k, self, h.applied_workitem)
    r[kk] = attribute(k, h.applied_workitem, opts)
    r
  }
end

#feiObject

Returns the Ruote::FlowExpressionId for this expression.



123
124
125
126
# File 'lib/ruote/exp/flowexpression.rb', line 123

def fei

  Ruote::FlowExpressionId.new(h.fei)
end

#handle_on_error(msg, error) ⇒ Object

Looks up parent with on_error attribute and triggers it



622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
# File 'lib/ruote/exp/flowexpression.rb', line 622

def handle_on_error(msg, error)

  return false if h.state == 'failing'

  oe_parent = lookup_on_error

  return false unless oe_parent
    # no parent with on_error attribute found

  handler = oe_parent.on_error.to_s

  return false if handler == ''
    # empty on_error handler nullifies ancestor's on_error

  workitem = msg['workitem']

  workitem['fields']['__error__'] = {
    'fei' => fei,
    'at' => Ruote.now_to_utc_s,
    'class' => error.class.to_s,
    'message' => error.message,
    'trace' => error.backtrace
  }

  @context.storage.put_msg(
    'fail',
    'fei' => oe_parent.h.fei,
    'workitem' => workitem)

  true # yes, error is being handled.
end

#has_attribute(*args) ⇒ Object Also known as: has_att

Given a list of attribute names, returns the first attribute name for which there is a value.



37
38
39
40
41
42
# File 'lib/ruote/exp/ro_attributes.rb', line 37

def has_attribute(*args)

  args.each { |a| a = a.to_s; return a if attributes[a] != nil }

  nil
end

#initial_persistObject

Persists and fetches the _rev identifier from the storage.

Only used by the worker when creating the expression.



41
42
43
44
45
46
47
48
49
50
# File 'lib/ruote/exp/ro_persist.rb', line 41

def initial_persist

  r = @context.storage.put(@h, :update_rev => true)

  #t = Thread.current.object_id.to_s[-3..-1]
  #puts "+ per #{h.fei['expid']} #{tree[0]} r#{h._rev} t#{t} -> #{r.class}"
  #Ruote.p_caller('+ per')

  raise_or_return('initial_persist failed', r)
end

#iterative_var_lookup(k) ⇒ Object

TODO : redoc rewrite needed

This method is mostly used by the worker when looking up a process name or participant name bound under a variable.



115
116
117
118
119
120
121
122
# File 'lib/ruote/exp/ro_variables.rb', line 115

def iterative_var_lookup(k)

  v = lookup_variable(k)

  return [ k, v ] unless (v.is_a?(String) or v.is_a?(Symbol))

  iterative_var_lookup(v)
end

#launch_sub(pos, subtree, opts = {}) ⇒ Object

Launches a subprocesses (usually called from the #apply of certain expression implementations.



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/ruote/exp/flowexpression.rb', line 548

def launch_sub(pos, subtree, opts={})

  i = h.fei.merge(
    'subid' => Ruote.generate_subid(h.fei.inspect),
    'expid' => pos)

  #p '=== launch_sub ==='
  #p [ :launcher, h.fei['expid'], h.fei['subid'], h.fei['wfid'] ]
  #p [ :launched, i['expid'], i['subid'], i['wfid'] ]

  forget = opts[:forget]

  register_child(i) unless forget

  variables = (
    forget ? compile_variables : {}
  ).merge!(opts[:variables] || {})

  @context.storage.put_msg(
    'launch',
    'fei' => i,
    'parent_id' => forget ? nil : h.fei,
    'tree' => subtree,
    'workitem' => opts[:workitem] || h.applied_workitem,
    'variables' => variables,
    'forgotten' => forget)
end

#lookup_on_errorObject

Looks up “on_error” attribute



591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/ruote/exp/flowexpression.rb', line 591

def lookup_on_error

  if h.on_error

    self

  elsif h.parent_id

    par = parent
      # :( get_parent would probably be a better name for #parent

    #if par.nil? && ($DEBUG || ARGV.include?('-d'))
    #  puts "~~"
    #  puts "parent gone for"
    #  puts "fei          #{Ruote.sid(h.fei)}"
    #  puts "tree         #{tree.inspect}"
    #  puts "replying to  #{Ruote.sid(h.parent_id)}"
    #  puts "~~"
    #end
      # is sometimes helpful during debug sessions

    par ? par.lookup_on_error : nil

  else

    nil
  end
end

#lookup_val(att_options = {}) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/ruote/exp/ro_attributes.rb', line 104

def lookup_val(att_options={})

  lval(
    VV,
    s_cartesian(%w[ v var variable ], VV),
    s_cartesian(%w[ f fld field ], VV),
    att_options)
end

#lookup_val_prefix(prefix, att_options = {}) ⇒ Object

prefix = ‘on’ => will lookup on, on_val, on_value, on_v, on_var, on_variable, on_f, on_fld, on_field…



95
96
97
98
99
100
101
102
# File 'lib/ruote/exp/ro_attributes.rb', line 95

def lookup_val_prefix(prefix, att_options={})

  lval(
    [ prefix ] + [ 'val', 'value' ].map { |s| "#{prefix}_#{s}" },
    %w[ v var variable ].map { |s| "#{prefix}_#{s}" },
    %w[ f fld field ].map { |s| "#{prefix}_#{s}" },
    att_options)
end

#lookup_variable(var, prefix = nil) ⇒ Object Also known as: v, lv

Looks up the value of a variable in expression tree (seen from a leaf, it looks more like a stack than a tree)



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ruote/exp/ro_variables.rb', line 55

def lookup_variable(var, prefix=nil)

  var, prefix = split_prefix(var, prefix)

  return @context.storage.get_engine_variable(var) \
    if prefix.length >= 2

  return parent.lookup_variable(var, prefix) \
    if h.parent_id && prefix.length >= 1

  if h.variables

    val = Ruote.lookup(h.variables, var)

    return val if val != nil
  end

  if h.parent_id && h.parent_id['engine_id'] == @context.engine_id
    #
    # do not lookup variables in a remote engine ...

    (return parent.lookup_variable(var, prefix)) rescue nil
      # if the lookup fails (parent gone) then rescue and let go
  end

  @context.storage.get_engine_variable(var)
end

#nameObject

Returns the name of this expression, like ‘sequence’, ‘participant’, ‘cursor’, etc…



688
689
690
691
# File 'lib/ruote/exp/flowexpression.rb', line 688

def name

  tree[0]
end

#parentObject

Fetches the parent expression, or returns nil if there is no parent expression.



139
140
141
142
# File 'lib/ruote/exp/flowexpression.rb', line 139

def parent

  Ruote::Exp::FlowExpression.fetch(@context, h.parent_id)
end

#parent_idObject

Returns the Ruote::FlowExpressionIf of the parent expression, or nil if there is no parent expression.



131
132
133
134
# File 'lib/ruote/exp/flowexpression.rb', line 131

def parent_id

  h.parent_id ? Ruote::FlowExpressionId.new(h.parent_id) : nil
end

#persist_or_raiseObject Also known as: persist



83
84
85
86
# File 'lib/ruote/exp/ro_persist.rb', line 83

def persist_or_raise

  p_or_raise(true)
end

#reply(workitem) ⇒ Object

A default implementation for all the expressions.



380
381
382
383
# File 'lib/ruote/exp/flowexpression.rb', line 380

def reply(workitem)

  reply_to_parent(workitem)
end

#reply_to_parent(workitem, delete = true) ⇒ Object

FlowExpression call this method when they’re done and they want their parent expression to take over (it will end up calling the #reply of the parent expression).



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/ruote/exp/flowexpression.rb', line 270

def reply_to_parent(workitem, delete=true)

  filter(workitem)

  if h.tagname

    unset_variable(h.tagname)

    Ruote::Workitem.remove_tag(workitem, h.tagname)

    @context.storage.put_msg(
      'left_tag',
      'tag' => h.tagname,
      'fei' => h.fei,
      'workitem' => workitem)
  end

  if h.timeout_schedule_id && h.state != 'timing_out'

    @context.storage.delete_schedule(h.timeout_schedule_id)
  end

  if h.state == 'failing' # on_error is implicit (#fail got called)

    trigger('on_error', workitem)

  elsif h.state == 'cancelling' and h.on_cancel

    trigger('on_cancel', workitem)

  elsif h.state == 'cancelling' and h.on_re_apply

    trigger('on_re_apply', workitem)

  elsif h.state == 'timing_out' and h.on_timeout

    trigger('on_timeout', workitem)

  elsif h.lost and h.state == nil

    # do not reply, sit here (and wait for cancellation probably)

  else # vanilla reply

    (do_unpersist || return) if delete
      # remove expression from storage

    if h.parent_id

      @context.storage.put_msg(
        'reply',
        'fei' => h.parent_id,
        'workitem' => workitem.merge!('fei' => h.fei),
        'updated_tree' => h.updated_tree) # nil most of the time
    else

      @context.storage.put_msg(
        h.forgotten ? 'ceased' : 'terminated',
        'wfid' => h.fei['wfid'],
        'fei' => h.fei,
        'workitem' => workitem)
    end
  end
end

#set_variable(var, val) ⇒ Object

Sets a variable to a given value. (will set at the appropriate level).



94
95
96
97
98
99
# File 'lib/ruote/exp/ro_variables.rb', line 94

def set_variable(var, val)

  fexp, v = locate_var(var)

  fexp.un_set_variable(:set, v, val, (fexp.h.fei != h.fei)) if fexp
end

#to_hObject

Turns this FlowExpression instance into a Hash (well, just hands back the base hash behind it).



147
148
149
150
# File 'lib/ruote/exp/flowexpression.rb', line 147

def to_h

  @h
end

#treeObject

Returns the current version of the tree (returns the updated version if it got updated.



661
662
663
# File 'lib/ruote/exp/flowexpression.rb', line 661

def tree
  h.updated_tree || h.original_tree
end

#tree_childrenObject

Returns the “AST” view on the children of this expression…



703
704
705
706
# File 'lib/ruote/exp/flowexpression.rb', line 703

def tree_children

  tree[2]
end

#try_persistObject



52
53
54
55
56
57
58
59
60
61
# File 'lib/ruote/exp/ro_persist.rb', line 52

def try_persist

  r = @context.storage.put(@h)

  #t = Thread.current.object_id.to_s[-3..-1]
  #puts "+ per #{h.fei['expid']} #{tree[0]} r#{h._rev} t#{t} -> #{r.class}"
  #Ruote.p_caller('+ per')

  r
end

#try_unpersistObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ruote/exp/ro_persist.rb', line 63

def try_unpersist

  r = @context.storage.delete(@h)

  #t = Thread.current.object_id.to_s[-3..-1]
  #puts "- unp #{h.fei['expid']} #{tree[0]} r#{h._rev} t#{t} -> #{r.class}"
  #Ruote.p_caller('- unp')

  return r if r

  #if h.has_error
  err = @context.storage.get('errors', "err_#{Ruote.to_storage_id(h.fei)}")
  @context.storage.delete(err) if err
  #end
    # removes any error in the journal for this expression
    # since it will now be gone, no need to keep track of its errors

  nil
end

#unpersist_or_raiseObject Also known as: unpersist



88
89
90
91
# File 'lib/ruote/exp/ro_persist.rb', line 88

def unpersist_or_raise

  p_or_raise(false)
end

#unset_variable(var) ⇒ Object

Unbinds a variables.



103
104
105
106
107
108
# File 'lib/ruote/exp/ro_variables.rb', line 103

def unset_variable(var)

  fexp, v = locate_var(var)

  fexp.un_set_variable(:unset, v, nil, (fexp.h.fei != h.fei)) if fexp
end

#update_tree(t = nil) ⇒ Object

Updates the tree of this expression

update_tree(t)

will set the updated tree to t

update_tree

will copy (deep copy) the original tree as the updated_tree.

Adding a child to a sequence expression :

seq.update_tree
seq.updated_tree[2] << [ 'participant', { 'ref' => 'bob' }, [] ]
seq.do_persist


681
682
683
# File 'lib/ruote/exp/flowexpression.rb', line 681

def update_tree(t=nil)
  h.updated_tree = t || Ruote.fulldup(h.original_tree)
end

#variablesObject

A shortcut to the variables held in the expression (nil if none held).



35
36
37
38
# File 'lib/ruote/exp/ro_variables.rb', line 35

def variables

  @h['variables']
end