Rails 2.3.3.1

Update to latest Rails.
A little bit of jiggery-pokery is involved, since they
neglected to re-include vendored Rack in this release.
This commit is contained in:
Jacques Distler 2009-08-04 10:16:03 -05:00
parent 329fafafce
commit 664552ac02
257 changed files with 4346 additions and 1682 deletions

View file

@ -957,6 +957,8 @@ module ActiveRecord
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
# "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
# will use a foreign key of "favorite_person_id".
# [:primary_key]
# Specify the method that returns the primary key of associated object used for the association. By default this is id.
# [:dependent]
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
@ -981,15 +983,21 @@ module ActiveRecord
# If false, don't validate the associated objects when saving the parent object. +false+ by default.
# [:autosave]
# If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
# [:touch]
# If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
# destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
#
# Option examples:
# belongs_to :firm, :foreign_key => "client_of"
# belongs_to :person, :primary_key => "name", :foreign_key => "person_name"
# belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
# belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
# :conditions => 'discounts > #{payments_count}'
# belongs_to :attachable, :polymorphic => true
# belongs_to :project, :readonly => true
# belongs_to :post, :counter_cache => true
# belongs_to :company, :touch => true
# belongs_to :company, :touch => :employees_last_updated_at
def belongs_to(association_id, options = {})
reflection = create_belongs_to_reflection(association_id, options)
@ -1001,28 +1009,8 @@ module ActiveRecord
association_constructor_method(:create, reflection, BelongsToAssociation)
end
# Create the callbacks to update counter cache
if options[:counter_cache]
cache_column = reflection.counter_cache_column
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
end
after_create method_name
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
end
before_destroy method_name
module_eval(
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
)
end
add_counter_cache_callbacks(reflection) if options[:counter_cache]
add_touch_callbacks(reflection, options[:touch]) if options[:touch]
configure_dependency_for_belongs_to(reflection)
end
@ -1329,6 +1317,43 @@ module ActiveRecord
end
end
def add_counter_cache_callbacks(reflection)
cache_column = reflection.counter_cache_column
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
association.class.increment_counter(cache_column, association.id) unless association.nil?
end
after_create(method_name)
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
association.class.decrement_counter(cache_column, association.id) unless association.nil?
end
before_destroy(method_name)
module_eval(
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
)
end
def add_touch_callbacks(reflection, touch_attribute)
method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
if touch_attribute == true
association.touch unless association.nil?
else
association.touch(touch_attribute) unless association.nil?
end
end
after_save(method_name)
after_destroy(method_name)
end
def find_with_associations(options = {})
catch :invalid_query do
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
@ -1353,7 +1378,7 @@ module ActiveRecord
dependent_conditions = []
dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions]
dependent_conditions << extra_conditions if extra_conditions
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
dependent_conditions = dependent_conditions.gsub('@', '\@')
@ -1497,9 +1522,9 @@ module ActiveRecord
mattr_accessor :valid_keys_for_belongs_to_association
@@valid_keys_for_belongs_to_association = [
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
:class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions,
:include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
:validate
:validate, :touch
]
def create_belongs_to_reflection(association_id, options)
@ -1643,17 +1668,29 @@ module ActiveRecord
string.scan(/([\.a-zA-Z_]+).?\./).flatten
end
def tables_in_hash(hash)
return [] if hash.blank?
tables = hash.map do |key, value|
if value.is_a?(Hash)
key.to_s
else
tables_in_string(key) if key.is_a?(String)
end
end
tables.flatten.compact
end
def conditions_tables(options)
# look in both sets of conditions
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
case cond
when nil then all
when Array then all << cond.first
when Hash then all << cond.keys
else all << cond
when Array then all << tables_in_string(cond.first)
when Hash then all << tables_in_hash(cond)
else all << tables_in_string(cond)
end
end
tables_in_string(conditions.join(' '))
conditions.flatten
end
def order_tables(options)
@ -2101,7 +2138,7 @@ module ActiveRecord
klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
[through_reflection, reflection].each do |ref|
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions]
end
join

View file

@ -169,8 +169,8 @@ module ActiveRecord
end
# Forwards the call to the reflection class.
def sanitize_sql(sql)
@reflection.klass.send(:sanitize_sql, sql)
def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name)
@reflection.klass.send(:sanitize_sql, sql, table_name)
end
# Assigns the ID of the owner to the corresponding foreign key in +record+.

