activerecord 5.2.1.1 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +738 -445
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +18 -9
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +69 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +15 -29
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +11 -10
- data/lib/active_record/associations/has_many_through_association.rb +42 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +114 -38
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +27 -13
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -20
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +104 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +38 -7
- data/lib/active_record/errors.rb +30 -16
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +91 -64
- data/lib/active_record/model_schema.rb +34 -10
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +233 -28
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- data/lib/active_record/railtie.rb +81 -46
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +320 -70
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +67 -57
- data/lib/active_record/relation/delegation.rb +48 -35
- data/lib/active_record/relation/finder_methods.rb +30 -30
- data/lib/active_record/relation/merger.rb +19 -25
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +17 -10
- data/lib/active_record/relation/query_methods.rb +236 -73
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +195 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -27
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -4,7 +4,6 @@ require "active_model/forbidden_attributes_protection"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module AttributeAssignment
|
7
|
-
extend ActiveSupport::Concern
|
8
7
|
include ActiveModel::AttributeAssignment
|
9
8
|
|
10
9
|
private
|
@@ -46,16 +45,14 @@ module ActiveRecord
|
|
46
45
|
def execute_callstack_for_multiparameter_attributes(callstack)
|
47
46
|
errors = []
|
48
47
|
callstack.each do |name, values_with_empty_parameters|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
values = values_with_empty_parameters
|
54
|
-
end
|
55
|
-
send("#{name}=", values)
|
56
|
-
rescue => ex
|
57
|
-
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
|
48
|
+
if values_with_empty_parameters.each_value.all?(&:nil?)
|
49
|
+
values = nil
|
50
|
+
else
|
51
|
+
values = values_with_empty_parameters
|
58
52
|
end
|
53
|
+
send("#{name}=", values)
|
54
|
+
rescue => ex
|
55
|
+
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
|
59
56
|
end
|
60
57
|
unless errors.empty?
|
61
58
|
error_descriptions = errors.map(&:message).join(",")
|
@@ -22,16 +22,7 @@ module ActiveRecord
|
|
22
22
|
delegate :column_for_attribute, to: :class
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
def self.set_name_cache(name, value)
|
27
|
-
const_name = "ATTR_#{name}"
|
28
|
-
unless const_defined? const_name
|
29
|
-
const_set const_name, value.dup.freeze
|
30
|
-
end
|
31
|
-
end
|
32
|
-
}
|
33
|
-
|
34
|
-
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
25
|
+
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
35
26
|
|
36
27
|
class GeneratedAttributeMethods < Module #:nodoc:
|
37
28
|
include Mutex_m
|
@@ -44,7 +35,8 @@ module ActiveRecord
|
|
44
35
|
end
|
45
36
|
|
46
37
|
def initialize_generated_modules # :nodoc:
|
47
|
-
@generated_attribute_methods = GeneratedAttributeMethods.new
|
38
|
+
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
39
|
+
private_constant :GeneratedAttributeMethods
|
48
40
|
@attribute_methods_generated = false
|
49
41
|
include @generated_attribute_methods
|
50
42
|
|
@@ -59,7 +51,7 @@ module ActiveRecord
|
|
59
51
|
# attribute methods.
|
60
52
|
generated_attribute_methods.synchronize do
|
61
53
|
return false if @attribute_methods_generated
|
62
|
-
superclass.define_attribute_methods unless
|
54
|
+
superclass.define_attribute_methods unless base_class?
|
63
55
|
super(attribute_names)
|
64
56
|
@attribute_methods_generated = true
|
65
57
|
end
|
@@ -123,7 +115,7 @@ module ActiveRecord
|
|
123
115
|
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
|
124
116
|
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
|
125
117
|
def dangerous_class_method?(method_name)
|
126
|
-
|
118
|
+
RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
|
127
119
|
end
|
128
120
|
|
129
121
|
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
@@ -167,57 +159,6 @@ module ActiveRecord
|
|
167
159
|
end
|
168
160
|
end
|
169
161
|
|
170
|
-
# Regexp whitelist. Matches the following:
|
171
|
-
# "#{table_name}.#{column_name}"
|
172
|
-
# "#{column_name}"
|
173
|
-
COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
|
174
|
-
|
175
|
-
# Regexp whitelist. Matches the following:
|
176
|
-
# "#{table_name}.#{column_name}"
|
177
|
-
# "#{table_name}.#{column_name} #{direction}"
|
178
|
-
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
179
|
-
# "#{table_name}.#{column_name} NULLS LAST"
|
180
|
-
# "#{column_name}"
|
181
|
-
# "#{column_name} #{direction}"
|
182
|
-
# "#{column_name} #{direction} NULLS FIRST"
|
183
|
-
# "#{column_name} NULLS LAST"
|
184
|
-
COLUMN_NAME_ORDER_WHITELIST = /
|
185
|
-
\A
|
186
|
-
(?:\w+\.)?
|
187
|
-
\w+
|
188
|
-
(?:\s+asc|\s+desc)?
|
189
|
-
(?:\s+nulls\s+(?:first|last))?
|
190
|
-
\z
|
191
|
-
/ix
|
192
|
-
|
193
|
-
def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
|
194
|
-
unexpected = args.reject do |arg|
|
195
|
-
arg.kind_of?(Arel::Node) ||
|
196
|
-
arg.is_a?(Arel::Nodes::SqlLiteral) ||
|
197
|
-
arg.is_a?(Arel::Attributes::Attribute) ||
|
198
|
-
arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
|
199
|
-
end
|
200
|
-
|
201
|
-
return if unexpected.none?
|
202
|
-
|
203
|
-
if allow_unsafe_raw_sql == :deprecated
|
204
|
-
ActiveSupport::Deprecation.warn(
|
205
|
-
"Dangerous query method (method whose arguments are used as raw " \
|
206
|
-
"SQL) called with non-attribute argument(s): " \
|
207
|
-
"#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
|
208
|
-
"arguments will be disallowed in Rails 6.0. This method should " \
|
209
|
-
"not be called with user-provided values, such as request " \
|
210
|
-
"parameters or model attributes. Known-safe values can be passed " \
|
211
|
-
"by wrapping them in Arel.sql()."
|
212
|
-
)
|
213
|
-
else
|
214
|
-
raise(ActiveRecord::UnknownAttributeReference,
|
215
|
-
"Query method called with non-attribute argument(s): " +
|
216
|
-
unexpected.map(&:inspect).join(", ")
|
217
|
-
)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
162
|
# Returns true if the given attribute exists, otherwise false.
|
222
163
|
#
|
223
164
|
# class Person < ActiveRecord::Base
|
@@ -270,21 +211,14 @@ module ActiveRecord
|
|
270
211
|
def respond_to?(name, include_private = false)
|
271
212
|
return false unless super
|
272
213
|
|
273
|
-
case name
|
274
|
-
when :to_partial_path
|
275
|
-
name = "to_partial_path".freeze
|
276
|
-
when :to_model
|
277
|
-
name = "to_model".freeze
|
278
|
-
else
|
279
|
-
name = name.to_s
|
280
|
-
end
|
281
|
-
|
282
214
|
# If the result is true then check for the select case.
|
283
215
|
# For queries selecting a subset of columns, return false for unselected columns.
|
284
216
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
285
217
|
# have been allocated but not yet initialized.
|
286
|
-
if defined?(@attributes)
|
287
|
-
|
218
|
+
if defined?(@attributes)
|
219
|
+
if name = self.class.symbol_column_to_string(name.to_sym)
|
220
|
+
return has_attribute?(name)
|
221
|
+
end
|
288
222
|
end
|
289
223
|
|
290
224
|
true
|
@@ -344,15 +278,8 @@ module ActiveRecord
|
|
344
278
|
# person.attribute_for_inspect(:tag_ids)
|
345
279
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
346
280
|
def attribute_for_inspect(attr_name)
|
347
|
-
value =
|
348
|
-
|
349
|
-
if value.is_a?(String) && value.length > 50
|
350
|
-
"#{value[0, 50]}...".inspect
|
351
|
-
elsif value.is_a?(Date) || value.is_a?(Time)
|
352
|
-
%("#{value.to_s(:db)}")
|
353
|
-
else
|
354
|
-
value.inspect
|
355
|
-
end
|
281
|
+
value = _read_attribute(attr_name)
|
282
|
+
format_for_inspect(value)
|
356
283
|
end
|
357
284
|
|
358
285
|
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
@@ -443,23 +370,12 @@ module ActiveRecord
|
|
443
370
|
@attributes.accessed
|
444
371
|
end
|
445
372
|
|
446
|
-
|
447
|
-
|
448
|
-
def attribute_method?(attr_name) # :nodoc:
|
373
|
+
private
|
374
|
+
def attribute_method?(attr_name)
|
449
375
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
450
376
|
defined?(@attributes) && @attributes.key?(attr_name)
|
451
377
|
end
|
452
378
|
|
453
|
-
private
|
454
|
-
|
455
|
-
def attributes_with_values_for_create(attribute_names)
|
456
|
-
attributes_with_values(attributes_for_create(attribute_names))
|
457
|
-
end
|
458
|
-
|
459
|
-
def attributes_with_values_for_update(attribute_names)
|
460
|
-
attributes_with_values(attributes_for_update(attribute_names))
|
461
|
-
end
|
462
|
-
|
463
379
|
def attributes_with_values(attribute_names)
|
464
380
|
attribute_names.each_with_object({}) do |name, attrs|
|
465
381
|
attrs[name] = _read_attribute(name)
|
@@ -468,7 +384,8 @@ module ActiveRecord
|
|
468
384
|
|
469
385
|
# Filters the primary keys and readonly attributes from the attribute names.
|
470
386
|
def attributes_for_update(attribute_names)
|
471
|
-
attribute_names
|
387
|
+
attribute_names &= self.class.column_names
|
388
|
+
attribute_names.delete_if do |name|
|
472
389
|
readonly_attribute?(name)
|
473
390
|
end
|
474
391
|
end
|
@@ -476,17 +393,28 @@ module ActiveRecord
|
|
476
393
|
# Filters out the primary keys, from the attribute names, when the primary
|
477
394
|
# key is to be generated (e.g. the id attribute has no value).
|
478
395
|
def attributes_for_create(attribute_names)
|
479
|
-
attribute_names
|
396
|
+
attribute_names &= self.class.column_names
|
397
|
+
attribute_names.delete_if do |name|
|
480
398
|
pk_attribute?(name) && id.nil?
|
481
399
|
end
|
482
400
|
end
|
483
401
|
|
402
|
+
def format_for_inspect(value)
|
403
|
+
if value.is_a?(String) && value.length > 50
|
404
|
+
"#{value[0, 50]}...".inspect
|
405
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
406
|
+
%("#{value.to_s(:db)}")
|
407
|
+
else
|
408
|
+
value.inspect
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
484
412
|
def readonly_attribute?(name)
|
485
413
|
self.class.readonly_attributes.include?(name)
|
486
414
|
end
|
487
415
|
|
488
416
|
def pk_attribute?(name)
|
489
|
-
name ==
|
417
|
+
name == @primary_key
|
490
418
|
end
|
491
419
|
end
|
492
420
|
end
|
@@ -46,6 +46,7 @@ module ActiveRecord
|
|
46
46
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
47
47
|
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
48
48
|
def read_attribute_before_type_cast(attr_name)
|
49
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
49
50
|
@attributes[attr_name.to_s].value_before_type_cast
|
50
51
|
end
|
51
52
|
|
@@ -60,17 +61,19 @@ module ActiveRecord
|
|
60
61
|
# task.attributes_before_type_cast
|
61
62
|
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
62
63
|
def attributes_before_type_cast
|
64
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
63
65
|
@attributes.values_before_type_cast
|
64
66
|
end
|
65
67
|
|
66
68
|
private
|
67
69
|
|
68
|
-
#
|
70
|
+
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
|
69
71
|
def attribute_before_type_cast(attribute_name)
|
70
72
|
read_attribute_before_type_cast(attribute_name)
|
71
73
|
end
|
72
74
|
|
73
75
|
def attribute_came_from_user?(attribute_name)
|
76
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
74
77
|
@attributes[attribute_name].came_from_user?
|
75
78
|
end
|
76
79
|
end
|
@@ -16,9 +16,6 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
class_attribute :partial_writes, instance_writer: false, default: true
|
18
18
|
|
19
|
-
after_create { changes_applied }
|
20
|
-
after_update { changes_applied }
|
21
|
-
|
22
19
|
# Attribute methods for "changed in last call to save?"
|
23
20
|
attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
|
24
21
|
attribute_method_prefix("saved_change_to_")
|
@@ -32,18 +29,17 @@ module ActiveRecord
|
|
32
29
|
# <tt>reload</tt> the record and clears changed attributes.
|
33
30
|
def reload(*)
|
34
31
|
super.tap do
|
35
|
-
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
36
32
|
@mutations_before_last_save = nil
|
37
|
-
@attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
|
38
33
|
@mutations_from_database = nil
|
39
34
|
end
|
40
35
|
end
|
41
36
|
|
42
|
-
# Did this attribute change when we last saved?
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
37
|
+
# Did this attribute change when we last saved?
|
38
|
+
#
|
39
|
+
# This method is useful in after callbacks to determine if an attribute
|
40
|
+
# was changed during the save that triggered the callbacks to run. It can
|
41
|
+
# be invoked as +saved_change_to_name?+ instead of
|
42
|
+
# <tt>saved_change_to_attribute?("name")</tt>.
|
47
43
|
#
|
48
44
|
# ==== Options
|
49
45
|
#
|
@@ -53,28 +49,29 @@ module ActiveRecord
|
|
53
49
|
# +to+ When passed, this method will return false unless the value was
|
54
50
|
# changed to the given value
|
55
51
|
def saved_change_to_attribute?(attr_name, **options)
|
56
|
-
mutations_before_last_save.changed?(attr_name,
|
52
|
+
mutations_before_last_save.changed?(attr_name.to_s, options)
|
57
53
|
end
|
58
54
|
|
59
55
|
# Returns the change to an attribute during the last save. If the
|
60
56
|
# attribute was changed, the result will be an array containing the
|
61
57
|
# original value and the saved value.
|
62
58
|
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# <tt>saved_change_to_attribute("name")</tt>
|
59
|
+
# This method is useful in after callbacks, to see the change in an
|
60
|
+
# attribute during the save that triggered the callbacks to run. It can be
|
61
|
+
# invoked as +saved_change_to_name+ instead of
|
62
|
+
# <tt>saved_change_to_attribute("name")</tt>.
|
68
63
|
def saved_change_to_attribute(attr_name)
|
69
|
-
mutations_before_last_save.change_to_attribute(attr_name)
|
64
|
+
mutations_before_last_save.change_to_attribute(attr_name.to_s)
|
70
65
|
end
|
71
66
|
|
72
67
|
# Returns the original value of an attribute before the last save.
|
73
|
-
#
|
74
|
-
# callbacks to get the original value of an
|
75
|
-
#
|
68
|
+
#
|
69
|
+
# This method is useful in after callbacks to get the original value of an
|
70
|
+
# attribute before the save that triggered the callbacks to run. It can be
|
71
|
+
# invoked as +name_before_last_save+ instead of
|
72
|
+
# <tt>attribute_before_last_save("name")</tt>.
|
76
73
|
def attribute_before_last_save(attr_name)
|
77
|
-
mutations_before_last_save.original_value(attr_name)
|
74
|
+
mutations_before_last_save.original_value(attr_name.to_s)
|
78
75
|
end
|
79
76
|
|
80
77
|
# Did the last call to +save+ have any changes to change?
|
@@ -87,58 +84,137 @@ module ActiveRecord
|
|
87
84
|
mutations_before_last_save.changes
|
88
85
|
end
|
89
86
|
|
90
|
-
#
|
87
|
+
# Will this attribute change the next time we save?
|
88
|
+
#
|
89
|
+
# This method is useful in validations and before callbacks to determine
|
90
|
+
# if the next call to +save+ will change a particular attribute. It can be
|
91
|
+
# invoked as +will_save_change_to_name?+ instead of
|
92
|
+
# <tt>will_save_change_to_attribute("name")</tt>.
|
93
|
+
#
|
94
|
+
# ==== Options
|
95
|
+
#
|
96
|
+
# +from+ When passed, this method will return false unless the original
|
97
|
+
# value is equal to the given option
|
98
|
+
#
|
99
|
+
# +to+ When passed, this method will return false unless the value will be
|
100
|
+
# changed to the given value
|
91
101
|
def will_save_change_to_attribute?(attr_name, **options)
|
92
|
-
mutations_from_database.changed?(attr_name,
|
102
|
+
mutations_from_database.changed?(attr_name.to_s, options)
|
93
103
|
end
|
94
104
|
|
95
|
-
#
|
105
|
+
# Returns the change to an attribute that will be persisted during the
|
106
|
+
# next save.
|
107
|
+
#
|
108
|
+
# This method is useful in validations and before callbacks, to see the
|
109
|
+
# change to an attribute that will occur when the record is saved. It can
|
110
|
+
# be invoked as +name_change_to_be_saved+ instead of
|
111
|
+
# <tt>attribute_change_to_be_saved("name")</tt>.
|
112
|
+
#
|
113
|
+
# If the attribute will change, the result will be an array containing the
|
114
|
+
# original value and the new value about to be saved.
|
96
115
|
def attribute_change_to_be_saved(attr_name)
|
97
|
-
mutations_from_database.change_to_attribute(attr_name)
|
116
|
+
mutations_from_database.change_to_attribute(attr_name.to_s)
|
98
117
|
end
|
99
118
|
|
100
|
-
#
|
119
|
+
# Returns the value of an attribute in the database, as opposed to the
|
120
|
+
# in-memory value that will be persisted the next time the record is
|
121
|
+
# saved.
|
122
|
+
#
|
123
|
+
# This method is useful in validations and before callbacks, to see the
|
124
|
+
# original value of an attribute prior to any changes about to be
|
125
|
+
# saved. It can be invoked as +name_in_database+ instead of
|
126
|
+
# <tt>attribute_in_database("name")</tt>.
|
101
127
|
def attribute_in_database(attr_name)
|
102
|
-
mutations_from_database.original_value(attr_name)
|
128
|
+
mutations_from_database.original_value(attr_name.to_s)
|
103
129
|
end
|
104
130
|
|
105
|
-
#
|
131
|
+
# Will the next call to +save+ have any changes to persist?
|
106
132
|
def has_changes_to_save?
|
107
133
|
mutations_from_database.any_changes?
|
108
134
|
end
|
109
135
|
|
110
|
-
#
|
136
|
+
# Returns a hash containing all the changes that will be persisted during
|
137
|
+
# the next save.
|
111
138
|
def changes_to_save
|
112
139
|
mutations_from_database.changes
|
113
140
|
end
|
114
141
|
|
115
|
-
#
|
142
|
+
# Returns an array of the names of any attributes that will change when
|
143
|
+
# the record is next saved.
|
116
144
|
def changed_attribute_names_to_save
|
117
145
|
mutations_from_database.changed_attribute_names
|
118
146
|
end
|
119
147
|
|
120
|
-
#
|
148
|
+
# Returns a hash of the attributes that will change when the record is
|
149
|
+
# next saved.
|
150
|
+
#
|
151
|
+
# The hash keys are the attribute names, and the hash values are the
|
152
|
+
# original attribute values in the database (as opposed to the in-memory
|
153
|
+
# values about to be saved).
|
121
154
|
def attributes_in_database
|
122
155
|
mutations_from_database.changed_values
|
123
156
|
end
|
124
157
|
|
125
158
|
private
|
126
|
-
def
|
159
|
+
def mutations_from_database
|
160
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
def mutations_before_last_save
|
165
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
169
|
+
def write_attribute_without_type_cast(attr_name, value)
|
127
170
|
result = super
|
128
171
|
clear_attribute_change(attr_name)
|
129
172
|
result
|
130
173
|
end
|
131
174
|
|
132
|
-
def
|
133
|
-
|
175
|
+
def _touch_row(attribute_names, time)
|
176
|
+
@_touch_attr_names = Set.new(attribute_names)
|
177
|
+
|
178
|
+
affected_rows = super
|
179
|
+
|
180
|
+
if @_skip_dirty_tracking ||= false
|
181
|
+
clear_attribute_changes(@_touch_attr_names)
|
182
|
+
return affected_rows
|
183
|
+
end
|
184
|
+
|
185
|
+
changes = {}
|
186
|
+
@attributes.keys.each do |attr_name|
|
187
|
+
next if @_touch_attr_names.include?(attr_name)
|
188
|
+
|
189
|
+
if attribute_changed?(attr_name)
|
190
|
+
changes[attr_name] = _read_attribute(attr_name)
|
191
|
+
_write_attribute(attr_name, attribute_was(attr_name))
|
192
|
+
clear_attribute_change(attr_name)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
changes_applied
|
197
|
+
changes.each { |attr_name, value| _write_attribute(attr_name, value) }
|
198
|
+
|
199
|
+
affected_rows
|
200
|
+
ensure
|
201
|
+
@_touch_attr_names, @_skip_dirty_tracking = nil, nil
|
202
|
+
end
|
203
|
+
|
204
|
+
def _update_record(attribute_names = attribute_names_for_partial_writes)
|
205
|
+
affected_rows = super
|
206
|
+
changes_applied
|
207
|
+
affected_rows
|
134
208
|
end
|
135
209
|
|
136
|
-
def _create_record(
|
137
|
-
|
210
|
+
def _create_record(attribute_names = attribute_names_for_partial_writes)
|
211
|
+
id = super
|
212
|
+
changes_applied
|
213
|
+
id
|
138
214
|
end
|
139
215
|
|
140
|
-
def
|
141
|
-
changed_attribute_names_to_save
|
216
|
+
def attribute_names_for_partial_writes
|
217
|
+
partial_writes? ? changed_attribute_names_to_save : attribute_names
|
142
218
|
end
|
143
219
|
end
|
144
220
|
end
|