has_normalized_sti 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'jeweler'
5
+ gem 'mocha'
6
+ gem 'i18n'
7
+ end
8
+
9
+ group :test do
10
+ gem 'rspec'
11
+ gem 'rspec-rails'
12
+ gem 'activerecord', '~> 3.0.0'
13
+ gem 'sqlite3'
14
+ end
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
@@ -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
@@ -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
+