assignable_values 0.14.0 → 0.16.2

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.
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
+ require 'active_support/deprecation'
2
3
  require 'assignable_values/errors'
3
4
  require 'assignable_values/active_record'
4
5
  require 'assignable_values/active_record/restriction/base'
@@ -48,25 +48,31 @@ module AssignableValues
48
48
  end
49
49
 
50
50
  def assignable_values(record, options = {})
51
- assignable_values = []
51
+ additional_assignable_values = []
52
52
  current_values = assignable_values_from_record_or_delegate(record)
53
53
 
54
54
  if options.fetch(:include_old_value, true) && has_previously_saved_value?(record)
55
55
  old_value = previously_saved_value(record)
56
56
  if @options[:multiple]
57
57
  if old_value.is_a?(Array)
58
- assignable_values |= old_value
58
+ additional_assignable_values |= old_value
59
59
  end
60
60
  elsif !old_value.blank? && !current_values.include?(old_value)
61
- assignable_values << old_value
61
+ additional_assignable_values << old_value
62
62
  end
63
63
  end
64
64
 
65
- assignable_values += current_values
66
65
  if options[:decorate]
67
- assignable_values = decorate_values(assignable_values)
66
+ current_values = decorate_values(current_values)
67
+ additional_assignable_values = decorate_values(additional_assignable_values)
68
+ end
69
+
70
+ if additional_assignable_values.present?
71
+ # will not keep current_values scoped
72
+ additional_assignable_values + current_values
73
+ else
74
+ current_values
68
75
  end
69
- assignable_values
70
76
  end
71
77
 
72
78
  private
@@ -94,7 +100,21 @@ module AssignableValues
94
100
  def assignable_single_value?(record, value)
95
101
  (has_previously_saved_value?(record) && value == previously_saved_value(record)) ||
96
102
  (value.blank? && allow_blank?(record)) ||
97
- assignable_values(record, :include_old_value => false).include?(value)
103
+ included_in_assignable_values?(record, value)
104
+ end
105
+
106
+ def included_in_assignable_values?(record, value)
107
+ values_or_scope = assignable_values(record, :include_old_value => false)
108
+
109
+ if is_scope?(values_or_scope)
110
+ values_or_scope.exists?(value.id)
111
+ else
112
+ values_or_scope.include?(value)
113
+ end
114
+ end
115
+
116
+ def is_scope?(object)
117
+ object.respond_to?(:scoped) || object.respond_to?(:all)
98
118
  end
99
119
 
100
120
  def assignable_multi_value?(record, value)
@@ -217,9 +237,9 @@ module AssignableValues
217
237
 
218
238
  def assignable_values_from_record_or_delegate(record)
219
239
  if delegate?
220
- assignable_values_from_delegate(record).to_a
240
+ assignable_values_from_delegate(record)
221
241
  else
222
- record.instance_exec(&@values).to_a
242
+ record.instance_exec(&@values)
223
243
  end
224
244
  end
225
245
 
@@ -240,7 +260,7 @@ module AssignableValues
240
260
  delegate = delegate(record)
241
261
  delegate.present? or raise DelegateUnavailable, "Cannot query a nil delegate for assignable values"
242
262
  delegate_query_method = :"assignable_#{model.name.underscore.gsub('/', '_')}_#{property.to_s.pluralize}"
243
- args = delegate.method(delegate_query_method).arity == 1 ? [record] : []
263
+ args = delegate.method(delegate_query_method).arity == 0 ? [] : [record]
244
264
  delegate.send(delegate_query_method, *args)
245
265
  end
246
266
 
@@ -7,7 +7,7 @@ module AssignableValues
7
7
  super
8
8
  define_humanized_value_instance_method
9
9
  define_humanized_value_class_method
10
- define_humanized_values_instance_method
10
+ define_humanized_assignable_values_instance_method
11
11
  end
12
12
 
13
13
  def humanized_value(value)
@@ -17,8 +17,8 @@ module AssignableValues
17
17
  end
18
18
  end
19
19
 
20
- def humanized_values(record)
21
- values = assignable_values(record)
20
+ def humanized_assignable_values(record, options = {})
21
+ values = assignable_values(record, options)
22
22
  values.collect do |value|
23
23
  HumanizedValue.new(value, humanized_value(value))
24
24
  end
@@ -37,7 +37,7 @@ module AssignableValues
37
37
  def define_humanized_value_class_method
