Rails 2.1.1

Among other things, a security fix.
This commit is contained in:
Jacques Distler 2008-09-07 00:54:05 -05:00
parent d2c4c8737c
commit d4f97345db
354 changed files with 21027 additions and 3072 deletions

View file

@ -13,8 +13,8 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
end
def test_create_database_with_encoding
assert_equal "CREATE DATABASE matt ENCODING = 'utf8'", create_database(:matt)
assert_equal "CREATE DATABASE aimonetti ENCODING = 'latin1'", create_database(:aimonetti, :encoding => :latin1)
assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt)
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
end
private

View file

@ -118,7 +118,7 @@ class AdapterTest < ActiveRecord::TestCase
sql_inject = "1, 7 procedure help()"
if current_adapter?(:MysqlAdapter)
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7)
else
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)

View file

@ -409,4 +409,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable = new_member
assert_equal nil, sponsor.sponsorable_id
end
def test_save_fails_for_invalid_belongs_to
assert log = AuditLog.create(:developer_id=>0,:message=>"")
log.developer = Developer.new
assert !log.developer.valid?
assert !log.valid?
assert !log.save
assert_equal "is invalid", log.errors.on("developer")
end
def test_save_succeeds_for_invalid_belongs_to_with_validate_false
assert log = AuditLog.create(:developer_id=>0,:message=>"")
log.unvalidated_developer = Developer.new
assert !log.unvalidated_developer.valid?
assert log.valid?
assert log.save
end
end

View file

@ -9,7 +9,7 @@ require 'models/topic'
require 'models/reply'
class CascadedEagerLoadingTest < ActiveRecord::TestCase
fixtures :authors, :mixins, :companies, :posts, :topics
fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations
def test_eager_association_loading_with_cascaded_two_levels
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
@ -68,6 +68,18 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
end
def test_eager_association_loading_with_has_many_sti_and_subclasses
silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1)
silly.parent_id = 1
assert silly.save
topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id')
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
end
end
def test_eager_association_loading_with_belongs_to_sti
replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
assert replies.include?(topics(:second))

View file

@ -0,0 +1,36 @@
require 'cases/helper'
require 'models/post'
require 'models/tagging'
module Namespaced
class Post < ActiveRecord::Base
set_table_name 'posts'
has_one :tagging, :as => :taggable, :class_name => 'Tagging'
end
end
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
def setup
generate_test_objects
end
def generate_test_objects
post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 )
tagging = Tagging.create( :taggable => post )
end
def test_class_names
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = false
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
assert_nil post.tagging
ActiveRecord::Base.store_full_sti_class = true
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
assert_equal 'Tagging', post.tagging.class.name
ensure
ActiveRecord::Base.store_full_sti_class = old
end
end

View file

@ -14,11 +14,14 @@ require 'models/job'
require 'models/subscriber'
require 'models/subscription'
require 'models/book'
require 'models/developer'
require 'models/project'
class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :categories, :categories_posts,
:companies, :accounts, :tags, :taggings, :people, :readers,
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
:developers, :projects, :developers_projects
def test_loading_with_one_association
posts = Post.find(:all, :include => :comments)
@ -35,6 +38,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_with_one_association_with_non_preload
posts = Post.find(:all, :include => :last_comment, :order => 'comments.id DESC')
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_conditions_with_or
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
@ -556,6 +565,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_nothing_raised { Post.find(:all, :include => 'comments') }
end
def test_eager_with_floating_point_numbers
assert_queries(2) do
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
Comment.find :all, :conditions => "123.456 = 123.456", :include => :post
end
end
def test_preconfigured_includes_with_belongs_to
author = posts(:welcome).author_with_posts
assert_no_queries {assert_equal 5, author.posts.size}
@ -609,4 +625,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
Comment.find :all, :include => :post
end
end
def test_conditions_on_join_table_with_include_and_limit
assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
end
def test_order_on_join_table_with_include_and_limit
assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
end
end

View file

@ -70,7 +70,7 @@ end
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :treasures, :price_estimates
:parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
def test_has_and_belongs_to_many
david = Developer.find(1)
@ -299,6 +299,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, projects(:active_record, :reload).developers.size
end
def test_uniq_option_prevents_duplicate_push
project = projects(:active_record)
project.developers << developers(:jamis)
project.developers << developers(:david)
assert_equal 3, project.developers.size
project.developers << developers(:david)
project.developers << developers(:jamis)
assert_equal 3, project.developers.size
end
def test_deleting
david = Developer.find(1)
active_record = Project.find(1)
@ -439,6 +450,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
end
def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
# interpolate once:
assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
# interpolate again, for a different project id
assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
end
def test_find_in_association_with_custom_finder_sql_and_string_id
assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
end
@ -629,8 +647,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer.save
developer.reload
assert_equal 2, developer.projects.length
assert_equal projects(:active_record), developer.projects[0]
assert_equal projects(:action_controller), developer.projects[1]
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
end
def test_assign_ids_ignoring_blanks
@ -639,8 +656,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer.save
developer.reload
assert_equal 2, developer.projects.length
assert_equal projects(:active_record), developer.projects[0]
assert_equal projects(:action_controller), developer.projects[1]
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
end
def test_select_limited_ids_list
@ -681,4 +697,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developer, project.developers.find(:first)
assert_equal project, developer.projects.find(:first)
end
def test_dynamic_find_should_respect_association_include
# SQL error in sort clause if :include is not included
# due to Unknown column 'authors.id'
assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
end
end

View file

