aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/models
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/models')
-rw-r--r--activerecord/test/models/account.rb34
-rw-r--r--activerecord/test/models/admin.rb7
-rw-r--r--activerecord/test/models/admin/account.rb5
-rw-r--r--activerecord/test/models/admin/randomly_named_c1.rb9
-rw-r--r--activerecord/test/models/admin/user.rb42
-rw-r--r--activerecord/test/models/aircraft.rb7
-rw-r--r--activerecord/test/models/arunit2_model.rb5
-rw-r--r--activerecord/test/models/author.rb211
-rw-r--r--activerecord/test/models/auto_id.rb6
-rw-r--r--activerecord/test/models/autoloadable/extra_firm.rb4
-rw-r--r--activerecord/test/models/binary.rb4
-rw-r--r--activerecord/test/models/bird.rb14
-rw-r--r--activerecord/test/models/book.rb26
-rw-r--r--activerecord/test/models/boolean.rb7
-rw-r--r--activerecord/test/models/bulb.rb54
-rw-r--r--activerecord/test/models/cake_designer.rb5
-rw-r--r--activerecord/test/models/car.rb31
-rw-r--r--activerecord/test/models/carrier.rb4
-rw-r--r--activerecord/test/models/cat.rb12
-rw-r--r--activerecord/test/models/categorization.rb21
-rw-r--r--activerecord/test/models/category.rb46
-rw-r--r--activerecord/test/models/chef.rb10
-rw-r--r--activerecord/test/models/citation.rb5
-rw-r--r--activerecord/test/models/club.rb27
-rw-r--r--activerecord/test/models/college.rb12
-rw-r--r--activerecord/test/models/column.rb5
-rw-r--r--activerecord/test/models/column_name.rb5
-rw-r--r--activerecord/test/models/comment.rb85
-rw-r--r--activerecord/test/models/company.rb190
-rw-r--r--activerecord/test/models/company_in_module.rb100
-rw-r--r--activerecord/test/models/computer.rb5
-rw-r--r--activerecord/test/models/contact.rb43
-rw-r--r--activerecord/test/models/content.rb42
-rw-r--r--activerecord/test/models/contract.rb22
-rw-r--r--activerecord/test/models/country.rb7
-rw-r--r--activerecord/test/models/course.rb8
-rw-r--r--activerecord/test/models/customer.rb85
-rw-r--r--activerecord/test/models/customer_carrier.rb16
-rw-r--r--activerecord/test/models/dashboard.rb5
-rw-r--r--activerecord/test/models/default.rb4
-rw-r--r--activerecord/test/models/department.rb6
-rw-r--r--activerecord/test/models/developer.rb270
-rw-r--r--activerecord/test/models/dog.rb7
-rw-r--r--activerecord/test/models/dog_lover.rb7
-rw-r--r--activerecord/test/models/doubloon.rb14
-rw-r--r--activerecord/test/models/drink_designer.rb5
-rw-r--r--activerecord/test/models/edge.rb7
-rw-r--r--activerecord/test/models/electron.rb7
-rw-r--r--activerecord/test/models/engine.rb5
-rw-r--r--activerecord/test/models/entrant.rb5
-rw-r--r--activerecord/test/models/essay.rb8
-rw-r--r--activerecord/test/models/event.rb5
-rw-r--r--activerecord/test/models/eye.rb39
-rw-r--r--activerecord/test/models/face.rb11
-rw-r--r--activerecord/test/models/family.rb6
-rw-r--r--activerecord/test/models/family_tree.rb6
-rw-r--r--activerecord/test/models/friendship.rb8
-rw-r--r--activerecord/test/models/guid.rb4
-rw-r--r--activerecord/test/models/guitar.rb6
-rw-r--r--activerecord/test/models/hotel.rb13
-rw-r--r--activerecord/test/models/image.rb5
-rw-r--r--activerecord/test/models/interest.rb7
-rw-r--r--activerecord/test/models/invoice.rb6
-rw-r--r--activerecord/test/models/item.rb9
-rw-r--r--activerecord/test/models/job.rb9
-rw-r--r--activerecord/test/models/joke.rb9
-rw-r--r--activerecord/test/models/keyboard.rb5
-rw-r--r--activerecord/test/models/legacy_thing.rb5
-rw-r--r--activerecord/test/models/lesson.rb13
-rw-r--r--activerecord/test/models/line_item.rb5
-rw-r--r--activerecord/test/models/liquid.rb6
-rw-r--r--activerecord/test/models/man.rb13
-rw-r--r--activerecord/test/models/matey.rb6
-rw-r--r--activerecord/test/models/member.rb44
-rw-r--r--activerecord/test/models/member_detail.rb10
-rw-r--r--activerecord/test/models/member_type.rb5
-rw-r--r--activerecord/test/models/membership.rb38
-rw-r--r--activerecord/test/models/mentor.rb5
-rw-r--r--activerecord/test/models/minimalistic.rb4
-rw-r--r--activerecord/test/models/minivan.rb10
-rw-r--r--activerecord/test/models/mixed_case_monkey.rb5
-rw-r--r--activerecord/test/models/mocktail_designer.rb4
-rw-r--r--activerecord/test/models/molecule.rb8
-rw-r--r--activerecord/test/models/movie.rb7
-rw-r--r--activerecord/test/models/node.rb7
-rw-r--r--activerecord/test/models/non_primary_key.rb4
-rw-r--r--activerecord/test/models/notification.rb5
-rw-r--r--activerecord/test/models/numeric_data.rb10
-rw-r--r--activerecord/test/models/order.rb6
-rw-r--r--activerecord/test/models/organization.rb16
-rw-r--r--activerecord/test/models/other_dog.rb7
-rw-r--r--activerecord/test/models/owner.rb39
-rw-r--r--activerecord/test/models/parrot.rb30
-rw-r--r--activerecord/test/models/person.rb143
-rw-r--r--activerecord/test/models/personal_legacy_thing.rb6
-rw-r--r--activerecord/test/models/pet.rb20
-rw-r--r--activerecord/test/models/pet_treasure.rb8
-rw-r--r--activerecord/test/models/pirate.rb94
-rw-r--r--activerecord/test/models/possession.rb5
-rw-r--r--activerecord/test/models/post.rb317
-rw-r--r--activerecord/test/models/price_estimate.rb6
-rw-r--r--activerecord/test/models/professor.rb7
-rw-r--r--activerecord/test/models/project.rb42
-rw-r--r--activerecord/test/models/publisher.rb4
-rw-r--r--activerecord/test/models/publisher/article.rb6
-rw-r--r--activerecord/test/models/publisher/magazine.rb5
-rw-r--r--activerecord/test/models/randomly_named_c1.rb5
-rw-r--r--activerecord/test/models/rating.rb6
-rw-r--r--activerecord/test/models/reader.rb25
-rw-r--r--activerecord/test/models/recipe.rb5
-rw-r--r--activerecord/test/models/record.rb4
-rw-r--r--activerecord/test/models/reference.rb24
-rw-r--r--activerecord/test/models/reply.rb63
-rw-r--r--activerecord/test/models/ship.rb41
-rw-r--r--activerecord/test/models/ship_part.rb10
-rw-r--r--activerecord/test/models/shop.rb19
-rw-r--r--activerecord/test/models/shop_account.rb8
-rw-r--r--activerecord/test/models/speedometer.rb8
-rw-r--r--activerecord/test/models/sponsor.rb9
-rw-r--r--activerecord/test/models/string_key_object.rb5
-rw-r--r--activerecord/test/models/student.rb6
-rw-r--r--activerecord/test/models/subscriber.rb10
-rw-r--r--activerecord/test/models/subscription.rb6
-rw-r--r--activerecord/test/models/tag.rb15
-rw-r--r--activerecord/test/models/tagging.rb15
-rw-r--r--activerecord/test/models/task.rb7
-rw-r--r--activerecord/test/models/topic.rb120
-rw-r--r--activerecord/test/models/toy.rb8
-rw-r--r--activerecord/test/models/traffic_light.rb6
-rw-r--r--activerecord/test/models/treasure.rb16
-rw-r--r--activerecord/test/models/treaty.rb7
-rw-r--r--activerecord/test/models/tree.rb5
-rw-r--r--activerecord/test/models/tuning_peg.rb6
-rw-r--r--activerecord/test/models/tyre.rb13
-rw-r--r--activerecord/test/models/user.rb20
-rw-r--r--activerecord/test/models/uuid_child.rb5
-rw-r--r--activerecord/test/models/uuid_item.rb8
-rw-r--r--activerecord/test/models/uuid_parent.rb5
-rw-r--r--activerecord/test/models/vegetables.rb25
-rw-r--r--activerecord/test/models/vehicle.rb9
-rw-r--r--activerecord/test/models/vertex.rb11
-rw-r--r--activerecord/test/models/warehouse_thing.rb7
-rw-r--r--activerecord/test/models/wheel.rb5
-rw-r--r--activerecord/test/models/without_table.rb5
-rw-r--r--activerecord/test/models/zine.rb5
145 files changed, 3271 insertions, 0 deletions
diff --git a/activerecord/test/models/account.rb b/activerecord/test/models/account.rb
new file mode 100644
index 0000000000..0c3cd45a81
--- /dev/null
+++ b/activerecord/test/models/account.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+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
+ end
+
+ validate :check_empty_credit_limit
+
+ private
+ def check_empty_credit_limit
+ errors.add("credit_limit", :blank) if credit_limit.blank?
+ end
+
+ def private_method
+ "Sir, yes sir!"
+ end
+end
diff --git a/activerecord/test/models/admin.rb b/activerecord/test/models/admin.rb
new file mode 100644
index 0000000000..a40b5a33b2
--- /dev/null
+++ b/activerecord/test/models/admin.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Admin
+ def self.table_name_prefix
+ "admin_"
+ end
+end
diff --git a/activerecord/test/models/admin/account.rb b/activerecord/test/models/admin/account.rb
new file mode 100644
index 0000000000..41fe2d782b
--- /dev/null
+++ b/activerecord/test/models/admin/account.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Admin::Account < ActiveRecord::Base
+ has_many :users
+end
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..d89b8dd293
--- /dev/null
+++ b/activerecord/test/models/admin/randomly_named_c1.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS1 < ActiveRecord::Base
+ self.table_name = :randomly_named_table2
+end
+
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS2 < ActiveRecord::Base
+ self.table_name = :randomly_named_table3
+end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
new file mode 100644
index 0000000000..abb5cb28e7
--- /dev/null
+++ b/activerecord/test/models/admin/user.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+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..4fdea46cf7
--- /dev/null
+++ b/activerecord/test/models/aircraft.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Aircraft < ActiveRecord::Base
+ self.pluralize_table_names = false
+ has_many :engines, foreign_key: "car_id"
+ has_many :wheels, as: :wheelable
+end
diff --git a/activerecord/test/models/arunit2_model.rb b/activerecord/test/models/arunit2_model.rb
new file mode 100644
index 0000000000..5b0da8a249
--- /dev/null
+++ b/activerecord/test/models/arunit2_model.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..09958ca257
--- /dev/null
+++ b/activerecord/test/models/author.rb
@@ -0,0 +1,211 @@
+# frozen_string_literal: true
+
+class Author < ActiveRecord::Base
+ has_many :posts
+ has_many :serialized_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 :special_posts_with_default_scope, class_name: "SpecialPostWithDefaultScope"
+
+ has_many :sti_posts, class_name: "StiPost"
+ has_many :sti_post_comments, through: :sti_posts, source: :comments
+
+ has_many :special_nonexistent_posts, -> { where("posts.body = 'nonexistent'") }, class_name: "SpecialPost"
+ has_many :special_nonexistent_post_comments, -> { where("comments.post_id" => 0) }, through: :special_nonexistent_posts, source: :comments
+ has_many :nonexistent_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 :unchangeable_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 :unpublished_books, -> { where(status: [:proposed, :written]) }, class_name: "Book"
+ 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"
+
+ has_many :posts_with_extension, -> { order(:title) }, class_name: "Post" do
+ def extension_method; end
+ end
+
+ has_many :posts_with_extension_and_instance, ->(record) { order(:title) }, class_name: "Post" do
+ def extension_method; end
+ end
+
+ 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..fd672603bb
--- /dev/null
+++ b/activerecord/test/models/auto_id.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..c46e34c101
--- /dev/null
+++ b/activerecord/test/models/autoloadable/extra_firm.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class ExtraFirm < Company
+end
diff --git a/activerecord/test/models/binary.rb b/activerecord/test/models/binary.rb
new file mode 100644
index 0000000000..b93f87519f
--- /dev/null
+++ b/activerecord/test/models/binary.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class Binary < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/bird.rb b/activerecord/test/models/bird.rb
new file mode 100644
index 0000000000..be08636ac6
--- /dev/null
+++ b/activerecord/test/models/bird.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+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
+ throw(:abort)
+ end
+end
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
new file mode 100644
index 0000000000..afdda1a81e
--- /dev/null
+++ b/activerecord/test/models/book.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class Book < ActiveRecord::Base
+ belongs_to :author
+
+ 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, forgotten: nil }
+ enum nullable_status: [:single, :married]
+ enum language: [:english, :spanish, :french], _prefix: :in
+ enum author_visibility: [:visible, :invisible], _prefix: true
+ enum illustrator_visibility: [:visible, :invisible], _prefix: true
+ enum font_size: [:small, :medium, :large], _prefix: :with, _suffix: true
+ enum difficulty: [:easy, :medium, :hard], _suffix: :to_read
+ enum cover: { hard: "hard", soft: "soft" }
+
+ 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..bee757fb9c
--- /dev/null
+++ b/activerecord/test/models/boolean.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Boolean < ActiveRecord::Base
+ def has_fun
+ super
+ end
+end
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
new file mode 100644
index 0000000000..ab92f7025d
--- /dev/null
+++ b/activerecord/test/models/bulb.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+class Bulb < ActiveRecord::Base
+ default_scope { where(name: "defaulty") }
+ belongs_to :car, touch: true
+ scope :awesome, -> { where(frickinawesome: 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
+ throw(:abort)
+ end
+end
diff --git a/activerecord/test/models/cake_designer.rb b/activerecord/test/models/cake_designer.rb
new file mode 100644
index 0000000000..0b2a00edfd
--- /dev/null
+++ b/activerecord/test/models/cake_designer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..3d6a7a96c2
--- /dev/null
+++ b/activerecord/test/models/car.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+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_many :awesome_bulbs, -> { awesome }, class_name: "Bulb"
+
+ has_one :bulb
+
+ has_many :tyres
+ has_many :engines, dependent: :destroy, inverse_of: :my_car
+ has_many :wheels, as: :wheelable, dependent: :destroy
+
+ has_many :price_estimates, as: :estimate_of
+
+ 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/carrier.rb b/activerecord/test/models/carrier.rb
new file mode 100644
index 0000000000..995a9d3bef
--- /dev/null
+++ b/activerecord/test/models/carrier.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class Carrier < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/cat.rb b/activerecord/test/models/cat.rb
new file mode 100644
index 0000000000..43013964b6
--- /dev/null
+++ b/activerecord/test/models/cat.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class Cat < ActiveRecord::Base
+ self.abstract_class = true
+
+ enum gender: [:female, :male]
+
+ default_scope -> { where(is_vegetarian: false) }
+end
+
+class Lion < Cat
+end
diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb
new file mode 100644
index 0000000000..68b0ea90d3
--- /dev/null
+++ b/activerecord/test/models/categorization.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Categorization < ActiveRecord::Base
+ belongs_to :post
+ belongs_to :category, counter_cache: true
+ 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..2ccc00bed9
--- /dev/null
+++ b/activerecord/test/models/category.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+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") }
+
+ # Should be delegated `ast` and `locked` to `arel`.
+ def self.ast
+ raise
+ end
+
+ def self.locked
+ raise
+ end
+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..ff528644bc
--- /dev/null
+++ b/activerecord/test/models/chef.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class Chef < ActiveRecord::Base
+ belongs_to :employable, polymorphic: true
+ has_many :recipes
+end
+
+class ChefList < Chef
+ belongs_to :employable_list, polymorphic: true
+end
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
new file mode 100644
index 0000000000..3d786f27eb
--- /dev/null
+++ b/activerecord/test/models/citation.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..2006e05fcf
--- /dev/null
+++ b/activerecord/test/models/club.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+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
+
+ scope :general, -> { left_joins(:category).where(categories: { name: "General" }) }
+
+ 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..52017dda42
--- /dev/null
+++ b/activerecord/test/models/college.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+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..d3cd419a00
--- /dev/null
+++ b/activerecord/test/models/column.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..c6047c507b
--- /dev/null
+++ b/activerecord/test/models/column_name.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..61c54a77a7
--- /dev/null
+++ b/activerecord/test/models/comment.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+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
+ belongs_to :special_post_with_default_scope, foreign_key: :post_id
+
+ has_many :children, class_name: "Comment", foreign_key: :parent_id
+ belongs_to :parent, class_name: "Comment", counter_cache: :children_count
+
+ class ::OopsError < RuntimeError; end
+
+ module OopsExtension
+ def destroy_all(*)
+ raise OopsError
+ end
+ end
+
+ default_scope { extending OopsExtension }
+
+ scope :oops_comments, -> { extending OopsExtension }
+
+ # Should not be called if extending modules that having the method exists on an association.
+ def self.greeting
+ raise
+ end
+
+ 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
+ default_scope { where(deleted_at: nil) }
+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
+
+class CommentWithDefaultScopeReferencesAssociation < Comment
+ default_scope -> { includes(:developer).order("developers.name").references(:developer) }
+ belongs_to :developer
+end
+
+class CommentWithAfterCreateUpdate < Comment
+ after_create do
+ update_attributes(body: "bar")
+ end
+end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
new file mode 100644
index 0000000000..bbc5fc2b2d
--- /dev/null
+++ b/activerecord/test/models/company.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+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
+
+ class SpecialCo < Company
+ 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 :account_with_inexistent_foreign_key, class_name: "Account", foreign_key: "inexistent"
+ 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"
+
+ has_one :lead_developer, class_name: "Developer"
+ has_many :projects
+
+ 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
+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_hash_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
+
+require "models/account"
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
new file mode 100644
index 0000000000..52b7e06a63
--- /dev/null
+++ b/activerecord/test/models/company_in_module.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+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
+
+ private
+
+ def check_empty_credit_limit
+ errors.add("credit_card", :blank) if credit_card.blank?
+ end
+ end
+ end
+end
diff --git a/activerecord/test/models/computer.rb b/activerecord/test/models/computer.rb
new file mode 100644
index 0000000000..582b4a38b5
--- /dev/null
+++ b/activerecord/test/models/computer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..6e02ff199b
--- /dev/null
+++ b/activerecord/test/models/contact.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module ContactFakeColumns
+ def self.extended(base)
+ base.class_eval do
+ establish_connection(adapter: "fake")
+
+ connection.data_sources = [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/content.rb b/activerecord/test/models/content.rb
new file mode 100644
index 0000000000..14bbee53d8
--- /dev/null
+++ b/activerecord/test/models/content.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class Content < ActiveRecord::Base
+ self.table_name = "content"
+ has_one :content_position, dependent: :destroy
+
+ def self.destroyed_ids
+ @destroyed_ids ||= []
+ end
+
+ before_destroy do |object|
+ Content.destroyed_ids << object.id
+ end
+end
+
+class ContentWhichRequiresTwoDestroyCalls < ActiveRecord::Base
+ self.table_name = "content"
+ has_one :content_position, foreign_key: "content_id", dependent: :destroy
+
+ after_initialize do
+ @destroy_count = 0
+ end
+
+ before_destroy do
+ @destroy_count += 1
+ if @destroy_count == 1
+ throw :abort
+ end
+ end
+end
+
+class ContentPosition < ActiveRecord::Base
+ belongs_to :content, dependent: :destroy
+
+ def self.destroyed_ids
+ @destroyed_ids ||= []
+ end
+
+ before_destroy do |object|
+ ContentPosition.destroyed_ids << object.id
+ end
+end
diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb
new file mode 100644
index 0000000000..9454217e8d
--- /dev/null
+++ b/activerecord/test/models/contract.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+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..0c84a40de2
--- /dev/null
+++ b/activerecord/test/models/country.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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..4f346124ea
--- /dev/null
+++ b/activerecord/test/models/course.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+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..524a9d7bd9
--- /dev/null
+++ b/activerecord/test/models/customer.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+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(&: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
+ composed_of :fullname_no_converter, mapping: %w(name to_s), class_name: "Fullname"
+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)
+ latitude == other.latitude && longitude == other.longitude
+ end
+end
+
+class Fullname
+ attr_reader :first, :last
+
+ def self.parse(str)
+ return nil unless str
+
+ if str.is_a?(Hash)
+ new(str[:first], str[:last])
+ else
+ new(*str.to_s.split)
+ end
+ 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/customer_carrier.rb b/activerecord/test/models/customer_carrier.rb
new file mode 100644
index 0000000000..6cb9d5239d
--- /dev/null
+++ b/activerecord/test/models/customer_carrier.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class CustomerCarrier < ActiveRecord::Base
+ cattr_accessor :current_customer
+
+ belongs_to :customer
+ belongs_to :carrier
+
+ default_scope -> {
+ if current_customer
+ where(customer: current_customer)
+ else
+ all
+ end
+ }
+end
diff --git a/activerecord/test/models/dashboard.rb b/activerecord/test/models/dashboard.rb
new file mode 100644
index 0000000000..d25ceeafb1
--- /dev/null
+++ b/activerecord/test/models/dashboard.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..90f1046d87
--- /dev/null
+++ b/activerecord/test/models/default.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+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..868b9bf4bf
--- /dev/null
+++ b/activerecord/test/models/department.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..56aafca60b
--- /dev/null
+++ b/activerecord/test/models/developer.rb
@@ -0,0 +1,270 @@
+# frozen_string_literal: true
+
+require "ostruct"
+
+module DeveloperProjectsAssociationExtension2
+ def find_least_recent
+ order("id ASC").first
+ end
+end
+
+class Developer < ActiveRecord::Base
+ self.ignored_columns = %w(first_name last_name)
+
+ has_and_belongs_to_many :projects do
+ def find_most_recent
+ order("id DESC").first
+ end
+ end
+
+ belongs_to :mentor
+
+ accepts_nested_attributes_for :projects
+
+ has_and_belongs_to_many :shared_computers, class_name: "Computer"
+
+ 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
+ has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") }
+ has_many :ratings, through: :comments
+ has_one :ship, dependent: :nullify
+
+ belongs_to :firm
+ has_many :contracted_projects, class_name: "Project"
+
+ 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
+
+ attr_accessor :last_name
+ define_attribute_method "last_name"
+
+ 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
+ Thread.current[:default_scope_delay].call
+ limit(1)
+ end
+end
+
+class CachedDeveloper < ActiveRecord::Base
+ self.table_name = "developers"
+ self.cache_timestamp_format = :number
+end
+
+class DeveloperWithIncorrectlyOrderedHasManyThrough < ActiveRecord::Base
+ self.table_name = "developers"
+ has_many :companies, through: :contracts
+ has_many :contracts, foreign_key: :developer_id
+end
diff --git a/activerecord/test/models/dog.rb b/activerecord/test/models/dog.rb
new file mode 100644
index 0000000000..75d284ac25
--- /dev/null
+++ b/activerecord/test/models/dog.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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..aabe914f77
--- /dev/null
+++ b/activerecord/test/models/dog_lover.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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/doubloon.rb b/activerecord/test/models/doubloon.rb
new file mode 100644
index 0000000000..febadc3a5a
--- /dev/null
+++ b/activerecord/test/models/doubloon.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class AbstractDoubloon < ActiveRecord::Base
+ # This has functionality that might be shared by multiple classes.
+
+ self.abstract_class = true
+ belongs_to :pirate
+end
+
+class Doubloon < AbstractDoubloon
+ # This uses an abstract class that defines attributes and associations.
+
+ self.table_name = "doubloons"
+end
diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb
new file mode 100644
index 0000000000..1c407844c5
--- /dev/null
+++ b/activerecord/test/models/drink_designer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..a04ab103de
--- /dev/null
+++ b/activerecord/test/models/edge.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# 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..902006b314
--- /dev/null
+++ b/activerecord/test/models/electron.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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..396a52b3b9
--- /dev/null
+++ b/activerecord/test/models/engine.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..2c086e451f
--- /dev/null
+++ b/activerecord/test/models/entrant.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..e59db4d877
--- /dev/null
+++ b/activerecord/test/models/essay.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class Essay < ActiveRecord::Base
+ belongs_to :author
+ 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..a7cdc39e5c
--- /dev/null
+++ b/activerecord/test/models/event.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Event < ActiveRecord::Base
+ validates_uniqueness_of :title
+end
diff --git a/activerecord/test/models/eye.rb b/activerecord/test/models/eye.rb
new file mode 100644
index 0000000000..f3608b62ef
--- /dev/null
+++ b/activerecord/test/models/eye.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+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.has_changes_to_save?
+ end
+ alias trace_after_update2 trace_after_update
+
+ def trace_after_save
+ (@after_save_callbacks_stack ||= []) << iris.has_changes_to_save?
+ 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..796aaa4dc9
--- /dev/null
+++ b/activerecord/test/models/face.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Face < ActiveRecord::Base
+ belongs_to :man, inverse_of: :face
+ belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_face
+ # Oracle identifier length 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/family.rb b/activerecord/test/models/family.rb
new file mode 100644
index 0000000000..0713dba1a6
--- /dev/null
+++ b/activerecord/test/models/family.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Family < ActiveRecord::Base
+ has_many :family_trees, -> { where(token: nil) }
+ has_many :members, through: :family_trees
+end
diff --git a/activerecord/test/models/family_tree.rb b/activerecord/test/models/family_tree.rb
new file mode 100644
index 0000000000..a8ea907c05
--- /dev/null
+++ b/activerecord/test/models/family_tree.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class FamilyTree < ActiveRecord::Base
+ belongs_to :member, class_name: "User", foreign_key: "member_id"
+ belongs_to :family
+end
diff --git a/activerecord/test/models/friendship.rb b/activerecord/test/models/friendship.rb
new file mode 100644
index 0000000000..9f1712a8ec
--- /dev/null
+++ b/activerecord/test/models/friendship.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+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..ec71c37690
--- /dev/null
+++ b/activerecord/test/models/guid.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class Guid < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/guitar.rb b/activerecord/test/models/guitar.rb
new file mode 100644
index 0000000000..649b998665
--- /dev/null
+++ b/activerecord/test/models/guitar.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Guitar < ActiveRecord::Base
+ has_many :tuning_pegs, index_errors: true
+ accepts_nested_attributes_for :tuning_pegs
+end
diff --git a/activerecord/test/models/hotel.rb b/activerecord/test/models/hotel.rb
new file mode 100644
index 0000000000..1a433c3cab
--- /dev/null
+++ b/activerecord/test/models/hotel.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+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
+
+ has_many :chef_lists, as: :employable_list
+ has_many :mocktail_designers, through: :chef_lists, source: :employable, source_type: "MocktailDesigner"
+
+ has_many :recipes, through: :chefs
+end
diff --git a/activerecord/test/models/image.rb b/activerecord/test/models/image.rb
new file mode 100644
index 0000000000..b4808293cc
--- /dev/null
+++ b/activerecord/test/models/image.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Image < ActiveRecord::Base
+ belongs_to :imageable, foreign_key: :imageable_identifier, foreign_type: :imageable_class
+end
diff --git a/activerecord/test/models/interest.rb b/activerecord/test/models/interest.rb
new file mode 100644
index 0000000000..899b8f9b9d
--- /dev/null
+++ b/activerecord/test/models/interest.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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..1851792ed5
--- /dev/null
+++ b/activerecord/test/models/invoice.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..8d079d56e6
--- /dev/null
+++ b/activerecord/test/models/item.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+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..52817a8435
--- /dev/null
+++ b/activerecord/test/models/job.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+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..436ffb6471
--- /dev/null
+++ b/activerecord/test/models/joke.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+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..d200e0fb56
--- /dev/null
+++ b/activerecord/test/models/keyboard.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..e0210c8922
--- /dev/null
+++ b/activerecord/test/models/legacy_thing.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..e546339689
--- /dev/null
+++ b/activerecord/test/models/lesson.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+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..3a51cf03b2
--- /dev/null
+++ b/activerecord/test/models/line_item.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..b2fd305d66
--- /dev/null
+++ b/activerecord/test/models/liquid.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..3acd89a48e
--- /dev/null
+++ b/activerecord/test/models/man.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+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..a77ac21e96
--- /dev/null
+++ b/activerecord/test/models/matey.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..4315ba1941
--- /dev/null
+++ b/activerecord/test/models/member.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+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_one :general_club, -> { general }, through: :current_membership, source: :club
+
+ has_many :current_memberships, -> { where favourite: true }
+ has_many :clubs, through: :current_memberships
+
+ has_many :tenant_memberships
+ has_many :tenant_clubs, through: :tenant_memberships, class_name: "Club", source: :club
+
+ has_one :club_through_many, through: :current_memberships, source: :club
+
+ belongs_to :admittable, polymorphic: true
+ has_one :premium_club, through: :admittable
+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..87f7aab9a2
--- /dev/null
+++ b/activerecord/test/models/member_detail.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class MemberDetail < ActiveRecord::Base
+ belongs_to :member, inverse_of: false
+ belongs_to :organization
+ has_one :member_type, through: :member
+ has_one :membership, 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..b49b168d03
--- /dev/null
+++ b/activerecord/test/models/member_type.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..09ee7544b3
--- /dev/null
+++ b/activerecord/test/models/membership.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class Membership < ActiveRecord::Base
+ enum type: %i(Membership CurrentMembership SuperMembership SelectedMembership TenantMembership)
+ 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
+
+class TenantMembership < Membership
+ cattr_accessor :current_member
+
+ belongs_to :member
+ belongs_to :club
+
+ default_scope -> {
+ if current_member
+ where(member: current_member)
+ else
+ all
+ end
+ }
+end
diff --git a/activerecord/test/models/mentor.rb b/activerecord/test/models/mentor.rb
new file mode 100644
index 0000000000..2fbb62c435
--- /dev/null
+++ b/activerecord/test/models/mentor.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Mentor < ActiveRecord::Base
+ has_many :developers
+end
diff --git a/activerecord/test/models/minimalistic.rb b/activerecord/test/models/minimalistic.rb
new file mode 100644
index 0000000000..c67b086853
--- /dev/null
+++ b/activerecord/test/models/minimalistic.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+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..d9d331798a
--- /dev/null
+++ b/activerecord/test/models/minivan.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+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..8e92f68817
--- /dev/null
+++ b/activerecord/test/models/mixed_case_monkey.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class MixedCaseMonkey < ActiveRecord::Base
+ belongs_to :man
+end
diff --git a/activerecord/test/models/mocktail_designer.rb b/activerecord/test/models/mocktail_designer.rb
new file mode 100644
index 0000000000..123ff4fb3d
--- /dev/null
+++ b/activerecord/test/models/mocktail_designer.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class MocktailDesigner < DrinkDesigner
+end
diff --git a/activerecord/test/models/molecule.rb b/activerecord/test/models/molecule.rb
new file mode 100644
index 0000000000..7da08a85c4
--- /dev/null
+++ b/activerecord/test/models/molecule.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+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..fa2ea900c7
--- /dev/null
+++ b/activerecord/test/models/movie.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Movie < ActiveRecord::Base
+ self.primary_key = "movieid"
+
+ validates_presence_of :name
+end
diff --git a/activerecord/test/models/node.rb b/activerecord/test/models/node.rb
new file mode 100644
index 0000000000..ae46c76b46
--- /dev/null
+++ b/activerecord/test/models/node.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Node < ActiveRecord::Base
+ belongs_to :tree, touch: true
+ belongs_to :parent, class_name: "Node", touch: true, optional: true
+ has_many :children, class_name: "Node", foreign_key: :parent_id, dependent: :destroy
+end
diff --git a/activerecord/test/models/non_primary_key.rb b/activerecord/test/models/non_primary_key.rb
new file mode 100644
index 0000000000..e954375989
--- /dev/null
+++ b/activerecord/test/models/non_primary_key.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class NonPrimaryKey < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/notification.rb b/activerecord/test/models/notification.rb
new file mode 100644
index 0000000000..3f8728af5e
--- /dev/null
+++ b/activerecord/test/models/notification.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Notification < ActiveRecord::Base
+ validates_presence_of :message
+end
diff --git a/activerecord/test/models/numeric_data.rb b/activerecord/test/models/numeric_data.rb
new file mode 100644
index 0000000000..666e1a5778
--- /dev/null
+++ b/activerecord/test/models/numeric_data.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class NumericData < ActiveRecord::Base
+ self.table_name = "numeric_data"
+ # Decimal columns with 0 scale being automatically treated as integers
+ # is deprecated, and will be removed in a future version of Rails.
+ attribute :world_population, :big_integer
+ attribute :my_house_population, :big_integer
+ attribute :atoms_in_universe, :big_integer
+end
diff --git a/activerecord/test/models/order.rb b/activerecord/test/models/order.rb
new file mode 100644
index 0000000000..36866b398f
--- /dev/null
+++ b/activerecord/test/models/order.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..099e7e38e0
--- /dev/null
+++ b/activerecord/test/models/organization.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+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
+
+ has_many :posts, through: :author, source: :posts
+
+ scope :clubs, -> { from("clubs") }
+end
diff --git a/activerecord/test/models/other_dog.rb b/activerecord/test/models/other_dog.rb
new file mode 100644
index 0000000000..a0fda5ae1b
--- /dev/null
+++ b/activerecord/test/models/other_dog.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require_dependency "models/arunit2_model"
+
+class OtherDog < ARUnit2Model
+ self.table_name = "dogs"
+end
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
new file mode 100644
index 0000000000..5fa50d9918
--- /dev/null
+++ b/activerecord/test/models/owner.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+class Owner < ActiveRecord::Base
+ self.primary_key = :owner_id
+ has_many :pets, -> { order "pets.name desc" }
+ has_many :toys, through: :pets
+ has_many :persons, through: :pets
+
+ belongs_to :last_pet, class_name: "Pet"
+ scope :including_last_pet, -> {
+ select('
+ 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
+
+ accepts_nested_attributes_for :pets, allow_destroy: true
+
+ 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..ba9ddb8c6a
--- /dev/null
+++ b/activerecord/test/models/parrot.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+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
+
+ attribute :cancel_save_from_callback
+ before_save :cancel_save_callback_method, if: :cancel_save_from_callback
+ def cancel_save_callback_method
+ throw(:abort)
+ end
+
+ before_update :increment_updated_count
+ def increment_updated_count
+ self.updated_count += 1
+ end
+end
+
+class LiveParrot < Parrot
+end
+
+class DeadParrot < Parrot
+ belongs_to :killer, class_name: "Pirate", foreign_key: :killer_id
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
new file mode 100644
index 0000000000..5cba1e440e
--- /dev/null
+++ b/activerecord/test/models/person.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+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 :personal_legacy_things, dependent: :destroy
+
+ 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") }
+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/personal_legacy_thing.rb b/activerecord/test/models/personal_legacy_thing.rb
new file mode 100644
index 0000000000..ed8b70cfcc
--- /dev/null
+++ b/activerecord/test/models/personal_legacy_thing.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class PersonalLegacyThing < ActiveRecord::Base
+ self.locking_column = :version
+ belongs_to :person, counter_cache: true
+end
diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb
new file mode 100644
index 0000000000..9bda2109e6
--- /dev/null
+++ b/activerecord/test/models/pet.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class Pet < ActiveRecord::Base
+ attr_accessor :current_user
+
+ self.primary_key = :pet_id
+ belongs_to :owner, touch: true
+ has_many :toys
+ has_many :pet_treasures
+ has_many :treasures, through: :pet_treasures
+ has_many :persons, through: :treasures, source: :looter, source_type: "Person"
+
+ 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/pet_treasure.rb b/activerecord/test/models/pet_treasure.rb
new file mode 100644
index 0000000000..47b9f57fad
--- /dev/null
+++ b/activerecord/test/models/pet_treasure.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class PetTreasure < ActiveRecord::Base
+ self.table_name = "pets_treasures"
+
+ belongs_to :pet
+ belongs_to :treasure
+end
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
new file mode 100644
index 0000000000..c8617d1cfe
--- /dev/null
+++ b/activerecord/test/models/pirate.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+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(&:empty?)
+ accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&: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
+ throw(:abort)
+ 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
diff --git a/activerecord/test/models/possession.rb b/activerecord/test/models/possession.rb
new file mode 100644
index 0000000000..9b843e1525
--- /dev/null
+++ b/activerecord/test/models/possession.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..4b3576fce8
--- /dev/null
+++ b/activerecord/test/models/post.rb
@@ -0,0 +1,317 @@
+# frozen_string_literal: true
+
+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
+ "hullo"
+ end
+ end
+
+ scope :containing_the_letter_a, -> { where("body LIKE '%a%'") }
+ scope :titled_with_an_apostrophe, -> { where("title LIKE '%''%'") }
+ scope :ranked_by_comments, -> { order("comments_count DESC") }
+
+ scope :limit_by, lambda { |l| limit(l) }
+ scope :locked, -> { lock }
+
+ belongs_to :author
+ belongs_to :readonly_author, -> { readonly }, class_name: "Author", foreign_key: :author_id
+
+ 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 }) }
+ scope :tagged_with_comment, ->(comment) { joins(:taggings).where(taggings: { comment: comment }) }
+
+ scope :typographically_interesting, -> { containing_the_letter_a.or(titled_with_an_apostrophe) }
+
+ has_many :comments do
+ def find_most_recent
+ order("id DESC").first
+ end
+
+ def newest
+ created.last
+ end
+
+ def the_association
+ proxy_association
+ end
+
+ def with_content(content)
+ self.detect { |comment| comment.body == content }
+ 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_one :very_special_comment
+ has_one :very_special_comment_with_post, -> { includes(:post) }, class_name: "VerySpecialComment"
+ has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order("posts.id") }, class_name: "VerySpecialComment"
+ has_many :special_comments
+ has_many :nonexistent_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, counter_cache: :taggings_with_delete_all_count
+ has_many :taggings_with_destroy, class_name: "Tagging", as: :taggable, dependent: :destroy, counter_cache: :taggings_with_destroy_count
+
+ has_many :tags_with_destroy, through: :taggings, source: :tag, dependent: :destroy, counter_cache: :tags_with_destroy_count
+ has_many :tags_with_nullify, through: :taggings, source: :tag, dependent: :nullify, counter_cache: :tags_with_nullify_count
+
+ 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 :images, as: :imageable, foreign_key: :imageable_identifier, foreign_type: :imageable_class
+ has_one :main_image, as: :imageable, foreign_key: :imageable_identifier, foreign_type: :imageable_class, class_name: "Image"
+
+ 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.inheritance_column = :disabled
+ 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 TaggedPost < Post
+ has_many :taggings, -> { rewhere(taggable_type: "TaggedPost") }, as: :taggable
+ has_many :tags, through: :taggings
+end
+
+class PostWithDefaultInclude < ActiveRecord::Base
+ self.inheritance_column = :disabled
+ 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.inheritance_column = :disabled
+ self.table_name = "posts"
+ default_scope { order(:title) }
+end
+
+class PostWithPreloadDefaultScope < ActiveRecord::Base
+ self.table_name = "posts"
+
+ has_many :readers, foreign_key: "post_id"
+
+ default_scope { preload(:readers) }
+end
+
+class PostWithIncludesDefaultScope < ActiveRecord::Base
+ self.table_name = "posts"
+
+ has_many :readers, foreign_key: "post_id"
+
+ default_scope { includes(:readers) }
+end
+
+class SpecialPostWithDefaultScope < ActiveRecord::Base
+ self.inheritance_column = :disabled
+ self.table_name = "posts"
+ default_scope { where(id: [1, 5, 6]) }
+end
+
+class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
+ self.inheritance_column = :disabled
+ self.table_name = "posts"
+ has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
+
+ after_save do |post|
+ post.comments.load
+ end
+end
+
+class PostWithAfterCreateCallback < ActiveRecord::Base
+ self.inheritance_column = :disabled
+ self.table_name = "posts"
+ has_many :comments, foreign_key: :post_id
+
+ after_create do |post|
+ update_attribute(:author_id, comments.first.id)
+ end
+end
+
+class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base
+ self.inheritance_column = :disabled
+ self.table_name = "posts"
+ has_many :comment_with_default_scope_references_associations, foreign_key: :post_id
+ has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id
+end
+
+class SerializedPost < ActiveRecord::Base
+ serialize :title
+end
+
+class ConditionalStiPost < Post
+ default_scope { where(title: "Untitled") }
+end
+
+class SubConditionalStiPost < ConditionalStiPost
+end
+
+class FakeKlass
+ extend ActiveRecord::Delegation::DelegateCache
+
+ inherited self
+
+ class << self
+ def connection
+ Post.connection
+ end
+
+ def table_name
+ "posts"
+ end
+
+ def attribute_alias?(name)
+ false
+ end
+
+ def sanitize_sql(sql)
+ sql
+ end
+
+ def sanitize_sql_for_order(sql)
+ sql
+ end
+
+ def arel_attribute(name, table)
+ table[name]
+ end
+ end
+end
diff --git a/activerecord/test/models/price_estimate.rb b/activerecord/test/models/price_estimate.rb
new file mode 100644
index 0000000000..f1f88d8d8d
--- /dev/null
+++ b/activerecord/test/models/price_estimate.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class PriceEstimate < ActiveRecord::Base
+ belongs_to :estimate_of, polymorphic: true
+ belongs_to :thing, polymorphic: true
+end
diff --git a/activerecord/test/models/professor.rb b/activerecord/test/models/professor.rb
new file mode 100644
index 0000000000..abc23f40ff
--- /dev/null
+++ b/activerecord/test/models/professor.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require_dependency "models/arunit2_model"
+
+class Professor < ARUnit2Model
+ has_and_belongs_to_many :courses
+end
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
new file mode 100644
index 0000000000..846cef625b
--- /dev/null
+++ b/activerecord/test/models/project.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class Project < ActiveRecord::Base
+ belongs_to :mentor
+ 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_paid_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, class_name: "Developer"
+ belongs_to :firm
+ has_one :lead_developer, through: :firm, inverse_of: :contracted_projects
+
+ begin
+ previous_value, ActiveRecord::Base.belongs_to_required_by_default =
+ ActiveRecord::Base.belongs_to_required_by_default, true
+ has_and_belongs_to_many :developers_required_by_default, class_name: "Developer"
+ ensure
+ ActiveRecord::Base.belongs_to_required_by_default = previous_value
+ end
+
+ 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..53677197c4
--- /dev/null
+++ b/activerecord/test/models/publisher.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+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..355c22dcc5
--- /dev/null
+++ b/activerecord/test/models/publisher/article.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..425ede8df2
--- /dev/null
+++ b/activerecord/test/models/publisher/magazine.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..f90a7b9336
--- /dev/null
+++ b/activerecord/test/models/randomly_named_c1.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table1
+end
diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb
new file mode 100644
index 0000000000..cf06bc6931
--- /dev/null
+++ b/activerecord/test/models/rating.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..d25627e430
--- /dev/null
+++ b/activerecord/test/models/reader.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+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/recipe.rb b/activerecord/test/models/recipe.rb
new file mode 100644
index 0000000000..e53f5c8fb1
--- /dev/null
+++ b/activerecord/test/models/recipe.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Recipe < ActiveRecord::Base
+ belongs_to :chef
+end
diff --git a/activerecord/test/models/record.rb b/activerecord/test/models/record.rb
new file mode 100644
index 0000000000..63143e296a
--- /dev/null
+++ b/activerecord/test/models/record.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+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..2a7a1e3b77
--- /dev/null
+++ b/activerecord/test/models/reference.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+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..bc829ec67f
--- /dev/null
+++ b/activerecord/test/models/reply.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+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..7973219a79
--- /dev/null
+++ b/activerecord/test/models/ship.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+class Ship < ActiveRecord::Base
+ self.record_timestamps = false
+
+ belongs_to :pirate
+ belongs_to :update_only_pirate, class_name: "Pirate"
+ belongs_to :developer, dependent: :destroy
+ has_many :parts, class_name: "ShipPart"
+ has_many :treasures
+
+ accepts_nested_attributes_for :parts, allow_destroy: true
+ accepts_nested_attributes_for :pirate, allow_destroy: true, reject_if: proc(&: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
+ throw(:abort)
+ end
+end
+
+class ShipWithoutNestedAttributes < ActiveRecord::Base
+ self.table_name = "ships"
+ has_many :prisoners, inverse_of: :ship, foreign_key: :ship_id
+ has_many :parts, class_name: "ShipPart", foreign_key: :ship_id
+
+ validates :name, presence: true
+end
+
+class Prisoner < ActiveRecord::Base
+ belongs_to :ship, autosave: true, class_name: "ShipWithoutNestedAttributes", inverse_of: :prisoners
+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..f6d7a8ae5e
--- /dev/null
+++ b/activerecord/test/models/ship_part.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class ShipPart < ActiveRecord::Base
+ belongs_to :ship
+ has_many :trinkets, class_name: "Treasure", as: :looter
+ accepts_nested_attributes_for :trinkets, allow_destroy: true
+ accepts_nested_attributes_for :ship
+
+ validates_presence_of :name
+end
diff --git a/activerecord/test/models/shop.rb b/activerecord/test/models/shop.rb
new file mode 100644
index 0000000000..92afe70b92
--- /dev/null
+++ b/activerecord/test/models/shop.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+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/shop_account.rb b/activerecord/test/models/shop_account.rb
new file mode 100644
index 0000000000..97fb058331
--- /dev/null
+++ b/activerecord/test/models/shop_account.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class ShopAccount < ActiveRecord::Base
+ belongs_to :customer
+ belongs_to :customer_carrier
+
+ has_one :carrier, through: :customer_carrier
+end
diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb
new file mode 100644
index 0000000000..e456907a22
--- /dev/null
+++ b/activerecord/test/models/speedometer.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+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..f190860fd1
--- /dev/null
+++ b/activerecord/test/models/sponsor.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+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..473c145f4c
--- /dev/null
+++ b/activerecord/test/models/string_key_object.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..e750798f74
--- /dev/null
+++ b/activerecord/test/models/student.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Student < ActiveRecord::Base
+ has_and_belongs_to_many :lessons
+ belongs_to :college
+end
diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb
new file mode 100644
index 0000000000..b21969ca2d
--- /dev/null
+++ b/activerecord/test/models/subscriber.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+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..d1d5d21621
--- /dev/null
+++ b/activerecord/test/models/subscription.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..4495ac4a09
--- /dev/null
+++ b/activerecord/test/models/tag.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+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
+
+class OrderedTag < Tag
+ self.table_name = "tags"
+
+ has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id"
+end
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
new file mode 100644
index 0000000000..fc0af026c5
--- /dev/null
+++ b/activerecord/test/models/tagging.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+# 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..dabe3ce06b
--- /dev/null
+++ b/activerecord/test/models/task.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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..2154b50ef7
--- /dev/null
+++ b/activerecord/test/models/topic.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+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, -> {} 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", autosave: true
+ 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
+
+ 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
+
+ private
+
+ def default_written_on
+ self.written_on = Time.now unless attribute_present?("written_on")
+ end
+
+ def destroy_children
+ self.class.where("parent_id = #{id}").delete_all
+ end
+
+ def set_email_address
+ unless 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..4a5697eeb1
--- /dev/null
+++ b/activerecord/test/models/toy.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+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..0b88815cbd
--- /dev/null
+++ b/activerecord/test/models/traffic_light.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+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..b51db56c37
--- /dev/null
+++ b/activerecord/test/models/treasure.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class Treasure < ActiveRecord::Base
+ has_and_belongs_to_many :parrots
+ belongs_to :looter, polymorphic: true
+ # No counter_cache option given
+ belongs_to :ship
+
+ 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..5c1d75aa09
--- /dev/null
+++ b/activerecord/test/models/treaty.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Treaty < ActiveRecord::Base
+ self.primary_key = :treaty_id
+
+ has_and_belongs_to_many :countries
+end
diff --git a/activerecord/test/models/tree.rb b/activerecord/test/models/tree.rb
new file mode 100644
index 0000000000..77050c5fff
--- /dev/null
+++ b/activerecord/test/models/tree.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Tree < ActiveRecord::Base
+ has_many :nodes, dependent: :destroy
+end
diff --git a/activerecord/test/models/tuning_peg.rb b/activerecord/test/models/tuning_peg.rb
new file mode 100644
index 0000000000..6e052e1d0f
--- /dev/null
+++ b/activerecord/test/models/tuning_peg.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class TuningPeg < ActiveRecord::Base
+ belongs_to :guitar
+ validates_numericality_of :pitch
+end
diff --git a/activerecord/test/models/tyre.rb b/activerecord/test/models/tyre.rb
new file mode 100644
index 0000000000..d627026585
--- /dev/null
+++ b/activerecord/test/models/tyre.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class Tyre < ActiveRecord::Base
+ belongs_to :car
+
+ def self.custom_find(id)
+ find(id)
+ end
+
+ def self.custom_find_by(*args)
+ find_by(*args)
+ end
+end
diff --git a/activerecord/test/models/user.rb b/activerecord/test/models/user.rb
new file mode 100644
index 0000000000..3efbc45d2a
--- /dev/null
+++ b/activerecord/test/models/user.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require "models/job"
+
+class User < ActiveRecord::Base
+ has_secure_token
+ has_secure_token :auth_token
+
+ has_and_belongs_to_many :jobs_pool,
+ class_name: "Job",
+ join_table: "jobs_pool"
+
+ has_one :family_tree, -> { where(token: nil) }, foreign_key: "member_id"
+ has_one :family, through: :family_tree
+ has_many :family_members, through: :family, source: :members
+end
+
+class UserWithNotification < User
+ after_create -> { Notification.create! message: "A new user has been created." }
+end
diff --git a/activerecord/test/models/uuid_child.rb b/activerecord/test/models/uuid_child.rb
new file mode 100644
index 0000000000..9fce361cc8
--- /dev/null
+++ b/activerecord/test/models/uuid_child.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class UuidChild < ActiveRecord::Base
+ belongs_to :uuid_parent
+end
diff --git a/activerecord/test/models/uuid_item.rb b/activerecord/test/models/uuid_item.rb
new file mode 100644
index 0000000000..41f68c4c18
--- /dev/null
+++ b/activerecord/test/models/uuid_item.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class UuidItem < ActiveRecord::Base
+end
+
+class UuidValidatingItem < UuidItem
+ validates_uniqueness_of :uuid
+end
diff --git a/activerecord/test/models/uuid_parent.rb b/activerecord/test/models/uuid_parent.rb
new file mode 100644
index 0000000000..05db61855e
--- /dev/null
+++ b/activerecord/test/models/uuid_parent.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..cfaab08ed1
--- /dev/null
+++ b/activerecord/test/models/vegetables.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+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/vehicle.rb b/activerecord/test/models/vehicle.rb
new file mode 100644
index 0000000000..c9b3338522
--- /dev/null
+++ b/activerecord/test/models/vehicle.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Vehicle < ActiveRecord::Base
+ self.abstract_class = true
+ default_scope -> { where("tires_count IS NOT NULL") }
+end
+
+class Bus < Vehicle
+end
diff --git a/activerecord/test/models/vertex.rb b/activerecord/test/models/vertex.rb
new file mode 100644
index 0000000000..0ad8114898
--- /dev/null
+++ b/activerecord/test/models/vertex.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+# 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..099633af55
--- /dev/null
+++ b/activerecord/test/models/warehouse_thing.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+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..e05fb64477
--- /dev/null
+++ b/activerecord/test/models/wheel.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..fc0a52d6ad
--- /dev/null
+++ b/activerecord/test/models/without_table.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+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..6f361665ef
--- /dev/null
+++ b/activerecord/test/models/zine.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Zine < ActiveRecord::Base
+ has_many :interests, inverse_of: :zine
+end