activemodel 5.2.7.1 → 6.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|