aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md20
-rw-r--r--activerecord/README.rdoc8
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb1
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb237
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb14
-rw-r--r--activerecord/lib/active_record/core.rb9
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb1
-rw-r--r--activerecord/lib/active_record/railtie.rb6
-rw-r--r--activerecord/lib/active_record/railties/databases.rake5
-rw-r--r--activerecord/lib/active_record/relation/batches.rb27
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb19
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb25
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb27
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb44
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb36
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb20
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb22
-rw-r--r--activerecord/test/cases/base_test.rb9
-rw-r--r--activerecord/test/cases/calculations_test.rb16
-rw-r--r--activerecord/test/cases/helper.rb16
27 files changed, 523 insertions, 91 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8f1f315e42..4f4e087acd 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,25 @@
## Rails 4.0.0 (unreleased) ##
+* Allow blocks for `count` with `ActiveRecord::Relation`, to work similar as
+ `Array#count`:
+
+ Person.where("age > 26").count { |person| gender == 'female' }
+
+ *Chris Finne & Carlos Antonio da Silva*
+
+* Added support to `CollectionAssociation#delete` for passing `fixnum`
+ or `string` values as record ids. This finds the records responding
+ to the `id` and executes delete on them.
+
+ class Person < ActiveRecord::Base
+ has_many :pets
+ end
+
+ person.pets.delete("1") # => [#<Pet id: 1>]
+ person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>]
+
+ *Francesco Rodriguez*
+
* Deprecated most of the 'dynamic finder' methods. All dynamic methods
except for `find_by_...` and `find_by_...!` are deprecated. Here's
how you can rewrite the code:
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc
index 30a66ff5f0..d080e0b0f5 100644
--- a/activerecord/README.rdoc
+++ b/activerecord/README.rdoc
@@ -61,10 +61,10 @@ A short rundown of some of the major features:
* Validation rules that can differ for new or existing objects.
class Account < ActiveRecord::Base
- validates_presence_of :subdomain, :name, :email_address, :password
- validates_uniqueness_of :subdomain
- validates_acceptance_of :terms_of_service, :on => :create
- validates_confirmation_of :password, :email_address, :on => :create
+ validates :subdomain, :name, :email_address, :password, presence: true
+ validates :subdomain, uniqueness: true
+ validates :terms_of_service, acceptance: true, on: :create
+ validates :password, :email_address, confirmation: true, on: :create
end
{Learn more}[link:classes/ActiveRecord/Validations.html]
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 210820062b..f8526bb691 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -80,6 +80,7 @@ module ActiveRecord
autoload :Sanitization
autoload :Schema
autoload :SchemaDumper
+ autoload :SchemaMigration
autoload :Scoping
autoload :Serialization
autoload :SessionStore
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 4ec176e641..e94fe35170 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -230,6 +230,7 @@ module ActiveRecord
delete_records(:all, dependent)
end
else
+ records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
delete_or_destroy(records, dependent)
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 100fb38dec..2fb80fdc4c 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -343,6 +343,9 @@ module ActiveRecord
##
# :method: delete_all
#
+ # :call-seq:
+ # delete_all()
+ #
# Deletes all the records from the collection. For +has_many+ asssociations,
# the deletion is done according to the strategy specified by the <tt>:dependent</tt>
# option. Returns an array with the deleted records.
@@ -435,6 +438,9 @@ module ActiveRecord
##
# :method: destroy_all
#
+ # :call-seq:
+ # destroy_all()
+ #
# Deletes the records of the collection directly from the database.
# This will _always_ remove the records ignoring the +:dependent+
# option.
@@ -459,12 +465,135 @@ module ActiveRecord
# Pet.find(1) # => Couldn't find Pet with id=1
##
+ # :method: delete
+ #
+ # :call-seq:
+ # delete(*records)
+ # delete(*fixnum_ids)
+ # delete(*string_ids)
+ #
+ # Deletes the +records+ supplied and removes them from the collection. For
+ # +has_many+ associations, the deletion is done according to the strategy
+ # specified by the <tt>:dependent</tt> option. Returns an array with the
+ # deleted records.
+ #
+ # If no <tt>:dependent</tt> option is given, then it will follow the default
+ # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
+ # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
+ # strategy is +delete_all+.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets # dependent: :nullify option by default
+ # end
+ #
+ # person.pets.size # => 3
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # person.pets.delete(Pet.find(1))
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
+ #
+ # person.pets.size # => 2
+ # person.pets
+ # # => [
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # Pet.find(1)
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
+ #
+ # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
+ # their +destroy+ method. See +destroy+ for more information.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets, dependent: :destroy
+ # end
+ #
+ # person.pets.size # => 3
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # person.pets.delete(Pet.find(1), Pet.find(3))
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # person.pets.size # => 1
+ # person.pets
+ # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
+ #
+ # Pet.find(1, 3)
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
+ #
+ # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
+ # *without* calling their +destroy+ method.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets, dependent: :delete_all
+ # end
+ #
+ # person.pets.size # => 3
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # person.pets.delete(Pet.find(1))
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
+ #
+ # person.pets.size # => 2
+ # person.pets
+ # # => [
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # Pet.find(1)
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
+ #
+ # You can pass +Fixnum+ or +String+ values, it finds the records
+ # responding to the +id+ and executes delete on them.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets
+ # end
+ #
+ # person.pets.size # => 3
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # person.pets.delete("1")
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
+ #
+ # person.pets.delete(2, 3)
+ # # => [
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+
+ ##
# :method: destroy
#
# :call-seq:
# destroy(*records)
#
- # Destroy the +records+ supplied and remove them from the collection.
+ # Destroys the +records+ supplied and removes them from the collection.
# This method will _always_ remove record from the database ignoring
# the +:dependent+ option. Returns an array with the removed records.
#
@@ -534,8 +663,52 @@ module ActiveRecord
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
##
+ # :method: uniq
+ #
+ # :call-seq:
+ # uniq()
+ #
+ # Specifies whether the records should be unique or not.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets
+ # end
+ #
+ # person.pets.select(:name)
+ # # => [
+ # # #<Pet name: "Fancy-Fancy">,
+ # # #<Pet name: "Fancy-Fancy">
+ # # ]
+ #
+ # person.pets.select(:name).uniq
+ # # => [#<Pet name: "Fancy-Fancy">]
+
+ ##
+ # :method: count
+ #
+ # :call-seq:
+ # count()
+ #
+ # Count all records using SQL.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets
+ # end
+ #
+ # person.pets.count # => 3
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+
+ ##
# :method: size
#
+ # :call-seq:
+ # size()
+ #
# Returns the size of the collection. If the collection hasn't been loaded,
# it executes a <tt>SELECT COUNT(*)</tt> query.
#
@@ -560,6 +733,9 @@ module ActiveRecord
##
# :method: length
#
+ # :call-seq:
+ # length()
+ #
# Returns the size of the collection calling +size+ on the target.
# If the collection has been already loaded, +length+ and +size+ are
# equivalent.
@@ -699,7 +875,7 @@ module ActiveRecord
:any?, :many?, :include?,
:to => :@association
- def initialize(association)
+ def initialize(association) #:nodoc:
@association = association
super association.klass, association.klass.arel_table
merge! association.scoped
@@ -731,10 +907,67 @@ module ActiveRecord
end
end
+ # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
+ # contain the same number of elements and if each element is equal
+ # to the corresponding element in the other array, otherwise returns
+ # +false+.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets
+ # end
+ #
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
+ # # ]
+ #
+ # other = person.pets.to_ary
+ #
+ # person.pets == other
+ # # => true
+ #
+ # other = [Pet.new(id: 1), Pet.new(id: 2)]
+ #
+ # person.pets == other
+ # # => false
def ==(other)
load_target == other
end
+ # Returns a new array of objects from the collection. If the collection
+ # hasn't been loaded, it fetches the records from the database.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets
+ # end
+ #
+ # person.pets
+ # # => [
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
+ # # ]
+ #
+ # other_pets = person.pets.to_ary
+ # # => [
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
+ # # ]
+ #
+ # other_pets.replace([Pet.new(name: 'BooGoo')])
+ #
+ # other_pets
+ # # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
+ #
+ # person.pets
+ # # This is not affected by replace
+ # # => [
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
+ # # ]
def to_ary
load_target.dup
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 165785c8fb..706fbf0546 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -72,12 +72,13 @@ module ActiveRecord
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
end
- def initialize_attributes(attributes) #:nodoc:
- super
+ def initialize_attributes(attributes, options = {}) #:nodoc:
+ serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
+ super(attributes, options)
serialized_attributes.each do |key, coder|
if attributes.key?(key)
- attributes[key] = Attribute.new(coder, attributes[key], :serialized)
+ attributes[key] = Attribute.new(coder, attributes[key], serialized)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 62b0f51bb2..5758ac4569 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1,5 +1,4 @@
require 'active_support/deprecation/reporting'
-require 'active_record/schema_migration'
require 'active_record/migration/join_table'
module ActiveRecord
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 9794c5663e..692473abc5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -313,10 +313,10 @@ module ActiveRecord
sql = "SHOW TABLES"
end
- select_all(sql).map { |table|
+ select_all(sql, 'SCHEMA').map { |table|
table.delete('Table_type')
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
- exec_without_stmt(sql).first['Create Table'] + ";\n\n"
+ exec_without_stmt(sql, 'SCHEMA').first['Create Table'] + ";\n\n"
}.join
end
@@ -508,7 +508,7 @@ module ActiveRecord
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
- variables = select_all("SHOW VARIABLES LIKE '#{name}'")
+ variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
variables.first['Value'] unless variables.empty?
end
@@ -630,7 +630,7 @@ module ActiveRecord
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
end
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
add_column_options!(rename_column_sql, options)
rename_column_sql
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 350ccce03d..8fc172f6e8 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -268,7 +268,7 @@ module ActiveRecord
# increase timeout so mysql server doesn't disconnect us
wait_timeout = @config[:wait_timeout]
- wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum)
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
variable_assignments << "@@wait_timeout = #{wait_timeout}"
execute("SET #{variable_assignments.join(', ')}", :skip_logging)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 0bd7403c32..03c318f5f7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -524,7 +524,7 @@ module ActiveRecord
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
- @table_alias_length ||= query('SHOW max_identifier_length')[0][0].to_i
+ @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
end
# QUOTING ==================================================
@@ -965,28 +965,28 @@ module ActiveRecord
binds = [[nil, table]]
binds << [nil, schema] if schema
- exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
+ exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
SELECT COUNT(*)
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind in ('v','r')
- AND c.relname = $1
- AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
+ AND c.relname = '#{table.gsub(/(^"|"$)/,'')}'
+ AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
SQL
end
# Returns true if schema exists.
def schema_exists?(name)
- exec_query(<<-SQL, 'SCHEMA', [[nil, name]]).rows.first[0].to_i > 0
+ exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
SELECT COUNT(*)
FROM pg_namespace
- WHERE nspname = $1
+ WHERE nspname = '#{name}'
SQL
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)
- result = query(<<-SQL, name)
+ result = query(<<-SQL, 'SCHEMA')
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -998,7 +998,6 @@ module ActiveRecord
ORDER BY i.relname
SQL
-
result.map do |row|
index_name = row[0]
unique = row[1] == 't'
@@ -1037,7 +1036,7 @@ module ActiveRecord
# Returns the current database name.
def current_database
- query('select current_database()')[0][0]
+ query('select current_database()', 'SCHEMA')[0][0]
end
# Returns the current schema name.
@@ -1047,7 +1046,7 @@ module ActiveRecord
# Returns the current database encoding format.
def encoding
- query(<<-end_sql)[0][0]
+ query(<<-end_sql, 'SCHEMA')[0][0]
SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
WHERE pg_database.datname LIKE '#{current_database}'
end_sql
@@ -1055,7 +1054,7 @@ module ActiveRecord
# Returns an array of schema names.
def schema_names
- query(<<-SQL).flatten
+ query(<<-SQL, 'SCHEMA').flatten
SELECT nspname
FROM pg_namespace
WHERE nspname !~ '^pg_.*'
@@ -1111,8 +1110,8 @@ module ActiveRecord
end
def serial_sequence(table, column)
- result = exec_query(<<-eosql, 'SCHEMA', [[nil, table], [nil, column]])
- SELECT pg_get_serial_sequence($1, $2)
+ result = exec_query(<<-eosql, 'SCHEMA')
+ SELECT pg_get_serial_sequence('#{table}', '#{column}')
eosql
result.rows.first.first
end
@@ -1189,13 +1188,13 @@ module ActiveRecord
# Returns just a table's primary key
def primary_key(table)
- row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first
+ row = exec_query(<<-end_sql, 'SCHEMA').rows.first
SELECT DISTINCT(attr.attname)
FROM pg_attribute attr
INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
WHERE cons.contype = 'p'
- AND dep.refobjid = $1::regclass
+ AND dep.refobjid = '#{table}'::regclass
end_sql
row && row.first
@@ -1472,7 +1471,7 @@ module ActiveRecord
end
def last_insert_id_result(sequence_name) #:nodoc:
- exec_query("SELECT currval($1)", 'SQL', [[nil, sequence_name]])
+ exec_query("SELECT currval('#{sequence_name}')", 'SQL')
end
# Executes a SELECT query and returns the results, performing any data type
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index d4ffa82b17..a0c7e559ce 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -208,7 +208,6 @@ module ActiveRecord
true
end
-
# QUOTING ==================================================
def quote(value, column = nil)
@@ -220,7 +219,6 @@ module ActiveRecord
end
end
-
def quote_string(s) #:nodoc:
@connection.class.quote(s)
end
@@ -359,7 +357,7 @@ module ActiveRecord
# SCHEMA STATEMENTS ========================================
- def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
+ def tables(name = nil, table_name = nil) #:nodoc:
sql = <<-SQL
SELECT name
FROM sqlite_master
@@ -367,13 +365,13 @@ module ActiveRecord
SQL
sql << " AND name = #{quote_table_name(table_name)}" if table_name
- exec_query(sql, name).map do |row|
+ exec_query(sql, 'SCHEMA').map do |row|
row['name']
end
end
- def table_exists?(name)
- name && tables('SCHEMA', name).any?
+ def table_exists?(table_name)
+ table_name && tables(nil, table_name).any?
end
# Returns an array of +SQLite3Column+ objects for the table specified by +table_name+.
@@ -394,12 +392,12 @@ module ActiveRecord
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
IndexDefinition.new(
table_name,
row['name'],
row['unique'] != 0,
- exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
+ exec_query("PRAGMA index_info('#{row['name']}')", "Columns for index #{row['name']} on #{table_name}").map { |col|
col['name']
})
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index f2833fbf3c..1fa6c701bb 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -10,9 +10,10 @@ module ActiveRecord
included do
##
# :singleton-method:
- # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
- # which is then passed on to any new database connections made and which can be retrieved on both
- # a class and instance level by calling +logger+.
+ #
+ # Accepts a logger conforming to the interface of Log4r which is then
+ # passed on to any new database connections made and which can be
+ # retrieved on both a class and instance level by calling +logger+.
config_attribute :logger, :global => true
##
@@ -240,7 +241,7 @@ module ActiveRecord
##
def initialize_dup(other) # :nodoc:
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
- self.class.initialize_attributes(cloned_attributes)
+ self.class.initialize_attributes(cloned_attributes, :serialized => false)
cloned_attributes.delete(self.class.primary_key)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index a3412582fa..05e052b953 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -168,7 +168,7 @@ module ActiveRecord
# start the lock version at zero. Note we can't use
# <tt>locking_enabled?</tt> at this point as
# <tt>@attributes</tt> may not have been initialized yet.
- def initialize_attributes(attributes) #:nodoc:
+ def initialize_attributes(attributes, options = {}) #:nodoc:
if attributes.key?(locking_column) && lock_optimistically
attributes[locking_column] ||= 0
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 2a9139749d..ac4f53c774 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,7 +1,6 @@
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/class/attribute_accessors"
require 'active_support/deprecation'
-require 'active_record/schema_migration'
require 'set'
module ActiveRecord
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index eb2769f1ef..1e497b2a79 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -30,6 +30,7 @@ module ActiveRecord
)
rake_tasks do
+ require "active_record/base"
load "active_record/railties/databases.rake"
end
@@ -38,10 +39,15 @@ module ActiveRecord
# first time. Also, make it output to STDERR.
console do |app|
require "active_record/railties/console_sandbox" if app.sandbox?
+ require "active_record/base"
console = ActiveSupport::Logger.new(STDERR)
Rails.logger.extend ActiveSupport::Logger.broadcast console
end
+ runner do |app|
+ require "active_record/base"
+ end
+
initializer "active_record.initialize_timezone" do
ActiveSupport.on_load(:active_record) do
self.time_zone_aware_attributes = true
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index f26e18b1e0..d8d4834d22 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -406,10 +406,11 @@ db_namespace = namespace :db do
set_psql_env(abcs[Rails.env])
search_path = abcs[Rails.env]['schema_search_path']
unless search_path.blank?
- search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ")
+ search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
end
- `pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}`
+ `pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(abcs[Rails.env]['database'])}`
raise 'Error dumping database' if $?.exitstatus == 1
+ File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
when /sqlite/
dbfile = abcs[Rails.env]['database']
`sqlite3 #{dbfile} .schema > #{filename}`
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 15f838a5ab..fb4388d4b2 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -2,20 +2,26 @@ require 'active_support/core_ext/object/blank'
module ActiveRecord
module Batches
- # Yields each record that was found by the find +options+. The find is
- # performed by find_in_batches with a batch size of 1000 (or as
+ # Looping through a collection of records from the database
+ # (using the +all+ method, for example) is very inefficient
+ # since it will try to instantiate all the objects at once.
+ #
+ # In that case, batch processing methods allow you to work
+ # with the records in batches, thereby greatly reducing memory consumption.
+ #
+ # The <tt>find_each</tt> method uses <tt>find_in_batches</tt> with a batch size of 1000 (or as
# specified by the <tt>:batch_size</tt> option).
#
- # Example:
+ # Person.all.find_each do |person|
+ # person.do_awesome_stuff
+ # end
#
# Person.where("age > 21").find_each do |person|
# person.party_all_night!
# end
#
- # Note: This method is only intended to use for batch processing of
- # large amounts of records that wouldn't fit in memory all at once. If
- # you just need to loop over less than 1000 records, it's probably
- # better just to use the regular find methods.
+ # You can also pass the <tt>:start</tt> option to specify
+ # an offset to control the starting point.
def find_each(options = {})
find_in_batches(options) do |records|
records.each { |record| yield record }
@@ -39,12 +45,15 @@ module ActiveRecord
# primary keys. You can't set the limit either, that's used to control
# the batch sizes.
#
- # Example:
- #
# Person.where("age > 21").find_in_batches do |group|
# sleep(50) # Make sure it doesn't get too crowded in there!
# group.each { |person| person.party_all_night! }
# end
+ #
+ # # Let's process the next 2000 records
+ # Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
+ # group.each { |person| person.party_all_night! }
+ # end
def find_in_batches(options = {})
options.assert_valid_keys(:start, :batch_size)
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 31d99f0192..ad49c80e4f 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -16,9 +16,16 @@ module ActiveRecord
#
# Person.count(:age, distinct: true)
# # => counts the number of different age values
+ #
+ # Person.where("age > 26").count { |person| gender == 'female' }
+ # # => queries people where "age > 26" then count the loaded results filtering by gender
def count(column_name = nil, options = {})
- column_name, options = nil, column_name if column_name.is_a?(Hash)
- calculate(:count, column_name, options)
+ if block_given?
+ self.to_a.count { |item| yield item }
+ else
+ column_name, options = nil, column_name if column_name.is_a?(Hash)
+ calculate(:count, column_name, options)
+ end
end
# Calculates the average value on a given column. Returns +nil+ if there's
@@ -52,9 +59,13 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.sum('age') # => 4562
+ # # => returns the total sum of all people's age
+ #
+ # Person.where('age > 100').sum { |person| person.age - 100 }
+ # # queries people where "age > 100" then perform a sum calculation with the block returns
def sum(*args)
if block_given?
- self.to_a.sum(*args) {|*block_args| yield(*block_args)}
+ self.to_a.sum(*args) { |item| yield item }
else
calculate(:sum, *args)
end
@@ -118,7 +129,7 @@ module ActiveRecord
# Person.all.map(&:name)
#
# Pluck returns an <tt>Array</tt> of attribute values type-casted to match
- # the plucked column name, if it can be deduced. Plucking a SQL fragment
+ # the plucked column name, if it can be deduced. Plucking an SQL fragment
# returns String values by default.
#
# Examples:
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 4fedd33d64..5f6898b45a 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -7,8 +7,6 @@ module ActiveRecord
# If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
# is an integer, find by id coerces its arguments using +to_i+.
#
- # ==== Examples
- #
# Person.find(1) # returns the object for ID = 1
# Person.find("1") # returns the object for ID = 1
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
@@ -49,7 +47,6 @@ module ActiveRecord
#
# Post.find_by name: 'Spartacus', rating: 4
# Post.find_by "published_at < ?", 2.weeks.ago
- #
def find_by(*args)
where(*args).take
end
@@ -64,8 +61,6 @@ module ActiveRecord
# order. The order will depend on the database implementation.
# If an order is supplied it will be respected.
#
- # Examples:
- #
# Person.take # returns an object fetched by SELECT * FROM people
# Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
# Person.where(["name LIKE '%?'", name]).take
@@ -82,12 +77,11 @@ module ActiveRecord
# Find the first record (or first N records if a parameter is supplied).
# If no order is defined it will order by primary key.
#
- # Examples:
- #
# Person.first # returns the first object fetched by SELECT * FROM people
# Person.where(["user_name = ?", user_name]).first
# Person.where(["user_name = :u", { :u => user_name }]).first
# Person.order("created_on DESC").offset(5).first
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
def first(limit = nil)
if limit
if order_values.empty? && primary_key
@@ -109,11 +103,18 @@ module ActiveRecord
# Find the last record (or last N records if a parameter is supplied).
# If no order is defined it will order by primary key.
#
- # Examples:
- #
# Person.last # returns the last object fetched by SELECT * FROM people
# Person.where(["user_name = ?", user_name]).last
# Person.order("created_on DESC").offset(5).last
+ # Person.last(3) # returns the last three objects fetched by SELECT * FROM people.
+ #
+ # Take note that in that last case, the results are sorted in ascending order:
+ #
+ # [#<Person id:2>, #<Person id:3>, #<Person id:4>]
+ #
+ # and not:
+ #
+ # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
def last(limit = nil)
if limit
if order_values.empty? && primary_key
@@ -132,7 +133,8 @@ module ActiveRecord
last or raise RecordNotFound
end
- # Examples:
+ # Runs the query on the database and returns records with the used query
+ # methods.
#
# Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
# Person.where(["category IN (?)", categories]).limit(50).all
@@ -163,11 +165,10 @@ module ActiveRecord
# 'Jamie'</tt>), since it would be sanitized and then queried against
# the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
#
- # ==== Examples
# Person.exists?(5)
# Person.exists?('5')
- # Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
+ # Person.exists?(:name => "David")
# Person.exists?
def exists?(id = false)
id = id.id if ActiveRecord::Model === id
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 684c7f5929..276c499276 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -4,6 +4,13 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
@connection = ActiveRecord::Model.connection
+ @connection.extend(LogIntercepter)
+ @connection.intercepted = true
+ end
+
+ def teardown
+ @connection.intercepted = false
+ @connection.logged = []
end
def test_no_automatic_reconnection_after_timeout
@@ -45,6 +52,26 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_logs_name_structure_dump
+ @connection.structure_dump
+ assert_equal "SCHEMA", @connection.logged[0][1]
+ assert_equal "SCHEMA", @connection.logged[2][1]
+ end
+
+ def test_logs_name_show_variable
+ @connection.show_variable 'foo'
+ assert_equal "SCHEMA", @connection.logged[0][1]
+ end
+
+ def test_logs_name_rename_column_sql
+ @connection.execute "CREATE TABLE `bar_baz` (`foo` varchar(255))"
+ @connection.logged = []
+ @connection.send(:rename_column_sql, 'bar_baz', 'foo', 'foo2')
+ assert_equal "SCHEMA", @connection.logged[0][1]
+ ensure
+ @connection.execute "DROP TABLE `bar_baz`"
+ end
+
private
def run_without_connection
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 4baec749ff..adb2cef010 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -8,6 +8,13 @@ module ActiveRecord
def setup
super
@connection = ActiveRecord::Base.connection
+ @connection.extend(LogIntercepter)
+ @connection.intercepted = true
+ end
+
+ def teardown
+ @connection.intercepted = false
+ @connection.logged = []
end
def test_encoding
@@ -25,5 +32,42 @@ module ActiveRecord
expect = NonExistentTable.connection.query('show geqo').first.first
assert_equal 'off', expect
end
+
+ def test_tables_logs_name
+ @connection.tables('hello')
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
+ def test_indexes_logs_name
+ @connection.indexes('items', 'hello')
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
+ def test_table_exists_logs_name
+ @connection.table_exists?('items')
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
+ def test_table_alias_length_logs_name
+ @connection.instance_variable_set("@table_alias_length", nil)
+ @connection.table_alias_length
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
+ def test_current_database_logs_name
+ @connection.current_database
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
+ def test_encoding_logs_name
+ @connection.encoding
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
+ def test_schema_names_logs_name
+ @connection.schema_names
+ assert_equal 'SCHEMA', @connection.logged[0][1]
+ end
+
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 8a7f44d0a3..5e947799cc 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -20,6 +20,14 @@ module ActiveRecord
number integer
)
eosql
+
+ @conn.extend(LogIntercepter)
+ @conn.intercepted = true
+ end
+
+ def teardown
+ @conn.intercepted = false
+ @conn.logged = []
end
def test_column_types
@@ -232,13 +240,23 @@ module ActiveRecord
end
def test_tables_logs_name
- name = "hello"
- assert_logged [[name, []]] do
- @conn.tables(name)
+ assert_logged [['SCHEMA', []]] do
+ @conn.tables('hello')
assert_not_nil @conn.logged.first.shift
end
end
+ def test_indexes_logs_name
+ assert_logged [["PRAGMA index_list(\"items\")", 'SCHEMA', []]] do
+ @conn.indexes('items', 'hello')
+ end
+ end
+
+ def test_table_exists_logs_name
+ assert @conn.table_exists?('items')
+ assert_equal 'SCHEMA', @conn.logged[0][1]
+ end
+
def test_columns
columns = @conn.columns('items').sort_by { |x| x.name }
assert_equal 2, columns.length
@@ -274,7 +292,6 @@ module ActiveRecord
end
def test_indexes_logs
- intercept_logs_on @conn
assert_difference('@conn.logged.length') do
@conn.indexes('items')
end
@@ -326,21 +343,10 @@ module ActiveRecord
private
def assert_logged logs
- intercept_logs_on @conn
yield
assert_equal logs, @conn.logged
end
- def intercept_logs_on ctx
- @conn.extend(Module.new {
- attr_accessor :logged
- def log sql, name, binds = []
- @logged << [sql, name, binds]
- yield
- end
- })
- @conn.logged = []
- end
end
end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 8b384c2513..0d8f311117 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -936,10 +936,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, summit.client_of
end
- def test_deleting_type_mismatch
+ def test_deleting_by_fixnum_id
david = Developer.find(1)
- david.projects.reload
- assert_raise(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(1) }
+
+ assert_difference 'david.projects.count', -1 do
+ assert_equal 1, david.projects.delete(1).size
+ end
+
+ assert_equal 1, david.projects.size
+ end
+
+ def test_deleting_by_string_id
+ david = Developer.find(1)
+
+ assert_difference 'david.projects.count', -1 do
+ assert_equal 1, david.projects.delete('1').size
+ end
+
+ assert_equal 1, david.projects.size
end
def test_deleting_self_type_mismatch
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index ecc676f300..783b83631c 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -578,7 +578,27 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_deleting_junk_from_has_many_through_should_raise_type_mismatch
- assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete("Uhh what now?") }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete(Object.new) }
+ end
+
+ def test_deleting_by_fixnum_id_from_has_many_through
+ post = posts(:thinking)
+
+ assert_difference 'post.tags.count', -1 do
+ assert_equal 1, post.tags.delete(1).size
+ end
+
+ assert_equal 0, post.tags.size
+ end
+
+ def test_deleting_by_string_id_from_has_many_through
+ post = posts(:thinking)
+
+ assert_difference 'post.tags.count', -1 do
+ assert_equal 1, post.tags.delete('1').size
+ end
+
+ assert_equal 0, post.tags.size
end
def test_has_many_through_sum_uses_calculations
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 619fb881fa..f95230ff50 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1309,6 +1309,15 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal({ :foo => :bar }, t.content_before_type_cast)
end
+ def test_serialized_attribute_calling_dup_method
+ klass = Class.new(ActiveRecord::Base)
+ klass.table_name = "topics"
+ klass.serialize :content, JSON
+
+ t = klass.new(:content => { :foo => :bar }).dup
+ assert_equal({ :foo => :bar }, t.content_before_type_cast)
+ end
+
def test_serialized_attribute_declared_in_subclass
hash = { 'important1' => 'value1', 'important2' => 'value2' }
important_topic = ImportantTopic.create("important" => hash)
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 041f8ffb7c..a279b0e77c 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -376,6 +376,22 @@ class CalculationsTest < ActiveRecord::TestCase
Company.where(:type => "Firm").from('companies').count(:type)
end
+ def test_count_with_block_acts_as_array
+ accounts = Account.where('id > 0')
+ assert_equal Account.count, accounts.count { true }
+ assert_equal 0, accounts.count { false }
+ assert_equal Account.where('credit_limit > 50').size, accounts.count { |account| account.credit_limit > 50 }
+ assert_equal Account.count, Account.count { true }
+ assert_equal 0, Account.count { false }
+ end
+
+ def test_sum_with_block_acts_as_array
+ accounts = Account.where('id > 0')
+ assert_equal Account.sum(:credit_limit), accounts.sum { |account| account.credit_limit }
+ assert_equal Account.sum(:credit_limit) + Account.count, accounts.sum{ |account| account.credit_limit + 1 }
+ assert_equal 0, accounts.sum { |account| 0 }
+ end
+
def test_sum_with_from_option
assert_equal Account.sum(:credit_limit), Account.from('accounts').sum(:credit_limit)
assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 37fa13f771..afff020561 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -121,3 +121,19 @@ class << Time
@now = nil
end
end
+
+module LogIntercepter
+ attr_accessor :logged, :intercepted
+ def self.extended(base)
+ base.logged = []
+ end
+ def log(sql, name, binds = [], &block)
+ if @intercepted
+ @logged << [sql, name, binds]
+ yield
+ else
+ super(sql, name,binds, &block)
+ end
+ end
+end
+