activemodel 4.2.11.3 → 5.0.7.2
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 +149 -56
- data/MIT-LICENSE +1 -1
- data/README.rdoc +8 -16
- data/lib/active_model/attribute_assignment.rb +52 -0
- data/lib/active_model/attribute_methods.rb +16 -16
- data/lib/active_model/callbacks.rb +3 -3
- data/lib/active_model/conversion.rb +5 -5
- data/lib/active_model/dirty.rb +41 -40
- data/lib/active_model/errors.rb +175 -68
- data/lib/active_model/forbidden_attributes_protection.rb +3 -2
- data/lib/active_model/gem_version.rb +5 -5
- data/lib/active_model/lint.rb +32 -28
- data/lib/active_model/locale/en.yml +2 -1
- data/lib/active_model/model.rb +3 -4
- data/lib/active_model/naming.rb +5 -4
- data/lib/active_model/secure_password.rb +2 -9
- data/lib/active_model/serialization.rb +36 -9
- data/lib/active_model/type/big_integer.rb +13 -0
- data/lib/active_model/type/binary.rb +50 -0
- data/lib/active_model/type/boolean.rb +21 -0
- data/lib/active_model/type/date.rb +54 -0
- data/lib/active_model/type/date_time.rb +44 -0
- data/lib/active_model/type/decimal.rb +66 -0
- data/lib/active_model/type/decimal_without_scale.rb +11 -0
- data/lib/active_model/type/float.rb +25 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +35 -0
- data/lib/active_model/type/helpers/mutable.rb +18 -0
- data/lib/active_model/type/helpers/numeric.rb +34 -0
- data/lib/active_model/type/helpers/time_value.rb +77 -0
- data/lib/active_model/type/helpers.rb +4 -0
- data/lib/active_model/type/immutable_string.rb +29 -0
- data/lib/active_model/type/integer.rb +66 -0
- data/lib/active_model/type/registry.rb +64 -0
- data/lib/active_model/type/string.rb +24 -0
- data/lib/active_model/type/text.rb +11 -0
- data/lib/active_model/type/time.rb +42 -0
- data/lib/active_model/type/unsigned_integer.rb +15 -0
- data/lib/active_model/type/value.rb +116 -0
- data/lib/active_model/type.rb +59 -0
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +57 -9
- data/lib/active_model/validations/callbacks.rb +3 -3
- data/lib/active_model/validations/clusivity.rb +4 -3
- data/lib/active_model/validations/confirmation.rb +16 -4
- data/lib/active_model/validations/exclusion.rb +3 -1
- data/lib/active_model/validations/format.rb +1 -1
- data/lib/active_model/validations/helper_methods.rb +13 -0
- data/lib/active_model/validations/inclusion.rb +3 -3
- data/lib/active_model/validations/length.rb +49 -18
- data/lib/active_model/validations/numericality.rb +20 -11
- data/lib/active_model/validations/validates.rb +1 -1
- data/lib/active_model/validations/with.rb +0 -10
- data/lib/active_model/validations.rb +34 -1
- data/lib/active_model/validator.rb +5 -5
- data/lib/active_model/version.rb +1 -1
- data/lib/active_model.rb +4 -2
- metadata +31 -22
- data/lib/active_model/serializers/xml.rb +0 -238
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Type
|
3
|
+
module Helpers
|
4
|
+
module Mutable # :nodoc:
|
5
|
+
def cast(value)
|
6
|
+
deserialize(serialize(value))
|
7
|
+
end
|
8
|
+
|
9
|
+
# +raw_old_value+ will be the `_before_type_cast` version of the
|
10
|
+
# value (likely a string). +new_value+ will be the current, type
|
11
|
+
# cast value.
|
12
|
+
def changed_in_place?(raw_old_value, new_value)
|
13
|
+
raw_old_value != serialize(new_value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Type
|
3
|
+
module Helpers
|
4
|
+
module Numeric # :nodoc:
|
5
|
+
def cast(value)
|
6
|
+
value = case value
|
7
|
+
when true then 1
|
8
|
+
when false then 0
|
9
|
+
when ::String then value.presence
|
10
|
+
else value
|
11
|
+
end
|
12
|
+
super(value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc:
|
16
|
+
super || number_to_non_number?(old_value, new_value_before_type_cast)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def number_to_non_number?(old_value, new_value_before_type_cast)
|
22
|
+
old_value != nil && non_numeric_string?(new_value_before_type_cast)
|
23
|
+
end
|
24
|
+
|
25
|
+
def non_numeric_string?(value)
|
26
|
+
# 'wibble'.to_i will give zero, we want to make sure
|
27
|
+
# that we aren't marking int zero to string zero as
|
28
|
+
# changed.
|
29
|
+
value.to_s !~ /\A-?\d+\.?\d*\z/
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "active_support/core_ext/time/zones"
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Type
|
5
|
+
module Helpers
|
6
|
+
module TimeValue # :nodoc:
|
7
|
+
def serialize(value)
|
8
|
+
value = apply_seconds_precision(value)
|
9
|
+
|
10
|
+
if value.acts_like?(:time)
|
11
|
+
zone_conversion_method = is_utc? ? :getutc : :getlocal
|
12
|
+
|
13
|
+
if value.respond_to?(zone_conversion_method)
|
14
|
+
value = value.send(zone_conversion_method)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
value
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_utc?
|
22
|
+
::Time.zone_default.nil? || ::Time.zone_default =~ 'UTC'
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_timezone
|
26
|
+
if is_utc?
|
27
|
+
:utc
|
28
|
+
else
|
29
|
+
:local
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def apply_seconds_precision(value)
|
34
|
+
return value unless precision && value.respond_to?(:usec)
|
35
|
+
number_of_insignificant_digits = 6 - precision
|
36
|
+
round_power = 10 ** number_of_insignificant_digits
|
37
|
+
value.change(usec: value.usec / round_power * round_power)
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_cast_for_schema(value)
|
41
|
+
"'#{value.to_s(:db)}'"
|
42
|
+
end
|
43
|
+
|
44
|
+
def user_input_in_time_zone(value)
|
45
|
+
value.in_time_zone
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
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
|
+
|
54
|
+
if offset
|
55
|
+
time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
56
|
+
return unless time
|
57
|
+
|
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
|
63
|
+
end
|
64
|
+
|
65
|
+
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
66
|
+
|
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
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Type
|
3
|
+
class ImmutableString < Value # :nodoc:
|
4
|
+
def type
|
5
|
+
:string
|
6
|
+
end
|
7
|
+
|
8
|
+
def serialize(value)
|
9
|
+
case value
|
10
|
+
when ::Numeric, ActiveSupport::Duration then value.to_s
|
11
|
+
when true then "t"
|
12
|
+
when false then "f"
|
13
|
+
else super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def cast_value(value)
|
20
|
+
result = case value
|
21
|
+
when true then "t"
|
22
|
+
when false then "f"
|
23
|
+
else value.to_s
|
24
|
+
end
|
25
|
+
result.freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Type
|
3
|
+
class Integer < Value # :nodoc:
|
4
|
+
include Helpers::Numeric
|
5
|
+
|
6
|
+
# Column storage size in bytes.
|
7
|
+
# 4 bytes means a MySQL int or Postgres integer as opposed to smallint etc.
|
8
|
+
DEFAULT_LIMIT = 4
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@range = min_value...max_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def type
|
16
|
+
:integer
|
17
|
+
end
|
18
|
+
|
19
|
+
def deserialize(value)
|
20
|
+
return if value.nil?
|
21
|
+
value.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def serialize(value)
|
25
|
+
result = cast(value)
|
26
|
+
if result
|
27
|
+
ensure_in_range(result)
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
attr_reader :range
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def cast_value(value)
|
39
|
+
case value
|
40
|
+
when true then 1
|
41
|
+
when false then 0
|
42
|
+
else
|
43
|
+
value.to_i rescue nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def ensure_in_range(value)
|
48
|
+
unless range.cover?(value)
|
49
|
+
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def max_value
|
54
|
+
1 << (_limit * 8 - 1) # 8 bits per byte with one bit for sign
|
55
|
+
end
|
56
|
+
|
57
|
+
def min_value
|
58
|
+
-max_value
|
59
|
+
end
|
60
|
+
|
61
|
+
def _limit
|
62
|
+
self.limit || DEFAULT_LIMIT
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
# :stopdoc:
|
3
|
+
module Type
|
4
|
+
class Registry
|
5
|
+
def initialize
|
6
|
+
@registrations = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def register(type_name, klass = nil, **options, &block)
|
10
|
+
block ||= proc { |_, *args| klass.new(*args) }
|
11
|
+
registrations << registration_klass.new(type_name, block, **options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def lookup(symbol, *args)
|
15
|
+
registration = find_registration(symbol, *args)
|
16
|
+
|
17
|
+
if registration
|
18
|
+
registration.call(self, symbol, *args)
|
19
|
+
else
|
20
|
+
raise ArgumentError, "Unknown type #{symbol.inspect}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
attr_reader :registrations
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def registration_klass
|
31
|
+
Registration
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_registration(symbol, *args)
|
35
|
+
registrations.find { |r| r.matches?(symbol, *args) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Registration
|
40
|
+
# Options must be taken because of https://bugs.ruby-lang.org/issues/10856
|
41
|
+
def initialize(name, block, **)
|
42
|
+
@name = name
|
43
|
+
@block = block
|
44
|
+
end
|
45
|
+
|
46
|
+
def call(_registry, *args, **kwargs)
|
47
|
+
if kwargs.any? # https://bugs.ruby-lang.org/issues/10856
|
48
|
+
block.call(*args, **kwargs)
|
49
|
+
else
|
50
|
+
block.call(*args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def matches?(type_name, *args, **kwargs)
|
55
|
+
type_name == name
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
attr_reader :name, :block
|
61
|
+
end
|
62
|
+
end
|
63
|
+
# :startdoc:
|
64
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "active_model/type/immutable_string"
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Type
|
5
|
+
class String < ImmutableString # :nodoc:
|
6
|
+
def changed_in_place?(raw_old_value, new_value)
|
7
|
+
if new_value.is_a?(::String)
|
8
|
+
raw_old_value != new_value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def cast_value(value)
|
15
|
+
case value
|
16
|
+
when ::String then ::String.new(value)
|
17
|
+
when true then "t".freeze
|
18
|
+
when false then "f".freeze
|
19
|
+
else value.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Type
|
3
|
+
class Time < Value # :nodoc:
|
4
|
+
include Helpers::TimeValue
|
5
|
+
include Helpers::AcceptsMultiparameterTime.new(
|
6
|
+
defaults: { 1 => 1970, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
|
7
|
+
)
|
8
|
+
|
9
|
+
def type
|
10
|
+
:time
|
11
|
+
end
|
12
|
+
|
13
|
+
def user_input_in_time_zone(value)
|
14
|
+
return unless value.present?
|
15
|
+
|
16
|
+
case value
|
17
|
+
when ::String
|
18
|
+
value = "2000-01-01 #{value}"
|
19
|
+
when ::Time
|
20
|
+
value = value.change(year: 2000, day: 1, month: 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
super(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def cast_value(value)
|
29
|
+
return apply_seconds_precision(value) unless value.is_a?(::String)
|
30
|
+
return if value.empty?
|
31
|
+
|
32
|
+
dummy_time_value = value.sub(/\A(\d\d\d\d-\d\d-\d\d |)/, "2000-01-01 ")
|
33
|
+
|
34
|
+
fast_string_to_time(dummy_time_value) || begin
|
35
|
+
time_hash = ::Date._parse(dummy_time_value)
|
36
|
+
return if time_hash[:hour].nil?
|
37
|
+
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Type
|
3
|
+
class Value
|
4
|
+
attr_reader :precision, :scale, :limit
|
5
|
+
|
6
|
+
def initialize(precision: nil, limit: nil, scale: nil)
|
7
|
+
@precision = precision
|
8
|
+
@scale = scale
|
9
|
+
@limit = limit
|
10
|
+
end
|
11
|
+
|
12
|
+
def type # :nodoc:
|
13
|
+
end
|
14
|
+
|
15
|
+
# Converts a value from database input to the appropriate ruby type. The
|
16
|
+
# return value of this method will be returned from
|
17
|
+
# ActiveRecord::AttributeMethods::Read#read_attribute. The default
|
18
|
+
# implementation just calls Value#cast.
|
19
|
+
#
|
20
|
+
# +value+ The raw input, as provided from the database.
|
21
|
+
def deserialize(value)
|
22
|
+
cast(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Type casts a value from user input (e.g. from a setter). This value may
|
26
|
+
# be a string from the form builder, or a ruby object passed to a setter.
|
27
|
+
# There is currently no way to differentiate between which source it came
|
28
|
+
# from.
|
29
|
+
#
|
30
|
+
# The return value of this method will be returned from
|
31
|
+
# ActiveRecord::AttributeMethods::Read#read_attribute. See also:
|
32
|
+
# Value#cast_value.
|
33
|
+
#
|
34
|
+
# +value+ The raw input, as provided to the attribute setter.
|
35
|
+
def cast(value)
|
36
|
+
cast_value(value) unless value.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
# Casts a value from the ruby type to a type that the database knows how
|
40
|
+
# to understand. The returned value from this method should be a
|
41
|
+
# +String+, +Numeric+, +Date+, +Time+, +Symbol+, +true+, +false+, or
|
42
|
+
# +nil+.
|
43
|
+
def serialize(value)
|
44
|
+
value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Type casts a value for schema dumping. This method is private, as we are
|
48
|
+
# hoping to remove it entirely.
|
49
|
+
def type_cast_for_schema(value) # :nodoc:
|
50
|
+
value.inspect
|
51
|
+
end
|
52
|
+
|
53
|
+
# These predicates are not documented, as I need to look further into
|
54
|
+
# their use, and see if they can be removed entirely.
|
55
|
+
def binary? # :nodoc:
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
# Determines whether a value has changed for dirty checking. +old_value+
|
60
|
+
# and +new_value+ will always be type-cast. Types should not need to
|
61
|
+
# override this method.
|
62
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
63
|
+
old_value != new_value
|
64
|
+
end
|
65
|
+
|
66
|
+
# Determines whether the mutable value has been modified since it was
|
67
|
+
# read. Returns +false+ by default. If your type returns an object
|
68
|
+
# which could be mutated, you should override this method. You will need
|
69
|
+
# to either:
|
70
|
+
#
|
71
|
+
# - pass +new_value+ to Value#serialize and compare it to
|
72
|
+
# +raw_old_value+
|
73
|
+
#
|
74
|
+
# or
|
75
|
+
#
|
76
|
+
# - pass +raw_old_value+ to Value#deserialize and compare it to
|
77
|
+
# +new_value+
|
78
|
+
#
|
79
|
+
# +raw_old_value+ The original value, before being passed to
|
80
|
+
# +deserialize+.
|
81
|
+
#
|
82
|
+
# +new_value+ The current value, after type casting.
|
83
|
+
def changed_in_place?(raw_old_value, new_value)
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
def map(value) # :nodoc:
|
88
|
+
yield value
|
89
|
+
end
|
90
|
+
|
91
|
+
def ==(other)
|
92
|
+
self.class == other.class &&
|
93
|
+
precision == other.precision &&
|
94
|
+
scale == other.scale &&
|
95
|
+
limit == other.limit
|
96
|
+
end
|
97
|
+
alias eql? ==
|
98
|
+
|
99
|
+
def hash
|
100
|
+
[self.class, precision, scale, limit].hash
|
101
|
+
end
|
102
|
+
|
103
|
+
def assert_valid_value(*)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
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
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'active_model/type/helpers'
|
2
|
+
require 'active_model/type/value'
|
3
|
+
|
4
|
+
require 'active_model/type/big_integer'
|
5
|
+
require 'active_model/type/binary'
|
6
|
+
require 'active_model/type/boolean'
|
7
|
+
require 'active_model/type/date'
|
8
|
+
require 'active_model/type/date_time'
|
9
|
+
require 'active_model/type/decimal'
|
10
|
+
require 'active_model/type/decimal_without_scale'
|
11
|
+
require 'active_model/type/float'
|
12
|
+
require 'active_model/type/immutable_string'
|
13
|
+
require 'active_model/type/integer'
|
14
|
+
require 'active_model/type/string'
|
15
|
+
require 'active_model/type/text'
|
16
|
+
require 'active_model/type/time'
|
17
|
+
require 'active_model/type/unsigned_integer'
|
18
|
+
|
19
|
+
require 'active_model/type/registry'
|
20
|
+
|
21
|
+
module ActiveModel
|
22
|
+
module Type
|
23
|
+
@registry = Registry.new
|
24
|
+
|
25
|
+
class << self
|
26
|
+
attr_accessor :registry # :nodoc:
|
27
|
+
delegate :add_modifier, to: :registry
|
28
|
+
|
29
|
+
# Add a new type to the registry, allowing it to be referenced as a
|
30
|
+
# symbol by ActiveModel::Attributes::ClassMethods#attribute. If your
|
31
|
+
# type is only meant to be used with a specific database adapter, you can
|
32
|
+
# do so by passing +adapter: :postgresql+. If your type has the same
|
33
|
+
# name as a native type for the current adapter, an exception will be
|
34
|
+
# raised unless you specify an +:override+ option. +override: true+ will
|
35
|
+
# cause your type to be used instead of the native type. +override:
|
36
|
+
# false+ will cause the native type to be used over yours if one exists.
|
37
|
+
def register(type_name, klass = nil, **options, &block)
|
38
|
+
registry.register(type_name, klass, **options, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def lookup(*args, **kwargs) # :nodoc:
|
42
|
+
registry.lookup(*args, **kwargs)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
register(:big_integer, Type::BigInteger)
|
47
|
+
register(:binary, Type::Binary)
|
48
|
+
register(:boolean, Type::Boolean)
|
49
|
+
register(:date, Type::Date)
|
50
|
+
register(:datetime, Type::DateTime)
|
51
|
+
register(:decimal, Type::Decimal)
|
52
|
+
register(:float, Type::Float)
|
53
|
+
register(:immutable_string, Type::ImmutableString)
|
54
|
+
register(:integer, Type::Integer)
|
55
|
+
register(:string, Type::String)
|
56
|
+
register(:text, Type::Text)
|
57
|
+
register(:time, Type::Time)
|
58
|
+
end
|
59
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
# == Active Model Absence Validator
|
3
|
+
# == \Active \Model Absence Validator
|
4
4
|
class AbsenceValidator < EachValidator #:nodoc:
|
5
5
|
def validate_each(record, attr_name, value)
|
6
6
|
record.errors.add(attr_name, :present, options) if value.present?
|
@@ -3,22 +3,69 @@ module ActiveModel
|
|
3
3
|
module Validations
|
4
4
|
class AcceptanceValidator < EachValidator # :nodoc:
|
5
5
|
def initialize(options)
|
6
|
-
super({ allow_nil: true, accept: "1" }.merge!(options))
|
6
|
+
super({ allow_nil: true, accept: ["1", true] }.merge!(options))
|
7
7
|
setup!(options[:class])
|
8
8
|
end
|
9
9
|
|
10
10
|
def validate_each(record, attribute, value)
|
11
|
-
unless value
|
11
|
+
unless acceptable_option?(value)
|
12
12
|
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
|
+
|
17
18
|
def setup!(klass)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
|
20
|
+
end
|
21
|
+
|
22
|
+
def acceptable_option?(value)
|
23
|
+
Array(options[:accept]).include?(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
class LazilyDefineAttributes < Module
|
27
|
+
def initialize(attribute_definition)
|
28
|
+
define_method(:respond_to_missing?) do |method_name, include_private=false|
|
29
|
+
super(method_name, include_private) || attribute_definition.matches?(method_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
33
|
+
if attribute_definition.matches?(method_name)
|
34
|
+
attribute_definition.define_on(self.class)
|
35
|
+
send(method_name, *args, &block)
|
36
|
+
else
|
37
|
+
super(method_name, *args, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class AttributeDefinition
|
44
|
+
def initialize(attributes)
|
45
|
+
@attributes = attributes.map(&:to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def matches?(method_name)
|
49
|
+
attr_name = convert_to_reader_name(method_name)
|
50
|
+
attributes.include?(attr_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def define_on(klass)
|
54
|
+
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
55
|
+
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
56
|
+
klass.send(:attr_reader, *attr_readers)
|
57
|
+
klass.send(:attr_writer, *attr_writers)
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
attr_reader :attributes
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def convert_to_reader_name(method_name)
|
67
|
+
method_name.to_s.chomp('=')
|
68
|
+
end
|
22
69
|
end
|
23
70
|
end
|
24
71
|
|
@@ -38,9 +85,10 @@ module ActiveModel
|
|
38
85
|
# Configuration options:
|
39
86
|
# * <tt>:message</tt> - A custom error message (default is: "must be
|
40
87
|
# accepted").
|
41
|
-
# * <tt>:accept</tt> - Specifies value that is considered accepted.
|
42
|
-
#
|
43
|
-
# an
|
88
|
+
# * <tt>:accept</tt> - Specifies a value that is considered accepted.
|
89
|
+
# Also accepts an array of possible values. The default value is
|
90
|
+
# an array ["1", true], which makes it easy to relate to an HTML
|
91
|
+
# checkbox. This should be set to, or include, +true+ if you are validating
|
44
92
|
# a database column, since the attribute is typecast from "1" to +true+
|
45
93
|
# before validation.
|
46
94
|
#
|
@@ -15,15 +15,15 @@ module ActiveModel
|
|
15
15
|
# after_validation :do_stuff_after_validation
|
16
16
|
# end
|
17
17
|
#
|
18
|
-
# Like other <tt>before_*</tt> callbacks if +before_validation+
|
19
|
-
# +
|
18
|
+
# Like other <tt>before_*</tt> callbacks if +before_validation+ throws
|
19
|
+
# +:abort+ then <tt>valid?</tt> will not be called.
|
20
20
|
module Callbacks
|
21
21
|
extend ActiveSupport::Concern
|
22
22
|
|
23
23
|
included do
|
24
24
|
include ActiveSupport::Callbacks
|
25
25
|
define_callbacks :validation,
|
26
|
-
terminator:
|
26
|
+
terminator: deprecated_false_terminator,
|
27
27
|
skip_after_callbacks_if_terminated: true,
|
28
28
|
scope: [:kind, :name]
|
29
29
|
end
|