ruote-extras 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/openwfe/extras/engine/db_persisted_engine.rb +100 -0
- data/lib/openwfe/extras/expool/dberrorjournal.rb +189 -0
- data/lib/openwfe/extras/expool/dbexpstorage.rb +353 -0
- data/lib/openwfe/extras/listeners/sqslisteners.rb +146 -0
- data/lib/openwfe/extras/misc/activityfeed.rb +264 -0
- data/lib/openwfe/extras/misc/basecamp.rb +485 -0
- data/lib/openwfe/extras/participants/activeparticipants.rb +739 -0
- data/lib/openwfe/extras/participants/atomfeed_participants.rb +174 -0
- data/lib/openwfe/extras/participants/atompub_participants.rb +268 -0
- data/lib/openwfe/extras/participants/basecamp_participants.rb +87 -0
- data/lib/openwfe/extras/participants/csvparticipants.rb +127 -0
- data/lib/openwfe/extras/participants/sqsparticipants.rb +125 -0
- data/lib/openwfe/extras/participants/twitterparticipants.rb +176 -0
- metadata +70 -0
@@ -0,0 +1,739 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2007-2008, John Mettraux, Tomaso Tosolini OpenWFE.org
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# . Redistributions of source code must retain the above copyright notice, this
|
10
|
+
# list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# . Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
14
|
+
# and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# . Neither the name of the "OpenWFE" nor the names of its contributors may be
|
17
|
+
# used to endorse or promote products derived from this software without
|
18
|
+
# specific prior written permission.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
#++
|
32
|
+
#
|
33
|
+
|
34
|
+
#
|
35
|
+
# "made in Japan"
|
36
|
+
#
|
37
|
+
# John Mettraux at openwfe.org
|
38
|
+
# Tomaso Tosolini
|
39
|
+
#
|
40
|
+
|
41
|
+
#require 'rubygems'
|
42
|
+
|
43
|
+
#require_gem 'activerecord'
|
44
|
+
gem 'activerecord'; require 'active_record'
|
45
|
+
|
46
|
+
|
47
|
+
require 'openwfe/workitem'
|
48
|
+
require 'openwfe/flowexpressionid'
|
49
|
+
require 'openwfe/engine/engine'
|
50
|
+
require 'openwfe/participants/participant'
|
51
|
+
|
52
|
+
|
53
|
+
module OpenWFE
|
54
|
+
module Extras
|
55
|
+
|
56
|
+
#MUTEX = Mutex.new
|
57
|
+
|
58
|
+
#
|
59
|
+
# The migration for ActiveParticipant and associated classes.
|
60
|
+
#
|
61
|
+
# There are two tables 'workitems' and 'fields'. As its name implies,
|
62
|
+
# the latter table stores the fields (also called attributes in OpenWFE
|
63
|
+
# speak) of the workitems.
|
64
|
+
#
|
65
|
+
# See Workitem and Field for more details.
|
66
|
+
#
|
67
|
+
# For centralization purposes, the migration and the model are located
|
68
|
+
# in the same source file. It should be quite easy for the Rails hackers
|
69
|
+
# among you to sort that out for a Rails based usage.
|
70
|
+
#
|
71
|
+
class WorkitemTables < ActiveRecord::Migration
|
72
|
+
|
73
|
+
def self.up
|
74
|
+
|
75
|
+
create_table :workitems do |t|
|
76
|
+
t.column :fei, :string
|
77
|
+
t.column :wfid, :string
|
78
|
+
t.column :wf_name, :string
|
79
|
+
t.column :wf_revision, :string
|
80
|
+
t.column :participant_name, :string
|
81
|
+
t.column :store_name, :string
|
82
|
+
t.column :dispatch_time, :timestamp
|
83
|
+
t.column :last_modified, :timestamp
|
84
|
+
|
85
|
+
t.column :yattributes, :text
|
86
|
+
# when using compact_workitems, attributes are stored here
|
87
|
+
end
|
88
|
+
add_index :workitems, :fei, :unique => true
|
89
|
+
add_index :workitems, :wfid
|
90
|
+
add_index :workitems, :wf_name
|
91
|
+
add_index :workitems, :wf_revision
|
92
|
+
add_index :workitems, :participant_name
|
93
|
+
add_index :workitems, :store_name
|
94
|
+
|
95
|
+
create_table :fields do |t|
|
96
|
+
t.column :fkey, :string, :null => false
|
97
|
+
t.column :vclass, :string, :null => false
|
98
|
+
t.column :svalue, :string
|
99
|
+
t.column :yvalue, :text
|
100
|
+
t.column :workitem_id, :integer, :null => false
|
101
|
+
end
|
102
|
+
add_index :fields, [ :workitem_id, :fkey ], :unique => true
|
103
|
+
add_index :fields, :fkey
|
104
|
+
add_index :fields, :vclass
|
105
|
+
add_index :fields, :svalue
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.down
|
109
|
+
|
110
|
+
drop_table :workitems
|
111
|
+
drop_table :fields
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Reopening InFlowWorkItem to add a 'db_id' attribute.
|
117
|
+
#
|
118
|
+
class OpenWFE::InFlowWorkItem
|
119
|
+
|
120
|
+
attr_accessor :db_id
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# The ActiveRecord version of an OpenWFEru workitem (InFlowWorkItem).
|
125
|
+
#
|
126
|
+
# One can very easily build a worklist based on a participant name via :
|
127
|
+
#
|
128
|
+
# wl = OpenWFE::Extras::Workitem.find_all_by_participant_name("toto")
|
129
|
+
# puts "found #{wl.size} workitems for participant 'toto'"
|
130
|
+
#
|
131
|
+
# These workitems are not OpenWFEru workitems directly. But the conversion
|
132
|
+
# is pretty easy.
|
133
|
+
# Note that you probaly won't need to do the conversion by yourself,
|
134
|
+
# except for certain advanced scenarii.
|
135
|
+
#
|
136
|
+
# awi = OpenWFE::Extras::Workitem.find_by_participant_name("toto")
|
137
|
+
# #
|
138
|
+
# # returns the first workitem in the database whose participant
|
139
|
+
# # name is 'toto'.
|
140
|
+
#
|
141
|
+
# owi = awi.as_owfe_workitem
|
142
|
+
# #
|
143
|
+
# # Now we have a copy of the reference as a OpenWFEru
|
144
|
+
# # InFlowWorkItem instance.
|
145
|
+
#
|
146
|
+
# awi = OpenWFE::Extras::Workitem.from_owfe_workitem(owi)
|
147
|
+
# #
|
148
|
+
# # turns an OpenWFEru InFlowWorkItem instance into an
|
149
|
+
# # 'active workitem'.
|
150
|
+
#
|
151
|
+
class Workitem < ActiveRecord::Base
|
152
|
+
|
153
|
+
has_many :fields, :dependent => :destroy
|
154
|
+
|
155
|
+
serialize :yattributes
|
156
|
+
|
157
|
+
|
158
|
+
#
|
159
|
+
# Returns the flow expression id of this work (its unique OpenWFEru
|
160
|
+
# identifier) as a FlowExpressionId instance.
|
161
|
+
# (within the Workitem it's just stored as a String).
|
162
|
+
#
|
163
|
+
def full_fei
|
164
|
+
|
165
|
+
OpenWFE::FlowExpressionId.from_s(fei)
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# Generates a (new) Workitem from an OpenWFEru InFlowWorkItem instance.
|
170
|
+
#
|
171
|
+
# This is a 'static' method :
|
172
|
+
#
|
173
|
+
# awi = OpenWFE::Extras::Workitem.from_owfe_workitem(wi)
|
174
|
+
#
|
175
|
+
# (This method saves the 'ActiveWorkitem').
|
176
|
+
#
|
177
|
+
def Workitem.from_owfe_workitem (wi, store_name=nil)
|
178
|
+
|
179
|
+
i = nil
|
180
|
+
|
181
|
+
#MUTEX.synchronize do
|
182
|
+
|
183
|
+
i = Workitem.new
|
184
|
+
i.fei = wi.fei.to_s
|
185
|
+
i.wfid = wi.fei.wfid
|
186
|
+
i.wf_name = wi.fei.workflow_definition_name
|
187
|
+
i.wf_revision = wi.fei.workflow_definition_revision
|
188
|
+
i.participant_name = wi.participant_name
|
189
|
+
i.dispatch_time = wi.dispatch_time
|
190
|
+
i.last_modified = nil
|
191
|
+
|
192
|
+
i.store_name = store_name
|
193
|
+
|
194
|
+
i.save!
|
195
|
+
# save workitem before adding any field
|
196
|
+
# making sure it has an id...
|
197
|
+
|
198
|
+
|
199
|
+
# This is a field set by the active participant immediately
|
200
|
+
# before calling this method.
|
201
|
+
# the default behavior is "use field method"
|
202
|
+
|
203
|
+
if wi.attributes["compact_workitems"]
|
204
|
+
|
205
|
+
wi.attributes.delete("compact_workitems")
|
206
|
+
i.yattributes = wi.attributes
|
207
|
+
|
208
|
+
else
|
209
|
+
|
210
|
+
i.yattributes = nil
|
211
|
+
|
212
|
+
wi.attributes.each do |k, v|
|
213
|
+
i.fields << Field.new_field(k, v)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
i.save!
|
218
|
+
# making sure to throw an exception in case of trouble
|
219
|
+
#
|
220
|
+
# damn, insert then update :(
|
221
|
+
|
222
|
+
#end
|
223
|
+
|
224
|
+
i
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# Turns the densha Workitem into an OpenWFEru InFlowWorkItem.
|
229
|
+
#
|
230
|
+
def as_owfe_workitem
|
231
|
+
|
232
|
+
wi = OpenWFE::InFlowWorkItem.new
|
233
|
+
|
234
|
+
wi.fei = full_fei
|
235
|
+
wi.participant_name = participant_name
|
236
|
+
wi.attributes = fields_hash
|
237
|
+
|
238
|
+
wi.dispatch_time = dispatch_time
|
239
|
+
wi.last_modified = last_modified
|
240
|
+
|
241
|
+
wi.db_id = self.id
|
242
|
+
|
243
|
+
wi
|
244
|
+
end
|
245
|
+
|
246
|
+
#
|
247
|
+
# Returns a hash version of the 'fields' of this workitem.
|
248
|
+
#
|
249
|
+
# (Each time this method is called, it returns a new hash).
|
250
|
+
#
|
251
|
+
def fields_hash
|
252
|
+
|
253
|
+
return self.yattributes if self.yattributes
|
254
|
+
|
255
|
+
fields.inject({}) do |r, f|
|
256
|
+
r[f.fkey] = f.value
|
257
|
+
r
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
#
|
262
|
+
# Replaces the current fields of this workitem with the given hash.
|
263
|
+
#
|
264
|
+
# This method modifies the content of the db.
|
265
|
+
#
|
266
|
+
def replace_fields (fhash)
|
267
|
+
|
268
|
+
if self.yattributes
|
269
|
+
|
270
|
+
self.yattributes = fhash
|
271
|
+
|
272
|
+
else
|
273
|
+
|
274
|
+
fields.delete_all
|
275
|
+
|
276
|
+
fhash.each do |k, v|
|
277
|
+
fields << Field.new_field(k, v)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
#f = Field.new_field("___map_type", "smap")
|
282
|
+
#
|
283
|
+
# an old trick for backward compatibility with OpenWFEja
|
284
|
+
|
285
|
+
save!
|
286
|
+
# making sure to throw an exception in case of trouble
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Returns the Field instance with the given key. This method accept
|
291
|
+
# symbols as well as strings as its parameter.
|
292
|
+
#
|
293
|
+
# wi.field("customer_name")
|
294
|
+
# wi.field :customer_name
|
295
|
+
#
|
296
|
+
def field (key)
|
297
|
+
|
298
|
+
if self.yattributes
|
299
|
+
return self.yattributes[key.to_s]
|
300
|
+
end
|
301
|
+
|
302
|
+
fields.find_by_fkey key.to_s
|
303
|
+
end
|
304
|
+
|
305
|
+
#
|
306
|
+
# A shortcut method, replies to the workflow engine and removes self
|
307
|
+
# from the database.
|
308
|
+
# Handy for people who don't want to play with an ActiveParticipant
|
309
|
+
# instance when just consuming workitems (that an active participant
|
310
|
+
# pushed in the database).
|
311
|
+
#
|
312
|
+
def reply (engine)
|
313
|
+
|
314
|
+
engine.reply self.as_owfe_workitem
|
315
|
+
self.destroy
|
316
|
+
end
|
317
|
+
|
318
|
+
alias :forward :reply
|
319
|
+
alias :proceed :reply
|
320
|
+
|
321
|
+
#
|
322
|
+
# Simply sets the 'last_modified' field to now.
|
323
|
+
# (Doesn't save the workitem though).
|
324
|
+
#
|
325
|
+
def touch
|
326
|
+
|
327
|
+
self.last_modified = Time.now
|
328
|
+
end
|
329
|
+
|
330
|
+
#
|
331
|
+
# Opening engine to update its reply method to accept these
|
332
|
+
# active record workitems.
|
333
|
+
#
|
334
|
+
class OpenWFE::Engine
|
335
|
+
|
336
|
+
alias :oldreply :reply
|
337
|
+
|
338
|
+
def reply (workitem)
|
339
|
+
|
340
|
+
if workitem.is_a?(Workitem)
|
341
|
+
|
342
|
+
oldreply(workitem.as_owfe_workitem)
|
343
|
+
workitem.destroy
|
344
|
+
else
|
345
|
+
|
346
|
+
oldreply(workitem)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
alias :forward :reply
|
351
|
+
alias :proceed :reply
|
352
|
+
end
|
353
|
+
|
354
|
+
#
|
355
|
+
# Returns all the workitems belonging to the stores listed
|
356
|
+
# in the parameter storename_list.
|
357
|
+
# The result is a Hash whose keys are the store names and whose
|
358
|
+
# values are list of workitems.
|
359
|
+
#
|
360
|
+
def self.find_in_stores (storename_list)
|
361
|
+
|
362
|
+
workitems = find_all_by_store_name(storename_list)
|
363
|
+
|
364
|
+
result = {}
|
365
|
+
|
366
|
+
workitems.each do |wi|
|
367
|
+
(result[wi.store_name] ||= []) << wi
|
368
|
+
end
|
369
|
+
|
370
|
+
result
|
371
|
+
end
|
372
|
+
|
373
|
+
#
|
374
|
+
# A kind of 'google search' among workitems
|
375
|
+
#
|
376
|
+
# == Note
|
377
|
+
#
|
378
|
+
# when this is used on compact_workitems, it will not be able to search
|
379
|
+
# info within the fields, because they aren't used by this kind of
|
380
|
+
# workitems. In this case the search will be limited to participant_name
|
381
|
+
#
|
382
|
+
def self.search (search_string, storename_list=nil)
|
383
|
+
|
384
|
+
#t = OpenWFE::Timer.new
|
385
|
+
|
386
|
+
storename_list = Array(storename_list) if storename_list
|
387
|
+
|
388
|
+
# participant_name
|
389
|
+
|
390
|
+
result = find(
|
391
|
+
:all,
|
392
|
+
:conditions => conditions(
|
393
|
+
"participant_name", search_string, storename_list),
|
394
|
+
:order => "participant_name")
|
395
|
+
# :limit => 10)
|
396
|
+
|
397
|
+
ids = result.collect { |wi| wi.id }
|
398
|
+
|
399
|
+
# search in fields
|
400
|
+
|
401
|
+
fields = Field.search search_string, storename_list
|
402
|
+
merge_search_results ids, result, fields
|
403
|
+
|
404
|
+
#puts "... took #{t.duration} ms"
|
405
|
+
|
406
|
+
# over.
|
407
|
+
|
408
|
+
result
|
409
|
+
end
|
410
|
+
|
411
|
+
#
|
412
|
+
# Not really about 'just launched', but rather about finding the first
|
413
|
+
# workitem for a given process instance (wfid) and a participant.
|
414
|
+
# It deserves its own method because the workitem could be in a
|
415
|
+
# subprocess, thus escaping the vanilla find_by_wfid_and_participant()
|
416
|
+
#
|
417
|
+
def self.find_just_launched (wfid, participant_name)
|
418
|
+
|
419
|
+
find(
|
420
|
+
:first,
|
421
|
+
:conditions => [
|
422
|
+
"wfid LIKE ? AND participant_name = ?",
|
423
|
+
"#{wfid}%",
|
424
|
+
participant_name ])
|
425
|
+
end
|
426
|
+
|
427
|
+
protected
|
428
|
+
|
429
|
+
#
|
430
|
+
# builds the condition (the WHERE clause) for the
|
431
|
+
# search.
|
432
|
+
#
|
433
|
+
def self.conditions (keyname, search_string, storename_list)
|
434
|
+
|
435
|
+
cs = [ "#{keyname} LIKE ?", search_string ]
|
436
|
+
|
437
|
+
if storename_list
|
438
|
+
|
439
|
+
cs[0] = "#{cs[0]} AND workitems.store_name IN (?)"
|
440
|
+
cs << storename_list
|
441
|
+
end
|
442
|
+
|
443
|
+
cs
|
444
|
+
end
|
445
|
+
|
446
|
+
def self.merge_search_results (ids, wis, new_wis)
|
447
|
+
|
448
|
+
return if new_wis.size < 1
|
449
|
+
|
450
|
+
new_wis.each do |wi|
|
451
|
+
wi = wi.workitem if wi.kind_of?(Field)
|
452
|
+
next if ids.include? wi.id
|
453
|
+
ids << wi.id
|
454
|
+
wis << wi
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
# A workaround is in place for some classes when then have to get
|
461
|
+
# serialized. The names of thoses classes are listed in this array.
|
462
|
+
#
|
463
|
+
SPECIAL_FIELD_CLASSES = [ 'Time', 'Date', 'DateTime' ]
|
464
|
+
|
465
|
+
#
|
466
|
+
# A Field (Attribute) of a Workitem.
|
467
|
+
#
|
468
|
+
class Field < ActiveRecord::Base
|
469
|
+
|
470
|
+
belongs_to :workitem
|
471
|
+
serialize :yvalue
|
472
|
+
|
473
|
+
#
|
474
|
+
# A quick method for doing
|
475
|
+
#
|
476
|
+
# f = Field.new
|
477
|
+
# f.key = key
|
478
|
+
# f.value = value
|
479
|
+
#
|
480
|
+
# One can then quickly add fields to an [active] workitem via :
|
481
|
+
#
|
482
|
+
# wi.fields << Field.new_field("toto", "b")
|
483
|
+
#
|
484
|
+
# This method does not save the new Field.
|
485
|
+
#
|
486
|
+
def self.new_field (key, value)
|
487
|
+
|
488
|
+
f = Field.new
|
489
|
+
f.fkey = key
|
490
|
+
f.vclass = value.class.to_s
|
491
|
+
f.value = value
|
492
|
+
f
|
493
|
+
end
|
494
|
+
|
495
|
+
def value= (v)
|
496
|
+
|
497
|
+
limit = connection.native_database_types[:string][:limit]
|
498
|
+
|
499
|
+
if v.is_a?(String) and v.length <= limit
|
500
|
+
|
501
|
+
self.svalue = v
|
502
|
+
|
503
|
+
elsif SPECIAL_FIELD_CLASSES.include?(v.class.to_s)
|
504
|
+
|
505
|
+
self.svalue = v.to_yaml
|
506
|
+
|
507
|
+
else
|
508
|
+
|
509
|
+
self.yvalue = v
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def value
|
514
|
+
|
515
|
+
return YAML.load(self.svalue) \
|
516
|
+
if SPECIAL_FIELD_CLASSES.include?(self.vclass)
|
517
|
+
|
518
|
+
self.svalue || self.yvalue
|
519
|
+
end
|
520
|
+
|
521
|
+
#
|
522
|
+
# Will return all the fields that contain the given text.
|
523
|
+
#
|
524
|
+
# Looks in svalue and fkey. Looks as well in yvalue if it contains
|
525
|
+
# a string.
|
526
|
+
#
|
527
|
+
# This method is used by Workitem.search()
|
528
|
+
#
|
529
|
+
def self.search (text, storename_list=nil)
|
530
|
+
|
531
|
+
cs = build_search_conditions(text)
|
532
|
+
|
533
|
+
if storename_list
|
534
|
+
|
535
|
+
cs[0] = "(#{cs[0]}) AND workitems.store_name IN (?)"
|
536
|
+
cs << storename_list
|
537
|
+
end
|
538
|
+
|
539
|
+
find :all, :conditions => cs, :include => :workitem
|
540
|
+
end
|
541
|
+
|
542
|
+
protected
|
543
|
+
|
544
|
+
#
|
545
|
+
# The search operates on the content of these columns
|
546
|
+
#
|
547
|
+
FIELDS_TO_SEARCH = %w{ svalue fkey yvalue }
|
548
|
+
|
549
|
+
#
|
550
|
+
# Builds the condition array for a pseudo text search
|
551
|
+
#
|
552
|
+
def self.build_search_conditions (text)
|
553
|
+
|
554
|
+
has_percent = (text.index("%") != nil)
|
555
|
+
|
556
|
+
conds = []
|
557
|
+
|
558
|
+
conds << FIELDS_TO_SEARCH.collect { |key|
|
559
|
+
|
560
|
+
count = has_percent ? 1 : 4
|
561
|
+
|
562
|
+
s = ([ "#{key} LIKE ?" ] * count).join(" OR ")
|
563
|
+
|
564
|
+
s = "(vclass = ? AND (#{s}))" if key == 'yvalue'
|
565
|
+
|
566
|
+
s
|
567
|
+
}.join(" OR ")
|
568
|
+
|
569
|
+
FIELDS_TO_SEARCH.each do |key|
|
570
|
+
|
571
|
+
conds << 'String' if key == 'yvalue'
|
572
|
+
|
573
|
+
conds << text
|
574
|
+
|
575
|
+
unless has_percent
|
576
|
+
conds << "% #{text} %"
|
577
|
+
conds << "% #{text}"
|
578
|
+
conds << "#{text} %"
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
conds
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
|
587
|
+
#
|
588
|
+
# A basic 'ActiveParticipant'.
|
589
|
+
# A store participant whose store is a set of ActiveRecord tables.
|
590
|
+
#
|
591
|
+
# Sample usage :
|
592
|
+
#
|
593
|
+
# class MyDefinition < OpenWFE::ProcessDefinition
|
594
|
+
# sequence do
|
595
|
+
# active0
|
596
|
+
# active1
|
597
|
+
# end
|
598
|
+
# end
|
599
|
+
#
|
600
|
+
# def play_with_the_engine
|
601
|
+
#
|
602
|
+
# engine = OpenWFE::Engine.new
|
603
|
+
#
|
604
|
+
# engine.register_participant(
|
605
|
+
# :active0, OpenWFE::Extras::ActiveParticipant)
|
606
|
+
# engine.register_participant(
|
607
|
+
# :active1, OpenWFE::Extras::ActiveParticipant)
|
608
|
+
#
|
609
|
+
# li = OpenWFE::LaunchItem.new(MyDefinition)
|
610
|
+
# li.customer_name = 'toto'
|
611
|
+
# engine.launch li
|
612
|
+
#
|
613
|
+
# sleep 0.500
|
614
|
+
# # give some slack to the engine, it's asynchronous after all
|
615
|
+
#
|
616
|
+
# wi = OpenWFE::Extras::Workitem.find_by_participant_name("active0")
|
617
|
+
#
|
618
|
+
# # ...
|
619
|
+
# end
|
620
|
+
#
|
621
|
+
# == Compact workitems
|
622
|
+
#
|
623
|
+
# It is possible to save all the workitem data into a single table,
|
624
|
+
# the workitems table, without
|
625
|
+
# splitting info between workitems and fields tables.
|
626
|
+
#
|
627
|
+
# You can configure the "compact_workitems" behavior by adding to the
|
628
|
+
# previous lines:
|
629
|
+
#
|
630
|
+
# active0 = engine.register_participant(
|
631
|
+
# :active0, OpenWFE::Extras::ActiveParticipant)
|
632
|
+
#
|
633
|
+
# active0.compact_workitems = true
|
634
|
+
#
|
635
|
+
# This behaviour is determined participant per participant, it's ok to
|
636
|
+
# have a participant instance that compacts will there is another that
|
637
|
+
# doesn't compact.
|
638
|
+
#
|
639
|
+
class ActiveParticipant
|
640
|
+
include OpenWFE::LocalParticipant
|
641
|
+
|
642
|
+
#
|
643
|
+
# when compact_workitems is set to true, the attributes of a workitem
|
644
|
+
# are stored in the yattributes column (they are not expanded into
|
645
|
+
# the Fields table).
|
646
|
+
# By default, workitem attributes are expanded.
|
647
|
+
#
|
648
|
+
attr :compact_workitems, true
|
649
|
+
|
650
|
+
#
|
651
|
+
# This is the method called by the OpenWFEru engine to hand a
|
652
|
+
# workitem to this participant.
|
653
|
+
#
|
654
|
+
def consume (workitem)
|
655
|
+
|
656
|
+
if compact_workitems
|
657
|
+
workitem.attributes["compact_workitems"] = true
|
658
|
+
end
|
659
|
+
|
660
|
+
Workitem.from_owfe_workitem workitem
|
661
|
+
end
|
662
|
+
|
663
|
+
#
|
664
|
+
# Called by the engine when the whole process instance (or just the
|
665
|
+
# segment of it that sports this participant) is cancelled.
|
666
|
+
# Will removed the workitem with the same fei as the cancelitem
|
667
|
+
# from the database.
|
668
|
+
#
|
669
|
+
# No expression will be raised if there is no corresponding workitem.
|
670
|
+
#
|
671
|
+
def cancel (cancelitem)
|
672
|
+
|
673
|
+
Workitem.delete_all([ "fei = ?", cancelitem.fei.to_s ])
|
674
|
+
end
|
675
|
+
|
676
|
+
#
|
677
|
+
# When the activity/work/operation whatever is over and the flow
|
678
|
+
# should resume, this is the method to use to hand back the [modified]
|
679
|
+
# workitem to the [local] engine.
|
680
|
+
#
|
681
|
+
def reply_to_engine (workitem)
|
682
|
+
|
683
|
+
super workitem.as_owfe_workitem
|
684
|
+
#
|
685
|
+
# replies to the workflow engine
|
686
|
+
|
687
|
+
workitem.destroy
|
688
|
+
#
|
689
|
+
# removes the workitem from the database
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
#
|
694
|
+
# An extension of ActiveParticipant. It has a 'store_name' and it
|
695
|
+
# makes sure to flag every workitem it 'consumes' with that name
|
696
|
+
# (in its 'store_name' column/field).
|
697
|
+
#
|
698
|
+
# This is the participant used mainly in 'densha' for human users.
|
699
|
+
#
|
700
|
+
class ActiveStoreParticipant < ActiveParticipant
|
701
|
+
include Enumerable
|
702
|
+
|
703
|
+
def initialize (store_name)
|
704
|
+
|
705
|
+
super()
|
706
|
+
@store_name = store_name
|
707
|
+
end
|
708
|
+
|
709
|
+
#
|
710
|
+
# This is the method called by the OpenWFEru engine to hand a
|
711
|
+
# workitem to this participant.
|
712
|
+
#
|
713
|
+
def consume (workitem)
|
714
|
+
|
715
|
+
if compact_workitems
|
716
|
+
workitem.attributes["compact_workitems"] = true
|
717
|
+
end
|
718
|
+
|
719
|
+
Workitem.from_owfe_workitem(workitem, @store_name)
|
720
|
+
end
|
721
|
+
|
722
|
+
#
|
723
|
+
# Iterates over the workitems currently in this store.
|
724
|
+
#
|
725
|
+
def each (&block)
|
726
|
+
|
727
|
+
return unless block
|
728
|
+
|
729
|
+
wis = Workitem.find_by_store_name @store_name
|
730
|
+
|
731
|
+
wis.each do |wi|
|
732
|
+
block.call wi
|
733
|
+
end
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|