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 +6 -0
- data/README.md +1 -1
- data/lib/multiple_table_inheritance.rb +3 -1
- data/lib/multiple_table_inheritance/child.rb +7 -0
- data/lib/multiple_table_inheritance/{active_record/child.rb → child/base.rb} +19 -6
- data/lib/multiple_table_inheritance/migration.rb +14 -0
- data/lib/multiple_table_inheritance/parent.rb +8 -0
- data/lib/multiple_table_inheritance/parent/base.rb +43 -0
- data/lib/multiple_table_inheritance/parent/relation.rb +60 -0
- data/lib/multiple_table_inheritance/railtie.rb +7 -3
- data/lib/multiple_table_inheritance/version.rb +1 -1
- data/spec/active_record/child_spec.rb +93 -19
- data/spec/active_record/parent_spec.rb +55 -13
- data/spec/spec_helper.rb +21 -27
- metadata +20 -20
- data/lib/multiple_table_inheritance/active_record.rb +0 -9
- data/lib/multiple_table_inheritance/active_record/migration.rb +0 -16
- data/lib/multiple_table_inheritance/active_record/parent.rb +0 -85
- data/spec/active_record/helper_spec.rb +0 -11
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module MultipleTableInheritance
|
2
|
-
module
|
3
|
-
module
|
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 =
|
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,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::
|
14
|
-
include MultipleTableInheritance::
|
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::
|
22
|
+
include MultipleTableInheritance::Migration
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
@@ -1,12 +1,54 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MultipleTableInheritance::
|
3
|
+
describe MultipleTableInheritance::Child do
|
4
4
|
context 'retrieving records' do
|
5
|
-
|
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
|
-
|
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
|
-
|
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 '
|
27
|
-
|
82
|
+
context 'methods' do
|
83
|
+
before do
|
84
|
+
mock_everything!
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'accessing parent records' do
|
28
88
|
before do
|
29
|
-
@
|
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
|
34
|
-
|
35
|
-
|
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 '
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
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::
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
13
|
-
|
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.
|
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.
|
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
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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.
|
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-
|
12
|
+
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: &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: *
|
24
|
+
version_requirements: *2156557900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
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: *
|
35
|
+
version_requirements: *2156557340
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec-rails
|
38
|
-
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: *
|
46
|
+
version_requirements: *2156556760
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec_tag_matchers
|
49
|
-
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: *
|
57
|
+
version_requirements: *2156556240
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: sqlite3-ruby
|
60
|
-
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: *
|
68
|
+
version_requirements: *2156555400
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: database_cleaner
|
71
|
-
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: *
|
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/
|
96
|
-
- lib/multiple_table_inheritance/
|
97
|
-
- lib/multiple_table_inheritance/
|
98
|
-
- lib/multiple_table_inheritance/
|
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,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
|