activerecord 5.1.0 → 5.2.3

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 (261) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +596 -450
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record.rb +11 -4
  8. data/lib/active_record/aggregations.rb +6 -5
  9. data/lib/active_record/association_relation.rb +7 -5
  10. data/lib/active_record/associations.rb +77 -85
  11. data/lib/active_record/associations/alias_tracker.rb +23 -32
  12. data/lib/active_record/associations/association.rb +49 -35
  13. data/lib/active_record/associations/association_scope.rb +55 -55
  14. data/lib/active_record/associations/belongs_to_association.rb +30 -11
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  16. data/lib/active_record/associations/builder/association.rb +4 -7
  17. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  18. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +2 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +66 -53
  24. data/lib/active_record/associations/collection_proxy.rb +30 -73
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +13 -2
  27. data/lib/active_record/associations/has_many_through_association.rb +37 -19
  28. data/lib/active_record/associations/has_one_association.rb +14 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +52 -96
  31. data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
  32. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  33. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/preloader/association.rb +53 -92
  36. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +27 -12
  39. data/lib/active_record/attribute_assignment.rb +2 -5
  40. data/lib/active_record/attribute_decorators.rb +3 -2
  41. data/lib/active_record/attribute_methods.rb +65 -24
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +33 -216
  44. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  45. data/lib/active_record/attribute_methods/query.rb +2 -0
  46. data/lib/active_record/attribute_methods/read.rb +9 -3
  47. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  49. data/lib/active_record/attribute_methods/write.rb +22 -19
  50. data/lib/active_record/attributes.rb +7 -6
  51. data/lib/active_record/autosave_association.rb +15 -13
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +12 -6
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +2 -0
  56. data/lib/active_record/collection_cache_key.rb +15 -11
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
  70. data/lib/active_record/connection_adapters/column.rb +4 -2
  71. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  73. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
  118. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
  127. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  128. data/lib/active_record/connection_handling.rb +4 -2
  129. data/lib/active_record/core.rb +41 -61
  130. data/lib/active_record/counter_cache.rb +20 -15
  131. data/lib/active_record/define_callbacks.rb +5 -3
  132. data/lib/active_record/dynamic_matchers.rb +9 -9
  133. data/lib/active_record/enum.rb +18 -13
  134. data/lib/active_record/errors.rb +60 -15
  135. data/lib/active_record/explain.rb +3 -1
  136. data/lib/active_record/explain_registry.rb +2 -0
  137. data/lib/active_record/explain_subscriber.rb +2 -0
  138. data/lib/active_record/fixture_set/file.rb +2 -0
  139. data/lib/active_record/fixtures.rb +67 -60
  140. data/lib/active_record/gem_version.rb +4 -2
  141. data/lib/active_record/inheritance.rb +49 -19
  142. data/lib/active_record/integration.rb +58 -19
  143. data/lib/active_record/internal_metadata.rb +2 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  145. data/lib/active_record/locking/optimistic.rb +30 -42
  146. data/lib/active_record/locking/pessimistic.rb +10 -7
  147. data/lib/active_record/log_subscriber.rb +46 -4
  148. data/lib/active_record/migration.rb +189 -139
  149. data/lib/active_record/migration/command_recorder.rb +11 -9
  150. data/lib/active_record/migration/compatibility.rb +81 -29
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +74 -58
  153. data/lib/active_record/nested_attributes.rb +18 -6
  154. data/lib/active_record/no_touching.rb +3 -1
  155. data/lib/active_record/null_relation.rb +2 -0
  156. data/lib/active_record/persistence.rb +199 -54
  157. data/lib/active_record/query_cache.rb +8 -10
  158. data/lib/active_record/querying.rb +5 -3
  159. data/lib/active_record/railtie.rb +62 -6
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +2 -0
  162. data/lib/active_record/railties/databases.rake +48 -38
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +137 -207
  165. data/lib/active_record/relation.rb +132 -207
  166. data/lib/active_record/relation/batches.rb +32 -17
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +66 -25
  169. data/lib/active_record/relation/delegation.rb +45 -29
  170. data/lib/active_record/relation/finder_methods.rb +76 -85
  171. data/lib/active_record/relation/from_clause.rb +2 -8
  172. data/lib/active_record/relation/merger.rb +53 -23
  173. data/lib/active_record/relation/predicate_builder.rb +60 -79
  174. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  176. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  177. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  178. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  179. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  181. data/lib/active_record/relation/query_attribute.rb +28 -2
  182. data/lib/active_record/relation/query_methods.rb +135 -103
  183. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  184. data/lib/active_record/relation/spawn_methods.rb +4 -2
  185. data/lib/active_record/relation/where_clause.rb +65 -67
  186. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  187. data/lib/active_record/result.rb +2 -0
  188. data/lib/active_record/runtime_registry.rb +2 -0
  189. data/lib/active_record/sanitization.rb +129 -121
  190. data/lib/active_record/schema.rb +4 -2
  191. data/lib/active_record/schema_dumper.rb +36 -26
  192. data/lib/active_record/schema_migration.rb +2 -0
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/scoping/default.rb +10 -7
  195. data/lib/active_record/scoping/named.rb +40 -12
  196. data/lib/active_record/secure_token.rb +2 -0
  197. data/lib/active_record/serialization.rb +2 -0
  198. data/lib/active_record/statement_cache.rb +22 -12
  199. data/lib/active_record/store.rb +3 -1
  200. data/lib/active_record/suppressor.rb +2 -0
  201. data/lib/active_record/table_metadata.rb +12 -3
  202. data/lib/active_record/tasks/database_tasks.rb +38 -26
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +13 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +32 -27
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type.rb +4 -1
  211. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  212. data/lib/active_record/type/date.rb +2 -0
  213. data/lib/active_record/type/date_time.rb +2 -0
  214. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  215. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  216. data/lib/active_record/type/internal/timezone.rb +2 -0
  217. data/lib/active_record/type/json.rb +30 -0
  218. data/lib/active_record/type/serialized.rb +6 -0
  219. data/lib/active_record/type/text.rb +2 -0
  220. data/lib/active_record/type/time.rb +2 -0
  221. data/lib/active_record/type/type_map.rb +2 -0
  222. data/lib/active_record/type/unsigned_integer.rb +2 -0
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/type_caster/connection.rb +2 -0
  225. data/lib/active_record/type_caster/map.rb +3 -1
  226. data/lib/active_record/validations.rb +2 -0
  227. data/lib/active_record/validations/absence.rb +2 -0
  228. data/lib/active_record/validations/associated.rb +2 -0
  229. data/lib/active_record/validations/length.rb +2 -0
  230. data/lib/active_record/validations/presence.rb +2 -0
  231. data/lib/active_record/validations/uniqueness.rb +36 -6
  232. data/lib/active_record/version.rb +2 -0
  233. data/lib/rails/generators/active_record.rb +3 -1
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration.rb +2 -0
  237. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  238. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  239. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. metadata +24 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -124
  256. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,51 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/string/conversions"