View file

@ -14,7 +14,7 @@ module ActiveRecord
if record.nil?
if counter_cache_name && !@owner.new_record?
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
@reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
end
@target = @owner[@reflection.primary_key_name] = nil
@ -27,7 +27,7 @@ module ActiveRecord
end
@target = (AssociationProxy === record ? record.target : record)
@owner[@reflection.primary_key_name] = record.id unless record.new_record?
@owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
@updated = true
end
@ -41,18 +41,36 @@ module ActiveRecord
private
def find_target
@reflection.klass.find(
find_method = if @reflection.options[:primary_key]
"find_by_#{@reflection.options[:primary_key]}"
else
"find"
end
@reflection.klass.send(find_method,
@owner[@reflection.primary_key_name],
:select => @reflection.options[:select],
:conditions => conditions,
:include => @reflection.options[:include],
:readonly => @reflection.options[:readonly]
)
) if @owner[@reflection.primary_key_name]
end
def foreign_key_present
!@owner[@reflection.primary_key_name].nil?
end
def record_id(record)
record.send(@reflection.options[:primary_key] || :id)
end
def previous_record_id
@previous_record_id ||= if @reflection.options[:primary_key]
previous_record = @owner.send(@reflection.name)
previous_record.nil? ? nil : previous_record.id
else
@owner[@reflection.primary_key_name]
end
end
end
end
end

View file

@ -7,7 +7,7 @@ module ActiveRecord
else
@target = (AssociationProxy === record ? record.target : record)
@owner[@reflection.primary_key_name] = record.id
@owner[@reflection.primary_key_name] = record_id(record)
@owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
@updated = true
@ -41,6 +41,10 @@ module ActiveRecord
!@owner[@reflection.primary_key_name].nil?
end
def record_id(record)
record.send(@reflection.options[:primary_key] || :id)
end
def association_class
@owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
end

View file

@ -1,31 +1,31 @@
module ActiveRecord
module Associations
class HasOneThroughAssociation < HasManyThroughAssociation
def create_through_record(new_value) #nodoc:
klass = @reflection.through_reflection.klass
current_object = @owner.send(@reflection.through_reflection.name)
if current_object
current_object.update_attributes(construct_join_attributes(new_value))
new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
else
@owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value)))
@owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value
end
end
private
def find(*args)
super(args.merge(:limit => 1))
end
def find_target
super.first
end
def reset_target!
@target = nil
end
end
end
end
end
end

View file

@ -311,11 +311,13 @@ module ActiveRecord
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_has_one_association(reflection)
if (association = association_instance_get(reflection.name)) && !association.target.nil?
if reflection.options[:autosave] && association.marked_for_destruction?
autosave = reflection.options[:autosave]
if autosave && association.marked_for_destruction?
association.destroy
elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave
association[reflection.primary_key_name] = id
association.save(false)
association.save(!autosave)
end
end
end
@ -330,13 +332,16 @@ module ActiveRecord
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_belongs_to_association(reflection)
if association = association_instance_get(reflection.name)
if reflection.options[:autosave] && association.marked_for_destruction?
autosave = reflection.options[:autosave]
if autosave && association.marked_for_destruction?
association.destroy
else
association.save(false) if association.new_record? || reflection.options[:autosave]
association.save(!autosave) if association.new_record? || autosave
if association.updated?
self[reflection.primary_key_name] = association.id
association_id = association.send(reflection.options[:primary_key] || :id)
self[reflection.primary_key_name] = association_id
# TODO: Removing this code doesn't seem to matter…
if reflection.options[:polymorphic]
self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s

View file

