Updating to Rails 3.2.4.

Among other fixes, this addresses the Rails security advisory
from 5/31/2012:

http://groups.google.com/group/rubyonrails-security/browse_thread/thread/7546a238e1962f59
http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f1203e3376acec0f

Thanks Joe and Trevor!

Squashed commit of the following:

commit d7031cebcc
Author: Joe Vennix <Joe_Vennix@rapid7.com>
Date:   Thu May 31 16:57:29 2012 -0500

    Update activerecord in gemcache to support rails 3.2.4. [#30507689]

commit c7369f6d66
Author: Joe Vennix <Joe_Vennix@rapid7.com>
Date:   Thu May 31 16:53:01 2012 -0500

    Bump rails version.
This commit is contained in:
Joe Vennix 2012-05-31 17:09:59 -05:00 committed by Tod Beardsley
parent c463bd7c6d
commit daf5ae8e4b
152 changed files with 403 additions and 147 deletions

View File

@ -1,5 +1,5 @@
source 'http://rubygems.org'
gem 'rails', '3.2.2'
gem 'rails', '3.2.4'
gem 'metasploit_data_models', '0.0.2', :git => "git://github.com/rapid7/metasploit_data_models.git"
gem 'pg', '>=0.13'
gem 'msgpack'

View File

@ -0,0 +1,14 @@
#!/usr/bin/env ruby
original_file=ARGV[0]
ARGV.shift
$PROGRAM_NAME=original_file
require 'rubygems'
begin
require 'rubygems-bundler/noexec'
rescue LoadError
warn "unable to load rubygems-bundler/noexec" if ENV.key?('NOEXEC_DEBUG')
end
eval File.read(original_file), binding, original_file

View File

@ -1,3 +1,43 @@
## Rails 3.2.4 (May 31, 2012) ##
* Perf fix: Don't load the records when doing assoc.delete_all.
GH #6289. *Jon Leighton*
* Association preloading shouldn't be affected by the current scoping.
This could cause infinite recursion and potentially other problems.
See GH #5667. *Jon Leighton*
* Datetime attributes are forced to be changed. GH #3965
* Fix attribute casting. GH #5549
* Fix #5667. Preloading should ignore scoping.
* Predicate builder should not recurse for determining where columns.
Thanks to Ben Murphy for reporting this! CVE-2012-2661
## Rails 3.2.3 (March 30, 2012) ##
* Added find_or_create_by_{attribute}! dynamic method. *Andrew White*
* Whitelist all attribute assignment by default. Change the default for newly generated applications to whitelist all attribute assignment. Also update the generated model classes so users are reminded of the importance of attr_accessible. *NZKoz*
* Update ActiveRecord::AttributeMethods#attribute_present? to return false for empty strings. *Jacobkg*
* Fix associations when using per class databases. *larskanis*
* Revert setting NOT NULL constraints in add_timestamps *fxn*
* Fix mysql to use proper text types. Fixes #3931. *kennyj*
* Fix #5069 - Protect foreign key from mass assignment through association builder. *byroot*
## Rails 3.2.2 (March 1, 2012) ##
* No changes.
## Rails 3.2.1 (January 26, 2012) ##
* The threshold for auto EXPLAIN is ignored if there's no logger. *fxn*
@ -192,7 +232,8 @@
*Brian Durand*
## Rails 3.1.3 (unreleased) ##
## Rails 3.1.3 (November 20, 2011) ##
* Perf fix: If we're deleting all records in an association, don't add a IN(..) clause
to the query. *GH 3672*
@ -205,7 +246,8 @@
*Christos Zisopoulos and Kenny J*
## Rails 3.1.2 (unreleased) ##
## Rails 3.1.2 (November 18, 2011) ##
* Fix bug with PostgreSQLAdapter#indexes. When the search path has multiple schemas, spaces
were not being stripped from the schema names after the first.
@ -252,6 +294,7 @@
*Kenny J*
## Rails 3.1.1 (October 7, 2011) ##
* Add deprecation for the preload_associations method. Fixes #3022.

View File

@ -203,7 +203,7 @@ The latest version of Active Record can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
* https://github.com/rails/rails/tree/master/activerecord
* https://github.com/rails/rails/tree/3-2-stable/activerecord
== License

View File

@ -46,7 +46,7 @@ module ActiveRecord
#
# def <=>(other_money)
# if currency == other_money.currency
# amount <=> amount
# amount <=> other_money.amount
# else
# amount <=> other_money.exchange_to(currency).amount
# end

View File

@ -1513,8 +1513,8 @@ module ActiveRecord
# * <tt>Developer#projects.size</tt>
# * <tt>Developer#projects.find(id)</tt>
# * <tt>Developer#projects.exists?(...)</tt>
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
# The declaration may include an options hash to specialize the behavior of the association.
#
# === Options

View File

@ -5,12 +5,13 @@ module ActiveRecord
# Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
# ActiveRecord::Associations::ThroughAssociationScope
class AliasTracker # :nodoc:
attr_reader :aliases, :table_joins
attr_reader :aliases, :table_joins, :connection
# table_joins is an array of arel joins which might conflict with the aliases we assign here
def initialize(table_joins = [])
def initialize(connection = ActiveRecord::Model.connection, table_joins = [])
@aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
@table_joins = table_joins
@connection = connection
end
def aliased_table_for(table_name, aliased_name = nil)
@ -70,10 +71,6 @@ module ActiveRecord
def truncate(name)
name.slice(0, connection.table_alias_length - 2)
end
def connection
ActiveRecord::Base.connection
end
end
end
end

View File

@ -231,7 +231,8 @@ module ActiveRecord
def build_record(attributes, options)
reflection.build_association(attributes, options) do |record|
record.assign_attributes(create_scope.except(*record.changed), :without_protection => true)
attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
record.assign_attributes(attributes, :without_protection => true)
end
end
end

View File

@ -10,7 +10,7 @@ module ActiveRecord
def initialize(association)
@association = association
@alias_tracker = AliasTracker.new
@alias_tracker = AliasTracker.new klass.connection
end
def scope
@ -75,7 +75,7 @@ module ActiveRecord
conditions.each do |condition|
if options[:through] && condition.is_a?(Hash)
condition = { table.name => condition }
condition = disambiguate_condition(table, condition)
end
scope = scope.where(interpolate(condition))
@ -114,6 +114,21 @@ module ActiveRecord
end
end
def disambiguate_condition(table, condition)
if condition.is_a?(Hash)
Hash[
condition.map do |k, v|
if v.is_a?(Hash)
[k, v]
else
[table.table_alias || table.name, { k => v }]
end
end
]
else
condition
end
end
end
end
end

View File

@ -154,7 +154,7 @@ module ActiveRecord
#
# See delete for more info.
def delete_all
delete(load_target).tap do
delete(:all).tap do
reset
loaded!
end
@ -226,7 +226,17 @@ module ActiveRecord
# are actually removed from the database, that depends precisely on
# +delete_records+. They are in any case removed from the collection.
def delete(*records)
delete_or_destroy(records, options[:dependent])
dependent = options[:dependent]
if records.first == :all
if loaded? || dependent == :destroy
delete_or_destroy(load_target, dependent)
else
delete_records(:all, dependent)
end
else
delete_or_destroy(records, dependent)
end
end
# Destroy +records+ and remove them from this association calling
@ -481,6 +491,8 @@ module ActiveRecord
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
"new records could not be saved."
end
target
end
def concat_records(records)

View File

@ -44,13 +44,20 @@ module ActiveRecord
def delete_records(records, method)
if sql = options[:delete_sql]
records = load_target if records == :all
records.each { |record| owner.connection.delete(interpolate(sql, record)) }
else
relation = join_table
stmt = relation.where(relation[reflection.foreign_key].eq(owner.id).
and(relation[reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
).compile_delete
owner.connection.delete stmt
relation = join_table
condition = relation[reflection.foreign_key].eq(owner.id)
unless records == :all
condition = condition.and(
relation[reflection.association_foreign_key].
in(records.map { |x| x.id }.compact)
)
end
owner.connection.delete(relation.where(condition).compile_delete)
end
end

View File

@ -89,8 +89,12 @@ module ActiveRecord
records.each { |r| r.destroy }
update_counter(-records.length) unless inverse_updates_counter_cache?
else
keys = records.map { |r| r[reflection.association_primary_key] }
scope = scoped.where(reflection.association_primary_key => keys)
if records == :all
scope = scoped
else
keys = records.map { |r| r[reflection.association_primary_key] }
scope = scoped.where(reflection.association_primary_key => keys)
end
if method == :delete_all
update_counter(-scope.delete_all)

View File

@ -73,7 +73,9 @@ module ActiveRecord
# association
def build_through_record(record)
@through_records[record.object_id] ||= begin
through_record = through_association.build(construct_join_attributes(record))
ensure_mutable
through_record = through_association.build
through_record.send("#{source_reflection.name}=", record)
through_record
end
@ -124,6 +126,10 @@ module ActiveRecord
def delete_records(records, method)
ensure_not_nested
# This is unoptimised; it will load all the target records
# even when we just want to delete everything.
records = load_target if records == :all
scope = through_association.scoped.where(construct_join_attributes(*records))
case method

View File

@ -13,7 +13,7 @@ module ActiveRecord
@join_parts = [JoinBase.new(base)]
@associations = {}
@reflections = []
@alias_tracker = AliasTracker.new(joins)
@alias_tracker = AliasTracker.new(base.connection, joins)
@alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
build(associations)
end

View File

@ -77,7 +77,7 @@ module ActiveRecord
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size)
records = sliced.map { |slice| records_for(slice) }.flatten
records = sliced.map { |slice| records_for(slice).to_a }.flatten
end
# Each record may have multiple owners, and vice-versa
@ -93,7 +93,8 @@ module ActiveRecord
end
def build_scope
scope = klass.scoped
scope = klass.unscoped
scope.default_scoped = true
scope = scope.where(process_conditions(options[:conditions]))
scope = scope.where(process_conditions(preload_options[:conditions]))

View File

@ -37,9 +37,7 @@ module ActiveRecord
# situation it is more natural for the user to just create or modify their join records
# directly as required.
def construct_join_attributes(*records)
if source_reflection.macro != :belongs_to
raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
end
ensure_mutable
join_attributes = {
source_reflection.foreign_key =>
@ -73,6 +71,12 @@ module ActiveRecord
!owner[through_reflection.foreign_key].nil?
end
def ensure_mutable
if source_reflection.macro != :belongs_to
raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
end
end
def ensure_not_nested
if reflection.nested?
raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)

View File

@ -64,6 +64,7 @@ module ActiveRecord
return if attribute_methods_generated?
superclass.define_attribute_methods unless self == base_class
super(column_names)
column_names.each { |name| define_external_attribute_method(name) }
@attribute_methods_generated = true
end
end
@ -212,7 +213,7 @@ module ActiveRecord
# nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
def attribute_present?(attribute)
value = read_attribute(attribute)
!value.nil? || (value.respond_to?(:empty?) && !value.empty?)
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
end
# Returns the column object for the named attribute.

View File

@ -67,19 +67,9 @@ module ActiveRecord
# we first define with the __temp__ identifier, and then use alias method to
# rename it to what we want.
def define_method_attribute(attr_name)
cast_code = attribute_cast_code(attr_name)
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__
#{internal_attribute_access_code(attr_name, cast_code)}
end
alias_method '#{attr_name}', :__temp__
undef_method :__temp__
STR
generated_external_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__(v, attributes, attributes_cache, attr_name)
#{external_attribute_access_code(attr_name, cast_code)}
#{internal_attribute_access_code(attr_name, attribute_cast_code(attr_name))}
end
alias_method '#{attr_name}', :__temp__
undef_method :__temp__
@ -87,6 +77,17 @@ module ActiveRecord
end
private
def define_external_attribute_method(attr_name)
generated_external_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__(v, attributes, attributes_cache, attr_name)
#{external_attribute_access_code(attr_name, attribute_cast_code(attr_name))}
end
alias_method '#{attr_name}', :__temp__
undef_method :__temp__
STR
end
def cacheable_column?(column)
attribute_types_cached_by_default.include?(column.type)
end

View File

@ -43,6 +43,7 @@ module ActiveRecord
end
time = time.in_time_zone rescue nil if time
write_attribute(:#{attr_name}, original_time)
#{attr_name}_will_change!
@attributes_cache["#{attr_name}"] = time
end
EOV

View File

@ -208,6 +208,9 @@ module ActiveRecord #:nodoc:
# # Now 'Bob' exist and is an 'admin'
# User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
#
# Adding an exclamation point (!) on to the end of <tt>find_or_create_by_</tt> will
# raise an <tt>ActiveRecord::RecordInvalid</tt> error if the new record is invalid.
#
# Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without
# saving it first. Protected attributes won't be set unless they are given in a block.
#
@ -439,7 +442,7 @@ module ActiveRecord #:nodoc:
if self == ActiveRecord::Base
ActiveRecord::Base
else
connection_handler.connection_pools[name] ? self : superclass.arel_engine
connection_handler.retrieve_connection_pool(self) ? self : superclass.arel_engine
end
end
end

View File

@ -92,13 +92,18 @@ module ActiveRecord
# #connection can be called any number of times; the connection is
# held in a hash keyed by the thread id.
def connection
@reserved_connections[current_connection_id] ||= checkout
synchronize do
@reserved_connections[current_connection_id] ||= checkout
end
end
# Check to see if there is an active connection in this connection
# pool.
# Is there an open connection that is being used for the current thread?
def active_connection?
active_connections.any?
synchronize do
@reserved_connections.fetch(current_connection_id) {
return false
}.in_use?
end
end
# Signal that the thread is finished with the current connection.
@ -225,8 +230,9 @@ connection. For example: ActiveRecord::Base.connection.close
# - ConnectionTimeoutError: no connection can be obtained from the pool
# within the timeout period.
def checkout
# Checkout an available connection
synchronize do
waited_time = 0
loop do
conn = @connections.find { |c| c.lease }
@ -242,17 +248,25 @@ connection. For example: ActiveRecord::Base.connection.close
return conn
end
@queue.wait(@timeout)
if(active_connections.size < @connections.size)
next
else
clear_stale_cached_connections!
if @size == active_connections.size
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
end
if waited_time >= @timeout
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout} (waited #{waited_time} seconds). The max pool size is currently #{@size}; consider increasing it."
end
# Sometimes our wait can end because a connection is available,
# but another thread can snatch it up first. If timeout hasn't
# passed but no connection is avail, looks like that happened --
# loop and wait again, for the time remaining on our timeout.
before_wait = Time.now
@queue.wait( [@timeout - waited_time, 0].max )
waited_time += (Time.now - before_wait)
# Will go away in Rails 4, when we don't clean up
# after leaked connections automatically anymore. Right now, clean
# up after we've returned from a 'wait' if it looks like it's
# needed, then loop and try again.
if(active_connections.size >= @connections.size)
clear_stale_cached_connections!
end
end
end
end
@ -268,11 +282,29 @@ connection. For example: ActiveRecord::Base.connection.close
conn.expire
@queue.signal
end
release conn
end
end
private
def release(conn)
synchronize do
thread_id = nil
if @reserved_connections[current_connection_id] == conn
thread_id = current_connection_id
else
thread_id = @reserved_connections.keys.find { |k|
@reserved_connections[k] == conn
}
end
@reserved_connections.delete thread_id if thread_id
end
end
def new_connection
ActiveRecord::Base.send(spec.adapter_method, spec.config)
end
@ -344,9 +376,7 @@ connection. For example: ActiveRecord::Base.connection.close
connection_pools.values.any? { |pool| pool.active_connection? }
end
# Returns any connections in use by the current thread back to the pool,
# and also returns connections to the pool cached by threads that are no
# longer alive.
# Returns any connections in use by the current thread back to the pool.
def clear_active_connections!
@connection_pools.each_value {|pool| pool.release_connection }
end

View File

@ -160,7 +160,7 @@ module ActiveRecord
yield td if block_given?
if options[:force] && table_exists?(table_name)
drop_table(table_name)
drop_table(table_name, options)
end
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
@ -252,7 +252,7 @@ module ActiveRecord
end
# Drops a table from the database.
def drop_table(table_name)
def drop_table(table_name, options = {})
execute "DROP TABLE #{quote_table_name(table_name)}"
end
@ -269,7 +269,15 @@ module ActiveRecord
# remove_column(:suppliers, :qualification)
# remove_columns(:suppliers, :qualification, :experience)
def remove_column(table_name, *column_names)
columns_for_remove(table_name, *column_names).each {|column_name| execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" }
if column_names.flatten!
message = 'Passing array to remove_columns is deprecated, please use ' +
'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
ActiveSupport::Deprecation.warn message, caller
end
columns_for_remove(table_name, *column_names).each do |column_name|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}"
end
end
alias :remove_columns :remove_column
@ -508,8 +516,8 @@ module ActiveRecord
# ===== Examples
# add_timestamps(:suppliers)
def add_timestamps(table_name)
add_column table_name, :created_at, :datetime, :null => false
add_column table_name, :updated_at, :datetime, :null => false
add_column table_name, :created_at, :datetime
add_column table_name, :updated_at, :datetime
end
# Removes the timestamp columns (created_at and updated_at) from the table definition.

View File

@ -54,7 +54,7 @@ module ActiveRecord
define_callbacks :checkout, :checkin
attr_accessor :visitor, :pool
attr_reader :schema_cache, :last_use, :in_use
attr_reader :schema_cache, :last_use, :in_use, :logger
alias :in_use? :in_use
def initialize(connection, logger = nil, pool = nil) #:nodoc:

View File

@ -484,15 +484,26 @@ module ActiveRecord
# Maps logical Rails types to MySQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
return super unless type.to_s == 'integer'
case limit
when 1; 'tinyint'
when 2; 'smallint'
when 3; 'mediumint'
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
when 5..8; 'bigint'
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
case type.to_s
when 'integer'
case limit
when 1; 'tinyint'
when 2; 'smallint'
when 3; 'mediumint'
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
when 5..8; 'bigint'
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
end
when 'text'
case limit
when 0..0xff; 'tinytext'
when nil, 0x100..0xffff; 'text'
when 0x10000..0xffffff; 'mediumtext'
when 0x1000000..0xffffffff; 'longtext'
else raise(ActiveRecordError, "No text type has character length #{limit}")
end
else
super
end
end
@ -514,7 +525,7 @@ module ActiveRecord
def pk_and_sequence_for(table)
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
create_table = each_hash(result).first[:"Create Table"]
if create_table.to_s =~ /PRIMARY KEY\s+\((.+)\)/
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
keys.length == 1 ? [keys.first, nil] : nil
else

View File

@ -264,7 +264,7 @@ module ActiveRecord
# increase timeout so mysql server doesn't disconnect us
wait_timeout = @config[:wait_timeout]
wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum)
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
variable_assignments << "@@wait_timeout = #{wait_timeout}"
execute("SET #{variable_assignments.join(', ')}", :skip_logging)

View File

@ -885,7 +885,7 @@ module ActiveRecord
# This should be not be called manually but set in database.yml.
def schema_search_path=(schema_csv)
if schema_csv
execute "SET search_path TO #{schema_csv}"
execute("SET search_path TO #{schema_csv}", 'SCHEMA')
@schema_search_path = schema_csv
end
end
@ -1067,14 +1067,25 @@ module ActiveRecord
# Maps logical Rails types to PostgreSQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
return super unless type.to_s == 'integer'
return 'integer' unless limit
case limit
when 1, 2; 'smallint'
when 3, 4; 'integer'
when 5..8; 'bigint'
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
case type.to_s
when 'binary'
# PostgreSQL doesn't support limits on binary (bytea) columns.
# The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
case limit
when nil, 0..0x3fffffff; super(type)
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
end
when 'integer'
return 'integer' unless limit
case limit
when 1, 2; 'smallint'
when 3, 4; 'integer'
when 5..8; 'bigint'
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
end
else
super
end
end

View File

@ -204,7 +204,7 @@ module ActiveRecord
value = super
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
@logger.error "Binary data inserted for `string` type on column `#{column.name}`"
logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
value.encode! 'utf-8'
end
value
@ -407,7 +407,14 @@ module ActiveRecord
def remove_column(table_name, *column_names) #:nodoc:
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
if column_names.flatten!
message = 'Passing array to remove_columns is deprecated, please use ' +
'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
ActiveSupport::Deprecation.warn message, caller
end
column_names.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
end

View File

@ -18,6 +18,10 @@ module ActiveRecord
when /^find_by_([_a-zA-Z]\w*)\!$/
bang = true
names = $1
when /^find_or_create_by_([_a-zA-Z]\w*)\!$/
bang = true
instantiator = :create
names = $1
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
instantiator = $1 == 'initialize' ? :new : :create
names = $2
@ -52,5 +56,13 @@ module ActiveRecord
def bang?
@bang
end
def save_record?
@instantiator == :create
end
def save_method
bang? ? :save! : :save
end
end
end

View File

@ -419,11 +419,15 @@ module ActiveRecord
cache_for_connection(connection).update(fixtures_map)
end
def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
#--
# TODO:NOTE: in the next version, the __with_new_arity suffix and
# the method with the old arity will be removed.
#++
def self.instantiate_fixtures__with_new_arity(object, fixture_set, load_instances = true) # :nodoc:
if load_instances
fixtures.each do |name, fixture|
fixture_set.each do |fixture_name, fixture|
begin
object.instance_variable_set "@#{name}", fixture.find
object.instance_variable_set "@#{fixture_name}", fixture.find
rescue FixtureClassNotFound
nil
end
@ -431,9 +435,24 @@ module ActiveRecord
end
end
# The use with parameters <tt>(object, fixture_set_name, fixture_set, load_instances = true)</tt> is deprecated, +fixture_set_name+ parameter is not used.
# Use as:
#
# instantiate_fixtures(object, fixture_set, load_instances = true)
def self.instantiate_fixtures(object, fixture_set, load_instances = true, rails_3_2_compatibility_argument = true)
unless load_instances == true || load_instances == false
ActiveSupport::Deprecation.warn(
"ActiveRecord::Fixtures.instantiate_fixtures with parameters (object, fixture_set_name, fixture_set, load_instances = true) is deprecated and shall be removed from future releases. Use it with parameters (object, fixture_set, load_instances = true) instead (skip fixture_set_name).",
caller)
fixture_set = load_instances
load_instances = rails_3_2_compatibility_argument
end
instantiate_fixtures__with_new_arity(object, fixture_set, load_instances)
end
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
all_loaded_fixtures.each do |table_name, fixtures|
ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
all_loaded_fixtures.each_value do |fixture_set|
ActiveRecord::Fixtures.instantiate_fixtures(object, fixture_set, load_instances)
end
end
@ -659,9 +678,6 @@ module ActiveRecord
"#{@fixture_path}.yml"
end
def yaml_fixtures_key(path)
::File.basename(@fixture_path).split(".").first
end
end
class Fixture #:nodoc:
@ -893,8 +909,8 @@ module ActiveRecord
ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
@loaded_fixtures.each do |fixture_name, fixtures|
ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
@loaded_fixtures.each_value do |fixture_set|
ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
end
end
end

View File

@ -90,7 +90,7 @@ module ActiveRecord
end
def add(record)
repository[record.class.symbolized_sti_name][record.id] = record
repository[record.class.symbolized_sti_name][record.id] = record if contain_all_columns?(record)
end
def remove(record)
@ -104,6 +104,12 @@ module ActiveRecord
def clear
repository.clear
end
private
def contain_all_columns?(record)
(record.class.column_names - record.attribute_names).empty?
end
end
# Reinitialize an Identity Map model object from +coder+.

View File

@ -63,15 +63,7 @@ module ActiveRecord
record_id = sti_class.primary_key && record[sti_class.primary_key]
if ActiveRecord::IdentityMap.enabled? && record_id
if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
record_id = record_id.to_i
end
if instance = IdentityMap.get(sti_class, record_id)
instance.reinit_with('attributes' => record)
else
instance = sti_class.allocate.init_with('attributes' => record)
IdentityMap.add(instance)
end
instance = use_identity_map(sti_class, record_id, record)
else
instance = sti_class.allocate.init_with('attributes' => record)
end
@ -122,6 +114,21 @@ module ActiveRecord
private
def use_identity_map(sti_class, record_id, record)
if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
record_id = record_id.to_i
end
if instance = IdentityMap.get(sti_class, record_id)
instance.reinit_with('attributes' => record)
else
instance = sti_class.allocate.init_with('attributes' => record)
IdentityMap.add(instance)
end
instance
end
def find_sti_class(type_name)
if type_name.blank? || !columns_hash.include?(inheritance_column)
self

View File

@ -346,12 +346,24 @@ module ActiveRecord
@name = self.class.name
@version = nil
@connection = nil
@reverting = false
end
# instantiate the delegate object after initialize is defined
self.verbose = true
self.delegate = new
def revert
@reverting = true
yield
ensure
@reverting = false
end
def reverting?
@reverting
end
def up
self.class.delegate = self
return unless self.class.respond_to?(:up)
@ -385,9 +397,11 @@ module ActiveRecord
end
@connection = conn
time = Benchmark.measure {
recorder.inverse.each do |cmd, args|
send(cmd, *args)
end
self.revert {
recorder.inverse.each do |cmd, args|
send(cmd, *args)
end
}
}
else
time = Benchmark.measure { change }
@ -442,9 +456,11 @@ module ActiveRecord
arg_list = arguments.map{ |a| a.inspect } * ', '
say_with_time "#{method}(#{arg_list})" do
unless arguments.empty? || method == :execute
arguments[0] = Migrator.proper_table_name(arguments.first)
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
unless reverting?
unless arguments.empty? || method == :execute
arguments[0] = Migrator.proper_table_name(arguments.first)
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
end
end
return super unless connection.respond_to?(method)
connection.send(method, *arguments, &block)

Some files were not shown because too many files have changed in this diff Show More