activerecord 3.2.22.5 → 4.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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  attr_reader :join_table
6
6
 
7
7
  def initialize(owner, reflection)
8
- @join_table = Arel::Table.new(reflection.options[:join_table])
8
+ @join_table = Arel::Table.new(reflection.join_table)
9
9
  super
10
10
  end
11
11
 
@@ -32,10 +32,6 @@ module ActiveRecord
32
32
  record
33
33
  end
34
34
 
35
- # ActiveRecord::Relation#delete_all needs to support joins before we can use a
36
- # SQL-only implementation.
37
- alias delete_all_on_destroy delete_all
38
-
39
35
  private
40
36
 
41
37
  def count_records
@@ -47,11 +43,17 @@ module ActiveRecord
47
43
  records = load_target if records == :all
48
44
  records.each { |record| owner.connection.delete(interpolate(sql, record)) }
49
45
  else
50
- relation = join_table
51
- stmt = relation.where(relation[reflection.foreign_key].eq(owner.id).
52
- and(relation[reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
53
- ).compile_delete
54
- owner.connection.delete stmt
46
+ relation = join_table
47
+ condition = relation[reflection.foreign_key].eq(owner.id)
48
+
49
+ unless records == :all
50
+ condition = condition.and(
51
+ relation[reflection.association_foreign_key]
52
+ .in(records.map { |x| x.id }.compact)
53
+ )
54
+ end
55
+
56
+ owner.connection.delete(relation.where(condition).compile_delete)
55
57
  end
56
58
  end
57
59
 
@@ -7,9 +7,30 @@ module ActiveRecord
7
7
  # is provided by its child HasManyThroughAssociation.
8
8
  class HasManyAssociation < CollectionAssociation #:nodoc:
9
9
 
10
+ def handle_dependency
11
+ case options[:dependent]
12
+ when :restrict, :restrict_with_exception
13
+ raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
14
+
15
+ when :restrict_with_error
16
+ unless empty?
17
+ record = klass.human_attribute_name(reflection.name).downcase
18
+ owner.errors.add(:base, :"restrict_dependent_destroy.many", record: record)
19
+ false
20
+ end
21
+
22
+ else
23
+ if options[:dependent] == :destroy
24
+ # No point in executing the counter update since we're going to destroy the parent anyway
25
+ load_target.each(&:mark_for_destruction)
26
+ end
27
+
28
+ delete_all
29
+ end
30
+ end
31
+
10
32
  def insert_record(record, validate = true, raise = false)
11
33
  set_owner_attributes(record)
12
- set_inverse_instance(record)
13
34
 
14
35
  if raise
15
36
  record.save!(:validate => validate)
@@ -39,7 +60,7 @@ module ActiveRecord
39
60
  elsif options[:counter_sql] || options[:finder_sql]
40
61
  reflection.klass.count_by_sql(custom_counter_sql)
41
62
  else
42
- scoped.count
63
+ scope.count
43
64
  end
44
65
 
45
66
  # If there's nothing in the database and @target has no new records
@@ -47,18 +68,18 @@ module ActiveRecord
47
68
  # documented side-effect of the method that may avoid an extra SELECT.
48
69
  @target ||= [] and loaded! if count == 0
49
70
 
50
- [options[:limit], count].compact.min
71
+ [association_scope.limit_value, count].compact.min
51
72
  end
52
73
 
53
- def has_cached_counter?(reflection = self.reflection)
74
+ def has_cached_counter?(reflection = reflection)
54
75
  owner.attribute_present?(cached_counter_attribute_name(reflection))
55
76
  end
56
77
 
57
- def cached_counter_attribute_name(reflection = self.reflection)
58
- "#{reflection.name}_count"
78
+ def cached_counter_attribute_name(reflection = reflection)
79
+ options[:counter_cache] || "#{reflection.name}_count"
59
80
  end
60
81
 
61
- def update_counter(difference, reflection = self.reflection)
82
+ def update_counter(difference, reflection = reflection)
62
83
  if has_cached_counter?(reflection)
63
84
  counter = cached_counter_attribute_name(reflection)
64
85
  owner.class.update_counters(owner.id, counter => difference)
@@ -77,7 +98,7 @@ module ActiveRecord
77
98
  # it will be decremented twice.
78
99
  #
79
100
  # Hence this method.
80
- def inverse_updates_counter_cache?(reflection = self.reflection)
101
+ def inverse_updates_counter_cache?(reflection = reflection)
81
102
  counter_name = cached_counter_attribute_name(reflection)
82
103
  reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
83
104
  inverse_reflection.counter_cache_column == counter_name
@@ -90,7 +111,12 @@ module ActiveRecord
90
111
  records.each { |r| r.destroy }
91
112
  update_counter(-records.length) unless inverse_updates_counter_cache?
92
113
  else
93
- scope = self.scoped.where(reflection.klass.primary_key => records)
114
+ if records == :all
115
+ scope = self.scope
116
+ else
117
+ keys = records.map { |r| r[reflection.association_primary_key] }
118
+ scope = self.scope.where(reflection.association_primary_key => keys)
119
+ end
94
120
 
95
121
  if method == :delete_all
96
122
  update_counter(-scope.delete_all)
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/object/blank'
2
1
 
3
2
  module ActiveRecord
4
3
  # = Active Record Has Many Through Association
@@ -38,6 +37,20 @@ module ActiveRecord
38
37
  super
39
38
  end
40
39
 
40
+ def concat_records(records)
41
+ ensure_not_nested
42
+
43
+ records = super
44
+
45
+ if owner.new_record? && records
46
+ records.flatten.each do |record|
47
+ build_through_record(record)
48
+ end
49
+ end
50
+
51
+ records
52
+ end
53
+
41
54
  def insert_record(record, validate = true, raise = false)
42
55
  ensure_not_nested
43
56
 
@@ -54,10 +67,6 @@ module ActiveRecord
54
67
  record
55
68
  end
56
69
 
57
- # ActiveRecord::Relation#delete_all needs to support joins before we can use a
58
- # SQL-only implementation.
59
- alias delete_all_on_destroy delete_all
60
-
61
70
  private
62
71
 
63
72
  def through_association
@@ -87,10 +96,10 @@ module ActiveRecord
87
96
  @through_records.delete(record.object_id)
88
97
  end
89
98
 
90
- def build_record(attributes, options = {})
99
+ def build_record(attributes)
91
100
  ensure_not_nested
92
101
 
93
- record = super(attributes, options)
102
+ record = super(attributes)
94
103
 
95
104
  inverse = source_reflection.inverse_of
96
105
  if inverse
@@ -105,11 +114,7 @@ module ActiveRecord
105
114
  end
106
115
 
107
116
  def target_reflection_has_associated_record?
108
- if through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?
109
- false
110
- else
111
- true
112
- end
117
+ !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?)
113
118
  end
114
119
 
115
120
  def update_through_counter?(method)
@@ -126,7 +131,12 @@ module ActiveRecord
126
131
  def delete_records(records, method)
127
132
  ensure_not_nested
128
133
 
129
- scope = through_association.scoped.where(construct_join_attributes(*records))
134
+ # This is unoptimised; it will load all the target records
135
+ # even when we just want to delete everything.
136
+ records = load_target if records == :all
137
+
138
+ scope = through_association.scope
139
+ scope.where! construct_join_attributes(*records)
130
140
 
131
141
  case method
132
142
  when :destroy
@@ -175,7 +185,7 @@ module ActiveRecord
175
185
 
176
186
  def find_target
177
187
  return [] unless target_reflection_has_associated_record?
178
- scoped.all
188
+ scope.to_a
179
189
  end
180
190
 
181
191
  # NOTE - not sure that we can actually cope with inverses here
@@ -1,9 +1,26 @@
1
- require 'active_support/core_ext/object/inclusion'
2
1
 
3
2
  module ActiveRecord
4
3
  # = Active Record Belongs To Has One Association
5
4
  module Associations
6
5
  class HasOneAssociation < SingularAssociation #:nodoc:
6
+
7
+ def handle_dependency
8
+ case options[:dependent]
9
+ when :restrict, :restrict_with_exception
10
+ raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target
11
+
12
+ when :restrict_with_error
13
+ if load_target
14
+ record = klass.human_attribute_name(reflection.name).downcase
15
+ owner.errors.add(:base, :"restrict_dependent_destroy.one", record: record)
16
+ false
17
+ end
18
+
19
+ else
20
+ delete
21
+ end
22
+ end
23
+
7
24
  def replace(record, save = true)
8
25
  raise_on_type_mismatch(record) if record
9
26
  load_target
@@ -13,11 +30,11 @@ module ActiveRecord
13
30
  if (target || record) && target != record
14
31
  transaction_if(save) do
15
32
  remove_target!(options[:dependent]) if target && !target.destroyed?
16
-
33
+
17
34
  if record
18
35
  set_owner_attributes(record)
19
36
  set_inverse_instance(record)
20
-
37
+
21
38
  if owner.persisted? && save && !record.save
22
39
  nullify_owner_attributes(record)
23
40
  set_owner_attributes(target) if target
@@ -38,7 +55,7 @@ module ActiveRecord
38
55
  when :destroy
39
56
  target.destroy
40
57
  when :nullify
41
- target.update_attribute(reflection.foreign_key, nil)
58
+ target.update_columns(reflection.foreign_key => nil)
42
59
  end
43
60
  end
44
61
  end
@@ -54,16 +71,19 @@ module ActiveRecord
54
71
  end
55
72
 
56
73
  def remove_target!(method)
57
- if method.in?([:delete, :destroy])
58
- target.send(method)
59
- else
60
- nullify_owner_attributes(target)
74
+ case method
75
+ when :delete
76
+ target.delete
77
+ when :destroy
78
+ target.destroy
79
+ else
80
+ nullify_owner_attributes(target)
61
81
 
62
- if target.persisted? && owner.persisted? && !target.save
63
- set_owner_attributes(target)
64
- raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
65
- "The record failed to save when after its foreign key was set to nil."
66
- end
82
+ if target.persisted? && owner.persisted? && !target.save
83
+ set_owner_attributes(target)
84
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
85
+ "The record failed to save after its foreign key was set to nil."
86
+ end
67
87
  end
68
88
  end
69
89
 
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  attributes = construct_join_attributes(record)
24
24
 
25
25
  if through_record
26
- through_record.update_attributes(attributes)
26
+ through_record.update(attributes)
27
27
  elsif owner.new_record?
28
28
  through_proxy.build(attributes)
29
29
  else
@@ -68,7 +68,7 @@ module ActiveRecord
68
68
  remove_duplicate_results!(base, records, association)
69
69
  end
70
70
  when Hash
71
- associations.keys.each do |name|
71
+ associations.each_key do |name|
72
72
  reflection = base.reflections[name]
73
73
  remove_uniq_by_reflection(reflection, records)
74
74
 
@@ -109,7 +109,7 @@ module ActiveRecord
109
109
  case associations
110
110
  when Symbol, String
111
111
  reflection = parent.reflections[associations.to_s.intern] or
112
- raise ConfigurationError, "Association named '#{ associations }' was not found on #{parent.active_record.name}; perhaps you misspelled it?"
112
+ raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
113
113
  unless join_association = find_join_association(reflection, parent)
114
114
  @reflections << reflection
115
115
  join_association = build_join_association(reflection, parent)
@@ -55,12 +55,7 @@ module ActiveRecord
55
55
 
56
56
  def find_parent_in(other_join_dependency)
57
57
  other_join_dependency.join_parts.detect do |join_part|
58
- case parent
59
- when JoinBase
60
- parent.active_record == join_part.active_record
61
- else
62
- parent == join_part
63
- end
58
+ parent == join_part
64
59
  end
65
60
  end
66
61
 
@@ -97,11 +92,21 @@ module ActiveRecord
97
92
 
98
93
  constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
99
94
 
100
- conditions = self.conditions[i].dup
101
- conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
95
+ scope_chain_items = scope_chain[i]
96
+
97
+ if reflection.type
98
+ scope_chain_items += [
99
+ ActiveRecord::Relation.new(reflection.klass, table)
100
+ .where(reflection.type => foreign_klass.base_class.name)
101
+ ]
102
+ end
103
+
104
+ scope_chain_items.each do |item|
105
+ unless item.is_a?(Relation)
106
+ item = ActiveRecord::Relation.new(reflection.klass, table).instance_exec(self, &item)
107
+ end
102
108
 
103
- unless conditions.empty?
104
- constraint = constraint.and(sanitize(conditions, table))
109
+ constraint = constraint.and(item.arel.constraints) unless item.arel.constraints.empty?
105
110
  end
106
111
 
107
112
  relation.from(join(table, constraint))
@@ -139,18 +144,8 @@ module ActiveRecord
139
144
  table.table_alias || table.name
140
145
  end
141
146
 
142
- def conditions
143
- @conditions ||= reflection.conditions.reverse
144
- end
145
-
146
- private
147
-
148
- def interpolate(conditions)
149
- if conditions.respond_to?(:to_proc) && !conditions.is_a?(Hash)
150
- instance_eval(&conditions)
151
- else
152
- conditions
153
- end
147
+ def scope_chain
148
+ @scope_chain ||= reflection.scope_chain.reverse
154
149
  end
155
150
 
156
151
  end
@@ -54,7 +54,7 @@ module ActiveRecord
54
54
  unless @column_names_with_alias
55
55
  @column_names_with_alias = []
56
56
 
57
- ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
57
+ ([primary_key] + (column_names - [primary_key])).compact.each_with_index do |column_name, i|
58
58
  @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
59
59
  end
60
60
  end
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
 
20
20
  if reflection.source_macro == :has_and_belongs_to_many
21
21
  tables << alias_tracker.aliased_table_for(
22
- (reflection.source_reflection || reflection).options[:join_table],
22
+ (reflection.source_reflection || reflection).join_table,
23
23
  table_alias_for(reflection, true)
24
24
  )
25
25
  end
@@ -40,16 +40,6 @@ module ActiveRecord
40
40
  def join(table, constraint)
41
41
  table.create_join(table, table.create_on(constraint), join_type)
42
42
  end
43
-
44
- def sanitize(conditions, table)
45
- conditions = conditions.map do |condition|
46
- condition = active_record.send(:sanitize_sql, interpolate(condition), table.table_alias || table.name)
47
- condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
48
- condition
49
- end
50
-
51
- conditions.length == 1 ? conditions.first : Arel::Nodes::And.new(conditions)
52
- end
53
43
  end
54
44
  end
55
45
  end
@@ -12,7 +12,7 @@ module ActiveRecord
12
12
  # and all of its books via a single query:
13
13
  #
14
14
  # SELECT * FROM authors
15
- # LEFT OUTER JOIN books ON authors.id = books.id
15
+ # LEFT OUTER JOIN books ON authors.id = books.author_id
16
16
  # WHERE authors.name = 'Ken Akamatsu'
17
17
  #
18
18
  # However, this could result in many rows that contain redundant data. After
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
47
47
  end
48
48
 
49
- attr_reader :records, :associations, :options, :model
49
+ attr_reader :records, :associations, :preload_scope, :model
50
50
 
51
51
  # Eager loads the named associations for the given Active Record record(s).
52
52
  #
@@ -72,7 +72,7 @@ module ActiveRecord
72
72
  # books.
73
73
  # - a Hash which specifies multiple association names, as well as
74
74
  # association names for the to-be-preloaded association objects. For
75
- # example, specifying <tt>{ :author => :avatar }</tt> will preload a
75
+ # example, specifying <tt>{ author: :avatar }</tt> will preload a
76
76
  # book's author, as well as that author's avatar.
77
77
  #
78
78
  # +:associations+ has the same format as the +:include+ option for
@@ -80,17 +80,12 @@ module ActiveRecord
80
80
  #
81
81
  # :books
82
82
  # [ :books, :author ]
83
- # { :author => :avatar }
84
- # [ :books, { :author => :avatar } ]
85
- #
86
- # +options+ contains options that will be passed to ActiveRecord::Base#find
87
- # (which is called under the hood for preloading records). But it is passed
88
- # only one level deep in the +associations+ argument, i.e. it's not passed
89
- # to the child associations when +associations+ is a Hash.
90
- def initialize(records, associations, options = {})
91
- @records = Array.wrap(records).compact.uniq
92
- @associations = Array.wrap(associations)
93
- @options = options
83
+ # { author: :avatar }
84
+ # [ :books, { author: :avatar } ]
85
+ def initialize(records, associations, preload_scope = nil)
86
+ @records = Array.wrap(records).compact.uniq
87
+ @associations = Array.wrap(associations)
88
+ @preload_scope = preload_scope || Relation.new(nil, nil)
94
89
  end
95
90
 
96
91
  def run
@@ -105,7 +100,9 @@ module ActiveRecord
105
100
  case association
106
101
  when Hash
107
102
  preload_hash(association)
108
- when String, Symbol
103
+ when Symbol
104
+ preload_one(association)
105
+ when String
109
106
  preload_one(association.to_sym)
110
107
  else
111
108
  raise ArgumentError, "#{association.inspect} was not recognised for preload"
@@ -114,7 +111,7 @@ module ActiveRecord
114
111
 
115
112
  def preload_hash(association)
116
113
  association.each do |parent, child|
117
- Preloader.new(records, parent, options).run
114
+ Preloader.new(records, parent, preload_scope).run
118
115
  Preloader.new(records.map { |record| record.send(parent) }.flatten, child).run
119
116
  end
120
117
  end
@@ -129,7 +126,7 @@ module ActiveRecord
129
126
  def preload_one(association)
130
127
  grouped_records(association).each do |reflection, klasses|
131
128
  klasses.each do |klass, records|
132
- preloader_for(reflection).new(klass, records, reflection, options).run
129
+ preloader_for(reflection).new(klass, records, reflection, preload_scope).run
133
130
  end
134
131
  end
135
132
  end