activerecord 4.2.0 → 5.0.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -7,8 +7,8 @@ module ActiveRecord
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- class_attribute :_reflections
11
- class_attribute :aggregate_reflections
10
+ class_attribute :_reflections, instance_writer: false
11
+ class_attribute :aggregate_reflections, instance_writer: false
12
12
  self._reflections = {}
13
13
  self.aggregate_reflections = {}
14
14
  end
@@ -32,6 +32,7 @@ module ActiveRecord
32
32
  end
33
33
 
34
34
  def self.add_reflection(ar, name, reflection)
35
+ ar.clear_reflections_cache
35
36
  ar._reflections = ar._reflections.merge(name.to_s => reflection)
36
37
  end
37
38
 
@@ -39,9 +40,9 @@ module ActiveRecord
39
40
  ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
40
41
  end
41
42
 
42
- # \Reflection enables interrogating of Active Record classes and objects
43
- # about their associations and aggregations. This information can,
44
- # for example, be used in a form builder that takes an Active Record object
43
+ # \Reflection enables the ability to examine the associations and aggregations of
44
+ # Active Record classes and objects. This information, for example,
45
+ # can be used in a form builder that takes an Active Record object
45
46
  # and creates input fields for all of the attributes depending on their type
46
47
  # and displays the associations to other objects.
47
48
  #
@@ -61,22 +62,27 @@ module ActiveRecord
61
62
  aggregate_reflections[aggregation.to_s]
62
63
  end
63
64
 
64
- # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
65
+ # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
65
66
  #
66
67
  # Account.reflections # => {"balance" => AggregateReflection}
67
68
  #
68
- # @api public
69
69
  def reflections
70
- ref = {}
71
- _reflections.each do |name, reflection|
72
- parent_name, parent_reflection = reflection.parent_reflection
73
- if parent_name
74
- ref[parent_name] = parent_reflection
75
- else
76
- ref[name] = reflection
70
+ @__reflections ||= begin
71
+ ref = {}
72
+
73
+ _reflections.each do |name, reflection|
74
+ parent_reflection = reflection.parent_reflection
75
+
76
+ if parent_reflection
77
+ parent_name = parent_reflection.name
78
+ ref[parent_name.to_s] = parent_reflection
79
+ else
80
+ ref[name] = reflection
81
+ end
77
82
  end
83
+
84
+ ref
78
85
  end
79
- ref
80
86
  end
81
87
 
82
88
  # Returns an array of AssociationReflection objects for all the
@@ -89,10 +95,10 @@ module ActiveRecord
89
95
  # Account.reflect_on_all_associations # returns an array of all associations
90
96
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
91
97
  #
92
- # @api public
93
98
  def reflect_on_all_associations(macro = nil)
94
99
  association_reflections = reflections.values
95
- macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
100
+ association_reflections.select! { |reflection| reflection.macro == macro } if macro
101
+ association_reflections
96
102
  end
97
103
 
98
104
  # Returns the AssociationReflection object for the +association+ (use the symbol).
@@ -100,27 +106,42 @@ module ActiveRecord
100
106
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
101
107
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
102
108
  #
103
- # @api public
104
109
  def reflect_on_association(association)
105
110
  reflections[association.to_s]
106
111
  end
107
112
 
108
- # @api private
109
113
  def _reflect_on_association(association) #:nodoc:
110
114
  _reflections[association.to_s]
111
115
  end
112
116
 
113
117
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
114
- #
115
- # @api public
116
118
  def reflect_on_all_autosave_associations
117
119
  reflections.values.select { |reflection| reflection.options[:autosave] }
118
120
  end
121
+
122
+ def clear_reflections_cache # :nodoc:
123
+ @__reflections = nil
124
+ end
119
125
  end
120
126
 
