activemodel 5.2.3

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +114 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +264 -0
  5. data/lib/active_model.rb +77 -0
  6. data/lib/active_model/attribute.rb +248 -0
  7. data/lib/active_model/attribute/user_provided_default.rb +52 -0
  8. data/lib/active_model/attribute_assignment.rb +57 -0
  9. data/lib/active_model/attribute_methods.rb +478 -0
  10. data/lib/active_model/attribute_mutation_tracker.rb +124 -0
  11. data/lib/active_model/attribute_set.rb +114 -0
  12. data/lib/active_model/attribute_set/builder.rb +126 -0
  13. data/lib/active_model/attribute_set/yaml_encoder.rb +41 -0
  14. data/lib/active_model/attributes.rb +111 -0
  15. data/lib/active_model/callbacks.rb +153 -0
  16. data/lib/active_model/conversion.rb +111 -0
  17. data/lib/active_model/dirty.rb +343 -0
  18. data/lib/active_model/errors.rb +517 -0
  19. data/lib/active_model/forbidden_attributes_protection.rb +31 -0
  20. data/lib/active_model/gem_version.rb +17 -0
  21. data/lib/active_model/lint.rb +118 -0
  22. data/lib/active_model/locale/en.yml +36 -0
  23. data/lib/active_model/model.rb +99 -0
  24. data/lib/active_model/naming.rb +318 -0
  25. data/lib/active_model/railtie.rb +14 -0
  26. data/lib/active_model/secure_password.rb +129 -0
  27. data/lib/active_model/serialization.rb +192 -0
  28. data/lib/active_model/serializers/json.rb +146 -0
  29. data/lib/active_model/translation.rb +70 -0
  30. data/lib/active_model/type.rb +53 -0
  31. data/lib/active_model/type/big_integer.rb +15 -0
  32. data/lib/active_model/type/binary.rb +52 -0
  33. data/lib/active_model/type/boolean.rb +38 -0
  34. data/lib/active_model/type/date.rb +57 -0
  35. data/lib/active_model/type/date_time.rb +51 -0
  36. data/lib/active_model/type/decimal.rb +70 -0
  37. data/lib/active_model/type/float.rb +36 -0
  38. data/lib/active_model/type/helpers.rb +7 -0
  39. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +41 -0
  40. data/lib/active_model/type/helpers/mutable.rb +20 -0
  41. data/lib/active_model/type/helpers/numeric.rb +37 -0
  42. data/lib/active_model/type/helpers/time_value.rb +68 -0
  43. data/lib/active_model/type/helpers/timezone.rb +19 -0
  44. data/lib/active_model/type/immutable_string.rb +32 -0
  45. data/lib/active_model/type/integer.rb +70 -0
  46. data/lib/active_model/type/registry.rb +70 -0
  47. data/lib/active_model/type/string.rb +26 -0
  48. data/lib/active_model/type/time.rb +51 -0
  49. data/lib/active_model/type/value.rb +126 -0
  50. data/lib/active_model/validations.rb +439 -0
  51. data/lib/active_model/validations/absence.rb +33 -0
  52. data/lib/active_model/validations/acceptance.rb +106 -0
  53. data/lib/active_model/validations/callbacks.rb +122 -0
  54. data/lib/active_model/validations/clusivity.rb +54 -0
  55. data/lib/active_model/validations/confirmation.rb +80 -0
  56. data/lib/active_model/validations/exclusion.rb +49 -0
  57. data/lib/active_model/validations/format.rb +114 -0
  58. data/lib/active_model/validations/helper_methods.rb +15 -0
  59. data/lib/active_model/validations/inclusion.rb +47 -0
  60. data/lib/active_model/validations/length.rb +129 -0
  61. data/lib/active_model/validations/numericality.rb +189 -0
  62. data/lib/active_model/validations/presence.rb +39 -0
  63. data/lib/active_model/validations/validates.rb +174 -0
  64. data/lib/active_model/validations/with.rb +147 -0
  65. data/lib/active_model/validator.rb +183 -0
  66. data/lib/active_model/version.rb +10 -0
  67. metadata +125 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ # == Active \Model \Translation
