r38y-aasm 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,247 @@
1
+ module AASM
2
+ module Persistence
3
+ module MongoidPersistence
4
+ # This method:
5
+ #
6
+ # * extends the model with ClassMethods
7
+ # * includes InstanceMethods
8
+ #
9
+ # Unless the corresponding methods are already defined, it includes
10
+ # * ReadState
11
+ # * WriteState
12
+ # * WriteStateWithoutPersistence
13
+ #
14
+ # Adds
15
+ #
16
+ # before_validation :aasm_ensure_initial_state
17
+ #
18
+ # As a result, it doesn't matter when you define your methods - the following 2 are equivalent
19
+ #
20
+ # class Foo
21
+ # include Mongoid::Document
22
+ # def aasm_write_state(state)
23
+ # "bar"
24
+ # end
25
+ # include AASM
26
+ # end
27
+ #
28
+ # class Foo
29
+ # include Mongoid::Document
30
+ # include AASM
31
+ # def aasm_write_state(state)
32
+ # "bar"
33
+ # end
34
+ # end
35
+ #
36
+ def self.included(base)
37
+ base.extend AASM::Persistence::MongoidPersistence::ClassMethods
38
+ base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
39
+ base.send(:include, AASM::Persistence::MongoidPersistence::ReadState) unless base.method_defined?(:aasm_read_state)
40
+ base.send(:include, AASM::Persistence::MongoidPersistence::WriteState) unless base.method_defined?(:aasm_write_state)
41
+ base.send(:include, AASM::Persistence::MongoidPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence)
42
+
43
+ # if base.respond_to?(:named_scope)
44
+ # base.extend(AASM::Persistence::MongoidPersistence::NamedScopeMethods)
45
+ #
46
+ # base.class_eval do
47
+ # class << self
48
+ # unless method_defined?(:aasm_state_without_named_scope)
49
+ # alias_method :aasm_state_without_named_scope, :aasm_state
50
+ # alias_method :aasm_state, :aasm_state_with_named_scope
51
+ # end
52
+ # end
53
+ # end
54
+ # end
55
+
56
+ # Mongoid's Validatable gem dependency goes not have a before_validation_on_xxx hook yet.
57
+ # base.before_validation_on_create :aasm_ensure_initial_state
58
+ base.before_validation :aasm_ensure_initial_state
59
+ end
60
+
61
+ module ClassMethods
62
+ # Maps to the aasm_column in the database. Deafults to "aasm_state". You can write:
63
+ #
64
+ # class Foo
65
+ # include Mongoid::Document
66
+ # include AASM
67
+ # field :aasm_state
68
+ # end
69
+ #
70
+ # OR:
71
+ #
72
+ # class Foo
73
+ # include Mongoid::Document
74
+ # include AASM
75
+ # field :status
76
+ # aasm_column :status
77
+ # end
78
+ #
79
+ # This method is both a getter and a setter
80
+ def aasm_column(column_name=nil)
81
+ if column_name
82
+ AASM::StateMachine[self].config.column = column_name.to_sym
83
+ # @aasm_column = column_name.to_sym
84
+ else
85
+ AASM::StateMachine[self].config.column ||= :aasm_state
86
+ # @aasm_column ||= :aasm_state
87
+ end
88
+ # @aasm_column
89
+ AASM::StateMachine[self].config.column
90
+ end
91
+
92
+ # def find_in_state(number, state, *args)
93
+ # with_state_scope state do
94
+ # find(number, *args)
95
+ # end
96
+ # end
97
+ #
98
+ # def count_in_state(state, *args)
99
+ # with_state_scope state do
100
+ # count(*args)
101
+ # end
102
+ # end
103
+ #
104
+ # def calculate_in_state(state, *args)
105
+ # with_state_scope state do
106
+ # calculate(*args)
107
+ # end
108
+ # end
109
+
110
+ protected
111
+ def with_state_scope(state)
112
+ with_scope :find => {:conditions => ["#{table_name}.#{aasm_column} = ?", state.to_s]} do
113
+ yield if block_given?
114
+ end
115
+ end
116
+ end
117
+
118
+ module InstanceMethods
119
+
120
+ # Returns the current aasm_state of the object. Respects reload and
121
+ # any changes made to the aasm_state field directly
122
+ #
123
+ # Internally just calls <tt>aasm_read_state</tt>
124
+ #
125
+ # foo = Foo.find(1)
126
+ # foo.aasm_current_state # => :pending
127
+ # foo.aasm_state = "opened"
128
+ # foo.aasm_current_state # => :opened
129
+ # foo.close # => calls aasm_write_state_without_persistence
130
+ # foo.aasm_current_state # => :closed
131
+ # foo.reload
132
+ # foo.aasm_current_state # => :pending
133
+ #
134
+ def aasm_current_state
135
+ @current_state = aasm_read_state
136
+ end
137
+
138
+ private
139
+
140
+ # Ensures that if the aasm_state column is nil and the record is new
141
+ # that the initial state gets populated before validation on create
142
+ #
143
+ # foo = Foo.new
144
+ # foo.aasm_state # => nil
145
+ # foo.valid?
146
+ # foo.aasm_state # => "open" (where :open is the initial state)
147
+ #
148
+ #
149
+ # foo = Foo.find(:first)
150
+ # foo.aasm_state # => 1
151
+ # foo.aasm_state = nil
152
+ # foo.valid?
153
+ # foo.aasm_state # => nil
154
+ #
155
+ def aasm_ensure_initial_state
156
+ send("#{self.class.aasm_column}=", self.aasm_enter_initial_state.to_s) if send(self.class.aasm_column).blank?
157
+ end
158
+
159
+ end
160
+
161
+ module WriteStateWithoutPersistence
162
+ # Writes <tt>state</tt> to the state column, but does not persist it to the database
163
+ #
164
+ # foo = Foo.find(1)
165
+ # foo.aasm_current_state # => :opened
166
+ # foo.close
167
+ # foo.aasm_current_state # => :closed
168
+ # Foo.find(1).aasm_current_state # => :opened
169
+ # foo.save
170
+ # foo.aasm_current_state # => :closed
171
+ # Foo.find(1).aasm_current_state # => :closed
172
+ #
173
+ # NOTE: intended to be called from an event
174
+ def aasm_write_state_without_persistence(state)
175
+ write_attribute(self.class.aasm_column, state.to_s)
176
+ end
177
+ end
178
+
179
+ module WriteState
180
+ # Writes <tt>state</tt> to the state column and persists it to the database
181
+ # using update_attribute (which bypasses validation)
182
+ #
183
+ # foo = Foo.find(1)
184
+ # foo.aasm_current_state # => :opened
185
+ # foo.close!
186
+ # foo.aasm_current_state # => :closed
187
+ # Foo.find(1).aasm_current_state # => :closed
188
+ #
189
+ # NOTE: intended to be called from an event
190
+ def aasm_write_state(state)
191
+ old_value = read_attribute(self.class.aasm_column)
192
+ write_attribute(self.class.aasm_column, state.to_s)
193
+
194
+ unless self.save(false)
195
+ write_attribute(self.class.aasm_column, old_value)
196
+ return false
197
+ end
198
+
199
+ true
200
+ end
201
+ end
202
+
203
+ module ReadState
204
+
205
+ # Returns the value of the aasm_column - called from <tt>aasm_current_state</tt>
206
+ #
207
+ # If it's a new record, and the aasm state column is blank it returns the initial state:
208
+ #
209
+ # class Foo
210
+ # include Mongoid::Document
211
+ # include AASM
212
+ # aasm_column :status
213
+ # aasm_state :opened
214
+ # aasm_state :closed
215
+ # end
216
+ #
217
+ # foo = Foo.new
218
+ # foo.current_state # => :opened
219
+ # foo.close
220
+ # foo.current_state # => :closed
221
+ #
222
+ # foo = Foo.find(1)
223
+ # foo.current_state # => :opened
224
+ # foo.aasm_state = nil
225
+ # foo.current_state # => nil
226
+ #
227
+ # NOTE: intended to be called from an event
228
+ #
229
+ # This allows for nil aasm states - be sure to add validation to your model
230
+ def aasm_read_state
231
+ if new_record?
232
+ send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym
233
+ else
234
+ send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
235
+ end
236
+ end
237
+ end
238
+
239
+ module NamedScopeMethods
240
+ def aasm_state_with_named_scope name, options = {}
241
+ aasm_state_without_named_scope name, options
242
+ self.named_scope name, :conditions => { "#{table_name}.#{self.aasm_column}" => name.to_s} unless self.respond_to?(name)
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,53 @@
1
+ class AASM::SupportingClasses::State
2
+ attr_reader :name, :options
3
+
4
+ def initialize(name, options={})
5
+ @name = name
6
+ update(options)
7
+ end
8
+
9
+ def ==(state)
10
+ if state.is_a? Symbol
11
+ name == state
12
+ else
13
+ name == state.name
14
+ end
15
+ end
16
+
17
+ def call_action(action, record)
18
+ action = @options[action]
19
+ catch :halt_aasm_chain do
20
+ action.is_a?(Array) ?
21
+ action.each {|a| _call_action(a, record)} :
22
+ _call_action(action, record)
23
+ end
24
+ end
25
+
26
+ def display_name
27
+ @display_name ||= name.to_s.gsub(/_/, ' ').capitalize
28
+ end
29
+
30
+ def for_select
31
+ [display_name, name.to_s]
32
+ end
33
+
34
+ def update(options = {})
35
+ if options.key?(:display) then
36
+ @display_name = options.delete(:display)
37
+ end
38
+ @options = options
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ def _call_action(action, record)
45
+ case action
46
+ when Symbol, String
47
+ record.send(action)
48
+ when Proc
49
+ action.call(record)
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,32 @@
1
+ class AASM::StateMachine
2
+ def self.[](*args)
3
+ (@machines ||= {})[args]
4
+ end
5
+
6
+ def self.[]=(*args)
7
+ val = args.pop
8
+ (@machines ||= {})[args] = val
9
+ end
10
+
11
+ attr_accessor :states, :events, :initial_state, :config
12
+ attr_reader :name
13
+
14
+ def initialize(name)
15
+ @name = name
16
+ @initial_state = nil
17
+ @states = []
18
+ @events = {}
19
+ @config = OpenStruct.new
20
+ end
21
+
22
+ def clone
23
+ klone = super
24
+ klone.states = states.clone
25
+ klone.events = events.clone
26
+ klone
27
+ end
28
+
29
+ def create_state(name, options)
30
+ @states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ class AASM::SupportingClasses::StateTransition
2
+ attr_reader :from, :to, :opts
3
+ alias_method :options, :opts
4
+
5
+ def initialize(opts)
6
+ @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
7
+ @opts = opts
8
+ end
9
+
10
+ def perform(obj)
11
+ case @guard
12
+ when Symbol, String
13
+ obj.send(@guard)
14
+ when Proc
15
+ @guard.call(obj)
16
+ else
17
+ true
18
+ end
19
+ end
20
+
21
+ def execute(obj, *args)
22
+ @on_transition.is_a?(Array) ?
23
+ @on_transition.each {|ot| _execute(obj, ot, *args)} :
24
+ _execute(obj, @on_transition, *args)
25
+ end
26
+
27
+ def ==(obj)
28
+ @from == obj.from && @to == obj.to
29
+ end
30
+
31
+ def from?(value)
32
+ @from == value
33
+ end
34
+
35
+ private
36
+
37
+ def _execute(obj, on_transition, *args)
38
+ case on_transition
39
+ when Symbol, String
40
+ obj.send(on_transition, *args)
41
+ when Proc
42
+ on_transition.call(obj, *args)
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,6 @@
1
+ module AASM::SupportingClasses
2
+ end
3
+
4
+ require File.join(File.dirname(__FILE__), 'state_transition')
5
+ require File.join(File.dirname(__FILE__), 'event')
6
+ require File.join(File.dirname(__FILE__), 'state')
@@ -0,0 +1,97 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{r38y-aasm}
8
+ s.version = "2.1.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Scott Barron", "Scott Petersen", "Travis Tilley"]
12
+ s.date = %q{2010-02-04}
13
+ s.description = %q{AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.}
14
+ s.email = %q{scott@elitists.net, ttilley@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "aasm.gemspec",
27
+ "lib/aasm.rb",
28
+ "lib/aasm/aasm.rb",
29
+ "lib/aasm/event.rb",
30
+ "lib/aasm/persistence.rb",
31
+ "lib/aasm/persistence/active_record_persistence.rb",
32
+ "lib/aasm/persistence/mongo_mapper_persistence.rb",
33
+ "lib/aasm/persistence/mongoid_persistence.rb",
34
+ "lib/aasm/state.rb",
35
+ "lib/aasm/state_machine.rb",
36
+ "lib/aasm/state_transition.rb",
37
+ "lib/aasm/supporting_classes.rb",
38
+ "r38y-aasm.gemspec",
39
+ "spec/functional/conversation.rb",
40
+ "spec/functional/conversation_spec.rb",
41
+ "spec/spec_helper.rb",
42
+ "spec/unit/aasm_spec.rb",
43
+ "spec/unit/active_record_persistence_spec.rb",
44
+ "spec/unit/before_after_callbacks_spec.rb",
45
+ "spec/unit/event_spec.rb",
46
+ "spec/unit/state_spec.rb",
47
+ "spec/unit/state_transition_spec.rb",
48
+ "test/functional/auth_machine_test.rb",
49
+ "test/test_helper.rb",
50
+ "test/unit/aasm_test.rb",
51
+ "test/unit/event_test.rb",
52
+ "test/unit/state_test.rb",
53
+ "test/unit/state_transition_test.rb"
54
+ ]
55
+ s.homepage = %q{http://rubyist.github.com/aasm/}
56
+ s.rdoc_options = ["--charset=UTF-8"]
57
+ s.require_paths = ["lib"]
58
+ s.rubygems_version = %q{1.3.5}
59
+ s.summary = %q{State machine mixin for Ruby objects}
60
+ s.test_files = [
61
+ "spec/functional/conversation.rb",
62
+ "spec/functional/conversation_spec.rb",
63
+ "spec/spec_helper.rb",
64
+ "spec/unit/aasm_spec.rb",
65
+ "spec/unit/active_record_persistence_spec.rb",
66
+ "spec/unit/before_after_callbacks_spec.rb",
67
+ "spec/unit/event_spec.rb",
68
+ "spec/unit/state_spec.rb",
69
+ "spec/unit/state_transition_spec.rb",
70
+ "test/functional/auth_machine_test.rb",
71
+ "test/test_helper.rb",
72
+ "test/unit/aasm_test.rb",
73
+ "test/unit/event_test.rb",
74
+ "test/unit/state_test.rb",
75
+ "test/unit/state_transition_test.rb"
76
+ ]
77
+
78
+ if s.respond_to? :specification_version then
79
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
80
+ s.specification_version = 3
81
+
82
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
83
+ s.add_development_dependency(%q<rspec>, [">= 0"])
84
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
85
+ s.add_development_dependency(%q<sdoc>, [">= 0"])
86
+ else
87
+ s.add_dependency(%q<rspec>, [">= 0"])
88
+ s.add_dependency(%q<shoulda>, [">= 0"])
89
+ s.add_dependency(%q<sdoc>, [">= 0"])
90
+ end
91
+ else
92
+ s.add_dependency(%q<rspec>, [">= 0"])
93
+ s.add_dependency(%q<shoulda>, [">= 0"])
94
+ s.add_dependency(%q<sdoc>, [">= 0"])
95
+ end
96
+ end
97
+