@ -687,14 +687,9 @@ module ActiveRecord #:nodoc:
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
def exists?(id_or_conditions = {})
connection.select_all(
construct_finder_sql(
:select => "#{quoted_table_name}.#{primary_key}",
:conditions => expand_id_conditions(id_or_conditions),
:limit => 1
),
"#{name} Exists"
).size > 0
find_initial(
:select => "#{quoted_table_name}.#{primary_key}",
:conditions => expand_id_conditions(id_or_conditions)) ? true : false
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
@ -2168,7 +2163,7 @@ module ActiveRecord #:nodoc:
# default_scope :order => 'last_name, first_name'
# end
def default_scope(options = {})
self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} }
end
# Test whether the given method and optional key are scoped.
@ -2228,12 +2223,12 @@ module ActiveRecord #:nodoc:
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
# { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
def sanitize_sql_for_conditions(condition)
def sanitize_sql_for_conditions(condition, table_name = quoted_table_name)
return nil if condition.blank?
case condition
when Array; sanitize_sql_array(condition)
when Hash; sanitize_sql_hash_for_conditions(condition)
when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
else condition
end
end
@ -3034,11 +3029,11 @@ module ActiveRecord #:nodoc:
def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
callstack.each do |name, values|
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
if values.empty?
send(name + "=", nil)
else
begin
begin
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
if values.empty?
send(name + "=", nil)
else
value = if Time == klass
instantiate_time_object(name, values)
elsif Date == klass
@ -3052,9 +3047,9 @@ module ActiveRecord #:nodoc:
end
send(name + "=", value)
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
end
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
end
end
unless errors.empty?
@ -3080,7 +3075,7 @@ module ActiveRecord #:nodoc:
end
def type_cast_attribute_value(multiparameter_name, value)
multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
end
def find_parameter_position(multiparameter_name)

View file

@ -141,30 +141,22 @@ module ActiveRecord
def construct_count_options_from_args(*args)
options = {}
column_name = :all
# We need to handle
# count()
# count(:column_name=:all)
# count(options={})
# count(column_name=:all, options={})
# selects specified by scopes
case args.size
when 0
column_name = scope(:find)[:select] if scope(:find)
when 1
if args[0].is_a?(Hash)
column_name = scope(:find)[:select] if scope(:find)
options = args[0]
else
column_name = args[0]
end
args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
when 2
column_name, options = args
else
raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
end
[column_name || :all, options]
end if args.size > 0
[column_name, options]
end
def construct_calculation_sql(operation, column_name, options) #:nodoc:

View file

@ -287,7 +287,13 @@ module ActiveRecord
# Escapes binary strings for bytea input to the database.
def escape_bytea(value)
if PGconn.respond_to?(:escape_bytea)
if @connection.respond_to?(:escape_bytea)
self.class.instance_eval do
define_method(:escape_bytea) do |value|
@connection.escape_bytea(value) if value
end
end
elsif PGconn.respond_to?(:escape_bytea)
self.class.instance_eval do
define_method(:escape_bytea) do |value|
PGconn.escape_bytea(value) if value
@ -376,7 +382,13 @@ module ActiveRecord
# Quotes strings for use in SQL input in the postgres driver for better performance.
def quote_string(s) #:nodoc:
if PGconn.respond_to?(:escape)
if @connection.respond_to?(:escape)
self.class.instance_eval do
define_method(:quote_string) do |s|
@connection.escape(s)
end
end
elsif PGconn.respond_to?(:escape)
self.class.instance_eval do
define_method(:quote_string) do |s|
PGconn.escape(s)
@ -392,9 +404,28 @@ module ActiveRecord
quote_string(s)
end
# Checks the following cases:
#
# - table_name
# - "table.name"
# - schema_name.table_name
# - schema_name."table.name"
# - "schema.name".table_name
# - "schema.name"."table.name"
def quote_table_name(name)
schema, name_part = extract_pg_identifier_from_name(name.to_s)
unless name_part
quote_column_name(schema)
else
table_name, name_part = extract_pg_identifier_from_name(name_part)
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
end
end
# Quotes column names for use in SQL queries.
def quote_column_name(name) #:nodoc:
%("#{name}")
PGconn.quote_ident(name.to_s)
end
# Quote date/time values for use in SQL input. Includes microseconds
@ -621,33 +652,36 @@ module ActiveRecord
def indexes(table_name, name = nil)
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
result = query(<<-SQL, name)
SELECT distinct i.relname, d.indisunique, a.attname
FROM pg_class t, pg_class i, pg_index d, pg_attribute a
SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
FROM pg_class t, pg_class i, pg_index d
WHERE i.relkind = 'i'
AND d.indexrelid = i.oid
AND d.indisprimary = 'f'
AND t.oid = d.indrelid
AND t.relname = '#{table_name}'
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
AND a.attrelid = t.oid
AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
ORDER BY i.relname
SQL
current_index = nil
indexes = []
result.each do |row|
if current_index != row[0]
indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", [])
current_index = row[0]
end
indexes = result.map do |row|
index_name = row[0]
unique = row[1] == 't'
indkey = row[2].split(" ")
oid = row[3]
columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist}
SELECT a.attname, a.attnum
FROM pg_attribute a
WHERE a.attrelid = #{oid}
AND a.attnum IN (#{indkey.join(",")})
SQL
column_names = indkey.map {|attnum| columns[attnum] }
IndexDefinition.new(table_name, index_name, unique, column_names)
indexes.last.columns << row[2]
end
indexes
@ -745,7 +779,7 @@ module ActiveRecord
AND attr.attrelid = cons.conrelid
AND attr.attnum = cons.conkey[1]
AND cons.contype = 'p'
AND dep.refobjid = '#{table}'::regclass
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql
if result.nil? or result.empty?
@ -764,7 +798,7 @@ module ActiveRecord
JOIN pg_attribute attr ON (t.oid = attrelid)
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
WHERE t.oid = '#{table}'::regclass
WHERE t.oid = '#{quote_table_name(table)}'::regclass
AND cons.contype = 'p'
AND def.adsrc ~* 'nextval'
end_sql
@ -839,7 +873,7 @@ module ActiveRecord
# Drops an index from a table.
def remove_index(table_name, options = {})
execute "DROP INDEX #{index_name(table_name, options)}"
execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}"
end
# Maps logical Rails types to PostgreSQL-specific data types.
@ -1040,11 +1074,21 @@ module ActiveRecord
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
WHERE a.attrelid = '#{table_name}'::regclass
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum
end_sql
end
def extract_pg_identifier_from_name(name)
match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
if match_data
rest = name[match_data[0].length..-1]
rest = rest[1..-1] if rest[0,1] == "."
[match_data[1], (rest.length > 0 ? rest : nil)]
end
end
end
end
end

