aasm 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3953eaa965802447de8e3783a35c91dcc89e87a4
4
- data.tar.gz: 1b70e517fc29f1fad325a0a46d126cab2eebf029
3
+ metadata.gz: 402286c5cd8a9288380d837b7faeb96429a5d5e0
4
+ data.tar.gz: 45868b7761fa2ed0ffc42d7ab0c7ef637e52a668
5
5
  SHA512:
6
- metadata.gz: 220319a5169ac9f1928a5beaaacf81c72d47ab3cf972dea079633fcc82ac8bfa3d42c97c0ffa38b9526e1a50b729ba331cc7ae29a1267dddd4d3e9ae52a7cd70
7
- data.tar.gz: 0eff6b102e298af64e76e6f71cff21989d260c626efada6ed5dc763d092d351fac20a9696f1882ab8110e535b20897def8c5c7d2c013d7df82e4b6c437590a2f
6
+ metadata.gz: e5d7c42efeb5438ba6693008b2f92c9cffdcdced9982f550091e2c12b8f756d233a0d6ddb9f6517c9754bf8204c8e7ecdd96da4e9a6209b7bc3df1fd1e35c109
7
+ data.tar.gz: dd23c1b785e7e917334ce577cd56412c1e1f6a6a30105edf827ab89296c9e1e77394a3762aeff67996401a8fbc887240a7a21ecd86ab178157fefd792accb670
data/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@
4
4
 
5
5
  * deprecated old aasm_* class methods (old-style DSL), in preparation for AASM v4.0.0
6
6
 
