activemodel 7.0.8.7 → 7.2.2.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 +4 -4
- data/CHANGELOG.md +30 -263
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_model/access.rb +16 -0
- data/lib/active_model/api.rb +5 -5
- data/lib/active_model/attribute/user_provided_default.rb +4 -0
- data/lib/active_model/attribute.rb +27 -2
- data/lib/active_model/attribute_assignment.rb +4 -2
- data/lib/active_model/attribute_methods.rb +145 -85
- data/lib/active_model/attribute_registration.rb +117 -0
- data/lib/active_model/attribute_set.rb +10 -1
- data/lib/active_model/attributes.rb +78 -48
- data/lib/active_model/callbacks.rb +6 -6
- data/lib/active_model/conversion.rb +14 -4
- data/lib/active_model/deprecator.rb +7 -0
- data/lib/active_model/dirty.rb +134 -13
- data/lib/active_model/error.rb +4 -3
- data/lib/active_model/errors.rb +37 -6
- data/lib/active_model/forbidden_attributes_protection.rb +2 -0
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/lint.rb +1 -1
- data/lib/active_model/locale/en.yml +1 -0
- data/lib/active_model/model.rb +34 -2
- data/lib/active_model/naming.rb +29 -10
- data/lib/active_model/railtie.rb +4 -0
- data/lib/active_model/secure_password.rb +62 -24
- data/lib/active_model/serialization.rb +3 -3
- data/lib/active_model/serializers/json.rb +1 -1
- data/lib/active_model/translation.rb +18 -16
- data/lib/active_model/type/big_integer.rb +23 -1
- data/lib/active_model/type/binary.rb +7 -1
- data/lib/active_model/type/boolean.rb +11 -9
- data/lib/active_model/type/date.rb +28 -2
- data/lib/active_model/type/date_time.rb +45 -3
- data/lib/active_model/type/decimal.rb +39 -1
- data/lib/active_model/type/float.rb +30 -1
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +5 -1
- data/lib/active_model/type/helpers/numeric.rb +6 -1
- data/lib/active_model/type/helpers/time_value.rb +50 -13
- data/lib/active_model/type/helpers/timezone.rb +5 -1
- data/lib/active_model/type/immutable_string.rb +37 -1
- data/lib/active_model/type/integer.rb +44 -1
- data/lib/active_model/type/registry.rb +2 -3
- data/lib/active_model/type/serialize_cast_value.rb +47 -0
- data/lib/active_model/type/string.rb +9 -1
- data/lib/active_model/type/time.rb +48 -7
- data/lib/active_model/type/value.rb +17 -1
- data/lib/active_model/type.rb +1 -0
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/callbacks.rb +5 -5
- data/lib/active_model/validations/clusivity.rb +5 -8
- data/lib/active_model/validations/comparability.rb +0 -11
- data/lib/active_model/validations/comparison.rb +16 -8
- data/lib/active_model/validations/format.rb +6 -7
- data/lib/active_model/validations/length.rb +10 -8
- data/lib/active_model/validations/numericality.rb +35 -23
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/resolve_value.rb +26 -0
- data/lib/active_model/validations/validates.rb +4 -4
- data/lib/active_model/validations/with.rb +9 -2
- data/lib/active_model/validations.rb +44 -9
- data/lib/active_model/validator.rb +7 -5
- data/lib/active_model/version.rb +1 -1
- data/lib/active_model.rb +5 -1
- metadata +12 -7
@@ -1,33 +1,67 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_model/attribute_set"
|
4
|
-
require "active_model/attribute/user_provided_default"
|
5
|
-
|
6
3
|
module ActiveModel
|
7
|
-
|
4
|
+
# = Active \Model \Attributes
|
5
|
+
#
|
6
|
+
# The Attributes module allows models to define attributes beyond simple Ruby
|
7
|
+
# readers and writers. Similar to Active Record attributes, which are
|
8
|
+
# typically inferred from the database schema, Active Model Attributes are
|
9
|
+
# aware of data types, can have default values, and can handle casting and
|
10
|
+
# serialization.
|
11
|
+
#
|
12
|
+
# To use Attributes, include the module in your model class and define your
|
13
|
+
# attributes using the +attribute+ macro. It accepts a name, a type, a default
|
14
|
+
# value, and any other options supported by the attribute type.
|
15
|
+
#
|
16
|
+
# ==== Examples
|
17
|
+
#
|
18
|
+
# class Person
|
19
|
+
# include ActiveModel::Attributes
|
20
|
+
#
|
21
|
+
# attribute :name, :string
|
22
|
+
# attribute :active, :boolean, default: true
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# person = Person.new
|
26
|
+
# person.name = "Volmer"
|
27
|
+
#
|
28
|
+
# person.name # => "Volmer"
|
29
|
+
# person.active # => true
|
30
|
+
module Attributes
|
8
31
|
extend ActiveSupport::Concern
|
32
|
+
include ActiveModel::AttributeRegistration
|
9
33
|
include ActiveModel::AttributeMethods
|
10
34
|
|
11
35
|
included do
|
12
36
|
attribute_method_suffix "=", parameters: "value"
|
13
|
-
class_attribute :attribute_types, :_default_attributes, instance_accessor: false
|
14
|
-
self.attribute_types = Hash.new(Type.default_value)
|
15
|
-
self._default_attributes = AttributeSet.new({})
|
16
37
|
end
|
17
38
|
|
18
39
|
module ClassMethods
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
40
|
+
##
|
41
|
+
# :call-seq: attribute(name, cast_type = nil, default: nil, **options)
|
42
|
+
#
|
43
|
+
# Defines a model attribute. In addition to the attribute name, a cast
|
44
|
+
# type and default value may be specified, as well as any options
|
45
|
+
# supported by the given cast type.
|
46
|
+
#
|
47
|
+
# class Person
|
48
|
+
# include ActiveModel::Attributes
|
49
|
+
#
|
50
|
+
# attribute :name, :string
|
51
|
+
# attribute :active, :boolean, default: true
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# person = Person.new
|
55
|
+
# person.name = "Volmer"
|
56
|
+
#
|
57
|
+
# person.name # => "Volmer"
|
58
|
+
# person.active # => true
|
59
|
+
def attribute(name, ...)
|
60
|
+
super
|
27
61
|
define_attribute_method(name)
|
28
62
|
end
|
29
63
|
|
30
|
-
# Returns an array of attribute names as strings
|
64
|
+
# Returns an array of attribute names as strings.
|
31
65
|
#
|
32
66
|
# class Person
|
33
67
|
# include ActiveModel::Attributes
|
@@ -36,18 +70,30 @@ module ActiveModel
|
|
36
70
|
# attribute :age, :integer
|
37
71
|
# end
|
38
72
|
#
|
39
|
-
# Person.attribute_names
|
40
|
-
# # => ["name", "age"]
|
73
|
+
# Person.attribute_names # => ["name", "age"]
|
41
74
|
def attribute_names
|
42
75
|
attribute_types.keys
|
43
76
|
end
|
44
77
|
|
78
|
+
##
|
79
|
+
# :method: type_for_attribute
|
80
|
+
# :call-seq: type_for_attribute(attribute_name, &block)
|
81
|
+
#
|
82
|
+
# Returns the type of the specified attribute after applying any
|
83
|
+
# modifiers. This method is the only valid source of information for
|
84
|
+
# anything related to the types of a model's attributes. The return value
|
85
|
+
# of this method will implement the interface described by
|
86
|
+
# ActiveModel::Type::Value (though the object itself may not subclass it).
|
87
|
+
#--
|
88
|
+
# Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
|
89
|
+
|
90
|
+
##
|
45
91
|
private
|
46
|
-
def define_method_attribute=(
|
92
|
+
def define_method_attribute=(canonical_name, owner:, as: canonical_name)
|
47
93
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
48
|
-
owner,
|
94
|
+
owner, canonical_name, writer: true,
|
49
95
|
) do |temp_method_name, attr_name_expr|
|
50
|
-
owner.define_cached_method("#{
|
96
|
+
owner.define_cached_method(temp_method_name, as: "#{as}=", namespace: :active_model) do |batch|
|
51
97
|
batch <<
|
52
98
|
"def #{temp_method_name}(value)" <<
|
53
99
|
" _write_attribute(#{attr_name_expr}, value)" <<
|
@@ -55,27 +101,9 @@ module ActiveModel
|
|
55
101
|
end
|
56
102
|
end
|
57
103
|
end
|
58
|
-
|
59
|
-
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
60
|
-
private_constant :NO_DEFAULT_PROVIDED
|
61
|
-
|
62
|
-
def define_default_attribute(name, value, type)
|
63
|
-
self._default_attributes = _default_attributes.deep_dup
|
64
|
-
if value == NO_DEFAULT_PROVIDED
|
65
|
-
default_attribute = _default_attributes[name].with_type(type)
|
66
|
-
else
|
67
|
-
default_attribute = Attribute::UserProvidedDefault.new(
|
68
|
-
name,
|
69
|
-
value,
|
70
|
-
type,
|
71
|
-
_default_attributes.fetch(name.to_s) { nil },
|
72
|
-
)
|
73
|
-
end
|
74
|
-
_default_attributes[name] = default_attribute
|
75
|
-
end
|
76
104
|
end
|
77
105
|
|
78
|
-
def initialize(*)
|
106
|
+
def initialize(*) # :nodoc:
|
79
107
|
@attributes = self.class._default_attributes.deep_dup
|
80
108
|
super
|
81
109
|
end
|
@@ -85,7 +113,8 @@ module ActiveModel
|
|
85
113
|
super
|
86
114
|
end
|
87
115
|
|
88
|
-
# Returns a hash of all the attributes with their names as keys and the
|
116
|
+
# Returns a hash of all the attributes with their names as keys and the
|
117
|
+
# values of the attributes as values.
|
89
118
|
#
|
90
119
|
# class Person
|
91
120
|
# include ActiveModel::Attributes
|
@@ -94,14 +123,16 @@ module ActiveModel
|
|
94
123
|
# attribute :age, :integer
|
95
124
|
# end
|
96
125
|
#
|
97
|
-
# person = Person.new
|
98
|
-
# person.
|
99
|
-
#
|
126
|
+
# person = Person.new
|
127
|
+
# person.name = "Francesco"
|
128
|
+
# person.age = 22
|
129
|
+
#
|
130
|
+
# person.attributes # => { "name" => "Francesco", "age" => 22}
|
100
131
|
def attributes
|
101
132
|
@attributes.to_hash
|
102
133
|
end
|
103
134
|
|
104
|
-
# Returns an array of attribute names as strings
|
135
|
+
# Returns an array of attribute names as strings.
|
105
136
|
#
|
106
137
|
# class Person
|
107
138
|
# include ActiveModel::Attributes
|
@@ -111,13 +142,12 @@ module ActiveModel
|
|
111
142
|
# end
|
112
143
|
#
|
113
144
|
# person = Person.new
|
114
|
-
# person.attribute_names
|
115
|
-
# # => ["name", "age"]
|
145
|
+
# person.attribute_names # => ["name", "age"]
|
116
146
|
def attribute_names
|
117
147
|
@attributes.keys
|
118
148
|
end
|
119
149
|
|
120
|
-
def freeze
|
150
|
+
def freeze # :nodoc:
|
121
151
|
@attributes = @attributes.clone.freeze unless frozen?
|
122
152
|
super
|
123
153
|
end
|
@@ -4,14 +4,14 @@ require "active_support/core_ext/array/extract_options"
|
|
4
4
|
require "active_support/core_ext/hash/keys"
|
5
5
|
|
6
6
|
module ActiveModel
|
7
|
-
#
|
7
|
+
# = Active \Model \Callbacks
|
8
8
|
#
|
9
9
|
# Provides an interface for any class to have Active Record like callbacks.
|
10
10
|
#
|
11
11
|
# Like the Active Record methods, the callback chain is aborted as soon as
|
12
12
|
# one of the methods throws +:abort+.
|
13
13
|
#
|
14
|
-
# First, extend ActiveModel::Callbacks from the class you are creating:
|
14
|
+
# First, extend +ActiveModel::Callbacks+ from the class you are creating:
|
15
15
|
#
|
16
16
|
# class MyModel
|
17
17
|
# extend ActiveModel::Callbacks
|
@@ -60,7 +60,7 @@ module ActiveModel
|
|
60
60
|
# Would only create the +after_create+ and +before_create+ callback methods in
|
61
61
|
# your class.
|
62
62
|
#
|
63
|
-
# NOTE:
|
63
|
+
# NOTE: Defining the same callback multiple times will overwrite previous callback definitions.
|
64
64
|
#
|
65
65
|
module Callbacks
|
66
66
|
def self.extended(base) # :nodoc:
|
@@ -69,7 +69,7 @@ module ActiveModel
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
# define_model_callbacks accepts the same options +define_callbacks+ does,
|
72
|
+
# +define_model_callbacks+ accepts the same options +define_callbacks+ does,
|
73
73
|
# in case you want to overwrite a default. Besides that, it also accepts an
|
74
74
|
# <tt>:only</tt> option, where you can choose if you want all types (before,
|
75
75
|
# around or after) or just some.
|
@@ -77,7 +77,7 @@ module ActiveModel
|
|
77
77
|
# define_model_callbacks :initialize, only: :after
|
78
78
|
#
|
79
79
|
# Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
|
80
|
-
# on that method call. To get around this you can call the define_model_callbacks
|
80
|
+
# on that method call. To get around this you can call the +define_model_callbacks+
|
81
81
|
# method as many times as you need.
|
82
82
|
#
|
83
83
|
# define_model_callbacks :create, only: :after
|
@@ -104,7 +104,7 @@ module ActiveModel
|
|
104
104
|
# end
|
105
105
|
# end
|
106
106
|
#
|
107
|
-
# NOTE: +method_name+ passed to define_model_callbacks must not end with
|
107
|
+
# NOTE: +method_name+ passed to +define_model_callbacks+ must not end with
|
108
108
|
# <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
|
109
109
|
def define_model_callbacks(*callbacks)
|
110
110
|
options = callbacks.extract_options!
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveModel
|
4
|
-
#
|
4
|
+
# = Active \Model \Conversion
|
5
5
|
#
|
6
6
|
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
|
7
7
|
#
|
@@ -24,6 +24,14 @@ module ActiveModel
|
|
24
24
|
module Conversion
|
25
25
|
extend ActiveSupport::Concern
|
26
26
|
|
27
|
+
included do
|
28
|
+
##
|
29
|
+
# :singleton-method:
|
30
|
+
#
|
31
|
+
# Accepts a string that will be used as a delimiter of object's key values in the `to_param` method.
|
32
|
+
class_attribute :param_delimiter, instance_reader: false, default: "-"
|
33
|
+
end
|
34
|
+
|
27
35
|
# If your object is already designed to implement all of the \Active \Model
|
28
36
|
# you can use the default <tt>:to_model</tt> implementation, which simply
|
29
37
|
# returns +self+.
|
@@ -58,7 +66,7 @@ module ActiveModel
|
|
58
66
|
# person.to_key # => [1]
|
59
67
|
def to_key
|
60
68
|
key = respond_to?(:id) && id
|
61
|
-
key ?
|
69
|
+
key ? Array(key) : nil
|
62
70
|
end
|
63
71
|
|
64
72
|
# Returns a +string+ representing the object's key suitable for use in URLs,
|
@@ -80,7 +88,7 @@ module ActiveModel
|
|
80
88
|
# person = Person.new(1)
|
81
89
|
# person.to_param # => "1"
|
82
90
|
def to_param
|
83
|
-
(persisted? && key = to_key) ? key.join(
|
91
|
+
(persisted? && (key = to_key) && key.all?) ? key.join(self.class.param_delimiter) : nil
|
84
92
|
end
|
85
93
|
|
86
94
|
# Returns a +string+ identifying the path associated with the object.
|
@@ -100,7 +108,9 @@ module ActiveModel
|
|
100
108
|
# Provide a class level cache for #to_partial_path. This is an
|
101
109
|
# internal method and should not be accessed directly.
|
102
110
|
def _to_partial_path # :nodoc:
|
103
|
-
@_to_partial_path ||=
|
111
|
+
@_to_partial_path ||= if respond_to?(:model_name)
|
112
|
+
"#{model_name.collection}/#{model_name.element}"
|
113
|
+
else
|
104
114
|
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
|
105
115
|
collection = ActiveSupport::Inflector.tableize(name)
|
106
116
|
"#{collection}/#{element}"
|
data/lib/active_model/dirty.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_model/attribute_mutation_tracker"
|
4
4
|
|
5
5
|
module ActiveModel
|
6
|
-
#
|
6
|
+
# = Active \Model \Dirty
|
7
7
|
#
|
8
8
|
# Provides a way to track changes in your object in the same way as
|
9
9
|
# Active Record does.
|
@@ -13,8 +13,7 @@ module ActiveModel
|
|
13
13
|
# * <tt>include ActiveModel::Dirty</tt> in your object.
|
14
14
|
# * Call <tt>define_attribute_methods</tt> passing each method you want to
|
15
15
|
# track.
|
16
|
-
# * Call <tt
|
17
|
-
# attribute.
|
16
|
+
# * Call <tt>*_will_change!</tt> before each change to the tracked attribute.
|
18
17
|
# * Call <tt>changes_applied</tt> after the changes are persisted.
|
19
18
|
# * Call <tt>clear_changes_information</tt> when you want to reset the changes
|
20
19
|
# information.
|
@@ -109,20 +108,136 @@ module ActiveModel
|
|
109
108
|
# person.changes # => {"name" => ["Bill", "Bob"]}
|
110
109
|
#
|
111
110
|
# If an attribute is modified in-place then make use of
|
112
|
-
#
|
111
|
+
# {*_will_change!}[rdoc-label:method-i-2A_will_change-21] to mark that the attribute is changing.
|
113
112
|
# Otherwise \Active \Model can't track changes to in-place attributes. Note
|
114
113
|
# that Active Record can detect in-place modifications automatically. You do
|
115
|
-
# not need to call <tt
|
114
|
+
# not need to call <tt>*_will_change!</tt> on Active Record models.
|
116
115
|
#
|
117
116
|
# person.name_will_change!
|
118
117
|
# person.name_change # => ["Bill", "Bill"]
|
119
118
|
# person.name << 'y'
|
120
119
|
# person.name_change # => ["Bill", "Billy"]
|
120
|
+
#
|
121
|
+
# Methods can be invoked as +name_changed?+ or by passing an argument to the
|
122
|
+
# generic method <tt>attribute_changed?("name")</tt>.
|
121
123
|
module Dirty
|
122
124
|
extend ActiveSupport::Concern
|
123
125
|
include ActiveModel::AttributeMethods
|
124
126
|
|
125
127
|
included do
|
128
|
+
##
|
129
|
+
# :method: *_previously_changed?
|
130
|
+
#
|
131
|
+
# :call-seq: *_previously_changed?(**options)
|
132
|
+
#
|
133
|
+
# This method is generated for each attribute.
|
134
|
+
#
|
135
|
+
# Returns true if the attribute previously had unsaved changes.
|
136
|
+
#
|
137
|
+
# person = Person.new
|
138
|
+
# person.name = 'Britanny'
|
139
|
+
# person.save
|
140
|
+
# person.name_previously_changed? # => true
|
141
|
+
# person.name_previously_changed?(from: nil, to: 'Britanny') # => true
|
142
|
+
|
143
|
+
##
|
144
|
+
# :method: *_changed?
|
145
|
+
#
|
146
|
+
# This method is generated for each attribute.
|
147
|
+
#
|
148
|
+
# Returns true if the attribute has unsaved changes.
|
149
|
+
#
|
150
|
+
# person = Person.new
|
151
|
+
# person.name = 'Andrew'
|
152
|
+
# person.name_changed? # => true
|
153
|
+
|
154
|
+
##
|
155
|
+
# :method: *_change
|
156
|
+
#
|
157
|
+
# This method is generated for each attribute.
|
158
|
+
#
|
159
|
+
# Returns the old and the new value of the attribute.
|
160
|
+
#
|
161
|
+
# person = Person.new
|
162
|
+
# person.name = 'Nick'
|
163
|
+
# person.name_change # => [nil, 'Nick']
|
164
|
+
|
165
|
+
##
|
166
|
+
# :method: *_will_change!
|
167
|
+
#
|
168
|
+
# This method is generated for each attribute.
|
169
|
+
#
|
170
|
+
# If an attribute is modified in-place then make use of
|
171
|
+
# <tt>*_will_change!</tt> to mark that the attribute is changing.
|
172
|
+
# Otherwise Active Model can’t track changes to in-place attributes. Note
|
173
|
+
# that Active Record can detect in-place modifications automatically. You
|
174
|
+
# do not need to call <tt>*_will_change!</tt> on Active Record
|
175
|
+
# models.
|
176
|
+
#
|
177
|
+
# person = Person.new('Sandy')
|
178
|
+
# person.name_will_change!
|
179
|
+
# person.name_change # => ['Sandy', 'Sandy']
|
180
|
+
|
181
|
+
##
|
182
|
+
# :method: *_was
|
183
|
+
#
|
184
|
+
# This method is generated for each attribute.
|
185
|
+
#
|
186
|
+
# Returns the old value of the attribute.
|
187
|
+
#
|
188
|
+
# person = Person.new(name: 'Steph')
|
189
|
+
# person.name = 'Stephanie'
|
190
|
+
# person.name_was # => 'Steph'
|
191
|
+
|
192
|
+
##
|
193
|
+
# :method: *_previous_change
|
194
|
+
#
|
195
|
+
# This method is generated for each attribute.
|
196
|
+
#
|
197
|
+
# Returns the old and the new value of the attribute before the last save.
|
198
|
+
#
|
199
|
+
# person = Person.new
|
200
|
+
# person.name = 'Emmanuel'
|
201
|
+
# person.save
|
202
|
+
# person.name_previous_change # => [nil, 'Emmanuel']
|
203
|
+
|
204
|
+
##
|
205
|
+
# :method: *_previously_was
|
206
|
+
#
|
207
|
+
# This method is generated for each attribute.
|
208
|
+
#
|
209
|
+
# Returns the old value of the attribute before the last save.
|
210
|
+
#
|
211
|
+
# person = Person.new
|
212
|
+
# person.name = 'Sage'
|
213
|
+
# person.save
|
214
|
+
# person.name_previously_was # => nil
|
215
|
+
|
216
|
+
##
|
217
|
+
# :method: restore_*!
|
218
|
+
#
|
219
|
+
# This method is generated for each attribute.
|
220
|
+
#
|
221
|
+
# Restores the attribute to the old value.
|
222
|
+
#
|
223
|
+
# person = Person.new
|
224
|
+
# person.name = 'Amanda'
|
225
|
+
# person.restore_name!
|
226
|
+
# person.name # => nil
|
227
|
+
|
228
|
+
##
|
229
|
+
# :method: clear_*_change
|
230
|
+
#
|
231
|
+
# This method is generated for each attribute.
|
232
|
+
#
|
233
|
+
# Clears all dirty data of the attribute: current changes and previous changes.
|
234
|
+
#
|
235
|
+
# person = Person.new(name: 'Chris')
|
236
|
+
# person.name = 'Jason'
|
237
|
+
# person.name_change # => ['Chris', 'Jason']
|
238
|
+
# person.clear_name_change
|
239
|
+
# person.name_change # => nil
|
240
|
+
|
126
241
|
attribute_method_suffix "_previously_changed?", "_changed?", parameters: "**options"
|
127
242
|
attribute_method_suffix "_change", "_will_change!", "_was", parameters: false
|
128
243
|
attribute_method_suffix "_previous_change", "_previously_was", parameters: false
|
@@ -174,23 +289,23 @@ module ActiveModel
|
|
174
289
|
mutations_from_database.changed_attribute_names
|
175
290
|
end
|
176
291
|
|
177
|
-
# Dispatch target for
|
178
|
-
def attribute_changed?(attr_name, **options)
|
292
|
+
# Dispatch target for {*_changed?}[rdoc-label:method-i-2A_changed-3F] attribute methods.
|
293
|
+
def attribute_changed?(attr_name, **options)
|
179
294
|
mutations_from_database.changed?(attr_name.to_s, **options)
|
180
295
|
end
|
181
296
|
|
182
|
-
# Dispatch target for
|
183
|
-
def attribute_was(attr_name)
|
297
|
+
# Dispatch target for {*_was}[rdoc-label:method-i-2A_was] attribute methods.
|
298
|
+
def attribute_was(attr_name)
|
184
299
|
mutations_from_database.original_value(attr_name.to_s)
|
185
300
|
end
|
186
301
|
|
187
|
-
# Dispatch target for
|
188
|
-
def attribute_previously_changed?(attr_name, **options)
|
302
|
+
# Dispatch target for {*_previously_changed?}[rdoc-label:method-i-2A_previously_changed-3F] attribute methods.
|
303
|
+
def attribute_previously_changed?(attr_name, **options)
|
189
304
|
mutations_before_last_save.changed?(attr_name.to_s, **options)
|
190
305
|
end
|
191
306
|
|
192
|
-
# Dispatch target for
|
193
|
-
def attribute_previously_was(attr_name)
|
307
|
+
# Dispatch target for {*_previously_was}[rdoc-label:method-i-2A_previously_was] attribute methods.
|
308
|
+
def attribute_previously_was(attr_name)
|
194
309
|
mutations_before_last_save.original_value(attr_name.to_s)
|
195
310
|
end
|
196
311
|
|
@@ -247,6 +362,12 @@ module ActiveModel
|
|
247
362
|
end
|
248
363
|
|
249
364
|
private
|
365
|
+
def init_internals
|
366
|
+
super
|
367
|
+
@mutations_before_last_save = nil
|
368
|
+
@mutations_from_database = nil
|
369
|
+
end
|
370
|
+
|
250
371
|
def clear_attribute_change(attr_name)
|
251
372
|
mutations_from_database.forget_change(attr_name.to_s)
|
252
373
|
end
|
data/lib/active_model/error.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/core_ext/class/attribute"
|
4
4
|
|
5
5
|
module ActiveModel
|
6
|
-
#
|
6
|
+
# = Active \Model \Error
|
7
7
|
#
|
8
8
|
# Represents one single error
|
9
9
|
class Error
|
@@ -121,9 +121,10 @@ module ActiveModel
|
|
121
121
|
attr_reader :attribute
|
122
122
|
# The type of error, defaults to +:invalid+ unless specified
|
123
123
|
attr_reader :type
|
124
|
-
# The raw value provided as the second parameter when calling
|
124
|
+
# The raw value provided as the second parameter when calling
|
125
|
+
# <tt>errors#add</tt>
|
125
126
|
attr_reader :raw_type
|
126
|
-
# The options provided when calling
|
127
|
+
# The options provided when calling <tt>errors#add</tt>
|
127
128
|
attr_reader :options
|
128
129
|
|
129
130
|
# Returns the error message.
|
data/lib/active_model/errors.rb
CHANGED
@@ -3,13 +3,12 @@
|
|
3
3
|
require "active_support/core_ext/array/conversions"
|
4
4
|
require "active_support/core_ext/string/inflections"
|
5
5
|
require "active_support/core_ext/object/deep_dup"
|
6
|
-
require "active_support/core_ext/string/filters"
|
7
6
|
require "active_model/error"
|
8
7
|
require "active_model/nested_error"
|
9
8
|
require "forwardable"
|
10
9
|
|
11
10
|
module ActiveModel
|
12
|
-
#
|
11
|
+
# = Active \Model \Errors
|
13
12
|
#
|
14
13
|
# Provides error related functionalities you can include in your object
|
15
14
|
# for handling error messages and interacting with Action View helpers.
|
@@ -64,6 +63,7 @@ module ActiveModel
|
|
64
63
|
|
65
64
|
extend Forwardable
|
66
65
|
|
66
|
+
##
|
67
67
|
# :method: each
|
68
68
|
#
|
69
69
|
# :call-seq: each(&block)
|
@@ -75,6 +75,31 @@ module ActiveModel
|
|
75
75
|
# # Will yield <#ActiveModel::Error attribute=name, type=too_short,
|
76
76
|
# options={:count=>3}>
|
77
77
|
# end
|
78
|
+
|
79
|
+
##
|
80
|
+
# :method: clear
|
81
|
+
#
|
82
|
+
# :call-seq: clear
|
83
|
+
#
|
84
|
+
# Clears all errors. Clearing the errors does not, however, make the model
|
85
|
+
# valid. The next time the validations are run (for example, via
|
86
|
+
# ActiveRecord::Validations#valid?), the errors collection will be filled
|
87
|
+
# again if any validations fail.
|
88
|
+
|
89
|
+
##
|
90
|
+
# :method: empty?
|
91
|
+
#
|
92
|
+
# :call-seq: empty?
|
93
|
+
#
|
94
|
+
# Returns true if there are no errors.
|
95
|
+
|
96
|
+
##
|
97
|
+
# :method: size
|
98
|
+
#
|
99
|
+
# :call-seq: size
|
100
|
+
#
|
101
|
+
# Returns number of errors.
|
102
|
+
|
78
103
|
def_delegators :@errors, :each, :clear, :empty?, :size, :uniq!
|
79
104
|
|
80
105
|
# The actual array of +Error+ objects
|
@@ -215,7 +240,7 @@ module ActiveModel
|
|
215
240
|
|
216
241
|
# Returns a Hash that can be used as the JSON representation for this
|
217
242
|
# object. You can pass the <tt>:full_messages</tt> option. This determines
|
218
|
-
# if the
|
243
|
+
# if the JSON object should contain full messages or not (false by default).
|
219
244
|
#
|
220
245
|
# person.errors.as_json # => {:name=>["cannot be nil"]}
|
221
246
|
# person.errors.as_json(full_messages: true) # => {:name=>["name cannot be nil"]}
|
@@ -287,7 +312,7 @@ module ActiveModel
|
|
287
312
|
# person.errors.messages
|
288
313
|
# # => {:name=>["can't be blank"]}
|
289
314
|
#
|
290
|
-
# person.errors.add(:name, :too_long,
|
315
|
+
# person.errors.add(:name, :too_long, count: 25)
|
291
316
|
# person.errors.messages
|
292
317
|
# # => ["is too long (maximum is 25 characters)"]
|
293
318
|
#
|
@@ -338,7 +363,7 @@ module ActiveModel
|
|
338
363
|
# If the error requires options, then it returns +true+ with
|
339
364
|
# the correct options, or +false+ with incorrect or missing options.
|
340
365
|
#
|
341
|
-
# person.errors.add :name, :too_long,
|
366
|
+
# person.errors.add :name, :too_long, count: 25
|
342
367
|
# person.errors.added? :name, :too_long, count: 25 # => true
|
343
368
|
# person.errors.added? :name, "is too long (maximum is 25 characters)" # => true
|
344
369
|
# person.errors.added? :name, :too_long, count: 24 # => false
|
@@ -360,7 +385,7 @@ module ActiveModel
|
|
360
385
|
# present, or +false+ otherwise. +type+ is treated the same as for +add+.
|
361
386
|
#
|
362
387
|
# person.errors.add :age
|
363
|
-
# person.errors.add :name, :too_long,
|
388
|
+
# person.errors.add :name, :too_long, count: 25
|
364
389
|
# person.errors.of_kind? :age # => true
|
365
390
|
# person.errors.of_kind? :name # => false
|
366
391
|
# person.errors.of_kind? :name, :too_long # => true
|
@@ -472,6 +497,8 @@ module ActiveModel
|
|
472
497
|
end
|
473
498
|
end
|
474
499
|
|
500
|
+
# = Active \Model \StrictValidationFailed
|
501
|
+
#
|
475
502
|
# Raised when a validation cannot be corrected by end users and are considered
|
476
503
|
# exceptional.
|
477
504
|
#
|
@@ -490,10 +517,14 @@ module ActiveModel
|
|
490
517
|
class StrictValidationFailed < StandardError
|
491
518
|
end
|
492
519
|
|
520
|
+
# = Active \Model \RangeError
|
521
|
+
#
|
493
522
|
# Raised when attribute values are out of range.
|
494
523
|
class RangeError < ::RangeError
|
495
524
|
end
|
496
525
|
|
526
|
+
# = Active \Model \UnknownAttributeError
|
527
|
+
#
|
497
528
|
# Raised when unknown attributes are supplied via mass assignment.
|
498
529
|
#
|
499
530
|
# class Person
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveModel
|
4
|
-
# Returns the currently loaded version of \Active \Model as a
|
4
|
+
# Returns the currently loaded version of \Active \Model as a +Gem::Version+.
|
5
5
|
def self.gem_version
|
6
6
|
Gem::Version.new VERSION::STRING
|
7
7
|
end
|
8
8
|
|
9
9
|
module VERSION
|
10
10
|
MAJOR = 7
|
11
|
-
MINOR =
|
12
|
-
TINY =
|
13
|
-
PRE = "
|
11
|
+
MINOR = 2
|
12
|
+
TINY = 2
|
13
|
+
PRE = "1"
|
14
14
|
|
15
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
16
|
end
|
data/lib/active_model/lint.rb
CHANGED
@@ -5,7 +5,7 @@ module ActiveModel
|
|
5
5
|
# == Active \Model \Lint \Tests
|
6
6
|
#
|
7
7
|
# You can test whether an object is compliant with the Active \Model API by
|
8
|
-
# including
|
8
|
+
# including +ActiveModel::Lint::Tests+ in your TestCase. It will
|
9
9
|
# include tests that tell you whether your object is fully compliant,
|
10
10
|
# or if not, which aspects of the API are not implemented.
|
11
11
|
#
|