Class: Ruote::ProcessStatus
- Inherits:
-
Object
- Object
- Ruote::ProcessStatus
- Defined in:
- lib/ruote/dboard/process_status.rb
Overview
A ‘view’ on the status of a process instance.
Returned by the #process and the #processes methods of Ruote::Dashboard.
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
An array of errors currently plaguing the process instance.
-
#expressions ⇒ Object
readonly
The expressions that compose the process instance.
-
#root_expression ⇒ Object
readonly
Returns the expression at the root of the process instance.
-
#schedules ⇒ Object
readonly
An array of schedules (open structs yielding information about the schedules of this process).
-
#stored_workitems ⇒ Object
readonly
An array of the workitems currently in the storage participant for this process instance.
-
#trackers ⇒ Object
readonly
TODO.
Class Method Summary collapse
-
.fetch(context, wfids, opts) ⇒ Object
Used by Ruote::Dashboard#process and #processes.
Instance Method Summary collapse
-
#all_tags ⇒ Object
Returns a hash tagname => array of feis of all the tags set in the process instance.
-
#all_variables ⇒ Object
Returns a hash fei => variable_hash containing all the variable bindings (expression by expression) of the process instance.
-
#current_tree(fexp = root_expression) ⇒ Object
Returns the current version of the process definition tree.
-
#definition_name ⇒ Object
For a process.
-
#definition_revision ⇒ Object
For a process.
-
#fexp(fei) ⇒ Object
Given a fei, returns the flow expression with that fei (only looks in the expressions stored here, in this ProcessStatus instance, doesn’t query the storage).
- #hinspect(indent, h) ⇒ Object
-
#initialize(context, expressions, sworkitems, errors, schedules, trackers) ⇒ ProcessStatus
constructor
Called by Ruote::Dashboard#processes or Ruote::Dashboard#process.
- #inspect ⇒ Object
-
#last_active ⇒ Object
Returns a parseable UTC datetime string which indicates when the process was last active.
-
#launched_time ⇒ Object
Returns a Time instance indicating when the process instance was launched.
-
#leaves ⇒ Object
Returns the expressions where the flow is currently, ak the leaves of the execution tree.
-
#original_tree ⇒ Object
Returns the process definition tree as it was when this process instance was launched.
-
#past_tags ⇒ Object
Returns the list of “past tags”, tags that have been entered and left.
-
#position ⇒ Object
Returns the ‘position’ of the process.
-
#root_expression_for(fei) ⇒ Object
Given an expression id, returns the root (top ancestor) for its expression.
-
#root_expressions ⇒ Object
Returns a list of all the expressions that have no parent expression.
-
#root_workitem ⇒ Object
Returns the workitem as was applied at the root expression.
-
#tags ⇒ Object
Returns a hash tagname => fei of tags set at the root of the process instance.
-
#to_dot(opts = {}) ⇒ Object
Returns a ‘dot’ representation of the process.
-
#to_h ⇒ Object
Outputs the process status as a hash (easily JSONifiable).
- #to_s ⇒ Object
-
#variables ⇒ Object
Returns the process variables set for this process instance.
-
#wfid ⇒ Object
Returns the unique identifier for this process instance.
-
#workitems ⇒ Object
Returns a list of the workitems currently ‘out’ to participants.
Constructor Details
#initialize(context, expressions, sworkitems, errors, schedules, trackers) ⇒ ProcessStatus
Called by Ruote::Dashboard#processes or Ruote::Dashboard#process.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ruote/dboard/process_status.rb', line 69 def initialize(context, expressions, sworkitems, errors, schedules, trackers) # # preparing data @expressions = expressions.collect { |e| Ruote::Exp::FlowExpression.from_h(context, e) }.sort_by { |e| e.fei.expid } @stored_workitems = sworkitems.map { |h| Ruote::Workitem.new(h) } @errors = errors.sort! { |a, b| a.fei.expid <=> b.fei.expid } @schedules = schedules.sort! { |a, b| a['owner'].sid <=> b['owner'].sid } @root_expression = root_expressions.first # # linking errors and expressions for easy navigation @errors.each do |err| err.flow_expression = @expressions.find { |fexp| fexp.fei == err.fei } err.flow_expression.error = err if err.flow_expression end @trackers = trackers end |
Instance Attribute Details
#errors ⇒ Object (readonly)
An array of errors currently plaguing the process instance. Hopefully, this array is empty.
56 57 58 |
# File 'lib/ruote/dboard/process_status.rb', line 56 def errors @errors end |
#expressions ⇒ Object (readonly)
The expressions that compose the process instance.
40 41 42 |
# File 'lib/ruote/dboard/process_status.rb', line 40 def expressions @expressions end |
#root_expression ⇒ Object (readonly)
Returns the expression at the root of the process instance.
44 45 46 |
# File 'lib/ruote/dboard/process_status.rb', line 44 def root_expression @root_expression end |
#schedules ⇒ Object (readonly)
An array of schedules (open structs yielding information about the schedules of this process)
61 62 63 |
# File 'lib/ruote/dboard/process_status.rb', line 61 def schedules @schedules end |
#stored_workitems ⇒ Object (readonly)
An array of the workitems currently in the storage participant for this process instance.
Do not confuse with #workitems
51 52 53 |
# File 'lib/ruote/dboard/process_status.rb', line 51 def stored_workitems @stored_workitems end |
#trackers ⇒ Object (readonly)
TODO
65 66 67 |
# File 'lib/ruote/dboard/process_status.rb', line 65 def trackers @trackers end |
Class Method Details
.fetch(context, wfids, opts) ⇒ Object
Used by Ruote::Dashboard#process and #processes
546 547 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 575 576 577 578 579 580 581 582 583 584 585 586 |
# File 'lib/ruote/dboard/process_status.rb', line 546 def self.fetch(context, wfids, opts) swfids = wfids.collect { |wfid| /!#{wfid}-\d+$/ } batch = { 'id' => "#{Thread.current.object_id}-#{Time.now.to_f}" } # # some storages may optimize when they can distinguish # which get_many fit in the same batch... exps = context.storage.get_many( 'expressions', wfids, :batch => batch).compact swis = context.storage.get_many( 'workitems', wfids, :batch => batch).compact errs = context.storage.get_many( 'errors', wfids, :batch => batch).compact schs = context.storage.get_many( 'schedules', swfids, :batch => batch).compact # # some slow storages need the compaction... couch... errs = errs.collect { |err| ProcessError.new(err) } schs = schs.collect { |sch| Ruote.schedule_to_h(sch) } by_wfid = {} as = lambda { [ [], [], [], [], [] ] } exps.each { |exp| (by_wfid[exp['fei']['wfid']] ||= as.call)[0] << exp } swis.each { |swi| (by_wfid[swi['fei']['wfid']] ||= as.call)[1] << swi } errs.each { |err| (by_wfid[err.wfid] ||= as.call)[2] << err } schs.each { |sch| (by_wfid[sch['wfid']] ||= as.call)[3] << sch } # TODO: trackers wfids = by_wfid.keys.sort wfids = wfids.reverse if opts[:descending] # re-adjust list of wfids, only take what was found wfids.collect { |wfid| info = by_wfid[wfid] info ? self.new(context, *info) : nil }.compact end |
Instance Method Details
#all_tags ⇒ Object
Returns a hash tagname => array of feis of all the tags set in the process instance.
161 162 163 164 165 166 |
# File 'lib/ruote/dboard/process_status.rb', line 161 def all_variables.remap do |(fei, vars), h| vars.each { |k, v| (h[k] ||= []) << v if Ruote.is_a_fei?(v) } end end |
#all_variables ⇒ Object
Returns a hash fei => variable_hash containing all the variable bindings (expression by expression) of the process instance.
139 140 141 142 143 144 145 146 |
# File 'lib/ruote/dboard/process_status.rb', line 139 def all_variables return nil if @expressions.empty? @expressions.each_with_object({}) { |exp, h| h[exp.fei] = exp.variables if exp.variables } end |
#current_tree(fexp = root_expression) ⇒ Object
Returns the current version of the process definition tree. If no manipulation (gardening) was performed on the tree, this method yields the same result as the #original_tree method.
Returns nil if there are no expressions (happens in the case of an orphan workitem)
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/ruote/dboard/process_status.rb', line 503 def current_tree(fexp=root_expression) return nil unless fexp t = Ruote.fulldup(fexp.tree) fexp.children.each do |cfei| cexp = fexp(cfei) next unless cexp ct = current_tree(cexp) #trigger = ct[1]['_triggered'] #if trigger && trigger != 'on_re_apply' # # # # ignore any on_cancel / on_error / ... # # # #ct = t[2][cexp.child_id] # # loses any change in the re_applied tree # # # # just flag the original tree as _triggered # # loses any change in the re_applied tree # # # #ct = t[2][cexp.child_id] # #ct[1]['_triggered'] = trigger # # # # extracts the new tree, discards the layers around it # # # ot = t[2][cexp.child_id] # ct = ct[2][0][2][0] # ct[1]['_triggered'] = [ trigger, ot[1][trigger] ].join('/') #end # return the real current tree, do not tweak with it! t[2][cexp.child_id] = ct end t end |
#definition_name ⇒ Object
For a process
Ruote.process_definition :name => 'review', :revision => '0.1' do
reviewer
end
will yield ‘review’.
213 214 215 216 217 218 |
# File 'lib/ruote/dboard/process_status.rb', line 213 def definition_name @root_expression && ( @root_expression.attribute('name') || @root_expression.attribute_text) end |
#definition_revision ⇒ Object
For a process
Ruote.process_definition :name => 'review', :revision => '0.1' do
reviewer
end
will yield ‘0.1’.
229 230 231 232 233 234 |
# File 'lib/ruote/dboard/process_status.rb', line 229 def definition_revision @root_expression && ( @root_expression.attribute('revision') || @root_expression.attribute('rev')) end |
#fexp(fei) ⇒ Object
Given a fei, returns the flow expression with that fei (only looks in the expressions stored here, in this ProcessStatus instance, doesn’t query the storage).
592 593 594 595 596 597 |
# File 'lib/ruote/dboard/process_status.rb', line 592 def fexp(fei) fei = Ruote.extract_fei(fei) @expressions.find { |e| e.fei == fei } end |
#hinspect(indent, h) ⇒ Object
363 364 365 366 367 368 369 370 371 372 |
# File 'lib/ruote/dboard/process_status.rb', line 363 def hinspect(indent, h) if h h.collect { |k, v| s << "#{' ' * indent}#{k.inspect}: #{v.inspect}" }.join("\n") else "#{' ' * indent}(nil)" end end |
#inspect ⇒ Object
374 375 376 377 378 379 380 381 382 383 384 385 386 387 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 435 436 437 438 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 |
# File 'lib/ruote/dboard/process_status.rb', line 374 def inspect vars = variables rescue nil avars = (all_variables || {}).remap { |(k, v), h| h[Ruote.sid(k)] = v } s = [ "== #{self.class} ==" ] s << '' s << " wfid: #{wfid}" s << " name: #{definition_name}" s << " revision: #{definition_revision}" s << " last_active: #{last_active}" s << " launched_time: #{launched_time}" s << '' s << " expressions: #{@expressions.size}" s << '' @expressions.each do |e| eflags = %w[ flanking forgotten attached ].each_with_object([]) { |f, a| a << f if e.h[f] } s << " #{e.fei.to_storage_id}" s << " | #{e.name}" s << " | _rev: #{e.h._rev.inspect}" s << " | * #{e.state} *" if e.state s << " | #{e.attributes.inspect}" e.children.each do |ce| s << " | . child-> #{Ruote.sid(ce)}" end if e.children.any? s << " | timers: #{e.h.timers.collect { |t| t[1] }}" if e.h.timers s << " | tagname: #{e.h.tagname}" if e.h.tagname s << " | (#{eflags.join(', ')})" if eflags.any? s << " `-parent--> #{e.h.parent_id ? e.parent_id.to_storage_id : 'nil'}" end s << '' s << " schedules: #{@schedules.size}" if @schedules.size > 0 @schedules.each do |sched| s << " * #{sched['original']}" s << " #{sched['flavour']} #{sched['at']}" s << " #{sched['action']}" s << " #{Ruote.sid(sched['target']) rescue '** no target **'}" end s << '' end s << " stored workitems: #{@stored_workitems.size}" s << '' s << " initial workitem fields:" if @root_expression s << hinspect(4, @root_expression.h.applied_workitem['fields']) else s << " (no root expression identified)" end s << '' s << " variables:"; s << hinspect(4, vars) s << '' s << " all_variables:"; s << hinspect(4, avars) s << '' s << " errors: #{@errors.size}" @errors.each do |e| s << " ***" s << " #{e.fei.to_storage_id} :" if e.fei s << " action: #{e.action}" s << " message: #{e.message}" s << " trace:" e.trace.split("\n").each do |line| s << " #{line}" end s << " details:" (e.details || '').split("\n").each do |line| s << " #{line}" end if e.respond_to?(:deviations) s << " deviations:" (e.deviations || []).each do |line| s << " #{line.inspect}" end end s << " fields:"; s << hinspect(6, e.fields) end # TODO: add trackers s.join("\n") + "\n" end |
#last_active ⇒ Object
Returns a parseable UTC datetime string which indicates when the process was last active.
331 332 333 334 |
# File 'lib/ruote/dboard/process_status.rb', line 331 def last_active @expressions.collect { |fexp| fexp.h.put_at }.max end |
#launched_time ⇒ Object
Returns a Time instance indicating when the process instance was launched.
346 347 348 349 |
# File 'lib/ruote/dboard/process_status.rb', line 346 def launched_time @root_expression && @root_expression.created_time end |
#leaves ⇒ Object
Returns the expressions where the flow is currently, ak the leaves of the execution tree.
Whereas #position only looks at participant expressions (and errors), #leaves looks at any expressions that is a leave (which has no child at this point).
Returns an array of FlowExpression instances. (Note that they may have their attribute #error set).
283 284 285 286 287 288 |
# File 'lib/ruote/dboard/process_status.rb', line 283 def leaves expressions.inject([]) { |a, exp| a.select { |e| ! exp.ancestor?(e.fei) } + [ exp ] } end |
#original_tree ⇒ Object
Returns the process definition tree as it was when this process instance was launched.
339 340 341 342 |
# File 'lib/ruote/dboard/process_status.rb', line 339 def original_tree @root_expression && @root_expression.original_tree end |
#past_tags ⇒ Object
Returns the list of “past tags”, tags that have been entered and left.
The list elements look like:
[ full_tagname, fei_as_string, nil_or_left_status, variables ]
For example:
[ 'a', '0_1_0!8f233fb935c!20120106-jagitepi', nil, {} ]
or
[ 'stage0/stage1', '0_1_0!8fb935c666d!20120106-jagitepi', 'cancelling', nil ]
The second to last entry is nil when the tag (its expression) replied normally, if it was cancelled or something else, the entry contains a string describing the reason (‘cancelling’ here). The last entry is the variables as they were at the tag point when the execution left the tag.
188 189 190 191 192 193 |
# File 'lib/ruote/dboard/process_status.rb', line 188 def (@root_expression ? @root_expression.variables['__past_tags__'] : nil ) || [] end |
#position ⇒ Object
Returns the ‘position’ of the process.
pdef = Ruote.process_definition do
alpha :task => 'clean car'
end
wfid = engine.launch(pdef)
sleep 0.500
engine.process(wfid) # => [["0_0", "alpha", {"task"=>"clean car"}]]
A process with concurrent branches will yield multiple ‘positions’.
It uses #workitems underneath.
If you want to list all the expressions where the “flow currently is” regardless they are participant expressions or errors, look at the #leaves method.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/ruote/dboard/process_status.rb', line 255 def position workitems.collect { |wi| r = [ wi.fei.sid, wi.participant_name ] params = (wi.fields['params'] || {}).dup params.delete('ref') if err = errors.find { |e| e.fei == wi.fei } params['error'] = err. end r << params r } end |
#root_expression_for(fei) ⇒ Object
Given an expression id, returns the root (top ancestor) for its expression.
115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/ruote/dboard/process_status.rb', line 115 def root_expression_for(fei) sfei = Ruote.sid(fei) exp = @expressions.find { |fe| sfei == Ruote.sid(fe.fei) } return nil unless exp return exp if exp.parent_id.nil? root_expression_for(exp.parent_id) end |
#root_expressions ⇒ Object
Returns a list of all the expressions that have no parent expression. The list is sorted with the deeper (closer to the original root) first.
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/ruote/dboard/process_status.rb', line 101 def root_expressions roots = @expressions.select { |e| e.h.parent_id == nil } roots = roots.each_with_object({}) { |e, h| h["#{e.h.fei['expid']}__#{e.h.fei['subid']}"] = e } roots.keys.sort.collect { |k| roots[k] } end |
#root_workitem ⇒ Object
Returns the workitem as was applied at the root expression.
Returns nil if no root expression could be found.
294 295 296 297 298 299 |
# File 'lib/ruote/dboard/process_status.rb', line 294 def root_workitem return nil unless root_expression Ruote::Workitem.new(root_expression.h.applied_workitem) end |
#tags ⇒ Object
Returns a hash tagname => fei of tags set at the root of the process instance.
Returns nil if there is no defined root expression.
153 154 155 156 |
# File 'lib/ruote/dboard/process_status.rb', line 153 def variables ? Hash[variables.select { |k, v| Ruote.is_a_fei?(v) }] : nil end |
#to_dot(opts = {}) ⇒ Object
Returns a ‘dot’ representation of the process. A graph describing the tree of flow expressions that compose the process.
469 470 471 472 473 474 475 476 477 |
# File 'lib/ruote/dboard/process_status.rb', line 469 def to_dot(opts={}) s = [ "digraph \"process wfid #{wfid}\" {" ] @expressions.each { |e| s.push(*e.send(:to_dot, opts)) } @errors.each { |e| s.push(*e.send(:to_dot, opts)) } s << '}' s.join("\n") end |
#to_h ⇒ Object
Outputs the process status as a hash (easily JSONifiable).
481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/ruote/dboard/process_status.rb', line 481 def to_h %w[ expressions errors stored_workitems schedules trackers ].each_with_object({}) do |a, h| k = a == 'stored_workitems' ? 'workitems' : a v = self.send(a) v = v.collect { |e| e.respond_to?(:h) ? e.h : e } h[k] = v end end |
#to_s ⇒ Object
351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/ruote/dboard/process_status.rb', line 351 def to_s '(' + [ "process_status wfid '#{wfid}'", "expressions #{@expressions.size}", "stored_workitems #{@stored_workitems.size}", "errors #{@errors.size}", "schedules #{@schedules.size}", "trackers #{@trackers.size}" ].join(', ') + ')' end |
#variables ⇒ Object
Returns the process variables set for this process instance.
Returns nil if there is no defined root expression.
131 132 133 134 |
# File 'lib/ruote/dboard/process_status.rb', line 131 def variables @root_expression && @root_expression.variables end |
#wfid ⇒ Object
Returns the unique identifier for this process instance.
197 198 199 200 201 202 |
# File 'lib/ruote/dboard/process_status.rb', line 197 def wfid l = [ @expressions, @errors, @stored_workitems ].find { |l| l.any? } l ? l.first.fei.wfid : nil end |
#workitems ⇒ Object
Returns a list of the workitems currently ‘out’ to participants
For example, with an instance of
Ruote.process_definition do
concurrence do
alpha :task => 'clean car'
bravo :task => 'sell car'
end
end
calling engine.process(wfid).workitems will yield two workitems (alpha and bravo).
Warning : do not confuse the workitems here with the workitems held in a storage participant or equivalent.
318 319 320 321 322 323 324 325 326 |
# File 'lib/ruote/dboard/process_status.rb', line 318 def workitems @expressions.select { |fexp| #fexp.is_a?(Ruote::Exp::ParticipantExpression) fexp.h.name == 'participant' }.collect { |fexp| Ruote::Workitem.new(fexp.h.applied_workitem) } end |