class-table-inheritance 1.2.0 → 1.5.0
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +150 -0
- data/Rakefile +13 -0
- data/class-table-inheritance.gemspec +27 -0
- data/init.rb +1 -0
- data/lib/class-table-inheritance.rb +2 -140
- data/lib/class-table-inheritance/class-table-inheritance.rb +142 -0
- data/lib/class-table-inheritance/inherits-migration.rb +27 -0
- data/lib/class-table-inheritance/version.rb +3 -0
- data/test/class_table_inheritance_test.rb +67 -4
- data/test/database.yml +4 -0
- data/test/models/book.rb +3 -0
- data/test/models/manager.rb +3 -0
- data/test/models/mod.rb +5 -0
- data/test/models/mod/user.rb +2 -0
- data/test/models/mod/video.rb +3 -0
- data/test/models/product.rb +2 -0
- data/test/schema.rb +24 -0
- data/test/test_helper.rb +19 -0
- metadata +106 -51
- data/README +0 -83
- data/lib/inherits-migration.rb +0 -32
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2b5ca50887e18888dd90d93d729ea1bb10360998d9eb291bbdafa7971039e8c2
|
4
|
+
data.tar.gz: e1896a0829ed6c0362f2bd112e65bfcc305ad5f537a66f8ca5758f032e2f1702
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2adbe784037311bd3cb7f99029b338a5ecfee6acecc8605441a36797e1f9963f9adf38af8cc262ef59ccea6b6f132414982148954c65c8f5c1f873d6af821e7a
|
7
|
+
data.tar.gz: 35c4a207b2d9a7efe06eb9aab0bd98921e7b372b2d17d851545712a5146871dfcaa416178eeb172e9cfb39886f4aabcf3aa8acdd084da1e9f4f00e4cfc884655
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
[](https://travis-ci.org/brunofrank/class-table-inheritance)
|
2
|
+
|
3
|
+
Change log
|
4
|
+
----------
|
5
|
+
|
6
|
+
### 1.5.0
|
7
|
+
|
8
|
+
* Add ActiveRecord 6.0 support
|
9
|
+
* Require Ruby 2.5 or newer.
|
10
|
+
|
11
|
+
|
12
|
+
### 1.4.0
|
13
|
+
|
14
|
+
* Add ActiveRecord 5.2 support
|
15
|
+
* Require Ruby 2.2 or newer.
|
16
|
+
|
17
|
+
|
18
|
+
### 1.3.1
|
19
|
+
|
20
|
+
* Removed 'set_primary_key' deprecation warning
|
21
|
+
* Make the gem depencies explicit and require ActiveRecord 4.x or 5.0
|
22
|
+
|
23
|
+
|
24
|
+
### 1.3.0
|
25
|
+
* Now you can inherits from and to modules like inherits_from 'Module::Model', see the the name of
|
26
|
+
field must be module_model_id:integer thanks for Marc Remolt (https://github.com/mremolt).
|
27
|
+
* Unit test
|
28
|
+
|
29
|
+
|
30
|
+
Note about version
|
31
|
+
------------------
|
32
|
+
|
33
|
+
If you are using Rails 2.3.8 or other version < 3, you have to use the version 1.1.x of this gem.
|
34
|
+
For Rails 3 you need to use the version 1.2.x or master of this gem.
|
35
|
+
For Rails 4 and 5 you need to use the version 1.3.x to 1.4.x.
|
36
|
+
For Rails 6 you need to use version 1.5.x or master of this gem.
|
37
|
+
|
38
|
+
ClassTableInheritance
|
39
|
+
---------------------
|
40
|
+
|
41
|
+
This is an ActiveRecord plugin designed to allow
|
42
|
+
simple multiple table (class) inheritance.
|
43
|
+
|
44
|
+
This plugin was inspired by:
|
45
|
+
inherits_from plugin => http://github.com/rwl4/inherits_from and
|
46
|
+
Multiple Table Inheritance with ActiveRecord => http://mediumexposure.com/multiple-table-inheritance-active-record/
|
47
|
+
|
48
|
+
How to install
|
49
|
+
--------------
|
50
|
+
|
51
|
+
gem install class-table-inheritance
|
52
|
+
|
53
|
+
Example
|
54
|
+
-------
|
55
|
+
|
56
|
+
### Migrations
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
create_table :products do |t|
|
60
|
+
t.string :description, :null => false
|
61
|
+
t.string :subtype # Only if you need access of both side see example
|
62
|
+
t.decimal :price
|
63
|
+
t.timestamps
|
64
|
+
end
|
65
|
+
|
66
|
+
create_table :books, :inherits => :product do |t|
|
67
|
+
t.string :author, :null => false
|
68
|
+
end
|
69
|
+
|
70
|
+
create_table :videos, :inherits => :product do |t|
|
71
|
+
t.string :year, :null => false
|
72
|
+
t.string :genre, :null => false
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### Models
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class Product < ActiveRecord::Base
|
80
|
+
acts_as_superclass # only if you want top-down access.
|
81
|
+
end
|
82
|
+
|
83
|
+
class Book < ActiveRecord::Base
|
84
|
+
inherits_from :product
|
85
|
+
end
|
86
|
+
|
87
|
+
class Video < ActiveRecord::Base
|
88
|
+
inherits_from :product
|
89
|
+
end
|
90
|
+
|
91
|
+
book = Book.find(1)
|
92
|
+
book.name => "Agile Development with Rails"
|
93
|
+
book.author => "Dave Thomas"
|
94
|
+
book.price => 19.00
|
95
|
+
|
96
|
+
video = Video.find(2)
|
97
|
+
video.name => "Inseption"
|
98
|
+
video.year => "2010"
|
99
|
+
video.genre => "SCI-FI"
|
100
|
+
video.price => 22.00
|
101
|
+
|
102
|
+
book = Book.new
|
103
|
+
book.name = "Hamlet"
|
104
|
+
book.author = "Shakespeare, William"
|
105
|
+
book.price => 14.00
|
106
|
+
book.save
|
107
|
+
```
|
108
|
+
|
109
|
+
Module inheritance
|
110
|
+
------------------
|
111
|
+
|
112
|
+
### Migrations
|
113
|
+
```ruby
|
114
|
+
create_table :mod_users do |t|
|
115
|
+
t.string :name, :null => false
|
116
|
+
end
|
117
|
+
|
118
|
+
create_table :managers, :inherits => 'Mod::User' do |t|
|
119
|
+
t.string :salary, :null => false
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
### Models
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
class Mod::User < ActiveRecord::Base
|
127
|
+
end
|
128
|
+
|
129
|
+
class Manager < ActiveRecord::Base
|
130
|
+
inherits_from 'Mod::User'
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Top-down access (Polymorphic)
|
135
|
+
-----------------------------
|
136
|
+
|
137
|
+
if you want to access product and get field in the subclass do you need to create a field subtype:string in superclass and ad acts_as_superclass in superclass and now you can do like this.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
product = Product.find 1 # This is a Book instance.
|
141
|
+
product.author
|
142
|
+
|
143
|
+
product = Product.find 2 # This is a Video instance.
|
144
|
+
product.genre
|
145
|
+
```
|
146
|
+
|
147
|
+
if you need help contanct me: bfscordeiro (at) gmail.com .
|
148
|
+
|
149
|
+
|
150
|
+
Copyright (c) 2010 Bruno Cordeiro, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rake/testtask'
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
# just 'rake test'
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << 'lib'
|
8
|
+
t.libs << 'test'
|
9
|
+
t.pattern = 'test/**/*_test.rb'
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :test
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'class-table-inheritance/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "class-table-inheritance"
|
7
|
+
s.version = ClassTableInheritance::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bruno Frank"]
|
10
|
+
s.email = ["bfscordeiro@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/brunofrank/class-table-inheritance"
|
12
|
+
s.summary = %q{ActiveRecord plugin designed to allow simple multiple table (class) inheritance.}
|
13
|
+
s.description = %q{ActiveRecord plugin designed to allow simple multiple table (class) inheritance.}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.required_ruby_version = '>= 2.5', '< 4'
|
21
|
+
|
22
|
+
s.add_runtime_dependency 'activerecord', '~>6.0'
|
23
|
+
|
24
|
+
s.add_development_dependency 'minitest-reporters','~>1.1'
|
25
|
+
s.add_development_dependency 'rake', '>=11'
|
26
|
+
s.add_development_dependency 'sqlite3', '~>1.3'
|
27
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'class-table-inheritance'
|
@@ -1,141 +1,3 @@
|
|
1
1
|
require 'active_record'
|
2
|
-
require 'inherits-migration'
|
3
|
-
|
4
|
-
# ClassTableInheritance is an ActiveRecord plugin designed to allow
|
5
|
-
# simple multiple table (class) inheritance.
|
6
|
-
class ActiveRecord::Base
|
7
|
-
attr_reader :reflection
|
8
|
-
|
9
|
-
def self.acts_as_superclass
|
10
|
-
if self.column_names.include?("subtype")
|
11
|
-
def self.find(*args)
|
12
|
-
super_classes = super
|
13
|
-
begin
|
14
|
-
if super_classes.kind_of? Array
|
15
|
-
super_classes.map do |item|
|
16
|
-
if !item.subtype.nil? && !item.subtype.blank?
|
17
|
-
inherits_type = Object.const_get(item.subtype.to_s)
|
18
|
-
inherits_type.send(:find, item.id)
|
19
|
-
else
|
20
|
-
super_classes
|
21
|
-
end
|
22
|
-
end
|
23
|
-
else
|
24
|
-
if !super_classes.subtype.nil? && !super_classes.subtype.blank?
|
25
|
-
inherits_type = Object.const_get(super_classes.subtype.to_s)
|
26
|
-
inherits_type.send(:find, *args)
|
27
|
-
else
|
28
|
-
super_classes
|
29
|
-
end
|
30
|
-
end
|
31
|
-
rescue
|
32
|
-
super_classes
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.inherits_from(association_id)
|
39
|
-
|
40
|
-
# add an association, and set the foreign key.
|
41
|
-
has_one association_id, :foreign_key => :id
|
42
|
-
|
43
|
-
|
44
|
-
# set the primary key, it' need because the generalized table doesn't have
|
45
|
-
# a field ID.
|
46
|
-
set_primary_key "#{association_id}_id"
|
47
|
-
|
48
|
-
|
49
|
-
# Autobuild method to make a instance of association
|
50
|
-
define_method("#{association_id}_with_autobuild") do
|
51
|
-
send("#{association_id}_without_autobuild") || send("build_#{association_id}")
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
# Set a method chain whith autobuild.
|
56
|
-
alias_method_chain association_id, :autobuild
|
57
|
-
|
58
|
-
|
59
|
-
# bind the before save, this method call the save of association, and
|
60
|
-
# get our generated ID an set to association_id field.
|
61
|
-
before_save :save_inherit
|
62
|
-
|
63
|
-
|
64
|
-
# Bind the validation of association.
|
65
|
-
validate :inherit_association_must_be_valid
|
66
|
-
|
67
|
-
# Generate a method to validate the field of association.
|
68
|
-
define_method("inherit_association_must_be_valid") do
|
69
|
-
association = send(association_id)
|
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
|
-
|
81
|
-
|
82
|
-
# get the class of association by reflection, this is needed because
|
83
|
-
# i need to get the methods and attributes to make a proxy methods.
|
84
|
-
reflection = create_reflection(:has_one, association_id, {}, self)
|
85
|
-
association_class = Object.const_get(reflection.class_name)
|
86
|
-
# Get the colluns of association class.
|
87
|
-
inherited_columns = association_class.column_names
|
88
|
-
# Make a filter in association colluns to exclude the colluns that
|
89
|
-
# the generalized class already have.
|
90
|
-
inherited_columns = inherited_columns.reject { |c| self.column_names.grep(c).length > 0 || c == "type" || c == "subtype"}
|
91
|
-
# Get the methods of the association class and tun it to an Array of Strings.
|
92
|
-
inherited_methods = association_class.reflections.map { |key,value| key.to_s }
|
93
|
-
# Make a filter in association methods to exclude the methods that
|
94
|
-
# the generalizae class already have.
|
95
|
-
inherited_methods = inherited_methods.reject { |c| self.reflections.map {|key, value| key.to_s }.include?(c) }
|
96
|
-
|
97
|
-
|
98
|
-
# create the proxy methods to get and set the properties and methods
|
99
|
-
# in association class.
|
100
|
-
(inherited_columns + inherited_methods).each do |name|
|
101
|
-
define_method name do
|
102
|
-
# if the field is ID than i only bind that with the association field.
|
103
|
-
# this is needed to bypass the overflow problem when the ActiveRecord
|
104
|
-
# try to get the id to find the association.
|
105
|
-
if name == 'id'
|
106
|
-
self["#{association_id}_id"]
|
107
|
-
else
|
108
|
-
assoc = send(association_id)
|
109
|
-
assoc.send(name)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
define_method "#{name}=" do |new_value|
|
115
|
-
# if the field is ID than i only bind that with the association field.
|
116
|
-
# this is needed to bypass the overflow problem when the ActiveRecord
|
117
|
-
# try to get the id to find the association.
|
118
|
-
if name == 'id'
|
119
|
-
self["#{association_id}_id"] = new_value
|
120
|
-
else
|
121
|
-
assoc = send(association_id)
|
122
|
-
assoc.send("#{name}=", new_value)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
|
128
|
-
# Create a method do bind in before_save callback, this method
|
129
|
-
# only call the save of association class and set the id in the
|
130
|
-
# generalized class.
|
131
|
-
define_method("save_inherit") do |*args|
|
132
|
-
association = send(association_id)
|
133
|
-
if association.attribute_names.include?("subtype")
|
134
|
-
association.subtype = self.class.to_s
|
135
|
-
end
|
136
|
-
association.save
|
137
|
-
self["#{association_id}_id"] = association.id
|
138
|
-
true
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
2
|
+
require 'class-table-inheritance/inherits-migration'
|
3
|
+
require 'class-table-inheritance/class-table-inheritance'
|
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
# ClassTableInheritance is an ActiveRecord plugin designed to allow
|
3
|
+
# simple multiple table (class) inheritance.
|
4
|
+
class ActiveRecord::Base
|
5
|
+
attr_reader :reflection
|
6
|
+
|
7
|
+
def self.acts_as_superclass
|
8
|
+
if self.column_names.include?("subtype")
|
9
|
+
def self.find(*args)
|
10
|
+
super_classes = super
|
11
|
+
begin
|
12
|
+
if super_classes.kind_of? Array
|
13
|
+
super_classes.map do |item|
|
14
|
+
if !item.subtype.nil? && !item.subtype.blank?
|
15
|
+
inherits_type = super_classes.subtype.to_s.classify.constantize
|
16
|
+
inherits_type.send(:find, item.id)
|
17
|
+
else
|
18
|
+
super_classes
|
19
|
+
end
|
20
|
+
end
|
21
|
+
else
|
22
|
+
if !super_classes.subtype.nil? && !super_classes.subtype.blank?
|
23
|
+
inherits_type = super_classes.subtype.to_s.classify.constantize
|
24
|
+
inherits_type.send(:find, *args)
|
25
|
+
else
|
26
|
+
super_classes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rescue
|
30
|
+
super_classes
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.inherits_from(association_id)
|
37
|
+
# Subst the module simbol to dash and if this is string
|
38
|
+
if association_id.kind_of?(String)
|
39
|
+
class_name = association_id
|
40
|
+
association_id = association_id.to_s.gsub(/::/, '_').downcase.to_sym
|
41
|
+
else
|
42
|
+
class_name = association_id.to_s.classify
|
43
|
+
end
|
44
|
+
|
45
|
+
# add an association
|
46
|
+
belongs_to association_id, :class_name => class_name, :dependent => :destroy
|
47
|
+
|
48
|
+
|
49
|
+
# set the primary key, it' need because the generalized table doesn't have
|
50
|
+
# a field ID.
|
51
|
+
self.primary_key = "#{association_id}_id"
|
52
|
+
|
53
|
+
|
54
|
+
# Autobuild method to make an instance of association
|
55
|
+
m = const_set("#{association_id.to_s.camelize}Builder", Module.new)
|
56
|
+
m.send(:define_method, association_id) do
|
57
|
+
super() || send("build_#{association_id}")
|
58
|
+
end
|
59
|
+
prepend(m)
|
60
|
+
|
61
|
+
# bind the before save, this method call the save of association, and
|
62
|
+
# get our generated ID an set to association_id field.
|
63
|
+
before_save :save_inherit
|
64
|
+
|
65
|
+
|
66
|
+
# Bind the validation of association.
|
67
|
+
validate :inherit_association_must_be_valid
|
68
|
+
|
69
|
+
# Generate a method to validate the field of association.
|
70
|
+
define_method("inherit_association_must_be_valid") do
|
71
|
+
association = send(association_id)
|
72
|
+
|
73
|
+
unless valid = association.valid?
|
74
|
+
association.errors.each do |attr, message|
|
75
|
+
errors.add(attr, message)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
valid
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
# get the class of association by reflection, this is needed because
|
85
|
+
# i need to get the methods and attributes to make a proxy methods.
|
86
|
+
association_class = class_name.constantize
|
87
|
+
# Get the colluns of association class.
|
88
|
+
inherited_columns = association_class.column_names
|
89
|
+
# Make a filter in association colluns to exclude the colluns that
|
90
|
+
# the generalized class already have.
|
91
|
+
inherited_columns = inherited_columns.reject { |c| self.column_names.grep(c).length > 0 || c == "type" || c == "subtype"}
|
92
|
+
# Get the methods of the association class and tun it to an Array of Strings.
|
93
|
+
inherited_methods = association_class.reflections.map { |key,value| key.to_s }
|
94
|
+
# Make a filter in association methods to exclude the methods that
|
95
|
+
# the generalizae class already have.
|
96
|
+
inherited_methods = inherited_methods.reject { |c| self.reflections.map {|key, value| key.to_s }.include?(c) }
|
97
|
+
|
98
|
+
|
99
|
+
# create the proxy methods to get and set the properties and methods
|
100
|
+
# in association class.
|
101
|
+
(inherited_columns + inherited_methods).each do |name|
|
102
|
+
define_method name do
|
103
|
+
# if the field is ID than i only bind that with the association field.
|
104
|
+
# this is needed to bypass the overflow problem when the ActiveRecord
|
105
|
+
# try to get the id to find the association.
|
106
|
+
if name == 'id'
|
107
|
+
self["#{association_id}_id"]
|
108
|
+
else
|
109
|
+
assoc = send(association_id)
|
110
|
+
assoc.send(name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
define_method "#{name}=" do |new_value|
|
116
|
+
# if the field is ID than i only bind that with the association field.
|
117
|
+
# this is needed to bypass the overflow problem when the ActiveRecord
|
118
|
+
# try to get the id to find the association.
|
119
|
+
if name == 'id'
|
120
|
+
self["#{association_id}_id"] = new_value
|
121
|
+
else
|
122
|
+
assoc = send(association_id)
|
123
|
+
assoc.send("#{name}=", new_value)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# Create a method do bind in before_save callback, this method
|
130
|
+
# only call the save of association class and set the id in the
|
131
|
+
# generalized class.
|
132
|
+
define_method("save_inherit") do |*args|
|
133
|
+
association = send(association_id)
|
134
|
+
if association.attribute_names.include?("subtype")
|
135
|
+
association.subtype = self.class.to_s
|
136
|
+
end
|
137
|
+
association.save
|
138
|
+
self["#{association_id}_id"] = association.id
|
139
|
+
true
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module InheritsMigration
|
2
|
+
# Generate the association field.
|
3
|
+
def create_table(table_name, options = {}, &block)
|
4
|
+
options[:id] ||= false if options[:inherits]
|
5
|
+
|
6
|
+
super(table_name, options) do |table_defintion|
|
7
|
+
if options[:inherits]
|
8
|
+
if options[:inherits].kind_of?(String)
|
9
|
+
column_to_create = options[:inherits].gsub(/::/, '_').downcase
|
10
|
+
association_type = options[:inherits].constantize
|
11
|
+
else
|
12
|
+
column_to_create = options[:inherits]
|
13
|
+
association_type = options[:inherits].to_s.classify.constantize
|
14
|
+
end
|
15
|
+
association_inst = association_type.send(:new)
|
16
|
+
attr_column = association_inst.column_for_attribute(association_type.primary_key)
|
17
|
+
|
18
|
+
field_option = {:primary_key => true, :null => false}
|
19
|
+
field_option[:limit] = attr_column.limit if attr_column.limit
|
20
|
+
table_defintion.column "#{column_to_create}_id", attr_column.type, field_option
|
21
|
+
end
|
22
|
+
yield table_defintion
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveRecord::ConnectionAdapters::SchemaStatements.prepend(InheritsMigration)
|
@@ -1,8 +1,71 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class ClassTableInheritanceTest <
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class ClassTableInheritanceTest < Minitest::Test
|
4
|
+
|
5
|
+
def test_create
|
6
|
+
name = 'Bike'
|
7
|
+
|
8
|
+
product = Product.new
|
9
|
+
product.name = name
|
10
|
+
assert_equal true, product.save
|
11
|
+
|
12
|
+
product = Product.find product.id
|
13
|
+
assert_equal name, product.name
|
7
14
|
end
|
15
|
+
|
16
|
+
|
17
|
+
def test_inheritance_book
|
18
|
+
title = 'Atlas Shrugged'
|
19
|
+
isbn = '9780451191144'
|
20
|
+
|
21
|
+
book = Book.new
|
22
|
+
book.name = title
|
23
|
+
book.isbn = isbn
|
24
|
+
assert_equal true, book.save
|
25
|
+
|
26
|
+
book = Book.find book.id
|
27
|
+
assert_equal title, book.name
|
28
|
+
assert_equal isbn, book.isbn
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_inheritance_video
|
32
|
+
name = 'Amy Whinehouse - Rehab'
|
33
|
+
url = 'http://www.youtube.com/watch?v=3QI8RjKibyc'
|
34
|
+
|
35
|
+
video = Mod::Video.new
|
36
|
+
video.name = name
|
37
|
+
video.url = url
|
38
|
+
assert_equal true, video.save
|
39
|
+
|
40
|
+
video = Mod::Video.find video.id
|
41
|
+
assert_equal name, video.name
|
42
|
+
assert_equal url, video.url
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_inheritance_user_save
|
46
|
+
name = 'bfscordeiro'
|
47
|
+
|
48
|
+
user = Mod::User.new
|
49
|
+
user.name = name
|
50
|
+
assert_equal true, user.save
|
51
|
+
|
52
|
+
user = Mod::User.find user.id
|
53
|
+
assert_equal name, user.name
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_inheritance_manager_save
|
57
|
+
name = 'bfscordeiro'
|
58
|
+
salary = '6000'
|
59
|
+
|
60
|
+
manager = Manager.new
|
61
|
+
manager.name = name
|
62
|
+
manager.salary = salary
|
63
|
+
assert_equal true, manager.save
|
64
|
+
|
65
|
+
manager = Manager.find manager.id
|
66
|
+
assert_equal name, manager.name
|
67
|
+
assert_equal salary, manager.salary
|
68
|
+
end
|
69
|
+
|
70
|
+
|
8
71
|
end
|
data/test/database.yml
ADDED
data/test/models/book.rb
ADDED
data/test/models/mod.rb
ADDED
data/test/schema.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :products, :force => true do |t|
|
3
|
+
t.string :name
|
4
|
+
end
|
5
|
+
|
6
|
+
create_table :books, :force => true do |t|
|
7
|
+
t.string :isbn
|
8
|
+
t.integer :product_id
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :mod_videos, :force => true do |t|
|
12
|
+
t.integer :product_id
|
13
|
+
t.string :url
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :mod_users, :force => true do |t|
|
17
|
+
t.string :name
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :managers, :force => true do |t|
|
21
|
+
t.integer :mod_user_id
|
22
|
+
t.string :salary
|
23
|
+
end
|
24
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/reporters'
|
4
|
+
require 'active_record'
|
5
|
+
require 'class-table-inheritance'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
Minitest::Reporters.use!
|
9
|
+
|
10
|
+
database = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
11
|
+
ActiveRecord::Base.establish_connection(database['sqlite3'])
|
12
|
+
load(File.dirname(__FILE__) + "/schema.rb") if !File.exist?(database['sqlite3'][:database])
|
13
|
+
|
14
|
+
require 'models/product'
|
15
|
+
require 'models/book'
|
16
|
+
require 'models/mod'
|
17
|
+
require 'models/mod/video'
|
18
|
+
require 'models/mod/user'
|
19
|
+
require 'models/manager'
|
metadata
CHANGED
@@ -1,67 +1,122 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: class-table-inheritance
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
segments:
|
6
|
-
- 1
|
7
|
-
- 2
|
8
|
-
- 0
|
9
|
-
version: 1.2.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.5.0
|
10
5
|
platform: ruby
|
11
|
-
authors:
|
12
|
-
- Bruno Frank
|
13
|
-
autorequire:
|
6
|
+
authors:
|
7
|
+
- Bruno Frank
|
8
|
+
autorequire:
|
14
9
|
bindir: bin
|
15
10
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
date: 2021-03-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-reporters
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '11'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
description: ActiveRecord plugin designed to allow simple multiple table (class) inheritance.
|
70
|
+
email:
|
71
|
+
- bfscordeiro@gmail.com
|
23
72
|
executables: []
|
24
|
-
|
25
73
|
extensions: []
|
26
|
-
|
27
|
-
|
28
|
-
-
|
29
|
-
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
78
|
+
- Gemfile
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- class-table-inheritance.gemspec
|
82
|
+
- init.rb
|
30
83
|
- lib/class-table-inheritance.rb
|
31
|
-
- lib/
|
32
|
-
-
|
84
|
+
- lib/class-table-inheritance/class-table-inheritance.rb
|
85
|
+
- lib/class-table-inheritance/inherits-migration.rb
|
86
|
+
- lib/class-table-inheritance/version.rb
|
33
87
|
- test/class_table_inheritance_test.rb
|
34
|
-
|
35
|
-
|
88
|
+
- test/database.yml
|
89
|
+
- test/models/book.rb
|
90
|
+
- test/models/manager.rb
|
91
|
+
- test/models/mod.rb
|
92
|
+
- test/models/mod/user.rb
|
93
|
+
- test/models/mod/video.rb
|
94
|
+
- test/models/product.rb
|
95
|
+
- test/schema.rb
|
96
|
+
- test/test_helper.rb
|
97
|
+
homepage: https://github.com/brunofrank/class-table-inheritance
|
36
98
|
licenses: []
|
37
|
-
|
99
|
+
metadata: {}
|
38
100
|
post_install_message:
|
39
101
|
rdoc_options: []
|
40
|
-
|
41
|
-
require_paths:
|
102
|
+
require_paths:
|
42
103
|
- lib
|
43
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
-
|
45
|
-
requirements:
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
46
106
|
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
requirements:
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '2.5'
|
109
|
+
- - "<"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '4'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
54
114
|
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
|
57
|
-
- 0
|
58
|
-
version: "0"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
59
117
|
requirements: []
|
60
|
-
|
61
|
-
rubyforge_project:
|
62
|
-
rubygems_version: 1.3.7
|
118
|
+
rubygems_version: 3.0.9
|
63
119
|
signing_key:
|
64
|
-
specification_version:
|
65
|
-
summary: ActiveRecord plugin designed to allow
|
66
|
-
test_files:
|
67
|
-
- test/class_table_inheritance_test.rb
|
120
|
+
specification_version: 4
|
121
|
+
summary: ActiveRecord plugin designed to allow simple multiple table (class) inheritance.
|
122
|
+
test_files: []
|
data/README
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
ClassTableInheritance 1.1.0 BETA
|
2
|
-
================================
|
3
|
-
|
4
|
-
This is an ActiveRecord plugin designed to allow
|
5
|
-
simple multiple table (class) inheritance.
|
6
|
-
|
7
|
-
This plugin was inspired by:
|
8
|
-
inherits_from plugin => http://github.com/rwl4/inherits_from and
|
9
|
-
Multiple Table Inheritance with ActiveRecord => http://mediumexposure.com/multiple-table-inheritance-active-record/
|
10
|
-
|
11
|
-
How to install
|
12
|
-
=============
|
13
|
-
|
14
|
-
gem install class-table-inheritance
|
15
|
-
|
16
|
-
Example
|
17
|
-
=======
|
18
|
-
|
19
|
-
# Migrations
|
20
|
-
|
21
|
-
create_table :product do |t|
|
22
|
-
t.string :description, :null => false
|
23
|
-
t.string :subtype # Only if you need access of both side see example
|
24
|
-
t.decimal :price
|
25
|
-
t.timestamps
|
26
|
-
end
|
27
|
-
|
28
|
-
create_table :book, :inherits => :product do |t|
|
29
|
-
t.string :author, :null => false
|
30
|
-
end
|
31
|
-
|
32
|
-
create_table :videos, :inherits => :product do |t|
|
33
|
-
t.string :year, :null => false
|
34
|
-
t.string :genre, :null => false
|
35
|
-
end
|
36
|
-
|
37
|
-
# Models
|
38
|
-
|
39
|
-
class Product < ActiveRecord::Base
|
40
|
-
acts_as_superclass # only if you want top-down access.
|
41
|
-
end
|
42
|
-
|
43
|
-
class Book < ActiveRecord::Base
|
44
|
-
inherits_from :product
|
45
|
-
end
|
46
|
-
|
47
|
-
class Video < ActiveRecord::Base
|
48
|
-
inherits_from :product
|
49
|
-
end
|
50
|
-
|
51
|
-
book = Book.find(1)
|
52
|
-
book.name => "Agile Development with Rails"
|
53
|
-
book.author => "Dave Thomas"
|
54
|
-
book.price => 19.00
|
55
|
-
|
56
|
-
video = Video.find(2)
|
57
|
-
video.name => "Inseption"
|
58
|
-
video.year => "2010"
|
59
|
-
video.genre => "SCI-FI"
|
60
|
-
video.price => 22.00
|
61
|
-
|
62
|
-
book = Book.new
|
63
|
-
book.name = "Hamlet"
|
64
|
-
book.author = "Shakespeare, William"
|
65
|
-
book.price => 14.00
|
66
|
-
book.save
|
67
|
-
|
68
|
-
Top-down access (Polymorphic)
|
69
|
-
=============================
|
70
|
-
|
71
|
-
if you want to access product and get field in the subclass do you need to create a field subtype:string in superclass and ad acts_as_superclass in superclass and now you can do like this.
|
72
|
-
|
73
|
-
product = Product.find 1 # This is a Book instance.
|
74
|
-
product.author
|
75
|
-
|
76
|
-
product = Product.find 2 # This is a Video instance.
|
77
|
-
product.genre
|
78
|
-
|
79
|
-
|
80
|
-
if you need help contanct me: bfscordeiro (at) gmail.com .
|
81
|
-
|
82
|
-
|
83
|
-
Copyright (c) 2010 Bruno Cordeiro, released under the MIT license
|
data/lib/inherits-migration.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
|
3
|
-
module InheritsMigration
|
4
|
-
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
alias_method_chain :create_table , :inherits
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
# Generate the association field.
|
12
|
-
def create_table_with_inherits(table_name, options = {}, &block)
|
13
|
-
options[:id] ||= false if options[:inherits]
|
14
|
-
|
15
|
-
create_table_without_inherits(table_name, options) do |table_defintion|
|
16
|
-
if options[:inherits]
|
17
|
-
association_type = Object.const_get(options[:inherits].to_s.capitalize)
|
18
|
-
association_inst = association_type.send(:new)
|
19
|
-
attr_column = association_inst.column_for_attribute(association_type.primary_key)
|
20
|
-
|
21
|
-
field_option = {:primary_key => true, :null => false}
|
22
|
-
field_option[:limit] = attr_column.limit if attr_column.limit
|
23
|
-
table_defintion.column "#{options[:inherits]}_id", attr_column.type, field_option
|
24
|
-
end
|
25
|
-
yield table_defintion
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
#ActiveRecord::ConnectionAdapters::SchemaStatements::send(:include, InheritsMigration)
|
31
|
-
ActiveRecord::Base
|
32
|
-
ActiveRecord::ConnectionAdapters::SchemaStatements::send(:include, InheritsMigration)
|