Socket
Book a DemoInstallSign in
Socket

flowchart

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

flowchart

1.0.1
bundlerRubygems
Version published
Maintainers
1
Created
Source

= FlowChart

  • Flowchart is a rubygem for StateFlow(state-action-transition) and WorkFlow(user-assigned-assigner) workflow.
  • Flowchart(Stateflow) works out of the box both for ActiveRecord and Non-ActiveRecord Models.
  • Flowchart(Workflow) works out of the box both for ActiveRecord
  • It provides an easy DSL to create a workflow for your model's object.
  • State Flow + Work FLow = Process Flow
  • You can not only define states-action-transitions but also do a lot more with pre-process and post-process steps
  • You can independently use either of StateFlow and WorkFlow or together, the way you need it

== INSTALLATION

gem install flowchart

No other gem dependencies! Tested on Ruby 1.8.7 and Ruby 1.9.3 | Rails 3.2.13 and Rails 2.3.8

= SYNTAX & OPTIONS

== STATEFLOW only

flowchart do init_flowstate :init

flowstate :init do
  preprocess Proc.new { |o| p "blah blah" }
  postprocess :some_method
end

flowstate :uploaded do
    preprocess Proc.new  :do_something
    postprocess Proc.new  { |o| p "File has been uploaded" }
end

action :upload do
  transitions :from => :init, :to => :uploaded, :condition => :file_parsable?
end

end

=== OPTIONS

  • By Default flowchart assumes that the model attribute it works on is "process_status. If you want to override it then : flowchart do state_column :some_other_column ..... end

  • Every flowstate can have options preprocess and postprocess blocks. You can mention any instance methods or Procs/lamdas to execute.

  • In Actions you can mention :condition with evaluate truth or falsity based on which the transition action(:from -> :to) is possible or not is determined. If :condition is not given transition is always evaluated to truth.

  • In Actions you can also mention :branch, when you have multiple :to states from one single :from state then you can mention a ":branch" condition which you can execute and determine which branch state will be the next! Useful in decision trees.

=== Methods

  • obj.{state_name}? returns boolean true/false if object is in that that state

  • obj.{action} returns the transtion to next state on executing the action

  • obj.{action}! returns the transition to next state on executing the action and save after call

  • obj.current_state returns the current_state of the object e.g #<FlowChart::State:0x9e6e3f4 @flowstatename=:uploaded, @before_and_after_works={:postprocess=>#Proc:0x099dc28c@(irb):30, :preprocess=>#Proc:0x099dc3e0@(irb):29}>

  • obj.flowprocessor returns the complete tree of flowchart for the object! e.g. #<FlowChart::FlowProcessor:0x9e6e5e8 @actions= {:close=> #<FlowChart::Action:0x9e6df80 @flowprocessor=#<FlowChart::FlowProcessor:0x9e6e5e8 ...>, @name=:close, @transitions= [#<FlowChart::Transition:0x9e6dea4 @branch=nil, @condition=:file_close?, @from=[:init, :uploaded, :open], @to=:closed>]>, :upload=> #<FlowChart::Action:0x9e6e110 @flowprocessor=#<FlowChart::FlowProcessor:0x9e6e5e8 ...>, @name=:upload, @transitions= [#<FlowChart::Transition:0x9e6e084 @branch=nil, @condition=:file_parsable?, @from=[:init], @to=:uploaded>]>, :process=> #<FlowChart::Action:0x9e6e070 @flowprocessor=#<FlowChart::FlowProcessor:0x9e6e5e8 ...>, @name=:process, @transitions= [#<FlowChart::Transition:0x9e6df94 @branch=:choose_branch, @condition=nil, @from=[:uploaded], @to=[:open, :closed]>]>}, @flowstates= {:init=> #<FlowChart::State:0x9e6e4f8 @before_and_after_works= {:postprocess=>:notify_user, :preprocess=>#Proc:0x099dca84@(irb):24}, @flowstatename=:init>, :open=> #<FlowChart::State:0x9e6e2dc @before_and_after_works={:postprocess=>#Proc:0x099db8f0@(irb):35}, @flowstatename=:open>, :uploaded=> #<FlowChart::State:0x9e6e3f4 @before_and_after_works= {:postprocess=>#Proc:0x099dc28c@(irb):30, :preprocess=>#Proc:0x099dc3e0@(irb):29}, @flowstatename=:uploaded>, :closed=> #<FlowChart::State:0x9e6e1d8 @before_and_after_works= {:postprocess=>:notify_user, :preprocess=>#Proc:0x099daf18@(irb):39}, @flowstatename=:closed>}, @starting_flowstate= #<FlowChart::State:0x9e6e4f8 @before_and_after_works= {:postprocess=>:notify_user, :preprocess=>#Proc:0x099dca84@(irb):24}, @flowstatename=:init>, @starting_flowstate_name=:init, @state_column=:process_status>

== WORKFLOW only

Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing only Work Machine

Require 'active_record'

