multiple_table_inheritance 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ 0.1.2
2
+
3
+ * Fixed a bug that resulted in parent record not being accessible after
4
+ performing a find directly on the child model.
5
+ * Added many more unit tests.
6
+
1
7
  0.1.1
2
8
 
3
9
  * Fixed bidirectional deletion of records.
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.0'
26
+ gem 'multiple_table_inheritance', '~> 0.1.2'
27
27
 
28
28
  Usage
29
29
  =====
@@ -3,5 +3,7 @@ require 'multiple_table_inheritance/railtie'
3
3
  module MultipleTableInheritance
4
4
  extend ActiveSupport::Autoload
5
5
 
6
- autoload :ActiveRecord
6
+ autoload :Child
7
+ autoload :Parent
8
+ autoload :Migration
7
9
  end
@@ -0,0 +1,7 @@
1
+ module MultipleTableInheritance
2
+ module Child
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Base
6
+ end
7
+ end
@@ -1,6 +1,6 @@
1
1
  module MultipleTableInheritance
2
- module ActiveRecord
3
- module Child
2
+ module Child
3
+ module Base
4
4
  def self.default_options
5
5
  { :dependent => :destroy, :inherit_methods => false }
6
6
  end
@@ -13,12 +13,11 @@ module MultipleTableInheritance
13
13
  module ClassMethods
14
14
  def inherits_from(association_name, options={})
15
15
  # Standardize options, and remove those that should not affect the belongs_to relationship
16
- options = Child::default_options.merge(options.to_options.reject { |k,v| v.nil? })
16
+ options = Base::default_options.merge(options.to_options)
17
17
  inherit_methods = options.delete(:inherit_methods)
18
18
 
19
- extend SharedMethods
20
- include SharedMethods
21
- include InstanceMethods
19
+ extend FinderMethods, SharedMethods
20
+ include InstanceMethods, SharedMethods
22
21
  include DelegateMethods if inherit_methods
23
22
 
24
23
  # Set association references.
@@ -74,6 +73,16 @@ module MultipleTableInheritance
74
73
  end
75
74
  end
76
75
 
76
+ module FinderMethods
77
+ def find_by_sql(*args)
78
+ child_records = super(*args)
79
+
80
+ child_records.each do |child|
81
+ child.send(:parent_association=, parent_association_class.as_supertype.find_by_id(child.id))
82
+ end
83
+ end
84
+ end
85
+
77
86
  module SharedMethods
78
87
  def find_by_id(*args)
79
88
  send("find_by_#{parent_association_name}_id", *args)
@@ -87,6 +96,10 @@ module MultipleTableInheritance
87
96
  send(self.class.parent_association_name)
88
97
  end
89
98
 
99
+ def parent_association=(record)
100
+ send("#{self.class.parent_association_name}=", record)
101
+ end
102
+
90
103
  def set_association_subtype
91
104
  association = parent_association
92
105
  if association.attribute_names.include?(association.class.subtype_column)
