assignable_values 0.15.0 → 0.16.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0359f423877ba1c7d9b303053434f7d80077da0cf314a964b6577f31b97fd01
4
- data.tar.gz: e3818b1b35b173d4db2a896092a7cf0ee7f8d93381631cd1cfbbf62f64fc6783
3
+ metadata.gz: 400d27572075ed4ae3361303c97ccb7acf0afb00e63cfdc8e3a667d8da73b874
4
+ data.tar.gz: 20e2628282f45a9265d036a0b3ca1078a5bb5532cf06654d05c79bd67c878d96
5
5
  SHA512:
6
- metadata.gz: 479c5751e8086031feadcc70a06b878d04020f5fc023b69afd790c2a684f219d56e6d44abfa31387aa342a5c08b4ecfdcea6da085bc9060685ac04750fef3fba
7
- data.tar.gz: c38e04dc6003f5f3ab055eeb2b4a9183351c4a7101da42750bd381263e190cdbca0e9638c5e44a88e6510f6aa741eac1612285b1bbfb5347dbb599fdefa95388
6
+ metadata.gz: 0d8d5c406b2829863c62fe04851341667ce4f49cc2e6f3c29e6e58cc4710fe70d123ac7cbc315f56f8fdd1734dcab7c5b9693186bd9b12033db3df03fd686261
7
+ data.tar.gz: d0e460d55fa4ce7d26207a239778177f56a4c1dcd73a8f7ee7aa6167094b55b75af84a0a8a9cc9e9e3a371c693b4fb813df16978057e545b4dbaa50415926668
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.3.8
@@ -1,63 +1,80 @@
1
1
  language: ruby
2
2
 
3
+ dist: trusty
4
+
3
5
  rvm:
4
6
  - 1.8.7
5
7
  - 2.1.8
6
- - 2.3.1
8
+ - 2.3.8
7
9
  - 2.4.2
10
+ - 2.5.3
8
11
 
9
12
  gemfile:
10
- - gemfiles/Gemfile.2.3
11
- - gemfiles/Gemfile.3.2
12
- - gemfiles/Gemfile.4.2
13
- - gemfiles/Gemfile.5.0
14
- - gemfiles/Gemfile.5.1
15
- - gemfiles/Gemfile.5.1.pg
13
+ - Gemfile.2.3
14
+ - Gemfile.3.2
15
+ - Gemfile.4.2
16
+ - Gemfile.5.0
17
+ - Gemfile.5.1
18
+ - Gemfile.5.1.pg
19
+ - Gemfile.6.0.pg
16
20
 
17
21
  matrix:
18
22
  exclude:
19
- - gemfile: gemfiles/Gemfile.2.3
23
+ - gemfile: Gemfile.2.3
20
24
  rvm: 2.1.8
21
- - gemfile: gemfiles/Gemfile.2.3
22
- rvm: 2.3.1
23
- - gemfile: gemfiles/Gemfile.2.3
25
+ - gemfile: Gemfile.2.3
26
+ rvm: 2.3.8
27
+ - gemfile: Gemfile.2.3
24
28
  rvm: 2.4.2
25
- - gemfile: gemfiles/Gemfile.3.2
26
- rvm: 2.3.1
27
- - gemfile: gemfiles/Gemfile.3.2
29
+ - gemfile: Gemfile.2.3
30
+ rvm: 2.5.3
31
+ - gemfile: Gemfile.3.2
32
+ rvm: 2.3.8
33
+ - gemfile: Gemfile.3.2
28
34
  rvm: 2.4.2
29
- - gemfile: gemfiles/Gemfile.4.2
35
+ - gemfile: Gemfile.3.2
36
+ rvm: 2.5.3
37
+ - gemfile: Gemfile.4.2
30
38
  rvm: 1.8.7
31
- - gemfile: gemfiles/Gemfile.4.2
39
+ - gemfile: Gemfile.4.2
32
40
  rvm: 2.4.2
33
- - gemfile: gemfiles/Gemfile.5.0
41
+ - gemfile: Gemfile.4.2
42
+ rvm: 2.5.3
43
+ - gemfile: Gemfile.5.0
44
+ rvm: 1.8.7
45
+ - gemfile: Gemfile.5.0
34
46
  rvm: 2.1.8
35
- - gemfile: gemfiles/Gemfile.5.0
47
+ - gemfile: Gemfile.5.1
36
48
  rvm: 1.8.7
37
- - gemfile: gemfiles/Gemfile.5.1
49
+ - gemfile: Gemfile.5.1
38
50
  rvm: 2.1.8
39
- - gemfile: gemfiles/Gemfile.5.1
51
+ - gemfile: Gemfile.5.1.pg
40
52
  rvm: 1.8.7
41
- - gemfile: gemfiles/Gemfile.5.1.pg
53
+ - gemfile: Gemfile.5.1.pg
42
54
  rvm: 2.1.8
43
- - gemfile: gemfiles/Gemfile.5.1.pg
55
+ - gemfile: Gemfile.6.0.pg
44
56
  rvm: 1.8.7
57
+ - gemfile: Gemfile.6.0.pg
58
+ rvm: 2.1.8
59
+ - gemfile: Gemfile.6.0.pg
60
+ rvm: 2.3.8
61
+ - gemfile: Gemfile.6.0.pg
62
+ rvm: 2.4.2
63
+
64
+ addons:
65
+ postgresql: "9.4"
45
66
 
46
67
  sudo: false
47
68
 
48
69
  cache: bundler
49
70
 
50
- notifications:
51
- email:
52
- - fail@makandra.de
53
-
54
71
  before_script:
55
72
  - psql -c 'create database assignable_values_test;' -U postgres
56
73
  - mysql -e 'create database IF NOT EXISTS assignable_values_test;'
57
74
 
58
75
  install:
59
76
  # Old Travis CI bundler explodes when lockfile version doesn't match recently bumped version
60
- - gem install bundler --version='=1.12.5'
77
+ - gem install bundler --version='=1.17.3'
61
78
  # This is the default Travis CI install step
62
79
  - bundle install --jobs=3 --retry=3 --deployment --path=${BUNDLE_PATH:-vendor/bundle}
63
80
 
@@ -8,6 +8,39 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
8
8
 
9
9
  ### Compatible changes
10
10
 
