alexrevin-aasm_numerical 2.3.1

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.
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