activemodel 5.0.7.2 → 5.1.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 +5 -5
- data/CHANGELOG.md +15 -219
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_model.rb +11 -12
- data/lib/active_model/attribute_assignment.rb +11 -11
- data/lib/active_model/attribute_methods.rb +13 -15
- data/lib/active_model/callbacks.rb +19 -20
- data/lib/active_model/conversion.rb +12 -3
- data/lib/active_model/dirty.rb +14 -14
- data/lib/active_model/errors.rb +27 -103
- data/lib/active_model/forbidden_attributes_protection.rb +1 -1
- data/lib/active_model/gem_version.rb +3 -3
- data/lib/active_model/lint.rb +0 -1
- data/lib/active_model/model.rb +3 -4
- data/lib/active_model/naming.rb +9 -10
- data/lib/active_model/secure_password.rb +1 -1
- data/lib/active_model/serialization.rb +2 -2
- data/lib/active_model/serializers/json.rb +2 -2
- data/lib/active_model/translation.rb +2 -3
- data/lib/active_model/type.rb +15 -19
- data/lib/active_model/type/big_integer.rb +4 -4
- data/lib/active_model/type/binary.rb +1 -1
- data/lib/active_model/type/boolean.rb +20 -9
- data/lib/active_model/type/date.rb +25 -25
- data/lib/active_model/type/date_time.rb +21 -21
- data/lib/active_model/type/decimal.rb +35 -39
- data/lib/active_model/type/float.rb +17 -8
- data/lib/active_model/type/helpers.rb +4 -4
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +2 -2
- data/lib/active_model/type/helpers/mutable.rb +2 -2
- data/lib/active_model/type/helpers/numeric.rb +18 -17
- data/lib/active_model/type/helpers/time_value.rb +23 -23
- data/lib/active_model/type/immutable_string.rb +9 -8
- data/lib/active_model/type/integer.rb +23 -21
- data/lib/active_model/type/registry.rb +12 -8
- data/lib/active_model/type/string.rb +1 -6
- data/lib/active_model/type/time.rb +15 -11
- data/lib/active_model/type/value.rb +6 -6
- data/lib/active_model/validations.rb +9 -12
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +41 -40
- data/lib/active_model/validations/callbacks.rb +4 -7
- data/lib/active_model/validations/clusivity.rb +7 -7
- data/lib/active_model/validations/confirmation.rb +15 -16
- data/lib/active_model/validations/exclusion.rb +1 -2
- data/lib/active_model/validations/format.rb +24 -24
- data/lib/active_model/validations/inclusion.rb +1 -2
- data/lib/active_model/validations/length.rb +6 -42
- data/lib/active_model/validations/numericality.rb +3 -11
- data/lib/active_model/validations/presence.rb +1 -2
- data/lib/active_model/validations/validates.rb +6 -6
- data/lib/active_model/validations/with.rb +4 -2
- data/lib/active_model/validator.rb +5 -6
- data/lib/active_model/version.rb +1 -1
- metadata +8 -11
- data/lib/active_model/test_case.rb +0 -4
- data/lib/active_model/type/decimal_without_scale.rb +0 -11
- data/lib/active_model/type/text.rb +0 -11
- data/lib/active_model/type/unsigned_integer.rb +0 -15
@@ -1,14 +1,15 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Type
|
3
|
-
module Helpers
|
4
|
-
module Numeric
|
3
|
+
module Helpers # :nodoc: all
|
4
|
+
module Numeric
|
5
5
|
def cast(value)
|
6
|
-
value =
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
value = \
|
7
|
+
case value
|
8
|
+
when true then 1
|
9
|
+
when false then 0
|
10
|
+
when ::String then value.presence
|
11
|
+
else value
|
12
|
+
end
|
12
13
|
super(value)
|
13
14
|
end
|
14
15
|
|
@@ -18,16 +19,16 @@ module ActiveModel
|
|
18
19
|
|
19
20
|
private
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def number_to_non_number?(old_value, new_value_before_type_cast)
|
23
|
+
old_value != nil && non_numeric_string?(new_value_before_type_cast)
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
def non_numeric_string?(value)
|
27
|
+
# 'wibble'.to_i will give zero, we want to make sure
|
28
|
+
# that we aren't marking int zero to string zero as
|
29
|
+
# changed.
|
30
|
+
value.to_s !~ /\A-?\d+\.?\d*\z/
|
31
|
+
end
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -2,8 +2,8 @@ require "active_support/core_ext/time/zones"
|
|
2
2
|
|
3
3
|
module ActiveModel
|
4
4
|
module Type
|
5
|
-
module Helpers
|
6
|
-
module TimeValue
|
5
|
+
module Helpers # :nodoc: all
|
6
|
+
module TimeValue
|
7
7
|
def serialize(value)
|
8
8
|
value = apply_seconds_precision(value)
|
9
9
|
|
@@ -19,7 +19,7 @@ module ActiveModel
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def is_utc?
|
22
|
-
::Time.zone_default.nil? || ::Time.zone_default =~
|
22
|
+
::Time.zone_default.nil? || ::Time.zone_default =~ "UTC"
|
23
23
|
end
|
24
24
|
|
25
25
|
def default_timezone
|
@@ -33,8 +33,8 @@ module ActiveModel
|
|
33
33
|
def apply_seconds_precision(value)
|
34
34
|
return value unless precision && value.respond_to?(:usec)
|
35
35
|
number_of_insignificant_digits = 6 - precision
|
36
|
-
round_power = 10
|
37
|
-
value.change(usec: value.usec
|
36
|
+
round_power = 10**number_of_insignificant_digits
|
37
|
+
value.change(usec: value.usec - value.usec % round_power)
|
38
38
|
end
|
39
39
|
|
40
40
|
def type_cast_for_schema(value)
|
@@ -47,30 +47,30 @@ module ActiveModel
|
|
47
47
|
|
48
48
|
private
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
|
51
|
+
# Treat 0000-00-00 00:00:00 as nil.
|
52
|
+
return if year.nil? || (year == 0 && mon == 0 && mday == 0)
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
if offset
|
55
|
+
time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
56
|
+
return unless time
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
time -= offset
|
59
|
+
is_utc? ? time : time.getlocal
|
60
|
+
else
|
61
|
+
::Time.public_send(default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
|
62
|
+
end
|
62
63
|
end
|
63
|
-
end
|
64
64
|
|
65
|
-
|
65
|
+
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
# Doesn't handle time zones.
|
68
|
+
def fast_string_to_time(string)
|
69
|
+
if string =~ ISO_DATETIME
|
70
|
+
microsec = ($7.to_r * 1_000_000).to_i
|
71
|
+
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
72
|
+
end
|
72
73
|
end
|
73
|
-
end
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -16,14 +16,15 @@ module ActiveModel
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
def cast_value(value)
|
20
|
+
result = \
|
21
|
+
case value
|
22
|
+
when true then "t"
|
23
|
+
when false then "f"
|
24
|
+
else value.to_s
|
25
|
+
end
|
26
|
+
result.freeze
|
27
|
+
end
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -29,38 +29,40 @@ module ActiveModel
|
|
29
29
|
result
|
30
30
|
end
|
31
31
|
|
32
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
33
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
32
34
|
protected
|
33
35
|
|
34
|
-
|
36
|
+
attr_reader :range
|
35
37
|
|
36
38
|
private
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def cast_value(value)
|
41
|
+
case value
|
42
|
+
when true then 1
|
43
|
+
when false then 0
|
44
|
+
else
|
45
|
+
value.to_i rescue nil
|
46
|
+
end
|
44
47
|
end
|
45
|
-
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
def ensure_in_range(value)
|
50
|
+
unless range.cover?(value)
|
51
|
+
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
52
|
+
end
|
50
53
|
end
|
51
|
-
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
def max_value
|
56
|
+
1 << (_limit * 8 - 1) # 8 bits per byte with one bit for sign
|
57
|
+
end
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
59
|
+
def min_value
|
60
|
+
-max_value
|
61
|
+
end
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
def _limit
|
64
|
+
limit || DEFAULT_LIMIT
|
65
|
+
end
|
64
66
|
end
|
65
67
|
end
|
66
68
|
end
|
@@ -21,19 +21,21 @@ module ActiveModel
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
25
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
24
26
|
protected
|
25
27
|
|
26
|
-
|
28
|
+
attr_reader :registrations
|
27
29
|
|
28
30
|
private
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def registration_klass
|
33
|
+
Registration
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
def find_registration(symbol, *args)
|
37
|
+
registrations.find { |r| r.matches?(symbol, *args) }
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
class Registration
|
@@ -55,9 +57,11 @@ module ActiveModel
|
|
55
57
|
type_name == name
|
56
58
|
end
|
57
59
|
|
60
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
61
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
58
62
|
protected
|
59
63
|
|
60
|
-
|
64
|
+
attr_reader :name, :block
|
61
65
|
end
|
62
66
|
end
|
63
67
|
# :startdoc:
|
@@ -25,18 +25,22 @@ module ActiveModel
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
28
|
+
def cast_value(value)
|
29
|
+
return value unless value.is_a?(::String)
|
30
|
+
return if value.empty?
|
31
|
+
|
32
|
+
if value.start_with?("2000-01-01")
|
33
|
+
dummy_time_value = value
|
34
|
+
else
|
35
|
+
dummy_time_value = "2000-01-01 #{value}"
|
36
|
+
end
|
37
|
+
|
38
|
+
fast_string_to_time(dummy_time_value) || begin
|
39
|
+
time_hash = ::Date._parse(dummy_time_value)
|
40
|
+
return if time_hash[:hour].nil?
|
41
|
+
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
|
42
|
+
end
|
38
43
|
end
|
39
|
-
end
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
@@ -105,12 +105,12 @@ module ActiveModel
|
|
105
105
|
|
106
106
|
private
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
108
|
+
# Convenience method for types which do not need separate type casting
|
109
|
+
# behavior for user and database inputs. Called by Value#cast for
|
110
|
+
# values except +nil+.
|
111
|
+
def cast_value(value) # :doc:
|
112
|
+
value
|
113
|
+
end
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "active_support/core_ext/array/extract_options"
|
2
|
+
require "active_support/core_ext/hash/keys"
|
3
|
+
require "active_support/core_ext/hash/except"
|
4
4
|
|
5
5
|
module ActiveModel
|
6
|
-
|
7
6
|
# == Active \Model \Validations
|
8
7
|
#
|
9
8
|
# Provides a full validation framework to your objects.
|
@@ -51,7 +50,7 @@ module ActiveModel
|
|
51
50
|
define_callbacks :validate, scope: :name
|
52
51
|
|
53
52
|
class_attribute :_validators, instance_writer: false
|
54
|
-
self._validators = Hash.new { |h,k| h[k] = [] }
|
53
|
+
self._validators = Hash.new { |h, k| h[k] = [] }
|
55
54
|
end
|
56
55
|
|
57
56
|
module ClassMethods
|
@@ -69,7 +68,7 @@ module ActiveModel
|
|
69
68
|
#
|
70
69
|
# Options:
|
71
70
|
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
72
|
-
# Runs in all validation contexts by default
|
71
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
73
72
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
74
73
|
# <tt>on: :custom_validation_context</tt> or
|
75
74
|
# <tt>on: [:create, :custom_validation_context]</tt>)
|
@@ -135,7 +134,7 @@ module ActiveModel
|
|
135
134
|
#
|
136
135
|
# Options:
|
137
136
|
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
138
|
-
# Runs in all validation contexts by default
|
137
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
139
138
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
140
139
|
# <tt>on: :custom_validation_context</tt> or
|
141
140
|
# <tt>on: [:create, :custom_validation_context]</tt>)
|
@@ -304,8 +303,6 @@ module ActiveModel
|
|
304
303
|
# Runs all the specified validations and returns +true+ if no errors were
|
305
304
|
# added otherwise +false+.
|
306
305
|
#
|
307
|
-
# Aliased as validate.
|
308
|
-
#
|
309
306
|
# class Person
|
310
307
|
# include ActiveModel::Validations
|
311
308
|
#
|
@@ -402,14 +399,14 @@ module ActiveModel
|
|
402
399
|
# end
|
403
400
|
alias :read_attribute_for_validation :send
|
404
401
|
|
405
|
-
|
402
|
+
private
|
406
403
|
|
407
|
-
def run_validations!
|
404
|
+
def run_validations!
|
408
405
|
_run_validate_callbacks
|
409
406
|
errors.empty?
|
410
407
|
end
|
411
408
|
|
412
|
-
def raise_validation_error
|
409
|
+
def raise_validation_error # :doc:
|
413
410
|
raise(ValidationError.new(self))
|
414
411
|
end
|
415
412
|
end
|
@@ -22,7 +22,7 @@ module ActiveModel
|
|
22
22
|
#
|
23
23
|
# There is also a list of default options supported by every validator:
|
24
24
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
25
|
-
# See <tt>ActiveModel::
|
25
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
26
26
|
def validates_absence_of(*attr_names)
|
27
27
|
validates_with AbsenceValidator, _merge_attributes(attr_names)
|
28
28
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module ActiveModel
|
2
|
-
|
3
2
|
module Validations
|
4
3
|
class AcceptanceValidator < EachValidator # :nodoc:
|
5
4
|
def initialize(options)
|
@@ -15,58 +14,60 @@ module ActiveModel
|
|
15
14
|
|
16
15
|
private
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
def setup!(klass)
|
18
|
+
klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def acceptable_option?(value)
|
22
|
+
Array(options[:accept]).include?(value)
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
class LazilyDefineAttributes < Module
|
26
|
+
def initialize(attribute_definition)
|
27
|
+
define_method(:respond_to_missing?) do |method_name, include_private = false|
|
28
|
+
super(method_name, include_private) || attribute_definition.matches?(method_name)
|
29
|
+
end
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
32
|
+
if attribute_definition.matches?(method_name)
|
33
|
+
attribute_definition.define_on(self.class)
|
34
|
+
send(method_name, *args, &block)
|
35
|
+
else
|
36
|
+
super(method_name, *args, &block)
|
37
|
+
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
-
end
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
class AttributeDefinition
|
43
|
+
def initialize(attributes)
|
44
|
+
@attributes = attributes.map(&:to_s)
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
def matches?(method_name)
|
48
|
+
attr_name = convert_to_reader_name(method_name)
|
49
|
+
attributes.include?(attr_name)
|
50
|
+
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
def define_on(klass)
|
53
|
+
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
54
|
+
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
55
|
+
klass.send(:attr_reader, *attr_readers)
|
56
|
+
klass.send(:attr_writer, *attr_writers)
|
57
|
+
end
|
59
58
|
|
60
|
-
|
59
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
60
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
61
|
+
protected
|
61
62
|
|
62
|
-
|
63
|
+
attr_reader :attributes
|
63
64
|
|
64
|
-
|
65
|
+
private
|
65
66
|
|
66
|
-
|
67
|
-
|
67
|
+
def convert_to_reader_name(method_name)
|
68
|
+
method_name.to_s.chomp("=")
|
69
|
+
end
|
68
70
|
end
|
69
|
-
end
|
70
71
|
end
|
71
72
|
|
72
73
|
module HelperMethods
|
@@ -94,7 +95,7 @@ module ActiveModel
|
|
94
95
|
#
|
95
96
|
# There is also a list of default options supported by every validator:
|
96
97
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
97
|
-
# See <tt>ActiveModel::
|
98
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information.
|
98
99
|
def validates_acceptance_of(*attr_names)
|
99
100
|
validates_with AcceptanceValidator, _merge_attributes(attr_names)
|
100
101
|
end
|