121
- # Holds all the methods that are shared between MacroReflection, AssociationReflection
122
- # and ThroughReflection
127
+ # Holds all the methods that are shared between MacroReflection and ThroughReflection.
128
+ #
129
+ # AbstractReflection
130
+ # MacroReflection
131
+ # AggregateReflection
132
+ # AssociationReflection
133
+ # HasManyReflection
134
+ # HasOneReflection
135
+ # BelongsToReflection
136
+ # HasAndBelongsToManyReflection
137
+ # ThroughReflection
138
+ # PolymorphicReflection
139
+ # RuntimeReflection
123
140
  class AbstractReflection # :nodoc:
141
+ def through_reflection?
142
+ false
143
+ end
144
+
124
145
  def table_name
125
146
  klass.table_name
126
147
  end
@@ -149,29 +170,87 @@ module ActiveRecord
149
170
 
150
171
  JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
151
172
 
152
- def join_keys(assoc_klass)
173
+ def join_keys(association_klass)
153
174
  JoinKeys.new(foreign_key, active_record_primary_key)
154
175
  end
155
176
 
156
- def source_macro
157
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
158
- ActiveRecord::Base.source_macro is deprecated and will be removed
159
- without replacement.
160
- MSG
177
+ def constraints
178
+ scope_chain.flatten
179
+ end
180
+
181
+ def counter_cache_column
182
+ if belongs_to?
183
+ if options[:counter_cache] == true
184
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
185
+ elsif options[:counter_cache]
186
+ options[:counter_cache].to_s
187
+ end
188
+ else
189
+ options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
190
+ end
191
+ end
192
+
193
+ def inverse_of
194
+ return unless inverse_name
161
195
 
162
- macro
196
+ @inverse_of ||= klass._reflect_on_association inverse_name
197
+ end
198
+
199
+ def check_validity_of_inverse!
200
+ unless polymorphic?
201
+ if has_inverse? && inverse_of.nil?
202
+ raise InverseOfAssociationNotFoundError.new(self)
203
+ end
204
+ end
205
+ end
206
+
207
+ # This shit is nasty. We need to avoid the following situation:
208
+ #
209
+ # * An associated record is deleted via record.destroy
210
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
211
+ # :counter_cache options which points back at our owner. So they update the
212
+ # counter cache.
213
+ # * In which case, we must make sure to *not* update the counter cache, or else
214
+ # it will be decremented twice.
215
+ #
216
+ # Hence this method.
217
+ def inverse_which_updates_counter_cache
218
+ return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
219
+ @inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
220
+ inverse.counter_cache_column == counter_cache_column
221
+ end
222
+ end
223
+ alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
224
+
225
+ def inverse_updates_counter_in_memory?
226
+ inverse_of && inverse_which_updates_counter_cache == inverse_of
227
+ end
228
+
229
+ # Returns whether a counter cache should be used for this association.
230
+ #
231
+ # The counter_cache option must be given on either the owner or inverse
232
+ # association, and the column must be present on the owner.
233
+ def has_cached_counter?
234
+ options[:counter_cache] ||
235
+ inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
236
+ !!active_record.columns_hash[counter_cache_column]
237
+ end
238
+
239
+ def counter_must_be_updated_by_has_many?
240
+ !inverse_updates_counter_in_memory? && has_cached_counter?
241
+ end
242
+
243
+ def alias_candidate(name)
244
+ "#{plural_name}_#{name}"
245
+ end
246
+
247
+ def chain
248
+ collect_join_chain
163
249
  end
164
250
  end
251
+
165
252
  # Base class for AggregateReflection and AssociationReflection. Objects of
166
253
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
167
- #
168
- # MacroReflection
169
- # AssociationReflection
170
- # AggregateReflection
171
- # HasManyReflection
172
- # HasOneReflection
173
- # BelongsToReflection
174
- # ThroughReflection
175
254
  class MacroReflection < AbstractReflection
176
255
  # Returns the name of the macro.
177
256
  #
@@ -196,7 +275,7 @@ module ActiveRecord
196
275
  @scope = scope
