rails-workflow 1.4.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 411f127c2c5c4be97813b1f68fe9e64224463dac
4
+ data.tar.gz: 2622936f556615b749b913f553b546b2782ae724
5
+ SHA512:
6
+ metadata.gz: 5e95a1eecf1dbc84a320e14027a9b3a4de4bb20ef8e8ca68ad41957ee91e65ebb8992957aab4814dd3d87c32522d19b93ffa6733979d2073337e778fc2e88457
7
+ data.tar.gz: b26bfe36a5d64de2b6d72735439fe334137fa0b73fb75e90a32b8305cd14b473a1d0378ac7ed9c3127e6e513e9182f18442d7d9ccda6a3ff2d6ac36a04758bbd
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ nbproject
2
+ html/
3
+ *.swp
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile*.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
21
+
22
+ .byebug_history
data/.travis.yml ADDED
@@ -0,0 +1,27 @@
1
+ before_install:
2
+ - sudo apt-get install -qq graphviz
3
+ before_script:
4
+ - gem list
5
+ - bundle show
6
+
7
+ rvm:
8
+ - 2.3.1
9
+ gemfile:
10
+ - gemfiles/Gemfile.rails-edge
11
+
12
+ matrix:
13
+ include:
14
+ - rvm: 1.9.3
15
+ gemfile: gemfiles/Gemfile.rails-3.x
16
+
17
+ - rvm: 2.0.0
18
+ gemfile: gemfiles/Gemfile.rails-3.x
19
+
20
+ - rvm: 2.0.0
21
+ gemfile: gemfiles/Gemfile.rails-4.0
22
+
23
+ - rvm: 2.3.1
24
+ gemfile: gemfiles/Gemfile.rails-4.0
25
+
26
+ - rvm: 2.3.1
27
+ gemfile: gemfiles/Gemfile.rails-5.0
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --markup=markdown
2
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+ gem 'byebug'
3
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Vodafone
2
+ Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,426 @@
1
+ What is workflow?
2
+ -----------------
3
+
4
+ This Gem is a fork of Vladimir Dobriakov's [Workflow Gem](http://github.com/geekq/workflow). Credit goes to him for the core code. Please read [the original README](http://github.com/geekq/workflow) for a full introduction,
5
+ as this README skims through much of that content and focuses on new / changed features.
6
+
7
+ ## What's different in rails-workflow
8
+
9
+ The primary difference here is the use of [ActiveSupport::Callbacks](http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html)
10
+ to enable a more flexible application of callbacks.
11
+ You now have access to the same DSL you're used to from [ActionController Callbacks](http://guides.rubyonrails.org/action_controller_overview.html#filters),
12
+ including the ability to wrap state transitions in an `around_transition`, to place
13
+ conditional logic on application of callbacks, or to have callbacks run for only
14
+ a set of state-change events.
15
+
16
+ I've made `ActiveRecord` and `ActiveSupport` into runtime dependencies.
17
+
18
+ You can also take advantage of ActiveRecord's conditional validation syntax,
19
+ to apply validations only to specific state transitions.
20
+
21
+
22
+ Installation
23
+ ------------
24
+
25
+ gem install rails-workflow
26
+
27
+
28
+ Ruby Version
29
+ --------
30
+
31
+ I've only tested with Ruby 2.3. ;) Time to upgrade.
32
+
33
+
34
+ # Basic workflow definition:
35
+
36
+ class Article
37
+ include Workflow
38
+ workflow do
39
+ state :new do
40
+ event :submit, :transitions_to => :awaiting_review
41
+ end
42
+ state :awaiting_review do
43
+ event :review, :transitions_to => :being_reviewed
44
+ end
45
+ state :being_reviewed do
46
+ event :accept, :transitions_to => :accepted
47
+ event :reject, :transitions_to => :rejected
48
+ end
49
+ state :accepted
50
+ state :rejected
51
+ end
52
+ end
53
+
54
+ Access an object representing the current state of the entity,
55
+ including available events and transitions:
56
+
57
+ article.current_state
58
+ => #<Workflow::State:0x7f1e3d6731f0 @events={
59
+ :submit=>#<Workflow::Event:0x7f1e3d6730d8 @action=nil,
60
+ @transitions_to=:awaiting_review, @name=:submit, @meta={}>},
61
+ name:new, meta{}
62
+
63
+ On Ruby 1.9 and above, you can check whether a state comes before or
64
+ after another state (by the order they were defined):
65
+
66
+ article.current_state
67
+ => being_reviewed
68
+ article.current_state < :accepted
69
+ => true
70
+ article.current_state >= :accepted
71
+ => false
72
+ article.current_state.between? :awaiting_review, :rejected
73
+ => true
74
+
75
+ Now we can call the submit event, which transitions to the
76
+ <tt>:awaiting_review</tt> state:
77
+
78
+ article.submit!
79
+ article.awaiting_review? # => true
80
+
81
+
82
+ Callbacks
83
+ -------------------------
84
+
85
+ The DSL syntax here is very much similar to ActionController or ActiveRecord callbacks.
86
+
87
+ Callbacks with this strategy used the same as [ActionController Callbacks](http://guides.rubyonrails.org/action_controller_overview.html#filters).
88
+
89
+ You can configure any number of `before`, `around`, or `after` transition callbacks.
90
+
91
+ `before_transition` and `around_transition` are called in the order they are set,
92
+ and `after_transition` callbacks are called in reverse order.
93
+
94
+ ## Around Transition
95
+
96
+ Allows you to run code surrounding the state transition.
97
+
98
+ around_transition :wrap_in_transaction
99
+
100
+ def wrap_in_transaction(&block)
101
+ Article.transaction(&block)
102
+ end
103
+
104
+ You can also define the callback using a block:
105
+
106
+ around_transition do |object, transition|
107
+ object.with_lock do
108
+ transition.call
109
+ end
110
+ end
111
+
112
+ ### Replacement for workflow's `on_error` proc:
113
+
114
+ around_transition :catch_errors
115
+
116
+ def catch_errors
117
+ begin
118
+ yield
119
+ rescue SomeApplicationError => ex
120
+ logger.error 'Oh noes!'
121
+ end
122
+ end
123
+
124
+
125
+ ## before_transition
126
+
127
+ Allows you to run code prior to the state transition.
128
+ If you `halt` or `throw :abort` within a `before_transition`, the callback chain
129
+ will be halted, the transition will be canceled and the event action
130
+ will return false.
131
+
132
+ before_transition :check_title
133
+
134
+ def check_title
135
+ halt('Title was bad.') unless title == "Good Title"
136
+ end
137
+
138
+ Or again, in block expression:
139
+
140
+ before_transition do |article|
141
+ throw :abort unless article.title == "Good Title"
142
+ end
143
+
144
+ ## After Transition
145
+
146
+ Runs code after the transition.
147
+
148
+ after_transition :check_title
149
+
150
+
151
+ ## Prepend Transitions
152
+
153
+ To add a callback to the beginning of the sequence:
154
+
155
+ prepend_before_transition :some_before_transition
156
+ prepend_around_transition :some_around_transition
157
+ prepend_after_transition :some_after_transition
158
+
159
+ ## Skip Transitions
160
+
161
+ skip_before_transition :some_before_transition
162
+
163
+
164
+ ## Conditions
165
+
166
+ ### if/unless
167
+
168
+ The callback will run `if` or `unless` the named method returns a truthy value.
169
+
170
+ before_transition :do_something, if: :valid?
171
+
172
+ ### only/except
173
+
174
+ The callback will run `if` or `unless` the event being processed is in the list given
175
+
176
+ # Run this callback only on the `accept` and `publish` events.
177
+ before_transition :do_something, only: [:accept, :publish]
178
+
179
+ # Run this callback on events other than the `accept` and `publish` events.
180
+ before_transition :do_something_else, except: [:accept, :publish]
181
+
182
+ ## Conditional Validations
183
+
184
+ If you are using `ActiveRecord`, you'll have access to a set of methods which
185
+ describe the current transition underway.
186
+
187
+ Inside the same Article class which was begun above, the following three
188
+ validations would all run when the `submit` event is used to transition
189
+ from `new` to `awaiting_review`.
190
+
191
+ validates :title, presence: true, if: :transitioning_to_awaiting_review?
192
+ validates :body, presence: true, if: :transitioning_from_new?
193
+ validates :author, presence: true, if: :transitioning_via_event_submit?
194
+
195
+ ### Halting if validations fail
196
+
197
+ # This will create a transition callback which will stop the event
198
+ # and return false if validations fail.
199
+ halt_transition_unless_valid!
200
+
201
+ # This is the same as
202
+
203
+ ### Checking A Transition
204
+
205
+ Call `can_transition?` to determine whether the validations would pass if a
206
+ given event was called:
207
+
208
+ if article.can_transition?(:submit)
209
+ # Do something interesting
210
+ end
211
+
212
+ # Transition Context
213
+
214
+ During transition you can refer to the `transition_context` object on your model,
215
+ for information about the current transition. See [Workflow::TransitionContext].
216
+
217
+ ## Naming Event Arguments
218
+
219
+ If you will normally call each of your events with the same arguments, the following
220
+ will help:
221
+
222
+ class Article < ApplicationRecord
223
+ include Workflow
224
+
225
+ before_transition :check_reviewer
226
+
227
+ def check_reviewer
228
+ # Ability is a class from the cancan gem: https://github.com/CanCanCommunity/cancancan
229
+ halt('Access denied') unless Ability.new(transition_context.reviewer).can?(:review, self)
230
+ end
231
+
232
+ workflow do
233
+ event_args :reviewer, :reviewed_at
234
+ state :new do
235
+ event :review, transitions_to: :reviewed
236
+ end
237
+ state :reviewed
238
+ end
239
+ end
240
+
241
+
242
+ Transition event handler
243
+ ------------------------
244
+
245
+ The best way is to use convention over configuration and to define a
246
+ method with the same name as the event. Then it is automatically invoked
247
+ when event is raised. For the Article workflow defined earlier it would
248
+ be:
249
+
250
+ class Article
251
+ def reject
252
+ puts 'sending email to the author explaining the reason...'
253
+ end
254
+ end
255
+
256
+ `article.review!; article.reject!` will cause state transition to
257
+ `being_reviewed` state, persist the new state (if integrated with
258
+ ActiveRecord), invoke this user defined `reject` method and finally
259
+ persist the `rejected` state.
260
+
261
+ Note: on successful transition from one state to another the workflow
262
+ gem immediately persists the new workflow state with `update_column()`,
263
+ bypassing any ActiveRecord callbacks including `updated_at` update.
264
+ This way it is possible to deal with the validation and to save the
265
+ pending changes to a record at some later point instead of the moment
266
+ when transition occurs.
267
+
268
+ You can also define event handler accepting/requiring additional
269
+ arguments:
270
+
271
+ class Article
272
+ def review(reviewer = '')
273
+ puts "[#{reviewer}] is now reviewing the article"
274
+ end
275
+ end
276
+
277
+ article2 = Article.new
278
+ article2.submit!
279
+ article2.review!('Homer Simpson') # => [Homer Simpson] is now reviewing the article
280
+
281
+
282
+ Integration with ActiveRecord
283
+ -----------------------------
284
+
285
+ Workflow library can handle the state persistence fully automatically. You
286
+ only need to define a string field on the table called `workflow_state`
287
+ and include the workflow mixin in your model class as usual:
288
+
289
+ class Order < ActiveRecord::Base
290
+ include Workflow
291
+ workflow do
292
+ # list states and transitions here
293
+ end
294
+ end
295
+
296
+ On a database record loading all the state check methods e.g.
297
+ `article.state`, `article.awaiting_review?` are immediately available.
298
+ For new records or if the `workflow_state` field is not set the state
299
+ defaults to the first state declared in the workflow specification. In
300
+ our example it is `:new`, so `Article.new.new?` returns true and
301
+ `Article.new.approved?` returns false.
302
+
303
+ At the end of a successful state transition like `article.approve!` the
304
+ new state is immediately saved in the database.
305
+
306
+ You can change this behaviour by overriding `persist_workflow_state`
307
+ method.
308
+
309
+ ### Scopes
310
+
311
+ Workflow library also adds automatically generated scopes with names based on
312
+ states names:
313
+
314
+ class Order < ActiveRecord::Base
315
+ include Workflow
316
+ workflow do
317
+ state :approved
318
+ state :pending
319
+ end
320
+ end
321
+
322
+ # returns all orders with `approved` state
323
+ Order.with_approved_state
324
+
325
+ # returns all orders with `pending` state
326
+ Order.with_pending_state
327
+
328
+ ### Wrap State Transition in a locking transaction
329
+
330
+ Wrap your transition in a locking transaction to ensure that any exceptions
331
+ raised later in the transition sequence will roll back earlier changes made to
332
+ the record:
333
+
334
+ class Order < ActiveRecord::Base
335
+ include Workflow
336
+ workflow transactional: true do
337
+ state :approved
338
+ state :pending
339
+ end
340
+ end
341
+
342
+ Conditional event transitions
343
+ -----------------------------
344
+
345
+ Conditions can be a "method name symbol" with a corresponding instance method, a `proc` or `lambda` which are added to events, like so:
346
+
347
+ state :off
348
+ event :turn_on, :transition_to => :on,
349
+ :if => :sufficient_battery_level?
350
+
351
+ event :turn_on, :transition_to => :low_battery,
352
+ :if => proc { |device| device.battery_level > 0 }
353
+ end
354
+
355
+ # corresponding instance method
356
+ def sufficient_battery_level?
357
+ battery_level > 10
358
+ end
359
+
360
+ When calling a `device.can_<fire_event>?` check, or attempting a `device.<event>!`, each event is checked in turn:
361
+
362
+ * With no `:if` check, proceed as usual.
363
+ * If an `:if` check is present, proceed if it evaluates to true, or drop to the next event.
364
+ * If you've run out of events to check (eg. `battery_level == 0`), then the transition isn't possible.
365
+
366
+
367
+
368
+ Accessing your workflow specification
369
+ -------------------------------------
370
+
371
+ You can easily reflect on workflow specification programmatically - for
372
+ the whole class or for the current object. Examples:
373
+
374
+ article2.current_state.events # lists possible events from here
375
+ article2.current_state.events[:reject].transitions_to # => :rejected
376
+
377
+ Article.workflow_spec.states.keys
378
+ #=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new]
379
+
380
+ Article.workflow_spec.state_names
381
+ #=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new]
382
+
383
+ # list all events for all states
384
+ Article.workflow_spec.states.values.collect &:events
385
+
386
+
387
+ You can also store and later retrieve additional meta data for every
388
+ state and every event:
389
+
390
+ class MyProcess
391
+ include Workflow
392
+ workflow do
393
+ state :main, :meta => {:importance => 8}
394
+ state :supplemental, :meta => {:importance => 1}
395
+ end
396
+ end
397
+ puts MyProcess.workflow_spec.states[:supplemental].meta[:importance] # => 1
398
+
399
+ The workflow library itself uses this feature to tweak the graphical
400
+ representation of the workflow. See below.
401
+
402
+
403
+ Earlier versions
404
+ ----------------
405
+
406
+ The `workflow` gem is the work of Vladimir Dobriakov, <http://www.mobile-web-consulting.de>, <http://blog.geekq.net/>.
407
+
408
+ This project is a fork of his work, and the bulk of the workflow specification code
409
+ and DSL are virtually unchanged.
410
+
411
+
412
+ About
413
+ -----
414
+ Author: Tyler Gannon [https://github.com/tylergannon]
415
+
416
+ Original Author: Vladimir Dobriakov, <http://www.mobile-web-consulting.de>, <http://blog.geekq.net/>
417
+
418
+ Copyright (c) 2010-2014 Vladimir Dobriakov, www.mobile-web-consulting.de
419
+
420
+ Copyright (c) 2008-2009 Vodafone
421
+
422
+ Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
423
+
424
+ Based on the work of Ryan Allen and Scott Barron
425
+
426
+ Licensed under MIT license, see the MIT-LICENSE file.