omg-activemodel 8.0.0.alpha1
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/CHANGELOG.md +67 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +266 -0
- data/lib/active_model/access.rb +16 -0
- data/lib/active_model/api.rb +99 -0
- data/lib/active_model/attribute/user_provided_default.rb +55 -0
- data/lib/active_model/attribute.rb +277 -0
- data/lib/active_model/attribute_assignment.rb +78 -0
- data/lib/active_model/attribute_methods.rb +592 -0
- data/lib/active_model/attribute_mutation_tracker.rb +189 -0
- data/lib/active_model/attribute_registration.rb +117 -0
- data/lib/active_model/attribute_set/builder.rb +182 -0
- data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
- data/lib/active_model/attribute_set.rb +118 -0
- data/lib/active_model/attributes.rb +165 -0
- data/lib/active_model/callbacks.rb +155 -0
- data/lib/active_model/conversion.rb +121 -0
- data/lib/active_model/deprecator.rb +7 -0
- data/lib/active_model/dirty.rb +416 -0
- data/lib/active_model/error.rb +208 -0
- data/lib/active_model/errors.rb +547 -0
- data/lib/active_model/forbidden_attributes_protection.rb +33 -0
- data/lib/active_model/gem_version.rb +17 -0
- data/lib/active_model/lint.rb +118 -0
- data/lib/active_model/locale/en.yml +38 -0
- data/lib/active_model/model.rb +78 -0
- data/lib/active_model/naming.rb +359 -0
- data/lib/active_model/nested_error.rb +22 -0
- data/lib/active_model/railtie.rb +24 -0
- data/lib/active_model/secure_password.rb +231 -0
- data/lib/active_model/serialization.rb +198 -0
- data/lib/active_model/serializers/json.rb +154 -0
- data/lib/active_model/translation.rb +78 -0
- data/lib/active_model/type/big_integer.rb +36 -0
- data/lib/active_model/type/binary.rb +62 -0
- data/lib/active_model/type/boolean.rb +48 -0
- data/lib/active_model/type/date.rb +78 -0
- data/lib/active_model/type/date_time.rb +88 -0
- data/lib/active_model/type/decimal.rb +107 -0
- data/lib/active_model/type/float.rb +64 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +53 -0
- data/lib/active_model/type/helpers/mutable.rb +24 -0
- data/lib/active_model/type/helpers/numeric.rb +61 -0
- data/lib/active_model/type/helpers/time_value.rb +127 -0
- data/lib/active_model/type/helpers/timezone.rb +23 -0
- data/lib/active_model/type/helpers.rb +7 -0
- data/lib/active_model/type/immutable_string.rb +71 -0
- data/lib/active_model/type/integer.rb +113 -0
- data/lib/active_model/type/registry.rb +37 -0
- data/lib/active_model/type/serialize_cast_value.rb +47 -0
- data/lib/active_model/type/string.rb +43 -0
- data/lib/active_model/type/time.rb +87 -0
- data/lib/active_model/type/value.rb +157 -0
- data/lib/active_model/type.rb +55 -0
- data/lib/active_model/validations/absence.rb +33 -0
- data/lib/active_model/validations/acceptance.rb +113 -0
- data/lib/active_model/validations/callbacks.rb +119 -0
- data/lib/active_model/validations/clusivity.rb +54 -0
- data/lib/active_model/validations/comparability.rb +18 -0
- data/lib/active_model/validations/comparison.rb +90 -0
- data/lib/active_model/validations/confirmation.rb +80 -0
- data/lib/active_model/validations/exclusion.rb +49 -0
- data/lib/active_model/validations/format.rb +112 -0
- data/lib/active_model/validations/helper_methods.rb +15 -0
- data/lib/active_model/validations/inclusion.rb +47 -0
- data/lib/active_model/validations/length.rb +130 -0
- data/lib/active_model/validations/numericality.rb +222 -0
- data/lib/active_model/validations/presence.rb +39 -0
- data/lib/active_model/validations/resolve_value.rb +26 -0
- data/lib/active_model/validations/validates.rb +175 -0
- data/lib/active_model/validations/with.rb +154 -0
- data/lib/active_model/validations.rb +489 -0
- data/lib/active_model/validator.rb +190 -0
- data/lib/active_model/version.rb +10 -0
- data/lib/active_model.rb +84 -0
- metadata +139 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class PresenceValidator < EachValidator # :nodoc:
|
6
|
+
def validate_each(record, attr_name, value)
|
7
|
+
record.errors.add(attr_name, :blank, **options) if value.blank?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module HelperMethods
|
12
|
+
# Validates that the specified attributes are not blank (as defined by
|
13
|
+
# Object#blank?).
|
14
|
+
#
|
15
|
+
# class Person < ActiveRecord::Base
|
16
|
+
# validates_presence_of :first_name
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The first_name attribute must be in the object and it cannot be blank.
|
20
|
+
#
|
21
|
+
# If you want to validate the presence of a boolean field (where the real
|
22
|
+
# values are +true+ and +false+), you will want to use
|
23
|
+
# <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
|
24
|
+
#
|
25
|
+
# This is due to the way Object#blank? handles boolean values:
|
26
|
+
# <tt>false.blank? # => true</tt>.
|
27
|
+
#
|
28
|
+
# Configuration options:
|
29
|
+
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
|
30
|
+
#
|
31
|
+
# There is also a list of default options supported by every validator:
|
32
|
+
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
33
|
+
# See ActiveModel::Validations::ClassMethods#validates for more information.
|
34
|
+
def validates_presence_of(*attr_names)
|
35
|
+
validates_with PresenceValidator, _merge_attributes(attr_names)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
module ResolveValue # :nodoc:
|
6
|
+
def resolve_value(record, value)
|
7
|
+
case value
|
8
|
+
when Proc
|
9
|
+
if value.arity == 0
|
10
|
+
value.call
|
11
|
+
else
|
12
|
+
value.call(record)
|
13
|
+
end
|
14
|
+
when Symbol
|
15
|
+
record.send(value)
|
16
|
+
else
|
17
|
+
if value.respond_to?(:call)
|
18
|
+
value.call(record)
|
19
|
+
else
|
20
|
+
value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/slice"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module Validations
|
7
|
+
module ClassMethods
|
8
|
+
# This method is a shortcut to all default validators and any custom
|
9
|
+
# validator classes ending in 'Validator'. Note that \Rails default
|
10
|
+
# validators can be overridden inside specific classes by creating
|
11
|
+
# custom validator classes in their place such as PresenceValidator.
|
12
|
+
#
|
13
|
+
# Examples of using the default Rails validators:
|
14
|
+
#
|
15
|
+
# validates :username, absence: true
|
16
|
+
# validates :terms, acceptance: true
|
17
|
+
# validates :password, confirmation: true
|
18
|
+
# validates :username, exclusion: { in: %w(admin superuser) }
|
19
|
+
# validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
|
20
|
+
# validates :age, inclusion: { in: 0..9 }
|
21
|
+
# validates :first_name, length: { maximum: 30 }
|
22
|
+
# validates :age, numericality: true
|
23
|
+
# validates :username, presence: true
|
24
|
+
#
|
25
|
+
# The power of the +validates+ method comes when using custom validators
|
26
|
+
# and default validators in one call for a given attribute.
|
27
|
+
#
|
28
|
+
# class EmailValidator < ActiveModel::EachValidator
|
29
|
+
# def validate_each(record, attribute, value)
|
30
|
+
# record.errors.add attribute, (options[:message] || "is not an email") unless
|
31
|
+
# /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# class Person
|
36
|
+
# include ActiveModel::Validations
|
37
|
+
# attr_accessor :name, :email
|
38
|
+
#
|
39
|
+
# validates :name, presence: true, length: { maximum: 100 }
|
40
|
+
# validates :email, presence: true, email: true
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# Validator classes may also exist within the class being validated
|
44
|
+
# allowing custom modules of validators to be included as needed.
|
45
|
+
#
|
46
|
+
# class Film
|
47
|
+
# include ActiveModel::Validations
|
48
|
+
#
|
49
|
+
# class TitleValidator < ActiveModel::EachValidator
|
50
|
+
# def validate_each(record, attribute, value)
|
51
|
+
# record.errors.add attribute, "must start with 'the'" unless /\Athe/i.match?(value)
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# validates :name, title: true
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# Additionally validator classes may be in another namespace and still
|
59
|
+
# used within any class.
|
60
|
+
#
|
61
|
+
# validates :name, :'film/title' => true
|
62
|
+
#
|
63
|
+
# The validators hash can also handle regular expressions, ranges, arrays
|
64
|
+
# and strings in shortcut form.
|
65
|
+
#
|
66
|
+
# validates :email, format: /@/
|
67
|
+
# validates :role, inclusion: %w(admin contributor)
|
68
|
+
# validates :password, length: 6..20
|
69
|
+
#
|
70
|
+
# When using shortcut form, ranges and arrays are passed to your
|
71
|
+
# validator's initializer as <tt>options[:in]</tt> while other types
|
72
|
+
# including regular expressions and strings are passed as <tt>options[:with]</tt>.
|
73
|
+
#
|
74
|
+
# There is also a list of options that could be used along with validators:
|
75
|
+
#
|
76
|
+
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
77
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
78
|
+
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
79
|
+
# <tt>on: :custom_validation_context</tt> or
|
80
|
+
# <tt>on: [:create, :custom_validation_context]</tt>)
|
81
|
+
# * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
|
82
|
+
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
83
|
+
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
|
84
|
+
# proc or string should return or evaluate to a +true+ or +false+ value.
|
85
|
+
# * <tt>:unless</tt> - Specifies a method, proc, or string to call to determine
|
86
|
+
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
|
87
|
+
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
88
|
+
# method, proc, or string should return or evaluate to a +true+ or
|
89
|
+
# +false+ value.
|
90
|
+
# * <tt>:allow_nil</tt> - Skip validation if the attribute is +nil+.
|
91
|
+
# * <tt>:allow_blank</tt> - Skip validation if the attribute is blank.
|
92
|
+
# * <tt>:strict</tt> - If the <tt>:strict</tt> option is set to true
|
93
|
+
# will raise ActiveModel::StrictValidationFailed instead of adding the error.
|
94
|
+
# <tt>:strict</tt> option can also be set to any other exception.
|
95
|
+
#
|
96
|
+
# Example:
|
97
|
+
#
|
98
|
+
# validates :password, presence: true, confirmation: true, if: :password_required?
|
99
|
+
# validates :token, length: { is: 24 }, strict: TokenLengthException
|
100
|
+
#
|
101
|
+
#
|
102
|
+
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+
|
103
|
+
# and +:message+ can be given to one specific validator, as a hash:
|
104
|
+
#
|
105
|
+
# validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
|
106
|
+
def validates(*attributes)
|
107
|
+
defaults = attributes.extract_options!.dup
|
108
|
+
validations = defaults.slice!(*_validates_default_keys)
|
109
|
+
|
110
|
+
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
|
111
|
+
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
|
112
|
+
|
113
|
+
defaults[:attributes] = attributes
|
114
|
+
|
115
|
+
validations.each do |key, options|
|
116
|
+
key = "#{key.to_s.camelize}Validator"
|
117
|
+
|
118
|
+
begin
|
119
|
+
validator = const_get(key)
|
120
|
+
rescue NameError
|
121
|
+
raise ArgumentError, "Unknown validator: '#{key}'"
|
122
|
+
end
|
123
|
+
|
124
|
+
next unless options
|
125
|
+
|
126
|
+
validates_with(validator, defaults.merge(_parse_validates_options(options)))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# This method is used to define validations that cannot be corrected by end
|
131
|
+
# users and are considered exceptional. So each validator defined with bang
|
132
|
+
# or <tt>:strict</tt> option set to <tt>true</tt> will always raise
|
133
|
+
# ActiveModel::StrictValidationFailed instead of adding error
|
134
|
+
# when validation fails. See <tt>validates</tt> for more information about
|
135
|
+
# the validation itself.
|
136
|
+
#
|
137
|
+
# class Person
|
138
|
+
# include ActiveModel::Validations
|
139
|
+
#
|
140
|
+
# attr_accessor :name
|
141
|
+
# validates! :name, presence: true
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# person = Person.new
|
145
|
+
# person.name = ''
|
146
|
+
# person.valid?
|
147
|
+
# # => ActiveModel::StrictValidationFailed: Name can't be blank
|
148
|
+
def validates!(*attributes)
|
149
|
+
options = attributes.extract_options!
|
150
|
+
options[:strict] = true
|
151
|
+
validates(*(attributes << options))
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
# When creating custom validators, it might be useful to be able to specify
|
156
|
+
# additional default keys. This can be done by overwriting this method.
|
157
|
+
def _validates_default_keys
|
158
|
+
[:if, :unless, :on, :allow_blank, :allow_nil, :strict]
|
159
|
+
end
|
160
|
+
|
161
|
+
def _parse_validates_options(options)
|
162
|
+
case options
|
163
|
+
when TrueClass
|
164
|
+
{}
|
165
|
+
when Hash
|
166
|
+
options
|
167
|
+
when Range, Array
|
168
|
+
{ in: options }
|
169
|
+
else
|
170
|
+
{ with: options }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/extract_options"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module Validations
|
7
|
+
class WithValidator < EachValidator # :nodoc:
|
8
|
+
def validate_each(record, attr, val)
|
9
|
+
method_name = options[:with]
|
10
|
+
|
11
|
+
if record.method(method_name).arity == 0
|
12
|
+
record.send method_name
|
13
|
+
else
|
14
|
+
record.send method_name, attr
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# Passes the record off to the class or classes specified and allows them
|
21
|
+
# to add errors based on more complex conditions.
|
22
|
+
#
|
23
|
+
# class Person
|
24
|
+
# include ActiveModel::Validations
|
25
|
+
# validates_with MyValidator
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# class MyValidator < ActiveModel::Validator
|
29
|
+
# def validate(record)
|
30
|
+
# if some_complex_logic
|
31
|
+
# record.errors.add :base, 'This record is invalid'
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# private
|
36
|
+
# def some_complex_logic
|
37
|
+
# # ...
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# You may also pass it multiple classes, like so:
|
42
|
+
#
|
43
|
+
# class Person
|
44
|
+
# include ActiveModel::Validations
|
45
|
+
# validates_with MyValidator, MyOtherValidator, on: :create
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# There is no default error message for +validates_with+. You must
|
49
|
+
# manually add errors to the record's errors collection in the validator
|
50
|
+
# class.
|
51
|
+
#
|
52
|
+
# To implement the validate method, you must have a +record+ parameter
|
53
|
+
# defined, which is the record to be validated.
|
54
|
+
#
|
55
|
+
# Configuration options:
|
56
|
+
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
57
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
58
|
+
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
59
|
+
# <tt>on: :custom_validation_context</tt> or
|
60
|
+
# <tt>on: [:create, :custom_validation_context]</tt>)
|
61
|
+
# * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
|
62
|
+
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
63
|
+
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
|
64
|
+
# The method, proc, or string should return or evaluate to a +true+ or
|
65
|
+
# +false+ value.
|
66
|
+
# * <tt>:unless</tt> - Specifies a method, proc, or string to call to
|
67
|
+
# determine if the validation should not occur
|
68
|
+
# (e.g. <tt>unless: :skip_validation</tt>, or
|
69
|
+
# <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>).
|
70
|
+
# The method, proc, or string should return or evaluate to a +true+ or
|
71
|
+
# +false+ value.
|
72
|
+
# * <tt>:strict</tt> - Specifies whether validation should be strict.
|
73
|
+
# See <tt>ActiveModel::Validations#validates!</tt> for more information.
|
74
|
+
#
|
75
|
+
# If you pass any additional configuration options, they will be passed
|
76
|
+
# to the class and available as +options+:
|
77
|
+
#
|
78
|
+
# class Person
|
79
|
+
# include ActiveModel::Validations
|
80
|
+
# validates_with MyValidator, my_custom_key: 'my custom value'
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# class MyValidator < ActiveModel::Validator
|
84
|
+
# def validate(record)
|
85
|
+
# options[:my_custom_key] # => "my custom value"
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
def validates_with(*args, &block)
|
89
|
+
options = args.extract_options!
|
90
|
+
options[:class] = self
|
91
|
+
|
92
|
+
args.each do |klass|
|
93
|
+
validator = klass.new(options.dup, &block)
|
94
|
+
|
95
|
+
if validator.respond_to?(:attributes) && !validator.attributes.empty?
|
96
|
+
validator.attributes.each do |attribute|
|
97
|
+
_validators[attribute.to_sym] << validator
|
98
|
+
end
|
99
|
+
else
|
100
|
+
_validators[nil] << validator
|
101
|
+
end
|
102
|
+
|
103
|
+
validate(validator, options)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Passes the record off to the class or classes specified and allows them
|
109
|
+
# to add errors based on more complex conditions.
|
110
|
+
#
|
111
|
+
# class Person
|
112
|
+
# include ActiveModel::Validations
|
113
|
+
#
|
114
|
+
# validate :instance_validations
|
115
|
+
#
|
116
|
+
# def instance_validations
|
117
|
+
# validates_with MyValidator
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# Please consult the class method documentation for more information on
|
122
|
+
# creating your own validator.
|
123
|
+
#
|
124
|
+
# You may also pass it multiple classes, like so:
|
125
|
+
#
|
126
|
+
# class Person
|
127
|
+
# include ActiveModel::Validations
|
128
|
+
#
|
129
|
+
# validate :instance_validations, on: :create
|
130
|
+
#
|
131
|
+
# def instance_validations
|
132
|
+
# validates_with MyValidator, MyOtherValidator
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# Standard configuration options (<tt>:on</tt>, <tt>:if</tt> and
|
137
|
+
# <tt>:unless</tt>), which are available on the class version of
|
138
|
+
# +validates_with+, should instead be placed on the +validates+ method
|
139
|
+
# as these are applied and tested in the callback.
|
140
|
+
#
|
141
|
+
# If you pass any additional configuration options, they will be passed
|
142
|
+
# to the class and available as +options+, please refer to the
|
143
|
+
# class version of this method for more information.
|
144
|
+
def validates_with(*args, &block)
|
145
|
+
options = args.extract_options!
|
146
|
+
options[:class] = self.class
|
147
|
+
|
148
|
+
args.each do |klass|
|
149
|
+
validator = klass.new(options.dup, &block)
|
150
|
+
validator.validate(self)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|