omg-activemodel 8.0.0.alpha1

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 (77) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +67 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +266 -0
  5. data/lib/active_model/access.rb +16 -0
  6. data/lib/active_model/api.rb +99 -0
  7. data/lib/active_model/attribute/user_provided_default.rb +55 -0
  8. data/lib/active_model/attribute.rb +277 -0
  9. data/lib/active_model/attribute_assignment.rb +78 -0
  10. data/lib/active_model/attribute_methods.rb +592 -0
  11. data/lib/active_model/attribute_mutation_tracker.rb +189 -0
  12. data/lib/active_model/attribute_registration.rb +117 -0
  13. data/lib/active_model/attribute_set/builder.rb +182 -0
  14. data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
  15. data/lib/active_model/attribute_set.rb +118 -0
  16. data/lib/active_model/attributes.rb +165 -0
  17. data/lib/active_model/callbacks.rb +155 -0
  18. data/lib/active_model/conversion.rb +121 -0
  19. data/lib/active_model/deprecator.rb +7 -0
  20. data/lib/active_model/dirty.rb +416 -0
  21. data/lib/active_model/error.rb +208 -0
  22. data/lib/active_model/errors.rb +547 -0
  23. data/lib/active_model/forbidden_attributes_protection.rb +33 -0
  24. data/lib/active_model/gem_version.rb +17 -0
  25. data/lib/active_model/lint.rb +118 -0
  26. data/lib/active_model/locale/en.yml +38 -0
  27. data/lib/active_model/model.rb +78 -0
  28. data/lib/active_model/naming.rb +359 -0
  29. data/lib/active_model/nested_error.rb +22 -0
  30. data/lib/active_model/railtie.rb +24 -0
  31. data/lib/active_model/secure_password.rb +231 -0
  32. data/lib/active_model/serialization.rb +198 -0
  33. data/lib/active_model/serializers/json.rb +154 -0
  34. data/lib/active_model/translation.rb +78 -0
  35. data/lib/active_model/type/big_integer.rb +36 -0
  36. data/lib/active_model/type/binary.rb +62 -0
  37. data/lib/active_model/type/boolean.rb +48 -0
  38. data/lib/active_model/type/date.rb +78 -0
  39. data/lib/active_model/type/date_time.rb +88 -0
  40. data/lib/active_model/type/decimal.rb +107 -0
  41. data/lib/active_model/type/float.rb +64 -0
  42. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +53 -0
  43. data/lib/active_model/type/helpers/mutable.rb +24 -0
  44. data/lib/active_model/type/helpers/numeric.rb +61 -0
  45. data/lib/active_model/type/helpers/time_value.rb +127 -0
  46. data/lib/active_model/type/helpers/timezone.rb +23 -0
  47. data/lib/active_model/type/helpers.rb +7 -0
  48. data/lib/active_model/type/immutable_string.rb +71 -0
  49. data/lib/active_model/type/integer.rb +113 -0
  50. data/lib/active_model/type/registry.rb +37 -0
  51. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  52. data/lib/active_model/type/string.rb +43 -0
  53. data/lib/active_model/type/time.rb +87 -0
  54. data/lib/active_model/type/value.rb +157 -0
  55. data/lib/active_model/type.rb +55 -0
  56. data/lib/active_model/validations/absence.rb +33 -0
  57. data/lib/active_model/validations/acceptance.rb +113 -0
  58. data/lib/active_model/validations/callbacks.rb +119 -0
  59. data/lib/active_model/validations/clusivity.rb +54 -0
  60. data/lib/active_model/validations/comparability.rb +18 -0
  61. data/lib/active_model/validations/comparison.rb +90 -0
  62. data/lib/active_model/validations/confirmation.rb +80 -0
  63. data/lib/active_model/validations/exclusion.rb +49 -0
  64. data/lib/active_model/validations/format.rb +112 -0
  65. data/lib/active_model/validations/helper_methods.rb +15 -0
  66. data/lib/active_model/validations/inclusion.rb +47 -0
  67. data/lib/active_model/validations/length.rb +130 -0
  68. data/lib/active_model/validations/numericality.rb +222 -0
  69. data/lib/active_model/validations/presence.rb +39 -0
  70. data/lib/active_model/validations/resolve_value.rb +26 -0
  71. data/lib/active_model/validations/validates.rb +175 -0
  72. data/lib/active_model/validations/with.rb +154 -0
  73. data/lib/active_model/validations.rb +489 -0
  74. data/lib/active_model/validator.rb +190 -0
  75. data/lib/active_model/version.rb +10 -0
  76. data/lib/active_model.rb +84 -0
  77. metadata +139 -0
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ # = Active \Model \Attributes
5
+ #
6
+ # The Attributes module allows models to define attributes beyond simple Ruby
7
+ # readers and writers. Similar to Active Record attributes, which are
8
+ # typically inferred from the database schema, Active Model Attributes are
9
+ # aware of data types, can have default values, and can handle casting and
10
+ # serialization.
11
+ #
12
+ # To use Attributes, include the module in your model class and define your
13
+ # attributes using the +attribute+ macro. It accepts a name, a type, a default
14
+ # value, and any other options supported by the attribute type.
15
+ #
16
+ # ==== Examples
17
+ #
18
+ # class Person
19
+ # include ActiveModel::Attributes
20
+ #
21
+ # attribute :name, :string
22
+ # attribute :active, :boolean, default: true
23
+ # end
24
+ #
25
+ # person = Person.new
26
+ # person.name = "Volmer"
27
+ #
28
+ # person.name # => "Volmer"
29
+ # person.active # => true
30
+ module Attributes
31
+ extend ActiveSupport::Concern
32
+ include ActiveModel::AttributeRegistration
33
+ include ActiveModel::AttributeMethods
34
+
35
+ included do
36
+ attribute_method_suffix "=", parameters: "value"
37
+ end
38
+
39
+ module ClassMethods
40
+ ##
41
+ # :call-seq: attribute(name, cast_type = nil, default: nil, **options)
42
+ #
43
+ # Defines a model attribute. In addition to the attribute name, a cast
44
+ # type and default value may be specified, as well as any options
45
+ # supported by the given cast type.
46
+ #
47
+ # class Person
48
+ # include ActiveModel::Attributes
49
+ #
50
+ # attribute :name, :string
51
+ # attribute :active, :boolean, default: true
52
+ # end
53
+ #
54
+ # person = Person.new
55
+ # person.name = "Volmer"
56
+ #
57
+ # person.name # => "Volmer"
58
+ # person.active # => true
59
+ def attribute(name, ...)
60
+ super
61
+ define_attribute_method(name)
62
+ end
63
+
64
+ # Returns an array of attribute names as strings.
65
+ #
66
+ # class Person
67
+ # include ActiveModel::Attributes
68
+ #
69
+ # attribute :name, :string
70
+ # attribute :age, :integer
71
+ # end
72
+ #
73
+ # Person.attribute_names # => ["name", "age"]
74
+ def attribute_names
75
+ attribute_types.keys
76
+ end
77
+
78
+ ##
79
+ # :method: type_for_attribute
80
+ # :call-seq: type_for_attribute(attribute_name, &block)
81
+ #
82
+ # Returns the type of the specified attribute after applying any
83
+ # modifiers. This method is the only valid source of information for
84
+ # anything related to the types of a model's attributes. The return value
85
+ # of this method will implement the interface described by
86
+ # ActiveModel::Type::Value (though the object itself may not subclass it).
87
+ #--
88
+ # Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
89
+
90
+ ##
91
+ private
92
+ def define_method_attribute=(canonical_name, owner:, as: canonical_name)
93
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
94
+ owner, canonical_name, writer: true,
95
+ ) do |temp_method_name, attr_name_expr|
96
+ owner.define_cached_method(temp_method_name, as: "#{as}=", namespace: :active_model) do |batch|
97
+ batch <<
98
+ "def #{temp_method_name}(value)" <<
99
+ " _write_attribute(#{attr_name_expr}, value)" <<
100
+ "end"
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def initialize(*) # :nodoc:
107
+ @attributes = self.class._default_attributes.deep_dup
108
+ super
109
+ end
110
+
111
+ def initialize_dup(other) # :nodoc:
112
+ @attributes = @attributes.deep_dup
113
+ super
114
+ end
115
+
116
+ # Returns a hash of all the attributes with their names as keys and the
117
+ # values of the attributes as values.
118
+ #
119
+ # class Person
120
+ # include ActiveModel::Attributes
121
+ #
122
+ # attribute :name, :string
123
+ # attribute :age, :integer
124
+ # end
125
+ #
126
+ # person = Person.new
127
+ # person.name = "Francesco"
128
+ # person.age = 22
129
+ #
130
+ # person.attributes # => { "name" => "Francesco", "age" => 22}
131
+ def attributes
132
+ @attributes.to_hash
133
+ end
134
+
135
+ # Returns an array of attribute names as strings.
136
+ #
137
+ # class Person
138
+ # include ActiveModel::Attributes
139
+ #
140
+ # attribute :name, :string
141
+ # attribute :age, :integer
142
+ # end
143
+ #
144
+ # person = Person.new
145
+ # person.attribute_names # => ["name", "age"]
146
+ def attribute_names
147
+ @attributes.keys
148
+ end
149
+
150
+ def freeze # :nodoc:
151
+ @attributes = @attributes.clone.freeze unless frozen?
152
+ super
153
+ end
154
+
155
+ private
156
+ def _write_attribute(attr_name, value)
157
+ @attributes.write_from_user(attr_name, value)
158
+ end
159
+ alias :attribute= :_write_attribute
160
+
161
+ def attribute(attr_name)
162
+ @attributes.fetch_value(attr_name)
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/extract_options"
4
+ require "active_support/core_ext/hash/keys"
5
+
6
+ module ActiveModel
7
+ # = Active \Model \Callbacks
8
+ #
9
+ # Provides an interface for any class to have Active Record like callbacks.
10
+ #
11
+ # Like the Active Record methods, the callback chain is aborted as soon as
12
+ # one of the methods throws +:abort+.
13
+ #
14
+ # First, extend +ActiveModel::Callbacks+ from the class you are creating:
15
+ #
16
+ # class MyModel
17
+ # extend ActiveModel::Callbacks
18
+ # end
19
+ #
20
+ # Then define a list of methods that you want callbacks attached to:
21
+ #
22
+ # define_model_callbacks :create, :update
23
+ #
24
+ # This will provide all three standard callbacks (before, around and after)
25
+ # for both the <tt>:create</tt> and <tt>:update</tt> methods. To implement,
26
+ # you need to wrap the methods you want callbacks on in a block so that the
27
+ # callbacks get a chance to fire:
28
+ #
29
+ # def create
30
+ # run_callbacks :create do
31
+ # # Your create action methods here
32
+ # end
33
+ # end
34
+ #
35
+ # Then in your class, you can use the +before_create+, +after_create+, and
36
+ # +around_create+ methods, just as you would in an Active Record model.
37
+ #
38
+ # before_create :action_before_create
39
+ #
40
+ # def action_before_create
41
+ # # Your code here
42
+ # end
43
+ #
44
+ # When defining an around callback remember to yield to the block, otherwise
45
+ # it won't be executed:
46
+ #
47
+ # around_create :log_status
48
+ #
49
+ # def log_status
50
+ # puts 'going to call the block...'
51
+ # yield
52
+ # puts 'block successfully called.'
53
+ # end
54
+ #
55
+ # You can choose to have only specific callbacks by passing a hash to the
56
+ # +define_model_callbacks+ method.
57
+ #
58
+ # define_model_callbacks :create, only: [:after, :before]
59
+ #
60
+ # Would only create the +after_create+ and +before_create+ callback methods in
61
+ # your class.
62
+ #
63
+ # NOTE: Defining the same callback multiple times will overwrite previous callback definitions.
64
+ #
65
+ module Callbacks
66
+ def self.extended(base) # :nodoc:
67
+ base.class_eval do
68
+ include ActiveSupport::Callbacks
69
+ end
70
+ end
71
+
72
+ # +define_model_callbacks+ accepts the same options +define_callbacks+ does,
73
+ # in case you want to overwrite a default. Besides that, it also accepts an
74
+ # <tt>:only</tt> option, where you can choose if you want all types (before,
75
+ # around or after) or just some.
76
+ #
77
+ # define_model_callbacks :initialize, only: :after
78
+ #
79
+ # Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
80
+ # on that method call. To get around this you can call the +define_model_callbacks+
81
+ # method as many times as you need.
82
+ #
83
+ # define_model_callbacks :create, only: :after
84
+ # define_model_callbacks :update, only: :before
85
+ # define_model_callbacks :destroy, only: :around
86
+ #
87
+ # Would create +after_create+, +before_update+, and +around_destroy+ methods
88
+ # only.
89
+ #
90
+ # You can pass in a class to before_<type>, after_<type> and around_<type>,
91
+ # in which case the callback will call that class's <action>_<type> method
92
+ # passing the object that the callback is being called on.
93
+ #
94
+ # class MyModel
95
+ # extend ActiveModel::Callbacks
96
+ # define_model_callbacks :create
97
+ #
98
+ # before_create AnotherClass
99
+ # end
100
+ #
101
+ # class AnotherClass
102
+ # def self.before_create( obj )
103
+ # # obj is the MyModel instance that the callback is being called on
104
+ # end
105
+ # end
106
+ #
107
+ # NOTE: +method_name+ passed to +define_model_callbacks+ must not end with
108
+ # <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
109
+ def define_model_callbacks(*callbacks)
110
+ options = callbacks.extract_options!
111
+ options = {
112
+ skip_after_callbacks_if_terminated: true,
113
+ scope: [:kind, :name],
114
+ only: [:before, :around, :after]
115
+ }.merge!(options)
116
+
117
+ types = Array(options.delete(:only))
118
+
119
+ callbacks.each do |callback|
120
+ define_callbacks(callback, options)
121
+
122
+ types.each do |type|
123
+ send("_define_#{type}_model_callback", self, callback)
124
+ end
125
+ end
126
+ end
127
+
128
+ private
129
+ def _define_before_model_callback(klass, callback)
130
+ klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
131
+ options.assert_valid_keys(:if, :unless, :prepend)
132
+ set_callback(:"#{callback}", :before, *args, options, &block)
133
+ end
134
+ end
135
+
136
+ def _define_around_model_callback(klass, callback)
137
+ klass.define_singleton_method("around_#{callback}") do |*args, **options, &block|
138
+ options.assert_valid_keys(:if, :unless, :prepend)
139
+ set_callback(:"#{callback}", :around, *args, options, &block)
140
+ end
141
+ end
142
+
143
+ def _define_after_model_callback(klass, callback)
144
+ klass.define_singleton_method("after_#{callback}") do |*args, **options, &block|
145
+ options.assert_valid_keys(:if, :unless, :prepend)
146
+ options[:prepend] = true
147
+ conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
148
+ v != false
149
+ }
150
+ options[:if] = Array(options[:if]) + [conditional]
151
+ set_callback(:"#{callback}", :after, *args, options, &block)
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ # = Active \Model \Conversion
5
+ #
6
+ # Handles default conversions: to_model, to_key, to_param, and to_partial_path.
7
+ #
8
+ # Let's take for example this non-persisted object.
9
+ #
10
+ # class ContactMessage
11
+ # include ActiveModel::Conversion
12
+ #
13
+ # # ContactMessage are never persisted in the DB
14
+ # def persisted?
15
+ # false
16
+ # end
17
+ # end
18
+ #
19
+ # cm = ContactMessage.new
20
+ # cm.to_model == cm # => true
21
+ # cm.to_key # => nil
22
+ # cm.to_param # => nil
23
+ # cm.to_partial_path # => "contact_messages/contact_message"
24
+ module Conversion
25
+ extend ActiveSupport::Concern
26
+
27
+ included do
28
+ ##
29
+ # :singleton-method:
30
+ #
31
+ # Accepts a string that will be used as a delimiter of object's key values in the `to_param` method.
32
+ class_attribute :param_delimiter, instance_reader: false, default: "-"
33
+ end
34
+
35
+ # If your object is already designed to implement all of the \Active \Model
36
+ # you can use the default <tt>:to_model</tt> implementation, which simply
37
+ # returns +self+.
38
+ #
39
+ # class Person
40
+ # include ActiveModel::Conversion
41
+ # end
42
+ #
43
+ # person = Person.new
44
+ # person.to_model == person # => true
45
+ #
46
+ # If your model does not act like an \Active \Model object, then you should
47
+ # define <tt>:to_model</tt> yourself returning a proxy object that wraps
48
+ # your object with \Active \Model compliant methods.
49
+ def to_model
50
+ self
51
+ end
52
+
53
+ # Returns an Array of all key attributes if any of the attributes is set, whether or not
54
+ # the object is persisted. Returns +nil+ if there are no key attributes.
55
+ #
56
+ # class Person
57
+ # include ActiveModel::Conversion
58
+ # attr_accessor :id
59
+ #
60
+ # def initialize(id)
61
+ # @id = id
62
+ # end
63
+ # end
64
+ #
65
+ # person = Person.new(1)
66
+ # person.to_key # => [1]
67
+ def to_key
68
+ key = respond_to?(:id) && id
69
+ key ? Array(key) : nil
70
+ end
71
+
72
+ # Returns a +string+ representing the object's key suitable for use in URLs,
73
+ # or +nil+ if <tt>persisted?</tt> is +false+.
74
+ #
75
+ # class Person
76
+ # include ActiveModel::Conversion
77
+ # attr_accessor :id
78
+ #
79
+ # def initialize(id)
80
+ # @id = id
81
+ # end
82
+ #
83
+ # def persisted?
84
+ # true
85
+ # end
86
+ # end
87
+ #
88
+ # person = Person.new(1)
89
+ # person.to_param # => "1"
90
+ def to_param
91
+ (persisted? && (key = to_key) && key.all?) ? key.join(self.class.param_delimiter) : nil
92
+ end
93
+
94
+ # Returns a +string+ identifying the path associated with the object.
95
+ # ActionPack uses this to find a suitable partial to represent the object.
96
+ #
97
+ # class Person
98
+ # include ActiveModel::Conversion
99
+ # end
100
+ #
101
+ # person = Person.new
102
+ # person.to_partial_path # => "people/person"
103
+ def to_partial_path
104
+ self.class._to_partial_path
105
+ end
106
+
107
+ module ClassMethods # :nodoc:
108
+ # Provide a class level cache for #to_partial_path. This is an
109
+ # internal method and should not be accessed directly.
110
+ def _to_partial_path # :nodoc:
111
+ @_to_partial_path ||= if respond_to?(:model_name)
112
+ "#{model_name.collection}/#{model_name.element}"
113
+ else
114
+ element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
115
+ collection = ActiveSupport::Inflector.tableize(name)
116
+ "#{collection}/#{element}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ def self.deprecator # :nodoc:
5
+ @deprecator ||= ActiveSupport::Deprecation.new
6
+ end
7
+ end