38
38
  restriction = self
39
39
  enhance_model_singleton do
40
- define_method :"humanized_#{restriction.property}" do |given_value|
40
+ define_method :"humanized_#{restriction.property.to_s.singularize}" do |given_value|
41
41
  restriction.humanized_value(given_value)
42
42
  end
43
43
  end
@@ -45,20 +45,45 @@ module AssignableValues
45
45
 
46
46
  def define_humanized_value_instance_method
47
47
  restriction = self
48
+ multiple = @options[:multiple]
48
49
  enhance_model do
49
- define_method :"humanized_#{restriction.property}" do |*args|
50
+ define_method :"humanized_#{restriction.property.to_s.singularize}" do |*args|
51
+ if args.size > 1 || (multiple && args.size == 0)
52
+ raise ArgumentError.new("wrong number of arguments (#{args.size} for #{multiple ? '1' : '0..1'})")
53
+ end
50
54
  given_value = args[0]
51
55
  value = given_value || send(restriction.property)
52
56
  restriction.humanized_value(value)
53
57
  end
58
+
59
+ if multiple
60
+ define_method :"humanized_#{restriction.property}" do
61
+ values = send(restriction.property)
62
+ if values.respond_to?(:map)
63
+ values.map do |value|
64
+ restriction.humanized_value(value)
65
+ end
66
+ else
67
+ values
68
+ end
69
+ end
70
+ end
54
71
  end
55
72
  end
56
73
 
57
- def define_humanized_values_instance_method
74
+ def define_humanized_assignable_values_instance_method
58
75
  restriction = self
76
+ multiple = @options[:multiple]
59
77
  enhance_model do
60
- define_method :"humanized_#{restriction.property.to_s.pluralize}" do
61
- restriction.humanized_values(self)
78
+ define_method :"humanized_assignable_#{restriction.property.to_s.pluralize}" do |*args|
79
+ restriction.humanized_assignable_values(self, *args)
80
+ end
81
+
82
+ unless multiple
83
+ define_method :"humanized_#{restriction.property.to_s.pluralize}" do
84
+ ActiveSupport::Deprecation.warn("humanized_<value>s is deprecated, use humanized_assignable_<value>s instead", caller)
85
+ restriction.humanized_assignable_values(self)
86
+ end
62
87
  end
63
88
  end
64
89
  end
@@ -8,7 +8,8 @@ class HumanizableString < String
8
8
  end
9
9
 
10
10
  def humanized
11
+ ActiveSupport::Deprecation.warn("assignable_<value>.humanized is deprecated, use humanized_assignable_<value>s.humanized instead", caller)
11
12
  @humanization
12
13
  end
13
14
 
14
- end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module AssignableValues
2
- VERSION = '0.14.0'
2
+ VERSION = '0.16.2'
3
3
  end
@@ -1,6 +1,14 @@
1
1
  require 'spec_helper'
2
2
  require 'ostruct'
3
3
 
4
+ def save_without_validation(record)
5
+ if ActiveRecord::VERSION::MAJOR < 3
6
+ record.save(false)
7
+ else
8
+ record.save(:validate => false)
9
+ end
10
+ end
11
+
4
12
  describe AssignableValues::ActiveRecord do
5
13
 
6
14
  describe '.assignable_values' do
@@ -17,24 +25,30 @@ describe AssignableValues::ActiveRecord do
17
25
 
18
26
  before :each do
19
27
  @klass = Song.disposable_copy do
20
- assignable_values_for :sub_genre do
28
+ assignable_values_for :virtual_sub_genre do
29
+ %w[pop rock]
30
+ end
31
+
32
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
21
33
  %w[pop rock]
22
34
  end
23
35
  end
24
36
  end
25
37
 
26
38
  it 'should validate that the attribute is allowed' do
27
- @klass.new(:sub_genre => 'pop').should be_valid
28
- @klass.new(:sub_genre => 'disallowed value').should_not be_valid
39
+ @klass.new(:virtual_sub_genre => 'pop').should be_valid
40
+ @klass.new(:virtual_sub_genre => 'disallowed value').should_not be_valid
29
41
  end
30
42
 
31
43
  it 'should not allow nil for the attribute value' do
32
- @klass.new(:sub_genre => nil).should_not be_valid
44
+ @klass.new(:virtual_sub_genre => nil).should_not be_valid
33
45
  end
34
46
 
35
47
  it 'should generate a method returning the humanized value' do
