has_normalized_sti 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/.rspec +0 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +75 -0
- data/README +55 -0
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/has_normalized_sti.gemspec +59 -0
- data/init.rb +1 -0
- data/lib/has_normalized_sti.rb +135 -0
- data/spec/has_normalized_sti_spec.rb +121 -0
- data/spec/schema.rb +12 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/speed_test.rb +32 -0
- metadata +103 -0
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
actionpack (3.0.8)
|
6
|
+
activemodel (= 3.0.8)
|
7
|
+
activesupport (= 3.0.8)
|
8
|
+
builder (~> 2.1.2)
|
9
|
+
erubis (~> 2.6.6)
|
10
|
+
i18n (~> 0.5.0)
|
11
|
+
rack (~> 1.2.1)
|
12
|
+
rack-mount (~> 0.6.14)
|
13
|
+
rack-test (~> 0.5.7)
|
14
|
+
tzinfo (~> 0.3.23)
|
15
|
+
activemodel (3.0.8)
|
16
|
+
activesupport (= 3.0.8)
|
17
|
+
builder (~> 2.1.2)
|
18
|
+
i18n (~> 0.5.0)
|
19
|
+
activerecord (3.0.8)
|
20
|
+
activemodel (= 3.0.8)
|
21
|
+
activesupport (= 3.0.8)
|
22
|
+
arel (~> 2.0.10)
|
23
|
+
tzinfo (~> 0.3.23)
|
24
|
+
activesupport (3.0.8)
|
25
|
+
arel (2.0.10)
|
26
|
+
builder (2.1.2)
|
27
|
+
diff-lcs (1.1.2)
|
28
|
+
erubis (2.6.6)
|
29
|
+
abstract (>= 1.0.0)
|
30
|
+
git (1.2.5)
|
31
|
+
i18n (0.5.0)
|
32
|
+
jeweler (1.6.2)
|
33
|
+
bundler (~> 1.0)
|
34
|
+
git (>= 1.2.5)
|
35
|
+
rake
|
36
|
+
mocha (0.9.12)
|
37
|
+
rack (1.2.3)
|
38
|
+
rack-mount (0.6.14)
|
39
|
+
rack (>= 1.0.0)
|
40
|
+
rack-test (0.5.7)
|
41
|
+
rack (>= 1.0)
|
42
|
+
railties (3.0.8)
|
43
|
+
actionpack (= 3.0.8)
|
44
|
+
activesupport (= 3.0.8)
|
45
|
+
rake (>= 0.8.7)
|
46
|
+
thor (~> 0.14.4)
|
47
|
+
rake (0.9.2)
|
48
|
+
rspec (2.6.0)
|
49
|
+
rspec-core (~> 2.6.0)
|
50
|
+
rspec-expectations (~> 2.6.0)
|
51
|
+
rspec-mocks (~> 2.6.0)
|
52
|
+
rspec-core (2.6.4)
|
53
|
+
rspec-expectations (2.6.0)
|
54
|
+
diff-lcs (~> 1.1.2)
|
55
|
+
rspec-mocks (2.6.0)
|
56
|
+
rspec-rails (2.6.1)
|
57
|
+
actionpack (~> 3.0)
|
58
|
+
activesupport (~> 3.0)
|
59
|
+
railties (~> 3.0)
|
60
|
+
rspec (~> 2.6.0)
|
61
|
+
sqlite3 (1.3.3)
|
62
|
+
thor (0.14.6)
|
63
|
+
tzinfo (0.3.28)
|
64
|
+
|
65
|
+
PLATFORMS
|
66
|
+
ruby
|
67
|
+
|
68
|
+
DEPENDENCIES
|
69
|
+
activerecord (~> 3.0.0)
|
70
|
+
i18n
|
71
|
+
jeweler
|
72
|
+
mocha
|
73
|
+
rspec
|
74
|
+
rspec-rails
|
75
|
+
sqlite3
|
data/README
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
has_normalized_sti
|
2
|
+
==================
|
3
|
+
|
4
|
+
This extension will allow Rails STI to work a normalized type column.
|
5
|
+
|
6
|
+
For example
|
7
|
+
|
8
|
+
create_table :people, :force => true do |t|
|
9
|
+
t.string :full_name
|
10
|
+
t.integer :type_id
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :person_types, :force => true do |t|
|
14
|
+
t.string :type_name
|
15
|
+
end
|
16
|
+
|
17
|
+
class Person < ActiveRecord::Base
|
18
|
+
has_normalized_sti
|
19
|
+
end
|
20
|
+
|
21
|
+
class PersonType < ActiveRecord::Base
|
22
|
+
end
|
23
|
+
|
24
|
+
after calling has_normalized_sti:
|
25
|
+
* type - returns the name of the class of the type just as regular STI
|
26
|
+
* type= - set the type to something specific like regular STI
|
27
|
+
* normal_type - the Type object through the relation
|
28
|
+
|
29
|
+
|
30
|
+
Configuration options are:
|
31
|
+
* type_class_name - belong_to this model for the type storage
|
32
|
+
(default: #{class_name}Type)
|
33
|
+
* foreign_key - specifies the column for id of the type (default: type_id)
|
34
|
+
* type_column - specifies the column name for the type string on the types table
|
35
|
+
(default: type_name)
|
36
|
+
|
37
|
+
Override example
|
38
|
+
|
39
|
+
create_table :people, :force => true do |t|
|
40
|
+
t.string :full_name
|
41
|
+
t.integer :special_type_id
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table :person_types, :force => true do |t|
|
45
|
+
t.string :special_person_type
|
46
|
+
end
|
47
|
+
|
48
|
+
class SpecialPerson < ActiveRecord::Base
|
49
|
+
set_table_name :people
|
50
|
+
has_normalized_sti :type_class_name => 'SpecialPersonType', :type_column => 'special_person_type', :foreign_key => 'special_type_id'
|
51
|
+
end
|
52
|
+
|
53
|
+
class SpecialPersonType < ActiveRecord::Base
|
54
|
+
set_table_name :person_types
|
55
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = 'has_normalized_sti'
|
5
|
+
gem.summary = 'allows rails STI to work when the type is normalized out'
|
6
|
+
gem.description = <<-DESC
|
7
|
+
has_normalzied_sti expects the model to have a type_id column
|
8
|
+
referencing a types table containg all the possible types.
|
9
|
+
the types table will be auto populated with new types as new
|
10
|
+
subclasses are saved
|
11
|
+
DESC
|
12
|
+
gem.email = 'kevin@glowacz.info'
|
13
|
+
gem.author = 'Kevin Glowacz'
|
14
|
+
gem.files.exclude '.rvmrc'
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rspec/core/rake_task'
|
21
|
+
RSpec::Core::RakeTask.new(:spec)
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{has_normalized_sti}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kevin Glowacz"]
|
12
|
+
s.date = %q{2011-06-24}
|
13
|
+
s.description = %q{ has_normalzied_sti expects the model to have a type_id column
|
14
|
+
referencing a types table containg all the possible types.
|
15
|
+
the types table will be auto populated with new types as new
|
16
|
+
subclasses are saved
|
17
|
+
}
|
18
|
+
s.email = %q{kevin@glowacz.info}
|
19
|
+
s.extra_rdoc_files = [
|
20
|
+
"README"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".rspec",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"README",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"has_normalized_sti.gemspec",
|
30
|
+
"init.rb",
|
31
|
+
"lib/has_normalized_sti.rb",
|
32
|
+
"spec/has_normalized_sti_spec.rb",
|
33
|
+
"spec/schema.rb",
|
34
|
+
"spec/spec_helper.rb",
|
35
|
+
"spec/speed_test.rb"
|
36
|
+
]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.6.2}
|
39
|
+
s.summary = %q{allows rails STI to work when the type is normalized out}
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
s.specification_version = 3
|
43
|
+
|
44
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
46
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<i18n>, [">= 0"])
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
50
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
51
|
+
s.add_dependency(%q<i18n>, [">= 0"])
|
52
|
+
end
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
55
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
56
|
+
s.add_dependency(%q<i18n>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'has_normalized_sti'
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module HasNormalizedSti
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
# This extension will allow Rails STI to work a normalized type column.
|
7
|
+
|
8
|
+
# For example
|
9
|
+
|
10
|
+
# create_table :people, :force => true do |t|
|
11
|
+
# t.string :full_name
|
12
|
+
# t.integer :type_id
|
13
|
+
# end
|
14
|
+
|
15
|
+
# create_table :person_types, :force => true do |t|
|
16
|
+
# t.string :type_name
|
17
|
+
# end
|
18
|
+
|
19
|
+
# class Person < ActiveRecord::Base
|
20
|
+
# has_normalized_sti
|
21
|
+
# end
|
22
|
+
|
23
|
+
# class PersonType < ActiveRecord::Base
|
24
|
+
# end
|
25
|
+
|
26
|
+
# after calling has_normalized_sti:
|
27
|
+
# * <tt>type</tt> - returns the name of the class of the type just as regular STI
|
28
|
+
# * <tt>type=</tt> - set the type to something specific like regular STI
|
29
|
+
# * <tt>normal_type</tt> - the Type object through the relation
|
30
|
+
module ClassMethods
|
31
|
+
# Configuration options are:
|
32
|
+
#
|
33
|
+
# * type_class_name - belong_to this model for the type storage
|
34
|
+
# (default: #{class_name}Type)
|
35
|
+
# * foreign_key - specifies the column for id of the type (default: type_id)
|
36
|
+
# * type_column - specifies the column name for the type string on the types table
|
37
|
+
# (default: type_name)
|
38
|
+
def has_normalized_sti(options = {})
|
39
|
+
extend HasNormalizedSti::SingletonMethods
|
40
|
+
include HasNormalizedSti::InstanceMethods
|
41
|
+
|
42
|
+
class_inheritable_accessor :sti_config
|
43
|
+
self.sti_config = {
|
44
|
+
:type_class_name => "#{table_name.classify}Type",
|
45
|
+
:foreign_key => 'type_id',
|
46
|
+
:type_column => 'type_name'
|
47
|
+
}
|
48
|
+
sti_config.update(options)
|
49
|
+
sti_config[:type_class_name] = sti_config[:type_class_name].to_s.classify
|
50
|
+
|
51
|
+
begin
|
52
|
+
sti_config[:type_class_name].constantize
|
53
|
+
rescue NameError
|
54
|
+
txt = "has_normalized_sti could not load #{sti_config[:type_class_name]}\n"
|
55
|
+
txt << "please make sure #{sti_config[:type_class_name]} is loaded before #{self.to_s}\n"
|
56
|
+
txt << "you might need to add require_dependency '#{sti_config[:type_class_name].underscore}'\n"
|
57
|
+
txt << "to #{self.to_s.underscore}.rb"
|
58
|
+
raise LoadError, txt
|
59
|
+
end
|
60
|
+
|
61
|
+
belongs_to :normal_type, :class_name => sti_config[:type_class_name], :foreign_key => sti_config[:foreign_key]
|
62
|
+
default_scope joins(:normal_type).select("#{table_name}.*, #{sti_config[:type_class_name].constantize.table_name}.#{sti_config[:type_column]}")
|
63
|
+
validates_associated :normal_type
|
64
|
+
validates_presence_of :normal_type
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module SingletonMethods
|
69
|
+
def instantiate(record)
|
70
|
+
if record.has_key?(sti_config[:type_column])
|
71
|
+
type_name = record[sti_config[:type_column]]
|
72
|
+
else
|
73
|
+
associated_record = sti_config[:type_class_name].constantize.find_by_id(record[sti_config[:foreign_key].to_s])
|
74
|
+
type_name = associated_record.try(sti_config[:type_column])
|
75
|
+
end
|
76
|
+
model = find_sti_class(type_name).allocate
|
77
|
+
model.init_with('attributes' => record)
|
78
|
+
model
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_sti_class(type_name)
|
82
|
+
if type_name.blank?
|
83
|
+
self
|
84
|
+
else
|
85
|
+
begin
|
86
|
+
if store_full_sti_class
|
87
|
+
ActiveSupport::Dependencies.constantize(type_name)
|
88
|
+
else
|
89
|
+
compute_type(type_name)
|
90
|
+
end
|
91
|
+
rescue NameError
|
92
|
+
raise SubclassNotFound,
|
93
|
+
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
|
94
|
+
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
95
|
+
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
96
|
+
"or overwrite #{name}.inheritance_column to use another column for that information."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def descends_from_active_record?
|
102
|
+
if superclass.abstract_class?
|
103
|
+
superclass.descends_from_active_record?
|
104
|
+
else
|
105
|
+
superclass == ActiveRecord::Base
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def type_condition
|
110
|
+
sti_column = sti_config[:type_class_name].constantize.arel_table[sti_config[:type_column]]
|
111
|
+
condition = sti_column.eq(sti_name)
|
112
|
+
descendants.each { |subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
|
113
|
+
|
114
|
+
condition
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module InstanceMethods
|
119
|
+
def initialize(attributes = {})
|
120
|
+
super
|
121
|
+
self.type = self.class.to_s
|
122
|
+
end
|
123
|
+
|
124
|
+
def type
|
125
|
+
self.normal_type.try(sti_config[:type_column]) || self.class.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
def type=(type_name)
|
129
|
+
type_class = self.class.sti_config[:type_class_name].constantize
|
130
|
+
self.normal_type = type_class.send("find_or_initialize_by_#{sti_config[:type_column]}", type_name)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
ActiveRecord::Base.send :include, HasNormalizedSti
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe 'has_normalized_sti' do
|
4
|
+
describe 'classes' do
|
5
|
+
it 'should hook in to AR:Base' do
|
6
|
+
ActiveRecord::Base.should respond_to :has_normalized_sti
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'instances' do
|
11
|
+
before do
|
12
|
+
@person = Person.new
|
13
|
+
@royal = Royal.new
|
14
|
+
@peasant = Peasant.new
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should respond to type' do
|
18
|
+
@person.should respond_to(:type)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should have type set to the (sub)class name' do
|
22
|
+
@person.type.should == 'Person'
|
23
|
+
@royal.type.should == 'Royal'
|
24
|
+
@peasant.type.should == 'Peasant'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should have an associated normal_type' do
|
28
|
+
@person.should respond_to(:normal_type)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should have the normal_type be a new record if the type does not exist yet' do
|
32
|
+
@person.normal_type.should be_new_record
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should save the assocated normal_type if it was new' do
|
36
|
+
@person.save
|
37
|
+
@person.normal_type.should_not be_new_record
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should not allow a nil normal_type' do
|
41
|
+
@person.normal_type = nil
|
42
|
+
@person.save.should == false
|
43
|
+
@person.should have(1).errors_on(:normal_type)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should set the normal_type to an existing record' do
|
47
|
+
PersonType.create(:type_name => 'Person')
|
48
|
+
Person.new.type_id.should_not be_nil
|
49
|
+
Person.new.normal_type.should_not be_new_record
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should return an object of the subclassed type when searched on the parent' do
|
53
|
+
@royal.save!
|
54
|
+
person = Person.find(@royal.id)
|
55
|
+
person.should be_a(Royal)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should allow the type to be set manual and read back' do
|
59
|
+
@person.type = 'Royal'
|
60
|
+
@person.type.should == 'Royal'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should allow a custom type class' do
|
64
|
+
@peasant.normal_type.should be_a(SpecialPersonType)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should allow a custom foreign key' do
|
68
|
+
@peasant.save!
|
69
|
+
@peasant.type_id.should be_nil
|
70
|
+
@peasant.special_type_id.should_not be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should store the type in a custom column' do
|
74
|
+
@peasant.normal_type.type_name.should be_nil
|
75
|
+
@peasant.normal_type.special_person_type.should == 'Peasant'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should be the right object when found from the base' do
|
79
|
+
@peasant.save!
|
80
|
+
SpecialPerson.find(@peasant.id).should be_a(Peasant)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should work with the options as symbols' do
|
84
|
+
@farmer = Farmer.new
|
85
|
+
@farmer.normal_type.should be_a(SpecialPersonType)
|
86
|
+
@farmer.normal_type.type_name.should be_nil
|
87
|
+
@farmer.normal_type.special_person_type.should == 'Farmer'
|
88
|
+
@farmer.save!
|
89
|
+
@farmer.type_id.should be_nil
|
90
|
+
@farmer.special_type_id.should_not be_nil
|
91
|
+
ReallySpecialPerson.find(@farmer.id).should be_a(Farmer)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should error if the type class is not loaded' do
|
95
|
+
lambda{
|
96
|
+
class ErrPerson < ActiveRecord::Base
|
97
|
+
has_normalized_sti :type_class_name => 'NonExistantType'
|
98
|
+
end
|
99
|
+
}.should raise_error(LoadError)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should only return subclassed records' do
|
103
|
+
person = Person.create!
|
104
|
+
royal = Royal.create!
|
105
|
+
|
106
|
+
Royal.all.count.should == 1
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should only return subclassed and subsubclassed' do
|
110
|
+
person = Person.create!
|
111
|
+
royal = Royal.create!
|
112
|
+
|
113
|
+
class King < Royal
|
114
|
+
end
|
115
|
+
|
116
|
+
King.create!
|
117
|
+
Royal.all.count.should == 2
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :people, :force => true do |t|
|
3
|
+
t.string :full_name
|
4
|
+
t.integer :type_id
|
5
|
+
t.integer :special_type_id
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :person_types, :force => true do |t|
|
9
|
+
t.string :type_name
|
10
|
+
t.string :special_person_type
|
11
|
+
end
|
12
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'rspec/rails/extensions'
|
6
|
+
require 'rspec/rails/matchers'
|
7
|
+
require 'rspec/rails/adapters'
|
8
|
+
require 'rspec/rails/fixture_support'
|
9
|
+
|
10
|
+
RSpec.configure do |c|
|
11
|
+
c.use_transactional_examples = true
|
12
|
+
end
|
13
|
+
|
14
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
15
|
+
require File.dirname(__FILE__) + '/../init'
|
16
|
+
|
17
|
+
ActiveRecord::Base.configurations={'test' => {:adapter => "sqlite3", :database => ":memory:"} }
|
18
|
+
ActiveRecord::Base.establish_connection('test')
|
19
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
20
|
+
|
21
|
+
class PersonType < ActiveRecord::Base
|
22
|
+
end
|
23
|
+
|
24
|
+
class SpecialPersonType < ActiveRecord::Base
|
25
|
+
set_table_name :person_types
|
26
|
+
end
|
27
|
+
|
28
|
+
class Person < ActiveRecord::Base
|
29
|
+
has_normalized_sti
|
30
|
+
end
|
31
|
+
|
32
|
+
class SpecialPerson < ActiveRecord::Base
|
33
|
+
set_table_name :people
|
34
|
+
has_normalized_sti :type_class_name => 'SpecialPersonType', :type_column => 'special_person_type', :foreign_key => 'special_type_id'
|
35
|
+
end
|
36
|
+
|
37
|
+
class Royal < Person
|
38
|
+
end
|
39
|
+
|
40
|
+
class Peasant < SpecialPerson
|
41
|
+
end
|
42
|
+
|
43
|
+
class ReallySpecialPerson < ActiveRecord::Base
|
44
|
+
set_table_name :people
|
45
|
+
has_normalized_sti :type_class_name => :special_person_type, :type_column => :special_person_type, :foreign_key => :special_type_id
|
46
|
+
end
|
47
|
+
|
48
|
+
class Farmer < ReallySpecialPerson
|
49
|
+
end
|
data/spec/speed_test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
class TestPerson < ActiveRecord::Base
|
4
|
+
set_table_name "people"
|
5
|
+
set_inheritance_column :full_name
|
6
|
+
end
|
7
|
+
|
8
|
+
control_insert_start = Time.now
|
9
|
+
10000.times do |i|
|
10
|
+
TestPerson.create!
|
11
|
+
end
|
12
|
+
control_insert_end = Time.now
|
13
|
+
puts "Baseline insert #{control_insert_end - control_insert_start}"
|
14
|
+
|
15
|
+
control_select_start = Time.now
|
16
|
+
TestPerson.all
|
17
|
+
control_select_end = Time.now
|
18
|
+
puts "Baseline select #{control_select_end - control_select_start}"
|
19
|
+
|
20
|
+
TestPerson.delete_all
|
21
|
+
|
22
|
+
test_insert_start = Time.now
|
23
|
+
10000.times do |i|
|
24
|
+
Person.create!
|
25
|
+
end
|
26
|
+
test_insert_end = Time.now
|
27
|
+
puts "Test insert #{test_insert_end - test_insert_start}"
|
28
|
+
|
29
|
+
test_select_start = Time.now
|
30
|
+
Person.all
|
31
|
+
test_select_end = Time.now
|
32
|
+
puts "Test select #{test_select_end - test_select_start}"
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_normalized_sti
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kevin Glowacz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-06-24 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: jeweler
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mocha
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: i18n
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
description: " has_normalzied_sti expects the model to have a type_id column\n referencing a types table containg all the possible types.\n the types table will be auto populated with new types as new\n subclasses are saved\n"
|
50
|
+
email: kevin@glowacz.info
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- README
|
57
|
+
files:
|
58
|
+
- .rspec
|
59
|
+
- Gemfile
|
60
|
+
- Gemfile.lock
|
61
|
+
- README
|
62
|
+
- Rakefile
|
63
|
+
- VERSION
|
64
|
+
- has_normalized_sti.gemspec
|
65
|
+
- init.rb
|
66
|
+
- lib/has_normalized_sti.rb
|
67
|
+
- spec/has_normalized_sti_spec.rb
|
68
|
+
- spec/schema.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
- spec/speed_test.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage:
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3468596275297658209
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.6.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: allows rails STI to work when the type is normalized out
|
102
|
+
test_files: []
|
103
|
+
|