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,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,11 @@
1
+ *.sw?
2
+ *~
3
+ .DS_Store
4
+ .idea
5
+ coverage
6
+ pkg
7
+ rdoc
8
+ Gemfile.lock
9
+ spec/debug.log
10
+ spec/*.db
11
+ TODO
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Scott Barron
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # AASM - Ruby state machines #
2
+
3
+ This package contains AASM, a library for adding finite state machines to Ruby classes.
4
+
5
+ AASM started as the acts_as_state_machine plugin but has evolved into a more generic library that no longer targets only ActiveRecord models.
6
+
7
+ ## Features ##
8
+
9
+ * States
10
+ * Machines
11
+ * Events
12
+ * Transitions
13
+
14
+ ## New Callbacks ##
15
+
16
+ The callback chain & order on a successful event looks like:
17
+
18
+ oldstate:exit*
19
+ event:before
20
+ __find transition, if possible__
21
+ transition:on_transition*
22
+ oldstate:before_exit
23
+ newstate:before_enter
24
+ newstate:enter*
25
+ __update state__
26
+ event:success*
27
+ oldstate:after_exit
28
+ newstate:after_enter
29
+ event:after
30
+ obj:aasm_event_fired*
31
+
32
+ (*) marks old callbacks
33
+
34
+
35
+ ## Download ##
36
+
37
+ The latest AASM can currently be pulled from the git repository on github.
38
+
39
+ * http://github.com/rubyist/aasm/tree/master
40
+
41
+
42
+ ## Installation ##
43
+
44
+ ### From RubyGems.org ###
45
+
46
+ ```sh
47
+ % gem install aasm
48
+ ```
49
+
50
+ ### Building your own gems ###
51
+
52
+ ```sh
53
+ % rake build
54
+ % sudo gem install pkg/aasm-x.y.z.gem
55
+ ```
56
+
57
+ ## Simple Example ##
58
+
59
+ Here's a quick example highlighting some of the features.
60
+
61
+ ```ruby
62
+ class Conversation
63
+ include AASM
64
+
65
+ aasm_column :current_state # defaults to aasm_state
66
+
67
+ aasm_initial_state :unread
68
+
69
+ aasm_state :unread
70
+ aasm_state :read
71
+ aasm_state :closed
72
+
73
+
74
+ aasm_event :view do
75
+ transitions :to => :read, :from => [:unread]
76
+ end
77
+
78
+ aasm_event :close do
79
+ transitions :to => :closed, :from => [:read, :unread]
80
+ end
81
+ end
82
+ ```
83
+
84
+ ## A Slightly More Complex Example ##
85
+
86
+ This example uses a few of the more complex features available.
87
+
88
+ ```ruby
89
+ class Relationship
90
+ include AASM
91
+
92
+ aasm_column :status
93
+
94
+ aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
95
+
96
+ aasm_state :dating, :enter => :make_happy, :exit => :make_depressed
97
+ aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
98
+ aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
99
+
100
+ aasm_event :get_intimate do
101
+ transitions :to => :intimate, :from => [:dating], :guard => :drunk?
102
+ end
103
+
104
+ aasm_event :get_married do
105
+ transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
106
+ end
107
+
108
+ def strictly_for_fun?; end
109
+ def drunk?; end
110
+ def willing_to_give_up_manhood?; end
111
+ def make_happy; end
112
+ def make_depressed; end
113
+ def make_very_happy; end
114
+ def never_speak_again; end
115
+ def give_up_intimacy; end
116
+ def buy_exotic_car_and_wear_a_combover; end
117
+ end
118
+ ```
119
+
120
+ ## Callbacks around events
121
+ ```ruby
122
+ class Relationship
123
+ include AASM
124
+
125
+ aasm_state :dating
126
+ aasm_state :married
127
+
128
+ aasm_event :get_married,
129
+ :before => :make_vows,
130
+ :after => :eat_wedding_cake do
131
+ transitions :to => :married, :from => [:dating]
132
+ end
133
+ end
134
+ ```
135
+
136
+
137
+ # Other Stuff #
138
+
139
+ Author:: Scott Barron <scott at elitists dot net>
140
+ License:: Original code Copyright 2006, 2007, 2008 by Scott Barron.
141
+ Released under an MIT-style license. See the LICENSE file
142
+ included in the distribution.
143
+
144
+ ## Warranty ##
145
+
146
+ This software is provided "as is" and without any express or
147
+ implied warranties, including, without limitation, the implied
148
+ warranties of merchantibility and fitness for a particular
149
+ purpose.
@@ -0,0 +1,27 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ require 'rake/testtask'
10
+ Rake::TestTask.new(:test) do |test|
11
+ test.libs << 'lib' << 'test'
12
+ test.pattern = 'test/**/*_test.rb'
13
+ test.verbose = true
14
+ end
15
+
16
+ task :default => :test
17
+
18
+ require 'rdoc/task'
19
+ require 'alexrevin-aasm_numerical/version'
20
+ require 'sdoc'
21
+
22
+ Rake::RDocTask.new do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = "aasm #{AASM::VERSION}"
25
+ rdoc.rdoc_files.include('README*')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ end
@@ -0,0 +1,10 @@
1
+ module AASM
2
+ end
3
+
4
+ require 'ostruct'
5
+
6
+ require File.join(File.dirname(__FILE__), 'alexrevin-aasm_numerical', 'supporting_classes')
7
+ require File.join(File.dirname(__FILE__), 'alexrevin-aasm_numerical', 'state_machine')
8
+ require File.join(File.dirname(__FILE__), 'alexrevin-aasm_numerical', 'persistence')
9
+ require File.join(File.dirname(__FILE__), 'alexrevin-aasm_numerical', 'aasm')
10
+ require File.join(File.dirname(__FILE__), 'alexrevin-aasm_numerical', 'localizer')
@@ -0,0 +1,222 @@
1
+ module AASM
2
+ class InvalidTransition < RuntimeError
3
+ end
4
+
5
+ class UndefinedState < RuntimeError
6
+ end
7
+
8
+ def self.included(base) #:nodoc:
9
+ base.extend AASM::ClassMethods
10
+
11
+ AASM::Persistence.set_persistence(base)
12
+ unless AASM::StateMachine[base]
13
+ AASM::StateMachine[base] = AASM::StateMachine.new('')
14
+ end
15
+ super
16
+ end
17
+
18
+ module ClassMethods
19
+ def inherited(klass)
20
+ AASM::StateMachine[klass] = AASM::StateMachine[self].clone
21
+ super
22
+ end
23
+
24
+ def aasm_initial_state(set_state=nil)
25
+ if set_state
26
+ AASM::StateMachine[self].initial_state = set_state
27
+ else
28
+ AASM::StateMachine[self].initial_state
29
+ end
30
+ end
31
+
32
+ def aasm_initial_state=(state)
33
+ AASM::StateMachine[self].initial_state = state
34
+ end
35
+
36
+ def aasm_state(name, options={})
37
+ sm = AASM::StateMachine[self]
38
+ sm.create_state(name, options)
39
+ sm.initial_state = name unless sm.initial_state
40
+
41
+ define_method("#{name.to_s}?") do
42
+ aasm_current_state == name
43
+ end
44
+ end
45
+
46
+ def aasm_event(name, options = {}, &block)
47
+ sm = AASM::StateMachine[self]
48
+
49
+ unless sm.events.has_key?(name)
50
+ sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
51
+ end
52
+
53
+ # an addition over standard aasm so that, before firing an event, you can ask
54
+ # may_event? and get back a boolean that tells you whether the guard method
55
+ # on the transition will let this happen.
56
+ define_method("may_#{name.to_s}?") do |*args|
57
+ aasm_test_event(name, *args)
58
+ end
59
+
60
+ define_method("#{name.to_s}!") do |*args|
61
+ aasm_fire_event(name, true, *args)
62
+ end
63
+
64
+ define_method("#{name.to_s}") do |*args|
65
+ aasm_fire_event(name, false, *args)
66
+ end
67
+ end
68
+
69
+ def aasm_states
70
+ AASM::StateMachine[self].states
71
+ end
72
+
73
+ def aasm_events
74
+ AASM::StateMachine[self].events
75
+ end
76
+
77
+ def aasm_states_for_select
78
+ AASM::StateMachine[self].states.map { |state| state.for_select }
79
+ end
80
+
81
+ def human_event_name(event)
82
+ AASM::Localizer.new.human_event_name(self, event)
83
+ end
84
+ end
85
+
86
+ # Instance methods
87
+ def aasm_current_state
88
+ return @aasm_current_state if @aasm_current_state
89
+
90
+ if self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
91
+ @aasm_current_state = aasm_read_state
92
+ end
93
+ return @aasm_current_state if @aasm_current_state
94
+
95
+ aasm_enter_initial_state
96
+ end
97
+
98
+ def aasm_enter_initial_state
99
+ state_name = aasm_determine_state_name(self.class.aasm_initial_state)
100
+ state = aasm_state_object_for_state(state_name)
101
+
102
+ state.call_action(:before_enter, self)
103
+ state.call_action(:enter, self)
104
+ self.aasm_current_state = state_name
105
+ state.call_action(:after_enter, self)
106
+
107
+ state_name
108
+ end
109
+
110
+ def aasm_events_for_current_state
111
+ aasm_events_for_state(aasm_current_state)
112
+ end
113
+
114
+ # filters the results of events_for_current_state so that only those that
115
+ # are really currently possible (given transition guards) are shown.
116
+ def aasm_permissible_events_for_current_state
117
+ aasm_events_for_current_state.select{ |e| self.send(("may_" + e.to_s + "?").to_sym) }
118
+ end
119
+
120
+ def aasm_events_for_state(state)
121
+ events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
122
+ events.map {|event| event.name}
123
+ end
124
+
125
+ def human_state
126
+ AASM::Localizer.new.human_state(self)
127
+ end
128
+
129
+ private
130
+
131
+ def set_aasm_current_state_with_persistence(state)
132
+ save_success = true
133
+ if self.respond_to?(:aasm_write_state) || self.private_methods.include?('aasm_write_state')
134
+ save_success = aasm_write_state(state)
135
+ end
136
+ self.aasm_current_state = state if save_success
137
+
138
+ save_success
139
+ end
140
+
141
+ def aasm_current_state=(state)
142
+ if self.respond_to?(:aasm_write_state_without_persistence) || self.private_methods.include?('aasm_write_state_without_persistence')
143
+ aasm_write_state_without_persistence(state)
144
+ end
145
+ @aasm_current_state = state
146
+ end
147
+
148
+ def aasm_determine_state_name(state)
149
+ case state
150
+ when Symbol, String
151
+ state
152
+ when Proc
153
+ state.call(self)
154
+ else
155
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
156
+ end
157
+ end
158
+
159
+ def aasm_state_object_for_state(name)
160
+ obj = self.class.aasm_states.find {|s| s == name}
161
+ raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
162
+ obj
163
+ end
164
+
165
+ def aasm_test_event(name, *args)
166
+ event = self.class.aasm_events[name]
167
+ event.may_fire?(self, *args)
168
+ end
169
+
170
+ def aasm_fire_event(name, persist, *args)
171
+ event = self.class.aasm_events[name]
172
+ begin
173
+ old_state = aasm_state_object_for_state(aasm_current_state)
174
+
175
+
176
+ old_state.call_action(:exit, self)
177
+
178
+ # new event before callback
179
+ event.call_action(:before, self)
180
+
181
+ new_state_name = event.fire(self, *args)
182
+
183
+ unless new_state_name.nil?
184
+ new_state = aasm_state_object_for_state(new_state_name)
185
+
186
+ # new before_ callbacks
187
+ old_state.call_action(:before_exit, self)
188
+ new_state.call_action(:before_enter, self)
189
+
190
+ new_state.call_action(:enter, self)
191
+
192
+ persist_successful = true
193
+ if persist
194
+ persist_successful = set_aasm_current_state_with_persistence(new_state_name)
195
+ event.execute_success_callback(self) if persist_successful
196
+ else
197
+ self.aasm_current_state = new_state_name
198
+ end
199
+
200
+ if persist_successful
201
+ old_state.call_action(:after_exit, self)
202
+ new_state.call_action(:after_enter, self)
203
+ event.call_action(:after, self)
204
+
205
+ self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
206
+ else
207
+ self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
208
+ end
209
+
210
+ persist_successful
211
+ else
212
+ if self.respond_to?(:aasm_event_failed)
213
+ self.aasm_event_failed(name, old_state.name)
214
+ end
215
+
216
+ false
217
+ end
218
+ rescue StandardError => e
219
+ event.execute_error_callback(self, e)
220
+ end
221
+ end
222
+ end