@@ -0,0 +1,14 @@
1
+ module MultipleTableInheritance
2
+ module Migration
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
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
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module MultipleTableInheritance
2
+ module Parent
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Base
6
+ autoload :Relation
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ module MultipleTableInheritance
2
+ module Parent
3
+ module Base
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.class_attribute :subtype_column
7
+ end
8
+
9
+ def self.default_options
10
+ { :subtype => 'subtype' }
11
+ end
12
+
13
+ module ClassMethods
14
+ delegate :as_supertype, :to => :scoped
15
+
16
+ def acts_as_superclass(options={})
17
+ options = Base::default_options.merge(options.to_options)
18
+ self.subtype_column = options[:subtype]
19
+
20
+ if column_names.include?(subtype_column.to_s)
21
+ include InstanceMethods
22
+ before_destroy :destroy_child_association
23
+ end
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ def destroy_child_association
29
+ child_class = send(subtype_column.to_sym).constantize
30
+ if child = child_class.find_by_id(id)
31
+ child.delete
32
+ end
33
+ rescue NameError => e
34
+ # TODO log error
35
+ end
36
+
37
+ def find_by_subtype(*args)
38
+ super || send("find_by_#{subtype_column}", *args)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ module MultipleTableInheritance
2
+ module Parent
3
+ module Relation
4
+ def self.included(base)
5
+ base.class_eval do
6
+ attr_accessor :return_supertype
7
+ alias_method_chain :to_a, :inherits
8
+ end
9
+ end
10
+
11
+ def to_a_with_inherits
12
+ parent_records = to_a_without_inherits
13
+ return parent_records if @return_supertype || !@klass.subtype_column
14
+
15
+ # Find all child records.
16
+ child_records = []
17
+ ids_by_type(parent_records).each do |type, ids|
18
+ begin
19
+ klass = type.constantize
20
+ child_records += klass.find(ids)
21
+ rescue NameError => e
22
+ # TODO log error
23
+ end
24
+ end
25
+
26
+ # Associate the parent records with the child records to reduce SQL calls and prevent recursion.
27
+ child_records.each do |child|
28
+ association_name = @klass.to_s.demodulize.underscore
29
+ parent = parent_records.find { |parent| child.id == parent.id }
30
+ child.send("#{association_name}=", parent)
31
+ end
32
+
33
+ # Order the child_records array to match the order of the parent_records array.
34
+ child_records.sort! do |a, b|
35
+ a_index = parent_records.index { |parent| parent.id == a.id }
36
+ b_index = parent_records.index { |parent| parent.id == b.id }
37
+ a_index <=> b_index
38
+ end
39
+ end
40
+
41
+ def as_supertype
42
+ relation = clone
43
+ relation.return_supertype = true
44
+ relation
45
+ end
46
+
47
+ private
48
+
49
+ def ids_by_type(records)
50
+ subtypes = records.collect(&@klass.subtype_column.to_sym).uniq
51
+ subtypes = subtypes.collect do |subtype|
52
+ subtype_records = records.select { |record| record[@klass.subtype_column.to_sym] == subtype}
53
+ subtype_ids = subtype_records.collect { |record| record.id }
54
+ [subtype, subtype_ids]
55
+ end
56
+ Hash[subtypes]
57
+ end
58
+ end
59
+ end
60
+ end
@@ -10,12 +10,16 @@ module MultipleTableInheritance
10
10
  class Railtie
11
11
  def self.insert
12
12
  ::ActiveRecord::Base.module_eval do
13
- include MultipleTableInheritance::ActiveRecord::Child
14
- include MultipleTableInheritance::ActiveRecord::Parent
13
+ include MultipleTableInheritance::Child::Base
14
+ include MultipleTableInheritance::Parent::Base
15
+ end
16
+
17
+ ::ActiveRecord::Relation.module_eval do
18
+ include MultipleTableInheritance::Parent::Relation
15
19
  end
16
20
 
17
21
  ::ActiveRecord::ConnectionAdapters::SchemaStatements.module_eval do
18
- include MultipleTableInheritance::ActiveRecord::Migration
22
+ include MultipleTableInheritance::Migration
19
23
  end
20
24
  end
21
25
  end
@@ -1,3 +1,3 @@
1
1
  module MultipleTableInheritance
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,12 +1,54 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MultipleTableInheritance::ActiveRecord::Child do
3
+ describe MultipleTableInheritance::Child do
4
4
  context 'retrieving records' do
5
- pending "todo"
5
+ before do
6
+ programmer = Programmer.create!(:first_name => 'Bob', :last_name => 'Smith', :salary => 50000)
7
+ @programmer_id = programmer.id
8
+ @programmer = Programmer.find(@programmer_id)
9
+ end
10
+
11
+ it 'should retrieve child records' do
12
+ @programmer.should be_instance_of(Programmer)
13
+ @programmer.id.should be(@programmer_id)
14
+ end
15
+
16
+ it 'should allow access to parent record' do
17
+ @programmer.employee.should be_instance_of(Employee)
18
+ @programmer.employee.id.should be(@programmer_id)
19
+ end
6
20
  end
7
21
 
8
22
  context 'creating records' do
