multiple_table_inheritance 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/multiple_table_inheritance/child/base.rb +2 -2
- data/lib/multiple_table_inheritance/migration.rb +4 -6
- data/lib/multiple_table_inheritance/parent/base.rb +2 -2
- data/lib/multiple_table_inheritance/version.rb +1 -1
- data/spec/active_record/child_spec.rb +2 -2
- data/spec/active_record/parent_spec.rb +109 -60
- data/spec/spec_helper.rb +21 -1
- data/spec/support/models.rb +38 -0
- data/spec/support/tables.rb +54 -32
- metadata +13 -13
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -51,7 +51,7 @@ module MultipleTableInheritance
|
|
51
51
|
|
52
52
|
def parent_association_class
|
53
53
|
reflection = create_reflection(:belongs_to, parent_association_name, {}, self)
|
54
|
-
reflection.
|
54
|
+
reflection.klass
|
55
55
|
end
|
56
56
|
|
57
57
|
def inherited_columns_and_associations
|
@@ -76,7 +76,7 @@ module MultipleTableInheritance
|
|
76
76
|
module FinderMethods
|
77
77
|
def find_by_sql(*args)
|
78
78
|
child_records = super(*args)
|
79
|
-
|
79
|
+
|
80
80
|
child_records.each do |child|
|
81
81
|
child.send(:parent_association=, parent_association_class.as_supertype.find_by_id(child.id))
|
82
82
|
end
|
@@ -1,14 +1,12 @@
|
|
1
1
|
module MultipleTableInheritance
|
2
2
|
module Migration
|
3
3
|
def self.included(base)
|
4
|
-
base.
|
4
|
+
base.alias_method_chain :create_table, :inherits
|
5
5
|
end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
super(table_name, options, &block)
|
11
|
-
end
|
7
|
+
def create_table_with_inherits(table_name, options = {}, &block)
|
8
|
+
options[:primary_key] = "#{options[:inherits]}_id" if options[:inherits]
|
9
|
+
create_table_without_inherits(table_name, options, &block)
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
@@ -16,7 +16,7 @@ module MultipleTableInheritance
|
|
16
16
|
def acts_as_superclass(options={})
|
17
17
|
options = Base::default_options.merge(options.to_options)
|
18
18
|
self.subtype_column = options[:subtype]
|
19
|
-
|
19
|
+
|
20
20
|
if column_names.include?(subtype_column.to_s)
|
21
21
|
include InstanceMethods
|
22
22
|
before_destroy :destroy_child_association
|
@@ -33,7 +33,7 @@ module MultipleTableInheritance
|
|
33
33
|
rescue NameError => e
|
34
34
|
# TODO log error
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def find_by_subtype(*args)
|
38
38
|
super || send("find_by_#{subtype_column}", *args)
|
39
39
|
end
|
@@ -57,7 +57,7 @@ describe MultipleTableInheritance::Child do
|
|
57
57
|
|
58
58
|
context 'deleting records' do
|
59
59
|
before do
|
60
|
-
|
60
|
+
mock_employees!
|
61
61
|
@programmer = Programmer.first
|
62
62
|
@programmer_id = @programmer.id
|
63
63
|
end
|
@@ -81,7 +81,7 @@ describe MultipleTableInheritance::Child do
|
|
81
81
|
|
82
82
|
context 'methods' do
|
83
83
|
before do
|
84
|
-
|
84
|
+
mock_employees!
|
85
85
|
end
|
86
86
|
|
87
87
|
context 'accessing parent records' do
|
@@ -10,61 +10,81 @@ describe MultipleTableInheritance::Parent do
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
context '
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
context 'non-namespaced classes' do
|
14
|
+
context 'retrieving records' do
|
15
|
+
before do
|
16
|
+
mock_employees!
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
it 'should retrieve child records' do
|
20
|
+
Employee.find_each do |programmer_or_manager|
|
21
|
+
programmer_or_manager.should_not be_instance_of(Employee)
|
22
|
+
['Programmer', 'Manager'].should include(programmer_or_manager.class.to_s)
|
23
|
+
end
|
22
24
|
end
|
23
|
-
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
it 'should allow access to parent record' do
|
27
|
+
programmer_or_manager = Employee.first
|
28
|
+
programmer_or_manager.employee.should be_instance_of(Employee)
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
it 'should include all records' do
|
32
|
+
modified_results = Employee.all
|
33
|
+
original_results = Employee.as_supertype.all
|
34
|
+
modified_results.size.should == original_results.size
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
it 'should maintain result order' do
|
38
|
+
modified_results = Employee.order("id desc").all
|
39
|
+
original_results = Employee.as_supertype.order("id desc").all
|
40
|
+
modified_results.collect(&:id).should == original_results.collect(&:id)
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
context 'associations preloading' do
|
44
|
+
context 'is enabled' do
|
45
|
+
before do
|
46
|
+
@programmer_or_manager = Employee.includes(:team).first
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
it 'should not perform an extra find' do
|
50
|
+
pending "ensure that team is not retrieved from the database"
|
51
|
+
Team.any_instance.should_not_receive(:find_by_sql)
|
52
|
+
@programmer_or_manager.employee.team
|
53
|
+
end
|
52
54
|
end
|
53
|
-
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
context 'is disabled' do
|
57
|
+
before do
|
58
|
+
@programmer_or_manager = Employee.first
|
59
|
+
end
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
it 'should not perform an extra find' do
|
62
|
+
pending "ensure that team is retrieved from the database"
|
63
|
+
Team.any_instance.should_receive(:find_by_sql).at_least(:once)
|
64
|
+
@programmer_or_manager.employee.team
|
65
|
+
end
|
64
66
|
end
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
70
|
+
context 'deleting records' do
|
71
|
+
before do
|
72
|
+
programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000, :team => @team)
|
73
|
+
@employee = programmer.employee
|
74
|
+
@employee_id = programmer.id
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should delete the parent record' do
|
78
|
+
@employee.destroy.should be_true
|
79
|
+
Employee.find_by_id(@employee_id).should be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should delete the child record' do
|
83
|
+
@employee.destroy
|
84
|
+
Programmer.find_by_id(@employee_id).should be_nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
68
88
|
context 'an invalid subtype exists' do
|
69
89
|
before do
|
70
90
|
@employee = Employee.create!(:first_name => 'Sub', :last_name => 'Type', :salary => 50000, :team => @team) do |employee|
|
@@ -81,34 +101,63 @@ describe MultipleTableInheritance::Parent do
|
|
81
101
|
end
|
82
102
|
end
|
83
103
|
|
84
|
-
context 'default subtype
|
104
|
+
context 'default subtype' do
|
85
105
|
pending "test_everything"
|
86
106
|
end
|
87
107
|
|
88
|
-
context 'custom subtype
|
89
|
-
pending "test_everything"
|
90
|
-
end
|
91
|
-
|
92
|
-
context 'namespaced classes are being used' do
|
108
|
+
context 'custom subtype' do
|
93
109
|
pending "test_everything"
|
94
110
|
end
|
95
111
|
end
|
96
112
|
|
97
|
-
context '
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
113
|
+
context 'namespaced classes' do
|
114
|
+
context 'retrieving records' do
|
115
|
+
before(:each) do
|
116
|
+
mock_pets!
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should retrieve child records' do
|
120
|
+
Pet::Pet.find_each do |cat_or_dog|
|
121
|
+
cat_or_dog.should_not be_instance_of(Pet::Pet)
|
122
|
+
['Pet::Cat', 'Pet::Dog'].should include(cat_or_dog.class.to_s)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should allow access to parent record' do
|
127
|
+
cat_or_dog = Pet::Pet.first
|
128
|
+
cat_or_dog.pet.should be_instance_of(Pet::Pet)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should include all records' do
|
132
|
+
modified_results = Pet::Pet.all
|
133
|
+
original_results = Pet::Pet.as_supertype.all
|
134
|
+
modified_results.size.should == original_results.size
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should maintain result order' do
|
138
|
+
modified_results = Pet::Pet.order("id desc").all
|
139
|
+
original_results = Pet::Pet.as_supertype.order("id desc").all
|
140
|
+
modified_results.collect(&:id).should == original_results.collect(&:id)
|
141
|
+
end
|
107
142
|
end
|
108
143
|
|
109
|
-
|
110
|
-
|
111
|
-
|
144
|
+
context 'deleting records' do
|
145
|
+
before do
|
146
|
+
mock_pets!
|
147
|
+
dog = Pet::Dog.first
|
148
|
+
@pet = dog.pet
|
149
|
+
@pet_id = dog.id
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should delete the parent record' do
|
153
|
+
@pet.destroy.should be_true
|
154
|
+
Pet::Pet.find_by_id(@pet_id).should be_nil
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should delete the child record' do
|
158
|
+
@pet.destroy
|
159
|
+
Pet::Dog.find_by_id(@pet_id).should be_nil
|
160
|
+
end
|
112
161
|
end
|
113
162
|
end
|
114
163
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,7 +8,7 @@ require 'support/tables'
|
|
8
8
|
require 'support/models'
|
9
9
|
|
10
10
|
module MultipleTableInheritanceSpecHelper
|
11
|
-
def
|
11
|
+
def mock_employees!
|
12
12
|
3.times do |i|
|
13
13
|
team = Team.create!(:name => "Team#{i}")
|
14
14
|
language = Language.create!(:name => "Java 1.#{i + 4}")
|
@@ -30,6 +30,26 @@ module MultipleTableInheritanceSpecHelper
|
|
30
30
|
:bonus => i * 2500) # manager-specific field
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
def mock_pets!
|
35
|
+
3.times do |i|
|
36
|
+
owner = Pet::Owner.create!(:first_name => 'Bob', :last_name => "Smith #{i}") do |owner|
|
37
|
+
owner.ssn = (123456789 + i).to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
dog = Pet::Dog.create!(:name => "Rover #{i}") do |dog|
|
41
|
+
dog.owner = owner
|
42
|
+
dog.favorite_toy = "#{i + 1}-inch Bone"
|
43
|
+
end
|
44
|
+
puts dog.inspect
|
45
|
+
|
46
|
+
cat = Pet::Cat.create!(:name => "Mittens #{i}") do |cat|
|
47
|
+
cat.owner = owner
|
48
|
+
cat.longest_nap = 100 + i
|
49
|
+
end
|
50
|
+
puts cat.inspect
|
51
|
+
end
|
52
|
+
end
|
33
53
|
end
|
34
54
|
|
35
55
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
data/spec/support/models.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
###############################################
|
2
|
+
# Non-namespaced models
|
3
|
+
###############################################
|
4
|
+
|
1
5
|
class Employee < ActiveRecord::Base
|
2
6
|
acts_as_superclass
|
3
7
|
attr_accessible :first_name, :last_name, :salary, :team, :team_id
|
@@ -39,3 +43,37 @@ class KnownLanguage < ActiveRecord::Base
|
|
39
43
|
validates :programmer_id, :presence => true
|
40
44
|
validates :language_id, :presence => true, :uniqueness => { :scope => :programmer_id }
|
41
45
|
end
|
46
|
+
|
47
|
+
###############################################
|
48
|
+
# Namespaced models
|
49
|
+
###############################################
|
50
|
+
|
51
|
+
module Pet
|
52
|
+
def self.table_name_prefix
|
53
|
+
'pet_'
|
54
|
+
end
|
55
|
+
|
56
|
+
class Owner < ActiveRecord::Base
|
57
|
+
attr_accessible :first_name, :last_name
|
58
|
+
has_many :pets
|
59
|
+
validates :first_name, :presence => true
|
60
|
+
validates :last_name, :presence => true
|
61
|
+
validates :ssn, :presence => true
|
62
|
+
end
|
63
|
+
|
64
|
+
class Pet < ActiveRecord::Base
|
65
|
+
acts_as_superclass :subtype => 'species'
|
66
|
+
attr_accessible :name
|
67
|
+
belongs_to :owner
|
68
|
+
validates :owner_id, :presence => true
|
69
|
+
validates :name, :presence => true
|
70
|
+
end
|
71
|
+
|
72
|
+
class Dog < ActiveRecord::Base
|
73
|
+
inherits_from :pet, :class_name => 'Pet::Pet'
|
74
|
+
end
|
75
|
+
|
76
|
+
class Dog < ActiveRecord::Base
|
77
|
+
inherits_from :pet, :class_name => 'Pet::Pet'
|
78
|
+
end
|
79
|
+
end
|
data/spec/support/tables.rb
CHANGED
@@ -3,36 +3,58 @@ ActiveRecord::Base.establish_connection(
|
|
3
3
|
:database => File.expand_path(File.join(File.dirname(__FILE__), '../../db/multiple_table_inheritance.db'))
|
4
4
|
)
|
5
5
|
|
6
|
-
ActiveRecord::Base.connection
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
6
|
+
conn = ActiveRecord::Base.connection
|
7
|
+
|
8
|
+
TABLES = ['employees', 'programmers', 'managers', 'teams', 'languages', 'known_languages', 'pet_owners', 'pet_pets', 'pet_dogs', 'pet_cats'].freeze
|
9
|
+
TABLES.each do |table|
|
10
|
+
conn.execute "DROP TABLE IF EXISTS '#{table}'"
|
11
|
+
end
|
12
|
+
|
13
|
+
conn.create_table :employees do |t|
|
14
|
+
t.string :subtype, :null => false
|
15
|
+
t.string :first_name, :null => false
|
16
|
+
t.string :last_name, :null => false
|
17
|
+
t.integer :salary, :null => false
|
18
|
+
t.integer :team_id
|
19
|
+
end
|
20
|
+
|
21
|
+
conn.create_table :programmers, :inherits => :employee do |t|
|
22
|
+
end
|
23
|
+
|
24
|
+
conn.create_table :managers, :inherits => :employee do |t|
|
25
|
+
t.integer :bonus
|
26
|
+
end
|
27
|
+
|
28
|
+
conn.create_table :teams do |t|
|
29
|
+
t.string :name, :null => false
|
30
|
+
end
|
31
|
+
|
32
|
+
conn.create_table :languages do |t|
|
33
|
+
t.string :name, :null => false
|
34
|
+
end
|
35
|
+
|
36
|
+
conn.create_table :known_languages do |t|
|
37
|
+
t.integer :programmer_id, :null => false
|
38
|
+
t.integer :language_id, :null => false
|
39
|
+
end
|
40
|
+
|
41
|
+
conn.create_table :pet_owners do |t|
|
42
|
+
t.string :first_name, :null => false
|
43
|
+
t.string :last_name, :null => false
|
44
|
+
t.string :ssn, :null => false
|
45
|
+
end
|
46
|
+
|
47
|
+
conn.create_table :pet_pets do |t|
|
48
|
+
t.string :species, :null => false
|
49
|
+
t.integer :owner_id, :null => false
|
50
|
+
t.string :name, :null => false
|
51
|
+
t.string :color
|
52
|
+
end
|
53
|
+
|
54
|
+
conn.create_table :pet_dogs, :inherits => :pets do |t|
|
55
|
+
t.string :favorite_toy
|
56
|
+
end
|
57
|
+
|
58
|
+
conn.create_table :pet_cats, :inherits => :pets do |t|
|
59
|
+
t.integer :longest_nap
|
38
60
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multiple_table_inheritance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-03-11 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &2161139800 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2161139800
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &2161139340 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.0.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2161139340
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec-rails
|
38
|
-
requirement: &
|
38
|
+
requirement: &2161138880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.8.0
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2161138880
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec_tag_matchers
|
49
|
-
requirement: &
|
49
|
+
requirement: &2161138420 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.0.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2161138420
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: sqlite3-ruby
|
60
|
-
requirement: &
|
60
|
+
requirement: &2161137960 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.3.3
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2161137960
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: database_cleaner
|
71
|
-
requirement: &
|
71
|
+
requirement: &2161137500 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: 0.7.1
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *2161137500
|
80
80
|
description: ActiveRecord plugin designed to allow simple multiple table inheritance.
|
81
81
|
email:
|
82
82
|
- matt@matthuggins.com
|