activerecord 4.0.4 → 4.1.16

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1632 -1797
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/examples/performance.rb +30 -18
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +4 -0
  9. data/lib/active_record/associations/alias_tracker.rb +49 -29
  10. data/lib/active_record/associations/association.rb +9 -17
  11. data/lib/active_record/associations/association_scope.rb +59 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
  14. data/lib/active_record/associations/builder/association.rb +84 -54
  15. data/lib/active_record/associations/builder/belongs_to.rb +90 -58
  16. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
  18. data/lib/active_record/associations/builder/has_many.rb +3 -3
  19. data/lib/active_record/associations/builder/has_one.rb +5 -7
  20. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  21. data/lib/active_record/associations/collection_association.rb +121 -111
  22. data/lib/active_record/associations/collection_proxy.rb +73 -18
  23. data/lib/active_record/associations/has_many_association.rb +14 -11
  24. data/lib/active_record/associations/has_many_through_association.rb +33 -6
  25. data/lib/active_record/associations/has_one_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
  27. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  28. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  29. data/lib/active_record/associations/join_dependency.rb +208 -168
  30. data/lib/active_record/associations/preloader/association.rb +69 -27
  31. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  35. data/lib/active_record/associations/preloader.rb +63 -49
  36. data/lib/active_record/associations/singular_association.rb +6 -5
  37. data/lib/active_record/associations/through_association.rb +30 -9
  38. data/lib/active_record/associations.rb +116 -42
  39. data/lib/active_record/attribute_assignment.rb +6 -3
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  41. data/lib/active_record/attribute_methods/dirty.rb +35 -26
  42. data/lib/active_record/attribute_methods/primary_key.rb +8 -1
  43. data/lib/active_record/attribute_methods/read.rb +56 -29
  44. data/lib/active_record/attribute_methods/serialization.rb +44 -12
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
  46. data/lib/active_record/attribute_methods/write.rb +59 -26
  47. data/lib/active_record/attribute_methods.rb +82 -43
  48. data/lib/active_record/autosave_association.rb +209 -194
  49. data/lib/active_record/base.rb +6 -2
  50. data/lib/active_record/callbacks.rb +2 -2
  51. data/lib/active_record/coders/json.rb +13 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +45 -70
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
  76. data/lib/active_record/connection_handling.rb +39 -5
  77. data/lib/active_record/core.rb +38 -54
  78. data/lib/active_record/counter_cache.rb +9 -10
  79. data/lib/active_record/dynamic_matchers.rb +6 -2
  80. data/lib/active_record/enum.rb +199 -0
  81. data/lib/active_record/errors.rb +22 -5
  82. data/lib/active_record/fixture_set/file.rb +2 -1
  83. data/lib/active_record/fixtures.rb +173 -76
  84. data/lib/active_record/gem_version.rb +15 -0
  85. data/lib/active_record/inheritance.rb +23 -9
  86. data/lib/active_record/integration.rb +54 -1
  87. data/lib/active_record/locking/optimistic.rb +7 -2
  88. data/lib/active_record/locking/pessimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +6 -13
  90. data/lib/active_record/migration/command_recorder.rb +8 -2
  91. data/lib/active_record/migration.rb +91 -56
  92. data/lib/active_record/model_schema.rb +7 -14
  93. data/lib/active_record/nested_attributes.rb +25 -13
  94. data/lib/active_record/no_touching.rb +52 -0
  95. data/lib/active_record/null_relation.rb +26 -6
  96. data/lib/active_record/persistence.rb +23 -29
  97. data/lib/active_record/querying.rb +15 -12
  98. data/lib/active_record/railtie.rb +12 -61
  99. data/lib/active_record/railties/databases.rake +37 -56
  100. data/lib/active_record/readonly_attributes.rb +0 -6
  101. data/lib/active_record/reflection.rb +230 -79
  102. data/lib/active_record/relation/batches.rb +74 -24
  103. data/lib/active_record/relation/calculations.rb +52 -48
  104. data/lib/active_record/relation/delegation.rb +54 -39
  105. data/lib/active_record/relation/finder_methods.rb +210 -67
  106. data/lib/active_record/relation/merger.rb +15 -12
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
  109. data/lib/active_record/relation/predicate_builder.rb +81 -40
  110. data/lib/active_record/relation/query_methods.rb +185 -108
  111. data/lib/active_record/relation/spawn_methods.rb +8 -5
  112. data/lib/active_record/relation.rb +79 -84
  113. data/lib/active_record/result.rb +45 -6
  114. data/lib/active_record/runtime_registry.rb +5 -0
  115. data/lib/active_record/sanitization.rb +4 -4
  116. data/lib/active_record/schema_dumper.rb +18 -6
  117. data/lib/active_record/schema_migration.rb +31 -18
  118. data/lib/active_record/scoping/default.rb +5 -18
  119. data/lib/active_record/scoping/named.rb +14 -29
  120. data/lib/active_record/scoping.rb +5 -0
  121. data/lib/active_record/store.rb +67 -18
  122. data/lib/active_record/tasks/database_tasks.rb +66 -26
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
  124. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  125. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  126. data/lib/active_record/timestamp.rb +6 -6
  127. data/lib/active_record/transactions.rb +10 -12
  128. data/lib/active_record/validations/presence.rb +1 -1
  129. data/lib/active_record/validations/uniqueness.rb +19 -9
  130. data/lib/active_record/version.rb +4 -7
  131. data/lib/active_record.rb +5 -7
  132. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  133. data/lib/rails/generators/active_record/migration.rb +18 -0
  134. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  135. data/lib/rails/generators/active_record.rb +2 -8
  136. metadata +18 -30
  137. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  138. data/lib/active_record/associations/join_helper.rb +0 -45
  139. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  141. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  142. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  143. data/lib/active_record/test_case.rb +0 -96
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
 
