lib/openwfe/extras/participants/activeparticipants.rb in openwferu-extras-0.9.16 vs lib/openwfe/extras/participants/activeparticipants.rb in openwferu-extras-0.9.17

- old
+ new

@@ -1,8 +1,8 @@ # #-- -# Copyright (c) 2007, John Mettraux OpenWFE.org +# Copyright (c) 2007-2008, John Mettraux, Tomaso Tosolini 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: # @@ -35,15 +35,14 @@ # "made in Japan" # # John Mettraux at openwfe.org # -require 'rubygems' +#require 'rubygems' #require_gem 'activerecord' -gem 'activerecord' -require 'active_record' +gem 'activerecord'; require 'active_record' require 'openwfe/workitem' require 'openwfe/flowexpressionid' require 'openwfe/engine/engine' @@ -79,25 +78,32 @@ t.column :wf_revision, :string t.column :participant_name, :string t.column :store_name, :string t.column :dispatch_time, :timestamp t.column :last_modified, :timestamp + + t.column :yattributes, :text + # when using compact_workitems, attributes are stored here + end + add_index :workitems, :fei, :unique => true add_index :workitems, :wfid add_index :workitems, :wf_name add_index :workitems, :wf_revision add_index :workitems, :participant_name add_index :workitems, :store_name create_table :fields do |t| t.column :fkey, :string, :null => false + t.column :vclass, :string, :null => false t.column :svalue, :string t.column :yvalue, :text t.column :workitem_id, :integer, :null => false end add_index :fields, [ :workitem_id, :fkey ], :unique => true add_index :fields, :fkey + add_index :fields, :vclass add_index :fields, :svalue end def self.down @@ -107,11 +113,11 @@ end # # Reopening InFlowWorkItem to add a 'db_id' attribute. # - class InFlowWorkItem + class OpenWFE::InFlowWorkItem attr_accessor :db_id end # @@ -144,10 +150,13 @@ # class Workitem < ActiveRecord::Base has_many :fields, :dependent => :destroy + serialize :yattributes + + # # 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). # @@ -180,12 +189,26 @@ i.dispatch_time = wi.dispatch_time i.last_modified = nil i.store_name = store_name - wi.attributes.each do |k, v| - i.fields << Field.new_field(k, v) + + # This is a field set by the active participant immediately + # before calling this method. + # the default behavior is "use field method" + + if wi.attributes["compact_workitems"] + + wi.attributes.delete("compact_workitems") + i.yattributes= wi.attributes + else + + i.yattributes= nil + + wi.attributes.each do |k, v| + i.fields << Field.new_field(k, v) + end end i.save! # making sure to throw an exception in case of trouble end @@ -215,28 +238,36 @@ # # (Each time this method is called, it returns a new hash). # def fields_hash - h = {} - fields.each do |f| - h[f.fkey] = f.value + return self.yattributes if self.yattributes + + fields.inject({}) do |r, f| + r[f.fkey] = f.value + r end - h end # # Replaces the current fields of this workitem with the given hash. # # This method modifies the content of the db. # def replace_fields (fhash) - fields.delete_all + if self.yattributes - fhash.each do |k, v| - fields << Field.new_field(k, v) + self.yattributes = fhash + + else + + fields.delete_all + + fhash.each do |k, v| + fields << Field.new_field(k, v) + end end #f = Field.new_field("___map_type", "smap") # # an old trick for backward compatibility with OpenWFEja @@ -251,12 +282,16 @@ # # wi.field("customer_name") # wi.field :customer_name # def field (key) + + if self.yattributes + return self.yattributes[key.to_s] + end - fields.find_by_fkey(key.to_s) + fields.find_by_fkey key.to_s end # # A shortcut method, replies to the workflow engine and removes self # from the database. @@ -272,10 +307,19 @@ alias :forward :reply alias :proceed :reply # + # Simply sets the 'last_modified' field to now. + # (Doesn't save the workitem though). + # + def touch + + self.last_modified = Time.now + end + + # # Opening engine to update its reply method to accept these # active record workitems. # class OpenWFE::Engine @@ -301,11 +345,11 @@ # Returns all the workitems belonging to the stores listed # in the parameter storename_list. # The result is a Hash whose keys are the store names and whose # values are list of workitems. # - def Workitem.find_in_stores (storename_list) + def self.find_in_stores (storename_list) workitems = find_all_by_store_name(storename_list) result = {} @@ -317,12 +361,20 @@ end # # A kind of 'google search' among workitems # - def Workitem.search (search_string, storename_list=nil) + # == Note + # + # when this is used on compact_workitems, it will not be able to search + # info within the fields, because they aren't used by this kind of + # workitems. In this case the search will be limited to participant_name + # + def self.search (search_string, storename_list=nil) + #t = OpenWFE::Timer.new + storename_list = Array(storename_list) if storename_list # participant_name result = find( @@ -332,53 +384,58 @@ :order => "participant_name") # :limit => 10) ids = result.collect { |wi| wi.id } - # svalue + # search in fields - fields = Field.find( - :all, - :conditions => conditions( - "svalue", search_string, storename_list), - :include => :workitem) + fields = Field.search search_string, storename_list + merge_search_results ids, result, fields - merge_search_results(ids, result, fields) + #puts "... took #{t.duration} ms" - # fkey - - fields = Field.find( - :all, - :conditions => conditions( - "fkey", search_string, storename_list), - :include => :workitem) - - merge_search_results(ids, result, fields) - # over. result end + # + # Not really about 'just launched', but rather about finding the first + # workitem for a given process instance (wfid) and a participant. + # It deserves its own method because the workitem could be in a + # subprocess, thus escaping the vanilla find_by_wfid_and_participant() + # + def self.find_just_launched (wfid, participant_name) + + find( + :first, + :conditions => [ + "wfid LIKE ? AND participant_name = ?", + "#{wfid}%", + participant_name ]) + end + protected # # builds the condition (the WHERE clause) for the # search. # - def Workitem.conditions (keyname, search_string, storename_list) + def self.conditions (keyname, search_string, storename_list) + cs = [ "#{keyname} LIKE ?", search_string ] + if storename_list - [ "#{keyname} LIKE ? AND workitems.store_name IN (?)", - search_string, storename_list ] - else - [ "#{keyname} LIKE ?", - search_string ] + + cs[0] = "#{cs[0]} AND workitems.store_name IN (?)" + cs << storename_list end + + cs end - def Workitem.merge_search_results (ids, wis, new_wis) + def self.merge_search_results (ids, wis, new_wis) return if new_wis.size < 1 new_wis.each do |wi| wi = wi.workitem if wi.kind_of?(Field) @@ -412,28 +469,94 @@ # def self.new_field (key, value) f = Field.new f.fkey = key + f.vclass = value.class.to_s f.value = value f end def value= (v) - if v.is_a?(String) + limit = connection.native_database_types[:string][:limit] + + if v.is_a?(String) and v.length <= limit self.svalue = v else self.yvalue = v end end def value - return self.svalue if self.svalue - self.yvalue + self.svalue || self.yvalue end + + # + # Will return all the fields that contain the given text. + # + # Looks in svalue and fkey. Looks as well in yvalue if it contains + # a string. + # + # This method is used by Workitem.search() + # + def self.search (text, storename_list=nil) + + cs = build_search_conditions(text) + + if storename_list + + cs[0] = "(#{cs[0]}) AND workitems.store_name IN (?)" + cs << storename_list + end + + find :all, :conditions => cs, :include => :workitem + end + + protected + + # + # The search operates on the content of these columns + # + FIELDS_TO_SEARCH = %w{ svalue fkey yvalue } + + # + # Builds the condition array for a pseudo text search + # + def self.build_search_conditions (text) + + has_percent = (text.index("%") != nil) + + conds = [] + + conds << FIELDS_TO_SEARCH.collect { |key| + + count = has_percent ? 1 : 4 + + s = ([ "#{key} LIKE ?" ] * count).join(" OR ") + + s = "(vclass = ? AND (#{s}))" if key == 'yvalue' + + s + }.join(" OR ") + + FIELDS_TO_SEARCH.each do |key| + + conds << 'String' if key == 'yvalue' + + conds << text + + unless has_percent + conds << "% #{text} %" + conds << "% #{text}" + conds << "#{text} %" + end + end + + conds + end end # # A basic 'ActiveParticipant'. @@ -467,20 +590,50 @@ # wi = OpenWFE::Extras::Workitem.find_by_participant_name("active0") # # # ... # end # + # == Compact workitems + # + # It is possible to save all the workitem data into a single table, + # the workitems table, without + # splitting info between workitems and fields tables. + # + # You can configure the "compact_workitems" behavior by adding to the + # previous lines: + # + # active0 = engine.register_participant( + # :active0, OpenWFE::Extras::ActiveParticipant) + # + # active0.compact_workitems = true + # + # This behaviour is determined participant per participant, it's ok to + # have a participant instance that compacts will there is another that + # doesn't compact. + # class ActiveParticipant include OpenWFE::LocalParticipant # + # when compact_workitems is set to true, the attributes of a workitem + # are stored in the yattributes column (they are not expanded into + # the Fields table). + # By default, workitem attributes are expanded. + # + attr :compact_workitems, true + + # # This is the method called by the OpenWFEru engine to hand a # workitem to this participant. # def consume (workitem) - Workitem.from_owfe_workitem(workitem) + if compact_workitems + workitem.attributes["compact_workitems"] = true + end + + Workitem.from_owfe_workitem workitem end # # Called by the engine when the whole process instance (or just the # segment of it that sports this participant) is cancelled. @@ -530,9 +683,13 @@ # # This is the method called by the OpenWFEru engine to hand a # workitem to this participant. # def consume (workitem) + + if compact_workitems + workitem.attributes["compact_workitems"] = true + end Workitem.from_owfe_workitem(workitem, @store_name) end #