@ -14,7 +14,7 @@ require 'models/reader'
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
:developers_projects, :topics, :authors, :comments, :author_addresses,
:people, :posts
:people, :posts, :readers
def setup
Client.destroyed_client_ids.clear
@ -37,15 +37,21 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_counting_with_single_conditions
assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
assert_equal 1, Firm.find(:first).plain_clients.count(:conditions => ['name=?', "Microsoft"])
end
def test_counting_with_single_hash
assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
assert_equal 1, Firm.find(:first).plain_clients.count(:conditions => {:name => "Microsoft"})
end
def test_counting_with_column_name_and_hash
assert_equal 2, Firm.find(:first).plain_clients.count(:all, :conditions => '1=1')
assert_equal 2, Firm.find(:first).plain_clients.count(:name)
end
def test_counting_with_association_limit
firm = companies(:first_firm)
assert_equal firm.limited_clients.length, firm.limited_clients.size
assert_equal firm.limited_clients.length, firm.limited_clients.count
end
def test_finding
@ -342,6 +348,34 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert new_firm.new_record?
end
def test_invalid_adding_with_validate_false
firm = Firm.find(:first)
client = Client.new
firm.unvalidated_clients_of_firm << client
assert firm.valid?
assert !client.valid?
assert firm.save
assert client.new_record?
end
def test_valid_adding_with_validate_false
no_of_clients = Client.count
firm = Firm.find(:first)
client = Client.new("name" => "Apple")
assert firm.valid?
assert client.valid?
assert client.new_record?
firm.unvalidated_clients_of_firm << client
assert firm.save
assert !client.new_record?
assert_equal no_of_clients+1, Client.count
end
def test_build
company = companies(:first_firm)
new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
@ -356,6 +390,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, company.clients_of_firm(true).size
end
def test_collection_size_after_building
company = companies(:first_firm) # company already has one client
company.clients_of_firm.build("name" => "Another Client")
company.clients_of_firm.build("name" => "Yet Another Client")
assert_equal 3, company.clients_of_firm.size
end
def test_collection_size_twice_for_regressions
post = posts(:thinking)
assert_equal 0, post.readers.size
# This test needs a post that has no readers, we assert it to ensure it holds,
# but need to reload the post because the very call to #size hides the bug.
post.reload
post.readers.build
size1 = post.readers.size
size2 = post.readers.size
assert_equal size1, size2
end
def test_build_many
company = companies(:first_firm)
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
@ -386,6 +439,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, first_topic.replies.to_ary.size
end
def test_build_via_block
company = companies(:first_firm)
new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
assert !company.clients_of_firm.loaded?
assert_equal "Another Client", new_client.name
assert new_client.new_record?
assert_equal new_client, company.clients_of_firm.last
company.name += '-changed'
assert_queries(2) { assert company.save }
assert !new_client.new_record?
assert_equal 2, company.clients_of_firm(true).size
end
def test_build_many_via_block
company = companies(:first_firm)
new_clients = assert_no_queries do
company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
client.name = "changed"
end
end
assert_equal 2, new_clients.size
assert_equal "changed", new_clients.first.name
assert_equal "changed", new_clients.last.name
company.name += '-changed'
assert_queries(3) { assert company.save }
assert_equal 3, company.clients_of_firm(true).size
end
def test_create_without_loading_association
first_firm = companies(:first_firm)
Firm.column_names
@ -929,4 +1013,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert firm.clients.loaded?
end
def test_joins_with_namespaced_model_should_use_correct_type
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = true
firm = Namespaced::Firm.create({ :name => 'Some Company' })
firm.clients.create({ :name => 'Some Client' })
stats = Namespaced::Firm.find(firm.id, {
:select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
:joins => :clients,
:group => "#{Namespaced::Firm.table_name}.id"
})
assert_equal 1, stats.num_clients.to_i
ensure
ActiveRecord::Base.store_full_sti_class = old
end
end

View file

@ -187,4 +187,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post.people_with_callbacks.clear
assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort
end
def test_dynamic_find_should_respect_association_include
# SQL error in sort clause if :include is not included
# due to Unknown column 'comments.id'
assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
end
def test_count_with_include_should_alias_join_table
assert_equal 2, people(:michael).posts.count(:include => :readers)
end
end

View file

@ -72,6 +72,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
end
def test_natural_assignment_to_already_associated_record
company = companies(:first_firm)
account = accounts(:signals37)
assert_equal company.account, account
company.account = account
company.reload
account.reload
assert_equal company.account, account
end
def test_assignment_without_replacement
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
@ -275,6 +285,18 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal "is invalid", firm.errors.on("account")
end
def test_save_succeeds_for_invalid_has_one_with_validate_false
firm = Firm.find(:first)
assert firm.valid?
firm.unvalidated_account = Account.new
assert !firm.unvalidated_account.valid?
assert firm.valid?
assert firm.save
end
def test_assignment_before_either_saved
firm = Firm.new("name" => "GlobalMegaCorp")
firm.account = a = Account.new("credit_limit" => 1000)

View file

@ -44,19 +44,23 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_polymorphic
assert_equal clubs(:moustache_club), @member.sponsor_club
end
def has_one_through_to_has_many
assert_equal 2, @member.fellow_members.size
end
def test_has_one_through_eager_loading
members = Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
members = assert_queries(3) do #base table, through table, clubs table
Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].club}
end
def test_has_one_through_eager_loading_through_polymorphic
members = Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
members = assert_queries(3) do #base table, through table, clubs table
Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].sponsor_club}
end
@ -71,4 +75,39 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_not_nil assert_no_queries {clubs[0].sponsored_member}
end
def test_has_one_through_nonpreload_eagerloading
members = assert_queries(1) do
Member.find(:all, :include => :club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].club}
end
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
members = assert_queries(1) do
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries {members[0].sponsor_club}
end
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
members = assert_queries(1) do
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC') #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
assert_equal clubs(:crazy_club), members[0].sponsor_club
end
def test_uninitialized_has_one_through_should_return_nil_for_unsaved_record
assert_nil Member.new.club
end
def test_assigning_association_correctly_assigns_target
new_member = Member.create(:name => "Chris")
new_member.club = new_club = Club.create(:name => "LRUG")
assert_equal new_club, new_member.club.target
end
end

View file

@ -694,6 +694,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert ! david.categories.include?(category)
end
def test_has_many_through_goes_through_all_sti_classes
sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
new_comment = sub_sti_post.comments.create(:body => 'test')
assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
end
private
# create dynamic Post models to allow different dependency options
def find_post_with_dependency(post_id, association, association_name, dependency)

View file

@ -27,7 +27,7 @@ require 'models/sponsor'
class AssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
:computers
:computers, :people, :readers
def test_include_with_order_works
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
@ -45,7 +45,7 @@ class AssociationsTest < ActiveRecord::TestCase
assert_equal [], person.readers.find(:all)
person.save!
reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
assert_equal [reader], person.readers.find(:all)
assert person.readers.find(reader.id)
end
def test_force_reload

View file

@ -137,7 +137,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
end
def test_time_attributes_are_retrieved_in_current_time_zone
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
@ -145,7 +145,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record[:written_on] = utc_time
assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
end
end
@ -156,7 +156,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = utc_time
assert_equal utc_time, record.written_on
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
end
end
@ -168,7 +168,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = cst_time
assert_equal utc_time, record.written_on
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
end
end
@ -181,12 +181,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = time_string
assert_equal Time.zone.parse(time_string), record.written_on
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
end
end
end
def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
@ -202,7 +202,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = time_string
assert_equal Time.zone.parse(time_string), record.written_on
assert_equal TimeZone[timezone_offset], record.written_on.time_zone
assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
assert_equal Time.utc(2008, 1, 1), record.written_on.time
end
end
@ -214,7 +214,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = utc_time.in_time_zone
assert_equal utc_time, record.written_on
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
end
end
@ -223,12 +223,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def time_related_columns_on_topic
Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
end
def in_time_zone(zone)
old_zone = Time.zone
old_tz = ActiveRecord::Base.time_zone_aware_attributes
Time.zone = zone ? TimeZone[zone] : nil
Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
yield
ensure

