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 CHANGED
@@ -1,16 +1,23 @@
1
1
  = FlowChart
2
2
 
3
- - Flowchart is a rubygem for State Machine(state-action-transition) workflow.
4
- - Flowchart works out of the box both for ActiveRecord and Non-ActiveRecord Models.
5
- - It provides an easy DSL to create a state machine flow for your model's object.
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
- == SYNTAX & OPTIONS
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
- # OPTIONS
34
- 1. By Default flowchart assumes that the model attribute it works on is "process_status. If you want to override it then :
35
- flowchart do
36
- state_column :some_other_column
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
- 2. Every flowstate can have options preprocess and postprocess blocks. You can mention any instance methods or Procs/lamdas to execute.
148
+ class SampleWork
149
+ include FlowChart
150
+ require 'active_record'
151
+ attr_accessor :assigned_to,:assigned_by
152
+ workchart do
41
153
 
42
- 3. In Actions you can mention :condition with evaluate truth or falsity based on which the transition action(:from -> :to) is possible or not is determined. If :condition is not given transition is always evaluated to truth.
154
+ assigned_to_column :assigned_to
155
+ assigned_by_column :assigned_by
43
156
 
44
- 4. In Actions you can also mention :branch, when you have multiple :to states from one single :from state then you can mention a ":branch" condition which you can execute and determine which branch state will be the next! Useful in decision trees.
157
+ workowner :user do
158
+ goal_time lambda{ 2.days }
159
+ dead_line lambda{ 3.days }
160
+ end
45
161
 
46
- # Methods
162
+ delegate :feed_pairing_work
163
+ delegate :feed_publishing_work
164
+ delegate :confirmation_work
165
+ end
47
166
 
48
- 1. obj.{state_name}? returns boolean true/false if object is in that that state
167
+ end
49
168
 
50
- 2. obj.{action} returns the transtion to next state on executing the action
51
169
 
52
- 3. obj.{action}! returns the transition to next state on executing the action and save after call
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
- 4. obj.current_state returns the current_state of the object
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
- 5. obj.flowprocessor returns the complete tree of flowchart for the object!
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
- == Sample Use
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 = '0.0.1'
3
+ f.version = '1.0.1'
4
4
  f.date = %q{2013-09-27}
5
- f.summary = %q{State Machine and State Flow for Ruby}
6
- f.description = %q{Flowchart is a State Machine(state-action-transition) workflow for Non-ActiveRecord and ActiveRecord Model in Ruby or Rails}
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/sample_one.rb"]
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
@@ -1,6 +1,6 @@
1
- # Example of a Non-ActiveRecord Class using FlowChart in Ruby
1
+ # Example of a Non-ActiveRecord Class using FlowChart in Ruby - showing only State Machine
2
2
 
3
- class SampleOne
3
+ class SampleState
4
4
  include FlowChart
5
5
  attr_accessor :process_status
6
6
 
@@ -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: 29
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
- - 0
7
+ - 1
8
8
  - 0
9
9
  - 1
10
- version: 0.0.1
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 State Machine(state-action-transition) workflow for Non-ActiveRecord and ActiveRecord Model in Ruby or Rails
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
- - sample/sample_one.rb
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 Machine and State Flow for Ruby
73
+ summary: State Flow and Work Flow Process gem in Ruby
71
74
  test_files: []
72
75