10
10
  def handle_dependency
11
11
  case options[:dependent]
12
- when :restrict, :restrict_with_exception
12
+ when :restrict_with_exception
13
13
  raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
14
14
 
15
15
  when :restrict_with_error
@@ -59,8 +59,6 @@ module ActiveRecord
59
59
  def count_records
60
60
  count = if has_cached_counter?
61
61
  owner.send(:read_attribute, cached_counter_attribute_name)
62
- elsif options[:counter_sql] || options[:finder_sql]
63
- reflection.klass.count_by_sql(custom_counter_sql)
64
62
  else
65
63
  scope.count
66
64
  end
@@ -73,15 +71,15 @@ module ActiveRecord
73
71
  [association_scope.limit_value, count].compact.min
74
72
  end
75
73
 
76
- def has_cached_counter?(reflection = reflection)
74
+ def has_cached_counter?(reflection = reflection())
77
75
  owner.attribute_present?(cached_counter_attribute_name(reflection))
78
76
  end
79
77
 
80
- def cached_counter_attribute_name(reflection = reflection)
78
+ def cached_counter_attribute_name(reflection = reflection())
81
79
  options[:counter_cache] || "#{reflection.name}_count"
82
80
  end
83
81
 
84
- def update_counter(difference, reflection = reflection)
82
+ def update_counter(difference, reflection = reflection())
85
83
  if has_cached_counter?(reflection)
86
84
  counter = cached_counter_attribute_name(reflection)
87
85
  owner.class.update_counters(owner.id, counter => difference)
@@ -100,9 +98,10 @@ module ActiveRecord
100
98
  # it will be decremented twice.
101
99
  #
102
100
  # Hence this method.
103
- def inverse_updates_counter_cache?(reflection = reflection)
101
+ def inverse_updates_counter_cache?(reflection = reflection())
104
102
  counter_name = cached_counter_attribute_name(reflection)
105
- reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
103
+ reflection.klass._reflections.values.any? { |inverse_reflection|
104
+ :belongs_to == inverse_reflection.macro &&
106
105
  inverse_reflection.counter_cache_column == counter_name
107
106
  }