View file

@ -19,6 +19,7 @@ require 'models/warehouse_thing'
require 'rexml/document'
class Category < ActiveRecord::Base; end
class Categorization < ActiveRecord::Base; end
class Smarts < ActiveRecord::Base; end
class CreditCard < ActiveRecord::Base
class PinNumber < ActiveRecord::Base
@ -75,7 +76,7 @@ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
end
class BasicsTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories
def test_table_exists
assert !NonExistentTable.table_exists?
@ -130,22 +131,22 @@ class BasicsTest < ActiveRecord::TestCase
def test_read_attributes_before_type_cast
category = Category.new({:name=>"Test categoty", :type => nil})
category_attrs = {"name"=>"Test categoty", "type" => nil}
category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
assert_equal category_attrs , category.attributes_before_type_cast
end
if current_adapter?(:MysqlAdapter)
def test_read_attributes_before_type_cast_on_boolean
bool = Booleantest.create({ "value" => false })
assert_equal 0, bool.attributes_before_type_cast["value"]
end
end
def test_read_attributes_before_type_cast_on_datetime
developer = Developer.find(:first)
assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
end
def test_hash_content
topic = Topic.new
topic.content = { "one" => 1, "two" => 2 }
@ -251,7 +252,7 @@ class BasicsTest < ActiveRecord::TestCase
topic = Topic.create("title" => "New Topic")
topicReloaded = Topic.find(topic.id)
assert_equal(topic, topicReloaded)
end
end
def test_create_through_factory_with_block
topic = Topic.create("title" => "New Topic") do |t|
@ -576,7 +577,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_destroy_all
original_count = Topic.count
topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'")
Topic.destroy_all mary
assert_equal original_count - topics_by_mary, Topic.count
end
@ -614,6 +615,22 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal -2, Topic.find(2).replies_count
end
def test_update_counter
category = categories(:general)
assert_nil category.categorizations_count
assert_equal 2, category.categorizations.count
Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
category.reload
assert_not_nil category.categorizations_count
assert_equal 2, category.categorizations_count
Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
category.reload
assert_not_nil category.categorizations_count
assert_equal 4, category.categorizations_count
end
def test_update_all
assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
assert_equal "bulk updated!", Topic.find(1).content
@ -665,7 +682,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_delete_all
assert Topic.count > 0
assert_equal Topic.count, Topic.delete_all
end
@ -970,7 +987,7 @@ class BasicsTest < ActiveRecord::TestCase
topic.attributes = attributes
assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
end
def test_multiparameter_attributes_on_time_with_old_date
attributes = {
"written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
@ -998,7 +1015,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.default_timezone = :utc
Time.zone = TimeZone[-28800]
Time.zone = ActiveSupport::TimeZone[-28800]
attributes = {
"written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
"written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
@ -1016,7 +1033,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
ActiveRecord::Base.time_zone_aware_attributes = false
Time.zone = TimeZone[-28800]
Time.zone = ActiveSupport::TimeZone[-28800]
attributes = {
"written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
"written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
@ -1032,7 +1049,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.default_timezone = :utc
Time.zone = TimeZone[-28800]
Time.zone = ActiveSupport::TimeZone[-28800]
Topic.skip_time_zone_conversion_for_attributes = [:written_on]
attributes = {
"written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
@ -1336,6 +1353,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
def test_serialized_time_attribute
myobj = Time.local(2008,1,1,1,0)
topic = Topic.create("content" => myobj).reload
assert_equal(myobj, topic.content)
end
def test_nil_serialized_attribute_with_class_constraint
myobj = MyObject.new('value1', 'value2')
topic = Topic.new
@ -1647,7 +1670,7 @@ class BasicsTest < ActiveRecord::TestCase
last = Developer.find :last
assert_equal last, Developer.find(:first, :order => 'id desc')
end
def test_last
assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
end
@ -1655,7 +1678,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_all_with_conditions
assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc')
end
def test_find_ordered_last
last = Developer.find :last, :order => 'developers.salary ASC'
assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
@ -1670,14 +1693,14 @@ class BasicsTest < ActiveRecord::TestCase
last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
end
def test_find_scoped_ordered_last
last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
Developer.find(:last)
end
assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
end
def test_abstract_class
assert !ActiveRecord::Base.abstract_class?
assert LoosePerson.abstract_class?

View file

@ -1,6 +1,7 @@
require "cases/helper"
require 'models/company'
require 'models/topic'
require 'models/edge'
Company.has_many :accounts
@ -99,6 +100,12 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_return_zero_if_sum_conditions_return_nothing
assert_equal 0, Account.sum(:credit_limit, :conditions => '1 = 2')
assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2')
end
def test_sum_should_return_valid_values_for_decimals
NumericData.create(:bank_balance => 19.83)
assert_equal 19.83, NumericData.sum(:bank_balance)
end
def test_should_group_by_summed_field_with_conditions
@ -266,6 +273,51 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_expression
assert_equal "636", Account.sum("2 * credit_limit")
assert_equal 636, Account.sum("2 * credit_limit")
end
def test_count_with_from_option
assert_equal Company.count(:all), Company.count(:all, :from => 'companies')
assert_equal Account.count(:all, :conditions => "credit_limit = 50"),
Account.count(:all, :from => 'accounts', :conditions => "credit_limit = 50")
assert_equal Company.count(:type, :conditions => {:type => "Firm"}),
Company.count(:type, :conditions => {:type => "Firm"}, :from => 'companies')
end
def test_sum_with_from_option
assert_equal Account.sum(:credit_limit), Account.sum(:credit_limit, :from => 'accounts')
assert_equal Account.sum(:credit_limit, :conditions => "credit_limit > 50"),
Account.sum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
end
def test_average_with_from_option
assert_equal Account.average(:credit_limit), Account.average(:credit_limit, :from => 'accounts')
assert_equal Account.average(:credit_limit, :conditions => "credit_limit > 50"),
Account.average(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
end
def test_minimum_with_from_option
assert_equal Account.minimum(:credit_limit), Account.minimum(:credit_limit, :from => 'accounts')
assert_equal Account.minimum(:credit_limit, :conditions => "credit_limit > 50"),
Account.minimum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
end
def test_maximum_with_from_option
assert_equal Account.maximum(:credit_limit), Account.maximum(:credit_limit, :from => 'accounts')
assert_equal Account.maximum(:credit_limit, :conditions => "credit_limit > 50"),
Account.maximum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
end
def test_from_option_with_specified_index
if Edge.connection.adapter_name == 'MySQL'
assert_equal Edge.count(:all), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)')
assert_equal Edge.count(:all, :conditions => 'sink_id < 5'),
Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)', :conditions => 'sink_id < 5')
end
end
def test_from_option_with_table_different_than_class
assert_equal Account.count(:all), Company.count(:all, :from => 'accounts')
end
end

View file

@ -0,0 +1,36 @@
require "cases/helper"
class ColumnDefinitionTest < ActiveRecord::TestCase
def setup
@adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(nil)
def @adapter.native_database_types
{:string => "varchar"}
end
end
# Avoid column definitions in create table statements like:
# `title` varchar(255) DEFAULT NULL
def test_should_not_include_default_clause_when_default_is_null
column = ActiveRecord::ConnectionAdapters::Column.new("title", nil, "varchar(20)")
column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
@adapter, column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
assert_equal "title varchar(20)", column_def.to_sql
end
def test_should_include_default_clause_when_default_is_present
column = ActiveRecord::ConnectionAdapters::Column.new("title", "Hello", "varchar(20)")
column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
@adapter, column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
assert_equal %Q{title varchar(20) DEFAULT 'Hello'}, column_def.to_sql
end
def test_should_specify_not_null_if_null_option_is_false
column = ActiveRecord::ConnectionAdapters::Column.new("title", "Hello", "varchar(20)", false)
column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
@adapter, column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
assert_equal %Q{title varchar(20) DEFAULT 'Hello' NOT NULL}, column_def.to_sql
end
end

View file

@ -0,0 +1,12 @@
require "cases/helper"
class DatabaseStatementsTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
end
def test_insert_should_return_the_inserted_id
id = @connection.insert("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)")
assert_not_nil id
end
end

View file

@ -5,7 +5,7 @@ require 'models/entrant'
class DefaultTest < ActiveRecord::TestCase
def test_nil_defaults_for_not_null_columns
column_defaults =
if current_adapter?(:MysqlAdapter) && Mysql.client_version < 50051
if current_adapter?(:MysqlAdapter) && (Mysql.client_version < 50051 || (50100..50122).include?(Mysql.client_version))
{ 'id' => nil, 'name' => '', 'course_id' => nil }
else
{ 'id' => nil, 'name' => nil, 'course_id' => nil }

View file

@ -2,6 +2,7 @@ require 'cases/helper'
require 'models/topic' # For booleans
require 'models/pirate' # For timestamps
require 'models/parrot'
require 'models/person' # For optimistic locking
class Pirate # Just reopening it, not defining it
attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
@ -54,6 +55,33 @@ class DirtyTest < ActiveRecord::TestCase
end
end
def test_zero_to_blank_marked_as_changed
pirate = Pirate.new
pirate.catchphrase = "Yarrrr, me hearties"
pirate.parrot_id = 1
pirate.save
# check the change from 1 to ''
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
pirate.parrot_id = ''
assert pirate.parrot_id_changed?
assert_equal([1, nil], pirate.parrot_id_change)
pirate.save
# check the change from nil to 0
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
pirate.parrot_id = 0
assert pirate.parrot_id_changed?
assert_equal([nil, 0], pirate.parrot_id_change)
pirate.save
# check the change from 0 to ''
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
pirate.parrot_id = ''
assert pirate.parrot_id_changed?
assert_equal([0, nil], pirate.parrot_id_change)
end
def test_object_should_be_changed_if_any_attribute_is_changed
pirate = Pirate.new
assert !pirate.changed?
@ -125,6 +153,24 @@ class DirtyTest < ActiveRecord::TestCase
end
end
def test_partial_update_with_optimistic_locking
person = Person.new(:first_name => 'foo')
old_lock_version = 1
with_partial_updates Person, false do
assert_queries(2) { 2.times { person.save! } }
Person.update_all({ :first_name => 'baz' }, :id => person.id)
end
with_partial_updates Person, true do
assert_queries(0) { 2.times { person.save! } }
assert_equal old_lock_version, person.reload.lock_version
assert_queries(1) { person.first_name = 'bar'; person.save! }
assert_not_equal old_lock_version, person.reload.lock_version
end
end
def test_changed_attributes_should_be_preserved_if_save_failure
pirate = Pirate.new
pirate.parrot_id = 1
@ -145,6 +191,54 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.changed?
end
def test_reverted_changes_are_not_dirty
phrase = "shiver me timbers"
pirate = Pirate.create!(:catchphrase => phrase)
pirate.catchphrase = "*hic*"
assert pirate.changed?
pirate.catchphrase = phrase
assert !pirate.changed?
end
def test_reverted_changes_are_not_dirty_after_multiple_changes
phrase = "shiver me timbers"
pirate = Pirate.create!(:catchphrase => phrase)
10.times do |i|
pirate.catchphrase = "*hic*" * i
assert pirate.changed?
end
assert pirate.changed?
pirate.catchphrase = phrase
assert !pirate.changed?
end
def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
pirate = Pirate.create!(:catchphrase => "Yar!")
pirate.parrot_id = 1
assert pirate.changed?
assert pirate.parrot_id_changed?
assert !pirate.catchphrase_changed?
pirate.parrot_id = nil
assert !pirate.changed?
assert !pirate.parrot_id_changed?
assert !pirate.catchphrase_changed?
end
def test_save_should_store_serialized_attributes_even_with_partial_updates
with_partial_updates(Topic) do
topic = Topic.create!(:content => {:a => "a"})
topic.content[:b] = "b"
#assert topic.changed? # Known bug, will fail
topic.save!
assert_equal "b", topic.content[:b]
topic.reload
assert_equal "b", topic.content[:b]
end
end
private
def with_partial_updates(klass, on = true)
old = klass.partial_updates?

View file

@ -1,5 +1,6 @@
require "cases/helper"
require 'models/author'
require 'models/categorization'
require 'models/comment'
require 'models/company'
require 'models/topic'
@ -394,6 +395,12 @@ class FinderTest < ActiveRecord::TestCase
assert_equal '1,1,1', bind('?', os)
end
def test_named_bind_with_postgresql_type_casts
l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
assert_nothing_raised(&l)
assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call
end
def test_string_sanitation
assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table")

View file

@ -32,13 +32,13 @@ end
ActiveRecord::Base.connection.class.class_eval do
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/]
def execute_with_counting(sql, name = nil, &block)
$query_count ||= 0
$query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
execute_without_counting(sql, name, &block)
def execute_with_query_record(sql, name = nil, &block)
$queries_executed ||= []
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
execute_without_query_record(sql, name, &block)
end
alias_method_chain :execute, :counting
alias_method_chain :execute, :query_record
end
# Make with_scope public for tests

View file

@ -191,6 +191,13 @@ class InheritanceTest < ActiveRecord::TestCase
assert_not_nil account.instance_variable_get("@firm"), "nil proves eager load failed"
end
def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} IN \(1\)\)/) do
Account.find(1, :include => :firm)
end
end
def test_alt_eager_loading
switch_to_alt_inheritance_column
test_eager_load_belongs_to_something_inherited
@ -223,11 +230,11 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
fixtures :companies
def setup
Dependencies.log_activity = true
ActiveSupport::Dependencies.log_activity = true
end
def teardown
Dependencies.log_activity = false
ActiveSupport::Dependencies.log_activity = false
self.class.const_remove :FirmOnTheFly rescue nil
Firm.const_remove :FirmOnTheFly rescue nil
end

