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