activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,75 +1,80 @@
1
- require 'active_support/core_ext/string/conversions'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/conversions"
2
4
 
3
5
  module ActiveRecord
4
6
  module Associations
5
- # Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
6
- # ActiveRecord::Associations::ThroughAssociationScope
7
+ # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
7
8
  class AliasTracker # :nodoc:
8
- attr_reader :aliases, :table_joins, :connection
9
-
10
- # table_joins is an array of arel joins which might conflict with the aliases we assign here
11
- def initialize(connection = ActiveRecord::Model.connection, table_joins = [])
12
- @aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
13
- @table_joins = table_joins
14
- @connection = connection
9
+ def self.create(connection, initial_table, joins)
10
+ if joins.empty?
11
+ aliases = Hash.new(0)
12
+ else
13
+ aliases = Hash.new { |h, k|
14
+ h[k] = initial_count_for(connection, k, joins)
15
+ }
16
+ end
17
+ aliases[initial_table] = 1
18
+ new(connection, aliases)
15
19
  end
16
20
 
17
- def aliased_table_for(table_name, aliased_name = nil)
18
- table_alias = aliased_name_for(table_name, aliased_name)
21
+ def self.initial_count_for(connection, name, table_joins)
22
+ quoted_name = nil
19
23
 
20
- if table_alias == table_name
21
- Arel::Table.new(table_name)
22
- else
23
- Arel::Table.new(table_name).alias(table_alias)
24
+ counts = table_joins.map do |join|
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
+
29
+ # Table names + table aliases
30
+ join.left.scan(
31
+ /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
32
+ ).size
33
+ elsif join.is_a?(Arel::Nodes::Join)
34
+ join.left.name == name ? 1 : 0
35
+ elsif join.is_a?(Hash)
36
+ join[name]
37
+ else
38
+ raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
39
+ end
24
40
  end
41
+
42
+ counts.sum
25
43
  end
26
44
 
27
- def aliased_name_for(table_name, aliased_name = nil)
28
- aliased_name ||= table_name
45
+ # table_joins is an array of arel joins which might conflict with the aliases we assign here
46
+ def initialize(connection, aliases)
47
+ @aliases = aliases
48
+ @connection = connection
49
+ end
29
50
 
51
+ def aliased_table_for(table_name, aliased_name, type_caster)
30
52
  if aliases[table_name].zero?
31
53
  # If it's zero, we can have our table_name
32
54
  aliases[table_name] = 1
33
- table_name
55
+ Arel::Table.new(table_name, type_caster: type_caster)
34
56
  else
35
57
  # Otherwise, we need to use an alias
36
- aliased_name = connection.table_alias_for(aliased_name)
58
+ aliased_name = @connection.table_alias_for(aliased_name)
37
59
 
38
60
  # Update the count
39
61
  aliases[aliased_name] += 1
40
62
 
41
- if aliases[aliased_name] > 1
63
+ table_alias = if aliases[aliased_name] > 1
42
64
  "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
43
65
  else
44
66
  aliased_name
45
67
  end
68
+ Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
46
69
  end
47
70
  end
48
71
 
49
- private
50
-
51
- def initial_count_for(name)
52
- return 0 if Arel::Table === table_joins
53
-
54
- # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
55
- quoted_name = connection.quote_table_name(name).downcase
72
+ attr_reader :aliases
56
73
 
57
- counts = table_joins.map do |join|
58
- if join.is_a?(Arel::Nodes::StringJoin)
59
- # Table names + table aliases
60
- join.left.downcase.scan(
61
- /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
62
- ).size
63
- else
64
- join.left.table_name == name ? 1 : 0
65
- end
66
- end
67
-
68
- counts.sum
69
- end
74
+ private
70
75
 
71
76
  def truncate(name)
72
- name.slice(0, connection.table_alias_length - 2)
77
+ name.slice(0, @connection.table_alias_length - 2)
73
78
  end
74
79
  end
75
80
  end
@@ -1,5 +1,6 @@
1
- require 'active_support/core_ext/array/wrap'
2
- require 'active_support/core_ext/object/inclusion'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/wrap"
3
4
 