View file

@ -2,6 +2,7 @@ require "cases/helper"
require 'models/topic'
require 'models/developer'
require 'models/reply'
require 'models/minimalistic'
class Topic; def after_find() end end
class Developer; def after_find() end end
@ -44,6 +45,14 @@ class TopicObserver < ActiveRecord::Observer
end
end
class MinimalisticObserver < ActiveRecord::Observer
attr_reader :minimalistic
def after_find(minimalistic)
@minimalistic = minimalistic
end
end
class MultiObserver < ActiveRecord::Observer
attr_reader :record
@ -65,7 +74,7 @@ class MultiObserver < ActiveRecord::Observer
end
class LifecycleTest < ActiveRecord::TestCase
fixtures :topics, :developers
fixtures :topics, :developers, :minimalistics
def test_before_destroy
original_count = Topic.count
@ -134,6 +143,50 @@ class LifecycleTest < ActiveRecord::TestCase
assert_equal developer.name, multi_observer.record.name
end
def test_after_find_can_be_observed_when_its_not_defined_on_the_model
observer = MinimalisticObserver.instance
assert_equal Minimalistic, MinimalisticObserver.observed_class
minimalistic = Minimalistic.find(1)
assert_equal minimalistic, observer.minimalistic
end
def test_after_find_can_be_observed_when_its_defined_on_the_model
observer = TopicObserver.instance
assert_equal Topic, TopicObserver.observed_class
topic = Topic.find(1)
assert_equal topic, observer.topic
end
def test_after_find_is_not_created_if_its_not_used
# use a fresh class so an observer can't have defined an
# after_find on it
model_class = Class.new(ActiveRecord::Base)
observer_class = Class.new(ActiveRecord::Observer)
observer_class.observe(model_class)
observer = observer_class.instance
assert !model_class.method_defined?(:after_find)
end
def test_after_find_is_not_clobbered_if_it_already_exists
# use a fresh observer class so we can instantiate it (Observer is
# a Singleton)
model_class = Class.new(ActiveRecord::Base) do
def after_find; end
end
original_method = model_class.instance_method(:after_find)
observer_class = Class.new(ActiveRecord::Observer) do
def after_find; end
end
observer_class.observe(model_class)
observer = observer_class.instance
assert_equal original_method, model_class.instance_method(:after_find)
end
def test_invalid_observer
assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
end

