aasm 3.2.1 → 3.3.0

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.
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