activerecord 5.2.8.1 → 6.1.0
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 +849 -630
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +5 -4
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +21 -21
- data/lib/active_record/associations/belongs_to_association.rb +50 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -5
- data/lib/active_record/associations/builder/association.rb +23 -21
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +8 -17
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +24 -18
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -20
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +47 -34
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +133 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +45 -8
- data/lib/active_record/autosave_association.rb +57 -42
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +2 -15
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +203 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +381 -146
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- 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 +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- 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 +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.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 +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +222 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +285 -33
- data/lib/active_record/core.rb +304 -106
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- 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 +3 -4
- data/lib/active_record/enum.rb +71 -17
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- 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 +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +197 -481
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +26 -22
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +34 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +141 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +205 -156
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +113 -74
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +402 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +109 -93
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +153 -90
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +64 -39
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -7
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -40
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +472 -186
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +108 -58
- data/lib/active_record/relation.rb +375 -104
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +4 -6
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- 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 +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -43
- data/lib/active_record/tasks/database_tasks.rb +276 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +58 -116
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- 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 +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- 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 +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -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/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -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 +15 -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 +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -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 +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -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 +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -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/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +120 -35
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -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,25 +19,25 @@ 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
|
-
|
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)
|
24
|
+
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
35
25
|
|
36
26
|
class GeneratedAttributeMethods < Module #:nodoc:
|
37
27
|
include Mutex_m
|
38
28
|
end
|
39
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
|
+
|
40
41
|
module ClassMethods
|
41
42
|
def inherited(child_class) #:nodoc:
|
42
43
|
child_class.initialize_generated_modules
|
@@ -44,7 +45,8 @@ module ActiveRecord
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def initialize_generated_modules # :nodoc:
|
47
|
-
@generated_attribute_methods = GeneratedAttributeMethods.new
|
48
|
+
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
49
|
+
private_constant :GeneratedAttributeMethods
|
48
50
|
@attribute_methods_generated = false
|
49
51
|
include @generated_attribute_methods
|
50
52
|
|
@@ -59,7 +61,7 @@ module ActiveRecord
|
|
59
61
|
# attribute methods.
|
60
62
|
generated_attribute_methods.synchronize do
|
61
63
|
return false if @attribute_methods_generated
|
62
|
-
superclass.define_attribute_methods unless
|
64
|
+
superclass.define_attribute_methods unless base_class?
|
63
65
|
super(attribute_names)
|
64
66
|
@attribute_methods_generated = true
|
65
67
|
end
|
@@ -105,7 +107,7 @@ module ActiveRecord
|
|
105
107
|
# A method name is 'dangerous' if it is already (re)defined by Active Record, but
|
106
108
|
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
|
107
109
|
def dangerous_attribute_method?(name) # :nodoc:
|
108
|
-
|
110
|
+
::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(name.to_s)
|
109
111
|
end
|
110
112
|
|
111
113
|
def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
@@ -123,13 +125,11 @@ module ActiveRecord
|
|
123
125
|
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
|
124
126
|
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
|
125
127
|
def dangerous_class_method?(method_name)
|
126
|
-
|
127
|
-
end
|
128
|
+
return true if RESTRICTED_CLASS_METHODS.include?(method_name.to_s)
|
128
129
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
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
|
133
133
|
else
|
134
134
|
true
|
135
135
|
end
|
@@ -148,7 +148,7 @@ module ActiveRecord
|
|
148
148
|
# Person.attribute_method?(:age=) # => true
|
149
149
|
# Person.attribute_method?(:nothing) # => false
|
150
150
|
def attribute_method?(attribute)
|
151
|
-
super || (table_exists? && column_names.include?(attribute.to_s.
|
151
|
+
super || (table_exists? && column_names.include?(attribute.to_s.delete_suffix("=")))
|
152
152
|
end
|
153
153
|
|
154
154
|
# Returns an array of column names as strings if it's not an abstract class and
|
@@ -164,90 +164,27 @@ module ActiveRecord
|
|
164
164
|
attribute_types.keys
|
165
165
|
else
|
166
166
|
[]
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
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
|
167
|
+
end.freeze
|
219
168
|
end
|
220
169
|
|
221
170
|
# Returns true if the given attribute exists, otherwise false.
|
222
171
|
#
|
223
172
|
# class Person < ActiveRecord::Base
|
173
|
+
# alias_attribute :new_name, :name
|
224
174
|
# end
|
225
175
|
#
|
226
|
-
# Person.has_attribute?('name')
|
227
|
-
# Person.has_attribute?(
|
228
|
-
# 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
|
229
180
|
def has_attribute?(attr_name)
|
230
|
-
|
181
|
+
attr_name = attr_name.to_s
|
182
|
+
attr_name = attribute_aliases[attr_name] || attr_name
|
183
|
+
attribute_types.key?(attr_name)
|
231
184
|
end
|
232
185
|
|
233
|
-
|
234
|
-
|
235
|
-
# named attribute does not exist.
|
236
|
-
#
|
237
|
-
# class Person < ActiveRecord::Base
|
238
|
-
# end
|
239
|
-
#
|
240
|
-
# person = Person.new
|
241
|
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
242
|
-
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
243
|
-
#
|
244
|
-
# person.column_for_attribute(:nothing)
|
245
|
-
# # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
|
246
|
-
def column_for_attribute(name)
|
247
|
-
name = name.to_s
|
248
|
-
columns_hash.fetch(name) do
|
249
|
-
ConnectionAdapters::NullColumn.new(name)
|
250
|
-
end
|
186
|
+
def _has_attribute?(attr_name) # :nodoc:
|
187
|
+
attribute_types.key?(attr_name)
|
251
188
|
end
|
252
189
|
end
|
253
190
|
|
@@ -270,21 +207,14 @@ module ActiveRecord
|
|
270
207
|
def respond_to?(name, include_private = false)
|
271
208
|
return false unless super
|
272
209
|
|
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
210
|
# If the result is true then check for the select case.
|
283
211
|
# For queries selecting a subset of columns, return false for unselected columns.
|
284
212
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
285
213
|
# have been allocated but not yet initialized.
|
286
|
-
if defined?(@attributes)
|
287
|
-
|
214
|
+
if defined?(@attributes)
|
215
|
+
if name = self.class.symbol_column_to_string(name.to_sym)
|
216
|
+
return _has_attribute?(name)
|
217
|
+
end
|
288
218
|
end
|
289
219
|
|
290
220
|
true
|
@@ -293,14 +223,22 @@ module ActiveRecord
|
|
293
223
|
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
|
294
224
|
#
|
295
225
|
# class Person < ActiveRecord::Base
|
226
|
+
# alias_attribute :new_name, :name
|
296
227
|
# end
|
297
228
|
#
|
298
229
|
# person = Person.new
|
299
|
-
# person.has_attribute?(:name)
|
300
|
-
# person.has_attribute?(
|
301
|
-
# 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
|
302
234
|
def has_attribute?(attr_name)
|
303
|
-
|
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)
|
304
242
|
end
|
305
243
|
|
306
244
|
# Returns an array of names for the attributes available on this object.
|
@@ -344,15 +282,10 @@ module ActiveRecord
|
|
344
282
|
# person.attribute_for_inspect(:tag_ids)
|
345
283
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
346
284
|
def attribute_for_inspect(attr_name)
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
elsif value.is_a?(Date) || value.is_a?(Time)
|
352
|
-
%("#{value.to_s(:db)}")
|
353
|
-
else
|
354
|
-
value.inspect
|
355
|
-
end
|
285
|
+
attr_name = attr_name.to_s
|
286
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
287
|
+
value = _read_attribute(attr_name)
|
288
|
+
format_for_inspect(attr_name, value)
|
356
289
|
end
|
357
290
|
|
358
291
|
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
@@ -370,8 +303,10 @@ module ActiveRecord
|
|
370
303
|
# task.is_done = true
|
371
304
|
# task.attribute_present?(:title) # => true
|
372
305
|
# task.attribute_present?(:is_done) # => true
|
373
|
-
def attribute_present?(
|
374
|
-
|
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)
|
375
310
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
376
311
|
end
|
377
312
|
|
@@ -443,50 +378,53 @@ module ActiveRecord
|
|
443
378
|
@attributes.accessed
|
444
379
|
end
|
445
380
|
|
446
|
-
|
447
|
-
|
448
|
-
def attribute_method?(attr_name) # :nodoc:
|
381
|
+
private
|
382
|
+
def attribute_method?(attr_name)
|
449
383
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
450
384
|
defined?(@attributes) && @attributes.key?(attr_name)
|
451
385
|
end
|
452
386
|
|
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
387
|
def attributes_with_values(attribute_names)
|
464
|
-
attribute_names.
|
465
|
-
|
388
|
+
attribute_names.index_with do |name|
|
389
|
+
_read_attribute(name)
|
466
390
|
end
|
467
391
|
end
|
468
392
|
|
469
393
|
# Filters the primary keys and readonly attributes from the attribute names.
|
470
394
|
def attributes_for_update(attribute_names)
|
471
|
-
attribute_names
|
472
|
-
|
395
|
+
attribute_names &= self.class.column_names
|
396
|
+
attribute_names.delete_if do |name|
|
397
|
+
self.class.readonly_attribute?(name)
|
473
398
|
end
|
474
399
|
end
|
475
400
|
|
476
401
|
# Filters out the primary keys, from the attribute names, when the primary
|
477
402
|
# key is to be generated (e.g. the id attribute has no value).
|
478
403
|
def attributes_for_create(attribute_names)
|
479
|
-
attribute_names
|
404
|
+
attribute_names &= self.class.column_names
|
405
|
+
attribute_names.delete_if do |name|
|
480
406
|
pk_attribute?(name) && id.nil?
|
481
407
|
end
|
482
408
|
end
|
483
409
|
|
484
|
-
def
|
485
|
-
|
410
|
+
def format_for_inspect(name, value)
|
411
|
+
if value.nil?
|
412
|
+
value.inspect
|
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
|
421
|
+
|
422
|
+
inspection_filter.filter_param(name, inspected_value)
|
423
|
+
end
|
486
424
|
end
|
487
425
|
|
488
426
|
def pk_attribute?(name)
|
489
|
-
name ==
|
427
|
+
name == @primary_key
|
490
428
|
end
|
491
429
|
end
|
492
430
|
end
|
@@ -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
|
@@ -41,6 +44,9 @@ module ActiveRecord
|
|
41
44
|
# +range+ (PostgreSQL only) specifies that the type should be a range (see the
|
42
45
|
# examples below).
|
43
46
|
#
|
47
|
+
# When using a symbol for +cast_type+, extra options are forwarded to the
|
48
|
+
# constructor of the type object.
|
49
|
+
#
|
44
50
|
# ==== Examples
|
45
51
|
#
|
46
52
|
# The type detected by Active Record can be overridden.
|
@@ -112,6 +118,16 @@ module ActiveRecord
|
|
112
118
|
# my_float_range: 1.0..3.5
|
113
119
|
# }
|
114
120
|
#
|
121
|
+
# Passing options to the type constructor
|
122
|
+
#
|
123
|
+
# # app/models/my_model.rb
|
124
|
+
# class MyModel < ActiveRecord::Base
|
125
|
+
# attribute :small_int, :integer, limit: 2
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# MyModel.create(small_int: 65537)
|
129
|
+
# # => Error: 65537 is out of range for the limit of two bytes
|
130
|
+
#
|
115
131
|
# ==== Creating Custom Types
|
116
132
|
#
|
117
133
|
# Users may also define their own custom types, as long as they respond
|
@@ -192,13 +208,13 @@ module ActiveRecord
|
|
192
208
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
193
209
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
194
210
|
# methods in ActiveModel::Type::Value for more details.
|
195
|
-
def attribute(name, cast_type =
|
211
|
+
def attribute(name, cast_type = nil, **options, &block)
|
196
212
|
name = name.to_s
|
197
213
|
reload_schema_from_cache
|
198
214
|
|
199
215
|
self.attributes_to_define_after_schema_loads =
|
200
216
|
attributes_to_define_after_schema_loads.merge(
|
201
|
-
name => [cast_type, options]
|
217
|
+
name => [cast_type || block, options]
|
202
218
|
)
|
203
219
|
end
|
204
220
|
|
@@ -233,16 +249,11 @@ module ActiveRecord
|
|
233
249
|
def load_schema! # :nodoc:
|
234
250
|
super
|
235
251
|
attributes_to_define_after_schema_loads.each do |name, (type, options)|
|
236
|
-
|
237
|
-
type = ActiveRecord::Type.lookup(type, **options.except(:default))
|
238
|
-
end
|
239
|
-
|
240
|
-
define_attribute(name, type, **options.slice(:default))
|
252
|
+
define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
|
241
253
|
end
|
242
254
|
end
|
243
255
|
|
244
256
|
private
|
245
|
-
|
246
257
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
247
258
|
private_constant :NO_DEFAULT_PROVIDED
|
248
259
|
|
@@ -261,6 +272,32 @@ module ActiveRecord
|
|
261
272
|
end
|
262
273
|
_default_attributes[name] = default_attribute
|
263
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
|
264
301
|
end
|
265
302
|
end
|
266
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 method_defined?(name)
|
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,7 +288,6 @@ 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.
|
@@ -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
|
@@ -330,21 +351,16 @@ module ActiveRecord
|
|
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
356
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
end
|
347
|
-
end
|
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
|
@@ -416,7 +433,7 @@ module ActiveRecord
|
|
416
433
|
saved = record.save(validate: false)
|
417
434
|
end
|
418
435
|
|
419
|
-
raise
|
436
|
+
raise(RecordInvalid.new(association.owner)) unless saved
|
420
437
|
end
|
421
438
|
end
|
422
439
|
end
|
@@ -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
|
@@ -504,9 +521,7 @@ module ActiveRecord
|
|
504
521
|
end
|
505
522
|
|
506
523
|
def _ensure_no_duplicate_errors
|
507
|
-
errors.
|
508
|
-
errors[attribute].uniq!
|
509
|
-
end
|
524
|
+
errors.uniq!
|
510
525
|
end
|
511
526
|
end
|
512
527
|
end
|