multiple_table_inheritance 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|