lib/openwfe/workitem.rb in ruote-0.9.18 vs lib/openwfe/workitem.rb in ruote-0.9.19

- old
+ new

@@ -1,34 +1,34 @@ # #-- # Copyright (c) 2005-2008, John Mettraux, OpenWFE.org # All rights reserved. -# -# Redistribution and use in source and binary forms, with or without +# +# 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 +# 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 +# +# 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. #++ # # @@ -41,459 +41,466 @@ require 'openwfe/utils' module OpenWFE - # - # The convention for the result of some expressions is to store - # their result in a workitem field named "__result__". - # - FIELD_RESULT = "__result__" + # + # The convention for the result of some expressions is to store + # their result in a workitem field named "__result__". + # + FIELD_RESULT = "__result__" - #-- - # WORKITEMS - #++ + #-- + # WORKITEMS + #++ - # - # The base class for all the workitems. - # - class WorkItem + # + # The base class for all the workitems. + # + class WorkItem - attr_accessor :last_modified, :attributes + attr_accessor :last_modified, :attributes - def initialize - @last_modified = nil - @attributes = {} - end + def initialize + @last_modified = nil + @attributes = {} + end - alias :fields :attributes - alias :fields= :attributes= + alias :fields :attributes + alias :fields= :attributes= - # - # Sets the last_modified field to now - # - def touch + # + # Sets the last_modified field to now + # + def touch - @last_modified = Time.now - end + @last_modified = Time.now + end - def to_h + def to_h - h = {} - h[:type] = self.class.name - h[:last_modified] = @last_modified - h[:attributes] = @attributes - h - end + h = {} + h['type'] = self.class.name + h['last_modified'] = @last_modified + h['attributes'] = @attributes + h + end - def self.from_h (h) + def self.from_h (h) - #wi = eval("#{h[:type]}.new") - wi = OpenWFE.get_class(h).new - wi.last_modified = h[:last_modified] - wi.attributes = h[:attributes] - wi - end + wi = OpenWFE.get_class(h).new + wi.last_modified = h['last_modified'] + wi.attributes = h['attributes'] + wi + end - # - # A shortcut for - # - # workitem.attributes['key'] - # - # is - # - # workitem['key'] - # - # (Note that - # - # workitem.key - # - # will raise an exception if there is no attribute key). - # - def [] (key) + # + # A shortcut for + # + # workitem.attributes['key'] + # + # is + # + # workitem['key'] + # + # (Note that + # + # workitem.key + # + # will raise an exception if there is no attribute key). + # + def [] (key) - @attributes[key] - end + @attributes[key] + end - # - # A shortcut for - # - # workitem.attributes['key'] = value - # - # is - # - # workitem['key'] = value - # - def []= (key, value) + # + # A shortcut for + # + # workitem.attributes['key'] = value + # + # is + # + # workitem['key'] = value + # + def []= (key, value) - @attributes[key] = value - end + @attributes[key] = value + end - # - # In order to simplify code like : - # - # value = workitem.attributes['xyz'] - # - # to - # - # value = workitem.xyz - # - # or - # - # value = workitem['xyz'] - # - # we overrode method_missing. - # - # workitem.xyz = "my new value" - # - # is also possible - # - def method_missing (m, *args) + # + # In order to simplify code like : + # + # value = workitem.attributes['xyz'] + # + # to + # + # value = workitem.xyz + # + # or + # + # value = workitem['xyz'] + # + # we overrode method_missing. + # + # workitem.xyz = "my new value" + # + # is also possible + # + def method_missing (m, *args) - methodname = m.to_s + methodname = m.to_s - if args.length == 0 - value = @attributes[methodname] - return value if value - raise "Missing attribute '#{methodname}' in workitem" - end + if args.length == 0 + value = @attributes[methodname] + return value if value != nil + raise "Missing attribute '#{methodname}' in workitem" + end - #if methodname == "[]" and args.length == 1 - # value = @attributes[args[0]] - # return value if value - # raise "Missing attribute '#{methodname}' in workitem" - #end - #if methodname == "[]=" and args.length == 2 - # return @attributes[args[0]] = args[1] - #end + #if methodname == "[]" and args.length == 1 + # value = @attributes[args[0]] + # return value if value + # raise "Missing attribute '#{methodname}' in workitem" + #end + #if methodname == "[]=" and args.length == 2 + # return @attributes[args[0]] = args[1] + #end - if args.length == 1 and methodname[-1, 1] == "=" - return @attributes[methodname[0..-2]] = args[0] - end + if args.length == 1 and methodname[-1, 1] == '=' + return @attributes[methodname[0..-2]] = args[0] + end - super(m, args) - end + super(m, args) + end - # - # Produces a deep copy of the workitem - # - def dup - OpenWFE::fulldup(self) - end + # + # Produces a deep copy of the workitem + # + def dup + OpenWFE::fulldup(self) + end - # - # A smarter alternative to - # - # value = workitem.attributes[x] - # - # Via this method, nested values can be reached. For example : - # - # wi = InFlowWorkItem.new() - # wi.attributes = { - # "field0" => "value0", - # "field1" => [ 0, 1, 2, 3, [ "a", "b", "c" ]], - # "field2" => { - # "a" => "AA", - # "b" => "BB", - # "c" => [ "C0", "C1", "C3" ] - # }, - # "field3" => 3, - # "field99" => nil - # } - # - # will verify the following assertions : - # - # assert wi.lookup_attribute("field3") == 3 - # assert wi.lookup_attribute("field1.1") == 1 - # assert wi.lookup_attribute("field1.4.1") == "b" - # assert wi.lookup_attribute("field2.c.1") == "C1" - # - def lookup_attribute (key) - OpenWFE.lookup_attribute(@attributes, key) - end + # + # A smarter alternative to + # + # value = workitem.attributes[x] + # + # Via this method, nested values can be reached. For example : + # + # wi = InFlowWorkItem.new() + # wi.attributes = { + # "field0" => "value0", + # "field1" => [ 0, 1, 2, 3, [ "a", "b", "c" ]], + # "field2" => { + # "a" => "AA", + # "b" => "BB", + # "c" => [ "C0", "C1", "C3" ] + # }, + # "field3" => 3, + # "field99" => nil + # } + # + # will verify the following assertions : + # + # assert wi.lookup_attribute("field3") == 3 + # assert wi.lookup_attribute("field1.1") == 1 + # assert wi.lookup_attribute("field1.4.1") == "b" + # assert wi.lookup_attribute("field2.c.1") == "C1" + # + def lookup_attribute (key) + OpenWFE.lookup_attribute(@attributes, key) + end - # - # The partner to the lookup_attribute() method. Behaves like it. - # - def has_attribute? (key) - OpenWFE.has_attribute?(@attributes, key) - end + # + # The partner to the lookup_attribute() method. Behaves like it. + # + def has_attribute? (key) + OpenWFE.has_attribute?(@attributes, key) + end - # - # set_attribute() accomodates itself with nested key constructs. - # - def set_attribute (key, value) - OpenWFE.set_attribute(@attributes, key, value) - end - - # - # unset_attribute() accomodates itself with nested key constructs. - # - def unset_attribute (key) - OpenWFE.unset_attribute(@attributes, key) - end - - alias :lookup_field :lookup_attribute - alias :has_field? :has_attribute? - alias :set_field :set_attribute - alias :unset_field :unset_attribute - + # + # set_attribute() accomodates itself with nested key constructs. + # + def set_attribute (key, value) + OpenWFE.set_attribute(@attributes, key, value) end # - # The common parent class for InFlowWorkItem and CancelItem. + # unset_attribute() accomodates itself with nested key constructs. # - class InFlowItem < WorkItem + def unset_attribute (key) + OpenWFE.unset_attribute(@attributes, key) + end - attr_accessor :flow_expression_id, :participant_name + alias :lookup_field :lookup_attribute + alias :has_field? :has_attribute? + alias :set_field :set_attribute + alias :unset_field :unset_attribute - def last_expression_id - @flow_expression_id - end + end - def last_expression_id= (fei) - @flow_expression_id = fei - end + # + # The common parent class for InFlowWorkItem and CancelItem. + # + class InFlowItem < WorkItem - # - # Just a handy alias for flow_expression_id - # - alias :fei :flow_expression_id - alias :fei= :flow_expression_id= + attr_accessor :flow_expression_id, :participant_name - def to_h - h = super - h[:flow_expression_id] = @flow_expression_id.to_h - h[:participant_name] = @participant_name - h - end + def last_expression_id + @flow_expression_id + end - def InFlowItem.from_h (h) - wi = super - wi.flow_expression_id = FlowExpressionId.from_h(h[:flow_expression_id]) - wi.participant_name = h[:participant_name] - wi - end + def last_expression_id= (fei) + @flow_expression_id = fei end # - # When the term 'workitem' is used it's generally referring to instances - # of this InFlowWorkItem class. - # InFlowWorkItem are circulating within process instances and carrying - # data around. Their 'payload' is located in their attribute Hash field. + # Just a handy alias for flow_expression_id # - class InFlowWorkItem < InFlowItem + alias :fei :flow_expression_id + alias :fei= :flow_expression_id= - attr_accessor :dispatch_time - attr_accessor :filter + def to_h + h = super + h['flow_expression_id'] = @flow_expression_id.to_h + h['participant_name'] = @participant_name + h + end - # - # In OpenWFEja, workitem history was stored, OpenWFEru doesn't do - # it (no need to copy history over and over). - # - # (deprecated) - # - attr_accessor :history + def InFlowItem.from_h (h) + wi = super + wi.flow_expression_id = FlowExpressionId.from_h(h['flow_expression_id']) + wi.participant_name = h['participant_name'] + wi + end + end - attr_accessor :store - # - # special : added by the ruby lib, not given by the worklist + # + # When the term 'workitem' is used it's generally referring to instances + # of this InFlowWorkItem class. + # InFlowWorkItem are circulating within process instances and carrying + # data around. Their 'payload' is located in their attribute Hash field. + # + class InFlowWorkItem < InFlowItem - # - # Outputting the workitem in a human readable format - # - def to_s + attr_accessor :dispatch_time + attr_accessor :filter - s = "" - s << " #{self.class} :\n" - s << " - flow_expression_id : #{@flow_expression_id}\n" - s << " - participant_name : #{@participant_name}\n" - s << " - last_modified : #{@last_modified}\n" - s << " - dispatch_time : #{@dispatch_time}\n" - s << " - attributes :\n" + # + # in some contexts (ruote-rest, ruote-web2, the web...) workitems + # have an URI + # + attr_accessor :uri - s << " {\n" - @attributes.keys.sort.each do |k| - v = @attributes[k] - s << " #{k.inspect} => #{v.inspect},\n" - end - s << " }" - s - end + # + # In OpenWFEja, workitem history was stored, OpenWFEru doesn't do + # it (no need to copy history over and over). + # + # (deprecated) + # + attr_accessor :history - # - # For some easy YAML encoding, turns the workitem into a Hash - # (Any YAML-enabled platform can thus read it). - # - def to_h + attr_accessor :store + # + # special : added by the ruby lib, not given by the worklist - h = super - h[:dispatch_time] = @dispatch_time - #h[:history] = @history - h[:filter] = @filter - h - end + # + # Outputting the workitem in a human readable format + # + def to_s - # - # Rebuilds an InFlowWorkItem from its hash version. - # - def InFlowWorkItem.from_h (h) + s = "" + s << " #{self.class} :\n" + s << " - flow_expression_id : #{@flow_expression_id}\n" + s << " - participant_name : #{@participant_name}\n" + s << " - last_modified : #{@last_modified}\n" + s << " - dispatch_time : #{@dispatch_time}\n" + s << " - attributes :\n" - wi = super - wi.dispatch_time = h[:dispatch_time] - #wi.history = h[:history] - wi.filter = h[:filter] - wi - end + s << " {\n" + @attributes.keys.sort.each do |k| + v = @attributes[k] + s << " #{k.inspect} => #{v.inspect},\n" + end + s << " }" + s + end - # - # Sets the '__result__' field of this workitem - # - def set_result (result) + # + # For some easy YAML encoding, turns the workitem into a Hash + # (Any YAML-enabled platform can thus read it). + # + def to_h - @attributes[FIELD_RESULT] = result - end + h = super + h['dispatch_time'] = @dispatch_time + #h[:history] = @history + h['filter'] = @filter + h + end - # - # Makes sure the '__result__' field of this workitem is empty. - # - def unset_result + # + # Rebuilds an InFlowWorkItem from its hash version. + # + def InFlowWorkItem.from_h (h) - @attributes.delete FIELD_RESULT - end + wi = super + wi.dispatch_time = h['dispatch_time'] + wi.filter = h['filter'] + wi + end - # - # Just a shortcut (for consistency) of - # - # workitem.attributes["__result__"] - # - def get_result + # + # Sets the '__result__' field of this workitem + # + def set_result (result) - @attributes[FIELD_RESULT] - end + @attributes[FIELD_RESULT] = result + end - # - # Returns true or false. - # - def get_boolean_result + # + # Makes sure the '__result__' field of this workitem is empty. + # + def unset_result - r = get_result - return false unless r - (r == true or r == "true") - end + @attributes.delete FIELD_RESULT end # - # When it needs to cancel a branch of a process instance, the engine - # emits a CancelItem towards it. - # It's especially important for participants to react correctly upon - # receiving a cancel item. + # Just a shortcut (for consistency) of # - class CancelItem < InFlowItem + # workitem.attributes["__result__"] + # + def get_result - def initialize (workitem) - - super() - @flow_expression_id = workitem.fei.dup - end + @attributes[FIELD_RESULT] end # - # LaunchItem instances are used to instantiate and launch processes. - # They contain attributes that are used as the initial payload of the - # workitem circulating in the process instances. + # Returns true or false. # - class LaunchItem < WorkItem + def get_boolean_result - DEF = "__definition" - FIELD_DEF = "field:#{DEF}" + r = get_result + return false unless r + (r == true or r == 'true') + end + end - attr_accessor :workflow_definition_url - #, :description_map + # + # When it needs to cancel a branch of a process instance, the engine + # emits a CancelItem towards it. + # It's especially important for participants to react correctly upon + # receiving a cancel item. + # + class CancelItem < InFlowItem - alias :wfdurl :workflow_definition_url - alias :wfdurl= :workflow_definition_url= + def initialize (workitem) - # - # This constructor will build an empty LaunchItem. - # - # If the optional parameter process_definition is set, the - # definition will be embedded in the launchitem attributes - # for retrieval by the engine. - # - # There are several ways to specify the process definition. - # Here are some examples: - # - # # Use a Ruby class that extends OpenWFE::ProcessDefinition - # LaunchItem.new(MyProcessDefinition) - # - # # Provide an XML process definition as a string - # definition = """ - # <process-definition name="x" revision="y"> - # <sequence> - # <participant ref="alpha" /> - # <participant ref="bravo" /> - # </sequence> - # </process-definition> - # """.strip - # LaunchItem.new(definition) - # - # # Load an XML process definition from a local file - # require 'uri' - # LaunchItem.new(URI.parse("file:///tmp/my_process_definition.xml")) - # - # # If you initialized your engine with - # # {:remote_definitions_allowed => true}, then you can also load an - # # XML process definition from a remote url - # require 'uri' - # LaunchItem.new(URI.parse("http://foo.bar/my_process_definition.xml")) - # - def initialize (process_definition=nil) + super() + @flow_expression_id = workitem.fei.dup + end + end - super() + # + # LaunchItem instances are used to instantiate and launch processes. + # They contain attributes that are used as the initial payload of the + # workitem circulating in the process instances. + # + class LaunchItem < WorkItem - if process_definition - @workflow_definition_url = FIELD_DEF - @attributes[DEF] = process_definition - end - end + DEF = "__definition" + FIELD_DEF = "field:#{DEF}" - # - # Turns the LaunchItem instance into a simple 'hash' (easily - # serializable to other formats). - # - def to_h + attr_accessor :workflow_definition_url + #, :description_map - h = super - h[:workflow_definition_url] = @workflow_definition_url - h - end + alias :wfdurl :workflow_definition_url + alias :wfdurl= :workflow_definition_url= - def self.from_h (h) + # + # This constructor will build an empty LaunchItem. + # + # If the optional parameter process_definition is set, the + # definition will be embedded in the launchitem attributes + # for retrieval by the engine. + # + # There are several ways to specify the process definition. + # Here are some examples: + # + # # Use a Ruby class that extends OpenWFE::ProcessDefinition + # LaunchItem.new(MyProcessDefinition) + # + # # Provide an XML process definition as a string + # definition = """ + # <process-definition name="x" revision="y"> + # <sequence> + # <participant ref="alpha" /> + # <participant ref="bravo" /> + # </sequence> + # </process-definition> + # """.strip + # LaunchItem.new(definition) + # + # # Load an XML process definition from a local file + # require 'uri' + # LaunchItem.new(URI.parse("file:///tmp/my_process_definition.xml")) + # + # # If you initialized your engine with + # # {:remote_definitions_allowed => true}, then you can also load an + # # XML process definition from a remote url + # require 'uri' + # LaunchItem.new(URI.parse("http://foo.bar/my_process_definition.xml")) + # + def initialize (process_definition=nil) - li = super - li.workflow_definition_url = h[:workflow_definition_url] - li - end + super() + + if process_definition + @workflow_definition_url = FIELD_DEF + @attributes[DEF] = process_definition + end end # - # Turns a hash into its corresponding workitem (InFlowWorkItem, CancelItem, - # LaunchItem). + # Turns the LaunchItem instance into a simple 'hash' (easily + # serializable to other formats). # - def OpenWFE.workitem_from_h (h) + def to_h - #wi_class = eval(h[:type]) - wi_class = get_class(h) - wi_class.from_h(h) + h = super + h['workflow_definition_url'] = @workflow_definition_url + h end - def OpenWFE.get_class (h) + def self.from_h (h) - cl = h[:type] - return nil if cl.index(";") - return nil if cl.index(" ") - eval(cl) + li = super + li.workflow_definition_url = h['workflow_definition_url'] + li end + end + + # + # Turns a hash into its corresponding workitem (InFlowWorkItem, CancelItem, + # LaunchItem). + # + def OpenWFE.workitem_from_h (h) + + wi_class = get_class(h) + wi_class.from_h(h) + end + + WI_CLASSES = [ + OpenWFE::LaunchItem, OpenWFE::InFlowWorkItem, OpenWFE::CancelItem + ].inject({}) { |r, c| r[c.to_s] = c; r } + + # + # returns the workitem class for the given hash + # + def OpenWFE.get_class (h) + + WI_CLASSES[h['type']] + end end