class User attr_accessor :email def initialize(email) @email=email end end u1=User.new("1@gmail.com") u2=User.new("2@gmail.com") u3=User.new("3@gmail.com") u4=User.new("4@gmail.com")

class SampleWork include FlowChart require 'active_record' attr_accessor :assigned_to,:assigned_by workchart do

  assigned_to_column :assigned_to
  assigned_by_column :assigned_by

  workowner :user do
    goal_time lambda{ 2.days }
    dead_line lambda{ 3.days }
  end

  delegate :feed_pairing_work
  delegate :feed_publishing_work
  delegate :confirmation_work
end

end

=== OPTIONS 1. By Default flowchart assumes that the model attributes for work_assignments are 'assigned_to' & assigned_by' If you want to override it then : flowchart do assigned_to_column :some_other_column1 assigned_by_column :some_other_column2 ..... end

2. Every Workchart belongs to a workowner, here it is User Model. goal_time and dead_line are default SLA you can mention
for the work! You can call these procs later to monitor the SLA in your Application.
  ...
  workowner :user do
    goal_time lambda{ 2.days }
    dead_line lambda{ 3.days }
  end
  ...

3. There can be multiple tasks associated with a work. Each task here is knows as 'delegate'
  ...
  delegate :feed_pairing_work
  ...

4. Once the object of SampleWork is created you can use your delegate tasks and assign users to them\
  ...
   obj.feed_pairing_work(u1,u2)
   # or obj.feed_pairing_work!(u1,u2)
   # mentioning ! will save the obj object after callbacks
  ...

5. We cav have multiple delegates(tasks) which means each of these tasks can have completely separate set of WorkOwners(assigned_by, assigned_to). At any given point 'obj.current_work_owners' should give the work_owners at the moment for each delegates!

6. If you want to see the trace of WorkOwners for each delegates go through the tree 'obj.workprocessor'. You will see for every delegate actions we keep the trace of work_owners which changes i.e yesterday it might be u1,u2 today it might be u3,u4 for a delegate. Keeping trace can be an added feature that can be used for logging in your Application.

=== Methods 1. obj.{current_work_owners} returns the set of current_work_owners for each delegate task e.g. [{:feed_pairing_work=>{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">, :assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">}}, {:feed_publishing_work=>{}}, {:confirmation_work=>{}}]

