activerecord 5.2.3.rc1 → 6.0.0.beta1
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 +326 -696
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/associations/association.rb +35 -19
- 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/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- 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 +15 -20
- data/lib/active_record/associations/join_dependency/join_association.rb +6 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +31 -28
- data/lib/active_record/associations/preloader/association.rb +1 -2
- 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 +34 -56
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- 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 +15 -16
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +10 -14
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +14 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +40 -26
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -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/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +93 -60
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +184 -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 +74 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- 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 +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/model_schema.rb +30 -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 +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- 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 +94 -43
- data/lib/active_record/reflection.rb +46 -34
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +34 -23
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- 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 +15 -12
- data/lib/active_record/relation/query_methods.rb +26 -47
- data/lib/active_record/relation/where_clause.rb +4 -0
- 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 +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/scoping/default.rb +8 -1
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- 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 +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +2 -21
- 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 +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/arel.rb +44 -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 +67 -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/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 +63 -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 +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.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -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 +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -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/model/model_generator.rb +1 -0
- metadata +101 -23
@@ -24,7 +24,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
24
24
|
if block_given?
|
25
25
|
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
|
26
26
|
extension = Module.new(&Proc.new)
|
27
|
-
model.
|
27
|
+
model.module_parent.const_set(extension_module_name, extension)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -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].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
|
|
@@ -109,8 +109,9 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
# Add +records+ to this association.
|
113
|
-
#
|
112
|
+
# Add +records+ to this association. Returns +self+ so method calls may
|
113
|
+
# be chained. Since << flattens its argument list and inserts each record,
|
114
|
+
# +push+ and +concat+ behave identically.
|
114
115
|
def concat(*records)
|
115
116
|
records = records.flatten
|
116
117
|
if owner.new_record?
|
@@ -211,9 +212,11 @@ module ActiveRecord
|
|
211
212
|
def size
|
212
213
|
if !find_target? || loaded?
|
213
214
|
target.size
|
215
|
+
elsif @association_ids
|
216
|
+
@association_ids.size
|
214
217
|
elsif !association_scope.group_values.empty?
|
215
218
|
load_target.size
|
216
|
-
elsif !association_scope.distinct_value && target.
|
219
|
+
elsif !association_scope.distinct_value && !target.empty?
|
217
220
|
unsaved_records = target.select(&:new_record?)
|
218
221
|
unsaved_records.size + count_records
|
219
222
|
else
|
@@ -230,10 +233,10 @@ module ActiveRecord
|
|
230
233
|
# loaded and you are going to fetch the records anyway it is better to
|
231
234
|
# check <tt>collection.length.zero?</tt>.
|
232
235
|
def empty?
|
233
|
-
if loaded?
|
236
|
+
if loaded? || @association_ids
|
234
237
|
size.zero?
|
235
238
|
else
|
236
|
-
|
239
|
+
target.empty? && !scope.exists?
|
237
240
|
end
|
238
241
|
end
|
239
242
|
|
@@ -300,23 +303,6 @@ module ActiveRecord
|
|
300
303
|
end
|
301
304
|
|
302
305
|
private
|
303
|
-
|
304
|
-
def find_target
|
305
|
-
scope = self.scope
|
306
|
-
return scope.to_a if skip_statement_cache?(scope)
|
307
|
-
|
308
|
-
conn = klass.connection
|
309
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
310
|
-
as = AssociationScope.create { params.bind }
|
311
|
-
target_scope.merge!(as.scope(self))
|
312
|
-
end
|
313
|
-
|
314
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
315
|
-
sc.execute(binds, conn) do |record|
|
316
|
-
set_inverse_instance(record)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
306
|
# We have some records loaded from the database (persisted) and some that are
|
321
307
|
# in-memory (memory). The same record may be represented in the persisted array
|
322
308
|
# and in the memory array.
|
@@ -361,6 +347,7 @@ module ActiveRecord
|
|
361
347
|
add_to_target(record) do
|
362
348
|
result = insert_record(record, true, raise) {
|
363
349
|
@_was_loaded = loaded?
|
350
|
+
@association_ids = nil
|
364
351
|
}
|
365
352
|
end
|
366
353
|
raise ActiveRecord::Rollback unless result
|
@@ -396,8 +383,7 @@ module ActiveRecord
|
|
396
383
|
records.each { |record| callback(:before_remove, record) }
|
397
384
|
|
398
385
|
delete_records(existing_records, method) if existing_records.any?
|
399
|
-
|
400
|
-
@association_ids = nil
|
386
|
+
@target -= records
|
401
387
|
|
402
388
|
records.each { |record| callback(:after_remove, record) }
|
403
389
|
end
|
@@ -438,6 +424,7 @@ module ActiveRecord
|
|
438
424
|
unless owner.new_record?
|
439
425
|
result &&= insert_record(record, true, raise) {
|
440
426
|
@_was_loaded = loaded?
|
427
|
+
@association_ids = nil
|
441
428
|
}
|
442
429
|
end
|
443
430
|
end
|
@@ -460,7 +447,6 @@ module ActiveRecord
|
|
460
447
|
if index
|
461
448
|
target[index] = record
|
462
449
|
elsif @_was_loaded || !loaded?
|
463
|
-
@association_ids = nil
|
464
450
|
target << record
|
465
451
|
end
|
466
452
|
|
@@ -366,6 +366,34 @@ module ActiveRecord
|
|
366
366
|
@association.create!(attributes, &block)
|
367
367
|
end
|
368
368
|
|
369
|
+
# Add one or more records to the collection by setting their foreign keys
|
370
|
+
# to the association's primary key. Since #<< flattens its argument list and
|
371
|
+
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
372
|
+
# so method calls may be chained.
|
373
|
+
#
|
374
|
+
# class Person < ActiveRecord::Base
|
375
|
+
# has_many :pets
|
376
|
+
# end
|
377
|
+
#
|
378
|
+
# person.pets.size # => 0
|
379
|
+
# person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
|
380
|
+
# person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
|
381
|
+
# person.pets.size # => 3
|
382
|
+
#
|
383
|
+
# person.id # => 1
|
384
|
+
# person.pets
|
385
|
+
# # => [
|
386
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
387
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
388
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
389
|
+
# # ]
|
390
|
+
#
|
391
|
+
# person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
|
392
|
+
# person.pets.size # => 5
|
393
|
+
def concat(*records)
|
394
|
+
@association.concat(*records)
|
395
|
+
end
|
396
|
+
|
369
397
|
# Replaces this collection with +other_array+. This will perform a diff
|
370
398
|
# and delete/add only records that have changed.
|
371
399
|
#
|
@@ -1005,9 +1033,8 @@ module ActiveRecord
|
|
1005
1033
|
end
|
1006
1034
|
|
1007
1035
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
1008
|
-
# to the association's primary key.
|
1009
|
-
#
|
1010
|
-
# so several appends may be chained together.
|
1036
|
+
# to the association's primary key. Returns +self+, so several appends may be
|
1037
|
+
# chained together.
|
1011
1038
|
#
|
1012
1039
|
# class Person < ActiveRecord::Base
|
1013
1040
|
# has_many :pets
|
@@ -1030,7 +1057,6 @@ module ActiveRecord
|
|
1030
1057
|
end
|
1031
1058
|
alias_method :push, :<<
|
1032
1059
|
alias_method :append, :<<
|
1033
|
-
alias_method :concat, :<<
|
1034
1060
|
|
1035
1061
|
def prepend(*args)
|
1036
1062
|
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
@@ -1062,7 +1088,7 @@ module ActiveRecord
|
|
1062
1088
|
# person.pets.reload # fetches pets from the database
|
1063
1089
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1064
1090
|
def reload
|
1065
|
-
proxy_association.reload
|
1091
|
+
proxy_association.reload(true)
|
1066
1092
|
reset_scope
|
1067
1093
|
end
|
1068
1094
|
|
@@ -1099,7 +1125,7 @@ module ActiveRecord
|
|
1099
1125
|
SpawnMethods,
|
1100
1126
|
].flat_map { |klass|
|
1101
1127
|
klass.public_instance_methods(false)
|
1102
|
-
} - self.public_instance_methods(false) - [:select] + [:scoping]
|
1128
|
+
} - self.public_instance_methods(false) - [:select] + [:scoping, :values]
|
1103
1129
|
|
1104
1130
|
delegate(*delegate_methods, to: :scope)
|
1105
1131
|
|
@@ -21,20 +21,6 @@ module ActiveRecord
|
|
21
21
|
super
|
22
22
|
end
|
23
23
|
|
24
|
-
def concat_records(records)
|
25
|
-
ensure_not_nested
|
26
|
-
|
27
|
-
records = super(records, true)
|
28
|
-
|
29
|
-
if owner.new_record? && records
|
30
|
-
records.flatten.each do |record|
|
31
|
-
build_through_record(record)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
records
|
36
|
-
end
|
37
|
-
|
38
24
|
def insert_record(record, validate = true, raise = false)
|
39
25
|
ensure_not_nested
|
40
26
|
|
@@ -48,6 +34,20 @@ module ActiveRecord
|
|
48
34
|
end
|
49
35
|
|
50
36
|
private
|
37
|
+
def concat_records(records)
|
38
|
+
ensure_not_nested
|
39
|
+
|
40
|
+
records = super(records, true)
|
41
|
+
|
42
|
+
if owner.new_record? && records
|
43
|
+
records.flatten.each do |record|
|
44
|
+
build_through_record(record)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
records
|
49
|
+
end
|
50
|
+
|
51
51
|
# The through record (built with build_record) is temporarily cached
|
52
52
|
# so that it may be reused if insert_record is subsequently called.
|
53
53
|
#
|
@@ -23,35 +23,6 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def replace(record, save = true)
|
27
|
-
raise_on_type_mismatch!(record) if record
|
28
|
-
load_target
|
29
|
-
|
30
|
-
return target unless target || record
|
31
|
-
|
32
|
-
assigning_another_record = target != record
|
33
|
-
if assigning_another_record || record.has_changes_to_save?
|
34
|
-
save &&= owner.persisted?
|
35
|
-
|
36
|
-
transaction_if(save) do
|
37
|
-
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
38
|
-
|
39
|
-
if record
|
40
|
-
set_owner_attributes(record)
|
41
|
-
set_inverse_instance(record)
|
42
|
-
|
43
|
-
if save && !record.save
|
44
|
-
nullify_owner_attributes(record)
|
45
|
-
set_owner_attributes(target) if target
|
46
|
-
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
self.target = record
|
53
|
-
end
|
54
|
-
|
55
26
|
def delete(method = options[:dependent])
|
56
27
|
if load_target
|
57
28
|
case method
|
@@ -62,12 +33,39 @@ module ActiveRecord
|
|
62
33
|
target.destroy
|
63
34
|
throw(:abort) unless target.destroyed?
|
64
35
|
when :nullify
|
65
|
-
target.update_columns(
|
36
|
+
target.update_columns(nullified_owner_attributes) if target.persisted?
|
66
37
|
end
|
67
38
|
end
|
68
39
|
end
|
69
40
|
|
70
41
|
private
|
42
|
+
def replace(record, save = true)
|
43
|
+
raise_on_type_mismatch!(record) if record
|
44
|
+
|
45
|
+
return target unless load_target || record
|
46
|
+
|
47
|
+
assigning_another_record = target != record
|
48
|
+
if assigning_another_record || record.has_changes_to_save?
|
49
|
+
save &&= owner.persisted?
|
50
|
+
|
51
|
+
transaction_if(save) do
|
52
|
+
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
53
|
+
|
54
|
+
if record
|
55
|
+
set_owner_attributes(record)
|
56
|
+
set_inverse_instance(record)
|
57
|
+
|
58
|
+
if save && !record.save
|
59
|
+
nullify_owner_attributes(record)
|
60
|
+
set_owner_attributes(target) if target
|
61
|
+
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
self.target = record
|
68
|
+
end
|
71
69
|
|
72
70
|
# The reason that the save param for replace is false, if for create (not just build),
|
73
71
|
# is because the setting of the foreign keys is actually handled by the scoping when
|
@@ -6,12 +6,12 @@ module ActiveRecord
|
|
6
6
|
class HasOneThroughAssociation < HasOneAssociation #:nodoc:
|
7
7
|
include ThroughAssociation
|
8
8
|
|
9
|
-
def replace(record, save = true)
|
10
|
-
create_through_record(record, save)
|
11
|
-
self.target = record
|
12
|
-
end
|
13
|
-
|
14
9
|
private
|
10
|
+
def replace(record, save = true)
|
11
|
+
create_through_record(record, save)
|
12
|
+
self.target = record
|
13
|
+
end
|
14
|
+
|
15
15
|
def create_through_record(record, save)
|
16
16
|
ensure_not_nested
|
17
17
|
|
@@ -14,10 +14,8 @@ module ActiveRecord
|
|
14
14
|
i[column.name] = column.alias
|
15
15
|
}
|
16
16
|
}
|
17
|
-
@
|
18
|
-
h[table.node] = table.columns
|
19
|
-
[column.name, column.alias]
|
20
|
-
}
|
17
|
+
@columns_cache = tables.each_with_object({}) { |table, h|
|
18
|
+
h[table.node] = table.columns
|
21
19
|
}
|
22
20
|
end
|
23
21
|
|
@@ -25,9 +23,8 @@ module ActiveRecord
|
|
25
23
|
@tables.flat_map(&:column_aliases)
|
26
24
|
end
|
27
25
|
|
28
|
-
# An array of [column_name, alias] pairs for the table
|
29
26
|
def column_aliases(node)
|
30
|
-
@
|
27
|
+
@columns_cache[node]
|
31
28
|
end
|
32
29
|
|
33
30
|
def column_alias(node, column)
|
@@ -116,7 +113,7 @@ module ActiveRecord
|
|
116
113
|
result_set.each { |row_hash|
|
117
114
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
118
115
|
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
119
|
-
construct(parent, join_root, row_hash,
|
116
|
+
construct(parent, join_root, row_hash, seen, model_cache)
|
120
117
|
}
|
121
118
|
end
|
122
119
|
|
@@ -128,9 +125,11 @@ module ActiveRecord
|
|
128
125
|
end
|
129
126
|
|
130
127
|
protected
|
131
|
-
attr_reader :
|
128
|
+
attr_reader :join_root
|
132
129
|
|
133
130
|
private
|
131
|
+
attr_reader :alias_tracker
|
132
|
+
|
134
133
|
def aliases
|
135
134
|
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
136
135
|
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
@@ -170,7 +169,7 @@ module ActiveRecord
|
|
170
169
|
end
|
171
170
|
|
172
171
|
def table_alias_for(reflection, parent, join)
|
173
|
-
name =
|
172
|
+
name = reflection.alias_candidate(parent.table_name)
|
174
173
|
join ? "#{name}_join" : name
|
175
174
|
end
|
176
175
|
|
@@ -202,7 +201,7 @@ module ActiveRecord
|
|
202
201
|
end
|
203
202
|
end
|
204
203
|
|
205
|
-
def construct(ar_parent, parent, row,
|
204
|
+
def construct(ar_parent, parent, row, seen, model_cache)
|
206
205
|
return if ar_parent.nil?
|
207
206
|
|
208
207
|
parent.children.each do |node|
|
@@ -211,7 +210,7 @@ module ActiveRecord
|
|
211
210
|
other.loaded!
|
212
211
|
elsif ar_parent.association_cached?(node.reflection.name)
|
213
212
|
model = ar_parent.association(node.reflection.name).target
|
214
|
-
construct(model, node, row,
|
213
|
+
construct(model, node, row, seen, model_cache)
|
215
214
|
next
|
216
215
|
end
|
217
216
|
|
@@ -226,22 +225,17 @@ module ActiveRecord
|
|
226
225
|
model = seen[ar_parent.object_id][node][id]
|
227
226
|
|
228
227
|
if model
|
229
|
-
construct(model, node, row,
|
228
|
+
construct(model, node, row, seen, model_cache)
|
230
229
|
else
|
231
|
-
model = construct_model(ar_parent, node, row, model_cache, id
|
232
|
-
|
233
|
-
if node.reflection.scope &&
|
234
|
-
node.reflection.scope_for(node.base_klass.unscoped).readonly_value
|
235
|
-
model.readonly!
|
236
|
-
end
|
230
|
+
model = construct_model(ar_parent, node, row, model_cache, id)
|
237
231
|
|
238
232
|
seen[ar_parent.object_id][node][id] = model
|
239
|
-
construct(model, node, row,
|
233
|
+
construct(model, node, row, seen, model_cache)
|
240
234
|
end
|
241
235
|
end
|
242
236
|
end
|
243
237
|
|
244
|
-
def construct_model(record, node, row, model_cache, id
|
238
|
+
def construct_model(record, node, row, model_cache, id)
|
245
239
|
other = record.association(node.reflection.name)
|
246
240
|
|
247
241
|
model = model_cache[node][id] ||=
|
@@ -255,6 +249,7 @@ module ActiveRecord
|
|
255
249
|
other.target = model
|
256
250
|
end
|
257
251
|
|
252
|
+
model.readonly! if node.readonly?
|
258
253
|
model
|
259
254
|
end
|
260
255
|
end
|