workflow 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c18ece78f869cf0939fd801a99b63a93b99eafd
4
- data.tar.gz: 949a3b5bff0e79ced88e7ba5a3050820f58efd40
3
+ metadata.gz: 9b3ca21163c2fe680f13487bd176530fa13e09b0
4
+ data.tar.gz: 3413ad93c300e668a778db3480a7ab11ea3910f1
5
5
  SHA512:
6
- metadata.gz: 801d65a4147f19643d429d38e794f9529ff760147cf63185ab19762ddf618deb8f8ff19deb0ec590b8a3cd7166c99bfa129fdf716dc8b261c7cb98478685a494
7
- data.tar.gz: 5cc6722b3029ab9a96a2247215706315561735f6f7fa5950727f22b969040c53bba907532e3fada31d485c9c9ac6a39a66b130ed956f61b8ef68a2f566018332
6
+ metadata.gz: 78329832051f3798e4fc5742eb1ea0f8d10e4f342bcd42e5cf6cb680a7355747e1e12dbcc9a13bc17537843e59e238cc98edd72e67aff106d9b087b2f53901b8
7
+ data.tar.gz: 992fe9887c6825b047fbdb28dcc16f10994705fad01aeaedb27266e92469ab4e28f91784d8cb8cb7901b82ce1843b51c03f28abca4b9f8b2550a95b8c88e6c14
data/.gitignore CHANGED
@@ -6,7 +6,7 @@ html/
6
6
  .bundle
7
7
  .config
8
8
  .yardoc
9
- Gemfile.lock
9
+ Gemfile*.lock
10
10
  InstalledFiles
11
11
  _yardoc
12
12
  coverage
@@ -19,3 +19,9 @@ matrix:
19
19
 
20
20
  - rvm: 2.0.0
21
21
  gemfile: gemfiles/Gemfile.rails-3.x
