activerecord 4.2.11.3 → 5.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. data/lib/active_record/type/value.rb +0 -110
@@ -2,23 +2,25 @@ require 'active_support/core_ext/string/conversions'
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
- # Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
6
- # ActiveRecord::Associations::ThroughAssociationScope
5
+ # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
7
6
  class AliasTracker # :nodoc:
8
- attr_reader :aliases, :connection
7
+ attr_reader :aliases
9
8
 
10
- def self.empty(connection)
11
- new connection, Hash.new(0)
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
12
13
  end
13
14
 
14
- def self.create(connection, table_joins)
15
- if table_joins.empty?
16
- empty connection
15
+ def self.create_with_joins(connection, initial_table, joins, type_caster)
16
+ if joins.empty?
17
+ create(connection, initial_table, type_caster)
17
18
  else
18
- aliases = Hash.new { |h,k|
19
- h[k] = initial_count_for(connection, k, table_joins)
19
+ aliases = Hash.new { |h, k|
20
+ h[k] = initial_count_for(connection, k, joins)
20
21
  }
21
- new connection, aliases
22
+ aliases[initial_table] = 1
23
+ new connection, aliases, type_caster
22
24
  end
23
25
  end
24
26
 
@@ -51,19 +53,20 @@ module ActiveRecord
51
53
  end
52
54
 
53
55
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
54
- def initialize(connection, aliases)
56
+ def initialize(connection, aliases, type_caster)
55
57
  @aliases = aliases
56
58
  @connection = connection
59
+ @type_caster = type_caster
57
60
  end
58
61
 
59
62
  def aliased_table_for(table_name, aliased_name)
60
63
  if aliases[table_name].zero?
61
64
  # If it's zero, we can have our table_name
62
65
  aliases[table_name] = 1
63
- Arel::Table.new(table_name)
66
+ Arel::Table.new(table_name, type_caster: @type_caster)
64
67
  else
65
68
  # Otherwise, we need to use an alias
66
- aliased_name = connection.table_alias_for(aliased_name)
69
+ aliased_name = @connection.table_alias_for(aliased_name)
67
70
 
68
71
  # Update the count
69
72
  aliases[aliased_name] += 1
@@ -73,14 +76,14 @@ module ActiveRecord
73
76
  else
74
77
  aliased_name
75
78
  end
76
- Arel::Table.new(table_name).alias(table_alias)
79
+ Arel::Table.new(table_name, type_caster: @type_caster).alias(table_alias)
77
80
  end
78
81
  end
79
82
 
80
83
  private
81
84
 
82
85
  def truncate(name)
83
- name.slice(0, connection.table_alias_length - 2)
86
+ name.slice(0, @connection.table_alias_length - 2)
84
87
  end
85
88
  end
86
89
  end
@@ -8,12 +8,12 @@ module ActiveRecord
8
8
  #
9
9
  # Association
10
10
  # SingularAssociation
11
- # HasOneAssociation
11
+ # HasOneAssociation + ForeignAssociation
12
12
  # HasOneThroughAssociation + ThroughAssociation
13
13
  # BelongsToAssociation
14
14
  # BelongsToPolymorphicAssociation
15
15
  # CollectionAssociation
16
- # HasManyAssociation
16
+ # HasManyAssociation + ForeignAssociation
17
17
  # HasManyThroughAssociation + ThroughAssociation
18
18
  class Association #:nodoc:
19
19
  attr_reader :owner, :target, :reflection
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
122
122
  # through association's scope)
123
123
  def target_scope
124
- AssociationRelation.create(klass, klass.arel_table, self).merge!(klass.all)
124
+ AssociationRelation.create(klass, klass.arel_table, klass.predicate_builder, self).merge!(klass.all)
125
125
  end
126
126
 
127
127
  # Loads the \target if needed and returns it.
@@ -163,9 +163,12 @@ module ActiveRecord
163
163
  @reflection = @owner.class._reflect_on_association(reflection_name)
164
164
  end
165
165
 
166
- def initialize_attributes(record) #:nodoc:
166
+ def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
167
+ except_from_scope_attributes ||= {}
167
168
  skip_assign = [reflection.foreign_key, reflection.type].compact
168
- attributes = create_scope.except(*(record.changed - skip_assign))
169
+ assigned_keys = record.changed
170
+ assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
171
+ attributes = create_scope.except(*(assigned_keys - skip_assign))
169
172
  record.assign_attributes(attributes)
170
173
  set_inverse_instance(record)
171
174
  end
@@ -248,7 +251,7 @@ module ActiveRecord
248
251
 
249
252
  def build_record(attributes)
250
253
  reflection.build_association(attributes) do |record|
251
- initialize_attributes(record)
254
+ initialize_attributes(record, attributes)
252
255
  end
253
256
  end
254
257
 
@@ -256,8 +259,7 @@ module ActiveRecord
256
259
  def skip_statement_cache?
257
260
  reflection.scope_chain.any?(&:any?) ||
258
261
  scope.eager_loading? ||
259
- klass.current_scope ||
260
- klass.default_scopes.any? ||
262
+ klass.scope_attributes? ||
261
263
  reflection.source_reflection.active_record.default_scopes.any?
262
264
  end
263
265
  end
@@ -2,41 +2,30 @@ module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope #:nodoc:
4
4
  def self.scope(association, connection)
5
- INSTANCE.scope association, connection
6
- end
7
-
8
- class BindSubstitution
9
- def initialize(block)
10
- @block = block
11
- end
12
-
13
- def bind_value(scope, column, value, alias_tracker)
14
- substitute = alias_tracker.connection.substitute_at(column)
15
- scope.bind_values += [[column, @block.call(value)]]
16
- substitute
17
- end
5
+ INSTANCE.scope(association, connection)
18
6
  end
19
7
 
20
8
  def self.create(&block)
21
- block = block ? block : lambda { |val| val }
22
- new BindSubstitution.new(block)
9
+ block ||= lambda { |val| val }
10
+ new(block)
23
11
  end
24
12
 
25
- def initialize(bind_substitution)
26
- @bind_substitution = bind_substitution
13
+ def initialize(value_transformation)
14
+ @value_transformation = value_transformation
27
15
  end
28
16
 
29
17
  INSTANCE = create
30
18
 
31
19
  def scope(association, connection)
32
- klass = association.klass
33
- reflection = association.reflection
34
- scope = klass.unscoped
35
- owner = association.owner
36
- alias_tracker = AliasTracker.empty connection
20
+ klass = association.klass
21
+ reflection = association.reflection
22
+ scope = klass.unscoped
23
+ 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)
37
26
 