108
107
  end
@@ -110,10 +109,10 @@ module ActiveRecord
110
109
  # Deletes the records according to the <tt>:dependent</tt> option.
111
110
  def delete_records(records, method)
112
111
  if method == :destroy
113
- records.each { |r| r.destroy }
112
+ records.each(&:destroy!)
114
113
  update_counter(-records.length) unless inverse_updates_counter_cache?
115
114
  else
116
- if records == :all
115
+ if records == :all || !reflection.klass.primary_key
117
116
  scope = self.scope
118
117
  else
119
118
  scope = self.scope.where(reflection.klass.primary_key => records)
@@ -128,7 +127,11 @@ module ActiveRecord
128
127
  end
129
128
 
130
129
  def foreign_key_present?
131
- owner.attribute_present?(reflection.association_primary_key)
130
+ if reflection.klass.primary_key
131
+ owner.attribute_present?(reflection.association_primary_key)
132
+ else
133
+ false
134
+ end
132
135
  end
133
136
  end
134
137
  end
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  elsif loaded?
23
23
  target.size
24
24
  else
25
- count
25
+ super
26
26
  end
27
27
  end
28
28
 
@@ -30,7 +30,6 @@ module ActiveRecord
30
30
  unless owner.new_record?
31
31
  records.flatten.each do |record|
32
32
  raise_on_type_mismatch!(record)
33
- record.save! if record.new_record?
34
33
  end
35
34
  end
36
35
 
@@ -40,7 +39,7 @@ module ActiveRecord
40
39
  def concat_records(records)
41
40
  ensure_not_nested
42
41
 
43
- records = super
42
+ records = super(records, true)
44
43
 
45
44
  if owner.new_record? && records
46
45
  records.flatten.each do |record|
@@ -84,12 +83,22 @@ module ActiveRecord
84
83
  @through_records[record.object_id] ||= begin
85
84
  ensure_mutable
86
85
 
87
- through_record = through_association.build
86
+ through_record = through_association.build(*options_for_through_record)
88
87
  through_record.send("#{source_reflection.name}=", record)
89
88
  through_record
90
89
  end
91
90
  end
92
91
 
92
+ def options_for_through_record
93
+ [through_scope_attributes]
94
+ end
95
+
96
+ def through_scope_attributes
97
+ scope.where_values_hash(through_association.reflection.name.to_s).
98
+ except!(through_association.reflection.foreign_key,
99
+ through_association.reflection.klass.inheritance_column)
100
+ end
101
+
93
102
  def save_through_record(record)
94
103
  build_through_record(record).save!
95
104
  ensure
@@ -140,7 +149,21 @@ module ActiveRecord
140
149
 
141
150
  case method
142
151
  when :destroy
143
- count = scope.destroy_all.length
152
+ if scope.klass.primary_key
153
+ count = scope.destroy_all.length
154
+ else
155
+ scope.to_a.each do |record|
156
+ record.run_callbacks :destroy
157
+ end
158
+
159
+ arel = scope.arel
160
+
161
+ stmt = Arel::DeleteManager.new arel.engine
162
+ stmt.from scope.klass.arel_table
163
+ stmt.wheres = arel.constraints
164
+
165
+ count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
166
+ end
144
167
  when :nullify
145
168
  count = scope.update_all(source_reflection.foreign_key => nil)
146
169
  else
@@ -164,7 +187,11 @@ module ActiveRecord
164
187
  def through_records_for(record)
165
188
  attributes = construct_join_attributes(record)
166
189
  candidates = Array.wrap(through_association.target)
167
- candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
190
+ candidates.find_all do |c|
191
+ attributes.all? do |key, value|
192
+ c.public_send(key) == value
193
+ end
194
+ end
168
195
  end
169
196
 
170
197
  def delete_through_records(records)
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
 
7
7
  def handle_dependency
