assignable_values 0.16.0 → 0.16.5

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.5.1.pg CHANGED
@@ -10,7 +10,7 @@ gem 'rake'
10
10
  gem 'database_cleaner'
11
11
  gem 'rspec'
12
12
  gem 'rspec_candy'
13
- gem 'gemika'
13
+ gem 'gemika', '>= 0.5.0'
14
14
 
15
15
  # Gem under test
16
16
  gem 'assignable_values', :path => '.'
data/Gemfile.5.1.pg.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- assignable_values (0.15.1)
4
+ assignable_values (0.16.5)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -22,7 +22,7 @@ GEM
22
22
  concurrent-ruby (1.0.5)
23
23
  database_cleaner (1.6.1)
24
24
  diff-lcs (1.3)
25
- gemika (0.3.2)
25
+ gemika (0.5.0)
26
26
  i18n (0.9.0)
27
27
  concurrent-ruby (~> 1.0)
28
28
  minitest (5.10.3)
@@ -57,7 +57,7 @@ DEPENDENCIES
57
57
  activerecord (~> 5.1.0)
58
58
  assignable_values!
59
59
  database_cleaner
60
- gemika
60
+ gemika (>= 0.5.0)
61
61
  i18n
62
62
  pg (< 1)
63
63
  rake
@@ -65,4 +65,4 @@ DEPENDENCIES
65
65
  rspec_candy
66
66
 
67
67
  BUNDLED WITH
68
- 1.16.4
68
+ 2.1.4
@@ -1,16 +1,16 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Runtime dependencies
4
- gem 'activerecord', '~>3.2.0'
5
- gem 'i18n', '<0.7'
6
- gem 'mysql2', '= 0.3.17'
4
+ gem 'activerecord', '~>6.0.0'
5
+ gem 'i18n'
6
+ gem 'pg', '<1'
7
7
 
8
8
  # Development dependencies
9
9
  gem 'rake'
10
- gem 'database_cleaner', '~> 1.0.0'
11
- gem 'rspec', '~> 3.4'
10
+ gem 'database_cleaner'
11
+ gem 'rspec'
12
12
  gem 'rspec_candy'
13
- gem 'gemika'
13
+ gem 'gemika', '>= 0.5.0'
14
14
 
15
15
  # Gem under test
