class-table-inheritance 1.0.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.
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