multiple_table_inheritance 0.1.1 → 0.1.2

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