36
- song = @klass.new(:sub_genre => 'pop')
37
- song.humanized_sub_genre.should == 'Pop music'
48
+ song = @klass.new(:virtual_sub_genre => 'pop')
49
+ song.humanized_virtual_sub_genre.should == 'Pop music'
50
+ song.humanized_virtual_sub_genre('rock').should == 'Rock music'
51
+ song.humanized_virtual_multi_genre('rock').should == 'Rock music'
38
52
  end
39
53
 
40
54
  end
@@ -45,21 +59,21 @@ describe AssignableValues::ActiveRecord do
45
59
 
46
60
  before :each do
47
61
  @klass = Song.disposable_copy do
48
- assignable_values_for :sub_genres,:multiple => true do
62
+ assignable_values_for :virtual_multi_genres, :multiple => true do
49
63
  %w[pop rock]
50
64
  end
51
65
  end
52
66
  end
53
67
 
54
68
  it 'should validate that the attribute is a subset' do
55
- @klass.new(:sub_genres => ['pop']).should be_valid
56
- @klass.new(:sub_genres => ['pop', 'rock']).should be_valid
57
- @klass.new(:sub_genres => ['pop', 'disallowed value']).should_not be_valid
69
+ @klass.new(:virtual_multi_genres => ['pop']).should be_valid
70
+ @klass.new(:virtual_multi_genres => ['pop', 'rock']).should be_valid
71
+ @klass.new(:virtual_multi_genres => ['pop', 'disallowed value']).should_not be_valid
58
72
  end
59
73
 
60
74
  it 'should not allow nil or [] for allow_blank: false' do
61
- @klass.new(:sub_genres => nil).should_not be_valid
62
- @klass.new(:sub_genres => []).should_not be_valid
75
+ @klass.new(:virtual_multi_genres => nil).should_not be_valid
76
+ @klass.new(:virtual_multi_genres => []).should_not be_valid
63
77
  end
64
78
 
65
79
  end
@@ -68,15 +82,15 @@ describe AssignableValues::ActiveRecord do
68
82
 
69
83
  before :each do
70
84
  @klass = Song.disposable_copy do
71
- assignable_values_for :sub_genres, :multiple => true, :allow_blank => true do
85
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
72
86
  %w[pop rock]
73
87
  end
74
88
  end
75
89
  end
76
90
 
77
91
  it 'should allow nil or [] for allow_blank: false' do
78
- @klass.new(:sub_genres => nil).should be_valid
79
- @klass.new(:sub_genres => []).should be_valid
92
+ @klass.new(:virtual_multi_genres => nil).should be_valid
93
+ @klass.new(:virtual_multi_genres => []).should be_valid
80
94
  end
81
95
 
82
96
  end
@@ -92,6 +106,10 @@ describe AssignableValues::ActiveRecord do
92
106
  assignable_values_for :genre do
93
107
  %w[pop rock]
94
108
  end
109
+
110
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
111
+ %w[pop rock]
112
+ end
95
113
  end
96
114
  end
97
115
 
@@ -153,6 +171,31 @@ describe AssignableValues::ActiveRecord do
153
171
  @klass.humanized_genre('rock').should == 'Rock music'
154
172
  end
155
173
 
174
+ context 'for multiple: true' do
175
+ it 'should raise when trying to humanize a value without an argument' do
176
+ song = @klass.new
177
+ proc { song.humanized_virtual_multi_genre }.should raise_error(ArgumentError)
178
+ end
179
+
180
+ it 'should generate an instance method to retrieve the humanization of any given value' do
181
+ song = @klass.new(:genre => 'pop')
182
+ song.humanized_virtual_multi_genre('rock').should == 'Rock music'
183
+ end
184
+
185
+ it 'should generate a class method to retrieve the humanization of any given value' do
186
+ @klass.humanized_virtual_multi_genre('rock').should == 'Rock music'
187
+ end
188
+
189
+ it 'should generate an instance method to retrieve the humanizations of all current values' do
190
+ song = @klass.new
191
+ song.virtual_multi_genres = nil
192
+ song.humanized_virtual_multi_genres.should == nil
193
+ song.virtual_multi_genres = []
194
+ song.humanized_virtual_multi_genres.should == []
195
+ song.virtual_multi_genres = ['pop', 'rock']
196
+ song.humanized_virtual_multi_genres.should == ['Pop music', 'Rock music']
197
+ end
198
+ end
156
199
  end