8
8
  case options[:dependent]
9
- when :restrict, :restrict_with_exception
9
+ when :restrict_with_exception
10
10
  raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target
11
11
 
12
12
  when :restrict_with_error
@@ -1,128 +1,80 @@
1
+ require 'active_record/associations/join_dependency/join_part'
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class JoinDependency # :nodoc:
4
6
  class JoinAssociation < JoinPart # :nodoc:
5
- include JoinHelper
6
-
7
7
  # The reflection of the association represented
8
8
  attr_reader :reflection
9
9
 
10
- # The JoinDependency object which this JoinAssociation exists within. This is mainly
11
- # relevant for generating aliases which do not conflict with other joins which are
12
- # part of the query.
13
- attr_reader :join_dependency
14
-
15
- # A JoinBase instance representing the active record we are joining onto.
16
- # (So in Author.has_many :posts, the Author would be that base record.)
17
- attr_reader :parent
18
-
19
- # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
20
- attr_accessor :join_type
21
-
22
- # These implement abstract methods from the superclass
23
- attr_reader :aliased_prefix
24
-
25
- attr_reader :tables
26
-
27
- delegate :options, :through_reflection, :source_reflection, :chain, :to => :reflection
28
- delegate :table, :table_name, :to => :parent, :prefix => :parent
29
- delegate :alias_tracker, :to => :join_dependency
30
-
31
- alias :alias_suffix :parent_table_name
10
+ attr_accessor :tables
32
11
 
33
- def initialize(reflection, join_dependency, parent = nil)
34
- reflection.check_validity!
35
-
36
- if reflection.options[:polymorphic]
37
- raise EagerLoadPolymorphicError.new(reflection)
38
- end
39
-
40
- super(reflection.klass)
12
+ def initialize(reflection, children)
13
+ super(reflection.klass, children)
41
14
 
42
15
  @reflection = reflection
43
- @join_dependency = join_dependency
44
- @parent = parent
45
- @join_type = Arel::InnerJoin
46
- @aliased_prefix = "t#{ join_dependency.join_parts.size }"
47
- @tables = construct_tables.reverse
16
+ @tables = nil
48
17
  end
49
18
 
50
- def ==(other)
51
- other.class == self.class &&
52
- other.reflection == reflection &&
53
- other.parent == parent
19
+ def match?(other)
20
+ return true if self == other
21
+ super && reflection == other.reflection
54
22
  end
55
23
 
56
- def find_parent_in(other_join_dependency)
57
- other_join_dependency.join_parts.detect do |join_part|
58
- case parent
59
- when JoinBase
60
- parent.base_klass == join_part.base_klass
61
- else
62
- parent == join_part
63
- end
64
- end
65
- end
24
+ def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
25
+ joins = []
26
+ tables = tables.reverse
66
27
 
67
- def join_to(manager)
68
- tables = @tables.dup
69
- foreign_table = parent_table
70
- foreign_klass = parent.base_klass
28
+ scope_chain_index = 0
29
+ scope_chain = scope_chain.reverse
71
30
 
72
31
  # The chain starts with the target table, but we want to end with it here (makes
73
32
  # more sense in this context), so we reverse
74
- chain.reverse.each_with_index do |reflection, i|
33
+ chain.reverse_each do |reflection|
75
34
  table = tables.shift
35
+ klass = reflection.klass
76
36
 
77
37
  case reflection.source_macro
78
38
  when :belongs_to
79
39
  key = reflection.association_primary_key
80
40
  foreign_key = reflection.foreign_key
81
- when :has_and_belongs_to_many
82
- # Join the join table first...
83
- manager.from(join(
84
- table,
85
- table[reflection.foreign_key].
86
- eq(foreign_table[reflection.active_record_primary_key])
87
- ))
88
-
89
- foreign_table, table = table, tables.shift
90
-
91
- key = reflection.association_primary_key
92
- foreign_key = reflection.association_foreign_key
93
41
  else
