aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-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
14 files changed, 147 insertions, 105 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