activerecord 5.0.7.2 → 5.1.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.

Files changed (216) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +389 -2252
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record.rb +20 -20
  8. data/lib/active_record/aggregations.rb +244 -244
  9. data/lib/active_record/association_relation.rb +5 -5
  10. data/lib/active_record/associations.rb +1579 -1569
  11. data/lib/active_record/associations/alias_tracker.rb +1 -1
  12. data/lib/active_record/associations/association.rb +23 -15
  13. data/lib/active_record/associations/association_scope.rb +83 -81
  14. data/lib/active_record/associations/belongs_to_association.rb +0 -1
  15. data/lib/active_record/associations/builder/belongs_to.rb +16 -14
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  18. data/lib/active_record/associations/collection_association.rb +74 -241
  19. data/lib/active_record/associations/collection_proxy.rb +144 -70
  20. data/lib/active_record/associations/has_many_association.rb +15 -19
  21. data/lib/active_record/associations/has_many_through_association.rb +12 -5
  22. data/lib/active_record/associations/has_one_association.rb +22 -28
  23. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  24. data/lib/active_record/associations/join_dependency.rb +117 -115
  25. data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
  26. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  28. data/lib/active_record/associations/preloader.rb +94 -94
  29. data/lib/active_record/associations/preloader/association.rb +87 -64
  30. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  31. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  32. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  33. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +34 -41
  35. data/lib/active_record/associations/singular_association.rb +8 -25
  36. data/lib/active_record/associations/through_association.rb +3 -6
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  39. data/lib/active_record/attribute_assignment.rb +61 -61
  40. data/lib/active_record/attribute_decorators.rb +35 -13
  41. data/lib/active_record/attribute_methods.rb +56 -65
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  43. data/lib/active_record/attribute_methods/dirty.rb +216 -34
  44. data/lib/active_record/attribute_methods/primary_key.rb +78 -73
  45. data/lib/active_record/attribute_methods/read.rb +39 -35
  46. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  48. data/lib/active_record/attribute_methods/write.rb +36 -30
  49. data/lib/active_record/attribute_mutation_tracker.rb +53 -10
  50. data/lib/active_record/attribute_set.rb +9 -6
  51. data/lib/active_record/attribute_set/builder.rb +41 -49
  52. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  53. data/lib/active_record/attributes.rb +21 -21
  54. data/lib/active_record/autosave_association.rb +13 -13
  55. data/lib/active_record/base.rb +24 -22
  56. data/lib/active_record/callbacks.rb +52 -14
  57. data/lib/active_record/coders/yaml_column.rb +9 -11
  58. data/lib/active_record/collection_cache_key.rb +6 -17
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
  71. data/lib/active_record/connection_adapters/column.rb +27 -5
  72. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  73. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  94. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  97. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
  98. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
  116. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  117. data/lib/active_record/connection_handling.rb +14 -26
  118. data/lib/active_record/core.rb +110 -93
  119. data/lib/active_record/counter_cache.rb +62 -13
  120. data/lib/active_record/define_callbacks.rb +20 -0
  121. data/lib/active_record/dynamic_matchers.rb +80 -79
  122. data/lib/active_record/enum.rb +8 -6
  123. data/lib/active_record/errors.rb +58 -15
  124. data/lib/active_record/explain.rb +1 -2
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +7 -4
  127. data/lib/active_record/fixture_set/file.rb +11 -8
  128. data/lib/active_record/fixtures.rb +66 -53
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +93 -79
  131. data/lib/active_record/integration.rb +7 -7
  132. data/lib/active_record/internal_metadata.rb +3 -16
  133. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  134. data/lib/active_record/locking/optimistic.rb +64 -56
  135. data/lib/active_record/locking/pessimistic.rb +10 -1
  136. data/lib/active_record/log_subscriber.rb +29 -29
  137. data/lib/active_record/migration.rb +155 -172
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +76 -37
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/model_schema.rb +85 -119
  142. data/lib/active_record/nested_attributes.rb +200 -199
  143. data/lib/active_record/null_relation.rb +10 -33
  144. data/lib/active_record/persistence.rb +45 -38
  145. data/lib/active_record/query_cache.rb +4 -8
  146. data/lib/active_record/querying.rb +2 -3
  147. data/lib/active_record/railtie.rb +16 -17
  148. data/lib/active_record/railties/controller_runtime.rb +6 -2
  149. data/lib/active_record/railties/databases.rake +125 -140
  150. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  151. data/lib/active_record/readonly_attributes.rb +2 -2
  152. data/lib/active_record/reflection.rb +79 -96
  153. data/lib/active_record/relation.rb +72 -115
  154. data/lib/active_record/relation/batches.rb +87 -58
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  156. data/lib/active_record/relation/calculations.rb +154 -160
  157. data/lib/active_record/relation/delegation.rb +30 -29
  158. data/lib/active_record/relation/finder_methods.rb +195 -226
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder.rb +92 -89
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  165. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  166. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +247 -295
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +79 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/result.rb +29 -31
  174. data/lib/active_record/runtime_registry.rb +3 -3
  175. data/lib/active_record/sanitization.rb +182 -197
  176. data/lib/active_record/schema.rb +3 -3
  177. data/lib/active_record/schema_dumper.rb +14 -37
  178. data/lib/active_record/schema_migration.rb +3 -3
  179. data/lib/active_record/scoping.rb +9 -10
  180. data/lib/active_record/scoping/default.rb +87 -91
  181. data/lib/active_record/scoping/named.rb +16 -28
  182. data/lib/active_record/secure_token.rb +2 -2
  183. data/lib/active_record/statement_cache.rb +13 -15
  184. data/lib/active_record/store.rb +31 -32
  185. data/lib/active_record/suppressor.rb +2 -1
  186. data/lib/active_record/table_metadata.rb +9 -5
  187. data/lib/active_record/tasks/database_tasks.rb +72 -65
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  191. data/lib/active_record/timestamp.rb +39 -25
  192. data/lib/active_record/touch_later.rb +1 -2
  193. data/lib/active_record/transactions.rb +98 -110
  194. data/lib/active_record/type.rb +17 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +9 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/serialized.rb +8 -8
  199. data/lib/active_record/type/text.rb +9 -0
  200. data/lib/active_record/type/time.rb +0 -1
  201. data/lib/active_record/type/type_map.rb +11 -15
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type_caster.rb +2 -2
  204. data/lib/active_record/type_caster/connection.rb +8 -6
  205. data/lib/active_record/type_caster/map.rb +3 -1
  206. data/lib/active_record/validations.rb +4 -4
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/presence.rb +2 -2
  209. data/lib/active_record/validations/uniqueness.rb +8 -39
  210. data/lib/active_record/version.rb +1 -1
  211. data/lib/rails/generators/active_record.rb +4 -4
  212. data/lib/rails/generators/active_record/migration.rb +2 -2
  213. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  215. metadata +22 -13
  216. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -12,14 +12,7 @@ module ActiveRecord
