aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/base.rb46
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb59
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb62
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb18
-rw-r--r--activerecord/lib/active_record/railtie.rb1
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/result.rb4
-rw-r--r--activerecord/lib/active_record/session_store.rb4
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb36
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb11
-rw-r--r--activerecord/test/cases/base_test.rb27
-rw-r--r--activerecord/test/cases/calculations_test.rb6
-rw-r--r--activerecord/test/cases/coders/yaml_column_test.rb5
-rw-r--r--activerecord/test/cases/connection_pool_test.rb64
-rw-r--r--activerecord/test/cases/habtm_destroy_order_test.rb17
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb5
-rw-r--r--activerecord/test/models/contact.rb12
-rw-r--r--activerecord/test/models/lesson.rb11
-rw-r--r--activerecord/test/models/student.rb3
-rw-r--r--activerecord/test/schema/schema.rb13
27 files changed, 346 insertions, 118 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 285ce32e21..398936b3d8 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1450,8 +1450,8 @@ module ActiveRecord
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def destroy # def destroy
- #{reflection.name}.clear # posts.clear
super # super
+ #{reflection.name}.clear # posts.clear
end # end
RUBY
}
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 0941700803..effb17b2ff 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -249,6 +249,17 @@ module ActiveRecord #:nodoc:
# user = User.create(:preferences => %w( one two three ))
# User.find(user.id).preferences # raises SerializationTypeMismatch
#
+ # When you specify a class option, the default value for that attribute will be a new
+ # instance of that class.
+ #
+ # class User < ActiveRecord::Base
+ # serialize :preferences, OpenStruct
+ # end
+ #
+ # user = User.new
+ # user.preferences.theme_color = "red"
+ #
+ #
# == Single table inheritance
#
# Active Record allows inheritance by storing the name of the class in a column that by
@@ -668,16 +679,12 @@ module ActiveRecord #:nodoc:
# Returns an array of column objects for the table associated with this class.
def columns
- @@columns[table_name] ||= connection.columns(
- table_name, "#{name} Columns"
- ).tap { |columns|
- columns.each { |column| column.primary = column.name == primary_key }
- }
+ connection_pool.columns[table_name]
end
# Returns a hash of column objects for the table associated with this class.
def columns_hash
- @@columns_hash[table_name] ||= Hash[columns.map { |column| [column.name, column] }]
+ connection_pool.columns_hash[table_name]
end
# Returns an array of column names as strings.
@@ -734,14 +741,14 @@ module ActiveRecord #:nodoc:
def reset_column_information
connection.clear_cache!
undefine_attribute_methods
- reset_column_cache
+ connection_pool.clear_table_cache!(table_name) if table_exists?
+
@column_names = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
- @arel_engine = @relation = @arel_table = nil
+ @arel_engine = @relation = nil
end
- def reset_column_cache # :nodoc:
- @@columns.delete table_name
- @@columns_hash.delete table_name
+ def clear_cache! # :nodoc:
+ connection_pool.clear_cache!
end
def attribute_method?(attribute)
@@ -840,7 +847,7 @@ module ActiveRecord #:nodoc:
end
def arel_table
- @arel_table ||= Arel::Table.new(table_name, arel_engine)
+ Arel::Table.new(table_name, arel_engine)
end
def arel_engine
@@ -1388,8 +1395,6 @@ MSG
quoted_value
end
end
- @@columns_hash = {}
- @@columns = {}
public
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -1409,6 +1414,7 @@ MSG
@changed_attributes = {}
ensure_proper_type
+ set_serialized_attributes
populate_with_current_scope_attributes
self.attributes = attributes unless attributes.nil?
@@ -1447,10 +1453,7 @@ MSG
def init_with(coder)
@attributes = coder['attributes']
- (@attributes.keys & self.class.serialized_attributes.keys).each do |key|
- coder = self.class.serialized_attributes[key]
- @attributes[key] = coder.load @attributes[key]
- end
+ set_serialized_attributes
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
@association_cache = {}
@@ -1724,6 +1727,13 @@ MSG
private
+ def set_serialized_attributes
+ (@attributes.keys & self.class.serialized_attributes.keys).each do |key|
+ coder = self.class.serialized_attributes[key]
+ @attributes[key] = coder.load @attributes[key]
+ end
+ end
+
# Sets the attribute used for single table inheritance to this class name if this is not the
# ActiveRecord::Base descendant.
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index fcecc11aba..fb59d9fb07 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -19,6 +19,7 @@ module ActiveRecord
end
def load(yaml)
+ return object_class.new if object_class != Object && yaml.nil?
return yaml unless yaml.is_a?(String) && yaml =~ /^---/
begin
obj = YAML.load(yaml)
@@ -27,6 +28,7 @@ module ActiveRecord
raise SerializationTypeMismatch,
"Attribute was supposed to be a #{object_class}, but was a #{obj.class}"
end
+ obj ||= object_class.new if object_class != Object
obj
rescue *RESCUE_ERRORS
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 54f70c59f8..4475019e0e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -57,7 +57,9 @@ module ActiveRecord
# * +wait_timeout+: number of seconds to block and wait for a connection
# before giving up and raising a timeout error (default 5 seconds).
class ConnectionPool
+ attr_accessor :automatic_reconnect
attr_reader :spec, :connections
+ attr_reader :columns, :columns_hash, :primary_keys
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
# object which describes database connection information (e.g. adapter,
@@ -81,6 +83,59 @@ module ActiveRecord
@connections = []
@checked_out = []
+ @automatic_reconnect = true
+
+ @tables = Hash.new do |h, table_name|
+ with_connection do |conn|
+ h[table_name] = conn.table_exists?(table_name)
+ end
+ end
+
+ @columns = Hash.new do |h, table_name|
+ h[table_name] = with_connection do |conn|
+
+ # Fetch a list of columns
+ conn.columns(table_name, "#{table_name} Columns").tap do |columns|
+
+ # set primary key information
+ columns.each do |column|
+ column.primary = column.name == primary_keys[table_name]
+ end
+ end
+ end
+ end
+
+ @columns_hash = Hash.new do |h, table_name|
+ h[table_name] = Hash[columns[table_name].map { |col|
+ [col.name, col]
+ }]
+ end
+
+ @primary_keys = Hash.new do |h, table_name|
+ h[table_name] = with_connection do |conn|
+ @tables[table_name] ? conn.primary_key(table_name) : 'id'
+ end
+ end
+ end
+
+ # Clears out internal caches:
+ #
+ # * columns
+ # * columns_hash
+ # * primary_keys
+ # * tables
+ def clear_cache!
+ @columns.clear
+ @columns_hash.clear
+ @primary_keys.clear
+ @tables.clear
+ end
+
+ # Clear out internal caches for table with +table_name+
+ def clear_table_cache!(table_name)
+ @columns.delete table_name
+ @columns_hash.delete table_name
+ @primary_keys.delete table_name
end
# Retrieve the connection associated with the current thread, or call
@@ -232,6 +287,8 @@ module ActiveRecord
end
def checkout_new_connection
+ raise ConnectionNotEstablished unless @automatic_reconnect
+
c = new_connection
@connections << c
checkout_and_verify(c)
@@ -330,7 +387,7 @@ module ActiveRecord
pool = @connection_pools[klass.name]
return nil unless pool
- @connection_pools.delete_if { |key, value| value == pool }
+ pool.automatic_reconnect = false
pool.disconnect!
pool.spec.config
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index beb06ea622..4e3d8a096f 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -238,28 +238,28 @@ module ActiveRecord
def simplified_type(field_type)
case field_type
- when /int/i
- :integer
- when /float|double/i
- :float
- when /decimal|numeric|number/i
- extract_scale(field_type) == 0 ? :integer : :decimal
- when /datetime/i
- :datetime
- when /timestamp/i
- :timestamp
- when /time/i
- :time
- when /date/i
- :date
- when /clob/i, /text/i
- :text
- when /blob/i, /binary/i
- :binary
- when /char/i, /string/i
- :string
- when /boolean/i
- :boolean
+ when /int/i
+ :integer
+ when /float|double/i
+ :float
+ when /decimal|numeric|number/i
+ extract_scale(field_type) == 0 ? :integer : :decimal
+ when /datetime/i
+ :datetime
+ when /timestamp/i
+ :timestamp
+ when /time/i
+ :time
+ when /date/i
+ :date
+ when /clob/i, /text/i
+ :text
+ when /blob/i, /binary/i
+ :binary
+ when /char/i, /string/i
+ :string
+ when /boolean/i
+ :boolean
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index a04fc01d6f..acf1832938 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -42,62 +42,17 @@ module ActiveRecord
super
end
- # Returns the Ruby class that corresponds to the abstract data type.
- def klass
- case type
- when :integer then Fixnum
- when :float then Float
- when :decimal then BigDecimal
- when :datetime then Time
- when :date then Date
- when :timestamp then Time
- when :time then Time
- when :text, :string then String
- when :binary then String
- when :boolean then Object
- end
- end
-
- def type_cast(value)
- return nil if value.nil?
- case type
- when :string then value
- when :text then value
- when :integer then value.to_i rescue value ? 1 : 0
- when :float then value.to_f # returns self if it's already a Float
- when :decimal then self.class.value_to_decimal(value)
- when :datetime, :timestamp then value.class == Time ? value : self.class.string_to_time(value)
- when :time then value.class == Time ? value : self.class.string_to_dummy_time(value)
- when :date then value.class == Date ? value : self.class.string_to_date(value)
- when :binary then value
- when :boolean then self.class.value_to_boolean(value)
- else value
- end
- end
-
- def type_cast_code(var_name)
- case type
- when :string then var_name
- when :text then var_name
- when :integer then "#{var_name}.to_i rescue #{var_name} ? 1 : 0"
- when :float then "#{var_name}.to_f"
- when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
- when :datetime, :timestamp then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_time(#{var_name})"
- when :time then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_dummy_time(#{var_name})"
- when :date then "#{var_name}.class == Date ? #{var_name} : #{self.class.name}.string_to_date(#{var_name})"
- when :binary then var_name
- when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
- else var_name
- end
- end
-
private
def simplified_type(field_type)
return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL)
- return :string if field_type =~ /enum/i or field_type =~ /set/i
- return :integer if field_type =~ /year/i
- return :binary if field_type =~ /bit/i
- super
+
+ case field_type
+ when /enum/i, /set/i then :string
+ when /year/i then :integer
+ when /bit/i then :binary
+ else
+ super
+ end
end
def extract_limit(sql_type)
@@ -376,6 +331,7 @@ module ActiveRecord
end
sql
end
+ deprecate :add_limit_offset!
# SCHEMA STATEMENTS ========================================
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 15488cee52..cdf1ebfee4 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -537,7 +537,7 @@ module ActiveRecord
def columns(table_name, name = nil)#:nodoc:
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
columns = []
- result = execute(sql, :skip_logging)
+ result = execute(sql)
result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
result.free
columns
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index a4b1aa7154..46c0f3fafe 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -786,7 +786,7 @@ module ActiveRecord
def pk_and_sequence_for(table) #:nodoc:
# First try looking for a sequence with a dependency on the
# given table's primary key.
- result = query(<<-end_sql, 'PK and serial sequence')[0]
+ result = exec_query(<<-end_sql, 'PK and serial sequence').rows.first
SELECT attr.attname, seq.relname
FROM pg_class seq,
pg_attribute attr,
@@ -1071,7 +1071,7 @@ module ActiveRecord
# - format_type includes the column size constraint, e.g. varchar(50)
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
- query <<-end_sql
+ exec_query(<<-end_sql).rows
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index b04383d5bf..f650a1bc74 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -152,8 +152,11 @@ module ActiveRecord
# Don't cache statements without bind values
if binds.empty?
- stmt = @connection.prepare(sql)
- cols = stmt.columns
+ stmt = @connection.prepare(sql)
+ cols = stmt.columns
+ records = stmt.to_a
+ stmt.close
+ stmt = records
else
cache = @statements[sql] ||= {
:stmt => @connection.prepare(sql)
@@ -233,6 +236,15 @@ module ActiveRecord
def columns(table_name, name = nil) #:nodoc:
table_structure(table_name).map do |field|
+ case field["dflt_value"]
+ when /^null$/i
+ field["dflt_value"] = nil
+ when /^'(.*)'$/
+ field["dflt_value"] = $1.gsub(/''/, "'")
+ when /^"(.*)"$/
+ field["dflt_value"] = $1.gsub(/""/, '"')
+ end
+
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
end
end
@@ -334,7 +346,7 @@ module ActiveRecord
end
def table_structure(table_name)
- structure = @connection.table_info(quote_table_name(table_name))
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})").to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
structure
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 2accf0a48f..72687c9ca3 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -72,6 +72,7 @@ module ActiveRecord
ActiveSupport.on_load(:active_record) do
ActionDispatch::Reloader.to_cleanup do
ActiveRecord::Base.clear_reloadable_connections!
+ ActiveRecord::Base.clear_cache!
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 9924755ddf..49d4b8f76b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -193,7 +193,7 @@ db_namespace = namespace :db do
file_list = []
Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
# only files matching "20091231235959_some_name.rb" pattern
- if match_data = /(\d{14})_(.+)\.rb/.match(file)
+ if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
status = db_list.delete(match_data[1]) ? 'up' : 'down'
file_list << [status, match_data[1], match_data[2]]
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index b75a65e3ca..abc4c54109 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -285,7 +285,7 @@ module ActiveRecord
case operation
when 'count' then value.to_i
when 'sum' then type_cast_using_column(value || '0', column)
- when 'average' then value.try(:to_d)
+ when 'average' then value.respond_to?(:to_d) ? value.to_d : value
else type_cast_using_column(value, column)
end
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 47a5ac2700..0465b21e88 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -20,6 +20,10 @@ module ActiveRecord
hash_rows.each { |row| yield row }
end
+ def to_hash
+ hash_rows
+ end
+
private
def hash_rows
@hash_rows ||= @rows.map { |row|
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index e3342f046f..7e77aefb21 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -59,12 +59,12 @@ module ActiveRecord
end
def drop_table!
- reset_column_cache
+ connection_pool.clear_table_cache!(table_name)
connection.drop_table table_name
end
def create_table!
- reset_column_cache
+ connection_pool.clear_table_cache!(table_name)
connection.create_table(table_name) do |t|
t.string session_id_column, :limit => 255
t.text data_column_name
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
new file mode 100644
index 0000000000..1c2942170e
--- /dev/null
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -0,0 +1,36 @@
+module ActiveRecord
+ class Base
+ def self.fake_connection(config)
+ ConnectionAdapters::FakeAdapter.new nil, logger
+ end
+ end
+
+ module ConnectionAdapters
+ class FakeAdapter < AbstractAdapter
+ attr_accessor :tables, :primary_keys
+
+ def initialize(connection, logger)
+ super
+ @tables = []
+ @primary_keys = {}
+ @columns = Hash.new { |h,k| h[k] = [] }
+ end
+
+ def primary_key(table)
+ @primary_keys[table]
+ end
+
+ def merge_column(table_name, name, sql_type = nil, options = {})
+ @columns[table_name] << ActiveRecord::ConnectionAdapters::Column.new(
+ name.to_s,
+ options[:default],
+ sql_type.to_s,
+ options[:null])
+ end
+
+ def columns(table_name, message)
+ @columns[table_name]
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 30730c7094..126b767d06 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -365,7 +365,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_removing_associations_on_destroy
david = DeveloperWithBeforeDestroyRaise.find(1)
assert !david.projects.empty?
- assert_raise(RuntimeError) { david.destroy }
+ david.destroy
assert david.projects.empty?
assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
end
@@ -757,10 +757,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david = Developer.find(1)
# clear cache possibly created by other tests
david.projects.reset_column_information
- assert_queries(0) { david.projects.columns; david.projects.columns }
- # and again to verify that reset_column_information clears the cache correctly
+
+ # One query for columns, one for primary key
+ assert_queries(2) { david.projects.columns; david.projects.columns }
+
+ ## and again to verify that reset_column_information clears the cache correctly
david.projects.reset_column_information
- assert_queries(0) { david.projects.columns; david.projects.columns }
+ assert_queries(2) { david.projects.columns; david.projects.columns }
end
def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 5cbc52732b..68adeff882 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1002,6 +1002,25 @@ class BasicsTest < ActiveRecord::TestCase
Topic.serialize(:content)
end
+ def test_serialized_default_class
+ Topic.serialize(:content, Hash)
+ topic = Topic.new
+ assert_equal Hash, topic.content.class
+ assert_equal Hash, topic.read_attribute(:content).class
+ topic.content["beer"] = "MadridRb"
+ assert topic.save
+ topic.reload
+ assert_equal Hash, topic.content.class
+ assert_equal "MadridRb", topic.content["beer"]
+ ensure
+ Topic.serialize(:content)
+ end
+
+ def test_serialized_no_default_class_for_object
+ topic = Topic.new
+ assert_nil topic.content
+ end
+
def test_serialized_boolean_value_true
Topic.serialize(:content)
topic = Topic.new(:content => true)
@@ -1534,6 +1553,14 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_clear_cache!
+ # preheat cache
+ c1 = Post.columns
+ ActiveRecord::Base.clear_cache!
+ c2 = Post.columns
+ assert_not_equal c1, c2
+ end
+
def test_default_scope_is_reset
Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
UnloadablePost.table_name = 'posts'
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 644c9cb528..3121f1615d 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -28,6 +28,12 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 3.5, value
end
+ def test_should_return_integer_average_if_db_returns_such
+ Account.connection.stubs :select_value => 3
+ value = Account.average(:id)
+ assert_equal 3, value
+ end
+
def test_should_return_nil_as_average
assert_nil NumericData.average(:bank_balance)
end
diff --git a/activerecord/test/cases/coders/yaml_column_test.rb b/activerecord/test/cases/coders/yaml_column_test.rb
index f85f11b57f..c7dcc21809 100644
--- a/activerecord/test/cases/coders/yaml_column_test.rb
+++ b/activerecord/test/cases/coders/yaml_column_test.rb
@@ -1,3 +1,4 @@
+
require "cases/helper"
module ActiveRecord
@@ -20,9 +21,9 @@ module ActiveRecord
assert_nil coder.load "--- "
end
- def test_nil_is_ok_with_different_class
+ def test_returns_new_with_different_class
coder = YAMLColumn.new SerializationTypeMismatch
- assert_nil coder.load "--- "
+ assert_equal SerializationTypeMismatch, coder.load("--- ").class
end
def test_returns_string_unless_starts_with_dash
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 2e18117895..55ac1bc406 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -3,6 +3,50 @@ require "cases/helper"
module ActiveRecord
module ConnectionAdapters
class ConnectionPoolTest < ActiveRecord::TestCase
+ def setup
+ # Keep a duplicate pool so we do not bother others
+ @pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ end
+
+ def test_pool_caches_columns
+ columns = @pool.columns['posts']
+ assert_equal columns, @pool.columns['posts']
+ end
+
+ def test_pool_caches_columns_hash
+ columns_hash = @pool.columns_hash['posts']
+ assert_equal columns_hash, @pool.columns_hash['posts']
+ end
+
+ def test_clearing_cache
+ @pool.columns['posts']
+ @pool.columns_hash['posts']
+ @pool.primary_keys['posts']
+
+ @pool.clear_cache!
+
+ assert_equal 0, @pool.columns.size
+ assert_equal 0, @pool.columns_hash.size
+ assert_equal 0, @pool.primary_keys.size
+ end
+
+ def test_primary_key
+ assert_equal 'id', @pool.primary_keys['posts']
+ end
+
+ def test_primary_key_for_non_existent_table
+ assert_equal 'id', @pool.primary_keys['omgponies']
+ end
+
+ def test_primary_key_is_set_on_columns
+ posts_columns = @pool.columns_hash['posts']
+ assert posts_columns['id'].primary
+
+ (posts_columns.keys - ['id']).each do |key|
+ assert !posts_columns[key].primary
+ end
+ end
+
def test_clear_stale_cached_connections!
pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
@@ -55,6 +99,26 @@ module ActiveRecord
end.join()
end
+
+ def test_automatic_reconnect=
+ pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ assert pool.automatic_reconnect
+ assert pool.connection
+
+ pool.disconnect!
+ assert pool.connection
+
+ pool.disconnect!
+ pool.automatic_reconnect = false
+
+ assert_raises(ConnectionNotEstablished) do
+ pool.connection
+ end
+
+ assert_raises(ConnectionNotEstablished) do
+ pool.with_connection
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/habtm_destroy_order_test.rb b/activerecord/test/cases/habtm_destroy_order_test.rb
new file mode 100644
index 0000000000..15598392e2
--- /dev/null
+++ b/activerecord/test/cases/habtm_destroy_order_test.rb
@@ -0,0 +1,17 @@
+require "cases/helper"
+require "models/lesson"
+require "models/student"
+
+class HabtmDestroyOrderTest < ActiveRecord::TestCase
+ test "may not delete a lesson with students" do
+ sicp = Lesson.new(:name => "SICP")
+ ben = Student.new(:name => "Ben Bitdiddle")
+ sicp.students << ben
+ sicp.save!
+ assert_raises LessonError do
+ assert_no_difference('Lesson.count') do
+ sicp.destroy
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 2cc993b6ed..97bb631d2d 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -50,7 +50,7 @@ ensure
end
ActiveRecord::Base.connection.class.class_eval do
- IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
+ IGNORED_SQL = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/]
# FIXME: this needs to be refactored so specific database can add their own
# ignored SQL. This ignored SQL is for Oracle.
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6f0f73e3bd..9d7c49768b 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -2022,7 +2022,10 @@ if ActiveRecord::Base.connection.supports_migrations?
assert ! column(:name).default
assert_equal :date, column(:birthdate).type
- assert_queries(1) do
+ # One query for columns (delete_me table)
+ # One query for primary key (delete_me table)
+ # One query to do the bulk change
+ assert_queries(3) do
with_bulk_change_table do |t|
t.change :name, :string, :default => 'NONAME'
t.change :birthdate, :datetime
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 5bbe7ebb12..e081eee661 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -1,12 +1,14 @@
class Contact < ActiveRecord::Base
- def self.columns
- @columns
- end
+ establish_connection(:adapter => 'fake')
+
+ connection.tables = ['contacts']
+ connection.primary_keys = {
+ 'contacts' => 'id'
+ }
# mock out self.columns so no pesky db is needed for these tests
def self.column(name, sql_type = nil, options = {})
- @columns ||= []
- @columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, options[:default], sql_type.to_s, options[:null])
+ connection.merge_column('contacts', name, sql_type, options)
end
column :name, :string
diff --git a/activerecord/test/models/lesson.rb b/activerecord/test/models/lesson.rb
new file mode 100644
index 0000000000..4c88153068
--- /dev/null
+++ b/activerecord/test/models/lesson.rb
@@ -0,0 +1,11 @@
+class LessonError < Exception
+end
+
+class Lesson < ActiveRecord::Base
+ has_and_belongs_to_many :students
+ before_destroy :ensure_no_students
+
+ def ensure_no_students
+ raise LessonError unless students.empty?
+ end
+end
diff --git a/activerecord/test/models/student.rb b/activerecord/test/models/student.rb
new file mode 100644
index 0000000000..f459f2a9a3
--- /dev/null
+++ b/activerecord/test/models/student.rb
@@ -0,0 +1,3 @@
+class Student < ActiveRecord::Base
+ has_and_belongs_to_many :lessons
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 5f9bb7ee41..326c336317 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -279,6 +279,15 @@ ActiveRecord::Schema.define do
t.integer :version, :null => false, :default => 0
end
+ create_table :lessons, :force => true do |t|
+ t.string :name
+ end
+
+ create_table :lessons_students, :id => false, :force => true do |t|
+ t.references :lesson
+ t.references :student
+ end
+
create_table :line_items, :force => true do |t|
t.integer :invoice_id
t.integer :amount
@@ -509,6 +518,10 @@ ActiveRecord::Schema.define do
t.string :sponsorable_type
end
+ create_table :students, :force => true do |t|
+ t.string :name
+ end
+
create_table :subscribers, :force => true, :id => false do |t|
t.string :nick, :null => false
t.string :name