9
- pending "todo"
23
+ context 'with mass assignment security' do
24
+ context 'specifying fields that are accessible' do
25
+ it 'should assign accessible fields' do
26
+ pending "create a model that allows for proper testing"
27
+ end
28
+
29
+ it 'should save child associations' do
30
+ pending "create a model that allows for proper testing"
31
+ end
32
+
33
+ it 'should save parent associations' do
34
+ pending "create a model that allows for proper testing"
35
+ end
36
+ end
37
+
38
+ context 'specifying fields that are not accessible' do
39
+ it 'should not assign secure fields' do
40
+ pending "create a model that allows for proper testing"
41
+ end
42
+
43
+ it 'should not save child associations' do
44
+ pending "create a model that allows for proper testing"
45
+ end
46
+
47
+ it 'should not save parent associations' do
48
+ pending "create a model that allows for proper testing"
49
+ end
50
+ end
51
+ end
10
52
  end
11
53
 
12
54
  context 'updating records' do
@@ -14,7 +56,21 @@ describe MultipleTableInheritance::ActiveRecord::Child do
14
56
  end
15
57
 
16
58
  context 'deleting records' do
17
- pending "todo"
59
+ before do
60
+ mock_everything!
61
+ @programmer = Programmer.first
62
+ @programmer_id = @programmer.id
63
+ end
64
+
65
+ it 'should delete the parent record' do
66
+ @programmer.destroy
67
+ Employee.find_by_id(@programmer_id).should be_nil
68
+ end
69
+
70
+ it 'should delete the child record' do
71
+ @programmer.destroy.should be_true
72
+ Programmer.find_by_id(@programmer_id).should be_nil
73
+ end
18
74
  end
19
75
 
20
76
  context 'when instructed to inherit parent methods' do
@@ -23,29 +79,47 @@ describe MultipleTableInheritance::ActiveRecord::Child do
23
79
  end
24
80
  end
25
81
 
26
- context 'searching by id' do
27
- context 'at the class level' do
82
+ context 'methods' do
83
+ before do
84
+ mock_everything!
85
+ end
86
+
87
+ context 'accessing parent records' do
28
88
  before do
29
- @employee_id = Employee.first.id
30
- @employee = Employee.find_by_id(@employee_id)
89
+ @programmer = Programmer.create!(:first_name => 'Bob', :last_name => 'Smith', :salary => 50000)
31
90
  end
32
91
 
33
- it 'should return a valid record' do
34
- @employee.should be_instance_of(@employee.subtype.constantize)
35
- @employee.id.should be(@employee_id)
92
+ it 'should allow access to parent records' do
93
+ employee = @programmer.employee
94
+ employee.should be_instance_of(Employee)
95
+ employee.id.should be(@programmer.id)
36
96
  end
37
97
  end
38
98
 
39
- context 'at the instance level' do
40
- before do
41
- team = Team.first
42
- @employee_id = team.employees.first.id
43
- @employee = team.employees.find_by_id(@employee_id)
99
+ context 'searching by id' do
100
+ context 'at the class level' do
101
+ before do
102
+ @employee_id = Employee.first.id
103
+ @employee = Employee.find_by_id(@employee_id)
104
+ end
105
+
106
+ it 'should return a valid record' do
107
+ @employee.should be_instance_of(@employee.subtype.constantize)
108
+ @employee.id.should be(@employee_id)
109
+ end
44
110
  end
45
111
 
46
- it 'should return a valid record' do
47
- @employee.should be_instance_of(@employee.subtype.constantize)
48
- @employee.id.should be(@employee_id)
112
+ context 'at the instance level' do
113
+ before do
114
+ team = Team.first
115
+ @employee_id = team.employees.first.id
116
+ @employee = team.employees.find_by_id(@employee_id)
117
+ end
118
+
119
+ it 'should return a valid record' do
120
+ @employee.should be_instance_of(@employee.subtype.constantize)
121
+ @employee.id.should be(@employee_id)
122
+ end
49
123
  end
50
124
  end
51
125
  end