View file

@ -1,3 +1,4 @@
# encoding: binary
require 'active_record/connection_adapters/abstract_adapter'
module ActiveRecord
@ -46,6 +47,7 @@ module ActiveRecord
class SQLiteColumn < Column #:nodoc:
class << self
def string_to_binary(value)
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
value.gsub(/\0|\%/n) do |b|
case b
when "\0" then "%00"
@ -55,6 +57,7 @@ module ActiveRecord
end
def binary_to_string(value)
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
value.gsub(/%00|%25/n) do |b|
case b
when "%00" then "\0"

View file

@ -1,6 +1,7 @@
require 'erb'
require 'yaml'
require 'csv'
require 'zlib'
require 'active_support/dependencies'
require 'active_support/test_case'
@ -433,6 +434,7 @@ end
# Any fixture labeled "DEFAULTS" is safely ignored.
class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
MAX_ID = 2 ** 31 - 1
DEFAULT_FILTER_RE = /\.ya?ml$/
@@all_cached_fixtures = {}
@ -524,11 +526,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
cached_fixtures(connection, table_names)
end
# Returns a consistent identifier for +label+. This will always
# be a positive integer, and will always be the same for a given
# label, assuming the same OS, platform, and version of Ruby.
# Returns a consistent, platform-independent identifier for +label+.
# Identifiers are positive integers less than 2^32.
def self.identify(label)
label.to_s.hash.abs
Zlib.crc32(label.to_s) % MAX_ID
end
attr_reader :table_name, :name

View file

@ -114,7 +114,7 @@ module ActiveRecord
end
end
delegate :scopes, :with_scope, :to => :proxy_scope
delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
def initialize(proxy_scope, options, &block)
options ||= {}
@ -178,7 +178,7 @@ module ActiveRecord
else
with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
method = :new if method == :build
if current_scoped_methods_when_defined
if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
with_scope current_scoped_methods_when_defined do
proxy_scope.send(method, *args, &block)
end

View file

