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.
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