@@ -1,28 +1,70 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MultipleTableInheritance::ActiveRecord::Parent do
3
+ describe MultipleTableInheritance::Parent do
4
+ describe 'methods' do
5
+ it 'should allow records to be found without inheritance' do
6
+ employees = Employee.as_supertype.all
7
+ employees.each do |employee|
8
+ employee.should be_instance_of(Employee)
9
+ end
10
+ end
11
+ end
12
+
4
13
  context 'retrieving records' do
5
- it 'should only fetch child records' do
6
- Employee.find_each do |employee|
7
- employee.should_not be_instance_of(Employee)
8
- ['Programmer', 'Manager'].should include(employee.class.to_s)
14
+ before do
15
+ mock_everything!
16
+ end
17
+
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)
9
22
  end
10
23
  end
11
24
 
12
- it 'should fetch all child records' do
13
- pending "find_without_inheritance is not working as intended"
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
29
+
30
+ it 'should include all records' do
14
31
  modified_results = Employee.all
15
- original_results = Employee.find_without_inheritance { Employee.all }
32
+ original_results = Employee.as_supertype.all
16
33
  modified_results.size.should == original_results.size
17
34
  end
18
35
 
19
36
  it 'should maintain result order' do
20
- pending "find_without_inheritance is not working as intended"
21
37
  modified_results = Employee.order("id desc").all
22
- original_results = Employee.find_without_inheritance { Employee.order("id desc").all }
38
+ original_results = Employee.as_supertype.order("id desc").all
23
39
  modified_results.collect(&:id).should == original_results.collect(&:id)
24
40
  end
25
41
 
42
+ context 'associations preloading' do
43
+ context 'is enabled' do
44
+ before do
45
+ @programmer_or_manager = Employee.includes(:team).first
46
+ end
47
+
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
52
+ end
53
+ end
54
+
55
+ context 'is disabled' do
56
+ before do
57
+ @programmer_or_manager = Employee.first
58
+ end
59
+
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
64
+ end
65
+ end
66
+ end
67
+
26
68
  context 'an invalid subtype exists' do
27
69
  before do
28
70
  @employee = Employee.create!(:first_name => 'Sub', :last_name => 'Type', :salary => 50000, :team => @team) do |employee|
@@ -39,11 +81,11 @@ describe MultipleTableInheritance::ActiveRecord::Parent do
39
81
  end
40
82
  end
41
83
 
42
- context 'default subtype used' do
84
+ context 'default subtype is used' do
43
85
  pending "test_everything"
44
86
  end
45
87
 
46
- context 'custom subtype used' do
88
+ context 'custom subtype is used' do
47
89
  pending "test_everything"
48
90
  end
49
91
 
@@ -65,7 +107,7 @@ describe MultipleTableInheritance::ActiveRecord::Parent do
65
107
  end
66
108
 
67
109
  it 'should delete the child record' do
68
- @employee.destroy.should be_true
110
+ @employee.destroy
69
111
  Programmer.find_by_id(@employee_id).should be_nil
70
112
  end
71
113
  end
data/spec/spec_helper.rb CHANGED
@@ -2,38 +2,33 @@ require 'active_record'
2
2
  require 'database_cleaner'
3
3
  require 'multiple_table_inheritance'
4
4
 
5
- MultipleTableInheritance::ActiveRecord::Parent::ClassMethods.module_eval do
6
- def find_without_inheritance(&block)
7
- # remove_method :find_by_sql
8
- extend ActiveRecord::Querying
9
- result = yield
10
- extend MultipleTableInheritance::ActiveRecord::Parent::FinderMethods
11
- result
12
- end
13
- end
14
-
15
5
  MultipleTableInheritance::Railtie.insert
16
6
 
17
7
  require 'support/tables'
18
8
  require 'support/models'
19
9
 
20
10
  module MultipleTableInheritanceSpecHelper