4
5
  module ActiveRecord
5
6
  module Associations
@@ -9,44 +10,33 @@ module ActiveRecord
9
10
  #
10
11
  # Association
11
12
  # SingularAssociation
12
- # HasOneAssociation
13
+ # HasOneAssociation + ForeignAssociation
13
14
  # HasOneThroughAssociation + ThroughAssociation
14
15
  # BelongsToAssociation
15
16
  # BelongsToPolymorphicAssociation
16
17
  # CollectionAssociation
17
- # HasAndBelongsToManyAssociation
18
- # HasManyAssociation
18
+ # HasManyAssociation + ForeignAssociation
19
19
  # HasManyThroughAssociation + ThroughAssociation
20
20
  class Association #:nodoc:
21
21
  attr_reader :owner, :target, :reflection
22
22
 
23
- delegate :options, :to => :reflection
23
+ delegate :options, to: :reflection
24
24
 
25
25
  def initialize(owner, reflection)
26
26
  reflection.check_validity!
27
27
 
28
- @target = nil
29
28
  @owner, @reflection = owner, reflection
30
- @updated = false
31
29
 
32
30
  reset
33
31
  reset_scope
34
32
  end
35
33
 
36
- # Returns the name of the table of the related class:
37
- #
38
- # post.comments.aliased_table_name # => "comments"
39
- #
40
- def aliased_table_name
41
- reflection.klass.table_name
42
- end
43
-
44
34
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
45
35
  def reset
46
36
  @loaded = false
47
- IdentityMap.remove(target) if IdentityMap.enabled? && target
48
37
  @target = nil
49
38
  @stale_state = nil
39
+ @inversed = false
50
40
  end
51
41
 
52
42
  # Reloads the \target and returns +self+ on success.
@@ -64,18 +54,19 @@ module ActiveRecord
64
54
 
65
55
  # Asserts the \target has been loaded setting the \loaded flag to +true+.
66
56
  def loaded!
67
- @loaded = true
57
+ @loaded = true
68
58
  @stale_state = stale_state
59
+ @inversed = false
69
60
  end
70
61
 
71
62
  # The target is stale if the target no longer points to the record(s) that the
72
63
  # relevant foreign_key(s) refers to. If stale, the association accessor method
73
64
  # on the owner will reload the target. It's up to subclasses to implement the
74
- # state_state method if relevant.
65
+ # stale_state method if relevant.
75
66
  #
76
67
  # Note that if the target has not been loaded, it is not considered stale.
77
68
  def stale_target?
78
- loaded? && @stale_state != stale_state
69
+ !@inversed && loaded? && @stale_state != stale_state
79
70
  end
80
71
 
81
72
  # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@@ -84,19 +75,19 @@ module ActiveRecord
84
75
  loaded!
85
76
  end
86
77
 
87
- def scoped
88
- target_scope.merge(association_scope)
78
+ def scope
79
+ target_scope.merge!(association_scope)
89
80
  end
90
81
 
91
82
  # The scope for this association.
92
83
  #
93
84
  # Note that the association_scope is merged into the target_scope only when the
94
- # scoped method is called. This is because at that point the call may be surrounded
85
+ # scope method is called. This is because at that point the call may be surrounded
95
86
  # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
96
87
  # actually gets built.
97
88
  def association_scope
98
89
  if klass
99
- @association_scope ||= AssociationScope.new(self).scope
90
+ @association_scope ||= AssociationScope.scope(self)
100
91
  end
101
92
  end
102
93
 
@@ -106,13 +97,33 @@ module ActiveRecord
106
97
 
107
98
  # Set the inverse association, if possible
108
99
  def set_inverse_instance(record)
109
- if record && invertible_for?(record)
110
- inverse = record.association(inverse_reflection_for(record).name)
111
- inverse.target = owner
100
+ if inverse = inverse_association_for(record)
101
+ inverse.inversed_from(owner)
112
102
  end
103
+ record
113
104
  end
114
105
 