94
42
  key = reflection.foreign_key
95
43
  foreign_key = reflection.active_record_primary_key
96
44
  end
97
45
 
98
- constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
46
+ constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
99
47
 
100
- scope_chain_items = scope_chain[i]
101
-
102
- if reflection.type
103
- scope_chain_items += [
104
- ActiveRecord::Relation.new(reflection.klass, table)
105
- .where(reflection.type => foreign_klass.base_class.name)
106
- ]
48
+ scope_chain_items = scope_chain[scope_chain_index].map do |item|
49
+ if item.is_a?(Relation)
50
+ item
51
+ else
52
+ ActiveRecord::Relation.create(klass, table).instance_exec(node, &item)
53
+ end
107
54
  end
55
+ scope_chain_index += 1
108
56
 
109
- scope_chain_items += [reflection.klass.send(:build_default_scope)].compact
57
+ scope_chain_items.concat [klass.send(:build_default_scope, ActiveRecord::Relation.create(klass, table))].compact
110
58
 
111
- scope_chain_items.each do |item|
112
- unless item.is_a?(Relation)
113
- item = ActiveRecord::Relation.new(reflection.klass, table).instance_exec(self, &item)
114
- end
59
+ rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
60
+ left.merge right
61
+ end
115
62
 
116
- constraint = constraint.and(item.arel.constraints) unless item.arel.constraints.empty?
63
+ if reflection.type
64
+ constraint = constraint.and table[reflection.type].eq foreign_klass.base_class.name
117
65
  end
118
66
 
119
- manager.from(join(table, constraint))
67
+ if rel && !rel.arel.constraints.empty?
68
+ constraint = constraint.and rel.arel.constraints
69
+ end
70
+
71
+ joins << table.create_join(table, table.create_on(constraint), join_type)
120
72
 
121
73
  # The current table in this iteration becomes the foreign table in the next
122
- foreign_table, foreign_klass = table, reflection.klass
74
+ foreign_table, foreign_klass = table, klass
123
75
  end
124
76
 
125
- manager
77
+ joins
126
78
  end
127
79
 
128
80
  # Builds equality condition.
@@ -134,42 +86,32 @@ module ActiveRecord
134
86
  # end
135
87
  #
136
88
  # If I execute `Physician.joins(:appointments).to_a` then
137
- # reflection #=> #<ActiveRecord::Reflection::AssociationReflection @macro=:has_many ...>
138
- # table #=> #<Arel::Table @name="appointments" ...>
139
- # key #=> physician_id
140
- # foreign_table #=> #<Arel::Table @name="physicians" ...>
141
- # foreign_key #=> id
89
+ # reflection # => #<ActiveRecord::Reflection::AssociationReflection @macro=:has_many ...>
90
+ # table # => #<Arel::Table @name="appointments" ...>
91
+ # key # => physician_id
92
+ # foreign_table # => #<Arel::Table @name="physicians" ...>
93
+ # foreign_key # => id
142
94
  #
143
- def build_constraint(reflection, table, key, foreign_table, foreign_key)
95
+ def build_constraint(klass, table, key, foreign_table, foreign_key)
144
96
  constraint = table[key].eq(foreign_table[foreign_key])
145
97
 
146
- if reflection.klass.finder_needs_type_condition?
98
+ if klass.finder_needs_type_condition?
147
99
  constraint = table.create_and([
148
100
  constraint,
149
- reflection.klass.send(:type_condition, table)
101
+ klass.send(:type_condition, table)
150
102
  ])
151
103
  end
152
104
 
153
105
  constraint
154
106
  end
155
107
 
156
- def join_relation(joining_relation)
157
- self.join_type = Arel::OuterJoin
158
- joining_relation.joins(self)
159
- end
160
-
161
108
  def table
162
- tables.last
109
+ tables.first
163
110
  end
164
111
 
165
112
  def aliased_table_name
166
113
  table.table_alias || table.name
167
114
  end
