module Workflow

See also README.markdown for documentation

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.

Public Class Methods

create_workflow_diagram(klass, target_dir='.', graph_options='rankdir="LR", size="7,11.6", ratio="fill"') click to toggle source

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

@param klass A class with the Workflow mixin, for which you wish the graphical workflow representation @param [String] target_dir Directory, where to save the dot and the pdf files @param [String] graph_options You can change graph orientation, size etc. See graphviz documentation

# 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) click to toggle source
# 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

Public Instance Methods

workflow_diagram(graph_options) click to toggle source

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

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