38
27
  scope.extending! Array(reflection.options[:extend])
39
- add_constraints(scope, owner, klass, reflection, alias_tracker)
28
+ add_constraints(scope, owner, klass, reflection, chain_head, chain_tail)
40
29
  end
41
30
 
42
31
  def join_type
@@ -60,133 +49,115 @@ module ActiveRecord
60
49
  binds
61
50
  end
62
51
 
63
- private
52
+ protected
64
53
 
65
- def construct_tables(chain, klass, refl, alias_tracker)
66
- chain.map do |reflection|
67
- alias_tracker.aliased_table_for(
68
- table_name_for(reflection, klass, refl),
69
- table_alias_for(reflection, refl, reflection != refl)
70
- )
71
- end
72
- end
73
-
74
- def table_alias_for(reflection, refl, join = false)
75
- name = "#{reflection.plural_name}_#{alias_suffix(refl)}"
76
- name << "_join" if join
77
- name
78
- end
54
+ attr_reader :value_transformation
79
55
 
56
+ private
80
57
  def join(table, constraint)
81
58
  table.create_join(table, table.create_on(constraint), join_type)
82
59
  end
83
60
 
84
- def column_for(table_name, column_name, alias_tracker)
85
- columns = alias_tracker.connection.schema_cache.columns_hash(table_name)
86
- columns[column_name]
87
- end
88
-
89
- def bind_value(scope, column, value, alias_tracker)
90
- @bind_substitution.bind_value scope, column, value, alias_tracker
91
- end
92
-
93
- def bind(scope, table_name, column_name, value, tracker)
94
- column = column_for table_name, column_name, tracker
95
- bind_value scope, column, value, tracker
96
- end
97
-
98
- def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass)
99
- join_keys = reflection.join_keys(assoc_klass)
61
+ def last_chain_scope(scope, table, reflection, owner, association_klass)
62
+ join_keys = reflection.join_keys(association_klass)
100
63
  key = join_keys.key
