validates_type 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +22 -0
- data/README.md +90 -0
- data/errors/unsupported_type.rb +4 -0
- data/lib/arguments.rb +44 -0
- data/lib/validates_type.rb +138 -0
- data/spec/active_model/base_spec.rb +33 -0
- data/spec/active_model/modifiers_spec.rb +319 -0
- data/spec/active_model/types_spec.rb +217 -0
- data/spec/active_model_helper.rb +47 -0
- data/spec/active_record_helper.rb +33 -0
- data/spec/activerecord/base_spec.rb +33 -0
- data/spec/activerecord/modifiers_spec.rb +319 -0
- data/spec/activerecord/types_spec.rb +307 -0
- data/spec/arguments_spec.rb +83 -0
- data/spec/spec_helper.rb +7 -0
- data/validates_type.gemspec +28 -0
- data/version.rb +3 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 095e33afbb9a0aebd616f77ee2be6a6c6f44598b
|
4
|
+
data.tar.gz: 27aa41f31c5dd6935ce8ccc41c5fde70ac54844f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e4996fa85a21db804072016958f327f6831e9a4dd34c9702b483467f62411d713b5cbb034a53eb5e309674751eb8a8e6a51f5f988337412c46429e44e703d5de
|
7
|
+
data.tar.gz: 0ba9a4f043b605d20bed49fc6b7a6d78c77614d34af5bdd20cf9f60f5b851332a015c0c0acbaae252a3123cc9bf265bf9b1bd25a3e20179d46af64d1618cfe57
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (4.2.1)
|
5
|
+
activesupport (= 4.2.1)
|
6
|
+
builder (~> 3.1)
|
7
|
+
activerecord (4.2.1)
|
8
|
+
activemodel (= 4.2.1)
|
9
|
+
activesupport (= 4.2.1)
|
10
|
+
arel (~> 6.0)
|
11
|
+
activesupport (4.2.1)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
arel (6.0.0)
|
18
|
+
builder (3.2.2)
|
19
|
+
coderay (1.1.0)
|
20
|
+
diff-lcs (1.2.5)
|
21
|
+
i18n (0.7.0)
|
22
|
+
json (1.8.2)
|
23
|
+
method_source (0.8.2)
|
24
|
+
minitest (5.6.1)
|
25
|
+
pry (0.10.1)
|
26
|
+
coderay (~> 1.1.0)
|
27
|
+
method_source (~> 0.8.1)
|
28
|
+
slop (~> 3.4)
|
29
|
+
rspec (3.2.0)
|
30
|
+
rspec-core (~> 3.2.0)
|
31
|
+
rspec-expectations (~> 3.2.0)
|
32
|
+
rspec-mocks (~> 3.2.0)
|
33
|
+
rspec-core (3.2.3)
|
34
|
+
rspec-support (~> 3.2.0)
|
35
|
+
rspec-expectations (3.2.1)
|
36
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
37
|
+
rspec-support (~> 3.2.0)
|
38
|
+
rspec-mocks (3.2.1)
|
39
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
40
|
+
rspec-support (~> 3.2.0)
|
41
|
+
rspec-support (3.2.2)
|
42
|
+
ruby-boolean (1.3.3)
|
43
|
+
slop (3.6.0)
|
44
|
+
sqlite3 (1.3.10)
|
45
|
+
thread_safe (0.3.5)
|
46
|
+
tzinfo (1.2.2)
|
47
|
+
thread_safe (~> 0.1)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
ruby
|
51
|
+
|
52
|
+
DEPENDENCIES
|
53
|
+
activemodel
|
54
|
+
activerecord
|
55
|
+
pry
|
56
|
+
rspec
|
57
|
+
ruby-boolean
|
58
|
+
sqlite3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Jake Yesbeck
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
## validates_type
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
### Rails type validation
|
6
|
+
|
7
|
+
#### Purpose
|
8
|
+
|
9
|
+
Most Rails applications will have types coerced by their ORM connection adapters (like the `pg` gem or `mysql2`). However, this only useful for applications with very well defined schemas. If your application has a legacy storage layer that you can no longer modify or a lot of `store_accessor` columns, this solution is a nice middle ground to ensure your data is robust.
|
10
|
+
|
11
|
+
This also prevents your data from being coerced into values that you did not intend by ActiveRecord adapters.
|
12
|
+
|
13
|
+
#### Usage
|
14
|
+
|
15
|
+
##### With ActiveRecord
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class Foo < ActiveRecord::Base
|
19
|
+
|
20
|
+
# validate that attribute :bar is a String
|
21
|
+
validates_type :bar, :string
|
22
|
+
|
23
|
+
# validate that attribute :baz is an Integer with a custom error message
|
24
|
+
validates_type :baz, :integer, message: 'Baz must be an Integer'
|
25
|
+
|
26
|
+
# validate that attribute :qux is an Array, allow blank
|
27
|
+
validates_type :qux, :array, allow_blank: true
|
28
|
+
|
29
|
+
# validate that attribute :whatever is a Boolean
|
30
|
+
validates_type :whatever, :boolean
|
31
|
+
|
32
|
+
# validate that attribute :thing is a Float or nil
|
33
|
+
validates_type :thing, :float, allow_nil: true
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
##### With ActiveModel
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class Bar
|
41
|
+
include ActiveModel::Validations
|
42
|
+
|
43
|
+
attr_accessor :foo, :qux
|
44
|
+
|
45
|
+
validates_type :foo, :string
|
46
|
+
|
47
|
+
# Custom error message support
|
48
|
+
validates_type :qux, :boolean, message: 'Attribute qux must be a boolean!'
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
##### With shortcut syntax
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class Banana < ActiveRecord::Base
|
56
|
+
|
57
|
+
# The banana's peel attribute must be a string
|
58
|
+
validates :peel, type: { type: :string }
|
59
|
+
|
60
|
+
# Custom error message for ripeness of banana
|
61
|
+
validates :ripe, type: { type: :boolean, message: 'Only ripe bananas allowed' }
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
##### With multiple modifiers
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class Foo < ActiveRecord::Base
|
69
|
+
# validate that attribute :baz is an Integer with a custom error message
|
70
|
+
# only if :conditional_method evaluates to true
|
71
|
+
validates_type :baz, :integer, message: 'Baz must be an Integer', if: :conditional_method
|
72
|
+
|
73
|
+
def conditional_method
|
74
|
+
# some kind of logic that is important to pass
|
75
|
+
end
|
76
|
+
|
77
|
+
# validate that attribute :baz is a Float and is included in a specific array
|
78
|
+
validates_type :foo, :float, in: [1.0, 2.5, 3.0]
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
#### Supported types
|
83
|
+
|
84
|
+
- `:array`
|
85
|
+
- `:boolean`
|
86
|
+
- `:float`
|
87
|
+
- `:hash`
|
88
|
+
- `:integer`
|
89
|
+
- `:string`
|
90
|
+
- `:symbol`
|
data/lib/arguments.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module ValidatesType
|
2
|
+
# Wrapper class for arguments consumed by validates_with
|
3
|
+
class Arguments
|
4
|
+
# @initialize
|
5
|
+
# param: attribute_name <Symbol> - name of attribute that will be validated
|
6
|
+
# param: attribute_type <Symbol> - type for which to validate the attribute against
|
7
|
+
# param: options <Hash> - extra options to pass along to the validator
|
8
|
+
# i.e. allow_nil: true, message: 'my custom message'
|
9
|
+
# return: nil
|
10
|
+
def initialize(attribute_name, attribute_type, options)
|
11
|
+
@attribute_name = attribute_name
|
12
|
+
@attribute_type = attribute_type
|
13
|
+
@options = options.is_a?(Hash) ? options : {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# format expected by _merge_attributes
|
17
|
+
#
|
18
|
+
# @to_validation_attributes
|
19
|
+
# return: <Array> - cardinality of 2
|
20
|
+
def to_validation_attributes
|
21
|
+
[@attribute_name, merged_options]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# helper method to compact all the options together along
|
27
|
+
# with the type for validation
|
28
|
+
#
|
29
|
+
# @merged_options
|
30
|
+
# return: <Hash>
|
31
|
+
def merged_options
|
32
|
+
type.merge(@options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# helper method to impose the type for validation into an option
|
36
|
+
# that will be merged later
|
37
|
+
#
|
38
|
+
# @type
|
39
|
+
# return: <Hash>
|
40
|
+
def type
|
41
|
+
{ :type => @attribute_type }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'ruby-boolean'
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
require_relative '../errors/unsupported_type'
|
5
|
+
require_relative './arguments'
|
6
|
+
|
7
|
+
module ActiveModel
|
8
|
+
module Validations
|
9
|
+
class TypeValidator < ActiveModel::EachValidator
|
10
|
+
# Set default message for failure here
|
11
|
+
#
|
12
|
+
# @initialize
|
13
|
+
# param: options <Hash> - options hash of how to validate this attribute
|
14
|
+
# including custom messaging due to failures, specifying
|
15
|
+
# the type of the attribute to validate against, etc.
|
16
|
+
# return: result of ActiveModel::Validations::EachValidator initialize
|
17
|
+
def initialize(options)
|
18
|
+
merged_options = {
|
19
|
+
:message => "is expected to be a #{ symbol_class(options[:type]) } and is not."
|
20
|
+
}.merge(options)
|
21
|
+
|
22
|
+
super(merged_options)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Validate that the value is the type we expect
|
26
|
+
#
|
27
|
+
# @validate_each
|
28
|
+
# param: record <Object> - subject containing attribute to validate
|
29
|
+
# param: attribute <Symbol> - name of attribute to validate
|
30
|
+
# param: value <Variable> - value of attribute to validate
|
31
|
+
# return: nil
|
32
|
+
def validate_each(record, attribute, value)
|
33
|
+
value_to_test = type_before_coercion(record, attribute, value)
|
34
|
+
expected_type = symbol_class(options[:type])
|
35
|
+
|
36
|
+
add_errors_or_raise(options, record, attribute) unless value_to_test.is_a?(expected_type)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Helper method to either add messages to the errors object
|
42
|
+
# or raise an exception in :strict mode
|
43
|
+
#
|
44
|
+
# @add_errors_or_raise
|
45
|
+
# param: options <Hash> - options hash with strict flag or class
|
46
|
+
# param: record <Object> - subject containg attribute to validate
|
47
|
+
# param: attribute <Symbol> - name of attribute under validation
|
48
|
+
# return: nil
|
49
|
+
def add_errors_or_raise(options, record, attribute)
|
50
|
+
error = options_error(options[:strict])
|
51
|
+
|
52
|
+
raise error unless error.nil?
|
53
|
+
|
54
|
+
record.errors.add(attribute, options[:message])
|
55
|
+
end
|
56
|
+
|
57
|
+
# Helper method to return the base expected error:
|
58
|
+
# ActiveModel::StrictValidationFailed, a custom error, or nil
|
59
|
+
#
|
60
|
+
# @options_error
|
61
|
+
# param: strict_error <true or subclass of Exception> - either the flag
|
62
|
+
# to raise an error or the actual error to raise
|
63
|
+
# return: custom error, ActiveModel::StrictValidationFailed, or nil
|
64
|
+
def options_error(strict_error)
|
65
|
+
if strict_error == true
|
66
|
+
ActiveModel::StrictValidationFailed
|
67
|
+
elsif strict_error.try(:ancestors).try(:include?, Exception)
|
68
|
+
strict_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Helper method to convert a symbol into a class constant
|
73
|
+
#
|
74
|
+
# ex:
|
75
|
+
# symbol_class(:string) -> String
|
76
|
+
# symbol_class(:boolean) -> Boolean
|
77
|
+
# symbol_class(:hash) -> Hash
|
78
|
+
#
|
79
|
+
# @symbol_class
|
80
|
+
# param: symbol <Symbol> - symbol to turn into a classconstant
|
81
|
+
# return: class constant of supported types or raises UnsupportedType
|
82
|
+
def symbol_class(symbol)
|
83
|
+
@symbol_class ||= {
|
84
|
+
:array => Array,
|
85
|
+
:boolean => Boolean,
|
86
|
+
:float => Float,
|
87
|
+
:hash => Hash,
|
88
|
+
:integer => Integer,
|
89
|
+
:string => String,
|
90
|
+
:symbol => Symbol,
|
91
|
+
}[symbol] || fail(ValidatesType::UnsupportedType,
|
92
|
+
"Unsupported type #{ symbol.to_s.camelize } given for validates_type.")
|
93
|
+
end
|
94
|
+
|
95
|
+
# Helper method to circumvent active record's coercion
|
96
|
+
#
|
97
|
+
# @type_before_coercion
|
98
|
+
# param: record <Object> - subject of validation
|
99
|
+
# param: value <Variable> - current value of attribute
|
100
|
+
# return: the value of the attribute before active record's coercion
|
101
|
+
# or the current value
|
102
|
+
def type_before_coercion(record, attribute, value)
|
103
|
+
record.try(:"#{ attribute }_before_type_cast") || value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module ClassMethods
|
108
|
+
# Validates the type of an attribute with supported types:
|
109
|
+
# - :array
|
110
|
+
# - :boolean
|
111
|
+
# - :float
|
112
|
+
# - :hash
|
113
|
+
# - :integer
|
114
|
+
# - :string
|
115
|
+
# - :symbol
|
116
|
+
#
|
117
|
+
# class Foo
|
118
|
+
# include ActiveModel::Validations
|
119
|
+
#
|
120
|
+
# attr_accessor :thing, :something
|
121
|
+
#
|
122
|
+
# validates_type :thing, :boolean
|
123
|
+
# validates_type :something, :array
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# @validates_type
|
127
|
+
# param: attribute_name <Symbol> - name of attribute to validate
|
128
|
+
# param: attribute_type <Symbol> - type of attribute to validate against
|
129
|
+
# param: options <Hash> - other common options to validate methods calls
|
130
|
+
# i.e. message: 'my custom error message'
|
131
|
+
# return: nil
|
132
|
+
def validates_type(attribute_name, attribute_type, options = {})
|
133
|
+
args = ValidatesType::Arguments.new(attribute_name, attribute_type, options)
|
134
|
+
validates_with TypeValidator, _merge_attributes(args.to_validation_attributes)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe 'ValidatesType' do
|
4
|
+
context 'validates_type :attribute' do
|
5
|
+
subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:string) }
|
6
|
+
|
7
|
+
it 'adds a validator to the subject' do
|
8
|
+
klass = subject.class
|
9
|
+
expect(klass.validators).to_not be_empty
|
10
|
+
expect(klass.validators).to include(ActiveModel::Validations::TypeValidator)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'adds the correct validator to the subject' do
|
14
|
+
validator = subject.class.validators.find { |v| v.is_a?(ActiveModel::Validations::TypeValidator) }
|
15
|
+
expect(validator.options[:type]).to eq(:string)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'validates :attribute, type: { type: type }.merge(other_options)' do
|
20
|
+
subject { ActiveModel::TypeValidationTestClass.set_accessor_and_validator(:string) }
|
21
|
+
|
22
|
+
it 'adds a validator to the subject' do
|
23
|
+
klass = subject.class
|
24
|
+
expect(klass.validators).to_not be_empty
|
25
|
+
expect(klass.validators).to include(ActiveModel::Validations::TypeValidator)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'adds the correct validator to the subject' do
|
29
|
+
validator = subject.class.validators.find { |v| v.is_a?(ActiveModel::Validations::TypeValidator) }
|
30
|
+
expect(validator.options[:type]).to eq(:string)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,319 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe 'ValidatesType' do
|
4
|
+
%i[set_accessor_and_long_validator
|
5
|
+
set_accessor_and_validator].each do |validate_version|
|
6
|
+
context "#{ validate_version }" do
|
7
|
+
context 'custom modifiers' do
|
8
|
+
before do
|
9
|
+
subject.attribute = value
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'message' do
|
13
|
+
subject do
|
14
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
15
|
+
:string, message: 'is not a String!')
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when validation fails' do
|
19
|
+
let(:value) { 1 }
|
20
|
+
|
21
|
+
specify do
|
22
|
+
subject.validate
|
23
|
+
expect(subject.errors.messages[:attribute][0]).to match(/is not a String!/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'allow_nil' do
|
29
|
+
subject do
|
30
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
31
|
+
:string, allow_nil: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'field value is nil' do
|
35
|
+
let(:value) { nil }
|
36
|
+
|
37
|
+
specify do
|
38
|
+
expect(subject).to be_valid
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'field value is not nil' do
|
43
|
+
context 'field value is the specified type' do
|
44
|
+
let(:value) { 'I am a string'}
|
45
|
+
|
46
|
+
specify do
|
47
|
+
expect(subject).to be_valid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'field value is not specified type' do
|
52
|
+
let(:value) { -1 }
|
53
|
+
|
54
|
+
specify do
|
55
|
+
expect(subject).to_not be_valid
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'allow_blank' do
|
62
|
+
subject do
|
63
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
64
|
+
:integer, allow_blank: true)
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'field value is nil' do
|
68
|
+
let(:value) { nil }
|
69
|
+
|
70
|
+
specify do
|
71
|
+
expect(subject).to be_valid
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'field value is an empty string' do
|
76
|
+
let(:value) { '' }
|
77
|
+
|
78
|
+
specify do
|
79
|
+
expect(subject).to be_valid
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'field value is not nil' do
|
84
|
+
context 'field value is the specified type' do
|
85
|
+
let(:value) { 1 }
|
86
|
+
|
87
|
+
specify do
|
88
|
+
expect(subject).to be_valid
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'field value is not specified type' do
|
93
|
+
let(:value) { { foo: :bar } }
|
94
|
+
|
95
|
+
specify do
|
96
|
+
expect(subject).to_not be_valid
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'strict' do
|
103
|
+
subject do
|
104
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
105
|
+
:string, strict: true)
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'field value is expected type' do
|
109
|
+
let(:value) { 'I am a string' }
|
110
|
+
|
111
|
+
specify do
|
112
|
+
expect do
|
113
|
+
subject.valid?
|
114
|
+
end.to_not raise_error
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'field value is not expected type' do
|
119
|
+
let(:value) { -1 }
|
120
|
+
|
121
|
+
specify do
|
122
|
+
expect do
|
123
|
+
subject.valid?
|
124
|
+
end.to raise_error(ActiveModel::StrictValidationFailed)
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'with a custom error class' do
|
128
|
+
class UhOhSpaghettios < StandardError; end
|
129
|
+
subject do
|
130
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
131
|
+
:string, strict: UhOhSpaghettios)
|
132
|
+
end
|
133
|
+
|
134
|
+
specify do
|
135
|
+
expect do
|
136
|
+
subject.valid?
|
137
|
+
end.to raise_error(UhOhSpaghettios)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'if' do
|
144
|
+
subject do
|
145
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
146
|
+
:string, if: condition)
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'field value is expected type' do
|
150
|
+
let(:value) { 'I am a string' }
|
151
|
+
|
152
|
+
context 'condition is true' do
|
153
|
+
let(:condition) { ->{ true } }
|
154
|
+
|
155
|
+
specify do
|
156
|
+
expect(subject).to be_valid
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'condition is false' do
|
161
|
+
let(:condition) { ->{ false } }
|
162
|
+
|
163
|
+
specify do
|
164
|
+
expect(subject).to be_valid
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'field value is not expected type' do
|
170
|
+
let(:value) { -1 }
|
171
|
+
|
172
|
+
context 'condition is true' do
|
173
|
+
let(:condition) { ->{ true } }
|
174
|
+
|
175
|
+
specify do
|
176
|
+
expect(subject).to_not be_valid
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'condition is false' do
|
181
|
+
let(:condition) { ->{ false } }
|
182
|
+
|
183
|
+
specify do
|
184
|
+
expect(subject).to be_valid
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe 'unless' do
|
191
|
+
subject do
|
192
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
193
|
+
:string, unless: condition)
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'field value is expected type' do
|
197
|
+
let(:value) { 'I am a string' }
|
198
|
+
|
199
|
+
context 'condition is true' do
|
200
|
+
let(:condition) { ->{ true } }
|
201
|
+
|
202
|
+
specify do
|
203
|
+
expect(subject).to be_valid
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'condition is false' do
|
208
|
+
let(:condition) { ->{ false } }
|
209
|
+
|
210
|
+
specify do
|
211
|
+
expect(subject).to be_valid
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'field value is not expected type' do
|
217
|
+
let(:value) { -1 }
|
218
|
+
|
219
|
+
context 'condition is true' do
|
220
|
+
let(:condition) { ->{ true } }
|
221
|
+
|
222
|
+
specify do
|
223
|
+
expect(subject).to be_valid
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context 'condition is false' do
|
228
|
+
let(:condition) { ->{ false } }
|
229
|
+
|
230
|
+
specify do
|
231
|
+
expect(subject).to_not be_valid
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe 'on' do
|
238
|
+
let(:value) { nil }
|
239
|
+
subject do
|
240
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
241
|
+
:string, on: :some_test_method)
|
242
|
+
end
|
243
|
+
|
244
|
+
before do
|
245
|
+
allow(subject).to receive(:some_test_method) { subject.validate }
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'on: criteria is met' do
|
249
|
+
specify do
|
250
|
+
expect(subject).to receive(:validate)
|
251
|
+
subject.some_test_method
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'on: criteria is not met' do
|
256
|
+
specify do
|
257
|
+
expect(subject).to_not receive(:validate)
|
258
|
+
subject.valid?
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context 'multiple custom modifiers' do
|
265
|
+
before do
|
266
|
+
subject.attribute = value
|
267
|
+
end
|
268
|
+
|
269
|
+
describe 'message: with if:' do
|
270
|
+
subject do
|
271
|
+
ActiveModel::TypeValidationTestClass.send(validate_version,
|
272
|
+
:string, message: 'This is bad!', if: condition)
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'condition is true' do
|
276
|
+
let(:condition) { -> { true } }
|
277
|
+
|
278
|
+
context 'field value is the expected type' do
|
279
|
+
let(:value) { 'I am a string' }
|
280
|
+
|
281
|
+
specify do
|
282
|
+
expect(subject).to be_valid
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'field value is not the expected type' do
|
287
|
+
let(:value) { -1 }
|
288
|
+
|
289
|
+
specify do
|
290
|
+
expect(subject).to_not be_valid
|
291
|
+
expect(subject.errors.messages[:attribute][0]).to match(/This is bad!/)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context 'condition is false' do
|
297
|
+
let(:condition) { -> { false } }
|
298
|
+
|
299
|
+
context 'field value is the expected type' do
|
300
|
+
let(:value) { 'I am a string' }
|
301
|
+
|
302
|
+
specify do
|
303
|
+
expect(subject).to be_valid
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context 'field value is not the expected type' do
|
308
|
+
let(:value) { -1 }
|
309
|
+
|
310
|
+
specify do
|
311
|
+
expect(subject).to be_valid
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|