12
12
  when :restrict_with_error
13
13
  if load_target
14
14
  record = owner.class.human_attribute_name(reflection.name).downcase
15
- message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
16
- if message
17
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
18
- The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
19
- Please use `:'restrict_dependent_destroy.has_one'` instead.
20
- MESSAGE
21
- end
22
- owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
15
+ owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record)
23
16
  throw(:abort)
24
17
  end
25
18
 
@@ -32,10 +25,10 @@ module ActiveRecord
32
25
  raise_on_type_mismatch!(record) if record
33
26
  load_target
34
27
 
35
- return self.target if !(target || record)
28
+ return target unless target || record
36
29
 
37
30
  assigning_another_record = target != record
38
- if assigning_another_record || record.changed?
31
+ if assigning_another_record || record.has_changes_to_save?
39
32
  save &&= owner.persisted?
40
33
 
41
34
  transaction_if(save) do
@@ -60,12 +53,12 @@ module ActiveRecord
60
53
  def delete(method = options[:dependent])
61
54
  if load_target
62
55
  case method
63
- when :delete
64
- target.delete
65
- when :destroy
66
- target.destroy
67
- when :nullify
68
- target.update_columns(reflection.foreign_key => nil) if target.persisted?
56
+ when :delete
57
+ target.delete
58
+ when :destroy
59
+ target.destroy
60
+ when :nullify
61
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
69
62
  end
