Rails 2.3.3.1
Update to latest Rails. A little bit of jiggery-pokery is involved, since they neglected to re-include vendored Rack in this release.
This commit is contained in:
parent
329fafafce
commit
664552ac02
257 changed files with 4346 additions and 1682 deletions
11
vendor/rails/activerecord/CHANGELOG
vendored
11
vendor/rails/activerecord/CHANGELOG
vendored
|
@ -1,3 +1,14 @@
|
|||
*2.3.3 (July 12, 2009)*
|
||||
|
||||
* Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha]
|
||||
# employees.company_name references companies.name
|
||||
Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name'
|
||||
|
||||
* Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH]
|
||||
|
||||
* Added ActiveRecord::Base#touch to update the updated_at/on attributes (or another specified timestamp) with the current time [DHH]
|
||||
|
||||
|
||||
*2.3.2 [Final] (March 15, 2009)*
|
||||
|
||||
* Added ActiveRecord::Base.find_each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
|
||||
|
|
5
vendor/rails/activerecord/Rakefile
vendored
5
vendor/rails/activerecord/Rakefile
vendored
|
@ -4,7 +4,6 @@ require 'rake/testtask'
|
|||
require 'rake/rdoctask'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rake/contrib/sshpublisher'
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
||||
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
|
||||
|
@ -177,7 +176,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.2' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD)
|
||||
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
||||
|
@ -231,12 +230,14 @@ end
|
|||
|
||||
desc "Publish the beta gem"
|
||||
task :pgem => [:package] do
|
||||
require 'rake/contrib/sshpublisher'
|
||||
Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
||||
`ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
|
||||
end
|
||||
|
||||
desc "Publish the API documentation"
|
||||
task :pdoc => [:rdoc] do
|
||||
require 'rake/contrib/sshpublisher'
|
||||
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
|
||||
end
|
||||
|
||||
|
|
|
@ -957,6 +957,8 @@ module ActiveRecord
|
|||
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
|
||||
# "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
|
||||
# will use a foreign key of "favorite_person_id".
|
||||
# [:primary_key]
|
||||
# Specify the method that returns the primary key of associated object used for the association. By default this is id.
|
||||
# [:dependent]
|
||||
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
|
||||
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
|
||||
|
@ -981,15 +983,21 @@ module ActiveRecord
|
|||
# If false, don't validate the associated objects when saving the parent object. +false+ by default.
|
||||
# [:autosave]
|
||||
# If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
|
||||
# [:touch]
|
||||
# If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
|
||||
# destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
|
||||
#
|
||||
# Option examples:
|
||||
# belongs_to :firm, :foreign_key => "client_of"
|
||||
# belongs_to :person, :primary_key => "name", :foreign_key => "person_name"
|
||||
# belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
|
||||
# belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
|
||||
# :conditions => 'discounts > #{payments_count}'
|
||||
# belongs_to :attachable, :polymorphic => true
|
||||
# belongs_to :project, :readonly => true
|
||||
# belongs_to :post, :counter_cache => true
|
||||
# belongs_to :company, :touch => true
|
||||
# belongs_to :company, :touch => :employees_last_updated_at
|
||||
def belongs_to(association_id, options = {})
|
||||
reflection = create_belongs_to_reflection(association_id, options)
|
||||
|
||||
|
@ -1001,28 +1009,8 @@ module ActiveRecord
|
|||
association_constructor_method(:create, reflection, BelongsToAssociation)
|
||||
end
|
||||
|
||||
# Create the callbacks to update counter cache
|
||||
if options[:counter_cache]
|
||||
cache_column = reflection.counter_cache_column
|
||||
|
||||
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
end
|
||||
after_create method_name
|
||||
|
||||
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
end
|
||||
before_destroy method_name
|
||||
|
||||
module_eval(
|
||||
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
|
||||
)
|
||||
end
|
||||
add_counter_cache_callbacks(reflection) if options[:counter_cache]
|
||||
add_touch_callbacks(reflection, options[:touch]) if options[:touch]
|
||||
|
||||
configure_dependency_for_belongs_to(reflection)
|
||||
end
|
||||
|
@ -1329,6 +1317,43 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def add_counter_cache_callbacks(reflection)
|
||||
cache_column = reflection.counter_cache_column
|
||||
|
||||
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.increment_counter(cache_column, association.id) unless association.nil?
|
||||
end
|
||||
after_create(method_name)
|
||||
|
||||
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.decrement_counter(cache_column, association.id) unless association.nil?
|
||||
end
|
||||
before_destroy(method_name)
|
||||
|
||||
module_eval(
|
||||
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
|
||||
)
|
||||
end
|
||||
|
||||
def add_touch_callbacks(reflection, touch_attribute)
|
||||
method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
|
||||
if touch_attribute == true
|
||||
association.touch unless association.nil?
|
||||
else
|
||||
association.touch(touch_attribute) unless association.nil?
|
||||
end
|
||||
end
|
||||
after_save(method_name)
|
||||
after_destroy(method_name)
|
||||
end
|
||||
|
||||
def find_with_associations(options = {})
|
||||
catch :invalid_query do
|
||||
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
|
||||
|
@ -1353,7 +1378,7 @@ module ActiveRecord
|
|||
dependent_conditions = []
|
||||
dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
|
||||
dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
|
||||
dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
|
||||
dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions]
|
||||
dependent_conditions << extra_conditions if extra_conditions
|
||||
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
|
||||
dependent_conditions = dependent_conditions.gsub('@', '\@')
|
||||
|
@ -1497,9 +1522,9 @@ module ActiveRecord
|
|||
|
||||
mattr_accessor :valid_keys_for_belongs_to_association
|
||||
@@valid_keys_for_belongs_to_association = [
|
||||
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
|
||||
:class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions,
|
||||
:include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
|
||||
:validate
|
||||
:validate, :touch
|
||||
]
|
||||
|
||||
def create_belongs_to_reflection(association_id, options)
|
||||
|
@ -1643,17 +1668,29 @@ module ActiveRecord
|
|||
string.scan(/([\.a-zA-Z_]+).?\./).flatten
|
||||
end
|
||||
|
||||
def tables_in_hash(hash)
|
||||
return [] if hash.blank?
|
||||
tables = hash.map do |key, value|
|
||||
if value.is_a?(Hash)
|
||||
key.to_s
|
||||
else
|
||||
tables_in_string(key) if key.is_a?(String)
|
||||
end
|
||||
end
|
||||
tables.flatten.compact
|
||||
end
|
||||
|
||||
def conditions_tables(options)
|
||||
# look in both sets of conditions
|
||||
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
|
||||
case cond
|
||||
when nil then all
|
||||
when Array then all << cond.first
|
||||
when Hash then all << cond.keys
|
||||
else all << cond
|
||||
when Array then all << tables_in_string(cond.first)
|
||||
when Hash then all << tables_in_hash(cond)
|
||||
else all << tables_in_string(cond)
|
||||
end
|
||||
end
|
||||
tables_in_string(conditions.join(' '))
|
||||
conditions.flatten
|
||||
end
|
||||
|
||||
def order_tables(options)
|
||||
|
@ -2101,7 +2138,7 @@ module ActiveRecord
|
|||
klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
|
||||
|
||||
[through_reflection, reflection].each do |ref|
|
||||
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
|
||||
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions]
|
||||
end
|
||||
|
||||
join
|
||||
|
|
|
@ -169,8 +169,8 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
# Forwards the call to the reflection class.
|
||||
def sanitize_sql(sql)
|
||||
@reflection.klass.send(:sanitize_sql, sql)
|
||||
def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name)
|
||||
@reflection.klass.send(:sanitize_sql, sql, table_name)
|
||||
end
|
||||
|
||||
# Assigns the ID of the owner to the corresponding foreign key in +record+.
|
||||
|
|
|
@ -14,7 +14,7 @@ module ActiveRecord
|
|||
|
||||
if record.nil?
|
||||
if counter_cache_name && !@owner.new_record?
|
||||
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
|
||||
@reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
|
||||
end
|
||||
|
||||
@target = @owner[@reflection.primary_key_name] = nil
|
||||
|
@ -27,7 +27,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
@target = (AssociationProxy === record ? record.target : record)
|
||||
@owner[@reflection.primary_key_name] = record.id unless record.new_record?
|
||||
@owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
|
||||
@updated = true
|
||||
end
|
||||
|
||||
|
@ -41,18 +41,36 @@ module ActiveRecord
|
|||
|
||||
private
|
||||
def find_target
|
||||
@reflection.klass.find(
|
||||
find_method = if @reflection.options[:primary_key]
|
||||
"find_by_#{@reflection.options[:primary_key]}"
|
||||
else
|
||||
"find"
|
||||
end
|
||||
@reflection.klass.send(find_method,
|
||||
@owner[@reflection.primary_key_name],
|
||||
:select => @reflection.options[:select],
|
||||
:conditions => conditions,
|
||||
:include => @reflection.options[:include],
|
||||
:readonly => @reflection.options[:readonly]
|
||||
)
|
||||
) if @owner[@reflection.primary_key_name]
|
||||
end
|
||||
|
||||
def foreign_key_present
|
||||
!@owner[@reflection.primary_key_name].nil?
|
||||
end
|
||||
|
||||
def record_id(record)
|
||||
record.send(@reflection.options[:primary_key] || :id)
|
||||
end
|
||||
|
||||
def previous_record_id
|
||||
@previous_record_id ||= if @reflection.options[:primary_key]
|
||||
previous_record = @owner.send(@reflection.name)
|
||||
previous_record.nil? ? nil : previous_record.id
|
||||
else
|
||||
@owner[@reflection.primary_key_name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ module ActiveRecord
|
|||
else
|
||||
@target = (AssociationProxy === record ? record.target : record)
|
||||
|
||||
@owner[@reflection.primary_key_name] = record.id
|
||||
@owner[@reflection.primary_key_name] = record_id(record)
|
||||
@owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
|
||||
|
||||
@updated = true
|
||||
|
@ -41,6 +41,10 @@ module ActiveRecord
|
|||
!@owner[@reflection.primary_key_name].nil?
|
||||
end
|
||||
|
||||
def record_id(record)
|
||||
record.send(@reflection.options[:primary_key] || :id)
|
||||
end
|
||||
|
||||
def association_class
|
||||
@owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
|
||||
end
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
module ActiveRecord
|
||||
module Associations
|
||||
class HasOneThroughAssociation < HasManyThroughAssociation
|
||||
|
||||
|
||||
def create_through_record(new_value) #nodoc:
|
||||
klass = @reflection.through_reflection.klass
|
||||
|
||||
current_object = @owner.send(@reflection.through_reflection.name)
|
||||
|
||||
|
||||
if current_object
|
||||
current_object.update_attributes(construct_join_attributes(new_value))
|
||||
new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
|
||||
else
|
||||
@owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value)))
|
||||
@owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def find(*args)
|
||||
super(args.merge(:limit => 1))
|
||||
end
|
||||
|
||||
|
||||
def find_target
|
||||
super.first
|
||||
end
|
||||
|
||||
def reset_target!
|
||||
@target = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -311,11 +311,13 @@ module ActiveRecord
|
|||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_has_one_association(reflection)
|
||||
if (association = association_instance_get(reflection.name)) && !association.target.nil?
|
||||
if reflection.options[:autosave] && association.marked_for_destruction?
|
||||
autosave = reflection.options[:autosave]
|
||||
|
||||
if autosave && association.marked_for_destruction?
|
||||
association.destroy
|
||||
elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
|
||||
elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave
|
||||
association[reflection.primary_key_name] = id
|
||||
association.save(false)
|
||||
association.save(!autosave)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -330,13 +332,16 @@ module ActiveRecord
|
|||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_belongs_to_association(reflection)
|
||||
if association = association_instance_get(reflection.name)
|
||||
if reflection.options[:autosave] && association.marked_for_destruction?
|
||||
autosave = reflection.options[:autosave]
|
||||
|
||||
if autosave && association.marked_for_destruction?
|
||||
association.destroy
|
||||
else
|
||||
association.save(false) if association.new_record? || reflection.options[:autosave]
|
||||
association.save(!autosave) if association.new_record? || autosave
|
||||
|
||||
if association.updated?
|
||||
self[reflection.primary_key_name] = association.id
|
||||
association_id = association.send(reflection.options[:primary_key] || :id)
|
||||
self[reflection.primary_key_name] = association_id
|
||||
# TODO: Removing this code doesn't seem to matter…
|
||||
if reflection.options[:polymorphic]
|
||||
self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
|
||||
|
|
|
@ -687,14 +687,9 @@ module ActiveRecord #:nodoc:
|
|||
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
||||
# Person.exists?
|
||||
def exists?(id_or_conditions = {})
|
||||
connection.select_all(
|
||||
construct_finder_sql(
|
||||
:select => "#{quoted_table_name}.#{primary_key}",
|
||||
:conditions => expand_id_conditions(id_or_conditions),
|
||||
:limit => 1
|
||||
),
|
||||
"#{name} Exists"
|
||||
).size > 0
|
||||
find_initial(
|
||||
:select => "#{quoted_table_name}.#{primary_key}",
|
||||
:conditions => expand_id_conditions(id_or_conditions)) ? true : false
|
||||
end
|
||||
|
||||
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
|
||||
|
@ -2168,7 +2163,7 @@ module ActiveRecord #:nodoc:
|
|||
# default_scope :order => 'last_name, first_name'
|
||||
# end
|
||||
def default_scope(options = {})
|
||||
self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
|
||||
self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} }
|
||||
end
|
||||
|
||||
# Test whether the given method and optional key are scoped.
|
||||
|
@ -2228,12 +2223,12 @@ module ActiveRecord #:nodoc:
|
|||
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
|
||||
# { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
|
||||
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
|
||||
def sanitize_sql_for_conditions(condition)
|
||||
def sanitize_sql_for_conditions(condition, table_name = quoted_table_name)
|
||||
return nil if condition.blank?
|
||||
|
||||
case condition
|
||||
when Array; sanitize_sql_array(condition)
|
||||
when Hash; sanitize_sql_hash_for_conditions(condition)
|
||||
when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
|
||||
else condition
|
||||
end
|
||||
end
|
||||
|
@ -3034,11 +3029,11 @@ module ActiveRecord #:nodoc:
|
|||
def execute_callstack_for_multiparameter_attributes(callstack)
|
||||
errors = []
|
||||
callstack.each do |name, values|
|
||||
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
|
||||
if values.empty?
|
||||
send(name + "=", nil)
|
||||
else
|
||||
begin
|
||||
begin
|
||||
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
|
||||
if values.empty?
|
||||
send(name + "=", nil)
|
||||
else
|
||||
value = if Time == klass
|
||||
instantiate_time_object(name, values)
|
||||
elsif Date == klass
|
||||
|
@ -3052,9 +3047,9 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
send(name + "=", value)
|
||||
rescue => ex
|
||||
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
|
||||
end
|
||||
rescue => ex
|
||||
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
|
||||
end
|
||||
end
|
||||
unless errors.empty?
|
||||
|
@ -3080,7 +3075,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
def type_cast_attribute_value(multiparameter_name, value)
|
||||
multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
|
||||
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
|
||||
end
|
||||
|
||||
def find_parameter_position(multiparameter_name)
|
||||
|
|
|
@ -141,30 +141,22 @@ module ActiveRecord
|
|||
def construct_count_options_from_args(*args)
|
||||
options = {}
|
||||
column_name = :all
|
||||
|
||||
|
||||
# We need to handle
|
||||
# count()
|
||||
# count(:column_name=:all)
|
||||
# count(options={})
|
||||
# count(column_name=:all, options={})
|
||||
# selects specified by scopes
|
||||
case args.size
|
||||
when 0
|
||||
column_name = scope(:find)[:select] if scope(:find)
|
||||
when 1
|
||||
if args[0].is_a?(Hash)
|
||||
column_name = scope(:find)[:select] if scope(:find)
|
||||
options = args[0]
|
||||
else
|
||||
column_name = args[0]
|
||||
end
|
||||
args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
|
||||
when 2
|
||||
column_name, options = args
|
||||
else
|
||||
raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
|
||||
end
|
||||
|
||||
[column_name || :all, options]
|
||||
end if args.size > 0
|
||||
|
||||
[column_name, options]
|
||||
end
|
||||
|
||||
def construct_calculation_sql(operation, column_name, options) #:nodoc:
|
||||
|
|
|
@ -287,7 +287,13 @@ module ActiveRecord
|
|||
|
||||
# Escapes binary strings for bytea input to the database.
|
||||
def escape_bytea(value)
|
||||
if PGconn.respond_to?(:escape_bytea)
|
||||
if @connection.respond_to?(:escape_bytea)
|
||||
self.class.instance_eval do
|
||||
define_method(:escape_bytea) do |value|
|
||||
@connection.escape_bytea(value) if value
|
||||
end
|
||||
end
|
||||
elsif PGconn.respond_to?(:escape_bytea)
|
||||
self.class.instance_eval do
|
||||
define_method(:escape_bytea) do |value|
|
||||
PGconn.escape_bytea(value) if value
|
||||
|
@ -376,7 +382,13 @@ module ActiveRecord
|
|||
|
||||
# Quotes strings for use in SQL input in the postgres driver for better performance.
|
||||
def quote_string(s) #:nodoc:
|
||||
if PGconn.respond_to?(:escape)
|
||||
if @connection.respond_to?(:escape)
|
||||
self.class.instance_eval do
|
||||
define_method(:quote_string) do |s|
|
||||
@connection.escape(s)
|
||||
end
|
||||
end
|
||||
elsif PGconn.respond_to?(:escape)
|
||||
self.class.instance_eval do
|
||||
define_method(:quote_string) do |s|
|
||||
PGconn.escape(s)
|
||||
|
@ -392,9 +404,28 @@ module ActiveRecord
|
|||
quote_string(s)
|
||||
end
|
||||
|
||||
# Checks the following cases:
|
||||
#
|
||||
# - table_name
|
||||
# - "table.name"
|
||||
# - schema_name.table_name
|
||||
# - schema_name."table.name"
|
||||
# - "schema.name".table_name
|
||||
# - "schema.name"."table.name"
|
||||
def quote_table_name(name)
|
||||
schema, name_part = extract_pg_identifier_from_name(name.to_s)
|
||||
|
||||
unless name_part
|
||||
quote_column_name(schema)
|
||||
else
|
||||
table_name, name_part = extract_pg_identifier_from_name(name_part)
|
||||
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
|
||||
end
|
||||
end
|
||||
|
||||
# Quotes column names for use in SQL queries.
|
||||
def quote_column_name(name) #:nodoc:
|
||||
%("#{name}")
|
||||
PGconn.quote_ident(name.to_s)
|
||||
end
|
||||
|
||||
# Quote date/time values for use in SQL input. Includes microseconds
|
||||
|
@ -621,33 +652,36 @@ module ActiveRecord
|
|||
def indexes(table_name, name = nil)
|
||||
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
|
||||
result = query(<<-SQL, name)
|
||||
SELECT distinct i.relname, d.indisunique, a.attname
|
||||
FROM pg_class t, pg_class i, pg_index d, pg_attribute a
|
||||
SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
|
||||
FROM pg_class t, pg_class i, pg_index d
|
||||
WHERE i.relkind = 'i'
|
||||
AND d.indexrelid = i.oid
|
||||
AND d.indisprimary = 'f'
|
||||
AND t.oid = d.indrelid
|
||||
AND t.relname = '#{table_name}'
|
||||
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
|
||||
AND a.attrelid = t.oid
|
||||
AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
|
||||
OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
|
||||
OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
|
||||
OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
|
||||
OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
|
||||
ORDER BY i.relname
|
||||
SQL
|
||||
|
||||
current_index = nil
|
||||
|
||||
indexes = []
|
||||
|
||||
result.each do |row|
|
||||
if current_index != row[0]
|
||||
indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", [])
|
||||
current_index = row[0]
|
||||
end
|
||||
indexes = result.map do |row|
|
||||
index_name = row[0]
|
||||
unique = row[1] == 't'
|
||||
indkey = row[2].split(" ")
|
||||
oid = row[3]
|
||||
|
||||
columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist}
|
||||
SELECT a.attname, a.attnum
|
||||
FROM pg_attribute a
|
||||
WHERE a.attrelid = #{oid}
|
||||
AND a.attnum IN (#{indkey.join(",")})
|
||||
SQL
|
||||
|
||||
column_names = indkey.map {|attnum| columns[attnum] }
|
||||
IndexDefinition.new(table_name, index_name, unique, column_names)
|
||||
|
||||
indexes.last.columns << row[2]
|
||||
end
|
||||
|
||||
indexes
|
||||
|
@ -745,7 +779,7 @@ module ActiveRecord
|
|||
AND attr.attrelid = cons.conrelid
|
||||
AND attr.attnum = cons.conkey[1]
|
||||
AND cons.contype = 'p'
|
||||
AND dep.refobjid = '#{table}'::regclass
|
||||
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
||||
end_sql
|
||||
|
||||
if result.nil? or result.empty?
|
||||
|
@ -764,7 +798,7 @@ module ActiveRecord
|
|||
JOIN pg_attribute attr ON (t.oid = attrelid)
|
||||
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
||||
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
||||
WHERE t.oid = '#{table}'::regclass
|
||||
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
||||
AND cons.contype = 'p'
|
||||
AND def.adsrc ~* 'nextval'
|
||||
end_sql
|
||||
|
@ -839,7 +873,7 @@ module ActiveRecord
|
|||
|
||||
# Drops an index from a table.
|
||||
def remove_index(table_name, options = {})
|
||||
execute "DROP INDEX #{index_name(table_name, options)}"
|
||||
execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}"
|
||||
end
|
||||
|
||||
# Maps logical Rails types to PostgreSQL-specific data types.
|
||||
|
@ -1040,11 +1074,21 @@ module ActiveRecord
|
|||
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
|
||||
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
||||
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
||||
WHERE a.attrelid = '#{table_name}'::regclass
|
||||
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
|
||||
AND a.attnum > 0 AND NOT a.attisdropped
|
||||
ORDER BY a.attnum
|
||||
end_sql
|
||||
end
|
||||
|
||||
def extract_pg_identifier_from_name(name)
|
||||
match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
||||
|
||||
if match_data
|
||||
rest = name[match_data[0].length..-1]
|
||||
rest = rest[1..-1] if rest[0,1] == "."
|
||||
[match_data[1], (rest.length > 0 ? rest : nil)]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# encoding: binary
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
|
||||
module ActiveRecord
|
||||
|
@ -46,6 +47,7 @@ module ActiveRecord
|
|||
class SQLiteColumn < Column #:nodoc:
|
||||
class << self
|
||||
def string_to_binary(value)
|
||||
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
|
||||
value.gsub(/\0|\%/n) do |b|
|
||||
case b
|
||||
when "\0" then "%00"
|
||||
|
@ -55,6 +57,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def binary_to_string(value)
|
||||
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
|
||||
value.gsub(/%00|%25/n) do |b|
|
||||
case b
|
||||
when "%00" then "\0"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'erb'
|
||||
require 'yaml'
|
||||
require 'csv'
|
||||
require 'zlib'
|
||||
require 'active_support/dependencies'
|
||||
require 'active_support/test_case'
|
||||
|
||||
|
@ -433,6 +434,7 @@ end
|
|||
# Any fixture labeled "DEFAULTS" is safely ignored.
|
||||
|
||||
class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
||||
MAX_ID = 2 ** 31 - 1
|
||||
DEFAULT_FILTER_RE = /\.ya?ml$/
|
||||
|
||||
@@all_cached_fixtures = {}
|
||||
|
@ -524,11 +526,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
|||
cached_fixtures(connection, table_names)
|
||||
end
|
||||
|
||||
# Returns a consistent identifier for +label+. This will always
|
||||
# be a positive integer, and will always be the same for a given
|
||||
# label, assuming the same OS, platform, and version of Ruby.
|
||||
# Returns a consistent, platform-independent identifier for +label+.
|
||||
# Identifiers are positive integers less than 2^32.
|
||||
def self.identify(label)
|
||||
label.to_s.hash.abs
|
||||
Zlib.crc32(label.to_s) % MAX_ID
|
||||
end
|
||||
|
||||
attr_reader :table_name, :name
|
||||
|
|
|
@ -114,7 +114,7 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
delegate :scopes, :with_scope, :to => :proxy_scope
|
||||
delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
|
||||
|
||||
def initialize(proxy_scope, options, &block)
|
||||
options ||= {}
|
||||
|
@ -178,7 +178,7 @@ module ActiveRecord
|
|||
else
|
||||
with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
|
||||
method = :new if method == :build
|
||||
if current_scoped_methods_when_defined
|
||||
if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
|
||||
with_scope current_scoped_methods_when_defined do
|
||||
proxy_scope.send(method, *args, &block)
|
||||
end
|
||||
|
|
|
@ -78,11 +78,14 @@ HEADER
|
|||
begin
|
||||
tbl = StringIO.new
|
||||
|
||||
# first dump primary key column
|
||||
if @connection.respond_to?(:pk_and_sequence_for)
|
||||
pk, pk_seq = @connection.pk_and_sequence_for(table)
|
||||
elsif @connection.respond_to?(:primary_key)
|
||||
pk = @connection.primary_key(table)
|
||||
end
|
||||
pk ||= 'id'
|
||||
|
||||
|
||||
tbl.print " create_table #{table.inspect}"
|
||||
if columns.detect { |c| c.name == pk }
|
||||
if pk != 'id'
|
||||
|
@ -94,6 +97,7 @@ HEADER
|
|||
tbl.print ", :force => true"
|
||||
tbl.puts " do |t|"
|
||||
|
||||
# then dump all non-primary key columns
|
||||
column_specs = columns.map do |column|
|
||||
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
||||
next if column.name == pk
|
||||
|
|
|
@ -5,8 +5,9 @@ module ActiveRecord #:nodoc:
|
|||
class Serializer #:nodoc:
|
||||
attr_reader :options
|
||||
|
||||
def initialize(record, options = {})
|
||||
@record, @options = record, options.dup
|
||||
def initialize(record, options = nil)
|
||||
@record = record
|
||||
@options = options ? options.dup : {}
|
||||
end
|
||||
|
||||
# To replicate the behavior in ActiveRecord#attributes,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'active_support/json'
|
||||
require 'active_support/core_ext/module/model_naming'
|
||||
|
||||
module ActiveRecord #:nodoc:
|
||||
module Serialization
|
||||
def self.included(base)
|
||||
base.cattr_accessor :include_root_in_json, :instance_writer => false
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
# Returns a JSON string representing the model. Some configuration is
|
||||
|
@ -72,28 +74,16 @@ module ActiveRecord #:nodoc:
|
|||
# {"comments": [{"body": "Don't think too hard"}],
|
||||
# "title": "So I was thinking"}]}
|
||||
def to_json(options = {})
|
||||
if include_root_in_json
|
||||
"{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
|
||||
else
|
||||
JsonSerializer.new(self, options).to_s
|
||||
end
|
||||
hash = Serializer.new(self, options).serializable_record
|
||||
hash = { self.class.model_name.element => hash } if include_root_in_json
|
||||
ActiveSupport::JSON.encode(hash)
|
||||
end
|
||||
|
||||
def as_json(options = nil) self end #:nodoc:
|
||||
|
||||
def from_json(json)
|
||||
self.attributes = ActiveSupport::JSON.decode(json)
|
||||
self
|
||||
end
|
||||
|
||||
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
|
||||
def serialize
|
||||
serializable_record.to_json
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def json_class_name
|
||||
@json_class_name ||= name.demodulize.underscore.inspect
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -295,7 +295,7 @@ module ActiveRecord
|
|||
|
||||
def set_session(env, sid, session_data)
|
||||
Base.silence do
|
||||
record = env[SESSION_RECORD_KEY] ||= find_session(sid)
|
||||
record = get_session_model(env, sid)
|
||||
record.data = session_data
|
||||
return false unless record.save
|
||||
|
||||
|
@ -309,6 +309,14 @@ module ActiveRecord
|
|||
|
||||
return true
|
||||
end
|
||||
|
||||
def get_session_model(env, sid)
|
||||
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
|
||||
env[SESSION_RECORD_KEY] = find_session(sid)
|
||||
else
|
||||
env[SESSION_RECORD_KEY] ||= find_session(sid)
|
||||
end
|
||||
end
|
||||
|
||||
def find_session(id)
|
||||
@@session_class.find_by_session_id(id) ||
|
||||
|
|
|
@ -15,27 +15,57 @@ module ActiveRecord
|
|||
base.class_inheritable_accessor :record_timestamps, :instance_writer => false
|
||||
base.record_timestamps = true
|
||||
end
|
||||
|
||||
# Saves the record with the updated_at/on attributes set to the current time.
|
||||
# If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised.
|
||||
# If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# product.touch # updates updated_at
|
||||
# product.touch(:designed_at) # updates the designed_at attribute
|
||||
def touch(attribute = nil)
|
||||
current_time = current_time_from_proper_timezone
|
||||
|
||||
if attribute
|
||||
write_attribute(attribute, current_time)
|
||||
else
|
||||
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
||||
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
||||
end
|
||||
|
||||
save!
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def create_with_timestamps #:nodoc:
|
||||
if record_timestamps
|
||||
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
||||
write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
|
||||
write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
|
||||
current_time = current_time_from_proper_timezone
|
||||
|
||||
write_attribute('updated_at', t) if respond_to?(:updated_at) && updated_at.nil?
|
||||
write_attribute('updated_on', t) if respond_to?(:updated_on) && updated_on.nil?
|
||||
write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil?
|
||||
write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil?
|
||||
|
||||
write_attribute('updated_at', current_time) if respond_to?(:updated_at) && updated_at.nil?
|
||||
write_attribute('updated_on', current_time) if respond_to?(:updated_on) && updated_on.nil?
|
||||
end
|
||||
|
||||
create_without_timestamps
|
||||
end
|
||||
|
||||
def update_with_timestamps(*args) #:nodoc:
|
||||
if record_timestamps && (!partial_updates? || changed?)
|
||||
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
||||
write_attribute('updated_at', t) if respond_to?(:updated_at)
|
||||
write_attribute('updated_on', t) if respond_to?(:updated_on)
|
||||
current_time = current_time_from_proper_timezone
|
||||
|
||||
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
||||
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
||||
end
|
||||
|
||||
update_without_timestamps(*args)
|
||||
end
|
||||
|
||||
def current_time_from_proper_timezone
|
||||
self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@ module ActiveRecord
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 2
|
||||
TINY = 3
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ require 'models/tagging'
|
|||
require 'models/comment'
|
||||
require 'models/sponsor'
|
||||
require 'models/member'
|
||||
require 'models/essay'
|
||||
|
||||
class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :topics,
|
||||
|
@ -25,6 +26,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key
|
||||
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
|
||||
assert_equal companies(:first_firm).name, client.firm_with_primary_key.name
|
||||
end
|
||||
|
||||
def test_proxy_assignment
|
||||
account = Account.find(1)
|
||||
assert_nothing_raised { account.firm = account.firm }
|
||||
|
@ -47,6 +53,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_natural_assignment_with_primary_key
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Client.create("name" => "Primary key client")
|
||||
citibank.firm_with_primary_key = apple
|
||||
assert_equal apple.name, citibank.firm_name
|
||||
end
|
||||
|
||||
def test_no_unexpected_aliasing
|
||||
first_firm = companies(:first_firm)
|
||||
another_firm = companies(:another_firm)
|
||||
|
@ -69,6 +82,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal apple, citibank.firm
|
||||
end
|
||||
|
||||
def test_creating_the_belonging_object_with_primary_key
|
||||
client = Client.create(:name => "Primary key client")
|
||||
apple = client.create_firm_with_primary_key("name" => "Apple")
|
||||
assert_equal apple, client.firm_with_primary_key
|
||||
client.save
|
||||
client.reload
|
||||
assert_equal apple, client.firm_with_primary_key
|
||||
end
|
||||
|
||||
def test_building_the_belonging_object
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple = citibank.build_firm("name" => "Apple")
|
||||
|
@ -76,6 +98,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_building_the_belonging_object_with_primary_key
|
||||
client = Client.create(:name => "Primary key client")
|
||||
apple = client.build_firm_with_primary_key("name" => "Apple")
|
||||
client.save
|
||||
assert_equal apple.name, client.firm_name
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil
|
||||
client = Client.find(3)
|
||||
client.firm = nil
|
||||
|
@ -84,6 +113,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_nil client.client_of
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil_with_primary_key
|
||||
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
|
||||
client.firm_with_primary_key = nil
|
||||
client.save
|
||||
assert_nil client.firm_with_primary_key(true)
|
||||
assert_nil client.client_of
|
||||
end
|
||||
|
||||
def test_with_different_class_name
|
||||
assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
|
||||
assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm"
|
||||
|
@ -110,6 +147,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_counter
|
||||
debate = Topic.create("title" => "debate")
|
||||
assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
|
||||
|
||||
trash = debate.replies_with_primary_key.create("title" => "blah!", "content" => "world around!")
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
|
||||
|
||||
trash.destroy
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_with_assigning_nil
|
||||
p = Post.find(1)
|
||||
c = Comment.find(1)
|
||||
|
@ -122,6 +170,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 1, Post.find(p.id).comments.size
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_counter_with_assigning_nil
|
||||
debate = Topic.create("title" => "debate")
|
||||
reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate")
|
||||
|
||||
assert_equal debate.title, reply.parent_title
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count")
|
||||
|
||||
reply.topic_with_primary_key = nil
|
||||
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count")
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_with_reassigning
|
||||
t1 = Topic.create("title" => "t1")
|
||||
t2 = Topic.create("title" => "t2")
|
||||
|
@ -219,6 +279,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal firm, final_cut.firm(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_child_saved_with_primary_key
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
firm = Firm.find(1)
|
||||
final_cut.firm_with_primary_key = firm
|
||||
assert final_cut.new_record?
|
||||
assert final_cut.save
|
||||
assert !final_cut.new_record?
|
||||
assert !firm.new_record?
|
||||
assert_equal firm, final_cut.firm_with_primary_key
|
||||
assert_equal firm, final_cut.firm_with_primary_key(true)
|
||||
end
|
||||
|
||||
def test_new_record_with_foreign_key_but_no_object
|
||||
c = Client.new("firm_id" => 1)
|
||||
assert_equal Firm.find(:first), c.firm_with_basic_id
|
||||
|
@ -297,26 +369,52 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
member = Member.create
|
||||
sponsor.sponsorable = member
|
||||
assert_equal "Member", sponsor.sponsorable_type
|
||||
|
||||
|
||||
# should update when assigning a new record
|
||||
sponsor = Sponsor.new
|
||||
member = Member.new
|
||||
sponsor.sponsorable = member
|
||||
assert_equal "Member", sponsor.sponsorable_type
|
||||
end
|
||||
|
||||
|
||||
def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating
|
||||
# should update when assigning a saved record
|
||||
essay = Essay.new
|
||||
writer = Author.create(:name => "David")
|
||||
essay.writer = writer
|
||||
assert_equal "Author", essay.writer_type
|
||||
|
||||
# should update when assigning a new record
|
||||
essay = Essay.new
|
||||
writer = Author.new
|
||||
essay.writer = writer
|
||||
assert_equal "Author", essay.writer_type
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records
|
||||
sponsor = Sponsor.new
|
||||
saved_member = Member.create
|
||||
new_member = Member.new
|
||||
|
||||
|
||||
sponsor.sponsorable = saved_member
|
||||
assert_equal saved_member.id, sponsor.sponsorable_id
|
||||
|
||||
|
||||
sponsor.sponsorable = new_member
|
||||
assert_equal nil, sponsor.sponsorable_id
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records
|
||||
essay = Essay.new
|
||||
saved_writer = Author.create(:name => "David")
|
||||
new_writer = Author.new
|
||||
|
||||
essay.writer = saved_writer
|
||||
assert_equal saved_writer.name, essay.writer_id
|
||||
|
||||
essay.writer = new_writer
|
||||
assert_equal nil, essay.writer_id
|
||||
end
|
||||
|
||||
def test_belongs_to_proxy_should_not_respond_to_private_methods
|
||||
assert_raise(NoMethodError) { companies(:first_firm).private_method }
|
||||
assert_raise(NoMethodError) { companies(:second_client).firm.private_method }
|
||||
|
|
|
@ -223,6 +223,18 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_conditions_hash
|
||||
comments = []
|
||||
assert_nothing_raised do
|
||||
comments = Comment.find(:all, :include => :post, :conditions => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id')
|
||||
end
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [5,6,7], comments.collect { |c| c.id }
|
||||
assert_no_queries do
|
||||
comments.first.post
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
|
||||
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
|
||||
assert_nothing_raised do
|
||||
|
|
|
@ -719,6 +719,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert Client.find(:all, :conditions => "firm_id=#{firm.id}").empty?
|
||||
end
|
||||
|
||||
def test_dependence_for_associations_with_hash_condition
|
||||
david = authors(:david)
|
||||
post = posts(:thinking).id
|
||||
assert_difference('Post.count', -1) { assert david.destroy }
|
||||
end
|
||||
|
||||
def test_destroy_dependent_when_deleted_from_association
|
||||
firm = Firm.find(:first)
|
||||
assert_equal 2, firm.clients.size
|
||||
|
|
|
@ -43,7 +43,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|||
@member.reload
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_set_record_to_nil_should_delete_association
|
||||
@member.club = nil
|
||||
@member.reload
|
||||
assert_equal nil, @member.current_membership
|
||||
assert_nil @member.club
|
||||
end
|
||||
|
||||
def test_has_one_through_polymorphic
|
||||
assert_equal clubs(:moustache_club), @member.sponsor_club
|
||||
end
|
||||
|
|
|
@ -29,6 +29,11 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
|
|||
assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_applies_aliases_tables_on_association_conditions
|
||||
result = Author.find(:all, :joins => [:thinking_posts, :welcome_posts])
|
||||
assert_equal authors(:david), result.first
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_unpacks_nested_joins
|
||||
sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
|
||||
assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
|
||||
|
|
|
@ -38,6 +38,17 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
|
||||
def test_should_save_parent_but_not_invalid_child
|
||||
firm = Firm.new(:name => 'GlobalMegaCorp')
|
||||
assert firm.valid?
|
||||
|
||||
firm.build_account_using_primary_key
|
||||
assert !firm.build_account_using_primary_key.valid?
|
||||
|
||||
assert firm.save
|
||||
assert firm.account_using_primary_key.new_record?
|
||||
end
|
||||
|
||||
def test_save_fails_for_invalid_has_one
|
||||
firm = Firm.find(:first)
|
||||
assert firm.valid?
|
||||
|
@ -126,6 +137,17 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
|
|||
end
|
||||
|
||||
class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
|
||||
def test_should_save_parent_but_not_invalid_child
|
||||
client = Client.new(:name => 'Joe (the Plumber)')
|
||||
assert client.valid?
|
||||
|
||||
client.build_firm
|
||||
assert !client.firm.valid?
|
||||
|
||||
assert client.save
|
||||
assert client.firm.new_record?
|
||||
end
|
||||
|
||||
def test_save_fails_for_invalid_belongs_to
|
||||
assert log = AuditLog.create(:developer_id => 0, :message => "")
|
||||
|
||||
|
|
|
@ -1756,7 +1756,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_scoped_find_with_group_and_having
|
||||
developers = Developer.with_scope(:find => { :group => 'salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
|
||||
developers = Developer.with_scope(:find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
|
||||
Developer.find(:all)
|
||||
end
|
||||
assert_equal 3, developers.size
|
||||
|
@ -2014,7 +2014,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
|
||||
def test_inspect_instance
|
||||
topic = topics(:first)
|
||||
assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, type: nil>), topic.inspect
|
||||
assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil>), topic.inspect
|
||||
end
|
||||
|
||||
def test_inspect_new_instance
|
||||
|
|
|
@ -2,6 +2,9 @@ require "cases/helper"
|
|||
require 'models/company'
|
||||
require 'models/topic'
|
||||
require 'models/edge'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
require 'models/toy'
|
||||
|
||||
Company.has_many :accounts
|
||||
|
||||
|
@ -10,7 +13,7 @@ class NumericData < ActiveRecord::Base
|
|||
end
|
||||
|
||||
class CalculationsTest < ActiveRecord::TestCase
|
||||
fixtures :companies, :accounts, :topics
|
||||
fixtures :companies, :accounts, :topics, :owners, :pets, :toys
|
||||
|
||||
def test_should_sum_field
|
||||
assert_equal 318, Account.sum(:credit_limit)
|
||||
|
@ -264,19 +267,6 @@ 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
|
||||
|
@ -297,6 +287,10 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
assert_raise(ArgumentError) { Account.count(1, 2, 3) }
|
||||
end
|
||||
|
||||
def test_count_with_scoped_has_many_through_association
|
||||
assert_equal 1, owners(:blackbeard).toys.with_name('Bone').count
|
||||
end
|
||||
|
||||
def test_should_sum_expression
|
||||
assert_equal '636', Account.sum("2 * credit_limit")
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ class CopyTableTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_copy_table(from = 'companies', to = 'companies2', options = {})
|
||||
def test_copy_table(from = 'customers', to = 'customers2', options = {})
|
||||
assert_nothing_raised {copy_table(from, to, options)}
|
||||
assert_equal row_count(from), row_count(to)
|
||||
|
||||
|
@ -24,11 +24,11 @@ class CopyTableTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_copy_table_renaming_column
|
||||
test_copy_table('companies', 'companies2',
|
||||
:rename => {'client_of' => 'fan_of'}) do |from, to, options|
|
||||
expected = column_values(from, 'client_of')
|
||||
test_copy_table('customers', 'customers2',
|
||||
:rename => {'name' => 'person_name'}) do |from, to, options|
|
||||
expected = column_values(from, 'name')
|
||||
assert expected.any?, 'only nils in resultset; real values are needed'
|
||||
assert_equal expected, column_values(to, 'fan_of')
|
||||
assert_equal expected, column_values(to, 'person_name')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -119,6 +119,12 @@ class FinderTest < ActiveRecord::TestCase
|
|||
Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
|
||||
end
|
||||
|
||||
def test_exists_with_scoped_include
|
||||
Developer.with_scope(:find => { :include => :projects, :order => "projects.name" }) do
|
||||
assert Developer.exists?
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_by_array_of_one_id
|
||||
assert_kind_of(Array, Topic.find([ 1 ]))
|
||||
assert_equal(1, Topic.find([ 1 ]).length)
|
||||
|
|
|
@ -518,6 +518,11 @@ class FoxyFixturesTest < ActiveRecord::TestCase
|
|||
assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo))
|
||||
end
|
||||
|
||||
def test_identifies_consistently
|
||||
assert_equal 1281023246, Fixtures.identify(:ruby)
|
||||
assert_equal 2140105598, Fixtures.identify(:sapphire_2)
|
||||
end
|
||||
|
||||
TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
|
||||
|
||||
def test_populates_timestamp_columns
|
||||
|
|
|
@ -5,8 +5,7 @@ require 'config'
|
|||
|
||||
require 'rubygems'
|
||||
require 'test/unit'
|
||||
gem 'mocha', '>= 0.9.5'
|
||||
require 'mocha'
|
||||
require 'stringio'
|
||||
|
||||
require 'active_record'
|
||||
require 'active_record/test_case'
|
||||
|
|
|
@ -26,19 +26,19 @@ class JsonSerializationTest < ActiveRecord::TestCase
|
|||
NamespacedContact.include_root_in_json = true
|
||||
@contact = NamespacedContact.new :name => 'whatever'
|
||||
json = @contact.to_json
|
||||
assert_match %r{^\{"namespaced_contact": \{}, json
|
||||
assert_match %r{^\{"namespaced_contact":\{}, json
|
||||
end
|
||||
|
||||
def test_should_include_root_in_json
|
||||
Contact.include_root_in_json = true
|
||||
json = @contact.to_json
|
||||
|
||||
assert_match %r{^\{"contact": \{}, json
|
||||
assert_match %r{"name": "Konata Izumi"}, json
|
||||
assert_match %r{"age": 16}, json
|
||||
assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome": true}, json
|
||||
assert_match %r{"preferences": \{"shows": "anime"\}}, json
|
||||
assert_match %r{^\{"contact":\{}, json
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
ensure
|
||||
Contact.include_root_in_json = false
|
||||
end
|
||||
|
@ -46,31 +46,31 @@ class JsonSerializationTest < ActiveRecord::TestCase
|
|||
def test_should_encode_all_encodable_attributes
|
||||
json = @contact.to_json
|
||||
|
||||
assert_match %r{"name": "Konata Izumi"}, json
|
||||
assert_match %r{"age": 16}, json
|
||||
assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome": true}, json
|
||||
assert_match %r{"preferences": \{"shows": "anime"\}}, json
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
end
|
||||
|
||||
def test_should_allow_attribute_filtering_with_only
|
||||
json = @contact.to_json(:only => [:name, :age])
|
||||
|
||||
assert_match %r{"name": "Konata Izumi"}, json
|
||||
assert_match %r{"age": 16}, json
|
||||
assert_no_match %r{"awesome": true}, json
|
||||
assert !json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_no_match %r{"preferences": \{"shows": "anime"\}}, json
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert_no_match %r{"awesome":true}, json
|
||||
assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_no_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
end
|
||||
|
||||
def test_should_allow_attribute_filtering_with_except
|
||||
json = @contact.to_json(:except => [:name, :age])
|
||||
|
||||
assert_no_match %r{"name": "Konata Izumi"}, json
|
||||
assert_no_match %r{"age": 16}, json
|
||||
assert_match %r{"awesome": true}, json
|
||||
assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"preferences": \{"shows": "anime"\}}, json
|
||||
assert_no_match %r{"name":"Konata Izumi"}, json
|
||||
assert_no_match %r{"age":16}, json
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
end
|
||||
|
||||
def test_methods_are_called_on_object
|
||||
|
@ -79,12 +79,12 @@ class JsonSerializationTest < ActiveRecord::TestCase
|
|||
def @contact.favorite_quote; "Constraints are liberating"; end
|
||||
|
||||
# Single method.
|
||||
assert_match %r{"label": "Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
|
||||
assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
|
||||
|
||||
# Both methods.
|
||||
methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
|
||||
assert_match %r{"label": "Has cheezburger"}, methods_json
|
||||
assert_match %r{"favorite_quote": "Constraints are liberating"}, methods_json
|
||||
assert_match %r{"label":"Has cheezburger"}, methods_json
|
||||
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -99,42 +99,42 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
|
|||
def test_includes_uses_association_name
|
||||
json = @david.to_json(:include => :posts)
|
||||
|
||||
assert_match %r{"posts": \[}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"id": 1}, json
|
||||
assert_match %r{"name": "David"}, json
|
||||
assert_match %r{"id":1}, json
|
||||
assert_match %r{"name":"David"}, json
|
||||
|
||||
assert_match %r{"author_id": 1}, json
|
||||
assert_match %r{"title": "Welcome to the weblog"}, json
|
||||
assert_match %r{"body": "Such a lovely day"}, json
|
||||
assert_match %r{"author_id":1}, json
|
||||
assert_match %r{"title":"Welcome to the weblog"}, json
|
||||
assert_match %r{"body":"Such a lovely day"}, json
|
||||
|
||||
assert_match %r{"title": "So I was thinking"}, json
|
||||
assert_match %r{"body": "Like I hopefully always am"}, json
|
||||
assert_match %r{"title":"So I was thinking"}, json
|
||||
assert_match %r{"body":"Like I hopefully always am"}, json
|
||||
end
|
||||
|
||||
def test_includes_uses_association_name_and_applies_attribute_filters
|
||||
json = @david.to_json(:include => { :posts => { :only => :title } })
|
||||
|
||||
assert_match %r{"name": "David"}, json
|
||||
assert_match %r{"posts": \[}, json
|
||||
assert_match %r{"name":"David"}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"title": "Welcome to the weblog"}, json
|
||||
assert_no_match %r{"body": "Such a lovely day"}, json
|
||||
assert_match %r{"title":"Welcome to the weblog"}, json
|
||||
assert_no_match %r{"body":"Such a lovely day"}, json
|
||||
|
||||
assert_match %r{"title": "So I was thinking"}, json
|
||||
assert_no_match %r{"body": "Like I hopefully always am"}, json
|
||||
assert_match %r{"title":"So I was thinking"}, json
|
||||
assert_no_match %r{"body":"Like I hopefully always am"}, json
|
||||
end
|
||||
|
||||
def test_includes_fetches_second_level_associations
|
||||
json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } })
|
||||
|
||||
assert_match %r{"name": "David"}, json
|
||||
assert_match %r{"posts": \[}, json
|
||||
assert_match %r{"name":"David"}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"comments": \[}, json
|
||||
assert_match %r{\{"body": "Thank you again for the welcome"\}}, json
|
||||
assert_match %r{\{"body": "Don't think too hard"\}}, json
|
||||
assert_no_match %r{"post_id": }, json
|
||||
assert_match %r{"comments":\[}, json
|
||||
assert_match %r{\{"body":"Thank you again for the welcome"\}}, json
|
||||
assert_match %r{\{"body":"Don't think too hard"\}}, json
|
||||
assert_no_match %r{"post_id":}, json
|
||||
end
|
||||
|
||||
def test_includes_fetches_nth_level_associations
|
||||
|
@ -151,11 +151,11 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
|
|||
}
|
||||
})
|
||||
|
||||
assert_match %r{"name": "David"}, json
|
||||
assert_match %r{"posts": \[}, json
|
||||
assert_match %r{"name":"David"}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"taggings": \[}, json
|
||||
assert_match %r{"tag": \{"name": "General"\}}, json
|
||||
assert_match %r{"taggings":\[}, json
|
||||
assert_match %r{"tag":\{"name":"General"\}}, json
|
||||
end
|
||||
|
||||
def test_should_not_call_methods_on_associations_that_dont_respond
|
||||
|
@ -163,33 +163,33 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
|
|||
json = @david.to_json(:include => :posts, :methods => :favorite_quote)
|
||||
|
||||
assert !@david.posts.first.respond_to?(:favorite_quote)
|
||||
assert_match %r{"favorite_quote": "Constraints are liberating"}, json
|
||||
assert_equal %r{"favorite_quote": }.match(json).size, 1
|
||||
assert_match %r{"favorite_quote":"Constraints are liberating"}, json
|
||||
assert_equal %r{"favorite_quote":}.match(json).size, 1
|
||||
end
|
||||
|
||||
def test_should_allow_only_option_for_list_of_authors
|
||||
authors = [@david, @mary]
|
||||
|
||||
assert_equal %([{"name": "David"}, {"name": "Mary"}]), authors.to_json(:only => :name)
|
||||
assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, :only => :name)
|
||||
end
|
||||
|
||||
def test_should_allow_except_option_for_list_of_authors
|
||||
authors = [@david, @mary]
|
||||
|
||||
assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id])
|
||||
assert_equal %([{"id":1},{"id":2}]), ActiveSupport::JSON.encode(authors, :except => [:name, :author_address_id, :author_address_extra_id])
|
||||
end
|
||||
|
||||
def test_should_allow_includes_for_list_of_authors
|
||||
authors = [@david, @mary]
|
||||
json = authors.to_json(
|
||||
json = ActiveSupport::JSON.encode(authors,
|
||||
:only => :name,
|
||||
:include => {
|
||||
:posts => { :only => :id }
|
||||
}
|
||||
)
|
||||
|
||||
['"name": "David"', '"posts": [', '{"id": 1}', '{"id": 2}', '{"id": 4}',
|
||||
'{"id": 5}', '{"id": 6}', '"name": "Mary"', '"posts": [{"id": 7}]'].each do |fragment|
|
||||
['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}',
|
||||
'{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[{"id":7}]'].each do |fragment|
|
||||
assert json.include?(fragment), json
|
||||
end
|
||||
end
|
||||
|
@ -200,6 +200,6 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
|
|||
2 => @mary
|
||||
}
|
||||
|
||||
assert_equal %({"1": {"name": "David"}}), authors_hash.to_json(:only => [1, :name])
|
||||
assert_equal %({"1":{"name":"David"}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -591,6 +591,16 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
|||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_string
|
||||
assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
|
||||
assert_equal nil, DeveloperCalledDavid.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_hash
|
||||
assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
|
||||
assert_equal 'Jamis', DeveloperCalledJamis.create!.name
|
||||
end
|
||||
|
||||
def test_default_scoping_with_threads
|
||||
scope = [{ :create => {}, :find => { :order => 'salary DESC' } }]
|
||||
|
||||
|
@ -628,9 +638,9 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
|||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_named_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.salary }
|
||||
def test_named_scope_overwrites_default
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name }
|
||||
received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
|
|
|
@ -21,25 +21,25 @@ class ReflectionTest < ActiveRecord::TestCase
|
|||
|
||||
def test_read_attribute_names
|
||||
assert_equal(
|
||||
%w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id type ).sort,
|
||||
%w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort,
|
||||
@first.attribute_names
|
||||
)
|
||||
end
|
||||
|
||||
def test_columns
|
||||
assert_equal 12, Topic.columns.length
|
||||
assert_equal 13, Topic.columns.length
|
||||
end
|
||||
|
||||
def test_columns_are_returned_in_the_order_they_were_declared
|
||||
column_names = Topic.columns.map { |column| column.name }
|
||||
assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id type), column_names
|
||||
assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names
|
||||
end
|
||||
|
||||
def test_content_columns
|
||||
content_columns = Topic.content_columns
|
||||
content_column_names = content_columns.map {|column| column.name}
|
||||
assert_equal 8, content_columns.length
|
||||
assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved).sort, content_column_names.sort
|
||||
assert_equal 9, content_columns.length
|
||||
assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort
|
||||
end
|
||||
|
||||
def test_column_string_type_and_limit
|
||||
|
|
|
@ -22,6 +22,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|||
assert_no_match %r{create_table "sqlite_sequence"}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_camelcase_table_name
|
||||
output = standard_dump
|
||||
assert_match %r{create_table "CamelCase"}, output
|
||||
end
|
||||
|
||||
def assert_line_up(lines, pattern, required = false)
|
||||
return assert(true) if lines.empty?
|
||||
matches = lines.map { |line| line.match(pattern) }
|
||||
|
@ -147,19 +152,24 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_schema_dumps_index_columns_in_right_order
|
||||
index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
|
||||
assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition
|
||||
end
|
||||
|
||||
def test_schema_dump_should_honor_nonstandard_primary_keys
|
||||
output = standard_dump
|
||||
match = output.match(%r{create_table "movies"(.*)do})
|
||||
assert_not_nil(match, "nonstandardpk table not found")
|
||||
assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
def test_schema_dump_should_not_add_default_value_for_mysql_text_field
|
||||
output = standard_dump
|
||||
assert_match %r{t.text\s+"body",\s+:null => false$}, output
|
||||
end
|
||||
|
||||
def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
|
||||
output = standard_dump
|
||||
match = output.match(%r{create_table "movies"(.*)do})
|
||||
assert_not_nil(match, "nonstandardpk table not found")
|
||||
assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
|
||||
output = standard_dump
|
||||
assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
|
||||
|
|
|
@ -6,6 +6,7 @@ class SchemaTest < ActiveRecord::TestCase
|
|||
SCHEMA_NAME = 'test_schema'
|
||||
SCHEMA2_NAME = 'test_schema2'
|
||||
TABLE_NAME = 'things'
|
||||
CAPITALIZED_TABLE_NAME = 'Things'
|
||||
INDEX_A_NAME = 'a_index_things_on_name'
|
||||
INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
|
||||
INDEX_A_COLUMN = 'name'
|
||||
|
@ -18,9 +19,27 @@ class SchemaTest < ActiveRecord::TestCase
|
|||
'moment timestamp without time zone default now()'
|
||||
]
|
||||
|
||||
class Thing1 < ActiveRecord::Base
|
||||
set_table_name "test_schema.things"
|
||||
end
|
||||
|
||||
class Thing2 < ActiveRecord::Base
|
||||
set_table_name "test_schema2.things"
|
||||
end
|
||||
|
||||
class Thing3 < ActiveRecord::Base
|
||||
set_table_name 'test_schema."things.table"'
|
||||
end
|
||||
|
||||
class Thing4 < ActiveRecord::Base
|
||||
set_table_name 'test_schema."Things"'
|
||||
end
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});"
|
||||
@connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});"
|
||||
|
@ -39,6 +58,12 @@ class SchemaTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_with_schema_prefixed_capitalized_table_name
|
||||
assert_nothing_raised do
|
||||
assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_schema_search_path
|
||||
assert_nothing_raised do
|
||||
with_schema_search_path(SCHEMA_NAME) do
|
||||
|
@ -47,6 +72,47 @@ class SchemaTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def test_proper_encoding_of_table_name
|
||||
assert_equal '"table_name"', @connection.quote_table_name('table_name')
|
||||
assert_equal '"table.name"', @connection.quote_table_name('"table.name"')
|
||||
assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name')
|
||||
assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"')
|
||||
assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name')
|
||||
assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"')
|
||||
end
|
||||
|
||||
def test_classes_with_qualified_schema_name
|
||||
assert_equal 0, Thing1.count
|
||||
assert_equal 0, Thing2.count
|
||||
assert_equal 0, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 0, Thing2.count
|
||||
assert_equal 0, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 1, Thing2.count
|
||||
assert_equal 0, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 1, Thing2.count
|
||||
assert_equal 1, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 1, Thing2.count
|
||||
assert_equal 1, Thing3.count
|
||||
assert_equal 1, Thing4.count
|
||||
end
|
||||
|
||||
def test_raise_on_unquoted_schema_name
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
with_schema_search_path '$user,public'
|
||||
|
@ -69,6 +135,16 @@ class SchemaTest < ActiveRecord::TestCase
|
|||
do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2)
|
||||
end
|
||||
|
||||
def test_with_uppercase_index_name
|
||||
ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
|
||||
assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "#{SCHEMA_NAME}.things_Index"}
|
||||
|
||||
ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
|
||||
ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME
|
||||
assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "things_Index"}
|
||||
ActiveRecord::Base.connection.schema_search_path = "public"
|
||||
end
|
||||
|
||||
private
|
||||
def columns(table_name)
|
||||
@connection.send(:column_definitions, table_name).map do |name, type, default|
|
||||
|
|
75
vendor/rails/activerecord/test/cases/timestamp_test.rb
vendored
Normal file
75
vendor/rails/activerecord/test/cases/timestamp_test.rb
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
require 'cases/helper'
|
||||
require 'models/developer'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
|
||||
class TimestampTest < ActiveRecord::TestCase
|
||||
fixtures :developers, :owners, :pets
|
||||
|
||||
def setup
|
||||
@developer = Developer.first
|
||||
@previously_updated_at = @developer.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_changed_record_updates_its_timestamp
|
||||
@developer.name = "Jack Bauer"
|
||||
@developer.save!
|
||||
|
||||
assert @previously_updated_at != @developer.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_unchanged_record_doesnt_update_its_timestamp
|
||||
@developer.save!
|
||||
|
||||
assert @previously_updated_at == @developer.updated_at
|
||||
end
|
||||
|
||||
def test_touching_a_record_updates_its_timestamp
|
||||
@developer.touch
|
||||
|
||||
assert @previously_updated_at != @developer.updated_at
|
||||
end
|
||||
|
||||
def test_touching_a_different_attribute
|
||||
previously_created_at = @developer.created_at
|
||||
@developer.touch(:created_at)
|
||||
|
||||
assert previously_created_at != @developer.created_at
|
||||
end
|
||||
|
||||
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_updated_at = owner.updated_at
|
||||
|
||||
pet.name = "Fluffy the Third"
|
||||
pet.save
|
||||
|
||||
assert previously_owner_updated_at != pet.owner.updated_at
|
||||
end
|
||||
|
||||
def test_destroying_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_updated_at = owner.updated_at
|
||||
|
||||
pet.destroy
|
||||
|
||||
assert previously_owner_updated_at != pet.owner.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
|
||||
Pet.belongs_to :owner, :touch => :happy_at
|
||||
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_happy_at = owner.happy_at
|
||||
|
||||
pet.name = "Fluffy the Third"
|
||||
pet.save
|
||||
|
||||
assert previously_owner_happy_at != pet.owner.happy_at
|
||||
ensure
|
||||
Pet.belongs_to :owner, :touch => true
|
||||
end
|
||||
end
|
|
@ -25,6 +25,8 @@ class Author < ActiveRecord::Base
|
|||
has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
|
||||
has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post
|
||||
|
||||
has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' }, :dependent => :delete_all
|
||||
has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' }
|
||||
|
||||
has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC'
|
||||
has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1
|
||||
|
@ -85,6 +87,8 @@ class Author < ActiveRecord::Base
|
|||
has_many :tags, :through => :posts # through has_many :through
|
||||
has_many :post_categories, :through => :posts, :source => :categories
|
||||
|
||||
has_one :essay, :primary_key => :name, :as => :writer
|
||||
|
||||
belongs_to :author_address, :dependent => :destroy
|
||||
belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
|
||||
|
||||
|
|
14
vendor/rails/activerecord/test/models/company.rb
vendored
14
vendor/rails/activerecord/test/models/company.rb
vendored
|
@ -78,19 +78,13 @@ class DependentFirm < Company
|
|||
has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :nullify
|
||||
end
|
||||
|
||||
class ExclusivelyDependentFirm < Company
|
||||
has_one :account, :foreign_key => "firm_id", :dependent => :delete
|
||||
has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
|
||||
has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
|
||||
has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'}
|
||||
end
|
||||
|
||||
class Client < Company
|
||||
belongs_to :firm, :foreign_key => "client_of"
|
||||
belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id"
|
||||
belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id"
|
||||
belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
|
||||
belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1]
|
||||
belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name"
|
||||
belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true
|
||||
|
||||
# Record destruction so we can test whether firm.clients.clear has
|
||||
|
@ -125,6 +119,12 @@ class Client < Company
|
|||
end
|
||||
end
|
||||
|
||||
class ExclusivelyDependentFirm < Company
|
||||
has_one :account, :foreign_key => "firm_id", :dependent => :delete
|
||||
has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
|
||||
has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
|
||||
has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'}
|
||||
end
|
||||
|
||||
class SpecialClient < Client
|
||||
end
|
||||
|
|
|
@ -89,3 +89,13 @@ class DeveloperOrderedBySalary < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DeveloperCalledDavid < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => "name = 'David'"
|
||||
end
|
||||
|
||||
class DeveloperCalledJamis < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => { :name => 'Jamis' }
|
||||
end
|
||||
|
|
3
vendor/rails/activerecord/test/models/essay.rb
vendored
Normal file
3
vendor/rails/activerecord/test/models/essay.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
class Essay < ActiveRecord::Base
|
||||
belongs_to :writer, :primary_key => :name, :polymorphic => true
|
||||
end
|
2
vendor/rails/activerecord/test/models/pet.rb
vendored
2
vendor/rails/activerecord/test/models/pet.rb
vendored
|
@ -1,5 +1,5 @@
|
|||
class Pet < ActiveRecord::Base
|
||||
set_primary_key :pet_id
|
||||
belongs_to :owner
|
||||
belongs_to :owner, :touch => true
|
||||
has_many :toys
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ class Project < ActiveRecord::Base
|
|||
:after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},
|
||||
:before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
|
||||
:after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
|
||||
has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary"
|
||||
has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary"
|
||||
|
||||
attr_accessor :developers_log
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@ class Reply < Topic
|
|||
named_scope :base
|
||||
|
||||
belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
|
||||
belongs_to :topic_with_primary_key, :class_name => "Topic", :primary_key => "title", :foreign_key => "parent_title", :counter_cache => "replies_count"
|
||||
has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id"
|
||||
|
||||
validate :errors_on_empty_content
|
||||
validate_on_create :title_is_wrong_create
|
||||
|
||||
attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read
|
||||
attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read, :parent_title
|
||||
|
||||
def validate
|
||||
errors.add("title", "Empty") unless attribute_present? "title"
|
||||
|
|
|
@ -39,6 +39,7 @@ class Topic < ActiveRecord::Base
|
|||
named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }}
|
||||
|
||||
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
|
||||
has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title"
|
||||
serialize :content
|
||||
|
||||
before_create :default_written_on
|
||||
|
|
2
vendor/rails/activerecord/test/models/toy.rb
vendored
2
vendor/rails/activerecord/test/models/toy.rb
vendored
|
@ -1,4 +1,6 @@
|
|||
class Toy < ActiveRecord::Base
|
||||
set_primary_key :toy_id
|
||||
belongs_to :pet
|
||||
|
||||
named_scope :with_name, lambda { |name| {:conditions => {:name => name}} }
|
||||
end
|
||||
|
|
15
vendor/rails/activerecord/test/schema/schema.rb
vendored
15
vendor/rails/activerecord/test/schema/schema.rb
vendored
|
@ -68,6 +68,10 @@ ActiveRecord::Schema.define do
|
|||
t.boolean :value
|
||||
end
|
||||
|
||||
create_table "CamelCase", :force => true do |t|
|
||||
t.string :name
|
||||
end
|
||||
|
||||
create_table :categories, :force => true do |t|
|
||||
t.string :name, :null => false
|
||||
t.string :type
|
||||
|
@ -114,6 +118,8 @@ ActiveRecord::Schema.define do
|
|||
t.integer :rating, :default => 1
|
||||
end
|
||||
|
||||
add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index"
|
||||
|
||||
create_table :computers, :force => true do |t|
|
||||
t.integer :developer, :null => false
|
||||
t.integer :extendedWarranty, :null => false
|
||||
|
@ -155,6 +161,12 @@ ActiveRecord::Schema.define do
|
|||
t.integer :course_id, :null => false
|
||||
end
|
||||
|
||||
create_table :essays, :force => true do |t|
|
||||
t.string :name
|
||||
t.string :writer_id
|
||||
t.string :writer_type
|
||||
end
|
||||
|
||||
create_table :events, :force => true do |t|
|
||||
t.string :title, :limit => 5
|
||||
end
|
||||
|
@ -281,6 +293,8 @@ ActiveRecord::Schema.define do
|
|||
|
||||
create_table :owners, :primary_key => :owner_id ,:force => true do |t|
|
||||
t.string :name
|
||||
t.column :updated_at, :datetime
|
||||
t.column :happy_at, :datetime
|
||||
end
|
||||
|
||||
|
||||
|
@ -410,6 +424,7 @@ ActiveRecord::Schema.define do
|
|||
t.boolean :approved, :default => true
|
||||
t.integer :replies_count, :default => 0
|
||||
t.integer :parent_id
|
||||
t.string :parent_title
|
||||
t.string :type
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue