aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb7
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb5
-rw-r--r--activerecord/lib/active_record/schema_migration.rb2
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb3
-rw-r--r--activerecord/lib/active_record/test_case.rb62
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb48
-rw-r--r--activerecord/test/cases/helper.rb31
-rw-r--r--activerecord/test/cases/test_case.rb67
12 files changed, 154 insertions, 119 deletions
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index a1e34a3aa1..889c80386f 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -194,6 +194,7 @@ module ActiveRecord
# Returns the column object for the named attribute.
def column_for_attribute(name)
+ # FIXME: should this return a null object for columns that don't exist?
self.class.columns_hash[name.to_s]
end
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 948809c65a..1e841dc8e0 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -10,8 +10,11 @@ module ActiveRecord
end
def query_attribute(attr_name)
- unless value = read_attribute(attr_name)
- false
+ value = read_attribute(attr_name)
+
+ case value
+ when true then true
+ when false, nil then false
else
column = self.class.columns_hash[attr_name]
if column.nil?
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index fde55b95da..6b0230384b 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -37,30 +37,16 @@ module ActiveRecord
alias_method :raw_write_attribute, :write_attribute
private
- # Handle *= for method_missing.
- def attribute=(attribute_name, value)
- write_attribute(attribute_name, value)
- end
+ # Handle *= for method_missing.
+ def attribute=(attribute_name, value)
+ write_attribute(attribute_name, value)
+ end
- def type_cast_attribute_for_write(column, value)
- if column && column.number?
- convert_number_column_value(value)
- else
- value
- end
- end
+ def type_cast_attribute_for_write(column, value)
+ return value unless column
- def convert_number_column_value(value)
- if value == false
- 0
- elsif value == true
- 1
- elsif value.is_a?(String) && value.blank?
- nil
- else
- value
- end
- end
+ column.type_cast_for_write value
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 2ecb198edb..34d88edff3 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -66,6 +66,21 @@ module ActiveRecord
end
end
+ # Casts a Ruby value to something appropriate for writing to the database.
+ def type_cast_for_write(value)
+ return value unless number?
+
+ if value == false
+ 0
+ elsif value == true
+ 1
+ elsif value.is_a?(String) && value.blank?
+ nil
+ else
+ value
+ end
+ end
+
# Casts value (which is a String) to an appropriate instance.
def type_cast(value)
return nil if value.nil?
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index fe77eb725e..194c814e5b 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -14,7 +14,7 @@ module ActiveRecord
# Forward any unused config params to PGconn.connect.
[:statement_limit, :encoding, :min_messages, :schema_search_path,
- :schema_order, :adapter, :pool, :wait_timeout,
+ :schema_order, :adapter, :pool, :wait_timeout, :template,
:reaping_frequency].each do |key|
conn_params.delete key
end
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
index fc76410499..1f8c4fc203 100644
--- a/activerecord/lib/active_record/explain_subscriber.rb
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -11,7 +11,10 @@ module ActiveRecord
# SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
# our own EXPLAINs now matter how loopingly beautiful that would be.
- IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
+ #
+ # On the other hand, we want to monitor the performance of our real database
+ # queries, not the performance of the access to the query cache.
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
def ignore_payload?(payload)
payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name])
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index 257963c2ce..236ec563d2 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -4,6 +4,8 @@ require 'active_record/base'
module ActiveRecord
class SchemaMigration < ActiveRecord::Base
+ attr_accessible :version
+
def self.table_name
Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 7f1dba5095..2e60521638 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -162,8 +162,9 @@ module ActiveRecord #:nodoc:
#
# class IHaveMyOwnXML < ActiveRecord::Base
# def to_xml(options = {})
+ # require 'builder'
# options[:indent] ||= 2
- # xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ # xml = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
# xml.instruct! unless options[:skip_instruct]
# xml.level_one do
# xml.tag!(:second_level, 'content')
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index 86687afdda..4d881f0f7d 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -14,7 +14,7 @@ module ActiveRecord
end
def teardown
- ActiveRecord::SQLCounter.log.clear
+ SQLCounter.log.clear
end
def cleanup_identity_map
@@ -30,5 +30,65 @@ module ActiveRecord
assert_equal expected.to_s, actual.to_s, message
end
end
+
+ def assert_sql(*patterns_to_match)
+ SQLCounter.log = []
+ yield
+ SQLCounter.log
+ ensure
+ failed_patterns = []
+ patterns_to_match.each do |pattern|
+ failed_patterns << pattern unless SQLCounter.log.any?{ |sql| pattern === sql }
+ end
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
+ end
+
+ def assert_queries(num = 1)
+ SQLCounter.log = []
+ yield
+ ensure
+ assert_equal num, SQLCounter.log.size, "#{SQLCounter.log.size} instead of #{num} queries were executed.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
+ end
+
+ def assert_no_queries(&block)
+ prev_ignored_sql = SQLCounter.ignored_sql
+ SQLCounter.ignored_sql = []
+ assert_queries(0, &block)
+ ensure
+ SQLCounter.ignored_sql = prev_ignored_sql
+ end
+
+ end
+
+ class SQLCounter
+ class << self
+ attr_accessor :ignored_sql, :log
+ end
+
+ self.log = []
+
+ self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
+
+ # FIXME: this needs to be refactored so specific database can add their own
+ # ignored SQL. This ignored SQL is for Oracle.
+ ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
+
+
+ attr_reader :ignore
+
+ def initialize(ignore = Regexp.union(self.class.ignored_sql))
+ @ignore = ignore
+ end
+
+ def call(name, start, finish, message_id, values)
+ sql = values[:sql]
+
+ # FIXME: this seems bad. we should probably have a better way to indicate
+ # the query was cached
+ return if 'CACHE' == values[:name] || ignore =~ sql
+ self.class.log << sql
+ end
end
+
+ ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
end
diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
new file mode 100644
index 0000000000..e118add44c
--- /dev/null
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -0,0 +1,48 @@
+require 'cases/helper'
+
+if ActiveRecord::Base.connection.supports_explain?
+ class ExplainSubscriberTest < ActiveRecord::TestCase
+ SUBSCRIBER = ActiveRecord::ExplainSubscriber.new
+
+ def test_collects_nothing_if_available_queries_for_explain_is_nil
+ with_queries(nil) do
+ SUBSCRIBER.call
+ assert_nil Thread.current[:available_queries_for_explain]
+ end
+ end
+
+ def test_collects_nothing_if_the_payload_has_an_exception
+ with_queries([]) do |queries|
+ SUBSCRIBER.call(:exception => Exception.new)
+ assert queries.empty?
+ end
+ end
+
+ def test_collects_nothing_for_ignored_payloads
+ with_queries([]) do |queries|
+ ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS.each do |ip|
+ SUBSCRIBER.call(:name => ip)
+ end
+ assert queries.empty?
+ end
+ end
+
+ def test_collects_pairs_of_queries_and_binds
+ sql = 'select 1 from users'
+ binds = [1, 2]
+ with_queries([]) do |queries|
+ SUBSCRIBER.call(:name => 'SQL', :sql => sql, :binds => binds)
+ assert_equal 1, queries.size
+ assert_equal sql, queries[0][0]
+ assert_equal binds, queries[0][1]
+ end
+ end
+
+ def with_queries(queries)
+ Thread.current[:available_queries_for_explain] = queries
+ yield queries
+ ensure
+ Thread.current[:available_queries_for_explain] = nil
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 734d017b6e..9f5f012073 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -61,37 +61,6 @@ ensure
ActiveRecord::Base.default_timezone = old_zone
end
-module ActiveRecord
- class SQLCounter
- cattr_accessor :ignored_sql
- self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
-
- # FIXME: this needs to be refactored so specific database can add their own
- # ignored SQL. This ignored SQL is for Oracle.
- ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
-
- cattr_accessor :log
- self.log = []
-
- attr_reader :ignore
-
- def initialize(ignore = Regexp.union(self.class.ignored_sql))
- @ignore = ignore
- end
-
- def call(name, start, finish, message_id, values)
- sql = values[:sql]
-
- # FIXME: this seems bad. we should probably have a better way to indicate
- # the query was cached
- return if 'CACHE' == values[:name] || ignore =~ sql
- self.class.log << sql
- end
- end
-
- ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
-end
-
unless ENV['FIXTURE_DEBUG']
module ActiveRecord::TestFixtures::ClassMethods
def try_to_load_dependency_with_silence(*args)
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index f0fefe6c48..94a13d386c 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -1,63 +1,10 @@
-require 'active_support/test_case'
-
-module ActiveRecord
- # = Active Record Test Case
- #
- # Defines some test assertions to test against SQL queries.
- class TestCase < ActiveSupport::TestCase #:nodoc:
- setup :cleanup_identity_map
-
- def setup
- cleanup_identity_map
- end
-
- def teardown
- ActiveRecord::SQLCounter.log.clear
- end
-
- def cleanup_identity_map
- ActiveRecord::IdentityMap.clear
- end
-
- def assert_date_from_db(expected, actual, message = nil)
- # SybaseAdapter doesn't have a separate column type just for dates,
- # so the time is in the string and incorrectly formatted
- if current_adapter?(:SybaseAdapter)
- assert_equal expected.to_s, actual.to_date.to_s, message
- else
- assert_equal expected.to_s, actual.to_s, message
- end
- end
-
- def assert_sql(*patterns_to_match)
- ActiveRecord::SQLCounter.log = []
- yield
- ActiveRecord::SQLCounter.log
- ensure
- failed_patterns = []
- patterns_to_match.each do |pattern|
- failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
- end
- assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
- end
-
- def assert_queries(num = 1)
- ActiveRecord::SQLCounter.log = []
- yield
- ensure
- assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
- end
-
- def assert_no_queries(&block)
- prev_ignored_sql = ActiveRecord::SQLCounter.ignored_sql
- ActiveRecord::SQLCounter.ignored_sql = []
- assert_queries(0, &block)
- ensure
- ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql
- end
+require 'active_support/deprecation'
+ActiveSupport::Deprecation.silence do
+ require 'active_record/test_case'
+end
- def sqlite3? connection
- connection.class.name.split('::').last == "SQLite3Adapter"
- end
+ActiveRecord::TestCase.class_eval do
+ def sqlite3? connection
+ connection.class.name.split('::').last == "SQLite3Adapter"
end
end