21
- def mock_everything
22
- team = Team.create!(:name => 'Website')
23
- java = Language.create!(:name => 'Java')
24
- cpp = Language.create!(:name => 'C++')
25
- Programmer.create!(
26
- :first_name => 'Mario',
27
- :last_name => 'Mario',
28
- :salary => 65000,
29
- :team => team,
30
- :languages => [java, cpp]) # programmer-specific relationship
31
- Manager.create!(
32
- :first_name => 'King',
33
- :last_name => 'Koopa',
34
- :salary => 70000,
35
- :team => team,
36
- :bonus => 5000) # manager-specific field
11
+ def mock_everything!
12
+ 3.times do |i|
13
+ team = Team.create!(:name => "Team#{i}")
14
+ language = Language.create!(:name => "Java 1.#{i + 4}")
15
+ end
16
+
17
+ 3.times do |i|
18
+ Programmer.create!(
19
+ :first_name => "Joe",
20
+ :last_name => "Schmoe #{i}",
21
+ :salary => 60000 + (i * 5000),
22
+ :team => Team.first,
23
+ :languages => Language.limit(2)) # programmer-specific relationship
24
+
25
+ Manager.create!(
26
+ :first_name => "Bob",
27
+ :last_name => "Smith #{i}",
28
+ :salary => 70000 + (i * 2500),
29
+ :team => Team.first,
30
+ :bonus => i * 2500) # manager-specific field
31
+ end
37
32
  end
38
33
  end
39
34
 
@@ -52,7 +47,6 @@ RSpec.configure do |config|
52
47
 
53
48
  config.before(:each) do
54
49
  DatabaseCleaner.start
55
- mock_everything
56
50
  end
57
51
 
58
52
  config.after(:each) do
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.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-08 00:00:00.000000000Z
12
+ date: 2012-03-11 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &2155943940 !ruby/object:Gem::Requirement
16
+ requirement: &2156557900 !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: *2155943940
24
+ version_requirements: *2156557900
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &2155943220 !ruby/object:Gem::Requirement
27
+ requirement: &2156557340 !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: *2155943220
35
+ version_requirements: *2156557340
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &2155942320 !ruby/object:Gem::Requirement
38
+ requirement: &2156556760 !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: *2155942320
46
+ version_requirements: *2156556760
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec_tag_matchers
49
- requirement: &2155941500 !ruby/object:Gem::Requirement
49
+ requirement: &2156556240 !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: *2155941500
57
+ version_requirements: *2156556240
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: sqlite3-ruby
60
- requirement: &2155940740 !ruby/object:Gem::Requirement
60
+ requirement: &2156555400 !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: *2155940740
68
+ version_requirements: *2156555400
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: database_cleaner
71
- requirement: &2155939560 !ruby/object:Gem::Requirement
71
+ requirement: &2156554360 !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: *2155939560
79
+ version_requirements: *2156554360
80
80
  description: ActiveRecord plugin designed to allow simple multiple table inheritance.
81
81
  email:
82
82
  - matt@matthuggins.com
@@ -92,15 +92,16 @@ files:
92
92
  - README.md
93
93
  - Rakefile
94
94
  - lib/multiple_table_inheritance.rb
95
- - lib/multiple_table_inheritance/active_record.rb
96
- - lib/multiple_table_inheritance/active_record/child.rb
97
- - lib/multiple_table_inheritance/active_record/migration.rb
98
- - lib/multiple_table_inheritance/active_record/parent.rb
95
+ - lib/multiple_table_inheritance/child.rb
96
+ - lib/multiple_table_inheritance/child/base.rb
97
+ - lib/multiple_table_inheritance/migration.rb
98
+ - lib/multiple_table_inheritance/parent.rb
99
+ - lib/multiple_table_inheritance/parent/base.rb
100
+ - lib/multiple_table_inheritance/parent/relation.rb
99
101
  - lib/multiple_table_inheritance/railtie.rb
100
102
  - lib/multiple_table_inheritance/version.rb
101
103
  - multiiple_table_inheritance.gemspec
102
104
  - spec/active_record/child_spec.rb
103
- - spec/active_record/helper_spec.rb
104
105
  - spec/active_record/parent_spec.rb
105
106
  - spec/spec_helper.rb
106
107
  - spec/support/models.rb
@@ -132,7 +133,6 @@ specification_version: 3
132
133
  summary: ActiveRecord plugin designed to allow simple multiple table inheritance.