197
276
  @options = options
198
277
  @active_record = active_record
199
- @klass = options[:class]
278
+ @klass = options[:anonymous_class]
200
279
  @plural_name = active_record.pluralize_table_names ?
201
280
  name.to_s.pluralize : name.to_s
202
281
  end
@@ -204,7 +283,7 @@ module ActiveRecord
204
283
  def autosave=(autosave)
205
284
  @automatic_inverse_of = false
206
285
  @options[:autosave] = autosave
207
- _, parent_reflection = self.parent_reflection
286
+ parent_reflection = self.parent_reflection
208
287
  if parent_reflection
209
288
  parent_reflection.autosave = autosave
210
289
  end
@@ -272,7 +351,7 @@ module ActiveRecord
272
351
  end
273
352
 
274
353
  attr_reader :type, :foreign_type
275
- attr_accessor :parent_reflection # [:name, Reflection]
354
+ attr_accessor :parent_reflection # Reflection
276
355
 
277
356
  def initialize(name, scope, options, active_record)
278
357
  super
@@ -303,7 +382,7 @@ module ActiveRecord
303
382
  end
304
383
 
305
384
  def foreign_key
306
- @foreign_key ||= options[:foreign_key] || derive_foreign_key
385
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
307
386
  end
308
387
 
309
388
  def association_foreign_key
@@ -319,37 +398,18 @@ module ActiveRecord
319
398
  @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
320
399
  end
321
400
 
322
- def counter_cache_column
323
- if options[:counter_cache] == true
324
- "#{active_record.name.demodulize.underscore.pluralize}_count"
325
- elsif options[:counter_cache]
326
- options[:counter_cache].to_s
327
- end
328
- end
329
-
330
401
  def check_validity!
331
402
  check_validity_of_inverse!
332
403
  end
333
404
 
334
- def check_validity_of_inverse!
335
- unless polymorphic?
336
- if has_inverse? && inverse_of.nil?
337
- raise InverseOfAssociationNotFoundError.new(self)
338
- end
339
- end
340
- end
341
-
342
405
  def check_preloadable!
343
406
  return unless scope
344
407
 
345
408
  if scope.arity > 0
346
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
409
+ raise ArgumentError, <<-MSG.squish
347
410
  The association scope '#{name}' is instance dependent (the scope
348
- block takes an argument). Preloading happens before the individual
349
- instances are created. This means that there is no instance being
350
- passed to the association scope. This will most likely result in
351
- broken or incorrect behavior. Joining, Preloading and eager loading
352
- of these associations is deprecated and will be removed in the future.
411
+ block takes an argument). Preloading instance dependent scopes is
412
+ not supported.
353
413
  MSG
354
414
  end
355
415
  end
@@ -369,10 +429,16 @@ module ActiveRecord
369
429
 
370
430
  # A chain of reflections from this one back to the owner. For more see the explanation in
371
431
  # ThroughReflection.
372
- def chain
432
+ def collect_join_chain
373
433
  [self]
374
434
  end
375
435
 
436
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
437
+ # SQL queries on associations.
438
+ def clear_association_scope_cache # :nodoc:
439
+ @association_scope_cache.clear
440
+ end
441
+
376
442
  def nested?
377
443
  false
378
444
  end
@@ -383,14 +449,12 @@ module ActiveRecord
383
449
  scope ? [[scope]] : [[]]
384
450
  end
385
451
 
386
- def has_inverse?
387
- inverse_name
452
+ def has_scope?
453
+ scope
388
454
  end
389
455
 
390
- def inverse_of
391
- return unless inverse_name
392
-
393
- @inverse_of ||= klass._reflect_on_association inverse_name
456
+ def has_inverse?
457
+ inverse_name
394
458
  end
395
459
 
396
460
  def polymorphic_inverse_of(associated_class)