2
4
 
3
5
  module ActiveRecord
4
6
  module Associations
5
7
  # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
6
8
  class AliasTracker # :nodoc:
7
- attr_reader :aliases
8
-
9
- def self.create(connection, initial_table, type_caster)
10
- aliases = Hash.new(0)
11
- aliases[initial_table] = 1
12
- new connection, aliases, type_caster
13
- end
14
-
15
- def self.create_with_joins(connection, initial_table, joins, type_caster)
9
+ def self.create(connection, initial_table, joins)
16
10
  if joins.empty?
17
- create(connection, initial_table, type_caster)
11
+ aliases = Hash.new(0)
18
12
  else
19
13
  aliases = Hash.new { |h, k|
20
14
  h[k] = initial_count_for(connection, k, joins)
21
15
  }
22
- aliases[initial_table] = 1
23
- new connection, aliases, type_caster
24
16
  end
17
+ aliases[initial_table] = 1
18
+ new(connection, aliases)
25
19
  end
26
20
 
27
21
  def self.initial_count_for(connection, name, table_joins)
28
- # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
29
- quoted_name = connection.quote_table_name(name).downcase
22
+ quoted_name = nil
30
23
 
31
24
  counts = table_joins.map do |join|
32
25
  if join.is_a?(Arel::Nodes::StringJoin)