101
64
  foreign_key = join_keys.foreign_key
102
65
 
103
- bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
104
- scope = scope.where(table[key].eq(bind_val))
66
+ value = transform_value(owner[foreign_key])
67
+ scope = scope.where(table.name => { key => value })
105
68
 
106
69
  if reflection.type
107
- value = owner.class.base_class.name
108
- bind_val = bind scope, table.table_name, reflection.type, value, tracker
109
- scope = scope.where(table[reflection.type].eq(bind_val))
110
- else
111
- scope
70
+ polymorphic_type = transform_value(owner.class.base_class.name)
71
+ scope = scope.where(table.name => { reflection.type => polymorphic_type })
112
72
  end
73
+
74
+ scope
113
75
  end
114
76
 
115
- def next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
116
- join_keys = reflection.join_keys(assoc_klass)
77
+ def transform_value(value)
78
+ value_transformation.call(value)
79
+ end
80
+
81
+ def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
82
+ join_keys = reflection.join_keys(association_klass)
117
83
  key = join_keys.key
118
84
  foreign_key = join_keys.foreign_key
119
85
 
120
86
  constraint = table[key].eq(foreign_table[foreign_key])
121
87
 
122
88
  if reflection.type
123
- value = next_reflection.klass.base_class.name
124
- bind_val = bind scope, table.table_name, reflection.type, value, tracker
125
- scope = scope.where(table[reflection.type].eq(bind_val))
89
+ value = transform_value(next_reflection.klass.base_class.name)
90
+ scope = scope.where(table.name => { reflection.type => value })
126
91
  end
127
92
 
128
93
  scope = scope.joins(join(foreign_table, constraint))
129
94
  end
130
95
 
131
- def add_constraints(scope, owner, assoc_klass, refl, tracker)
132
- chain = refl.chain
133
- scope_chain = refl.scope_chain
96
+ class ReflectionProxy < SimpleDelegator # :nodoc:
97
+ attr_accessor :next
98
+ attr_reader :alias_name
134
99
 
135
- tables = construct_tables(chain, assoc_klass, refl, tracker)
100
+ def initialize(reflection, alias_name)
101
+ super(reflection)
102
+ @alias_name = alias_name
103
+ end
136
104
 
137
- owner_reflection = chain.last
138
- table = tables.last
139
- scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass)
105
+ def all_includes; nil; end
106
+ end
140
107
 
141
- chain.each_with_index do |reflection, i|
142
- table, foreign_table = tables.shift, tables.first
108
+ def get_chain(reflection, association, tracker)
109
+ name = reflection.name
110
+ runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
111
+ previous_reflection = runtime_reflection
112
+ reflection.chain.drop(1).each do |refl|
113
+ alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
114
+ proxy = ReflectionProxy.new(refl, alias_name)
115
+ previous_reflection.next = proxy
116
+ previous_reflection = proxy
117
+ end
118
+ [runtime_reflection, previous_reflection]
119
+ end
143
120
 
144
- unless reflection == chain.last
145
- next_reflection = chain[i + 1]
146
- scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
147
- end
121
+ def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
122
+ owner_reflection = chain_tail
123
+ table = owner_reflection.alias_name
124
+ scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
148
125
 
149
- is_first_chain = i == 0
150
- klass = is_first_chain ? assoc_klass : reflection.klass
126
+ reflection = chain_head
127
+ loop do
128
+ break unless reflection
129
+ table = reflection.alias_name
130
+
131
+ unless reflection == chain_tail
132
+ next_reflection = reflection.next
133
+ foreign_table = next_reflection.alias_name
134
+ scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
135
+ end
151
136
 
152
137
  # Exclude the scope of the association itself, because that
153
138
  # was already merged in the #scope method.
154
- scope_chain[i].each do |scope_chain_item|
155
- item = eval_scope(klass, scope_chain_item, owner)
139
+ reflection.constraints.each do |scope_chain_item|
140
+ item = eval_scope(reflection.klass, scope_chain_item, owner)
156
141
 
