activemodel 5.2.4.1 → 6.0.1

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -66
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/active_model.rb +1 -1
  6. data/lib/active_model/attribute.rb +3 -4
  7. data/lib/active_model/attribute/user_provided_default.rb +1 -2
  8. data/lib/active_model/attribute_assignment.rb +1 -1
  9. data/lib/active_model/attribute_methods.rb +54 -15
  10. data/lib/active_model/attribute_mutation_tracker.rb +88 -34
  11. data/lib/active_model/attribute_set.rb +2 -10
  12. data/lib/active_model/attribute_set/builder.rb +1 -3
  13. data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
  14. data/lib/active_model/attributes.rb +60 -33
  15. data/lib/active_model/callbacks.rb +10 -7
  16. data/lib/active_model/conversion.rb +1 -1
  17. data/lib/active_model/dirty.rb +36 -99
  18. data/lib/active_model/errors.rb +104 -20
  19. data/lib/active_model/gem_version.rb +4 -4
  20. data/lib/active_model/naming.rb +19 -3
  21. data/lib/active_model/railtie.rb +6 -0
  22. data/lib/active_model/secure_password.rb +47 -48
  23. data/lib/active_model/serializers/json.rb +10 -9
  24. data/lib/active_model/type/binary.rb +1 -1
  25. data/lib/active_model/type/date.rb +0 -4
  26. data/lib/active_model/type/date_time.rb +3 -7
  27. data/lib/active_model/type/float.rb +0 -2
  28. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +4 -0
  29. data/lib/active_model/type/helpers/numeric.rb +9 -2
  30. data/lib/active_model/type/helpers/time_value.rb +17 -4
  31. data/lib/active_model/type/integer.rb +7 -19
  32. data/lib/active_model/type/registry.rb +2 -10
  33. data/lib/active_model/type/string.rb +2 -2
  34. data/lib/active_model/type/time.rb +1 -5
  35. data/lib/active_model/validations.rb +0 -2
  36. data/lib/active_model/validations/acceptance.rb +33 -25
  37. data/lib/active_model/validations/clusivity.rb +1 -1
  38. data/lib/active_model/validations/confirmation.rb +2 -2
  39. data/lib/active_model/validations/inclusion.rb +1 -1
  40. data/lib/active_model/validations/length.rb +1 -1
  41. data/lib/active_model/validations/numericality.rb +2 -2
  42. data/lib/active_model/validations/validates.rb +2 -2
  43. data/lib/active_model/validator.rb +1 -1
  44. metadata +11 -8
@@ -40,7 +40,7 @@ module ActiveModel
40
40
  alias_method :to_str, :to_s
41
41
 
42
42
  def hex
43
- @value.unpack("H*")[0]
43
+ @value.unpack1("H*")
44
44
  end
45
45
 
46
46
  def ==(other)
@@ -10,10 +10,6 @@ module ActiveModel
10
10
  :date
11
11
  end
12
12
 
13
- def serialize(value)
14
- cast(value)
15
- end
16
-
17
13
  def type_cast_for_schema(value)
18
14
  value.to_s(:db).inspect
19
15
  end
@@ -13,10 +13,6 @@ module ActiveModel
13
13
  :datetime
14
14
  end
15
15
 
16
- def serialize(value)
17
- super(cast(value))
18
- end
19
-
20
16
  private
21
17
 
22
18
  def cast_value(value)
@@ -40,9 +36,9 @@ module ActiveModel
40
36
  end
41
37
 
42
38
  def value_from_multiparameter_assignment(values_hash)
43
- missing_parameter = (1..3).detect { |key| !values_hash.key?(key) }
44
- if missing_parameter
45
- raise ArgumentError, missing_parameter
39
+ missing_parameters = (1..3).select { |key| !values_hash.key?(key) }
40
+ if missing_parameters.any?
41
+ raise ArgumentError, "Provided hash #{values_hash} doesn't contain necessary keys: #{missing_parameters}"
46
42
  end
47
43
  super
48
44
  end
@@ -18,8 +18,6 @@ module ActiveModel
18
18
  end
