aasm 3.0.0 → 3.0.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.
- data/CHANGELOG.md +4 -0
- data/README.md +1 -0
- data/lib/aasm.rb +1 -0
- data/lib/aasm/persistence.rb +3 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +247 -0
- data/lib/aasm/supporting_classes/state_transition.rb +1 -0
- data/lib/aasm/version.rb +1 -1
- data/spec/unit/active_record_persistence_spec.rb +22 -26
- metadata +5 -4
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
data/lib/aasm.rb
CHANGED
data/lib/aasm/persistence.rb
CHANGED
@@ -9,6 +9,9 @@ module AASM
|
|
9
9
|
if hierarchy.include?("ActiveRecord::Base")
|
10
10
|
require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
|
11
11
|
base.send(:include, AASM::Persistence::ActiveRecordPersistence)
|
12
|
+
elsif hierarchy.include?("Mongoid::Document")
|
13
|
+
require File.join(File.dirname(__FILE__), 'persistence', 'mongoid_persistence')
|
14
|
+
base.send(:include, AASM::Persistence::MongoidPersistence)
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
@@ -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(:validate => 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
|
data/lib/aasm/version.rb
CHANGED
@@ -143,58 +143,56 @@ end
|
|
143
143
|
|
144
144
|
describe Gate, "instance methods" do
|
145
145
|
|
146
|
+
before do
|
147
|
+
@gate = Gate.new
|
148
|
+
end
|
149
|
+
|
146
150
|
it "should respond to aasm read state when not previously defined" do
|
147
|
-
|
151
|
+
@gate.should respond_to(:aasm_read_state)
|
148
152
|
end
|
149
153
|
|
150
154
|
it "should respond to aasm write state when not previously defined" do
|
151
|
-
|
155
|
+
@gate.should respond_to(:aasm_write_state)
|
152
156
|
end
|
153
157
|
|
154
158
|
it "should respond to aasm write state without persistence when not previously defined" do
|
155
|
-
|
159
|
+
@gate.should respond_to(:aasm_write_state_without_persistence)
|
156
160
|
end
|
157
161
|
|
158
162
|
it "should return the initial state when new and the aasm field is nil" do
|
159
|
-
|
163
|
+
@gate.aasm_current_state.should == :opened
|
160
164
|
end
|
161
165
|
|
162
166
|
it "should return the aasm column when new and the aasm field is not nil" do
|
163
|
-
|
164
|
-
|
165
|
-
foo.aasm_current_state.should == :closed
|
167
|
+
@gate.aasm_state = "closed"
|
168
|
+
@gate.aasm_current_state.should == :closed
|
166
169
|
end
|
167
170
|
|
168
171
|
it "should return the aasm column when not new and the aasm_column is not nil" do
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
foo.aasm_current_state.should == :state
|
172
|
+
@gate.stub!(:new_record?).and_return(false)
|
173
|
+
@gate.aasm_state = "state"
|
174
|
+
@gate.aasm_current_state.should == :state
|
173
175
|
end
|
174
176
|
|
175
177
|
it "should allow a nil state" do
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
foo.aasm_current_state.should be_nil
|
178
|
+
@gate.stub!(:new_record?).and_return(false)
|
179
|
+
@gate.aasm_state = nil
|
180
|
+
@gate.aasm_current_state.should be_nil
|
180
181
|
end
|
181
182
|
|
182
183
|
it "should have aasm_ensure_initial_state" do
|
183
|
-
|
184
|
-
foo.send :aasm_ensure_initial_state
|
184
|
+
@gate.send :aasm_ensure_initial_state
|
185
185
|
end
|
186
186
|
|
187
187
|
it "should call aasm_ensure_initial_state on validation before create" do
|
188
|
-
|
189
|
-
|
190
|
-
foo.valid?
|
188
|
+
@gate.should_receive(:aasm_ensure_initial_state).and_return(true)
|
189
|
+
@gate.valid?
|
191
190
|
end
|
192
191
|
|
193
192
|
it "should call aasm_ensure_initial_state on validation before create" do
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
foo.valid?
|
193
|
+
@gate.stub!(:new_record?).and_return(false)
|
194
|
+
@gate.should_not_receive(:aasm_ensure_initial_state)
|
195
|
+
@gate.valid?
|
198
196
|
end
|
199
197
|
|
200
198
|
end
|
@@ -248,5 +246,3 @@ describe 'Thieves' do
|
|
248
246
|
Thief.new(:skilled => false).aasm_current_state.should == :jailed
|
249
247
|
end
|
250
248
|
end
|
251
|
-
|
252
|
-
# TODO: figure out how to test ActiveRecord reload! without a database
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 3.0.
|
9
|
+
- 1
|
10
|
+
version: 3.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Scott Barron
|
@@ -18,7 +18,7 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2011-
|
21
|
+
date: 2011-12-21 00:00:00 +01:00
|
22
22
|
default_executable:
|
23
23
|
dependencies:
|
24
24
|
- !ruby/object:Gem::Dependency
|
@@ -172,6 +172,7 @@ files:
|
|
172
172
|
- lib/aasm/errors.rb
|
173
173
|
- lib/aasm/persistence.rb
|
174
174
|
- lib/aasm/persistence/active_record_persistence.rb
|
175
|
+
- lib/aasm/persistence/mongoid_persistence.rb
|
175
176
|
- lib/aasm/state_machine.rb
|
176
177
|
- lib/aasm/supporting_classes/event.rb
|
177
178
|
- lib/aasm/supporting_classes/localizer.rb
|