flowchart 0.0.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +328 -21
- data/flowchart.gemspec +4 -4
- data/lib/flow_chart.rb +48 -2
- data/lib/flowchart/work_processor.rb +85 -0
- data/sample/sample_flowchart.rb +87 -0
- data/sample/{sample_one.rb → sample_state.rb} +2 -2
- data/sample/sample_work.rb +33 -0
- metadata +9 -6
data/README.rdoc
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
= FlowChart
|
2
2
|
|
3
|
-
- Flowchart is a rubygem for
|
4
|
-
- Flowchart works out of the box both for ActiveRecord and Non-ActiveRecord Models.
|
5
|
-
-
|
3
|
+
- Flowchart is a rubygem for StateFlow(state-action-transition) and WorkFlow(user-assigned-assigner) workflow.
|
4
|
+
- Flowchart(Stateflow) works out of the box both for ActiveRecord and Non-ActiveRecord Models.
|
5
|
+
- Flowchart(Workflow) works out of the box both for ActiveRecord
|
6
|
+
- It provides an easy DSL to create a workflow for your model's object.
|
7
|
+
- State Flow + Work FLow = Process Flow
|
8
|
+
- You can not only define states-action-transitions but also do a lot more with pre-process and post-process steps
|
9
|
+
- You can independently use either of StateFlow and WorkFlow or together, the way you need it
|
6
10
|
|
7
11
|
== INSTALLATION
|
8
12
|
|
9
13
|
gem install flowchart
|
10
14
|
|
11
|
-
# No other gem dependencies! Tested on Ruby 1.8.7 and Ruby 1.9.3
|
15
|
+
# No other gem dependencies! Tested on Ruby 1.8.7 and Ruby 1.9.3 | Rails 3.2.13 and Rails 2.3.8
|
12
16
|
|
13
|
-
|
17
|
+
= SYNTAX & OPTIONS
|
18
|
+
|
19
|
+
|
20
|
+
== STATEFLOW only
|
14
21
|
|
15
22
|
flowchart do
|
16
23
|
init_flowstate :init
|
@@ -30,34 +37,331 @@
|
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
=== OPTIONS
|
41
|
+
1. By Default flowchart assumes that the model attribute it works on is "process_status.
|
42
|
+
If you want to override it then :
|
43
|
+
flowchart do
|
44
|
+
state_column :some_other_column
|
45
|
+
.....
|
46
|
+
end
|
47
|
+
|
48
|
+
2. Every flowstate can have options preprocess and postprocess blocks.
|
49
|
+
You can mention any instance methods or Procs/lamdas to execute.
|
50
|
+
|
51
|
+
3. In Actions you can mention :condition with evaluate truth or falsity based on which the
|
52
|
+
transition action(:from -> :to) is possible or not is determined.
|
53
|
+
If :condition is not given transition is always evaluated to truth.
|
54
|
+
|
55
|
+
4. In Actions you can also mention :branch, when you have multiple :to states from one single :from state
|
56
|
+
then you can mention a ":branch" condition which you can execute and determine
|
57
|
+
which branch state will be the next! Useful in decision trees.
|
58
|
+
|
59
|
+
=== Methods
|
60
|
+
1. obj.{state_name}? returns boolean true/false if object is in that that state
|
61
|
+
|
62
|
+
2. obj.{action} returns the transtion to next state on executing the action
|
63
|
+
|
64
|
+
3. obj.{action}! returns the transition to next state on executing the action and save after call
|
65
|
+
|
66
|
+
4. obj.current_state returns the current_state of the object
|
67
|
+
e.g #<FlowChart::State:0x9e6e3f4 @flowstatename=:uploaded, @before_and_after_works={:postprocess=>#<Proc:0x099dc28c@(irb):30>, :preprocess=>#<Proc:0x099dc3e0@(irb):29>}>
|
68
|
+
|
69
|
+
5. obj.flowprocessor returns the complete tree of flowchart for the object!
|
70
|
+
e.g. #<FlowChart::FlowProcessor:0x9e6e5e8
|
71
|
+
@actions=
|
72
|
+
{:close=>
|
73
|
+
#<FlowChart::Action:0x9e6df80
|
74
|
+
@flowprocessor=#<FlowChart::FlowProcessor:0x9e6e5e8 ...>,
|
75
|
+
@name=:close,
|
76
|
+
@transitions=
|
77
|
+
[#<FlowChart::Transition:0x9e6dea4
|
78
|
+
@branch=nil,
|
79
|
+
@condition=:file_close?,
|
80
|
+
@from=[:init, :uploaded, :open],
|
81
|
+
@to=:closed>]>,
|
82
|
+
:upload=>
|
83
|
+
#<FlowChart::Action:0x9e6e110
|
84
|
+
@flowprocessor=#<FlowChart::FlowProcessor:0x9e6e5e8 ...>,
|
85
|
+
@name=:upload,
|
86
|
+
@transitions=
|
87
|
+
[#<FlowChart::Transition:0x9e6e084
|
88
|
+
@branch=nil,
|
89
|
+
@condition=:file_parsable?,
|
90
|
+
@from=[:init],
|
91
|
+
@to=:uploaded>]>,
|
92
|
+
:process=>
|
93
|
+
#<FlowChart::Action:0x9e6e070
|
94
|
+
@flowprocessor=#<FlowChart::FlowProcessor:0x9e6e5e8 ...>,
|
95
|
+
@name=:process,
|
96
|
+
@transitions=
|
97
|
+
[#<FlowChart::Transition:0x9e6df94
|
98
|
+
@branch=:choose_branch,
|
99
|
+
@condition=nil,
|
100
|
+
@from=[:uploaded],
|
101
|
+
@to=[:open, :closed]>]>},
|
102
|
+
@flowstates=
|
103
|
+
{:init=>
|
104
|
+
#<FlowChart::State:0x9e6e4f8
|
105
|
+
@before_and_after_works=
|
106
|
+
{:postprocess=>:notify_user, :preprocess=>#<Proc:0x099dca84@(irb):24>},
|
107
|
+
@flowstatename=:init>,
|
108
|
+
:open=>
|
109
|
+
#<FlowChart::State:0x9e6e2dc
|
110
|
+
@before_and_after_works={:postprocess=>#<Proc:0x099db8f0@(irb):35>},
|
111
|
+
@flowstatename=:open>,
|
112
|
+
:uploaded=>
|
113
|
+
#<FlowChart::State:0x9e6e3f4
|
114
|
+
@before_and_after_works=
|
115
|
+
{:postprocess=>#<Proc:0x099dc28c@(irb):30>,
|
116
|
+
:preprocess=>#<Proc:0x099dc3e0@(irb):29>},
|
117
|
+
@flowstatename=:uploaded>,
|
118
|
+
:closed=>
|
119
|
+
#<FlowChart::State:0x9e6e1d8
|
120
|
+
@before_and_after_works=
|
121
|
+
{:postprocess=>:notify_user, :preprocess=>#<Proc:0x099daf18@(irb):39>},
|
122
|
+
@flowstatename=:closed>},
|
123
|
+
@starting_flowstate=
|
124
|
+
#<FlowChart::State:0x9e6e4f8
|
125
|
+
@before_and_after_works=
|
126
|
+
{:postprocess=>:notify_user, :preprocess=>#<Proc:0x099dca84@(irb):24>},
|
127
|
+
@flowstatename=:init>,
|
128
|
+
@starting_flowstate_name=:init,
|
129
|
+
@state_column=:process_status>
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
== WORKFLOW only
|
134
|
+
|
135
|
+
# Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing only Work Machine
|
136
|
+
# Require 'active_record'
|
137
|
+
class User
|
138
|
+
attr_accessor :email
|
139
|
+
def initialize(email)
|
140
|
+
@email=email
|
38
141
|
end
|
142
|
+
end
|
143
|
+
u1=User.new("1@gmail.com")
|
144
|
+
u2=User.new("2@gmail.com")
|
145
|
+
u3=User.new("3@gmail.com")
|
146
|
+
u4=User.new("4@gmail.com")
|
39
147
|
|
40
|
-
|
148
|
+
class SampleWork
|
149
|
+
include FlowChart
|
150
|
+
require 'active_record'
|
151
|
+
attr_accessor :assigned_to,:assigned_by
|
152
|
+
workchart do
|
41
153
|
|
42
|
-
|
154
|
+
assigned_to_column :assigned_to
|
155
|
+
assigned_by_column :assigned_by
|
43
156
|
|
44
|
-
|
157
|
+
workowner :user do
|
158
|
+
goal_time lambda{ 2.days }
|
159
|
+
dead_line lambda{ 3.days }
|
160
|
+
end
|
45
161
|
|
46
|
-
|
162
|
+
delegate :feed_pairing_work
|
163
|
+
delegate :feed_publishing_work
|
164
|
+
delegate :confirmation_work
|
165
|
+
end
|
47
166
|
|
48
|
-
|
167
|
+
end
|
49
168
|
|
50
|
-
2. obj.{action} returns the transtion to next state on executing the action
|
51
169
|
|
52
|
-
|
170
|
+
=== OPTIONS
|
171
|
+
1. By Default flowchart assumes that the model attributes for work_assignments are 'assigned_to' & assigned_by'
|
172
|
+
If you want to override it then :
|
173
|
+
flowchart do
|
174
|
+
assigned_to_column :some_other_column1
|
175
|
+
assigned_by_column :some_other_column2
|
176
|
+
.....
|
177
|
+
end
|
53
178
|
|
54
|
-
|
179
|
+
2. Every Workchart belongs to a workowner, here it is User Model. goal_time and dead_line are default SLA you can mention
|
180
|
+
for the work! You can call these procs later to monitor the SLA in your Application.
|
181
|
+
...
|
182
|
+
workowner :user do
|
183
|
+
goal_time lambda{ 2.days }
|
184
|
+
dead_line lambda{ 3.days }
|
185
|
+
end
|
186
|
+
...
|
187
|
+
|
188
|
+
3. There can be multiple tasks associated with a work. Each task here is knows as 'delegate'
|
189
|
+
...
|
190
|
+
delegate :feed_pairing_work
|
191
|
+
...
|
192
|
+
|
193
|
+
4. Once the object of SampleWork is created you can use your delegate tasks and assign users to them\
|
194
|
+
...
|
195
|
+
obj.feed_pairing_work(u1,u2)
|
196
|
+
# or obj.feed_pairing_work!(u1,u2)
|
197
|
+
# mentioning ! will save the obj object after callbacks
|
198
|
+
...
|
199
|
+
|
200
|
+
5. We cav have multiple delegates(tasks) which means each of these tasks can have completely separate set of WorkOwners(assigned_by, assigned_to). At any given point 'obj.current_work_owners' should give the work_owners at the moment for each delegates!
|
201
|
+
|
202
|
+
6. If you want to see the trace of WorkOwners for each delegates go through the tree 'obj.workprocessor'. You will see for every delegate actions we keep the trace of work_owners which changes i.e yesterday it might be u1,u2 today it might be u3,u4 for a delegate. Keeping trace can be an added feature that can be used for logging in your Application.
|
203
|
+
|
204
|
+
|
205
|
+
=== Methods
|
206
|
+
1. obj.{current_work_owners} returns the set of current_work_owners for each delegate task
|
207
|
+
e.g. [{:feed_pairing_work=>{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">, :assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">}}, {:feed_publishing_work=>{}}, {:confirmation_work=>{}}]
|
208
|
+
|
209
|
+
2. YAML.load(obj.assigned_by) returns the set of work_owners(assiged_by) for each delegate task
|
210
|
+
e.g. [{:feed_pairing_work=>#<User:0x9eac744 @email="2@gmail.com">}, {:feed_publishing_work=>""}, {:confirmation_work=>""}]
|
211
|
+
|
212
|
+
3. YAML.load(obj.assigned_to) returns the set of work_owners(assiged_to) for each delegate task
|
213
|
+
e.g. [{:feed_pairing_work=>#<User:0x9ea9db4 @email="1@gmail.com">}, {:feed_publishing_work=>""}, {:confirmation_work=>""}]
|
214
|
+
|
215
|
+
3. obj.{delegate}(user1obj,user2obj) assigns the delegate task where 'user2obj' is assigner and user1.obj is assigned_to
|
216
|
+
|
217
|
+
4. obj.workprocessor.work_owners returns the Hash of owner types for this work item with sla configs
|
218
|
+
e.g. {:user=>#<FlowChart::WorkOwner:0x9e6d760 @sla={:goal_time=>#<Proc:0x099d86b4@(irb):63>, :dead_line=>#<Proc:0x099d859c@(irb):64>}, @work_owner_model=User>}
|
219
|
+
|
220
|
+
5. obj.workprocessor.delegate_actions returns all the delegates actions for this work that has been defined
|
221
|
+
e.g. {:feed_pairing_work=>
|
222
|
+
#<FlowChart::WorkDelegateAction:0x9e6d404
|
223
|
+
@current_work_owners=
|
224
|
+
{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
|
225
|
+
:assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">},
|
226
|
+
@delegate_action_name=:feed_pairing_work,
|
227
|
+
@work_owners_trace=
|
228
|
+
[{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
|
229
|
+
:assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">}]>,
|
230
|
+
:feed_publishing_work=>
|
231
|
+
#<FlowChart::WorkDelegateAction:0x9e6d3f0
|
232
|
+
@current_work_owners={},
|
233
|
+
@delegate_action_name=:feed_publishing_work,
|
234
|
+
@work_owners_trace=[]>,
|
235
|
+
:confirmation_work=>
|
236
|
+
#<FlowChart::WorkDelegateAction:0x9e6d3b4
|
237
|
+
@current_work_owners={},
|
238
|
+
@delegate_action_name=:confirmation_work,
|
239
|
+
@work_owners_trace=[]>}
|
240
|
+
|
241
|
+
5. obj.workprocessor returns the complete tree of flowchart for the object!
|
242
|
+
e.g. #<FlowChart::WorkProcessor:0x9e6d814
|
243
|
+
@assigned_by_column=:assigned_by,
|
244
|
+
@assigned_to_column=:assigned_to,
|
245
|
+
@delegate_actions=
|
246
|
+
{:feed_pairing_work=>
|
247
|
+
#<FlowChart::WorkDelegateAction:0x9e6d404
|
248
|
+
@current_work_owners=
|
249
|
+
{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
|
250
|
+
:assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">},
|
251
|
+
@delegate_action_name=:feed_pairing_work,
|
252
|
+
@work_owners_trace=
|
253
|
+
[{:assigned_to=>#<User:0x99e3d84 @email="1@gmail.com">,
|
254
|
+
:assigned_by=>#<User:0x9a027d4 @email="2@gmail.com">}]>,
|
255
|
+
:feed_publishing_work=>
|
256
|
+
#<FlowChart::WorkDelegateAction:0x9e6d3f0
|
257
|
+
@current_work_owners={},
|
258
|
+
@delegate_action_name=:feed_publishing_work,
|
259
|
+
@work_owners_trace=[]>,
|
260
|
+
:confirmation_work=>
|
261
|
+
#<FlowChart::WorkDelegateAction:0x9e6d3b4
|
262
|
+
@current_work_owners={},
|
263
|
+
@delegate_action_name=:confirmation_work,
|
264
|
+
@work_owners_trace=[]>},
|
265
|
+
@work_owners=
|
266
|
+
{:user=>
|
267
|
+
#<FlowChart::WorkOwner:0x9e6d760
|
268
|
+
@sla=
|
269
|
+
{:goal_time=>#<Proc:0x099d86b4@(irb):63>,
|
270
|
+
:dead_line=>#<Proc:0x099d859c@(irb):64>},
|
271
|
+
@work_owner_model=User>}>
|
272
|
+
|
273
|
+
|
274
|
+
== WORKFLOW+STATEFLOW (Complete Flowchart)
|
275
|
+
# Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing State Machine & Work Machine = Flowchart
|
276
|
+
# requires 'active_record' for Workflow
|
277
|
+
class User
|
278
|
+
attr_accessor :email
|
279
|
+
def initialize(email)
|
280
|
+
@email=email
|
281
|
+
end
|
282
|
+
end
|
283
|
+
u1=User.new("1@gmail.com")
|
284
|
+
u2=User.new("2@gmail.com")
|
285
|
+
u3=User.new("3@gmail.com")
|
286
|
+
u4=User.new("4@gmail.com")
|
55
287
|
|
56
|
-
|
288
|
+
class SampleFlowchart
|
289
|
+
include FlowChart
|
290
|
+
require 'active_record'
|
291
|
+
attr_accessor :process_status
|
292
|
+
attr_accessor :assigned_to,:assigned_by
|
57
293
|
|
294
|
+
flowchart do
|
295
|
+
init_flowstate :init
|
58
296
|
|
297
|
+
flowstate :init do
|
298
|
+
preprocess Proc.new { |o| p "Initializing File" }
|
299
|
+
postprocess :notify_user
|
300
|
+
end
|
59
301
|
|
60
|
-
|
302
|
+
flowstate :uploaded do
|
303
|
+
preprocess Proc.new { |o| p "Validating File" }
|
304
|
+
postprocess Proc.new { |o| p "File has been uploaded in system" }
|
305
|
+
end
|
306
|
+
|
307
|
+
flowstate :open do
|
308
|
+
postprocess :notify_user
|
309
|
+
postprocess Proc.new { |o| p "File has been closed" }
|
310
|
+
end
|
311
|
+
|
312
|
+
flowstate :closed do
|
313
|
+
preprocess Proc.new { |o| p "File closed" }
|
314
|
+
postprocess :notify_user
|
315
|
+
end
|
316
|
+
|
317
|
+
action :upload do
|
318
|
+
transitions :from => :init, :to => :uploaded, :condition => :file_parsable?
|
319
|
+
end
|
320
|
+
|
321
|
+
action :process do
|
322
|
+
transitions :from => :uploaded, :to => [:open, :closed], :branch => :choose_branch
|
323
|
+
end
|
324
|
+
|
325
|
+
action :close do
|
326
|
+
transitions :from => [:init,:uploaded,:open], :to => :closed, :condition => :file_close?
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
workchart do
|
332
|
+
|
333
|
+
assigned_to_column :assigned_to
|
334
|
+
assigned_by_column :assigned_by
|
335
|
+
|
336
|
+
workowner :user do
|
337
|
+
goal_time lambda{ 2.days }
|
338
|
+
dead_line lambda{ 3.days }
|
339
|
+
end
|
340
|
+
|
341
|
+
delegate :feed_pairing_work
|
342
|
+
delegate :feed_publishing_work
|
343
|
+
delegate :confirmation_work
|
344
|
+
end
|
345
|
+
|
346
|
+
def choose_branch
|
347
|
+
6 > 5 ? :open : :closed
|
348
|
+
end
|
349
|
+
|
350
|
+
def notify_user
|
351
|
+
p "Notifying User!"
|
352
|
+
end
|
353
|
+
|
354
|
+
def file_parsable?
|
355
|
+
3 > 2 ? true : false
|
356
|
+
end
|
357
|
+
|
358
|
+
def file_close?
|
359
|
+
1 > 0 ? true : false
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
== Sample showing Stateflow only
|
61
365
|
|
62
366
|
# Example of a Non-ActiveRecord Class using FlowChart in Ruby
|
63
367
|
# in irb console
|
@@ -153,6 +457,9 @@
|
|
153
457
|
t.current_state
|
154
458
|
#<FlowChart::State:0x9a3fbfc @flowstatename=:closed, @before_and_after_works={:preprocess=>#<Proc:0x09a41024@(irb):26>, :postprocess=>:notify_user}>
|
155
459
|
|
460
|
+
== Samples
|
461
|
+
You can refer to the example codes mentioned here in 'samples' directory
|
462
|
+
|
156
463
|
== Save! in ActiveRecord Model
|
157
464
|
|
158
|
-
For any action if you choose to follow it up with a '!' then the model object is saved after action call. Similarly for an action if you choose to not follow it up with a '!' it will only update the attributes for the model object
|
465
|
+
For any action in Stateflow and any delgate action in Work Flow if you choose to follow it up with a '!' then the model object is saved after action call. Similarly for an action in Stateflow and any delgate action in Work Flow if you choose to not follow it up with a '!' it will only update the attributes for the model object, not save it.
|
data/flowchart.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |f|
|
2
2
|
f.name = 'flowchart'
|
3
|
-
f.version = '
|
3
|
+
f.version = '1.0.1'
|
4
4
|
f.date = %q{2013-09-27}
|
5
|
-
f.summary = %q{State
|
6
|
-
f.description = %q{Flowchart is a
|
5
|
+
f.summary = %q{State Flow and Work Flow Process gem in Ruby}
|
6
|
+
f.description = %q{Flowchart is a StateFlow(states-actions-transitions) and WorkFlow(users-assigned-assigners) process design for Non-ActiveRecord and ActiveRecord Model in Ruby or Rails}
|
7
7
|
f.authors = %q{Shuddhashil Ray}
|
8
8
|
f.email = %q{rayshuddhashil@gmail.com}
|
9
|
-
f.files = ["README.rdoc","LICENSE.txt","flowchart.gemspec","lib/flow_chart.rb","lib/flowchart/flow_processor.rb","sample/
|
9
|
+
f.files = ["README.rdoc","LICENSE.txt","flowchart.gemspec","lib/flow_chart.rb","lib/flowchart/flow_processor.rb","lib/flowchart/work_processor.rb","sample/sample_state.rb","sample/sample_work.rb","sample/sample_flowchart.rb"]
|
10
10
|
f.require_paths = ["lib"]
|
11
11
|
f.homepage = %q{http://github.com/raycoding/flowchart}
|
12
12
|
f.license = "MIT"
|
data/lib/flow_chart.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
1
3
|
require 'flowchart/flow_processor'
|
4
|
+
require 'flowchart/work_processor'
|
2
5
|
module FlowChart
|
3
6
|
def self.included(base)
|
4
7
|
base.extend ClassMethods
|
@@ -6,7 +9,7 @@ module FlowChart
|
|
6
9
|
end
|
7
10
|
|
8
11
|
module ClassMethods
|
9
|
-
attr_reader :flowprocessor
|
12
|
+
attr_reader :flowprocessor,:workprocessor
|
10
13
|
|
11
14
|
def flowchart(&block)
|
12
15
|
@flowprocessor = FlowChart::FlowProcessor.new(&block)
|
@@ -29,11 +32,31 @@ module FlowChart
|
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
35
|
+
|
36
|
+
def workchart(&block)
|
37
|
+
@workprocessor = FlowChart::WorkProcessor.new(&block)
|
38
|
+
|
39
|
+
#Driver Instance Methods for each evet for triggering the delegation action!
|
40
|
+
@workprocessor.delegate_actions.keys.each do |key|
|
41
|
+
define_method "#{key}" do |assigned_to,assigned_by|
|
42
|
+
process_delegate_action(key,assigned_to,assigned_by,:save_object=>false)
|
43
|
+
end
|
44
|
+
|
45
|
+
define_method "#{key}!" do |assigned_to,assigned_by|
|
46
|
+
process_delegate_action(key,assigned_to,assigned_by,:save_object=>true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
32
51
|
end
|
33
52
|
|
34
53
|
module InstanceMethods
|
35
54
|
attr_accessor :previous_state
|
36
55
|
|
56
|
+
def workprocessor
|
57
|
+
self.class.workprocessor
|
58
|
+
end
|
59
|
+
|
37
60
|
def flowprocessor
|
38
61
|
self.class.flowprocessor
|
39
62
|
end
|
@@ -57,7 +80,21 @@ module FlowChart
|
|
57
80
|
self.save
|
58
81
|
end
|
59
82
|
end
|
60
|
-
|
83
|
+
|
84
|
+
def current_work_owners
|
85
|
+
@current_work_owners ||= workprocessor.delegate_actions.map{|work_name,work_owners| {work_name=>work_owners.current_work_owners}}
|
86
|
+
@current_work_owners
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_current_work_owners(options = {})
|
90
|
+
@current_work_owners = workprocessor.delegate_actions.map{|work_name,work_owners| {work_name=>work_owners.current_work_owners}}
|
91
|
+
send("#{workprocessor.assigned_to_column}=".to_sym, YAML.dump(current_work_owners.map{|work| work.map{|work_name,owners| {work_name=>owners[workprocessor.assigned_to_column.to_sym]||""}}}.flatten))
|
92
|
+
send("#{workprocessor.assigned_by_column}=".to_sym, YAML.dump(current_work_owners.map{|work| work.map{|work_name,owners| {work_name=>owners[workprocessor.assigned_by_column.to_sym]||""}}}.flatten))
|
93
|
+
if options[:save_object] and !(defined?(ActiveRecord::Base).nil?) and self.class.ancestors.include? ActiveRecord::Base
|
94
|
+
self.save
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
61
98
|
private
|
62
99
|
def process_action(action_name,options_for_action = {})
|
63
100
|
action = flowprocessor.actions[action_name.to_sym]
|
@@ -67,5 +104,14 @@ module FlowChart
|
|
67
104
|
end
|
68
105
|
action.process_action!(self,current_state,options_for_action)
|
69
106
|
end
|
107
|
+
|
108
|
+
def process_delegate_action(action_name,assigned_to,assigned_by,options_for_delegate_action={})
|
109
|
+
delegate_action = workprocessor.delegate_actions[action_name.to_sym]
|
110
|
+
if delegate_action.nil?
|
111
|
+
p "Error: #{action_name} not found!"
|
112
|
+
raise FlowChart::InvalidWorkDelegateAction.new("Error: #{delegate_action} not found!")
|
113
|
+
end
|
114
|
+
delegate_action.process_delegate_action!(self,current_work_owners,assigned_to,assigned_by,options_for_delegate_action)
|
115
|
+
end
|
70
116
|
end
|
71
117
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module FlowChart
|
2
|
+
class InvalidWorkDelegateAction < NoMethodError; end
|
3
|
+
|
4
|
+
class WorkOwner
|
5
|
+
attr_accessor :work_owner_model,:sla
|
6
|
+
|
7
|
+
def initialize(work_owner_model, &workownerblock)
|
8
|
+
@work_owner_model,@sla = work_owner_model.to_s.classify.constantize,Hash.new
|
9
|
+
instance_eval(&workownerblock) if block_given?
|
10
|
+
end
|
11
|
+
|
12
|
+
def goal_time(method = nil, &block)
|
13
|
+
@sla[:goal_time] = method.nil? ? block : method
|
14
|
+
end
|
15
|
+
|
16
|
+
def dead_line(method = nil, &block)
|
17
|
+
@sla[:dead_line] = method.nil? ? block : method
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.work_assign(to,by)
|
21
|
+
assigned_to,assigned_by = to,by
|
22
|
+
return Hash[:assigned_to=> assigned_to, :assigned_by=>assigned_by]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class WorkDelegateAction
|
27
|
+
attr_accessor :delegate_action_name
|
28
|
+
attr_accessor :current_work_owners
|
29
|
+
attr_accessor :work_owners_trace
|
30
|
+
|
31
|
+
def initialize(delegate_action_name,workprocessor=nil,&block)
|
32
|
+
@current_work_owners = Hash.new
|
33
|
+
@work_owners_trace = []
|
34
|
+
@delegate_action_name = delegate_action_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_delegate_action!(implementor_class,current_work_owners,assigned_to,assigned_by,options_for_delegate_action)
|
38
|
+
puts @delegate_action_name
|
39
|
+
new_work_owners = WorkOwner.work_assign(assigned_to,assigned_by)
|
40
|
+
@work_owners_trace << new_work_owners
|
41
|
+
@current_work_owners = new_work_owners
|
42
|
+
implementor_class.set_current_work_owners(options_for_delegate_action)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# Main Driver for WorkChart
|
48
|
+
class WorkProcessor
|
49
|
+
attr_accessor :work_owners,:delegate_actions
|
50
|
+
|
51
|
+
def initialize(&workprocessor)
|
52
|
+
@work_owners, @delegate_actions = Hash.new, Hash.new
|
53
|
+
begin
|
54
|
+
instance_eval(&workprocessor)
|
55
|
+
rescue => e
|
56
|
+
p e.backtrace
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
## Default state_column is assumed to be assigned_to if not mentioned
|
61
|
+
# You can override the default state_column by mentioning in your class
|
62
|
+
# assigned_to_column :something_else
|
63
|
+
def assigned_to_column(name = :assigned_to)
|
64
|
+
@assigned_to_column ||= name
|
65
|
+
end
|
66
|
+
|
67
|
+
## Default state_column is assumed to be assigned_by if not mentioned
|
68
|
+
# You can override the default state_column by mentioning in your class
|
69
|
+
# assigned_by_column :something_else
|
70
|
+
def assigned_by_column(name = :assigned_by)
|
71
|
+
@assigned_by_column ||= name
|
72
|
+
end
|
73
|
+
|
74
|
+
def workowner(work_owner_model, &block)
|
75
|
+
work_owner = FlowChart::WorkOwner.new(work_owner_model,&block)
|
76
|
+
@work_owners[work_owner_model.to_sym] = work_owner
|
77
|
+
end
|
78
|
+
|
79
|
+
def delegate(delegate_action_name, &block)
|
80
|
+
delegate_action = FlowChart::WorkDelegateAction.new(delegate_action_name,self,&block)
|
81
|
+
@delegate_actions[delegate_action_name.to_sym] = delegate_action
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing State Machine & Work Machine = Flowchart
|
2
|
+
# requires 'active_record' for Workflow
|
3
|
+
class User
|
4
|
+
attr_accessor :email
|
5
|
+
def initialize(email)
|
6
|
+
@email=email
|
7
|
+
end
|
8
|
+
end
|
9
|
+
u1=User.new("1@gmail.com")
|
10
|
+
u2=User.new("2@gmail.com")
|
11
|
+
u3=User.new("3@gmail.com")
|
12
|
+
u4=User.new("4@gmail.com")
|
13
|
+
|
14
|
+
class SampleFlowchart
|
15
|
+
include FlowChart
|
16
|
+
require 'active_record'
|
17
|
+
attr_accessor :process_status
|
18
|
+
attr_accessor :assigned_to,:assigned_by
|
19
|
+
|
20
|
+
flowchart do
|
21
|
+
init_flowstate :init
|
22
|
+
|
23
|
+
flowstate :init do
|
24
|
+
preprocess Proc.new { |o| p "Initializing File" }
|
25
|
+
postprocess :notify_user
|
26
|
+
end
|
27
|
+
|
28
|
+
flowstate :uploaded do
|
29
|
+
preprocess Proc.new { |o| p "Validating File" }
|
30
|
+
postprocess Proc.new { |o| p "File has been uploaded in system" }
|
31
|
+
end
|
32
|
+
|
33
|
+
flowstate :open do
|
34
|
+
postprocess :notify_user
|
35
|
+
postprocess Proc.new { |o| p "File has been closed" }
|
36
|
+
end
|
37
|
+
|
38
|
+
flowstate :closed do
|
39
|
+
preprocess Proc.new { |o| p "File closed" }
|
40
|
+
postprocess :notify_user
|
41
|
+
end
|
42
|
+
|
43
|
+
action :upload do
|
44
|
+
transitions :from => :init, :to => :uploaded, :condition => :file_parsable?
|
45
|
+
end
|
46
|
+
|
47
|
+
action :process do
|
48
|
+
transitions :from => :uploaded, :to => [:open, :closed], :branch => :choose_branch
|
49
|
+
end
|
50
|
+
|
51
|
+
action :close do
|
52
|
+
transitions :from => [:init,:uploaded,:open], :to => :closed, :condition => :file_close?
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
workchart do
|
58
|
+
|
59
|
+
assigned_to_column :assigned_to
|
60
|
+
assigned_by_column :assigned_by
|
61
|
+
|
62
|
+
workowner :user do
|
63
|
+
goal_time lambda{ 2.days }
|
64
|
+
dead_line lambda{ 3.days }
|
65
|
+
end
|
66
|
+
|
67
|
+
delegate :feed_pairing_work
|
68
|
+
delegate :feed_publishing_work
|
69
|
+
delegate :confirmation_work
|
70
|
+
end
|
71
|
+
|
72
|
+
def choose_branch
|
73
|
+
6 > 5 ? :open : :closed
|
74
|
+
end
|
75
|
+
|
76
|
+
def notify_user
|
77
|
+
p "Notifying User!"
|
78
|
+
end
|
79
|
+
|
80
|
+
def file_parsable?
|
81
|
+
3 > 2 ? true : false
|
82
|
+
end
|
83
|
+
|
84
|
+
def file_close?
|
85
|
+
1 > 0 ? true : false
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing only Work Machine
|
2
|
+
# Require 'active_record'
|
3
|
+
class User
|
4
|
+
attr_accessor :email
|
5
|
+
def initialize(email)
|
6
|
+
@email=email
|
7
|
+
end
|
8
|
+
end
|
9
|
+
u1=User.new("1@gmail.com")
|
10
|
+
u2=User.new("2@gmail.com")
|
11
|
+
u3=User.new("3@gmail.com")
|
12
|
+
u4=User.new("4@gmail.com")
|
13
|
+
|
14
|
+
class SampleWork
|
15
|
+
include FlowChart
|
16
|
+
require 'active_record'
|
17
|
+
attr_accessor :assigned_to,:assigned_by
|
18
|
+
workchart do
|
19
|
+
|
20
|
+
assigned_to_column :assigned_to
|
21
|
+
assigned_by_column :assigned_by
|
22
|
+
|
23
|
+
workowner :user do
|
24
|
+
goal_time lambda{ 2.days }
|
25
|
+
dead_line lambda{ 3.days }
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate :feed_pairing_work
|
29
|
+
delegate :feed_publishing_work
|
30
|
+
delegate :confirmation_work
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flowchart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
7
|
+
- 1
|
8
8
|
- 0
|
9
9
|
- 1
|
10
|
-
version:
|
10
|
+
version: 1.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Shuddhashil Ray
|
@@ -19,7 +19,7 @@ date: 2013-09-27 00:00:00 +05:30
|
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
22
|
-
description: Flowchart is a
|
22
|
+
description: Flowchart is a StateFlow(states-actions-transitions) and WorkFlow(users-assigned-assigners) process design for Non-ActiveRecord and ActiveRecord Model in Ruby or Rails
|
23
23
|
email: rayshuddhashil@gmail.com
|
24
24
|
executables: []
|
25
25
|
|
@@ -33,7 +33,10 @@ files:
|
|
33
33
|
- flowchart.gemspec
|
34
34
|
- lib/flow_chart.rb
|
35
35
|
- lib/flowchart/flow_processor.rb
|
36
|
-
-
|
36
|
+
- lib/flowchart/work_processor.rb
|
37
|
+
- sample/sample_state.rb
|
38
|
+
- sample/sample_work.rb
|
39
|
+
- sample/sample_flowchart.rb
|
37
40
|
has_rdoc: true
|
38
41
|
homepage: http://github.com/raycoding/flowchart
|
39
42
|
licenses:
|
@@ -67,6 +70,6 @@ rubyforge_project:
|
|
67
70
|
rubygems_version: 1.5.3
|
68
71
|
signing_key:
|
69
72
|
specification_version: 3
|
70
|
-
summary: State
|
73
|
+
summary: State Flow and Work Flow Process gem in Ruby
|
71
74
|
test_files: []
|
72
75
|
|