aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb4
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb3
-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/serialization.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb30
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb19
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb5
-rw-r--r--activerecord/lib/active_record/fixtures.rb3
-rw-r--r--activerecord/lib/active_record/locale/en.yml4
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb3
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-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/store.rb12
-rw-r--r--activerecord/lib/active_record/test_case.rb62
24 files changed, 140 insertions, 67 deletions
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index c3fa4a05fd..2059d8acdf 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -74,7 +74,9 @@ module ActiveRecord::Associations::Builder
if dependent_restrict_raises?
raise ActiveRecord::DeleteRestrictionError.new(name)
else
- errors.add(:base, :restrict_dependent_destroy, :model => name.to_s.singularize)
+ key = association(name).reflection.macro == :has_one ? "one" : "many"
+ errors.add(:base, :"restrict_dependent_destroy.#{key}",
+ :record => self.class.human_attribute_name(name).downcase)
return false
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index ba01df00e3..5eda0387c4 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -82,9 +82,8 @@ module ActiveRecord
proxy_association.send :add_to_target, r
yield(r) if block_given?
end
- end
- if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
+ elsif target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
if load_target
if target.respond_to?(method)
target.send(method, *args, &block)
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/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 0c8e4e4b9a..7efef73472 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -88,6 +88,14 @@ module ActiveRecord
super
end
end
+
+ def read_attribute_before_type_cast(attr_name)
+ if serialized_attributes.include?(attr_name)
+ super.unserialized_value
+ else
+ super
+ end
+ end
end
end
end
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/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index fb59d9fb07..77af540c3e 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -15,7 +15,7 @@ module ActiveRecord
end
def dump(obj)
- YAML.dump obj
+ YAML.dump(obj) unless obj.nil?
end
def load(yaml)
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 84c340770a..1f9321edb6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -56,7 +56,7 @@ module ActiveRecord
# Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values.
- def columns(table_name, name = nil) end
+ def columns(table_name) end
# Checks to see if a column exists in a given table.
#
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 9d9dbcc355..e1dad5b166 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -409,7 +409,7 @@ module ActiveRecord
end
# Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name, name = nil)#:nodoc:
+ def columns(table_name)#:nodoc:
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
execute_and_free(sql, 'SCHEMA') do |result|
each_hash(result).map do |field|
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 1d8e5d813a..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
@@ -455,14 +455,14 @@ module ActiveRecord
# Escapes binary strings for bytea input to the database.
def escape_bytea(value)
- @connection.escape_bytea(value) if value
+ PGconn.escape_bytea(value) if value
end
# Unescapes bytea output from a database to the binary string it represents.
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
# on escaped binary output from database drive.
def unescape_bytea(value)
- @connection.unescape_bytea(value) if value
+ PGconn.unescape_bytea(value) if value
end
# Quotes PostgreSQL-specific data types for SQL input.
@@ -891,7 +891,7 @@ module ActiveRecord
end
# Returns the list of all column definitions for a table.
- def columns(table_name, name = nil)
+ def columns(table_name)
# Limit, precision, and scale are all handled by the superclass.
column_definitions(table_name).collect do |column_name, type, default, notnull|
PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index 4e8932a695..962718da56 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -9,7 +9,7 @@ module ActiveRecord
@tables = {}
@columns = Hash.new do |h, table_name|
- h[table_name] = conn.columns(table_name, "#{table_name} Columns")
+ h[table_name] = conn.columns(table_name)
end
@columns_hash = Hash.new do |h, table_name|
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 0520fc8b62..55eca48efe 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -6,26 +6,11 @@ module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLiteColumn < Column #:nodoc:
class << self
- def string_to_binary(value)
- value.gsub(/\0|\%/n) do |b|
- case b
- when "\0" then "%00"
- when "%" then "%25"
- end
- end
- end
-
def binary_to_string(value)
if value.encoding != Encoding::ASCII_8BIT
value = value.force_encoding(Encoding::ASCII_8BIT)
end
-
- value.gsub(/%00|%25/n) do |b|
- case b
- when "%00" then "\0"
- when "%25" then "%"
- end
- end
+ value
end
end
end
@@ -339,7 +324,7 @@ module ActiveRecord
end
# Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
- def columns(table_name, name = nil) #:nodoc:
+ def columns(table_name) #:nodoc:
table_structure(table_name).map do |field|
case field["dflt_value"]
when /^null$/i
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 97ffd11f2d..5b88b26310 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -209,6 +209,8 @@ module ActiveRecord
# The dup method does not preserve the timestamps (created|updated)_(at|on).
def initialize_dup(other)
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
+ self.class.initialize_attributes(cloned_attributes)
+
cloned_attributes.delete(self.class.primary_key)
@attributes = cloned_attributes
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/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index cf315b687c..b82d5b5621 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -3,7 +3,6 @@ require 'yaml'
require 'zlib'
require 'active_support/dependencies'
require 'active_support/core_ext/object/blank'
-require 'active_support/ordered_hash'
require 'active_record/fixtures/file'
if defined? ActiveRecord
@@ -508,7 +507,7 @@ module ActiveRecord
@name = fixture_name
@class_name = class_name
- @fixtures = ActiveSupport::OrderedHash.new
+ @fixtures = {}
# Should be an AR::Base type class
if class_name.is_a?(Class)
diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml
index 8892f7ef2f..896132d566 100644
--- a/activerecord/lib/active_record/locale/en.yml
+++ b/activerecord/lib/active_record/locale/en.yml
@@ -10,7 +10,9 @@ en:
messages:
taken: "has already been taken"
record_invalid: "Validation failed: %{errors}"
- restrict_dependent_destroy: "Cannot delete record because dependent %{model} exists"
+ restrict_dependent_destroy:
+ one: "Cannot delete record because a dependent %{record} exists"
+ many: "Cannot delete record because dependent %{record} exist"
# Append your own errors here or at the model/attributes scope.
# You can define own errors for models or model attributes.
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index f02f0544c5..8f8a3ec3bb 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -229,8 +229,8 @@ module ActiveRecord
end
end
- def columns(tbl_name, log_msg)
- @columns ||= klass.connection.columns(tbl_name, log_msg)
+ def columns(tbl_name)
+ @columns ||= klass.connection.columns(tbl_name)
end
def reset_column_information
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 6bf3050af9..50239f7cb2 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -177,6 +177,9 @@ module ActiveRecord
# Person.where(:confirmed => true).limit(5).pluck(:id)
#
def pluck(column_name)
+ if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
+ column_name = "#{table_name}.#{column_name}"
+ end
klass.connection.select_all(select(column_name).arel).map! do |attributes|
klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index cc4ef2d078..87dd513880 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -201,7 +201,7 @@ module ActiveRecord
#
# The returned NullRelation inherits from Relation and implements the
# Null Object pattern so it is an object with defined null behavior:
- # it always returns an empty array of records and avoids any database query.
+ # it always returns an empty array of records and does not query the database.
#
# Any subsequent condition chained to the returned relation will continue
# generating an empty relation and will not fire any query to the database.
@@ -212,7 +212,7 @@ module ActiveRecord
# For example:
#
# @posts = current_user.visible_posts.where(:name => params[:name])
- # # => the visible_post method response has to be a chainable Relation
+ # # => the visible_posts method is expected to return a chainable Relation
#
# def visible_posts
# case role
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/store.rb b/activerecord/lib/active_record/store.rb
index 8cc84f81d0..1c7b839e5e 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -15,7 +15,7 @@ module ActiveRecord
# class User < ActiveRecord::Base
# store :settings, accessors: [ :color, :homepage ]
# end
- #
+ #
# u = User.new(color: 'black', homepage: '37signals.com')
# u.color # Accessor stored attribute
# u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
@@ -26,7 +26,7 @@ module ActiveRecord
# end
module Store
extend ActiveSupport::Concern
-
+
module ClassMethods
def store(store_attribute, options = {})
serialize store_attribute, Hash
@@ -34,17 +34,19 @@ module ActiveRecord
end
def store_accessor(store_attribute, *keys)
- Array(keys).flatten.each do |key|
+ keys.flatten.each do |key|
define_method("#{key}=") do |value|
+ send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
send(store_attribute)[key] = value
send("#{store_attribute}_will_change!")
end
-
+
define_method(key) do
+ send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
send(store_attribute)[key]
end
end
end
end
end
-end \ No newline at end of file
+end
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