View file

@ -29,10 +29,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
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
p2.first_name = 'sue'
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
end
@ -42,11 +44,14 @@ class OptimisticLockingTest < ActiveRecord::TestCase
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
p2.first_name = 'sue'
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
p2.first_name = 'sue2'
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
end
@ -54,15 +59,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase
p1 = Person.new(:first_name => 'anika')
assert_equal 0, p1.lock_version
p1.first_name = 'anika2'
p1.save!
p2 = Person.find(p1.id)
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
p1.first_name = 'anika3'
p1.save!
assert_equal 1, p1.lock_version
assert_equal 0, p2.lock_version
p2.first_name = 'sue'
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
end
@ -81,10 +89,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, t1.version
assert_equal 0, t2.version
t1.tps_report_number = 700
t1.save!
assert_equal 1, t1.version
assert_equal 0, t2.version
t2.tps_report_number = 800
assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
end
@ -93,6 +103,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, p1.lock_version
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
p1.first_name = 'bianca2'
p1.save!
assert_equal 1, p1.lock_version
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
@ -146,6 +157,15 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert ref.save
end
# Useful for partial updates, don't only update the lock_version if there
# is nothing else being updated.
def test_update_without_attributes_does_not_only_update_lock_version
assert_nothing_raised do
p1 = Person.new(:first_name => 'anika')
p1.send(:update_with_lock, [])
end
end
private
def add_counter_column_to(model)

View file

@ -6,7 +6,7 @@ require 'models/post'
require 'models/category'
class MethodScopingTest < ActiveRecord::TestCase
fixtures :developers, :projects, :comments, :posts
fixtures :developers, :projects, :comments, :posts, :developers_projects
def test_set_conditions
Developer.with_scope(:find => { :conditions => 'just a test...' }) do
@ -87,6 +87,16 @@ class MethodScopingTest < ActiveRecord::TestCase
assert_equal 1, scoped_developers.size
end
def test_scoped_find_joins
scoped_developers = Developer.with_scope(:find => { :joins => 'JOIN developers_projects ON id = developer_id' } ) do
Developer.find(:all, :conditions => 'developers_projects.project_id = 2')
end
assert scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
assert_equal 1, scoped_developers.size
assert_equal developers(:david).attributes, scoped_developers.first.attributes
end
def test_scoped_count_include
# with the include, will retrieve only developers for the given project
Developer.with_scope(:find => { :include => :projects }) do

View file

