Module: Workflow

Defined in:
lib/workflow.rb,
lib/workflow/transactional.rb,
lib/workflow/mongoid_persistence.rb,
lib/workflow/remodel_persistence.rb,
lib/workflow/active_model_persistence.rb,
lib/workflow/state_dependent_validations.rb

Overview

Provides transaction rollback on halt. For now, you can choose between normal halt without any change to ordinary persistence (halt) or halt with transaction rollback (halt_with_rollback!), which will raise an ActiveRecord::Rollback exception. So this only works with ActiveRecord atm.

Defined Under Namespace

Modules: ActiveModelPersistence, MongoidPersistence, RemodelPersistence, StateDependentValidations, Transactional, WorkflowClassMethods, WorkflowInstanceMethods Classes: Event, NoTransitionAllowed, Specification, State, TransitionHalted, WorkflowDefinitionError, WorkflowError

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_workflow_diagram(klass, target_dir = '.', graph_options = 'rankdir="LR", size="7,11.6", ratio="fill"') ⇒ Object

Generates a ‘dot` graph of the workflow. Prerequisite: the `dot` binary. (Download from www.graphviz.org/) You can use this method in your own Rakefile like this:

namespace :doc do
  desc "Generate a graph of the workflow."
  task :workflow => :environment do # needs access to the Rails environment
    Workflow::create_workflow_diagram(Order)
  end
end

You can influence the placement of nodes by specifying additional meta information in your states and transition descriptions. You can assign higher ‘doc_weight` value to the typical transitions in your workflow. All other states and transitions will be arranged around that main line. See also `weight` in the graphviz documentation. Example:

state :new do
  event :approve, :transitions_to => :approved, :meta => {:doc_weight => 8}
end

Parameters:

  • klass

    A class with the Workflow mixin, for which you wish the graphical workflow representation

  • target_dir (String) (defaults to: '.')

    Directory, where to save the dot and the pdf files

  • graph_options (String) (defaults to: 'rankdir="LR", size="7,11.6", ratio="fill"')

    You can change graph orientation, size etc. See graphviz documentation



410
411
412
413
414
415
416
417
418
# File 'lib/workflow.rb', line 410

def self.create_workflow_diagram(klass, target_dir='.', graph_options='rankdir="LR", size="7,11.6", ratio="fill"')
  workflow_name = "#{klass.name.tableize}_workflow".gsub('/', '_')
  fname = File.join(target_dir, "generated_#{workflow_name}")
  File.open("#{fname}.dot", 'w') do |file|
    file.puts klass.new.workflow_diagram(graph_options)
  end
  `dot -Tpdf -o'#{fname}.pdf' '#{fname}.dot'`
  puts "A PDF file was generated at '#{fname}.pdf'"
end

.included(klass) ⇒ Object



371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/workflow.rb', line 371

def self.included(klass)
  klass.send :include, WorkflowInstanceMethods
  klass.extend WorkflowClassMethods

#     [ActiveModelPersistence, MongoidPersistence, RemodelPersistence].each do |konst|
#       if konst.happy_to_be_included_in? klass
#       raise "including #{konst}"
#         raise "including #{konst}"
#         klass.send :include, konst
#       end
#     end
end

Instance Method Details

#workflow_diagram(graph_options) ⇒ Object

Returns a representation of the state diagram for the calling model as a string in dot language. See Workflow.create_workflow_diagram for more deails



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
# File 'lib/workflow.rb', line 423

def workflow_diagram(graph_options)
  str = "digraph \#{self.class} {\ngraph [\#{graph_options}];\nnode [shape=box];\nedge [len=1];\n  EOS\n\n  self.class.workflow_spec.states.each do |state_name, state|\n    state_meta = state.meta\n    if state == self.class.workflow_spec.initial_state\n      str << %Q{  \#{state.name} [label=\"\#{state.name}\", shape=circle];\\n}\n    else\n      str << %Q{  \#{state.name} [label=\"\#{state.name}\", shape=\#{state_meta[:terminal] ? 'doublecircle' : 'box, style=rounded'}];\\n}\n    end\n    state.events.each do |event_name, event|\n      event_meta = event.meta\n      event_meta[:doc_weight] = 6 if event_meta[:main_path]\n      if event_meta[:doc_weight]\n        weight_prop = \", weight=\#{event_meta[:doc_weight]}, penwidth=\#{event_meta[:doc_weight] / 2 || 0.0}\\n\"\n      else\n        weight_prop = ''\n      end\n      str << %Q{  \#{state.name} -> \#{event.transitions_to} [label=\"\#{event_name.to_s.humanize}\" \#{weight_prop}];\\n}\n    end\n  end\n  str << \"}\\n\"\n  return str\nend\n"