activerecord 4.1.16 → 4.2.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 +634 -2185
- data/README.rdoc +15 -10
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +51 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module ActiveRecord
|
3
2
|
# = Active Record Has Many Through Association
|
4
3
|
module Associations
|
@@ -12,13 +11,14 @@ module ActiveRecord
|
|
12
11
|
@through_association = nil
|
13
12
|
end
|
14
13
|
|
15
|
-
# Returns the size of the collection by executing a SELECT COUNT(*) query
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
14
|
+
# Returns the size of the collection by executing a SELECT COUNT(*) query
|
15
|
+
# if the collection hasn't been loaded, and by calling collection.size if
|
16
|
+
# it has. If the collection will likely have a size greater than zero,
|
17
|
+
# and if fetching the collection will be needed afterwards, one less
|
18
|
+
# SELECT query will be generated by using #length instead.
|
19
19
|
def size
|
20
20
|
if has_cached_counter?
|
21
|
-
owner.
|
21
|
+
owner.read_attribute cached_counter_attribute_name(reflection)
|
22
22
|
elsif loaded?
|
23
23
|
target.size
|
24
24
|
else
|
@@ -62,7 +62,15 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
|
64
64
|
save_through_record(record)
|
65
|
-
|
65
|
+
if has_cached_counter? && !through_reflection_updates_counter_cache?
|
66
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
67
|
+
Automatic updating of counter caches on through associations has been
|
68
|
+
deprecated, and will be removed in Rails 5.0. Instead, please set the
|
69
|
+
appropriate counter_cache options on the has_many and belongs_to for
|
70
|
+
your associations to #{through_reflection.name}.
|
71
|
+
MESSAGE
|
72
|
+
update_counter_in_database(1)
|
73
|
+
end
|
66
74
|
record
|
67
75
|
end
|
68
76
|
|
@@ -72,13 +80,11 @@ module ActiveRecord
|
|
72
80
|
@through_association ||= owner.association(through_reflection.name)
|
73
81
|
end
|
74
82
|
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# want to use the exact same object.
|
83
|
+
# The through record (built with build_record) is temporarily cached
|
84
|
+
# so that it may be reused if insert_record is subsequently called.
|
78
85
|
#
|
79
|
-
# However, after insert_record has been called,
|
80
|
-
#
|
81
|
-
# association
|
86
|
+
# However, after insert_record has been called, the cache is cleared in
|
87
|
+
# order to allow multiple instances of the same record in an association.
|
82
88
|
def build_through_record(record)
|
83
89
|
@through_records[record.object_id] ||= begin
|
84
90
|
ensure_mutable
|
@@ -112,9 +118,9 @@ module ActiveRecord
|
|
112
118
|
|
113
119
|
inverse = source_reflection.inverse_of
|
114
120
|
if inverse
|
115
|
-
if inverse.
|
121
|
+
if inverse.collection?
|
116
122
|
record.send(inverse.name) << build_through_record(record)
|
117
|
-
elsif inverse.
|
123
|
+
elsif inverse.has_one?
|
118
124
|
record.send("#{inverse.name}=", build_through_record(record))
|
119
125
|
end
|
120
126
|
end
|
@@ -123,7 +129,7 @@ module ActiveRecord
|
|
123
129
|
end
|
124
130
|
|
125
131
|
def target_reflection_has_associated_record?
|
126
|
-
!(through_reflection.
|
132
|
+
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
|
127
133
|
end
|
128
134
|
|
129
135
|
def update_through_counter?(method)
|
@@ -137,13 +143,13 @@ module ActiveRecord
|
|
137
143
|
end
|
138
144
|
end
|
139
145
|
|
146
|
+
def delete_or_nullify_all_records(method)
|
147
|
+
delete_records(load_target, method)
|
148
|
+
end
|
149
|
+
|
140
150
|
def delete_records(records, method)
|
141
151
|
ensure_not_nested
|
142
152
|
|
143
|
-
# This is unoptimised; it will load all the target records
|
144
|
-
# even when we just want to delete everything.
|
145
|
-
records = load_target if records == :all
|
146
|
-
|
147
153
|
scope = through_association.scope
|
148
154
|
scope.where! construct_join_attributes(*records)
|
149
155
|
|
@@ -177,7 +183,7 @@ module ActiveRecord
|
|
177
183
|
klass.decrement_counter counter, records.map(&:id)
|
178
184
|
end
|
179
185
|
|
180
|
-
if through_reflection.
|
186
|
+
if through_reflection.collection? && update_through_counter?(method)
|
181
187
|
update_counter(-count, through_reflection)
|
182
188
|
end
|
183
189
|
|
@@ -198,7 +204,7 @@ module ActiveRecord
|
|
198
204
|
records.each do |record|
|
199
205
|
through_records = through_records_for(record)
|
200
206
|
|
201
|
-
if through_reflection.
|
207
|
+
if through_reflection.collection?
|
202
208
|
through_records.each { |r| through_association.target.delete(r) }
|
203
209
|
else
|
204
210
|
if through_records.include?(through_association.target)
|
@@ -212,13 +218,18 @@ module ActiveRecord
|
|
212
218
|
|
213
219
|
def find_target
|
214
220
|
return [] unless target_reflection_has_associated_record?
|
215
|
-
|
221
|
+
get_records
|
216
222
|
end
|
217
223
|
|
218
224
|
# NOTE - not sure that we can actually cope with inverses here
|
219
225
|
def invertible_for?(record)
|
220
226
|
false
|
221
227
|
end
|
228
|
+
|
229
|
+
def through_reflection_updates_counter_cache?
|
230
|
+
counter_name = cached_counter_attribute_name
|
231
|
+
inverse_updates_counter_named?(counter_name, through_reflection)
|
232
|
+
end
|
222
233
|
end
|
223
234
|
end
|
224
235
|
end
|
@@ -131,7 +131,6 @@ module ActiveRecord
|
|
131
131
|
|
132
132
|
def instantiate(result_set, aliases)
|
133
133
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
134
|
-
type_caster = result_set.column_type primary_key
|
135
134
|
|
136
135
|
seen = Hash.new { |h,parent_klass|
|
137
136
|
h[parent_klass] = Hash.new { |i,parent_id|
|
@@ -144,9 +143,7 @@ module ActiveRecord
|
|
144
143
|
column_aliases = aliases.column_aliases join_root
|
145
144
|
|
146
145
|
result_set.each { |row_hash|
|
147
|
-
|
148
|
-
primary_id = type_caster.type_cast parent_key
|
149
|
-
parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases)
|
146
|
+
parent = parents[row_hash[primary_key]] ||= join_root.instantiate(row_hash, column_aliases)
|
150
147
|
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
151
148
|
}
|
152
149
|
|
@@ -165,17 +162,17 @@ module ActiveRecord
|
|
165
162
|
def make_outer_joins(parent, child)
|
166
163
|
tables = table_aliases_for(parent, child)
|
167
164
|
join_type = Arel::Nodes::OuterJoin
|
168
|
-
|
165
|
+
info = make_constraints parent, child, tables, join_type
|
169
166
|
|
170
|
-
|
167
|
+
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
171
168
|
end
|
172
169
|
|
173
170
|
def make_inner_joins(parent, child)
|
174
171
|
tables = child.tables
|
175
172
|
join_type = Arel::Nodes::InnerJoin
|
176
|
-
|
173
|
+
info = make_constraints parent, child, tables, join_type
|
177
174
|
|
178
|
-
|
175
|
+
[info] + child.children.flat_map { |c| make_inner_joins(child, c) }
|
179
176
|
end
|
180
177
|
|
181
178
|
def table_aliases_for(parent, node)
|
@@ -216,8 +213,9 @@ module ActiveRecord
|
|
216
213
|
associations.map do |name, right|
|
217
214
|
reflection = find_reflection base_klass, name
|
218
215
|
reflection.check_validity!
|
216
|
+
reflection.check_eager_loadable!
|
219
217
|
|
220
|
-
if reflection.
|
218
|
+
if reflection.polymorphic?
|
221
219
|
raise EagerLoadPolymorphicError.new(reflection)
|
222
220
|
end
|
223
221
|
|
@@ -21,8 +21,11 @@ module ActiveRecord
|
|
21
21
|
super && reflection == other.reflection
|
22
22
|
end
|
23
23
|
|
24
|
+
JoinInformation = Struct.new :joins, :binds
|
25
|
+
|
24
26
|
def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
|
25
27
|
joins = []
|
28
|
+
bind_values = []
|
26
29
|
tables = tables.reverse
|
27
30
|
|
28
31
|
scope_chain_index = 0
|
@@ -34,14 +37,9 @@ module ActiveRecord
|
|
34
37
|
table = tables.shift
|
35
38
|
klass = reflection.klass
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
foreign_key = reflection.foreign_key
|
41
|
-
else
|
42
|
-
key = reflection.foreign_key
|
43
|
-
foreign_key = reflection.active_record_primary_key
|
44
|
-
end
|
40
|
+
join_keys = reflection.join_keys(klass)
|
41
|
+
key = join_keys.key
|
42
|
+
foreign_key = join_keys.foreign_key
|
45
43
|
|
46
44
|
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
47
45
|
|
@@ -60,21 +58,27 @@ module ActiveRecord
|
|
60
58
|
left.merge right
|
61
59
|
end
|
62
60
|
|
63
|
-
if reflection.type
|
64
|
-
constraint = constraint.and table[reflection.type].eq foreign_klass.base_class.name
|
65
|
-
end
|
66
|
-
|
67
61
|
if rel && !rel.arel.constraints.empty?
|
62
|
+
bind_values.concat rel.bind_values
|
68
63
|
constraint = constraint.and rel.arel.constraints
|
69
64
|
end
|
70
65
|
|
66
|
+
if reflection.type
|
67
|
+
value = foreign_klass.base_class.name
|
68
|
+
column = klass.columns_hash[column.to_s]
|
69
|
+
|
70
|
+
substitute = klass.connection.substitute_at(column, bind_values.length)
|
71
|
+
bind_values.push [column, value]
|
72
|
+
constraint = constraint.and table[reflection.type].eq substitute
|
73
|
+
end
|
74
|
+
|
71
75
|
joins << table.create_join(table, table.create_on(constraint), join_type)
|
72
76
|
|
73
77
|
# The current table in this iteration becomes the foreign table in the next
|
74
78
|
foreign_table, foreign_klass = table, klass
|
75
79
|
end
|
76
80
|
|
77
|
-
joins
|
81
|
+
JoinInformation.new joins, bind_values
|
78
82
|
end
|
79
83
|
|
80
84
|
# Builds equality condition.
|
@@ -86,7 +90,7 @@ module ActiveRecord
|
|
86
90
|
# end
|
87
91
|
#
|
88
92
|
# If I execute `Physician.joins(:appointments).to_a` then
|
89
|
-
#
|
93
|
+
# klass # => Physician
|
90
94
|
# table # => #<Arel::Table @name="appointments" ...>
|
91
95
|
# key # => physician_id
|
92
96
|
# foreign_table # => #<Arel::Table @name="physicians" ...>
|
@@ -80,7 +80,7 @@ module ActiveRecord
|
|
80
80
|
# { author: :avatar }
|
81
81
|
# [ :books, { author: :avatar } ]
|
82
82
|
|
83
|
-
NULL_RELATION = Struct.new(:values).new({})
|
83
|
+
NULL_RELATION = Struct.new(:values, :bind_values).new({}, [])
|
84
84
|
|
85
85
|
def preload(records, associations, preload_scope = nil)
|
86
86
|
records = Array.wrap(records).compact.uniq
|
@@ -169,7 +169,6 @@ module ActiveRecord
|
|
169
169
|
class NullPreloader
|
170
170
|
def self.new(klass, owners, reflection, preload_scope); self; end
|
171
171
|
def self.run(preloader); end
|
172
|
-
def self.preloaded_records; []; end
|
173
172
|
end
|
174
173
|
|
175
174
|
def preloader_for(reflection, owners, rhs_klass)
|
@@ -178,6 +177,7 @@ module ActiveRecord
|
|
178
177
|
if owners.first.association(reflection.name).loaded?
|
179
178
|
return AlreadyLoaded
|
180
179
|
end
|
180
|
+
reflection.check_preloadable!
|
181
181
|
|
182
182
|
case reflection.macro
|
183
183
|
when :has_many
|
@@ -104,13 +104,11 @@ module ActiveRecord
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def association_key_type
|
107
|
-
|
108
|
-
column && column.type
|
107
|
+
@klass.type_for_attribute(association_key_name.to_s).type
|
109
108
|
end
|
110
109
|
|
111
110
|
def owner_key_type
|
112
|
-
|
113
|
-
column && column.type
|
111
|
+
@model.type_for_attribute(owner_key_name.to_s).type
|
114
112
|
end
|
115
113
|
|
116
114
|
def load_slices(slices)
|
@@ -134,10 +132,13 @@ module ActiveRecord
|
|
134
132
|
scope = klass.unscoped
|
135
133
|
|
136
134
|
values = reflection_scope.values
|
135
|
+
reflection_binds = reflection_scope.bind_values
|
137
136
|
preload_values = preload_scope.values
|
137
|
+
preload_binds = preload_scope.bind_values
|
138
138
|
|
139
139
|
scope.where_values = Array(values[:where]) + Array(preload_values[:where])
|
140
140
|
scope.references_values = Array(values[:references]) + Array(preload_values[:references])
|
141
|
+
scope.bind_values = (reflection_binds + preload_binds)
|
141
142
|
|
142
143
|
scope._select! preload_values[:select] || values[:select] || table[Arel.star]
|
143
144
|
scope.includes! preload_values[:includes] || values[:includes]
|
@@ -150,11 +151,14 @@ module ActiveRecord
|
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
154
|
+
if preload_values[:readonly] || values[:readonly]
|
155
|
+
scope.readonly!
|
156
|
+
end
|
157
|
+
|
153
158
|
if options[:as]
|
154
159
|
scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
|
155
160
|
end
|
156
161
|
|
157
|
-
scope.unscope_values = Array(values[:unscope])
|
158
162
|
klass.default_scoped.merge(scope)
|
159
163
|
end
|
160
164
|
end
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
23
23
|
|
24
24
|
reset_association owners, through_reflection.name
|
25
25
|
|
26
|
-
middle_records = through_records.
|
26
|
+
middle_records = through_records.flat_map { |(_,rec)| rec }
|
27
27
|
|
28
28
|
preloaders = preloader.preload(middle_records,
|
29
29
|
source_reflection.name,
|
@@ -63,7 +63,7 @@ module ActiveRecord
|
|
63
63
|
should_reset = (through_scope != through_reflection.klass.unscoped) ||
|
64
64
|
(reflection.options[:source_type] && through_reflection.collection?)
|
65
65
|
|
66
|
-
#
|
66
|
+
# Don't cache the association - we would only be caching a subset
|
67
67
|
if should_reset
|
68
68
|
owners.each { |owner|
|
69
69
|
owner.association(association_name).reset
|
@@ -84,7 +84,7 @@ module ActiveRecord
|
|
84
84
|
end
|
85
85
|
|
86
86
|
scope.references! reflection_scope.values[:references]
|
87
|
-
scope.order
|
87
|
+
scope = scope.order reflection_scope.values[:order] if scope.eager_loading?
|
88
88
|
end
|
89
89
|
|
90
90
|
scope
|
@@ -38,8 +38,23 @@ module ActiveRecord
|
|
38
38
|
scope.scope_for_create.stringify_keys.except(klass.primary_key)
|
39
39
|
end
|
40
40
|
|
41
|
+
def get_records
|
42
|
+
return scope.limit(1).to_a if reflection.scope_chain.any?(&:any?)
|
43
|
+
|
44
|
+
conn = klass.connection
|
45
|
+
sc = reflection.association_scope_cache(conn, owner) do
|
46
|
+
StatementCache.create(conn) { |params|
|
47
|
+
as = AssociationScope.create { params.bind }
|
48
|
+
target_scope.merge(as.scope(self, conn)).limit(1)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
53
|
+
sc.execute binds, klass, klass.connection
|
54
|
+
end
|
55
|
+
|
41
56
|
def find_target
|
42
|
-
if record =
|
57
|
+
if record = get_records.first
|
43
58
|
set_inverse_instance record
|
44
59
|
end
|
45
60
|
end
|
@@ -3,7 +3,7 @@ module ActiveRecord
|
|
3
3
|
module Associations
|
4
4
|
module ThroughAssociation #:nodoc:
|
5
5
|
|
6
|
-
delegate :source_reflection, :through_reflection, :
|
6
|
+
delegate :source_reflection, :through_reflection, :to => :reflection
|
7
7
|
|
8
8
|
protected
|
9
9
|
|
@@ -13,13 +13,9 @@ module ActiveRecord
|
|
13
13
|
# 2. To get the type conditions for any STI models in the chain
|
14
14
|
def target_scope
|
15
15
|
scope = super
|
16
|
-
chain.drop(1).each do |reflection|
|
16
|
+
reflection.chain.drop(1).each do |reflection|
|
17
17
|
relation = reflection.klass.all
|
18
|
-
|
19
|
-
reflection_scope = reflection.scope
|
20
|
-
if reflection_scope && reflection_scope.arity.zero?
|
21
|
-
relation.merge!(reflection_scope)
|
22
|
-
end
|
18
|
+
relation.merge!(reflection.scope) if reflection.scope
|
23
19
|
|
24
20
|
scope.merge!(
|
25
21
|
relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
|
@@ -71,18 +67,17 @@ module ActiveRecord
|
|
71
67
|
# Note: this does not capture all cases, for example it would be crazy to try to
|
72
68
|
# properly support stale-checking for nested associations.
|
73
69
|
def stale_state
|
74
|
-
if through_reflection.
|
70
|
+
if through_reflection.belongs_to?
|
75
71
|
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
|
76
72
|
end
|
77
73
|
end
|
78
74
|
|
79
75
|
def foreign_key_present?
|
80
|
-
through_reflection.
|
81
|
-
!owner[through_reflection.foreign_key].nil?
|
76
|
+
through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
|
82
77
|
end
|
83
78
|
|
84
79
|
def ensure_mutable
|
85
|
-
|
80
|
+
unless source_reflection.belongs_to?
|
86
81
|
raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
|
87
82
|
end
|
88
83
|
end
|
@@ -92,17 +87,6 @@ module ActiveRecord
|
|
92
87
|
raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
|
93
88
|
end
|
94
89
|
end
|
95
|
-
|
96
|
-
def build_record(attributes)
|
97
|
-
inverse = source_reflection.inverse_of
|
98
|
-
target = through_association.target
|
99
|
-
|
100
|
-
if inverse && target && !target.is_a?(Array)
|
101
|
-
attributes[inverse.foreign_key] = target.id
|
102
|
-
end
|
103
|
-
|
104
|
-
super(attributes)
|
105
|
-
end
|
106
90
|
end
|
107
91
|
end
|
108
92
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Attribute # :nodoc:
|
3
|
+
class << self
|
4
|
+
def from_database(name, value, type)
|
5
|
+
FromDatabase.new(name, value, type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_user(name, value, type)
|
9
|
+
FromUser.new(name, value, type)
|
10
|
+
end
|
11
|
+
|
12
|
+
def null(name)
|
13
|
+
Null.new(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def uninitialized(name, type)
|
17
|
+
Uninitialized.new(name, type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :name, :value_before_type_cast, :type
|
22
|
+
|
23
|
+
# This method should not be called directly.
|
24
|
+
# Use #from_database or #from_user
|
25
|
+
def initialize(name, value_before_type_cast, type)
|
26
|
+
@name = name
|
27
|
+
@value_before_type_cast = value_before_type_cast
|
28
|
+
@type = type
|
29
|
+
end
|
30
|
+
|
31
|
+
def value
|
32
|
+
# `defined?` is cheaper than `||=` when we get back falsy values
|
33
|
+
@value = original_value unless defined?(@value)
|
34
|
+
@value
|
35
|
+
end
|
36
|
+
|
37
|
+
def original_value
|
38
|
+
type_cast(value_before_type_cast)
|
39
|
+
end
|
40
|
+
|
41
|
+
def value_for_database
|
42
|
+
type.type_cast_for_database(value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def changed_from?(old_value)
|
46
|
+
type.changed?(old_value, value, value_before_type_cast)
|
47
|
+
end
|
48
|
+
|
49
|
+
def changed_in_place_from?(old_value)
|
50
|
+
type.changed_in_place?(old_value, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_value_from_user(value)
|
54
|
+
self.class.from_user(name, value, type)
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_value_from_database(value)
|
58
|
+
self.class.from_database(name, value, type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def type_cast(*)
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialized?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
self.class == other.class &&
|
71
|
+
name == other.name &&
|
72
|
+
value_before_type_cast == other.value_before_type_cast &&
|
73
|
+
type == other.type
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def initialize_dup(other)
|
79
|
+
if defined?(@value) && @value.duplicable?
|
80
|
+
@value = @value.dup
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class FromDatabase < Attribute # :nodoc:
|
85
|
+
def type_cast(value)
|
86
|
+
type.type_cast_from_database(value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class FromUser < Attribute # :nodoc:
|
91
|
+
def type_cast(value)
|
92
|
+
type.type_cast_from_user(value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Null < Attribute # :nodoc:
|
97
|
+
def initialize(name)
|
98
|
+
super(name, nil, Type::Value.new)
|
99
|
+
end
|
100
|
+
|
101
|
+
def value
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def with_value_from_database(value)
|
106
|
+
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
|
107
|
+
end
|
108
|
+
alias_method :with_value_from_user, :with_value_from_database
|
109
|
+
end
|
110
|
+
|
111
|
+
class Uninitialized < Attribute # :nodoc:
|
112
|
+
def initialize(name, type)
|
113
|
+
super(name, nil, type)
|
114
|
+
end
|
115
|
+
|
116
|
+
def value
|
117
|
+
if block_given?
|
118
|
+
yield name
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def value_for_database
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialized?
|
126
|
+
false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
|
130
|
+
end
|
131
|
+
end
|