Instiki 0.16.5
Update to Rails 2.3.2 (the stable Rails 2.3 release). Add audio/speex support Update CHANGELOG Bump version number
This commit is contained in:
parent
801d307405
commit
e2ccdfd812
264 changed files with 4850 additions and 1906 deletions
7
vendor/rails/activerecord/CHANGELOG
vendored
7
vendor/rails/activerecord/CHANGELOG
vendored
|
@ -1,12 +1,9 @@
|
|||
*2.3.1 [RC2] (March 5, 2009)*
|
||||
*2.3.2 [Final] (March 15, 2009)*
|
||||
|
||||
* Added ActiveRecord::Base.each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
|
||||
* Added ActiveRecord::Base.find_each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
|
||||
|
||||
* Added that ActiveRecord::Base.exists? can be called with no arguments #1817 [Scott Taylor]
|
||||
|
||||
|
||||
*2.3.0 [RC1] (February 1st, 2009)*
|
||||
|
||||
* Add Support for updating deeply nested models from a single form. #1202 [Eloy Duran]
|
||||
|
||||
class Book < ActiveRecord::Base
|
||||
|
|
2
vendor/rails/activerecord/Rakefile
vendored
2
vendor/rails/activerecord/Rakefile
vendored
|
@ -177,7 +177,7 @@ spec = Gem::Specification.new do |s|
|
|||
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD)
|
||||
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
||||
|
|
|
@ -51,6 +51,12 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
|
||||
def initialize(reflection)
|
||||
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
|
||||
end
|
||||
end
|
||||
|
||||
class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
|
||||
def initialize(reflection)
|
||||
super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
|
||||
|
@ -65,7 +71,7 @@ module ActiveRecord
|
|||
|
||||
# See ActiveRecord::Associations::ClassMethods for documentation.
|
||||
module Associations # :nodoc:
|
||||
# These classes will be loaded when associatoins are created.
|
||||
# These classes will be loaded when associations are created.
|
||||
# So there is no need to eager load them.
|
||||
autoload :AssociationCollection, 'active_record/associations/association_collection'
|
||||
autoload :AssociationProxy, 'active_record/associations/association_proxy'
|
||||
|
@ -997,9 +1003,7 @@ module ActiveRecord
|
|||
|
||||
# Create the callbacks to update counter cache
|
||||
if options[:counter_cache]
|
||||
cache_column = options[:counter_cache] == true ?
|
||||
"#{self.to_s.demodulize.underscore.pluralize}_count" :
|
||||
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
|
||||
|
@ -1526,6 +1530,10 @@ module ActiveRecord
|
|||
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
||||
|
||||
reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
|
||||
|
||||
if reflection.association_foreign_key == reflection.primary_key_name
|
||||
raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
|
||||
end
|
||||
|
||||
reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
|
||||
|
||||
|
@ -1848,9 +1856,10 @@ module ActiveRecord
|
|||
def construct(parent, associations, joins, row)
|
||||
case associations
|
||||
when Symbol, String
|
||||
while (join = joins.shift).reflection.name.to_s != associations.to_s
|
||||
raise ConfigurationError, "Not Enough Associations" if joins.empty?
|
||||
end
|
||||
join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name }
|
||||
raise(ConfigurationError, "No such association") if join.nil?
|
||||
|
||||
joins.delete(join)
|
||||
construct_association(parent, join, row)
|
||||
when Array
|
||||
associations.each do |association|
|
||||
|
@ -1858,7 +1867,11 @@ module ActiveRecord
|
|||
end
|
||||
when Hash
|
||||
associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
|
||||
association = construct_association(parent, joins.shift, row)
|
||||
join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name }
|
||||
raise(ConfigurationError, "No such association") if join.nil?
|
||||
|
||||
association = construct_association(parent, join, row)
|
||||
joins.delete(join)
|
||||
construct(association, associations[name], joins, row) if association
|
||||
end
|
||||
else
|
||||
|
|
|
@ -60,7 +60,7 @@ module ActiveRecord
|
|||
@reflection.klass.find(*args)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Fetches the first one using SQL if possible.
|
||||
def first(*args)
|
||||
if fetch_first_or_last_using_find?(args)
|
||||
|
@ -143,6 +143,8 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
# Remove all records from this association
|
||||
#
|
||||
# See delete for more info.
|
||||
def delete_all
|
||||
load_target
|
||||
delete(@target)
|
||||
|
@ -186,7 +188,6 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Removes +records+ from this association calling +before_remove+ and
|
||||
# +after_remove+ callbacks.
|
||||
#
|
||||
|
@ -195,22 +196,25 @@ 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)
|
||||
records = flatten_deeper(records)
|
||||
records.each { |record| raise_on_type_mismatch(record) }
|
||||
|
||||
transaction do
|
||||
records.each { |record| callback(:before_remove, record) }
|
||||
|
||||
old_records = records.reject {|r| r.new_record? }
|
||||
remove_records(records) do |records, old_records|
|
||||
delete_records(old_records) if old_records.any?
|
||||
|
||||
records.each do |record|
|
||||
@target.delete(record)
|
||||
callback(:after_remove, record)
|
||||
end
|
||||
records.each { |record| @target.delete(record) }
|
||||
end
|
||||
end
|
||||
|
||||
# Destroy +records+ and remove them from this association calling
|
||||
# +before_remove+ and +after_remove+ callbacks.
|
||||
#
|
||||
# Note that this method will _always_ remove records from the database
|
||||
# ignoring the +:dependent+ option.
|
||||
def destroy(*records)
|
||||
remove_records(records) do |records, old_records|
|
||||
old_records.each { |record| record.destroy }
|
||||
end
|
||||
|
||||
load_target
|
||||
end
|
||||
|
||||
# Removes all records from this association. Returns +self+ so method calls may be chained.
|
||||
def clear
|
||||
return self if length.zero? # forces load_target if it hasn't happened already
|
||||
|
@ -223,15 +227,16 @@ module ActiveRecord
|
|||
|
||||
self
|
||||
end
|
||||
|
||||
def destroy_all
|
||||
transaction do
|
||||
each { |record| record.destroy }
|
||||
end
|
||||
|
||||
# Destory all the records from this association.
|
||||
#
|
||||
# See destroy for more info.
|
||||
def destroy_all
|
||||
load_target
|
||||
destroy(@target)
|
||||
reset_target!
|
||||
end
|
||||
|
||||
|
||||
def create(attrs = {})
|
||||
if attrs.is_a?(Array)
|
||||
attrs.collect { |attr| create(attr) }
|
||||
|
@ -431,6 +436,18 @@ module ActiveRecord
|
|||
record
|
||||
end
|
||||
|
||||
def remove_records(*records)
|
||||
records = flatten_deeper(records)
|
||||
records.each { |record| raise_on_type_mismatch(record) }
|
||||
|
||||
transaction do
|
||||
records.each { |record| callback(:before_remove, record) }
|
||||
old_records = records.reject { |r| r.new_record? }
|
||||
yield(records, old_records)
|
||||
records.each { |record| callback(:after_remove, record) }
|
||||
end
|
||||
end
|
||||
|
||||
def callback(method, record)
|
||||
callbacks_for(method).each do |callback|
|
||||
ActiveSupport::Callbacks::Callback.new(method, callback, record).call(@owner, record)
|
||||
|
|
|
@ -23,8 +23,8 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
# Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
|
||||
# calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero
|
||||
# and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length.
|
||||
# calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero,
|
||||
# and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length.
|
||||
def size
|
||||
return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?
|
||||
return @target.size if loaded?
|
||||
|
@ -150,7 +150,7 @@ module ActiveRecord
|
|||
end
|
||||
else
|
||||
reflection_primary_key = @reflection.source_reflection.primary_key_name
|
||||
source_primary_key = @reflection.klass.primary_key
|
||||
source_primary_key = @reflection.through_reflection.klass.primary_key
|
||||
if @reflection.source_reflection.options[:as]
|
||||
polymorphic_join = "AND %s.%s = %s" % [
|
||||
@reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type",
|
||||
|
|
|
@ -29,8 +29,17 @@ module ActiveRecord
|
|||
|
||||
unless @target.nil? || @target == obj
|
||||
if dependent? && !dont_save
|
||||
@target.destroy unless @target.new_record?
|
||||
@owner.clear_association_cache
|
||||
case @reflection.options[:dependent]
|
||||
when :delete
|
||||
@target.delete unless @target.new_record?
|
||||
@owner.clear_association_cache
|
||||
when :destroy
|
||||
@target.destroy unless @target.new_record?
|
||||
@owner.clear_association_cache
|
||||
when :nullify
|
||||
@target[@reflection.primary_key_name] = nil
|
||||
@target.save unless @owner.new_record? || @target.new_record?
|
||||
end
|
||||
else
|
||||
@target[@reflection.primary_key_name] = nil
|
||||
@target.save unless @owner.new_record? || @target.new_record?
|
||||
|
|
|
@ -283,7 +283,7 @@ module ActiveRecord
|
|||
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
|
||||
records.each do |record|
|
||||
if autosave && record.marked_for_destruction?
|
||||
record.destroy
|
||||
association.destroy(record)
|
||||
elsif @new_record_before_save || record.new_record?
|
||||
if autosave
|
||||
association.send(:insert_record, record, false, false)
|
||||
|
@ -310,7 +310,7 @@ module ActiveRecord
|
|||
# This all happens inside a transaction, _if_ the Transactions module is included into
|
||||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_has_one_association(reflection)
|
||||
if association = association_instance_get(reflection.name)
|
||||
if (association = association_instance_get(reflection.name)) && !association.target.nil?
|
||||
if reflection.options[:autosave] && association.marked_for_destruction?
|
||||
association.destroy
|
||||
elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
|
||||
|
|
|
@ -416,7 +416,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
@@subclasses = {}
|
||||
|
||||
|
||||
##
|
||||
# :singleton-method:
|
||||
# Contains the database configuration - as is typically stored in config/database.yml -
|
||||
|
@ -661,7 +661,6 @@ module ActiveRecord #:nodoc:
|
|||
connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
|
||||
end
|
||||
|
||||
|
||||
# Returns true if a record exists in the table that matches the +id+ or
|
||||
# conditions given, or false otherwise. The argument can take five forms:
|
||||
#
|
||||
|
@ -737,12 +736,12 @@ module ActiveRecord #:nodoc:
|
|||
# ==== Parameters
|
||||
#
|
||||
# * +id+ - This should be the id or an array of ids to be updated.
|
||||
# * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes.
|
||||
# * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes.
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# # Updating one record:
|
||||
# Person.update(15, { :user_name => 'Samuel', :group => 'expert' })
|
||||
# Person.update(15, :user_name => 'Samuel', :group => 'expert')
|
||||
#
|
||||
# # Updating multiple records:
|
||||
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
|
||||
|
@ -1003,7 +1002,6 @@ module ActiveRecord #:nodoc:
|
|||
update_counters(id, counter_name => -1)
|
||||
end
|
||||
|
||||
|
||||
# Attributes named in this macro are protected from mass-assignment,
|
||||
# such as <tt>new(attributes)</tt>,
|
||||
# <tt>update_attributes(attributes)</tt>, or
|
||||
|
@ -1104,7 +1102,6 @@ module ActiveRecord #:nodoc:
|
|||
read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
|
||||
end
|
||||
|
||||
|
||||
# Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
|
||||
# directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used
|
||||
# to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class
|
||||
|
@ -1347,7 +1344,7 @@ module ActiveRecord #:nodoc:
|
|||
subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
|
||||
end
|
||||
|
||||
def self_and_descendents_from_active_record#nodoc:
|
||||
def self_and_descendants_from_active_record#nodoc:
|
||||
klass = self
|
||||
classes = [klass]
|
||||
while klass != klass.base_class
|
||||
|
@ -1367,7 +1364,7 @@ module ActiveRecord #:nodoc:
|
|||
# module now.
|
||||
# Specify +options+ with additional translating options.
|
||||
def human_attribute_name(attribute_key_name, options = {})
|
||||
defaults = self_and_descendents_from_active_record.map do |klass|
|
||||
defaults = self_and_descendants_from_active_record.map do |klass|
|
||||
:"#{klass.name.underscore}.#{attribute_key_name}"
|
||||
end
|
||||
defaults << options[:default] if options[:default]
|
||||
|
@ -1382,7 +1379,7 @@ module ActiveRecord #:nodoc:
|
|||
# Default scope of the translation is activerecord.models
|
||||
# Specify +options+ with additional translating options.
|
||||
def human_name(options = {})
|
||||
defaults = self_and_descendents_from_active_record.map do |klass|
|
||||
defaults = self_and_descendants_from_active_record.map do |klass|
|
||||
:"#{klass.name.underscore}"
|
||||
end
|
||||
defaults << self.name.humanize
|
||||
|
@ -1417,7 +1414,6 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def quote_value(value, column = nil) #:nodoc:
|
||||
connection.quote(value,column)
|
||||
end
|
||||
|
@ -1486,7 +1482,7 @@ module ActiveRecord #:nodoc:
|
|||
elsif match = DynamicScopeMatch.match(method_id)
|
||||
return true if all_attributes_exists?(match.attribute_names)
|
||||
end
|
||||
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -1537,7 +1533,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
def reverse_sql_order(order_query)
|
||||
reversed_query = order_query.split(/,/).each { |s|
|
||||
reversed_query = order_query.to_s.split(/,/).each { |s|
|
||||
if s.match(/\s(asc|ASC)$/)
|
||||
s.gsub!(/\s(asc|ASC)$/, ' DESC')
|
||||
elsif s.match(/\s(desc|DESC)$/)
|
||||
|
@ -1690,7 +1686,7 @@ module ActiveRecord #:nodoc:
|
|||
def construct_finder_sql(options)
|
||||
scope = scope(:find)
|
||||
sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
|
||||
sql << "FROM #{options[:from] || (scope && scope[:from]) || quoted_table_name} "
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
add_conditions!(sql, options[:conditions], scope)
|
||||
|
@ -1745,7 +1741,9 @@ module ActiveRecord #:nodoc:
|
|||
scoped_order = scope[:order] if scope
|
||||
if order
|
||||
sql << " ORDER BY #{order}"
|
||||
sql << ", #{scoped_order}" if scoped_order
|
||||
if scoped_order && scoped_order != order
|
||||
sql << ", #{scoped_order}"
|
||||
end
|
||||
else
|
||||
sql << " ORDER BY #{scoped_order}" if scoped_order
|
||||
end
|
||||
|
@ -1754,12 +1752,12 @@ module ActiveRecord #:nodoc:
|
|||
def add_group!(sql, group, having, scope = :auto)
|
||||
if group
|
||||
sql << " GROUP BY #{group}"
|
||||
sql << " HAVING #{having}" if having
|
||||
sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having
|
||||
else
|
||||
scope = scope(:find) if :auto == scope
|
||||
if scope && (scoped_group = scope[:group])
|
||||
sql << " GROUP BY #{scoped_group}"
|
||||
sql << " HAVING #{scope[:having]}" if scope[:having]
|
||||
sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -2014,7 +2012,6 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Defines an "attribute" method (like +inheritance_column+ or
|
||||
# +table_name+). A new (class) method will be created with the
|
||||
# given name. If a value is specified, the new method will
|
||||
|
@ -2111,7 +2108,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
# Merge scopings
|
||||
if action == :merge && current_scoped_methods
|
||||
if [:merge, :reverse_merge].include?(action) && current_scoped_methods
|
||||
method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
|
||||
case hash[method]
|
||||
when Hash
|
||||
|
@ -2133,7 +2130,11 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
end
|
||||
else
|
||||
hash[method] = hash[method].merge(params)
|
||||
if action == :reverse_merge
|
||||
hash[method] = hash[method].merge(params)
|
||||
else
|
||||
hash[method] = params.merge(hash[method])
|
||||
end
|
||||
end
|
||||
else
|
||||
hash[method] = params
|
||||
|
@ -2143,7 +2144,6 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
self.scoped_methods << method_scoping
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
|
@ -2174,7 +2174,7 @@ module ActiveRecord #:nodoc:
|
|||
# Test whether the given method and optional key are scoped.
|
||||
def scoped?(method, key = nil) #:nodoc:
|
||||
if current_scoped_methods && (scope = current_scoped_methods[method])
|
||||
!key || scope.has_key?(key)
|
||||
!key || !scope[key].nil?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -2749,7 +2749,6 @@ module ActiveRecord #:nodoc:
|
|||
assign_multiparameter_attributes(multi_parameter_attributes)
|
||||
end
|
||||
|
||||
|
||||
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
||||
def attributes
|
||||
self.attribute_names.inject({}) do |attrs, name|
|
||||
|
|
|
@ -4,21 +4,24 @@ module ActiveRecord
|
|||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# When processing large numbers of records, it's often a good idea to do so in batches to prevent memory ballooning.
|
||||
# When processing large numbers of records, it's often a good idea to do
|
||||
# so in batches to prevent memory ballooning.
|
||||
module ClassMethods
|
||||
# Yields each record that was found by the find +options+. The find is performed by find_in_batches
|
||||
# with a batch size of 1000 (or as specified by the +batch_size+ option).
|
||||
# Yields each record that was found by the find +options+. The find is
|
||||
# performed by find_in_batches with a batch size of 1000 (or as
|
||||
# specified by the <tt>:batch_size</tt> option).
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Person.each(:conditions => "age > 21") do |person|
|
||||
# Person.find_each(:conditions => "age > 21") do |person|
|
||||
# person.party_all_night!
|
||||
# end
|
||||
#
|
||||
# Note: This method is only intended to use for batch processing of large amounts of records that wouldn't fit in
|
||||
# memory all at once. If you just need to loop over less than 1000 records, it's probably better just to use the
|
||||
# regular find methods.
|
||||
def each(options = {})
|
||||
# Note: This method is only intended to use for batch processing of
|
||||
# large amounts of records that wouldn't fit in memory all at once. If
|
||||
# you just need to loop over less than 1000 records, it's probably
|
||||
# better just to use the regular find methods.
|
||||
def find_each(options = {})
|
||||
find_in_batches(options) do |records|
|
||||
records.each { |record| yield record }
|
||||
end
|
||||
|
@ -26,17 +29,22 @@ module ActiveRecord
|
|||
self
|
||||
end
|
||||
|
||||
# Yields each batch of records that was found by the find +options+ as an array. The size of each batch is
|
||||
# set by the +batch_size+ option; the default is 1000.
|
||||
# Yields each batch of records that was found by the find +options+ as
|
||||
# an array. The size of each batch is set by the <tt>:batch_size</tt>
|
||||
# option; the default is 1000.
|
||||
#
|
||||
# You can control the starting point for the batch processing by supplying the +start+ option. This is especially
|
||||
# useful if you want multiple workers dealing with the same processing queue. You can make worker 1 handle all the
|
||||
# records between id 0 and 10,000 and worker 2 handle from 10,000 and beyond (by setting the +start+ option on that
|
||||
# worker).
|
||||
# You can control the starting point for the batch processing by
|
||||
# supplying the <tt>:start</tt> option. This is especially useful if you
|
||||
# want multiple workers dealing with the same processing queue. You can
|
||||
# make worker 1 handle all the records between id 0 and 10,000 and
|
||||
# worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
|
||||
# option on that worker).
|
||||
#
|
||||
# It's not possible to set the order. That is automatically set to ascending on the primary key ("id ASC")
|
||||
# to make the batch ordering work. This also mean that this method only works with integer-based primary keys.
|
||||
# You can't set the limit either, that's used to control the the batch sizes.
|
||||
# It's not possible to set the order. That is automatically set to
|
||||
# ascending on the primary key ("id ASC") to make the batch ordering
|
||||
# work. This also mean that this method only works with integer-based
|
||||
# primary keys. You can't set the limit either, that's used to control
|
||||
# the the batch sizes.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
|
@ -49,12 +57,15 @@ module ActiveRecord
|
|||
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit]
|
||||
|
||||
start = options.delete(:start).to_i
|
||||
batch_size = options.delete(:batch_size) || 1000
|
||||
|
||||
with_scope(:find => options.merge(:order => batch_order, :limit => options.delete(:batch_size) || 1000)) do
|
||||
with_scope(:find => options.merge(:order => batch_order, :limit => batch_size)) do
|
||||
records = find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ])
|
||||
|
||||
while records.any?
|
||||
yield records
|
||||
|
||||
break if records.size < batch_size
|
||||
records = find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", records.last.id ])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -141,22 +141,30 @@ 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
|
||||
args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
|
||||
if args[0].is_a?(Hash)
|
||||
column_name = scope(:find)[:select] if scope(:find)
|
||||
options = args[0]
|
||||
else
|
||||
column_name = args[0]
|
||||
end
|
||||
when 2
|
||||
column_name, options = args
|
||||
else
|
||||
raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
|
||||
end if args.size > 0
|
||||
|
||||
[column_name, options]
|
||||
end
|
||||
|
||||
[column_name || :all, options]
|
||||
end
|
||||
|
||||
def construct_calculation_sql(operation, column_name, options) #:nodoc:
|
||||
|
@ -214,13 +222,15 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
if options[:group] && options[:having]
|
||||
having = sanitize_sql_for_conditions(options[:having])
|
||||
|
||||
# FrontBase requires identifiers in the HAVING clause and chokes on function calls
|
||||
if connection.adapter_name == 'FrontBase'
|
||||
options[:having].downcase!
|
||||
options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
|
||||
having.downcase!
|
||||
having.gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
|
||||
end
|
||||
|
||||
sql << " HAVING #{options[:having]} "
|
||||
sql << " HAVING #{having} "
|
||||
end
|
||||
|
||||
sql << " ORDER BY #{options[:order]} " if options[:order]
|
||||
|
|
|
@ -18,7 +18,7 @@ module ActiveRecord
|
|||
|
||||
db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
|
||||
|
||||
ConnectionAdapters::SQLite3Adapter.new(db, logger)
|
||||
ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ module ActiveRecord
|
|||
|
||||
# "Downgrade" deprecated sqlite API
|
||||
if SQLite.const_defined?(:Version)
|
||||
ConnectionAdapters::SQLite2Adapter.new(db, logger)
|
||||
ConnectionAdapters::SQLite2Adapter.new(db, logger, config)
|
||||
else
|
||||
ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger)
|
||||
ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -72,10 +72,31 @@ module ActiveRecord
|
|||
#
|
||||
# * <tt>:database</tt> - Path to the database file.
|
||||
class SQLiteAdapter < AbstractAdapter
|
||||
class Version
|
||||
include Comparable
|
||||
|
||||
def initialize(version_string)
|
||||
@version = version_string.split('.').map(&:to_i)
|
||||
end
|
||||
|
||||
def <=>(version_string)
|
||||
@version <=> version_string.split('.').map(&:to_i)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(connection, logger, config)
|
||||
super(connection, logger)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def adapter_name #:nodoc:
|
||||
'SQLite'
|
||||
end
|
||||
|
||||
def supports_ddl_transactions?
|
||||
sqlite_version >= '2.0.0'
|
||||
end
|
||||
|
||||
def supports_migrations? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
@ -83,6 +104,10 @@ module ActiveRecord
|
|||
def requires_reloading?
|
||||
true
|
||||
end
|
||||
|
||||
def supports_add_column?
|
||||
sqlite_version >= '3.1.6'
|
||||
end
|
||||
|
||||
def disconnect!
|
||||
super
|
||||
|
@ -164,7 +189,6 @@ module ActiveRecord
|
|||
catch_schema_changes { @connection.rollback }
|
||||
end
|
||||
|
||||
|
||||
# SELECT ... FOR UPDATE is redundant since the table is locked.
|
||||
def add_lock!(sql, options) #:nodoc:
|
||||
sql
|
||||
|
@ -213,14 +237,20 @@ module ActiveRecord
|
|||
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
||||
end
|
||||
|
||||
# See: http://www.sqlite.org/lang_altertable.html
|
||||
# SQLite has an additional restriction on the ALTER TABLE statement
|
||||
def valid_alter_table_options( type, options)
|
||||
type.to_sym != :primary_key
|
||||
end
|
||||
|
||||
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
||||
if @connection.respond_to?(:transaction_active?) && @connection.transaction_active?
|
||||
raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction'
|
||||
if supports_add_column? && valid_alter_table_options( type, options )
|
||||
super(table_name, column_name, type, options)
|
||||
else
|
||||
alter_table(table_name) do |definition|
|
||||
definition.column(column_name, type, options)
|
||||
end
|
||||
end
|
||||
|
||||
super(table_name, column_name, type, options)
|
||||
# See last paragraph on http://www.sqlite.org/lang_altertable.html
|
||||
execute "VACUUM"
|
||||
end
|
||||
|
||||
def remove_column(table_name, *column_names) #:nodoc:
|
||||
|
@ -380,7 +410,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def sqlite_version
|
||||
@sqlite_version ||= select_value('select sqlite_version(*)')
|
||||
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
|
||||
end
|
||||
|
||||
def default_primary_key_type
|
||||
|
@ -393,23 +423,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
class SQLite2Adapter < SQLiteAdapter # :nodoc:
|
||||
def supports_count_distinct? #:nodoc:
|
||||
false
|
||||
end
|
||||
|
||||
def rename_table(name, new_name)
|
||||
move_table(name, new_name)
|
||||
end
|
||||
|
||||
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
||||
if @connection.respond_to?(:transaction_active?) && @connection.transaction_active?
|
||||
raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction'
|
||||
end
|
||||
|
||||
alter_table(table_name) do |definition|
|
||||
definition.column(column_name, type, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc:
|
||||
|
|
|
@ -23,6 +23,16 @@ module ActiveRecord
|
|||
# p2.first_name = "should fail"
|
||||
# p2.save # Raises a ActiveRecord::StaleObjectError
|
||||
#
|
||||
# Optimistic locking will also check for stale data when objects are destroyed. Example:
|
||||
#
|
||||
# p1 = Person.find(1)
|
||||
# p2 = Person.find(1)
|
||||
#
|
||||
# p1.first_name = "Michael"
|
||||
# p1.save
|
||||
#
|
||||
# p2.destroy # Raises a ActiveRecord::StaleObjectError
|
||||
#
|
||||
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
|
||||
# or otherwise apply the business logic needed to resolve the conflict.
|
||||
#
|
||||
|
@ -39,6 +49,7 @@ module ActiveRecord
|
|||
base.lock_optimistically = true
|
||||
|
||||
base.alias_method_chain :update, :lock
|
||||
base.alias_method_chain :destroy, :lock
|
||||
base.alias_method_chain :attributes_from_column_definition, :lock
|
||||
|
||||
class << base
|
||||
|
@ -98,6 +109,28 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def destroy_with_lock #:nodoc:
|
||||
return destroy_without_lock unless locking_enabled?
|
||||
|
||||
unless new_record?
|
||||
lock_col = self.class.locking_column
|
||||
previous_value = send(lock_col).to_i
|
||||
|
||||
affected_rows = connection.delete(
|
||||
"DELETE FROM #{self.class.quoted_table_name} " +
|
||||
"WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " +
|
||||
"AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}",
|
||||
"#{self.class.name} Destroy"
|
||||
)
|
||||
|
||||
unless affected_rows == 1
|
||||
raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object"
|
||||
end
|
||||
end
|
||||
|
||||
freeze
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
DEFAULT_LOCKING_COLUMN = 'lock_version'
|
||||
|
||||
|
|
|
@ -89,7 +89,12 @@ module ActiveRecord
|
|||
when Hash
|
||||
options
|
||||
when Proc
|
||||
options.call(*args)
|
||||
case parent_scope
|
||||
when Scope
|
||||
with_scope(:find => parent_scope.proxy_options) { options.call(*args) }
|
||||
else
|
||||
options.call(*args)
|
||||
end
|
||||
end, &block)
|
||||
end
|
||||
(class << self; self end).instance_eval do
|
||||
|
@ -99,7 +104,7 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Scope
|
||||
attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined
|
||||
NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
|
||||
|
@ -112,6 +117,7 @@ module ActiveRecord
|
|||
delegate :scopes, :with_scope, :to => :proxy_scope
|
||||
|
||||
def initialize(proxy_scope, options, &block)
|
||||
options ||= {}
|
||||
[options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
|
||||
extend Module.new(&block) if block_given?
|
||||
unless Scope === proxy_scope
|
||||
|
@ -170,7 +176,7 @@ module ActiveRecord
|
|||
if scopes.include?(method)
|
||||
scopes[method].call(self, *args)
|
||||
else
|
||||
with_scope :find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {} do
|
||||
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
|
||||
with_scope current_scoped_methods_when_defined do
|
||||
|
|
|
@ -197,7 +197,7 @@ module ActiveRecord
|
|||
|
||||
def counter_cache_column
|
||||
if options[:counter_cache] == true
|
||||
"#{active_record.name.underscore.pluralize}_count"
|
||||
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
||||
elsif options[:counter_cache]
|
||||
options[:counter_cache]
|
||||
end
|
||||
|
|
|
@ -231,16 +231,22 @@ module ActiveRecord #:nodoc:
|
|||
def add_associations(association, records, opts)
|
||||
if records.is_a?(Enumerable)
|
||||
tag = reformat_name(association.to_s)
|
||||
type = options[:skip_types] ? {} : {:type => "array"}
|
||||
|
||||
if records.empty?
|
||||
builder.tag!(tag, :type => :array)
|
||||
builder.tag!(tag, type)
|
||||
else
|
||||
builder.tag!(tag, :type => :array) do
|
||||
builder.tag!(tag, type) do
|
||||
association_name = association.to_s.singularize
|
||||
records.each do |record|
|
||||
record.to_xml opts.merge(
|
||||
:root => association_name,
|
||||
:type => (record.class.to_s.underscore == association_name ? nil : record.class.name)
|
||||
)
|
||||
if options[:skip_types]
|
||||
record_type = {}
|
||||
else
|
||||
record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
|
||||
record_type = {:type => record_class}
|
||||
end
|
||||
|
||||
record.to_xml opts.merge(:root => association_name).merge(record_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,5 +49,18 @@ module ActiveRecord
|
|||
ActiveRecord::Base.clear_all_connections!
|
||||
ActiveRecord::Base.establish_connection(@connection)
|
||||
end
|
||||
|
||||
def with_kcode(kcode)
|
||||
if RUBY_VERSION < '1.9'
|
||||
orig_kcode, $KCODE = $KCODE, kcode
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
$KCODE = orig_kcode
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ module ActiveRecord
|
|||
|
||||
message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
|
||||
|
||||
defaults = @base.class.self_and_descendents_from_active_record.map do |klass|
|
||||
defaults = @base.class.self_and_descendants_from_active_record.map do |klass|
|
||||
[ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
|
||||
:"models.#{klass.name.underscore}.#{message}" ]
|
||||
end
|
||||
|
@ -720,20 +720,20 @@ module ActiveRecord
|
|||
# class (which has a database table to query from).
|
||||
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
||||
|
||||
is_text_column = finder_class.columns_hash[attr_name.to_s].text?
|
||||
column = finder_class.columns_hash[attr_name.to_s]
|
||||
|
||||
if value.nil?
|
||||
comparison_operator = "IS ?"
|
||||
elsif is_text_column
|
||||
elsif column.text?
|
||||
comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
|
||||
value = value.to_s
|
||||
value = column.limit ? value.to_s[0, column.limit] : value.to_s
|
||||
else
|
||||
comparison_operator = "= ?"
|
||||
end
|
||||
|
||||
sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
|
||||
|
||||
if value.nil? || (configuration[:case_sensitive] || !is_text_column)
|
||||
if value.nil? || (configuration[:case_sensitive] || !column.text?)
|
||||
condition_sql = "#{sql_attribute} #{comparison_operator}"
|
||||
condition_params = [value]
|
||||
else
|
||||
|
@ -802,7 +802,7 @@ module ActiveRecord
|
|||
# Validates whether the value of the specified attribute is available in a particular enumerable object.
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
|
||||
# validates_inclusion_of :gender, :in => %w( m f )
|
||||
# validates_inclusion_of :age, :in => 0..99
|
||||
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list"
|
||||
# end
|
||||
|
@ -1040,6 +1040,11 @@ module ActiveRecord
|
|||
errors.empty?
|
||||
end
|
||||
|
||||
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added, false otherwise.
|
||||
def invalid?
|
||||
!valid?
|
||||
end
|
||||
|
||||
# Returns the Errors object that holds all information about attribute error messages.
|
||||
def errors
|
||||
@errors ||= Errors.new(self)
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActiveRecord
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 1
|
||||
TINY = 2
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -154,6 +154,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 0, Topic.find(t2.id).replies.size
|
||||
end
|
||||
|
||||
def test_belongs_to_reassign_with_namespaced_models_and_counters
|
||||
t1 = Web::Topic.create("title" => "t1")
|
||||
t2 = Web::Topic.create("title" => "t2")
|
||||
r1 = Web::Reply.new("title" => "r1", "content" => "r1")
|
||||
r1.topic = t1
|
||||
|
||||
assert r1.save
|
||||
assert_equal 1, Web::Topic.find(t1.id).replies.size
|
||||
assert_equal 0, Web::Topic.find(t2.id).replies.size
|
||||
|
||||
r1.topic = Web::Topic.find(t2.id)
|
||||
|
||||
assert r1.save
|
||||
assert_equal 0, Web::Topic.find(t1.id).replies.size
|
||||
assert_equal 1, Web::Topic.find(t2.id).replies.size
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_after_save
|
||||
topic = Topic.create!(:title => "monday night")
|
||||
topic.replies.create!(:title => "re: monday night", :content => "football")
|
||||
|
@ -301,12 +318,28 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_belongs_to_proxy_should_not_respond_to_private_methods
|
||||
assert_raises(NoMethodError) { companies(:first_firm).private_method }
|
||||
assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
|
||||
assert_raise(NoMethodError) { companies(:first_firm).private_method }
|
||||
assert_raise(NoMethodError) { companies(:second_client).firm.private_method }
|
||||
end
|
||||
|
||||
def test_belongs_to_proxy_should_respond_to_private_methods_via_send
|
||||
companies(:first_firm).send(:private_method)
|
||||
companies(:second_client).firm.send(:private_method)
|
||||
end
|
||||
|
||||
def test_save_of_record_with_loaded_belongs_to
|
||||
@account = companies(:first_firm).account
|
||||
|
||||
assert_nothing_raised do
|
||||
Account.find(@account.id).save!
|
||||
Account.find(@account.id, :include => :firm).save!
|
||||
end
|
||||
|
||||
@account.firm.delete
|
||||
|
||||
assert_nothing_raised do
|
||||
Account.find(@account.id).save!
|
||||
Account.find(@account.id, :include => :firm).save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
require 'cases/helper'
|
||||
require 'models/author'
|
||||
require 'models/post'
|
||||
require 'models/comment'
|
||||
require 'models/category'
|
||||
require 'models/categorization'
|
||||
|
||||
module Remembered
|
||||
def self.included(base)
|
||||
|
@ -99,3 +104,27 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@davey_mcdave = Author.create(:name => 'Davey McDave')
|
||||
@first_post = @davey_mcdave.posts.create(:title => 'Davey Speaks', :body => 'Expressive wordage')
|
||||
@first_comment = @first_post.comments.create(:body => 'Inflamatory doublespeak')
|
||||
@first_categorization = @davey_mcdave.categorizations.create(:category => Category.first, :post => @first_post)
|
||||
end
|
||||
|
||||
def teardown
|
||||
@davey_mcdave.destroy
|
||||
@first_post.destroy
|
||||
@first_comment.destroy
|
||||
@first_categorization.destroy
|
||||
end
|
||||
|
||||
def test_missing_data_in_a_nested_include_should_not_cause_errors_when_constructing_objects
|
||||
assert_nothing_raised do
|
||||
# @davey_mcdave doesn't have any author_favorites
|
||||
includes = {:posts => :comments, :categorizations => :category, :author_favorites => :favorite_author }
|
||||
Author.all :include => includes, :conditions => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -549,16 +549,16 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_eager_with_invalid_association_reference
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=> :monkeys )
|
||||
}
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=>[ :monkeys ])
|
||||
}
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=>[ 'monkeys' ])
|
||||
}
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
|
||||
post = Post.find(6, :include=>[ :monkeys, :elephants ])
|
||||
}
|
||||
end
|
||||
|
|
|
@ -381,6 +381,33 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on.to_date
|
||||
end
|
||||
|
||||
def test_destroying
|
||||
david = Developer.find(1)
|
||||
active_record = Project.find(1)
|
||||
david.projects.reload
|
||||
assert_equal 2, david.projects.size
|
||||
assert_equal 3, active_record.developers.size
|
||||
|
||||
assert_difference "Project.count", -1 do
|
||||
david.projects.destroy(active_record)
|
||||
end
|
||||
|
||||
assert_equal 1, david.reload.projects.size
|
||||
assert_equal 1, david.projects(true).size
|
||||
end
|
||||
|
||||
def test_destroying_array
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
|
||||
assert_difference "Project.count", -Project.count do
|
||||
david.projects.destroy(Project.find(:all))
|
||||
end
|
||||
|
||||
assert_equal 0, david.reload.projects.size
|
||||
assert_equal 0, david.projects(true).size
|
||||
end
|
||||
|
||||
def test_destroy_all
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
|
@ -616,7 +643,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
|||
def test_updating_attributes_on_rich_associations
|
||||
david = projects(:action_controller).developers.first
|
||||
david.name = "DHH"
|
||||
assert_raises(ActiveRecord::ReadOnlyRecord) { david.save! }
|
||||
assert_raise(ActiveRecord::ReadOnlyRecord) { david.save! }
|
||||
end
|
||||
|
||||
def test_updating_attributes_on_rich_associations_with_limited_find_from_reflection
|
||||
|
@ -740,6 +767,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal developer, project.developers.find(:first)
|
||||
assert_equal project, developer.projects.find(:first)
|
||||
end
|
||||
|
||||
def test_self_referential_habtm_without_foreign_key_set_should_raise_exception
|
||||
assert_raise(ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded) {
|
||||
Member.class_eval do
|
||||
has_and_belongs_to_many :friends, :class_name => "Member", :join_table => "member_friends"
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_dynamic_find_should_respect_association_include
|
||||
# SQL error in sort clause if :include is not included
|
||||
|
|
|
@ -70,6 +70,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size
|
||||
end
|
||||
|
||||
def test_dynamic_find_last_without_specified_order
|
||||
assert_equal companies(:second_client), companies(:first_firm).unsorted_clients.find_last_by_type('Client')
|
||||
end
|
||||
|
||||
def test_dynamic_find_should_respect_association_order
|
||||
assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'")
|
||||
assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
|
||||
|
@ -176,7 +180,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
def test_find_ids
|
||||
firm = Firm.find(:first)
|
||||
|
||||
assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
|
||||
|
||||
client = firm.clients.find(2)
|
||||
assert_kind_of Client, client
|
||||
|
@ -190,7 +194,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 2, client_ary.size
|
||||
assert_equal client, client_ary.first
|
||||
|
||||
assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
|
||||
end
|
||||
|
||||
def test_find_string_ids_when_using_finder_sql
|
||||
|
@ -215,6 +219,45 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
|
||||
end
|
||||
|
||||
def test_find_each
|
||||
firm = companies(:first_firm)
|
||||
|
||||
assert ! firm.clients.loaded?
|
||||
|
||||
assert_queries(3) do
|
||||
firm.clients.find_each(:batch_size => 1) {|c| assert_equal firm.id, c.firm_id }
|
||||
end
|
||||
|
||||
assert ! firm.clients.loaded?
|
||||
end
|
||||
|
||||
def test_find_each_with_conditions
|
||||
firm = companies(:first_firm)
|
||||
|
||||
assert_queries(2) do
|
||||
firm.clients.find_each(:batch_size => 1, :conditions => {:name => "Microsoft"}) do |c|
|
||||
assert_equal firm.id, c.firm_id
|
||||
assert_equal "Microsoft", c.name
|
||||
end
|
||||
end
|
||||
|
||||
assert ! firm.clients.loaded?
|
||||
end
|
||||
|
||||
def test_find_in_batches
|
||||
firm = companies(:first_firm)
|
||||
|
||||
assert ! firm.clients.loaded?
|
||||
|
||||
assert_queries(2) do
|
||||
firm.clients.find_in_batches(:batch_size => 2) do |clients|
|
||||
clients.each {|c| assert_equal firm.id, c.firm_id }
|
||||
end
|
||||
end
|
||||
|
||||
assert ! firm.clients.loaded?
|
||||
end
|
||||
|
||||
def test_find_all_sanitized
|
||||
firm = Firm.find(:first)
|
||||
summit = firm.clients.find(:all, :conditions => "name = 'Summit'")
|
||||
|
@ -238,7 +281,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
|
||||
def test_find_in_collection
|
||||
assert_equal Client.find(2).name, companies(:first_firm).clients.find(2).name
|
||||
assert_raises(ActiveRecord::RecordNotFound) { companies(:first_firm).clients.find(6) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { companies(:first_firm).clients.find(6) }
|
||||
end
|
||||
|
||||
def test_find_grouped
|
||||
|
@ -278,36 +321,36 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_create_with_bang_on_has_many_when_parent_is_new_raises
|
||||
assert_raises(ActiveRecord::RecordNotSaved) do
|
||||
assert_raise(ActiveRecord::RecordNotSaved) do
|
||||
firm = Firm.new
|
||||
firm.plain_clients.create! :name=>"Whoever"
|
||||
end
|
||||
end
|
||||
|
||||
def test_regular_create_on_has_many_when_parent_is_new_raises
|
||||
assert_raises(ActiveRecord::RecordNotSaved) do
|
||||
assert_raise(ActiveRecord::RecordNotSaved) do
|
||||
firm = Firm.new
|
||||
firm.plain_clients.create :name=>"Whoever"
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_with_bang_on_has_many_raises_when_record_not_saved
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
assert_raise(ActiveRecord::RecordInvalid) do
|
||||
firm = Firm.find(:first)
|
||||
firm.plain_clients.create!
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_with_bang_on_habtm_when_parent_is_new_raises
|
||||
assert_raises(ActiveRecord::RecordNotSaved) do
|
||||
assert_raise(ActiveRecord::RecordNotSaved) do
|
||||
Developer.new("name" => "Aredridel").projects.create!
|
||||
end
|
||||
end
|
||||
|
||||
def test_adding_a_mismatch_class
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << nil }
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << 1 }
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << Topic.find(1) }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << nil }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << 1 }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << Topic.find(1) }
|
||||
end
|
||||
|
||||
def test_adding_a_collection
|
||||
|
@ -602,7 +645,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_invalid_belongs_to_dependent_option_raises_exception
|
||||
assert_raises ArgumentError do
|
||||
assert_raise ArgumentError do
|
||||
Author.belongs_to :special_author_address, :dependent => :nullify
|
||||
end
|
||||
end
|
||||
|
@ -628,13 +671,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
def test_deleting_type_mismatch
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(1) }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(1) }
|
||||
end
|
||||
|
||||
def test_deleting_self_type_mismatch
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(Project.find(1).developers) }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(Project.find(1).developers) }
|
||||
end
|
||||
|
||||
def test_destroying
|
||||
force_signal37_to_load_all_clients_of_firm
|
||||
|
||||
assert_difference "Client.count", -1 do
|
||||
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first)
|
||||
end
|
||||
|
||||
assert_equal 0, companies(:first_firm).reload.clients_of_firm.size
|
||||
assert_equal 0, companies(:first_firm).clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_destroying_a_collection
|
||||
force_signal37_to_load_all_clients_of_firm
|
||||
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
|
||||
assert_equal 2, companies(:first_firm).clients_of_firm.size
|
||||
|
||||
assert_difference "Client.count", -2 do
|
||||
companies(:first_firm).clients_of_firm.destroy([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1]])
|
||||
end
|
||||
|
||||
assert_equal 0, companies(:first_firm).reload.clients_of_firm.size
|
||||
assert_equal 0, companies(:first_firm).clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_destroy_all
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/person'
|
||||
require 'models/reference'
|
||||
require 'models/job'
|
||||
require 'models/reader'
|
||||
require 'models/comment'
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/author'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
require 'models/toy'
|
||||
|
||||
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :readers, :people, :comments, :authors
|
||||
fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys
|
||||
|
||||
def test_associate_existing
|
||||
assert_queries(2) { posts(:thinking);people(:david) }
|
||||
|
@ -87,6 +92,24 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
|||
assert posts(:welcome).reload.people(true).empty?
|
||||
end
|
||||
|
||||
def test_destroy_association
|
||||
assert_difference "Person.count", -1 do
|
||||
posts(:welcome).people.destroy(people(:michael))
|
||||
end
|
||||
|
||||
assert posts(:welcome).reload.people.empty?
|
||||
assert posts(:welcome).people(true).empty?
|
||||
end
|
||||
|
||||
def test_destroy_all
|
||||
assert_difference "Person.count", -1 do
|
||||
posts(:welcome).people.destroy_all
|
||||
end
|
||||
|
||||
assert posts(:welcome).reload.people.empty?
|
||||
assert posts(:welcome).people(true).empty?
|
||||
end
|
||||
|
||||
def test_replace_association
|
||||
assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
|
||||
|
||||
|
@ -249,4 +272,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
|||
author.author_favorites.create(:favorite_author_id => 3)
|
||||
assert_equal post.author.author_favorites, post.author_favorites
|
||||
end
|
||||
|
||||
def test_has_many_association_through_a_has_many_association_with_nonstandard_primary_keys
|
||||
assert_equal 1, owners(:blackbeard).toys.count
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,8 +59,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_type_mismatch
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 }
|
||||
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) }
|
||||
end
|
||||
|
||||
def test_natural_assignment
|
||||
|
@ -76,7 +76,25 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
companies(:first_firm).save
|
||||
assert_nil companies(:first_firm).account
|
||||
# account is dependent, therefore is destroyed when reference to owner is lost
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
|
||||
end
|
||||
|
||||
def test_nullification_on_association_change
|
||||
firm = companies(:rails_core)
|
||||
old_account_id = firm.account.id
|
||||
firm.account = Account.new
|
||||
# account is dependent with nullify, therefore its firm_id should be nil
|
||||
assert_nil Account.find(old_account_id).firm_id
|
||||
end
|
||||
|
||||
def test_association_changecalls_delete
|
||||
companies(:first_firm).deletable_account = Account.new
|
||||
assert_equal [], Account.destroyed_account_ids[companies(:first_firm).id]
|
||||
end
|
||||
|
||||
def test_association_change_calls_destroy
|
||||
companies(:first_firm).account = Account.new
|
||||
assert_equal [companies(:first_firm).id], Account.destroyed_account_ids[companies(:first_firm).id]
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_already_associated_record
|
||||
|
@ -263,8 +281,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_has_one_proxy_should_not_respond_to_private_methods
|
||||
assert_raises(NoMethodError) { accounts(:signals37).private_method }
|
||||
assert_raises(NoMethodError) { companies(:first_firm).account.private_method }
|
||||
assert_raise(NoMethodError) { accounts(:signals37).private_method }
|
||||
assert_raise(NoMethodError) { companies(:first_firm).account.private_method }
|
||||
end
|
||||
|
||||
def test_has_one_proxy_should_respond_to_private_methods_via_send
|
||||
|
@ -272,4 +290,20 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
companies(:first_firm).account.send(:private_method)
|
||||
end
|
||||
|
||||
def test_save_of_record_with_loaded_has_one
|
||||
@firm = companies(:first_firm)
|
||||
assert_not_nil @firm.account
|
||||
|
||||
assert_nothing_raised do
|
||||
Firm.find(@firm.id).save!
|
||||
Firm.find(@firm.id, :include => :account).save!
|
||||
end
|
||||
|
||||
@firm.account.destroy
|
||||
|
||||
assert_nothing_raised do
|
||||
Firm.find(@firm.id).save!
|
||||
Firm.find(@firm.id, :include => :account).save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -115,8 +115,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_has_one_through_proxy_should_not_respond_to_private_methods
|
||||
assert_raises(NoMethodError) { clubs(:moustache_club).private_method }
|
||||
assert_raises(NoMethodError) { @member.club.private_method }
|
||||
assert_raise(NoMethodError) { clubs(:moustache_club).private_method }
|
||||
assert_raise(NoMethodError) { @member.club.private_method }
|
||||
end
|
||||
|
||||
def test_has_one_through_proxy_should_respond_to_private_methods_via_send
|
||||
|
@ -173,4 +173,20 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|||
assert_not_nil assert_no_queries { @new_detail.member_type }
|
||||
end
|
||||
|
||||
def test_save_of_record_with_loaded_has_one_through
|
||||
@club = @member.club
|
||||
assert_not_nil @club.sponsored_member
|
||||
|
||||
assert_nothing_raised do
|
||||
Club.find(@club.id).save!
|
||||
Club.find(@club.id, :include => :sponsored_member).save!
|
||||
end
|
||||
|
||||
@club.sponsor.destroy
|
||||
|
||||
assert_nothing_raised do
|
||||
Club.find(@club.id).save!
|
||||
Club.find(@club.id, :include => :sponsored_member).save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -100,7 +100,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
|||
%w(save create_or_update).each do |method|
|
||||
klass = Class.new ActiveRecord::Base
|
||||
klass.class_eval "def #{method}() 'defined #{method}' end"
|
||||
assert_raises ActiveRecord::DangerousAttributeError do
|
||||
assert_raise ActiveRecord::DangerousAttributeError do
|
||||
klass.instance_method_already_implemented?(method)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -556,6 +556,41 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
|
|||
assert_raise(RuntimeError) { assert !@pirate.save }
|
||||
assert_equal before, @pirate.reload.send(association_name)
|
||||
end
|
||||
|
||||
# Add and remove callbacks tests for association collections.
|
||||
%w{ method proc }.each do |callback_type|
|
||||
define_method("test_should_run_add_callback_#{callback_type}s_for_#{association_name}") do
|
||||
association_name_with_callbacks = "#{association_name}_with_#{callback_type}_callbacks"
|
||||
|
||||
pirate = Pirate.new(:catchphrase => "Arr")
|
||||
pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
|
||||
|
||||
expected = [
|
||||
"before_adding_#{callback_type}_#{association_name.singularize}_<new>",
|
||||
"after_adding_#{callback_type}_#{association_name.singularize}_<new>"
|
||||
]
|
||||
|
||||
assert_equal expected, pirate.ship_log
|
||||
end
|
||||
|
||||
define_method("test_should_run_remove_callback_#{callback_type}s_for_#{association_name}") do
|
||||
association_name_with_callbacks = "#{association_name}_with_#{callback_type}_callbacks"
|
||||
|
||||
@pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
|
||||
@pirate.send(association_name_with_callbacks).each { |c| c.mark_for_destruction }
|
||||
child_id = @pirate.send(association_name_with_callbacks).first.id
|
||||
|
||||
@pirate.ship_log.clear
|
||||
@pirate.save
|
||||
|
||||
expected = [
|
||||
"before_removing_#{callback_type}_#{association_name.singularize}_#{child_id}",
|
||||
"after_removing_#{callback_type}_#{association_name.singularize}_#{child_id}"
|
||||
]
|
||||
|
||||
assert_equal expected, @pirate.ship_log
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -424,8 +424,8 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
def test_non_attribute_access_and_assignment
|
||||
topic = Topic.new
|
||||
assert !topic.respond_to?("mumbo")
|
||||
assert_raises(NoMethodError) { topic.mumbo }
|
||||
assert_raises(NoMethodError) { topic.mumbo = 5 }
|
||||
assert_raise(NoMethodError) { topic.mumbo }
|
||||
assert_raise(NoMethodError) { topic.mumbo = 5 }
|
||||
end
|
||||
|
||||
def test_preserving_date_objects
|
||||
|
@ -490,7 +490,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_record_not_found_exception
|
||||
assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
|
||||
end
|
||||
|
||||
def test_initialize_with_attributes
|
||||
|
@ -848,7 +848,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
client.delete
|
||||
assert client.frozen?
|
||||
assert_kind_of Firm, client.firm
|
||||
assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
|
||||
assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
|
||||
end
|
||||
|
||||
def test_destroy_new_record
|
||||
|
@ -862,7 +862,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
client.destroy
|
||||
assert client.frozen?
|
||||
assert_kind_of Firm, client.firm
|
||||
assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
|
||||
assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
|
||||
end
|
||||
|
||||
def test_update_attribute
|
||||
|
@ -910,8 +910,8 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
|
||||
def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
|
||||
topic = TopicWithProtectedContentAndAccessibleAuthorName.new
|
||||
assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
|
||||
assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
|
||||
assert_raise(RuntimeError) { topic.attributes = { "author_name" => "me" } }
|
||||
assert_raise(RuntimeError) { topic.attributes = { "content" => "stuff" } }
|
||||
end
|
||||
|
||||
def test_mass_assignment_protection
|
||||
|
@ -949,7 +949,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
def test_mass_assigning_invalid_attribute
|
||||
firm = Firm.new
|
||||
|
||||
assert_raises(ActiveRecord::UnknownAttributeError) do
|
||||
assert_raise(ActiveRecord::UnknownAttributeError) do
|
||||
firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 }
|
||||
end
|
||||
end
|
||||
|
@ -1402,7 +1402,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_sql_injection_via_find
|
||||
assert_raises(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
|
||||
assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
|
||||
Topic.find("123456 OR id > 0")
|
||||
end
|
||||
end
|
||||
|
@ -1790,6 +1790,11 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
|
||||
end
|
||||
|
||||
def test_find_symbol_ordered_last
|
||||
last = Developer.find :last, :order => :salary
|
||||
assert_equal last, Developer.find(:all, :order => :salary).last
|
||||
end
|
||||
|
||||
def test_find_scoped_ordered_last
|
||||
last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
|
||||
Developer.find(:last)
|
||||
|
@ -2099,18 +2104,4 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
assert_equal custom_datetime, parrot[attribute]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_kcode(kcode)
|
||||
if RUBY_VERSION < '1.9'
|
||||
orig_kcode, $KCODE = $KCODE, kcode
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
$KCODE = orig_kcode
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ class EachTest < ActiveRecord::TestCase
|
|||
|
||||
def test_each_should_excecute_one_query_per_batch
|
||||
assert_queries(Post.count + 1) do
|
||||
Post.each(:batch_size => 1) do |post|
|
||||
Post.find_each(:batch_size => 1) do |post|
|
||||
assert_kind_of Post, post
|
||||
end
|
||||
end
|
||||
|
@ -19,13 +19,13 @@ class EachTest < ActiveRecord::TestCase
|
|||
|
||||
def test_each_should_raise_if_the_order_is_set
|
||||
assert_raise(RuntimeError) do
|
||||
Post.each(:order => "title") { |post| post }
|
||||
Post.find_each(:order => "title") { |post| post }
|
||||
end
|
||||
end
|
||||
|
||||
def test_each_should_raise_if_the_limit_is_set
|
||||
assert_raise(RuntimeError) do
|
||||
Post.each(:limit => 1) { |post| post }
|
||||
Post.find_each(:limit => 1) { |post| post }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -46,4 +46,16 @@ class EachTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_in_batches_shouldnt_excute_query_unless_needed
|
||||
post_count = Post.count
|
||||
|
||||
assert_queries(2) do
|
||||
Post.find_in_batches(:batch_size => post_count) {|batch| assert_kind_of Array, batch }
|
||||
end
|
||||
|
||||
assert_queries(1) do
|
||||
Post.find_in_batches(:batch_size => post_count + 1) {|batch| assert_kind_of Array, batch }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -92,6 +92,14 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field_having_sanitized_condition
|
||||
c = Account.sum(:credit_limit, :group => :firm_id,
|
||||
:having => ['sum(credit_limit) > ?', 50])
|
||||
assert_nil c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_association
|
||||
c = Account.sum(:credit_limit, :group => :firm)
|
||||
assert_equal 50, c[companies(:first_firm)]
|
||||
|
@ -247,8 +255,8 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
Company.send(:validate_calculation_options, :count, :include => true)
|
||||
end
|
||||
|
||||
assert_raises(ArgumentError) { Company.send(:validate_calculation_options, :sum, :foo => :bar) }
|
||||
assert_raises(ArgumentError) { Company.send(:validate_calculation_options, :count, :foo => :bar) }
|
||||
assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :sum, :foo => :bar) }
|
||||
assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :count, :foo => :bar) }
|
||||
end
|
||||
|
||||
def test_should_count_selected_field_with_include
|
||||
|
@ -256,6 +264,19 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
|
||||
end
|
||||
|
||||
def test_should_count_scoped_select
|
||||
Account.update_all("credit_limit = NULL")
|
||||
assert_equal 0, Account.scoped(:select => "credit_limit").count
|
||||
end
|
||||
|
||||
def test_should_count_scoped_select_with_options
|
||||
Account.update_all("credit_limit = NULL")
|
||||
Account.last.update_attribute('credit_limit', 49)
|
||||
Account.first.update_attribute('credit_limit', 51)
|
||||
|
||||
assert_equal 1, Account.scoped(:select => "credit_limit").count(:conditions => ['credit_limit >= 50'])
|
||||
end
|
||||
|
||||
def test_should_count_manual_select_with_include
|
||||
assert_equal 6, Account.count(:select => "DISTINCT accounts.id", :include => :firm)
|
||||
end
|
||||
|
|
|
@ -352,13 +352,13 @@ class CallbacksTest < ActiveRecord::TestCase
|
|||
david = ImmutableDeveloper.find(1)
|
||||
assert david.valid?
|
||||
assert !david.save
|
||||
assert_raises(ActiveRecord::RecordNotSaved) { david.save! }
|
||||
assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
|
||||
|
||||
david = ImmutableDeveloper.find(1)
|
||||
david.salary = 10_000_000
|
||||
assert !david.valid?
|
||||
assert !david.save
|
||||
assert_raises(ActiveRecord::RecordInvalid) { david.save! }
|
||||
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
|
||||
|
||||
someone = CallbackCancellationDeveloper.find(1)
|
||||
someone.cancel_before_save = true
|
||||
|
|
|
@ -228,7 +228,7 @@ class DirtyTest < ActiveRecord::TestCase
|
|||
|
||||
pirate = Pirate.new
|
||||
pirate.parrot_id = 1
|
||||
assert_raises(ActiveRecord::RecordInvalid) { pirate.save! }
|
||||
assert_raise(ActiveRecord::RecordInvalid) { pirate.save! }
|
||||
check_pirate_after_save_failure(pirate)
|
||||
end
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_find_by_ids_missing_one
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
|
||||
end
|
||||
|
||||
def test_find_all_with_limit
|
||||
|
@ -191,6 +191,13 @@ class FinderTest < ActiveRecord::TestCase
|
|||
assert developers.all? { |developer| developer.salary > 10000 }
|
||||
end
|
||||
|
||||
def test_find_with_group_and_sanitized_having
|
||||
developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
|
||||
assert_equal 3, developers.size
|
||||
assert_equal 3, developers.map(&:salary).uniq.size
|
||||
assert developers.all? { |developer| developer.salary > 10000 }
|
||||
end
|
||||
|
||||
def test_find_with_entire_select_statement
|
||||
topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
|
||||
|
||||
|
@ -229,7 +236,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_unexisting_record_exception_handling
|
||||
assert_raises(ActiveRecord::RecordNotFound) {
|
||||
assert_raise(ActiveRecord::RecordNotFound) {
|
||||
Topic.find(1).parent
|
||||
}
|
||||
|
||||
|
@ -238,7 +245,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
|
||||
def test_find_only_some_columns
|
||||
topic = Topic.find(1, :select => "author_name")
|
||||
assert_raises(ActiveRecord::MissingAttributeError) {topic.title}
|
||||
assert_raise(ActiveRecord::MissingAttributeError) {topic.title}
|
||||
assert_equal "David", topic.author_name
|
||||
assert !topic.attribute_present?("title")
|
||||
#assert !topic.respond_to?("title")
|
||||
|
@ -260,22 +267,22 @@ class FinderTest < ActiveRecord::TestCase
|
|||
|
||||
def test_find_on_array_conditions
|
||||
assert Topic.find(1, :conditions => ["approved = ?", false])
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
|
||||
end
|
||||
|
||||
def test_find_on_hash_conditions
|
||||
assert Topic.find(1, :conditions => { :approved => false })
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
|
||||
end
|
||||
|
||||
def test_find_on_hash_conditions_with_explicit_table_name
|
||||
assert Topic.find(1, :conditions => { 'topics.approved' => false })
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
|
||||
end
|
||||
|
||||
def test_find_on_hash_conditions_with_hashed_table_name
|
||||
assert Topic.find(1, :conditions => {:topics => { :approved => false }})
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) }
|
||||
end
|
||||
|
||||
def test_find_with_hash_conditions_on_joined_table
|
||||
|
@ -293,7 +300,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
|
||||
david = customers(:david)
|
||||
assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address })
|
||||
assert_raises(ActiveRecord::RecordNotFound) {
|
||||
assert_raise(ActiveRecord::RecordNotFound) {
|
||||
Customer.find(david.id, :conditions => { 'customers.name' => david.name + "1", :address => david.address })
|
||||
}
|
||||
end
|
||||
|
@ -304,13 +311,13 @@ class FinderTest < ActiveRecord::TestCase
|
|||
|
||||
def test_find_on_hash_conditions_with_range
|
||||
assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1..2 }).map(&:id).sort
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
|
||||
end
|
||||
|
||||
def test_find_on_hash_conditions_with_end_exclusive_range
|
||||
assert_equal [1,2,3], Topic.find(:all, :conditions => { :id => 1..3 }).map(&:id).sort
|
||||
assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1...3 }).map(&:id).sort
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
|
||||
end
|
||||
|
||||
def test_find_on_hash_conditions_with_multiple_ranges
|
||||
|
@ -320,9 +327,9 @@ class FinderTest < ActiveRecord::TestCase
|
|||
|
||||
def test_find_on_multiple_hash_conditions
|
||||
assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
|
||||
end
|
||||
|
||||
def test_condition_interpolation
|
||||
|
@ -346,7 +353,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_hash_condition_find_malformed
|
||||
assert_raises(ActiveRecord::StatementInvalid) {
|
||||
assert_raise(ActiveRecord::StatementInvalid) {
|
||||
Company.find(:first, :conditions => { :id => 2, :dhh => true })
|
||||
}
|
||||
end
|
||||
|
@ -415,10 +422,10 @@ class FinderTest < ActiveRecord::TestCase
|
|||
assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
|
||||
assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!' OR 1=1"])
|
||||
assert_kind_of Time, Topic.find(:first, :conditions => ["id = ?", 1]).written_on
|
||||
assert_raises(ActiveRecord::PreparedStatementInvalid) {
|
||||
assert_raise(ActiveRecord::PreparedStatementInvalid) {
|
||||
Company.find(:first, :conditions => ["id=? AND name = ?", 2])
|
||||
}
|
||||
assert_raises(ActiveRecord::PreparedStatementInvalid) {
|
||||
assert_raise(ActiveRecord::PreparedStatementInvalid) {
|
||||
Company.find(:first, :conditions => ["id=?", 2, 3, 4])
|
||||
}
|
||||
end
|
||||
|
@ -435,11 +442,11 @@ class FinderTest < ActiveRecord::TestCase
|
|||
|
||||
def test_bind_arity
|
||||
assert_nothing_raised { bind '' }
|
||||
assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
|
||||
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
|
||||
|
||||
assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?' }
|
||||
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
|
||||
assert_nothing_raised { bind '?', 1 }
|
||||
assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
|
||||
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
|
||||
end
|
||||
|
||||
def test_named_bind_variables
|
||||
|
@ -544,7 +551,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
|
||||
def test_find_by_one_attribute_bang
|
||||
assert_equal topics(:first), Topic.find_by_title!("The First Topic")
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
|
||||
end
|
||||
|
||||
def test_find_by_one_attribute_caches_dynamic_finder
|
||||
|
@ -625,14 +632,14 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_find_by_one_missing_attribute
|
||||
assert_raises(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
|
||||
assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
|
||||
end
|
||||
|
||||
def test_find_by_invalid_method_syntax
|
||||
assert_raises(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
|
||||
assert_raises(NoMethodError) { Topic.find_by_title?("The First Topic") }
|
||||
assert_raises(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
|
||||
assert_raises(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
|
||||
assert_raise(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
|
||||
assert_raise(NoMethodError) { Topic.find_by_title?("The First Topic") }
|
||||
assert_raise(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
|
||||
assert_raise(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
|
||||
end
|
||||
|
||||
def test_find_by_two_attributes
|
||||
|
@ -654,8 +661,8 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_find_last_by_invalid_method_syntax
|
||||
assert_raises(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
|
||||
assert_raises(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
|
||||
assert_raise(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
|
||||
assert_raise(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
|
||||
end
|
||||
|
||||
def test_find_last_by_one_attribute_with_several_options
|
||||
|
@ -663,7 +670,7 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_find_last_by_one_missing_attribute
|
||||
assert_raises(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
|
||||
assert_raise(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
|
||||
end
|
||||
|
||||
def test_find_last_by_two_attributes
|
||||
|
@ -916,16 +923,16 @@ class FinderTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_find_with_bad_sql
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
|
||||
assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
|
||||
end
|
||||
|
||||
def test_find_with_invalid_params
|
||||
assert_raises(ArgumentError) { Topic.find :first, :join => "It should be `joins'" }
|
||||
assert_raises(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
|
||||
assert_raise(ArgumentError) { Topic.find :first, :join => "It should be `joins'" }
|
||||
assert_raise(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
|
||||
end
|
||||
|
||||
def test_dynamic_finder_with_invalid_params
|
||||
assert_raises(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
|
||||
assert_raise(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
|
||||
end
|
||||
|
||||
def test_find_all_with_limit
|
||||
|
@ -1057,6 +1064,14 @@ class FinderTest < ActiveRecord::TestCase
|
|||
assert_equal [0, 1, 1], posts.map(&:author_id).sort
|
||||
end
|
||||
|
||||
def test_finder_with_scoped_from
|
||||
all_topics = Topic.all
|
||||
|
||||
Topic.with_scope(:find => { :from => 'fake_topics' }) do
|
||||
assert_equal all_topics, Topic.all(:from => 'topics')
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def bind(statement, *vars)
|
||||
if vars.first.is_a?(Hash)
|
||||
|
|
|
@ -151,7 +151,7 @@ class FixturesTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_dirty_dirty_yaml_file
|
||||
assert_raises(Fixture::FormatError) do
|
||||
assert_raise(Fixture::FormatError) do
|
||||
Fixtures.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
|
||||
end
|
||||
end
|
||||
|
@ -420,7 +420,7 @@ class InvalidTableNameFixturesTest < ActiveRecord::TestCase
|
|||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_raises_error
|
||||
assert_raises FixtureClassNotFound do
|
||||
assert_raise FixtureClassNotFound do
|
||||
funny_jokes(:a_joke)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,7 +68,7 @@ class InheritanceTest < ActiveRecord::TestCase
|
|||
if current_adapter?(:SybaseAdapter)
|
||||
Company.connection.execute "SET IDENTITY_INSERT companies OFF"
|
||||
end
|
||||
assert_raises(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
||||
assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
||||
end
|
||||
|
||||
def test_inheritance_find
|
||||
|
@ -124,7 +124,7 @@ class InheritanceTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_finding_incorrect_type_data
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Firm.find(2) }
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Firm.find(2) }
|
||||
assert_nothing_raised { Firm.find(1) }
|
||||
end
|
||||
|
||||
|
|
|
@ -35,7 +35,25 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|||
assert_equal 0, p2.lock_version
|
||||
|
||||
p2.first_name = 'sue'
|
||||
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
end
|
||||
|
||||
def test_lock_destroy
|
||||
p1 = Person.find(1)
|
||||
p2 = Person.find(1)
|
||||
assert_equal 0, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p1.first_name = 'stu'
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
|
||||
|
||||
assert p1.destroy
|
||||
assert_equal true, p1.frozen?
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
|
||||
end
|
||||
|
||||
def test_lock_repeating
|
||||
|
@ -50,9 +68,9 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|||
assert_equal 0, p2.lock_version
|
||||
|
||||
p2.first_name = 'sue'
|
||||
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
p2.first_name = 'sue2'
|
||||
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
end
|
||||
|
||||
def test_lock_new
|
||||
|
@ -71,7 +89,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|||
assert_equal 0, p2.lock_version
|
||||
|
||||
p2.first_name = 'sue'
|
||||
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
end
|
||||
|
||||
def test_lock_new_with_nil
|
||||
|
@ -95,7 +113,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|||
assert_equal 0, t2.version
|
||||
|
||||
t2.tps_report_number = 800
|
||||
assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
|
||||
assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
|
||||
end
|
||||
|
||||
def test_lock_column_is_mass_assignable
|
||||
|
|
|
@ -262,6 +262,15 @@ class NestedScopingTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_merge_inner_scope_has_priority
|
||||
Developer.with_scope(:find => { :limit => 5 }) do
|
||||
Developer.with_scope(:find => { :limit => 10 }) do
|
||||
merged_option = Developer.instance_eval('current_scoped_methods')[:find]
|
||||
assert_equal({ :limit => 10 }, merged_option)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_replace_options
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
|
||||
|
@ -369,8 +378,10 @@ class NestedScopingTest < ActiveRecord::TestCase
|
|||
def test_merged_scoped_find
|
||||
poor_jamis = developers(:poor_jamis)
|
||||
Developer.with_scope(:find => { :conditions => "salary < 100000" }) do
|
||||
Developer.with_scope(:find => { :offset => 1 }) do
|
||||
assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
|
||||
Developer.with_scope(:find => { :offset => 1, :order => 'id asc' }) do
|
||||
assert_sql /ORDER BY id asc / do
|
||||
assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -400,6 +411,29 @@ class NestedScopingTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_create
|
||||
comment = nil
|
||||
Comment.with_scope(:create => { :post_id => 1}) do
|
||||
Comment.with_scope(:create => { :post_id => 2}) do
|
||||
assert_equal({ :post_id => 2 }, Comment.send(:current_scoped_methods)[:create])
|
||||
comment = Comment.create :body => "Hey guys, nested scopes are broken. Please fix!"
|
||||
end
|
||||
end
|
||||
assert_equal 2, comment.post_id
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope_for_create
|
||||
comment = nil
|
||||
Comment.with_scope(:create => { :body => "Hey guys, nested scopes are broken. Please fix!" }) do
|
||||
Comment.with_exclusive_scope(:create => { :post_id => 1 }) do
|
||||
assert_equal({ :post_id => 1 }, Comment.send(:current_scoped_methods)[:create])
|
||||
comment = Comment.create :body => "Hey guys"
|
||||
end
|
||||
end
|
||||
assert_equal 1, comment.post_id
|
||||
assert_equal 'Hey guys', comment.body
|
||||
end
|
||||
|
||||
def test_merged_scoped_find_on_blank_conditions
|
||||
[nil, " ", [], {}].each do |blank|
|
||||
Developer.with_scope(:find => {:conditions => blank}) do
|
||||
|
@ -523,7 +557,6 @@ class HasManyScopingTest< ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :posts, :categories, :categories_posts
|
||||
|
||||
|
@ -549,7 +582,6 @@ class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
class DefaultScopingTest < ActiveRecord::TestCase
|
||||
fixtures :developers
|
||||
|
||||
|
@ -577,7 +609,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
|||
# Scopes added on children should append to parent scope
|
||||
expected_klass_scope = [{ :create => {}, :find => { :order => 'salary DESC' }}, { :create => {}, :find => {} }]
|
||||
assert_equal expected_klass_scope, klass.send(:scoped_methods)
|
||||
|
||||
|
||||
# Parent should still have the original scope
|
||||
assert_equal scope, DeveloperOrderedBySalary.send(:scoped_methods)
|
||||
end
|
||||
|
@ -620,7 +652,6 @@ end
|
|||
=begin
|
||||
# We disabled the scoping for has_one and belongs_to as we can't think of a proper use case
|
||||
|
||||
|
||||
class BelongsToScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
|
@ -640,7 +671,6 @@ class BelongsToScopingTest< ActiveRecord::TestCase
|
|||
|
||||
end
|
||||
|
||||
|
||||
class HasOneScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
|
|
|
@ -93,6 +93,30 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
end
|
||||
|
||||
def testing_table_with_only_foo_attribute
|
||||
Person.connection.create_table :testings, :id => false do |t|
|
||||
t.column :foo, :string
|
||||
end
|
||||
|
||||
yield Person.connection
|
||||
ensure
|
||||
Person.connection.drop_table :testings rescue nil
|
||||
end
|
||||
protected :testing_table_with_only_foo_attribute
|
||||
|
||||
def test_create_table_without_id
|
||||
testing_table_with_only_foo_attribute do |connection|
|
||||
assert_equal connection.columns(:testings).size, 1
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_column_with_primary_key_attribute
|
||||
testing_table_with_only_foo_attribute do |connection|
|
||||
assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
|
||||
assert_equal connection.columns(:testings).size, 2
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_table_adds_id
|
||||
Person.connection.create_table :testings do |t|
|
||||
t.column :foo, :string
|
||||
|
@ -111,7 +135,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
end
|
||||
|
||||
assert_raises(ActiveRecord::StatementInvalid) do
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
Person.connection.execute "insert into testings (foo) values (NULL)"
|
||||
end
|
||||
ensure
|
||||
|
@ -278,7 +302,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
Person.connection.add_column :testings, :bar, :string, :null => false
|
||||
|
||||
assert_raises(ActiveRecord::StatementInvalid) do
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
|
||||
end
|
||||
ensure
|
||||
|
@ -297,7 +321,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
|
||||
assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
|
||||
|
||||
assert_raises(ActiveRecord::StatementInvalid) do
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
unless current_adapter?(:OpenBaseAdapter)
|
||||
Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
|
||||
else
|
||||
|
@ -545,7 +569,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
else
|
||||
ActiveRecord::ActiveRecordError
|
||||
end
|
||||
assert_raises(exception) do
|
||||
assert_raise(exception) do
|
||||
Person.connection.rename_column "hats", "nonexistent", "should_fail"
|
||||
end
|
||||
ensure
|
||||
|
@ -795,7 +819,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
assert_equal "hello world", Reminder.find(:first).content
|
||||
|
||||
WeNeedReminders.down
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
end
|
||||
|
||||
def test_add_table_with_decimals
|
||||
|
@ -856,7 +880,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
|
||||
GiveMeBigNumbers.down
|
||||
assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
|
||||
assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
|
||||
end
|
||||
|
||||
def test_migrator
|
||||
|
@ -876,7 +900,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
assert_equal 0, ActiveRecord::Migrator.current_version
|
||||
Person.reset_column_information
|
||||
assert !Person.column_methods_hash.include?(:last_name)
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
end
|
||||
|
||||
def test_migrator_one_up
|
||||
|
@ -928,11 +952,11 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
assert_equal(0, ActiveRecord::Migrator.current_version)
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
if ActiveRecord::Base.connection.supports_ddl_transactions?
|
||||
def test_migrator_one_up_with_exception_and_rollback
|
||||
assert !Person.column_methods_hash.include?(:last_name)
|
||||
|
||||
e = assert_raises(StandardError) do
|
||||
e = assert_raise(StandardError) do
|
||||
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
|
||||
end
|
||||
|
||||
|
@ -945,20 +969,20 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
|
||||
def test_finds_migrations
|
||||
migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
|
||||
[['1', 'people_have_last_names'],
|
||||
['2', 'we_need_reminders'],
|
||||
['3', 'innocent_jointable']].each_with_index do |pair, i|
|
||||
migrations[i].version == pair.first
|
||||
migrations[1].name == pair.last
|
||||
|
||||
[[1, 'PeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
|
||||
assert_equal migrations[i].version, pair.first
|
||||
assert_equal migrations[i].name, pair.last
|
||||
end
|
||||
end
|
||||
|
||||
def test_finds_pending_migrations
|
||||
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
|
||||
migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
|
||||
|
||||
assert_equal 1, migrations.size
|
||||
migrations[0].version == '3'
|
||||
migrations[0].name == 'innocent_jointable'
|
||||
assert_equal migrations[0].version, 3
|
||||
assert_equal migrations[0].name, 'InnocentJointable'
|
||||
end
|
||||
|
||||
def test_only_loads_pending_migrations
|
||||
|
@ -1107,7 +1131,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
assert_equal "hello world", Reminder.find(:first).content
|
||||
|
||||
WeNeedReminders.down
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
ensure
|
||||
ActiveRecord::Base.table_name_prefix = ''
|
||||
ActiveRecord::Base.table_name_suffix = ''
|
||||
|
@ -1137,13 +1161,13 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
|
||||
def test_migrator_with_duplicates
|
||||
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
|
||||
assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
|
||||
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrator_with_duplicate_names
|
||||
assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
|
||||
assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
|
||||
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
|
||||
end
|
||||
end
|
||||
|
@ -1159,7 +1183,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
|
||||
# table name is 29 chars, the standard sequence name will
|
||||
# be 33 chars and fail
|
||||
assert_raises(ActiveRecord::StatementInvalid) do
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
begin
|
||||
Person.connection.create_table :table_with_name_thats_just_ok do |t|
|
||||
t.column :foo, :string, :null => false
|
||||
|
@ -1186,7 +1210,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
|
||||
# confirm the custom sequence got dropped
|
||||
assert_raises(ActiveRecord::StatementInvalid) do
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
Person.connection.execute("select suitably_short_seq.nextval from dual")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
assert_equal Topic.find(:all), Topic.base
|
||||
assert_equal Topic.find(:all), Topic.base.to_a
|
||||
assert_equal Topic.find(:first), Topic.base.first
|
||||
assert_equal Topic.find(:all), Topic.base.each { |i| i }
|
||||
assert_equal Topic.find(:all), Topic.base.map { |i| i }
|
||||
end
|
||||
|
||||
def test_found_items_are_cached
|
||||
|
@ -99,6 +99,12 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
assert_equal topics_written_before_the_second, Topic.written_before(topics(:second).written_on)
|
||||
end
|
||||
|
||||
def test_procedural_scopes_returning_nil
|
||||
all_topics = Topic.find(:all)
|
||||
|
||||
assert_equal all_topics, Topic.written_before(nil)
|
||||
end
|
||||
|
||||
def test_scopes_with_joins
|
||||
address = author_addresses(:david_address)
|
||||
posts_with_authors_at_address = Post.find(
|
||||
|
@ -247,7 +253,7 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
topic = Topic.approved.create!({})
|
||||
assert topic.approved
|
||||
end
|
||||
|
||||
|
||||
def test_should_build_with_proxy_options_chained
|
||||
topic = Topic.approved.by_lifo.build({})
|
||||
assert topic.approved
|
||||
|
@ -287,15 +293,21 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
|
||||
end
|
||||
|
||||
def test_chanining_should_use_latest_conditions_when_creating
|
||||
post1 = Topic.rejected.approved.new
|
||||
assert post1.approved?
|
||||
def test_chaining_should_use_latest_conditions_when_creating
|
||||
post = Topic.rejected.new
|
||||
assert !post.approved?
|
||||
|
||||
post2 = Topic.approved.rejected.new
|
||||
assert ! post2.approved?
|
||||
post = Topic.rejected.approved.new
|
||||
assert post.approved?
|
||||
|
||||
post = Topic.approved.rejected.new
|
||||
assert !post.approved?
|
||||
|
||||
post = Topic.approved.rejected.approved.new
|
||||
assert post.approved?
|
||||
end
|
||||
|
||||
def test_chanining_should_use_latest_conditions_when_searching
|
||||
def test_chaining_should_use_latest_conditions_when_searching
|
||||
# Normal hash conditions
|
||||
assert_equal Topic.all(:conditions => {:approved => true}), Topic.rejected.approved.all
|
||||
assert_equal Topic.all(:conditions => {:approved => false}), Topic.approved.rejected.all
|
||||
|
@ -306,6 +318,24 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
# Nested hash conditions with different keys
|
||||
assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq
|
||||
end
|
||||
|
||||
def test_methods_invoked_within_scopes_should_respect_scope
|
||||
assert_equal [], Topic.approved.by_rejected_ids.proxy_options[:conditions][:id]
|
||||
end
|
||||
|
||||
def test_named_scopes_batch_finders
|
||||
assert_equal 3, Topic.approved.count
|
||||
|
||||
assert_queries(4) do
|
||||
Topic.approved.find_each(:batch_size => 1) {|t| assert t.approved? }
|
||||
end
|
||||
|
||||
assert_queries(2) do
|
||||
Topic.approved.find_in_batches(:batch_size => 2) do |group|
|
||||
group.each {|t| assert t.approved? }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DynamicScopeMatchTest < ActiveRecord::TestCase
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'models/customer'
|
|||
require 'models/company'
|
||||
require 'models/company_in_module'
|
||||
require 'models/subscriber'
|
||||
require 'models/pirate'
|
||||
|
||||
class ReflectionTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :customers, :companies, :subscribers
|
||||
|
@ -169,9 +170,9 @@ class ReflectionTest < ActiveRecord::TestCase
|
|||
|
||||
def test_reflection_of_all_associations
|
||||
# FIXME these assertions bust a lot
|
||||
assert_equal 26, Firm.reflect_on_all_associations.size
|
||||
assert_equal 20, Firm.reflect_on_all_associations(:has_many).size
|
||||
assert_equal 6, Firm.reflect_on_all_associations(:has_one).size
|
||||
assert_equal 28, Firm.reflect_on_all_associations.size
|
||||
assert_equal 21, Firm.reflect_on_all_associations(:has_many).size
|
||||
assert_equal 7, Firm.reflect_on_all_associations(:has_one).size
|
||||
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
|
||||
end
|
||||
|
||||
|
|
|
@ -214,7 +214,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_invalid_keys_for_transaction
|
||||
assert_raises ArgumentError do
|
||||
assert_raise ArgumentError do
|
||||
Topic.transaction :nested => true do
|
||||
end
|
||||
end
|
||||
|
@ -349,7 +349,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_sqlite_add_column_in_transaction_raises_statement_invalid
|
||||
def test_sqlite_add_column_in_transaction
|
||||
return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
|
||||
|
||||
# Test first if column creation/deletion works correctly when no
|
||||
|
@ -368,10 +368,15 @@ class TransactionTest < ActiveRecord::TestCase
|
|||
assert !Topic.column_names.include?('stuff')
|
||||
end
|
||||
|
||||
# Test now inside a transaction: add_column should raise a StatementInvalid
|
||||
Topic.transaction do
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
|
||||
raise ActiveRecord::Rollback
|
||||
if Topic.connection.supports_ddl_transactions?
|
||||
assert_nothing_raised do
|
||||
Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
|
||||
end
|
||||
else
|
||||
Topic.transaction do
|
||||
assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ require 'models/warehouse_thing'
|
|||
require 'models/guid'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
require 'models/event'
|
||||
|
||||
# The following methods in Topic are used in test_conditional_validation_*
|
||||
class Topic
|
||||
|
@ -113,8 +114,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_invalid_record_exception
|
||||
assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
|
||||
assert_raises(ActiveRecord::RecordInvalid) { Reply.new.save! }
|
||||
assert_raise(ActiveRecord::RecordInvalid) { Reply.create! }
|
||||
assert_raise(ActiveRecord::RecordInvalid) { Reply.new.save! }
|
||||
|
||||
begin
|
||||
r = Reply.new
|
||||
|
@ -126,13 +127,13 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_exception_on_create_bang_many
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
assert_raise(ActiveRecord::RecordInvalid) do
|
||||
Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_on_create_bang_with_block
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
assert_raise(ActiveRecord::RecordInvalid) do
|
||||
Reply.create!({ "title" => "OK" }) do |r|
|
||||
r.content = nil
|
||||
end
|
||||
|
@ -140,7 +141,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_exception_on_create_bang_many_with_block
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
assert_raise(ActiveRecord::RecordInvalid) do
|
||||
Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
|
||||
r.content = nil
|
||||
end
|
||||
|
@ -149,7 +150,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
def test_scoped_create_without_attributes
|
||||
Reply.with_scope(:create => {}) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
|
||||
assert_raise(ActiveRecord::RecordInvalid) { Reply.create! }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,7 +170,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_single_error_per_attr_iteration
|
||||
r = Reply.new
|
||||
|
@ -530,6 +531,14 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_validate_uniqueness_with_limit
|
||||
# Event.title is limited to 5 characters
|
||||
e1 = Event.create(:title => "abcde")
|
||||
assert e1.valid?, "Could not create an event with a unique, 5 character title"
|
||||
e2 = Event.create(:title => "abcdefgh")
|
||||
assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
|
||||
end
|
||||
|
||||
def test_validate_straight_inheritance_uniqueness
|
||||
w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
|
||||
assert w1.valid?, "Saving w1"
|
||||
|
@ -1421,6 +1430,17 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
assert_equal "can't be blank", t.errors.on("title").first
|
||||
end
|
||||
|
||||
def test_invalid_should_be_the_opposite_of_valid
|
||||
Topic.validates_presence_of :title
|
||||
|
||||
t = Topic.new
|
||||
assert t.invalid?
|
||||
assert t.errors.invalid?(:title)
|
||||
|
||||
t.title = 'Things are going to change'
|
||||
assert !t.invalid?
|
||||
end
|
||||
|
||||
# previous implementation of validates_presence_of eval'd the
|
||||
# string with the wrong binding, this regression test is to
|
||||
# ensure that it works correctly
|
||||
|
@ -1442,20 +1462,6 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.author_name = "Hubert J. Farnsworth"
|
||||
assert t.valid?, "A topic with an important title and author should be valid"
|
||||
end
|
||||
|
||||
private
|
||||
def with_kcode(kcode)
|
||||
if RUBY_VERSION < '1.9'
|
||||
orig_kcode, $KCODE = $KCODE, kcode
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
$KCODE = orig_kcode
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -38,11 +38,15 @@ class XmlSerializationTest < ActiveRecord::TestCase
|
|||
assert_match %r{<CreatedAt}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_skipped_types
|
||||
@xml = Contact.new(:age => 25).to_xml :skip_types => true
|
||||
assert %r{<age>25</age>}.match(@xml)
|
||||
end
|
||||
|
||||
def test_should_include_yielded_additions
|
||||
@xml = Contact.new.to_xml do |xml|
|
||||
xml.creator "David"
|
||||
end
|
||||
|
||||
assert_match %r{<creator>David</creator>}, @xml
|
||||
end
|
||||
end
|
||||
|
@ -145,6 +149,13 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
|
|||
assert_match %r{<hello-post type="StiPost">}, xml
|
||||
end
|
||||
|
||||
def test_included_associations_should_skip_types
|
||||
xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0, :skip_types => true
|
||||
assert_match %r{<hello-posts>}, xml
|
||||
assert_match %r{<hello-post>}, xml
|
||||
assert_match %r{<hello-post>}, xml
|
||||
end
|
||||
|
||||
def test_methods_are_called_on_object
|
||||
xml = authors(:david).to_xml :methods => :label, :indent => 0
|
||||
assert_match %r{<label>.*</label>}, xml
|
||||
|
|
4
vendor/rails/activerecord/test/fixtures/toys.yml
vendored
Normal file
4
vendor/rails/activerecord/test/fixtures/toys.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
bone:
|
||||
toy_id: 1
|
||||
name: Bone
|
||||
pet_id: 1
|
|
@ -37,6 +37,7 @@ class Firm < Company
|
|||
has_many :clients, :order => "id", :dependent => :destroy, :counter_sql =>
|
||||
"SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
|
||||
"AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
|
||||
has_many :unsorted_clients, :class_name => "Client"
|
||||
has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
|
||||
has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
|
||||
has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
|
||||
|
@ -69,6 +70,7 @@ class Firm < Company
|
|||
has_one :account_with_select, :foreign_key => "firm_id", :select => "id, firm_id", :class_name=>'Account'
|
||||
has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true
|
||||
has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account"
|
||||
has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
|
||||
end
|
||||
|
||||
class DependentFirm < Company
|
||||
|
|
3
vendor/rails/activerecord/test/models/event.rb
vendored
Normal file
3
vendor/rails/activerecord/test/models/event.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
class Event < ActiveRecord::Base
|
||||
validates_uniqueness_of :title
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
class Owner < ActiveRecord::Base
|
||||
set_primary_key :owner_id
|
||||
has_many :pets
|
||||
end
|
||||
has_many :toys, :through => :pets
|
||||
end
|
||||
|
|
3
vendor/rails/activerecord/test/models/pet.rb
vendored
3
vendor/rails/activerecord/test/models/pet.rb
vendored
|
@ -1,4 +1,5 @@
|
|||
class Pet < ActiveRecord::Base
|
||||
set_primary_key :pet_id
|
||||
belongs_to :owner
|
||||
end
|
||||
has_many :toys
|
||||
end
|
||||
|
|
49
vendor/rails/activerecord/test/models/pirate.rb
vendored
49
vendor/rails/activerecord/test/models/pirate.rb
vendored
|
@ -1,16 +1,63 @@
|
|||
class Pirate < ActiveRecord::Base
|
||||
belongs_to :parrot
|
||||
has_and_belongs_to_many :parrots
|
||||
has_many :treasures, :as => :looter
|
||||
has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
|
||||
:before_add => :log_before_add,
|
||||
:after_add => :log_after_add,
|
||||
:before_remove => :log_before_remove,
|
||||
:after_remove => :log_after_remove
|
||||
has_and_belongs_to_many :parrots_with_proc_callbacks, :class_name => "Parrot",
|
||||
:before_add => proc {|p,pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}"},
|
||||
:after_add => proc {|p,pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}"},
|
||||
:before_remove => proc {|p,pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}"},
|
||||
:after_remove => proc {|p,pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}"}
|
||||
|
||||
has_many :treasures, :as => :looter
|
||||
has_many :treasure_estimates, :through => :treasures, :source => :price_estimates
|
||||
|
||||
# These both have :autosave enabled because accepts_nested_attributes_for is used on them.
|
||||
has_one :ship
|
||||
has_many :birds
|
||||
has_many :birds_with_method_callbacks, :class_name => "Bird",
|
||||
:before_add => :log_before_add,
|
||||
:after_add => :log_after_add,
|
||||
:before_remove => :log_before_remove,
|
||||
:after_remove => :log_after_remove
|
||||
has_many :birds_with_proc_callbacks, :class_name => "Bird",
|
||||
:before_add => proc {|p,b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}"},
|
||||
:after_add => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}"},
|
||||
:before_remove => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"},
|
||||
:after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
|
||||
|
||||
accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
|
||||
:birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true
|
||||
|
||||
validates_presence_of :catchphrase
|
||||
|
||||
def ship_log
|
||||
@ship_log ||= []
|
||||
end
|
||||
|
||||
private
|
||||
def log_before_add(record)
|
||||
log(record, "before_adding_method")
|
||||
end
|
||||
|
||||
def log_after_add(record)
|
||||
log(record, "after_adding_method")
|
||||
end
|
||||
|
||||
def log_before_remove(record)
|
||||
log(record, "before_removing_method")
|
||||
end
|
||||
|
||||
def log_after_remove(record)
|
||||
log(record, "after_removing_method")
|
||||
end
|
||||
|
||||
def log(record, callback)
|
||||
ship_log << "#{callback}_#{record.class.name.downcase}_#{record.id || '<new>'}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,3 +37,9 @@ end
|
|||
class SillyReply < Reply
|
||||
belongs_to :reply, :foreign_key => "parent_id", :counter_cache => :replies_count
|
||||
end
|
||||
|
||||
module Web
|
||||
class Reply < Web::Topic
|
||||
belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true, :class_name => 'Web::Topic'
|
||||
end
|
||||
end
|
12
vendor/rails/activerecord/test/models/topic.rb
vendored
12
vendor/rails/activerecord/test/models/topic.rb
vendored
|
@ -1,7 +1,9 @@
|
|||
class Topic < ActiveRecord::Base
|
||||
named_scope :base
|
||||
named_scope :written_before, lambda { |time|
|
||||
{ :conditions => ['written_on < ?', time] }
|
||||
if time
|
||||
{ :conditions => ['written_on < ?', time] }
|
||||
end
|
||||
}
|
||||
named_scope :approved, :conditions => {:approved => true}
|
||||
named_scope :rejected, :conditions => {:approved => false}
|
||||
|
@ -33,6 +35,8 @@ class Topic < ActiveRecord::Base
|
|||
end
|
||||
named_scope :named_extension, :extend => NamedExtension
|
||||
named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne]
|
||||
|
||||
named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }}
|
||||
|
||||
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
|
||||
serialize :content
|
||||
|
@ -69,3 +73,9 @@ class Topic < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Web
|
||||
class Topic < ActiveRecord::Base
|
||||
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
|
||||
end
|
||||
end
|
4
vendor/rails/activerecord/test/models/toy.rb
vendored
Normal file
4
vendor/rails/activerecord/test/models/toy.rb
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
class Toy < ActiveRecord::Base
|
||||
set_primary_key :toy_id
|
||||
belongs_to :pet
|
||||
end
|
|
@ -155,6 +155,10 @@ ActiveRecord::Schema.define do
|
|||
t.integer :course_id, :null => false
|
||||
end
|
||||
|
||||
create_table :events, :force => true do |t|
|
||||
t.string :title, :limit => 5
|
||||
end
|
||||
|
||||
create_table :funny_jokes, :force => true do |t|
|
||||
t.string :name
|
||||
end
|
||||
|
@ -421,6 +425,11 @@ ActiveRecord::Schema.define do
|
|||
t.column :taggings_count, :integer, :default => 0
|
||||
end
|
||||
|
||||
create_table :toys, :primary_key => :toy_id ,:force => true do |t|
|
||||
t.string :name
|
||||
t.integer :pet_id, :integer
|
||||
end
|
||||
|
||||
create_table :treasures, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :looter_id, :integer
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue