assignable_values 0.15.1 → 0.16.4

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: 8a7a9d96e5e738273b161df4a8cf85c5871122f909b6e50aad62fde7e7a21f14
4
- data.tar.gz: 9b09dd1a6d0e1270876676442eeda26ca6f03650954b104efc9738aba80b0770
3
+ metadata.gz: fcf4880911a30c8f39fe52bb5b018226a899b7ea1b6fa0f4b232d54e198abf83
4
+ data.tar.gz: cb52c389c399947480293f65a69d29a2ca6f7ac02de1d743d52a68a1593ba6c2
5
5
  SHA512:
6
- metadata.gz: 3c040fd853c739f57b23698124e5e0087ddfdc3a40f0eb26b6162089ed740888c325248cb49c69d7bee230a4df2817c094f2179f72ae5b284a844a2a1cb34239
7
- data.tar.gz: 161e04475ee56b531af8ad5b1b8dc65ebc601ad3c2dbd02cc833b87e78d3efe7d595d9bf3b3165da771eec12fc25283be935247ed1e1c6d0eeaded4f9c725d0e
6
+ metadata.gz: c68e65b8fd131d928af7e0ebe32999ade79163cc44059718c8032d17ca2331abfbf1d0c1babe35378f0816edd22ba67e5c6c7ad296348e0608ea492ed7d72383
7
+ data.tar.gz: '08479bf3b543772e9a8810c4cdec60d72979fa152bc445c4e4f7029e5c4909b02c1484f8a40d0044b091e0ece3f068859ebb026ae797912d0d829da9fd682ded'
@@ -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,12 +8,47 @@ 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.4 - 2020-10-15
12
+
13
+ ### Compatible changes
14
+
15
+ - No longer crashes value blocks return `nil`.
16
+
17
+
18
+ ## 0.16.3 - 2020-10-15
19
+
20
+ ### Compatible changes
21
+
22
+ - No longer crashes when assigning `nil` to an attribute with assignable values that are provided as a scope.
23
+
24
+
25
+ ## 0.16.2 - 2020-10-06
26
+
27
+ ### Compatible changes
28
+
29
+ - when given a scope, do not load all records to memory during validation
30
+
31
+
32
+ ## 0.16.1 - 2019-05-14
33
+
34
+ ### Compatible changes
35
+
36
+ - add tests for Rails 6
37
+ - Humanized assignable value methods now also take the include_old_values option (fixes #25)
38
+
39
+
40
+ ## 0.16.0 - 2019-02-20
41
+
42
+ ### Breaking changes
43
+
44
+ - fix arity bug
45
+
11
46
 
12
47
  ## 0.15.1 - 2018-11-05
13
48
 
14
49
  ### Compatible changes
15
50
 
16
- - Add `#humanized_values` for the `mutliple: true` case.
51
+ - Add `#humanized_values` for the `multiple: true` case.
17
52
 
18
53
 
19
54
  ## 0.15.0 - 2018-10-26
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.1)
4
+ assignable_values (0.16.4)
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.1)
4
+ assignable_values (0.16.4)
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.1)
4
+ assignable_values (0.16.4)
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.1)
4
+ assignable_values (0.16.4)
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.1)
4
+ assignable_values (0.16.4)
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.1)
4
+ assignable_values (0.16.4)
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.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
@@ -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
@@ -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)
@@ -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
 
@@ -240,7 +266,7 @@ module AssignableValues
240
266
  delegate = delegate(record)
241
267
  delegate.present? or raise DelegateUnavailable, "Cannot query a nil delegate for assignable values"
242
268
  delegate_query_method = :"assignable_#{model.name.underscore.gsub('/', '_')}_#{property.to_s.pluralize}"
243
- args = delegate.method(delegate_query_method).arity == 1 ? [record] : []
269
+ args = delegate.method(delegate_query_method).arity == 0 ? [] : [record]
244
270
  delegate.send(delegate_query_method, *args)
245
271
  end
246
272
 
@@ -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.15.1'
2
+ VERSION = '0.16.4'
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
@@ -255,11 +263,11 @@ describe AssignableValues::ActiveRecord do
255
263
  context 'if the :message option is set to a string' do