168
-
169
- def scope_chain
170
- @scope_chain ||= reflection.scope_chain.reverse
171
- end
172
-
173
115
  end
174
116
  end
175
117
  end
@@ -1,18 +1,16 @@
1
+ require 'active_record/associations/join_dependency/join_part'
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class JoinDependency # :nodoc:
4
6
  class JoinBase < JoinPart # :nodoc:
5
- def ==(other)
6
- other.class == self.class &&
7
- other.base_klass == base_klass
8
- end
9
-
10
- def aliased_prefix
11
- "t0"
7
+ def match?(other)
8
+ return true if self == other
9
+ super && base_klass == other.base_klass
12
10
  end
13
11
 
14
12
  def table
15
- Arel::Table.new(table_name, arel_engine)
13
+ base_klass.arel_table
16
14
  end
17
15
 
18
16
  def aliased_table_name
@@ -8,34 +8,36 @@ module ActiveRecord
8
8
  # operations (for example a has_and_belongs_to_many JoinAssociation would result in
9
9
  # two; one for the join table and one for the target table).
10
10
  class JoinPart # :nodoc:
11
+ include Enumerable
12
+
11
13
  # The Active Record class which this join part is associated 'about'; for a JoinBase
12
14
  # this is the actual base model, for a JoinAssociation this is the target model of the
13
15
  # association.
14
- attr_reader :base_klass
16
+ attr_reader :base_klass, :children
15
17
 
16
- delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :base_klass
18
+ delegate :table_name, :column_names, :primary_key, :to => :base_klass
17
19
 
18
- def initialize(base_klass)
20
+ def initialize(base_klass, children)
19
21
  @base_klass = base_klass
20
- @cached_record = {}
21
22
  @column_names_with_alias = nil
23
+ @children = children
22
24
  end
23
25
 
24
- def aliased_table
25
- Arel::Nodes::TableAlias.new table, aliased_table_name
26
+ def name
27
+ reflection.name
26
28
  end
27
29
 
28
- def ==(other)
29
- raise NotImplementedError
30
+ def match?(other)
31
+ self.class == other.class
30
32
  end
31
33
 
32
- # An Arel::Table for the active_record
33
- def table
34
- raise NotImplementedError
34
+ def each(&block)
35
+ yield self
36
+ children.each { |child| child.each(&block) }
35
37
  end
36
38
 
37
- # The prefix to be used when aliasing columns in the active_record's table
38
- def aliased_prefix
39
+ # An Arel::Table for the active_record
40
+ def table
39
41
  raise NotImplementedError
40
42
  end
41
43
 
@@ -44,24 +46,7 @@ module ActiveRecord
44
46
  raise NotImplementedError
45
47
  end
46
48
 
47
- # The alias for the primary key of the active_record's table
48
- def aliased_primary_key
49
- "#{aliased_prefix}_r0"
50
- end
51
-
52
- # An array of [column_name, alias] pairs for the table
53
- def column_names_with_alias
54
- unless @column_names_with_alias
55
- @column_names_with_alias = []
56
-
57
- ([primary_key] + (column_names - [primary_key])).compact.each_with_index do |column_name, i|
58
- @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
59
- end
60
- end
61
- @column_names_with_alias
62
- end
63
-
64
- def extract_record(row)
49
+ def extract_record(row, column_names_with_alias)
65
50
  # This code is performance critical as it is called per row.
66
51
  # see: https://github.com/rails/rails/pull/12185
67
52
  hash = {}
@@ -78,12 +63,8 @@ module ActiveRecord
78
63
  hash
79
64
  end
80
65
 
81
- def record_id(row)
82
- row[aliased_primary_key]
83
- end
84
-
85
- def instantiate(row)
86
- @cached_record[record_id(row)] ||= base_klass.instantiate(extract_record(row))
66
+ def instantiate(row, aliases)
67
+ base_klass.instantiate(extract_record(row, aliases))
87
68
  end
88
69
  end
89
70
  end