workflow-rails4 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bc1ada744e6a1c958f266a6486f72b482feface7
4
+ data.tar.gz: 477a9ded220d31b5dd521392fde3d02f8ce16535
5
+ SHA512:
6
+ metadata.gz: eacef77e7effe92b41800ccd0badf87bd3fc3735f9f6e990363da33885fc2d08f4c52da3018d9587236072aa2347211286686704364e06935beb82d7c04871c8
7
+ data.tar.gz: b022ad6c08b11dab6714c533bca01ab1459e7750c8a4483f576d339dac7673042b1fac01d4ca9e26a32e191d25c9ee9f49124eb9a219a8799b77e29c558b99cd
@@ -0,0 +1,20 @@
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
@@ -0,0 +1,21 @@
1
+ before_install:
2
+ - sudo apt-get install -qq graphviz
3
+
4
+ rvm:
5
+ - 2.0.0
6
+ gemfile:
7
+ - gemfiles/Gemfile.rails-edge
8
+
9
+ matrix:
10
+ include:
11
+ # legacy build: Ruby 1.8.7, Rails 2.3, less features, smaller test set
12
+ - rvm: 1.8.7
13
+ gemfile: gemfiles/Gemfile.rails-2.3.x
14
+ # running a smaller test set for old Rails and Ruby
15
+ script: rake test_without_new_versions
16
+
17
+ - rvm: 1.9.3
18
+ gemfile: gemfiles/Gemfile.rails-3.x
19
+
20
+ - rvm: 2.0.0
21
+ gemfile: gemfiles/Gemfile.rails-3.x
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -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.
@@ -0,0 +1,663 @@
1
+ [![Build Status](https://travis-ci.org/geekq/workflow.png?branch=master)](https://travis-ci.org/geekq/workflow)
2
+
3
+
4
+ What is workflow?
5
+ -----------------
6
+
7
+ Workflow is a finite-state-machine-inspired API for modeling and
8
+ interacting with what we tend to refer to as 'workflow'.
9
+
10
+ A lot of business modeling tends to involve workflow-like concepts, and
11
+ the aim of this library is to make the expression of these concepts as
12
+ clear as possible, using similar terminology as found in state machine
13
+ theory.
14
+
15
+ So, a workflow has a state. It can only be in one state at a time. When
16
+ a workflow changes state, we call that a transition. Transitions occur
17
+ on an event, so events cause transitions to occur. Additionally, when an
18
+ event fires, other arbitrary code can be executed, we call those actions.
19
+ So any given state has a bunch of events, any event in a state causes a
20
+ transition to another state and potentially causes code to be executed
21
+ (an action). We can hook into states when they are entered, and exited
22
+ from, and we can cause transitions to fail (guards), and we can hook in
23
+ to every transition that occurs ever for whatever reason we can come up
24
+ with.
25
+
26
+ Now, all that's a mouthful, but we'll demonstrate the API bit by bit
27
+ with a real-ish world example.
28
+
29
+ Let's say we're modeling article submission from journalists. An article
30
+ is written, then submitted. When it's submitted, it's awaiting review.
31
+ Someone reviews the article, and then either accepts or rejects it.
32
+ Here is the expression of this workflow using the API:
33
+
34
+ class Article
35
+ include Workflow
36
+ workflow do
37
+ state :new do
38
+ event :submit, :transitions_to => :awaiting_review
39
+ end
40
+ state :awaiting_review do
41
+ event :review, :transitions_to => :being_reviewed
42
+ end
43
+ state :being_reviewed do
44
+ event :accept, :transitions_to => :accepted
45
+ event :reject, :transitions_to => :rejected
46
+ end
47
+ state :accepted
48
+ state :rejected
49
+ end
50
+ end
51
+
52
+ Nice, isn't it!
53
+
54
+ Note: the first state in the definition (`:new` in the example, but you
55
+ can name it as you wish) is used as the initial state - newly created
56
+ objects start their life cycle in that state.
57
+
58
+ Let's create an article instance and check in which state it is:
59
+
60
+ article = Article.new
61
+ article.accepted? # => false
62
+ article.new? # => true
63
+
64
+ You can also access the whole `current_state` object including the list
65
+ of possible events and other meta information:
66
+
67
+ article.current_state
68
+ => #<Workflow::State:0x7f1e3d6731f0 @events={
69
+ :submit=>#<Workflow::Event:0x7f1e3d6730d8 @action=nil,
70
+ @transitions_to=:awaiting_review, @name=:submit, @meta={}>},
71
+ name:new, meta{}
72
+
73
+ On Ruby 1.9 and above, you can check whether a state comes before or
74
+ after another state (by the order they were defined):
75
+
76
+ article.current_state
77
+ => being_reviewed
78
+ article.current_state < :accepted
79
+ => true
80
+ article.current_state >= :accepted
81
+ => false
82
+ article.between? :awaiting_review, :rejected
83
+ => true
84
+
85
+ Now we can call the submit event, which transitions to the
86
+ <tt>:awaiting_review</tt> state:
87
+
88
+ article.submit!
89
+ article.awaiting_review? # => true
90
+
91
+ Events are actually instance methods on a workflow, and depending on the
92
+ state you're in, you'll have a different set of events used to
93
+ transition to other states.
94
+
95
+ It is also easy to check, if a certain transition is possible from the
96
+ current state . `article.can_submit?` checks if there is a `:submit`
97
+ event (transition) defined for the current state.
98
+
99
+
100
+ Installation
101
+ ------------
102
+
103
+ gem install workflow
104
+
105
+ **Important**: If you're interested in graphing your workflow state machine, you will also need to
106
+ install the `active_support` and `ruby-graphviz` gems.
107
+
108
+ Versions up to and including 1.0.0 are also available as a single file download -
109
+ [lib/workflow.rb file](https://github.com/geekq/workflow/blob/v1.0.0/lib/workflow.rb).
110
+
111
+ Ruby 1.9
112
+ --------
113
+
114
+ Workflow gem does not work with some Ruby 1.9
115
+ builds due to a known bug in Ruby 1.9. Either
116
+
117
+ * use newer ruby build, 1.9.2-p136 and -p180 tested to work
118
+ * or compile your Ruby 1.9 from source
119
+ * or [comment out some lines in workflow](http://github.com/geekq/workflow/issues#issue/6)
120
+ (reduces functionality).
121
+
122
+ Examples
123
+ --------
124
+
125
+ After installation or downloading of the library you can easily try out
126
+ all the example code from this README in irb.
127
+
128
+ $ irb
129
+ require 'rubygems'
130
+ require 'workflow'
131
+
132
+ Now just copy and paste the source code from the beginning of this README
133
+ file snippet by snippet and observe the output.
134
+
135
+
136
+ Transition event handler
137
+ ------------------------
138
+
139
+ The best way is to use convention over configuration and to define a
140
+ method with the same name as the event. Then it is automatically invoked
141
+ when event is raised. For the Article workflow defined earlier it would
142
+ be:
143
+
144
+ class Article
145
+ def reject
146
+ puts 'sending email to the author explaining the reason...'
147
+ end
148
+ end
149
+
150
+ `article.review!; article.reject!` will cause a state transition, persist the new state
151
+ (if integrated with ActiveRecord) and invoke this user defined reject
152
+ method.
153
+
154
+ Note: on successful transition from one state to another the workflow
155
+ gem immediately persists the new workflow state with `update_column()`,
156
+ bypassing any ActiveRecord callbacks including `updated_at` update.
157
+ This way it is possible to deal with the validation and to save the
158
+ pending changes to a record at some later point instead of the moment
159
+ when transition occurs.
160
+
161
+ You can also define event handler accepting/requiring additional
162
+ arguments:
163
+
164
+ class Article
165
+ def review(reviewer = '')
166
+ puts "[#{reviewer}] is now reviewing the article"
167
+ end
168
+ end
169
+
170
+ article2 = Article.new
171
+ article2.submit!
172
+ article2.review!('Homer Simpson') # => [Homer Simpson] is now reviewing the article
173
+
174
+
175
+ ### The old, deprecated way
176
+
177
+ The old way, using a block is still supported but deprecated:
178
+
179
+ event :review, :transitions_to => :being_reviewed do |reviewer|
180
+ # store the reviewer
181
+ end
182
+
183
+ We've noticed, that mixing the list of events and states with the blocks
184
+ invoked for particular transitions leads to a bumpy and poorly readable code
185
+ due to a deep nesting. We tried (and dismissed) lambdas for this. Eventually
186
+ we decided to invoke an optional user defined callback method with the same
187
+ name as the event (convention over configuration) as explained before.
188
+
189
+
190
+ Integration with ActiveRecord
191
+ -----------------------------
192
+
193
+ Workflow library can handle the state persistence fully automatically. You
194
+ only need to define a string field on the table called `workflow_state`
195
+ and include the workflow mixin in your model class as usual:
196
+
197
+ class Order < ActiveRecord::Base
198
+ include Workflow
199
+ workflow do
200
+ # list states and transitions here
201
+ end
202
+ end
203
+
204
+ On a database record loading all the state check methods e.g.
205
+ `article.state`, `article.awaiting_review?` are immediately available.
206
+ For new records or if the `workflow_state` field is not set the state
207
+ defaults to the first state declared in the workflow specification. In
208
+ our example it is `:new`, so `Article.new.new?` returns true and
209
+ `Article.new.approved?` returns false.
210
+
211
+ At the end of a successful state transition like `article.approve!` the
212
+ new state is immediately saved in the database.
213
+
214
+ You can change this behaviour by overriding `persist_workflow_state`
215
+ method.
216
+
217
+ ### Scopes
218
+
219
+ Workflow library also adds automatically generated scopes with names based on
220
+ states names:
221
+
222
+ class Order < ActiveRecord::Base
223
+ include Workflow
224
+ workflow do
225
+ state :approved
226
+ state :pending
227
+ end
228
+ end
229
+
230
+ # returns all orders with `approved` state
231
+ Order.with_approved_state
232
+
233
+ # returns all orders with `pending` state
234
+ Order.with_pending_state
235
+
236
+
237
+ ### Custom workflow database column
238
+
239
+ [meuble](http://imeuble.info/) contributed a solution for using
240
+ custom persistence column easily, e.g. for a legacy database schema:
241
+
242
+ class LegacyOrder < ActiveRecord::Base
243
+ include Workflow
244
+
245
+ workflow_column :foo_bar # use this legacy database column for
246
+ # persistence
247
+ end
248
+
249
+
250
+
251
+ ### Single table inheritance
252
+
253
+ Single table inheritance is also supported. Descendant classes can either
254
+ inherit the workflow definition from the parent or override with its own
255
+ definition.
256
+
257
+ Custom workflow state persistence
258
+ ---------------------------------
259
+
260
+ If you do not use a relational database and ActiveRecord, you can still
261
+ integrate the workflow very easily. To implement persistence you just
262
+ need to override `load_workflow_state` and
263
+ `persist_workflow_state(new_value)` methods. Next section contains an example for
264
+ using CouchDB, a document oriented database.
265
+
266
+ [Tim Lossen](http://tim.lossen.de/) implemented support
267
+ for [remodel](http://github.com/tlossen/remodel) / [redis](http://github.com/antirez/redis)
268
+ key-value store.
269
+
270
+ Integration with CouchDB
271
+ ------------------------
272
+
273
+ We are using the compact [couchtiny library](http://github.com/geekq/couchtiny)
274
+ here. But the implementation would look similar for the popular
275
+ couchrest library.
276
+
277
+ require 'couchtiny'
278
+ require 'couchtiny/document'
279
+ require 'workflow'
280
+
281
+ class User < CouchTiny::Document
282
+ include Workflow
283
+ workflow do
284
+ state :submitted do
285
+ event :activate_via_link, :transitions_to => :proved_email
286
+ end
287
+ state :proved_email
288
+ end
289
+
290
+ def load_workflow_state
291
+ self[:workflow_state]
292
+ end
293
+
294
+ def persist_workflow_state(new_value)
295
+ self[:workflow_state] = new_value
296
+ save!
297
+ end
298
+ end
299
+
300
+ Please also have a look at
301
+ [the full source code](http://github.com/geekq/workflow/blob/master/test/couchtiny_example.rb).
302
+
303
+ Integration with Mongoid
304
+ ------------------------
305
+
306
+ You can integrate with Mongoid following the example above for CouchDB, but there is a gem that does that for you (and includes extensive tests):
307
+ [workflow_on_mongoid](http://github.com/bowsersenior/workflow_on_mongoid)
308
+
309
+ Accessing your workflow specification
310
+ -------------------------------------
311
+
312
+ You can easily reflect on workflow specification programmatically - for
313
+ the whole class or for the current object. Examples:
314
+
315
+ article2.current_state.events # lists possible events from here
316
+ article2.current_state.events[:reject].transitions_to # => :rejected
317
+
318
+ Article.workflow_spec.states.keys
319
+ #=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new]
320
+
321
+ Article.workflow_spec.state_names
322
+ #=> [:rejected, :awaiting_review, :being_reviewed, :accepted, :new]
323
+
324
+ # list all events for all states
325
+ Article.workflow_spec.states.values.collect &:events
326
+
327
+
328
+ You can also store and later retrieve additional meta data for every
329
+ state and every event:
330
+
331
+ class MyProcess
332
+ include Workflow
333
+ workflow do
334
+ state :main, :meta => {:importance => 8}
335
+ state :supplemental, :meta => {:importance => 1}
336
+ end
337
+ end
338
+ puts MyProcess.workflow_spec.states[:supplemental].meta[:importance] # => 1
339
+
340
+ The workflow library itself uses this feature to tweak the graphical
341
+ representation of the workflow. See below.
342
+
343
+
344
+ Advanced transition hooks
345
+ -------------------------
346
+
347
+ ### on_entry/on_exit
348
+
349
+ We already had a look at the declaring callbacks for particular workflow
350
+ events. If you would like to react to all transitions to/from the same state
351
+ in the same way you can use the on_entry/on_exit hooks. You can either define it
352
+ with a block inside the workflow definition or through naming
353
+ convention, e.g. for the state :pending just define the method
354
+ `on_pending_exit(new_state, event, *args)` somewhere in your class.
355
+
356
+ ### on_transition
357
+
358
+ If you want to be informed about everything happening everywhere, e.g. for
359
+ logging then you can use the universal `on_transition` hook:
360
+
361
+ workflow do
362
+ state :one do
363
+ event :increment, :transitions_to => :two
364
+ end
365
+ state :two
366
+ on_transition do |from, to, triggering_event, *event_args|
367
+ Log.info "#{from} -> #{to}"
368
+ end
369
+ end
370
+
371
+ Please also have a look at the [advanced end to end
372
+ example][advanced_hooks_and_validation_test].
373
+
374
+ [advanced_hooks_and_validation_test]: http://github.com/geekq/workflow/blob/master/test/advanced_hooks_and_validation_test.rb
375
+
376
+ ### on_error
377
+
378
+ If you want to do custom exception handling internal to workflow, you can define an `on_error` hook in your workflow.
379
+ For example:
380
+
381
+ workflow do
382
+ state :first do
383
+ event :forward, :transitions_to => :second
384
+ end
385
+ state :second
386
+
387
+ on_error do |error, from, to, event, *args|
388
+ Log.info "Exception(#error.class) on #{from} -> #{to}"
389
+ end
390
+ end
391
+
392
+ If forward! results in an exception, `on_error` is invoked and the workflow stays in a 'first' state. This capability
393
+ is particularly useful if your errors are transient and you want to queue up a job to retry in the future without
394
+ affecting the existing workflow state.
395
+
396
+ ### Guards
397
+
398
+ If you want to halt the transition conditionally, you can just raise an
399
+ exception in your [transition event handler](#transition_event_handler).
400
+ There is a helper called `halt!`, which raises the
401
+ Workflow::TransitionHalted exception. You can provide an additional
402
+ `halted_because` parameter.
403
+
404
+ def reject(reason)
405
+ halt! 'We do not reject articles unless the reason is important' \
406
+ unless reason =~ /important/i
407
+ end
408
+
409
+ The traditional `halt` (without the exclamation mark) is still supported
410
+ too. This just prevents the state change without raising an
411
+ exception.
412
+
413
+ You can check `halted?` and `halted_because` values later.
414
+
415
+ ### Hook order
416
+
417
+ The whole event sequence is as follows:
418
+
419
+ * before_transition
420
+ * event specific action
421
+ * on_transition (if action did not halt)
422
+ * on_exit
423
+ * PERSIST WORKFLOW STATE, i.e. transition
424
+ * on_entry
425
+ * after_transition
426
+
427
+
428
+ Multiple Workflows
429
+ ------------------
430
+
431
+ I am frequently asked if it's possible to represent multiple "workflows"
432
+ in an ActiveRecord class.
433
+
434
+ The solution depends on your business logic and how you want to
435
+ structure your implementation.
436
+
437
+ ### Use Single Table Inheritance
438
+
439
+ One solution can be to do it on the class level and use a class
440
+ hierarchy. You can use [single table inheritance][STI] so there is only
441
+ single `orders` table in the database. Read more in the chapter "Single
442
+ Table Inheritance" of the [ActiveRecord documentation][ActiveRecord].
443
+ Then you define your different classes:
444
+
445
+ class Order < ActiveRecord::Base
446
+ include Workflow
447
+ end
448
+
449
+ class SmallOrder < Order
450
+ workflow do
451
+ # workflow definition for small orders goes here
452
+ end
453
+ end
454
+
455
+ class BigOrder < Order
456
+ workflow do
457
+ # workflow for big orders, probably with a longer approval chain
458
+ end
459
+ end
460
+
461
+
462
+ ### Individual workflows for objects
463
+
464
+ Another solution would be to connect different workflows to object
465
+ instances via metaclass, e.g.
466
+
467
+ # Load an object from the database
468
+ booking = Booking.find(1234)
469
+
470
+ # Now define a workflow - exclusively for this object,
471
+ # probably depending on some condition or database field
472
+ if # some condition
473
+ class << booking
474
+ include Workflow
475
+ workflow do
476
+ state :state1
477
+ state :state2
478
+ end
479
+ end
480
+ # if some other condition, use a different workflow
481
+
482
+ You can also encapsulate this in a class method or even put in some
483
+ ActiveRecord callback. Please also have a look at [the full working
484
+ example][multiple_workflow_test]!
485
+
486
+ [STI]: http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
487
+ [ActiveRecord]: http://api.rubyonrails.org/classes/ActiveRecord/Base.html
488
+ [multiple_workflow_test]: http://github.com/geekq/workflow/blob/master/test/multiple_workflows_test.rb
489
+
490
+
491
+ Documenting with diagrams
492
+ -------------------------
493
+
494
+ You can generate a graphical representation of the workflow for
495
+ a particular class for documentation purposes.
496
+ Use `Workflow::create_workflow_diagram(class)` in your rake task like:
497
+
498
+ namespace :doc do
499
+ desc "Generate a workflow graph for a model passed e.g. as 'MODEL=Order'."
500
+ task :workflow => :environment do
501
+ require 'workflow/draw'
502
+ Workflow::Draw::workflow_diagram(ENV['MODEL'].constantize)
503
+ end
504
+ end
505
+
506
+
507
+ Earlier versions
508
+ ----------------
509
+
510
+ The `workflow` library was originally written by Ryan Allen.
511
+
512
+ The version 0.3 was almost completely (including ActiveRecord
513
+ integration, API for accessing workflow specification,
514
+ method_missing free implementation) rewritten by Vladimir Dobriakov
515
+ keeping the original workflow DSL spirit.
516
+
517
+
518
+ Migration from the original Ryan's library
519
+ ------------------------------------------
520
+
521
+ Credit: Michael (rockrep)
522
+
523
+ Accessing workflow specification
524
+
525
+ my_instance.workflow # old
526
+ MyClass.workflow_spec # new
527
+
528
+ Accessing states, events, meta, e.g.
529
+
530
+ my_instance.workflow.states(:some_state).events(:some_event).meta[:some_meta_tag] # old
531
+ MyClass.workflow_spec.states[:some_state].events[:some_event].meta[:some_meta_tag] # new
532
+
533
+ Causing state transitions
534
+
535
+ my_instance.workflow.my_event # old
536
+ my_instance.my_event! # new
537
+
538
+ when using both a block and a callback method for an event, the block executes prior to the callback
539
+
540
+
541
+ Changelog
542
+ ---------
543
+
544
+ ### New in the version 1.1.0
545
+
546
+ * Tested with ActiveRecord 4.0 (Rails 4.0)
547
+ * Tested with Ruby 2.0
548
+ * automatically generated scopes with names based on state names
549
+ * clean workflow definition override for class inheritance - undefining
550
+ the old convinience methods, s. <http://git.io/FZO02A>
551
+
552
+ ### New in the version 1.0.0
553
+
554
+ * **Support to private/protected callback methods.**
555
+ See also issues [#53](https://github.com/geekq/workflow/pull/53)
556
+ and [#58](https://github.com/geekq/workflow/pull/58). With the new
557
+ implementation:
558
+
559
+ * callback methods can be hidden (non public): both private methods
560
+ in the immediate class and protected methods somewhere in the class
561
+ hierarchy are supported
562
+ * no unintentional calls on `fail!` and other Kernel methods
563
+ * inheritance hierarchy with workflow is supported
564
+
565
+ * using Rails' 3.1 `update_column` whenever available so only the
566
+ workflow state column and not other pending attribute changes are
567
+ saved on state transition. Fallback to `update_attribute` for older
568
+ Rails and other ORMs. [commit](https://github.com/geekq/workflow/commit/7e091d8ded1aeeb0a86647bbf7d78ab3c9d0c458)
569
+
570
+ ### New in the version 0.8.7
571
+
572
+ * switch from [jeweler][] to pure bundler for building gems
573
+
574
+ ### New in the version 0.8.0
575
+
576
+ * check if a certain transition possible from the current state with
577
+ `can_....?`
578
+ * fix workflow_state persistence for multiple_workflows example
579
+ * add before_transition and after_transition hooks as suggested by
580
+ [kasperbn](https://github.com/kasperbn)
581
+
582
+ ### New in the version 0.7.0
583
+
584
+ * fix issue#10 Workflow::create_workflow_diagram documentation and path
585
+ escaping
586
+ * fix issue#7 workflow_column does not work STI (single table
587
+ inheritance) ActiveRecord models
588
+ * fix issue#5 Diagram generation fails for models in modules
589
+
590
+ ### New in the version 0.6.0
591
+
592
+ * enable multiple workflows by connecting workflow to object instances
593
+ (using metaclass) instead of connecting to a class, s. "Multiple
594
+ Workflows" section
595
+
596
+ ### New in the version 0.5.0
597
+
598
+ * fix issue#3 change the behaviour of halt! to immediately raise an
599
+ exception. See also http://github.com/geekq/workflow/issues/#issue/3
600
+
601
+ ### New in the version 0.4.0
602
+
603
+ * completely rewritten the documentation to match my branch
604
+ * switch to [jeweler][] for building gems
605
+ * use [gemcutter][] for gem distribution
606
+ * every described feature is backed up by an automated test
607
+
608
+ [jeweler]: http://github.com/technicalpickles/jeweler
609
+ [gemcutter]: http://gemcutter.org/gems/workflow
610
+
611
+ ### New in the version 0.3.0
612
+
613
+ Intermixing of transition graph definition (states, transitions)
614
+ on the one side and implementation of the actions on the other side
615
+ for a bigger state machine can introduce clutter.
616
+
617
+ To reduce this clutter it is now possible to use state entry- and
618
+ exit- hooks defined through a naming convention. For example, if there
619
+ is a state :pending, then instead of using a
620
+ block:
621
+
622
+ state :pending do
623
+ on_entry do
624
+ # your implementation here
625
+ end
626
+ end
627
+
628
+ you can hook in by defining method
629
+
630
+ def on_pending_exit(new_state, event, *args)
631
+ # your implementation here
632
+ end
633
+
634
+ anywhere in your class. You can also use a simpler function signature
635
+ like `def on_pending_exit(*args)` if your are not interested in
636
+ arguments. Please note: `def on_pending_exit()` with an empty list
637
+ would not work.
638
+
639
+ If both a function with a name according to naming convention and the
640
+ on_entry/on_exit block are given, then only on_entry/on_exit block is used.
641
+
642
+
643
+ Support
644
+ -------
645
+
646
+ ### Reporting bugs
647
+
648
+ <http://github.com/geekq/workflow/issues>
649
+
650
+
651
+ About
652
+ -----
653
+
654
+ Author: Vladimir Dobriakov, <http://www.innoq.com/blog/vd>, <http://blog.geekq.net/>
655
+
656
+ Copyright (c) 2008-2009 Vodafone
657
+
658
+ Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
659
+
660
+ Based on the work of Ryan Allen and Scott Barron
661
+
662
+ Licensed under MIT license, see the MIT-LICENSE file.
663
+