active_model_type_validator 1.0.0
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/.yardopts +3 -0
- data/Appraisals +3 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.md +22 -0
- data/README.md +74 -0
- data/Rakefile +8 -0
- data/active_model_type_validator.gemspec +37 -0
- data/gemfiles/rails_4.2.gemfile +7 -0
- data/lib/active_model_type_validator.rb +3 -0
- data/lib/active_model_type_validator/contents.rb +30 -0
- data/lib/active_model_type_validator/locale/en.yml +4 -0
- data/lib/active_model_type_validator/object_type.rb +99 -0
- data/lib/active_model_type_validator/version.rb +7 -0
- data/test/active_model_type_validator/active_model_type_validator_test.rb +11 -0
- data/test/active_model_type_validator/contents_test.rb +129 -0
- data/test/active_model_type_validator/object_type_test.rb +201 -0
- data/test/app/rails_4.2/Rakefile +6 -0
- data/test/app/rails_4.2/app/assets/javascripts/application.js +16 -0
- data/test/app/rails_4.2/app/assets/stylesheets/application.css +15 -0
- data/test/app/rails_4.2/app/controllers/application_controller.rb +5 -0
- data/test/app/rails_4.2/app/helpers/application_helper.rb +2 -0
- data/test/app/rails_4.2/app/views/layouts/application.html.erb +14 -0
- data/test/app/rails_4.2/bin/bundle +3 -0
- data/test/app/rails_4.2/bin/rails +8 -0
- data/test/app/rails_4.2/bin/rake +8 -0
- data/test/app/rails_4.2/bin/setup +29 -0
- data/test/app/rails_4.2/bin/spring +15 -0
- data/test/app/rails_4.2/config.ru +4 -0
- data/test/app/rails_4.2/config/application.rb +26 -0
- data/test/app/rails_4.2/config/boot.rb +3 -0
- data/test/app/rails_4.2/config/database.yml +25 -0
- data/test/app/rails_4.2/config/environment.rb +5 -0
- data/test/app/rails_4.2/config/environments/development.rb +41 -0
- data/test/app/rails_4.2/config/environments/production.rb +79 -0
- data/test/app/rails_4.2/config/environments/test.rb +42 -0
- data/test/app/rails_4.2/config/initializers/assets.rb +11 -0
- data/test/app/rails_4.2/config/initializers/backtrace_silencers.rb +7 -0
- data/test/app/rails_4.2/config/initializers/cookies_serializer.rb +3 -0
- data/test/app/rails_4.2/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/app/rails_4.2/config/initializers/inflections.rb +16 -0
- data/test/app/rails_4.2/config/initializers/mime_types.rb +4 -0
- data/test/app/rails_4.2/config/initializers/session_store.rb +3 -0
- data/test/app/rails_4.2/config/initializers/wrap_parameters.rb +14 -0
- data/test/app/rails_4.2/config/locales/en.yml +23 -0
- data/test/app/rails_4.2/config/routes.rb +56 -0
- data/test/app/rails_4.2/config/secrets.yml +22 -0
- data/test/app/rails_4.2/db/schema.rb +0 -0
- data/test/app/rails_4.2/db/seeds.rb +7 -0
- data/test/app/rails_4.2/public/404.html +67 -0
- data/test/app/rails_4.2/public/422.html +67 -0
- data/test/app/rails_4.2/public/500.html +66 -0
- data/test/app/rails_4.2/public/favicon.ico +0 -0
- data/test/app/rails_4.2/public/robots.txt +5 -0
- data/test/test_helper.rb +20 -0
- metadata +260 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 72c8544aafca46e9293835f295a5a8e87013d4c6
|
4
|
+
data.tar.gz: 9f9b086ba8474d5ed70057371c69f538455ea17a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2769e445e21187b1108d9863d5cbbb2dd78996eb396926c14397072d97ff090811aa0f40422365addac2a41ca732fbd319c01eaecc1dcb0c2815e3192f93713
|
7
|
+
data.tar.gz: ae548970359df5bc19f1ab603935d5e50ff139f19b4d0950fabf76d3733fd819410ce0f7f591bf0d0012923dae3e262ad0501f8a3d2c16199e2094cafada4da8
|
data/.yardopts
ADDED
data/Appraisals
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Todd Knarr
|
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,74 @@
|
|
1
|
+
# ActiveModelTypeValidator
|
2
|
+
|
3
|
+
Provides two ActiveModel validators:
|
4
|
+
|
5
|
+
1. One to validate that an attribute is of the correct type. Multiple types are allowed.
|
6
|
+
2. One to validate the contents of an attribute by calling `#valid?` on it, performing
|
7
|
+
the same job as ActiveRecord's `validates_associated`.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'active_model_type_validator'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install active_model_type_validator
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
The usage is fairly standard. The `object_type` validator takes one option `:type` which
|
28
|
+
is either a type name (string, symbol or type) or an array of type names. The `contents`
|
29
|
+
validator needs no options. Both take the standard options all validators do, except
|
30
|
+
that `object_type` ignores the `:allow_blank` option (you should use `:allow_nil` if you
|
31
|
+
need equivalent behavior).
|
32
|
+
|
33
|
+
Examples:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class MyClass
|
37
|
+
include ActiveModel::Model
|
38
|
+
include ActiveModel::Validations
|
39
|
+
|
40
|
+
validates :string_field, object_type: { type: String }
|
41
|
+
validates :integer_field, object_type: { type: Integer }
|
42
|
+
validates :numeric_field, object_type: { type: [Integer, Float] }
|
43
|
+
validates :object_field, contents: true, object_type: { type: ChildClass }
|
44
|
+
|
45
|
+
attr_accessor :string_field
|
46
|
+
attr_accessor :integer_field
|
47
|
+
attr_accessor :numeric_field
|
48
|
+
attr_accessor :object_field
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@string_field = 'my string'
|
52
|
+
@integer_field = 42
|
53
|
+
@numeric_field = 7
|
54
|
+
@object_field = ChildClass.new
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
The `contents` validator adds what's equivalent to ActiveModel's `associated` validator,
|
61
|
+
passing if calling `#valid?` on the attribute returns true.
|
62
|
+
|
63
|
+
The `object_type` validator lets you test ActiveModel objects used in an API to make sure
|
64
|
+
they contain objects of the expected types rather than something you aren't expecting. It
|
65
|
+
fills in a gap I was annoyed by when I validate incoming API objects immediately upon
|
66
|
+
receiving them and go no further if they fail validation.
|
67
|
+
|
68
|
+
## Contributing
|
69
|
+
|
70
|
+
1. Fork it ( https://github.com/tknarr/active_model_type_validator/fork )
|
71
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
72
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
73
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
74
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'active_model_type_validator/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "active_model_type_validator"
|
9
|
+
spec.version = ActiveModelTypeValidator::VERSION
|
10
|
+
spec.date = Time.now.strftime('%Y-%m-%d')
|
11
|
+
spec.author = 'Todd Knarr'
|
12
|
+
spec.email = 'tknarr@silverglass.org'
|
13
|
+
spec.summary = %q{ActiveModel validators to validate object types and contained/associated objects.}
|
14
|
+
spec.description = %q{ActiveModel validators that validate the type of attributes, and that do recursive validation of contained objects parallel to ActiveRecord's validates_associated.}
|
15
|
+
spec.homepage = 'https://github.com/tknarr/active_model_type_validator'
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = Dir.glob('lib/**/*').select { |path| File.file?(path) } +
|
19
|
+
[ __FILE__ ]
|
20
|
+
spec.test_files = Dir.glob('test/**/*').select { |path| File.file?(path) && !File.fnmatch('*.{log,sqlite3}', path, File::FNM_EXTGLOB) } +
|
21
|
+
Dir.glob('gemfiles/*.gemfile') + [ 'Rakefile', 'Appraisals' ]
|
22
|
+
spec.extra_rdoc_files = [ 'README.md', 'CHANGELOG.md', 'LICENSE.md', '.yardopts' ]
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_dependency 'rails', '~> 4.2'
|
26
|
+
spec.add_dependency 'activemodel', '~> 4.2'
|
27
|
+
|
28
|
+
# Development
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
spec.add_development_dependency 'yard', '~> 0'
|
32
|
+
|
33
|
+
# Testing
|
34
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
35
|
+
spec.add_development_dependency 'appraisal', '~> 2'
|
36
|
+
spec.add_development_dependency 'sqlite3', '~> 1'
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
|
3
|
+
module Validations
|
4
|
+
|
5
|
+
# Implements the mechanics of <tt>validates_contents_of</tt>.
|
6
|
+
class ContentsValidator < ActiveModel::EachValidator
|
7
|
+
|
8
|
+
# Standard validation method for an <tt>EachValidator</tt>.
|
9
|
+
def validate_each(object, attribute, value)
|
10
|
+
if Array.wrap(value).reject { |r| r.valid? }.any?
|
11
|
+
object.errors.add(attribute, :invalid, options.merge(:value => value))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module HelperMethods
|
18
|
+
|
19
|
+
# An implementation of the associated validator from ActiveRecord.
|
20
|
+
# All options and behaviors are identical. It simply calls #valid? on each
|
21
|
+
# of the attributes listed and returns the logical AND of them all.
|
22
|
+
def validates_contents_of(*attr_names)
|
23
|
+
validates_with ContentsValidator, _merge_attributes(attr_names)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
|
3
|
+
module Validations
|
4
|
+
|
5
|
+
# Implements the mechanics of <tt>validates_object_type_of</tt>.
|
6
|
+
#
|
7
|
+
# The standard <tt>#initialize</tt> and <tt>#validate_each</tt> methods are implemented.
|
8
|
+
class ObjectTypeValidator < EachValidator
|
9
|
+
|
10
|
+
# Standard initializer for an <tt>EachValidator</tt>.
|
11
|
+
def initialize(options)
|
12
|
+
@types = []
|
13
|
+
# Remove :allow_blank since it's nonsensical when testing types
|
14
|
+
options.delete(:allow_blank)
|
15
|
+
# Set :allow_nil true if it's not present
|
16
|
+
t = options[:allow_nil]
|
17
|
+
options[:allow_nil] = true if t.nil?
|
18
|
+
# Filter out missing or blank type entries and convert others
|
19
|
+
# to class via constantize.
|
20
|
+
t = Array(options.delete(:type))
|
21
|
+
t.each do |te|
|
22
|
+
if te.is_a?(Class)
|
23
|
+
@types.append(te)
|
24
|
+
elsif te.is_a?(String)
|
25
|
+
begin
|
26
|
+
@types.append(te.constantize) unless te.blank?
|
27
|
+
rescue NameError
|
28
|
+
raise ArgumentError, "Type #{te} is invalid"
|
29
|
+
end
|
30
|
+
elsif te.is_a?(Symbol)
|
31
|
+
begin
|
32
|
+
@types.append(te.to_s.constantize)
|
33
|
+
rescue NameError
|
34
|
+
raise ArgumentError, "Type #{te.to_s} is invalid"
|
35
|
+
end
|
36
|
+
else
|
37
|
+
raise ArgumentError, "Type #{te.to_s} is a #{te.class.name} which is invalid"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
raise ArgumentError, 'At least one type must be given' if @types.empty?
|
41
|
+
super(options)
|
42
|
+
check_validity!
|
43
|
+
end
|
44
|
+
|
45
|
+
# Standard validation method for an <tt>EachValidator</tt>.
|
46
|
+
def validate_each(record, attr_name, value)
|
47
|
+
errors_options = options.except(:type)
|
48
|
+
errors_options[:type] = value.class
|
49
|
+
right_type = false
|
50
|
+
@types.each do |t|
|
51
|
+
# Entries in @types are classes, not strings
|
52
|
+
right_type = true if value.is_a?(t)
|
53
|
+
end
|
54
|
+
record.errors.add(attr_name, (options[:message] || :wrong_type), errors_options) unless right_type
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
module HelperMethods
|
60
|
+
|
61
|
+
# Validates that the specified attributes are of a given type (as defined by <tt>#is_a?</tt>).
|
62
|
+
#
|
63
|
+
# class Person < ActiveRecord::Base
|
64
|
+
# validates_type_of :first_name, type: String
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# The +first_name+ attribute must be in the object and it must be a X.
|
68
|
+
#
|
69
|
+
# If you want to validate the type of a boolean field (where the real
|
70
|
+
# types are +TrueClass+ and +FalseClass+), you will want to use
|
71
|
+
# both those classes in the list of types and set +:allow_nil+ to false.
|
72
|
+
#
|
73
|
+
# By default this validator sets +:allow_nil+ to true to allow nil values to
|
74
|
+
# pass through validation even though +NilClass+ isn't explicitly listed, and
|
75
|
+
# you're expected to use a presence validator if you want the attribute to have
|
76
|
+
# to be present (non-nil). You can set +:allow_nil+ to false to cause even nil
|
77
|
+
# values to be checked against the type list and fail if NilClass isn't listed,
|
78
|
+
# mimicking the effect of a presence validator.
|
79
|
+
#
|
80
|
+
# Configuration options:
|
81
|
+
# * <tt>:type</tt> - An array of class names (or symbols or strings naming class
|
82
|
+
# names) which are legal for values of the attribute.
|
83
|
+
# * <tt>:message</tt> - A custom error message (default is: "cannot be of type X").
|
84
|
+
# Messages can find the offending type name for interpolation in +:type+.
|
85
|
+
#
|
86
|
+
# There is also a list of default options supported by every validator:
|
87
|
+
# +:if+, +:unless+, +:on+, +:allow_nil+, and +:strict+. +:allow_blank+ is
|
88
|
+
# not allowed because it's nonsensical and confusing when applied to type
|
89
|
+
# checking.
|
90
|
+
# See <tt>ActiveModel::Validation#validates</tt> for more information
|
91
|
+
def validates_object_type_of(*attr_names)
|
92
|
+
validates_with ObjectTypeValidator, _merge_attributes(attr_names)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require '../test_helper'
|
2
|
+
require 'active_model_type_validator'
|
3
|
+
|
4
|
+
class ActiveModelTypeValidatorTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
test 'API' do
|
7
|
+
assert_not_nil ActiveModelTypeValidator::VERSION
|
8
|
+
assert_kind_of String, ActiveModelTypeValidator::VERSION
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require '../test_helper'
|
2
|
+
require 'active_model_type_validator'
|
3
|
+
|
4
|
+
class MyClass1
|
5
|
+
include ActiveModel::Model
|
6
|
+
include ActiveModel::Validations
|
7
|
+
validates :a1, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
8
|
+
validates :b1, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
9
|
+
validates :c1, presence: true, contents: true
|
10
|
+
|
11
|
+
attr_accessor :a1
|
12
|
+
attr_accessor :b1
|
13
|
+
attr_accessor :c1
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class MyClass1a
|
18
|
+
include ActiveModel::Model
|
19
|
+
include ActiveModel::Validations
|
20
|
+
validates :a1, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
21
|
+
validates :b1, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
22
|
+
validates :c1, presence: true
|
23
|
+
|
24
|
+
attr_accessor :a1
|
25
|
+
attr_accessor :b1
|
26
|
+
attr_accessor :c1
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class MyClass1b
|
31
|
+
include ActiveModel::Model
|
32
|
+
include ActiveModel::Validations
|
33
|
+
validates :a1, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
34
|
+
validates :b1, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
35
|
+
validates :c1, contents: { allow_nil: true }
|
36
|
+
validates :c2, contents: true
|
37
|
+
|
38
|
+
attr_accessor :a1
|
39
|
+
attr_accessor :b1
|
40
|
+
attr_accessor :c1
|
41
|
+
attr_accessor :c2
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class MyClass2
|
46
|
+
include ActiveModel::Model
|
47
|
+
include ActiveModel::Validations
|
48
|
+
validates :a2, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
49
|
+
validates :b2, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
50
|
+
|
51
|
+
attr_accessor :a2
|
52
|
+
attr_accessor :b2
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class RecursiveTest < ActiveSupport::TestCase
|
57
|
+
|
58
|
+
def setup
|
59
|
+
@model = MyClass1.new
|
60
|
+
@model.a1 = 57
|
61
|
+
@model.b1 = nil
|
62
|
+
@model.c1 = MyClass2.new
|
63
|
+
@model.c1.a2 = 99
|
64
|
+
@model.c1.b2 = nil
|
65
|
+
|
66
|
+
@not_validated = MyClass1a.new
|
67
|
+
@not_validated.a1 = 57
|
68
|
+
@not_validated.b1 = nil
|
69
|
+
@not_validated.c1 = MyClass2.new
|
70
|
+
@not_validated.c1.a2 = 99
|
71
|
+
@not_validated.c1.b2 = nil
|
72
|
+
|
73
|
+
@no_child = MyClass1b.new
|
74
|
+
@no_child.a1 = 57
|
75
|
+
@no_child.b1 = nil
|
76
|
+
@no_child.c1 = MyClass2.new
|
77
|
+
@no_child.c1.a2 = 99
|
78
|
+
@no_child.c1.b2 = nil
|
79
|
+
@no_child.c2 = MyClass2.new
|
80
|
+
@no_child.c2.a2 = 99
|
81
|
+
@no_child.c2.b2 = nil
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
test 'valid parent valid child' do
|
86
|
+
@model.b1 = 17
|
87
|
+
@model.c1.b2 = 23
|
88
|
+
assert @model.valid?
|
89
|
+
end
|
90
|
+
|
91
|
+
test 'valid parent invalid child' do
|
92
|
+
@model.b1 = 17
|
93
|
+
@model.c1.b2 = -23
|
94
|
+
assert_not @model.valid?
|
95
|
+
end
|
96
|
+
|
97
|
+
test 'invalid parent valid child' do
|
98
|
+
@model.b1 = -17
|
99
|
+
@model.c1.b2 = 23
|
100
|
+
assert_not @model.valid?
|
101
|
+
end
|
102
|
+
|
103
|
+
test 'invalid parent invalid child' do
|
104
|
+
@model.b1 = -17
|
105
|
+
@model.c1.b2 = -23
|
106
|
+
assert_not @model.valid?
|
107
|
+
end
|
108
|
+
|
109
|
+
test 'valid parent invalid child not validated' do
|
110
|
+
@not_validated.b1 = 17
|
111
|
+
@not_validated.c1.b2 = -23
|
112
|
+
assert @not_validated.valid?
|
113
|
+
end
|
114
|
+
|
115
|
+
test 'valid parent without child allow nil' do
|
116
|
+
@no_child.b1 = 17
|
117
|
+
@no_child.c1 = nil
|
118
|
+
@no_child.c2.b2 = 23
|
119
|
+
assert @no_child.valid?
|
120
|
+
end
|
121
|
+
|
122
|
+
test 'valid parent without child no allow nil' do
|
123
|
+
@no_child.b1 = 17
|
124
|
+
@no_child.c1.b2 = 23
|
125
|
+
@no_child.c2 = nil
|
126
|
+
assert_not @model.valid?
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|