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,246 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'active_record'
4
+ require 'logger'
5
+
6
+ load_schema
7
+
8
+ ActiveRecord::Base.logger = Logger.new(STDERR)
9
+
10
+ class Gate < ActiveRecord::Base
11
+ include AASM
12
+
13
+ # Fake this column for testing purposes
14
+ attr_accessor :aasm_state
15
+
16
+ aasm_state :opened
17
+ aasm_state :closed
18
+
19
+ aasm_event :view do
20
+ transitions :to => :read, :from => [:needs_attention]
21
+ end
22
+ end
23
+
24
+ class Reader < ActiveRecord::Base
25
+ def aasm_read_state
26
+ "fi"
27
+ end
28
+ include AASM
29
+ end
30
+
31
+ class Writer < ActiveRecord::Base
32
+ def aasm_write_state(state)
33
+ "fo"
34
+ end
35
+ include AASM
36
+ end
37
+
38
+ class Transient < ActiveRecord::Base
39
+ def aasm_write_state_without_persistence(state)
40
+ "fum"
41
+ end
42
+ include AASM
43
+ end
44
+
45
+ class Simple < ActiveRecord::Base
46
+ include AASM
47
+ aasm_column :status
48
+ end
49
+
50
+ class Derivate < Simple
51
+ end
52
+
53
+ class Thief < ActiveRecord::Base
54
+ set_table_name "thieves"
55
+ include AASM
56
+ aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
57
+ aasm_state :rich
58
+ aasm_state :jailed
59
+ attr_accessor :skilled, :aasm_state
60
+ end
61
+
62
+ shared_examples_for "aasm model" do
63
+ it "should include AASM::Persistence::ActiveRecordPersistence" do
64
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
65
+ end
66
+ it "should include AASM::Persistence::ActiveRecordPersistence::InstanceMethods" do
67
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
68
+ end
69
+ end
70
+
71
+ describe Gate, "class methods" do
72
+ before(:each) do
73
+ @klass = Gate
74
+ end
75
+ it_should_behave_like "aasm model"
76
+ it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
77
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
78
+ end
79
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
80
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
81
+ end
82
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
83
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
84
+ end
85
+ end
86
+
87
+ describe Reader, "class methods" do
88
+ before(:each) do
89
+ @klass = Reader
90
+ end
91
+ it_should_behave_like "aasm model"
92
+ it "should not include AASM::Persistence::ActiveRecordPersistence::ReadState" do
93
+ @klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
94
+ end
95
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
96
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
97
+ end
98
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
99
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
100
+ end
101
+ end
102
+
103
+ describe Writer, "class methods" do
104
+ before(:each) do
105
+ @klass = Writer
106
+ end
107
+ it_should_behave_like "aasm model"
108
+ it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
109
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
110
+ end
111
+ it "should not include AASM::Persistence::ActiveRecordPersistence::WriteState" do
112
+ @klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
113
+ end
114
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
115
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
116
+ end
117
+ end
118
+
119
+ describe Transient, "class methods" do
120
+ before(:each) do
121
+ @klass = Transient
122
+ end
123
+ it_should_behave_like "aasm model"
124
+ it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
125
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
126
+ end
127
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
128
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
129
+ end
130
+ it "should not include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
131
+ @klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
132
+ end
133
+ end
134
+
135
+ describe Gate, "instance methods" do
136
+
137
+ it "should respond to aasm read state when not previously defined" do
138
+ Gate.new.should respond_to(:aasm_read_state)
139
+ end
140
+
141
+ it "should respond to aasm write state when not previously defined" do
142
+ Gate.new.should respond_to(:aasm_write_state)
143
+ end
144
+
145
+ it "should respond to aasm write state without persistence when not previously defined" do
146
+ Gate.new.should respond_to(:aasm_write_state_without_persistence)
147
+ end
148
+
149
+ it "should return the initial state when new and the aasm field is nil" do
150
+ Gate.new.aasm_current_state.should == :opened
151
+ end
152
+
153
+ it "should return the aasm column when new and the aasm field is not nil" do
154
+ foo = Gate.new
155
+ foo.aasm_state = "closed"
156
+ foo.aasm_current_state.should == :closed
157
+ end
158
+
159
+ it "should return the aasm column when not new and the aasm_column is not nil" do
160
+ foo = Gate.new
161
+ foo.stub!(:new_record?).and_return(false)
162
+ foo.aasm_state = "state"
163
+ foo.aasm_current_state.should == :state
164
+ end
165
+
166
+ it "should allow a nil state" do
167
+ foo = Gate.new
168
+ foo.stub!(:new_record?).and_return(false)
169
+ foo.aasm_state = nil
170
+ foo.aasm_current_state.should be_nil
171
+ end
172
+
173
+ it "should have aasm_ensure_initial_state" do
174
+ foo = Gate.new
175
+ foo.send :aasm_ensure_initial_state
176
+ end
177
+
178
+ it "should call aasm_ensure_initial_state on validation before create" do
179
+ foo = Gate.new
180
+ foo.should_receive(:aasm_ensure_initial_state).and_return(true)
181
+ foo.valid?
182
+ end
183
+
184
+ it "should call aasm_ensure_initial_state on validation before create" do
185
+ foo = Gate.new
186
+ foo.stub!(:new_record?).and_return(false)
187
+ foo.should_not_receive(:aasm_ensure_initial_state)
188
+ foo.valid?
189
+ end
190
+
191
+ end
192
+
193
+ describe 'Derivates' do
194
+ it "should have the same states as it's parent" do
195
+ Derivate.aasm_states.should == Simple.aasm_states
196
+ end
197
+
198
+ it "should have the same events as it's parent" do
199
+ Derivate.aasm_events.should == Simple.aasm_events
200
+ end
201
+
202
+ it "should have the same column as it's parent" do
203
+ Derivate.aasm_column.should == :status
204
+ end
205
+ end
206
+
207
+ describe AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods do
208
+
209
+ context "Does not already respond_to? the scope name" do
210
+ it "should add a scope" do
211
+ Simple.should_not respond_to(:unknown_scope)
212
+ Simple.aasm_state :unknown_scope
213
+ Simple.should respond_to(:unknown_scope)
214
+ Simple.unknown_scope.class.should == ActiveRecord::Relation
215
+ end
216
+ end
217
+
218
+ context "Already respond_to? the scope name" do
219
+ it "should not add a scope" do
220
+ Simple.aasm_state :new
221
+ Simple.should respond_to(:new)
222
+ Simple.new.class.should == Simple
223
+ end
224
+ end
225
+ end
226
+
227
+ describe 'Thieves' do
228
+
229
+ it 'should be rich if they\'re skilled' do
230
+ Thief.new(:skilled => true).aasm_current_state.should == :rich
231
+ end
232
+
233
+ it 'should be jailed if they\'re unskilled' do
234
+ Thief.new(:skilled => false).aasm_current_state.should == :jailed
235
+ end
236
+ end
237
+
238
+ # TODO: figure out how to test ActiveRecord reload! without a database
239
+
240
+ rescue LoadError => e
241
+ if e.message == "no such file to load -- active_record"
242
+ puts "You must install active record to run this spec. Install with sudo gem install activerecord"
243
+ else
244
+ raise
245
+ end
246
+ end
@@ -0,0 +1,79 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ class Foo2
4
+ include AASM
5
+ aasm_initial_state :open
6
+ aasm_state :open,
7
+ :before_enter => :before_enter_open,
8
+ :before_exit => :before_exit_open,
9
+ :after_enter => :after_enter_open,
10
+ :after_exit => :after_exit_open
11
+ aasm_state :closed,
12
+ :before_enter => :before_enter_closed,
13
+ :before_exit => :before_exit_closed,
14
+ :after_enter => :after_enter_closed,
15
+ :after_exit => :after_exit_closed
16
+
17
+ aasm_event :close, :before => :before, :after => :after do
18
+ transitions :to => :closed, :from => [:open]
19
+ end
20
+
21
+ aasm_event :open, :before => :before, :after => :after do
22
+ transitions :to => :open, :from => :closed
23
+ end
24
+
25
+ def before_enter_open
26
+ end
27
+ def before_exit_open
28
+ end
29
+ def after_enter_open
30
+ end
31
+ def after_exit_open
32
+ end
33
+
34
+ def before_enter_closed
35
+ end
36
+ def before_exit_closed
37
+ end
38
+ def after_enter_closed
39
+ end
40
+ def after_exit_closed
41
+ end
42
+
43
+ def before
44
+ end
45
+ def after
46
+ end
47
+ end
48
+
49
+ describe Foo2, '- new callbacks' do
50
+ before(:each) do
51
+ @foo = Foo2.new
52
+ end
53
+
54
+ it "should get close callbacks" do
55
+ @foo.should_receive(:before).once.ordered
56
+ @foo.should_receive(:before_exit_open).once.ordered # these should be before the state changes
57
+ @foo.should_receive(:before_enter_closed).once.ordered
58
+ @foo.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
59
+ @foo.should_receive(:after_exit_open).once.ordered # these should be after the state changes
60
+ @foo.should_receive(:after_enter_closed).once.ordered
61
+ @foo.should_receive(:after).once.ordered
62
+
63
+ @foo.close!
64
+ end
65
+
66
+ it "should get open callbacks" do
67
+ @foo.close!
68
+
69
+ @foo.should_receive(:before).once.ordered
70
+ @foo.should_receive(:before_exit_closed).once.ordered # these should be before the state changes
71
+ @foo.should_receive(:before_enter_open).once.ordered
72
+ @foo.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
73
+ @foo.should_receive(:after_exit_closed).once.ordered # these should be after the state changes
74
+ @foo.should_receive(:after_enter_open).once.ordered
75
+ @foo.should_receive(:after).once.ordered
76
+
77
+ @foo.open!
78
+ end
79
+ end
@@ -0,0 +1,140 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe AASM::SupportingClasses::Event do
4
+ before(:each) do
5
+ @name = :close_order
6
+ @success = :success_callback
7
+ end
8
+
9
+ def new_event
10
+ @event = AASM::SupportingClasses::Event.new(@name, {:success => @success}) do
11
+ transitions :to => :closed, :from => [:open, :received]
12
+ end
13
+ end
14
+
15
+ it 'should set the name' do
16
+ new_event
17
+ @event.name.should == @name
18
+ end
19
+
20
+ it 'should set the success option' do
21
+ new_event
22
+ @event.success.should == @success
23
+ end
24
+
25
+ it 'should create StateTransitions' do
26
+ AASM::SupportingClasses::StateTransition.should_receive(:new).with({:to => :closed, :from => :open})
27
+ AASM::SupportingClasses::StateTransition.should_receive(:new).with({:to => :closed, :from => :received})
28
+ new_event
29
+ end
30
+ end
31
+
32
+ describe AASM::SupportingClasses::Event, 'when firing an event' do
33
+ it 'should raise an AASM::InvalidTransition error if the transitions are empty' do
34
+ obj = mock('object')
35
+ obj.stub!(:aasm_current_state)
36
+
37
+ event = AASM::SupportingClasses::Event.new(:event)
38
+ lambda { event.fire(obj) }.should raise_error(AASM::InvalidTransition)
39
+ end
40
+
41
+ it 'should return the state of the first matching transition it finds' do
42
+ event = AASM::SupportingClasses::Event.new(:event) do
43
+ transitions :to => :closed, :from => [:open, :received]
44
+ end
45
+
46
+ obj = mock('object')
47
+ obj.stub!(:aasm_current_state).and_return(:open)
48
+
49
+ event.fire(obj).should == :closed
50
+ end
51
+
52
+
53
+ it 'should call the guard with the params passed in' do
54
+ event = AASM::SupportingClasses::Event.new(:event) do
55
+ transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
56
+ end
57
+
58
+ obj = mock('object')
59
+ obj.stub!(:aasm_current_state).and_return(:open)
60
+ obj.should_receive(:guard_fn).with('arg1', 'arg2').and_return(true)
61
+
62
+ event.fire(obj, nil, 'arg1', 'arg2').should == :closed
63
+ end
64
+
65
+ end
66
+
67
+ describe AASM::SupportingClasses::Event, 'when executing the success callback' do
68
+ class ThisNameBetterNotBeInUse
69
+ include AASM
70
+
71
+ aasm_state :initial
72
+ aasm_state :symbol
73
+ aasm_state :string
74
+ aasm_state :array
75
+ aasm_state :proc
76
+ end
77
+
78
+ it "should send the success callback if it's a symbol" do
79
+ ThisNameBetterNotBeInUse.instance_eval {
80
+ aasm_event :with_symbol, :success => :symbol_success_callback do
81
+ transitions :to => :symbol, :from => [:initial]
82
+ end
83
+ }
84
+
85
+ model = ThisNameBetterNotBeInUse.new
86
+ model.should_receive(:symbol_success_callback)
87
+ model.with_symbol!
88
+ end
89
+
90
+ it "should send the success callback if it's a string" do
91
+ ThisNameBetterNotBeInUse.instance_eval {
92
+ aasm_event :with_string, :success => 'string_success_callback' do
93
+ transitions :to => :string, :from => [:initial]
94
+ end
95
+ }
96
+
97
+ model = ThisNameBetterNotBeInUse.new
98
+ model.should_receive(:string_success_callback)
99
+ model.with_string!
100
+ end
101
+
102
+ it "should call each success callback if passed an array of strings and/or symbols" do
103
+ ThisNameBetterNotBeInUse.instance_eval {
104
+ aasm_event :with_array, :success => [:success_callback1, 'success_callback2'] do
105
+ transitions :to => :array, :from => [:initial]
106
+ end
107
+ }
108
+
109
+ model = ThisNameBetterNotBeInUse.new
110
+ model.should_receive(:success_callback1)
111
+ model.should_receive(:success_callback2)
112
+ model.with_array!
113
+ end
114
+
115
+ it "should call each success callback if passed an array of strings and/or symbols and/or procs" do
116
+ ThisNameBetterNotBeInUse.instance_eval {
117
+ aasm_event :with_array_including_procs, :success => [:success_callback1, 'success_callback2', lambda { |obj| obj.proc_success_callback }] do
118
+ transitions :to => :array, :from => [:initial]
119
+ end
120
+ }
121
+
122
+ model = ThisNameBetterNotBeInUse.new
123
+ model.should_receive(:success_callback1)
124
+ model.should_receive(:success_callback2)
125
+ model.should_receive(:proc_success_callback)
126
+ model.with_array_including_procs!
127
+ end
128
+
129
+ it "should call the success callback if it's a proc" do
130
+ ThisNameBetterNotBeInUse.instance_eval {
131
+ aasm_event :with_proc, :success => lambda { |obj| obj.proc_success_callback } do
132
+ transitions :to => :proc, :from => [:initial]
133
+ end
134
+ }
135
+
136
+ model = ThisNameBetterNotBeInUse.new
137
+ model.should_receive(:proc_success_callback)
138
+ model.with_proc!
139
+ end
140
+ end