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 CHANGED
@@ -1,3 +1,7 @@
1
+ 0.1.3
2
+
3
+ * Fixed a bug that prevented migrations from being run properly.
4
+
1
5
  0.1.2
2
6
 
3
7
  * Fixed a bug that resulted in parent record not being accessible after
data/README.md CHANGED
@@ -23,7 +23,7 @@ From the command line:
23
23
 
24
24
  From your Gemfile:
25
25
 
26
- gem 'multiple_table_inheritance', '~> 0.1.2'
26
+ gem 'multiple_table_inheritance', '~> 0.1.3'
27
27
 
28
28
  Usage
29
29
  =====
@@ -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.class_name.constantize
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.extend ClassMethods
4
+ base.alias_method_chain :create_table, :inherits
5
5
  end
6
6
 
7
- module ClassMethods
8
- def create_table(table_name, options = {}, &block)
9
- options[:primary_key] = "#{options[:inherits]}_id" if options[:inherits]
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
@@ -1,3 +1,3 @@
1
1
  module MultipleTableInheritance
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -57,7 +57,7 @@ describe MultipleTableInheritance::Child do
57
57
 
58
58
  context 'deleting records' do
59
59
  before do
60
- mock_everything!
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
- mock_everything!
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 'retrieving records' do
14
- before do
15
- mock_everything!
16
- end
13
+ context 'non-namespaced classes' do
14
+ context 'retrieving records' do
15
+ before do
16
+ mock_employees!
17
+ end
17
18
 
18
- it 'should retrieve child records' do
19
- Employee.find_each do |programmer_or_manager|
20
- programmer_or_manager.should_not be_instance_of(Employee)
21
- ['Programmer', 'Manager'].should include(programmer_or_manager.class.to_s)
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
- it 'should allow access to parent record' do
26
- programmer_or_manager = Employee.first
27
- programmer_or_manager.employee.should be_instance_of(Employee)
28
- end
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
- it 'should include all records' do
31
- modified_results = Employee.all
32
- original_results = Employee.as_supertype.all
33
- modified_results.size.should == original_results.size
34
- end
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
- it 'should maintain result order' do
37
- modified_results = Employee.order("id desc").all
38
- original_results = Employee.as_supertype.order("id desc").all
39
- modified_results.collect(&:id).should == original_results.collect(&:id)
40
- end
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
- context 'associations preloading' do
43
- context 'is enabled' do
44
- before do
45
- @programmer_or_manager = Employee.includes(:team).first
46
- end
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
- it 'should not perform an extra find' do
49
- pending "ensure that team is not retrieved from the database"
50
- Team.any_instance.should_not_receive(:find_by_sql)
51
- @programmer_or_manager.employee.team
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
- context 'is disabled' do
56
- before do
57
- @programmer_or_manager = Employee.first
58
- end
56
+ context 'is disabled' do
57
+ before do
58
+ @programmer_or_manager = Employee.first
59
+ end
59
60
 
60
- it 'should not perform an extra find' do
61
- pending "ensure that team is retrieved from the database"
62
- Team.any_instance.should_receive(:find_by_sql).at_least(:once)
63
- @programmer_or_manager.employee.team
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 is used' do
104
+ context 'default subtype' do
85
105
  pending "test_everything"
86
106
  end
87
107
 
88
- context 'custom subtype is used' do
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 'deleting records' do
98
- before do
99
- programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000, :team => @team)
100
- @employee = programmer.employee
101
- @employee_id = programmer.id
102
- end
103
-
104
- it 'should delete the parent record' do
105
- @employee.destroy.should be_true
106
- Employee.find_by_id(@employee_id).should be_nil
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
- it 'should delete the child record' do
110
- @employee.destroy
111
- Programmer.find_by_id(@employee_id).should be_nil
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 mock_everything!
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
@@ -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
@@ -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 do
7
- ['employees', 'programmers', 'managers', 'teams', 'languages', 'known_languages'].each do |table|
8
- execute "DROP TABLE IF EXISTS '#{table}'"
9
- end
10
-
11
- create_table :employees do |t|
12
- t.string :subtype, :null => false
13
- t.string :first_name, :null => false
14
- t.string :last_name, :null => false
15
- t.integer :salary, :null => false
16
- t.integer :team_id
17
- end
18
-
19
- create_table :programmers, :inherits => :employee do |t|
20
- end
21
-
22
- create_table :managers, :inherits => :employee do |t|
23
- t.integer :bonus
24
- end
25
-
26
- create_table :teams do |t|
27
- t.string :name, :null => false
28
- end
29
-
30
- create_table :languages do |t|
31
- t.string :name, :null => false
32
- end
33
-
34
- create_table :known_languages do |t|
35
- t.integer :programmer_id, :null => false
36
- t.integer :language_id, :null => false
37
- end
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.2
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: &2156557900 !ruby/object:Gem::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: *2156557900
24
+ version_requirements: *2161139800
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &2156557340 !ruby/object:Gem::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: *2156557340
35
+ version_requirements: *2161139340
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &2156556760 !ruby/object:Gem::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: *2156556760
46
+ version_requirements: *2161138880
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec_tag_matchers
49
- requirement: &2156556240 !ruby/object:Gem::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: *2156556240
57
+ version_requirements: *2161138420
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: sqlite3-ruby
60
- requirement: &2156555400 !ruby/object:Gem::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: *2156555400
68
+ version_requirements: *2161137960
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: database_cleaner
71
- requirement: &2156554360 !ruby/object:Gem::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: *2156554360
79
+ version_requirements: *2161137500
80
80
  description: ActiveRecord plugin designed to allow simple multiple table inheritance.
81
81
  email:
82
82
  - matt@matthuggins.com