115
- # This class of the target. belongs_to polymorphic overrides this to look at the
106
+ def set_inverse_instance_from_queries(record)
107
+ if inverse = inverse_association_for(record)
108
+ inverse.inversed_from_queries(owner)
109
+ end
110
+ record
111
+ end
112
+
113
+ # Remove the inverse association, if possible
114
+ def remove_inverse_instance(record)
115
+ if inverse = inverse_association_for(record)
116
+ inverse.inversed_from(nil)
117
+ end
118
+ end
119
+
120
+ def inversed_from(record)
121
+ self.target = record
122
+ @inversed = !!record
123
+ end
124
+ alias :inversed_from_queries :inversed_from
125
+
126
+ # Returns the class of the target. belongs_to polymorphic overrides this to look at the
116
127
  # polymorphic_type field on the owner.
117
128
  def klass
118
129
  reflection.klass
@@ -121,7 +132,17 @@ module ActiveRecord
121
132
  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
122
133
  # through association's scope)
123
134
  def target_scope
124
- klass.scoped
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
125
146
  end
126
147
 
127
148
  # Loads the \target if needed and returns it.
@@ -129,43 +150,54 @@ module ActiveRecord
129
150
  # This method is abstract in the sense that it relies on +find_target+,
130
151
  # which is expected to be provided by descendants.
131
152
  #
132
- # If the \target is stale(the target no longer points to the record(s) that the
133
- # relevant foreign_key(s) refers to.), force reload the \target.
134
- #
135
- # Otherwise if the \target is already \loaded it is just returned. Thus, you can
136
- # call +load_target+ unconditionally to get the \target.
153
+ # If the \target is already \loaded it is just returned. Thus, you can call
154
+ # +load_target+ unconditionally to get the \target.
137
155
  #
138
156
  # ActiveRecord::RecordNotFound is rescued within the method, and it is
139
157
  # not reraised. The proxy is \reset and +nil+ is the return value.
140
158
  def load_target
141
- if (@stale_state && stale_target?) || find_target?
142
- begin
143
- if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
144
- @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
145
- elsif @stale_state && stale_target?
146
- @target = find_target
147
- end
148
- rescue NameError
149
- nil
150
- ensure
151
- @target ||= find_target
152
- end
153
- end
159
+ @target = find_target if (@stale_state && stale_target?) || find_target?
160
+
154
161
  loaded! unless loaded?
155
162
  target
156
163
  rescue ActiveRecord::RecordNotFound
157
164
  reset
158
165
  end
159
166
 
160
- def interpolate(sql, record = nil)
161
- if sql.respond_to?(:to_proc) && !sql.is_a?(Hash)
162
- owner.send(:instance_exec, record, &sql)
163
- else
164
- sql
165
- end
167
+ # We can't dump @reflection and @through_reflection since it contains the scope proc
168
+ def marshal_dump
169
+ ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
170
+ [@reflection.name, ivars]
171
+ end
172
+
173
+ def marshal_load(data)
174
+ reflection_name, ivars = data
175
+ ivars.each { |name, val| instance_variable_set(name, val) }
176
+ @reflection = @owner.class._reflect_on_association(reflection_name)
177
+ end
178
+
179
+ def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
180
+ except_from_scope_attributes ||= {}
181
+ skip_assign = [reflection.foreign_key, reflection.type].compact
182
+ assigned_keys = record.changed_attribute_names_to_save
183
+ assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
184
+ attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
185
+ record.send(:_assign_attributes, attributes) if attributes.any?
186
+ set_inverse_instance(record)
187
+ end
188
+
189
+ def create(attributes = {}, &block)
190
+ _create_record(attributes, &block)
191
+ end
192
+
193
+ def create!(attributes = {}, &block)
194
+ _create_record(attributes, true, &block)
166
195
  end
167
196
 
168
197
  private
198
+ def scope_for_create
199
+ scope.scope_for_create
200
+ end
169
201
 
170
202
  def find_target?
171
203
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
@@ -174,11 +206,11 @@ module ActiveRecord
174
206
  def creation_attributes
175
207
  attributes = {}
176
208
 
177
- if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
209
+ if (reflection.has_one? || reflection.collection?) && !options[:through]
178
210
  attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