157
200
 
158
201
  context 'if the :allow_blank option is set to true' do
@@ -220,11 +263,11 @@ describe AssignableValues::ActiveRecord do
220
263
  context 'if the :message option is set to a string' do
221
264
 
222
265
  before :each do
223
- @klass = Song.disposable_copy do
224
- assignable_values_for :genre, :message => 'should be something different' do
225
- %w[pop rock]
226
- end
227
- end
266
+ @klass = Song.disposable_copy do
267
+ assignable_values_for :genre, :message => 'should be something different' do
268
+ %w[pop rock]
269
+ end
270
+ end
228
271
  end
229
272
 
230
273
  it 'should use this string as a custom error message' do
@@ -245,43 +288,39 @@ describe AssignableValues::ActiveRecord do
245
288
 
246
289
  before :each do
247
290
  @klass = Song.disposable_copy do
248
- assignable_values_for :genres, :multiple => true do
291
+ assignable_values_for :multi_genres, :multiple => true do
249
292
  %w[pop rock]
250
293
  end
251
294
  end
252
295
  end
253
296
 
254
297
  it 'should validate that the attribute is allowed' do
255
- @klass.new(:genres => ['pop']).should be_valid
256
- @klass.new(:genres => ['pop', 'rock']).should be_valid
257
- @klass.new(:genres => ['pop', 'invalid value']).should_not be_valid
298
+ @klass.new(:multi_genres => ['pop']).should be_valid
299
+ @klass.new(:multi_genres => ['pop', 'rock']).should be_valid
300
+ @klass.new(:multi_genres => ['pop', 'invalid value']).should_not be_valid
258
301
  end
259
302
 
260
303
  it 'should not allow a scalar attribute' do
261
- @klass.new(:genres => 'pop').should_not be_valid
304
+ @klass.new(:multi_genres => 'pop').should_not be_valid
262
305
  end
263
306
 
264
307
  it 'should not allow nil or [] for the attribute value' do
265
- @klass.new(:genres => nil).should_not be_valid
266
- @klass.new(:genres => []).should_not be_valid
308
+ @klass.new(:multi_genres => nil).should_not be_valid
309
+ @klass.new(:multi_genres => []).should_not be_valid
267
310
  end
268
311
 
269
312
  it 'should allow a subset of previously saved values even if that value is no longer allowed' do
270
- record = @klass.create!(:genres => ['pop'])
271
- record.genres = ['pretend', 'previously', 'valid', 'value']
272
- if ActiveRecord::VERSION::MAJOR < 3
273
- record.save(false)
274
- else
275
- record.save(:validate => false) # update without validations for the sake of this test
276
- end
313
+ record = @klass.create!(:multi_genres => ['pop'])
314
+ record.multi_genres = ['pretend', 'previously', 'valid', 'value']
315
+ save_without_validation(record) # update without validation for the sake of this test
277
316
  record.reload.should be_valid
278
- record.genres = ['valid', 'previously', 'pop']
317
+ record.multi_genres = ['valid', 'previously', 'pop']
279
318
  record.should be_valid
280
319
  end
281
320
 
282
321
  it 'should allow a previously saved, blank value even if that value is no longer allowed' do
283
- record = @klass.create!(:genres => ['pop'])
284
- @klass.update_all(:genres => []) # update without validations for the sake of this test
322
+ record = @klass.create!(:multi_genres => ['pop'])
323
+ @klass.update_all(:multi_genres => []) # update without validations for the sake of this test
285
324
  record.reload.should be_valid
286
325
  end
287
326
 
@@ -291,18 +330,18 @@ describe AssignableValues::ActiveRecord do
291
330
 
292
331
  before :each do
293
332
  @klass = Song.disposable_copy do
294
- assignable_values_for :genres, :multiple => true, :allow_blank => true do
333
+ assignable_values_for :multi_genres, :multiple => true, :allow_blank => true do
295
334
  %w[pop rock]
296
335
  end
297
336
  end
298
337
  end
299
338
 
300
339
  it 'should allow nil for the attribute value' do
301
- @klass.new(:genres => nil).should be_valid
340
+ @klass.new(:multi_genres => nil).should be_valid
302
341
  end
303
342
 
304
343
  it 'should allow an empty array as value' do
305
- @klass.new(:genres => []).should be_valid
344
+ @klass.new(:multi_genres => []).should be_valid
306
345
  end
