activerecord 5.2.2.1 → 6.0.2
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 +734 -508
- 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 +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -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 +16 -28
- 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 +3 -10
- data/lib/active_record/associations/has_many_through_association.rb +20 -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 +40 -32
- 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 +111 -40
- 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 +22 -8
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +137 -26
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +114 -130
- 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 +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +7 -11
- 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 +1 -1
- 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 +61 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -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 +37 -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 +33 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +231 -25
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -22
- data/lib/active_record/railtie.rb +80 -43
- 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 +199 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +311 -80
- 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 +26 -43
- data/lib/active_record/relation/finder_methods.rb +28 -28
- data/lib/active_record/relation/merger.rb +17 -23
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- 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 +247 -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 +20 -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 +194 -25
- 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 +225 -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 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -19,10 +19,6 @@ module ActiveRecord
|
|
19
19
|
owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
|
20
20
|
end
|
21
21
|
|
22
|
-
def different_target?(record)
|
23
|
-
super || record.class != klass
|
24
|
-
end
|
25
|
-
|
26
22
|
def inverse_reflection_for(record)
|
27
23
|
reflection.polymorphic_inverse_of(record.class)
|
28
24
|
end
|
@@ -27,40 +27,32 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
27
27
|
"Please choose a different association name."
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
reflection = create_reflection model, name, scope, options, extension
|
30
|
+
reflection = create_reflection(model, name, scope, options, &block)
|
32
31
|
define_accessors model, reflection
|
33
32
|
define_callbacks model, reflection
|
34
33
|
define_validations model, reflection
|
35
34
|
reflection
|
36
35
|
end
|
37
36
|
|
38
|
-
def self.create_reflection(model, name, scope, options,
|
37
|
+
def self.create_reflection(model, name, scope, options, &block)
|
39
38
|
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
|
40
39
|
|
41
40
|
validate_options(options)
|
42
41
|
|
43
|
-
|
42
|
+
extension = define_extensions(model, name, &block)
|
43
|
+
options[:extend] = [*options[:extend], extension] if extension
|
44
|
+
|
45
|
+
scope = build_scope(scope)
|
44
46
|
|
45
47
|
ActiveRecord::Reflection.create(macro, name, scope, options, model)
|
46
48
|
end
|
47
49
|
|
48
|
-
def self.build_scope(scope
|
49
|
-
new_scope = scope
|
50
|
-
|
50
|
+
def self.build_scope(scope)
|
51
51
|
if scope && scope.arity == 0
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
if extension
|
56
|
-
new_scope = wrap_scope new_scope, extension
|
52
|
+
proc { instance_exec(&scope) }
|
53
|
+
else
|
54
|
+
scope
|
57
55
|
end
|
58
|
-
|
59
|
-
new_scope
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.wrap_scope(scope, extension)
|
63
|
-
scope
|
64
56
|
end
|
65
57
|
|
66
58
|
def self.macro
|
@@ -136,5 +128,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
136
128
|
name = reflection.name
|
137
129
|
model.before_destroy lambda { |o| o.association(name).handle_dependency }
|
138
130
|
end
|
131
|
+
|
132
|
+
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
133
|
+
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
134
|
+
:valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
|
139
135
|
end
|
140
136
|
end
|
@@ -21,58 +21,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
21
21
|
add_default_callbacks(model, reflection) if reflection.options[:default]
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.define_accessors(mixin, reflection)
|
25
|
-
super
|
26
|
-
add_counter_cache_methods mixin
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.add_counter_cache_methods(mixin)
|
30
|
-
return if mixin.method_defined? :belongs_to_counter_cache_after_update
|
31
|
-
|
32
|
-
mixin.class_eval do
|
33
|
-
def belongs_to_counter_cache_after_update(reflection)
|
34
|
-
foreign_key = reflection.foreign_key
|
35
|
-
cache_column = reflection.counter_cache_column
|
36
|
-
|
37
|
-
if (@_after_replace_counter_called ||= false)
|
38
|
-
@_after_replace_counter_called = false
|
39
|
-
elsif association(reflection.name).target_changed?
|
40
|
-
if reflection.polymorphic?
|
41
|
-
model = attribute_in_database(reflection.foreign_type).try(:constantize)
|
42
|
-
model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
|
43
|
-
else
|
44
|
-
model = reflection.klass
|
45
|
-
model_was = reflection.klass
|
46
|
-
end
|
47
|
-
|
48
|
-
foreign_key_was = attribute_before_last_save foreign_key
|
49
|
-
foreign_key = attribute_in_database foreign_key
|
50
|
-
|
51
|
-
if foreign_key && model.respond_to?(:increment_counter)
|
52
|
-
foreign_key = counter_cache_target(reflection, model, foreign_key)
|
53
|
-
model.increment_counter(cache_column, foreign_key)
|
54
|
-
end
|
55
|
-
|
56
|
-
if foreign_key_was && model_was.respond_to?(:decrement_counter)
|
57
|
-
foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
|
58
|
-
model_was.decrement_counter(cache_column, foreign_key_was)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
def counter_cache_target(reflection, model, foreign_key)
|
65
|
-
primary_key = reflection.association_primary_key(model)
|
66
|
-
model.unscoped.where!(primary_key => foreign_key)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
24
|
def self.add_counter_cache_callbacks(model, reflection)
|
72
25
|
cache_column = reflection.counter_cache_column
|
73
26
|
|
74
27
|
model.after_update lambda { |record|
|
75
|
-
|
28
|
+
association = association(reflection.name)
|
29
|
+
|
30
|
+
if association.target_changed?
|
31
|
+
association.increment_counters
|
32
|
+
association.decrement_counters_before_last_save
|
33
|
+
end
|
76
34
|
}
|
77
35
|
|
78
36
|
klass = reflection.class_name.safe_constantize
|
@@ -116,19 +74,25 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
116
74
|
|
117
75
|
def self.add_touch_callbacks(model, reflection)
|
118
76
|
foreign_key = reflection.foreign_key
|
119
|
-
|
77
|
+
name = reflection.name
|
120
78
|
touch = reflection.options[:touch]
|
121
79
|
|
122
80
|
callback = lambda { |changes_method| lambda { |record|
|
123
|
-
BelongsTo.touch_record(record, record.send(changes_method), foreign_key,
|
81
|
+
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch, belongs_to_touch_method)
|
124
82
|
}}
|
125
83
|
|
126
|
-
|
84
|
+
if reflection.counter_cache_column
|
85
|
+
touch_callback = callback.(:saved_changes)
|
86
|
+
update_callback = lambda { |record|
|
87
|
+
instance_exec(record, &touch_callback) unless association(reflection.name).target_changed?
|
88
|
+
}
|
89
|
+
model.after_update update_callback, if: :saved_changes?
|
90
|
+
else
|
127
91
|
model.after_create callback.(:saved_changes), if: :saved_changes?
|
92
|
+
model.after_update callback.(:saved_changes), if: :saved_changes?
|
128
93
|
model.after_destroy callback.(:changes_to_save)
|
129
94
|
end
|
130
95
|
|
131
|
-
model.after_update callback.(:saved_changes), if: :saved_changes?
|
132
96
|
model.after_touch callback.(:changes_to_save)
|
133
97
|
end
|
134
98
|
|
@@ -159,5 +123,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
159
123
|
model.validates_presence_of reflection.name, message: :required
|
160
124
|
end
|
161
125
|
end
|
126
|
+
|
127
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks, :define_validations,
|
128
|
+
:add_counter_cache_callbacks, :add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
|
162
129
|
end
|
163
130
|
end
|
@@ -20,11 +20,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
-
def self.define_extensions(model, name)
|
23
|
+
def self.define_extensions(model, name, &block)
|
24
24
|
if block_given?
|
25
|
-
extension_module_name = "#{
|
26
|
-
extension = Module.new(&
|
27
|
-
model.
|
25
|
+
extension_module_name = "#{name.to_s.camelize}AssociationExtension"
|
26
|
+
extension = Module.new(&block)
|
27
|
+
model.const_set(extension_module_name, extension)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -67,16 +67,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
67
67
|
CODE
|
68
68
|
end
|
69
69
|
|
70
|
-
|
71
|
-
if scope
|
72
|
-
if scope.arity > 0
|
73
|
-
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
74
|
-
else
|
75
|
-
proc { instance_exec(&scope).extending(mod) }
|
76
|
-
end
|
77
|
-
else
|
78
|
-
proc { extending(mod) }
|
79
|
-
end
|
80
|
-
end
|
70
|
+
private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
|
81
71
|
end
|
82
72
|
end
|
@@ -2,39 +2,6 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
4
|
class HasAndBelongsToMany # :nodoc:
|
5
|
-
class JoinTableResolver # :nodoc:
|
6
|
-
KnownTable = Struct.new :join_table
|
7
|
-
|
8
|
-
class KnownClass # :nodoc:
|
9
|
-
def initialize(lhs_class, rhs_class_name)
|
10
|
-
@lhs_class = lhs_class
|
11
|
-
@rhs_class_name = rhs_class_name
|
12
|
-
@join_table = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def join_table
|
16
|
-
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def klass
|
22
|
-
@lhs_class.send(:compute_type, @rhs_class_name)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.build(lhs_class, name, options)
|
27
|
-
if options[:join_table]
|
28
|
-
KnownTable.new options[:join_table].to_s
|
29
|
-
else
|
30
|
-
class_name = options.fetch(:class_name) {
|
31
|
-
name.to_s.camelize.singularize
|
32
|
-
}
|
33
|
-
KnownClass.new lhs_class, class_name.to_s
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
5
|
attr_reader :lhs_model, :association_name, :options
|
39
6
|
|
40
7
|
def initialize(association_name, lhs_model, options)
|
@@ -44,8 +11,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
44
11
|
end
|
45
12
|
|
46
13
|
def through_model
|
47
|
-
habtm = JoinTableResolver.build lhs_model, association_name, options
|
48
|
-
|
49
14
|
join_model = Class.new(ActiveRecord::Base) {
|
50
15
|
class << self
|
51
16
|
attr_accessor :left_model
|
@@ -56,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
56
21
|
end
|
57
22
|
|
58
23
|
def self.table_name
|
59
|
-
|
24
|
+
# Table name needs to be resolved lazily
|
25
|
+
# because RHS class might not have been loaded
|
26
|
+
@table_name ||= table_name_resolver.call
|
60
27
|
end
|
61
28
|
|
62
29
|
def self.compute_type(class_name)
|
@@ -86,7 +53,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
86
53
|
}
|
87
54
|
|
88
55
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
89
|
-
join_model.table_name_resolver =
|
56
|
+
join_model.table_name_resolver = -> { table_name }
|
90
57
|
join_model.left_model = lhs_model
|
91
58
|
|
92
59
|
join_model.add_left_association :left_side, anonymous_class: lhs_model
|
@@ -96,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
96
63
|
|
97
64
|
def middle_reflection(join_model)
|
98
65
|
middle_name = [lhs_model.name.downcase.pluralize,
|
99
|
-
association_name].join("_"
|
66
|
+
association_name.to_s].sort.join("_").gsub("::", "_").to_sym
|
100
67
|
middle_options = middle_options join_model
|
101
68
|
|
102
69
|
HasMany.create_reflection(lhs_model,
|
@@ -117,6 +84,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
117
84
|
middle_options
|
118
85
|
end
|
119
86
|
|
87
|
+
def table_name
|
88
|
+
if options[:join_table]
|
89
|
+
options[:join_table].to_s
|
90
|
+
else
|
91
|
+
class_name = options.fetch(:class_name) {
|
92
|
+
association_name.to_s.camelize.singularize
|
93
|
+
}
|
94
|
+
klass = lhs_model.send(:compute_type, class_name.to_s)
|
95
|
+
[lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
120
99
|
def belongs_to_options(options)
|
121
100
|
rhs_options = {}
|
122
101
|
|
@@ -13,5 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
13
13
|
def self.valid_dependent_options
|
14
14
|
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
15
15
|
end
|
16
|
+
|
17
|
+
private_class_method :macro, :valid_options, :valid_dependent_options
|
16
18
|
end
|
17
19
|
end
|
@@ -7,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:as]
|
10
|
+
valid = super + [:as, :touch]
|
11
11
|
valid += [:through, :source, :source_type] if options[:through]
|
12
12
|
valid
|
13
13
|
end
|
@@ -16,6 +16,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
16
16
|
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.define_callbacks(model, reflection)
|
20
|
+
super
|
21
|
+
add_touch_callbacks(model, reflection) if reflection.options[:touch]
|
22
|
+
end
|
23
|
+
|
19
24
|
def self.add_destroy_callbacks(model, reflection)
|
20
25
|
super unless reflection.options[:through]
|
21
26
|
end
|
@@ -26,5 +31,34 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
26
31
|
model.validates_presence_of reflection.name, message: :required
|
27
32
|
end
|
28
33
|
end
|
34
|
+
|
35
|
+
def self.touch_record(o, name, touch)
|
36
|
+
record = o.send name
|
37
|
+
|
38
|
+
return unless record && record.persisted?
|
39
|
+
|
40
|
+
if touch != true
|
41
|
+
record.touch(touch)
|
42
|
+
else
|
43
|
+
record.touch
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.add_touch_callbacks(model, reflection)
|
48
|
+
name = reflection.name
|
49
|
+
touch = reflection.options[:touch]
|
50
|
+
|
51
|
+
callback = lambda { |record|
|
52
|
+
HasOne.touch_record(record, name, touch)
|
53
|
+
}
|
54
|
+
|
55
|
+
model.after_create callback, if: :saved_changes?
|
56
|
+
model.after_update callback, if: :saved_changes?
|
57
|
+
model.after_destroy callback
|
58
|
+
model.after_touch callback
|
59
|
+
end
|
60
|
+
|
61
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
|
62
|
+
:define_callbacks, :define_validations, :add_touch_callbacks
|
29
63
|
end
|
30
64
|
end
|
@@ -109,9 +109,8 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
# Add +records+ to this association.
|
113
|
-
#
|
114
|
-
# +push+ and +concat+ behave identically.
|
112
|
+
# Add +records+ to this association. Since +<<+ flattens its argument list
|
113
|
+
# and inserts each record, +push+ and +concat+ behave identically.
|
115
114
|
def concat(*records)
|
116
115
|
records = records.flatten
|
117
116
|
if owner.new_record?
|
@@ -212,9 +211,11 @@ module ActiveRecord
|
|
212
211
|
def size
|
213
212
|
if !find_target? || loaded?
|
214
213
|
target.size
|
214
|
+
elsif @association_ids
|
215
|
+
@association_ids.size
|
215
216
|
elsif !association_scope.group_values.empty?
|
216
217
|
load_target.size
|
217
|
-
elsif !association_scope.distinct_value && target.
|
218
|
+
elsif !association_scope.distinct_value && !target.empty?
|
218
219
|
unsaved_records = target.select(&:new_record?)
|
219
220
|
unsaved_records.size + count_records
|
220
221
|
else
|
@@ -231,10 +232,10 @@ module ActiveRecord
|
|
231
232
|
# loaded and you are going to fetch the records anyway it is better to
|
232
233
|
# check <tt>collection.length.zero?</tt>.
|
233
234
|
def empty?
|
234
|
-
if loaded?
|
235
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
235
236
|
size.zero?
|
236
237
|
else
|
237
|
-
|
238
|
+
target.empty? && !scope.exists?
|
238
239
|
end
|
239
240
|
end
|
240
241
|
|
@@ -301,23 +302,6 @@ module ActiveRecord
|
|
301
302
|
end
|
302
303
|
|
303
304
|
private
|
304
|
-
|
305
|
-
def find_target
|
306
|
-
scope = self.scope
|
307
|
-
return scope.to_a if skip_statement_cache?(scope)
|
308
|
-
|
309
|
-
conn = klass.connection
|
310
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
311
|
-
as = AssociationScope.create { params.bind }
|
312
|
-
target_scope.merge!(as.scope(self))
|
313
|
-
end
|
314
|
-
|
315
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
316
|
-
sc.execute(binds, conn) do |record|
|
317
|
-
set_inverse_instance(record)
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
305
|
# We have some records loaded from the database (persisted) and some that are
|
322
306
|
# in-memory (memory). The same record may be represented in the persisted array
|
323
307
|
# and in the memory array.
|
@@ -362,7 +346,6 @@ module ActiveRecord
|
|
362
346
|
add_to_target(record) do
|
363
347
|
result = insert_record(record, true, raise) {
|
364
348
|
@_was_loaded = loaded?
|
365
|
-
@association_ids = nil
|
366
349
|
}
|
367
350
|
end
|
368
351
|
raise ActiveRecord::Rollback unless result
|
@@ -395,10 +378,13 @@ module ActiveRecord
|
|
395
378
|
end
|
396
379
|
|
397
380
|
def remove_records(existing_records, records, method)
|
398
|
-
|
381
|
+
catch(:abort) do
|
382
|
+
records.each { |record| callback(:before_remove, record) }
|
383
|
+
end || return
|
399
384
|
|
400
385
|
delete_records(existing_records, method) if existing_records.any?
|
401
|
-
|
386
|
+
@target -= records
|
387
|
+
@association_ids = nil
|
402
388
|
|
403
389
|
records.each { |record| callback(:after_remove, record) }
|
404
390
|
end
|
@@ -439,7 +425,6 @@ module ActiveRecord
|
|
439
425
|
unless owner.new_record?
|
440
426
|
result &&= insert_record(record, true, raise) {
|
441
427
|
@_was_loaded = loaded?
|
442
|
-
@association_ids = nil
|
443
428
|
}
|
444
429
|
end
|
445
430
|
end
|
@@ -451,7 +436,9 @@ module ActiveRecord
|
|
451
436
|
end
|
452
437
|
|
453
438
|
def replace_on_target(record, index, skip_callbacks)
|
454
|
-
|
439
|
+
catch(:abort) do
|
440
|
+
callback(:before_add, record)
|
441
|
+
end || return unless skip_callbacks
|
455
442
|
|
456
443
|
set_inverse_instance(record)
|
457
444
|
|
@@ -462,6 +449,7 @@ module ActiveRecord
|
|
462
449
|
if index
|
463
450
|
target[index] = record
|
464
451
|
elsif @_was_loaded || !loaded?
|
452
|
+
@association_ids = nil
|
465
453
|
target << record
|
466
454
|
end
|
467
455
|
|