179
211
 
180
- if reflection.options[:as]
181
- attributes[reflection.type] = owner.class.base_class.name
212
+ if reflection.type
213
+ attributes[reflection.type] = owner.class.polymorphic_name
182
214
  end
183
215
  end
184
216
 
@@ -190,13 +222,14 @@ module ActiveRecord
190
222
  creation_attributes.each { |key, value| record[key] = value }
191
223
  end
192
224
 
193
- # Should be true if there is a foreign key present on the owner which
225
+ # Returns true if there is a foreign key present on the owner which
194
226
  # references the target. This is used to determine whether we can load
195
227
  # the target if the owner is currently a new record (and therefore
196
- # without a key).
228
+ # without a key). If the owner is a new record then foreign_key must
229
+ # be present in order to load target.
197
230
  #
198
231
  # Currently implemented by belongs_to (vanilla and polymorphic) and
199
- # has_one/has_many :through associations which go through a belongs_to
232
+ # has_one/has_many :through associations which go through a belongs_to.
200
233
  def foreign_key_present?
201
234
  false
202
235
  end
@@ -204,10 +237,20 @@ module ActiveRecord
204
237
  # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
205
238
  # the kind of the class of the associated objects. Meant to be used as
206
239
  # a sanity check when you are about to assign an associated record.
207
- def raise_on_type_mismatch(record)
208
- unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
209
- message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
210
- raise ActiveRecord::AssociationTypeMismatch, message
240
+ def raise_on_type_mismatch!(record)
241
+ unless record.is_a?(reflection.klass)
242
+ fresh_class = reflection.class_name.safe_constantize
243
+ unless fresh_class && record.is_a?(fresh_class)
244
+ message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
245
+ "got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
246
+ raise ActiveRecord::AssociationTypeMismatch, message
247
+ end
248
+ end
249
+ end
250
+
251
+ def inverse_association_for(record)
252
+ if invertible_for?(record)
253
+ record.association(inverse_reflection_for(record).name)
211
254
  end
212
255
  end
213
256
 
@@ -218,29 +261,38 @@ module ActiveRecord
218
261
  reflection.inverse_of
219
262
  end
220
263
 
221
- # Is this association invertible? Can be redefined by subclasses.
264
+ # Returns true if inverse association on the given record needs to be set.
265
+ # This method is redefined by subclasses.
222
266
  def invertible_for?(record)
223
- inverse_reflection_for(record)
267
+ foreign_key_for?(record) && inverse_reflection_for(record)
268
+ end
269
+
270
+ # Returns true if record contains the foreign_key
271
+ def foreign_key_for?(record)
272
+ record.has_attribute?(reflection.foreign_key)
224
273
  end
225
274
 
226
275
  # This should be implemented to return the values of the relevant key(s) on the owner,
227
- # so that when state_state is different from the value stored on the last find_target,
276
+ # so that when stale_state is different from the value stored on the last find_target,
228
277
  # the target is stale.
229
278
  #
230
- # This is only relevant to certain associations, which is why it returns nil by default.
279
+ # This is only relevant to certain associations, which is why it returns +nil+ by default.
231
280
  def stale_state
232
281
  end
233
282
 
234
- def association_class
235
- @reflection.klass
283
+ def build_record(attributes)
284
+ reflection.build_association(attributes) do |record|
285
+ initialize_attributes(record, attributes)
286
+ yield(record) if block_given?
287
+ end
236
288
  end
237
289
 
238
- def build_record(attributes, options)
239
- reflection.build_association(attributes, options) do |record|
240
- skip_assign = [reflection.foreign_key, reflection.type].compact
241
- attributes = create_scope.except(*(record.changed - skip_assign))
242
- record.assign_attributes(attributes, :without_protection => true)
243
- end
290
+ # Returns true if statement cache should be skipped on the association reader.
291
+ def skip_statement_cache?(scope)
292
+ reflection.has_scope? ||
293
+ scope.eager_loading? ||
294
+ klass.scope_attributes? ||
295
+ reflection.source_reflection.active_record.default_scopes.any?
244
296
  end
245
297
  end
246
298
  end