activerecord 6.0.0 → 6.1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1413 -614
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_record/aggregations.rb +5 -6
- data/lib/active_record/association_relation.rb +30 -10
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +55 -29
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +38 -13
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +77 -42
- data/lib/active_record/associations/preloader/association.rb +49 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +120 -13
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -9
- data/lib/active_record/autosave_association.rb +63 -44
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +24 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
- data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
- data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -56
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -66
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +40 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +219 -81
- data/lib/active_record/core.rb +283 -71
- data/lib/active_record/counter_cache.rb +4 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -41
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +80 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +58 -12
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +43 -10
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -18
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +28 -9
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +75 -21
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +115 -85
- data/lib/active_record/model_schema.rb +120 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +280 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/calculations.rb +106 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +55 -17
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +59 -40
- data/lib/active_record/relation/query_methods.rb +346 -181
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -62
- data/lib/active_record/relation.rb +116 -82
- data/lib/active_record/result.rb +41 -34
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -4
- data/lib/active_record/scoping/named.rb +7 -18
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +9 -4
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -36
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +87 -20
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +26 -73
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +8 -2
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record.rb +7 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +15 -12
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +6 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +31 -27
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "mutex_m"
|
4
|
+
require "active_support/core_ext/enumerable"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
# = Active Record Attribute Methods
|
@@ -18,8 +19,6 @@ module ActiveRecord
|
|
18
19
|
include TimeZoneConversion
|
19
20
|
include Dirty
|
20
21
|
include Serialization
|
21
|
-
|
22
|
-
delegate :column_for_attribute, to: :class
|
23
22
|
end
|
24
23
|
|
25
24
|
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
@@ -28,6 +27,17 @@ module ActiveRecord
|
|
28
27
|
include Mutex_m
|
29
28
|
end
|
30
29
|
|
30
|
+
class << self
|
31
|
+
def dangerous_attribute_methods # :nodoc:
|
32
|
+
@dangerous_attribute_methods ||= (
|
33
|
+
Base.instance_methods +
|
34
|
+
Base.private_instance_methods -
|
35
|
+
Base.superclass.instance_methods -
|
36
|
+
Base.superclass.private_instance_methods
|
37
|
+
).map { |m| -m.to_s }.to_set.freeze
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
31
41
|
module ClassMethods
|
32
42
|
def inherited(child_class) #:nodoc:
|
33
43
|
child_class.initialize_generated_modules
|
@@ -97,7 +107,7 @@ module ActiveRecord
|
|
97
107
|
# A method name is 'dangerous' if it is already (re)defined by Active Record, but
|
98
108
|
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
|
99
109
|
def dangerous_attribute_method?(name) # :nodoc:
|
100
|
-
|
110
|
+
::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(name.to_s)
|
101
111
|
end
|
102
112
|
|
103
113
|
def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
@@ -115,13 +125,11 @@ module ActiveRecord
|
|
115
125
|
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
|
116
126
|
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
|
117
127
|
def dangerous_class_method?(method_name)
|
118
|
-
RESTRICTED_CLASS_METHODS.include?(method_name.to_s)
|
119
|
-
end
|
128
|
+
return true if RESTRICTED_CLASS_METHODS.include?(method_name.to_s)
|
120
129
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
klass.method(name).owner != superklass.method(name).owner
|
130
|
+
if Base.respond_to?(method_name, true)
|
131
|
+
if Object.respond_to?(method_name, true)
|
132
|
+
Base.method(method_name).owner != Object.method(method_name).owner
|
125
133
|
else
|
126
134
|
true
|
127
135
|
end
|
@@ -140,7 +148,7 @@ module ActiveRecord
|
|
140
148
|
# Person.attribute_method?(:age=) # => true
|
141
149
|
# Person.attribute_method?(:nothing) # => false
|
142
150
|
def attribute_method?(attribute)
|
143
|
-
super || (table_exists? && column_names.include?(attribute.to_s.
|
151
|
+
super || (table_exists? && column_names.include?(attribute.to_s.delete_suffix("=")))
|
144
152
|
end
|
145
153
|
|
146
154
|
# Returns an array of column names as strings if it's not an abstract class and
|
@@ -156,39 +164,27 @@ module ActiveRecord
|
|
156
164
|
attribute_types.keys
|
157
165
|
else
|
158
166
|
[]
|
159
|
-
end
|
167
|
+
end.freeze
|
160
168
|
end
|
161
169
|
|
162
170
|
# Returns true if the given attribute exists, otherwise false.
|
163
171
|
#
|
164
172
|
# class Person < ActiveRecord::Base
|
173
|
+
# alias_attribute :new_name, :name
|
165
174
|
# end
|
166
175
|
#
|
167
|
-
# Person.has_attribute?('name')
|
168
|
-
# Person.has_attribute?(
|
169
|
-
# Person.has_attribute?(:
|
176
|
+
# Person.has_attribute?('name') # => true
|
177
|
+
# Person.has_attribute?('new_name') # => true
|
178
|
+
# Person.has_attribute?(:age) # => true
|
179
|
+
# Person.has_attribute?(:nothing) # => false
|
170
180
|
def has_attribute?(attr_name)
|
171
|
-
|
181
|
+
attr_name = attr_name.to_s
|
182
|
+
attr_name = attribute_aliases[attr_name] || attr_name
|
183
|
+
attribute_types.key?(attr_name)
|
172
184
|
end
|
173
185
|
|
174
|
-
|
175
|
-
|
176
|
-
# named attribute does not exist.
|
177
|
-
#
|
178
|
-
# class Person < ActiveRecord::Base
|
179
|
-
# end
|
180
|
-
#
|
181
|
-
# person = Person.new
|
182
|
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
183
|
-
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
184
|
-
#
|
185
|
-
# person.column_for_attribute(:nothing)
|
186
|
-
# # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
|
187
|
-
def column_for_attribute(name)
|
188
|
-
name = name.to_s
|
189
|
-
columns_hash.fetch(name) do
|
190
|
-
ConnectionAdapters::NullColumn.new(name)
|
191
|
-
end
|
186
|
+
def _has_attribute?(attr_name) # :nodoc:
|
187
|
+
attribute_types.key?(attr_name)
|
192
188
|
end
|
193
189
|
end
|
194
190
|
|
@@ -217,7 +213,7 @@ module ActiveRecord
|
|
217
213
|
# have been allocated but not yet initialized.
|
218
214
|
if defined?(@attributes)
|
219
215
|
if name = self.class.symbol_column_to_string(name.to_sym)
|
220
|
-
return
|
216
|
+
return _has_attribute?(name)
|
221
217
|
end
|
222
218
|
end
|
223
219
|
|
@@ -227,14 +223,22 @@ module ActiveRecord
|
|
227
223
|
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
|
228
224
|
#
|
229
225
|
# class Person < ActiveRecord::Base
|
226
|
+
# alias_attribute :new_name, :name
|
230
227
|
# end
|
231
228
|
#
|
232
229
|
# person = Person.new
|
233
|
-
# person.has_attribute?(:name)
|
234
|
-
# person.has_attribute?(
|
235
|
-
# person.has_attribute?(
|
230
|
+
# person.has_attribute?(:name) # => true
|
231
|
+
# person.has_attribute?(:new_name) # => true
|
232
|
+
# person.has_attribute?('age') # => true
|
233
|
+
# person.has_attribute?(:nothing) # => false
|
236
234
|
def has_attribute?(attr_name)
|
237
|
-
|
235
|
+
attr_name = attr_name.to_s
|
236
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
237
|
+
@attributes.key?(attr_name)
|
238
|
+
end
|
239
|
+
|
240
|
+
def _has_attribute?(attr_name) # :nodoc:
|
241
|
+
@attributes.key?(attr_name)
|
238
242
|
end
|
239
243
|
|
240
244
|
# Returns an array of names for the attributes available on this object.
|
@@ -278,8 +282,10 @@ module ActiveRecord
|
|
278
282
|
# person.attribute_for_inspect(:tag_ids)
|
279
283
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
280
284
|
def attribute_for_inspect(attr_name)
|
285
|
+
attr_name = attr_name.to_s
|
286
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
281
287
|
value = _read_attribute(attr_name)
|
282
|
-
format_for_inspect(value)
|
288
|
+
format_for_inspect(attr_name, value)
|
283
289
|
end
|
284
290
|
|
285
291
|
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
@@ -297,8 +303,10 @@ module ActiveRecord
|
|
297
303
|
# task.is_done = true
|
298
304
|
# task.attribute_present?(:title) # => true
|
299
305
|
# task.attribute_present?(:is_done) # => true
|
300
|
-
def attribute_present?(
|
301
|
-
|
306
|
+
def attribute_present?(attr_name)
|
307
|
+
attr_name = attr_name.to_s
|
308
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
309
|
+
value = _read_attribute(attr_name)
|
302
310
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
303
311
|
end
|
304
312
|
|
@@ -377,8 +385,8 @@ module ActiveRecord
|
|
377
385
|
end
|
378
386
|
|
379
387
|
def attributes_with_values(attribute_names)
|
380
|
-
attribute_names.
|
381
|
-
|
388
|
+
attribute_names.index_with do |name|
|
389
|
+
_read_attribute(name)
|
382
390
|
end
|
383
391
|
end
|
384
392
|
|
@@ -386,7 +394,7 @@ module ActiveRecord
|
|
386
394
|
def attributes_for_update(attribute_names)
|
387
395
|
attribute_names &= self.class.column_names
|
388
396
|
attribute_names.delete_if do |name|
|
389
|
-
readonly_attribute?(name)
|
397
|
+
self.class.readonly_attribute?(name)
|
390
398
|
end
|
391
399
|
end
|
392
400
|
|
@@ -399,18 +407,20 @@ module ActiveRecord
|
|
399
407
|
end
|
400
408
|
end
|
401
409
|
|
402
|
-
def format_for_inspect(value)
|
403
|
-
if value.
|
404
|
-
"#{value[0, 50]}...".inspect
|
405
|
-
elsif value.is_a?(Date) || value.is_a?(Time)
|
406
|
-
%("#{value.to_s(:db)}")
|
407
|
-
else
|
410
|
+
def format_for_inspect(name, value)
|
411
|
+
if value.nil?
|
408
412
|
value.inspect
|
409
|
-
|
410
|
-
|
413
|
+
else
|
414
|
+
inspected_value = if value.is_a?(String) && value.length > 50
|
415
|
+
"#{value[0, 50]}...".inspect
|
416
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
417
|
+
%("#{value.to_s(:inspect)}")
|
418
|
+
else
|
419
|
+
value.inspect
|
420
|
+
end
|
411
421
|
|
412
|
-
|
413
|
-
|
422
|
+
inspection_filter.filter_param(name, inspected_value)
|
423
|
+
end
|
414
424
|
end
|
415
425
|
|
416
426
|
def pk_attribute?(name)
|
@@ -12,6 +12,9 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
+
##
|
16
|
+
# :call-seq: attribute(name, cast_type = nil, **options)
|
17
|
+
#
|
15
18
|
# Defines an attribute with a type on this model. It will override the
|
16
19
|
# type of existing attributes if needed. This allows control over how
|
17
20
|
# values are converted to and from SQL when assigned to a model. It also
|
@@ -170,7 +173,7 @@ module ActiveRecord
|
|
170
173
|
# class Money < Struct.new(:amount, :currency)
|
171
174
|
# end
|
172
175
|
#
|
173
|
-
# class MoneyType < Type::Value
|
176
|
+
# class MoneyType < ActiveRecord::Type::Value
|
174
177
|
# def initialize(currency_converter:)
|
175
178
|
# @currency_converter = currency_converter
|
176
179
|
# end
|
@@ -205,13 +208,13 @@ module ActiveRecord
|
|
205
208
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
206
209
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
207
210
|
# methods in ActiveModel::Type::Value for more details.
|
208
|
-
def attribute(name, cast_type =
|
211
|
+
def attribute(name, cast_type = nil, **options, &block)
|
209
212
|
name = name.to_s
|
210
213
|
reload_schema_from_cache
|
211
214
|
|
212
215
|
self.attributes_to_define_after_schema_loads =
|
213
216
|
attributes_to_define_after_schema_loads.merge(
|
214
|
-
name => [cast_type, options]
|
217
|
+
name => [cast_type || block, options]
|
215
218
|
)
|
216
219
|
end
|
217
220
|
|
@@ -246,16 +249,11 @@ module ActiveRecord
|
|
246
249
|
def load_schema! # :nodoc:
|
247
250
|
super
|
248
251
|
attributes_to_define_after_schema_loads.each do |name, (type, options)|
|
249
|
-
|
250
|
-
type = ActiveRecord::Type.lookup(type, **options.except(:default))
|
251
|
-
end
|
252
|
-
|
253
|
-
define_attribute(name, type, **options.slice(:default))
|
252
|
+
define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
|
254
253
|
end
|
255
254
|
end
|
256
255
|
|
257
256
|
private
|
258
|
-
|
259
257
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
260
258
|
private_constant :NO_DEFAULT_PROVIDED
|
261
259
|
|
@@ -274,6 +272,32 @@ module ActiveRecord
|
|
274
272
|
end
|
275
273
|
_default_attributes[name] = default_attribute
|
276
274
|
end
|
275
|
+
|
276
|
+
def decorate_attribute_type(attr_name, **default)
|
277
|
+
type, options = attributes_to_define_after_schema_loads[attr_name]
|
278
|
+
|
279
|
+
default.with_defaults!(default: options[:default]) if options&.key?(:default)
|
280
|
+
|
281
|
+
attribute(attr_name, **default) do |cast_type|
|
282
|
+
if type && !type.is_a?(Proc)
|
283
|
+
cast_type = _lookup_cast_type(attr_name, type, options)
|
284
|
+
end
|
285
|
+
|
286
|
+
yield cast_type
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def _lookup_cast_type(name, type, options)
|
291
|
+
case type
|
292
|
+
when Symbol
|
293
|
+
adapter_name = ActiveRecord::Type.adapter_name_from(self)
|
294
|
+
ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
|
295
|
+
when Proc
|
296
|
+
type[type_for_attribute(name)]
|
297
|
+
else
|
298
|
+
type || type_for_attribute(name)
|
299
|
+
end
|
300
|
+
end
|
277
301
|
end
|
278
302
|
end
|
279
303
|
end
|
@@ -29,9 +29,9 @@ module ActiveRecord
|
|
29
29
|
# == Callbacks
|
30
30
|
#
|
31
31
|
# Association with autosave option defines several callbacks on your
|
32
|
-
# model (before_save, after_create, after_update). Please note that
|
32
|
+
# model (around_save, before_save, after_create, after_update). Please note that
|
33
33
|
# callbacks are executed in the order they were defined in
|
34
|
-
# model. You should avoid modifying the association content
|
34
|
+
# model. You should avoid modifying the association content before
|
35
35
|
# autosave callbacks are executed. Placing your callbacks after
|
36
36
|
# associations is usually a good practice.
|
37
37
|
#
|
@@ -91,8 +91,9 @@ module ActiveRecord
|
|
91
91
|
# post.save # => saves both post and comment
|
92
92
|
#
|
93
93
|
# post = Post.create(title: 'ruby rocks')
|
94
|
-
# post.comments.create(body: 'hello world')
|
95
|
-
#
|
94
|
+
# comment = post.comments.create(body: 'hello world')
|
95
|
+
# comment.body = 'hi everyone'
|
96
|
+
# post.save # => saves post, but not comment
|
96
97
|
#
|
97
98
|
# When <tt>:autosave</tt> is true all children are saved, no matter whether they
|
98
99
|
# are new records or not:
|
@@ -102,11 +103,10 @@ module ActiveRecord
|
|
102
103
|
# end
|
103
104
|
#
|
104
105
|
# post = Post.create(title: 'ruby rocks')
|
105
|
-
# post.comments.create(body: 'hello world')
|
106
|
-
#
|
106
|
+
# comment = post.comments.create(body: 'hello world')
|
107
|
+
# comment.body = 'hi everyone'
|
107
108
|
# post.comments.build(body: "good morning.")
|
108
|
-
# post.
|
109
|
-
# post.save # => saves both post and comments.
|
109
|
+
# post.save # => saves post and both comments.
|
110
110
|
#
|
111
111
|
# Destroying one of the associated models as part of the parent's save action
|
112
112
|
# is as simple as marking it for destruction:
|
@@ -127,6 +127,14 @@ module ActiveRecord
|
|
127
127
|
# Now it _is_ removed from the database:
|
128
128
|
#
|
129
129
|
# Comment.find_by(id: id).nil? # => true
|
130
|
+
#
|
131
|
+
# === Caveats
|
132
|
+
#
|
133
|
+
# Note that autosave will only trigger for already-persisted association records
|
134
|
+
# if the records themselves have been changed. This is to protect against
|
135
|
+
# <tt>SystemStackError</tt> caused by circular association validations. The one
|
136
|
+
# exception is if a custom validation context is used, in which case the validations
|
137
|
+
# will always fire on the associated records.
|
130
138
|
module AutosaveAssociation
|
131
139
|
extend ActiveSupport::Concern
|
132
140
|
|
@@ -147,9 +155,23 @@ module ActiveRecord
|
|
147
155
|
|
148
156
|
module ClassMethods # :nodoc:
|
149
157
|
private
|
158
|
+
if Module.method(:method_defined?).arity == 1 # MRI 2.5 and older
|
159
|
+
using Module.new {
|
160
|
+
refine Module do
|
161
|
+
def method_defined?(method, inherit = true)
|
162
|
+
if inherit
|
163
|
+
super(method)
|
164
|
+
else
|
165
|
+
instance_methods(false).include?(method.to_sym)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
}
|
170
|
+
end
|
150
171
|
|
151
172
|
def define_non_cyclic_method(name, &block)
|
152
|
-
return if
|
173
|
+
return if method_defined?(name, false)
|
174
|
+
|
153
175
|
define_method(name) do |*args|
|
154
176
|
result = true; @_already_called ||= {}
|
155
177
|
# Loop prevention for validation of associations
|
@@ -181,8 +203,7 @@ module ActiveRecord
|
|
181
203
|
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
182
204
|
|
183
205
|
if reflection.collection?
|
184
|
-
|
185
|
-
after_save :after_save_collection_association
|
206
|
+
around_save :around_save_collection_association
|
186
207
|
|
187
208
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
188
209
|
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
@@ -267,12 +288,11 @@ module ActiveRecord
|
|
267
288
|
end
|
268
289
|
|
269
290
|
private
|
270
|
-
|
271
291
|
# Returns the record for an association collection that should be validated
|
272
292
|
# or saved. If +autosave+ is +false+ only new records will be returned,
|
273
293
|
# unless the parent is/was a new record itself.
|
274
294
|
def associated_records_to_validate_or_save(association, new_record, autosave)
|
275
|
-
if new_record
|
295
|
+
if new_record || custom_validation_context?
|
276
296
|
association && association.target
|
277
297
|
elsif autosave
|
278
298
|
association.target.find_all(&:changed_for_autosave?)
|
@@ -281,8 +301,9 @@ module ActiveRecord
|
|
281
301
|
end
|
282
302
|
end
|
283
303
|
|
284
|
-
#
|
285
|
-
# any new ones), and return true if
|
304
|
+
# Go through nested autosave associations that are loaded in memory (without loading
|
305
|
+
# any new ones), and return true if any are changed for autosave.
|
306
|
+
# Returns false if already called to prevent an infinite loop.
|
286
307
|
def nested_records_changed_for_autosave?
|
287
308
|
@_nested_records_changed_for_autosave_already_called ||= false
|
288
309
|
return false if @_nested_records_changed_for_autosave_already_called
|
@@ -304,7 +325,7 @@ module ActiveRecord
|
|
304
325
|
def validate_single_association(reflection)
|
305
326
|
association = association_instance_get(reflection.name)
|
306
327
|
record = association && association.reader
|
307
|
-
association_valid?(reflection, record) if record && record.changed_for_autosave?
|
328
|
+
association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
|
308
329
|
end
|
309
330
|
|
310
331
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -324,27 +345,22 @@ module ActiveRecord
|
|
324
345
|
def association_valid?(reflection, record, index = nil)
|
325
346
|
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
326
347
|
|
327
|
-
context = validation_context
|
348
|
+
context = validation_context if custom_validation_context?
|
328
349
|
|
329
350
|
unless valid = record.valid?(context)
|
330
351
|
if reflection.options[:autosave]
|
331
352
|
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
|
332
353
|
|
333
|
-
record.errors.each
|
354
|
+
record.errors.group_by_attribute.each { |attribute, errors|
|
334
355
|
attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
|
335
|
-
errors[attribute] << message
|
336
|
-
errors[attribute].uniq!
|
337
|
-
end
|
338
|
-
|
339
|
-
record.errors.details.each_key do |attribute|
|
340
|
-
reflection_attribute =
|
341
|
-
normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
|
342
356
|
|
343
|
-
|
344
|
-
errors.
|
345
|
-
|
346
|
-
|
347
|
-
|
357
|
+
errors.each { |error|
|
358
|
+
self.errors.import(
|
359
|
+
error,
|
360
|
+
attribute: attribute
|
361
|
+
)
|
362
|
+
}
|
363
|
+
}
|
348
364
|
else
|
349
365
|
errors.add(reflection.name)
|
350
366
|
end
|
@@ -360,14 +376,15 @@ module ActiveRecord
|
|
360
376
|
end
|
361
377
|
end
|
362
378
|
|
363
|
-
# Is used as
|
379
|
+
# Is used as an around_save callback to check while saving a collection
|
364
380
|
# association whether or not the parent was a new record before saving.
|
365
|
-
def
|
366
|
-
@new_record_before_save
|
367
|
-
|
381
|
+
def around_save_collection_association
|
382
|
+
previously_new_record_before_save = (@new_record_before_save ||= false)
|
383
|
+
@new_record_before_save = !previously_new_record_before_save && new_record?
|
368
384
|
|
369
|
-
|
370
|
-
|
385
|
+
yield
|
386
|
+
ensure
|
387
|
+
@new_record_before_save = previously_new_record_before_save
|
371
388
|
end
|
372
389
|
|
373
390
|
# Saves any new associated records, or all loaded autosave associations if
|
@@ -440,13 +457,13 @@ module ActiveRecord
|
|
440
457
|
if autosave && record.marked_for_destruction?
|
441
458
|
record.destroy
|
442
459
|
elsif autosave != false
|
443
|
-
key = reflection.options[:primary_key] ?
|
460
|
+
key = reflection.options[:primary_key] ? public_send(reflection.options[:primary_key]) : id
|
444
461
|
|
445
|
-
if (autosave && record.changed_for_autosave?) ||
|
462
|
+
if (autosave && record.changed_for_autosave?) || record_changed?(reflection, record, key)
|
446
463
|
unless reflection.through_reflection
|
447
464
|
record[reflection.foreign_key] = key
|
448
465
|
if inverse_reflection = reflection.inverse_of
|
449
|
-
record.association(inverse_reflection.name).
|
466
|
+
record.association(inverse_reflection.name).inversed_from(self)
|
450
467
|
end
|
451
468
|
end
|
452
469
|
|
@@ -468,7 +485,7 @@ module ActiveRecord
|
|
468
485
|
def association_foreign_key_changed?(reflection, record, key)
|
469
486
|
return false if reflection.through_reflection?
|
470
487
|
|
471
|
-
record.
|
488
|
+
record._has_attribute?(reflection.foreign_key) && record._read_attribute(reflection.foreign_key) != key
|
472
489
|
end
|
473
490
|
|
474
491
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
@@ -489,7 +506,7 @@ module ActiveRecord
|
|
489
506
|
saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
|
490
507
|
|
491
508
|
if association.updated?
|
492
|
-
association_id = record.
|
509
|
+
association_id = record.public_send(reflection.options[:primary_key] || :id)
|
493
510
|
self[reflection.foreign_key] = association_id
|
494
511
|
association.loaded!
|
495
512
|
end
|
@@ -499,10 +516,12 @@ module ActiveRecord
|
|
499
516
|
end
|
500
517
|
end
|
501
518
|
|
519
|
+
def custom_validation_context?
|
520
|
+
validation_context && [:create, :update].exclude?(validation_context)
|
521
|
+
end
|
522
|
+
|
502
523
|
def _ensure_no_duplicate_errors
|
503
|
-
errors.
|
504
|
-
errors[attribute].uniq!
|
505
|
-
end
|
524
|
+
errors.uniq!
|
506
525
|
end
|
507
526
|
end
|
508
527
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -1,22 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "yaml"
|
4
3
|
require "active_support/benchmarkable"
|
5
4
|
require "active_support/dependencies"
|
6
5
|
require "active_support/descendants_tracker"
|
7
6
|
require "active_support/time"
|
8
|
-
require "active_support/core_ext/module/attribute_accessors"
|
9
|
-
require "active_support/core_ext/array/extract_options"
|
10
|
-
require "active_support/core_ext/hash/deep_merge"
|
11
|
-
require "active_support/core_ext/hash/slice"
|
12
|
-
require "active_support/core_ext/string/behavior"
|
13
|
-
require "active_support/core_ext/kernel/singleton_class"
|
14
|
-
require "active_support/core_ext/module/introspection"
|
15
|
-
require "active_support/core_ext/object/duplicable"
|
16
7
|
require "active_support/core_ext/class/subclasses"
|
17
|
-
require "active_record/attribute_decorators"
|
18
|
-
require "active_record/define_callbacks"
|
19
|
-
require "active_record/errors"
|
20
8
|
require "active_record/log_subscriber"
|
21
9
|
require "active_record/explain_subscriber"
|
22
10
|
require "active_record/relation/delegation"
|
@@ -285,6 +273,7 @@ module ActiveRecord #:nodoc:
|
|
285
273
|
extend Querying
|
286
274
|
extend Translation
|
287
275
|
extend DynamicMatchers
|
276
|
+
extend DelegatedType
|
288
277
|
extend Explain
|
289
278
|
extend Enum
|
290
279
|
extend Delegation::DelegateCache
|
@@ -303,10 +292,8 @@ module ActiveRecord #:nodoc:
|
|
303
292
|
include Validations
|
304
293
|
include CounterCache
|
305
294
|
include Attributes
|
306
|
-
include AttributeDecorators
|
307
295
|
include Locking::Optimistic
|
308
296
|
include Locking::Pessimistic
|
309
|
-
include DefineCallbacks
|
310
297
|
include AttributeMethods
|
311
298
|
include Callbacks
|
312
299
|
include Timestamp
|
@@ -321,6 +308,7 @@ module ActiveRecord #:nodoc:
|
|
321
308
|
include Serialization
|
322
309
|
include Store
|
323
310
|
include SecureToken
|
311
|
+
include SignedId
|
324
312
|
include Suppressor
|
325
313
|
end
|
326
314
|
|