Skip to content

Commit

Permalink
Add support for namespaced models
Browse files Browse the repository at this point in the history
  • Loading branch information
mhuggins committed Mar 11, 2012
1 parent 7bc293e commit a8ff332
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 106 deletions.
7 changes: 4 additions & 3 deletions lib/multiple_table_inheritance/child/base.rb
Expand Up @@ -51,7 +51,7 @@ def inherits_from(association_name, options={})

def parent_association_class
reflection = create_reflection(:belongs_to, parent_association_name, {}, self)
reflection.class_name.constantize
reflection.klass
end

def inherited_columns_and_associations
Expand All @@ -76,9 +76,10 @@ def inherited_columns_and_associations
module FinderMethods
def find_by_sql(*args)
child_records = super(*args)

child_records.each do |child|
child.send(:parent_association=, parent_association_class.as_supertype.find_by_id(child.id))
parent = parent_association_class.as_supertype.find_by_id(child.id)
child.send(:parent_association=, parent)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/multiple_table_inheritance/parent/base.rb
Expand Up @@ -16,7 +16,7 @@ module ClassMethods
def acts_as_superclass(options={})
options = Base::default_options.merge(options.to_options)
self.subtype_column = options[:subtype]

if column_names.include?(subtype_column.to_s)
include InstanceMethods
before_destroy :destroy_child_association
Expand All @@ -33,7 +33,7 @@ def destroy_child_association
rescue NameError => e
# TODO log error
end

def find_by_subtype(*args)
super || send("find_by_#{subtype_column}", *args)
end
Expand Down
4 changes: 2 additions & 2 deletions spec/active_record/child_spec.rb
Expand Up @@ -57,7 +57,7 @@

context 'deleting records' do
before do
mock_everything!
mock_employees!
@programmer = Programmer.first
@programmer_id = @programmer.id
end
Expand All @@ -81,7 +81,7 @@

context 'methods' do
before do
mock_everything!
mock_employees!
end

context 'accessing parent records' do
Expand Down
173 changes: 107 additions & 66 deletions spec/active_record/parent_spec.rb
Expand Up @@ -10,61 +10,81 @@
end
end

context 'retrieving records' do
before do
mock_everything!
end
context 'non-namespaced classes with default subtype' do
context 'retrieving records' do
before do
mock_employees!
end

it 'should retrieve child records' do
Employee.find_each do |programmer_or_manager|
programmer_or_manager.should_not be_instance_of(Employee)
['Programmer', 'Manager'].should include(programmer_or_manager.class.to_s)
it 'should retrieve child records' do
Employee.find_each do |programmer_or_manager|
programmer_or_manager.should_not be_instance_of(Employee)
['Programmer', 'Manager'].should include(programmer_or_manager.class.to_s)
end
end
end

it 'should allow access to parent record' do
programmer_or_manager = Employee.first
programmer_or_manager.employee.should be_instance_of(Employee)
end
it 'should allow access to parent record' do
programmer_or_manager = Employee.first
programmer_or_manager.employee.should be_instance_of(Employee)
end

it 'should include all records' do
modified_results = Employee.all
original_results = Employee.as_supertype.all
modified_results.size.should == original_results.size
end
it 'should include all records' do
modified_results = Employee.all
original_results = Employee.as_supertype.all
modified_results.size.should == original_results.size
end

it 'should maintain result order' do
modified_results = Employee.order("id desc").all
original_results = Employee.as_supertype.order("id desc").all
modified_results.collect(&:id).should == original_results.collect(&:id)
end
it 'should maintain result order' do
modified_results = Employee.order("id desc").all
original_results = Employee.as_supertype.order("id desc").all
modified_results.collect(&:id).should == original_results.collect(&:id)
end

context 'associations preloading' do
context 'is enabled' do
before do
@programmer_or_manager = Employee.includes(:team).first
end
context 'associations preloading' do
context 'is enabled' do
before do
@programmer_or_manager = Employee.includes(:team).first
end

it 'should not perform an extra find' do
pending "ensure that team is not retrieved from the database"
Team.any_instance.should_not_receive(:find_by_sql)
@programmer_or_manager.employee.team
it 'should not perform an extra find' do
pending "ensure that team is not retrieved from the database"
Team.any_instance.should_not_receive(:find_by_sql)
@programmer_or_manager.employee.team
end
end
end

context 'is disabled' do
before do
@programmer_or_manager = Employee.first
end
context 'is disabled' do
before do
@programmer_or_manager = Employee.first
end

it 'should not perform an extra find' do
pending "ensure that team is retrieved from the database"
Team.any_instance.should_receive(:find_by_sql).at_least(:once)
@programmer_or_manager.employee.team
it 'should not perform an extra find' do
pending "ensure that team is retrieved from the database"
Team.any_instance.should_receive(:find_by_sql).at_least(:once)
@programmer_or_manager.employee.team
end
end
end
end