@@ -434,28 +498,7 @@ module ActiveRecord
434
498
  # Returns +true+ if +self+ is a +has_one+ reflection.
435
499
  def has_one?; false; end
436
500
 
437
- def association_class
438
- case macro
439
- when :belongs_to
440
- if polymorphic?
441
- Associations::BelongsToPolymorphicAssociation
442
- else
443
- Associations::BelongsToAssociation
444
- end
445
- when :has_many
446
- if options[:through]
447
- Associations::HasManyThroughAssociation
448
- else
449
- Associations::HasManyAssociation
450
- end
451
- when :has_one
452
- if options[:through]
453
- Associations::HasOneThroughAssociation
454
- else
455
- Associations::HasOneAssociation
456
- end
457
- end
458
- end
501
+ def association_class; raise NotImplementedError; end
459
502
 
460
503
  def polymorphic?
461
504
  options[:polymorphic]
@@ -464,6 +507,18 @@ module ActiveRecord
464
507
  VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
465
508
  INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
466
509
 
510
+ def add_as_source(seed)
511
+ seed
512
+ end
513
+
514
+ def add_as_polymorphic_through(reflection, seed)
515
+ seed + [PolymorphicReflection.new(self, reflection)]
516
+ end
517
+
518
+ def add_as_through(seed)
519
+ seed + [self]
520
+ end
521
+
467
522
  protected
468
523
 
469
524
  def actual_source_reflection # FIXME: this is a horrible name
@@ -473,14 +528,7 @@ module ActiveRecord
473
528
  private
474
529
 
475
530
  def calculate_constructable(macro, options)
476
- case macro
477
- when :belongs_to
478
- !polymorphic?
479
- when :has_one
480
- !options[:through]
481
- else
482
- true
483
- end
531
+ true
484
532
  end
485
533
 
486
534
  # Attempts to find the inverse association name automatically.
@@ -496,10 +544,10 @@ module ActiveRecord
496
544
  end
497
545
  end
498
546
 
499
- # returns either nil or the inverse association name that it finds.
547
+ # returns either false or the inverse association name that it finds.
500
548
  def automatic_inverse_of
501
549
  if can_find_inverse_of_automatically?(self)
502
- inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
550
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
503
551
 
504
552
  begin
505
553
  reflection = klass._reflect_on_association(inverse_name)
@@ -573,42 +621,66 @@ module ActiveRecord
573
621
  end
574
622
 
575
623
  class HasManyReflection < AssociationReflection # :nodoc:
576
- def initialize(name, scope, options, active_record)
577
- super(name, scope, options, active_record)
578
- end
579
-
580
624
  def macro; :has_many; end
581
625
 
582
626
  def collection?; true; end
583
- end
584
627
 
585
- class HasOneReflection < AssociationReflection # :nodoc:
586
- def initialize(name, scope, options, active_record)
587
- super(name, scope, options, active_record)
628
+ def association_class
629
+ if options[:through]
630
+ Associations::HasManyThroughAssociation
631
+ else
632
+ Associations::HasManyAssociation
633
+ end
588
634
  end
635
+ end
589
636
 
637
+ class HasOneReflection < AssociationReflection # :nodoc:
590
638
  def macro; :has_one; end
591
639
 
592
640
  def has_one?; true; end
593
- end
594
641
 
595
- class BelongsToReflection < AssociationReflection # :nodoc:
596
- def initialize(name, scope, options, active_record)
597
- super(name, scope, options, active_record)
642
+ def association_class
643
+ if options[:through]
644
+ Associations::HasOneThroughAssociation
645
+ else
646
+ Associations::HasOneAssociation
647
+ end
598
648
  end
599
649
 
650
+ private
651
+
652
+ def calculate_constructable(macro, options)
653
+ !options[:through]
654
+ end
655
+ end
656
+
657
+ class BelongsToReflection < AssociationReflection # :nodoc:
600
658
  def macro; :belongs_to; end
601
659
 