@ -3,6 +3,7 @@ require 'bigdecimal/util'
require 'models/person'
require 'models/topic'
require 'models/developer'
require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
@ -153,9 +154,10 @@ if ActiveRecord::Base.connection.supports_migrations?
t.column :default_int, :integer
t.column :one_int, :integer, :limit => 1
t.column :four_int, :integer, :limit => 4
t.column :eight_int, :integer, :limit => 8
t.column :one_int, :integer, :limit => 1
t.column :four_int, :integer, :limit => 4
t.column :eight_int, :integer, :limit => 8
t.column :eleven_int, :integer, :limit => 11
end
end
@ -167,12 +169,20 @@ if ActiveRecord::Base.connection.supports_migrations?
one = columns.detect { |c| c.name == "one_int" }
four = columns.detect { |c| c.name == "four_int" }
eight = columns.detect { |c| c.name == "eight_int" }
eleven = columns.detect { |c| c.name == "eleven_int" }
if current_adapter?(:PostgreSQLAdapter)
assert_equal 'integer', default.sql_type
assert_equal 'smallint', one.sql_type
assert_equal 'integer', four.sql_type
assert_equal 'bigint', eight.sql_type
assert_equal 'integer', eleven.sql_type
elsif current_adapter?(:MysqlAdapter)
assert_match 'int(11)', default.sql_type
assert_match 'tinyint', one.sql_type
assert_match 'int', four.sql_type
assert_match 'bigint', eight.sql_type
assert_match 'int(11)', eleven.sql_type
elsif current_adapter?(:OracleAdapter)
assert_equal 'NUMBER(38)', default.sql_type
assert_equal 'NUMBER(1)', one.sql_type
@ -227,6 +237,39 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
def test_create_table_with_timestamps_should_create_datetime_columns
table_name = :testings
Person.connection.create_table table_name do |t|
t.timestamps
end
created_columns = Person.connection.columns(table_name)
created_at_column = created_columns.detect {|c| c.name == 'created_at' }
updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
assert created_at_column.null
assert updated_at_column.null
ensure
Person.connection.drop_table table_name rescue nil
end
def test_create_table_with_timestamps_should_create_datetime_columns_with_options
table_name = :testings
Person.connection.create_table table_name do |t|
t.timestamps :null => false
end
created_columns = Person.connection.columns(table_name)
created_at_column = created_columns.detect {|c| c.name == 'created_at' }
updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
assert !created_at_column.null
assert !updated_at_column.null
ensure
Person.connection.drop_table table_name rescue nil
end
# SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
# column to a table without a default value.
@ -399,10 +442,7 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
Person.reset_column_information
Person.create :intelligence_quotient => 300
jonnyg = Person.find(:first)
assert_equal 127, jonnyg.intelligence_quotient
jonnyg.destroy
assert_match /tinyint/, Person.columns_hash['intelligence_quotient'].sql_type
ensure
ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
end
@ -483,6 +523,37 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
def test_rename_column_preserves_default_value_not_null
begin
default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
assert_equal 70000, default_before
Developer.connection.rename_column "developers", "salary", "anual_salary"
Developer.reset_column_information
assert Developer.column_names.include?("anual_salary")
default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
assert_equal 70000, default_after
ensure
Developer.connection.rename_column "developers", "anual_salary", "salary"
Developer.reset_column_information
end
end
def test_rename_nonexistent_column
ActiveRecord::Base.connection.create_table(:hats) do |table|
table.column :hat_name, :string, :default => nil
end
exception = if current_adapter?(:PostgreSQLAdapter)
ActiveRecord::StatementInvalid
else
ActiveRecord::ActiveRecordError
end
assert_raises(exception) do
Person.connection.rename_column "hats", "nonexistent", "should_fail"
end
ensure
ActiveRecord::Base.connection.drop_table(:hats)
end
def test_rename_column_with_sql_reserved_word
begin
assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
@ -662,6 +733,55 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.drop_table :testings rescue nil
end
def test_keeping_default_and_notnull_constaint_on_change
Person.connection.create_table :testings do |t|
t.column :title, :string
end
person_klass = Class.new(Person)
person_klass.set_table_name 'testings'
person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
person_klass.reset_column_information
assert_equal 99, person_klass.columns_hash["wealth"].default
assert_equal false, person_klass.columns_hash["wealth"].null
assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
# change column default to see that column doesn't lose its not null definition
person_klass.connection.change_column_default "testings", "wealth", 100
person_klass.reset_column_information
assert_equal 100, person_klass.columns_hash["wealth"].default
assert_equal false, person_klass.columns_hash["wealth"].null
# rename column to see that column doesn't lose its not null and/or default definition
person_klass.connection.rename_column "testings", "wealth", "money"
person_klass.reset_column_information
assert_nil person_klass.columns_hash["wealth"]
assert_equal 100, person_klass.columns_hash["money"].default
assert_equal false, person_klass.columns_hash["money"].null
# change column
person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
person_klass.reset_column_information
assert_equal 1000, person_klass.columns_hash["money"].default
assert_equal false, person_klass.columns_hash["money"].null
# change column, make it nullable and clear default
person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
person_klass.reset_column_information
assert_nil person_klass.columns_hash["money"].default
assert_equal true, person_klass.columns_hash["money"].null
# change_column_null, make it not nullable and set null values to a default value
person_klass.connection.execute('UPDATE testings SET money = NULL')
person_klass.connection.change_column_null "testings", "money", false, 2000
person_klass.reset_column_information
assert_nil person_klass.columns_hash["money"].default
assert_equal false, person_klass.columns_hash["money"].null
assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
ensure
Person.connection.drop_table :testings rescue nil
end
def test_change_column_default_to_null
Person.connection.change_column_default "people", "first_name", nil
Person.reset_column_information
@ -799,6 +919,21 @@ if ActiveRecord::Base.connection.supports_migrations?
assert !Reminder.table_exists?
end
def test_migrator_double_up
assert_equal(0, ActiveRecord::Migrator.current_version)
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
assert_equal(1, ActiveRecord::Migrator.current_version)
end
def test_migrator_double_down
assert_equal(0, ActiveRecord::Migrator.current_version)
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
assert_equal(0, ActiveRecord::Migrator.current_version)
end
def test_finds_migrations
migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
[['1', 'people_have_last_names'],
@ -888,16 +1023,6 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
assert_equal(0, ActiveRecord::Migrator.current_version)
end
def test_migrator_run
assert_equal(0, ActiveRecord::Migrator.current_version)
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 3)
assert_equal(0, ActiveRecord::Migrator.current_version)
assert_equal(0, ActiveRecord::Migrator.current_version)
ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 3)
assert_equal(0, ActiveRecord::Migrator.current_version)
end
def test_schema_migrations_table_name
ActiveRecord::Base.table_name_prefix = "prefix_"
@ -1077,8 +1202,8 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_timestamps_creates_updated_at_and_created_at
with_new_table do |t|
t.expects(:column).with(:created_at, :datetime)
t.expects(:column).with(:updated_at, :datetime)
t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
t.timestamps
end
end
@ -1206,10 +1331,10 @@ if ActiveRecord::Base.connection.supports_migrations?
end
def integer_column
if current_adapter?(:SQLite3Adapter) || current_adapter?(:SQLiteAdapter) || current_adapter?(:PostgreSQLAdapter)
"integer"
else
if current_adapter?(:MysqlAdapter)
'int(11)'
else
'integer'
end
end