context 'deleting records' do
before do
programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000, :team => @team)
@employee = programmer.employee
@employee_id = programmer.id
end

it 'should delete the parent record' do
@employee.destroy.should be_true
Employee.find_by_id(@employee_id).should be_nil
end

it 'should delete the child record' do
@employee.destroy
Programmer.find_by_id(@employee_id).should be_nil
end
end

context 'an invalid subtype exists' do
before do
@employee = Employee.create!(:first_name => 'Sub', :last_name => 'Type', :salary => 50000, :team => @team) do |employee|
Expand All @@ -80,35 +100,56 @@
pending "logger.error should have been called"
end
end

context 'default subtype is used' do
pending "test_everything"
end

context 'custom subtype is used' do
pending "test_everything"
end

context 'namespaced classes are being used' do
pending "test_everything"
end
end

context 'deleting records' do
before do
programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000, :team => @team)
@employee = programmer.employee
@employee_id = programmer.id
end

it 'should delete the parent record' do
@employee.destroy.should be_true
Employee.find_by_id(@employee_id).should be_nil
context 'namespaced classes with custom subtype' do
context 'retrieving records' do
before(:each) do
mock_pets!
end

it 'should retrieve child records' do
Pet::Pet.find_each do |cat_or_dog|
cat_or_dog.should_not be_instance_of(Pet::Pet)
['Pet::Cat', 'Pet::Dog'].should include(cat_or_dog.class.to_s)
end
end

it 'should allow access to parent record' do
cat_or_dog = Pet::Pet.first
cat_or_dog.pet.should be_instance_of(Pet::Pet)
end

it 'should include all records' do
modified_results = Pet::Pet.all
original_results = Pet::Pet.as_supertype.all
modified_results.size.should == original_results.size
end

it 'should maintain result order' do
modified_results = Pet::Pet.order("id desc").all
original_results = Pet::Pet.as_supertype.order("id desc").all
modified_results.collect(&:id).should == original_results.collect(&:id)
end
end

it 'should delete the child record' do
@employee.destroy
Programmer.find_by_id(@employee_id).should be_nil
context 'deleting records' do
before do
mock_pets!
dog = Pet::Dog.first
@pet = dog.pet
@pet_id = dog.id
end

it 'should delete the parent record' do
@pet.destroy.should be_true
Pet::Pet.find_by_id(@pet_id).should be_nil
end

it 'should delete the child record' do
@pet.destroy
Pet::Dog.find_by_id(@pet_id).should be_nil
end
end
end
end
20 changes: 19 additions & 1 deletion spec/spec_helper.rb
Expand Up @@ -8,7 +8,7 @@
require 'support/models'

module MultipleTableInheritanceSpecHelper
def mock_everything!
def mock_employees!
3.times do |i|
team = Team.create!(:name => "Team#{i}")
language = Language.create!(:name => "Java 1.#{i + 4}")
Expand All @@ -30,6 +30,24 @@ def mock_everything!
:bonus => i * 2500) # manager-specific field
end
end

def mock_pets!
3.times do |i|
owner = Pet::Owner.create!(:first_name => 'Bob', :last_name => "Smith #{i}") do |owner|
owner.ssn = (123456789 + i).to_s
end

dog = Pet::Dog.create!(:name => "Rover #{i}") do |dog|
dog.owner = owner
dog.favorite_toy = "#{i + 1}-inch Bone"
end

cat = Pet::Cat.create!(:name => "Mittens #{i}") do |cat|
cat.owner = owner
cat.longest_nap = 100 + i
end
end
end
end

# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
Expand Down
38 changes: 38 additions & 0 deletions spec/support/models.rb
@@ -1,3 +1,7 @@
###############################################
# Non-namespaced models
###############################################

class Employee < ActiveRecord::Base
acts_as_superclass
attr_accessible :first_name, :last_name, :salary, :team, :team_id
Expand Down Expand Up @@ -39,3 +43,37 @@ class KnownLanguage < ActiveRecord::Base
validates :programmer_id, :presence => true
validates :language_id, :presence => true, :uniqueness => { :scope => :programmer_id }
end

###############################################
# Namespaced models
###############################################

module Pet
def self.table_name_prefix
'pet_'
end

class Owner < ActiveRecord::Base
attr_accessible :first_name, :last_name
has_many :pets
validates :first_name, :presence => true
validates :last_name, :presence => true
validates :ssn, :presence => true
end

class Pet < ActiveRecord::Base
acts_as_superclass :subtype => 'species'
attr_accessible :name
belongs_to :owner
validates :owner_id, :presence => true
validates :name, :presence => true
end

class Cat < ActiveRecord::Base
inherits_from :pet, :class_name => 'Pet::Pet'
end

class Dog < ActiveRecord::Base
inherits_from :pet, :class_name => 'Pet::Pet'
end
end

0 comments on commit a8ff332

Please sign in to comment.