19
19
  end
20
20
 
21
- alias serialize cast
22
-
23
21
  private
24
22
 
25
23
  def cast_value(value)
@@ -5,6 +5,10 @@ module ActiveModel
5
5
  module Helpers # :nodoc: all
6
6
  class AcceptsMultiparameterTime < Module
7
7
  def initialize(defaults: {})
8
+ define_method(:serialize) do |value|
9
+ super(cast(value))
10
+ end
11
+
8
12
  define_method(:cast) do |value|
9
13
  if value.is_a?(Hash)
10
14
  value_from_multiparameter_assignment(value)
@@ -4,6 +4,10 @@ module ActiveModel
4
4
  module Type
5
5
  module Helpers # :nodoc: all
6
6
  module Numeric
7
+ def serialize(value)
8
+ cast(value)
9
+ end
10
+
7
11
  def cast(value)
8
12
  value = \
9
13
  case value
@@ -22,15 +26,18 @@ module ActiveModel
22
26
  private
23
27
 
24
28
  def number_to_non_number?(old_value, new_value_before_type_cast)
25
- old_value != nil && non_numeric_string?(new_value_before_type_cast)
29
+ old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
26
30
  end
27
31
 
28
32
  def non_numeric_string?(value)
29
33
  # 'wibble'.to_i will give zero, we want to make sure
30
34
  # that we aren't marking int zero to string zero as
31
35
  # changed.
32
- !/\A[-+]?\d+/.match?(value.to_s)
36
+ !NUMERIC_REGEX.match?(value)
33
37
  end
38
+
39
+ NUMERIC_REGEX = /\A\s*[+-]?\d/
40
+ private_constant :NUMERIC_REGEX
34
41
  end
35
42
  end
36
43
  end
@@ -22,10 +22,17 @@ module ActiveModel
22
22
  end
23
23
 
24
24
  def apply_seconds_precision(value)
25
- return value unless precision && value.respond_to?(:usec)
26
- number_of_insignificant_digits = 6 - precision
25
+ return value unless precision && value.respond_to?(:nsec)
26
+
27
+ number_of_insignificant_digits = 9 - precision
27
28
  round_power = 10**number_of_insignificant_digits
28
- value.change(usec: value.usec - value.usec % round_power)
29
+ rounded_off_nsec = value.nsec % round_power
30
+
31
+ if rounded_off_nsec > 0
32
+ value.change(nsec: value.nsec - rounded_off_nsec)
33
+ else
34
+ value
35
+ end
29
36
  end
30
37
 
31
38
  def type_cast_for_schema(value)
@@ -58,7 +65,13 @@ module ActiveModel
58
65
  # Doesn't handle time zones.
59
66
  def fast_string_to_time(string)
60
67
  if string =~ ISO_DATETIME
61
- microsec = ($7.to_r * 1_000_000).to_i
68
+ microsec_part = $7
69
+ if microsec_part && microsec_part.start_with?(".") && microsec_part.length == 7
70
+ microsec_part[0] = ""
71
+ microsec = microsec_part.to_i
72
+ else
73
+ microsec = (microsec_part.to_r * 1_000_000).to_i
74
+ end
62
75
  new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
63
76
  end
64
77
  end
@@ -19,39 +19,27 @@ module ActiveModel
19
19
  end
20
20
 
21
21
  def deserialize(value)
22
- return if value.nil?
22
+ return if value.blank?
23
23
  value.to_i
24
24
  end
25
25
 
26
26
  def serialize(value)
27
- result = cast(value)
28
- if result
29
- ensure_in_range(result)
30
- end
31
- result
27
+ return if value.is_a?(::String) && non_numeric_string?(value)
28
+ ensure_in_range(super)
32
29
  end
33
30
 
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
31
  private
32
+ attr_reader :range
41
33
 
42
34
  def cast_value(value)
43
- case value
44
- when true then 1
45
- when false then 0
46
- else
47
- value.to_i rescue nil
48
- end
35
+ value.to_i rescue nil
49
36
  end
50
37
 
51
38
  def ensure_in_range(value)