View file

@ -51,7 +51,7 @@ class MultipleDbTest < ActiveRecord::TestCase
def test_course_connection_should_survive_dependency_reload
assert Course.connection
Dependencies.clear
ActiveSupport::Dependencies.clear
Object.send(:remove_const, :Course)
require_dependency 'models/course'

View file

@ -4,9 +4,10 @@ require 'models/topic'
require 'models/comment'
require 'models/reply'
require 'models/author'
require 'models/developer'
class NamedScopeTest < ActiveRecord::TestCase
fixtures :posts, :authors, :topics, :comments
fixtures :posts, :authors, :topics, :comments, :author_addresses
def test_implements_enumerable
assert !Topic.find(:all).empty?
@ -45,6 +46,17 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
end
def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
assert Topic.approved.respond_to?(:proxy_found)
assert Topic.approved.respond_to?(:count)
assert Topic.approved.respond_to?(:length)
end
def test_respond_to_respects_include_private_parameter
assert !Topic.approved.respond_to?(:load_found)
assert Topic.approved.respond_to?(:load_found, true)
end
def test_subclasses_inherit_scopes
assert Topic.scopes.include?(:base)
@ -59,6 +71,12 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal Topic.count(:conditions => {:approved => true}), Topic.approved.count
end
def test_scopes_with_string_name_can_be_composed
# NOTE that scopes defined with a string as a name worked on their own
# but when called on another scope the other scope was completely replaced
assert_equal Topic.replied.approved, Topic.replied.approved_as_string
end
def test_scopes_are_composable
assert_equal (approved = Topic.find(:all, :conditions => {:approved => true})), Topic.approved
assert_equal (replied = Topic.find(:all, :conditions => 'replies_count > 0')), Topic.replied
@ -77,6 +95,25 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal topics_written_before_the_second, Topic.written_before(topics(:second).written_on)
end
def test_scopes_with_joins
address = author_addresses(:david_address)
posts_with_authors_at_address = Post.find(
:all, :joins => 'JOIN authors ON authors.id = posts.author_id',
:conditions => [ 'authors.author_address_id = ?', address.id ]
)
assert_equal posts_with_authors_at_address, Post.with_authors_at_address(address)
end
def test_scopes_with_joins_respects_custom_select
address = author_addresses(:david_address)
posts_with_authors_at_address_titles = Post.find(:all,
:select => 'title',
:joins => 'JOIN authors ON authors.id = posts.author_id',
:conditions => [ 'authors.author_address_id = ?', address.id ]
)
assert_equal posts_with_authors_at_address_titles, Post.with_authors_at_address(address).find(:all, :select => 'title')
end
def test_extensions
assert_equal 1, Topic.anonymous_extension.one
assert_equal 2, Topic.named_extension.two
@ -154,4 +191,16 @@ class NamedScopeTest < ActiveRecord::TestCase
topics.empty? # use loaded (no query)
end
end
def test_find_all_should_behave_like_select
assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved)
end
def test_rand_should_select_a_random_object_from_proxy
assert Topic.approved.rand.is_a?(Topic)
end
def test_should_use_where_in_query_for_named_scope
assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises)
end
end

View file

@ -58,7 +58,7 @@ end
uses_mocha 'QueryCacheExpiryTest' do
class QueryCacheExpiryTest < ActiveRecord::TestCase
fixtures :tasks
fixtures :tasks, :posts, :categories, :categories_posts
def test_find
Task.connection.expects(:clear_query_cache).times(1)
@ -116,8 +116,9 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
def test_cache_is_expired_by_habtm_delete
ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
ActiveRecord::Base.cache do
c = Category.find(:first)
p = Post.find(:first)
c = Category.find(1)
p = Post.find(1)
assert p.categories.any?
p.categories.delete_all
end
end

View file

@ -160,9 +160,9 @@ class ReflectionTest < ActiveRecord::TestCase
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
assert_equal 20, Firm.reflect_on_all_associations.size
assert_equal 16, Firm.reflect_on_all_associations(:has_many).size
assert_equal 4, Firm.reflect_on_all_associations(:has_one).size
assert_equal 22, Firm.reflect_on_all_associations.size
assert_equal 17, Firm.reflect_on_all_associations(:has_many).size
assert_equal 5, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end

View file

@ -72,6 +72,52 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{:null => false}, output
end
def test_schema_dump_includes_limit_constraint_for_integer_columns
stream = StringIO.new
ActiveRecord::SchemaDumper.ignore_tables = [/^(?!integer_limits)/]
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
output = stream.string
if current_adapter?(:PostgreSQLAdapter)
assert_match %r{c_int_1.*:limit => 2}, output
assert_match %r{c_int_2.*:limit => 2}, output
# int 3 is 4 bytes in postgresql
assert_match %r{c_int_3.*}, output
assert_no_match %r{c_int_3.*:limit}, output
assert_match %r{c_int_4.*}, output
assert_no_match %r{c_int_4.*:limit}, output
elsif current_adapter?(:MysqlAdapter)
assert_match %r{c_int_1.*:limit => 1}, output
assert_match %r{c_int_2.*:limit => 2}, output
assert_match %r{c_int_3.*:limit => 3}, output
assert_match %r{c_int_4.*}, output
assert_no_match %r{c_int_4.*:limit}, output
elsif current_adapter?(:SQLiteAdapter)
assert_match %r{c_int_1.*:limit => 1}, output
assert_match %r{c_int_2.*:limit => 2}, output
assert_match %r{c_int_3.*:limit => 3}, output
assert_match %r{c_int_4.*:limit => 4}, output
end
assert_match %r{c_int_without_limit.*}, output
assert_no_match %r{c_int_without_limit.*:limit}, output
if current_adapter?(:SQLiteAdapter)
assert_match %r{c_int_5.*:limit => 5}, output
assert_match %r{c_int_6.*:limit => 6}, output
assert_match %r{c_int_7.*:limit => 7}, output
assert_match %r{c_int_8.*:limit => 8}, output
else
assert_match %r{c_int_5.*:limit => 8}, output
assert_match %r{c_int_6.*:limit => 8}, output
assert_match %r{c_int_7.*:limit => 8}, output
assert_match %r{c_int_8.*:limit => 8}, output
end
end
def test_schema_dump_with_string_ignored_table
stream = StringIO.new

View file