602
660
  def belongs_to?; true; end
603
661
 
604
- def join_keys(assoc_klass)
605
- key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
662
+ def association_class
663
+ if polymorphic?
664
+ Associations::BelongsToPolymorphicAssociation
665
+ else
666
+ Associations::BelongsToAssociation
667
+ end
668
+ end
669
+
670
+ def join_keys(association_klass)
671
+ key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
606
672
  JoinKeys.new(key, foreign_key)
607
673
  end
608
674
 
609
675
  def join_id_for(owner) # :nodoc:
610
676
  owner[foreign_key]
611
677
  end
678
+
679
+ private
680
+
681
+ def calculate_constructable(macro, options)
682
+ !polymorphic?
683
+ end
612
684
  end
613
685
 
614
686
  class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
@@ -632,10 +704,14 @@ module ActiveRecord
632
704
 
633
705
  def initialize(delegate_reflection)
634
706
  @delegate_reflection = delegate_reflection
635
- @klass = delegate_reflection.options[:class]
707
+ @klass = delegate_reflection.options[:anonymous_class]
636
708
  @source_reflection_name = delegate_reflection.options[:source]
637
709
  end
638
710
 
711
+ def through_reflection?
712
+ true
713
+ end
714
+
639
715
  def klass
640
716
  @klass ||= delegate_reflection.compute_class(class_name)
641
717
  end
@@ -694,14 +770,16 @@ module ActiveRecord
694
770
  # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
695
771
  # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
696
772
  #
697
- def chain
698
- @chain ||= begin
699
- a = source_reflection.chain
700
- b = through_reflection.chain
701
- chain = a + b
702
- chain[0] = self # Use self so we don't lose the information from :source_type
703
- chain
704
- end
773
+ def collect_join_chain
774
+ collect_join_reflections [self]
775
+ end
776
+
777
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
778
+ # SQL queries on associations.
779
+ def clear_association_scope_cache # :nodoc:
780
+ delegate_reflection.clear_association_scope_cache
781
+ source_reflection.clear_association_scope_cache
782
+ through_reflection.clear_association_scope_cache
705
783
  end
706
784
 
707
785
  # Consider the following example:
@@ -745,23 +823,19 @@ module ActiveRecord
745
823
  end
746
824
  end
747
825
 
748
- def join_keys(assoc_klass)
749
- source_reflection.join_keys(assoc_klass)
826
+ def has_scope?
827
+ scope || options[:source_type] ||
828
+ source_reflection.has_scope? ||
829
+ through_reflection.has_scope?
750
830
  end
751
831
 
752
- # The macro used by the source association
753
- def source_macro
754
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
755
- ActiveRecord::Base.source_macro is deprecated and will be removed
756
- without replacement.
757
- MSG
758
-
759
- source_reflection.source_macro
832
+ def join_keys(association_klass)
833
+ source_reflection.join_keys(association_klass)
760
834
  end
761
835
 
762
836
  # A through association is nested if there would be more than one join table
763
837
  def nested?
764
- chain.length > 2
838
+ source_reflection.through_reflection? || through_reflection.through_reflection?
765
839
  end
766
840
 
767
841
  # We want to use the klass from this reflection, rather than just delegate straight to
@@ -791,7 +865,7 @@ module ActiveRecord
791
865
  def source_reflection_name # :nodoc:
792
866
  return @source_reflection_name if @source_reflection_name
793
867
 
794
- names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
868
+ names = [name.to_s.singularize, name].collect(&:to_sym).uniq
795
869
  names = names.find_all { |n|
796
870
  through_reflection.klass._reflect_on_association(n)
797
871
  }
@@ -800,7 +874,7 @@ module ActiveRecord
800
874
  example_options = options.dup
801
875
  example_options[:source] = source_reflection_names.first
802
876
  ActiveSupport::Deprecation.warn \