11
+ ## 0.16.3 - 2020-10-15
12
+
13
+ ### Compatible changes
14
+
15
+ - No longer crashes when assigning `nil` to an attribute with assignable values that are provided as a scope.
16
+
17
+ ## 0.16.2 - 2020-10-06
18
+
19
+ ### Compatible changes
20
+
21
+ - when given a scope, do not load all records to memory during validation
22
+
23
+ ## 0.16.1 - 2019-05-14
24
+
25
+ ### Compatible changes
26
+
27
+ - add tests for Rails 6
28
+ - Humanized assignable value methods now also take the include_old_values option (fixes #25)
29
+
30
+
31
+ ## 0.16.0 - 2019-02-20
32
+
33
+ ### Breaking changes
34
+
35
+ - fix arity bug
36
+
37
+
38
+ ## 0.15.1 - 2018-11-05
39
+
40
+ ### Compatible changes
41
+
42
+ - Add `#humanized_values` for the `multiple: true` case.
43
+
11
44
 
12
45
  ## 0.15.0 - 2018-10-26
13
46
 
data/Gemfile CHANGED
@@ -1 +1 @@
1
- ./gemfiles/Gemfile.4.2
1
+ ./Gemfile.4.2
@@ -13,4 +13,4 @@ gem 'rspec_candy'
13
13
  gem 'gemika'
14
14
 
15
15
  # Gem under test
16
- gem 'assignable_values', :path => '..'
16
+ gem 'assignable_values', :path => '.'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ..
2
+ remote: .
3
3
  specs:
4
- assignable_values (0.15.0)
4
+ assignable_values (0.16.3)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -13,4 +13,4 @@ gem 'rspec_candy'
13
13
  gem 'gemika'
14
14
 
15
15
  # Gem under test
16
- gem 'assignable_values', :path => '..'
16
+ gem 'assignable_values', :path => '.'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ..
2
+ remote: .
3
3
  specs:
4
- assignable_values (0.15.0)
4
+ assignable_values (0.16.3)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -13,4 +13,4 @@ gem 'rspec_candy'
13
13
  gem 'gemika'
14
14
 
15
15
  # Gem under test
16
- gem 'assignable_values', :path => '..'
16
+ gem 'assignable_values', :path => '.'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ..
2
+ remote: .
3
3
  specs:
4
- assignable_values (0.15.0)
4
+ assignable_values (0.16.3)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -13,4 +13,4 @@ gem 'rspec_candy'
13
13
  gem 'gemika'
14
14
 
15
15
  # Gem under test
16
- gem 'assignable_values', :path => '..'
16
+ gem 'assignable_values', :path => '.'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ..
2
+ remote: .
3
3
  specs:
4
- assignable_values (0.15.0)
4
+ assignable_values (0.16.3)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -13,4 +13,4 @@ gem 'rspec_candy'
13
13
  gem 'gemika'
14
14
 
15
15
  # Gem under test
16
- gem 'assignable_values', :path => '..'
16
+ gem 'assignable_values', :path => '.'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ..
2
+ remote: .
3
3
  specs:
4
- assignable_values (0.15.0)
4
+ assignable_values (0.16.3)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -65,4 +65,4 @@ DEPENDENCIES
65
65
  rspec_candy
66
66
 
67
67
  BUNDLED WITH
68
- 1.16.4
68
+ 1.17.3
@@ -13,4 +13,4 @@ gem 'rspec_candy'
13
13
  gem 'gemika'
14
14
 
15
15
  # Gem under test
16
- gem 'assignable_values', :path => '..'
16
+ gem 'assignable_values', :path => '.'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ..
2
+ remote: .
3
3
  specs:
4
- assignable_values (0.15.0)
4
+ assignable_values (0.16.3)
5
5
  activerecord (>= 2.3)
6
6
 
7
7
  GEM
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Runtime dependencies
4
+ gem 'activerecord', '~>6.0.0'
5
+ gem 'i18n'
6
+ gem 'pg', '<1'
7
+
8
+ # Development dependencies
9
+ gem 'rake'
10
+ gem 'database_cleaner'
11
+ gem 'rspec'
12
+ gem 'rspec_candy'
13
+ gem 'gemika'
14
+
15
+ # Gem under test
16
+ gem 'assignable_values', :path => '.'
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ assignable_values (0.16.3)
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
@@ -1 +1 @@
1
- ./gemfiles/Gemfile.4.2.lock
1
+ ./Gemfile.4.2.lock
data/README.md CHANGED
@@ -5,7 +5,7 @@ assignable_values - Enums on vitamins [![Build Status](https://secure.travis-ci.
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
@@ -185,8 +185,8 @@ In this case, every *subset* of the given values is valid, for example `['pop',
185
185
  For humanization, you can still use
186
186
 
187
187
  ```
188
- song.humanizable_genre('pop') # => "Pop music"
189
- song.assignable_humanizable_genres.last.humanized # => "Electronic music"
188
+ song.humanized_genre('pop') # => "Pop music"
189
+ song.humanized_assignable_genres.last.humanized # => "Electronic music"
190
190
  ```
191
191
 
192
192
 
@@ -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,16 +349,19 @@ 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
+ `.travis.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 rspec`
359
+ - Run tests using `bundle exec rake current_rspec`
357
360
 
358
361
  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:
359
362
 
360
- - Install development dependencies using `bundle matrix:install`
361
- - Run tests using `bundle matrix:spec`
363
+ - Install development dependencies using `bundle exec rake matrix:install`
364
+ - Run tests using `bundle exec rake matrix:spec`
362
365
 
363
366
  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.
364
367
 
@@ -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)
@@ -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
 
@@ -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
@@ -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
@@ -55,6 +55,19 @@ module AssignableValues
55
55
  value = given_value || send(restriction.property)
56
56
  restriction.humanized_value(value)
57
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
58
71
  end
59
72
  end
60
73
 
@@ -62,8 +75,8 @@ module AssignableValues
62
75
  restriction = self
63
76
  multiple = @options[:multiple]
64
77
  enhance_model do
65
- define_method :"humanized_assignable_#{restriction.property.to_s.pluralize}" do
66
- restriction.humanized_assignable_values(self)
78
+ define_method :"humanized_assignable_#{restriction.property.to_s.pluralize}" do |*args|
79
+ restriction.humanized_assignable_values(self, *args)
67
80
  end
68
81
 
69
82
  unless multiple
@@ -1,3 +1,3 @@
1
1
  module AssignableValues
2
- VERSION = '0.15.0'
2
+ VERSION = '0.16.3'
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, :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
@@ -98,6 +106,10 @@ describe AssignableValues::ActiveRecord do
98
106
  assignable_values_for :genre do
99
107
  %w[pop rock]
100
108
  end
109
+
110
+ assignable_values_for :virtual_multi_genres, :multiple => true, :allow_blank => true do
111
+ %w[pop rock]
112
+ end
101
113
  end
102
114
  end
103
115
 
@@ -159,6 +171,31 @@ describe AssignableValues::ActiveRecord do
159
171
  @klass.humanized_genre('rock').should == 'Rock music'
160
172
  end
161
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
162
199
  end
163
200
 
164
201
  context 'if the :allow_blank option is set to true' do
@@ -226,11 +263,11 @@ describe AssignableValues::ActiveRecord do
226
263
  context 'if the :message option is set to a string' do
227
264
 
228
265
  before :each do
229
- @klass = Song.disposable_copy do
230
- assignable_values_for :genre, :message => 'should be something different' do
231
- %w[pop rock]
232
- end
233
- 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
234
271
  end
235
272
 
236
273
  it 'should use this string as a custom error message' do
@@ -251,43 +288,39 @@ describe AssignableValues::ActiveRecord do
251
288
 
252
289
  before :each do
253
290
  @klass = Song.disposable_copy do
254
- assignable_values_for :genres, :multiple => true do
291
+ assignable_values_for :multi_genres, :multiple => true do
255
292
  %w[pop rock]
256
293
  end
257
294
  end
258
295
  end
259
296
 
260
297
  it 'should validate that the attribute is allowed' do
261
- @klass.new(:genres => ['pop']).should be_valid
262
- @klass.new(:genres => ['pop', 'rock']).should be_valid
263
- @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
264
301
  end
265
302
 
266
303
  it 'should not allow a scalar attribute' do
267
- @klass.new(:genres => 'pop').should_not be_valid
304
+ @klass.new(:multi_genres => 'pop').should_not be_valid
268
305
  end
269
306
 
270
307
  it 'should not allow nil or [] for the attribute value' do
271
- @klass.new(:genres => nil).should_not be_valid
272
- @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
273
310
  end
274
311
 
275
312
  it 'should allow a subset of previously saved values even if that value is no longer allowed' do
276
- record = @klass.create!(:genres => ['pop'])
277
- record.genres = ['pretend', 'previously', 'valid', 'value']
278
- if ActiveRecord::VERSION::MAJOR < 3
279
- record.save(false)
280
- else
281
- record.save(:validate => false) # update without validations for the sake of this test
282
- 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
283
316
  record.reload.should be_valid
284
- record.genres = ['valid', 'previously', 'pop']
317
+ record.multi_genres = ['valid', 'previously', 'pop']
285
318
  record.should be_valid
286
319
  end
287
320
 
288
321
  it 'should allow a previously saved, blank value even if that value is no longer allowed' do
289
- record = @klass.create!(:genres => ['pop'])
290
- @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
291
324
  record.reload.should be_valid
292
325
  end
293
326
 
@@ -297,18 +330,18 @@ describe AssignableValues::ActiveRecord do
297
330
 
298
331
  before :each do
299
332
  @klass = Song.disposable_copy do
300
- assignable_values_for :genres, :multiple => true, :allow_blank => true do
333
+ assignable_values_for :multi_genres, :multiple => true, :allow_blank => true do
301
334
  %w[pop rock]
302
335
  end
303
336
  end
304
337
  end
305
338
 
306
339
  it 'should allow nil for the attribute value' do
307
- @klass.new(:genres => nil).should be_valid
340
+ @klass.new(:multi_genres => nil).should be_valid
308
341
  end
309
342
 
310
343
  it 'should allow an empty array as value' do
311
- @klass.new(:genres => []).should be_valid
344
+ @klass.new(:multi_genres => []).should be_valid
312
345
  end
313
346
 
314
347
  end
@@ -415,7 +448,7 @@ describe AssignableValues::ActiveRecord do
415
448
  record.should be_valid
416
449
  end
417
450
 
418
- 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
419
452
  allowed_association = Artist.create!
420
453
  klass = Song.disposable_copy
421
454
  record = klass.new(:artist => nil)
@@ -477,6 +510,63 @@ describe AssignableValues::ActiveRecord do
477
510
  record.valid?
478
511
  end
479
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
+
480
570
  end
481
571
 
482
572
  context 'when delegating using the :through option' do
@@ -485,7 +575,11 @@ describe AssignableValues::ActiveRecord do
485
575
  klass = Song.disposable_copy do
486
576
  assignable_values_for :genre, :through => :delegate
487
577
  def delegate
488
- OpenStruct.new(:assignable_song_genres => %w[pop rock])
578
+ Class.new do
579
+ def assignable_song_genres
580
+ %w[pop rock]
581
+ end
582
+ end.new
489
583
  end
490
584
  end
491
585
  klass.new(:genre => 'pop').should be_valid
@@ -496,9 +590,14 @@ describe AssignableValues::ActiveRecord do
496
590
  klass = Song.disposable_copy do
497
591
  assignable_values_for :genre, :through => lambda { delegate }
498
592
  def delegate
499
- OpenStruct.new(:assignable_song_genres => %w[pop rock])
593
+ Class.new do
594
+ def assignable_song_genres
595
+ %w[pop rock]
596
+ end
597
+ end.new
500
598
  end
501
599
  end
600
+
502
601
  klass.new(:genre => 'pop').should be_valid
503
602
  klass.new(:genre => 'disallowed value').should_not be_valid
504
603
  end
@@ -507,7 +606,11 @@ describe AssignableValues::ActiveRecord do
507
606
  klass = Recording::Vinyl.disposable_copy do
508
607
  assignable_values_for :year, :through => :delegate
509
608
  def delegate
510
- OpenStruct.new(:assignable_recording_vinyl_years => [1977, 1980, 1983])
609
+ Class.new do
610
+ def assignable_recording_vinyl_years
611
+ [1977, 1980, 1983]
612
+ end
613
+ end.new
511
614
  end
512
615
  end
513
616
  klass.new.assignable_years.should == [1977, 1980, 1983]
@@ -684,7 +787,7 @@ describe AssignableValues::ActiveRecord do
684
787
  klass.new.assignable_genres.should == %w[pop rock]
685
788
  end
686
789
 
687
- 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
688
791
  klass = Song.disposable_copy do
689
792
  assignable_values_for :year do
690
793
  1999..2001
@@ -710,10 +813,28 @@ describe AssignableValues::ActiveRecord do
710
813
  assignable_values_for :genre do
711
814
  %w[pop rock]
712
815
  end
816
+
817
+ assignable_values_for :multi_genres, :multiple => true do
818
+ %w[pop rock]
819
+ end
713
820
  end
714
- record = klass.create!(:genre => 'pop')
821
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
715
822
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
716
823
  record.reload.assignable_genres.should == %w[ballad pop rock]
824
+
825
+ humanized_genres = record.humanized_assignable_genres
826
+ humanized_genres.collect(&:value).should == %w[ballad pop rock]
827
+ humanized_genres.collect(&:humanized).should == ['Ballad', 'Pop music', 'Rock music']
828
+ humanized_genres.collect(&:to_s).should == ['Ballad', 'Pop music', 'Rock music']
829
+
830
+ record.multi_genres = %w[ballad classic]
831
+ save_without_validation(record) # update without validation for the sake of this test
832
+ record.reload.multi_genres.should == %w[ballad classic]
833
+
834
+ humanized_multi_genres = record.humanized_assignable_multi_genres
835
+ humanized_multi_genres.collect(&:value).should == %w[ballad classic pop rock]
836
+ humanized_multi_genres.collect(&:humanized).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
837
+ humanized_multi_genres.collect(&:to_s).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
717
838
  end
718
839
 
719
840
  it 'should not prepend a previously saved value to the top of the list if it is still allowed (bugfix)' do
@@ -753,10 +874,28 @@ describe AssignableValues::ActiveRecord do
753
874
  assignable_values_for :genre do
754
875
  %w[pop rock]
755
876
  end
877
+
878
+ assignable_values_for :multi_genres, :multiple => true do
879
+ %w[pop rock]
880
+ end
756
881
  end
757
- record = klass.create!(:genre => 'pop')
882
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
758
883
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
759
884
  record.reload.assignable_genres(:include_old_value => false).should == %w[pop rock]
885
+
886
+ humanized_genres = record.humanized_assignable_genres(:include_old_value => false)
887
+ humanized_genres.collect(&:value).should == %w[pop rock]
888
+ humanized_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
889
+ humanized_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
890
+
891
+ record.multi_genres = %w[ballad classic]
892
+ save_without_validation(record) # update without validation for the sake of this test
893
+ record.reload.multi_genres.should == %w[ballad classic]
894
+
895
+ humanized_multi_genres = record.humanized_assignable_multi_genres(:include_old_value => false)
896
+ humanized_multi_genres.collect(&:value).should == %w[pop rock]
897
+ humanized_multi_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
898
+ humanized_multi_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
760
899
  end
761
900
 
762
901
  it 'should allow omitting a previously saved association' do
@@ -897,7 +1036,41 @@ describe AssignableValues::ActiveRecord do
897
1036
  delegate = Object.new
898
1037
  def delegate.assignable_song_genres(record)
899
1038
  record_received(record)
900
- %w[pop rock]
1039
+ %w[pop rock]
1040
+ end
1041
+ klass = Song.disposable_copy do
1042
+ assignable_values_for :genre, :through => :delegate
1043
+ define_method :delegate do
1044
+ delegate
1045
+ end
1046
+ end
1047
+ record = klass.new
1048
+ delegate.should_receive(:record_received).with(record)
1049
+ record.assignable_genres.should == %w[pop rock]
1050
+ end
1051
+
1052
+ it "should call the given method on the delegate if the delegate's query method takes no arguments" do
1053
+ delegate = Object.new
1054
+ def delegate.assignable_song_genres
1055
+ no_record_received
1056
+ %w[pop rock]
1057
+ end
1058
+ klass = Song.disposable_copy do
1059
+ assignable_values_for :genre, :through => :delegate
1060
+ define_method :delegate do
1061
+ delegate
1062
+ end
1063
+ end
1064
+ record = klass.new
1065
+ delegate.should_receive(:no_record_received)
1066
+ record.assignable_genres.should == %w[pop rock]
1067
+ end
1068
+
1069
+ it "should pass the record to the given method if the delegate's query method takes variable arguments" do
1070
+ delegate = Object.new
1071
+ def delegate.assignable_song_genres(*records)
1072
+ record_received(*records)
1073
+ %w[pop rock]
901
1074
  end
902
1075
  klass = Song.disposable_copy do
903
1076
  assignable_values_for :genre, :through => :delegate
@@ -910,6 +1083,21 @@ describe AssignableValues::ActiveRecord do
910
1083
  record.assignable_genres.should == %w[pop rock]
911
1084
  end
912
1085
 
1086
+ it "should raise an error if the delegate's query method takes more than one argument" do
1087
+ delegate = Object.new
1088
+ def delegate.assignable_song_genres(record, other_arg)
1089
+ %w[pop rock]
1090
+ end
1091
+ klass = Song.disposable_copy do
1092
+ assignable_values_for :genre, :through => :delegate
1093
+ define_method :delegate do
1094
+ delegate
1095
+ end
1096
+ end
1097
+ record = klass.new
1098
+ expect{record.assignable_genres}.to raise_error(ArgumentError)
1099
+ end
1100
+
913
1101
  it 'should raise an error if the given method returns nil' do
914
1102
  klass = Song.disposable_copy do
915
1103
  assignable_values_for :genre, :through => :delegate
@@ -919,11 +1107,7 @@ describe AssignableValues::ActiveRecord do
919
1107
  end
920
1108
  expect { klass.new.assignable_genres }.to raise_error(AssignableValues::DelegateUnavailable)
921
1109
  end
922
-
923
1110
  end
924
-
925
1111
  end
926
-
927
1112
  end
928
-
929
1113
  end
@@ -10,7 +10,7 @@ database.rewrite_schema! do
10
10
  t.string :genre
11
11
  t.integer :year
12
12
  t.integer :duration
13
- t.string :genres, :array => true
13
+ t.string :multi_genres, :array => true
14
14
  end
15
15
 
16
16
  create_table :vinyl_recordings do |t|
@@ -15,7 +15,10 @@ en:
15
15
  multi_genres:
16
16
  pop: 'Pop music'
17
17
  rock: 'Rock music'
18
- sub_genre:
18
+ virtual_multi_genres:
19
+ pop: 'Pop music'
20
+ rock: 'Rock music'
21
+ virtual_sub_genre:
19
22
  pop: 'Pop music'
20
23
  rock: 'Rock music'
21
24
  year:
@@ -8,11 +8,11 @@ class Song < ActiveRecord::Base
8
8
 
9
9
  belongs_to :artist
10
10
 
11
- attr_accessor :sub_genre, :sub_genres, :multi_genres
11
+ attr_accessor :virtual_sub_genre, :virtual_sub_genres, :virtual_multi_genres
12
12
 
13
- if ActiveRecord::VERSION::MAJOR < 4 || !Song.new(:genres => ['test']).genres.is_a?(Array)
13
+ if ActiveRecord::VERSION::MAJOR < 4 || !Song.new(:multi_genres => ['test']).multi_genres.is_a?(Array)
14
14
  # Rails 4 or not postgres
15
- serialize :genres
15
+ serialize :multi_genres
16
16
  end
17
17
 
18
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assignable_values
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-26 00:00:00.000000000 Z
11
+ date: 2020-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -36,23 +36,25 @@ files:
36
36
  - ".travis.yml"
37
37
  - CHANGELOG.md
38
38
  - Gemfile
39
+ - Gemfile.2.3
40
+ - Gemfile.2.3.lock
41
+ - Gemfile.3.2
42
+ - Gemfile.3.2.lock
43
+ - Gemfile.4.2
44
+ - Gemfile.4.2.lock
45
+ - Gemfile.5.0
46
+ - Gemfile.5.0.lock
47
+ - Gemfile.5.1
48
+ - Gemfile.5.1.lock
49
+ - Gemfile.5.1.pg
50
+ - Gemfile.5.1.pg.lock
51
+ - Gemfile.6.0.pg
52
+ - Gemfile.6.0.pg.lock
39
53
  - Gemfile.lock
40
54
  - LICENSE
41
55
  - README.md
42
56
  - Rakefile
43
57
  - assignable_values.gemspec
44
- - gemfiles/Gemfile.2.3
45
- - gemfiles/Gemfile.2.3.lock
46
- - gemfiles/Gemfile.3.2
47
- - gemfiles/Gemfile.3.2.lock
48
- - gemfiles/Gemfile.4.2
49
- - gemfiles/Gemfile.4.2.lock
50
- - gemfiles/Gemfile.5.0
51
- - gemfiles/Gemfile.5.0.lock
52
- - gemfiles/Gemfile.5.1
53
- - gemfiles/Gemfile.5.1.lock
54
- - gemfiles/Gemfile.5.1.pg
55
- - gemfiles/Gemfile.5.1.pg.lock
56
58
  - lib/assignable_values.rb
57
59
  - lib/assignable_values/active_record.rb
58
60
  - lib/assignable_values/active_record/restriction/base.rb
@@ -89,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
91
  - !ruby/object:Gem::Version
90
92
  version: '0'
91
93
  requirements: []
92
- rubyforge_project:
93
- rubygems_version: 2.7.7
94
+ rubygems_version: 3.1.4
94
95
  signing_key:
95
96
  specification_version: 4
96
97
  summary: Restrict the values assignable to ActiveRecord attributes or associations