class-table-inheritance 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,64 @@
1
+ ClassTableInheritance
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
+ Example
12
+ =======
13
+
14
+ # Migrations
15
+
16
+ create_table :product do |t|
17
+ t.string :description, :null => false
18
+ t.decimal :price
19
+ t.timestamps
20
+ end
21
+
22
+ create_table :book, :inherits => :product do |t|
23
+ t.string :author, :null => false
24
+ end
25
+
26
+ create_table :videos, :inherits => :product do |t|
27
+ t.string :year, :null => false
28
+ t.string :genre, :null => false
29
+ end
30
+
31
+ # Models
32
+
33
+ class Product < ActiveRecord::Base
34
+ end
35
+
36
+ class Book < ActiveRecord::Base
37
+ inherits_from :product
38
+ end
39
+
40
+ class Video < ActiveRecord::Base
41
+ inherits_from :product
42
+ end
43
+
44
+ book = Book.find(1)
45
+ book.name => "Agile Development with Rails"
46
+ book.author => "Dave Thomas"
47
+ book.price => 19.00
48
+
49
+ video = Video.find(2)
50
+ video.name => "Inseption"
51
+ video.year => "2010"
52
+ video.genre => "SCI-FI"
53
+ video.price => 22.00
54
+
55
+ book = Book.new
56
+ book.name = "Hamlet"
57
+ book.author = "Shakespeare, William"
58
+ book.price => 14.00
59
+ book.save
60
+
61
+ if you need help contanct me: bfscordeiro (at) gmail.com .
62
+
63
+
64
+ Copyright (c) 2010 Bruno Cordeiro, released under the MIT license
@@ -0,0 +1,110 @@
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.inherits_from(association_id)
10
+
11
+ # add an association, and set the foreign key.
12
+ has_one association_id, :foreign_key => :id
13
+
14
+
15
+ # set the primary key, it' need because the generalized table doesn't have
16
+ # a field ID.
17
+ set_primary_key "#{association_id}_id"
18
+
19
+
20
+ # Autobuild method to make a instance of association
21
+ define_method("#{association_id}_with_autobuild") do
22
+ send("#{association_id}_without_autobuild") || send("build_#{association_id}")
23
+ end
24
+
25
+
26
+ # Set a method chain whith autobuild.
27
+ alias_method_chain association_id, :autobuild
28
+
29
+
30
+ # bind the before save, this method call the save of association, and
31
+ # get our generated ID an set to association_id field.
32
+ before_save :save_inherit
33
+
34
+
35
+ # Bind the validation of association.
36
+ validate :inherit_association_must_be_valid
37
+
38
+
39
+ # Generate a method to validate the field of association.
40
+ define_method("inherit_association_must_be_valid") do
41
+ association = send(association_id)
42
+
43
+ unless valid = association.valid?
44
+ association.errors.each do |attr, message|
45
+ errors.add(attr, message)
46
+ end
47
+ end
48
+
49
+ valid
50
+ end
51
+
52
+
53
+
54
+ # get the class of association by reflection, this is needed because
55
+ # i need to get the methods and attributes to make a proxy methods.
56
+ reflection = create_reflection(:has_one, association_id, {}, self)
57
+ association_class = Object.const_get(reflection.class_name)
58
+ # Get the colluns of association class.
59
+ inherited_columns = association_class.column_names
60
+ # Make a filter in association colluns to exclude the colluns that
61
+ # the generalized class already have.
62
+ inherited_columns = inherited_columns.reject { |c| self.column_names.grep(c).length > 0 || c == "type" }
63
+ # Get the methods of the association class and tun it to an Array of Strings.
64
+ inherited_methods = association_class.reflections.map { |key,value| key.to_s }
65
+ # Make a filter in association methods to exclude the methods that
66
+ # the generalizae class already have.
67
+ inherited_methods = inherited_methods.reject { |c| self.reflections.map {|key, value| key.to_s }.include?(c) }
68
+
69
+
70
+ # create the proxy methods to get and set the properties and methods
71
+ # in association class.
72
+ (inherited_columns + inherited_methods).each do |name|
73
+ define_method name do
74
+ # if the field is ID than i only bind that with the association field.
75
+ # this is needed to bypass the overflow problem when the ActiveRecord
76
+ # try to get the id to find the association.
77
+ if name == 'id'
78
+ self["#{association_id}_id"]
79
+ else
80
+ assoc = send(association_id)
81
+ assoc.send(name)
82
+ end
83
+ end
84
+
85
+
86
+ define_method "#{name}=" do |new_value|
87
+ # if the field is ID than i only bind that with the association field.
88
+ # this is needed to bypass the overflow problem when the ActiveRecord
89
+ # try to get the id to find the association.
90
+ if name == 'id'
91
+ self["#{association_id}_id"] = new_value
92
+ else
93
+ assoc = send(association_id)
94
+ assoc.send("#{name}=", new_value)
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+ # Create a method do bind in before_save callback, this method
101
+ # only call the save of association class and set the id in the
102
+ # generalized class.
103
+ define_method("save_inherit") do |*args|
104
+ association = send(association_id)
105
+ association.save
106
+ self["#{association_id}_id"] = association.id
107
+ true
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,28 @@
1
+ module InheritsMigration
2
+
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :create_table , :inherits
6
+ end
7
+ end
8
+
9
+ # Generate the association field.
10
+ def create_table_with_inherits(table_name, options = {}, &block)
11
+ options[:id] ||= false if options[:inherits]
12
+
13
+ create_table_without_inherits(table_name, options) do |table_defintion|
14
+ if options[:inherits]
15
+ association_type = Object.const_get(options[:inherits].to_s.capitalize)
16
+ association_inst = association_type.send(:new)
17
+ attr_column = association_inst.column_for_attribute(association_type.primary_key)
18
+
19
+ field_option = {:primary_key => true, :null => false}
20
+ field_option[:limit] = attr_column.limit if attr_column.limit
21
+ table_defintion.column "#{options[:inherits]}_id", attr_column.type, field_option
22
+ end
23
+ yield table_defintion
24
+ end
25
+ end
26
+ end
27
+
28
+ ActiveRecord::ConnectionAdapters::SchemaStatements::send(:include, InheritsMigration)
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class ClassTableInheritanceTest < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ test "the truth" do
6
+ assert true
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: class-table-inheritance
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Bruno Frank Cordeiro
14
+ autorequire: name
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-29 00:00:00 -02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: bfscordeiro@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README
30
+ files:
31
+ - lib/class-table-inheritance.rb
32
+ - lib/inherits-migration.rb
33
+ - README
34
+ - test/class_table_inheritance_test.rb
35
+ has_rdoc: true
36
+ homepage:
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ hash: 3
50
+ segments:
51
+ - 0
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.3.7
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: ActiveRecord plugin designed to allow simple multiple table (class) inheritance.
69
+ test_files:
70
+ - test/class_table_inheritance_test.rb