aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/models
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/models')
-rw-r--r--activerecord/test/models/admin.rb5
-rw-r--r--activerecord/test/models/admin/account.rb3
-rw-r--r--activerecord/test/models/admin/randomly_named_c1.rb3
-rw-r--r--activerecord/test/models/admin/user.rb40
-rw-r--r--activerecord/test/models/aircraft.rb4
-rw-r--r--activerecord/test/models/arunit2_model.rb3
-rw-r--r--activerecord/test/models/author.rb202
-rw-r--r--activerecord/test/models/auto_id.rb4
-rw-r--r--activerecord/test/models/autoloadable/extra_firm.rb2
-rw-r--r--activerecord/test/models/binary.rb2
-rw-r--r--activerecord/test/models/bird.rb12
-rw-r--r--activerecord/test/models/book.rb18
-rw-r--r--activerecord/test/models/boolean.rb2
-rw-r--r--activerecord/test/models/bulb.rb51
-rw-r--r--activerecord/test/models/cake_designer.rb3
-rw-r--r--activerecord/test/models/car.rb26
-rw-r--r--activerecord/test/models/categorization.rb19
-rw-r--r--activerecord/test/models/category.rb35
-rw-r--r--activerecord/test/models/chef.rb3
-rw-r--r--activerecord/test/models/citation.rb3
-rw-r--r--activerecord/test/models/club.rb23
-rw-r--r--activerecord/test/models/college.rb10
-rw-r--r--activerecord/test/models/column.rb3
-rw-r--r--activerecord/test/models/column_name.rb3
-rw-r--r--activerecord/test/models/comment.rb53
-rw-r--r--activerecord/test/models/company.rb223
-rw-r--r--activerecord/test/models/company_in_module.rb98
-rw-r--r--activerecord/test/models/computer.rb3
-rw-r--r--activerecord/test/models/contact.rb41
-rw-r--r--activerecord/test/models/contract.rb20
-rw-r--r--activerecord/test/models/country.rb7
-rw-r--r--activerecord/test/models/course.rb6
-rw-r--r--activerecord/test/models/customer.rb77
-rw-r--r--activerecord/test/models/dashboard.rb3
-rw-r--r--activerecord/test/models/default.rb2
-rw-r--r--activerecord/test/models/department.rb4
-rw-r--r--activerecord/test/models/developer.rb248
-rw-r--r--activerecord/test/models/dog.rb5
-rw-r--r--activerecord/test/models/dog_lover.rb5
-rw-r--r--activerecord/test/models/drink_designer.rb3
-rw-r--r--activerecord/test/models/edge.rb5
-rw-r--r--activerecord/test/models/electron.rb5
-rw-r--r--activerecord/test/models/engine.rb4
-rw-r--r--activerecord/test/models/entrant.rb3
-rw-r--r--activerecord/test/models/essay.rb5
-rw-r--r--activerecord/test/models/event.rb3
-rw-r--r--activerecord/test/models/eye.rb37
-rw-r--r--activerecord/test/models/face.rb9
-rw-r--r--activerecord/test/models/friendship.rb6
-rw-r--r--activerecord/test/models/guid.rb2
-rw-r--r--activerecord/test/models/hotel.rb6
-rw-r--r--activerecord/test/models/interest.rb5
-rw-r--r--activerecord/test/models/invoice.rb4
-rw-r--r--activerecord/test/models/item.rb7
-rw-r--r--activerecord/test/models/job.rb7
-rw-r--r--activerecord/test/models/joke.rb7
-rw-r--r--activerecord/test/models/keyboard.rb3
-rw-r--r--activerecord/test/models/legacy_thing.rb3
-rw-r--r--activerecord/test/models/lesson.rb11
-rw-r--r--activerecord/test/models/line_item.rb3
-rw-r--r--activerecord/test/models/liquid.rb4
-rw-r--r--activerecord/test/models/man.rb11
-rw-r--r--activerecord/test/models/matey.rb4
-rw-r--r--activerecord/test/models/member.rb35
-rw-r--r--activerecord/test/models/member_detail.rb7
-rw-r--r--activerecord/test/models/member_type.rb3
-rw-r--r--activerecord/test/models/membership.rb20
-rw-r--r--activerecord/test/models/minimalistic.rb2
-rw-r--r--activerecord/test/models/minivan.rb9
-rw-r--r--activerecord/test/models/mixed_case_monkey.rb3
-rw-r--r--activerecord/test/models/molecule.rb6
-rw-r--r--activerecord/test/models/movie.rb5
-rw-r--r--activerecord/test/models/order.rb4
-rw-r--r--activerecord/test/models/organization.rb12
-rw-r--r--activerecord/test/models/owner.rb34
-rw-r--r--activerecord/test/models/parrot.rb29
-rw-r--r--activerecord/test/models/person.rb141
-rw-r--r--activerecord/test/models/pet.rb15
-rw-r--r--activerecord/test/models/pirate.rb92
-rw-r--r--activerecord/test/models/possession.rb3
-rw-r--r--activerecord/test/models/post.rb219
-rw-r--r--activerecord/test/models/price_estimate.rb4
-rw-r--r--activerecord/test/models/project.rb29
-rw-r--r--activerecord/test/models/publisher.rb2
-rw-r--r--activerecord/test/models/publisher/article.rb4
-rw-r--r--activerecord/test/models/publisher/magazine.rb3
-rw-r--r--activerecord/test/models/randomly_named_c1.rb3
-rw-r--r--activerecord/test/models/rating.rb4
-rw-r--r--activerecord/test/models/reader.rb23
-rw-r--r--activerecord/test/models/record.rb2
-rw-r--r--activerecord/test/models/reference.rb22
-rw-r--r--activerecord/test/models/reply.rb61
-rw-r--r--activerecord/test/models/ship.rb25
-rw-r--r--activerecord/test/models/ship_part.rb7
-rw-r--r--activerecord/test/models/shop.rb17
-rw-r--r--activerecord/test/models/speedometer.rb6
-rw-r--r--activerecord/test/models/sponsor.rb7
-rw-r--r--activerecord/test/models/string_key_object.rb3
-rw-r--r--activerecord/test/models/student.rb4
-rw-r--r--activerecord/test/models/subject.rb16
-rw-r--r--activerecord/test/models/subscriber.rb8
-rw-r--r--activerecord/test/models/subscription.rb4
-rw-r--r--activerecord/test/models/tag.rb7
-rw-r--r--activerecord/test/models/tagging.rb13
-rw-r--r--activerecord/test/models/task.rb5
-rw-r--r--activerecord/test/models/topic.rb124
-rw-r--r--activerecord/test/models/toy.rb6
-rw-r--r--activerecord/test/models/traffic_light.rb4
-rw-r--r--activerecord/test/models/treasure.rb12
-rw-r--r--activerecord/test/models/treaty.rb7
-rw-r--r--activerecord/test/models/tyre.rb3
-rw-r--r--activerecord/test/models/uuid_child.rb3
-rw-r--r--activerecord/test/models/uuid_parent.rb3
-rw-r--r--activerecord/test/models/vegetables.rb24
-rw-r--r--activerecord/test/models/vertex.rb9
-rw-r--r--activerecord/test/models/warehouse_thing.rb5
-rw-r--r--activerecord/test/models/wheel.rb3
-rw-r--r--activerecord/test/models/without_table.rb3
-rw-r--r--activerecord/test/models/zine.rb3
119 files changed, 2558 insertions, 0 deletions
diff --git a/activerecord/test/models/admin.rb b/activerecord/test/models/admin.rb
new file mode 100644
index 0000000000..00e69fbed8
--- /dev/null
+++ b/activerecord/test/models/admin.rb
@@ -0,0 +1,5 @@
+module Admin
+ def self.table_name_prefix
+ 'admin_'
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/models/admin/account.rb b/activerecord/test/models/admin/account.rb
new file mode 100644
index 0000000000..46de28aae1
--- /dev/null
+++ b/activerecord/test/models/admin/account.rb
@@ -0,0 +1,3 @@
+class Admin::Account < ActiveRecord::Base
+ has_many :users
+end \ No newline at end of file
diff --git a/activerecord/test/models/admin/randomly_named_c1.rb b/activerecord/test/models/admin/randomly_named_c1.rb
new file mode 100644
index 0000000000..2f81d5b831
--- /dev/null
+++ b/activerecord/test/models/admin/randomly_named_c1.rb
@@ -0,0 +1,3 @@
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table
+end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
new file mode 100644
index 0000000000..48a110bd23
--- /dev/null
+++ b/activerecord/test/models/admin/user.rb
@@ -0,0 +1,40 @@
+class Admin::User < ActiveRecord::Base
+ class Coder
+ def initialize(default = {})
+ @default = default
+ end
+
+ def dump(o)
+ ActiveSupport::JSON.encode(o || @default)
+ end
+
+ def load(s)
+ s.present? ? ActiveSupport::JSON.decode(s) : @default.clone
+ end
+ end
+
+ belongs_to :account
+ store :params, accessors: [ :token ], coder: YAML
+ store :settings, :accessors => [ :color, :homepage ]
+ store_accessor :settings, :favorite_food
+ store :preferences, :accessors => [ :remember_login ]
+ store :json_data, :accessors => [ :height, :weight ], :coder => Coder.new
+ store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => Coder.new
+
+ def phone_number
+ read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/,'(\1) \2-\3')
+ end
+
+ def phone_number=(value)
+ write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/,''))
+ end
+
+ def color
+ super || 'red'
+ end
+
+ def color=(value)
+ value = 'blue' unless %w(black red green blue).include?(value)
+ super
+ end
+end
diff --git a/activerecord/test/models/aircraft.rb b/activerecord/test/models/aircraft.rb
new file mode 100644
index 0000000000..1f35ef45da
--- /dev/null
+++ b/activerecord/test/models/aircraft.rb
@@ -0,0 +1,4 @@
+class Aircraft < ActiveRecord::Base
+ self.pluralize_table_names = false
+ has_many :engines, :foreign_key => "car_id"
+end
diff --git a/activerecord/test/models/arunit2_model.rb b/activerecord/test/models/arunit2_model.rb
new file mode 100644
index 0000000000..04b8b15d3d
--- /dev/null
+++ b/activerecord/test/models/arunit2_model.rb
@@ -0,0 +1,3 @@
+class ARUnit2Model < ActiveRecord::Base
+ self.abstract_class = true
+end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
new file mode 100644
index 0000000000..8949cf5826
--- /dev/null
+++ b/activerecord/test/models/author.rb
@@ -0,0 +1,202 @@
+class Author < ActiveRecord::Base
+ has_many :posts
+ has_one :post
+ has_many :very_special_comments, :through => :posts
+ has_many :posts_with_comments, -> { includes(:comments) }, :class_name => "Post"
+ has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, :class_name => "Post"
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :class_name => "Post"
+ has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post"
+ has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
+ has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
+ has_many :posts_with_special_categorizations, :class_name => 'PostWithSpecialCategorization'
+ has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
+ has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
+ has_many :comments, through: :posts do
+ def ratings
+ Rating.joins(:comment).merge(self)
+ end
+ end
+ has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
+ has_many :comments_with_order_and_conditions, -> { order('comments.body').where("comments.body like 'Thank%'") }, :through => :posts, :source => :comments
+ has_many :comments_with_include, -> { includes(:post) }, :through => :posts, :source => :comments
+
+ has_many :first_posts
+ has_many :comments_on_first_posts, -> { order('posts.id desc, comments.id asc') }, :through => :first_posts, :source => :comments
+
+ has_one :first_post
+ has_one :comment_on_first_post, -> { order('posts.id desc, comments.id asc') }, :through => :first_post, :source => :comments
+
+ has_many :thinking_posts, -> { where(:title => 'So I was thinking') }, :dependent => :delete_all, :class_name => 'Post'
+ has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post'
+
+ has_many :welcome_posts_with_one_comment,
+ -> { where(title: 'Welcome to the weblog').where('comments_count = ?', 1) },
+ class_name: 'Post'
+ has_many :welcome_posts_with_comments,
+ -> { where(title: 'Welcome to the weblog').where(Post.arel_table[:comments_count].gt(0)) },
+ class_name: 'Post'
+
+ has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :funky_comments, :through => :posts, :source => :comments
+ has_many :ordered_uniq_comments, -> { distinct.order('comments.id') }, :through => :posts, :source => :comments
+ has_many :ordered_uniq_comments_desc, -> { distinct.order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :readonly_comments, -> { readonly }, :through => :posts, :source => :comments
+
+ 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, -> { where("posts.body = 'nonexistant'") }, :class_name => "SpecialPost"
+ has_many :special_nonexistant_post_comments, -> { where('comments.post_id' => 0) }, :through => :special_nonexistant_posts, :source => :comments
+ has_many :nonexistant_comments, :through => :posts
+
+ has_many :hello_posts, -> { where "posts.body = 'hello'" }, :class_name => "Post"
+ has_many :hello_post_comments, :through => :hello_posts, :source => :comments
+ has_many :posts_with_no_comments, -> { where('comments.id' => nil).includes(:comments) }, :class_name => 'Post'
+
+ has_many :hello_posts_with_hash_conditions, -> { where(:body => 'hello') }, :class_name => "Post"
+ has_many :hello_post_comments_with_hash_conditions, :through =>
+:hello_posts_with_hash_conditions, :source => :comments
+
+ has_many :other_posts, :class_name => "Post"
+ has_many :posts_with_callbacks, :class_name => "Post", :before_add => :log_before_adding,
+ :after_add => :log_after_adding,
+ :before_remove => :log_before_removing,
+ :after_remove => :log_after_removing
+ has_many :posts_with_proc_callbacks, :class_name => "Post",
+ :before_add => Proc.new {|o, r| o.post_log << "before_adding#{r.id || '<new>'}"},
+ :after_add => Proc.new {|o, r| o.post_log << "after_adding#{r.id || '<new>'}"},
+ :before_remove => Proc.new {|o, r| o.post_log << "before_removing#{r.id}"},
+ :after_remove => Proc.new {|o, r| o.post_log << "after_removing#{r.id}"}
+ has_many :posts_with_multiple_callbacks, :class_name => "Post",
+ :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id || '<new>'}"}],
+ :after_add => [:log_after_adding, Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}"}]
+ has_many :unchangable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding
+
+ has_many :categorizations
+ has_many :categories, :through => :categorizations
+ has_many :named_categories, :through => :categorizations
+
+ has_many :special_categorizations
+ has_many :special_categories, :through => :special_categorizations, :source => :category
+ has_one :special_category, :through => :special_categorizations, :source => :category
+
+ has_many :categories_like_general, -> { where(:name => 'General') }, :through => :categorizations, :source => :category, :class_name => 'Category'
+
+ has_many :categorized_posts, :through => :categorizations, :source => :post
+ has_many :unique_categorized_posts, -> { distinct }, :through => :categorizations, :source => :post
+
+ has_many :nothings, :through => :kateggorisatons, :class_name => 'Category'
+
+ has_many :author_favorites
+ has_many :favorite_authors, -> { order('name') }, :through => :author_favorites
+
+ has_many :taggings, :through => :posts, :source => :taggings
+ has_many :taggings_2, :through => :posts, :source => :tagging
+ has_many :tags, :through => :posts
+ has_many :post_categories, :through => :posts, :source => :categories
+ has_many :tagging_tags, :through => :taggings, :source => :tag
+
+ has_many :similar_posts, -> { distinct }, :through => :tags, :source => :tagged_posts
+ has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, :through => :posts, :source => :tags
+
+ has_many :tags_with_primary_key, :through => :posts
+
+ has_many :books
+ has_many :subscriptions, :through => :books
+ has_many :subscribers, -> { order("subscribers.nick") }, :through => :subscriptions
+ has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, :through => :subscriptions, :source => :subscriber
+
+ has_one :essay, :primary_key => :name, :as => :writer
+ has_one :essay_category, :through => :essay, :source => :category
+ has_one :essay_owner, :through => :essay, :source => :owner
+
+ has_one :essay_2, :primary_key => :name, :class_name => 'Essay', :foreign_key => :author_id
+ has_one :essay_category_2, :through => :essay_2, :source => :category
+
+ has_many :essays, :primary_key => :name, :as => :writer
+ has_many :essay_categories, :through => :essays, :source => :category
+ has_many :essay_owners, :through => :essays, :source => :owner
+
+ has_many :essays_2, :primary_key => :name, :class_name => 'Essay', :foreign_key => :author_id
+ has_many :essay_categories_2, :through => :essays_2, :source => :category
+
+ belongs_to :owned_essay, :primary_key => :name, :class_name => 'Essay'
+ has_one :owned_essay_category, :through => :owned_essay, :source => :category
+
+ belongs_to :author_address, :dependent => :destroy
+ belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
+
+ has_many :category_post_comments, :through => :categories, :source => :post_comments
+
+ has_many :misc_posts, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) }, :class_name => 'Post'
+ has_many :misc_post_first_blue_tags, :through => :misc_posts, :source => :first_blue_tags
+
+ has_many :misc_post_first_blue_tags_2, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) },
+ :through => :posts, :source => :first_blue_tags_2
+
+ has_many :posts_with_default_include, :class_name => 'PostWithDefaultInclude'
+ has_many :comments_on_posts_with_default_include, :through => :posts_with_default_include, :source => :comments
+
+ has_many :posts_with_signature, ->(record) { where("posts.title LIKE ?", "%by #{record.name.downcase}%") }, class_name: "Post"
+
+ scope :relation_include_posts, -> { includes(:posts) }
+ scope :relation_include_tags, -> { includes(:tags) }
+
+ attr_accessor :post_log
+ after_initialize :set_post_log
+
+ def set_post_log
+ @post_log = []
+ end
+
+ def label
+ "#{id}-#{name}"
+ end
+
+ def social
+ %w(twitter github)
+ end
+
+ validates_presence_of :name
+
+ private
+ def log_before_adding(object)
+ @post_log << "before_adding#{object.id || '<new>'}"
+ end
+
+ def log_after_adding(object)
+ @post_log << "after_adding#{object.id}"
+ end
+
+ def log_before_removing(object)
+ @post_log << "before_removing#{object.id}"
+ end
+
+ def log_after_removing(object)
+ @post_log << "after_removing#{object.id}"
+ end
+
+ def raise_exception(object)
+ raise Exception.new("You can't add a post")
+ end
+end
+
+class AuthorAddress < ActiveRecord::Base
+ has_one :author
+
+ def self.destroyed_author_address_ids
+ @destroyed_author_address_ids ||= []
+ end
+
+ before_destroy do |author_address|
+ AuthorAddress.destroyed_author_address_ids << author_address.id
+ end
+end
+
+class AuthorFavorite < ActiveRecord::Base
+ belongs_to :author
+ belongs_to :favorite_author, :class_name => "Author"
+end
diff --git a/activerecord/test/models/auto_id.rb b/activerecord/test/models/auto_id.rb
new file mode 100644
index 0000000000..82c6544bd5
--- /dev/null
+++ b/activerecord/test/models/auto_id.rb
@@ -0,0 +1,4 @@
+class AutoId < ActiveRecord::Base
+ self.table_name = "auto_id_tests"
+ self.primary_key = "auto_id"
+end
diff --git a/activerecord/test/models/autoloadable/extra_firm.rb b/activerecord/test/models/autoloadable/extra_firm.rb
new file mode 100644
index 0000000000..5578ba0d9b
--- /dev/null
+++ b/activerecord/test/models/autoloadable/extra_firm.rb
@@ -0,0 +1,2 @@
+class ExtraFirm < Company
+end
diff --git a/activerecord/test/models/binary.rb b/activerecord/test/models/binary.rb
new file mode 100644
index 0000000000..950c459199
--- /dev/null
+++ b/activerecord/test/models/binary.rb
@@ -0,0 +1,2 @@
+class Binary < ActiveRecord::Base
+end \ No newline at end of file
diff --git a/activerecord/test/models/bird.rb b/activerecord/test/models/bird.rb
new file mode 100644
index 0000000000..dff099c1fb
--- /dev/null
+++ b/activerecord/test/models/bird.rb
@@ -0,0 +1,12 @@
+class Bird < ActiveRecord::Base
+ belongs_to :pirate
+ validates_presence_of :name
+
+ accepts_nested_attributes_for :pirate
+
+ attr_accessor :cancel_save_from_callback
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ def cancel_save_callback_method
+ false
+ end
+end
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
new file mode 100644
index 0000000000..2170018068
--- /dev/null
+++ b/activerecord/test/models/book.rb
@@ -0,0 +1,18 @@
+class Book < ActiveRecord::Base
+ has_many :authors
+
+ has_many :citations, :foreign_key => 'book1_id'
+ has_many :references, -> { distinct }, through: :citations, source: :reference_of
+
+ has_many :subscriptions
+ has_many :subscribers, through: :subscriptions
+
+ enum status: [:proposed, :written, :published]
+ enum read_status: {unread: 0, reading: 2, read: 3}
+ enum nullable_status: [:single, :married]
+
+ def published!
+ super
+ "do publish work..."
+ end
+end
diff --git a/activerecord/test/models/boolean.rb b/activerecord/test/models/boolean.rb
new file mode 100644
index 0000000000..7bae22e5f9
--- /dev/null
+++ b/activerecord/test/models/boolean.rb
@@ -0,0 +1,2 @@
+class Boolean < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
new file mode 100644
index 0000000000..831a0d5387
--- /dev/null
+++ b/activerecord/test/models/bulb.rb
@@ -0,0 +1,51 @@
+class Bulb < ActiveRecord::Base
+ default_scope { where(:name => 'defaulty') }
+ belongs_to :car, :touch => true
+
+ attr_reader :scope_after_initialize, :attributes_after_initialize
+
+ after_initialize :record_scope_after_initialize
+ def record_scope_after_initialize
+ @scope_after_initialize = self.class.all
+ end
+
+ after_initialize :record_attributes_after_initialize
+ def record_attributes_after_initialize
+ @attributes_after_initialize = attributes.dup
+ end
+
+ def color=(color)
+ self[:color] = color.upcase + "!"
+ end
+
+ def self.new(attributes = {}, &block)
+ bulb_type = (attributes || {}).delete(:bulb_type)
+
+ if bulb_type.present?
+ bulb_class = "#{bulb_type.to_s.camelize}Bulb".constantize
+ bulb_class.new(attributes, &block)
+ else
+ super
+ end
+ end
+end
+
+class CustomBulb < Bulb
+ after_initialize :set_awesomeness
+
+ def set_awesomeness
+ self.frickinawesome = true if name == 'Dude'
+ end
+end
+
+class FunkyBulb < Bulb
+ before_destroy do
+ raise "before_destroy was called"
+ end
+end
+
+class FailedBulb < Bulb
+ before_destroy do
+ false
+ end
+end
diff --git a/activerecord/test/models/cake_designer.rb b/activerecord/test/models/cake_designer.rb
new file mode 100644
index 0000000000..9c57ef573a
--- /dev/null
+++ b/activerecord/test/models/cake_designer.rb
@@ -0,0 +1,3 @@
+class CakeDesigner < ActiveRecord::Base
+ has_one :chef, as: :employable
+end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
new file mode 100644
index 0000000000..db0f93f63b
--- /dev/null
+++ b/activerecord/test/models/car.rb
@@ -0,0 +1,26 @@
+class Car < ActiveRecord::Base
+ has_many :bulbs
+ has_many :all_bulbs, -> { unscope where: :name }, class_name: "Bulb"
+ has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy
+ has_many :failed_bulbs, class_name: 'FailedBulb', dependent: :destroy
+ has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
+
+ has_one :bulb
+
+ has_many :tyres
+ has_many :engines, :dependent => :destroy
+ has_many :wheels, :as => :wheelable, :dependent => :destroy
+
+ scope :incl_tyres, -> { includes(:tyres) }
+ scope :incl_engines, -> { includes(:engines) }
+
+ scope :order_using_new_style, -> { order('name asc') }
+end
+
+class CoolCar < Car
+ default_scope { order('name desc') }
+end
+
+class FastCar < Car
+ default_scope { order('name desc') }
+end
diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb
new file mode 100644
index 0000000000..6588531de6
--- /dev/null
+++ b/activerecord/test/models/categorization.rb
@@ -0,0 +1,19 @@
+class Categorization < ActiveRecord::Base
+ belongs_to :post
+ belongs_to :category
+ belongs_to :named_category, :class_name => 'Category', :foreign_key => :named_category_name, :primary_key => :name
+ belongs_to :author
+
+ has_many :post_taggings, :through => :author, :source => :taggings
+
+ belongs_to :author_using_custom_pk, :class_name => 'Author', :foreign_key => :author_id, :primary_key => :author_address_extra_id
+ has_many :authors_using_custom_pk, :class_name => 'Author', :foreign_key => :id, :primary_key => :category_id
+end
+
+class SpecialCategorization < ActiveRecord::Base
+ self.table_name = 'categorizations'
+ default_scope { where(:special => true) }
+
+ belongs_to :author
+ belongs_to :category
+end
diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb
new file mode 100644
index 0000000000..272223e1d8
--- /dev/null
+++ b/activerecord/test/models/category.rb
@@ -0,0 +1,35 @@
+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, -> { includes(:authors).order("authors.id") }, :class_name => "Post"
+
+ has_and_belongs_to_many :select_testing_posts,
+ -> { select 'posts.*, 1 as correctness_marker' },
+ :class_name => 'Post',
+ :foreign_key => 'category_id',
+ :association_foreign_key => 'post_id'
+
+ has_and_belongs_to_many :post_with_conditions,
+ -> { where :title => 'Yet Another Testing Title' },
+ :class_name => 'Post'
+
+ has_and_belongs_to_many :popular_grouped_posts, -> { group("posts.type").having("sum(comments.post_id) > 2").includes(:comments) }, :class_name => "Post"
+ has_and_belongs_to_many :posts_grouped_by_title, -> { group("title").select("title") }, :class_name => "Post"
+
+ def self.what_are_you
+ 'a category...'
+ end
+
+ has_many :categorizations
+ has_many :special_categorizations
+ has_many :post_comments, :through => :posts, :source => :comments
+
+ has_many :authors, :through => :categorizations
+ has_many :authors_with_select, -> { select 'authors.*, categorizations.post_id' }, :through => :categorizations, :source => :author
+
+ scope :general, -> { where(:name => 'General') }
+end
+
+class SpecialCategory < Category
+end
diff --git a/activerecord/test/models/chef.rb b/activerecord/test/models/chef.rb
new file mode 100644
index 0000000000..67a4e54f06
--- /dev/null
+++ b/activerecord/test/models/chef.rb
@@ -0,0 +1,3 @@
+class Chef < ActiveRecord::Base
+ belongs_to :employable, polymorphic: true
+end
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
new file mode 100644
index 0000000000..3d87eb795c
--- /dev/null
+++ b/activerecord/test/models/citation.rb
@@ -0,0 +1,3 @@
+class Citation < ActiveRecord::Base
+ belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id
+end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
new file mode 100644
index 0000000000..6ceafe5858
--- /dev/null
+++ b/activerecord/test/models/club.rb
@@ -0,0 +1,23 @@
+class Club < ActiveRecord::Base
+ has_one :membership
+ has_many :memberships, :inverse_of => false
+ has_many :members, :through => :memberships
+ has_one :sponsor
+ has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member"
+ belongs_to :category
+
+ has_many :favourites, -> { where(memberships: { favourite: true }) }, through: :memberships, source: :member
+
+ private
+
+ def private_method
+ "I'm sorry sir, this is a *private* club, not a *pirate* club"
+ end
+end
+
+class SuperClub < ActiveRecord::Base
+ self.table_name = "clubs"
+
+ has_many :memberships, class_name: 'SuperMembership', foreign_key: 'club_id'
+ has_many :members, through: :memberships
+end
diff --git a/activerecord/test/models/college.rb b/activerecord/test/models/college.rb
new file mode 100644
index 0000000000..501af4a8dd
--- /dev/null
+++ b/activerecord/test/models/college.rb
@@ -0,0 +1,10 @@
+require_dependency 'models/arunit2_model'
+require 'active_support/core_ext/object/with_options'
+
+class College < ARUnit2Model
+ has_many :courses
+
+ with_options dependent: :destroy do |assoc|
+ assoc.has_many :students, -> { where(active: true) }
+ end
+end
diff --git a/activerecord/test/models/column.rb b/activerecord/test/models/column.rb
new file mode 100644
index 0000000000..499358b4cf
--- /dev/null
+++ b/activerecord/test/models/column.rb
@@ -0,0 +1,3 @@
+class Column < ActiveRecord::Base
+ belongs_to :record
+end
diff --git a/activerecord/test/models/column_name.rb b/activerecord/test/models/column_name.rb
new file mode 100644
index 0000000000..460eb4fe20
--- /dev/null
+++ b/activerecord/test/models/column_name.rb
@@ -0,0 +1,3 @@
+class ColumnName < ActiveRecord::Base
+ self.table_name = "colnametests"
+end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
new file mode 100644
index 0000000000..15970758db
--- /dev/null
+++ b/activerecord/test/models/comment.rb
@@ -0,0 +1,53 @@
+class Comment < ActiveRecord::Base
+ scope :limit_by, lambda {|l| limit(l) }
+ scope :containing_the_letter_e, -> { where("comments.body LIKE '%e%'") }
+ scope :not_again, -> { where("comments.body NOT LIKE '%again%'") }
+ scope :for_first_post, -> { where(:post_id => 1) }
+ scope :for_first_author, -> { joins(:post).where("posts.author_id" => 1) }
+ scope :created, -> { all }
+
+ belongs_to :post, :counter_cache => true
+ belongs_to :author, polymorphic: true
+ belongs_to :resource, polymorphic: true
+
+ has_many :ratings
+
+ belongs_to :first_post, :foreign_key => :post_id
+
+ has_many :children, :class_name => 'Comment', :foreign_key => :parent_id
+ belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count
+
+ def self.what_are_you
+ 'a comment...'
+ end
+
+ def self.search_by_type(q)
+ where("#{QUOTED_TYPE} = ?", q)
+ end
+
+ def self.all_as_method
+ all
+ end
+ scope :all_as_scope, -> { all }
+
+ def to_s
+ body
+ end
+end
+
+class SpecialComment < Comment
+end
+
+class SubSpecialComment < SpecialComment
+end
+
+class VerySpecialComment < Comment
+end
+
+class CommentThatAutomaticallyAltersPostBody < Comment
+ belongs_to :post, class_name: "PostThatLoadsCommentsInAnAfterSaveHook", foreign_key: :post_id
+
+ after_save do |comment|
+ comment.post.update_attributes(body: "Automatically altered")
+ end
+end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
new file mode 100644
index 0000000000..76411ecb37
--- /dev/null
+++ b/activerecord/test/models/company.rb
@@ -0,0 +1,223 @@
+class AbstractCompany < ActiveRecord::Base
+ self.abstract_class = true
+end
+
+class Company < AbstractCompany
+ self.sequence_name = :companies_nonstd_seq
+
+ validates_presence_of :name
+
+ has_one :dummy_account, :foreign_key => "firm_id", :class_name => "Account"
+ has_many :contracts
+ has_many :developers, :through => :contracts
+
+ scope :of_first_firm, lambda {
+ joins(:account => :firm).
+ where('firms.id' => 1)
+ }
+
+ def arbitrary_method
+ "I am Jack's profound disappointment"
+ end
+
+ private
+
+ def private_method
+ "I am Jack's innermost fears and aspirations"
+ end
+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
+ to_param :name
+
+ has_many :clients, -> { order "id" }, :dependent => :destroy, :before_remove => :log_before_remove, :after_remove => :log_after_remove
+ has_many :unsorted_clients, :class_name => "Client"
+ has_many :unsorted_clients_with_symbol, :class_name => :Client
+ has_many :clients_sorted_desc, -> { order "id DESC" }, :class_name => "Client"
+ has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :inverse_of => :firm
+ has_many :clients_ordered_by_name, -> { order "name" }, :class_name => "Client"
+ has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
+ has_many :dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :destroy
+ has_many :exclusively_dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :limited_clients, -> { limit 1 }, :class_name => "Client"
+ has_many :clients_with_interpolated_conditions, ->(firm) { where "rating > #{firm.rating}" }, :class_name => "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
+ has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client"
+ has_many :plain_clients, :class_name => 'Client'
+ has_many :clients_using_primary_key, :class_name => 'Client',
+ :primary_key => 'name', :foreign_key => 'firm_name'
+ has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client',
+ :primary_key => 'name', :foreign_key => 'firm_name', :dependent => :delete_all
+ has_many :clients_grouped_by_firm_id, -> { group("firm_id").select("firm_id") }, :class_name => "Client"
+ has_many :clients_grouped_by_name, -> { group("name").select("name") }, :class_name => "Client"
+
+ 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, -> { select("id, firm_id") }, :foreign_key => "firm_id", :class_name=>'Account'
+ has_one :readonly_account, -> { readonly }, :foreign_key => "firm_id", :class_name => "Account"
+ # added order by id as in fixtures there are two accounts for Rails Core
+ # Oracle tests were failing because of that as the second fixture was selected
+ has_one :account_using_primary_key, -> { order('id') }, :primary_key => "firm_id", :class_name => "Account"
+ has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account"
+ has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
+
+ has_one :account_limit_500_with_hash_conditions, -> { where :credit_limit => 500 }, :foreign_key => "firm_id", :class_name => "Account"
+
+ has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+ has_many :accounts
+ has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+
+ has_many :association_with_references, -> { references(:foo) }, :class_name => 'Client'
+
+ def log
+ @log ||= []
+ end
+
+ private
+ def log_before_remove(record)
+ log << "before_remove#{record.id}"
+ end
+
+ def log_after_remove(record)
+ log << "after_remove#{record.id}"
+ end
+end
+
+class DependentFirm < Company
+ has_one :account, :foreign_key => "firm_id", :dependent => :nullify
+ has_many :companies, :foreign_key => 'client_of', :dependent => :nullify
+ has_one :company, :foreign_key => 'client_of', :dependent => :nullify
+end
+
+class RestrictedWithExceptionFirm < Company
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_exception
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_exception
+end
+
+class RestrictedWithErrorFirm < Company
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_error
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_error
+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, -> { select("id") }, :class_name => "Firm", :foreign_key => "firm_id"
+ belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
+ belongs_to :firm_with_condition, -> { where "1 = ?", 1 }, :class_name => "Firm", :foreign_key => "client_of"
+ belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name"
+ belongs_to :firm_with_primary_key_symbols, :class_name => "Firm", :primary_key => :name, :foreign_key => :firm_name
+ belongs_to :readonly_firm, -> { readonly }, :class_name => "Firm", :foreign_key => "firm_id"
+ belongs_to :bob_firm, -> { where :name => "Bob" }, :class_name => "Firm", :foreign_key => "client_of"
+ has_many :accounts, :through => :firm, :source => :accounts
+ belongs_to :account
+
+ validate do
+ firm
+ end
+
+ class RaisedOnSave < RuntimeError; end
+ attr_accessor :raise_on_save
+ before_save do
+ raise RaisedOnSave if raise_on_save
+ end
+
+ class RaisedOnDestroy < RuntimeError; end
+ attr_accessor :raise_on_destroy
+ before_destroy do
+ raise RaisedOnDestroy if raise_on_destroy
+ end
+
+ # Record destruction so we can test whether firm.clients.clear has
+ # is calling client.destroy, deleting from the database, or setting
+ # foreign keys to NULL.
+ def self.destroyed_client_ids
+ @destroyed_client_ids ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ before_destroy do |client|
+ if client.firm
+ Client.destroyed_client_ids[client.firm.id] << client.id
+ end
+ true
+ end
+
+ before_destroy :overwrite_to_raise
+
+ # Used to test that read and question methods are not generated for these attributes
+ def rating?
+ query_attribute :rating
+ end
+
+ def overwrite_to_raise
+ end
+
+ class << self
+ private
+
+ def private_method
+ "darkness"
+ end
+ end
+end
+
+class ExclusivelyDependentFirm < Company
+ has_one :account, :foreign_key => "firm_id", :dependent => :delete
+ has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+end
+
+class SpecialClient < Client
+end
+
+class VerySpecialClient < SpecialClient
+end
+
+class Account < ActiveRecord::Base
+ belongs_to :firm, :class_name => 'Company'
+ belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false
+
+ alias_attribute :available_credit, :credit_limit
+
+ def self.destroyed_account_ids
+ @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ # Test private kernel method through collection proxy using has_many.
+ def self.open
+ where('firm_name = ?', '37signals')
+ end
+
+ before_destroy do |account|
+ if account.firm
+ Account.destroyed_account_ids[account.firm.id] << account.id
+ end
+ true
+ end
+
+ validate :check_empty_credit_limit
+
+ protected
+
+ def check_empty_credit_limit
+ errors.add_on_empty "credit_limit"
+ end
+
+ private
+
+ def private_method
+ "Sir, yes sir!"
+ end
+end
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
new file mode 100644
index 0000000000..dae102d12b
--- /dev/null
+++ b/activerecord/test/models/company_in_module.rb
@@ -0,0 +1,98 @@
+require 'active_support/core_ext/object/with_options'
+
+module MyApplication
+ module Business
+ class Company < ActiveRecord::Base
+ end
+
+ class Firm < Company
+ has_many :clients, -> { order("id") }, :dependent => :destroy
+ has_many :clients_sorted_desc, -> { order("id DESC") }, :class_name => "Client"
+ has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
+ has_one :account, :class_name => 'MyApplication::Billing::Account', :dependent => :destroy
+ end
+
+ class Client < Company
+ belongs_to :firm, :foreign_key => "client_of"
+ belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
+
+ class Contact < ActiveRecord::Base; end
+ end
+
+ class Developer < ActiveRecord::Base
+ has_and_belongs_to_many :projects
+ validates_length_of :name, :within => (3..20)
+ end
+
+ class Project < ActiveRecord::Base
+ has_and_belongs_to_many :developers
+ end
+
+ module Prefixed
+ def self.table_name_prefix
+ 'prefixed_'
+ end
+
+ class Company < ActiveRecord::Base
+ end
+
+ class Firm < Company
+ self.table_name = 'companies'
+ end
+
+ module Nested
+ class Company < ActiveRecord::Base
+ end
+ end
+ end
+
+ module Suffixed
+ def self.table_name_suffix
+ '_suffixed'
+ end
+
+ class Company < ActiveRecord::Base
+ end
+
+ class Firm < Company
+ self.table_name = 'companies'
+ end
+
+ module Nested
+ class Company < ActiveRecord::Base
+ end
+ end
+ end
+ end
+
+ module Billing
+ class Firm < ActiveRecord::Base
+ self.table_name = 'companies'
+ end
+
+ module Nested
+ class Firm < ActiveRecord::Base
+ self.table_name = 'companies'
+ end
+ end
+
+ class Account < ActiveRecord::Base
+ with_options(:foreign_key => :firm_id) do |i|
+ i.belongs_to :firm, :class_name => 'MyApplication::Business::Firm'
+ i.belongs_to :qualified_billing_firm, :class_name => 'MyApplication::Billing::Firm'
+ i.belongs_to :unqualified_billing_firm, :class_name => 'Firm'
+ i.belongs_to :nested_qualified_billing_firm, :class_name => 'MyApplication::Billing::Nested::Firm'
+ i.belongs_to :nested_unqualified_billing_firm, :class_name => 'Nested::Firm'
+ end
+
+ validate :check_empty_credit_limit
+
+ protected
+
+ def check_empty_credit_limit
+ errors.add_on_empty "credit_limit"
+ end
+ end
+ end
+end
diff --git a/activerecord/test/models/computer.rb b/activerecord/test/models/computer.rb
new file mode 100644
index 0000000000..cc8deb1b2b
--- /dev/null
+++ b/activerecord/test/models/computer.rb
@@ -0,0 +1,3 @@
+class Computer < ActiveRecord::Base
+ belongs_to :developer, :foreign_key=>'developer'
+end
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
new file mode 100644
index 0000000000..3ea17c3abf
--- /dev/null
+++ b/activerecord/test/models/contact.rb
@@ -0,0 +1,41 @@
+module ContactFakeColumns
+ def self.extended(base)
+ base.class_eval do
+ establish_connection(:adapter => 'fake')
+
+ connection.tables = [table_name]
+ connection.primary_keys = {
+ table_name => 'id'
+ }
+
+ column :id, :integer
+ column :name, :string
+ column :age, :integer
+ column :avatar, :binary
+ column :created_at, :datetime
+ column :awesome, :boolean
+ column :preferences, :string
+ column :alternative_id, :integer
+
+ serialize :preferences
+
+ belongs_to :alternative, :class_name => 'Contact'
+ end
+ end
+
+ # mock out self.columns so no pesky db is needed for these tests
+ def column(name, sql_type = nil, options = {})
+ connection.merge_column(table_name, name, sql_type, options)
+ end
+end
+
+class Contact < ActiveRecord::Base
+ extend ContactFakeColumns
+end
+
+class ContactSti < ActiveRecord::Base
+ extend ContactFakeColumns
+ column :type, :string
+
+ def type; 'ContactSti' end
+end
diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb
new file mode 100644
index 0000000000..cdf7b267b5
--- /dev/null
+++ b/activerecord/test/models/contract.rb
@@ -0,0 +1,20 @@
+class Contract < ActiveRecord::Base
+ belongs_to :company
+ belongs_to :developer
+ belongs_to :firm, :foreign_key => 'company_id'
+
+ before_save :hi
+ after_save :bye
+
+ attr_accessor :hi_count, :bye_count
+
+ def hi
+ @hi_count ||= 0
+ @hi_count += 1
+ end
+
+ def bye
+ @bye_count ||= 0
+ @bye_count += 1
+ end
+end
diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb
new file mode 100644
index 0000000000..7db9a4e731
--- /dev/null
+++ b/activerecord/test/models/country.rb
@@ -0,0 +1,7 @@
+class Country < ActiveRecord::Base
+
+ self.primary_key = :country_id
+
+ has_and_belongs_to_many :treaties
+
+end
diff --git a/activerecord/test/models/course.rb b/activerecord/test/models/course.rb
new file mode 100644
index 0000000000..f3d0e05ff7
--- /dev/null
+++ b/activerecord/test/models/course.rb
@@ -0,0 +1,6 @@
+require_dependency 'models/arunit2_model'
+
+class Course < ARUnit2Model
+ belongs_to :college
+ has_many :entrants
+end
diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb
new file mode 100644
index 0000000000..7e8e82542f
--- /dev/null
+++ b/activerecord/test/models/customer.rb
@@ -0,0 +1,77 @@
+class Customer < ActiveRecord::Base
+ cattr_accessor :gps_conversion_was_run
+
+ composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true
+ composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
+ composed_of :gps_location, :allow_nil => true
+ composed_of :non_blank_gps_location, :class_name => "GpsLocation", :allow_nil => true, :mapping => %w(gps_location gps_location),
+ :converter => lambda { |gps| self.gps_conversion_was_run = true; gps.blank? ? nil : GpsLocation.new(gps)}
+ composed_of :fullname, :mapping => %w(name to_s), :constructor => Proc.new { |name| Fullname.parse(name) }, :converter => :parse
+end
+
+class Address
+ attr_reader :street, :city, :country
+
+ def initialize(street, city, country)
+ @street, @city, @country = street, city, country
+ end
+
+ def close_to?(other_address)
+ city == other_address.city && country == other_address.country
+ end
+
+ def ==(other)
+ other.is_a?(self.class) && other.street == street && other.city == city && other.country == country
+ end
+end
+
+class Money
+ attr_reader :amount, :currency
+
+ EXCHANGE_RATES = { "USD_TO_DKK" => 6, "DKK_TO_USD" => 0.6 }
+
+ def initialize(amount, currency = "USD")
+ @amount, @currency = amount, currency
+ end
+
+ def exchange_to(other_currency)
+ Money.new((amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor, other_currency)
+ end
+end
+
+class GpsLocation
+ attr_reader :gps_location
+
+ def initialize(gps_location)
+ @gps_location = gps_location
+ end
+
+ def latitude
+ gps_location.split("x").first
+ end
+
+ def longitude
+ gps_location.split("x").last
+ end
+
+ def ==(other)
+ self.latitude == other.latitude && self.longitude == other.longitude
+ end
+end
+
+class Fullname
+ attr_reader :first, :last
+
+ def self.parse(str)
+ return nil unless str
+ new(*str.to_s.split)
+ end
+
+ def initialize(first, last = nil)
+ @first, @last = first, last
+ end
+
+ def to_s
+ "#{first} #{last.upcase}"
+ end
+end
diff --git a/activerecord/test/models/dashboard.rb b/activerecord/test/models/dashboard.rb
new file mode 100644
index 0000000000..1b3b54545f
--- /dev/null
+++ b/activerecord/test/models/dashboard.rb
@@ -0,0 +1,3 @@
+class Dashboard < ActiveRecord::Base
+ self.primary_key = :dashboard_id
+end
diff --git a/activerecord/test/models/default.rb b/activerecord/test/models/default.rb
new file mode 100644
index 0000000000..887e9cc999
--- /dev/null
+++ b/activerecord/test/models/default.rb
@@ -0,0 +1,2 @@
+class Default < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/department.rb b/activerecord/test/models/department.rb
new file mode 100644
index 0000000000..08004a0ed3
--- /dev/null
+++ b/activerecord/test/models/department.rb
@@ -0,0 +1,4 @@
+class Department < ActiveRecord::Base
+ has_many :chefs
+ belongs_to :hotel
+end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
new file mode 100644
index 0000000000..5bd2f00129
--- /dev/null
+++ b/activerecord/test/models/developer.rb
@@ -0,0 +1,248 @@
+require 'ostruct'
+
+module DeveloperProjectsAssociationExtension2
+ def find_least_recent
+ order("id ASC").first
+ end
+end
+
+class Developer < ActiveRecord::Base
+ has_and_belongs_to_many :projects do
+ def find_most_recent
+ order("id DESC").first
+ end
+ end
+
+ accepts_nested_attributes_for :projects
+
+ has_and_belongs_to_many :projects_extended_by_name,
+ -> { extending(DeveloperProjectsAssociationExtension) },
+ :class_name => "Project",
+ :join_table => "developers_projects",
+ :association_foreign_key => "project_id"
+
+ has_and_belongs_to_many :projects_extended_by_name_twice,
+ -> { extending(DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2) },
+ :class_name => "Project",
+ :join_table => "developers_projects",
+ :association_foreign_key => "project_id"
+
+ has_and_belongs_to_many :projects_extended_by_name_and_block,
+ -> { extending(DeveloperProjectsAssociationExtension) },
+ :class_name => "Project",
+ :join_table => "developers_projects",
+ :association_foreign_key => "project_id" do
+ def find_least_recent
+ order("id ASC").first
+ end
+ end
+
+ has_and_belongs_to_many :special_projects, :join_table => 'developers_projects', :association_foreign_key => 'project_id'
+ has_and_belongs_to_many :sym_special_projects,
+ :join_table => :developers_projects,
+ :association_foreign_key => 'project_id',
+ :class_name => 'SpecialProject'
+
+ has_many :audit_logs
+ has_many :contracts
+ has_many :firms, :through => :contracts, :source => :firm
+
+ scope :jamises, -> { where(:name => 'Jamis') }
+
+ validates_inclusion_of :salary, :in => 50000..200000
+ validates_length_of :name, :within => 3..20
+
+ before_create do |developer|
+ developer.audit_logs.build :message => "Computer created"
+ end
+
+ def log=(message)
+ audit_logs.build :message => message
+ end
+
+ after_find :track_instance_count
+ cattr_accessor :instance_count
+
+ def track_instance_count
+ self.class.instance_count ||= 0
+ self.class.instance_count += 1
+ end
+ private :track_instance_count
+
+end
+
+class AuditLog < ActiveRecord::Base
+ belongs_to :developer, :validate => true
+ belongs_to :unvalidated_developer, :class_name => 'Developer'
+end
+
+class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :join_table => 'developers_projects', :foreign_key => 'developer_id'
+ before_destroy :raise_if_projects_empty!
+
+ def raise_if_projects_empty!
+ raise if projects.empty?
+ end
+end
+
+class DeveloperWithSelect < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope { select('name') }
+end
+
+class DeveloperWithIncludes < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_many :audit_logs, :foreign_key => :developer_id
+ default_scope { includes(:audit_logs) }
+end
+
+class DeveloperFilteredOnJoins < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ def self.default_scope
+ joins(:projects).where(:projects => { :name => 'Active Controller' })
+ end
+end
+
+class DeveloperOrderedBySalary < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope { order('salary DESC') }
+
+ scope :by_name, -> { order('name DESC') }
+end
+
+class DeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope { where("name = 'David'") }
+end
+
+class LazyLambdaDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope lambda { where(:name => 'David') }
+end
+
+class LazyBlockDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope { where(:name => 'David') }
+end
+
+class CallableDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope OpenStruct.new(:call => where(:name => 'David'))
+end
+
+class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ def self.default_scope
+ where(:name => 'David')
+ end
+end
+
+class ClassMethodReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ scope :david, -> { where(:name => 'David') }
+
+ def self.default_scope
+ david
+ end
+end
+
+class LazyBlockReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ scope :david, -> { where(:name => 'David') }
+ default_scope { david }
+end
+
+class DeveloperCalledJamis < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ default_scope { where(:name => 'Jamis') }
+ scope :poor, -> { where('salary < 150000') }
+ scope :david, -> { where name: "David" }
+ scope :david2, -> { unscoped.where name: "David" }
+end
+
+class PoorDeveloperCalledJamis < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ default_scope -> { where(:name => 'Jamis', :salary => 50000) }
+end
+
+class InheritedPoorDeveloperCalledJamis < DeveloperCalledJamis
+ self.table_name = 'developers'
+
+ default_scope -> { where(:salary => 50000) }
+end
+
+class MultiplePoorDeveloperCalledJamis < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ default_scope -> { where(:name => 'Jamis') }
+ default_scope -> { where(:salary => 50000) }
+end
+
+module SalaryDefaultScope
+ extend ActiveSupport::Concern
+
+ included { default_scope { where(:salary => 50000) } }
+end
+
+class ModuleIncludedPoorDeveloperCalledJamis < DeveloperCalledJamis
+ self.table_name = 'developers'
+
+ include SalaryDefaultScope
+end
+
+class EagerDeveloperWithDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ default_scope { includes(:projects) }
+end
+
+class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ def self.default_scope
+ includes(:projects)
+ end
+end
+
+class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ default_scope lambda { includes(:projects) }
+end
+
+class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ default_scope { includes(:projects) }
+end
+
+class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ default_scope OpenStruct.new(:call => includes(:projects))
+end
+
+class ThreadsafeDeveloper < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ def self.default_scope
+ sleep 0.05 if Thread.current[:long_default_scope]
+ limit(1)
+ end
+end
+
+class CachedDeveloper < ActiveRecord::Base
+ self.table_name = "developers"
+ self.cache_timestamp_format = :number
+end
diff --git a/activerecord/test/models/dog.rb b/activerecord/test/models/dog.rb
new file mode 100644
index 0000000000..b02b8447b8
--- /dev/null
+++ b/activerecord/test/models/dog.rb
@@ -0,0 +1,5 @@
+class Dog < ActiveRecord::Base
+ belongs_to :breeder, class_name: "DogLover", counter_cache: :bred_dogs_count
+ belongs_to :trainer, class_name: "DogLover", counter_cache: :trained_dogs_count
+ belongs_to :doglover, foreign_key: :dog_lover_id, class_name: "DogLover", counter_cache: true
+end
diff --git a/activerecord/test/models/dog_lover.rb b/activerecord/test/models/dog_lover.rb
new file mode 100644
index 0000000000..2c5be94aea
--- /dev/null
+++ b/activerecord/test/models/dog_lover.rb
@@ -0,0 +1,5 @@
+class DogLover < ActiveRecord::Base
+ has_many :trained_dogs, class_name: "Dog", foreign_key: :trainer_id, dependent: :destroy
+ has_many :bred_dogs, class_name: "Dog", foreign_key: :breeder_id
+ has_many :dogs
+end
diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb
new file mode 100644
index 0000000000..2db968ef11
--- /dev/null
+++ b/activerecord/test/models/drink_designer.rb
@@ -0,0 +1,3 @@
+class DrinkDesigner < ActiveRecord::Base
+ has_one :chef, as: :employable
+end
diff --git a/activerecord/test/models/edge.rb b/activerecord/test/models/edge.rb
new file mode 100644
index 0000000000..55e0c31fcb
--- /dev/null
+++ b/activerecord/test/models/edge.rb
@@ -0,0 +1,5 @@
+# This class models an edge in a directed graph.
+class Edge < ActiveRecord::Base
+ belongs_to :source, :class_name => 'Vertex', :foreign_key => 'source_id'
+ belongs_to :sink, :class_name => 'Vertex', :foreign_key => 'sink_id'
+end
diff --git a/activerecord/test/models/electron.rb b/activerecord/test/models/electron.rb
new file mode 100644
index 0000000000..6fc270673f
--- /dev/null
+++ b/activerecord/test/models/electron.rb
@@ -0,0 +1,5 @@
+class Electron < ActiveRecord::Base
+ belongs_to :molecule
+
+ validates_presence_of :name
+end
diff --git a/activerecord/test/models/engine.rb b/activerecord/test/models/engine.rb
new file mode 100644
index 0000000000..851ff8c22b
--- /dev/null
+++ b/activerecord/test/models/engine.rb
@@ -0,0 +1,4 @@
+class Engine < ActiveRecord::Base
+ belongs_to :my_car, :class_name => 'Car', :foreign_key => 'car_id', :counter_cache => :engines_count
+end
+
diff --git a/activerecord/test/models/entrant.rb b/activerecord/test/models/entrant.rb
new file mode 100644
index 0000000000..4682ce48c8
--- /dev/null
+++ b/activerecord/test/models/entrant.rb
@@ -0,0 +1,3 @@
+class Entrant < ActiveRecord::Base
+ belongs_to :course
+end
diff --git a/activerecord/test/models/essay.rb b/activerecord/test/models/essay.rb
new file mode 100644
index 0000000000..ec4b982b5b
--- /dev/null
+++ b/activerecord/test/models/essay.rb
@@ -0,0 +1,5 @@
+class Essay < ActiveRecord::Base
+ belongs_to :writer, :primary_key => :name, :polymorphic => true
+ belongs_to :category, :primary_key => :name
+ has_one :owner, :primary_key => :name
+end
diff --git a/activerecord/test/models/event.rb b/activerecord/test/models/event.rb
new file mode 100644
index 0000000000..99fa0feeb7
--- /dev/null
+++ b/activerecord/test/models/event.rb
@@ -0,0 +1,3 @@
+class Event < ActiveRecord::Base
+ validates_uniqueness_of :title
+end \ No newline at end of file
diff --git a/activerecord/test/models/eye.rb b/activerecord/test/models/eye.rb
new file mode 100644
index 0000000000..dc8ae2b3f6
--- /dev/null
+++ b/activerecord/test/models/eye.rb
@@ -0,0 +1,37 @@
+class Eye < ActiveRecord::Base
+ attr_reader :after_create_callbacks_stack
+ attr_reader :after_update_callbacks_stack
+ attr_reader :after_save_callbacks_stack
+
+ # Callbacks configured before the ones has_one sets up.
+ after_create :trace_after_create
+ after_update :trace_after_update
+ after_save :trace_after_save
+
+ has_one :iris
+ accepts_nested_attributes_for :iris
+
+ # Callbacks configured after the ones has_one sets up.
+ after_create :trace_after_create2
+ after_update :trace_after_update2
+ after_save :trace_after_save2
+
+ def trace_after_create
+ (@after_create_callbacks_stack ||= []) << !iris.persisted?
+ end
+ alias trace_after_create2 trace_after_create
+
+ def trace_after_update
+ (@after_update_callbacks_stack ||= []) << iris.changed?
+ end
+ alias trace_after_update2 trace_after_update
+
+ def trace_after_save
+ (@after_save_callbacks_stack ||= []) << iris.changed?
+ end
+ alias trace_after_save2 trace_after_save
+end
+
+class Iris < ActiveRecord::Base
+ belongs_to :eye
+end
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
new file mode 100644
index 0000000000..91e46f83e5
--- /dev/null
+++ b/activerecord/test/models/face.rb
@@ -0,0 +1,9 @@
+class Face < ActiveRecord::Base
+ belongs_to :man, :inverse_of => :face
+ belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_face
+ # Oracle identifier lengh is limited to 30 bytes or less, `polymorphic` renamed `poly`
+ belongs_to :poly_man_without_inverse, :polymorphic => true
+ # These is a "broken" inverse_of for the purposes of testing
+ belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face
+ belongs_to :horrible_polymorphic_man, :polymorphic => true, :inverse_of => :horrible_polymorphic_face
+end
diff --git a/activerecord/test/models/friendship.rb b/activerecord/test/models/friendship.rb
new file mode 100644
index 0000000000..4b411ca8e0
--- /dev/null
+++ b/activerecord/test/models/friendship.rb
@@ -0,0 +1,6 @@
+class Friendship < ActiveRecord::Base
+ belongs_to :friend, class_name: 'Person'
+ # friend_too exists to test a bug, and probably shouldn't be used elsewhere
+ belongs_to :friend_too, foreign_key: 'friend_id', class_name: 'Person', counter_cache: :friends_too_count
+ belongs_to :follower, class_name: 'Person'
+end
diff --git a/activerecord/test/models/guid.rb b/activerecord/test/models/guid.rb
new file mode 100644
index 0000000000..9208dc28fa
--- /dev/null
+++ b/activerecord/test/models/guid.rb
@@ -0,0 +1,2 @@
+class Guid < ActiveRecord::Base
+end \ No newline at end of file
diff --git a/activerecord/test/models/hotel.rb b/activerecord/test/models/hotel.rb
new file mode 100644
index 0000000000..b352cd22f3
--- /dev/null
+++ b/activerecord/test/models/hotel.rb
@@ -0,0 +1,6 @@
+class Hotel < ActiveRecord::Base
+ has_many :departments
+ has_many :chefs, through: :departments
+ has_many :cake_designers, source_type: 'CakeDesigner', source: :employable, through: :chefs
+ has_many :drink_designers, source_type: 'DrinkDesigner', source: :employable, through: :chefs
+end
diff --git a/activerecord/test/models/interest.rb b/activerecord/test/models/interest.rb
new file mode 100644
index 0000000000..d5d9226204
--- /dev/null
+++ b/activerecord/test/models/interest.rb
@@ -0,0 +1,5 @@
+class Interest < ActiveRecord::Base
+ belongs_to :man, :inverse_of => :interests
+ belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_interests
+ belongs_to :zine, :inverse_of => :interests
+end
diff --git a/activerecord/test/models/invoice.rb b/activerecord/test/models/invoice.rb
new file mode 100644
index 0000000000..fc6ef0230e
--- /dev/null
+++ b/activerecord/test/models/invoice.rb
@@ -0,0 +1,4 @@
+class Invoice < ActiveRecord::Base
+ has_many :line_items, :autosave => true
+ before_save {|record| record.balance = record.line_items.map(&:amount).sum }
+end
diff --git a/activerecord/test/models/item.rb b/activerecord/test/models/item.rb
new file mode 100644
index 0000000000..c2571dd7fb
--- /dev/null
+++ b/activerecord/test/models/item.rb
@@ -0,0 +1,7 @@
+class AbstractItem < ActiveRecord::Base
+ self.abstract_class = true
+ has_one :tagging, :as => :taggable
+end
+
+class Item < AbstractItem
+end
diff --git a/activerecord/test/models/job.rb b/activerecord/test/models/job.rb
new file mode 100644
index 0000000000..f7b0e787b1
--- /dev/null
+++ b/activerecord/test/models/job.rb
@@ -0,0 +1,7 @@
+class Job < ActiveRecord::Base
+ has_many :references
+ has_many :people, :through => :references
+ belongs_to :ideal_reference, :class_name => 'Reference'
+
+ has_many :agents, :through => :people
+end
diff --git a/activerecord/test/models/joke.rb b/activerecord/test/models/joke.rb
new file mode 100644
index 0000000000..edda4655dc
--- /dev/null
+++ b/activerecord/test/models/joke.rb
@@ -0,0 +1,7 @@
+class Joke < ActiveRecord::Base
+ self.table_name = 'funny_jokes'
+end
+
+class GoodJoke < ActiveRecord::Base
+ self.table_name = 'funny_jokes'
+end
diff --git a/activerecord/test/models/keyboard.rb b/activerecord/test/models/keyboard.rb
new file mode 100644
index 0000000000..39347e274e
--- /dev/null
+++ b/activerecord/test/models/keyboard.rb
@@ -0,0 +1,3 @@
+class Keyboard < ActiveRecord::Base
+ self.primary_key = 'key_number'
+end
diff --git a/activerecord/test/models/legacy_thing.rb b/activerecord/test/models/legacy_thing.rb
new file mode 100644
index 0000000000..eead181a0e
--- /dev/null
+++ b/activerecord/test/models/legacy_thing.rb
@@ -0,0 +1,3 @@
+class LegacyThing < ActiveRecord::Base
+ self.locking_column = :version
+end
diff --git a/activerecord/test/models/lesson.rb b/activerecord/test/models/lesson.rb
new file mode 100644
index 0000000000..4c88153068
--- /dev/null
+++ b/activerecord/test/models/lesson.rb
@@ -0,0 +1,11 @@
+class LessonError < Exception
+end
+
+class Lesson < ActiveRecord::Base
+ has_and_belongs_to_many :students
+ before_destroy :ensure_no_students
+
+ def ensure_no_students
+ raise LessonError unless students.empty?
+ end
+end
diff --git a/activerecord/test/models/line_item.rb b/activerecord/test/models/line_item.rb
new file mode 100644
index 0000000000..0dd921a300
--- /dev/null
+++ b/activerecord/test/models/line_item.rb
@@ -0,0 +1,3 @@
+class LineItem < ActiveRecord::Base
+ belongs_to :invoice, :touch => true
+end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
new file mode 100644
index 0000000000..69d4d7df1a
--- /dev/null
+++ b/activerecord/test/models/liquid.rb
@@ -0,0 +1,4 @@
+class Liquid < ActiveRecord::Base
+ self.table_name = :liquid
+ has_many :molecules, -> { distinct }
+end
diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb
new file mode 100644
index 0000000000..4fbb6b226b
--- /dev/null
+++ b/activerecord/test/models/man.rb
@@ -0,0 +1,11 @@
+class Man < ActiveRecord::Base
+ has_one :face, :inverse_of => :man
+ has_one :polymorphic_face, :class_name => 'Face', :as => :polymorphic_man, :inverse_of => :polymorphic_man
+ has_one :polymorphic_face_without_inverse, :class_name => 'Face', :as => :poly_man_without_inverse
+ has_many :interests, :inverse_of => :man
+ has_many :polymorphic_interests, :class_name => 'Interest', :as => :polymorphic_man, :inverse_of => :polymorphic_man
+ # These are "broken" inverse_of associations for the purposes of testing
+ has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man
+ has_many :secret_interests, :class_name => 'Interest', :inverse_of => :secret_man
+ has_one :mixed_case_monkey
+end
diff --git a/activerecord/test/models/matey.rb b/activerecord/test/models/matey.rb
new file mode 100644
index 0000000000..47b0baa974
--- /dev/null
+++ b/activerecord/test/models/matey.rb
@@ -0,0 +1,4 @@
+class Matey < ActiveRecord::Base
+ belongs_to :pirate
+ belongs_to :target, :class_name => 'Pirate'
+end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
new file mode 100644
index 0000000000..72095f9236
--- /dev/null
+++ b/activerecord/test/models/member.rb
@@ -0,0 +1,35 @@
+class Member < ActiveRecord::Base
+ has_one :current_membership
+ has_one :selected_membership
+ has_one :membership
+ has_one :club, :through => :current_membership
+ has_one :selected_club, :through => :selected_membership, :source => :club
+ has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club
+ has_one :hairy_club, -> { where :clubs => {:name => "Moustache and Eyebrow Fancier Club"} }, :through => :membership, :source => :club
+ has_one :sponsor, :as => :sponsorable
+ has_one :sponsor_club, :through => :sponsor
+ has_one :member_detail, :inverse_of => false
+ has_one :organization, :through => :member_detail
+ belongs_to :member_type
+
+ has_many :nested_member_types, :through => :member_detail, :source => :member_type
+ has_one :nested_member_type, :through => :member_detail, :source => :member_type
+
+ has_many :nested_sponsors, :through => :sponsor_club, :source => :sponsor
+ has_one :nested_sponsor, :through => :sponsor_club, :source => :sponsor
+
+ has_many :organization_member_details, :through => :member_detail
+ has_many :organization_member_details_2, :through => :organization, :source => :member_details
+
+ has_one :club_category, :through => :club, :source => :category
+
+ has_many :current_memberships, -> { where :favourite => true }
+ has_many :clubs, :through => :current_memberships
+
+ has_one :club_through_many, :through => :current_memberships, :source => :club
+end
+
+class SelfMember < ActiveRecord::Base
+ self.table_name = "members"
+ has_and_belongs_to_many :friends, :class_name => "SelfMember", :join_table => "member_friends"
+end
diff --git a/activerecord/test/models/member_detail.rb b/activerecord/test/models/member_detail.rb
new file mode 100644
index 0000000000..9d253aa126
--- /dev/null
+++ b/activerecord/test/models/member_detail.rb
@@ -0,0 +1,7 @@
+class MemberDetail < ActiveRecord::Base
+ belongs_to :member, :inverse_of => false
+ belongs_to :organization
+ has_one :member_type, :through => :member
+
+ has_many :organization_member_details, :through => :organization, :source => :member_details
+end
diff --git a/activerecord/test/models/member_type.rb b/activerecord/test/models/member_type.rb
new file mode 100644
index 0000000000..a13561c72a
--- /dev/null
+++ b/activerecord/test/models/member_type.rb
@@ -0,0 +1,3 @@
+class MemberType < ActiveRecord::Base
+ has_many :members
+end
diff --git a/activerecord/test/models/membership.rb b/activerecord/test/models/membership.rb
new file mode 100644
index 0000000000..df7167ee93
--- /dev/null
+++ b/activerecord/test/models/membership.rb
@@ -0,0 +1,20 @@
+class Membership < ActiveRecord::Base
+ belongs_to :member
+ belongs_to :club
+end
+
+class CurrentMembership < Membership
+ belongs_to :member
+ belongs_to :club
+end
+
+class SuperMembership < Membership
+ belongs_to :member, -> { order('members.id DESC') }
+ belongs_to :club
+end
+
+class SelectedMembership < Membership
+ def self.default_scope
+ select("'1' as foo")
+ end
+end
diff --git a/activerecord/test/models/minimalistic.rb b/activerecord/test/models/minimalistic.rb
new file mode 100644
index 0000000000..2e3f8e081a
--- /dev/null
+++ b/activerecord/test/models/minimalistic.rb
@@ -0,0 +1,2 @@
+class Minimalistic < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb
new file mode 100644
index 0000000000..4fe79720ad
--- /dev/null
+++ b/activerecord/test/models/minivan.rb
@@ -0,0 +1,9 @@
+class Minivan < ActiveRecord::Base
+ self.primary_key = :minivan_id
+
+ belongs_to :speedometer
+ has_one :dashboard, :through => :speedometer
+
+ attr_readonly :color
+
+end
diff --git a/activerecord/test/models/mixed_case_monkey.rb b/activerecord/test/models/mixed_case_monkey.rb
new file mode 100644
index 0000000000..1c35006665
--- /dev/null
+++ b/activerecord/test/models/mixed_case_monkey.rb
@@ -0,0 +1,3 @@
+class MixedCaseMonkey < ActiveRecord::Base
+ belongs_to :man
+end
diff --git a/activerecord/test/models/molecule.rb b/activerecord/test/models/molecule.rb
new file mode 100644
index 0000000000..26870c8f88
--- /dev/null
+++ b/activerecord/test/models/molecule.rb
@@ -0,0 +1,6 @@
+class Molecule < ActiveRecord::Base
+ belongs_to :liquid
+ has_many :electrons
+
+ accepts_nested_attributes_for :electrons
+end
diff --git a/activerecord/test/models/movie.rb b/activerecord/test/models/movie.rb
new file mode 100644
index 0000000000..0302abad1e
--- /dev/null
+++ b/activerecord/test/models/movie.rb
@@ -0,0 +1,5 @@
+class Movie < ActiveRecord::Base
+ self.primary_key = "movieid"
+
+ validates_presence_of :name
+end
diff --git a/activerecord/test/models/order.rb b/activerecord/test/models/order.rb
new file mode 100644
index 0000000000..e838c0b70d
--- /dev/null
+++ b/activerecord/test/models/order.rb
@@ -0,0 +1,4 @@
+class Order < ActiveRecord::Base
+ belongs_to :billing, :class_name => 'Customer', :foreign_key => 'billing_customer_id'
+ belongs_to :shipping, :class_name => 'Customer', :foreign_key => 'shipping_customer_id'
+end
diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb
new file mode 100644
index 0000000000..72e7bade68
--- /dev/null
+++ b/activerecord/test/models/organization.rb
@@ -0,0 +1,12 @@
+class Organization < ActiveRecord::Base
+ has_many :member_details
+ has_many :members, :through => :member_details
+
+ has_many :authors, :primary_key => :name
+ has_many :author_essay_categories, :through => :authors, :source => :essay_categories
+
+ has_one :author, :primary_key => :name
+ has_one :author_owned_essay_category, :through => :author, :source => :owned_essay_category
+
+ scope :clubs, -> { from('clubs') }
+end
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
new file mode 100644
index 0000000000..2e3a9a3681
--- /dev/null
+++ b/activerecord/test/models/owner.rb
@@ -0,0 +1,34 @@
+class Owner < ActiveRecord::Base
+ self.primary_key = :owner_id
+ has_many :pets, -> { order 'pets.name desc' }
+ has_many :toys, :through => :pets
+
+ belongs_to :last_pet, class_name: 'Pet'
+ scope :including_last_pet, -> {
+ select(%q[
+ owners.*, (
+ select p.pet_id from pets p
+ where p.owner_id = owners.owner_id
+ order by p.name desc
+ limit 1
+ ) as last_pet_id
+ ]).includes(:last_pet)
+ }
+
+ after_commit :execute_blocks
+
+ def blocks
+ @blocks ||= []
+ end
+
+ def on_after_commit(&block)
+ blocks << block
+ end
+
+ def execute_blocks
+ blocks.each do |block|
+ block.call(self)
+ end
+ @blocks = []
+ end
+end
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
new file mode 100644
index 0000000000..e76e83f314
--- /dev/null
+++ b/activerecord/test/models/parrot.rb
@@ -0,0 +1,29 @@
+class Parrot < ActiveRecord::Base
+ self.inheritance_column = :parrot_sti_class
+
+ has_and_belongs_to_many :pirates
+ has_and_belongs_to_many :treasures
+ has_many :loots, :as => :looter
+ alias_attribute :title, :name
+
+ validates_presence_of :name
+
+ attr_accessor :cancel_save_from_callback
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ def cancel_save_callback_method
+ false
+ end
+end
+
+class LiveParrot < Parrot
+end
+
+class DeadParrot < Parrot
+ belongs_to :killer, :class_name => 'Pirate'
+end
+
+class FunkyParrot < Parrot
+ before_destroy do
+ raise "before_destroy was called"
+ end
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
new file mode 100644
index 0000000000..c7e54e7b63
--- /dev/null
+++ b/activerecord/test/models/person.rb
@@ -0,0 +1,141 @@
+class Person < ActiveRecord::Base
+ has_many :readers
+ has_many :secure_readers
+ has_one :reader
+
+ has_many :posts, :through => :readers
+ has_many :secure_posts, :through => :secure_readers
+ has_many :posts_with_no_comments, -> { includes(:comments).where('comments.id is null').references(:comments) },
+ :through => :readers, :source => :post
+
+ has_many :friendships, foreign_key: 'friend_id'
+ # friends_too exists to test a bug, and probably shouldn't be used elsewhere
+ has_many :friends_too, foreign_key: 'friend_id', class_name: 'Friendship'
+ has_many :followers, through: :friendships
+
+ has_many :references
+ has_many :bad_references
+ has_many :fixed_bad_references, -> { where :favourite => true }, :class_name => 'BadReference'
+ has_one :favourite_reference, -> { where 'favourite=?', true }, :class_name => 'Reference'
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :through => :readers, :source => :post
+ has_many :first_posts, -> { where(id: [1, 2]) }, through: :readers
+
+ has_many :jobs, :through => :references
+ has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
+ has_many :jobs_with_dependent_delete_all, :source => :job, :through => :references, :dependent => :delete_all
+ has_many :jobs_with_dependent_nullify, :source => :job, :through => :references, :dependent => :nullify
+
+ belongs_to :primary_contact, :class_name => 'Person'
+ has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id'
+ has_many :agents_of_agents, :through => :agents, :source => :agents
+ belongs_to :number1_fan, :class_name => 'Person'
+
+ has_many :agents_posts, :through => :agents, :source => :posts
+ has_many :agents_posts_authors, :through => :agents_posts, :source => :author
+ has_many :essays, primary_key: "first_name", foreign_key: "writer_id"
+
+ scope :males, -> { where(:gender => 'M') }
+ scope :females, -> { where(:gender => 'F') }
+end
+
+class PersonWithDependentDestroyJobs < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_many :references, :foreign_key => :person_id
+ has_many :jobs, :source => :job, :through => :references, :dependent => :destroy
+end
+
+class PersonWithDependentDeleteAllJobs < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_many :references, :foreign_key => :person_id
+ has_many :jobs, :source => :job, :through => :references, :dependent => :delete_all
+end
+
+class PersonWithDependentNullifyJobs < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_many :references, :foreign_key => :person_id
+ has_many :jobs, :source => :job, :through => :references, :dependent => :nullify
+end
+
+
+class LoosePerson < ActiveRecord::Base
+ self.table_name = 'people'
+ self.abstract_class = true
+
+ has_one :best_friend, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
+ belongs_to :best_friend_of, :class_name => 'LoosePerson', :foreign_key => :best_friend_of_id
+ has_many :best_friends, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
+
+ accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends
+end
+
+class LooseDescendant < LoosePerson; end
+
+class TightPerson < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_one :best_friend, :class_name => 'TightPerson', :foreign_key => :best_friend_id
+ belongs_to :best_friend_of, :class_name => 'TightPerson', :foreign_key => :best_friend_of_id
+ has_many :best_friends, :class_name => 'TightPerson', :foreign_key => :best_friend_id
+
+ accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends
+end
+
+class TightDescendant < TightPerson; end
+
+class RichPerson < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_and_belongs_to_many :treasures, :join_table => 'peoples_treasures'
+
+ before_validation :run_before_create, on: :create
+ before_validation :run_before_validation
+
+ private
+
+ def run_before_create
+ self.first_name = first_name.to_s + 'run_before_create'
+ end
+
+ def run_before_validation
+ self.first_name = first_name.to_s + 'run_before_validation'
+ end
+end
+
+class NestedPerson < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_one :best_friend, :class_name => 'NestedPerson', :foreign_key => :best_friend_id
+ accepts_nested_attributes_for :best_friend, :update_only => true
+
+ def comments=(new_comments)
+ raise RuntimeError
+ end
+
+ def best_friend_first_name=(new_name)
+ assign_attributes({ :best_friend_attributes => { :first_name => new_name } })
+ end
+end
+
+class Insure
+ INSURES = %W{life annuality}
+
+ def self.load mask
+ INSURES.select do |insure|
+ (1 << INSURES.index(insure)) & mask.to_i > 0
+ end
+ end
+
+ def self.dump insures
+ numbers = insures.map { |insure| INSURES.index(insure) }
+ numbers.inject(0) { |sum, n| sum + (1 << n) }
+ end
+end
+
+class SerializedPerson < ActiveRecord::Base
+ self.table_name = 'people'
+
+ serialize :insures, Insure
+end
diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb
new file mode 100644
index 0000000000..f7970d7aab
--- /dev/null
+++ b/activerecord/test/models/pet.rb
@@ -0,0 +1,15 @@
+class Pet < ActiveRecord::Base
+ attr_accessor :current_user
+
+ self.primary_key = :pet_id
+ belongs_to :owner, :touch => true
+ has_many :toys
+
+ class << self
+ attr_accessor :after_destroy_output
+ end
+
+ after_destroy do |record|
+ Pet.after_destroy_output = record.current_user
+ end
+end
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
new file mode 100644
index 0000000000..90a3c3ecee
--- /dev/null
+++ b/activerecord/test/models/pirate.rb
@@ -0,0 +1,92 @@
+class Pirate < ActiveRecord::Base
+ belongs_to :parrot, :validate => true
+ belongs_to :non_validated_parrot, :class_name => 'Parrot'
+ has_and_belongs_to_many :parrots, -> { order('parrots.id ASC') }, :validate => true
+ has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
+ has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
+ :before_add => :log_before_add,
+ :after_add => :log_after_add,
+ :before_remove => :log_before_remove,
+ :after_remove => :log_after_remove
+ has_and_belongs_to_many :parrots_with_proc_callbacks, :class_name => "Parrot",
+ :before_add => proc {|p,pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}"},
+ :after_add => proc {|p,pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}"},
+ :before_remove => proc {|p,pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}"},
+ :after_remove => proc {|p,pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}"}
+ has_and_belongs_to_many :autosaved_parrots, class_name: "Parrot", autosave: true
+
+ has_many :treasures, :as => :looter
+ has_many :treasure_estimates, :through => :treasures, :source => :price_estimates
+
+ has_one :ship
+ has_one :update_only_ship, :class_name => 'Ship'
+ has_one :non_validated_ship, :class_name => 'Ship'
+ has_many :birds, -> { order('birds.id ASC') }
+ has_many :birds_with_method_callbacks, :class_name => "Bird",
+ :before_add => :log_before_add,
+ :after_add => :log_after_add,
+ :before_remove => :log_before_remove,
+ :after_remove => :log_after_remove
+ has_many :birds_with_proc_callbacks, :class_name => "Bird",
+ :before_add => proc {|p,b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}"},
+ :after_add => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}"},
+ :before_remove => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"},
+ :after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
+ has_many :birds_with_reject_all_blank, :class_name => "Bird"
+
+ has_one :foo_bulb, -> { where :name => 'foo' }, :foreign_key => :car_id, :class_name => "Bulb"
+
+ accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
+ accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
+ accepts_nested_attributes_for :update_only_ship, :update_only => true
+ accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
+ :birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true
+ accepts_nested_attributes_for :birds_with_reject_all_blank, :reject_if => :all_blank
+
+ validates_presence_of :catchphrase
+
+ def ship_log
+ @ship_log ||= []
+ end
+
+ def reject_empty_ships_on_create(attributes)
+ attributes.delete('_reject_me_if_new').present? && !persisted?
+ end
+
+ attr_accessor :cancel_save_from_callback, :parrots_limit
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ def cancel_save_callback_method
+ false
+ end
+
+ private
+ def log_before_add(record)
+ log(record, "before_adding_method")
+ end
+
+ def log_after_add(record)
+ log(record, "after_adding_method")
+ end
+
+ def log_before_remove(record)
+ log(record, "before_removing_method")
+ end
+
+ def log_after_remove(record)
+ log(record, "after_removing_method")
+ end
+
+ def log(record, callback)
+ ship_log << "#{callback}_#{record.class.name.downcase}_#{record.id || '<new>'}"
+ end
+end
+
+class DestructivePirate < Pirate
+ has_one :dependent_ship, :class_name => 'Ship', :foreign_key => :pirate_id, :dependent => :destroy
+end
+
+class FamousPirate < ActiveRecord::Base
+ self.table_name = 'pirates'
+ has_many :famous_ships
+ validates_presence_of :catchphrase, on: :conference
+end \ No newline at end of file
diff --git a/activerecord/test/models/possession.rb b/activerecord/test/models/possession.rb
new file mode 100644
index 0000000000..ddf759113b
--- /dev/null
+++ b/activerecord/test/models/possession.rb
@@ -0,0 +1,3 @@
+class Possession < ActiveRecord::Base
+ self.table_name = 'having'
+end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
new file mode 100644
index 0000000000..a29858213b
--- /dev/null
+++ b/activerecord/test/models/post.rb
@@ -0,0 +1,219 @@
+class Post < ActiveRecord::Base
+ class CategoryPost < ActiveRecord::Base
+ self.table_name = "categories_posts"
+ belongs_to :category
+ belongs_to :post
+ end
+
+ module NamedExtension
+ def author
+ 'lifo'
+ end
+ end
+
+ module NamedExtension2
+ def greeting
+ "hello"
+ end
+ end
+
+ scope :containing_the_letter_a, -> { where("body LIKE '%a%'") }
+ scope :ranked_by_comments, -> { order("comments_count DESC") }
+
+ scope :limit_by, lambda {|l| limit(l) }
+
+ belongs_to :author
+
+ belongs_to :author_with_posts, -> { includes(:posts) }, :class_name => "Author", :foreign_key => :author_id
+ belongs_to :author_with_address, -> { includes(:author_address) }, :class_name => "Author", :foreign_key => :author_id
+
+ def first_comment
+ super.body
+ end
+ has_one :first_comment, -> { order('id ASC') }, :class_name => 'Comment'
+ has_one :last_comment, -> { order('id desc') }, :class_name => 'Comment'
+
+ scope :with_special_comments, -> { joins(:comments).where(:comments => {:type => 'SpecialComment'}) }
+ scope :with_very_special_comments, -> { joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) }
+ scope :with_post, ->(post_id) { joins(:comments).where(:comments => { :post_id => post_id }) }
+
+ scope :with_comments, -> { preload(:comments) }
+ scope :with_tags, -> { preload(:taggings) }
+
+ scope :tagged_with, ->(id) { joins(:taggings).where(taggings: { tag_id: id }) }
+
+ has_many :comments do
+ def find_most_recent
+ order("id DESC").first
+ end
+
+ def newest
+ created.last
+ end
+
+ def the_association
+ proxy_association
+ end
+ end
+
+ has_many :comments_with_extend, extend: NamedExtension, class_name: "Comment", foreign_key: "post_id" do
+ def greeting
+ "hello"
+ end
+ end
+
+ has_many :comments_with_extend_2, extend: [NamedExtension, NamedExtension2], class_name: "Comment", foreign_key: "post_id"
+
+ has_many :author_favorites, :through => :author
+ has_many :author_categorizations, :through => :author, :source => :categorizations
+ has_many :author_addresses, :through => :author
+ has_many :author_address_extra_with_address,
+ through: :author_with_address,
+ source: :author_address_extra
+
+ has_many :comments_with_interpolated_conditions,
+ ->(p) { where "#{"#{p.aliased_table_name}." rescue ""}body = ?", 'Thank you for the welcome' },
+ :class_name => 'Comment'
+
+ has_one :very_special_comment
+ has_one :very_special_comment_with_post, -> { includes(:post) }, :class_name => "VerySpecialComment"
+ has_many :special_comments
+ has_many :nonexistant_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment'
+
+ has_many :special_comments_ratings, :through => :special_comments, :source => :ratings
+ has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings
+
+ has_many :category_posts, :class_name => 'CategoryPost'
+ has_many :scategories, through: :category_posts, source: :category
+ has_and_belongs_to_many :categories
+ has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
+
+ has_many :taggings, :as => :taggable, :counter_cache => :tags_count
+ has_many :tags, :through => :taggings do
+ def add_joins_and_select
+ select('tags.*, authors.id as author_id')
+ .joins('left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id')
+ .to_a
+ end
+ end
+
+ has_many :taggings_with_delete_all, :class_name => 'Tagging', :as => :taggable, :dependent => :delete_all
+ has_many :taggings_with_destroy, :class_name => 'Tagging', :as => :taggable, :dependent => :destroy
+
+ has_many :tags_with_destroy, :through => :taggings, :source => :tag, :dependent => :destroy
+ has_many :tags_with_nullify, :through => :taggings, :source => :tag, :dependent => :nullify
+
+ has_many :misc_tags, -> { where :tags => { :name => 'Misc' } }, :through => :taggings, :source => :tag
+ has_many :funky_tags, :through => :taggings, :source => :tag
+ has_many :super_tags, :through => :taggings
+ has_many :tags_with_primary_key, :through => :taggings, :source => :tag_with_primary_key
+ has_one :tagging, :as => :taggable
+
+ has_many :first_taggings, -> { where :taggings => { :comment => 'first' } }, :as => :taggable, :class_name => 'Tagging'
+ has_many :first_blue_tags, -> { where :tags => { :name => 'Blue' } }, :through => :first_taggings, :source => :tag
+
+ has_many :first_blue_tags_2, -> { where :taggings => { :comment => 'first' } }, :through => :taggings, :source => :blue_tag
+
+ has_many :invalid_taggings, -> { where 'taggings.id < 0' }, :as => :taggable, :class_name => "Tagging"
+ has_many :invalid_tags, :through => :invalid_taggings, :source => :tag
+
+ has_many :categorizations, :foreign_key => :category_id
+ has_many :authors, :through => :categorizations
+
+ has_many :categorizations_using_author_id, :primary_key => :author_id, :foreign_key => :post_id, :class_name => 'Categorization'
+ has_many :authors_using_author_id, :through => :categorizations_using_author_id, :source => :author
+
+ has_many :taggings_using_author_id, :primary_key => :author_id, :as => :taggable, :class_name => 'Tagging'
+ has_many :tags_using_author_id, :through => :taggings_using_author_id, :source => :tag
+
+ has_many :standard_categorizations, :class_name => 'Categorization', :foreign_key => :post_id
+ has_many :author_using_custom_pk, :through => :standard_categorizations
+ has_many :authors_using_custom_pk, :through => :standard_categorizations
+ has_many :named_categories, :through => :standard_categorizations
+
+ has_many :readers
+ has_many :secure_readers
+ has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader"
+ has_many :people, :through => :readers
+ has_many :single_people, :through => :readers
+ has_many :people_with_callbacks, :source=>:person, :through => :readers,
+ :before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) },
+ :after_add => lambda {|owner, reader| log(:added, :after, reader.first_name) },
+ :before_remove => lambda {|owner, reader| log(:removed, :before, reader.first_name) },
+ :after_remove => lambda {|owner, reader| log(:removed, :after, reader.first_name) }
+ has_many :skimmers, -> { where :skimmer => true }, :class_name => 'Reader'
+ has_many :impatient_people, :through => :skimmers, :source => :person
+
+ has_many :lazy_readers
+ has_many :lazy_readers_skimmers_or_not, -> { where(skimmer: [ true, false ]) }, :class_name => 'LazyReader'
+
+ has_many :lazy_people, :through => :lazy_readers, :source => :person
+ has_many :lazy_readers_unscope_skimmers, -> { skimmers_or_not }, :class_name => 'LazyReader'
+ has_many :lazy_people_unscope_skimmers, :through => :lazy_readers_unscope_skimmers, :source => :person
+
+ def self.top(limit)
+ ranked_by_comments.limit_by(limit)
+ end
+
+ def self.written_by(author)
+ where(id: author.posts.pluck(:id))
+ end
+
+ def self.reset_log
+ @log = []
+ end
+
+ def self.log(message=nil, side=nil, new_record=nil)
+ return @log if message.nil?
+ @log << [message, side, new_record]
+ end
+end
+
+class SpecialPost < Post; end
+
+class StiPost < Post
+ self.abstract_class = true
+ has_one :special_comment, :class_name => "SpecialComment"
+end
+
+class SubStiPost < StiPost
+ self.table_name = Post.table_name
+end
+
+class FirstPost < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope { where(:id => 1) }
+
+ has_many :comments, :foreign_key => :post_id
+ has_one :comment, :foreign_key => :post_id
+end
+
+class PostWithDefaultInclude < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope { includes(:comments) }
+ has_many :comments, :foreign_key => :post_id
+end
+
+class PostWithSpecialCategorization < Post
+ has_many :categorizations, :foreign_key => :post_id
+ default_scope { where(:type => 'PostWithSpecialCategorization').joins(:categorizations).where(:categorizations => { :special => true }) }
+end
+
+class PostWithDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope { order(:title) }
+end
+
+class SpecialPostWithDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope { where(:id => [1, 5,6]) }
+end
+
+class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
+ self.table_name = 'posts'
+ has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
+
+ after_save do |post|
+ post.comments.load
+ end
+end
diff --git a/activerecord/test/models/price_estimate.rb b/activerecord/test/models/price_estimate.rb
new file mode 100644
index 0000000000..d09e2a88a3
--- /dev/null
+++ b/activerecord/test/models/price_estimate.rb
@@ -0,0 +1,4 @@
+class PriceEstimate < ActiveRecord::Base
+ belongs_to :estimate_of, :polymorphic => true
+ belongs_to :thing, polymorphic: true
+end
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
new file mode 100644
index 0000000000..7f42a4b1f8
--- /dev/null
+++ b/activerecord/test/models/project.rb
@@ -0,0 +1,29 @@
+class Project < ActiveRecord::Base
+ has_and_belongs_to_many :developers, -> { distinct.order 'developers.name desc, developers.id desc' }
+ has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
+ has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
+ has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer"
+ has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, :class_name => "Developer"
+ has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(:name => 'David').distinct }, :class_name => "Developer"
+ has_and_belongs_to_many :salaried_developers, -> { where "salary > 0" }, :class_name => "Developer"
+ 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>'}"},
+ :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, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, :class_name => "Developer"
+
+ attr_accessor :developers_log
+ after_initialize :set_developers_log
+
+ def set_developers_log
+ @developers_log = []
+ end
+
+ def self.all_as_method
+ all
+ end
+ scope :all_as_scope, -> { all }
+end
+
+class SpecialProject < Project
+end
diff --git a/activerecord/test/models/publisher.rb b/activerecord/test/models/publisher.rb
new file mode 100644
index 0000000000..0d4a7f9235
--- /dev/null
+++ b/activerecord/test/models/publisher.rb
@@ -0,0 +1,2 @@
+module Publisher
+end
diff --git a/activerecord/test/models/publisher/article.rb b/activerecord/test/models/publisher/article.rb
new file mode 100644
index 0000000000..d73a8eb936
--- /dev/null
+++ b/activerecord/test/models/publisher/article.rb
@@ -0,0 +1,4 @@
+class Publisher::Article < ActiveRecord::Base
+ has_and_belongs_to_many :magazines
+ has_and_belongs_to_many :tags
+end
diff --git a/activerecord/test/models/publisher/magazine.rb b/activerecord/test/models/publisher/magazine.rb
new file mode 100644
index 0000000000..82e1a14008
--- /dev/null
+++ b/activerecord/test/models/publisher/magazine.rb
@@ -0,0 +1,3 @@
+class Publisher::Magazine < ActiveRecord::Base
+ has_and_belongs_to_many :articles
+end
diff --git a/activerecord/test/models/randomly_named_c1.rb b/activerecord/test/models/randomly_named_c1.rb
new file mode 100644
index 0000000000..18a86c4989
--- /dev/null
+++ b/activerecord/test/models/randomly_named_c1.rb
@@ -0,0 +1,3 @@
+class ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table
+end
diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb
new file mode 100644
index 0000000000..25a52c4ad7
--- /dev/null
+++ b/activerecord/test/models/rating.rb
@@ -0,0 +1,4 @@
+class Rating < ActiveRecord::Base
+ belongs_to :comment
+ has_many :taggings, :as => :taggable
+end
diff --git a/activerecord/test/models/reader.rb b/activerecord/test/models/reader.rb
new file mode 100644
index 0000000000..91afc1898c
--- /dev/null
+++ b/activerecord/test/models/reader.rb
@@ -0,0 +1,23 @@
+class Reader < ActiveRecord::Base
+ belongs_to :post
+ belongs_to :person, :inverse_of => :readers
+ belongs_to :single_person, :class_name => 'Person', :foreign_key => :person_id, :inverse_of => :reader
+ belongs_to :first_post, -> { where(id: [2, 3]) }
+end
+
+class SecureReader < ActiveRecord::Base
+ self.table_name = "readers"
+
+ belongs_to :secure_post, :class_name => "Post", :foreign_key => "post_id"
+ belongs_to :secure_person, :inverse_of => :secure_readers, :class_name => "Person", :foreign_key => "person_id"
+end
+
+class LazyReader < ActiveRecord::Base
+ self.table_name = "readers"
+ default_scope -> { where(skimmer: true) }
+
+ scope :skimmers_or_not, -> { unscope(:where => :skimmer) }
+
+ belongs_to :post
+ belongs_to :person
+end
diff --git a/activerecord/test/models/record.rb b/activerecord/test/models/record.rb
new file mode 100644
index 0000000000..f77ac9fc03
--- /dev/null
+++ b/activerecord/test/models/record.rb
@@ -0,0 +1,2 @@
+class Record < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb
new file mode 100644
index 0000000000..c2f9068f57
--- /dev/null
+++ b/activerecord/test/models/reference.rb
@@ -0,0 +1,22 @@
+class Reference < ActiveRecord::Base
+ belongs_to :person
+ belongs_to :job
+
+ has_many :agents_posts_authors, :through => :person
+
+ class << self; attr_accessor :make_comments; end
+ self.make_comments = false
+
+ before_destroy :make_comments
+
+ def make_comments
+ if self.class.make_comments
+ person.update comments: "Reference destroyed"
+ end
+ end
+end
+
+class BadReference < ActiveRecord::Base
+ self.table_name = 'references'
+ default_scope { where(:favourite => false) }
+end
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
new file mode 100644
index 0000000000..3e82e55d89
--- /dev/null
+++ b/activerecord/test/models/reply.rb
@@ -0,0 +1,61 @@
+require 'models/topic'
+
+class Reply < Topic
+ 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"
+end
+
+class UniqueReply < Reply
+ belongs_to :topic, :foreign_key => 'parent_id', :counter_cache => true
+ validates_uniqueness_of :content, :scope => 'parent_id'
+end
+
+class SillyUniqueReply < UniqueReply
+end
+
+class WrongReply < Reply
+ validate :errors_on_empty_content
+ validate :title_is_wrong_create, :on => :create
+
+ validate :check_empty_title
+ validate :check_content_mismatch, :on => :create
+ validate :check_wrong_update, :on => :update
+ validate :check_author_name_is_secret, :on => :special_case
+
+ def check_empty_title
+ errors[:title] << "Empty" unless attribute_present?("title")
+ end
+
+ def errors_on_empty_content
+ errors[:content] << "Empty" unless attribute_present?("content")
+ end
+
+ def check_content_mismatch
+ if attribute_present?("title") && attribute_present?("content") && content == "Mismatch"
+ errors[:title] << "is Content Mismatch"
+ end
+ end
+
+ def title_is_wrong_create
+ errors[:title] << "is Wrong Create" if attribute_present?("title") && title == "Wrong Create"
+ end
+
+ def check_wrong_update
+ errors[:title] << "is Wrong Update" if attribute_present?("title") && title == "Wrong Update"
+ end
+
+ def check_author_name_is_secret
+ errors[:author_name] << "Invalid" unless author_name == "secret"
+ end
+end
+
+class SillyReply < Reply
+ belongs_to :reply, :foreign_key => "parent_id", :counter_cache => :replies_count
+end
+
+module Web
+ class Reply < Web::Topic
+ belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true, :class_name => 'Web::Topic'
+ end
+end
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
new file mode 100644
index 0000000000..77a4728d0b
--- /dev/null
+++ b/activerecord/test/models/ship.rb
@@ -0,0 +1,25 @@
+class Ship < ActiveRecord::Base
+ self.record_timestamps = false
+
+ belongs_to :pirate
+ belongs_to :update_only_pirate, :class_name => 'Pirate'
+ has_many :parts, :class_name => 'ShipPart'
+
+ accepts_nested_attributes_for :parts, :allow_destroy => true
+ accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
+ accepts_nested_attributes_for :update_only_pirate, :update_only => true
+
+ validates_presence_of :name
+
+ attr_accessor :cancel_save_from_callback
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ def cancel_save_callback_method
+ false
+ end
+end
+
+class FamousShip < ActiveRecord::Base
+ self.table_name = 'ships'
+ belongs_to :famous_pirate
+ validates_presence_of :name, on: :conference
+end
diff --git a/activerecord/test/models/ship_part.rb b/activerecord/test/models/ship_part.rb
new file mode 100644
index 0000000000..b6a8a506b4
--- /dev/null
+++ b/activerecord/test/models/ship_part.rb
@@ -0,0 +1,7 @@
+class ShipPart < ActiveRecord::Base
+ belongs_to :ship
+ has_many :trinkets, :class_name => "Treasure", :as => :looter
+ accepts_nested_attributes_for :trinkets, :allow_destroy => true
+
+ validates_presence_of :name
+end \ No newline at end of file
diff --git a/activerecord/test/models/shop.rb b/activerecord/test/models/shop.rb
new file mode 100644
index 0000000000..607a0a5b41
--- /dev/null
+++ b/activerecord/test/models/shop.rb
@@ -0,0 +1,17 @@
+module Shop
+ class Collection < ActiveRecord::Base
+ has_many :products, :dependent => :nullify
+ end
+
+ class Product < ActiveRecord::Base
+ has_many :variants, :dependent => :delete_all
+ belongs_to :type
+
+ class Type < ActiveRecord::Base
+ has_many :products
+ end
+ end
+
+ class Variant < ActiveRecord::Base
+ end
+end
diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb
new file mode 100644
index 0000000000..497c3aba9a
--- /dev/null
+++ b/activerecord/test/models/speedometer.rb
@@ -0,0 +1,6 @@
+class Speedometer < ActiveRecord::Base
+ self.primary_key = :speedometer_id
+ belongs_to :dashboard
+
+ has_many :minivans
+end
diff --git a/activerecord/test/models/sponsor.rb b/activerecord/test/models/sponsor.rb
new file mode 100644
index 0000000000..ec3dcf8a97
--- /dev/null
+++ b/activerecord/test/models/sponsor.rb
@@ -0,0 +1,7 @@
+class Sponsor < ActiveRecord::Base
+ belongs_to :sponsor_club, :class_name => "Club", :foreign_key => "club_id"
+ belongs_to :sponsorable, :polymorphic => true
+ belongs_to :thing, :polymorphic => true, :foreign_type => :sponsorable_type, :foreign_key => :sponsorable_id
+ belongs_to :sponsorable_with_conditions, -> { where :name => 'Ernie'}, :polymorphic => true,
+ :foreign_type => 'sponsorable_type', :foreign_key => 'sponsorable_id'
+end
diff --git a/activerecord/test/models/string_key_object.rb b/activerecord/test/models/string_key_object.rb
new file mode 100644
index 0000000000..f084ec1bdc
--- /dev/null
+++ b/activerecord/test/models/string_key_object.rb
@@ -0,0 +1,3 @@
+class StringKeyObject < ActiveRecord::Base
+ self.primary_key = :id
+end
diff --git a/activerecord/test/models/student.rb b/activerecord/test/models/student.rb
new file mode 100644
index 0000000000..28a0b6c99b
--- /dev/null
+++ b/activerecord/test/models/student.rb
@@ -0,0 +1,4 @@
+class Student < ActiveRecord::Base
+ has_and_belongs_to_many :lessons
+ belongs_to :college
+end
diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb
new file mode 100644
index 0000000000..8e28f8b86b
--- /dev/null
+++ b/activerecord/test/models/subject.rb
@@ -0,0 +1,16 @@
+# used for OracleSynonymTest, see test/synonym_test_oracle.rb
+#
+class Subject < ActiveRecord::Base
+
+ # added initialization of author_email_address in the same way as in Topic class
+ # as otherwise synonym test was failing
+ after_initialize :set_email_address
+
+ protected
+ def set_email_address
+ unless self.persisted?
+ self.author_email_address = 'test@test.com'
+ end
+ end
+
+end
diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb
new file mode 100644
index 0000000000..76e85a0cd3
--- /dev/null
+++ b/activerecord/test/models/subscriber.rb
@@ -0,0 +1,8 @@
+class Subscriber < ActiveRecord::Base
+ self.primary_key = 'nick'
+ has_many :subscriptions
+ has_many :books, :through => :subscriptions
+end
+
+class SpecialSubscriber < Subscriber
+end
diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb
new file mode 100644
index 0000000000..bcac4738a3
--- /dev/null
+++ b/activerecord/test/models/subscription.rb
@@ -0,0 +1,4 @@
+class Subscription < ActiveRecord::Base
+ belongs_to :subscriber, :counter_cache => :books_count
+ belongs_to :book
+end
diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb
new file mode 100644
index 0000000000..80d4725f7e
--- /dev/null
+++ b/activerecord/test/models/tag.rb
@@ -0,0 +1,7 @@
+class Tag < ActiveRecord::Base
+ has_many :taggings
+ has_many :taggables, :through => :taggings
+ has_one :tagging
+
+ has_many :tagged_posts, :through => :taggings, :source => 'taggable', :source_type => 'Post'
+end
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
new file mode 100644
index 0000000000..a6c05da26a
--- /dev/null
+++ b/activerecord/test/models/tagging.rb
@@ -0,0 +1,13 @@
+# test that attr_readonly isn't called on the :taggable polymorphic association
+module Taggable
+end
+
+class Tagging < ActiveRecord::Base
+ belongs_to :tag, -> { includes(:tagging) }
+ belongs_to :super_tag, :class_name => 'Tag', :foreign_key => 'super_tag_id'
+ belongs_to :invalid_tag, :class_name => 'Tag', :foreign_key => 'tag_id'
+ belongs_to :blue_tag, -> { where :tags => { :name => 'Blue' } }, :class_name => 'Tag', :foreign_key => :tag_id
+ belongs_to :tag_with_primary_key, :class_name => 'Tag', :foreign_key => :tag_id, :primary_key => :custom_primary_key
+ belongs_to :taggable, :polymorphic => true, :counter_cache => :tags_count
+ has_many :things, :through => :taggable
+end
diff --git a/activerecord/test/models/task.rb b/activerecord/test/models/task.rb
new file mode 100644
index 0000000000..e36989dd56
--- /dev/null
+++ b/activerecord/test/models/task.rb
@@ -0,0 +1,5 @@
+class Task < ActiveRecord::Base
+ def updated_at
+ ending
+ end
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
new file mode 100644
index 0000000000..f81ffe1d90
--- /dev/null
+++ b/activerecord/test/models/topic.rb
@@ -0,0 +1,124 @@
+class Topic < ActiveRecord::Base
+ scope :base, -> { all }
+ scope :written_before, lambda { |time|
+ if time
+ where 'written_on < ?', time
+ end
+ }
+ scope :approved, -> { where(:approved => true) }
+ scope :rejected, -> { where(:approved => false) }
+
+ scope :scope_with_lambda, lambda { all }
+
+ scope :by_lifo, -> { where(:author_name => 'lifo') }
+ scope :replied, -> { where 'replies_count > 0' }
+
+ scope 'approved_as_string', -> { where(:approved => true) }
+ scope :anonymous_extension, -> { all } do
+ def one
+ 1
+ end
+ end
+
+ scope :with_object, Class.new(Struct.new(:klass)) {
+ def call
+ klass.where(:approved => true)
+ end
+ }.new(self)
+
+ module NamedExtension
+ def two
+ 2
+ end
+ end
+
+ has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
+ has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count'
+
+ has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+ has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+
+ serialize :content
+
+ before_create :default_written_on
+ before_destroy :destroy_children
+
+ # Explicitly define as :date column so that returned Oracle DATE values would be typecasted to Date and not Time.
+ # Some tests depend on assumption that this attribute will have Date values.
+ if current_adapter?(:OracleEnhancedAdapter)
+ set_date_columns :last_read
+ end
+
+ def parent
+ Topic.find(parent_id)
+ end
+
+ # trivial method for testing Array#to_xml with :methods
+ def topic_id
+ id
+ end
+
+ alias_attribute :heading, :title
+
+ before_validation :before_validation_for_transaction
+ before_save :before_save_for_transaction
+ before_destroy :before_destroy_for_transaction
+
+ after_save :after_save_for_transaction
+ after_create :after_create_for_transaction
+
+ after_initialize :set_email_address
+
+ class_attribute :after_initialize_called
+ after_initialize do
+ self.class.after_initialize_called = true
+ end
+
+ def approved=(val)
+ @custom_approved = val
+ write_attribute(:approved, val)
+ end
+
+ protected
+
+ def default_written_on
+ self.written_on = Time.now unless attribute_present?("written_on")
+ end
+
+ def destroy_children
+ self.class.delete_all "parent_id = #{id}"
+ end
+
+ def set_email_address
+ unless self.persisted?
+ self.author_email_address = 'test@test.com'
+ end
+ end
+
+ def before_validation_for_transaction; end
+ def before_save_for_transaction; end
+ def before_destroy_for_transaction; end
+ def after_save_for_transaction; end
+ def after_create_for_transaction; end
+end
+
+class ImportantTopic < Topic
+ serialize :important, Hash
+end
+
+class DefaultRejectedTopic < Topic
+ default_scope -> { where(approved: false) }
+end
+
+class BlankTopic < Topic
+ # declared here to make sure that dynamic finder with a bang can find a model that responds to `blank?`
+ def blank?
+ true
+ end
+end
+
+module Web
+ class Topic < ActiveRecord::Base
+ has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
+ end
+end
diff --git a/activerecord/test/models/toy.rb b/activerecord/test/models/toy.rb
new file mode 100644
index 0000000000..ddc7048a56
--- /dev/null
+++ b/activerecord/test/models/toy.rb
@@ -0,0 +1,6 @@
+class Toy < ActiveRecord::Base
+ self.primary_key = :toy_id
+ belongs_to :pet
+
+ scope :with_pet, -> { joins(:pet) }
+end
diff --git a/activerecord/test/models/traffic_light.rb b/activerecord/test/models/traffic_light.rb
new file mode 100644
index 0000000000..a6b7edb882
--- /dev/null
+++ b/activerecord/test/models/traffic_light.rb
@@ -0,0 +1,4 @@
+class TrafficLight < ActiveRecord::Base
+ serialize :state, Array
+ serialize :long_state, Array
+end
diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb
new file mode 100644
index 0000000000..a69d3fd3df
--- /dev/null
+++ b/activerecord/test/models/treasure.rb
@@ -0,0 +1,12 @@
+class Treasure < ActiveRecord::Base
+ has_and_belongs_to_many :parrots
+ belongs_to :looter, :polymorphic => true
+
+ has_many :price_estimates, :as => :estimate_of
+ has_and_belongs_to_many :rich_people, join_table: 'peoples_treasures', validate: false
+
+ accepts_nested_attributes_for :looter
+end
+
+class HiddenTreasure < Treasure
+end
diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb
new file mode 100644
index 0000000000..41fd1350f3
--- /dev/null
+++ b/activerecord/test/models/treaty.rb
@@ -0,0 +1,7 @@
+class Treaty < ActiveRecord::Base
+
+ self.primary_key = :treaty_id
+
+ has_and_belongs_to_many :countries
+
+end
diff --git a/activerecord/test/models/tyre.rb b/activerecord/test/models/tyre.rb
new file mode 100644
index 0000000000..bc3444aa7d
--- /dev/null
+++ b/activerecord/test/models/tyre.rb
@@ -0,0 +1,3 @@
+class Tyre < ActiveRecord::Base
+ belongs_to :car
+end
diff --git a/activerecord/test/models/uuid_child.rb b/activerecord/test/models/uuid_child.rb
new file mode 100644
index 0000000000..a3d0962ad6
--- /dev/null
+++ b/activerecord/test/models/uuid_child.rb
@@ -0,0 +1,3 @@
+class UuidChild < ActiveRecord::Base
+ belongs_to :uuid_parent
+end
diff --git a/activerecord/test/models/uuid_parent.rb b/activerecord/test/models/uuid_parent.rb
new file mode 100644
index 0000000000..5634f22d0c
--- /dev/null
+++ b/activerecord/test/models/uuid_parent.rb
@@ -0,0 +1,3 @@
+class UuidParent < ActiveRecord::Base
+ has_many :uuid_children
+end
diff --git a/activerecord/test/models/vegetables.rb b/activerecord/test/models/vegetables.rb
new file mode 100644
index 0000000000..1f41cde3a5
--- /dev/null
+++ b/activerecord/test/models/vegetables.rb
@@ -0,0 +1,24 @@
+class Vegetable < ActiveRecord::Base
+
+ validates_presence_of :name
+
+ def self.inheritance_column
+ 'custom_type'
+ end
+end
+
+class Cucumber < Vegetable
+end
+
+class Cabbage < Vegetable
+end
+
+class GreenCabbage < Cabbage
+end
+
+class KingCole < GreenCabbage
+end
+
+class RedCabbage < Cabbage
+ belongs_to :seller, :class_name => 'Company'
+end
diff --git a/activerecord/test/models/vertex.rb b/activerecord/test/models/vertex.rb
new file mode 100644
index 0000000000..48bb851e62
--- /dev/null
+++ b/activerecord/test/models/vertex.rb
@@ -0,0 +1,9 @@
+# This class models a vertex in a directed graph.
+class Vertex < ActiveRecord::Base
+ has_many :sink_edges, :class_name => 'Edge', :foreign_key => 'source_id'
+ has_many :sinks, :through => :sink_edges
+
+ has_and_belongs_to_many :sources,
+ :class_name => 'Vertex', :join_table => 'edges',
+ :foreign_key => 'sink_id', :association_foreign_key => 'source_id'
+end
diff --git a/activerecord/test/models/warehouse_thing.rb b/activerecord/test/models/warehouse_thing.rb
new file mode 100644
index 0000000000..f20bd1a245
--- /dev/null
+++ b/activerecord/test/models/warehouse_thing.rb
@@ -0,0 +1,5 @@
+class WarehouseThing < ActiveRecord::Base
+ self.table_name = "warehouse-things"
+
+ validates_uniqueness_of :value
+end
diff --git a/activerecord/test/models/wheel.rb b/activerecord/test/models/wheel.rb
new file mode 100644
index 0000000000..26868bce5e
--- /dev/null
+++ b/activerecord/test/models/wheel.rb
@@ -0,0 +1,3 @@
+class Wheel < ActiveRecord::Base
+ belongs_to :wheelable, :polymorphic => true, :counter_cache => true
+end
diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb
new file mode 100644
index 0000000000..50c824e4ac
--- /dev/null
+++ b/activerecord/test/models/without_table.rb
@@ -0,0 +1,3 @@
+class WithoutTable < ActiveRecord::Base
+ default_scope -> { where(:published => true) }
+end
diff --git a/activerecord/test/models/zine.rb b/activerecord/test/models/zine.rb
new file mode 100644
index 0000000000..c2d0fdaf25
--- /dev/null
+++ b/activerecord/test/models/zine.rb
@@ -0,0 +1,3 @@
+class Zine < ActiveRecord::Base
+ has_many :interests, :inverse_of => :zine
+end