@ -78,11 +78,14 @@ HEADER
begin
tbl = StringIO.new
# first dump primary key column
if @connection.respond_to?(:pk_and_sequence_for)
pk, pk_seq = @connection.pk_and_sequence_for(table)
elsif @connection.respond_to?(:primary_key)
pk = @connection.primary_key(table)
end
pk ||= 'id'
tbl.print " create_table #{table.inspect}"
if columns.detect { |c| c.name == pk }
if pk != 'id'
@ -94,6 +97,7 @@ HEADER
tbl.print ", :force => true"
tbl.puts " do |t|"
# then dump all non-primary key columns
column_specs = columns.map do |column|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
next if column.name == pk

View file

@ -5,8 +5,9 @@ module ActiveRecord #:nodoc:
class Serializer #:nodoc:
attr_reader :options
def initialize(record, options = {})
@record, @options = record, options.dup
def initialize(record, options = nil)
@record = record
@options = options ? options.dup : {}
end
# To replicate the behavior in ActiveRecord#attributes,

View file

@ -1,8 +1,10 @@
require 'active_support/json'
require 'active_support/core_ext/module/model_naming'
module ActiveRecord #:nodoc:
module Serialization
def self.included(base)
base.cattr_accessor :include_root_in_json, :instance_writer => false
base.extend ClassMethods
end
# Returns a JSON string representing the model. Some configuration is
@ -72,28 +74,16 @@ module ActiveRecord #:nodoc:
# {"comments": [{"body": "Don't think too hard"}],
# "title": "So I was thinking"}]}
def to_json(options = {})
if include_root_in_json
"{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
else
JsonSerializer.new(self, options).to_s
end
hash = Serializer.new(self, options).serializable_record
hash = { self.class.model_name.element => hash } if include_root_in_json
ActiveSupport::JSON.encode(hash)
end
def as_json(options = nil) self end #:nodoc:
def from_json(json)
self.attributes = ActiveSupport::JSON.decode(json)
self
end
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
def serialize
serializable_record.to_json
end
end
module ClassMethods
def json_class_name
@json_class_name ||= name.demodulize.underscore.inspect
end
end
end
end

View file

@ -295,7 +295,7 @@ module ActiveRecord
def set_session(env, sid, session_data)
Base.silence do
record = env[SESSION_RECORD_KEY] ||= find_session(sid)
record = get_session_model(env, sid)
record.data = session_data
return false unless record.save
@ -309,6 +309,14 @@ module ActiveRecord
return true
end
def get_session_model(env, sid)
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
env[SESSION_RECORD_KEY] = find_session(sid)
else
env[SESSION_RECORD_KEY] ||= find_session(sid)
end
end
def find_session(id)
@@session_class.find_by_session_id(id) ||

View file

@ -15,27 +15,57 @@ module ActiveRecord
base.class_inheritable_accessor :record_timestamps, :instance_writer => false
base.record_timestamps = true
end
# Saves the record with the updated_at/on attributes set to the current time.
# If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised.
# If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes.
#
# Examples:
#
# product.touch # updates updated_at
# product.touch(:designed_at) # updates the designed_at attribute
def touch(attribute = nil)
current_time = current_time_from_proper_timezone
if attribute
write_attribute(attribute, current_time)
else
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
end
save!
end
private
def create_with_timestamps #:nodoc:
if record_timestamps
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
current_time = current_time_from_proper_timezone
write_attribute('updated_at', t) if respond_to?(:updated_at) && updated_at.nil?
write_attribute('updated_on', t) if respond_to?(:updated_on) && updated_on.nil?
write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil?
write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil?
write_attribute('updated_at', current_time) if respond_to?(:updated_at) && updated_at.nil?
write_attribute('updated_on', current_time) if respond_to?(:updated_on) && updated_on.nil?
end
create_without_timestamps
end
def update_with_timestamps(*args) #:nodoc:
if record_timestamps && (!partial_updates? || changed?)
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
write_attribute('updated_at', t) if respond_to?(:updated_at)
write_attribute('updated_on', t) if respond_to?(:updated_on)
current_time = current_time_from_proper_timezone
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
end
update_without_timestamps(*args)
end
def current_time_from_proper_timezone
self.class.default_timezone == :utc ? Time.now.utc : Time.now
end
end
end
end

View file

@ -2,7 +2,7 @@ module ActiveRecord
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 2
TINY = 3
STRING = [MAJOR, MINOR, TINY].join('.')
end