alexrevin-aasm_numerical 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.document +5 -0
  2. data/.gitignore +11 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +20 -0
  5. data/README.md +149 -0
  6. data/Rakefile +27 -0
  7. data/lib/alexrevin-aasm_numerical.rb +10 -0
  8. data/lib/alexrevin-aasm_numerical/aasm.rb +222 -0
  9. data/lib/alexrevin-aasm_numerical/event.rb +127 -0
  10. data/lib/alexrevin-aasm_numerical/localizer.rb +36 -0
  11. data/lib/alexrevin-aasm_numerical/persistence.rb +14 -0
  12. data/lib/alexrevin-aasm_numerical/persistence/active_record_persistence.rb +257 -0
  13. data/lib/alexrevin-aasm_numerical/state.rb +53 -0
  14. data/lib/alexrevin-aasm_numerical/state_machine.rb +31 -0
  15. data/lib/alexrevin-aasm_numerical/state_transition.rb +46 -0
  16. data/lib/alexrevin-aasm_numerical/supporting_classes.rb +6 -0
  17. data/lib/alexrevin-aasm_numerical/version.rb +3 -0
  18. data/spec/database.yml +3 -0
  19. data/spec/en.yml +10 -0
  20. data/spec/functional/conversation.rb +49 -0
  21. data/spec/functional/conversation_spec.rb +8 -0
  22. data/spec/schema.rb +7 -0
  23. data/spec/spec_helper.rb +16 -0
  24. data/spec/unit/aasm_spec.rb +462 -0
  25. data/spec/unit/active_record_persistence_spec.rb +246 -0
  26. data/spec/unit/before_after_callbacks_spec.rb +79 -0
  27. data/spec/unit/event_spec.rb +140 -0
  28. data/spec/unit/localizer_spec.rb +51 -0
  29. data/spec/unit/state_spec.rb +85 -0
  30. data/spec/unit/state_transition_spec.rb +163 -0
  31. data/test/functional/auth_machine_test.rb +148 -0
  32. data/test/models/process.rb +18 -0
  33. data/test/test_helper.rb +43 -0
  34. data/test/unit/aasm_test.rb +0 -0
  35. data/test/unit/event_test.rb +54 -0
  36. data/test/unit/state_machine_test.rb +37 -0
  37. data/test/unit/state_test.rb +69 -0
  38. data/test/unit/state_transition_test.rb +75 -0
  39. metadata +254 -0
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+ require 'logger'
4
+ require 'i18n'
5
+
6
+ ActiveRecord::Base.logger = Logger.new(STDERR)
7
+
8
+ class LocalizerTestModel < ActiveRecord::Base
9
+ include AASM
10
+
11
+ attr_accessor :aasm_state
12
+
13
+ aasm_initial_state :open
14
+ aasm_state :opened
15
+ aasm_state :closed
16
+
17
+ aasm_event :close
18
+ aasm_event :open
19
+ end
20
+
21
+ describe AASM::Localizer do
22
+ before(:all) do
23
+ I18n.load_path << 'spec/en.yml'
24
+ I18n.default_locale = :en
25
+ end
26
+
27
+ after(:all) { I18n.load_path.clear }
28
+
29
+ let (:foo_opened) { LocalizerTestModel.new }
30
+ let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
31
+
32
+ context '.human_state' do
33
+ it 'should return translated state value' do
34
+ foo_opened.human_state.should == "It's opened now!"
35
+ end
36
+
37
+ it 'should return humanized value if not localized' do
38
+ foo_closed.human_state.should == "Closed"
39
+ end
40
+ end
41
+
42
+ context '.human_event_name' do
43
+ it 'should return translated event name' do
44
+ LocalizerTestModel.human_event_name(:close).should == "Let's close it!"
45
+ end
46
+
47
+ it 'should return humanized event name' do
48
+ LocalizerTestModel.human_event_name(:open).should == "Open"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,85 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ # TODO These are specs ported from original aasm
4
+ describe AASM::SupportingClasses::State do
5
+ before(:each) do
6
+ @name = :astate
7
+ @options = { :crazy_custom_key => 'key' }
8
+ end
9
+
10
+ def new_state(options={})
11
+ AASM::SupportingClasses::State.new(@name, @options.merge(options))
12
+ end
13
+
14
+ it 'should set the name' do
15
+ state = new_state
16
+
17
+ state.name.should == :astate
18
+ end
19
+
20
+ it 'should set the options and expose them as options' do
21
+ state = new_state
22
+
23
+ state.options.should == @options
24
+ end
25
+
26
+ it 'should be equal to a symbol of the same name' do
27
+ state = new_state
28
+
29
+ state.should == :astate
30
+ end
31
+
32
+ it 'should be equal to a State of the same name' do
33
+ new_state.should == new_state
34
+ end
35
+
36
+ it 'should send a message to the record for an action if the action is present as a symbol' do
37
+ state = new_state(:entering => :foo)
38
+
39
+ record = mock('record')
40
+ record.should_receive(:foo)
41
+
42
+ state.call_action(:entering, record)
43
+ end
44
+
45
+ it 'should send a message to the record for an action if the action is present as a string' do
46
+ state = new_state(:entering => 'foo')
47
+
48
+ record = mock('record')
49
+ record.should_receive(:foo)
50
+
51
+ state.call_action(:entering, record)
52
+ end
53
+
54
+ it 'should send a message to the record for each action' do
55
+ state = new_state(:entering => [:a, :b, "c", lambda {|r| r.foobar }])
56
+
57
+ record = mock('record')
58
+ record.should_receive(:a)
59
+ record.should_receive(:b)
60
+ record.should_receive(:c)
61
+ record.should_receive(:foobar)
62
+
63
+ state.call_action(:entering, record)
64
+ end
65
+
66
+ it "should stop calling actions if one of them raises :halt_aasm_chain" do
67
+ state = new_state(:entering => [:a, :b, :c])
68
+
69
+ record = mock('record')
70
+ record.should_receive(:a)
71
+ record.should_receive(:b).and_throw(:halt_aasm_chain)
72
+ record.should_not_receive(:c)
73
+
74
+ state.call_action(:entering, record)
75
+ end
76
+
77
+ it 'should call a proc, passing in the record for an action if the action is present' do
78
+ state = new_state(:entering => Proc.new {|r| r.foobar})
79
+
80
+ record = mock('record')
81
+ record.should_receive(:foobar)
82
+
83
+ state.call_action(:entering, record)
84
+ end
85
+ end
@@ -0,0 +1,163 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe AASM::SupportingClasses::StateTransition do
4
+ it 'should set from, to, and opts attr readers' do
5
+ opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
6
+ st = AASM::SupportingClasses::StateTransition.new(opts)
7
+
8
+ st.from.should == opts[:from]
9
+ st.to.should == opts[:to]
10
+ st.opts.should == opts
11
+ end
12
+
13
+ it 'should pass equality check if from and to are the same' do
14
+ opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
15
+ st = AASM::SupportingClasses::StateTransition.new(opts)
16
+
17
+ obj = mock('object')
18
+ obj.stub!(:from).and_return(opts[:from])
19
+ obj.stub!(:to).and_return(opts[:to])
20
+
21
+ st.should == obj
22
+ end
23
+
24
+ it 'should fail equality check if from are not the same' do
25
+ opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
26
+ st = AASM::SupportingClasses::StateTransition.new(opts)
27
+
28
+ obj = mock('object')
29
+ obj.stub!(:from).and_return('blah')
30
+ obj.stub!(:to).and_return(opts[:to])
31
+
32
+ st.should_not == obj
33
+ end
34
+
35
+ it 'should fail equality check if to are not the same' do
36
+ opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
37
+ st = AASM::SupportingClasses::StateTransition.new(opts)
38
+
39
+ obj = mock('object')
40
+ obj.stub!(:from).and_return(opts[:from])
41
+ obj.stub!(:to).and_return('blah')
42
+
43
+ st.should_not == obj
44
+ end
45
+ end
46
+
47
+ describe AASM::SupportingClasses::StateTransition, '- when performing guard checks' do
48
+ it 'should return true of there is no guard' do
49
+ opts = {:from => 'foo', :to => 'bar'}
50
+ st = AASM::SupportingClasses::StateTransition.new(opts)
51
+
52
+ st.perform(nil).should be_true
53
+ end
54
+
55
+ it 'should call the method on the object if guard is a symbol' do
56
+ opts = {:from => 'foo', :to => 'bar', :guard => :test}
57
+ st = AASM::SupportingClasses::StateTransition.new(opts)
58
+
59
+ obj = mock('object')
60
+ obj.should_receive(:test)
61
+
62
+ st.perform(obj)
63
+ end
64
+
65
+ it 'should call the method on the object if guard is a string' do
66
+ opts = {:from => 'foo', :to => 'bar', :guard => 'test'}
67
+ st = AASM::SupportingClasses::StateTransition.new(opts)
68
+
69
+ obj = mock('object')
70
+ obj.should_receive(:test)
71
+
72
+ st.perform(obj)
73
+ end
74
+
75
+ it 'should call the proc passing the object if the guard is a proc' do
76
+ opts = {:from => 'foo', :to => 'bar', :guard => Proc.new {|o| o.test}}
77
+ st = AASM::SupportingClasses::StateTransition.new(opts)
78
+
79
+ obj = mock('object')
80
+ obj.should_receive(:test)
81
+
82
+ st.perform(obj)
83
+ end
84
+ end
85
+
86
+ describe AASM::SupportingClasses::StateTransition, '- when executing the transition with a Proc' do
87
+ it 'should call a Proc on the object with args' do
88
+ opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {|o| o.test}}
89
+ st = AASM::SupportingClasses::StateTransition.new(opts)
90
+ args = {:arg1 => '1', :arg2 => '2'}
91
+ obj = mock('object')
92
+
93
+ opts[:on_transition].should_receive(:call).with(any_args)
94
+
95
+ st.execute(obj, args)
96
+ end
97
+
98
+ it 'should call a Proc on the object without args' do
99
+ opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {||}}
100
+ st = AASM::SupportingClasses::StateTransition.new(opts)
101
+ args = {:arg1 => '1', :arg2 => '2'}
102
+ obj = mock('object')
103
+
104
+ opts[:on_transition].should_receive(:call).with(no_args)
105
+
106
+ st.execute(obj, args)
107
+ end
108
+ end
109
+
110
+ describe AASM::SupportingClasses::StateTransition, '- when executing the transition with an :on_transtion method call' do
111
+ it 'should accept a String for the method name' do
112
+ opts = {:from => 'foo', :to => 'bar', :on_transition => 'test'}
113
+ st = AASM::SupportingClasses::StateTransition.new(opts)
114
+ args = {:arg1 => '1', :arg2 => '2'}
115
+ obj = mock('object')
116
+
117
+ obj.should_receive(:test)
118
+
119
+ st.execute(obj, args)
120
+ end
121
+
122
+ it 'should accept a Symbol for the method name' do
123
+ opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
124
+ st = AASM::SupportingClasses::StateTransition.new(opts)
125
+ args = {:arg1 => '1', :arg2 => '2'}
126
+ obj = mock('object')
127
+
128
+ obj.should_receive(:test)
129
+
130
+ st.execute(obj, args)
131
+ end
132
+
133
+ it 'should pass args if the target method accepts them' do
134
+ opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
135
+ st = AASM::SupportingClasses::StateTransition.new(opts)
136
+ args = {:arg1 => '1', :arg2 => '2'}
137
+ obj = mock('object')
138
+
139
+ obj.class.class_eval do
140
+ define_method(:test) {|*args| 'success'}
141
+ end
142
+
143
+ return_value = st.execute(obj, args)
144
+
145
+ return_value.should == 'success'
146
+ end
147
+
148
+ it 'should NOT pass args if the target method does NOT accept them' do
149
+ opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
150
+ st = AASM::SupportingClasses::StateTransition.new(opts)
151
+ args = {:arg1 => '1', :arg2 => '2'}
152
+ obj = mock('object')
153
+
154
+ obj.class.class_eval do
155
+ define_method(:test) {|*args| 'success'}
156
+ end
157
+
158
+ return_value = st.execute(obj, args)
159
+
160
+ return_value.should == 'success'
161
+ end
162
+
163
+ end
@@ -0,0 +1,148 @@
1
+ require 'test_helper'
2
+
3
+ class AuthMachine
4
+ include AASM
5
+
6
+ attr_accessor :activation_code, :activated_at, :deleted_at
7
+
8
+ aasm_initial_state :pending
9
+
10
+ aasm_state :passive
11
+ aasm_state :pending, :enter => :make_activation_code
12
+ aasm_state :active, :enter => :do_activate
13
+ aasm_state :suspended
14
+ aasm_state :deleted, :enter => :do_delete, :exit => :do_undelete
15
+
16
+ aasm_event :register do
17
+ transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
18
+ end
19
+
20
+ aasm_event :activate do
21
+ transitions :from => :pending, :to => :active
22
+ end
23
+
24
+ aasm_event :suspend do
25
+ transitions :from => [:passive, :pending, :active], :to => :suspended
26
+ end
27
+
28
+ aasm_event :delete do
29
+ transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
30
+ end
31
+
32
+ # a dummy event that can never happen
33
+ aasm_event :unpassify do
34
+ transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
35
+ end
36
+
37
+ aasm_event :unsuspend do
38
+ transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
39
+ transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
40
+ transitions :from => :suspended, :to => :passive
41
+ end
42
+
43
+ def initialize
44
+ # the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
45
+ # lets do something similar here for testing purposes.
46
+ aasm_enter_initial_state
47
+ end
48
+
49
+ def make_activation_code
50
+ @activation_code = 'moo'
51
+ end
52
+
53
+ def do_activate
54
+ @activated_at = Time.now
55
+ @activation_code = nil
56
+ end
57
+
58
+ def do_delete
59
+ @deleted_at = Time.now
60
+ end
61
+
62
+ def do_undelete
63
+ @deleted_at = false
64
+ end
65
+
66
+ def can_register?
67
+ true
68
+ end
69
+
70
+ def has_activated?
71
+ !!@activated_at
72
+ end
73
+
74
+ def has_activation_code?
75
+ !!@activation_code
76
+ end
77
+ end
78
+
79
+ class AuthMachineTest < Test::Unit::TestCase
80
+ context 'authentication state machine' do
81
+ context 'on initialization' do
82
+ setup do
83
+ @auth = AuthMachine.new
84
+ end
85
+
86
+ should 'be in the pending state' do
87
+ assert_equal :pending, @auth.aasm_current_state
88
+ end
89
+
90
+ should 'have an activation code' do
91
+ assert @auth.has_activation_code?
92
+ assert_not_nil @auth.activation_code
93
+ end
94
+ end
95
+
96
+ context 'when being unsuspended' do
97
+
98
+ should 'be able to be unsuspended' do
99
+ @auth = AuthMachine.new
100
+ @auth.activate!
101
+ @auth.suspend!
102
+ assert @auth.may_unsuspend?
103
+ end
104
+
105
+ should 'not be able to be unsuspended into active' do
106
+ @auth = AuthMachine.new
107
+ @auth.suspend!
108
+ assert_equal false, @auth.may_unsuspend?(:active)
109
+ end
110
+
111
+ should 'not be able to be unpassified' do
112
+ @auth = AuthMachine.new
113
+ @auth.activate!
114
+ @auth.suspend!
115
+ @auth.unsuspend!
116
+
117
+ assert_equal false, @auth.may_unpassify?
118
+ end
119
+
120
+ should 'be active if previously activated' do
121
+ @auth = AuthMachine.new
122
+ @auth.activate!
123
+ @auth.suspend!
124
+ @auth.unsuspend!
125
+
126
+ assert_equal :active, @auth.aasm_current_state
127
+ end
128
+
129
+ should 'be pending if not previously activated, but an activation code is present' do
130
+ @auth = AuthMachine.new
131
+ @auth.suspend!
132
+ @auth.unsuspend!
133
+
134
+ assert_equal :pending, @auth.aasm_current_state
135
+ end
136
+
137
+ should 'be passive if not previously activated and there is no activation code' do
138
+ @auth = AuthMachine.new
139
+ @auth.activation_code = nil
140
+ @auth.suspend!
141
+ @auth.unsuspend!
142
+
143
+ assert_equal :passive, @auth.aasm_current_state
144
+ end
145
+ end
146
+
147
+ end
148
+ end