52
- unless range.cover?(value)
39
+ if value && !range.cover?(value)
53
40
  raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
54
41
  end
42
+ value
55
43
  end
56
44
 
57
45
  def max_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
- # TODO Change this to private once we've dropped Ruby 2.2 support.
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
@@ -16,8 +16,8 @@ module ActiveModel
16
16
  def cast_value(value)
17
17
  case value
18
18
  when ::String then ::String.new(value)
19
- when true then "t".freeze
20
- when false then "f".freeze
19
+ when true then "t"
20
+ when false then "f"
21
21
  else value.to_s
22
22
  end
23
23
  end
@@ -6,17 +6,13 @@ module ActiveModel
6
6
  include Helpers::Timezone
7
7
  include Helpers::TimeValue
8
8
  include Helpers::AcceptsMultiparameterTime.new(
9
- defaults: { 1 => 1970, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
9
+ defaults: { 1 => 2000, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
10
10
  )
11
11
 
12
12
  def type
13
13
  :time
14
14
  end
15
15
 
16
- def serialize(value)
17
- super(cast(value))
18
- end
19
-
20
16
  def user_input_in_time_zone(value)
21
17
  return unless value.present?
22
18
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/array/extract_options"
4
- require "active_support/core_ext/hash/keys"
5
- require "active_support/core_ext/hash/except"
6
4
 
7
5
  module ActiveModel
8
6
  # == Active \Model \Validations
@@ -17,7 +17,8 @@ module ActiveModel
17
17
  private
18
18
 
19
19
  def setup!(klass)
20
- klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
20
+ define_attributes = LazilyDefineAttributes.new(attributes)
21
+ klass.include(define_attributes) unless klass.included_modules.include?(define_attributes)
21
22
  end
22
23
 
23
24
  def acceptable_option?(value)
@@ -25,50 +26,57 @@ module ActiveModel
25
26
  end
26
27
 
27
28
  class LazilyDefineAttributes < Module
28
- def initialize(attribute_definition)
29
+ def initialize(attributes)
30
+ @attributes = attributes.map(&:to_s)
31
+ end
32
+
33
+ def included(klass)
34
+ @lock = Mutex.new
35
+ mod = self
36
+
29
37
  define_method(:respond_to_missing?) do |method_name, include_private = false|
30
- super(method_name, include_private) || attribute_definition.matches?(method_name)
38
+ mod.define_on(klass)
39
+ super(method_name, include_private) || mod.matches?(method_name)
31
40
  end
32
41
 
33
42
  define_method(:method_missing) do |method_name, *args, &block|
34
- if attribute_definition.matches?(method_name)
35
- attribute_definition.define_on(self.class)
43
+ mod.define_on(klass)
44
+ if mod.matches?(method_name)
36
45
  send(method_name, *args, &block)
37
46
  else
38
47
  super(method_name, *args, &block)
39
48
  end
40
49
  end
41
50
  end
42
- end
43
-
44
- class AttributeDefinition
45
- def initialize(attributes)
46
- @attributes = attributes.map(&:to_s)
47
- end
48
51
 
49
52
  def matches?(method_name)
50
- attr_name = convert_to_reader_name(method_name)
51
- attributes.include?(attr_name)
53
+ attr_name = method_name.to_s.chomp("=")
54
+ attributes.any? { |name| name == attr_name }
52
55
  end
53
56
 
54
57
  def define_on(klass)
55
- attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
56
- attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
57
- klass.send(:attr_reader, *attr_readers)
58
- klass.send(:attr_writer, *attr_writers)
59
- end
58
+ @lock&.synchronize do
59
+ return unless @lock
60
60
 
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
61
+ attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
62
+ attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
64
63
 
65
- attr_reader :attributes
64
+ attr_reader(*attr_readers)
65
+ attr_writer(*attr_writers)
66
66
 
67
- private
67
+ remove_method :respond_to_missing?
68
+ remove_method :method_missing
68
69
 
69
- def convert_to_reader_name(method_name)
70
- method_name.to_s.chomp("=")
70
+ @lock = nil
71
71
  end
72
+ end
73
+
74
+ def ==(other)
75
+ self.class == other.class && attributes == other.attributes
76
+ end
77
+
78
+ protected
79
+ attr_reader :attributes
72
80
  end
73
81
  end
74
82
 
@@ -32,7 +32,7 @@ module ActiveModel
32
32
  @delimiter ||= options[:in] || options[:within]
33
33
  end
34
34
 
35
- # In Ruby 2.2 <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
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.send(:attr_reader, *attributes.map do |attribute|
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.send(:attr_writer, *attributes.map do |attribute|
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 :gender, in: %w( m f )
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] }
@@ -32,7 +32,7 @@ module ActiveModel
32
32
  value = options[key]
33
33
 
34
34
  unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY || value.is_a?(Symbol) || value.is_a?(Proc)
35
- raise ArgumentError, ":#{key} must be a nonnegative Integer, Infinity, Symbol, or Proc"
35
+ raise ArgumentError, ":#{key} must be a non-negative Integer, Infinity, Symbol, or Proc"
36
36
  end
37
37
  end
38
38
  end
@@ -99,11 +99,11 @@ module ActiveModel
99
99
  end
100
100
 
101
101
  def is_integer?(raw_value)
102
- INTEGER_REGEX === raw_value.to_s
102
+ INTEGER_REGEX.match?(raw_value.to_s)
103
103
  end
104
104
 
105
105
  def is_hexadecimal_literal?(raw_value)
106
- /\A0[xX]/ === raw_value.to_s
106
+ /\A0[xX]/.match?(raw_value.to_s)
107
107
  end
108
108
 
109
109
  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 :gender, inclusion: %w(male female)
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?("::".freeze) ? key.constantize : const_get(key)
119
+ validator = key.include?("::") ? key.constantize : const_get(key)
120
120
  rescue NameError
121
121
  raise ArgumentError, "Unknown validator: '#{key}'"
122
122
  end
@@ -90,7 +90,7 @@ module ActiveModel
90
90
  # class MyValidator < ActiveModel::Validator
91
91
  # def initialize(options={})
92
92
  # super
93
- # options[:class].send :attr_accessor, :custom_attribute
93
+ # options[:class].attr_accessor :custom_attribute
94
94
  # end
95
95
  # end
96
96
  class Validator
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: 5.2.4.1
4
+ version: 6.0.1
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: 2019-12-18 00:00:00.000000000 Z
11
+ date: 2019-11-05 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: 5.2.4.1
19
+ version: 6.0.1
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: 5.2.4.1
26
+ version: 6.0.1
27
27
  description: A toolkit for building modeling frameworks like Active Record. Rich support
28
28
  for attributes, callbacks, validations, serialization, internationalization, and
29
29
  testing.
@@ -97,12 +97,15 @@ files:
97
97
  - lib/active_model/validations/with.rb
98
98
  - lib/active_model/validator.rb
99
99
  - lib/active_model/version.rb
100
- homepage: http://rubyonrails.org
100
+ homepage: https://rubyonrails.org
101
101
  licenses:
102
102
  - MIT
103
103
  metadata:
104
- source_code_uri: https://github.com/rails/rails/tree/v5.2.4.1/activemodel
105
- changelog_uri: https://github.com/rails/rails/blob/v5.2.4.1/activemodel/CHANGELOG.md
104
+ bug_tracker_uri: https://github.com/rails/rails/issues
105
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.1/activemodel/CHANGELOG.md
106
+ documentation_uri: https://api.rubyonrails.org/v6.0.1/
107
+ mailing_list_uri: https://groups.google.com/forum/#!forum/rubyonrails-talk
108
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.1/activemodel
106
109
  post_install_message:
107
110
  rdoc_options: []
108
111
  require_paths:
@@ -111,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
114
  requirements:
112
115
  - - ">="
113
116
  - !ruby/object:Gem::Version
114
- version: 2.2.2
117
+ version: 2.5.0
115
118
  required_rubygems_version: !ruby/object:Gem::Requirement
116
119
  requirements:
117
120
  - - ">="