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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +104 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +42 -1
- data/Gemfile.4.2 +2 -2
- data/Gemfile.4.2.lock +6 -6
- data/Gemfile.5.0 +1 -1
- data/Gemfile.5.0.lock +34 -33
- data/Gemfile.5.1 +1 -1
- data/Gemfile.5.1.lock +32 -32
- data/Gemfile.5.1.pg +1 -1
- data/Gemfile.5.1.pg.lock +4 -4
- data/{Gemfile.3.2 → Gemfile.6.0.pg} +6 -6
- data/Gemfile.6.0.pg.lock +68 -0
- data/README.md +13 -10
- data/lib/assignable_values/active_record/restriction/base.rb +36 -10
- data/lib/assignable_values/active_record/restriction/scalar_attribute.rb +4 -4
- data/lib/assignable_values/version.rb +1 -1
- data/spec/assignable_values/active_record_spec.rb +179 -52
- data/spec/support/database.github.yml +13 -0
- data/spec/support/database.rb +1 -1
- data/spec/support/i18n.yml +4 -1
- data/spec/support/models.rb +3 -3
- metadata +8 -11
- data/.travis.yml +0 -64
- data/Gemfile.2.3 +0 -16
- data/Gemfile.2.3.lock +0 -40
- data/Gemfile.3.2.lock +0 -65
- data/spec/support/database.travis.yml +0 -4
data/Gemfile.5.1.pg
CHANGED
data/Gemfile.5.1.pg.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
assignable_values (0.
|
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.
|
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.
|
68
|
+
2.1.4
|
@@ -1,16 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Runtime dependencies
|
4
|
-
gem 'activerecord', '~>
|
5
|
-
gem 'i18n'
|
6
|
-
gem '
|
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'
|
11
|
-
gem 'rspec'
|
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 => '.'
|
data/Gemfile.6.0.pg.lock
ADDED
@@ -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 [![
|
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
|
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 :
|
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
|
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.
|
353
|
-
- Create a local test database `assignable_values_test` in both MySQL and PostgreSQL
|
354
|
-
|
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 `.
|
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
|
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
|
-
|
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
|
-
|
58
|
+
additional_assignable_values = old_value
|
59
59
|
end
|
60
60
|
elsif !old_value.blank? && !current_values.include?(old_value)
|
61
|
-
|
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
|
-
|
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
|
-
|
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)
|
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
|
-
|
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,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 :
|
28
|
+
assignable_values_for :virtual_sub_genre do
|
21
29
|
%w[pop rock]
|
22
30
|
end
|
23
31
|
|
24
|
-
assignable_values_for :
|
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(:
|
32
|
-
@klass.new(:
|
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(:
|
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(:
|
41
|
-
song.
|
42
|
-
song.
|
43
|
-
song.
|
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 :
|
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(:
|
62
|
-
@klass.new(:
|
63
|
-
@klass.new(:
|
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(:
|
68
|
-
@klass.new(:
|
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 :
|
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(:
|
85
|
-
@klass.new(:
|
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 :
|
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.
|
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.
|
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.
|
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.
|
184
|
-
song.
|
185
|
-
song.
|
186
|
-
song.
|
187
|
-
song.
|
188
|
-
song.
|
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 :
|
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(:
|
291
|
-
@klass.new(:
|
292
|
-
@klass.new(:
|
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(:
|
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(:
|
301
|
-
@klass.new(:
|
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!(:
|
306
|
-
record.
|
307
|
-
|
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.
|
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!(:
|
319
|
-
@klass.update_all(:
|
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 :
|
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(:
|
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(:
|
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 '
|
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
|
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
|