16
16
  gem 'assignable_values', :path => '.'
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ assignable_values (0.16.4)
5
+ activerecord (>= 2.3)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (6.0.0)
11
+ activesupport (= 6.0.0)
12
+ activerecord (6.0.0)
13
+ activemodel (= 6.0.0)
14
+ activesupport (= 6.0.0)
15
+ activesupport (6.0.0)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 0.7, < 2)
18
+ minitest (~> 5.1)
19
+ tzinfo (~> 1.1)
20
+ zeitwerk (~> 2.1, >= 2.1.8)
21
+ concurrent-ruby (1.1.5)
22
+ database_cleaner (1.7.0)
23
+ diff-lcs (1.3)
24
+ gemika (0.3.4)
25
+ i18n (1.6.0)
26
+ concurrent-ruby (~> 1.0)
27
+ minitest (5.11.3)
28
+ pg (0.21.0)
29
+ rake (12.3.2)
30
+ rspec (3.8.0)
31
+ rspec-core (~> 3.8.0)
32
+ rspec-expectations (~> 3.8.0)
33
+ rspec-mocks (~> 3.8.0)
34
+ rspec-core (3.8.0)
35
+ rspec-support (~> 3.8.0)
36
+ rspec-expectations (3.8.2)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.8.0)
39
+ rspec-mocks (3.8.0)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.8.0)
42
+ rspec-support (3.8.0)
43
+ rspec_candy (0.5.1)
44
+ rspec
45
+ sneaky-save
46
+ sneaky-save (0.1.3)
47
+ activerecord (>= 3.2.0)
48
+ thread_safe (0.3.6)
49
+ tzinfo (1.2.5)
50
+ thread_safe (~> 0.1)
51
+ zeitwerk (2.1.9)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ activerecord (~> 6.0.0)
58
+ assignable_values!
59
+ database_cleaner
60
+ gemika
61
+ i18n
62
+ pg (< 1)
63
+ rake
64
+ rspec
65
+ rspec_candy
66
+
67
+ BUNDLED WITH
68
+ 1.17.3
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
- assignable_values - Enums on vitamins [![Build Status](https://secure.travis-ci.org/makandra/assignable_values.png?branch=master)](https://travis-ci.org/makandra/assignable_values)
1
+ assignable_values - Enums on vitamins [![Tests](https://github.com/makandra/assignable_values/workflows/Tests/badge.svg)](https://github.com/makandra/assignable_values/actions)
2
2
  =====================================
3
3
 
4
4
  `assignable_values` lets you restrict the values that can be assigned to attributes or associations of ActiveRecord models. You can think of it as enums where the list of allowed values is generated at runtime and the value is checked during validation.
5
5
 
6
6
  We carefully enhanced the core enum functionality with small tweaks that are useful for web forms, internationalized applications and common authorization patterns.
7
7
 
8
- `assignable_values` is tested with Rails 2.3, 3.2, 4.2 and 5.0 on Ruby 1.8.7, 2.1 and 2.3.
8
+ `assignable_values` is tested with Rails 2.3, 3.2, 4.2, 5.1 and 6.0 (beta) on Ruby 1.8.7, 2.1, 2.3, 2.4 and 2.5.
9
9
 
10
10
 
11
11
  Restricting scalar attributes
@@ -91,7 +91,7 @@ The default is applied to new records:
91
91
  Defaults can be procs:
92
92
 
93
93
  class Song < ActiveRecord::Base
94
- assignable_values_for :genre, default: proc { Date.today.year } do
94
+ assignable_values_for :year, default: proc { Date.today.year } do
95
95
  1980 .. 2011
96
96
  end
97
97
  end
@@ -205,7 +205,7 @@ You can restrict `belongs_to` associations in the same manner as scalar attribut
205
205
 
206
206
  end
207
207
 
208
- Listing and validating als works the same:
208
+ Listing and validating also works the same:
209
209
 
210
210
  chicane = Artist.create!(name: 'Chicane', signed: true)
211
211
  lt2 = Artist.create!(name: 'LT2', signed: false)
@@ -349,18 +349,21 @@ Development
349
349
 
350
350
  There are tests in `spec`. We only accept PRs with tests. To run tests:
351
351
 
352
- - Install Ruby 2.1.8
353
- - Create a local test database `assignable_values_test` in both MySQL and PostgreSQL
354
- - Copy `spec/support/database.sample.yml` to `spec/support/database.yml` and enter your local credentials for the test database
352
+ - Install Ruby 2.3.8
353
+ - Create a local test database `assignable_values_test` in both MySQL and PostgreSQL (see
354
+ `.github/workflows/test.yml` for commands to do so)
355
+ - Copy `spec/support/database.sample.yml` to `spec/support/database.yml` and enter your local
356
+ credentials for the test databases (postgres entry is not required if you are using a socket
357
+ connection)
355
358
  - Install development dependencies using `bundle install`
356
- - Run tests using `bundle exec current_rspec`
359
+ - Run tests using `bundle exec rake current_rspec`
357
360
 
358
- We recommend to test large changes against multiple versions of Ruby and multiple dependency sets. Supported combinations are configured in `.travis.yml`. We provide some rake tasks to help with this:
361
+ We recommend to test large changes against multiple versions of Ruby and multiple dependency sets. Supported combinations are configured in `.github/workflows/test.yml`. We provide some rake tasks to help with this:
359
362
 
360
363
  - Install development dependencies using `bundle exec rake matrix:install`
361
364
  - Run tests using `bundle exec rake matrix:spec`
362
365
 
363
- Note that we have configured Travis CI to automatically run tests in all supported Ruby versions and dependency sets after each push. We will only merge pull requests after a green Travis build.
366
+ Note that we have configured GitHub Actions to automatically run tests in all supported Ruby versions and dependency sets after each push. We will only merge pull requests after a green GitHub Actions run.
364
367
 
365
368
  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).
366
369
 
@@ -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) unless value.nil?
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)
@@ -216,10 +236,16 @@ module AssignableValues
216
236
  end
217
237
 
218
238
  def assignable_values_from_record_or_delegate(record)
219
- if delegate?
220
- assignable_values_from_delegate(record).to_a
239
+ assignable_values = if delegate?
240
+ assignable_values_from_delegate(record)
241
+ else
242
+ record.instance_exec(&@values)
243
+ end
244
+
245
+ if is_scope?(assignable_values)
246
+ assignable_values
221
247
  else
222
- record.instance_exec(&@values).to_a
248
+ Array(assignable_values)
223
249
  end
224
250
  end
225
251
 
@@ -17,8 +17,8 @@ module AssignableValues
17
17
  end
18
18
  end
19
19
 
20
- def humanized_assignable_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
@@ -75,8 +75,8 @@ module AssignableValues
75
75
  restriction = self
76
76
  multiple = @options[:multiple]
77
77
  enhance_model do
78
- define_method :"humanized_assignable_#{restriction.property.to_s.pluralize}" do
79
- restriction.humanized_assignable_values(self)
78
+ define_method :"humanized_assignable_#{restriction.property.to_s.pluralize}" do |*args|
79
+ restriction.humanized_assignable_values(self, *args)
80
80
  end
81
81
 
82
82
  unless multiple
@@ -1,3 +1,3 @@
1
1
  module AssignableValues
2
- VERSION = '0.16.0'
2
+ VERSION = '0.16.5'
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,30 +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
21
29
  %w[pop rock]
22
30
  end
23
31
 
24
- assignable_values_for :multi_genres, :multiple => true, :allow_blank => true do
32
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
25
33
  %w[pop rock]
26
34
  end
27
35
  end
28
36
  end
29
37
 
30
38
  it 'should validate that the attribute is allowed' do
31
- @klass.new(:sub_genre => 'pop').should be_valid
32
- @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
33
41
  end
34
42
 
35
43
  it 'should not allow nil for the attribute value' do
36
- @klass.new(:sub_genre => nil).should_not be_valid
44
+ @klass.new(:virtual_sub_genre => nil).should_not be_valid
37
45
  end
38
46
 
39
47
  it 'should generate a method returning the humanized value' do
40
- song = @klass.new(:sub_genre => 'pop')
41
- song.humanized_sub_genre.should == 'Pop music'
42
- song.humanized_sub_genre('rock').should == 'Rock music'
43
- song.humanized_multi_genre('rock').should == 'Rock 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'
44
52
  end
45
53
 
46
54
  end
@@ -51,21 +59,21 @@ describe AssignableValues::ActiveRecord do
51
59
 
52
60
  before :each do
53
61
  @klass = Song.disposable_copy do
54
- assignable_values_for :multi_genres, :multiple => true do
62
+ assignable_values_for :virtual_multi_genres, :multiple => true do
55
63
  %w[pop rock]
56
64
  end
57
65
  end
58
66
  end
59
67
 
60
68
  it 'should validate that the attribute is a subset' do
61
- @klass.new(:multi_genres => ['pop']).should be_valid
62
- @klass.new(:multi_genres => ['pop', 'rock']).should be_valid
63
- @klass.new(:multi_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
64
72
  end
65
73
 
66
74
  it 'should not allow nil or [] for allow_blank: false' do
67
- @klass.new(:multi_genres => nil).should_not be_valid
68
- @klass.new(:multi_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
69
77
  end
70
78
 
71
79
  end
@@ -74,15 +82,15 @@ describe AssignableValues::ActiveRecord do
74
82
 
75
83
  before :each do
76
84
  @klass = Song.disposable_copy do
77
- assignable_values_for :multi_genres, :multiple => true, :allow_blank => true do
85
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
78
86
  %w[pop rock]
79
87
  end
80
88
  end
81
89
  end
82
90
 
83
91
  it 'should allow nil or [] for allow_blank: false' do
84
- @klass.new(:multi_genres => nil).should be_valid
85
- @klass.new(:multi_genres => []).should be_valid
92
+ @klass.new(:virtual_multi_genres => nil).should be_valid
93
+ @klass.new(:virtual_multi_genres => []).should be_valid
86
94
  end
87
95
 
88
96
  end
@@ -99,7 +107,7 @@ describe AssignableValues::ActiveRecord do
99
107
  %w[pop rock]
100
108
  end
101
109
 
102
- assignable_values_for :multi_genres, :multiple => true, :allow_blank => true do
110
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
103
111
  %w[pop rock]
104
112
  end
105
113
  end
@@ -166,26 +174,26 @@ describe AssignableValues::ActiveRecord do
166
174
  context 'for multiple: true' do
167
175
  it 'should raise when trying to humanize a value without an argument' do
168
176
  song = @klass.new
169
- proc { song.humanized_multi_genre }.should raise_error(ArgumentError)
177
+ proc { song.humanized_virtual_multi_genre }.should raise_error(ArgumentError)
170
178
  end
171
179
 
172
180
  it 'should generate an instance method to retrieve the humanization of any given value' do
173
181
  song = @klass.new(:genre => 'pop')
174
- song.humanized_multi_genre('rock').should == 'Rock music'
182
+ song.humanized_virtual_multi_genre('rock').should == 'Rock music'
175
183
  end
176
184
 
177
185
  it 'should generate a class method to retrieve the humanization of any given value' do
178
- @klass.humanized_multi_genre('rock').should == 'Rock music'
186
+ @klass.humanized_virtual_multi_genre('rock').should == 'Rock music'
179
187
  end
180
188
 
181
189
  it 'should generate an instance method to retrieve the humanizations of all current values' do
182
190
  song = @klass.new
183
- song.multi_genres = nil
184
- song.humanized_multi_genres.should == nil
185
- song.multi_genres = []
186
- song.humanized_multi_genres.should == []
187
- song.multi_genres = ['pop', 'rock']
188
- song.humanized_multi_genres.should == ['Pop music', 'Rock music']
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']
189
197
  end
190
198
  end
191
199
  end
@@ -280,43 +288,39 @@ describe AssignableValues::ActiveRecord do
280
288
 
281
289
  before :each do
282
290
  @klass = Song.disposable_copy do
283
- assignable_values_for :genres, :multiple => true do
291
+ assignable_values_for :multi_genres, :multiple => true do
284
292
  %w[pop rock]
285
293
  end
286
294
  end
287
295
  end
288
296
 
289
297
  it 'should validate that the attribute is allowed' do
290
- @klass.new(:genres => ['pop']).should be_valid
291
- @klass.new(:genres => ['pop', 'rock']).should be_valid
292
- @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
293
301
  end
294
302
 
295
303
  it 'should not allow a scalar attribute' do
296
- @klass.new(:genres => 'pop').should_not be_valid
304
+ @klass.new(:multi_genres => 'pop').should_not be_valid
297
305
  end
298
306
 
299
307
  it 'should not allow nil or [] for the attribute value' do
300
- @klass.new(:genres => nil).should_not be_valid
301
- @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
302
310
  end
303
311
 
304
312
  it 'should allow a subset of previously saved values even if that value is no longer allowed' do
305
- record = @klass.create!(:genres => ['pop'])
306
- record.genres = ['pretend', 'previously', 'valid', 'value']
307
- if ActiveRecord::VERSION::MAJOR < 3
308
- record.save(false)
309
- else
310
- record.save(:validate => false) # update without validations for the sake of this test
311
- 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
312
316
  record.reload.should be_valid
313
- record.genres = ['valid', 'previously', 'pop']
317
+ record.multi_genres = ['valid', 'previously', 'pop']
314
318
  record.should be_valid
315
319
  end
316
320
 
317
321
  it 'should allow a previously saved, blank value even if that value is no longer allowed' do
318
- record = @klass.create!(:genres => ['pop'])
319
- @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
320
324
  record.reload.should be_valid
321
325
  end
322
326
 
@@ -326,18 +330,18 @@ describe AssignableValues::ActiveRecord do
326
330
 
327
331
  before :each do
328
332
  @klass = Song.disposable_copy do
329
- assignable_values_for :genres, :multiple => true, :allow_blank => true do
333
+ assignable_values_for :multi_genres, :multiple => true, :allow_blank => true do
330
334
  %w[pop rock]
331
335
  end
332
336
  end
333
337
  end
334
338
 
335
339
  it 'should allow nil for the attribute value' do
336
- @klass.new(:genres => nil).should be_valid
340
+ @klass.new(:multi_genres => nil).should be_valid
337
341
  end
338
342
 
339
343
  it 'should allow an empty array as value' do
340
- @klass.new(:genres => []).should be_valid
344
+ @klass.new(:multi_genres => []).should be_valid
341
345
  end
342
346
 
343
347
  end
@@ -444,7 +448,7 @@ describe AssignableValues::ActiveRecord do
444
448
  record.should be_valid
445
449
  end
446
450
 
447
- 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
448
452
  allowed_association = Artist.create!
449
453
  klass = Song.disposable_copy
450
454
  record = klass.new(:artist => nil)
@@ -506,6 +510,63 @@ describe AssignableValues::ActiveRecord do
506
510
  record.valid?
507
511
  end
508
512
 
513
+ context 'when assignable values are provided as an ActiveRecord scope' do
514
+ MyArtist = Artist.disposable_copy
515
+ let(:klass) { Song.disposable_copy }
516
+
517
+ before do
518
+ klass.class_eval do
519
+ assignable_values_for :artist do
520
+ if ::ActiveRecord::VERSION::MAJOR < 4
521
+ MyArtist.scoped({})
522
+ else
523
+ MyArtist.all
524
+ end
525
+ end
526
+ end
527
+ end
528
+
529
+ it 'allows assigning a record from the scope' do
530
+ artist = MyArtist.create!
531
+ song = klass.new(:artist => artist)
532
+ song.should be_valid
533
+ end
534
+
535
+ it 'will not crash during validation when the assigned value is nil' do
536
+ song = klass.new
537
+ expect { song.valid? }.to_not raise_error
538
+ song.errors[:artist_id].should be_present
539
+ end
540
+
541
+ it 'should not load all records into memory' do
542
+ unless ::ActiveRecord::VERSION::MAJOR < 3 # somehow rails 2 still initializes Objects during the scope.exists?-call
543
+ initialized_artists_count = 0
544
+
545
+ MyArtist.class_eval do
546
+ after_initialize :increase_initialized_count
547
+
548
+ define_method :increase_initialized_count do
549
+ initialized_artists_count += 1
550
+ end
551
+ end
552
+
553
+ artist = MyArtist.create!
554
+ initialized_artists_count.should == 1
555
+
556
+ song = klass.new(:artist => artist)
557
+
558
+ song.valid?
559
+ initialized_artists_count.should == 1
560
+
561
+ song.assignable_artists
562
+ initialized_artists_count.should == 1
563
+
564
+ song.assignable_artists.to_a
565
+ initialized_artists_count.should == 2
566
+ end
567
+ end
568
+ end
569
+
509
570
  end
510
571
 
511
572
  context 'when delegating using the :through option' do
@@ -726,7 +787,7 @@ describe AssignableValues::ActiveRecord do
726
787
  klass.new.assignable_genres.should == %w[pop rock]
727
788
  end
728
789
 
729
- it 'should call #to_a on the list of assignable values, allowing ranges and scopes to be passed as allowed value descriptors' do
790
+ it 'should work with ranges' do
730
791
  klass = Song.disposable_copy do
731
792
  assignable_values_for :year do
732
793
  1999..2001
@@ -747,15 +808,63 @@ describe AssignableValues::ActiveRecord do
747
808
  klass.new.assignable_genres.should == %w[pop rock]
748
809
  end
749
810
 
811
+ it 'returns an array when the value block returns a single value' do
812
+ klass = Song.disposable_copy do
813
+ assignable_values_for :genre do
814
+ 'techno'
815
+ end
816
+ end
817
+
818
+ klass.new.assignable_genres.should == ['techno']
819
+ end
820
+
821
+ it 'returns an empty array when the value block returns nothing' do
822
+ klass = Song.disposable_copy do
823
+ assignable_values_for :genre do
824
+ nil
825
+ end
826
+ end
827
+
828
+ klass.new.assignable_genres.should == []
829
+ end
830
+
831
+ it 'should not return saved value twice (BUGFIX)' do
832
+ klass = Song.disposable_copy do
833
+ assignable_values_for :multi_genres, :multiple => true do
834
+ %w[pop rock]
835
+ end
836
+ end
837
+ record = klass.create!(:multi_genres => ['pop'])
838
+ record.assignable_multi_genres.should eq ['pop', 'rock']
839
+ end
840
+
750
841
  it 'should prepend a previously saved value to the top of the list, even if is no longer allowed' do
751
842
  klass = Song.disposable_copy do
752
843
  assignable_values_for :genre do
753
844
  %w[pop rock]
754
845
  end
846
+
847
+ assignable_values_for :multi_genres, :multiple => true do
848
+ %w[pop rock]
849
+ end
755
850
  end
756
- record = klass.create!(:genre => 'pop')
851
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
757
852
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
758
853
  record.reload.assignable_genres.should == %w[ballad pop rock]
854
+
855
+ humanized_genres = record.humanized_assignable_genres
856
+ humanized_genres.collect(&:value).should == %w[ballad pop rock]
857
+ humanized_genres.collect(&:humanized).should == ['Ballad', 'Pop music', 'Rock music']
858
+ humanized_genres.collect(&:to_s).should == ['Ballad', 'Pop music', 'Rock music']
859
+
860
+ record.multi_genres = %w[ballad classic]
861
+ save_without_validation(record) # update without validation for the sake of this test
862
+ record.reload.multi_genres.should == %w[ballad classic]
863
+
864
+ humanized_multi_genres = record.humanized_assignable_multi_genres
865
+ humanized_multi_genres.collect(&:value).should == %w[ballad classic pop rock]
866
+ humanized_multi_genres.collect(&:humanized).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
867
+ humanized_multi_genres.collect(&:to_s).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
759
868
  end
760
869
 
761
870
  it 'should not prepend a previously saved value to the top of the list if it is still allowed (bugfix)' do
@@ -795,10 +904,28 @@ describe AssignableValues::ActiveRecord do
795
904
  assignable_values_for :genre do
796
905
  %w[pop rock]
797
906
  end
907
+
908
+ assignable_values_for :multi_genres, :multiple => true do
909
+ %w[pop rock]
910
+ end
798
911
  end
799
- record = klass.create!(:genre => 'pop')
912
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
800
913
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
801
914
  record.reload.assignable_genres(:include_old_value => false).should == %w[pop rock]
915
+
916
+ humanized_genres = record.humanized_assignable_genres(:include_old_value => false)
917
+ humanized_genres.collect(&:value).should == %w[pop rock]
918
+ humanized_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
919
+ humanized_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
920
+
921
+ record.multi_genres = %w[ballad classic]
922
+ save_without_validation(record) # update without validation for the sake of this test
923
+ record.reload.multi_genres.should == %w[ballad classic]
924
+
925
+ humanized_multi_genres = record.humanized_assignable_multi_genres(:include_old_value => false)
926
+ humanized_multi_genres.collect(&:value).should == %w[pop rock]
927
+ humanized_multi_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
928
+ humanized_multi_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
802
929
  end
803
930
 
804
931
  it 'should allow omitting a previously saved association' do