70
63
  end
71
64
  end
@@ -82,18 +75,19 @@ module ActiveRecord
82
75
 
83
76
  def remove_target!(method)
84
77
  case method
85
- when :delete
86
- target.delete
87
- when :destroy
88
- target.destroy
89
- else
90
- nullify_owner_attributes(target)
91
-
92
- if target.persisted? && owner.persisted? && !target.save
93
- set_owner_attributes(target)
94
- raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
95
- "The record failed to save after its foreign key was set to nil."
96
- end
78
+ when :delete
79
+ target.delete
80
+ when :destroy
81
+ target.destroy
82
+ else
83
+ nullify_owner_attributes(target)
84
+ remove_inverse_instance(target)
85
+
86
+ if target.persisted? && owner.persisted? && !target.save
87
+ set_owner_attributes(target)
88
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " \
89
+ "The record failed to save after its foreign key was set to nil."
90
+ end
97
91
  end
98
92
  end
99
93
 
@@ -15,13 +15,17 @@ module ActiveRecord
15
15
  ensure_not_nested
16
16
 
17
17
  through_proxy = owner.association(through_reflection.name)
18
- through_record = through_proxy.send(:load_target)
18
+ through_record = through_proxy.load_target
19
19
 
20
20
  if through_record && !record
21
21
  through_record.destroy
22
22
  elsif record
23
23
  attributes = construct_join_attributes(record)
24
24
 
25
+ if through_record && through_record.destroyed?
26
+ through_record = through_proxy.tap(&:reload).target
27
+ end
28
+
25
29
  if through_record
26
30
  through_record.update(attributes)
27
31
  elsif owner.new_record?
@@ -1,18 +1,18 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class JoinDependency # :nodoc:
4
- autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
5
- autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
4
+ autoload :JoinBase, "active_record/associations/join_dependency/join_base"
5
+ autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
6
6
 
7
7
  class Aliases # :nodoc:
8
8
  def initialize(tables)
9
9
  @tables = tables