307
346
 
308
347
  end
@@ -409,7 +448,7 @@ describe AssignableValues::ActiveRecord do
409
448
  record.should be_valid
410
449
  end
411
450
 
412
- it 'sould not allow nil for an association (the "previously saved value") if the record is new' do
451
+ it 'should not allow nil for an association (the "previously saved value") if the record is new' do
413
452
  allowed_association = Artist.create!
414
453
  klass = Song.disposable_copy
415
454
  record = klass.new(:artist => nil)
@@ -471,6 +510,47 @@ describe AssignableValues::ActiveRecord do
471
510
  record.valid?
472
511
  end
473
512
 
513
+ it 'should not load all records to memory when assignable values are scoped' do
514
+ unless ::ActiveRecord::VERSION::MAJOR < 3 # somehow rails 2 still initializes Objects during the scope.exists?-call
515
+ initialized_artists_count = 0
516
+ MyArtist = Artist.disposable_copy
517
+
518
+ MyArtist.class_eval do
519
+ after_initialize :increase_initialized_count
520
+
521
+ define_method :increase_initialized_count do
522
+ initialized_artists_count += 1
523
+ end
524
+ end
525
+
526
+ klass = Song.disposable_copy
527
+
528
+ klass.class_eval do
529
+ assignable_values_for :artist do
530
+ if ::ActiveRecord::VERSION::MAJOR < 4
531
+ MyArtist.scoped
532
+ else
533
+ MyArtist.all
534
+ end
535
+ end
536
+ end
537
+
538
+ artist = MyArtist.create!
539
+ initialized_artists_count.should == 1
540
+
541
+ song = klass.new(:artist => artist)
542
+
543
+ song.valid?
544
+ initialized_artists_count.should == 1
545
+
546
+ song.assignable_artists
547
+ initialized_artists_count.should == 1
548
+
549
+ song.assignable_artists.to_a
550
+ initialized_artists_count.should == 2
551
+ end
552
+ end
553
+
474
554
  end
475
555
 
476
556
  context 'when delegating using the :through option' do
@@ -479,7 +559,11 @@ describe AssignableValues::ActiveRecord do
479
559
  klass = Song.disposable_copy do
480
560
  assignable_values_for :genre, :through => :delegate
481
561
  def delegate
482
- OpenStruct.new(:assignable_song_genres => %w[pop rock])
562
+ Class.new do
563
+ def assignable_song_genres
564
+ %w[pop rock]
565
+ end
566
+ end.new
483
567
  end
484
568
  end
485
569
  klass.new(:genre => 'pop').should be_valid
@@ -490,9 +574,14 @@ describe AssignableValues::ActiveRecord do
490
574
  klass = Song.disposable_copy do
491
575
  assignable_values_for :genre, :through => lambda { delegate }
492
576
  def delegate
493
- OpenStruct.new(:assignable_song_genres => %w[pop rock])
577
+ Class.new do
578
+ def assignable_song_genres
579
+ %w[pop rock]
580
+ end
581
+ end.new
494
582
  end
495
583
  end
584
+
496
585
  klass.new(:genre => 'pop').should be_valid
497
586
  klass.new(:genre => 'disallowed value').should_not be_valid
498
587
  end
@@ -501,7 +590,11 @@ describe AssignableValues::ActiveRecord do
501
590
  klass = Recording::Vinyl.disposable_copy do
502
591
  assignable_values_for :year, :through => :delegate
503
592
  def delegate
504
- OpenStruct.new(:assignable_recording_vinyl_years => [1977, 1980, 1983])
593
+ Class.new do
594
+ def assignable_recording_vinyl_years
595
+ [1977, 1980, 1983]
596
+ end
597
+ end.new
505
598
  end
506
599
  end
507
600
  klass.new.assignable_years.should == [1977, 1980, 1983]
@@ -678,7 +771,7 @@ describe AssignableValues::ActiveRecord do
678
771
  klass.new.assignable_genres.should == %w[pop rock]
679
772
  end
680
773
 
681
- it 'should call #to_a on the list of assignable values, allowing ranges and scopes to be passed as allowed value descriptors' do
774
+ it 'should work with ranges' do
682
775
  klass = Song.disposable_copy do
683
776
  assignable_values_for :year do
684
777
  1999..2001
@@ -704,10 +797,28 @@ describe AssignableValues::ActiveRecord do
704
797
  assignable_values_for :genre do