@ -451,6 +451,18 @@ class ValidationsTest < ActiveRecord::TestCase
t2.title = nil
assert t2.valid?, "should validate with nil"
assert t2.save, "should save with nil"
with_kcode('UTF8') do
t_utf8 = Topic.new("title" => "Я тоже уникальный!")
assert t_utf8.save, "Should save t_utf8 as unique"
# If database hasn't UTF-8 character set, this test fails
if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
assert !t2_utf8.valid?, "Shouldn't be valid"
assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
end
end
end
def test_validate_case_sensitive_uniqueness
@ -1059,6 +1071,18 @@ class ValidationsTest < ActiveRecord::TestCase
end
end
def test_validates_length_of_with_block
Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least %d words.",
:tokenizer => lambda {|str| str.scan(/\w+/) }
t = Topic.create!(:content => "this content should be long enough")
assert t.valid?
t.content = "not long enough"
assert !t.valid?
assert t.errors.on(:content)
assert_equal "Your essay must be at least 5 words.", t.errors[:content]
end
def test_validates_size_of_association_utf8
with_kcode('UTF8') do
assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
@ -1379,6 +1403,7 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
INTEGERS = [0, 10, -10] + INTEGER_STRINGS
BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
INFINITY = [1.0/0.0]
def setup
Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
@ -1390,27 +1415,27 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
Topic.validates_numericality_of :approved
invalid!(NIL + BLANK + JUNK)
valid!(FLOATS + INTEGERS + BIGDECIMAL)
valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
end
def test_validates_numericality_of_with_nil_allowed
Topic.validates_numericality_of :approved, :allow_nil => true
invalid!(BLANK + JUNK)
valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL)
valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
end
def test_validates_numericality_of_with_integer_only
Topic.validates_numericality_of :approved, :only_integer => true
invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL)
invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
valid!(INTEGERS)
end
def test_validates_numericality_of_with_integer_only_and_nil_allowed
Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL)
invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
valid!(NIL + INTEGERS)
end
@ -1431,7 +1456,7 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
def test_validates_numericality_with_equal_to
Topic.validates_numericality_of :approved, :equal_to => 10
invalid!([-10, 11], 'must be equal to 10')
invalid!([-10, 11] + INFINITY, 'must be equal to 10')
valid!([10])
end

View file

@ -1,6 +1,7 @@
class Author < ActiveRecord::Base
has_many :posts
has_many :posts_with_comments, :include => :comments, :class_name => "Post"
has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id'
has_many :posts_with_categories, :include => :categories, :class_name => "Post"
has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post"
has_many :posts_containing_the_letter_a, :class_name => "Post"
@ -31,6 +32,9 @@ class Author < ActiveRecord::Base
has_many :special_posts
has_many :special_post_comments, :through => :special_posts, :source => :comments
has_many :sti_posts, :class_name => 'StiPost'
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0"
has_many :nonexistant_comments, :through => :posts

View file

@ -2,6 +2,7 @@ class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
has_and_belongs_to_many :special_posts, :class_name => "Post"
has_and_belongs_to_many :other_posts, :class_name => "Post"
has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, :class_name => "Post", :include => :authors, :order => "authors.id"
has_and_belongs_to_many(:select_testing_posts,
:class_name => 'Post',

View file

@ -18,6 +18,13 @@ end
module Namespaced
class Company < ::Company
end
class Firm < ::Company
has_many :clients, :class_name => 'Namespaced::Client'
end
class Client < ::Company
end
end
class Firm < Company
@ -26,6 +33,7 @@ class Firm < Company
"AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
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
has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
has_many :limited_clients, :class_name => "Client", :order => "id", :limit => 1
@ -46,7 +54,8 @@ class Firm < Company
has_many :plain_clients, :class_name => 'Client'
has_many :readonly_clients, :class_name => 'Client', :readonly => true
has_one :account, :foreign_key => "firm_id", :dependent => :destroy
has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
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
end

View file

@ -43,6 +43,8 @@ class Developer < ActiveRecord::Base
has_many :audit_logs
named_scope :jamises, :conditions => {:name => 'Jamis'}
validates_inclusion_of :salary, :in => 50000..200000
validates_length_of :name, :within => 3..20
@ -56,7 +58,8 @@ class Developer < ActiveRecord::Base
end
class AuditLog < ActiveRecord::Base
belongs_to :developer
belongs_to :developer, :validate => true
belongs_to :unvalidated_developer, :class_name => 'Developer'
end
DeveloperSalary = Struct.new(:amount)

View file

@ -6,5 +6,5 @@ class Person < ActiveRecord::Base
has_many :references
has_many :jobs, :through => :references
has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
end

View file

@ -1,6 +1,11 @@
class Post < ActiveRecord::Base
named_scope :containing_the_letter_a, :conditions => "body LIKE '%a%'"
named_scope :with_authors_at_address, lambda { |address| {
:conditions => [ 'authors.author_address_id = ?', address.id ],
:joins => 'JOIN authors ON authors.id = posts.author_id'
}
}
belongs_to :author do
def greeting
"hello"

View file

@ -7,7 +7,7 @@ class Project < ActiveRecord::Base
has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
has_and_belongs_to_many :developers_named_david_with_hash_conditions, :class_name => "Developer", :conditions => { :name => 'David' }, :uniq => true
has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0"
has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id}'
has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id'
has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}"
has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"},
:after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},

View file

@ -4,6 +4,7 @@ class Topic < ActiveRecord::Base
{ :conditions => ['written_on < ?', time] }
}
named_scope :approved, :conditions => {:approved => true}
named_scope 'approved_as_string', :conditions => {:approved => true}
named_scope :replied, :conditions => ['replies_count > 0']
named_scope :anonymous_extension do
def one

View file

@ -1,5 +1,5 @@
ActiveRecord::Schema.define do
create_table :binary_fields, :force => true do |t|
create_table :binary_fields, :force => true, :options => 'CHARACTER SET latin1' do |t|
t.binary :tiny_blob, :limit => 255
t.binary :normal_blob, :limit => 65535
t.binary :medium_blob, :limit => 16777215
@ -9,4 +9,4 @@ ActiveRecord::Schema.define do
t.text :medium_text, :limit => 16777215
t.text :long_text, :limit => 2147483647
end
end
end

View file

@ -66,6 +66,7 @@ ActiveRecord::Schema.define do
create_table :categories, :force => true do |t|
t.string :name, :null => false
t.string :type
t.integer :categorizations_count
end
create_table :categories_posts, :force => true, :id => false do |t|
@ -407,6 +408,13 @@ ActiveRecord::Schema.define do
t.column :key, :string
end
create_table :integer_limits, :force => true do |t|
t.integer :"c_int_without_limit"
(1..8).each do |i|
t.integer :"c_int_#{i}", :limit => i
end
end
except 'SQLite' do
# fk_test_has_fk should be before fk_test_has_pk
create_table :fk_test_has_fk, :force => true do |t|