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



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

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



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

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



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

def workflow_diagram(graph_options)
  str = <<-EOS
digraph #{self.class} {
graph [#{graph_options}];
node [shape=box];
edge [len=1];
  EOS

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