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 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