omg-activemodel 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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