10
- @alias_cache = tables.each_with_object({}) { |table,h|
11
- h[table.node] = table.columns.each_with_object({}) { |column,i|
10
+ @alias_cache = tables.each_with_object({}) { |table, h|
11
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
12
12
  i[column.name] = column.alias
13
13
  }
14
14
  }
15
- @name_and_alias_cache = tables.each_with_object({}) { |table,h|
15
+ @name_and_alias_cache = tables.each_with_object({}) { |table, h|
16
16
  h[table.node] = table.columns.map { |column|
17
17
  [column.name, column.alias]
18
18
  }
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  @alias_cache[node][column]
33
33
  end
34
34
 
35
- class Table < Struct.new(:node, :columns) # :nodoc:
35
+ Table = Struct.new(:node, :columns) do # :nodoc:
36
36
  def table
37
37
  Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
38
38
  end
@@ -62,7 +62,7 @@ module ActiveRecord
62
62
  walk_tree assoc, hash
63
63
  end
64
64
  when Hash
65
- associations.each do |k,v|
65
+ associations.each do |k, v|
66
66
  cache = hash[k] ||= {}
67
67
  walk_tree v, cache
68
68
  end
@@ -92,8 +92,9 @@ module ActiveRecord
92
92
  # associations # => [:appointments]
93
93
  # joins # => []
94
94
  #
95
- def initialize(base, associations, joins)
95
+ def initialize(base, associations, joins, eager_loading: true)
96
96
  @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
97
+ @eager_loading = eager_loading
97
98
  tree = self.class.make_tree associations
98
99
  @join_root = JoinBase.new base, build(tree, base)
99
100
  @join_root.children.each { |child| construct_tables! @join_root, child }
@@ -125,8 +126,8 @@ module ActiveRecord
125
126
  end
126
127
 
127
128
  def aliases
128
- Aliases.new join_root.each_with_index.map { |join_part,i|
129
- columns = join_part.column_names.each_with_index.map { |column_name,j|
129
+ Aliases.new join_root.each_with_index.map { |join_part, i|
130
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
130
131
  Aliases::Column.new column_name, "t#{i}_r#{j}"
131
132
  }
132
133
  Aliases::Table.new(join_part, columns)
@@ -142,7 +143,7 @@ module ActiveRecord
142
143
  }
143
144
  }
144
145
 
145
- model_cache = Hash.new { |h,klass| h[klass] = {} }
146
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
146
147
  parents = model_cache[join_root]
147
148
  column_aliases = aliases.column_aliases join_root
148
149
 
@@ -153,7 +154,7 @@ module ActiveRecord
153
154
  class_name: join_root.base_klass.name
154
155
  }
155
156
 
156
- message_bus.instrument('instantiation.active_record', payload) do
157
+ message_bus.instrument("instantiation.active_record", payload) do
157
158
  result_set.each { |row_hash|
158
159
  parent_key = primary_key ? row_hash[primary_key] : row_hash
159
160
  parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
@@ -166,139 +167,140 @@ module ActiveRecord
166
167
 
167
168
  private
168
169
 
169
- def make_constraints(parent, child, tables, join_type)
170
- chain = child.reflection.chain
171
- foreign_table = parent.table
172
- foreign_klass = parent.base_klass
173
- child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, child.reflection.scope_chain, chain)
174
- end
170
+ def make_constraints(parent, child, tables, join_type)
171
+ chain = child.reflection.chain
172
+ foreign_table = parent.table
173
+ foreign_klass = parent.base_klass
174
+ child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, chain)
175
+ end
175
176
 
176
- def make_outer_joins(parent, child)
177
- tables = table_aliases_for(parent, child)
178
- join_type = Arel::Nodes::OuterJoin
179
- info = make_constraints parent, child, tables, join_type
177
+ def make_outer_joins(parent, child)
178
+ tables = table_aliases_for(parent, child)
179
+ join_type = Arel::Nodes::OuterJoin
180
+ info = make_constraints parent, child, tables, join_type
180
181
 
181
- [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
182
- end
182
+ [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
183
+ end
183
184
 
184
- def make_left_outer_joins(parent, child)
185
- tables = child.tables
186
- join_type = Arel::Nodes::OuterJoin
187
- info = make_constraints parent, child, tables, join_type
185
+ def make_left_outer_joins(parent, child)
186
+ tables = child.tables
187
+ join_type = Arel::Nodes::OuterJoin
188
+ info = make_constraints parent, child, tables, join_type
188
189
 
189
- [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
190
- end
190
+ [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
191
+ end
191
192
 
192
- def make_inner_joins(parent, child)
193
- tables = child.tables
194
- join_type = Arel::Nodes::InnerJoin
195
- info = make_constraints parent, child, tables, join_type
193
+ def make_inner_joins(parent, child)
194
+ tables = child.tables
195
+ join_type = Arel::Nodes::InnerJoin
196
+ info = make_constraints parent, child, tables, join_type
196
197
 
197
- [info] + child.children.flat_map { |c| make_inner_joins(child, c) }
198
- end
198
+ [info] + child.children.flat_map { |c| make_inner_joins(child, c) }
199
+ end
199
200
 
200
- def table_aliases_for(parent, node)
201
- node.reflection.chain.map { |reflection|
202
- alias_tracker.aliased_table_for(
203
- reflection.table_name,
204
- table_alias_for(reflection, parent, reflection != node.reflection)
205
- )
206
- }
207
- end
201
+ def table_aliases_for(parent, node)
202
+ node.reflection.chain.map { |reflection|
203
+ alias_tracker.aliased_table_for(
204
+ reflection.table_name,
205
+ table_alias_for(reflection, parent, reflection != node.reflection)
206
+ )
207
+ }
208
+ end
208
209
 
209
- def construct_tables!(parent, node)
210
- node.tables = table_aliases_for(parent, node)
211
- node.children.each { |child| construct_tables! node, child }
212
- end
210
+ def construct_tables!(parent, node)
211
+ node.tables = table_aliases_for(parent, node)
212
+ node.children.each { |child| construct_tables! node, child }
213
+ end
213
214
 
214
- def table_alias_for(reflection, parent, join)
215
- name = "#{reflection.plural_name}_#{parent.table_name}"
216
- name << "_join" if join
217
- name
218
- end
215
+ def table_alias_for(reflection, parent, join)
216
+ name = "#{reflection.plural_name}_#{parent.table_name}"
217
+ name << "_join" if join
218
+ name
219
+ end
219
220
 
220
- def walk(left, right)
221
- intersection, missing = right.children.map { |node1|
222
- [left.children.find { |node2| node1.match? node2 }, node1]
223
- }.partition(&:first)
221
+ def walk(left, right)
222
+ intersection, missing = right.children.map { |node1|
223
+ [left.children.find { |node2| node1.match? node2 }, node1]
224
+ }.partition(&:first)
224
225
 
225
- ojs = missing.flat_map { |_,n| make_outer_joins left, n }
226
- intersection.flat_map { |l,r| walk l, r }.concat ojs
227
- end
226
+ ojs = missing.flat_map { |_, n| make_outer_joins left, n }
227
+ intersection.flat_map { |l, r| walk l, r }.concat ojs
228
+ end
228
229
 
229
- def find_reflection(klass, name)
230
- klass._reflect_on_association(name) or
231
- raise ConfigurationError, "Can't join '#{ klass.name }' to association named '#{ name }'; perhaps you misspelled it?"
232
- end
230
+ def find_reflection(klass, name)
231
+ klass._reflect_on_association(name) ||
232
+ raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
233
+ end
233
234
 
234
- def build(associations, base_klass)
235
- associations.map do |name, right|
236
- reflection = find_reflection base_klass, name
237
- reflection.check_validity!
238
- reflection.check_eager_loadable!
235
+ def build(associations, base_klass)
236
+ associations.map do |name, right|
237
+ reflection = find_reflection base_klass, name
238
+ reflection.check_validity!
239
+ reflection.check_eager_loadable!
239
240
 
240
- if reflection.polymorphic?
241
- raise EagerLoadPolymorphicError.new(reflection)
242
- end
241
+ if reflection.polymorphic?
242
+ next unless @eager_loading
243
+ raise EagerLoadPolymorphicError.new(reflection)
244
+ end
243
245
 
244
- JoinAssociation.new reflection, build(right, reflection.klass)
246
+ JoinAssociation.new reflection, build(right, reflection.klass)
247
+ end.compact
245
248
  end
246
- end
247
249
 
248
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
249
- return if ar_parent.nil?
250
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
251
+ return if ar_parent.nil?
252
+
253
+ parent.children.each do |node|
254
+ if node.reflection.collection?
255
+ other = ar_parent.association(node.reflection.name)
256
+ other.loaded!
257
+ elsif ar_parent.association_cached?(node.reflection.name)
258
+ model = ar_parent.association(node.reflection.name).target
259
+ construct(model, node, row, rs, seen, model_cache, aliases)
260
+ next
261
+ end
250
262
 
251
- parent.children.each do |node|
252
- if node.reflection.collection?
253
- other = ar_parent.association(node.reflection.name)
254
- other.loaded!
255
- elsif ar_parent.association_cached?(node.reflection.name)
256
- model = ar_parent.association(node.reflection.name).target
257
- construct(model, node, row, rs, seen, model_cache, aliases)
258
- next
259
- end
263
+ key = aliases.column_alias(node, node.primary_key)
264
+ id = row[key]
265
+ if id.nil?
266
+ nil_association = ar_parent.association(node.reflection.name)
267
+ nil_association.loaded!
268
+ next
269
+ end
260
270
 
261
- key = aliases.column_alias(node, node.primary_key)
262
- id = row[key]
263
- if id.nil?
264
- nil_association = ar_parent.association(node.reflection.name)
265
- nil_association.loaded!
266
- next
267
- end
271
+ model = seen[ar_parent.object_id][node.base_klass][id]
268
272
 
269
- model = seen[ar_parent.object_id][node.base_klass][id]
273
+ if model
274
+ construct(model, node, row, rs, seen, model_cache, aliases)
275
+ else
276
+ model = construct_model(ar_parent, node, row, model_cache, id, aliases)
270
277
 
271
- if model
272
- construct(model, node, row, rs, seen, model_cache, aliases)
273
- else
274
- model = construct_model(ar_parent, node, row, model_cache, id, aliases)
278
+ if node.reflection.scope_for(node.base_klass).readonly_value
279
+ model.readonly!
280
+ end
275
281
 
276
- if node.reflection.scope_for(node.base_klass).readonly_value
277
- model.readonly!
282
+ seen[ar_parent.object_id][node.base_klass][id] = model
283
+ construct(model, node, row, rs, seen, model_cache, aliases)
278
284
  end
279
-
280
- seen[ar_parent.object_id][node.base_klass][id] = model
281
- construct(model, node, row, rs, seen, model_cache, aliases)
282
285
  end
283
286
  end
284
- end
285
287
 
286
- def construct_model(record, node, row, model_cache, id, aliases)
287
- other = record.association(node.reflection.name)
288
+ def construct_model(record, node, row, model_cache, id, aliases)
289
+ other = record.association(node.reflection.name)
288
290
 
289
- model = model_cache[node][id] ||=
290
- node.instantiate(row, aliases.column_aliases(node)) do |m|
291
- other.set_inverse_instance(m)
291
+ model = model_cache[node][id] ||=
292
+ node.instantiate(row, aliases.column_aliases(node)) do |m|
293
+ other.set_inverse_instance(m)
294
+ end
295
+
296
+ if node.reflection.collection?
297
+ other.target.push(model)
298
+ else
299
+ other.target = model
292
300
  end
293
301
 
294
- if node.reflection.collection?
295
- other.target.push(model)
296
- else
297
- other.target = model
302
+ model
298
303
  end
299
-
300
- model
301
- end
302
304
  end
303
305
  end
304
306
  end
@@ -1,4 +1,4 @@
1
- require 'active_record/associations/join_dependency/join_part'
1
+ require "active_record/associations/join_dependency/join_part"
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
@@ -23,14 +23,11 @@ module ActiveRecord
23
23
 
24
24
  JoinInformation = Struct.new :joins, :binds
25
25
 
26
- def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
26
+ def join_constraints(foreign_table, foreign_klass, node, join_type, tables, chain)
27
27
  joins = []
28
28
  binds = []
29
29
  tables = tables.reverse
30
30
 
31
- scope_chain_index = 0
32
- scope_chain = scope_chain.reverse
33
-
34
31
  # The chain starts with the target table, but we want to end with it here (makes
35
32
  # more sense in this context), so we reverse
36
33
  chain.reverse_each do |reflection|
@@ -44,7 +41,7 @@ module ActiveRecord
44
41
  constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
45
42
 
46
43
  predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
47
- scope_chain_items = scope_chain[scope_chain_index].map do |item|
44
+ scope_chain_items = reflection.scopes.map do |item|
48
45
  if item.is_a?(Relation)
49
46
  item
50
47
  else
@@ -52,19 +49,25 @@ module ActiveRecord
52
49
  .instance_exec(node, &item)
53
50
  end
54
51
  end
55
- scope_chain_index += 1
56
-
57
- relation = ActiveRecord::Relation.create(klass, table, predicate_builder)
58
- current_scope = klass.current_scope
59
52
 
60
53
  klass_scope =
61
- if current_scope && current_scope.empty_scope?
62
- relation
54
+ if klass.current_scope
55
+ klass.current_scope.clone.tap { |scope|
56
+ scope.joins_values = []
57
+ }
63
58
  else
59
+ relation = ActiveRecord::Relation.create(
60
+ klass,
61
+ table,
62
+ predicate_builder,
63
+ )
64
64
  klass.send(:build_default_scope, relation)
65
65
  end
66
+ scope_chain_items.concat [klass_scope].compact
66
67
 
67
- rel = scope_chain_items.inject(klass_scope || scope_chain_items.shift, &:merge!)
68
+ rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
69
+ left.merge right
70
+ end
68
71
 
69
72
  if rel && !rel.arel.constraints.empty?
70
73
  binds += rel.bound_attributes