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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- 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.
|
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
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
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
|
-
[
|
71
|
+
[association_scope.limit_value, count].compact.min
|
51
72
|
end
|
52
73
|
|
53
|
-
def has_cached_counter?(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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
99
|
+
def build_record(attributes)
|
91
100
|
ensure_not_nested
|
92
101
|
|
93
|
-
record = super(attributes
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
|
@@ -68,7 +68,7 @@ module ActiveRecord
|
|
68
68
|
remove_duplicate_results!(base, records, association)
|
69
69
|
end
|
70
70
|
when Hash
|
71
|
-
associations.
|
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
|
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
|
-
|
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
|
-
|
101
|
-
|
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
|
-
|
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
|
143
|
-
@
|
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).
|
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.
|
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, :
|
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>{ :
|
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
|
-
# { :
|
84
|
-
# [ :books, { :
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
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,
|
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,
|
129
|
+
preloader_for(reflection).new(klass, records, reflection, preload_scope).run
|
133
130
|
end
|
134
131
|
end
|
135
132
|
end
|