activerecord 4.1.15 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1162 -1792
- data/README.rdoc +15 -10
- data/lib/active_record.rb +4 -0
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- 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 +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/attribute.rb +163 -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 +56 -94
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -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 +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -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 +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration.rb +71 -46
- 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 +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- 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 +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -57,20 +57,10 @@ module ActiveRecord
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def aliased_table_for(table_name, aliased_name)
|
60
|
-
table_alias = aliased_name_for(table_name, aliased_name)
|
61
|
-
|
62
|
-
if table_alias == table_name
|
63
|
-
Arel::Table.new(table_name)
|
64
|
-
else
|
65
|
-
Arel::Table.new(table_name).alias(table_alias)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def aliased_name_for(table_name, aliased_name)
|
70
60
|
if aliases[table_name].zero?
|
71
61
|
# If it's zero, we can have our table_name
|
72
62
|
aliases[table_name] = 1
|
73
|
-
table_name
|
63
|
+
Arel::Table.new(table_name)
|
74
64
|
else
|
75
65
|
# Otherwise, we need to use an alias
|
76
66
|
aliased_name = connection.table_alias_for(aliased_name)
|
@@ -78,11 +68,12 @@ module ActiveRecord
|
|
78
68
|
# Update the count
|
79
69
|
aliases[aliased_name] += 1
|
80
70
|
|
81
|
-
if aliases[aliased_name] > 1
|
71
|
+
table_alias = if aliases[aliased_name] > 1
|
82
72
|
"#{truncate(aliased_name)}_#{aliases[aliased_name]}"
|
83
73
|
else
|
84
74
|
aliased_name
|
85
75
|
end
|
76
|
+
Arel::Table.new(table_name).alias(table_alias)
|
86
77
|
end
|
87
78
|
end
|
88
79
|
|
@@ -179,7 +179,7 @@ module ActiveRecord
|
|
179
179
|
def creation_attributes
|
180
180
|
attributes = {}
|
181
181
|
|
182
|
-
if (reflection.
|
182
|
+
if (reflection.has_one? || reflection.collection?) && !options[:through]
|
183
183
|
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
184
184
|
|
185
185
|
if reflection.options[:as]
|
@@ -211,9 +211,12 @@ module ActiveRecord
|
|
211
211
|
# the kind of the class of the associated objects. Meant to be used as
|
212
212
|
# a sanity check when you are about to assign an associated record.
|
213
213
|
def raise_on_type_mismatch!(record)
|
214
|
-
unless record.is_a?(reflection.klass)
|
215
|
-
|
216
|
-
|
214
|
+
unless record.is_a?(reflection.klass)
|
215
|
+
fresh_class = reflection.class_name.safe_constantize
|
216
|
+
unless fresh_class && record.is_a?(fresh_class)
|
217
|
+
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
|
218
|
+
raise ActiveRecord::AssociationTypeMismatch, message
|
219
|
+
end
|
217
220
|
end
|
218
221
|
end
|
219
222
|
|
@@ -248,6 +251,15 @@ module ActiveRecord
|
|
248
251
|
initialize_attributes(record)
|
249
252
|
end
|
250
253
|
end
|
254
|
+
|
255
|
+
# Returns true if statement cache should be skipped on the association reader.
|
256
|
+
def skip_statement_cache?
|
257
|
+
reflection.scope_chain.any?(&:any?) ||
|
258
|
+
scope.eager_loading? ||
|
259
|
+
klass.current_scope ||
|
260
|
+
klass.default_scopes.any? ||
|
261
|
+
reflection.source_reflection.active_record.default_scopes.any?
|
262
|
+
end
|
251
263
|
end
|
252
264
|
end
|
253
265
|
end
|
@@ -1,12 +1,33 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class AssociationScope #:nodoc:
|
4
|
-
INSTANCE = new
|
5
|
-
|
6
4
|
def self.scope(association, connection)
|
7
5
|
INSTANCE.scope association, connection
|
8
6
|
end
|
9
7
|
|
8
|
+
class BindSubstitution
|
9
|
+
def initialize(block)
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind_value(scope, column, value, alias_tracker)
|
14
|
+
substitute = alias_tracker.connection.substitute_at(column)
|
15
|
+
scope.bind_values += [[column, @block.call(value)]]
|
16
|
+
substitute
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.create(&block)
|
21
|
+
block = block ? block : lambda { |val| val }
|
22
|
+
new BindSubstitution.new(block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(bind_substitution)
|
26
|
+
@bind_substitution = bind_substitution
|
27
|
+
end
|
28
|
+
|
29
|
+
INSTANCE = create
|
30
|
+
|
10
31
|
def scope(association, connection)
|
11
32
|
klass = association.klass
|
12
33
|
reflection = association.reflection
|
@@ -22,6 +43,23 @@ module ActiveRecord
|
|
22
43
|
Arel::Nodes::InnerJoin
|
23
44
|
end
|
24
45
|
|
46
|
+
def self.get_bind_values(owner, chain)
|
47
|
+
binds = []
|
48
|
+
last_reflection = chain.last
|
49
|
+
|
50
|
+
binds << last_reflection.join_id_for(owner)
|
51
|
+
if last_reflection.type
|
52
|
+
binds << owner.class.base_class.name
|
53
|
+
end
|
54
|
+
|
55
|
+
chain.each_cons(2).each do |reflection, next_reflection|
|
56
|
+
if reflection.type
|
57
|
+
binds << next_reflection.klass.base_class.name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
binds
|
61
|
+
end
|
62
|
+
|
25
63
|
private
|
26
64
|
|
27
65
|
def construct_tables(chain, klass, refl, alias_tracker)
|
@@ -49,10 +87,7 @@ module ActiveRecord
|
|
49
87
|
end
|
50
88
|
|
51
89
|
def bind_value(scope, column, value, alias_tracker)
|
52
|
-
|
53
|
-
column, scope.bind_values.length)
|
54
|
-
scope.bind_values += [[column, value]]
|
55
|
-
substitute
|
90
|
+
@bind_substitution.bind_value scope, column, value, alias_tracker
|
56
91
|
end
|
57
92
|
|
58
93
|
def bind(scope, table_name, column_name, value, tracker)
|
@@ -60,47 +95,55 @@ module ActiveRecord
|
|
60
95
|
bind_value scope, column, value, tracker
|
61
96
|
end
|
62
97
|
|
98
|
+
def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass)
|
99
|
+
join_keys = reflection.join_keys(assoc_klass)
|
100
|
+
key = join_keys.key
|
101
|
+
foreign_key = join_keys.foreign_key
|
102
|
+
|
103
|
+
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
104
|
+
scope = scope.where(table[key].eq(bind_val))
|
105
|
+
|
106
|
+
if reflection.type
|
107
|
+
value = owner.class.base_class.name
|
108
|
+
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
109
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
110
|
+
else
|
111
|
+
scope
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
|
116
|
+
join_keys = reflection.join_keys(assoc_klass)
|
117
|
+
key = join_keys.key
|
118
|
+
foreign_key = join_keys.foreign_key
|
119
|
+
|
120
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
121
|
+
|
122
|
+
if reflection.type
|
123
|
+
value = next_reflection.klass.base_class.name
|
124
|
+
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
125
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
126
|
+
end
|
127
|
+
|
128
|
+
scope = scope.joins(join(foreign_table, constraint))
|
129
|
+
end
|
130
|
+
|
63
131
|
def add_constraints(scope, owner, assoc_klass, refl, tracker)
|
64
132
|
chain = refl.chain
|
65
133
|
scope_chain = refl.scope_chain
|
66
134
|
|
67
135
|
tables = construct_tables(chain, assoc_klass, refl, tracker)
|
68
136
|
|
137
|
+
owner_reflection = chain.last
|
138
|
+
table = tables.last
|
139
|
+
scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass)
|
140
|
+
|
69
141
|
chain.each_with_index do |reflection, i|
|
70
142
|
table, foreign_table = tables.shift, tables.first
|
71
143
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
else
|
76
|
-
key = reflection.association_primary_key
|
77
|
-
end
|
78
|
-
|
79
|
-
foreign_key = reflection.foreign_key
|
80
|
-
else
|
81
|
-
key = reflection.foreign_key
|
82
|
-
foreign_key = reflection.active_record_primary_key
|
83
|
-
end
|
84
|
-
|
85
|
-
if reflection == chain.last
|
86
|
-
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
87
|
-
scope = scope.where(table[key].eq(bind_val))
|
88
|
-
|
89
|
-
if reflection.type
|
90
|
-
value = owner.class.base_class.name
|
91
|
-
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
92
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
93
|
-
end
|
94
|
-
else
|
95
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
96
|
-
|
97
|
-
if reflection.type
|
98
|
-
value = chain[i + 1].klass.base_class.name
|
99
|
-
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
100
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
101
|
-
end
|
102
|
-
|
103
|
-
scope = scope.joins(join(foreign_table, constraint))
|
144
|
+
unless reflection == chain.last
|
145
|
+
next_reflection = chain[i + 1]
|
146
|
+
scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
|
104
147
|
end
|
105
148
|
|
106
149
|
is_first_chain = i == 0
|
@@ -119,7 +162,9 @@ module ActiveRecord
|
|
119
162
|
scope.includes! item.includes_values
|
120
163
|
end
|
121
164
|
|
165
|
+
scope.unscope!(*item.unscope_values)
|
122
166
|
scope.where_values += item.where_values
|
167
|
+
scope.bind_values += item.bind_values
|
123
168
|
scope.order_values |= item.order_values
|
124
169
|
end
|
125
170
|
end
|
@@ -31,6 +31,14 @@ module ActiveRecord
|
|
31
31
|
@updated
|
32
32
|
end
|
33
33
|
|
34
|
+
def decrement_counters # :nodoc:
|
35
|
+
with_cache_name { |name| decrement_counter name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def increment_counters # :nodoc:
|
39
|
+
with_cache_name { |name| increment_counter name }
|
40
|
+
end
|
41
|
+
|
34
42
|
private
|
35
43
|
|
36
44
|
def find_target?
|
@@ -51,23 +59,28 @@ module ActiveRecord
|
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
def
|
55
|
-
|
62
|
+
def decrement_counter(counter_cache_name)
|
63
|
+
if foreign_key_present?
|
64
|
+
klass.decrement_counter(counter_cache_name, target_id)
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
|
-
def
|
68
|
+
def increment_counter(counter_cache_name)
|
59
69
|
if foreign_key_present?
|
60
|
-
klass.
|
70
|
+
klass.increment_counter(counter_cache_name, target_id)
|
71
|
+
if target && !stale_target? && counter_cache_available_in_memory?(counter_cache_name)
|
72
|
+
target.increment(counter_cache_name)
|
73
|
+
end
|
61
74
|
end
|
62
75
|
end
|
63
76
|
|
64
77
|
# Checks whether record is different to the current target, without loading it
|
65
78
|
def different_target?(record)
|
66
|
-
record.id != owner
|
79
|
+
record.id != owner._read_attribute(reflection.foreign_key)
|
67
80
|
end
|
68
81
|
|
69
82
|
def replace_keys(record)
|
70
|
-
owner[reflection.foreign_key] = record
|
83
|
+
owner[reflection.foreign_key] = record._read_attribute(reflection.association_primary_key(record.class))
|
71
84
|
end
|
72
85
|
|
73
86
|
def remove_keys
|
@@ -75,26 +88,31 @@ module ActiveRecord
|
|
75
88
|
end
|
76
89
|
|
77
90
|
def foreign_key_present?
|
78
|
-
owner
|
91
|
+
owner._read_attribute(reflection.foreign_key)
|
79
92
|
end
|
80
93
|
|
81
94
|
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
82
95
|
# has_one associations.
|
83
96
|
def invertible_for?(record)
|
84
97
|
inverse = inverse_reflection_for(record)
|
85
|
-
inverse && inverse.
|
98
|
+
inverse && inverse.has_one?
|
86
99
|
end
|
87
100
|
|
88
101
|
def target_id
|
89
102
|
if options[:primary_key]
|
90
103
|
owner.send(reflection.name).try(:id)
|
91
104
|
else
|
92
|
-
owner
|
105
|
+
owner._read_attribute(reflection.foreign_key)
|
93
106
|
end
|
94
107
|
end
|
95
108
|
|
96
109
|
def stale_state
|
97
|
-
|
110
|
+
result = owner._read_attribute(reflection.foreign_key)
|
111
|
+
result && result.to_s
|
112
|
+
end
|
113
|
+
|
114
|
+
def counter_cache_available_in_memory?(counter_cache_name)
|
115
|
+
target.respond_to?(counter_cache_name)
|
98
116
|
end
|
99
117
|
end
|
100
118
|
end
|
@@ -36,6 +36,7 @@ module ActiveRecord::Associations::Builder
|
|
36
36
|
reflection = builder.build(model)
|
37
37
|
define_accessors model, reflection
|
38
38
|
define_callbacks model, reflection
|
39
|
+
define_validations model, reflection
|
39
40
|
builder.define_extensions model
|
40
41
|
reflection
|
41
42
|
end
|
@@ -85,7 +86,11 @@ module ActiveRecord::Associations::Builder
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def self.define_callbacks(model, reflection)
|
88
|
-
|
89
|
+
if dependent = reflection.options[:dependent]
|
90
|
+
check_dependent_options(dependent)
|
91
|
+
add_destroy_callbacks(model, reflection)
|
92
|
+
end
|
93
|
+
|
89
94
|
Association.extensions.each do |extension|
|
90
95
|
extension.build model, reflection
|
91
96
|
end
|
@@ -120,17 +125,23 @@ module ActiveRecord::Associations::Builder
|
|
120
125
|
CODE
|
121
126
|
end
|
122
127
|
|
128
|
+
def self.define_validations(model, reflection)
|
129
|
+
# noop
|
130
|
+
end
|
131
|
+
|
123
132
|
def self.valid_dependent_options
|
124
133
|
raise NotImplementedError
|
125
134
|
end
|
126
135
|
|
127
136
|
private
|
128
137
|
|
129
|
-
def self.
|
130
|
-
unless valid_dependent_options.include?
|
131
|
-
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{
|
138
|
+
def self.check_dependent_options(dependent)
|
139
|
+
unless valid_dependent_options.include? dependent
|
140
|
+
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
132
141
|
end
|
142
|
+
end
|
133
143
|
|
144
|
+
def self.add_destroy_callbacks(model, reflection)
|
134
145
|
name = reflection.name
|
135
146
|
model.before_destroy lambda { |o| o.association(name).handle_dependency }
|
136
147
|
end
|
@@ -26,28 +26,9 @@ module ActiveRecord::Associations::Builder
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def self.add_counter_cache_methods(mixin)
|
29
|
-
return if mixin.method_defined? :
|
29
|
+
return if mixin.method_defined? :belongs_to_counter_cache_after_update
|
30
30
|
|
31
31
|
mixin.class_eval do
|
32
|
-
def belongs_to_counter_cache_after_create(reflection)
|
33
|
-
if record = send(reflection.name)
|
34
|
-
cache_column = reflection.counter_cache_column
|
35
|
-
record.class.increment_counter(cache_column, record.id)
|
36
|
-
@_after_create_counter_called = true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def belongs_to_counter_cache_before_destroy(reflection)
|
41
|
-
foreign_key = reflection.foreign_key.to_sym
|
42
|
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
43
|
-
record = send reflection.name
|
44
|
-
if record && !self.destroyed?
|
45
|
-
cache_column = reflection.counter_cache_column
|
46
|
-
record.class.decrement_counter(cache_column, record.id)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
32
|
def belongs_to_counter_cache_after_update(reflection)
|
52
33
|
foreign_key = reflection.foreign_key
|
53
34
|
cache_column = reflection.counter_cache_column
|
@@ -73,14 +54,6 @@ module ActiveRecord::Associations::Builder
|
|
73
54
|
def self.add_counter_cache_callbacks(model, reflection)
|
74
55
|
cache_column = reflection.counter_cache_column
|
75
56
|
|
76
|
-
model.after_create lambda { |record|
|
77
|
-
record.belongs_to_counter_cache_after_create(reflection)
|
78
|
-
}
|
79
|
-
|
80
|
-
model.before_destroy lambda { |record|
|
81
|
-
record.belongs_to_counter_cache_before_destroy(reflection)
|
82
|
-
}
|
83
|
-
|
84
57
|
model.after_update lambda { |record|
|
85
58
|
record.belongs_to_counter_cache_after_update(reflection)
|
86
59
|
}
|
@@ -130,9 +103,14 @@ module ActiveRecord::Associations::Builder
|
|
130
103
|
BelongsTo.touch_record(record, foreign_key, n, touch)
|
131
104
|
}
|
132
105
|
|
133
|
-
model.after_save callback
|
106
|
+
model.after_save callback, if: :changed?
|
134
107
|
model.after_touch callback
|
135
108
|
model.after_destroy callback
|
136
109
|
end
|
110
|
+
|
111
|
+
def self.add_destroy_callbacks(model, reflection)
|
112
|
+
name = reflection.name
|
113
|
+
model.after_destroy lambda { |o| o.association(name).handle_dependency }
|
114
|
+
end
|
137
115
|
end
|
138
116
|
end
|
@@ -82,7 +82,11 @@ module ActiveRecord::Associations::Builder
|
|
82
82
|
|
83
83
|
def wrap_scope(scope, mod)
|
84
84
|
if scope
|
85
|
-
|
85
|
+
if scope.arity > 0
|
86
|
+
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
87
|
+
else
|
88
|
+
proc { instance_exec(&scope).extending(mod) }
|
89
|
+
end
|
86
90
|
else
|
87
91
|
proc { extending(mod) }
|
88
92
|
end
|