256
264
 
257
265
  before :each do
258
- @klass = Song.disposable_copy do
259
- assignable_values_for :genre, :message => 'should be something different' do
260
- %w[pop rock]
261
- end
262
- 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
263
271
  end
264
272
 
265
273
  it 'should use this string as a custom error message' do
@@ -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
@@ -514,7 +575,11 @@ describe AssignableValues::ActiveRecord do
514
575
  klass = Song.disposable_copy do
515
576
  assignable_values_for :genre, :through => :delegate
516
577
  def delegate
517
- 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
518
583
  end
519
584
  end
520
585
  klass.new(:genre => 'pop').should be_valid
@@ -525,9 +590,14 @@ describe AssignableValues::ActiveRecord do
525
590
  klass = Song.disposable_copy do
526
591
  assignable_values_for :genre, :through => lambda { delegate }
527
592
  def delegate
528
- 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
529
598
  end
530
599
  end
600
+
531
601
  klass.new(:genre => 'pop').should be_valid
532
602
  klass.new(:genre => 'disallowed value').should_not be_valid
533
603
  end
@@ -536,7 +606,11 @@ describe AssignableValues::ActiveRecord do
536
606
  klass = Recording::Vinyl.disposable_copy do
537
607
  assignable_values_for :year, :through => :delegate
538
608
  def delegate
539
- 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
540
614
  end
541
615
  end
542
616
  klass.new.assignable_years.should == [1977, 1980, 1983]
@@ -713,7 +787,7 @@ describe AssignableValues::ActiveRecord do
713
787
  klass.new.assignable_genres.should == %w[pop rock]
714
788
  end
715
789
 
716
- 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
717
791
  klass = Song.disposable_copy do
718
792
  assignable_values_for :year do
719
793
  1999..2001
@@ -734,15 +808,53 @@ describe AssignableValues::ActiveRecord do
734
808
  klass.new.assignable_genres.should == %w[pop rock]
735
809
  end
736
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
+
737
831
  it 'should prepend a previously saved value to the top of the list, even if is no longer allowed' do
738
832
  klass = Song.disposable_copy do
739
833
  assignable_values_for :genre do
740
834
  %w[pop rock]
741
835
  end
836
+
837
+ assignable_values_for :multi_genres, :multiple => true do
838
+ %w[pop rock]
839
+ end
742
840
  end
743
- record = klass.create!(:genre => 'pop')
841
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
744
842
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
745
843
  record.reload.assignable_genres.should == %w[ballad pop rock]
844
+
845
+ humanized_genres = record.humanized_assignable_genres
846
+ humanized_genres.collect(&:value).should == %w[ballad pop rock]
847
+ humanized_genres.collect(&:humanized).should == ['Ballad', 'Pop music', 'Rock music']
848
+ humanized_genres.collect(&:to_s).should == ['Ballad', 'Pop music', 'Rock music']
849
+
850
+ record.multi_genres = %w[ballad classic]
851
+ save_without_validation(record) # update without validation for the sake of this test
852
+ record.reload.multi_genres.should == %w[ballad classic]
853
+
854
+ humanized_multi_genres = record.humanized_assignable_multi_genres
855
+ humanized_multi_genres.collect(&:value).should == %w[ballad classic pop rock]
856
+ humanized_multi_genres.collect(&:humanized).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
857
+ humanized_multi_genres.collect(&:to_s).should == ['Ballad', 'Classic', 'Pop music', 'Rock music']
746
858
  end
747
859
 
748
860
  it 'should not prepend a previously saved value to the top of the list if it is still allowed (bugfix)' do
@@ -782,10 +894,28 @@ describe AssignableValues::ActiveRecord do
782
894
  assignable_values_for :genre do
783
895
  %w[pop rock]
784
896
  end
897
+
898
+ assignable_values_for :multi_genres, :multiple => true do
899
+ %w[pop rock]
900
+ end
785
901
  end
786
- record = klass.create!(:genre => 'pop')
902
+ record = klass.create!(:genre => 'pop', :multi_genres => ['rock'])
787
903
  klass.update_all(:genre => 'ballad') # update without validation for the sake of this test
