openwferu-extras 0.9.12.863
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/listeners/sqslisteners.rb +151 -0
- data/lib/openwfe/extras/participants/activeparticipants.rb +334 -0
- data/lib/openwfe/extras/participants/atomparticipants.rb +198 -0
- data/lib/openwfe/extras/participants/csvparticipants.rb +129 -0
- data/lib/openwfe/extras/participants/sqsparticipants.rb +124 -0
- data/lib/openwfe/extras/participants/twitterparticipants.rb +191 -0
- data/lib/openwfe/extras/util/csvtable.rb +448 -0
- data/lib/openwfe/extras/util/sqs.rb +581 -0
- metadata +56 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (c) 2007, John Mettraux, 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
|
+
# $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $
|
|
34
|
+
#
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# "made in Japan"
|
|
38
|
+
#
|
|
39
|
+
# John Mettraux at openwfe.org
|
|
40
|
+
#
|
|
41
|
+
|
|
42
|
+
require 'yaml'
|
|
43
|
+
require 'base64'
|
|
44
|
+
require 'monitor'
|
|
45
|
+
|
|
46
|
+
require 'openwfe/service'
|
|
47
|
+
require 'openwfe/util/scheduler'
|
|
48
|
+
require 'openwfe/listeners/listener'
|
|
49
|
+
|
|
50
|
+
require 'openwfe/extras/util/sqs'
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# some base listener implementations
|
|
55
|
+
#
|
|
56
|
+
module OpenWFE
|
|
57
|
+
module Extras
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Polls an Amazon SQS queue for workitems
|
|
61
|
+
#
|
|
62
|
+
# Workitems can be instances of InFlowWorkItem or LaunchItem.
|
|
63
|
+
#
|
|
64
|
+
# require 'openwfe/extras/listeners/sqslisteners'
|
|
65
|
+
#
|
|
66
|
+
# ql = OpenWFE::SqsListener("workqueue1", engine.application_context)
|
|
67
|
+
#
|
|
68
|
+
# engine.add_workitem_listener(ql, "2m30s")
|
|
69
|
+
# #
|
|
70
|
+
# # thus, the engine will poll our "workqueue1" SQS queue
|
|
71
|
+
# # every 2 minutes and 30 seconds
|
|
72
|
+
#
|
|
73
|
+
class SqsListener < Service
|
|
74
|
+
include MonitorMixin
|
|
75
|
+
include WorkItemListener
|
|
76
|
+
include Schedulable
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# The name of the Amazon SQS whom this listener cares for
|
|
80
|
+
#
|
|
81
|
+
attr_reader :queue_name
|
|
82
|
+
|
|
83
|
+
def initialize (queue_name, application_context)
|
|
84
|
+
|
|
85
|
+
@queue_name = queue_name.to_s
|
|
86
|
+
|
|
87
|
+
service_name = "#{self.class}::#{@queue_name}"
|
|
88
|
+
|
|
89
|
+
super(service_name, application_context)
|
|
90
|
+
|
|
91
|
+
linfo { "new() queue is '#{@queue_name}'" }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
#
|
|
95
|
+
# Will 'find' files in the work directory (by default ./work/in/),
|
|
96
|
+
# extract the workitem in them and feed it back to the engine.
|
|
97
|
+
#
|
|
98
|
+
def trigger (params)
|
|
99
|
+
synchronize do
|
|
100
|
+
|
|
101
|
+
ldebug { "trigger()" }
|
|
102
|
+
|
|
103
|
+
qs = SQS::QueueService.new
|
|
104
|
+
|
|
105
|
+
qs.create_queue(@queue_name)
|
|
106
|
+
# just to be sure it is there
|
|
107
|
+
|
|
108
|
+
while true
|
|
109
|
+
|
|
110
|
+
l = qs.get_messages(
|
|
111
|
+
@queue_name, :timeout => 0, :count => 255)
|
|
112
|
+
|
|
113
|
+
break if l.length < 1
|
|
114
|
+
|
|
115
|
+
l.each do |msg|
|
|
116
|
+
|
|
117
|
+
o = decode_object(msg)
|
|
118
|
+
|
|
119
|
+
handle_object(o)
|
|
120
|
+
|
|
121
|
+
msg.delete
|
|
122
|
+
|
|
123
|
+
ldebug do
|
|
124
|
+
"trigger() " +
|
|
125
|
+
"handled successfully msg #{msg.message_id}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Extracts a workitem from the message's body.
|
|
134
|
+
#
|
|
135
|
+
# By default, this listeners assumes the workitem is stored in
|
|
136
|
+
# its "hash form" (not directly as a Ruby InFlowWorkItem instance).
|
|
137
|
+
#
|
|
138
|
+
# LaunchItem instances (as hash as well) are also accepted.
|
|
139
|
+
#
|
|
140
|
+
def decode_object (message)
|
|
141
|
+
|
|
142
|
+
o = Base64.decode64(message.message_body)
|
|
143
|
+
o = YAML.load(o)
|
|
144
|
+
o = OpenWFE::workitem_from_h(o)
|
|
145
|
+
o
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (c) 2007, John Mettraux 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
|
+
#
|
|
39
|
+
|
|
40
|
+
require 'rubygems'
|
|
41
|
+
require_gem 'activerecord'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
require 'openwfe/workitem'
|
|
45
|
+
require 'openwfe/flowexpressionid'
|
|
46
|
+
require 'openwfe/participants/participant'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
module OpenWFE
|
|
50
|
+
module Extras
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# The migration for ActiveParticipant and associated classes.
|
|
54
|
+
#
|
|
55
|
+
# There are two tables 'workitems' and 'fields'. As its name implies,
|
|
56
|
+
# the latter table stores the fields (also called attributes in OpenWFE
|
|
57
|
+
# speak) of the workitems.
|
|
58
|
+
#
|
|
59
|
+
# See Workitem and Field for more details.
|
|
60
|
+
#
|
|
61
|
+
# For centralization purposes, the migration and the model are located
|
|
62
|
+
# in the same source file. It should be quite easy for the Rails hackers
|
|
63
|
+
# among you to sort that out for a Rails based usage.
|
|
64
|
+
#
|
|
65
|
+
class OwfeTables < ActiveRecord::Migration
|
|
66
|
+
def self.up
|
|
67
|
+
create_table :workitems do |t|
|
|
68
|
+
t.column :fei, :string
|
|
69
|
+
t.column :wfid, :string
|
|
70
|
+
t.column :wf_name, :string
|
|
71
|
+
t.column :wf_revision, :string
|
|
72
|
+
t.column :participant_name, :string
|
|
73
|
+
t.column :store, :string
|
|
74
|
+
end
|
|
75
|
+
create_table :fields do |t|
|
|
76
|
+
t.column :key, :string, :null => false
|
|
77
|
+
t.column :value, :text
|
|
78
|
+
t.column :workitem_id, :integer, :null => false
|
|
79
|
+
end
|
|
80
|
+
add_index :fields, [ :workitem_id, :key], :unique => true
|
|
81
|
+
end
|
|
82
|
+
def self.down
|
|
83
|
+
drop_table :workitems
|
|
84
|
+
drop_table :fields
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# The ActiveRecord version of an OpenWFEru workitem (InFlowWorkItem).
|
|
90
|
+
#
|
|
91
|
+
# One can very easily build a worklist based on a participant name via :
|
|
92
|
+
#
|
|
93
|
+
# wl = OpenWFE::Extras::Workitem.find_all_by_participant_name("toto")
|
|
94
|
+
# puts "found #{wl.size} workitems for participant 'toto'"
|
|
95
|
+
#
|
|
96
|
+
# These workitems are not OpenWFEru workitems directly. But the conversion
|
|
97
|
+
# is pretty easy.
|
|
98
|
+
# Note that you probaly won't need to do the conversion by yourself,
|
|
99
|
+
# except for certain advanced scenarii.
|
|
100
|
+
#
|
|
101
|
+
# awi = OpenWFE::Extras::Workitem.find_by_participant_name("toto")
|
|
102
|
+
# #
|
|
103
|
+
# # returns the first workitem in the database whose participant
|
|
104
|
+
# # name is 'toto'.
|
|
105
|
+
#
|
|
106
|
+
# owi = awi.as_owfe_workitem
|
|
107
|
+
# #
|
|
108
|
+
# # Now we have a copy of the reference as a OpenWFEru
|
|
109
|
+
# # InFlowWorkItem instance.
|
|
110
|
+
#
|
|
111
|
+
# awi = OpenWFE::Extras::Workitem.from_owfe_workitem(owi)
|
|
112
|
+
# #
|
|
113
|
+
# # turns an OpenWFEru InFlowWorkItem instance into an
|
|
114
|
+
# # 'active workitem'.
|
|
115
|
+
#
|
|
116
|
+
class Workitem < ActiveRecord::Base
|
|
117
|
+
|
|
118
|
+
has_many :fields, :dependent => :destroy
|
|
119
|
+
|
|
120
|
+
#
|
|
121
|
+
# Returns the flow expression id of this work (its unique OpenWFEru
|
|
122
|
+
# identifier) as a FlowExpressionId instance.
|
|
123
|
+
# (within the Workitem it's just stored as a String).
|
|
124
|
+
#
|
|
125
|
+
def full_fei
|
|
126
|
+
|
|
127
|
+
OpenWFE::FlowExpressionId.from_s(fei)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
#
|
|
131
|
+
# Generates a (new) Workitem from an OpenWFEru InFlowWorkItem instance.
|
|
132
|
+
#
|
|
133
|
+
# This is a 'static' method :
|
|
134
|
+
#
|
|
135
|
+
# awi = OpenWFE::Densha::Workitem.from_owfe_workitem(wi)
|
|
136
|
+
#
|
|
137
|
+
# (This method will not save the 'ActiveWorkitem').
|
|
138
|
+
#
|
|
139
|
+
def self.from_owfe_workitem (wi)
|
|
140
|
+
|
|
141
|
+
i = Workitem.new
|
|
142
|
+
i.fei = wi.fei.to_s
|
|
143
|
+
i.wfid = wi.fei.wfid
|
|
144
|
+
i.wf_name = wi.fei.workflow_definition_name
|
|
145
|
+
i.wf_revision = wi.fei.workflow_definition_revision
|
|
146
|
+
i.participant_name = wi.participant_name
|
|
147
|
+
|
|
148
|
+
wi.attributes.each do |k, v|
|
|
149
|
+
f = Field.new
|
|
150
|
+
f.key = k
|
|
151
|
+
f.value = v
|
|
152
|
+
i.fields << f
|
|
153
|
+
end
|
|
154
|
+
i
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
#
|
|
158
|
+
# Turns the densha Workitem into an OpenWFEru InFlowWorkItem.
|
|
159
|
+
#
|
|
160
|
+
def as_owfe_workitem
|
|
161
|
+
|
|
162
|
+
wi = OpenWFE::InFlowWorkItem.new
|
|
163
|
+
wi.fei = full_fei
|
|
164
|
+
wi.participant_name = participant_name
|
|
165
|
+
wi.attributes = fields_hash
|
|
166
|
+
wi
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
#
|
|
170
|
+
# Returns a hash version of the 'fields' of this workitem.
|
|
171
|
+
#
|
|
172
|
+
# (Each time this method is called, it returns a new hash).
|
|
173
|
+
#
|
|
174
|
+
def fields_hash
|
|
175
|
+
|
|
176
|
+
h = {}
|
|
177
|
+
fields.each do |f|
|
|
178
|
+
h[f.key] = f.value
|
|
179
|
+
end
|
|
180
|
+
h
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
#
|
|
184
|
+
# Returns the Field instance with the given key. This method accept
|
|
185
|
+
# symbols as well as strings as its parameter.
|
|
186
|
+
#
|
|
187
|
+
# wi.field("customer_name")
|
|
188
|
+
# wi.field :customer_name
|
|
189
|
+
#
|
|
190
|
+
def field (key)
|
|
191
|
+
|
|
192
|
+
fields.find_by_key(key.to_s)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
#
|
|
196
|
+
# A shortcut method, replies to the workflow engine and removes self
|
|
197
|
+
# from the database.
|
|
198
|
+
# Handy for people who don't want to play with an ActiveParticipant
|
|
199
|
+
# instance when just consuming workitems (that an active participant
|
|
200
|
+
# pushed in the database).
|
|
201
|
+
#
|
|
202
|
+
def reply (engine)
|
|
203
|
+
|
|
204
|
+
engine.reply self.as_owfe_workitem
|
|
205
|
+
self.destroy
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
alias :forward :reply
|
|
209
|
+
alias :proceed :reply
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
#
|
|
213
|
+
# A Field (Attribute) of a Workitem.
|
|
214
|
+
#
|
|
215
|
+
class Field < ActiveRecord::Base
|
|
216
|
+
|
|
217
|
+
belongs_to :workitem
|
|
218
|
+
serialize :value
|
|
219
|
+
|
|
220
|
+
#
|
|
221
|
+
# A quick method for doing
|
|
222
|
+
#
|
|
223
|
+
# f = Field.new
|
|
224
|
+
# f.key = key
|
|
225
|
+
# f.value = value
|
|
226
|
+
#
|
|
227
|
+
# One can then quickly add fields to an [active] workitem via :
|
|
228
|
+
#
|
|
229
|
+
# wi.fields << Field.new_field("toto", "b")
|
|
230
|
+
#
|
|
231
|
+
def self.new_field (key, value)
|
|
232
|
+
f = Field.new
|
|
233
|
+
f.key = key
|
|
234
|
+
f.value = value
|
|
235
|
+
f
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
#
|
|
241
|
+
# A basic 'ActiveParticipant'.
|
|
242
|
+
# A store participant whose store is a set of ActiveRecord tables.
|
|
243
|
+
#
|
|
244
|
+
# Sample usage :
|
|
245
|
+
#
|
|
246
|
+
# class MyDefinition < OpenWFE::ProcessDefinition
|
|
247
|
+
# sequence do
|
|
248
|
+
# active0
|
|
249
|
+
# active1
|
|
250
|
+
# end
|
|
251
|
+
# end
|
|
252
|
+
#
|
|
253
|
+
# def play_with_the_engine
|
|
254
|
+
#
|
|
255
|
+
# engine = OpenWFE::Engine.new
|
|
256
|
+
#
|
|
257
|
+
# engine.register_participant(
|
|
258
|
+
# :active0, OpenWFE::Extras::ActiveParticipant)
|
|
259
|
+
# engine.register_participant(
|
|
260
|
+
# :active1, OpenWFE::Extras::ActiveParticipant)
|
|
261
|
+
#
|
|
262
|
+
# li = OpenWFE::LaunchItem.new(MyDefinition)
|
|
263
|
+
# li.customer_name = 'toto'
|
|
264
|
+
# engine.launch li
|
|
265
|
+
#
|
|
266
|
+
# sleep 0.500
|
|
267
|
+
# # give some slack to the engine, it's asynchronous after all
|
|
268
|
+
#
|
|
269
|
+
# wi = OpenWFE::Extras::Workitem.find_by_participant_name("active0")
|
|
270
|
+
#
|
|
271
|
+
# # ...
|
|
272
|
+
# end
|
|
273
|
+
#
|
|
274
|
+
class ActiveParticipant
|
|
275
|
+
include OpenWFE::LocalParticipant
|
|
276
|
+
|
|
277
|
+
#
|
|
278
|
+
# This is the method called by the OpenWFEru engine to hand a
|
|
279
|
+
# workitem to this participant.
|
|
280
|
+
#
|
|
281
|
+
def consume (workitem)
|
|
282
|
+
|
|
283
|
+
awi = Workitem.from_owfe_workitem(workitem)
|
|
284
|
+
#
|
|
285
|
+
# turns the workitem into an 'active' one
|
|
286
|
+
|
|
287
|
+
awi.save
|
|
288
|
+
#
|
|
289
|
+
# and saves it in the db.
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
#
|
|
293
|
+
# Called by the engine when the whole process instance (or just the
|
|
294
|
+
# segment of it that sports this participant) is cancelled.
|
|
295
|
+
# Will removed the workitem with the same fei as the cancelitem
|
|
296
|
+
# from the database.
|
|
297
|
+
#
|
|
298
|
+
# No expression will be raised if there is no corresponding workitem.
|
|
299
|
+
#
|
|
300
|
+
def cancel (cancelitem)
|
|
301
|
+
|
|
302
|
+
Workitem.delete_all([ "fei = ?", cancelitem.fei.to_s ])
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
#
|
|
306
|
+
# When the activity/work/operation whatever is over and the flow
|
|
307
|
+
# should resume, this is the method to use to hand back the [modified]
|
|
308
|
+
# workitem to the [local] engine.
|
|
309
|
+
#
|
|
310
|
+
def reply_to_engine (workitem)
|
|
311
|
+
|
|
312
|
+
super workitem.as_owfe_workitem
|
|
313
|
+
#
|
|
314
|
+
# replies to the workflow engine
|
|
315
|
+
|
|
316
|
+
workitem.destroy
|
|
317
|
+
#
|
|
318
|
+
# removes the workitem from the database
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
#
|
|
323
|
+
# TODO : please document me
|
|
324
|
+
#
|
|
325
|
+
class ActiveStoreParticipant < ActiveParticipant
|
|
326
|
+
include Enumerable
|
|
327
|
+
|
|
328
|
+
def each (&block)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|