7
+ ## 3.3.0 (not yet released)
8
+
9
+ * support for Rails 4.1 enum fields (see [issue #124](https://github.com/aasm/aasm/issues/124), thanks to [@bkon](https://github.com/bkon))
10
+ * bugfix: allow lazy-evaluation for Rails 3 scopes (see [issue #144](https://github.com/aasm/aasm/issues/144), thanks to [@laurens](https://github.com/laurens))
11
+
7
12
  ## 3.2.1
8
13
 
9
14
  * bugfix: permissible_events and events did not contain events with an empty "from" transition (see [issue #140](https://github.com/aasm/aasm/issues/140) and [issue #141](https://github.com/aasm/aasm/issues/141), thanks to [@daniel-rikowski](https://github.com/daniel-rikowski))
data/README.md CHANGED
@@ -278,6 +278,40 @@ class Job < ActiveRecord::Base
278
278
  end
279
279
  ```
280
280
 
281
+ #### ActiveRecord enums
282
+
283
+ You can use
284
+ [enumerations](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html)
285
+ in Rails 4.1+ for your state column:
286
+
287
+ ```ruby
288
+ class Job < ActiveRecord::Base
289
+ include AASM
290
+
291
+ enum state {
292
+ sleeping: 5,
293
+ running: 99
294
+ }
295
+
296
+ aasm :column => :state, :enum => true do
297
+ state :sleeping, :initial => true
298
+ state :running
299
+ end
300
+ end
301
+ ```
302
+
303
+ You can explicitly pass the name of the method which provides access
304
+ to the enumeration mapping as a value of ```enum```, or you can simply
305
+ set it to ```true```. In the latter case AASM will try to use
306
+ pluralized column name to access possible enum states.
307
+
308
+ Furthermore, if your column has integer type (which is normally the
309
+ case when you're working with Rails enums), you can omit ```:enum```
310
+ setting --- AASM auto-detects this situation and enabled enum
311
+ support. If anything goes wrong, you can disable enum functionality
312
+ and fall back to the default behavior by setting ```:enum```
313
+ to ```false```.
314
+
281
315
  ### Sequel
282
316
 
283
317
  AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_ and _Mongoid_.
data/lib/aasm/base.rb CHANGED
@@ -18,6 +18,8 @@ module AASM
18
18
 
19
19
  # use requires_new for nested transactions
20
20
  configure :requires_new_transaction, true
21
+
22
+ configure :enum, nil
21
23
  end
22
24
 
23
25
  def initial_state(new_initial_state=nil)
@@ -85,10 +85,11 @@ module AASM
85
85
  # NOTE: intended to be called from an event
86
86
  def aasm_write_state(state)
87
87
  old_value = read_attribute(self.class.aasm_column)
88
- write_attribute(self.class.aasm_column, state.to_s)
88
+ aasm_write_attribute state
89
89
 
90
- success = if AASM::StateMachine[self.class].config.skip_validation_on_save
91
- self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm_column => state.to_s) == 1
90
+ success = if aasm_skipping_validations
91
+ value = aasm_raw_attribute_value state
92
+ self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm_column => value) == 1
92
93
  else
93
94
  self.save
94
95
  end
@@ -113,10 +114,42 @@ module AASM
113
114
  #
114
115
  # NOTE: intended to be called from an event
115
116
  def aasm_write_state_without_persistence(state)
116
- write_attribute(self.class.aasm_column, state.to_s)
117
+ aasm_write_attribute state
117
118
  end
118
119
 
119
120
  private
121
+ def aasm_enum
122
+ case AASM::StateMachine[self.class].config.enum
123
+ when false then nil
124
+ when true then aasm_guess_enum_method
125
+ when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
126
+ else AASM::StateMachine[self.class].config.enum
127
+ end
128
+ end
129
+
130
+ def aasm_column_looks_like_enum
131
+ self.class.columns_hash[self.class.aasm_column.to_s].type == :integer
132
+ end
133
+
134
+ def aasm_guess_enum_method
135
+ self.class.aasm_column.to_s.pluralize.to_sym
136
+ end
137
+
138
+ def aasm_skipping_validations
139
+ AASM::StateMachine[self.class].config.skip_validation_on_save
140
+ end
141
+
142
+ def aasm_write_attribute(state)
143
+ write_attribute self.class.aasm_column, aasm_raw_attribute_value(state)
144
+ end
145
+
146
+ def aasm_raw_attribute_value(state)
147
+ if aasm_enum
148
+ value = self.class.send(aasm_enum)[state]
149
+ else
150
+ value = state.to_s
151
+ end
152
+ end
120
153
 
121
154
  # Ensures that if the aasm_state column is nil and the record is new
122
155
  # that the initial state gets populated before validation on create
@@ -91,14 +91,10 @@ module AASM
91
91
  if @klass.ancestors.map {|klass| klass.to_s}.include?("ActiveRecord::Base")
92
92
 
93
93
  conditions = {"#{@klass.table_name}.#{@klass.aasm_column}" => name.to_s}
94
- if ActiveRecord::VERSION::MAJOR >= 4
94
+ if ActiveRecord::VERSION::MAJOR >= 3
95
95
  @klass.class_eval do
96
96
  scope name, lambda { where(conditions) }
97
97
  end
98
- elsif ActiveRecord::VERSION::MAJOR >= 3
99
- @klass.class_eval do
100
- scope name, where(conditions)
101
- end
102
98
  else
103
99
  @klass.class_eval do
104
100
  named_scope name, :conditions => conditions
data/lib/aasm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "3.2.1"
2
+ VERSION = "3.3.0"
3
3
  end
@@ -23,6 +23,227 @@ describe "instance methods" do
23
23
  expect(gate).to respond_to(:aasm_write_state_without_persistence)
24
24
  end
25
25
 
26
+ describe "aasm_column_looks_like_enum" do
27
+ subject { lambda{ gate.send(:aasm_column_looks_like_enum) } }
28
+
29
+ let(:column_name) { "value" }
30
+ let(:columns_hash) { Hash[column_name, column] }
31
+
32
+ before :each do
33
+ gate.class.stub(:aasm_column).and_return(column_name.to_sym)
34
+ gate.class.stub(:columns_hash).and_return(columns_hash)
35
+ end
36
+
37
+ context "when AASM column has integer type" do
38
+ let(:column) { double(Object, type: :integer) }
39
+
40
+ it "returns true" do
41
+ expect(subject.call).to be_true
42
+ end
43
+ end
44
+
45
+ context "when AASM column has string type" do
46
+ let(:column) { double(Object, type: :string) }
47
+
48
+ it "returns false" do
49
+ expect(subject.call).to be_false
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "aasm_guess_enum_method" do
55
+ subject { lambda{ gate.send(:aasm_guess_enum_method) } }
56
+
57
+ before :each do
58
+ gate.class.stub(:aasm_column).and_return(:value)
59
+ end
60
+
61
+ it "pluralizes AASM column name" do
62
+ expect(subject.call).to eq :values
63
+ end
64
+ end
65
+
66
+ describe "aasm_enum" do
67
+ subject { lambda{ gate.send(:aasm_enum) } }
68
+
69
+ context "when AASM enum setting contains an explicit enum method name" do
70
+ let(:enum) { :test }
71
+
72
+ before :each do
73
+ AASM::StateMachine[Gate].config.stub(:enum).and_return(enum)
74
+ end
75
+
76
+ it "returns whatever value was set in AASM config" do
77
+ expect(subject.call).to eq enum
78
+ end
79
+ end
80
+
81
+ context "when AASM enum setting is simply set to true" do
82
+ before :each do
83
+ AASM::StateMachine[Gate].config.stub(:enum).and_return(true)
84
+ Gate.stub(:aasm_column).and_return(:value)
85
+ gate.stub(:aasm_guess_enum_method).and_return(:values)
86
+ end
87
+
88
+ it "infers enum method name from pluralized column name" do
89
+ expect(subject.call).to eq :values
90
+ expect(gate).to have_received :aasm_guess_enum_method
91
+ end
92
+ end
93
+
94
+ context "when AASM enum setting is explicitly disabled" do
95
+ before :each do
96
+ AASM::StateMachine[Gate].config.stub(:enum).and_return(false)
97
+ end
98
+
99
+ it "returns nil" do
100
+ expect(subject.call).to be_nil
101
+ end
102
+ end
103
+
104
+ context "when AASM enum setting is not enabled" do
105
+ before :each do
106
+ AASM::StateMachine[Gate].config.stub(:enum).and_return(nil)
107
+ Gate.stub(:aasm_column).and_return(:value)
108
+ end
109
+
110
+ context "when AASM column looks like enum" do
111
+ before :each do
112
+ gate.stub(:aasm_column_looks_like_enum).and_return(true)
113
+ gate.stub(:aasm_guess_enum_method).and_return(:values)
114
+ end
115
+
116
+ it "infers enum method name from pluralized column name" do
117
+ expect(subject.call).to eq :values
118
+ expect(gate).to have_received :aasm_guess_enum_method
119
+ end
120
+ end
121
+
122
+ context "when AASM column doesn't look like enum'" do
123
+ before :each do
124
+ gate.stub(:aasm_column_looks_like_enum)
125
+ .and_return(false)
126
+ end
127
+
128
+ it "returns nil, as we're not using enum" do
129
+ expect(subject.call).to be_nil
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ context "when AASM is configured to use enum" do
136
+ let(:state_sym) { :running }
137
+ let(:state_code) { 2 }
138
+ let(:enum_name) { :states }
139
+ let(:enum) { Hash[state_sym, state_code] }
140
+
141
+ before :each do
142
+ gate
143
+ .stub(:aasm_enum)
144
+ .and_return(enum_name)
145
+ gate.stub(:aasm_write_attribute)
146
+ gate.stub(:write_attribute)
147
+
148
+ gate
149
+ .class
150
+ .stub(enum_name)
151
+ .and_return(enum)
152
+ end
153
+
154
+ describe "aasm_write_state" do
155
+ context "when AASM is configured to skip validations on save" do
156
+ before :each do
157
+ gate
158
+ .stub(:aasm_skipping_validations)
159
+ .and_return(true)
160
+ end
161
+
162
+ it "passes state code instead of state symbol to update_all" do
163
+ # stub_chain does not allow us to give expectations on call
164
+ # parameters in the middle of the chain, so we need to use
165
+ # intermediate object instead.
166
+ obj = double(Object, update_all: 1)
167
+ gate
168
+ .class
169
+ .stub(:where)
170
+ .and_return(obj)
171
+
172
+ gate.aasm_write_state state_sym
173
+
174
+ expect(obj).to have_received(:update_all)
175
+ .with(Hash[gate.class.aasm_column, state_code])
176
+ end
177
+ end
178
+
179
+ context "when AASM is not skipping validations" do
180
+ it "delegates state update to the helper method" do
181
+ # Let's pretend that validation is passed
182
+ gate.stub(:save).and_return(true)
183
+
184
+ gate.aasm_write_state state_sym
185
+
186
+ expect(gate).to have_received(:aasm_write_attribute).with(state_sym)
187
+ expect(gate).to_not have_received :write_attribute
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "aasm_write_state_without_persistence" do
193
+ it "delegates state update to the helper method" do
194
+ gate.aasm_write_state_without_persistence state_sym
195
+
196
+ expect(gate).to have_received(:aasm_write_attribute).with(state_sym)
197
+ expect(gate).to_not have_received :write_attribute
198
+ end
199
+ end
200
+
201
+ describe "aasm_raw_attribute_value" do
202
+ it "converts state symbol to state code" do
203
+ expect(gate.send(:aasm_raw_attribute_value, state_sym))
204
+ .to eq state_code
205
+ end
206
+ end
207
+ end
208
+
209
+ context "when AASM is configured to use string field" do
210
+ let(:state_sym) { :running }
211
+
212
+ before :each do
213
+ gate
214
+ .stub(:aasm_enum)
215
+ .and_return(nil)
216
+ end
217
+
218
+ describe "aasm_raw_attribute_value" do
219
+ it "converts state symbol to string" do
220
+ expect(gate.send(:aasm_raw_attribute_value, state_sym))
221
+ .to eq state_sym.to_s
222
+ end
223
+ end
224
+ end
225
+
226
+ describe "aasm_write_attribute helper method" do
227
+ let(:sym) { :sym }
228
+ let(:value) { 42 }
229
+
230
+ before :each do
231
+ gate.stub(:write_attribute)
232
+ gate.stub(:aasm_raw_attribute_value)
233
+ .and_return(value)
234
+
235
+ gate.send(:aasm_write_attribute, sym)
236
+ end
237
+
238
+ it "generates attribute value using a helper method" do
239
+ expect(gate).to have_received(:aasm_raw_attribute_value).with(sym)
240
+ end
241
+
242
+ it "writes attribute to the model" do
243
+ expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
244
+ end
245
+ end
246
+
26
247
  it "should return the initial state when new and the aasm field is nil" do
27
248
  expect(gate.aasm.current_state).to eq(:opened)
28
249
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Barron
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-06-14 00:00:00.000000000 Z
14
+ date: 2014-07-06 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rake