26
+ # quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
27
+ quoted_name ||= connection.quote_table_name(name)
28
+
33
29
  # Table names + table aliases
34
- join.left.downcase.scan(
35
- /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
30
+ join.left.scan(
31
+ /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
36
32
  ).size
37
- elsif join.respond_to? :left
38
- join.left.table_name == name ? 1 : 0
33
+ elsif join.is_a?(Arel::Nodes::Join)
34
+ join.left.name == name ? 1 : 0
35
+ elsif join.is_a?(Hash)
36
+ join[name]
39
37
  else
40
- # this branch is reached by two tests:
41
- #
42
- # activerecord/test/cases/associations/cascaded_eager_loading_test.rb:37
43
- # with :posts
44
- #
45
- # activerecord/test/cases/associations/eager_test.rb:1133
46
- # with :comments
47
- #
48
- 0
38
+ raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
49
39
  end
50
40
  end
51
41
 
@@ -53,17 +43,16 @@ module ActiveRecord
53
43
  end
54
44
 
55
45
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
56
- def initialize(connection, aliases, type_caster)
46
+ def initialize(connection, aliases)
57
47
  @aliases = aliases
58
48
  @connection = connection
59
- @type_caster = type_caster
60
49
  end
61
50
 
62
- def aliased_table_for(table_name, aliased_name)
51
+ def aliased_table_for(table_name, aliased_name, type_caster)
63
52
  if aliases[table_name].zero?
64
53
  # If it's zero, we can have our table_name
65
54
  aliases[table_name] = 1
66
- Arel::Table.new(table_name, type_caster: @type_caster)
55
+ Arel::Table.new(table_name, type_caster: type_caster)
67
56
  else
68
57
  # Otherwise, we need to use an alias
69
58
  aliased_name = @connection.table_alias_for(aliased_name)
@@ -76,10 +65,12 @@ module ActiveRecord
76
65
  else
77
66
  aliased_name
78
67
  end
79
- Arel::Table.new(table_name, type_caster: @type_caster).alias(table_alias)
68
+ Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
80
69
  end
81
70
  end
82
71
 
72
+ attr_reader :aliases
73
+
83
74
  private
84
75
 
85
76
  def truncate(name)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/array/wrap"
2
4
 
3
5
  module ActiveRecord
@@ -17,7 +19,6 @@ module ActiveRecord
17
19
  # HasManyThroughAssociation + ThroughAssociation
18
20
  class Association #:nodoc:
19
21
  attr_reader :owner, :target, :reflection
20
- attr_accessor :inversed
21
22
 
22
23
  delegate :options, to: :reflection
23
24
 
@@ -30,14 +31,6 @@ module ActiveRecord
30
31
  reset_scope
31
32
  end
32
33
 
33
- # Returns the name of the table of the associated class:
34
- #
35
- # post.comments.aliased_table_name # => "comments"
36
- #
37
- def aliased_table_name
38
- klass.table_name
39
- end
40
-
41
34
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
42
35
  def reset
43
36
  @loaded = false
@@ -73,7 +66,7 @@ module ActiveRecord
73
66
  #
74
67
  # Note that if the target has not been loaded, it is not considered stale.
75
68
  def stale_target?
76
- !inversed && loaded? && @stale_state != stale_state
69
+ !@inversed && loaded? && @stale_state != stale_state
77
70
  end
78
71
 
79
72
  # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@@ -94,7 +87,7 @@ module ActiveRecord
94
87
  # actually gets built.
95
88
  def association_scope
96
89
  if klass
97
- @association_scope ||= AssociationScope.scope(self, klass.connection)
90
+ @association_scope ||= AssociationScope.scope(self)
98
91
  end
99
92
  end
100
93
 
@@ -104,23 +97,32 @@ module ActiveRecord
104
97
 
105
98
  # Set the inverse association, if possible
106
99
  def set_inverse_instance(record)
107
- if invertible_for?(record)
108
- inverse = record.association(inverse_reflection_for(record).name)
109
- inverse.target = owner
110
- inverse.inversed = true
100
+ if inverse = inverse_association_for(record)
101
+ inverse.inversed_from(owner)
102
+ end
103
+ record
104
+ end
105
+
106
+ def set_inverse_instance_from_queries(record)
107
+ if inverse = inverse_association_for(record)
108
+ inverse.inversed_from_queries(owner)
111
109
  end
112
110
  record
113
111
  end
114
112
 
115
113
  # Remove the inverse association, if possible
116
114
  def remove_inverse_instance(record)
117
- if invertible_for?(record)
118
- inverse = record.association(inverse_reflection_for(record).name)
119
- inverse.target = nil
120
- inverse.inversed = false
115
+ if inverse = inverse_association_for(record)
116
+ inverse.inversed_from(nil)
121
117
  end
122
118
  end
123
119
 
120
+ def inversed_from(record)
121
+ self.target = record
122
+ @inversed = !!record
123
+ end
124
+ alias :inversed_from_queries :inversed_from
125
+
124
126
  # Returns the class of the target. belongs_to polymorphic overrides this to look at the
125
127
  # polymorphic_type field on the owner.
126
128
  def klass
@@ -130,7 +132,17 @@ module ActiveRecord
130
132
  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
131
133
  # through association's scope)
132
134
  def target_scope
133
- AssociationRelation.create(klass, klass.arel_table, klass.predicate_builder, self).merge!(klass.all)
135
+ AssociationRelation.create(klass, self).merge!(klass.all)
136
+ end
137
+
138
+ def extensions
139
+ extensions = klass.default_extensions | reflection.extensions
140
+
141
+ if reflection.scope
142
+ extensions |= reflection.scope_for(klass.unscoped, owner).extensions
143
+ end
144
+
145
+ extensions
134
146
  end
135
147
 
136
148
  # Loads the \target if needed and returns it.
@@ -152,17 +164,9 @@ module ActiveRecord
152
164
  reset
153
165
  end
154
166
 
155
- def interpolate(sql, record = nil)
156
- if sql.respond_to?(:to_proc)
157
- owner.instance_exec(record, &sql)
158
- else
159
- sql
160
- end
161
- end
162
-
163
- # We can't dump @reflection since it contains the scope proc
167
+ # We can't dump @reflection and @through_reflection since it contains the scope proc
164
168
  def marshal_dump
165
- ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
169
+ ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
166
170
  [@reflection.name, ivars]
167
171
  end
168
172
 
@@ -177,8 +181,8 @@ module ActiveRecord
177
181
  skip_assign = [reflection.foreign_key, reflection.type].compact
178
182
  assigned_keys = record.changed_attribute_names_to_save
179
183
  assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
180
- attributes = create_scope.except(*(assigned_keys - skip_assign))
181
- record.assign_attributes(attributes)
184
+ attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
185
+ record.send(:_assign_attributes, attributes) if attributes.any?
182
186
  set_inverse_instance(record)
183
187
  end
184
188
 
@@ -191,6 +195,9 @@ module ActiveRecord
191
195
  end
192
196
 
193
197
  private
198
+ def scope_for_create
199
+ scope.scope_for_create
200
+ end
194
201
 
195
202
  def find_target?
196
203
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
@@ -202,8 +209,8 @@ module ActiveRecord
202
209
  if (reflection.has_one? || reflection.collection?) && !options[:through]
203
210
  attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
204
211
 
205
- if reflection.options[:as]
206
- attributes[reflection.type] = owner.class.base_class.name
212
+ if reflection.type
213
+ attributes[reflection.type] = owner.class.polymorphic_name
207
214
  end
208
215
  end
209
216
 
@@ -241,6 +248,12 @@ module ActiveRecord
241
248
  end
242
249
  end
243
250
 
251
+ def inverse_association_for(record)
252
+ if invertible_for?(record)
253
+ record.association(inverse_reflection_for(record).name)
254
+ end
255
+ end
256
+
244
257
  # Can be redefined by subclasses, notably polymorphic belongs_to
245
258
  # The record parameter is necessary to support polymorphic inverses as we must check for
246
259
  # the association in the specific class of the record.
@@ -270,11 +283,12 @@ module ActiveRecord
270
283
  def build_record(attributes)
271
284
  reflection.build_association(attributes) do |record|
272
285
  initialize_attributes(record, attributes)
286
+ yield(record) if block_given?
273
287
  end
274
288
  end
275
289
 
276
290
  # Returns true if statement cache should be skipped on the association reader.
277
- def skip_statement_cache?
291
+ def skip_statement_cache?(scope)
278
292
  reflection.has_scope? ||
279
293
  scope.eager_loading? ||
280
294
  klass.scope_attributes? ||
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class AssociationScope #:nodoc:
4
- def self.scope(association, connection)
5
- INSTANCE.scope(association, connection)
6
+ def self.scope(association)
7
+ INSTANCE.scope(association)
6
8
  end
7
9
 
8
10
  def self.create(&block)
@@ -16,20 +18,15 @@ module ActiveRecord
16
18
 
17
19
  INSTANCE = create
18
20
 
19
- def scope(association, connection)
21
+ def scope(association)
20
22
  klass = association.klass
21
23
  reflection = association.reflection
22
24
  scope = klass.unscoped
23
25
  owner = association.owner
24
- alias_tracker = AliasTracker.create connection, association.klass.table_name, klass.type_caster
25
- chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
26
-
27
- scope.extending! Array(reflection.options[:extend])
28
- add_constraints(scope, owner, reflection, chain_head, chain_tail)
29
- end
26
+ chain = get_chain(reflection, association, scope.alias_tracker)
30
27
 
31
- def join_type
32
- Arel::Nodes::InnerJoin
28
+ scope.extending! reflection.extensions
29
+ add_constraints(scope, owner, chain)
33
30
  end
34
31
 
35
32
  def self.get_bind_values(owner, chain)
@@ -38,12 +35,12 @@ module ActiveRecord
38
35
 
39
36
  binds << last_reflection.join_id_for(owner)
40
37
  if last_reflection.type
41
- binds << owner.class.base_class.name
38
+ binds << owner.class.polymorphic_name
42
39
  end
43
40
 
44
41
  chain.each_cons(2).each do |reflection, next_reflection|
45
42
  if reflection.type
46
- binds << next_reflection.klass.base_class.name
43
+ binds << next_reflection.klass.polymorphic_name
47
44
  end
48
45
  end
49
46
  binds
@@ -57,20 +54,21 @@ module ActiveRecord
57
54
 
58
55
  private
59
56
  def join(table, constraint)
60
- table.create_join(table, table.create_on(constraint), join_type)
57
+ table.create_join(table, table.create_on(constraint))
61
58
  end
62
59
 
63
- def last_chain_scope(scope, table, reflection, owner)
60
+ def last_chain_scope(scope, reflection, owner)
64
61
  join_keys = reflection.join_keys
65
62
  key = join_keys.key
66
63
  foreign_key = join_keys.foreign_key
67
64
 
65
+ table = reflection.aliased_table
68
66
  value = transform_value(owner[foreign_key])
69
- scope = scope.where(table.name => { key => value })
67
+ scope = apply_scope(scope, table, key, value)
70
68
 
71
69
  if reflection.type
72
- polymorphic_type = transform_value(owner.class.base_class.name)
73
- scope = scope.where(table.name => { reflection.type => polymorphic_type })
70
+ polymorphic_type = transform_value(owner.class.polymorphic_name)
71
+ scope = apply_scope(scope, table, reflection.type, polymorphic_type)
74
72
  end
75
73
 
76
74
  scope
@@ -80,28 +78,29 @@ module ActiveRecord
80
78
  value_transformation.call(value)
81
79
  end
82
80
 
83
- def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
81
+ def next_chain_scope(scope, reflection, next_reflection)
84
82
  join_keys = reflection.join_keys
85
83
  key = join_keys.key
86
84
  foreign_key = join_keys.foreign_key
87
85
 
86
+ table = reflection.aliased_table
87
+ foreign_table = next_reflection.aliased_table
88
88
  constraint = table[key].eq(foreign_table[foreign_key])
89
89
 
90
90
  if reflection.type
91
- value = transform_value(next_reflection.klass.base_class.name)
92
- scope = scope.where(table.name => { reflection.type => value })
91
+ value = transform_value(next_reflection.klass.polymorphic_name)
92
+ scope = apply_scope(scope, table, reflection.type, value)
93
93
  end
94
94
 
95
- scope = scope.joins(join(foreign_table, constraint))
95
+ scope.joins!(join(foreign_table, constraint))
96
96
  end
97
97
 
98
98
  class ReflectionProxy < SimpleDelegator # :nodoc:
99
- attr_accessor :next
100
- attr_reader :alias_name
99
+ attr_reader :aliased_table
101
100
 
102
- def initialize(reflection, alias_name)
101
+ def initialize(reflection, aliased_table)
103
102
  super(reflection)
104
- @alias_name = alias_name
103
+ @aliased_table = aliased_table
105
104
  end
106
105
 
107
106
  def all_includes; nil; end
@@ -109,39 +108,34 @@ module ActiveRecord
109
108
 
110
109
  def get_chain(reflection, association, tracker)
111
110
  name = reflection.name
112
- runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
113
- previous_reflection = runtime_reflection
111
+ chain = [Reflection::RuntimeReflection.new(reflection, association)]
114
112
  reflection.chain.drop(1).each do |refl|
115
- alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
116
- proxy = ReflectionProxy.new(refl, alias_name)
117
- previous_reflection.next = proxy
118
- previous_reflection = proxy
113
+ aliased_table = tracker.aliased_table_for(
114
+ refl.table_name,
115
+ refl.alias_candidate(name),
116
+ refl.klass.type_caster
117
+ )
118
+ chain << ReflectionProxy.new(refl, aliased_table)
119
119
  end
120
- [runtime_reflection, previous_reflection]
120
+ chain
121
121
  end
122
122
 
123
- def add_constraints(scope, owner, refl, chain_head, chain_tail)
124
- owner_reflection = chain_tail
125
- table = owner_reflection.alias_name
126
- scope = last_chain_scope(scope, table, owner_reflection, owner)
123
+ def add_constraints(scope, owner, chain)
124
+ scope = last_chain_scope(scope, chain.last, owner)
127
125
 
128
- reflection = chain_head
129
- while reflection
130
- table = reflection.alias_name
131
- next_reflection = reflection.next
132
-
133
- unless reflection == chain_tail
134
- foreign_table = next_reflection.alias_name
135
- scope = next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
136
- end
126
+ chain.each_cons(2) do |reflection, next_reflection|
127
+ scope = next_chain_scope(scope, reflection, next_reflection)
128
+ end
137
129
 
130
+ chain_head = chain.first
131
+ chain.reverse_each do |reflection|
138
132
  # Exclude the scope of the association itself, because that
139
133
  # was already merged in the #scope method.
140
134
  reflection.constraints.each do |scope_chain_item|
141
- item = eval_scope(reflection.klass, table, scope_chain_item, owner)
135
+ item = eval_scope(reflection, scope_chain_item, owner)
142
136
 
143
- if scope_chain_item == refl.scope
144
- scope.merge! item.except(:where, :includes)
137
+ if scope_chain_item == chain_head.scope
138
+ scope.merge! item.except(:where, :includes, :unscope, :order)
145
139
  end
146
140
 
147
141
  reflection.all_includes do
@@ -150,18 +144,24 @@ module ActiveRecord
150
144
 
151
145
  scope.unscope!(*item.unscope_values)
152
146
  scope.where_clause += item.where_clause
153
- scope.order_values |= item.order_values
147
+ scope.order_values = item.order_values | scope.order_values
154
148
  end
155
-
156
- reflection = next_reflection
157
149
  end
158
150
 
159
151
  scope
160
152
  end
161
153
 
162
- def eval_scope(klass, table, scope, owner)
163
- predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
164
- ActiveRecord::Relation.create(klass, table, predicate_builder).instance_exec(owner, &scope)
154
+ def apply_scope(scope, table, key, value)
155
+ if scope.table == table
156
+ scope.where!(key => value)
157
+ else
158
+ scope.where!(table.name => { key => value })
159
+ end
160
+ end
161
+
162
+ def eval_scope(reflection, scope, owner)
163
+ relation = reflection.build_scope(reflection.aliased_table)
164
+ relation.instance_exec(owner, &scope) || relation
165
165
  end
166
166
  end
167
167
  end