# #-- # Copyright (c) 2007, John Mettraux OpenWFE.org # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # . Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # . Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # . Neither the name of the "OpenWFE" nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. #++ # # # "made in Japan" # # John Mettraux at openwfe.org # require 'rubygems' require_gem 'activerecord' require 'openwfe/workitem' require 'openwfe/flowexpressionid' require 'openwfe/participants/participant' module OpenWFE module Extras # # The migration for ActiveParticipant and associated classes. # # There are two tables 'workitems' and 'fields'. As its name implies, # the latter table stores the fields (also called attributes in OpenWFE # speak) of the workitems. # # See Workitem and Field for more details. # # For centralization purposes, the migration and the model are located # in the same source file. It should be quite easy for the Rails hackers # among you to sort that out for a Rails based usage. # class OwfeTables < ActiveRecord::Migration def self.up create_table :workitems do |t| t.column :fei, :string t.column :wfid, :string t.column :wf_name, :string t.column :wf_revision, :string t.column :participant_name, :string t.column :store, :string end create_table :fields do |t| t.column :key, :string, :null => false t.column :value, :text t.column :workitem_id, :integer, :null => false end add_index :fields, [ :workitem_id, :key], :unique => true end def self.down drop_table :workitems drop_table :fields end end # # The ActiveRecord version of an OpenWFEru workitem (InFlowWorkItem). # # One can very easily build a worklist based on a participant name via : # # wl = OpenWFE::Extras::Workitem.find_all_by_participant_name("toto") # puts "found #{wl.size} workitems for participant 'toto'" # # These workitems are not OpenWFEru workitems directly. But the conversion # is pretty easy. # Note that you probaly won't need to do the conversion by yourself, # except for certain advanced scenarii. # # awi = OpenWFE::Extras::Workitem.find_by_participant_name("toto") # # # # returns the first workitem in the database whose participant # # name is 'toto'. # # owi = awi.as_owfe_workitem # # # # Now we have a copy of the reference as a OpenWFEru # # InFlowWorkItem instance. # # awi = OpenWFE::Extras::Workitem.from_owfe_workitem(owi) # # # # turns an OpenWFEru InFlowWorkItem instance into an # # 'active workitem'. # class Workitem < ActiveRecord::Base has_many :fields, :dependent => :destroy # # Returns the flow expression id of this work (its unique OpenWFEru # identifier) as a FlowExpressionId instance. # (within the Workitem it's just stored as a String). # def full_fei OpenWFE::FlowExpressionId.from_s(fei) end # # Generates a (new) Workitem from an OpenWFEru InFlowWorkItem instance. # # This is a 'static' method : # # awi = OpenWFE::Densha::Workitem.from_owfe_workitem(wi) # # (This method will not save the 'ActiveWorkitem'). # def self.from_owfe_workitem (wi) i = Workitem.new i.fei = wi.fei.to_s i.wfid = wi.fei.wfid i.wf_name = wi.fei.workflow_definition_name i.wf_revision = wi.fei.workflow_definition_revision i.participant_name = wi.participant_name wi.attributes.each do |k, v| f = Field.new f.key = k f.value = v i.fields << f end i end # # Turns the densha Workitem into an OpenWFEru InFlowWorkItem. # def as_owfe_workitem wi = OpenWFE::InFlowWorkItem.new wi.fei = full_fei wi.participant_name = participant_name wi.attributes = fields_hash wi end # # Returns a hash version of the 'fields' of this workitem. # # (Each time this method is called, it returns a new hash). # def fields_hash h = {} fields.each do |f| h[f.key] = f.value end h end # # Returns the Field instance with the given key. This method accept # symbols as well as strings as its parameter. # # wi.field("customer_name") # wi.field :customer_name # def field (key) fields.find_by_key(key.to_s) end # # A shortcut method, replies to the workflow engine and removes self # from the database. # Handy for people who don't want to play with an ActiveParticipant # instance when just consuming workitems (that an active participant # pushed in the database). # def reply (engine) engine.reply self.as_owfe_workitem self.destroy end alias :forward :reply alias :proceed :reply end # # A Field (Attribute) of a Workitem. # class Field < ActiveRecord::Base belongs_to :workitem serialize :value # # A quick method for doing # # f = Field.new # f.key = key # f.value = value # # One can then quickly add fields to an [active] workitem via : # # wi.fields << Field.new_field("toto", "b") # def self.new_field (key, value) f = Field.new f.key = key f.value = value f end end # # A basic 'ActiveParticipant'. # A store participant whose store is a set of ActiveRecord tables. # # Sample usage : # # class MyDefinition < OpenWFE::ProcessDefinition # sequence do # active0 # active1 # end # end # # def play_with_the_engine # # engine = OpenWFE::Engine.new # # engine.register_participant( # :active0, OpenWFE::Extras::ActiveParticipant) # engine.register_participant( # :active1, OpenWFE::Extras::ActiveParticipant) # # li = OpenWFE::LaunchItem.new(MyDefinition) # li.customer_name = 'toto' # engine.launch li # # sleep 0.500 # # give some slack to the engine, it's asynchronous after all # # wi = OpenWFE::Extras::Workitem.find_by_participant_name("active0") # # # ... # end # class ActiveParticipant include OpenWFE::LocalParticipant # # This is the method called by the OpenWFEru engine to hand a # workitem to this participant. # def consume (workitem) awi = Workitem.from_owfe_workitem(workitem) # # turns the workitem into an 'active' one awi.save # # and saves it in the db. end # # Called by the engine when the whole process instance (or just the # segment of it that sports this participant) is cancelled. # Will removed the workitem with the same fei as the cancelitem # from the database. # # No expression will be raised if there is no corresponding workitem. # def cancel (cancelitem) Workitem.delete_all([ "fei = ?", cancelitem.fei.to_s ]) end # # When the activity/work/operation whatever is over and the flow # should resume, this is the method to use to hand back the [modified] # workitem to the [local] engine. # def reply_to_engine (workitem) super workitem.as_owfe_workitem # # replies to the workflow engine workitem.destroy # # removes the workitem from the database end end # # TODO : please document me # class ActiveStoreParticipant < ActiveParticipant include Enumerable def each (&block) end end end end