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
#