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
data/lib/active_model/naming.rb
CHANGED
@@ -110,6 +110,22 @@ module ActiveModel
|
|
110
110
|
# BlogPost.model_name.eql?('BlogPost') # => true
|
111
111
|
# BlogPost.model_name.eql?('Blog Post') # => false
|
112
112
|
|
113
|
+
##
|
114
|
+
# :method: match?
|
115
|
+
#
|
116
|
+
# :call-seq:
|
117
|
+
# match?(regexp)
|
118
|
+
#
|
119
|
+
# Equivalent to <tt>String#match?</tt>. Match the class name against the
|
120
|
+
# given regexp. Returns +true+ if there is a match, otherwise +false+.
|
121
|
+
#
|
122
|
+
# class BlogPost
|
123
|
+
# extend ActiveModel::Naming
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# BlogPost.model_name.match?(/Post/) # => true
|
127
|
+
# BlogPost.model_name.match?(/\d/) # => false
|
128
|
+
|
113
129
|
##
|
114
130
|
# :method: to_s
|
115
131
|
#
|
@@ -131,7 +147,7 @@ module ActiveModel
|
|
131
147
|
# to_str()
|
132
148
|
#
|
133
149
|
# Equivalent to +to_s+.
|
134
|
-
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
|
150
|
+
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :match?, :to_s,
|
135
151
|
:to_str, :as_json, to: :name
|
136
152
|
|
137
153
|
# Returns a new ActiveModel::Name instance. By default, the +namespace+
|
@@ -193,7 +209,7 @@ module ActiveModel
|
|
193
209
|
private
|
194
210
|
|
195
211
|
def _singularize(string)
|
196
|
-
ActiveSupport::Inflector.underscore(string).tr("/"
|
212
|
+
ActiveSupport::Inflector.underscore(string).tr("/", "_")
|
197
213
|
end
|
198
214
|
end
|
199
215
|
|
@@ -236,7 +252,7 @@ module ActiveModel
|
|
236
252
|
# Person.model_name.plural # => "people"
|
237
253
|
def model_name
|
238
254
|
@_model_name ||= begin
|
239
|
-
namespace =
|
255
|
+
namespace = module_parents.detect do |n|
|
240
256
|
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
|
241
257
|
end
|
242
258
|
ActiveModel::Name.new(self, namespace)
|
data/lib/active_model/railtie.rb
CHANGED
@@ -7,8 +7,14 @@ module ActiveModel
|
|
7
7
|
class Railtie < Rails::Railtie # :nodoc:
|
8
8
|
config.eager_load_namespaces << ActiveModel
|
9
9
|
|
10
|
+
config.active_model = ActiveSupport::OrderedOptions.new
|
11
|
+
|
10
12
|
initializer "active_model.secure_password" do
|
11
13
|
ActiveModel::SecurePassword.min_cost = Rails.env.test?
|
12
14
|
end
|
15
|
+
|
16
|
+
initializer "active_model.i18n_full_message" do
|
17
|
+
ActiveModel::Errors.i18n_full_message = config.active_model.delete(:i18n_full_message) || false
|
18
|
+
end
|
13
19
|
end
|
14
20
|
end
|
@@ -16,15 +16,16 @@ module ActiveModel
|
|
16
16
|
|
17
17
|
module ClassMethods
|
18
18
|
# Adds methods to set and authenticate against a BCrypt password.
|
19
|
-
# This mechanism requires you to have a +
|
19
|
+
# This mechanism requires you to have a +XXX_digest+ attribute.
|
20
|
+
# Where +XXX+ is the attribute name of your desired password.
|
20
21
|
#
|
21
22
|
# The following validations are added automatically:
|
22
23
|
# * Password must be present on creation
|
23
24
|
# * Password length should be less than or equal to 72 bytes
|
24
|
-
# * Confirmation of password (using a +
|
25
|
+
# * Confirmation of password (using a +XXX_confirmation+ attribute)
|
25
26
|
#
|
26
|
-
# If
|
27
|
-
# value for +
|
27
|
+
# If confirmation validation is not needed, simply leave out the
|
28
|
+
# value for +XXX_confirmation+ (i.e. don't provide a form field for
|
28
29
|
# it). When this attribute has a +nil+ value, the validation will not be
|
29
30
|
# triggered.
|
30
31
|
#
|
@@ -37,9 +38,10 @@ module ActiveModel
|
|
37
38
|
#
|
38
39
|
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
|
39
40
|
#
|
40
|
-
# # Schema: User(name:string, password_digest:string)
|
41
|
+
# # Schema: User(name:string, password_digest:string, recovery_password_digest:string)
|
41
42
|
# class User < ActiveRecord::Base
|
42
43
|
# has_secure_password
|
44
|
+
# has_secure_password :recovery_password, validations: false
|
43
45
|
# end
|
44
46
|
#
|
45
47
|
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
|
@@ -48,11 +50,15 @@ module ActiveModel
|
|
48
50
|
# user.save # => false, confirmation doesn't match
|
49
51
|
# user.password_confirmation = 'mUc3m00RsqyRe'
|
50
52
|
# user.save # => true
|
53
|
+
# user.recovery_password = "42password"
|
54
|
+
# user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
|
55
|
+
# user.save # => true
|
51
56
|
# user.authenticate('notright') # => false
|
52
57
|
# user.authenticate('mUc3m00RsqyRe') # => user
|
58
|
+
# user.authenticate_recovery_password('42password') # => user
|
53
59
|
# User.find_by(name: 'david').try(:authenticate, 'notright') # => false
|
54
60
|
# User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
|
55
|
-
def has_secure_password(
|
61
|
+
def has_secure_password(attribute = :password, validations: true)
|
56
62
|
# Load bcrypt gem only when has_secure_password is used.
|
57
63
|
# This is to avoid ActiveModel (and by extension the entire framework)
|
58
64
|
# being dependent on a binary library.
|
@@ -63,9 +69,40 @@ module ActiveModel
|
|
63
69
|
raise
|
64
70
|
end
|
65
71
|
|
66
|
-
|
72
|
+
attr_reader attribute
|
73
|
+
|
74
|
+
define_method("#{attribute}=") do |unencrypted_password|
|
75
|
+
if unencrypted_password.nil?
|
76
|
+
self.send("#{attribute}_digest=", nil)
|
77
|
+
elsif !unencrypted_password.empty?
|
78
|
+
instance_variable_set("@#{attribute}", unencrypted_password)
|
79
|
+
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
80
|
+
self.send("#{attribute}_digest=", BCrypt::Password.create(unencrypted_password, cost: cost))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
define_method("#{attribute}_confirmation=") do |unencrypted_password|
|
85
|
+
instance_variable_set("@#{attribute}_confirmation", unencrypted_password)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns +self+ if the password is correct, otherwise +false+.
|
89
|
+
#
|
90
|
+
# class User < ActiveRecord::Base
|
91
|
+
# has_secure_password validations: false
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
|
95
|
+
# user.save
|
96
|
+
# user.authenticate_password('notright') # => false
|
97
|
+
# user.authenticate_password('mUc3m00RsqyRe') # => user
|
98
|
+
define_method("authenticate_#{attribute}") do |unencrypted_password|
|
99
|
+
attribute_digest = send("#{attribute}_digest")
|
100
|
+
BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :authenticate, :authenticate_password if attribute == :password
|
67
104
|
|
68
|
-
if
|
105
|
+
if validations
|
69
106
|
include ActiveModel::Validations
|
70
107
|
|
71
108
|
# This ensures the model has a password by checking whether the password_digest
|
@@ -73,57 +110,13 @@ module ActiveModel
|
|
73
110
|
# when there is an error, the message is added to the password attribute instead
|
74
111
|
# so that the error message will make sense to the end-user.
|
75
112
|
validate do |record|
|
76
|
-
record.errors.add(
|
113
|
+
record.errors.add(attribute, :blank) unless record.send("#{attribute}_digest").present?
|
77
114
|
end
|
78
115
|
|
79
|
-
validates_length_of
|
80
|
-
validates_confirmation_of
|
116
|
+
validates_length_of attribute, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
|
117
|
+
validates_confirmation_of attribute, allow_blank: true
|
81
118
|
end
|
82
119
|
end
|
83
120
|
end
|
84
|
-
|
85
|
-
module InstanceMethodsOnActivation
|
86
|
-
# Returns +self+ if the password is correct, otherwise +false+.
|
87
|
-
#
|
88
|
-
# class User < ActiveRecord::Base
|
89
|
-
# has_secure_password validations: false
|
90
|
-
# end
|
91
|
-
#
|
92
|
-
# user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
|
93
|
-
# user.save
|
94
|
-
# user.authenticate('notright') # => false
|
95
|
-
# user.authenticate('mUc3m00RsqyRe') # => user
|
96
|
-
def authenticate(unencrypted_password)
|
97
|
-
BCrypt::Password.new(password_digest).is_password?(unencrypted_password) && self
|
98
|
-
end
|
99
|
-
|
100
|
-
attr_reader :password
|
101
|
-
|
102
|
-
# Encrypts the password into the +password_digest+ attribute, only if the
|
103
|
-
# new password is not empty.
|
104
|
-
#
|
105
|
-
# class User < ActiveRecord::Base
|
106
|
-
# has_secure_password validations: false
|
107
|
-
# end
|
108
|
-
#
|
109
|
-
# user = User.new
|
110
|
-
# user.password = nil
|
111
|
-
# user.password_digest # => nil
|
112
|
-
# user.password = 'mUc3m00RsqyRe'
|
113
|
-
# user.password_digest # => "$2a$10$4LEA7r4YmNHtvlAvHhsYAeZmk/xeUVtMTYqwIvYY76EW5GUqDiP4."
|
114
|
-
def password=(unencrypted_password)
|
115
|
-
if unencrypted_password.nil?
|
116
|
-
self.password_digest = nil
|
117
|
-
elsif !unencrypted_password.empty?
|
118
|
-
@password = unencrypted_password
|
119
|
-
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
120
|
-
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def password_confirmation=(unencrypted_password)
|
125
|
-
@password_confirmation = unencrypted_password
|
126
|
-
end
|
127
|
-
end
|
128
121
|
end
|
129
122
|
end
|
@@ -26,13 +26,13 @@ module ActiveModel
|
|
26
26
|
# user = User.find(1)
|
27
27
|
# user.as_json
|
28
28
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
29
|
-
# # "created_at" => "2006
|
29
|
+
# # "created_at" => "2006-08-01T17:27:133.000Z", "awesome" => true}
|
30
30
|
#
|
31
31
|
# ActiveRecord::Base.include_root_in_json = true
|
32
32
|
#
|
33
33
|
# user.as_json
|
34
34
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
35
|
-
# # "created_at" => "2006
|
35
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
36
36
|
#
|
37
37
|
# This behavior can also be achieved by setting the <tt>:root</tt> option
|
38
38
|
# to +true+ as in:
|
@@ -40,7 +40,7 @@ module ActiveModel
|
|
40
40
|
# user = User.find(1)
|
41
41
|
# user.as_json(root: true)
|
42
42
|
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
43
|
-
# # "created_at" => "2006
|
43
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
|
44
44
|
#
|
45
45
|
# Without any +options+, the returned Hash will include all the model's
|
46
46
|
# attributes.
|
@@ -48,7 +48,7 @@ module ActiveModel
|
|
48
48
|
# user = User.find(1)
|
49
49
|
# user.as_json
|
50
50
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
51
|
-
# # "created_at" => "2006
|
51
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true}
|
52
52
|
#
|
53
53
|
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
|
54
54
|
# the attributes included, and work similar to the +attributes+ method.
|
@@ -63,14 +63,14 @@ module ActiveModel
|
|
63
63
|
#
|
64
64
|
# user.as_json(methods: :permalink)
|
65
65
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
66
|
-
# # "created_at" => "2006
|
66
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
67
67
|
# # "permalink" => "1-konata-izumi" }
|
68
68
|
#
|
69
69
|
# To include associations use <tt>:include</tt>:
|
70
70
|
#
|
71
71
|
# user.as_json(include: :posts)
|
72
72
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
73
|
-
# # "created_at" => "2006
|
73
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
74
74
|
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
|
75
75
|
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
|
76
76
|
#
|
@@ -81,7 +81,7 @@ module ActiveModel
|
|
81
81
|
# only: :body } },
|
82
82
|
# only: :title } })
|
83
83
|
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
|
84
|
-
# # "created_at" => "2006
|
84
|
+
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
|
85
85
|
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
|
86
86
|
# # "title" => "Welcome to the weblog" },
|
87
87
|
# # { "comments" => [ { "body" => "Don't think too hard" } ],
|
@@ -93,11 +93,12 @@ module ActiveModel
|
|
93
93
|
include_root_in_json
|
94
94
|
end
|
95
95
|
|
96
|
+
hash = serializable_hash(options).as_json
|
96
97
|
if root
|
97
98
|
root = model_name.element if root == true
|
98
|
-
{ root =>
|
99
|
+
{ root => hash }
|
99
100
|
else
|
100
|
-
|
101
|
+
hash
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
@@ -14,16 +14,7 @@ module ActiveModel
|
|
14
14
|
# - Empty strings are coerced to +nil+
|
15
15
|
# - All other values will be coerced to +true+
|
16
16
|
class Boolean < Value
|
17
|
-
FALSE_VALUES = [
|
18
|
-
false, 0,
|
19
|
-
"0", :"0",
|
20
|
-
"f", :f,
|
21
|
-
"F", :F,
|
22
|
-
"false", :false,
|
23
|
-
"FALSE", :FALSE,
|
24
|
-
"off", :off,
|
25
|
-
"OFF", :OFF,
|
26
|
-
].to_set.freeze
|
17
|
+
FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"].to_set
|
27
18
|
|
28
19
|
def type # :nodoc:
|
29
20
|
:boolean
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module ActiveModel
|
4
4
|
module Type
|
5
5
|
class Date < Value # :nodoc:
|
6
|
-
include Helpers::Timezone
|
7
6
|
include Helpers::AcceptsMultiparameterTime.new
|
8
7
|
|
9
8
|
def type
|
@@ -50,7 +49,7 @@ module ActiveModel
|
|
50
49
|
|
51
50
|
def value_from_multiparameter_assignment(*)
|
52
51
|
time = super
|
53
|
-
time &&
|
52
|
+
time && time.to_date
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module ActiveModel
|
4
4
|
module Type
|
5
5
|
class DateTime < Value # :nodoc:
|
6
|
-
include Helpers::Timezone
|
7
6
|
include Helpers::TimeValue
|
8
7
|
include Helpers::AcceptsMultiparameterTime.new(
|
9
8
|
defaults: { 4 => 0, 5 => 0 }
|
@@ -40,9 +39,9 @@ module ActiveModel
|
|
40
39
|
end
|
41
40
|
|
42
41
|
def value_from_multiparameter_assignment(values_hash)
|
43
|
-
|
44
|
-
if
|
45
|
-
raise ArgumentError,
|
42
|
+
missing_parameters = (1..3).select { |key| !values_hash.key?(key) }
|
43
|
+
if missing_parameters.any?
|
44
|
+
raise ArgumentError, "Provided hash #{values_hash} doesn't contain necessary keys: #{missing_parameters}"
|
46
45
|
end
|
47
46
|
super
|
48
47
|
end
|
@@ -21,6 +21,18 @@ module ActiveModel
|
|
21
21
|
value
|
22
22
|
end
|
23
23
|
|
24
|
+
def is_utc?
|
25
|
+
::Time.zone_default.nil? || ::Time.zone_default =~ "UTC"
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_timezone
|
29
|
+
if is_utc?
|
30
|
+
:utc
|
31
|
+
else
|
32
|
+
:local
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
24
36
|
def apply_seconds_precision(value)
|
25
37
|
return value unless precision && value.respond_to?(:usec)
|
26
38
|
number_of_insignificant_digits = 6 - precision
|
@@ -58,7 +70,13 @@ module ActiveModel
|
|
58
70
|
# Doesn't handle time zones.
|
59
71
|
def fast_string_to_time(string)
|
60
72
|
if string =~ ISO_DATETIME
|
61
|
-
|
73
|
+
microsec_part = $7
|
74
|
+
if microsec_part && microsec_part.start_with?(".") && microsec_part.length == 7
|
75
|
+
microsec_part[0] = ""
|
76
|
+
microsec = microsec_part.to_i
|
77
|
+
else
|
78
|
+
microsec = (microsec_part.to_r * 1_000_000).to_i
|
79
|
+
end
|
62
80
|
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
63
81
|
end
|
64
82
|
end
|
@@ -31,13 +31,8 @@ module ActiveModel
|
|
31
31
|
result
|
32
32
|
end
|
33
33
|
|
34
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
35
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
36
|
-
protected
|
37
|
-
|
38
|
-
attr_reader :range
|
39
|
-
|
40
34
|
private
|
35
|
+
attr_reader :range
|
41
36
|
|
42
37
|
def cast_value(value)
|
43
38
|
case value
|
@@ -23,13 +23,8 @@ module ActiveModel
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
27
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
28
|
-
protected
|
29
|
-
|
30
|
-
attr_reader :registrations
|
31
|
-
|
32
26
|
private
|
27
|
+
attr_reader :registrations
|
33
28
|
|
34
29
|
def registration_klass
|
35
30
|
Registration
|
@@ -59,10 +54,7 @@ module ActiveModel
|
|
59
54
|
type_name == name
|
60
55
|
end
|
61
56
|
|
62
|
-
|
63
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
64
|
-
protected
|
65
|
-
|
57
|
+
private
|
66
58
|
attr_reader :name, :block
|
67
59
|
end
|
68
60
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module ActiveModel
|
4
4
|
module Type
|
5
5
|
class Time < Value # :nodoc:
|
6
|
-
include Helpers::Timezone
|
7
6
|
include Helpers::TimeValue
|
8
7
|
include Helpers::AcceptsMultiparameterTime.new(
|
9
8
|
defaults: { 1 => 1970, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
|
@@ -13,10 +12,6 @@ module ActiveModel
|
|
13
12
|
:time
|
14
13
|
end
|
15
14
|
|
16
|
-
def serialize(value)
|
17
|
-
super(cast(value))
|
18
|
-
end
|
19
|
-
|
20
15
|
def user_input_in_time_zone(value)
|
21
16
|
return unless value.present?
|
22
17
|
|
@@ -54,17 +54,13 @@ module ActiveModel
|
|
54
54
|
def define_on(klass)
|
55
55
|
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
56
56
|
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
57
|
-
klass.
|
58
|
-
klass.
|
57
|
+
klass.define_attribute_methods
|
58
|
+
klass.attr_reader(*attr_readers)
|
59
|
+
klass.attr_writer(*attr_writers)
|
59
60
|
end
|
60
61
|
|
61
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
62
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
63
|
-
protected
|
64
|
-
|
65
|
-
attr_reader :attributes
|
66
|
-
|
67
62
|
private
|
63
|
+
attr_reader :attributes
|
68
64
|
|
69
65
|
def convert_to_reader_name(method_name)
|
70
66
|
method_name.to_s.chomp("=")
|
@@ -32,7 +32,7 @@ module ActiveModel
|
|
32
32
|
@delimiter ||= options[:in] || options[:within]
|
33
33
|
end
|
34
34
|
|
35
|
-
#
|
35
|
+
# After Ruby 2.2, <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
|
36
36
|
# possible values in the range for equality, which is slower but more accurate.
|
37
37
|
# <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
|
38
38
|
# endpoints, which is fast but is only accurate on Numeric, Time, Date,
|
@@ -19,11 +19,11 @@ module ActiveModel
|
|
19
19
|
|
20
20
|
private
|
21
21
|
def setup!(klass)
|
22
|
-
klass.
|
22
|
+
klass.attr_reader(*attributes.map do |attribute|
|
23
23
|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
|
24
24
|
end.compact)
|
25
25
|
|
26
|
-
klass.
|
26
|
+
klass.attr_writer(*attributes.map do |attribute|
|
27
27
|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
|
28
28
|
end.compact)
|
29
29
|
end
|
@@ -19,7 +19,7 @@ module ActiveModel
|
|
19
19
|
# particular enumerable object.
|
20
20
|
#
|
21
21
|
# class Person < ActiveRecord::Base
|
22
|
-
# validates_inclusion_of :
|
22
|
+
# validates_inclusion_of :role, in: %w( admin contributor )
|
23
23
|
# validates_inclusion_of :age, in: 0..99
|
24
24
|
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
|
25
25
|
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "bigdecimal/util"
|
4
|
-
|
5
3
|
module ActiveModel
|
6
4
|
module Validations
|
7
5
|
class NumericalityValidator < EachValidator # :nodoc:
|
@@ -12,6 +10,7 @@ module ActiveModel
|
|
12
10
|
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
|
13
11
|
|
14
12
|
INTEGER_REGEX = /\A[+-]?\d+\z/
|
13
|
+
DECIMAL_REGEX = /\A[+-]?\d+\.?\d*(e|e[+-])?\d+\z/
|
15
14
|
|
16
15
|
def check_validity!
|
17
16
|
keys = CHECKS.keys - [:odd, :even]
|
@@ -93,17 +92,21 @@ module ActiveModel
|
|
93
92
|
raw_value
|
94
93
|
elsif is_integer?(raw_value)
|
95
94
|
raw_value.to_i
|
96
|
-
elsif !is_hexadecimal_literal?(raw_value)
|
97
|
-
|
95
|
+
elsif is_decimal?(raw_value) && !is_hexadecimal_literal?(raw_value)
|
96
|
+
BigDecimal(raw_value)
|
98
97
|
end
|
99
98
|
end
|
100
99
|
|
101
100
|
def is_integer?(raw_value)
|
102
|
-
INTEGER_REGEX
|
101
|
+
INTEGER_REGEX.match?(raw_value.to_s)
|
102
|
+
end
|
103
|
+
|
104
|
+
def is_decimal?(raw_value)
|
105
|
+
DECIMAL_REGEX.match?(raw_value.to_s)
|
103
106
|
end
|
104
107
|
|
105
108
|
def is_hexadecimal_literal?(raw_value)
|
106
|
-
/\A0[xX]
|
109
|
+
/\A0[xX]/.match?(raw_value)
|
107
110
|
end
|
108
111
|
|
109
112
|
def filtered_options(value)
|
@@ -63,7 +63,7 @@ module ActiveModel
|
|
63
63
|
# and strings in shortcut form.
|
64
64
|
#
|
65
65
|
# validates :email, format: /@/
|
66
|
-
# validates :
|
66
|
+
# validates :role, inclusion: %(admin contributor)
|
67
67
|
# validates :password, length: 6..20
|
68
68
|
#
|
69
69
|
# When using shortcut form, ranges and arrays are passed to your
|
@@ -116,7 +116,7 @@ module ActiveModel
|
|
116
116
|
key = "#{key.to_s.camelize}Validator"
|
117
117
|
|
118
118
|
begin
|
119
|
-
validator = key.include?("::"
|
119
|
+
validator = key.include?("::") ? key.constantize : const_get(key)
|
120
120
|
rescue NameError
|
121
121
|
raise ArgumentError, "Unknown validator: '#{key}'"
|
122
122
|
end
|
data/lib/active_model.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2004-
|
4
|
+
# Copyright (c) 2004-2019 David Heinemeier Hansson
|
5
5
|
#
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining
|
7
7
|
# a copy of this software and associated documentation files (the
|