activerecord 3.0.0.beta4 → 3.0.0.rc
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.
- data/CHANGELOG +267 -254
- data/README.rdoc +222 -0
- data/examples/performance.rb +9 -9
- data/lib/active_record/aggregations.rb +3 -4
- data/lib/active_record/association_preload.rb +15 -10
- data/lib/active_record/associations.rb +54 -37
- data/lib/active_record/associations/association_collection.rb +43 -17
- data/lib/active_record/associations/association_proxy.rb +2 -0
- data/lib/active_record/associations/belongs_to_association.rb +1 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +22 -7
- data/lib/active_record/associations/has_many_association.rb +6 -1
- data/lib/active_record/associations/has_many_through_association.rb +1 -0
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/has_one_through_association.rb +1 -0
- data/lib/active_record/associations/through_association_scope.rb +3 -2
- data/lib/active_record/attribute_methods.rb +1 -0
- data/lib/active_record/autosave_association.rb +4 -6
- data/lib/active_record/base.rb +106 -240
- data/lib/active_record/callbacks.rb +4 -25
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +22 -29
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +2 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +56 -7
- data/lib/active_record/connection_adapters/abstract_adapter.rb +10 -18
- data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +65 -69
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +20 -46
- data/lib/active_record/counter_cache.rb +14 -4
- data/lib/active_record/dynamic_finder_match.rb +9 -0
- data/lib/active_record/dynamic_scope_match.rb +7 -0
- data/lib/active_record/errors.rb +3 -0
- data/lib/active_record/fixtures.rb +5 -6
- data/lib/active_record/locale/en.yml +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -0
- data/lib/active_record/log_subscriber.rb +48 -0
- data/lib/active_record/migration.rb +64 -37
- data/lib/active_record/named_scope.rb +33 -19
- data/lib/active_record/nested_attributes.rb +17 -13
- data/lib/active_record/observer.rb +13 -6
- data/lib/active_record/persistence.rb +55 -22
- data/lib/active_record/query_cache.rb +1 -0
- data/lib/active_record/railtie.rb +14 -8
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +63 -33
- data/lib/active_record/reflection.rb +46 -28
- data/lib/active_record/relation.rb +38 -24
- data/lib/active_record/relation/finder_methods.rb +5 -5
- data/lib/active_record/relation/predicate_builder.rb +2 -4
- data/lib/active_record/relation/query_methods.rb +134 -115
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/schema.rb +2 -0
- data/lib/active_record/schema_dumper.rb +15 -12
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/session_store.rb +93 -79
- data/lib/active_record/test_case.rb +3 -0
- data/lib/active_record/timestamp.rb +49 -29
- data/lib/active_record/transactions.rb +5 -2
- data/lib/active_record/validations.rb +5 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -6
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- metadata +27 -14
- data/README +0 -351
- data/lib/active_record/railties/log_subscriber.rb +0 -32
@@ -216,7 +216,13 @@ module ActiveRecord
|
|
216
216
|
super(connection, logger)
|
217
217
|
@connection_parameters, @config = connection_parameters, config
|
218
218
|
|
219
|
+
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
220
|
+
@local_tz = nil
|
221
|
+
@table_alias_length = nil
|
222
|
+
@postgresql_version = nil
|
223
|
+
|
219
224
|
connect
|
225
|
+
@local_tz = execute('SHOW TIME ZONE').first["TimeZone"]
|
220
226
|
end
|
221
227
|
|
222
228
|
# Is this connection alive and ready for queries?
|
@@ -224,7 +230,7 @@ module ActiveRecord
|
|
224
230
|
if @connection.respond_to?(:status)
|
225
231
|
@connection.status == PGconn::CONNECTION_OK
|
226
232
|
else
|
227
|
-
# We're asking the driver, not
|
233
|
+
# We're asking the driver, not Active Record, so use @connection.query instead of #query
|
228
234
|
@connection.query 'SELECT 1'
|
229
235
|
true
|
230
236
|
end
|
@@ -258,7 +264,7 @@ module ActiveRecord
|
|
258
264
|
true
|
259
265
|
end
|
260
266
|
|
261
|
-
# Does PostgreSQL support finding primary key on non-
|
267
|
+
# Does PostgreSQL support finding primary key on non-Active Record tables?
|
262
268
|
def supports_primary_key? #:nodoc:
|
263
269
|
true
|
264
270
|
end
|
@@ -305,14 +311,16 @@ module ActiveRecord
|
|
305
311
|
|
306
312
|
# Quotes PostgreSQL-specific data types for SQL input.
|
307
313
|
def quote(value, column = nil) #:nodoc:
|
308
|
-
|
314
|
+
return super unless column
|
315
|
+
|
316
|
+
if value.kind_of?(String) && column.type == :binary
|
309
317
|
"'#{escape_bytea(value)}'"
|
310
|
-
elsif value.kind_of?(String) && column
|
318
|
+
elsif value.kind_of?(String) && column.sql_type == 'xml'
|
311
319
|
"xml '#{quote_string(value)}'"
|
312
|
-
elsif value.kind_of?(Numeric) && column
|
320
|
+
elsif value.kind_of?(Numeric) && column.sql_type == 'money'
|
313
321
|
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
314
|
-
"'#{value
|
315
|
-
elsif value.kind_of?(String) && column
|
322
|
+
"'#{value}'"
|
323
|
+
elsif value.kind_of?(String) && column.sql_type =~ /^bit/
|
316
324
|
case value
|
317
325
|
when /^[01]*$/
|
318
326
|
"B'#{value}'" # Bit-string notation
|
@@ -367,12 +375,12 @@ module ActiveRecord
|
|
367
375
|
|
368
376
|
def supports_disable_referential_integrity?() #:nodoc:
|
369
377
|
version = query("SHOW server_version")[0][0].split('.')
|
370
|
-
|
378
|
+
version[0].to_i >= 8 && version[1].to_i >= 1
|
371
379
|
rescue
|
372
380
|
return false
|
373
381
|
end
|
374
382
|
|
375
|
-
def disable_referential_integrity
|
383
|
+
def disable_referential_integrity #:nodoc:
|
376
384
|
if supports_disable_referential_integrity?() then
|
377
385
|
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
378
386
|
end
|
@@ -428,17 +436,37 @@ module ActiveRecord
|
|
428
436
|
def result_as_array(res) #:nodoc:
|
429
437
|
# check if we have any binary column and if they need escaping
|
430
438
|
unescape_col = []
|
431
|
-
|
432
|
-
|
433
|
-
unescape_col << ( res.ftype(j)==17 )
|
439
|
+
res.nfields.times do |j|
|
440
|
+
unescape_col << res.ftype(j)
|
434
441
|
end
|
435
442
|
|
436
443
|
ary = []
|
437
|
-
|
444
|
+
res.ntuples.times do |i|
|
438
445
|
ary << []
|
439
|
-
|
446
|
+
res.nfields.times do |j|
|
440
447
|
data = res.getvalue(i,j)
|
441
|
-
|
448
|
+
case unescape_col[j]
|
449
|
+
|
450
|
+
# unescape string passed BYTEA field (OID == 17)
|
451
|
+
when BYTEA_COLUMN_TYPE_OID
|
452
|
+
data = unescape_bytea(data) if String === data
|
453
|
+
|
454
|
+
# If this is a money type column and there are any currency symbols,
|
455
|
+
# then strip them off. Indeed it would be prettier to do this in
|
456
|
+
# PostgreSQLColumn.string_to_decimal but would break form input
|
457
|
+
# fields that call value_before_type_cast.
|
458
|
+
when MONEY_COLUMN_TYPE_OID
|
459
|
+
# Because money output is formatted according to the locale, there are two
|
460
|
+
# cases to consider (note the decimal separators):
|
461
|
+
# (1) $12,345,678.12
|
462
|
+
# (2) $12.345.678,12
|
463
|
+
case data
|
464
|
+
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
|
465
|
+
data.gsub!(/[^-\d\.]/, '')
|
466
|
+
when /^-?\D+[\d\.]+,\d{2}$/ # (2)
|
467
|
+
data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
|
468
|
+
end
|
469
|
+
end
|
442
470
|
ary[i] << data
|
443
471
|
end
|
444
472
|
end
|
@@ -606,27 +634,22 @@ module ActiveRecord
|
|
606
634
|
SQL
|
607
635
|
|
608
636
|
|
609
|
-
|
610
|
-
|
611
|
-
indexes = result.map do |row|
|
637
|
+
result.map do |row|
|
612
638
|
index_name = row[0]
|
613
639
|
unique = row[1] == 't'
|
614
640
|
indkey = row[2].split(" ")
|
615
641
|
oid = row[3]
|
616
642
|
|
617
|
-
columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")
|
618
|
-
SELECT a.
|
643
|
+
columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
|
644
|
+
SELECT a.attnum, a.attname
|
619
645
|
FROM pg_attribute a
|
620
646
|
WHERE a.attrelid = #{oid}
|
621
647
|
AND a.attnum IN (#{indkey.join(",")})
|
622
648
|
SQL
|
623
649
|
|
624
|
-
column_names = indkey.
|
625
|
-
IndexDefinition.new(table_name, index_name, unique, column_names)
|
626
|
-
|
627
|
-
end
|
628
|
-
|
629
|
-
indexes
|
650
|
+
column_names = columns.values_at(*indkey).compact
|
651
|
+
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names)
|
652
|
+
end.compact
|
630
653
|
end
|
631
654
|
|
632
655
|
# Returns the list of all column definitions for a table.
|
@@ -830,11 +853,12 @@ module ActiveRecord
|
|
830
853
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
831
854
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
832
855
|
return super unless type.to_s == 'integer'
|
856
|
+
return 'integer' unless limit
|
833
857
|
|
834
858
|
case limit
|
835
|
-
when 1
|
836
|
-
when 3
|
837
|
-
when 5..8;
|
859
|
+
when 1, 2; 'smallint'
|
860
|
+
when 3, 4; 'integer'
|
861
|
+
when 5..8; 'bigint'
|
838
862
|
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
839
863
|
end
|
840
864
|
end
|
@@ -891,6 +915,8 @@ module ActiveRecord
|
|
891
915
|
private
|
892
916
|
# The internal PostgreSQL identifier of the money data type.
|
893
917
|
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
|
918
|
+
# The internal PostgreSQL identifier of the BYTEA data type.
|
919
|
+
BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
|
894
920
|
|
895
921
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
896
922
|
# connected server's characteristics.
|
@@ -925,9 +951,13 @@ module ActiveRecord
|
|
925
951
|
# Use standard-conforming strings if available so we don't have to do the E'...' dance.
|
926
952
|
set_standard_conforming_strings
|
927
953
|
|
928
|
-
# If using
|
954
|
+
# If using Active Record's time zone support configure the connection to return
|
929
955
|
# TIMESTAMP WITH ZONE types in UTC.
|
930
|
-
|
956
|
+
if ActiveRecord::Base.default_timezone == :utc
|
957
|
+
execute("SET time zone 'UTC'")
|
958
|
+
elsif @local_tz
|
959
|
+
execute("SET time zone '#{@local_tz}'")
|
960
|
+
end
|
931
961
|
end
|
932
962
|
|
933
963
|
# Returns the current ID of a table's sequence.
|
@@ -939,51 +969,17 @@ module ActiveRecord
|
|
939
969
|
# conversions that are required to be performed here instead of in PostgreSQLColumn.
|
940
970
|
def select(sql, name = nil)
|
941
971
|
fields, rows = select_raw(sql, name)
|
942
|
-
|
943
|
-
|
944
|
-
row_hash = {}
|
945
|
-
fields.each_with_index do |f, i|
|
946
|
-
row_hash[f] = row[i]
|
947
|
-
end
|
948
|
-
result << row_hash
|
972
|
+
rows.map do |row|
|
973
|
+
Hash[*fields.zip(row).flatten]
|
949
974
|
end
|
950
|
-
result
|
951
975
|
end
|
952
976
|
|
953
977
|
def select_raw(sql, name = nil)
|
954
978
|
res = execute(sql, name)
|
955
979
|
results = result_as_array(res)
|
956
|
-
fields =
|
957
|
-
rows = []
|
958
|
-
if res.ntuples > 0
|
959
|
-
fields = res.fields
|
960
|
-
results.each do |row|
|
961
|
-
hashed_row = {}
|
962
|
-
row.each_index do |cell_index|
|
963
|
-
# If this is a money type column and there are any currency symbols,
|
964
|
-
# then strip them off. Indeed it would be prettier to do this in
|
965
|
-
# PostgreSQLColumn.string_to_decimal but would break form input
|
966
|
-
# fields that call value_before_type_cast.
|
967
|
-
if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID
|
968
|
-
# Because money output is formatted according to the locale, there are two
|
969
|
-
# cases to consider (note the decimal separators):
|
970
|
-
# (1) $12,345,678.12
|
971
|
-
# (2) $12.345.678,12
|
972
|
-
case column = row[cell_index]
|
973
|
-
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
|
974
|
-
row[cell_index] = column.gsub(/[^-\d\.]/, '')
|
975
|
-
when /^-?\D+[\d\.]+,\d{2}$/ # (2)
|
976
|
-
row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.')
|
977
|
-
end
|
978
|
-
end
|
979
|
-
|
980
|
-
hashed_row[fields[cell_index]] = column
|
981
|
-
end
|
982
|
-
rows << row
|
983
|
-
end
|
984
|
-
end
|
980
|
+
fields = res.fields
|
985
981
|
res.clear
|
986
|
-
return fields,
|
982
|
+
return fields, results
|
987
983
|
end
|
988
984
|
|
989
985
|
# Returns the list of a table's column names, data types, and default values.
|
@@ -4,7 +4,21 @@ module ActiveRecord
|
|
4
4
|
class Base
|
5
5
|
# sqlite3 adapter reuses sqlite_connection.
|
6
6
|
def self.sqlite3_connection(config) # :nodoc:
|
7
|
-
|
7
|
+
# Require database.
|
8
|
+
unless config[:database]
|
9
|
+
raise ArgumentError, "No database file specified. Missing argument: database"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Allow database path relative to Rails.root, but only if
|
13
|
+
# the database path is not the special path that tells
|
14
|
+
# Sqlite to build a database only in memory.
|
15
|
+
if defined?(Rails.root) && ':memory:' != config[:database]
|
16
|
+
config[:database] = File.expand_path(config[:database], Rails.root)
|
17
|
+
end
|
18
|
+
|
19
|
+
unless 'sqlite3' == config[:adapter]
|
20
|
+
raise ArgumentError, 'adapter name should be "sqlite3"'
|
21
|
+
end
|
8
22
|
|
9
23
|
unless self.class.const_defined?(:SQLite3)
|
10
24
|
require_library_or_gem(config[:adapter])
|
@@ -12,8 +26,7 @@ module ActiveRecord
|
|
12
26
|
|
13
27
|
db = SQLite3::Database.new(
|
14
28
|
config[:database],
|
15
|
-
:results_as_hash => true
|
16
|
-
:type_translation => false
|
29
|
+
:results_as_hash => true
|
17
30
|
)
|
18
31
|
|
19
32
|
db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
|
@@ -24,13 +37,13 @@ module ActiveRecord
|
|
24
37
|
|
25
38
|
module ConnectionAdapters #:nodoc:
|
26
39
|
class SQLite3Adapter < SQLiteAdapter # :nodoc:
|
27
|
-
|
40
|
+
|
28
41
|
# Returns the current database encoding format as a string, eg: 'UTF-8'
|
29
42
|
def encoding
|
30
43
|
if @connection.respond_to?(:encoding)
|
31
|
-
@connection.encoding
|
44
|
+
@connection.encoding.to_s
|
32
45
|
else
|
33
|
-
encoding = @connection.
|
46
|
+
encoding = @connection.execute('PRAGMA encoding')
|
34
47
|
encoding[0]['encoding']
|
35
48
|
end
|
36
49
|
end
|
@@ -2,25 +2,6 @@ require 'active_record/connection_adapters/abstract_adapter'
|
|
2
2
|
require 'active_support/core_ext/kernel/requires'
|
3
3
|
|
4
4
|
module ActiveRecord
|
5
|
-
class Base
|
6
|
-
class << self
|
7
|
-
private
|
8
|
-
def parse_sqlite_config!(config)
|
9
|
-
# Require database.
|
10
|
-
unless config[:database]
|
11
|
-
raise ArgumentError, "No database file specified. Missing argument: database"
|
12
|
-
end
|
13
|
-
|
14
|
-
# Allow database path relative to Rails.root, but only if
|
15
|
-
# the database path is not the special path that tells
|
16
|
-
# Sqlite to build a database only in memory.
|
17
|
-
if defined?(Rails.root) && ':memory:' != config[:database]
|
18
|
-
config[:database] = File.expand_path(config[:database], Rails.root)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
5
|
module ConnectionAdapters #:nodoc:
|
25
6
|
class SQLiteColumn < Column #:nodoc:
|
26
7
|
class << self
|
@@ -151,7 +132,7 @@ module ActiveRecord
|
|
151
132
|
# DATABASE STATEMENTS ======================================
|
152
133
|
|
153
134
|
def execute(sql, name = nil) #:nodoc:
|
154
|
-
|
135
|
+
log(sql, name) { @connection.execute(sql) }
|
155
136
|
end
|
156
137
|
|
157
138
|
def update_sql(sql, name = nil) #:nodoc:
|
@@ -176,15 +157,15 @@ module ActiveRecord
|
|
176
157
|
end
|
177
158
|
|
178
159
|
def begin_db_transaction #:nodoc:
|
179
|
-
|
160
|
+
@connection.transaction
|
180
161
|
end
|
181
162
|
|
182
163
|
def commit_db_transaction #:nodoc:
|
183
|
-
|
164
|
+
@connection.commit
|
184
165
|
end
|
185
166
|
|
186
167
|
def rollback_db_transaction #:nodoc:
|
187
|
-
|
168
|
+
@connection.rollback
|
188
169
|
end
|
189
170
|
|
190
171
|
# SCHEMA STATEMENTS ========================================
|
@@ -209,16 +190,21 @@ module ActiveRecord
|
|
209
190
|
|
210
191
|
def indexes(table_name, name = nil) #:nodoc:
|
211
192
|
execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
193
|
+
IndexDefinition.new(
|
194
|
+
table_name,
|
195
|
+
row['name'],
|
196
|
+
row['unique'].to_i != 0,
|
197
|
+
execute("PRAGMA index_info('#{row['name']}')").map { |col|
|
198
|
+
col['name']
|
199
|
+
})
|
216
200
|
end
|
217
201
|
end
|
218
202
|
|
219
203
|
def primary_key(table_name) #:nodoc:
|
220
|
-
column = table_structure(table_name).find {|field|
|
221
|
-
|
204
|
+
column = table_structure(table_name).find { |field|
|
205
|
+
field['pk'].to_i == 1
|
206
|
+
}
|
207
|
+
column && column['name']
|
222
208
|
end
|
223
209
|
|
224
210
|
def remove_index!(table_name, index_name) #:nodoc:
|
@@ -246,6 +232,7 @@ module ActiveRecord
|
|
246
232
|
end
|
247
233
|
|
248
234
|
def remove_column(table_name, *column_names) #:nodoc:
|
235
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
|
249
236
|
column_names.flatten.each do |column_name|
|
250
237
|
alter_table(table_name) do |definition|
|
251
238
|
definition.columns.delete(definition[column_name])
|
@@ -296,10 +283,8 @@ module ActiveRecord
|
|
296
283
|
def select(sql, name = nil) #:nodoc:
|
297
284
|
execute(sql, name).map do |row|
|
298
285
|
record = {}
|
299
|
-
row.
|
300
|
-
if key.is_a?(String)
|
301
|
-
record[key.sub(/^"?\w+"?\./, '')] = row[key]
|
302
|
-
end
|
286
|
+
row.each do |key, value|
|
287
|
+
record[key.sub(/^"?\w+"?\./, '')] = value if key.is_a?(String)
|
303
288
|
end
|
304
289
|
record
|
305
290
|
end
|
@@ -390,26 +375,15 @@ module ActiveRecord
|
|
390
375
|
end
|
391
376
|
end
|
392
377
|
|
393
|
-
def catch_schema_changes
|
394
|
-
return yield
|
395
|
-
rescue ActiveRecord::StatementInvalid => exception
|
396
|
-
if exception.message =~ /database schema has changed/
|
397
|
-
reconnect!
|
398
|
-
retry
|
399
|
-
else
|
400
|
-
raise
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
378
|
def sqlite_version
|
405
379
|
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
|
406
380
|
end
|
407
381
|
|
408
382
|
def default_primary_key_type
|
409
383
|
if supports_autoincrement?
|
410
|
-
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
|
384
|
+
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
|
411
385
|
else
|
412
|
-
'INTEGER PRIMARY KEY NOT NULL'
|
386
|
+
'INTEGER PRIMARY KEY NOT NULL'
|
413
387
|
end
|
414
388
|
end
|
415
389
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module ActiveRecord
|
2
|
+
# = Active Record Counter Cache
|
2
3
|
module CounterCache
|
3
4
|
# Resets one or more counter caches to their correct value using an SQL
|
4
5
|
# count query. This is useful when adding new counter caches, or if the
|
@@ -16,9 +17,18 @@ module ActiveRecord
|
|
16
17
|
def reset_counters(id, *counters)
|
17
18
|
object = find(id)
|
18
19
|
counters.each do |association|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
has_many_association = reflect_on_association(association.to_sym)
|
21
|
+
|
22
|
+
expected_name = if has_many_association.options[:as]
|
23
|
+
has_many_association.options[:as].to_s.classify
|
24
|
+
else
|
25
|
+
self.name
|
26
|
+
end
|
27
|
+
|
28
|
+
child_class = has_many_association.klass
|
29
|
+
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
|
30
|
+
reflection = belongs_to.find { |e| e.class_name == expected_name }
|
31
|
+
counter_name = reflection.counter_cache_column
|
22
32
|
|
23
33
|
self.unscoped.where(arel_table[self.primary_key].eq(object.id)).arel.update({
|
24
34
|
arel_table[counter_name] => object.send(association).count
|
@@ -102,4 +112,4 @@ module ActiveRecord
|
|
102
112
|
update_counters(id, counter_name => -1)
|
103
113
|
end
|
104
114
|
end
|
105
|
-
end
|
115
|
+
end
|