22
+
23
+ - rvm: 2.0.0
24
+ gemfile: gemfiles/Gemfile.rails-4.0
25
+
26
+ - rvm: 2.1.0
27
+ gemfile: gemfiles/Gemfile.rails-3.x
@@ -1,5 +1,8 @@
1
- [![Build Status](https://travis-ci.org/geekq/workflow.png?branch=master)](https://travis-ci.org/geekq/workflow)
1
+ [![Build Status](https://travis-ci.org/geekq/workflow.png?branch=master)](https://travis-ci.org/geekq/workflow) Tested with [different Ruby and Rails versions](https://travis-ci.org/geekq/workflow)
2
2
 
3
+ Note: you can find documentation for specific workflow rubygem versions at
4
+ http://rubygems.org/gems/workflow: select a version (optional, default
5
+ is latest release), click "Documentation" link.
3
6
 
4
7
  What is workflow?
5
8
  -----------------
@@ -79,7 +82,7 @@ after another state (by the order they were defined):
79
82
  => true
80
83
  article.current_state >= :accepted
81
84
  => false
82
- article.between? :awaiting_review, :rejected
85
+ article.current_state.between? :awaiting_review, :rejected
83
86
  => true
84
87
 
85
88
  Now we can call the submit event, which transitions to the
@@ -147,9 +150,10 @@ be:
147
150
  end
148
151
  end
149
152
 
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
+ `article.review!; article.reject!` will cause state transition to
154
+ `being_reviewed` state, persist the new state (if integrated with
155
+ ActiveRecord), invoke this user defined `reject` method and finally
156
+ persist the `rejected` state.
153
157
 
154
158
  Note: on successful transition from one state to another the workflow
155
159
  gem immediately persists the new workflow state with `update_column()`,
@@ -300,11 +304,87 @@ couchrest library.
300
304
  Please also have a look at
301
305
  [the full source code](http://github.com/geekq/workflow/blob/master/test/couchtiny_example.rb).
302
306
 
303
- Integration with Mongoid
304
- ------------------------
305
307
 
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
+ Adapters to support other databases
309
+ -----------------------------------
310
+
311
+ I get a lot of requests to integrate persistence support for different
312
+ databases, object-relational adapters, column stores, document
313
+ databases.
314
+
315
+ To enable highest possible quality, avoid too many dependencies and to
316
+ avoid unneeded maintenance burden on the `workflow` core it is best to
317
+ implement such support as a separate gem.
318
+
319
+ Only support for the ActiveRecord will remain for the foreseeable
320
+ future. So Rails beginners can expect `workflow` to work with Rails out
321
+ of the box. Other already included adapters stay for a while but should
322
+ be extracted to separate gems.
323
+
324
+ If you want to implement support for your favorite ORM mapper or your
325
+ favorite NoSQL database, you just need to implement a module which
326
+ overrides the persistence methods `load_workflow_state` and
327
+ `persist_workflow_state`. Example:
328
+
329
+ module Workflow
330
+ module SuperCoolDb
331
+ module InstanceMethods
332
+ def load_workflow_state
333
+ # Load and return the workflow_state from some storage.
334
+ # You can use self.class.workflow_column configuration.
335
+ end
336
+
337
+ def persist_workflow_state(new_value)
338
+ # save the new_value workflow state
339
+ end
340
+ end
341
+
342
+ module ClassMethods
343
+ # class methods of your adapter go here
344
+ end
345
+
346
+ def self.included(klass)
347
+ klass.send :include, InstanceMethods
348
+ klass.extend ClassMethods
349
+ end
350
+ end
351
+ end
352
+
353
+ The user of the adapter can use it then as:
354
+
355
+ class Article
356
+ include Workflow
357
+ include Workflow:SuperCoolDb
358
+ workflow do
359
+ state :submitted
360
+ # ...
361
+ end
362
+ end
363
+
364
+ I can then link to your implementation from this README. Please let me
365
+ also know, if you need any interface beyond `load_workflow_state` and
366
+ `persist_workflow_state` methods to implement an adapter for your
367
+ favorite database.
368
+
369
+
370
+ Custom Versions of Existing Adapters
371
+ ------------------------------------
372
+
373
+ Other adapters (such as a custom ActiveRecord plugin) can be selected by adding a `workflow_adapter` class method, eg.
374
+
375
+ ```ruby
376
+ class Example < ActiveRecord::Base
377
+ def self.workflow_adapter
378
+ MyCustomAdapter
379
+ end
380
+ include Workflow
381
+
382
+ # ...
383
+ end
384
+ ```
385
+
386
+ (The above will include `MyCustomAdapter` *instead* of `Workflow::Adapter::ActiveRecord`.)
387
+
308
388
 
309
389
  Accessing your workflow specification
310
390
  -------------------------------------
@@ -341,6 +421,25 @@ The workflow library itself uses this feature to tweak the graphical
341
421
  representation of the workflow. See below.
342
422
 
343
423
 
424
+ Conditional event transitions
425
+ -----------------------------
426
+
427
+ Conditions are procs or lambdas added to events, like so:
428
+
429
+ state :off
430
+ event :turn_on, :transition_to => :on,
431
+ :if => proc { |device| device.battery_level > 0 }
432
+ event :turn_on, :transition_to => :low_battery,
433
+ :if => proc { |device| device.battery_level > 10 }
434
+ end
435
+
436
+ When calling a `device.can_<fire_event>?` check, or attempting a `device.<event>!`, each event is checked in turn:
437
+
438
+ * With no :if check, proceed as usual.
439
+ * If an :if check is present, proceed if it evaluates to true, or drop to the next event.
440
+ * If you've run out of events to check (eg. battery_level == 0), then the transition isn't possible.
441
+
442
+
344
443
  Advanced transition hooks
345
444
  -------------------------
346
445
 
@@ -541,6 +640,16 @@ when using both a block and a callback method for an event, the block executes p
541
640
  Changelog
542
641
  ---------
543
642
 
643
+ ### New in the version 1.2.0
644
+
645
+ * Fix issue #98 protected on\_\* callbacks in Ruby 2
646
+ * #106 Inherit exceptions from StandardError instead of Exception
647
+ * #109 Conditional event transitions, contributed by [damncabbage](http://robhoward.id.au/)
648
+ * New policy for supporting other databases - extract to separate
649
+ gems. See the README section above.
650
+ * #111 Custom Versions of Existing Adapters by [damncabbage](http://robhoward.id.au/)
651
+
652
+
544
653
  ### New in the version 1.1.0
545
654
 
546
655
  * Tested with ActiveRecord 4.0 (Rails 4.0)
@@ -651,7 +760,9 @@ Support
651
760
  About
652
761
  -----
653
762
 
654
- Author: Vladimir Dobriakov, <http://www.innoq.com/blog/vd>, <http://blog.geekq.net/>
763
+ Author: Vladimir Dobriakov, <http://www.mobile-web-consulting.de>, <http://blog.geekq.net/>
764
+
765
+ Copyright (c) 2010-2014 Vladimir Dobriakov, www.mobile-web-consulting.de
655
766
 
656
767
  Copyright (c) 2008-2009 Vodafone
657
768
 
@@ -7,5 +7,5 @@ group :development do
7
7
  gem "sqlite3"
8
8
  gem "mocha"
9
9
  gem "rake"
10
- gem "ruby-graphviz", ">= 1.0"
10
+ gem "ruby-graphviz", "~> 1.0.0"
11
11
  end
@@ -7,5 +7,5 @@ group :development do
7
7
  gem "sqlite3"
8
8
  gem "mocha"
9
9
  gem "rake"
10
- gem "ruby-graphviz", ">= 1.0"
10
+ gem "ruby-graphviz", "~> 1.0.0"
11
11
  end
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "minitest", "< 5.0.0" # 5.0.0 introduced incompatible changes renaming all the classes
5
+ gem "rdoc", ">= 3.12"
6
+ gem "bundler", ">= 1.0.0"
7
+ gem "activerecord", "~>4.0"
8
+ gem 'protected_attributes'
9
+ gem "sqlite3"
10
+ gem "mocha"
11
+ gem "rake"
12
+ gem "ruby-graphviz", "~> 1.0.0"
13
+ end
@@ -1,12 +1,13 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  group :development do
4
+ gem "minitest", "< 5.0.0" # 5.0.0 introduced incompatible changes renaming all the classes
4
5
  gem "rdoc", ">= 3.12"
5
6
  gem "bundler", ">= 1.0.0"
6
7
  gem "activerecord"
7
8
  gem "sqlite3"
8
9
  gem "mocha"
9
10
  gem "rake"
10
- gem "ruby-graphviz", ">= 1.0"
11
+ gem "ruby-graphviz", "~> 1.0.0"
11
12
  gem 'protected_attributes'
12
13
  end
@@ -38,7 +38,7 @@ module Workflow
38
38
  undef_method "#{state_name}?"
39
39
  end
40
40
 
41
- state.events.values.each do |event|
41
+ state.events.flat.each do |event|
42
42
  event_name = event.name
43
43
  module_eval do
44
44
  undef_method "#{event_name}!".to_sym
@@ -57,7 +57,7 @@ module Workflow
57
57
  end
58
58
  end
59
59
 
60
- state.events.values.each do |event|
60
+ state.events.flat.each do |event|
61
61
  event_name = event.name
62
62
  module_eval do
63
63
  define_method "#{event_name}!".to_sym do |*args|
@@ -65,7 +65,7 @@ module Workflow
65
65
  end
66
66
 
67
67
  define_method "can_#{event_name}?" do
68
- return self.current_state.events.include?(event_name)
68
+ return !!current_state.events.first_applicable(event_name, self)
69
69
  end
70
70
  end
71
71
  end
@@ -94,7 +94,7 @@ module Workflow
94
94
  end
95
95
 
96
96
  def process_event!(name, *args)
97
- event = current_state.events[name.to_sym]
97
+ event = current_state.events.first_applicable(name, self)
98
98
  raise NoTransitionAllowed.new(
99
99
  "There is no event #{name.to_sym} defined for the #{current_state} state") \
100
100
  if event.nil?
@@ -111,7 +111,7 @@ module Workflow
111
111
 
112
112
  begin
113
113
  return_value = run_action(event.action, *args) || run_action_callback(event.name, *args)
114
- rescue Exception => e
114
+ rescue StandardError => e
115
115
  run_on_error(e, from, to, name, *args)
116
116
  end
117
117
 
@@ -214,7 +214,7 @@ module Workflow
214
214
  instance_exec(prior_state.name, triggering_event, *args, &state.on_entry)
215
215
  else
216
216
  hook_name = "on_#{state}_entry"
217
- self.send hook_name, prior_state, triggering_event, *args if self.respond_to? hook_name
217
+ self.send hook_name, prior_state, triggering_event, *args if has_callback?(hook_name)
218
218
  end
219
219
  end
220
220
 
@@ -224,7 +224,7 @@ module Workflow
224
224
  instance_exec(new_state.name, triggering_event, *args, &state.on_exit)
225
225
  else
226
226
  hook_name = "on_#{state}_exit"
227
- self.send hook_name, new_state, triggering_event, *args if self.respond_to? hook_name
227
+ self.send hook_name, new_state, triggering_event, *args if has_callback?(hook_name)
228
228
  end
229
229
  end
230
230
  end
@@ -262,15 +262,15 @@ module Workflow
262
262
 
263
263
  klass.extend ClassMethods
264
264
 
265
- if Object.const_defined?(:ActiveRecord)
266
- if klass < ActiveRecord::Base
267
- klass.send :include, Adapter::ActiveRecord::InstanceMethods
268
- klass.send :extend, Adapter::ActiveRecord::Scopes
269
- klass.before_validation :write_initial_state
265
+ # Look for a hook; otherwise detect based on ancestor class.
266
+ if klass.respond_to?(:workflow_adapter)
267
+ klass.send :include, klass.workflow_adapter
268
+ else
269
+ if Object.const_defined?(:ActiveRecord) && klass < ActiveRecord::Base
270
+ klass.send :include, Adapter::ActiveRecord
270
271
  end
271
- elsif Object.const_defined?(:Remodel)
272
- if klass < Adapter::Remodel::Entity
273
- klass.send :include, Remodel::InstanceMethods
272
+ if Object.const_defined?(:Remodel) && klass < Adapter::Remodel::Entity
273
+ klass.send :include, Adapter::Remodel::InstanceMethods
274
274
  end
275
275
  end
276
276
  end
@@ -1,6 +1,12 @@
1
1
  module Workflow
2
2
  module Adapter
3
3
  module ActiveRecord
4
+ def self.included(klass)
5
+ klass.send :include, Adapter::ActiveRecord::InstanceMethods
6
+ klass.send :extend, Adapter::ActiveRecord::Scopes
7
+ klass.before_validation :write_initial_state
8
+ end
9
+
4
10
  module InstanceMethods
5
11
  def load_workflow_state
6
12
  read_attribute(self.class.workflow_column)
@@ -1,7 +1,7 @@
1
1
  begin
2
2
  require 'rubygems'
3
3
 
4
- gem 'ruby-graphviz', '>=1.0'
4
+ gem 'ruby-graphviz', '~> 1.0.0'
5
5
  gem 'activesupport'
6
6
 
7
7
  require 'graphviz'
@@ -57,7 +57,7 @@ module Workflow
57
57
  node = state.draw(graph)
58
58
  node.fontname = options[:font]
59
59
 
60
- state.events.each do |_, event|
60
+ state.events.flat.each do |event|
61
61
  edge = event.draw(graph, state)
62
62
  edge.fontname = options[:font]
63
63
  end
@@ -1,5 +1,5 @@
1
1
  module Workflow
2
- class TransitionHalted < Exception
2
+ class TransitionHalted < StandardError
3
3
 
4
4
  attr_reader :halted_because
5
5
 
@@ -10,9 +10,9 @@ module Workflow
10
10
 
11
11
  end
12
12
 
13
- class NoTransitionAllowed < Exception; end
13
+ class NoTransitionAllowed < StandardError; end
14
14
 
15
- class WorkflowError < Exception; end
15
+ class WorkflowError < StandardError; end
16
16
 
17
- class WorkflowDefinitionError < Exception; end
18
- end
17
+ class WorkflowDefinitionError < StandardError; end
18
+ end
@@ -1,10 +1,22 @@
1
1
  module Workflow
2
2
  class Event
3
3
 
4
- attr_accessor :name, :transitions_to, :meta, :action
4
+ attr_accessor :name, :transitions_to, :meta, :action, :condition
5
5
 
6
- def initialize(name, transitions_to, meta = {}, &action)
7
- @name, @transitions_to, @meta, @action = name, transitions_to.to_sym, meta, action
6
+ def initialize(name, transitions_to, condition = nil, meta = {}, &action)
7
+ @name = name
8
+ @transitions_to = transitions_to.to_sym
9
+ @meta = meta
10
+ @action = action
11
+ @condition = if condition.nil? || condition.respond_to?(:call)
12
+ condition
13
+ else
14
+ raise TypeError, 'condition must be nil or callable (eg. a proc or lambda)'
15
+ end
16
+ end
17
+
18
+ def condition_applicable?(object)
19
+ condition ? condition.call(object) : true
8
20
  end
9
21
 
10
22
  def draw(graph, from_state)
@@ -15,4 +27,4 @@ module Workflow
15
27
  @name.to_s
16
28
  end
17
29
  end
18
- end
30
+ end
@@ -0,0 +1,36 @@
1
+ module Workflow
2
+ class EventCollection < Hash
3
+
4
+ def [](name)
5
+ super name.to_sym # Normalize to symbol
6
+ end
7
+
8
+ def push(name, event)
9
+ key = name.to_sym
10
+ self[key] ||= []
11
+ self[key] << event
12
+ end
13
+
14
+ def flat
15
+ self.values.flatten.uniq do |event|
16
+ [:name, :transitions_to, :meta, :action].map { |m| event.send(m) }
17
+ end
18
+ end
19
+
20
+ def include?(name_or_obj)
21
+ case name_or_obj
22
+ when Event
23
+ flat.include? name_or_obj
24
+ else
25
+ !(self[name_or_obj].nil?)
26
+ end
27
+ end
28
+
29
+ def first_applicable(name, object_context)
30
+ (self[name] || []).detect do |event|
31
+ event.condition_applicable?(object_context) && event
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -1,5 +1,6 @@
1
1
  require 'workflow/state'
2
2
  require 'workflow/event'
3
+ require 'workflow/event_collection'
3
4
  require 'workflow/errors'
4
5
 
5
6
  module Workflow
@@ -30,11 +31,13 @@ module Workflow
30
31
 
31
32
  def event(name, args = {}, &action)
32
33
  target = args[:transitions_to] || args[:transition_to]
34
+ condition = args[:if]
33
35
  raise WorkflowDefinitionError.new(
34
36
  "missing ':transitions_to' in workflow event definition for '#{name}'") \
35
37
  if target.nil?
36
- @scoped_state.events[name.to_sym] =
37
- Workflow::Event.new(name, target, (args[:meta] or {}), &action)
38
+ @scoped_state.events.push(
39
+ name, Workflow::Event.new(name, target, condition, (args[:meta] or {}), &action)
40
+ )
38
41
  end
39
42
 
40
43
  def on_entry(&proc)
@@ -61,4 +64,4 @@ module Workflow
61
64
  @on_error_proc = proc
62
65
  end
63
66
  end
64
- end
67
+ end
@@ -4,7 +4,7 @@ module Workflow
4
4
  attr_reader :spec
5
5
 
6
6
  def initialize(name, spec, meta = {})
7
- @name, @spec, @events, @meta = name, spec, Hash.new, meta
7
+ @name, @spec, @events, @meta = name, spec, EventCollection.new, meta
8
8
  end
9
9
 
10
10
  def draw(graph)
@@ -1,3 +1,3 @@
1
1
  module Workflow
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require 'workflow'
3
+ class AdapterHookTest < ActiveRecordTestCase
4
+ test 'hook to choose adapter' do
5
+
6
+ ActiveRecord::Schema.define do
7
+ create_table(:examples) { |t| t.string :workflow_state }
8
+ end
9
+
10
+ class DefaultAdapter < ActiveRecord::Base
11
+ self.table_name = :examples
12
+ include Workflow
13
+ workflow do
14
+ state(:initial) { event :progress, :transitions_to => :last }
15
+ state(:last)
16
+ end
17
+ end
18
+
19
+ class ChosenByHookAdapter < ActiveRecord::Base
20
+ self.table_name = :examples
21
+ attr_reader :foo
22
+ def self.workflow_adapter
23
+ Module.new do
24
+ def load_workflow_state
25
+ @foo if defined?(@foo)
26
+ end
27
+ def persist_workflow_state(new_value)
28
+ @foo = new_value
29
+ end
30
+ end
31
+ end
32
+
33
+ include Workflow
34
+ workflow do
35
+ state(:initial) { event :progress, :transitions_to => :last }
36
+ state(:last)
37
+ end
38
+ end
39
+
40
+ default = DefaultAdapter.create
41
+ assert default.initial?
42
+ default.progress!
43
+ assert default.last?
44
+ assert DefaultAdapter.find(default.id).last?, 'should have persisted via ActiveRecord'
45
+
46
+ hook = ChosenByHookAdapter.create
47
+ assert hook.initial?
48
+ hook.progress!
49
+ assert_equal hook.foo, 'last', 'should have "persisted" with custom adapter'
50
+ assert ChosenByHookAdapter.find(hook.id).initial?, 'should not have persisted via ActiveRecord'
51
+ end
52
+ end
@@ -28,13 +28,15 @@ class AdvanceExamplesTest < ActiveRecordTestCase
28
28
  spec.state_names.each do |state_name|
29
29
  state = spec.states[state_name]
30
30
 
31
- (state.events.values.reject {|e| e.name.to_s =~ /^revert_/ }).each do |event|
31
+ (state.events.flat.reject {|e| e.name.to_s =~ /^revert_/ }).each do |event|
32
32
  event_name = event.name
33
33
  revert_event_name = "revert_" + event_name.to_s
34
34
 
35
35
  # Add revert events
36
- spec.states[event.transitions_to.to_sym].events[revert_event_name.to_sym] =
37
- Workflow::Event.new(revert_event_name, state, {})
36
+ spec.states[event.transitions_to.to_sym].events.push(
37
+ revert_event_name,
38
+ Workflow::Event.new(revert_event_name, state)
39
+ )
38
40
 
39
41
  # Add methods for revert events
40
42
  Article.module_eval do
@@ -43,7 +43,7 @@ class Article < ActiveRecord::Base
43
43
  singleton = class << self; self end
44
44
  validations = Proc.new {}
45
45
 
46
- meta = Article.workflow_spec.states[from].events[triggering_event].meta
46
+ meta = Article.workflow_spec.states[from].events[triggering_event].first.meta
47
47
  fields_to_validate = meta[:validates_presence_of]
48
48
  if fields_to_validate
49
49
  validations = Proc.new {
@@ -282,6 +282,7 @@ class MainTest < ActiveRecordTestCase
282
282
  args = mock()
283
283
  args.expects(:log).with('in private callback').once
284
284
  args.expects(:log).with('in protected callback in the base class').once
285
+ args.expects(:log).with('in protected callback `on_assigned_entry`').once
285
286
 
286
287
  b = Class.new # the base class with a protected callback
287
288
  b.class_eval do
@@ -289,6 +290,7 @@ class MainTest < ActiveRecordTestCase
289
290
  def assign_old(args)
290
291
  args.log('in protected callback in the base class')
291
292
  end
293
+
292
294
  end
293
295
 
294
296
  c = Class.new(b) # inheriting class with an additional protected callback
@@ -303,6 +305,11 @@ class MainTest < ActiveRecordTestCase
303
305
  state :assigned_old
304
306
  end
305
307
 
308
+ protected
309
+ def on_assigned_entry(prev_state, event, args)
310
+ args.log('in protected callback `on_assigned_entry`')
311
+ end
312
+
306
313
  private
307
314
  def assign(args)
308
315
  args.log('in private callback')
@@ -509,6 +516,7 @@ class MainTest < ActiveRecordTestCase
509
516
  event :go_to_college, :transitions_to => :student
510
517
  end
511
518
  state :student
519
+ state :street
512
520
  end
513
521
  end
514
522
 
@@ -517,6 +525,38 @@ class MainTest < ActiveRecordTestCase
517
525
  assert_equal false, human.can_go_to_college?
518
526
  end
519
527
 
528
+ test 'can_<fire_event>? with conditions' do
529
+ c = Class.new do
530
+ include Workflow
531
+ workflow do
532
+ state :off do
533
+ event :turn_on, :transitions_to => :on, :if => proc { |obj| obj.battery > 10 }
534
+ event :turn_on, :transitions_to => :low_battery, :if => proc { |obj| obj.battery > 0 }
535
+ end
536
+ state :on
537
+ state :low_battery
538
+ end
539
+ attr_reader :battery
540
+ def initialize(battery)
541
+ @battery = battery
542
+ end
543
+ end
544
+
545
+ device = c.new 0
546
+ assert_equal false, device.can_turn_on?
547
+
548
+ device = c.new 5
549
+ assert device.can_turn_on?
550
+ device.turn_on!
551
+ assert device.low_battery?
552
+ assert_equal false, device.on?
553
+
554
+ device = c.new 50
555
+ assert device.can_turn_on?
556
+ device.turn_on!
557
+ assert device.on?
558
+ end
559
+
520
560
  test 'workflow graph generation' do
521
561
  Dir.chdir('/tmp') do
522
562
  capture_streams do
@@ -27,6 +27,6 @@ Gem::Specification.new do |gem|
27
27
  gem.add_development_dependency 'sqlite3'
28
28
  gem.add_development_dependency 'mocha'
29
29
  gem.add_development_dependency 'rake'
30
- gem.add_development_dependency 'ruby-graphviz', ['>= 1.0']
30
+ gem.add_development_dependency 'ruby-graphviz', ['~> 1.0.0']
31
31
  end
32
32
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: workflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dobriakov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-14 00:00:00.000000000 Z
11
+ date: 2014-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: ruby-graphviz
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '>='
101
+ - - ~>
102
102
  - !ruby/object:Gem::Version
103
- version: '1.0'
103
+ version: 1.0.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - ~>
109
109
  - !ruby/object:Gem::Version
110
- version: '1.0'
110
+ version: 1.0.0
111
111
  description: |2
112
112
  Workflow is a finite-state-machine-inspired API for modeling and interacting
113
113
  with what we tend to refer to as 'workflow'.
@@ -132,6 +132,7 @@ files:
132
132
  - Rakefile
133
133
  - gemfiles/Gemfile.rails-2.3.x
134
134
  - gemfiles/Gemfile.rails-3.x
135
+ - gemfiles/Gemfile.rails-4.0
135
136
  - gemfiles/Gemfile.rails-edge
136
137
  - lib/workflow.rb
137
138
  - lib/workflow/adapters/active_record.rb
@@ -139,11 +140,13 @@ files:
139
140
  - lib/workflow/draw.rb
140
141
  - lib/workflow/errors.rb
141
142
  - lib/workflow/event.rb
143
+ - lib/workflow/event_collection.rb
142
144
  - lib/workflow/specification.rb
143
145
  - lib/workflow/state.rb
144
146
  - lib/workflow/version.rb
145
147
  - orders_workflow.png
146
148
  - test/active_record_scopes_test.rb
149
+ - test/adapter_hook_test.rb
147
150
  - test/advanced_examples_test.rb
148
151
  - test/advanced_hooks_and_validation_test.rb
149
152
  - test/attr_protected_test.rb
@@ -178,12 +181,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
181
  version: '0'
179
182
  requirements: []
180
183
  rubyforge_project:
181
- rubygems_version: 2.0.0
184
+ rubygems_version: 2.0.14
182
185
  signing_key:
183
186
  specification_version: 4
184
187
  summary: A replacement for acts_as_state_machine.
185
188
  test_files:
186
189
  - test/active_record_scopes_test.rb
190
+ - test/adapter_hook_test.rb
187
191
  - test/advanced_examples_test.rb
188
192
  - test/advanced_hooks_and_validation_test.rb
189
193
  - test/attr_protected_test.rb