activemodel 5.2.7.1 → 6.0.0.beta1
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 +38 -158
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_model/attribute/user_provided_default.rb +1 -2
- data/lib/active_model/attribute.rb +3 -4
- data/lib/active_model/attribute_assignment.rb +1 -1
- data/lib/active_model/attribute_methods.rb +39 -1
- data/lib/active_model/attribute_mutation_tracker.rb +1 -6
- data/lib/active_model/attribute_set/builder.rb +1 -3
- data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
- data/lib/active_model/attribute_set.rb +2 -10
- data/lib/active_model/attributes.rb +10 -22
- data/lib/active_model/callbacks.rb +10 -7
- data/lib/active_model/conversion.rb +1 -1
- data/lib/active_model/dirty.rb +2 -2
- data/lib/active_model/errors.rb +90 -11
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/naming.rb +19 -3
- data/lib/active_model/railtie.rb +6 -0
- data/lib/active_model/secure_password.rb +48 -55
- data/lib/active_model/serializers/json.rb +10 -9
- data/lib/active_model/type/binary.rb +1 -1
- data/lib/active_model/type/boolean.rb +1 -10
- data/lib/active_model/type/date.rb +1 -2
- data/lib/active_model/type/date_time.rb +3 -4
- data/lib/active_model/type/decimal.rb +4 -0
- data/lib/active_model/type/helpers/time_value.rb +19 -1
- data/lib/active_model/type/helpers.rb +0 -1
- data/lib/active_model/type/integer.rb +1 -6
- data/lib/active_model/type/registry.rb +2 -10
- data/lib/active_model/type/string.rb +2 -2
- data/lib/active_model/type/time.rb +0 -5
- data/lib/active_model/validations/acceptance.rb +4 -8
- data/lib/active_model/validations/clusivity.rb +1 -1
- data/lib/active_model/validations/confirmation.rb +2 -2
- data/lib/active_model/validations/inclusion.rb +1 -1
- data/lib/active_model/validations/numericality.rb +9 -6
- data/lib/active_model/validations/validates.rb +2 -2
- data/lib/active_model/validations.rb +0 -2
- data/lib/active_model/validator.rb +1 -1
- data/lib/active_model.rb +1 -1
- metadata +13 -14
- data/lib/active_model/type/helpers/timezone.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69b55e1af8331655f54dbced151a75b1ccbd7e48757fc4a3fd1ee2e5f2236d5a
|
4
|
+
data.tar.gz: e756ba5fc97ccd6ee212f32568e58e372b6e94d79082c81711b8e39ac6d86a3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 394b1945a1c68549337a884d68985f0e15c2cbef0e0f968ee50ab55076327e6d55558d9d884d580b7359ea87f460c9a31211090f621f919b3967aa2e9b93188d
|
7
|
+
data.tar.gz: 0e1e72d58afc1f7b3f572276c9efa4b21586fa4741d8e31ab2c3bf7bb19e98888f571731d4b5dc04c0ae5f76e5e6c8ee4cf481458eef8e1669c03445277317ec
|
data/CHANGELOG.md
CHANGED
@@ -1,108 +1,17 @@
|
|
1
|
-
## Rails
|
1
|
+
## Rails 6.0.0.beta1 (January 18, 2019) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Add `ActiveModel::Errors#of_kind?`.
|
4
4
|
|
5
|
-
|
6
|
-
## Rails 5.2.7 (March 10, 2022) ##
|
7
|
-
|
8
|
-
* No changes.
|
9
|
-
|
10
|
-
|
11
|
-
## Rails 5.2.6.3 (March 08, 2022) ##
|
12
|
-
|
13
|
-
* No changes.
|
14
|
-
|
15
|
-
|
16
|
-
## Rails 5.2.6.2 (February 11, 2022) ##
|
17
|
-
|
18
|
-
* No changes.
|
19
|
-
|
20
|
-
|
21
|
-
## Rails 5.2.6.1 (February 11, 2022) ##
|
22
|
-
|
23
|
-
* No changes.
|
24
|
-
|
25
|
-
|
26
|
-
## Rails 5.2.6 (May 05, 2021) ##
|
27
|
-
|
28
|
-
* No changes.
|
29
|
-
|
30
|
-
|
31
|
-
## Rails 5.2.5 (March 26, 2021) ##
|
32
|
-
|
33
|
-
* No changes.
|
34
|
-
|
35
|
-
|
36
|
-
## Rails 5.2.4.6 (May 05, 2021) ##
|
37
|
-
|
38
|
-
* No changes.
|
39
|
-
|
40
|
-
|
41
|
-
## Rails 5.2.4.5 (February 10, 2021) ##
|
42
|
-
|
43
|
-
* No changes.
|
44
|
-
|
45
|
-
|
46
|
-
## Rails 5.2.4.4 (September 09, 2020) ##
|
47
|
-
|
48
|
-
* No changes.
|
49
|
-
|
50
|
-
|
51
|
-
## Rails 5.2.4.3 (May 18, 2020) ##
|
52
|
-
|
53
|
-
* No changes.
|
54
|
-
|
55
|
-
|
56
|
-
## Rails 5.2.4.2 (March 19, 2020) ##
|
57
|
-
|
58
|
-
* No changes.
|
59
|
-
|
60
|
-
|
61
|
-
## Rails 5.2.4.1 (December 18, 2019) ##
|
62
|
-
|
63
|
-
* No changes.
|
64
|
-
|
65
|
-
|
66
|
-
## Rails 5.2.4 (November 27, 2019) ##
|
67
|
-
|
68
|
-
* Type cast falsy boolean symbols on boolean attribute as false.
|
69
|
-
|
70
|
-
Fixes #35676.
|
71
|
-
|
72
|
-
*Ryuta Kamizono*
|
73
|
-
|
74
|
-
|
75
|
-
## Rails 5.2.3 (March 27, 2019) ##
|
76
|
-
|
77
|
-
* Fix date value when casting a multiparameter date hash to not convert
|
78
|
-
from Gregorian date to Julian date.
|
79
|
-
|
80
|
-
Before:
|
81
|
-
|
82
|
-
Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
|
83
|
-
=> #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil>
|
84
|
-
|
85
|
-
After:
|
86
|
-
|
87
|
-
Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
|
88
|
-
=> #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil>
|
89
|
-
|
90
|
-
Fixes #28521.
|
91
|
-
|
92
|
-
*Sayan Chakraborty*
|
5
|
+
*bogdanvlviv*, *Rafael Mendonça França*
|
93
6
|
|
94
7
|
* Fix numericality equality validation of `BigDecimal` and `Float`
|
95
8
|
by casting to `BigDecimal` on both ends of the validation.
|
96
9
|
|
97
10
|
*Gannon McGibbon*
|
98
11
|
|
12
|
+
* Add `#slice!` method to `ActiveModel::Errors`.
|
99
13
|
|
100
|
-
|
101
|
-
|
102
|
-
* No changes.
|
103
|
-
|
104
|
-
|
105
|
-
## Rails 5.2.2 (December 04, 2018) ##
|
14
|
+
*Daniel López Prat*
|
106
15
|
|
107
16
|
* Fix numericality validator to still use value before type cast except Active Record.
|
108
17
|
|
@@ -110,79 +19,50 @@
|
|
110
19
|
|
111
20
|
*Ryuta Kamizono*
|
112
21
|
|
22
|
+
* Fix `ActiveModel::Serializers::JSON#as_json` method for timestamps.
|
113
23
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
## Rails 5.2.1 (August 07, 2018) ##
|
120
|
-
|
121
|
-
* No changes.
|
122
|
-
|
123
|
-
|
124
|
-
## Rails 5.2.0 (April 09, 2018) ##
|
125
|
-
|
126
|
-
* Do not lose all multiple `:includes` with options in serialization.
|
127
|
-
|
128
|
-
*Mike Mangino*
|
129
|
-
|
130
|
-
* Models using the attributes API with a proc default can now be marshalled.
|
131
|
-
|
132
|
-
Fixes #31216.
|
133
|
-
|
134
|
-
*Sean Griffin*
|
135
|
-
|
136
|
-
* Fix to working before/after validation callbacks on multiple contexts.
|
137
|
-
|
138
|
-
*Yoshiyuki Hirano*
|
24
|
+
Before:
|
25
|
+
```
|
26
|
+
contact = Contact.new(created_at: Time.utc(2006, 8, 1))
|
27
|
+
contact.as_json["created_at"] # => 2006-08-01 00:00:00 UTC
|
28
|
+
```
|
139
29
|
|
140
|
-
|
30
|
+
After:
|
31
|
+
```
|
32
|
+
contact = Contact.new(created_at: Time.utc(2006, 8, 1))
|
33
|
+
contact.as_json["created_at"] # => "2006-08-01T00:00:00.000Z"
|
34
|
+
```
|
141
35
|
|
142
|
-
*
|
36
|
+
*Bogdan Gusiev*
|
143
37
|
|
144
|
-
*
|
38
|
+
* Allows configurable attribute name for `#has_secure_password`. This
|
39
|
+
still defaults to an attribute named 'password', causing no breaking
|
40
|
+
change. There is a new method `#authenticate_XXX` where XXX is the
|
41
|
+
configured attribute name, making the existing `#authenticate` now an
|
42
|
+
alias for this when the attribute is the default 'password'.
|
145
43
|
|
146
|
-
|
44
|
+
Example:
|
147
45
|
|
148
|
-
|
46
|
+
class User < ActiveRecord::Base
|
47
|
+
has_secure_password :recovery_password, validations: false
|
48
|
+
end
|
149
49
|
|
150
|
-
|
50
|
+
user = User.new()
|
51
|
+
user.recovery_password = "42password"
|
52
|
+
user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
|
53
|
+
user.authenticate_recovery_password('42password') # => user
|
151
54
|
|
152
|
-
*
|
153
|
-
values with more scale than the schema.
|
55
|
+
*Unathi Chonco*
|
154
56
|
|
155
|
-
|
57
|
+
* Add `config.active_model.i18n_full_message` in order to control whether
|
58
|
+
the `full_message` error format can be overridden at the attribute or model
|
59
|
+
level in the locale files. This is `false` by default.
|
156
60
|
|
157
|
-
*
|
61
|
+
*Martin Larochelle*
|
158
62
|
|
159
|
-
|
63
|
+
* Rails 6 requires Ruby 2.5.0 or newer.
|
160
64
|
|
161
|
-
|
65
|
+
*Jeremy Daer*, *Kasper Timm Hansen*
|
162
66
|
|
163
|
-
Example:
|
164
67
|
|
165
|
-
|
166
|
-
person = Person.new
|
167
|
-
person.errors.keys # => []
|
168
|
-
person.errors.values # => []
|
169
|
-
person.errors.messages # => {}
|
170
|
-
person.errors[:name] # => []
|
171
|
-
person.errors.messages # => {:name => []}
|
172
|
-
person.errors.keys # => [:name]
|
173
|
-
person.errors.values # => [[]]
|
174
|
-
|
175
|
-
# After
|
176
|
-
person = Person.new
|
177
|
-
person.errors.keys # => []
|
178
|
-
person.errors.values # => []
|
179
|
-
person.errors.messages # => {}
|
180
|
-
person.errors[:name] # => []
|
181
|
-
person.errors.messages # => {:name => []}
|
182
|
-
person.errors.keys # => []
|
183
|
-
person.errors.values # => []
|
184
|
-
|
185
|
-
*bogdanvlviv*
|
186
|
-
|
187
|
-
|
188
|
-
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activemodel/CHANGELOG.md) for previous changes.
|
68
|
+
Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -239,7 +239,7 @@ The latest version of Active Model can be installed with RubyGems:
|
|
239
239
|
|
240
240
|
Source code can be downloaded as part of the Rails project on GitHub
|
241
241
|
|
242
|
-
* https://github.com/rails/rails/tree/
|
242
|
+
* https://github.com/rails/rails/tree/master/activemodel
|
243
243
|
|
244
244
|
|
245
245
|
== License
|
@@ -133,10 +133,6 @@ module ActiveModel
|
|
133
133
|
end
|
134
134
|
|
135
135
|
protected
|
136
|
-
|
137
|
-
attr_reader :original_attribute
|
138
|
-
alias_method :assigned?, :original_attribute
|
139
|
-
|
140
136
|
def original_value_for_database
|
141
137
|
if assigned?
|
142
138
|
original_attribute.original_value_for_database
|
@@ -146,6 +142,9 @@ module ActiveModel
|
|
146
142
|
end
|
147
143
|
|
148
144
|
private
|
145
|
+
attr_reader :original_attribute
|
146
|
+
alias :assigned? :original_attribute
|
147
|
+
|
149
148
|
def initialize_dup(other)
|
150
149
|
if defined?(@value) && @value.duplicable?
|
151
150
|
@value = @value.dup
|
@@ -27,7 +27,7 @@ module ActiveModel
|
|
27
27
|
# cat.status # => 'sleeping'
|
28
28
|
def assign_attributes(new_attributes)
|
29
29
|
if !new_attributes.respond_to?(:stringify_keys)
|
30
|
-
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
|
30
|
+
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{new_attributes.class} passed."
|
31
31
|
end
|
32
32
|
return if new_attributes.empty?
|
33
33
|
|
@@ -369,7 +369,7 @@ module ActiveModel
|
|
369
369
|
"define_method(:'#{name}') do |*args|"
|
370
370
|
end
|
371
371
|
|
372
|
-
extra = (extra.map!(&:inspect) << "*args").join(", "
|
372
|
+
extra = (extra.map!(&:inspect) << "*args").join(", ")
|
373
373
|
|
374
374
|
target = if CALL_COMPILABLE_REGEXP.match?(send)
|
375
375
|
"#{"self." unless include_private}#{send}(#{extra})"
|
@@ -474,5 +474,43 @@ module ActiveModel
|
|
474
474
|
def _read_attribute(attr)
|
475
475
|
__send__(attr)
|
476
476
|
end
|
477
|
+
|
478
|
+
module AttrNames # :nodoc:
|
479
|
+
DEF_SAFE_NAME = /\A[a-zA-Z_]\w*\z/
|
480
|
+
|
481
|
+
# We want to generate the methods via module_eval rather than
|
482
|
+
# define_method, because define_method is slower on dispatch.
|
483
|
+
# Evaluating many similar methods may use more memory as the instruction
|
484
|
+
# sequences are duplicated and cached (in MRI). define_method may
|
485
|
+
# be slower on dispatch, but if you're careful about the closure
|
486
|
+
# created, then define_method will consume much less memory.
|
487
|
+
#
|
488
|
+
# But sometimes the database might return columns with
|
489
|
+
# characters that are not allowed in normal method names (like
|
490
|
+
# 'my_column(omg)'. So to work around this we first define with
|
491
|
+
# the __temp__ identifier, and then use alias method to rename
|
492
|
+
# it to what we want.
|
493
|
+
#
|
494
|
+
# We are also defining a constant to hold the frozen string of
|
495
|
+
# the attribute name. Using a constant means that we do not have
|
496
|
+
# to allocate an object on each call to the attribute method.
|
497
|
+
# Making it frozen means that it doesn't get duped when used to
|
498
|
+
# key the @attributes in read_attribute.
|
499
|
+
def self.define_attribute_accessor_method(mod, attr_name, writer: false)
|
500
|
+
method_name = "#{attr_name}#{'=' if writer}"
|
501
|
+
if attr_name.ascii_only? && DEF_SAFE_NAME.match?(attr_name)
|
502
|
+
yield method_name, "'#{attr_name}'.freeze"
|
503
|
+
else
|
504
|
+
safe_name = attr_name.unpack1("h*")
|
505
|
+
const_name = "ATTR_#{safe_name}"
|
506
|
+
const_set(const_name, attr_name) unless const_defined?(const_name)
|
507
|
+
temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
|
508
|
+
attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
|
509
|
+
yield temp_method_name, attr_name_expr
|
510
|
+
mod.alias_method method_name, temp_method_name
|
511
|
+
mod.undef_method temp_method_name
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
477
515
|
end
|
478
516
|
end
|
@@ -69,13 +69,8 @@ module ActiveModel
|
|
69
69
|
forced_changes << attr_name.to_s
|
70
70
|
end
|
71
71
|
|
72
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
73
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
74
|
-
protected
|
75
|
-
|
76
|
-
attr_reader :attributes, :forced_changes
|
77
|
-
|
78
72
|
private
|
73
|
+
attr_reader :attributes, :forced_changes
|
79
74
|
|
80
75
|
def attr_names
|
81
76
|
attributes.keys
|
@@ -90,9 +90,6 @@ module ActiveModel
|
|
90
90
|
end
|
91
91
|
|
92
92
|
protected
|
93
|
-
|
94
|
-
attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
|
95
|
-
|
96
93
|
def materialize
|
97
94
|
unless @materialized
|
98
95
|
values.each_key { |key| self[key] }
|
@@ -105,6 +102,7 @@ module ActiveModel
|
|
105
102
|
end
|
106
103
|
|
107
104
|
private
|
105
|
+
attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
|
108
106
|
|
109
107
|
def assign_default_value(name)
|
110
108
|
type = additional_types.fetch(name, types[name])
|
@@ -37,16 +37,8 @@ module ActiveModel
|
|
37
37
|
attributes.each_key.select { |name| self[name].initialized? }
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
# https://github.com/jruby/jruby/pull/2562
|
43
|
-
def fetch_value(name, &block)
|
44
|
-
self[name].value(&block)
|
45
|
-
end
|
46
|
-
else
|
47
|
-
def fetch_value(name)
|
48
|
-
self[name].value { |n| yield n if block_given? }
|
49
|
-
end
|
40
|
+
def fetch_value(name, &block)
|
41
|
+
self[name].value(&block)
|
50
42
|
end
|
51
43
|
|
52
44
|
def write_from_database(name, value)
|
@@ -29,17 +29,16 @@ module ActiveModel
|
|
29
29
|
private
|
30
30
|
|
31
31
|
def define_method_attribute=(name)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
STR
|
32
|
+
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
33
|
+
generated_attribute_methods, name, writer: true,
|
34
|
+
) do |temp_method_name, attr_name_expr|
|
35
|
+
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
36
|
+
def #{temp_method_name}(value)
|
37
|
+
name = #{attr_name_expr}
|
38
|
+
write_attribute(name, value)
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
end
|
43
42
|
end
|
44
43
|
|
45
44
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
@@ -97,15 +96,4 @@ module ActiveModel
|
|
97
96
|
write_attribute(attribute_name, value)
|
98
97
|
end
|
99
98
|
end
|
100
|
-
|
101
|
-
module AttributeMethods #:nodoc:
|
102
|
-
AttrNames = Module.new {
|
103
|
-
def self.set_name_cache(name, value)
|
104
|
-
const_name = "ATTR_#{name}"
|
105
|
-
unless const_defined? const_name
|
106
|
-
const_set const_name, value.dup.freeze
|
107
|
-
end
|
108
|
-
end
|
109
|
-
}
|
110
|
-
end
|
111
99
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/array/extract_options"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
4
5
|
|
5
6
|
module ActiveModel
|
6
7
|
# == Active \Model \Callbacks
|
@@ -127,26 +128,28 @@ module ActiveModel
|
|
127
128
|
private
|
128
129
|
|
129
130
|
def _define_before_model_callback(klass, callback)
|
130
|
-
klass.define_singleton_method("before_#{callback}") do |*args, &block|
|
131
|
-
|
131
|
+
klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
|
132
|
+
options.assert_valid_keys(:if, :unless, :prepend)
|
133
|
+
set_callback(:"#{callback}", :before, *args, options, &block)
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
135
137
|
def _define_around_model_callback(klass, callback)
|
136
|
-
klass.define_singleton_method("around_#{callback}") do |*args, &block|
|
137
|
-
|
138
|
+
klass.define_singleton_method("around_#{callback}") do |*args, **options, &block|
|
139
|
+
options.assert_valid_keys(:if, :unless, :prepend)
|
140
|
+
set_callback(:"#{callback}", :around, *args, options, &block)
|
138
141
|
end
|
139
142
|
end
|
140
143
|
|
141
144
|
def _define_after_model_callback(klass, callback)
|
142
|
-
klass.define_singleton_method("after_#{callback}") do |*args, &block|
|
143
|
-
options
|
145
|
+
klass.define_singleton_method("after_#{callback}") do |*args, **options, &block|
|
146
|
+
options.assert_valid_keys(:if, :unless, :prepend)
|
144
147
|
options[:prepend] = true
|
145
148
|
conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
|
146
149
|
v != false
|
147
150
|
}
|
148
151
|
options[:if] = Array(options[:if]) << conditional
|
149
|
-
set_callback(:"#{callback}", :after, *
|
152
|
+
set_callback(:"#{callback}", :after, *args, options, &block)
|
150
153
|
end
|
151
154
|
end
|
152
155
|
end
|
@@ -103,7 +103,7 @@ module ActiveModel
|
|
103
103
|
@_to_partial_path ||= begin
|
104
104
|
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
|
105
105
|
collection = ActiveSupport::Inflector.tableize(name)
|
106
|
-
"#{collection}/#{element}"
|
106
|
+
"#{collection}/#{element}"
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
data/lib/active_model/dirty.rb
CHANGED
@@ -153,7 +153,7 @@ module ActiveModel
|
|
153
153
|
@mutations_from_database = nil
|
154
154
|
end
|
155
155
|
|
156
|
-
# Returns +true+ if any of the attributes
|
156
|
+
# Returns +true+ if any of the attributes has unsaved changes, +false+ otherwise.
|
157
157
|
#
|
158
158
|
# person.changed? # => false
|
159
159
|
# person.name = 'bob'
|
@@ -306,7 +306,7 @@ module ActiveModel
|
|
306
306
|
|
307
307
|
# Handles <tt>*_previous_change</tt> for +method_missing+.
|
308
308
|
def attribute_previous_change(attr)
|
309
|
-
previous_changes[attr]
|
309
|
+
previous_changes[attr]
|
310
310
|
end
|
311
311
|
|
312
312
|
# Handles <tt>*_will_change!</tt> for +method_missing+.
|
data/lib/active_model/errors.rb
CHANGED
@@ -62,6 +62,11 @@ module ActiveModel
|
|
62
62
|
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
|
63
63
|
MESSAGE_OPTIONS = [:message]
|
64
64
|
|
65
|
+
class << self
|
66
|
+
attr_accessor :i18n_full_message # :nodoc:
|
67
|
+
end
|
68
|
+
self.i18n_full_message = false
|
69
|
+
|
65
70
|
attr_reader :messages, :details
|
66
71
|
|
67
72
|
# Pass in the instance of the object that is using the errors object.
|
@@ -107,6 +112,17 @@ module ActiveModel
|
|
107
112
|
@details.merge!(other.details) { |_, ary1, ary2| ary1 + ary2 }
|
108
113
|
end
|
109
114
|
|
115
|
+
# Removes all errors except the given keys. Returns a hash containing the removed errors.
|
116
|
+
#
|
117
|
+
# person.errors.keys # => [:name, :age, :gender, :city]
|
118
|
+
# person.errors.slice!(:age, :gender) # => { :name=>["cannot be nil"], :city=>["cannot be nil"] }
|
119
|
+
# person.errors.keys # => [:age, :gender]
|
120
|
+
def slice!(*keys)
|
121
|
+
keys = keys.map(&:to_sym)
|
122
|
+
@details.slice!(*keys)
|
123
|
+
@messages.slice!(*keys)
|
124
|
+
end
|
125
|
+
|
110
126
|
# Clear the error messages.
|
111
127
|
#
|
112
128
|
# person.errors.full_messages # => ["name cannot be nil"]
|
@@ -312,15 +328,15 @@ module ActiveModel
|
|
312
328
|
# person.errors.added? :name, :blank # => true
|
313
329
|
# person.errors.added? :name, "can't be blank" # => true
|
314
330
|
#
|
315
|
-
# If the error message requires
|
316
|
-
# the correct
|
331
|
+
# If the error message requires options, then it returns +true+ with
|
332
|
+
# the correct options, or +false+ with incorrect or missing options.
|
317
333
|
#
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
334
|
+
# person.errors.add :name, :too_long, { count: 25 }
|
335
|
+
# person.errors.added? :name, :too_long, count: 25 # => true
|
336
|
+
# person.errors.added? :name, "is too long (maximum is 25 characters)" # => true
|
337
|
+
# person.errors.added? :name, :too_long, count: 24 # => false
|
338
|
+
# person.errors.added? :name, :too_long # => false
|
339
|
+
# person.errors.added? :name, "is too long" # => false
|
324
340
|
def added?(attribute, message = :invalid, options = {})
|
325
341
|
message = message.call if message.respond_to?(:call)
|
326
342
|
|
@@ -331,6 +347,27 @@ module ActiveModel
|
|
331
347
|
end
|
332
348
|
end
|
333
349
|
|
350
|
+
# Returns +true+ if an error on the attribute with the given message is
|
351
|
+
# present, or +false+ otherwise. +message+ is treated the same as for +add+.
|
352
|
+
#
|
353
|
+
# person.errors.add :age
|
354
|
+
# person.errors.add :name, :too_long, { count: 25 }
|
355
|
+
# person.errors.of_kind? :age # => true
|
356
|
+
# person.errors.of_kind? :name # => false
|
357
|
+
# person.errors.of_kind? :name, :too_long # => true
|
358
|
+
# person.errors.of_kind? :name, "is too long (maximum is 25 characters)" # => true
|
359
|
+
# person.errors.of_kind? :name, :not_too_long # => false
|
360
|
+
# person.errors.of_kind? :name, "is too long" # => false
|
361
|
+
def of_kind?(attribute, message = :invalid)
|
362
|
+
message = message.call if message.respond_to?(:call)
|
363
|
+
|
364
|
+
if message.is_a? Symbol
|
365
|
+
details[attribute.to_sym].map { |e| e[:error] }.include? message
|
366
|
+
else
|
367
|
+
self[attribute].include? message
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
334
371
|
# Returns all the full error messages in an array.
|
335
372
|
#
|
336
373
|
# class Person
|
@@ -364,12 +401,54 @@ module ActiveModel
|
|
364
401
|
# Returns a full message for a given attribute.
|
365
402
|
#
|
366
403
|
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
|
404
|
+
#
|
405
|
+
# The `"%{attribute} %{message}"` error format can be overridden with either
|
406
|
+
#
|
407
|
+
# * <tt>activemodel.errors.models.person/contacts/addresses.attributes.street.format</tt>
|
408
|
+
# * <tt>activemodel.errors.models.person/contacts/addresses.format</tt>
|
409
|
+
# * <tt>activemodel.errors.models.person.attributes.name.format</tt>
|
410
|
+
# * <tt>activemodel.errors.models.person.format</tt>
|
411
|
+
# * <tt>errors.format</tt>
|
367
412
|
def full_message(attribute, message)
|
368
413
|
return message if attribute == :base
|
369
|
-
|
414
|
+
attribute = attribute.to_s
|
415
|
+
|
416
|
+
if self.class.i18n_full_message && @base.class.respond_to?(:i18n_scope)
|
417
|
+
attribute = attribute.remove(/\[\d\]/)
|
418
|
+
parts = attribute.split(".")
|
419
|
+
attribute_name = parts.pop
|
420
|
+
namespace = parts.join("/") unless parts.empty?
|
421
|
+
attributes_scope = "#{@base.class.i18n_scope}.errors.models"
|
422
|
+
|
423
|
+
if namespace
|
424
|
+
defaults = @base.class.lookup_ancestors.map do |klass|
|
425
|
+
[
|
426
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.attributes.#{attribute_name}.format",
|
427
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.format",
|
428
|
+
]
|
429
|
+
end
|
430
|
+
else
|
431
|
+
defaults = @base.class.lookup_ancestors.map do |klass|
|
432
|
+
[
|
433
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}.attributes.#{attribute_name}.format",
|
434
|
+
:"#{attributes_scope}.#{klass.model_name.i18n_key}.format",
|
435
|
+
]
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
defaults.flatten!
|
440
|
+
else
|
441
|
+
defaults = []
|
442
|
+
end
|
443
|
+
|
444
|
+
defaults << :"errors.format"
|
445
|
+
defaults << "%{attribute} %{message}"
|
446
|
+
|
447
|
+
attr_name = attribute.tr(".", "_").humanize
|
370
448
|
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
|
371
|
-
|
372
|
-
|
449
|
+
|
450
|
+
I18n.t(defaults.shift,
|
451
|
+
default: defaults,
|
373
452
|
attribute: attr_name,
|
374
453
|
message: message)
|
375
454
|
end
|