multiple_table_inheritance 0.1.0 → 0.1.1
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/.gitignore +4 -0
- data/CHANGELOG.md +10 -0
- data/README.md +10 -3
- data/Rakefile +0 -41
- data/lib/multiple_table_inheritance/active_record/child.rb +8 -0
- data/lib/multiple_table_inheritance/active_record/migration.rb +6 -10
- data/lib/multiple_table_inheritance/active_record/parent.rb +36 -16
- data/lib/multiple_table_inheritance/version.rb +1 -1
- data/multiiple_table_inheritance.gemspec +1 -1
- data/spec/active_record/child_spec.rb +34 -40
- data/spec/active_record/helper_spec.rb +11 -0
- data/spec/active_record/parent_spec.rb +67 -2
- data/spec/spec_helper.rb +23 -10
- data/spec/support/{classes.rb → models.rb} +8 -7
- data/spec/support/tables.rb +0 -4
- metadata +20 -17
- data/lib/multiple_table_inheritance/active_record/child.rb.bak +0 -132
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -65,6 +65,13 @@ extended.
|
|
65
65
|
acts_as_superclass
|
66
66
|
end
|
67
67
|
|
68
|
+
As mentioned in the migration section, the name of the subtype column can be
|
69
|
+
defined here if it's something other than the default of "subtype".
|
70
|
+
|
71
|
+
class Employee < ActiveRecord::Base
|
72
|
+
acts_as_superclass, :subtype => 'employee_type'
|
73
|
+
end
|
74
|
+
|
68
75
|
Conversely, the `inherits_from` method is used to represent that a given model
|
69
76
|
extends another model. It takes one optional parameter, which is the symbol
|
70
77
|
desired for referencing the relationship.
|
@@ -193,19 +200,19 @@ Associations will also work in the same way as other attributes.
|
|
193
200
|
belongs_to :team
|
194
201
|
end
|
195
202
|
|
196
|
-
class
|
203
|
+
class Programmer < ActiveRecord::Base
|
197
204
|
inherits_from :employee
|
198
205
|
has_many :known_languages
|
199
206
|
has_many :languages, :through => :known_languages
|
200
207
|
end
|
201
208
|
|
202
|
-
class
|
209
|
+
class Language < ActiveRecord::Base
|
203
210
|
attr_accessible :name
|
204
211
|
has_many :known_languages
|
205
212
|
has_many :programmers, :through => :known_languages
|
206
213
|
end
|
207
214
|
|
208
|
-
class
|
215
|
+
class KnownLanguage < ActiveRecord::Base
|
209
216
|
belongs_to :programmer
|
210
217
|
belongs_to :language
|
211
218
|
end
|
data/Rakefile
CHANGED
@@ -4,44 +4,3 @@ RSpec::Core::RakeTask.new('spec')
|
|
4
4
|
|
5
5
|
desc 'Default: run unit specs.'
|
6
6
|
task :default => :spec
|
7
|
-
|
8
|
-
# require 'rake'
|
9
|
-
# require 'rake/testtask'
|
10
|
-
# require 'rake/rdoctask'
|
11
|
-
#
|
12
|
-
# desc 'Default: run unit tests.'
|
13
|
-
# task :default => :test
|
14
|
-
#
|
15
|
-
# desc 'Test the multiple_table_inheritance plugin.'
|
16
|
-
# Rake::TestTask.new(:test) do |t|
|
17
|
-
# t.libs << 'lib'
|
18
|
-
# t.libs << 'test'
|
19
|
-
# t.pattern = 'test/**/*_test.rb'
|
20
|
-
# t.verbose = true
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# desc 'Generate documentation for the multiple_table_inheritance plugin.'
|
24
|
-
# Rake::RDocTask.new(:rdoc) do |rdoc|
|
25
|
-
# rdoc.rdoc_dir = 'rdoc'
|
26
|
-
# rdoc.title = 'MultipleTableInheritance'
|
27
|
-
# rdoc.options << '--line-numbers' << '--inline-source'
|
28
|
-
# rdoc.rdoc_files.include('README')
|
29
|
-
# rdoc.rdoc_files.include('lib/**/*.rb')
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# begin
|
33
|
-
# require 'jeweler'
|
34
|
-
# Jeweler::Tasks.new do |gem|
|
35
|
-
# gem.name = "multiple_table_inheritance"
|
36
|
-
# gem.summary = "ActiveRecord plugin designed to allow simple multiple table inheritance."
|
37
|
-
# gem.description = "ActiveRecord plugin designed to allow simple multiple table inheritance."
|
38
|
-
# gem.email = "tvdeyen@gmail.com"
|
39
|
-
# gem.homepage = "http://github.com/tvdeyen/multiple_table_inheritance"
|
40
|
-
# gem.authors = `git log --pretty=format:"%an"`.split("\n").uniq.sort
|
41
|
-
# gem.add_dependency "activerecord", ">=3.0.0"
|
42
|
-
# end
|
43
|
-
# Jeweler::GemcutterTasks.new
|
44
|
-
# rescue LoadError
|
45
|
-
# puts "Jeweler (or a dependency) not available."
|
46
|
-
# puts "Install it with: gem install jeweler"
|
47
|
-
# end
|
@@ -16,6 +16,8 @@ module MultipleTableInheritance
|
|
16
16
|
options = Child::default_options.merge(options.to_options.reject { |k,v| v.nil? })
|
17
17
|
inherit_methods = options.delete(:inherit_methods)
|
18
18
|
|
19
|
+
extend SharedMethods
|
20
|
+
include SharedMethods
|
19
21
|
include InstanceMethods
|
20
22
|
include DelegateMethods if inherit_methods
|
21
23
|
|
@@ -72,6 +74,12 @@ module MultipleTableInheritance
|
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
77
|
+
module SharedMethods
|
78
|
+
def find_by_id(*args)
|
79
|
+
send("find_by_#{parent_association_name}_id", *args)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
75
83
|
module InstanceMethods
|
76
84
|
private
|
77
85
|
|
@@ -2,18 +2,14 @@ module MultipleTableInheritance
|
|
2
2
|
module ActiveRecord
|
3
3
|
module Migration
|
4
4
|
def self.included(base)
|
5
|
-
base.
|
6
|
-
alias_method_chain :create_table, :inherits
|
7
|
-
end
|
5
|
+
base.extend ClassMethods
|
8
6
|
end
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
yield table_defintion
|
16
|
-
end
|
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
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
@@ -6,7 +6,7 @@ module MultipleTableInheritance
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.included(base)
|
9
|
-
base.extend
|
9
|
+
base.extend ClassMethods
|
10
10
|
base.class_attribute :subtype_column
|
11
11
|
end
|
12
12
|
|
@@ -15,20 +15,20 @@ module MultipleTableInheritance
|
|
15
15
|
options = Parent::default_options.merge(options.to_options.reject { |k,v| v.nil? })
|
16
16
|
self.subtype_column = options[:subtype]
|
17
17
|
|
18
|
-
if
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
if column_names.include?(subtype_column.to_s)
|
19
|
+
extend FinderMethods
|
20
|
+
include InstanceMethods
|
21
|
+
before_destroy :destroy_child_association
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def
|
28
|
-
parent_records =
|
24
|
+
end
|
25
|
+
|
26
|
+
module FinderMethods
|
27
|
+
def find_by_sql(*args)
|
28
|
+
parent_records = super(*args)
|
29
29
|
child_records = []
|
30
30
|
|
31
|
-
#
|
31
|
+
# Find all child records.
|
32
32
|
ids_by_type(parent_records).each do |type, ids|
|
33
33
|
begin
|
34
34
|
klass = type.constantize
|
@@ -38,20 +38,25 @@ module MultipleTableInheritance
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
#
|
41
|
+
# Associate the parent records with the child records to reduce SQL calls and prevent recursion.
|
42
42
|
child_records.each do |child|
|
43
43
|
association_name = to_s.demodulize.underscore
|
44
44
|
parent = parent_records.find { |parent| child.id == parent.id }
|
45
45
|
child.send("#{association_name}=", parent)
|
46
46
|
end
|
47
47
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
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
|
51
54
|
end
|
52
55
|
|
56
|
+
private
|
57
|
+
|
53
58
|
def ids_by_type(records)
|
54
|
-
subtypes = records.collect(&
|
59
|
+
subtypes = records.collect(&subtype_column.to_sym).uniq
|
55
60
|
subtypes = subtypes.collect do |subtype|
|
56
61
|
subtype_records = records.select { |record| record[subtype_column.to_sym] == subtype}
|
57
62
|
subtype_ids = subtype_records.collect { |record| record.id }
|
@@ -60,6 +65,21 @@ module MultipleTableInheritance
|
|
60
65
|
Hash[subtypes]
|
61
66
|
end
|
62
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
|
63
83
|
end
|
64
84
|
end
|
65
85
|
end
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.extra_rdoc_files = ["README.md"]
|
22
22
|
|
23
23
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
24
|
-
s.rubygems_version = %q{
|
24
|
+
s.rubygems_version = %q{1.3.6}
|
25
25
|
|
26
26
|
s.add_dependency('activerecord', '>= 3.0.0')
|
27
27
|
s.add_dependency('activesupport', '>= 3.0.0')
|
@@ -2,46 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe MultipleTableInheritance::ActiveRecord::Child do
|
4
4
|
context 'retrieving records' do
|
5
|
-
|
6
|
-
subtypes = Employee.all.collect(&:subtype).uniq
|
7
|
-
subtypes.each do |subtype|
|
8
|
-
['Programmer', 'Manager'].should include(subtype)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'should fetch all child records' do
|
13
|
-
pending "find_by_sql_without_inherit.size.should == find_by_sql_with_inherit.size"
|
14
|
-
end
|
15
|
-
|
16
|
-
context 'an invalid subtype is included' do
|
17
|
-
it 'should return successfully' do
|
18
|
-
pending "error should not be thrown"
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should generate log entry' do
|
22
|
-
pending "logger.error should have been called"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should maintain result order' do
|
27
|
-
pending "find_by_sql_without_inherit.collect(&:id).should == find_by_sql_with_inherit.collect(&:id)"
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'default subtype used' do
|
31
|
-
pending "test_everything"
|
32
|
-
end
|
33
|
-
|
34
|
-
context 'custom subtype used' do
|
35
|
-
pending "test_everything"
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should work with namespaced classes' do
|
39
|
-
pending "test_everything"
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'should inherit parent methods' do
|
43
|
-
pending 'todo'
|
44
|
-
end
|
5
|
+
pending "todo"
|
45
6
|
end
|
46
7
|
|
47
8
|
context 'creating records' do
|
@@ -55,4 +16,37 @@ describe MultipleTableInheritance::ActiveRecord::Child do
|
|
55
16
|
context 'deleting records' do
|
56
17
|
pending "todo"
|
57
18
|
end
|
19
|
+
|
20
|
+
context 'when instructed to inherit parent methods' do
|
21
|
+
it 'should inherit parent methods' do
|
22
|
+
pending "todo"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'searching by id' do
|
27
|
+
context 'at the class level' do
|
28
|
+
before do
|
29
|
+
@employee_id = Employee.first.id
|
30
|
+
@employee = Employee.find_by_id(@employee_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return a valid record' do
|
34
|
+
@employee.should be_instance_of(@employee.subtype.constantize)
|
35
|
+
@employee.id.should be(@employee_id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
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)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return a valid record' do
|
47
|
+
@employee.should be_instance_of(@employee.subtype.constantize)
|
48
|
+
@employee.id.should be(@employee_id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
58
52
|
end
|
@@ -0,0 +1,11 @@
|
|
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
|
@@ -1,7 +1,72 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe MultipleTableInheritance::ActiveRecord::Parent do
|
4
|
-
|
5
|
-
|
4
|
+
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)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should fetch all child records' do
|
13
|
+
pending "find_without_inheritance is not working as intended"
|
14
|
+
modified_results = Employee.all
|
15
|
+
original_results = Employee.find_without_inheritance { Employee.all }
|
16
|
+
modified_results.size.should == original_results.size
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should maintain result order' do
|
20
|
+
pending "find_without_inheritance is not working as intended"
|
21
|
+
modified_results = Employee.order("id desc").all
|
22
|
+
original_results = Employee.find_without_inheritance { Employee.order("id desc").all }
|
23
|
+
modified_results.collect(&:id).should == original_results.collect(&:id)
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'an invalid subtype exists' do
|
27
|
+
before do
|
28
|
+
@employee = Employee.create!(:first_name => 'Sub', :last_name => 'Type', :salary => 50000, :team => @team) do |employee|
|
29
|
+
employee.subtype = 'DoesNotExist'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return successfully' do
|
34
|
+
@employee.should be_instance_of(Employee)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should log an error' do
|
38
|
+
pending "logger.error should have been called"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'default subtype used' do
|
43
|
+
pending "test_everything"
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'custom subtype used' do
|
47
|
+
pending "test_everything"
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'namespaced classes are being used' do
|
51
|
+
pending "test_everything"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'deleting records' do
|
56
|
+
before do
|
57
|
+
programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000, :team => @team)
|
58
|
+
@employee = programmer.employee
|
59
|
+
@employee_id = programmer.id
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should delete the parent record' do
|
63
|
+
@employee.destroy.should be_true
|
64
|
+
Employee.find_by_id(@employee_id).should be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should delete the child record' do
|
68
|
+
@employee.destroy.should be_true
|
69
|
+
Programmer.find_by_id(@employee_id).should be_nil
|
70
|
+
end
|
6
71
|
end
|
7
72
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,25 +1,38 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'database_cleaner'
|
3
|
-
require
|
3
|
+
require 'multiple_table_inheritance'
|
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
|
+
MultipleTableInheritance::Railtie.insert
|
16
|
+
|
4
17
|
require 'support/tables'
|
5
|
-
require 'support/
|
18
|
+
require 'support/models'
|
6
19
|
|
7
20
|
module MultipleTableInheritanceSpecHelper
|
8
21
|
def mock_everything
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
22
|
+
team = Team.create!(:name => 'Website')
|
23
|
+
java = Language.create!(:name => 'Java')
|
24
|
+
cpp = Language.create!(:name => 'C++')
|
25
|
+
Programmer.create!(
|
13
26
|
:first_name => 'Mario',
|
14
27
|
:last_name => 'Mario',
|
15
28
|
:salary => 65000,
|
16
|
-
:team =>
|
17
|
-
:languages => [
|
18
|
-
|
29
|
+
:team => team,
|
30
|
+
:languages => [java, cpp]) # programmer-specific relationship
|
31
|
+
Manager.create!(
|
19
32
|
:first_name => 'King',
|
20
33
|
:last_name => 'Koopa',
|
21
34
|
:salary => 70000,
|
22
|
-
:team =>
|
35
|
+
:team => team,
|
23
36
|
:bonus => 5000) # manager-specific field
|
24
37
|
end
|
25
38
|
end
|
@@ -1,38 +1,39 @@
|
|
1
|
-
class
|
1
|
+
class Employee < ActiveRecord::Base
|
2
2
|
acts_as_superclass
|
3
|
-
attr_accessible :first_name, :last_name, :salary
|
3
|
+
attr_accessible :first_name, :last_name, :salary, :team, :team_id
|
4
4
|
belongs_to :team
|
5
5
|
validates :first_name, :presence => true
|
6
6
|
validates :last_name, :presence => true
|
7
7
|
validates :salary, :presence => true, :numericality => { :min => 0 }
|
8
8
|
end
|
9
9
|
|
10
|
-
class
|
10
|
+
class Programmer < ActiveRecord::Base
|
11
11
|
inherits_from :employee
|
12
|
+
attr_accessible :languages, :language_ids
|
12
13
|
has_many :known_languages
|
13
14
|
has_many :languages, :through => :known_languages
|
14
15
|
end
|
15
16
|
|
16
|
-
class
|
17
|
+
class Manager < ActiveRecord::Base
|
17
18
|
inherits_from :employee
|
18
19
|
attr_accessible :bonus
|
19
20
|
validates :bonus, :numericality => true
|
20
21
|
end
|
21
22
|
|
22
|
-
class
|
23
|
+
class Team < ActiveRecord::Base
|
23
24
|
attr_accessible :name, :description
|
24
25
|
has_many :employees
|
25
26
|
validates :name, :presence => true, :uniqueness => true
|
26
27
|
end
|
27
28
|
|
28
|
-
class
|
29
|
+
class Language < ActiveRecord::Base
|
29
30
|
attr_accessible :name
|
30
31
|
has_many :known_languages
|
31
32
|
has_many :programmers, :through => :known_languages
|
32
33
|
validates :name, :presence => true, :uniqueness => true
|
33
34
|
end
|
34
35
|
|
35
|
-
class
|
36
|
+
class KnownLanguage < ActiveRecord::Base
|
36
37
|
belongs_to :programmer
|
37
38
|
belongs_to :language
|
38
39
|
validates :programmer_id, :presence => true
|
data/spec/support/tables.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'multiple_table_inheritance/railtie'
|
2
|
-
|
3
|
-
MultipleTableInheritance::Railtie.insert
|
4
|
-
|
5
1
|
ActiveRecord::Base.establish_connection(
|
6
2
|
:adapter => 'sqlite3',
|
7
3
|
:database => File.expand_path(File.join(File.dirname(__FILE__), '../../db/multiple_table_inheritance.db'))
|
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.1
|
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-08 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &2155943940 !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: *2155943940
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &2155943220 !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: *2155943220
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec-rails
|
38
|
-
requirement: &
|
38
|
+
requirement: &2155942320 !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: *2155942320
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec_tag_matchers
|
49
|
-
requirement: &
|
49
|
+
requirement: &2155941500 !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: *2155941500
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: sqlite3-ruby
|
60
|
-
requirement: &
|
60
|
+
requirement: &2155940740 !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: *2155940740
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: database_cleaner
|
71
|
-
requirement: &
|
71
|
+
requirement: &2155939560 !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: *2155939560
|
80
80
|
description: ActiveRecord plugin designed to allow simple multiple table inheritance.
|
81
81
|
email:
|
82
82
|
- matt@matthuggins.com
|
@@ -85,23 +85,25 @@ extensions: []
|
|
85
85
|
extra_rdoc_files:
|
86
86
|
- README.md
|
87
87
|
files:
|
88
|
+
- .gitignore
|
88
89
|
- .rspec
|
90
|
+
- CHANGELOG.md
|
89
91
|
- Gemfile
|
90
92
|
- README.md
|
91
93
|
- Rakefile
|
92
94
|
- lib/multiple_table_inheritance.rb
|
93
95
|
- lib/multiple_table_inheritance/active_record.rb
|
94
96
|
- lib/multiple_table_inheritance/active_record/child.rb
|
95
|
-
- lib/multiple_table_inheritance/active_record/child.rb.bak
|
96
97
|
- lib/multiple_table_inheritance/active_record/migration.rb
|
97
98
|
- lib/multiple_table_inheritance/active_record/parent.rb
|
98
99
|
- lib/multiple_table_inheritance/railtie.rb
|
99
100
|
- lib/multiple_table_inheritance/version.rb
|
100
101
|
- multiiple_table_inheritance.gemspec
|
101
102
|
- spec/active_record/child_spec.rb
|
103
|
+
- spec/active_record/helper_spec.rb
|
102
104
|
- spec/active_record/parent_spec.rb
|
103
105
|
- spec/spec_helper.rb
|
104
|
-
- spec/support/
|
106
|
+
- spec/support/models.rb
|
105
107
|
- spec/support/tables.rb
|
106
108
|
homepage: http://github.com/mhuggins/multiple_table_inheritance
|
107
109
|
licenses: []
|
@@ -130,7 +132,8 @@ specification_version: 3
|
|
130
132
|
summary: ActiveRecord plugin designed to allow simple multiple table inheritance.
|
131
133
|
test_files:
|
132
134
|
- spec/active_record/child_spec.rb
|
135
|
+
- spec/active_record/helper_spec.rb
|
133
136
|
- spec/active_record/parent_spec.rb
|
134
137
|
- spec/spec_helper.rb
|
135
|
-
- spec/support/
|
138
|
+
- spec/support/models.rb
|
136
139
|
- spec/support/tables.rb
|
@@ -1,132 +0,0 @@
|
|
1
|
-
module MultipleTableInheritance
|
2
|
-
module ActiveRecord
|
3
|
-
module Child
|
4
|
-
def self.default_options
|
5
|
-
{ :dependent => :destroy, :inherit_methods => false }
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.included(base)
|
9
|
-
base.extend(ClassMethods)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
def inherits_from(association_name, options={})
|
14
|
-
# Standardize parameters.
|
15
|
-
association_name = association_name.to_sym
|
16
|
-
options = Child::default_options.merge(options.to_options.reject { |k,v| v.nil? })
|
17
|
-
|
18
|
-
# Remove options that should not affect the belongs_to relationship.
|
19
|
-
inherit_methods = options.delete(:inherit_methods)
|
20
|
-
|
21
|
-
# Add an association, and set the foreign key.
|
22
|
-
belongs_to association_name, options
|
23
|
-
|
24
|
-
# Set the primary key since the inheriting table includes no `id` column.
|
25
|
-
self.primary_key = "#{association_name}_id"
|
26
|
-
|
27
|
-
# Always return an instance of the parent class, whether it be an existing or new instance.
|
28
|
-
always_return_association(association_name)
|
29
|
-
|
30
|
-
# Ensure the parent specifies the current class as the subtype.
|
31
|
-
associate_before_validation(association_name)
|
32
|
-
|
33
|
-
# Bind the validation of association.
|
34
|
-
validate_association(association_name)
|
35
|
-
|
36
|
-
# Ensure both the parent and the child records are saved.
|
37
|
-
save_association(association_name)
|
38
|
-
|
39
|
-
# Create proxy methods for instances of this class.
|
40
|
-
create_instance_methods(association_name)
|
41
|
-
|
42
|
-
# Delegate all missing method calls to the parent association if preferred.
|
43
|
-
delegate_parent_methods(association_name) if inherit_methods
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def always_return_association(association_name)
|
49
|
-
define_method("#{association_name}_with_autobuild") do
|
50
|
-
send("#{association_name}_without_autobuild") || send("build_#{association_name}")
|
51
|
-
end
|
52
|
-
|
53
|
-
alias_method_chain association_name, :autobuild
|
54
|
-
end
|
55
|
-
|
56
|
-
def associate_before_validation(association_name)
|
57
|
-
define_method(:set_subtype) do
|
58
|
-
association = send(association_name)
|
59
|
-
if association.attribute_names.include?(association.class.subtype_column)
|
60
|
-
association[association.class.subtype_column] = self.class.to_s
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
before_validation :set_subtype
|
65
|
-
end
|
66
|
-
|
67
|
-
def validate_association(association_name)
|
68
|
-
define_method(:parent_association_must_be_valid) do
|
69
|
-
association = send(association_name)
|
70
|
-
|
71
|
-
unless valid = association.valid?
|
72
|
-
association.errors.each do |attr, message|
|
73
|
-
errors.add(attr, message)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
valid
|
78
|
-
end
|
79
|
-
|
80
|
-
validate :parent_association_must_be_valid
|
81
|
-
end
|
82
|
-
|
83
|
-
def save_association(association_name)
|
84
|
-
define_method(:parent_association_must_be_saved) do
|
85
|
-
association = send(association_name)
|
86
|
-
association.save(:validate => false)
|
87
|
-
self.id = association.id
|
88
|
-
end
|
89
|
-
|
90
|
-
before_save :parent_association_must_be_saved
|
91
|
-
end
|
92
|
-
|
93
|
-
def create_instance_methods(association_name)
|
94
|
-
inherited_columns_and_methods(association_name).each do |name|
|
95
|
-
delegate name, "#{name}=", :to => association_name
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def inherited_columns_and_methods(association_name)
|
100
|
-
# Get the class of association by reflection
|
101
|
-
reflection = create_reflection(:belongs_to, association_name, {}, self)
|
102
|
-
association_class = reflection.class_name.constantize
|
103
|
-
inherited_columns = association_class.column_names
|
104
|
-
inherited_methods = association_class.reflections.map { |key, value| key.to_s }
|
105
|
-
|
106
|
-
# Filter out columns that the class already has
|
107
|
-
inherited_columns = inherited_columns.reject do |column|
|
108
|
-
(self.column_names.grep(column).length > 0) || (column == 'type') || (column == association_class.subtype_column)
|
109
|
-
end
|
110
|
-
|
111
|
-
# Filter out columns that the class already has
|
112
|
-
inherited_methods = inherited_methods.reject do |method|
|
113
|
-
self.reflections.map { |key, value| key.to_s }.include?(method)
|
114
|
-
end
|
115
|
-
|
116
|
-
inherited_columns + inherited_methods - ['id']
|
117
|
-
end
|
118
|
-
|
119
|
-
def delegate_parent_methods(association_name)
|
120
|
-
define_method("method_missing") do |name, *args|
|
121
|
-
association = self.public_send(association_name)
|
122
|
-
if association.present? && association.respond_to?(name)
|
123
|
-
association.send(name, *args)
|
124
|
-
else
|
125
|
-
super(name, *args)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|