788
904
  record.reload.assignable_genres(:include_old_value => false).should == %w[pop rock]
905
+
906
+ humanized_genres = record.humanized_assignable_genres(:include_old_value => false)
907
+ humanized_genres.collect(&:value).should == %w[pop rock]
908
+ humanized_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
909
+ humanized_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
910
+
911
+ record.multi_genres = %w[ballad classic]
912
+ save_without_validation(record) # update without validation for the sake of this test
913
+ record.reload.multi_genres.should == %w[ballad classic]
914
+
915
+ humanized_multi_genres = record.humanized_assignable_multi_genres(:include_old_value => false)
916
+ humanized_multi_genres.collect(&:value).should == %w[pop rock]
917
+ humanized_multi_genres.collect(&:humanized).should == ['Pop music', 'Rock music']
918
+ humanized_multi_genres.collect(&:to_s).should == ['Pop music', 'Rock music']
789
919
  end
790
920
 
791
921
  it 'should allow omitting a previously saved association' do
@@ -926,7 +1056,41 @@ describe AssignableValues::ActiveRecord do
926
1056
  delegate = Object.new
927
1057
  def delegate.assignable_song_genres(record)
928
1058
  record_received(record)
929
- %w[pop rock]
1059
+ %w[pop rock]
1060
+ end
1061
+ klass = Song.disposable_copy do
1062
+ assignable_values_for :genre, :through => :delegate
1063
+ define_method :delegate do
1064
+ delegate
1065
+ end
1066
+ end
1067
+ record = klass.new
1068
+ delegate.should_receive(:record_received).with(record)
1069
+ record.assignable_genres.should == %w[pop rock]
1070
+ end
1071
+
1072
+ it "should call the given method on the delegate if the delegate's query method takes no arguments" do
1073
+ delegate = Object.new
1074
+ def delegate.assignable_song_genres
1075
+ no_record_received
1076
+ %w[pop rock]
1077
+ end
1078
+ klass = Song.disposable_copy do
1079
+ assignable_values_for :genre, :through => :delegate
1080
+ define_method :delegate do
1081
+ delegate
1082
+ end
1083
+ end
1084
+ record = klass.new
1085
+ delegate.should_receive(:no_record_received)
1086
+ record.assignable_genres.should == %w[pop rock]
1087
+ end
1088
+
1089
+ it "should pass the record to the given method if the delegate's query method takes variable arguments" do
1090
+ delegate = Object.new
1091
+ def delegate.assignable_song_genres(*records)
1092
+ record_received(*records)
1093
+ %w[pop rock]
930
1094
  end
931
1095
  klass = Song.disposable_copy do
932
1096
  assignable_values_for :genre, :through => :delegate
@@ -939,6 +1103,21 @@ describe AssignableValues::ActiveRecord do
939
1103
  record.assignable_genres.should == %w[pop rock]
940
1104
  end
941
1105
 
1106
+ it "should raise an error if the delegate's query method takes more than one argument" do
1107
+ delegate = Object.new
1108
+ def delegate.assignable_song_genres(record, other_arg)
1109
+ %w[pop rock]
1110
+ end
1111
+ klass = Song.disposable_copy do
1112
+ assignable_values_for :genre, :through => :delegate
1113
+ define_method :delegate do
1114
+ delegate
1115
+ end
1116
+ end
1117
+ record = klass.new
1118
+ expect{record.assignable_genres}.to raise_error(ArgumentError)
1119
+ end
1120
+
942
1121
  it 'should raise an error if the given method returns nil' do
943
1122
  klass = Song.disposable_copy do
944
1123
  assignable_values_for :genre, :through => :delegate
@@ -948,11 +1127,7 @@ describe AssignableValues::ActiveRecord do
948
1127
  end
949
1128
  expect { klass.new.assignable_genres }.to raise_error(AssignableValues::DelegateUnavailable)
950
1129
  end
951
-
952
1130
  end
953
-
954
1131
  end
955
-
956
1132
  end
957
-
958
1133
  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.1
4
+ version: 0.16.4
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-11-05 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.6
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