2. YAML.load(obj.assigned_by) returns the set of work_owners(assiged_by) for each delegate task
  e.g. [{:feed_pairing_work=>#<User:0x9eac744 @email="2@gmail.com">}, {:feed_publishing_work=>""}, {:confirmation_work=>""}]

3. YAML.load(obj.assigned_to) returns the set of work_owners(assiged_to) for each delegate task
  e.g. [{:feed_pairing_work=>#<User:0x9ea9db4 @email="1@gmail.com">}, {:feed_publishing_work=>""}, {:confirmation_work=>""}]

3. obj.{delegate}(user1obj,user2obj) assigns the delegate task where 'user2obj' is assigner and user1.obj is assigned_to

4. obj.workprocessor.work_owners returns the Hash of owner types for this work item with sla configs
  e.g. {:user=>#<FlowChart::WorkOwner:0x9e6d760 @sla={:goal_time=>#<Proc:0x099d86b4@(irb):63>, :dead_line=>#<Proc:0x099d859c@(irb):64>}, @work_owner_model=User>}

5. obj.workprocessor.delegate_actions returns all the delegates actions for this work that has been defined
  e.g. {:feed_pairing_work=>
        #<FlowChart::WorkDelegateAction:0x9e6d404
         @current_work_owners=
          {:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
           :assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">},
         @delegate_action_name=:feed_pairing_work,
         @work_owners_trace=
          [{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
            :assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">}]>,
       :feed_publishing_work=>
        #<FlowChart::WorkDelegateAction:0x9e6d3f0
         @current_work_owners={},
         @delegate_action_name=:feed_publishing_work,
         @work_owners_trace=[]>,
       :confirmation_work=>
        #<FlowChart::WorkDelegateAction:0x9e6d3b4
         @current_work_owners={},
         @delegate_action_name=:confirmation_work,
         @work_owners_trace=[]>}

5. obj.workprocessor returns the complete tree of flowchart for the object!
  e.g. #<FlowChart::WorkProcessor:0x9e6d814
         @assigned_by_column=:assigned_by,
         @assigned_to_column=:assigned_to,
         @delegate_actions=
          {:feed_pairing_work=>
            #<FlowChart::WorkDelegateAction:0x9e6d404
             @current_work_owners=
              {:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
               :assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">},
             @delegate_action_name=:feed_pairing_work,
             @work_owners_trace=
              [{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
                :assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">}]>,
           :feed_publishing_work=>
            #<FlowChart::WorkDelegateAction:0x9e6d3f0
             @current_work_owners={},
             @delegate_action_name=:feed_publishing_work,
             @work_owners_trace=[]>,
           :confirmation_work=>
            #<FlowChart::WorkDelegateAction:0x9e6d3b4
             @current_work_owners={},
             @delegate_action_name=:confirmation_work,
             @work_owners_trace=[]>},
         @work_owners=
          {:user=>
            #<FlowChart::WorkOwner:0x9e6d760
             @sla=
              {:goal_time=>#<Proc:0x099d86b4@(irb):63>,
               :dead_line=>#<Proc:0x099d859c@(irb):64>},
             @work_owner_model=User>}>

== WORKFLOW+STATEFLOW (Complete Flowchart)

Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing State Machine & Work Machine = Flowchart

requires 'active_record' for Workflow

class User attr_accessor :email def initialize(email) @email=email end end u1=User.new("1@gmail.com") u2=User.new("2@gmail.com") u3=User.new("3@gmail.com") u4=User.new("4@gmail.com")

class SampleFlowchart include FlowChart require 'active_record' attr_accessor :process_status attr_accessor :assigned_to,:assigned_by

flowchart do
  init_flowstate :init

  flowstate :init do
    preprocess Proc.new { |o| p "Initializing File" }
    postprocess :notify_user
  end

  flowstate :uploaded do
    preprocess Proc.new  { |o| p "Validating File" }
    postprocess Proc.new  { |o| p "File has been uploaded in system" }
  end

  flowstate :open do
    postprocess :notify_user
    postprocess Proc.new  { |o| p "File has been closed" }
  end

  flowstate :closed do
    preprocess Proc.new  { |o| p "File closed" }
    postprocess :notify_user
  end

  action :upload do
    transitions :from => :init, :to => :uploaded, :condition => :file_parsable?
  end

  action :process do
    transitions :from => :uploaded, :to => [:open, :closed], :branch => :choose_branch
  end

  action :close do
    transitions :from => [:init,:uploaded,:open], :to => :closed, :condition => :file_close?
  end

end

workchart do

  assigned_to_column :assigned_to
  assigned_by_column :assigned_by

  workowner :user do
    goal_time lambda{ 2.days }
    dead_line lambda{ 3.days }
  end

  delegate :feed_pairing_work
  delegate :feed_publishing_work
  delegate :confirmation_work
end

def choose_branch
  6 > 5 ? :open : :closed
end

def notify_user
  p "Notifying User!"
end

def file_parsable?
  3 > 2 ? true : false
end

def file_close?
  1 > 0 ? true : false 
end

end

== Sample showing Stateflow only

Example of a Non-ActiveRecord Class using FlowChart in Ruby

in irb console

class SampleOne include FlowChart attr_accessor :process_status

flowchart do
  init_flowstate :init

  flowstate :init do
    preprocess Proc.new { |o| p "Initializing File" }
    postprocess :notify_user
  end

  flowstate :uploaded do
    preprocess Proc.new  { |o| p "Validating File" }
    postprocess Proc.new  { |o| p "File has been uploaded in system" }
  end

  flowstate :open do
    postprocess :notify_user
    postprocess Proc.new  { |o| p "File has been closed" }
  end

  flowstate :closed do
    preprocess Proc.new  { |o| p "File closed" }
    postprocess :notify_user
  end

  action :upload do
    transitions :from => :init, :to => :uploaded, :condition => :file_parsable?
  end

  action :process do
    transitions :from => :uploaded, :to => [:open, :closed], :branch => :choose_branch
  end

  action :close do
    transitions :from => [:init,:uploaded,:open], :to => :closed, :condition => :file_close?
  end

end

def choose_branch
  6 > 5 ? :open : :closed
end

def notify_user
  p "Notifying User!"
end

def file_parsable?
  3 > 2 ? true : false
end

def file_close?
  1 > 0 ? true : false 
end

end

t=SampleOne.new #SampleOne:0x9a3d500

t.current_state #<FlowChart::State:0x9a3ff1c @flowstatename=:init, @before_and_after_works={:preprocess=>#Proc:0x09a41880@(irb):11, :postprocess=>:notify_user}>

t.upload "Notifying User!" "Validating File" true

t.current_state #<FlowChart::State:0x9a3fe18 @flowstatename=:uploaded, @before_and_after_works={:preprocess=>#Proc:0x09a415ec@(irb):16, :postprocess=>#Proc:0x09a414c0@(irb):17}>

t.process "File has been uploaded in system"

t.current_state #<FlowChart::State:0x9a3fd00 @flowstatename=:open, @before_and_after_works={:postprocess=>#Proc:0x09a41240@(irb):22}>

t.closed? false

t.open? true

t.close "File closed" true

t.current_state #<FlowChart::State:0x9a3fbfc @flowstatename=:closed, @before_and_after_works={:preprocess=>#Proc:0x09a41024@(irb):26, :postprocess=>:notify_user}>

== Samples You can refer to the example codes mentioned here in 'samples' directory

== Save! in ActiveRecord Model

For any action in Stateflow and any delgate action in Work Flow if you choose to follow it up with a '!' then the model object is saved after action call. Similarly for an action in Stateflow and any delgate action in Work Flow if you choose to not follow it up with a '!' it will only update the attributes for the model object, not save it.

FAQs

Package last updated on 03 Oct 2013

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.