5
+ #
6
+ # Provides integration between your object and the Rails internationalization
7
+ # (i18n) framework.
8
+ #
9
+ # A minimal implementation could be:
10
+ #
11
+ # class TranslatedPerson
12
+ # extend ActiveModel::Translation
13
+ # end
14
+ #
15
+ # TranslatedPerson.human_attribute_name('my_attribute')
16
+ # # => "My attribute"
17
+ #
18
+ # This also provides the required class methods for hooking into the
19
+ # Rails internationalization API, including being able to define a
20
+ # class based +i18n_scope+ and +lookup_ancestors+ to find translations in
21
+ # parent classes.
22
+ module Translation
23
+ include ActiveModel::Naming
24
+
25
+ # Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
26
+ def i18n_scope
27
+ :activemodel
28
+ end
29
+
30
+ # When localizing a string, it goes through the lookup returned by this
31
+ # method, which is used in ActiveModel::Name#human,
32
+ # ActiveModel::Errors#full_messages and
33
+ # ActiveModel::Translation#human_attribute_name.
34
+ def lookup_ancestors
35
+ ancestors.select { |x| x.respond_to?(:model_name) }
36
+ end
37
+
38
+ # Transforms attribute names into a more human format, such as "First name"
39
+ # instead of "first_name".
40
+ #
41
+ # Person.human_attribute_name("first_name") # => "First name"
42
+ #
43
+ # Specify +options+ with additional translating options.
44
+ def human_attribute_name(attribute, options = {})
45
+ options = { count: 1 }.merge!(options)
46
+ parts = attribute.to_s.split(".")
47
+ attribute = parts.pop
48
+ namespace = parts.join("/") unless parts.empty?
49
+ attributes_scope = "#{i18n_scope}.attributes"
50
+
51
+ if namespace
52
+ defaults = lookup_ancestors.map do |klass|
53
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
54
+ end
55
+ defaults << :"#{attributes_scope}.#{namespace}.#{attribute}"
56
+ else
57
+ defaults = lookup_ancestors.map do |klass|
58
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}"
59
+ end
60
+ end
61
+
62
+ defaults << :"attributes.#{attribute}"
63
+ defaults << options.delete(:default) if options[:default]
64
+ defaults << attribute.humanize
65
+
66
+ options[:default] = defaults
67
+ I18n.translate(defaults.shift, options)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/type/helpers"
4
+ require "active_model/type/value"
5
+
6
+ require "active_model/type/big_integer"
7
+ require "active_model/type/binary"
8
+ require "active_model/type/boolean"
9
+ require "active_model/type/date"
10
+ require "active_model/type/date_time"
11
+ require "active_model/type/decimal"
12
+ require "active_model/type/float"
13
+ require "active_model/type/immutable_string"
14
+ require "active_model/type/integer"
15
+ require "active_model/type/string"
16
+ require "active_model/type/time"
17
+
18
+ require "active_model/type/registry"
19
+
20
+ module ActiveModel
21
+ module Type
22
+ @registry = Registry.new
23
+
24
+ class << self
25
+ attr_accessor :registry # :nodoc:
26
+
27
+ # Add a new type to the registry, allowing it to be gotten through ActiveModel::Type#lookup
28
+ def register(type_name, klass = nil, **options, &block)
29
+ registry.register(type_name, klass, **options, &block)
30
+ end
31
+
32
+ def lookup(*args, **kwargs) # :nodoc:
33
+ registry.lookup(*args, **kwargs)
34
+ end
35
+
36
+ def default_value # :nodoc:
37
+ @default_value ||= Value.new
38
+ end
39
+ end
40
+
41
+ register(:big_integer, Type::BigInteger)
42
+ register(:binary, Type::Binary)
43
+ register(:boolean, Type::Boolean)
44
+ register(:date, Type::Date)
45
+ register(:datetime, Type::DateTime)
46
+ register(:decimal, Type::Decimal)
47
+ register(:float, Type::Float)
48
+ register(:immutable_string, Type::ImmutableString)
49
+ register(:integer, Type::Integer)
50
+ register(:string, Type::String)
51
+ register(:time, Type::Time)
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/type/integer"
4
+
5
+ module ActiveModel
6
+ module Type
7
+ class BigInteger < Integer # :nodoc:
8
+ private
9
+
10
+ def max_value
11
+ ::Float::INFINITY
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Type
5
+ class Binary < Value # :nodoc:
6
+ def type
7
+ :binary
8
+ end
9
+
10
+ def binary?
11
+ true
12
+ end
13
+
14
+ def cast(value)
15
+ if value.is_a?(Data)
16
+ value.to_s
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def serialize(value)
23
+ return if value.nil?
24
+ Data.new(super)
25
+ end
26
+
27
+ def changed_in_place?(raw_old_value, value)
28
+ old_value = deserialize(raw_old_value)
29
+ old_value != value
30
+ end
31
+
32
+ class Data # :nodoc:
33
+ def initialize(value)
34
+ @value = value.to_s
35
+ end
36
+
37
+ def to_s
38
+ @value
39
+ end
40
+ alias_method :to_str, :to_s
41
+
42
+ def hex
43
+ @value.unpack("H*")[0]
44
+ end
45
+
46
+ def ==(other)
47
+ other == to_s || super
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Type
5
+ # == Active \Model \Type \Boolean
6
+ #
7
+ # A class that behaves like a boolean type, including rules for coercion of user input.
8
+ #
9
+ # === Coercion
10
+ # Values set from user input will first be coerced into the appropriate ruby type.
11
+ # Coercion behavior is roughly mapped to Ruby's boolean semantics.
12
+ #
13
+ # - "false", "f" , "0", +0+ or any other value in +FALSE_VALUES+ will be coerced to +false+
14
+ # - Empty strings are coerced to +nil+
15
+ # - All other values will be coerced to +true+
16
+ class Boolean < Value
17
+ FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"].to_set
18
+
19
+ def type # :nodoc:
20
+ :boolean
21
+ end
22
+
23
+ def serialize(value) # :nodoc:
24
+ cast(value)
25
+ end
26
+
27
+ private
28
+
29
+ def cast_value(value)
30
+ if value == ""
31
+ nil
32
+ else
33
+ !FALSE_VALUES.include?(value)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Type
5
+ class Date < Value # :nodoc:
6
+ include Helpers::Timezone
7
+ include Helpers::AcceptsMultiparameterTime.new
8
+
9
+ def type
10
+ :date
11
+ end
12
+
13
+ def serialize(value)
14
+ cast(value)
15
+ end
16
+
17
+ def type_cast_for_schema(value)
18
+ value.to_s(:db).inspect
19
+ end
20
+
21
+ private
22
+
23
+ def cast_value(value)
24
+ if value.is_a?(::String)
25
+ return if value.empty?
26
+ fast_string_to_date(value) || fallback_string_to_date(value)
27
+ elsif value.respond_to?(:to_date)
28
+ value.to_date
29
+ else
30
+ value
31
+ end
32
+ end
33
+
34
+ ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
35
+ def fast_string_to_date(string)
36
+ if string =~ ISO_DATE
37
+ new_date $1.to_i, $2.to_i, $3.to_i
38
+ end
39
+ end
40
+
41
+ def fallback_string_to_date(string)
42
+ new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
43
+ end
44
+
45
+ def new_date(year, mon, mday)
46
+ unless year.nil? || (year == 0 && mon == 0 && mday == 0)
47
+ ::Date.new(year, mon, mday) rescue nil
48
+ end
49
+ end
50
+
51
+ def value_from_multiparameter_assignment(*)
52
+ time = super
53
+ time && new_date(time.year, time.mon, time.mday)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Type
5
+ class DateTime < Value # :nodoc:
6
+ include Helpers::Timezone
7
+ include Helpers::TimeValue
8
+ include Helpers::AcceptsMultiparameterTime.new(
9
+ defaults: { 4 => 0, 5 => 0 }
10
+ )
11
+
12
+ def type
13
+ :datetime
14
+ end
15
+
16
+ def serialize(value)
17
+ super(cast(value))
18
+ end
19
+
20
+ private
21
+
22
+ def cast_value(value)
23
+ return apply_seconds_precision(value) unless value.is_a?(::String)
24
+ return if value.empty?
25
+
26
+ fast_string_to_time(value) || fallback_string_to_time(value)
27
+ end
28
+
29
+ # '0.123456' -> 123456
30
+ # '1.123456' -> 123456
31
+ def microseconds(time)
32
+ time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
33
+ end
34
+
35
+ def fallback_string_to_time(string)
36
+ time_hash = ::Date._parse(string)
37
+ time_hash[:sec_fraction] = microseconds(time_hash)
38
+
39
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
40
+ end
41
+
42
+ 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
46
+ end
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal/util"
4
+
5
+ module ActiveModel
6
+ module Type
7
+ class Decimal < Value # :nodoc:
8
+ include Helpers::Numeric
9
+ BIGDECIMAL_PRECISION = 18
10
+
11
+ def type
12
+ :decimal
13
+ end
14
+
15
+ def type_cast_for_schema(value)
16
+ value.to_s.inspect
17
+ end
18
+
19
+ private
20
+
21
+ def cast_value(value)
22
+ casted_value = \
23
+ case value
24
+ when ::Float
25
+ convert_float_to_big_decimal(value)
26
+ when ::Numeric
27
+ BigDecimal(value, precision || BIGDECIMAL_PRECISION)
28
+ when ::String
29
+ begin
30
+ value.to_d
31
+ rescue ArgumentError
32
+ BigDecimal(0)
33
+ end
34
+ else
35
+ if value.respond_to?(:to_d)
36
+ value.to_d
37
+ else
38
+ cast_value(value.to_s)
39
+ end
40
+ end
41
+
42
+ apply_scale(casted_value)
43
+ end
44
+
45
+ def convert_float_to_big_decimal(value)
46
+ if precision
47
+ BigDecimal(apply_scale(value), float_precision)
48
+ else
49
+ value.to_d
50
+ end
51
+ end
52
+
53
+ def float_precision
54
+ if precision.to_i > ::Float::DIG + 1
55
+ ::Float::DIG + 1
56
+ else
57
+ precision.to_i
58
+ end
59
+ end
60
+
61
+ def apply_scale(value)
62
+ if scale
63
+ value.round(scale)
64
+ else
65
+ value
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Type
5
+ class Float < Value # :nodoc:
6
+ include Helpers::Numeric
7
+
8
+ def type
9
+ :float
10
+ end
11
+
12
+ def type_cast_for_schema(value)
13
+ return "::Float::NAN" if value.try(:nan?)
14
+ case value
15
+ when ::Float::INFINITY then "::Float::INFINITY"
16
+ when -::Float::INFINITY then "-::Float::INFINITY"
17
+ else super
18
+ end
19
+ end
20
+
21
+ alias serialize cast
22
+
23
+ private
24
+
25
+ def cast_value(value)
26
+ case value
27
+ when ::Float then value
28
+ when "Infinity" then ::Float::INFINITY
29
+ when "-Infinity" then -::Float::INFINITY
30
+ when "NaN" then ::Float::NAN
31
+ else value.to_f
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end