133
134
  test_files:
134
135
  - spec/active_record/child_spec.rb
135
- - spec/active_record/helper_spec.rb
136
136
  - spec/active_record/parent_spec.rb
137
137
  - spec/spec_helper.rb
138
138
  - spec/support/models.rb
@@ -1,9 +0,0 @@
1
- module MultipleTableInheritance
2
- module ActiveRecord
3
- extend ActiveSupport::Autoload
4
-
5
- autoload :Child
6
- autoload :Parent
7
- autoload :Migration
8
- end
9
- end
@@ -1,16 +0,0 @@
1
- module MultipleTableInheritance
2
- module ActiveRecord
3
- module Migration
4
- def self.included(base)
5
- base.extend ClassMethods
6
- end
7
-
8
- module ClassMethods
9
- def create_table(table_name, options = {}, &block)
10
- options[:primary_key] = "#{options[:inherits]}_id" if options[:inherits]
11
- super(table_name, options, &block)
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,85 +0,0 @@
1
- module MultipleTableInheritance
2
- module ActiveRecord
3
- module Parent
4
- def self.default_options
5
- { :subtype => 'subtype' }
6
- end
7
-
8
- def self.included(base)
9
- base.extend ClassMethods
10
- base.class_attribute :subtype_column
11
- end
12
-
13
- module ClassMethods
14
- def acts_as_superclass(options={})
15
- options = Parent::default_options.merge(options.to_options.reject { |k,v| v.nil? })
16
- self.subtype_column = options[:subtype]
17
-
18
- if column_names.include?(subtype_column.to_s)
19
- extend FinderMethods
20
- include InstanceMethods
21
- before_destroy :destroy_child_association
22
- end
23
- end
24
- end
25
-
26
- module FinderMethods
27
- def find_by_sql(*args)
28
- parent_records = super(*args)
29
- child_records = []
30
-
31
- # Find all child records.
32
- ids_by_type(parent_records).each do |type, ids|
33
- begin
34
- klass = type.constantize
35
- child_records += klass.find(ids)
36
- rescue NameError => e
37
- # TODO log error
38
- end
39
- end
40
-
41
- # Associate the parent records with the child records to reduce SQL calls and prevent recursion.
42
- child_records.each do |child|
43
- association_name = to_s.demodulize.underscore
44
- parent = parent_records.find { |parent| child.id == parent.id }
45
- child.send("#{association_name}=", parent)
46
- end
47
-
48
- # Order the child_records array to match the order of the parent_records array.
49
- child_records.sort! do |a, b|
50
- a_index = parent_records.index { |parent| parent.id == a.id }
51
- b_index = parent_records.index { |parent| parent.id == b.id }
52
- a_index <=> b_index
53
- end
54
- end
55
-
56
- private
57
-
58
- def ids_by_type(records)
59
- subtypes = records.collect(&subtype_column.to_sym).uniq
60
- subtypes = subtypes.collect do |subtype|
61
- subtype_records = records.select { |record| record[subtype_column.to_sym] == subtype}
62
- subtype_ids = subtype_records.collect { |record| record.id }
63
- [subtype, subtype_ids]
64
- end
65
- Hash[subtypes]
66
- end
67
- end
68
-
69
- module InstanceMethods
70
- def destroy_child_association
71
- child_class = send(subtype_column.to_sym).constantize
72
- if child = child_class.find_by_id(id)
73
- child.delete
74
- end
75
- rescue NameError => e
76
- # TODO log error
77
- end
78
-
79
- def find_by_subtype(*args)
80
- super || send("find_by_#{subtype_column}", *args)
81
- end
82
- end
83
- end
84
- end
85
- end
@@ -1,11 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe MultipleTableInheritanceSpecHelper do
4
- it 'should allow records to be found without inheritance' do
5
- pending "find_without_inheritance is not working as intended"
6
- employees = Employee.find_without_inheritance { Employee.all }
7
- employees.each do |employee|
8
- employee.should be_instance_of(Employee)
9
- end
10
- end
11
- end