assignable_values 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +77 -17
- data/assignable_values.gemspec +2 -0
- data/lib/assignable_values/active_record/restriction/base.rb +8 -3
- data/lib/assignable_values/active_record/restriction/scalar_attribute.rb +15 -3
- data/lib/assignable_values/version.rb +1 -1
- data/spec/assignable_values/active_record_spec.rb +90 -79
- metadata +30 -16
data/README.md
CHANGED
@@ -64,6 +64,16 @@ You can populate a `<select>` tag with pairs of internal values and human labels
|
|
64
64
|
|
65
65
|
form.collection_select :genre, form.object.assignable_genres, :to_s, :humanized
|
66
66
|
|
67
|
+
If you don't like to use your I18n dictionary for humanizations, you can also declare them directly in your model like this:
|
68
|
+
|
69
|
+
class Song < ActiveRecord::Base
|
70
|
+
assignable_values_for :genre do
|
71
|
+
{ 'pop' => 'Pop music',
|
72
|
+
'rock' => 'Rock music',
|
73
|
+
'electronic' => 'Electronic music' }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
67
77
|
|
68
78
|
### Defining default values
|
69
79
|
|
@@ -90,6 +100,20 @@ Defaults can be lambdas:
|
|
90
100
|
The lambda will be evaluated in the context of the record instance.
|
91
101
|
|
92
102
|
|
103
|
+
### Allowing blank values
|
104
|
+
|
105
|
+
By default, an attribute *must* be assigned an value. If the value of an attribute is blank, the attribute
|
106
|
+
will get a validation error.
|
107
|
+
|
108
|
+
If you would like to change this behavior and allow blank values to be valid, use the `:allow_blank` option:
|
109
|
+
|
110
|
+
class Song < ActiveRecord::Base
|
111
|
+
assignable_values_for :genre, :default => 'rock', :allow_blank => true do
|
112
|
+
['pop', 'rock', 'electronic']
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
93
117
|
### Values are only validated when they change
|
94
118
|
|
95
119
|
Values are only validated when they change. This is useful when the list of assignable values can change during runtime:
|
@@ -174,16 +198,18 @@ The list of assignable values is generated at runtime. Since the given block is
|
|
174
198
|
end
|
175
199
|
|
176
200
|
|
177
|
-
Obtaining assignable values from
|
178
|
-
|
201
|
+
Obtaining assignable values from another source
|
202
|
+
-----------------------------------------------
|
179
203
|
|
180
|
-
The list of assignable values can be provided by
|
204
|
+
The list of assignable values can be provided by any object that is accessible from your model. This is useful for authorization scenarios like [Consul](https://github.com/makandra/consul) or [CanCan](https://github.com/ryanb/cancan), where permissions are defined in a single class.
|
181
205
|
|
182
|
-
|
206
|
+
You can define the source of assignable values by setting the `:through` option to a lambda:
|
207
|
+
|
208
|
+
class Story < ActiveRecord::Base
|
183
209
|
assignable_values_for :state, :through => lambda { Power.current }
|
184
210
|
end
|
185
211
|
|
186
|
-
`Power.current` must now respond to a method `
|
212
|
+
`Power.current` must now respond to a method `assignable_story_states` or `assignable_story_states(story)` which returns an `Enumerable` of state strings:
|
187
213
|
|
188
214
|
class Power
|
189
215
|
|
@@ -193,7 +219,7 @@ The list of assignable values can be provided by a delegate. This is useful for
|
|
193
219
|
@role = role
|
194
220
|
end
|
195
221
|
|
196
|
-
def
|
222
|
+
def assignable_story_states(story)
|
197
223
|
states = ['draft', 'pending']
|
198
224
|
states << 'accepted' if @role == :admin
|
199
225
|
states
|
@@ -203,30 +229,64 @@ The list of assignable values can be provided by a delegate. This is useful for
|
|
203
229
|
|
204
230
|
Listing and validating works the same with delegation:
|
205
231
|
|
206
|
-
|
232
|
+
story = Story.new(:state => 'accepted')
|
207
233
|
|
208
234
|
Power.current = Power.new(:guest)
|
209
|
-
|
210
|
-
|
235
|
+
story.assignable_states # => ['draft', 'pending']
|
236
|
+
story.valid? # => false
|
211
237
|
|
212
238
|
Power.current = Power.new(:admin)
|
213
|
-
|
214
|
-
|
239
|
+
story.assignable_states # => ['draft', 'pending', 'accepted']
|
240
|
+
story.valid? # => true
|
215
241
|
|
216
242
|
Note that delegated validation is skipped when the delegate is `nil`. This way your model remains usable when there is no authorization context, like in batch processes or the console:
|
217
243
|
|
218
|
-
|
244
|
+
story = Story.new(:state => 'foo')
|
219
245
|
Power.current = nil
|
220
|
-
|
246
|
+
story.valid? # => true
|
247
|
+
|
248
|
+
Think of this as enabling an optional authorization layer on top of your model validations, which can be switched on or off depending on the current context.
|
221
249
|
|
222
250
|
Instead of a lambda you can also use the `:through` option to name an instance method:
|
223
251
|
|
224
|
-
class
|
252
|
+
class Story < ActiveRecord::Base
|
225
253
|
attr_accessor :power
|
226
254
|
assignable_values_for :state, :through => :power
|
227
255
|
end
|
228
256
|
|
229
257
|
|
258
|
+
### Obtaining assignable values from a Consul power
|
259
|
+
|
260
|
+
A common use case for the `:through` option is when there is some globally accessible object that knows about permissions for the current request. In practice you will find that it requires some effort to make sure such an object is properly instantiated and accessible.
|
261
|
+
|
262
|
+
If you are using [Consul](https://github.com/makandra/consul), you will get a lot of this plumbing for free. Consul gives you a macro `current_power` to instantiate a so called "power", which describes what the current user may access:
|
263
|
+
|
264
|
+
class ApplicationController < ActionController::Base
|
265
|
+
include Consul::Controller
|
266
|
+
|
267
|
+
current_power do
|
268
|
+
Power.new(current_user)
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
The code above will provide you with a helper method `current_power` for your controller and views. Everywhere else, you can simply access it from `Power.current`.
|
274
|
+
|
275
|
+
You can now delegate validation of assignable values to the current power by saying:
|
276
|
+
|
277
|
+
class Story < ActiveRecord::Base
|
278
|
+
authorize_values_for :state
|
279
|
+
end
|
280
|
+
|
281
|
+
This is a shortcut for saying:
|
282
|
+
|
283
|
+
class Story < ActiveRecord::Base
|
284
|
+
assignable_values_for :state, :through => lambda { Power.current }
|
285
|
+
end
|
286
|
+
|
287
|
+
Head over to the [Consul README](https://github.com/makandra/consul) for details.
|
288
|
+
|
289
|
+
|
230
290
|
Installation
|
231
291
|
------------
|
232
292
|
|
@@ -241,13 +301,13 @@ Development
|
|
241
301
|
-----------
|
242
302
|
|
243
303
|
- Fork the repository.
|
244
|
-
- Push your changes with specs.
|
304
|
+
- Push your changes with specs. There is a Rails 3 test application in `spec/app_root` if you need to test integration with a live Rails app.
|
245
305
|
- Send me a pull request.
|
246
306
|
|
247
|
-
I'm very eager to keep this gem leightweight and on topic. If you're unsure whether a change would make it into the gem, [talk to me beforehand](henning.koch@makandra.de).
|
307
|
+
I'm very eager to keep this gem leightweight and on topic. If you're unsure whether a change would make it into the gem, [talk to me beforehand](mailto:henning.koch@makandra.de).
|
248
308
|
|
249
309
|
|
250
310
|
Credits
|
251
311
|
-------
|
252
312
|
|
253
|
-
[Henning Koch](henning.koch@makandra.de) from [makandra](http://makandra.com/).
|
313
|
+
[Henning Koch](mailto:henning.koch@makandra.de) from [makandra](http://makandra.com/).
|
data/assignable_values.gemspec
CHANGED
@@ -15,6 +15,8 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
|
18
|
+
s.add_dependency('activerecord')
|
19
|
+
|
18
20
|
s.add_development_dependency('rails', '~>3.1')
|
19
21
|
s.add_development_dependency('rspec', '~>2.8')
|
20
22
|
s.add_development_dependency('rspec-rails', '~>2.8')
|
@@ -48,6 +48,10 @@ module AssignableValues
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
+
def parse_values(values)
|
52
|
+
values.to_a
|
53
|
+
end
|
54
|
+
|
51
55
|
def current_value(record)
|
52
56
|
record.send(property)
|
53
57
|
end
|
@@ -115,10 +119,11 @@ module AssignableValues
|
|
115
119
|
|
116
120
|
def raw_assignable_values(record)
|
117
121
|
if delegate?
|
118
|
-
assignable_values_from_delegate(record)
|
122
|
+
values = assignable_values_from_delegate(record)
|
119
123
|
else
|
120
|
-
record.instance_eval(&@values)
|
121
|
-
end
|
124
|
+
values = record.instance_eval(&@values)
|
125
|
+
end
|
126
|
+
parse_values(values)
|
122
127
|
end
|
123
128
|
|
124
129
|
def delegate(record)
|
@@ -10,13 +10,26 @@ module AssignableValues
|
|
10
10
|
|
11
11
|
def humanize_string_value(value)
|
12
12
|
if value.present?
|
13
|
-
|
14
|
-
|
13
|
+
if @hardcoded_humanizations
|
14
|
+
@hardcoded_humanizations[value]
|
15
|
+
else
|
16
|
+
dictionary_key = "assignable_values.#{model.name.underscore}.#{property}.#{value}"
|
17
|
+
I18n.t(dictionary_key, :default => value.humanize)
|
18
|
+
end
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
22
|
private
|
19
23
|
|
24
|
+
def parse_values(values)
|
25
|
+
if values.is_a?(Hash)
|
26
|
+
@hardcoded_humanizations = values
|
27
|
+
values = values.keys
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
20
33
|
def define_humanized_method
|
21
34
|
restriction = self
|
22
35
|
enhance_model do
|
@@ -44,7 +57,6 @@ module AssignableValues
|
|
44
57
|
record.send("#{property}_was")
|
45
58
|
end
|
46
59
|
|
47
|
-
|
48
60
|
end
|
49
61
|
end
|
50
62
|
end
|
@@ -4,13 +4,11 @@ require 'ostruct'
|
|
4
4
|
describe AssignableValues::ActiveRecord do
|
5
5
|
|
6
6
|
def disposable_song_class(&block)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
'Song'
|
11
|
-
end
|
7
|
+
klass = Class.new(Song, &block)
|
8
|
+
def klass.name
|
9
|
+
'Song'
|
12
10
|
end
|
13
|
-
|
11
|
+
klass
|
14
12
|
end
|
15
13
|
|
16
14
|
describe '.assignable_values' do
|
@@ -99,22 +97,22 @@ describe AssignableValues::ActiveRecord do
|
|
99
97
|
it 'should validate that the association is allowed' do
|
100
98
|
allowed_association = Artist.create!
|
101
99
|
disallowed_association = Artist.create!
|
102
|
-
|
100
|
+
klass = disposable_song_class do
|
103
101
|
assignable_values_for :artist do
|
104
102
|
[allowed_association]
|
105
103
|
end
|
106
104
|
end
|
107
|
-
|
108
|
-
|
105
|
+
klass.new(:artist => allowed_association).should be_valid
|
106
|
+
klass.new(:artist => disallowed_association).should_not be_valid
|
109
107
|
end
|
110
108
|
|
111
109
|
it 'should allow a nil association if the :allow_blank option is set' do
|
112
|
-
|
110
|
+
klass = disposable_song_class do
|
113
111
|
assignable_values_for :artist, :allow_blank => true do
|
114
112
|
[]
|
115
113
|
end
|
116
114
|
end
|
117
|
-
record =
|
115
|
+
record = klass.new
|
118
116
|
record.artist.should be_nil
|
119
117
|
record.should be_valid
|
120
118
|
end
|
@@ -122,9 +120,9 @@ describe AssignableValues::ActiveRecord do
|
|
122
120
|
it 'should allow a previously saved association even if that association is no longer allowed' do
|
123
121
|
allowed_association = Artist.create!
|
124
122
|
disallowed_association = Artist.create!
|
125
|
-
|
126
|
-
record =
|
127
|
-
|
123
|
+
klass = disposable_song_class
|
124
|
+
record = klass.create!(:artist => disallowed_association)
|
125
|
+
klass.class_eval do
|
128
126
|
assignable_values_for :artist do
|
129
127
|
[allowed_association]
|
130
128
|
end
|
@@ -134,36 +132,36 @@ describe AssignableValues::ActiveRecord do
|
|
134
132
|
|
135
133
|
it "should not load a previously saved association if the association's foreign key hasn't changed" do
|
136
134
|
association = Artist.create!
|
137
|
-
|
135
|
+
klass = disposable_song_class do
|
138
136
|
assignable_values_for :artist do
|
139
137
|
[association] # This example doesn't care about what's assignable. We're only interested in behavior up to the validation.
|
140
138
|
end
|
141
139
|
end
|
142
|
-
record =
|
140
|
+
record = klass.create!(:artist => association)
|
143
141
|
Artist.should_not_receive(:find_by_id)
|
144
142
|
record.valid?
|
145
143
|
end
|
146
144
|
|
147
145
|
it 'should not fail or allow nil if a previously saved association no longer exists in the database' do
|
148
146
|
allowed_association = Artist.create!
|
149
|
-
disposable_song_class
|
147
|
+
klass = disposable_song_class do
|
150
148
|
assignable_values_for :artist do
|
151
149
|
[allowed_association]
|
152
150
|
end
|
153
151
|
end
|
154
|
-
record =
|
152
|
+
record = klass.new
|
155
153
|
record.stub :artist_id_was => -1
|
156
154
|
record.should_not be_valid
|
157
155
|
end
|
158
156
|
|
159
157
|
it 'should uncache a stale association before validating' do
|
160
|
-
|
158
|
+
klass = disposable_song_class do
|
161
159
|
assignable_values_for :artist do
|
162
160
|
[] # This example doesn't care about what's assignable. We're only interested in behavior up to the validation.
|
163
161
|
end
|
164
162
|
end
|
165
163
|
association = Artist.create!
|
166
|
-
record =
|
164
|
+
record = klass.new
|
167
165
|
record.stub(:artist => association, :artist_id => -1) # This is a stale association: The associated object's id doesn't match the foreign key. This can happen in Rails 2, not Rails 3.
|
168
166
|
record.should_receive(:artist).ordered.and_return(association)
|
169
167
|
record.should_receive(:artist).ordered.with(true).and_return(association)
|
@@ -171,13 +169,13 @@ describe AssignableValues::ActiveRecord do
|
|
171
169
|
end
|
172
170
|
|
173
171
|
it 'should not uncache a fresh association before validating' do
|
174
|
-
|
172
|
+
klass = disposable_song_class do
|
175
173
|
assignable_values_for :artist do
|
176
174
|
[] # This example doesn't care about what's assignable. We're only interested in behavior up to the validation.
|
177
175
|
end
|
178
176
|
end
|
179
177
|
association = Artist.create!
|
180
|
-
record =
|
178
|
+
record = klass.new
|
181
179
|
record.stub(:artist => association, :artist_id => association.id) # This is a fresh association: The associated object's id matches the foreign key.
|
182
180
|
record.should_receive(:artist).with(no_args).and_return(association)
|
183
181
|
record.valid?
|
@@ -188,35 +186,35 @@ describe AssignableValues::ActiveRecord do
|
|
188
186
|
context 'when delegating using the :through option' do
|
189
187
|
|
190
188
|
it 'should obtain allowed values from a method with the given name' do
|
191
|
-
|
189
|
+
klass = disposable_song_class do
|
192
190
|
assignable_values_for :genre, :through => :delegate
|
193
191
|
def delegate
|
194
192
|
OpenStruct.new(:assignable_song_genres => %w[pop rock])
|
195
193
|
end
|
196
194
|
end
|
197
|
-
|
198
|
-
|
195
|
+
klass.new(:genre => 'pop').should be_valid
|
196
|
+
klass.new(:genre => 'disallowed value').should_not be_valid
|
199
197
|
end
|
200
198
|
|
201
199
|
it 'should be able to delegate to a lambda, which is evaluated in the context of the record instance' do
|
202
|
-
|
200
|
+
klass = disposable_song_class do
|
203
201
|
assignable_values_for :genre, :through => lambda { delegate }
|
204
202
|
def delegate
|
205
203
|
OpenStruct.new(:assignable_song_genres => %w[pop rock])
|
206
204
|
end
|
207
205
|
end
|
208
|
-
|
209
|
-
|
206
|
+
klass.new(:genre => 'pop').should be_valid
|
207
|
+
klass.new(:genre => 'disallowed value').should_not be_valid
|
210
208
|
end
|
211
209
|
|
212
210
|
it 'should skip the validation if that method returns nil' do
|
213
|
-
|
211
|
+
klass = disposable_song_class do
|
214
212
|
assignable_values_for :genre, :through => :delegate
|
215
213
|
def delegate
|
216
214
|
nil
|
217
215
|
end
|
218
216
|
end
|
219
|
-
|
217
|
+
klass.new(:genre => 'pop').should be_valid
|
220
218
|
end
|
221
219
|
|
222
220
|
end
|
@@ -224,25 +222,25 @@ describe AssignableValues::ActiveRecord do
|
|
224
222
|
context 'with :default option' do
|
225
223
|
|
226
224
|
it 'should allow to set a default' do
|
227
|
-
|
225
|
+
klass = disposable_song_class do
|
228
226
|
assignable_values_for :genre, :default => 'pop' do
|
229
227
|
%w[pop rock]
|
230
228
|
end
|
231
229
|
end
|
232
|
-
|
230
|
+
klass.new.genre.should == 'pop'
|
233
231
|
end
|
234
232
|
|
235
233
|
it 'should allow to set a default through a lambda' do
|
236
|
-
|
234
|
+
klass = disposable_song_class do
|
237
235
|
assignable_values_for :genre, :default => lambda { 'pop' } do
|
238
236
|
%w[pop rock]
|
239
237
|
end
|
240
238
|
end
|
241
|
-
|
239
|
+
klass.new.genre.should == 'pop'
|
242
240
|
end
|
243
241
|
|
244
242
|
it 'should evaluate a lambda default in the context of the record instance' do
|
245
|
-
|
243
|
+
klass = disposable_song_class do
|
246
244
|
assignable_values_for :genre, :default => lambda { default_genre } do
|
247
245
|
%w[pop rock]
|
248
246
|
end
|
@@ -250,7 +248,7 @@ describe AssignableValues::ActiveRecord do
|
|
250
248
|
'pop'
|
251
249
|
end
|
252
250
|
end
|
253
|
-
|
251
|
+
klass.new.genre.should == 'pop'
|
254
252
|
end
|
255
253
|
|
256
254
|
end
|
@@ -258,54 +256,25 @@ describe AssignableValues::ActiveRecord do
|
|
258
256
|
context 'when generating methods to list assignable values' do
|
259
257
|
|
260
258
|
it 'should generate an instance method returning a list of assignable values' do
|
261
|
-
|
259
|
+
klass = disposable_song_class do
|
262
260
|
assignable_values_for :genre do
|
263
261
|
%w[pop rock]
|
264
262
|
end
|
265
263
|
end
|
266
|
-
|
267
|
-
end
|
268
|
-
|
269
|
-
it "should define a method #humanized on strings in that list, which return up the value's' translation" do
|
270
|
-
@klass = disposable_song_class do
|
271
|
-
assignable_values_for :genre do
|
272
|
-
%w[pop rock]
|
273
|
-
end
|
274
|
-
end
|
275
|
-
@klass.new.assignable_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
|
276
|
-
end
|
277
|
-
|
278
|
-
it 'should use String#humanize as a default translation' do
|
279
|
-
@klass = disposable_song_class do
|
280
|
-
assignable_values_for :genre do
|
281
|
-
%w[electronic]
|
282
|
-
end
|
283
|
-
end
|
284
|
-
@klass.new.assignable_genres.collect(&:humanized).should == ['Electronic']
|
285
|
-
end
|
286
|
-
|
287
|
-
it 'should not define a method #humanized on values that are not strings' do
|
288
|
-
@klass = disposable_song_class do
|
289
|
-
assignable_values_for :year do
|
290
|
-
[1999, 2000, 2001]
|
291
|
-
end
|
292
|
-
end
|
293
|
-
years = @klass.new.assignable_years
|
294
|
-
years.should == [1999, 2000, 2001]
|
295
|
-
years.first.should_not respond_to(:humanized)
|
264
|
+
klass.new.assignable_genres.should == %w[pop rock]
|
296
265
|
end
|
297
266
|
|
298
267
|
it 'should call #to_a on the list of assignable values, allowing ranges and scopes to be passed as allowed value descriptors' do
|
299
|
-
|
268
|
+
klass = disposable_song_class do
|
300
269
|
assignable_values_for :year do
|
301
270
|
1999..2001
|
302
271
|
end
|
303
272
|
end
|
304
|
-
|
273
|
+
klass.new.assignable_years.should == [1999, 2000, 2001]
|
305
274
|
end
|
306
275
|
|
307
276
|
it 'should evaluate the value block in the context of the record instance' do
|
308
|
-
|
277
|
+
klass = disposable_song_class do
|
309
278
|
assignable_values_for :genre do
|
310
279
|
genres
|
311
280
|
end
|
@@ -313,30 +282,72 @@ describe AssignableValues::ActiveRecord do
|
|
313
282
|
%w[pop rock]
|
314
283
|
end
|
315
284
|
end
|
316
|
-
|
285
|
+
klass.new.assignable_genres.should == %w[pop rock]
|
317
286
|
end
|
318
287
|
|
319
288
|
it 'should include a previously saved value, even if is no longer allowed' do
|
320
|
-
|
289
|
+
klass = disposable_song_class do
|
321
290
|
assignable_values_for :genre do
|
322
291
|
%w[pop rock]
|
323
292
|
end
|
324
293
|
end
|
325
|
-
record =
|
294
|
+
record = klass.new(:genre => 'ballad')
|
326
295
|
record.save!(:validate => false)
|
327
296
|
record.assignable_genres.should =~ %w[pop rock ballad]
|
328
297
|
end
|
329
298
|
|
299
|
+
context 'humanization' do
|
300
|
+
|
301
|
+
it "should define a method #humanized on strings in that list, which return up the value's' translation" do
|
302
|
+
klass = disposable_song_class do
|
303
|
+
assignable_values_for :genre do
|
304
|
+
%w[pop rock]
|
305
|
+
end
|
306
|
+
end
|
307
|
+
klass.new.assignable_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'should use String#humanize as a default translation' do
|
311
|
+
klass = disposable_song_class do
|
312
|
+
assignable_values_for :genre do
|
313
|
+
%w[electronic]
|
314
|
+
end
|
315
|
+
end
|
316
|
+
klass.new.assignable_genres.collect(&:humanized).should == ['Electronic']
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'should not define a method #humanized on values that are not strings' do
|
320
|
+
klass = disposable_song_class do
|
321
|
+
assignable_values_for :year do
|
322
|
+
[1999, 2000, 2001]
|
323
|
+
end
|
324
|
+
end
|
325
|
+
years = klass.new.assignable_years
|
326
|
+
years.should == [1999, 2000, 2001]
|
327
|
+
years.first.should_not respond_to(:humanized)
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'should allow to directly declare humanized values by passing a hash to assignable_values_for' do
|
331
|
+
klass = disposable_song_class do
|
332
|
+
assignable_values_for :genre do
|
333
|
+
{ 'pop' => 'Pop music', 'rock' => 'Rock music' }
|
334
|
+
end
|
335
|
+
end
|
336
|
+
klass.new.assignable_genres.collect(&:humanized).should =~ ['Pop music', 'Rock music']
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
330
341
|
context 'with :through option' do
|
331
342
|
|
332
343
|
it 'should retrieve assignable values from the given method' do
|
333
|
-
|
344
|
+
klass = disposable_song_class do
|
334
345
|
assignable_values_for :genre, :through => :delegate
|
335
346
|
def delegate
|
336
347
|
@delegate ||= 'delegate'
|
337
348
|
end
|
338
349
|
end
|
339
|
-
record =
|
350
|
+
record = klass.new
|
340
351
|
record.delegate.should_receive(:assignable_song_genres).and_return %w[pop rock]
|
341
352
|
record.assignable_genres.should == %w[pop rock]
|
342
353
|
end
|
@@ -347,25 +358,25 @@ describe AssignableValues::ActiveRecord do
|
|
347
358
|
record_received(record)
|
348
359
|
%w[pop rock]
|
349
360
|
end
|
350
|
-
|
361
|
+
klass = disposable_song_class do
|
351
362
|
assignable_values_for :genre, :through => :delegate
|
352
363
|
define_method :delegate do
|
353
364
|
delegate
|
354
365
|
end
|
355
366
|
end
|
356
|
-
record =
|
367
|
+
record = klass.new
|
357
368
|
delegate.should_receive(:record_received).with(record)
|
358
369
|
record.assignable_genres.should == %w[pop rock]
|
359
370
|
end
|
360
371
|
|
361
372
|
it 'should raise an error if the given method returns nil' do
|
362
|
-
|
373
|
+
klass = disposable_song_class do
|
363
374
|
assignable_values_for :genre, :through => :delegate
|
364
375
|
def delegate
|
365
376
|
nil
|
366
377
|
end
|
367
378
|
end
|
368
|
-
expect {
|
379
|
+
expect { klass.new.assignable_genres }.to raise_error(AssignableValues::DelegateUnavailable)
|
369
380
|
end
|
370
381
|
|
371
382
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: assignable_values
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Henning Koch
|
@@ -15,12 +15,27 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-06-15 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
name: activerecord
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
24
39
|
none: false
|
25
40
|
requirements:
|
26
41
|
- - ~>
|
@@ -31,11 +46,11 @@ dependencies:
|
|
31
46
|
- 1
|
32
47
|
version: "3.1"
|
33
48
|
name: rails
|
34
|
-
version_requirements: *
|
35
|
-
type: :development
|
49
|
+
version_requirements: *id002
|
36
50
|
- !ruby/object:Gem::Dependency
|
51
|
+
type: :development
|
37
52
|
prerelease: false
|
38
|
-
requirement: &
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
54
|
none: false
|
40
55
|
requirements:
|
41
56
|
- - ~>
|
@@ -46,11 +61,11 @@ dependencies:
|
|
46
61
|
- 8
|
47
62
|
version: "2.8"
|
48
63
|
name: rspec
|
49
|
-
version_requirements: *
|
50
|
-
type: :development
|
64
|
+
version_requirements: *id003
|
51
65
|
- !ruby/object:Gem::Dependency
|
66
|
+
type: :development
|
52
67
|
prerelease: false
|
53
|
-
requirement: &
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
54
69
|
none: false
|
55
70
|
requirements:
|
56
71
|
- - ~>
|
@@ -61,11 +76,11 @@ dependencies:
|
|
61
76
|
- 8
|
62
77
|
version: "2.8"
|
63
78
|
name: rspec-rails
|
64
|
-
version_requirements: *
|
65
|
-
type: :development
|
79
|
+
version_requirements: *id004
|
66
80
|
- !ruby/object:Gem::Dependency
|
81
|
+
type: :development
|
67
82
|
prerelease: false
|
68
|
-
requirement: &
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
69
84
|
none: false
|
70
85
|
requirements:
|
71
86
|
- - ">="
|
@@ -75,8 +90,7 @@ dependencies:
|
|
75
90
|
- 0
|
76
91
|
version: "0"
|
77
92
|
name: sqlite3
|
78
|
-
version_requirements: *
|
79
|
-
type: :development
|
93
|
+
version_requirements: *id005
|
80
94
|
description: Restrict the values assignable to ActiveRecord attributes or associations. Or enums on steroids.
|
81
95
|
email: henning.koch@makandra.de
|
82
96
|
executables: []
|
@@ -157,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
171
|
requirements: []
|
158
172
|
|
159
173
|
rubyforge_project:
|
160
|
-
rubygems_version: 1.3.9.
|
174
|
+
rubygems_version: 1.3.9.4
|
161
175
|
signing_key:
|
162
176
|
specification_version: 3
|
163
177
|
summary: Restrict the values assignable to ActiveRecord attributes or associations. Or enums on steroids.
|