157
142
  if scope_chain_item == refl.scope
158
- scope.merge! item.except(:where, :includes, :bind)
143
+ scope.merge! item.except(:where, :includes)
159
144
  end
160
145
 
161
- if is_first_chain
146
+ reflection.all_includes do
162
147
  scope.includes! item.includes_values
163
148
  end
164
149
 
165
150
  scope.unscope!(*item.unscope_values)
166
- scope.where_values += item.where_values
167
- scope.bind_values += item.bind_values
151
+ scope.where_clause += item.where_clause
168
152
  scope.order_values |= item.order_values
169
153
  end
154
+
155
+ reflection = reflection.next
170
156
  end
171
157
 
172
158
  scope
173
159
  end
174
160
 
175
- def alias_suffix(refl)
176
- refl.name
177
- end
178
-
179
- def table_name_for(reflection, klass, refl)
180
- if reflection == refl
181
- # If this is a polymorphic belongs_to, we want to get the klass from the
182
- # association because it depends on the polymorphic_type attribute of
183
- # the owner
184
- klass.table_name
185
- else
186
- reflection.table_name
187
- end
188
- end
189
-
190
161
  def eval_scope(klass, scope, owner)
191
162
  klass.unscoped.instance_exec(owner, &scope)
192
163
  end
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  def replace(record)
11
11
  if record
12
12
  raise_on_type_mismatch!(record)
13
- update_counters(record)
13
+ update_counters_on_replace(record)
14
14
  replace_keys(record)
15
15
  set_inverse_instance(record)
16
16
  @updated = true
@@ -32,45 +32,37 @@ module ActiveRecord
32
32
  end
33
33
 
34
34
  def decrement_counters # :nodoc:
35
- with_cache_name { |name| decrement_counter name }
35
+ update_counters(-1)
36
36
  end
37
37
 
38
38
  def increment_counters # :nodoc:
39
- with_cache_name { |name| increment_counter name }
39
+ update_counters(1)
40
40
  end
41
41
 
42
42
  private
43
43
 
44
- def find_target?
45
- !loaded? && foreign_key_present? && klass
46
- end
47
-
48
- def with_cache_name
49
- counter_cache_name = reflection.counter_cache_column
50
- return unless counter_cache_name && owner.persisted?
51
- yield counter_cache_name
44
+ def update_counters(by)
45
+ if require_counter_update? && foreign_key_present?
46
+ if target && !stale_target?
47
+ target.increment!(reflection.counter_cache_column, by)
48
+ else
49
+ klass.update_counters(target_id, reflection.counter_cache_column => by)
50
+ end
51
+ end
52
52
  end
53
53
 
54
- def update_counters(record)
55
- with_cache_name do |name|
56
- return unless different_target? record
57
- record.class.increment_counter(name, record.id)
58
- decrement_counter name
59
- end
54
+ def find_target?
55
+ !loaded? && foreign_key_present? && klass
60
56
  end
61
57
 
62
- def decrement_counter(counter_cache_name)
63
- if foreign_key_present?
64
- klass.decrement_counter(counter_cache_name, target_id)
65
- end
58
+ def require_counter_update?
59
+ reflection.counter_cache_column && owner.persisted?
66
60
  end
67
61
 
68
- def increment_counter(counter_cache_name)
69
- if foreign_key_present?
70
- klass.increment_counter(counter_cache_name, target_id)
71
- if target && !stale_target? && counter_cache_available_in_memory?(counter_cache_name)
72
- target.increment(counter_cache_name)
73
- end
62
+ def update_counters_on_replace(record)
63
+ if require_counter_update? && different_target?(record)
64
+ record.increment!(reflection.counter_cache_column)
65
+ decrement_counters
74
66
  end
75
67
  end
76
68
 
@@ -107,13 +99,9 @@ module ActiveRecord
107
99
  end
108
100
 
109
101
  def stale_state
110
- result = owner._read_attribute(reflection.foreign_key)
102
+ result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
111
103
  result && result.to_s
112
104
  end
113
-
114
- def counter_cache_available_in_memory?(counter_cache_name)
115
- target.respond_to?(counter_cache_name)
116
- end
117
105
  end
118
106
  end
119
107
  end