705
798
  %w[pop rock]
706
799
  end
800
+
801
+ assignable_values_for :multi_genres, :multiple => true do
802
+ %w[pop rock]
803
+ end
707
804
  end
708
- record = klass.create!(:genre => 'pop')
805
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
709
806
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
710
807
  record.reload.assignable_genres.should == %w[ballad pop rock]
808
+
809
+ humanized_genres = record.humanized_assignable_genres
810
+ humanized_genres.collect(&:value).should == %w[ballad pop rock]
811
+ humanized_genres.collect(&:humanized).should == ['Ballad', 'Pop music', 'Rock music']
812
+ humanized_genres.collect(&:to_s).should == ['Ballad', 'Pop music', 'Rock music']
813
+
814
+ record.multi_genres = %w[ballad classic]
815
+ save_without_validation(record) # update without validation for the sake of this test
816
+ record.reload.multi_genres.should == %w[ballad classic]
817
+
818
+ humanized_multi_genres = record.humanized_assignable_multi_genres
819
+ humanized_multi_genres.collect(&:value).should == %w[ballad classic pop rock]
820
+ humanized_multi_genres.collect(&:humanized).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
821
+ humanized_multi_genres.collect(&:to_s).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
711
822
  end
712
823
 
713
824
  it 'should not prepend a previously saved value to the top of the list if it is still allowed (bugfix)' do
@@ -747,10 +858,28 @@ describe AssignableValues::ActiveRecord do
747
858
  assignable_values_for :genre do
748
859
  %w[pop rock]
749
860
  end
861
+
862
+ assignable_values_for :multi_genres, :multiple => true do
863
+ %w[pop rock]
864
+ end
750
865
  end
751
- record = klass.create!(:genre => 'pop')
866
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
752
867
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
753
868
  record.reload.assignable_genres(:include_old_value => false).should == %w[pop rock]
869
+
870
+ humanized_genres = record.humanized_assignable_genres(:include_old_value => false)
871
+ humanized_genres.collect(&:value).should == %w[pop rock]
872
+ humanized_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
873
+ humanized_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
874
+
875
+ record.multi_genres = %w[ballad classic]
876
+ save_without_validation(record) # update without validation for the sake of this test
877
+ record.reload.multi_genres.should == %w[ballad classic]
878
+
879
+ humanized_multi_genres = record.humanized_assignable_multi_genres(:include_old_value => false)
880
+ humanized_multi_genres.collect(&:value).should == %w[pop rock]
881
+ humanized_multi_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
882
+ humanized_multi_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
754
883
  end
755
884
 
756
885
  it 'should allow omitting a previously saved association' do
@@ -791,11 +920,18 @@ describe AssignableValues::ActiveRecord do
791
920
  assignable_values_for :genre do
792
921
  %w[pop rock]
793
922
  end
923
+
924
+ assignable_values_for :multi_genres, :multiple => true do
925
+ %w[pop rock]
926
+ end
794
927
  end
795
- genres = klass.new.humanized_genres
928
+ genres = klass.new.humanized_assignable_genres
796
929
  genres.collect(&:value).should == ['pop', 'rock']
797
930
  genres.collect(&:humanized).should == ['Pop music', 'Rock music']
798
931
  genres.collect(&:to_s).should == ['Pop music', 'Rock music']
932
+
933
+ multi_genres = klass.new.humanized_assignable_multi_genres
934
+ multi_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
799
935
  end
800
936
 
801
937
  it 'should use String#humanize as a default translation' do
@@ -804,7 +940,7 @@ describe AssignableValues::ActiveRecord do
804
940
  %w[electronic]
805
941
  end
806
942
  end
807
- klass.new.humanized_genres.collect(&:humanized).should == ['Electronic']
943
+ klass.new.humanized_assignable_genres.collect(&:humanized).should == ['Electronic']
808
944
  end
809
945
 
810
946
  it 'should allow to define humanizations for values that are not strings' do
@@ -813,7 +949,7 @@ describe AssignableValues::ActiveRecord do
813
949
  [1977, 1980, 1983]
814
950
  end
815
951
  end
816
- years = klass.new.humanized_years
952
+ years = klass.new.humanized_assignable_years
817
953
  years.collect(&:value).should == [1977, 1980, 1983]
818
954
  years.collect(&:humanized).should == ['The year a new hope was born', 'The year the Empire stroke back', 'The year the Jedi returned']
