activemodel 4.1.16 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activemodel might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -144
- data/README.rdoc +56 -32
- data/lib/active_model/conversion.rb +14 -7
- data/lib/active_model/dirty.rb +59 -10
- data/lib/active_model/errors.rb +9 -2
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/lint.rb +7 -4
- data/lib/active_model/locale/en.yml +9 -3
- data/lib/active_model/model.rb +3 -3
- data/lib/active_model/naming.rb +13 -9
- data/lib/active_model/secure_password.rb +25 -12
- data/lib/active_model/serialization.rb +8 -8
- data/lib/active_model/serializers/json.rb +2 -2
- data/lib/active_model/serializers/xml.rb +1 -1
- data/lib/active_model/validations.rb +12 -2
- data/lib/active_model/validations/numericality.rb +13 -1
- data/lib/active_model/validations/with.rb +1 -1
- data/lib/active_model/validator.rb +0 -16
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0ba324f942bc460e8ac9ddf05db07b267fddba7
|
4
|
+
data.tar.gz: dcc2d9b2e52755dc827b2880015524238d1d7b69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3c419a3cbe8624a7a38c212c8cb87e6931cc0b8319090c0fbe7029cc4650f875d4faba74dbe009d8940e6fd7a7fd8e3bb4d4d8c2059fb91663609f08f684931
|
7
|
+
data.tar.gz: 2fb78243d20a5ba5d167b132946d782629ea932308ff1ba970bf177ee0e79894b62b13985a112abd43a0cf3357071510090a6bbea38d3ce82eb2b0774fbec4ed
|
data/CHANGELOG.md
CHANGED
@@ -1,169 +1,58 @@
|
|
1
|
-
|
1
|
+
* Passwords with spaces only allowed in `ActiveModel::SecurePassword`.
|
2
2
|
|
3
|
-
|
3
|
+
Presence validation can be used to restore old behavior.
|
4
4
|
|
5
|
+
*Yevhene Shemet*
|
5
6
|
|
6
|
-
|
7
|
+
* Validate options passed to `ActiveModel::Validations.validate`.
|
7
8
|
|
8
|
-
|
9
|
+
Preventing, in many cases, the simple mistake of using `validate` instead of `validates`.
|
9
10
|
|
11
|
+
*Sonny Michaud*
|
10
12
|
|
11
|
-
|
13
|
+
* Deprecate `reset_#{attribute}` in favor of `restore_#{attribute}`.
|
12
14
|
|
13
|
-
|
15
|
+
These methods may cause confusion with the `reset_changes` that behaves differently
|
16
|
+
of them.
|
14
17
|
|
18
|
+
* Deprecate `ActiveModel::Dirty#reset_changes` in favor of `#clear_changes_information`.
|
15
19
|
|
16
|
-
|
20
|
+
This method name is causing confusion with the `reset_#{attribute}`
|
21
|
+
methods. While `reset_name` set the value of the name attribute for the
|
22
|
+
previous value `reset_changes` only discard the changes and previous
|
23
|
+
changes.
|
17
24
|
|
18
|
-
*
|
25
|
+
* Added `restore_attributes` method to `ActiveModel::Dirty` API to restore all the
|
26
|
+
changed values to the previous data.
|
19
27
|
|
28
|
+
*Igor G.*
|
20
29
|
|
21
|
-
|
30
|
+
* Allow proc and symbol as values for `only_integer` of `NumericalityValidator`
|
22
31
|
|
23
|
-
*
|
32
|
+
*Robin Mehner*
|
24
33
|
|
34
|
+
* `has_secure_password` now verifies that the given password is less than 72
|
35
|
+
characters if validations are enabled.
|
25
36
|
|
26
|
-
|
37
|
+
Fixes #14591.
|
27
38
|
|
28
|
-
*
|
39
|
+
*Akshay Vishnoi*
|
29
40
|
|
41
|
+
* Remove deprecated `Validator#setup` without replacement.
|
30
42
|
|
31
|
-
|
43
|
+
See #10716.
|
32
44
|
|
33
|
-
*
|
45
|
+
*Kuldeep Aggarwal*
|
34
46
|
|
47
|
+
* Add plural and singular form for length validator's default messages.
|
35
48
|
|
36
|
-
|
49
|
+
*Abd ar-Rahman Hamid*
|
37
50
|
|
38
|
-
*
|
51
|
+
* Introduce `validate` as an alias for `valid?`.
|
39
52
|
|
53
|
+
This is more intuitive when you want to run validations but don't care about
|
54
|
+
the return value.
|
40
55
|
|
41
|
-
|
56
|
+
*Henrik Nyh*
|
42
57
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
## Rails 4.1.9 (January 6, 2015) ##
|
47
|
-
|
48
|
-
* No changes.
|
49
|
-
|
50
|
-
|
51
|
-
## Rails 4.1.8 (November 16, 2014) ##
|
52
|
-
|
53
|
-
* No changes.
|
54
|
-
|
55
|
-
|
56
|
-
## Rails 4.1.7.1 (November 19, 2014) ##
|
57
|
-
|
58
|
-
* No changes.
|
59
|
-
|
60
|
-
|
61
|
-
## Rails 4.1.7 (October 29, 2014) ##
|
62
|
-
|
63
|
-
* No changes.
|
64
|
-
|
65
|
-
|
66
|
-
## Rails 4.1.6 (September 11, 2014) ##
|
67
|
-
|
68
|
-
* No changes.
|
69
|
-
|
70
|
-
|
71
|
-
## Rails 4.1.5 (August 18, 2014) ##
|
72
|
-
|
73
|
-
* No changes.
|
74
|
-
|
75
|
-
|
76
|
-
## Rails 4.1.4 (July 2, 2014) ##
|
77
|
-
|
78
|
-
* No changes.
|
79
|
-
|
80
|
-
|
81
|
-
## Rails 4.1.3 (July 2, 2014) ##
|
82
|
-
|
83
|
-
* No changes.
|
84
|
-
|
85
|
-
|
86
|
-
## Rails 4.1.2 (June 26, 2014) ##
|
87
|
-
|
88
|
-
* No changes.
|
89
|
-
|
90
|
-
|
91
|
-
## Rails 4.1.1 (May 6, 2014) ##
|
92
|
-
|
93
|
-
* No changes.
|
94
|
-
|
95
|
-
|
96
|
-
## Rails 4.1.0 (April 8, 2014) ##
|
97
|
-
|
98
|
-
* `#to_param` returns `nil` if `#to_key` returns `nil`. Fixes #11399.
|
99
|
-
|
100
|
-
*Yves Senn*
|
101
|
-
|
102
|
-
* Ability to specify multiple contexts when defining a validation.
|
103
|
-
|
104
|
-
Example:
|
105
|
-
|
106
|
-
class Person
|
107
|
-
include ActiveModel::Validations
|
108
|
-
|
109
|
-
attr_reader :name
|
110
|
-
validates_presence_of :name, on: [:verify, :approve]
|
111
|
-
end
|
112
|
-
|
113
|
-
person = Person.new
|
114
|
-
person.valid? # => true
|
115
|
-
person.valid?(:verify) # => false
|
116
|
-
person.errors.full_messages_for(:name) # => ["Name can't be blank"]
|
117
|
-
person.valid?(:approve) # => false
|
118
|
-
person.errors.full_messages_for(:name) # => ["Name can't be blank"]
|
119
|
-
|
120
|
-
*Vince Puzzella*
|
121
|
-
|
122
|
-
* `attribute_changed?` now accepts a hash to check if the attribute was
|
123
|
-
changed `:from` and/or `:to` a given value.
|
124
|
-
|
125
|
-
Example:
|
126
|
-
|
127
|
-
model.name_changed?(from: "Pete", to: "Ringo")
|
128
|
-
|
129
|
-
*Tejas Dinkar*
|
130
|
-
|
131
|
-
* Fix `has_secure_password` to honor bcrypt-ruby's cost attribute.
|
132
|
-
|
133
|
-
*T.J. Schuck*
|
134
|
-
|
135
|
-
* Updated the `ActiveModel::Dirty#changed_attributes` method to be indifferent between using
|
136
|
-
symbols and strings as keys.
|
137
|
-
|
138
|
-
*William Myers*
|
139
|
-
|
140
|
-
* Added new API methods `reset_changes` and `changes_applied` to `ActiveModel::Dirty`
|
141
|
-
that control changes state. Previsously you needed to update internal
|
142
|
-
instance variables, but now API methods are available.
|
143
|
-
|
144
|
-
*Bogdan Gusiev*
|
145
|
-
|
146
|
-
* Fix `has_secure_password` not to trigger `password_confirmation` validations
|
147
|
-
if no `password_confirmation` is set.
|
148
|
-
|
149
|
-
*Vladimir Kiselev*
|
150
|
-
|
151
|
-
* `inclusion` / `exclusion` validations with ranges will only use the faster
|
152
|
-
`Range#cover` for numerical ranges, and the more accurate `Range#include?`
|
153
|
-
for non-numerical ones.
|
154
|
-
|
155
|
-
Fixes range validations like `:a..:f` that used to pass with values like `:be`.
|
156
|
-
Fixes #10593.
|
157
|
-
|
158
|
-
*Charles Bergeron*
|
159
|
-
|
160
|
-
* Fix regression in `has_secure_password`. When a password is set, but a
|
161
|
-
confirmation is an empty string, it would incorrectly save.
|
162
|
-
|
163
|
-
*Steve Klabnik* and *Phillip Calvin*
|
164
|
-
|
165
|
-
* Deprecate `Validator#setup`. This should be done manually now in the validator's constructor.
|
166
|
-
|
167
|
-
*Nick Sutterer*
|
168
|
-
|
169
|
-
Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activemodel/CHANGELOG.md) for previous changes.
|
58
|
+
Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/README.rdoc
CHANGED
@@ -49,7 +49,8 @@ behavior out of the box:
|
|
49
49
|
send("#{attr}=", nil)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
|
+
person = Person.new
|
53
54
|
person.clear_name
|
54
55
|
person.clear_age
|
55
56
|
|
@@ -78,7 +79,21 @@ behavior out of the box:
|
|
78
79
|
class Person
|
79
80
|
include ActiveModel::Dirty
|
80
81
|
|
81
|
-
|
82
|
+
define_attribute_methods :name
|
83
|
+
|
84
|
+
def name
|
85
|
+
@name
|
86
|
+
end
|
87
|
+
|
88
|
+
def name=(val)
|
89
|
+
name_will_change! unless val == @name
|
90
|
+
@name = val
|
91
|
+
end
|
92
|
+
|
93
|
+
def save
|
94
|
+
# do persistence work
|
95
|
+
changes_applied
|
96
|
+
end
|
82
97
|
end
|
83
98
|
|
84
99
|
person = Person.new
|
@@ -88,6 +103,7 @@ behavior out of the box:
|
|
88
103
|
person.changed? # => true
|
89
104
|
person.changed # => ['name']
|
90
105
|
person.changes # => { 'name' => [nil, 'bob'] }
|
106
|
+
person.save
|
91
107
|
person.name = 'robert'
|
92
108
|
person.save
|
93
109
|
person.previous_changes # => {'name' => ['bob, 'robert']}
|
@@ -116,7 +132,10 @@ behavior out of the box:
|
|
116
132
|
"Name"
|
117
133
|
end
|
118
134
|
end
|
119
|
-
|
135
|
+
|
136
|
+
person = Person.new
|
137
|
+
person.name = nil
|
138
|
+
person.validate!
|
120
139
|
person.errors.full_messages
|
121
140
|
# => ["Name cannot be nil"]
|
122
141
|
|
@@ -128,7 +147,7 @@ behavior out of the box:
|
|
128
147
|
extend ActiveModel::Naming
|
129
148
|
end
|
130
149
|
|
131
|
-
NamedPerson.model_name
|
150
|
+
NamedPerson.model_name.name # => "NamedPerson"
|
132
151
|
NamedPerson.model_name.human # => "Named person"
|
133
152
|
|
134
153
|
{Learn more}[link:classes/ActiveModel/Naming.html]
|
@@ -180,41 +199,41 @@ behavior out of the box:
|
|
180
199
|
|
181
200
|
* Validation support
|
182
201
|
|
183
|
-
|
184
|
-
|
202
|
+
class Person
|
203
|
+
include ActiveModel::Validations
|
185
204
|
|
186
|
-
|
205
|
+
attr_accessor :first_name, :last_name
|
187
206
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
207
|
+
validates_each :first_name, :last_name do |record, attr, value|
|
208
|
+
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
|
209
|
+
end
|
210
|
+
end
|
192
211
|
|
193
|
-
|
194
|
-
|
195
|
-
|
212
|
+
person = Person.new
|
213
|
+
person.first_name = 'zoolander'
|
214
|
+
person.valid? # => false
|
196
215
|
|
197
216
|
{Learn more}[link:classes/ActiveModel/Validations.html]
|
198
217
|
|
199
218
|
* Custom validators
|
219
|
+
|
220
|
+
class HasNameValidator < ActiveModel::Validator
|
221
|
+
def validate(record)
|
222
|
+
record.errors[:name] = "must exist" if record.name.blank?
|
223
|
+
end
|
224
|
+
end
|
200
225
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
class HasNameValidator < ActiveModel::Validator
|
208
|
-
def validate(record)
|
209
|
-
record.errors[:name] = "must exist" if record.name.blank?
|
210
|
-
end
|
211
|
-
end
|
226
|
+
class ValidatorPerson
|
227
|
+
include ActiveModel::Validations
|
228
|
+
validates_with HasNameValidator
|
229
|
+
attr_accessor :name
|
230
|
+
end
|
212
231
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
232
|
+
p = ValidatorPerson.new
|
233
|
+
p.valid? # => false
|
234
|
+
p.errors.full_messages # => ["Name must exist"]
|
235
|
+
p.name = "Bob"
|
236
|
+
p.valid? # => true
|
218
237
|
|
219
238
|
{Learn more}[link:classes/ActiveModel/Validator.html]
|
220
239
|
|
@@ -227,7 +246,7 @@ The latest version of Active Model can be installed with RubyGems:
|
|
227
246
|
|
228
247
|
Source code can be downloaded as part of the Rails project on GitHub
|
229
248
|
|
230
|
-
* https://github.com/rails/rails/tree/
|
249
|
+
* https://github.com/rails/rails/tree/master/activemodel
|
231
250
|
|
232
251
|
|
233
252
|
== License
|
@@ -243,6 +262,11 @@ API documentation is at
|
|
243
262
|
|
244
263
|
* http://api.rubyonrails.org
|
245
264
|
|
246
|
-
Bug reports
|
265
|
+
Bug reports can be filed for the Ruby on Rails project here:
|
247
266
|
|
248
267
|
* https://github.com/rails/rails/issues
|
268
|
+
|
269
|
+
Feature requests should be discussed on the rails-core mailing list here:
|
270
|
+
|
271
|
+
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
272
|
+
|
@@ -40,13 +40,15 @@ module ActiveModel
|
|
40
40
|
self
|
41
41
|
end
|
42
42
|
|
43
|
-
# Returns an
|
43
|
+
# Returns an Array of all key attributes if any is set, regardless if
|
44
44
|
# the object is persisted or not. Returns +nil+ if there are no key attributes.
|
45
45
|
#
|
46
|
-
# class Person
|
46
|
+
# class Person
|
47
|
+
# include ActiveModel::Conversion
|
48
|
+
# attr_accessor :id
|
47
49
|
# end
|
48
50
|
#
|
49
|
-
# person = Person.create
|
51
|
+
# person = Person.create(id: 1)
|
50
52
|
# person.to_key # => [1]
|
51
53
|
def to_key
|
52
54
|
key = respond_to?(:id) && id
|
@@ -56,10 +58,15 @@ module ActiveModel
|
|
56
58
|
# Returns a +string+ representing the object's key suitable for use in URLs,
|
57
59
|
# or +nil+ if <tt>persisted?</tt> is +false+.
|
58
60
|
#
|
59
|
-
# class Person
|
61
|
+
# class Person
|
62
|
+
# include ActiveModel::Conversion
|
63
|
+
# attr_accessor :id
|
64
|
+
# def persisted?
|
65
|
+
# true
|
66
|
+
# end
|
60
67
|
# end
|
61
68
|
#
|
62
|
-
# person = Person.create
|
69
|
+
# person = Person.create(id: 1)
|
63
70
|
# person.to_param # => "1"
|
64
71
|
def to_param
|
65
72
|
(persisted? && key = to_key) ? key.join('-') : nil
|
@@ -83,8 +90,8 @@ module ActiveModel
|
|
83
90
|
# internal method and should not be accessed directly.
|
84
91
|
def _to_partial_path #:nodoc:
|
85
92
|
@_to_partial_path ||= begin
|
86
|
-
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(
|
87
|
-
collection = ActiveSupport::Inflector.tableize(
|
93
|
+
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
|
94
|
+
collection = ActiveSupport::Inflector.tableize(name)
|
88
95
|
"#{collection}/#{element}".freeze
|
89
96
|
end
|
90
97
|
end
|
data/lib/active_model/dirty.rb
CHANGED
@@ -15,8 +15,9 @@ module ActiveModel
|
|
15
15
|
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
|
16
16
|
# attribute.
|
17
17
|
# * Call <tt>changes_applied</tt> after the changes are persisted.
|
18
|
-
# * Call <tt>
|
18
|
+
# * Call <tt>clear_changes_information</tt> when you want to reset the changes
|
19
19
|
# information.
|
20
|
+
# * Call <tt>restore_attributes</tt> when you want to restore previous data.
|
20
21
|
#
|
21
22
|
# A minimal implementation could be:
|
22
23
|
#
|
@@ -36,11 +37,18 @@ module ActiveModel
|
|
36
37
|
#
|
37
38
|
# def save
|
38
39
|
# # do persistence work
|
40
|
+
#
|
39
41
|
# changes_applied
|
40
42
|
# end
|
41
43
|
#
|
42
44
|
# def reload!
|
43
|
-
#
|
45
|
+
# # get the values from the persistence layer
|
46
|
+
#
|
47
|
+
# clear_changes_information
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# def rollback!
|
51
|
+
# restore_attributes
|
44
52
|
# end
|
45
53
|
# end
|
46
54
|
#
|
@@ -72,6 +80,13 @@ module ActiveModel
|
|
72
80
|
# person.reload!
|
73
81
|
# person.previous_changes # => {}
|
74
82
|
#
|
83
|
+
# Rollback the changes:
|
84
|
+
#
|
85
|
+
# person.name = "Uncle Bob"
|
86
|
+
# person.rollback!
|
87
|
+
# person.name # => "Bill"
|
88
|
+
# person.name_changed? # => false
|
89
|
+
#
|
75
90
|
# Assigning the same value leaves the attribute unchanged:
|
76
91
|
#
|
77
92
|
# person.name = 'Bill'
|
@@ -84,9 +99,11 @@ module ActiveModel
|
|
84
99
|
# person.changed # => ["name"]
|
85
100
|
# person.changes # => {"name" => ["Bill", "Bob"]}
|
86
101
|
#
|
87
|
-
# If an attribute is modified in-place then make use of
|
88
|
-
# to mark that the attribute is changing.
|
89
|
-
# changes to in-place attributes.
|
102
|
+
# If an attribute is modified in-place then make use of
|
103
|
+
# +[attribute_name]_will_change!+ to mark that the attribute is changing.
|
104
|
+
# Otherwise Active Model can't track changes to in-place attributes. Note
|
105
|
+
# that Active Record can detect in-place modifications automatically. You do
|
106
|
+
# not need to call +[attribute_name]_will_change!+ on Active Record models.
|
90
107
|
#
|
91
108
|
# person.name_will_change!
|
92
109
|
# person.name_change # => ["Bill", "Bill"]
|
@@ -99,6 +116,7 @@ module ActiveModel
|
|
99
116
|
included do
|
100
117
|
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
|
101
118
|
attribute_method_affix prefix: 'reset_', suffix: '!'
|
119
|
+
attribute_method_affix prefix: 'restore_', suffix: '!'
|
102
120
|
end
|
103
121
|
|
104
122
|
# Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
|
@@ -162,20 +180,30 @@ module ActiveModel
|
|
162
180
|
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
|
163
181
|
end
|
164
182
|
|
183
|
+
# Restore all previous data of the provided attributes.
|
184
|
+
def restore_attributes(attributes = changed)
|
185
|
+
attributes.each { |attr| restore_attribute! attr }
|
186
|
+
end
|
187
|
+
|
165
188
|
private
|
166
189
|
|
167
190
|
# Removes current changes and makes them accessible through +previous_changes+.
|
168
|
-
def changes_applied
|
191
|
+
def changes_applied # :doc:
|
169
192
|
@previously_changed = changes
|
170
193
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
171
194
|
end
|
172
195
|
|
173
|
-
#
|
174
|
-
def
|
196
|
+
# Clear all dirty data: current changes and previous changes.
|
197
|
+
def clear_changes_information # :doc:
|
175
198
|
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
176
199
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
177
200
|
end
|
178
201
|
|
202
|
+
def reset_changes
|
203
|
+
ActiveSupport::Deprecation.warn "#reset_changes is deprecated and will be removed on Rails 5. Please use #clear_changes_information instead."
|
204
|
+
clear_changes_information
|
205
|
+
end
|
206
|
+
|
179
207
|
# Handle <tt>*_change</tt> for +method_missing+.
|
180
208
|
def attribute_change(attr)
|
181
209
|
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
|
@@ -191,15 +219,36 @@ module ActiveModel
|
|
191
219
|
rescue TypeError, NoMethodError
|
192
220
|
end
|
193
221
|
|
194
|
-
|
222
|
+
set_attribute_was(attr, value)
|
195
223
|
end
|
196
224
|
|
197
225
|
# Handle <tt>reset_*!</tt> for +method_missing+.
|
198
226
|
def reset_attribute!(attr)
|
227
|
+
ActiveSupport::Deprecation.warn "#reset_#{attr}! is deprecated and will be removed on Rails 5. Please use #restore_#{attr}! instead."
|
228
|
+
|
229
|
+
restore_attribute!(attr)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Handle <tt>restore_*!</tt> for +method_missing+.
|
233
|
+
def restore_attribute!(attr)
|
199
234
|
if attribute_changed?(attr)
|
200
235
|
__send__("#{attr}=", changed_attributes[attr])
|
201
|
-
|
236
|
+
clear_attribute_changes([attr])
|
202
237
|
end
|
203
238
|
end
|
239
|
+
|
240
|
+
# This is necessary because `changed_attributes` might be overridden in
|
241
|
+
# other implemntations (e.g. in `ActiveRecord`)
|
242
|
+
alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc:
|
243
|
+
|
244
|
+
# Force an attribute to have a particular "before" value
|
245
|
+
def set_attribute_was(attr, old_value)
|
246
|
+
attributes_changed_by_setter[attr] = old_value
|
247
|
+
end
|
248
|
+
|
249
|
+
# Remove changes information for the provided attributes.
|
250
|
+
def clear_attribute_changes(attributes)
|
251
|
+
attributes_changed_by_setter.except!(*attributes)
|
252
|
+
end
|
204
253
|
end
|
205
254
|
end
|
data/lib/active_model/errors.rb
CHANGED
@@ -23,7 +23,7 @@ module ActiveModel
|
|
23
23
|
# attr_reader :errors
|
24
24
|
#
|
25
25
|
# def validate!
|
26
|
-
# errors.add(:name, "cannot be nil") if name
|
26
|
+
# errors.add(:name, "cannot be nil") if name.nil?
|
27
27
|
# end
|
28
28
|
#
|
29
29
|
# # The following methods are needed to be minimally implemented
|
@@ -289,6 +289,13 @@ module ActiveModel
|
|
289
289
|
# # => NameIsInvalid: name is invalid
|
290
290
|
#
|
291
291
|
# person.errors.messages # => {}
|
292
|
+
#
|
293
|
+
# +attribute+ should be set to <tt>:base</tt> if the error is not
|
294
|
+
# directly associated with a single attribute.
|
295
|
+
#
|
296
|
+
# person.errors.add(:base, "either name or email must be present")
|
297
|
+
# person.errors.messages
|
298
|
+
# # => {:base=>["either name or email must be present"]}
|
292
299
|
def add(attribute, message = :invalid, options = {})
|
293
300
|
message = normalize_message(attribute, message, options)
|
294
301
|
if exception = options[:strict]
|
@@ -427,7 +434,7 @@ module ActiveModel
|
|
427
434
|
|
428
435
|
options = {
|
429
436
|
default: defaults,
|
430
|
-
model: @base.
|
437
|
+
model: @base.model_name.human,
|
431
438
|
attribute: @base.class.human_attribute_name(attribute),
|
432
439
|
value: value
|
433
440
|
}.merge!(options)
|
data/lib/active_model/lint.rb
CHANGED
@@ -73,16 +73,19 @@ module ActiveModel
|
|
73
73
|
|
74
74
|
# == \Naming
|
75
75
|
#
|
76
|
-
# Model.model_name must return a string with some
|
77
|
-
# <tt>:human</tt>, <tt>:singular</tt> and
|
78
|
-
# ActiveModel::Naming for more information.
|
76
|
+
# Model.model_name and Model#model_name must return a string with some
|
77
|
+
# convenience methods: # <tt>:human</tt>, <tt>:singular</tt> and
|
78
|
+
# <tt>:plural</tt>. Check ActiveModel::Naming for more information.
|
79
79
|
def test_model_naming
|
80
|
-
assert model.class.respond_to?(:model_name), "The model should respond to model_name"
|
80
|
+
assert model.class.respond_to?(:model_name), "The model class should respond to model_name"
|
81
81
|
model_name = model.class.model_name
|
82
82
|
assert model_name.respond_to?(:to_str)
|
83
83
|
assert model_name.human.respond_to?(:to_str)
|
84
84
|
assert model_name.singular.respond_to?(:to_str)
|
85
85
|
assert model_name.plural.respond_to?(:to_str)
|
86
|
+
|
87
|
+
assert model.respond_to?(:model_name), "The model instance should respond to model_name"
|
88
|
+
assert_equal model.model_name, model.class.model_name
|
86
89
|
end
|
87
90
|
|
88
91
|
# == \Errors Testing
|
@@ -14,9 +14,15 @@ en:
|
|
14
14
|
empty: "can't be empty"
|
15
15
|
blank: "can't be blank"
|
16
16
|
present: "must be blank"
|
17
|
-
too_long:
|
18
|
-
|
19
|
-
|
17
|
+
too_long:
|
18
|
+
one: "is too long (maximum is 1 character)"
|
19
|
+
other: "is too long (maximum is %{count} characters)"
|
20
|
+
too_short:
|
21
|
+
one: "is too short (minimum is 1 character)"
|
22
|
+
other: "is too short (minimum is %{count} characters)"
|
23
|
+
wrong_length:
|
24
|
+
one: "is the wrong length (should be 1 character)"
|
25
|
+
other: "is the wrong length (should be %{count} characters)"
|
20
26
|
not_a_number: "is not a number"
|
21
27
|
not_an_integer: "must be an integer"
|
22
28
|
greater_than: "must be greater than %{count}"
|
data/lib/active_model/model.rb
CHANGED
@@ -16,8 +16,8 @@ module ActiveModel
|
|
16
16
|
# end
|
17
17
|
#
|
18
18
|
# person = Person.new(name: 'bob', age: '18')
|
19
|
-
# person.name # =>
|
20
|
-
# person.age # => 18
|
19
|
+
# person.name # => "bob"
|
20
|
+
# person.age # => "18"
|
21
21
|
#
|
22
22
|
# Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
|
23
23
|
# to return +false+, which is the most common case. You may want to override
|
@@ -74,7 +74,7 @@ module ActiveModel
|
|
74
74
|
#
|
75
75
|
# person = Person.new(name: 'bob', age: '18')
|
76
76
|
# person.name # => "bob"
|
77
|
-
# person.age # => 18
|
77
|
+
# person.age # => "18"
|
78
78
|
def initialize(params={})
|
79
79
|
params.each do |attr, value|
|
80
80
|
self.public_send("#{attr}=", value)
|
data/lib/active_model/naming.rb
CHANGED
@@ -129,7 +129,7 @@ module ActiveModel
|
|
129
129
|
#
|
130
130
|
# Equivalent to +to_s+.
|
131
131
|
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
|
132
|
-
:to_str,
|
132
|
+
:to_str, to: :name
|
133
133
|
|
134
134
|
# Returns a new ActiveModel::Name instance. By default, the +namespace+
|
135
135
|
# and +name+ option will take the namespace and name of the given class
|
@@ -204,7 +204,7 @@ module ActiveModel
|
|
204
204
|
# extend ActiveModel::Naming
|
205
205
|
# end
|
206
206
|
#
|
207
|
-
# BookCover.model_name
|
207
|
+
# BookCover.model_name.name # => "BookCover"
|
208
208
|
# BookCover.model_name.human # => "Book cover"
|
209
209
|
#
|
210
210
|
# BookCover.model_name.i18n_key # => :book_cover
|
@@ -214,14 +214,20 @@ module ActiveModel
|
|
214
214
|
# is required to pass the Active Model Lint test. So either extending the
|
215
215
|
# provided method below, or rolling your own is required.
|
216
216
|
module Naming
|
217
|
+
def self.extended(base) #:nodoc:
|
218
|
+
base.remove_possible_method :model_name
|
219
|
+
base.delegate :model_name, to: :class
|
220
|
+
end
|
221
|
+
|
217
222
|
# Returns an ActiveModel::Name object for module. It can be
|
218
223
|
# used to retrieve all kinds of naming-related information
|
219
224
|
# (See ActiveModel::Name for more information).
|
220
225
|
#
|
221
|
-
# class Person
|
226
|
+
# class Person
|
227
|
+
# include ActiveModel::Model
|
222
228
|
# end
|
223
229
|
#
|
224
|
-
# Person.model_name
|
230
|
+
# Person.model_name.name # => "Person"
|
225
231
|
# Person.model_name.class # => ActiveModel::Name
|
226
232
|
# Person.model_name.singular # => "person"
|
227
233
|
# Person.model_name.plural # => "people"
|
@@ -298,12 +304,10 @@ module ActiveModel
|
|
298
304
|
end
|
299
305
|
|
300
306
|
def self.model_name_from_record_or_class(record_or_class) #:nodoc:
|
301
|
-
if record_or_class.respond_to?(:
|
302
|
-
record_or_class.model_name
|
303
|
-
elsif record_or_class.respond_to?(:to_model)
|
304
|
-
record_or_class.to_model.class.model_name
|
307
|
+
if record_or_class.respond_to?(:to_model)
|
308
|
+
record_or_class.to_model.model_name
|
305
309
|
else
|
306
|
-
record_or_class.
|
310
|
+
record_or_class.model_name
|
307
311
|
end
|
308
312
|
end
|
309
313
|
private_class_method :model_name_from_record_or_class
|
@@ -2,6 +2,11 @@ module ActiveModel
|
|
2
2
|
module SecurePassword
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
# BCrypt hash function can handle maximum 72 characters, and if we pass
|
6
|
+
# password of length more than 72 characters it ignores extra characters.
|
7
|
+
# Hence need to put a restriction on password length.
|
8
|
+
MAX_PASSWORD_LENGTH_ALLOWED = 72
|
9
|
+
|
5
10
|
class << self
|
6
11
|
attr_accessor :min_cost # :nodoc:
|
7
12
|
end
|
@@ -11,16 +16,20 @@ module ActiveModel
|
|
11
16
|
# Adds methods to set and authenticate against a BCrypt password.
|
12
17
|
# This mechanism requires you to have a +password_digest+ attribute.
|
13
18
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
19
|
+
# The following validations are added automatically:
|
20
|
+
# * Password must be present on creation
|
21
|
+
# * Password length should be less than or equal to 72 characters
|
22
|
+
# * Confirmation of password (using a +password_confirmation+ attribute)
|
23
|
+
#
|
24
|
+
# If password confirmation validation is not needed, simply leave out the
|
25
|
+
# value for +password_confirmation+ (i.e. don't provide a form field for
|
26
|
+
# it). When this attribute has a +nil+ value, the validation will not be
|
27
|
+
# triggered.
|
18
28
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# will not be triggered.
|
29
|
+
# For further customizability, it is possible to supress the default
|
30
|
+
# validations by passing <tt>validations: false</tt> as an argument.
|
22
31
|
#
|
23
|
-
#
|
32
|
+
# Add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password:
|
24
33
|
#
|
25
34
|
# gem 'bcrypt', '~> 3.1.7'
|
26
35
|
#
|
@@ -52,11 +61,11 @@ module ActiveModel
|
|
52
61
|
raise
|
53
62
|
end
|
54
63
|
|
55
|
-
attr_reader :password
|
56
|
-
|
57
64
|
include InstanceMethodsOnActivation
|
58
65
|
|
59
66
|
if options.fetch(:validations, true)
|
67
|
+
include ActiveModel::Validations
|
68
|
+
|
60
69
|
# This ensures the model has a password by checking whether the password_digest
|
61
70
|
# is present, so that this works with both new and existing records. However,
|
62
71
|
# when there is an error, the message is added to the password attribute instead
|
@@ -65,9 +74,11 @@ module ActiveModel
|
|
65
74
|
record.errors.add(:password, :blank) unless record.password_digest.present?
|
66
75
|
end
|
67
76
|
|
77
|
+
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
|
68
78
|
validates_confirmation_of :password, if: ->{ password.present? }
|
69
79
|
end
|
70
80
|
|
81
|
+
# This code is necessary as long as the protected_attributes gem is supported.
|
71
82
|
if respond_to?(:attributes_protected_by_default)
|
72
83
|
def self.attributes_protected_by_default #:nodoc:
|
73
84
|
super + ['password_digest']
|
@@ -91,8 +102,10 @@ module ActiveModel
|
|
91
102
|
BCrypt::Password.new(password_digest) == unencrypted_password && self
|
92
103
|
end
|
93
104
|
|
105
|
+
attr_reader :password
|
106
|
+
|
94
107
|
# Encrypts the password into the +password_digest+ attribute, only if the
|
95
|
-
# new password is not
|
108
|
+
# new password is not empty.
|
96
109
|
#
|
97
110
|
# class User < ActiveRecord::Base
|
98
111
|
# has_secure_password validations: false
|
@@ -106,7 +119,7 @@ module ActiveModel
|
|
106
119
|
def password=(unencrypted_password)
|
107
120
|
if unencrypted_password.nil?
|
108
121
|
self.password_digest = nil
|
109
|
-
elsif unencrypted_password.
|
122
|
+
elsif !unencrypted_password.empty?
|
110
123
|
@password = unencrypted_password
|
111
124
|
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
112
125
|
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
|
@@ -4,7 +4,7 @@ require 'active_support/core_ext/hash/slice'
|
|
4
4
|
module ActiveModel
|
5
5
|
# == Active \Model \Serialization
|
6
6
|
#
|
7
|
-
# Provides a basic serialization to a serializable_hash for your
|
7
|
+
# Provides a basic serialization to a serializable_hash for your objects.
|
8
8
|
#
|
9
9
|
# A minimal implementation could be:
|
10
10
|
#
|
@@ -25,14 +25,14 @@ module ActiveModel
|
|
25
25
|
# person.name = "Bob"
|
26
26
|
# person.serializable_hash # => {"name"=>"Bob"}
|
27
27
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# serializable hash will use instance methods that match the name
|
31
|
-
# attributes hash's keys. In order to override this behavior, take a look
|
32
|
-
# the private method +read_attribute_for_serialization+.
|
28
|
+
# An +attributes+ hash must be defined and should contain any attributes you
|
29
|
+
# need to be serialized. Attributes must be strings, not symbols.
|
30
|
+
# When called, serializable hash will use instance methods that match the name
|
31
|
+
# of the attributes hash's keys. In order to override this behavior, take a look
|
32
|
+
# at the private method +read_attribute_for_serialization+.
|
33
33
|
#
|
34
|
-
# Most of the time though,
|
35
|
-
#
|
34
|
+
# Most of the time though, either the JSON or XML serializations are needed.
|
35
|
+
# Both of these modules automatically include the
|
36
36
|
# <tt>ActiveModel::Serialization</tt> module, so there is no need to
|
37
37
|
# explicitly include it.
|
38
38
|
#
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
10
10
|
included do
|
11
11
|
extend ActiveModel::Naming
|
12
12
|
|
13
|
-
class_attribute :include_root_in_json
|
13
|
+
class_attribute :include_root_in_json
|
14
14
|
self.include_root_in_json = false
|
15
15
|
end
|
16
16
|
|
@@ -93,7 +93,7 @@ module ActiveModel
|
|
93
93
|
end
|
94
94
|
|
95
95
|
if root
|
96
|
-
root =
|
96
|
+
root = model_name.element if root == true
|
97
97
|
{ root => serializable_hash(options) }
|
98
98
|
else
|
99
99
|
serializable_hash(options)
|
@@ -84,7 +84,7 @@ module ActiveModel
|
|
84
84
|
@builder = options[:builder]
|
85
85
|
@builder.instruct! unless options[:skip_instruct]
|
86
86
|
|
87
|
-
root = (options[:root] || @serializable.
|
87
|
+
root = (options[:root] || @serializable.model_name.element).to_s
|
88
88
|
root = ActiveSupport::XmlMini.rename_key(root, options)
|
89
89
|
|
90
90
|
args = [root]
|
@@ -39,6 +39,7 @@ module ActiveModel
|
|
39
39
|
extend ActiveSupport::Concern
|
40
40
|
|
41
41
|
included do
|
42
|
+
extend ActiveModel::Naming
|
42
43
|
extend ActiveModel::Callbacks
|
43
44
|
extend ActiveModel::Translation
|
44
45
|
|
@@ -46,10 +47,9 @@ module ActiveModel
|
|
46
47
|
include HelperMethods
|
47
48
|
|
48
49
|
attr_accessor :validation_context
|
49
|
-
private :validation_context=
|
50
50
|
define_callbacks :validate, scope: :name
|
51
51
|
|
52
|
-
class_attribute :_validators
|
52
|
+
class_attribute :_validators
|
53
53
|
self._validators = Hash.new { |h,k| h[k] = [] }
|
54
54
|
end
|
55
55
|
|
@@ -142,6 +142,11 @@ module ActiveModel
|
|
142
142
|
# value.
|
143
143
|
def validate(*args, &block)
|
144
144
|
options = args.extract_options!
|
145
|
+
|
146
|
+
if args.all? { |arg| arg.is_a?(Symbol) }
|
147
|
+
options.assert_valid_keys([:on, :if, :unless])
|
148
|
+
end
|
149
|
+
|
145
150
|
if options.key?(:on)
|
146
151
|
options = options.dup
|
147
152
|
options[:if] = Array(options[:if])
|
@@ -149,6 +154,7 @@ module ActiveModel
|
|
149
154
|
Array(options[:on]).include?(o.validation_context)
|
150
155
|
}
|
151
156
|
end
|
157
|
+
|
152
158
|
args << options
|
153
159
|
set_callback(:validate, *args, &block)
|
154
160
|
end
|
@@ -286,6 +292,8 @@ module ActiveModel
|
|
286
292
|
# Runs all the specified validations and returns +true+ if no errors were
|
287
293
|
# added otherwise +false+.
|
288
294
|
#
|
295
|
+
# Aliased as validate.
|
296
|
+
#
|
289
297
|
# class Person
|
290
298
|
# include ActiveModel::Validations
|
291
299
|
#
|
@@ -320,6 +328,8 @@ module ActiveModel
|
|
320
328
|
self.validation_context = current_context
|
321
329
|
end
|
322
330
|
|
331
|
+
alias_method :validate, :valid?
|
332
|
+
|
323
333
|
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
|
324
334
|
# added, +false+ otherwise.
|
325
335
|
#
|
@@ -30,7 +30,7 @@ module ActiveModel
|
|
30
30
|
return
|
31
31
|
end
|
32
32
|
|
33
|
-
if
|
33
|
+
if allow_only_integer?(record)
|
34
34
|
unless value = parse_raw_value_as_an_integer(raw_value)
|
35
35
|
record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
|
36
36
|
return
|
@@ -75,6 +75,17 @@ module ActiveModel
|
|
75
75
|
filtered[:value] = value
|
76
76
|
filtered
|
77
77
|
end
|
78
|
+
|
79
|
+
def allow_only_integer?(record)
|
80
|
+
case options[:only_integer]
|
81
|
+
when Symbol
|
82
|
+
record.send(options[:only_integer])
|
83
|
+
when Proc
|
84
|
+
options[:only_integer].call(record)
|
85
|
+
else
|
86
|
+
options[:only_integer]
|
87
|
+
end
|
88
|
+
end
|
78
89
|
end
|
79
90
|
|
80
91
|
module HelperMethods
|
@@ -121,6 +132,7 @@ module ActiveModel
|
|
121
132
|
# * <tt>:equal_to</tt>
|
122
133
|
# * <tt>:less_than</tt>
|
123
134
|
# * <tt>:less_than_or_equal_to</tt>
|
135
|
+
# * <tt>:only_integer</tt>
|
124
136
|
#
|
125
137
|
# For example:
|
126
138
|
#
|
@@ -53,7 +53,7 @@ module ActiveModel
|
|
53
53
|
#
|
54
54
|
# Configuration options:
|
55
55
|
# * <tt>:on</tt> - Specifies when this validation is active
|
56
|
-
# (<tt>:create</tt> or <tt>:update</tt
|
56
|
+
# (<tt>:create</tt> or <tt>:update</tt>).
|
57
57
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
58
58
|
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
59
59
|
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
|
@@ -106,7 +106,6 @@ module ActiveModel
|
|
106
106
|
# Accepts options that will be made available through the +options+ reader.
|
107
107
|
def initialize(options = {})
|
108
108
|
@options = options.except(:class).freeze
|
109
|
-
deprecated_setup(options)
|
110
109
|
end
|
111
110
|
|
112
111
|
# Returns the kind for this validator.
|
@@ -122,21 +121,6 @@ module ActiveModel
|
|
122
121
|
def validate(record)
|
123
122
|
raise NotImplementedError, "Subclasses must implement a validate(record) method."
|
124
123
|
end
|
125
|
-
|
126
|
-
private
|
127
|
-
def deprecated_setup(options) # TODO: remove me in 4.2.
|
128
|
-
return unless respond_to?(:setup)
|
129
|
-
ActiveSupport::Deprecation.warn "The `Validator#setup` instance method is deprecated and will be removed on Rails 4.2. Do your setup in the constructor instead:
|
130
|
-
|
131
|
-
class MyValidator < ActiveModel::Validator
|
132
|
-
def initialize(options={})
|
133
|
-
super
|
134
|
-
options[:class].send :attr_accessor, :custom_attribute
|
135
|
-
end
|
136
|
-
end
|
137
|
-
"
|
138
|
-
setup(options[:class])
|
139
|
-
end
|
140
124
|
end
|
141
125
|
|
142
126
|
# +EachValidator+ is a validator which iterates through the attributes given
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activemodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 4.
|
19
|
+
version: 4.2.0.beta1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 4.
|
26
|
+
version: 4.2.0.beta1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: builder
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,12 +99,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
99
|
version: 1.9.3
|
100
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- - "
|
102
|
+
- - ">"
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version:
|
104
|
+
version: 1.3.1
|
105
105
|
requirements: []
|
106
106
|
rubyforge_project:
|
107
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.2.2
|
108
108
|
signing_key:
|
109
109
|
specification_version: 4
|
110
110
|
summary: A toolkit for building modeling frameworks (part of Rails).
|