omg-activemodel 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +67 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +266 -0
  5. data/lib/active_model/access.rb +16 -0
  6. data/lib/active_model/api.rb +99 -0
  7. data/lib/active_model/attribute/user_provided_default.rb +55 -0
  8. data/lib/active_model/attribute.rb +277 -0
  9. data/lib/active_model/attribute_assignment.rb +78 -0
  10. data/lib/active_model/attribute_methods.rb +592 -0
  11. data/lib/active_model/attribute_mutation_tracker.rb +189 -0
  12. data/lib/active_model/attribute_registration.rb +117 -0
  13. data/lib/active_model/attribute_set/builder.rb +182 -0
  14. data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
  15. data/lib/active_model/attribute_set.rb +118 -0
  16. data/lib/active_model/attributes.rb +165 -0
  17. data/lib/active_model/callbacks.rb +155 -0
  18. data/lib/active_model/conversion.rb +121 -0
  19. data/lib/active_model/deprecator.rb +7 -0
  20. data/lib/active_model/dirty.rb +416 -0
  21. data/lib/active_model/error.rb +208 -0
  22. data/lib/active_model/errors.rb +547 -0
  23. data/lib/active_model/forbidden_attributes_protection.rb +33 -0
  24. data/lib/active_model/gem_version.rb +17 -0
  25. data/lib/active_model/lint.rb +118 -0
  26. data/lib/active_model/locale/en.yml +38 -0
  27. data/lib/active_model/model.rb +78 -0
  28. data/lib/active_model/naming.rb +359 -0
  29. data/lib/active_model/nested_error.rb +22 -0
  30. data/lib/active_model/railtie.rb +24 -0
  31. data/lib/active_model/secure_password.rb +231 -0
  32. data/lib/active_model/serialization.rb +198 -0
  33. data/lib/active_model/serializers/json.rb +154 -0
  34. data/lib/active_model/translation.rb +78 -0
  35. data/lib/active_model/type/big_integer.rb +36 -0
  36. data/lib/active_model/type/binary.rb +62 -0
  37. data/lib/active_model/type/boolean.rb +48 -0
  38. data/lib/active_model/type/date.rb +78 -0
  39. data/lib/active_model/type/date_time.rb +88 -0
  40. data/lib/active_model/type/decimal.rb +107 -0
  41. data/lib/active_model/type/float.rb +64 -0
  42. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +53 -0
  43. data/lib/active_model/type/helpers/mutable.rb +24 -0
  44. data/lib/active_model/type/helpers/numeric.rb +61 -0
  45. data/lib/active_model/type/helpers/time_value.rb +127 -0
  46. data/lib/active_model/type/helpers/timezone.rb +23 -0
  47. data/lib/active_model/type/helpers.rb +7 -0
  48. data/lib/active_model/type/immutable_string.rb +71 -0
  49. data/lib/active_model/type/integer.rb +113 -0
  50. data/lib/active_model/type/registry.rb +37 -0
  51. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  52. data/lib/active_model/type/string.rb +43 -0
  53. data/lib/active_model/type/time.rb +87 -0
  54. data/lib/active_model/type/value.rb +157 -0
  55. data/lib/active_model/type.rb +55 -0
  56. data/lib/active_model/validations/absence.rb +33 -0
  57. data/lib/active_model/validations/acceptance.rb +113 -0
  58. data/lib/active_model/validations/callbacks.rb +119 -0
  59. data/lib/active_model/validations/clusivity.rb +54 -0
  60. data/lib/active_model/validations/comparability.rb +18 -0
  61. data/lib/active_model/validations/comparison.rb +90 -0
  62. data/lib/active_model/validations/confirmation.rb +80 -0
  63. data/lib/active_model/validations/exclusion.rb +49 -0
  64. data/lib/active_model/validations/format.rb +112 -0
  65. data/lib/active_model/validations/helper_methods.rb +15 -0
  66. data/lib/active_model/validations/inclusion.rb +47 -0
  67. data/lib/active_model/validations/length.rb +130 -0
  68. data/lib/active_model/validations/numericality.rb +222 -0
  69. data/lib/active_model/validations/presence.rb +39 -0
  70. data/lib/active_model/validations/resolve_value.rb +26 -0
  71. data/lib/active_model/validations/validates.rb +175 -0
  72. data/lib/active_model/validations/with.rb +154 -0
  73. data/lib/active_model/validations.rb +489 -0
  74. data/lib/active_model/validator.rb +190 -0
  75. data/lib/active_model/version.rb +10 -0
  76. data/lib/active_model.rb +84 -0
  77. 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