819
955
  end
@@ -824,18 +960,30 @@ describe AssignableValues::ActiveRecord do
824
960
  [1977, 1980, 1983]
825
961
  end
826
962
  end
827
- years = klass.new.humanized_years
963
+ years = klass.new.humanized_assignable_years
828
964
  years.collect(&:humanized).should == ['The year a new hope was born', 'The year the Empire stroke back', 'The year the Jedi returned']
829
965
  end
830
966
 
831
967
  context 'legacy methods for API compatibility' do
832
968
 
969
+ it 'should define a method that return pairs of values and their humanization' do
970
+ klass = Song.disposable_copy do
971
+ assignable_values_for :genre do
972
+ %w[pop rock]
973
+ end
974
+ end
975
+ ActiveSupport::Deprecation.should_receive(:warn)
976
+ genres = klass.new.humanized_genres
977
+ genres.collect(&:humanized).should == ['Pop music', 'Rock music']
978
+ end
979
+
833
980
  it "should define a method #humanized on assignable string values, which return up the value's' translation" do
834
981
  klass = Song.disposable_copy do
835
982
  assignable_values_for :genre do
836
983
  %w[pop rock]
837
984
  end
838
985
  end
986
+ ActiveSupport::Deprecation.should_receive(:warn).at_least(:once)
839
987
  klass.new.assignable_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
840
988
  end
841
989
 
@@ -872,7 +1020,7 @@ describe AssignableValues::ActiveRecord do
872
1020
  delegate = Object.new
873
1021
  def delegate.assignable_song_genres(record)
874
1022
  record_received(record)
875
- %w[pop rock]
1023
+ %w[pop rock]
876
1024
  end
877
1025
  klass = Song.disposable_copy do
878
1026
  assignable_values_for :genre, :through => :delegate
@@ -885,6 +1033,55 @@ describe AssignableValues::ActiveRecord do
885
1033
  record.assignable_genres.should == %w[pop rock]
886
1034
  end
887
1035
 
1036
+ it "should call the given method on the delegate if the delegate's query method takes no arguments" do
1037
+ delegate = Object.new
1038
+ def delegate.assignable_song_genres
1039
+ no_record_received
1040
+ %w[pop rock]
1041
+ end
1042
+ klass = Song.disposable_copy do
1043
+ assignable_values_for :genre, :through => :delegate
1044
+ define_method :delegate do
1045
+ delegate
1046
+ end
1047
+ end
1048
+ record = klass.new
1049
+ delegate.should_receive(:no_record_received)
1050
+ record.assignable_genres.should == %w[pop rock]
1051
+ end
1052
+
1053
+ it "should pass the record to the given method if the delegate's query method takes variable arguments" do
1054
+ delegate = Object.new
1055
+ def delegate.assignable_song_genres(*records)
1056
+ record_received(*records)
1057
+ %w[pop rock]
1058
+ end
1059
+ klass = Song.disposable_copy do
1060
+ assignable_values_for :genre, :through => :delegate
1061
+ define_method :delegate do
1062
+ delegate
1063
+ end
1064
+ end
1065
+ record = klass.new
1066
+ delegate.should_receive(:record_received).with(record)
1067
+ record.assignable_genres.should == %w[pop rock]
1068
+ end
1069
+
1070
+ it "should raise an error if the delegate's query method takes more than one argument" do
1071
+ delegate = Object.new
1072
+ def delegate.assignable_song_genres(record, other_arg)
1073
+ %w[pop rock]
1074
+ end
1075
+ klass = Song.disposable_copy do
1076
+ assignable_values_for :genre, :through => :delegate
1077
+ define_method :delegate do
1078
+ delegate
1079
+ end
1080
+ end
1081
+ record = klass.new
1082
+ expect{record.assignable_genres}.to raise_error(ArgumentError)
1083
+ end
1084
+
888
1085
  it 'should raise an error if the given method returns nil' do
889
1086
  klass = Song.disposable_copy do
890
1087
  assignable_values_for :genre, :through => :delegate
@@ -894,11 +1091,7 @@ describe AssignableValues::ActiveRecord do
894
1091
  end
895
1092
  expect { klass.new.assignable_genres }.to raise_error(AssignableValues::DelegateUnavailable)
896
1093
  end
897
-
898
1094
  end
899
-
900
1095
  end
901
-
902
1096
  end
903
-
904
1097
  end