803
- "Ambiguous source reflection for through association. Please " \
877
+ "Ambiguous source reflection for through association. Please " \
804
878
  "specify a :source directive on your declaration like:\n" \
805
879
  "\n" \
806
880
  " class #{active_record.name} < ActiveRecord::Base\n" \
@@ -855,6 +929,33 @@ module ActiveRecord
855
929
  check_validity_of_inverse!
856
930
  end
857
931
 
932
+ def constraints
933
+ scope_chain = source_reflection.constraints
934
+ scope_chain << scope if scope
935
+ scope_chain
936
+ end
937
+
938
+ def add_as_source(seed)
939
+ collect_join_reflections seed
940
+ end
941
+
942
+ def add_as_polymorphic_through(reflection, seed)
943
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
944
+ end
945
+
946
+ def add_as_through(seed)
947
+ collect_join_reflections(seed + [self])
948
+ end
949
+
950
+ def collect_join_reflections(seed)
951
+ a = source_reflection.add_as_source seed
952
+ if options[:source_type]
953
+ through_reflection.add_as_polymorphic_through self, a
954
+ else
955
+ through_reflection.add_as_through a
956
+ end
957
+ end
958
+
858
959
  protected
859
960
 
860
961
  def actual_source_reflection # FIXME: this is a horrible name
@@ -865,6 +966,8 @@ module ActiveRecord
865
966
  klass.primary_key || raise(UnknownPrimaryKey.new(klass))
866
967
  end
867
968
 
969
+ def inverse_name; delegate_reflection.send(:inverse_name); end
970
+
868
971
  private
869
972
  def derive_class_name
870
973
  # get the class_name of the belongs_to association of the through reflection
@@ -877,5 +980,81 @@ module ActiveRecord
877
980
  delegate(*delegate_methods, to: :delegate_reflection)
878
981
 
879
982
  end
983
+
984
+ class PolymorphicReflection < ThroughReflection # :nodoc:
985
+ def initialize(reflection, previous_reflection)
986
+ @reflection = reflection
987
+ @previous_reflection = previous_reflection
988
+ end
989
+
990
+ def klass
991
+ @reflection.klass
992
+ end
993
+
994
+ def scope
995
+ @reflection.scope
996
+ end
997
+
998
+ def table_name
999
+ @reflection.table_name
1000
+ end
1001
+
1002
+ def plural_name
1003
+ @reflection.plural_name
1004
+ end
1005
+
1006
+ def join_keys(association_klass)
1007
+ @reflection.join_keys(association_klass)
1008
+ end
1009
+
1010
+ def type
1011
+ @reflection.type
1012
+ end
1013
+
1014
+ def constraints
1015
+ @reflection.constraints + [source_type_info]
1016
+ end
1017
+
1018
+ def source_type_info
1019
+ type = @previous_reflection.foreign_type
1020
+ source_type = @previous_reflection.options[:source_type]
1021
+ lambda { |object| where(type => source_type) }
1022
+ end
1023
+ end
1024
+
1025
+ class RuntimeReflection < PolymorphicReflection # :nodoc:
1026
+ attr_accessor :next
1027
+
1028
+ def initialize(reflection, association)
1029
+ @reflection = reflection
1030
+ @association = association
1031
+ end
1032
+
1033
+ def klass
1034
+ @association.klass
1035
+ end
1036
+
1037
+ def table_name
1038
+ klass.table_name
1039
+ end
1040
+
1041
+ def constraints
1042
+ @reflection.constraints
1043
+ end
1044
+
1045
+ def source_type_info
1046
+ @reflection.source_type_info
1047
+ end
1048
+
1049
+ def alias_candidate(name)
1050
+ "#{plural_name}_#{name}_join"
1051
+ end
1052
+
1053
+ def alias_name
1054
+ Arel::Table.new(table_name)
1055
+ end
1056
+
1057
+ def all_includes; yield; end
1058
+ end
880
1059
  end
881
1060
  end