alexrothenberg-legacy_data 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/generators/models_from_tables/models_from_tables_generator.rb +13 -12
- data/generators/models_from_tables/templates/model.rb +4 -4
- data/legacy_data.gemspec +7 -4
- data/lib/legacy_data.rb +1 -0
- data/lib/legacy_data/schema.rb +34 -16
- data/lib/legacy_data/table_class_name_mapper.rb +57 -0
- data/spec/{legacy_data_schema_spec.rb → legacy_data/schema_spec.rb} +33 -32
- data/spec/legacy_data/table_class_name_mapper_spec.rb +21 -0
- metadata +7 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../lib/legacy_data/schema'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/legacy_data/table_class_name_mapper'
|
2
3
|
require File.dirname(__FILE__) + '/../../lib/active_record/connection_adapters/oracle_enhanced_adapter'
|
3
4
|
|
4
5
|
class ModelsFromTablesGenerator < Rails::Generator::Base
|
@@ -6,20 +7,20 @@ class ModelsFromTablesGenerator < Rails::Generator::Base
|
|
6
7
|
record do |m|
|
7
8
|
m.directory File.join('app/models')
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
puts "analyzing #{table_name}"
|
17
|
-
analysis = LegacyData::Schema.new(table_name, options[:table_naming_convention]).analyze_table
|
10
|
+
LegacyData::TableClassNameMapper.naming_convention = options[:table_naming_convention]
|
11
|
+
|
12
|
+
analyzed_tables = LegacyData::Schema.analyze(:table_name=>options[:table_name])
|
13
|
+
LegacyData::TableClassNameMapper.save_dictionary
|
14
|
+
puts "Please look at #{LegacyData::TableClassNameMapper.dictionary_file_name} [hit <enter> to continue]"
|
15
|
+
gets
|
16
|
+
LegacyData::TableClassNameMapper.load_dictionary
|
18
17
|
|
19
|
-
|
18
|
+
analyzed_tables.each do |analyzed_table|
|
19
|
+
analyzed_table[:class_name] = LegacyData::TableClassNameMapper.class_name_for(analyzed_table[:table_name])
|
20
|
+
m.class_collisions :class_path, analyzed_table[:class_name]
|
20
21
|
m.template 'model.rb',
|
21
|
-
File.join('app/models', "#{
|
22
|
-
:assigns =>
|
22
|
+
File.join('app/models', "#{analyzed_table[:class_name].underscore}.rb"),
|
23
|
+
:assigns => analyzed_table
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -3,11 +3,11 @@ class <%= class_name -%> < ActiveRecord::Base
|
|
3
3
|
<%= "set_primary_key #{primary_key.inspect}" if primary_key %>
|
4
4
|
|
5
5
|
# Relationships
|
6
|
-
<%- relations[:has_some].each do |
|
7
|
-
-%> has_many <%=
|
6
|
+
<%- relations[:has_some].each do |table_name, foreign_key|
|
7
|
+
-%> has_many <%= LegacyData::TableClassNameMapper.class_name_for(table_name).underscore.pluralize.to_sym.inspect %>, :foreign_key => <%= foreign_key.inspect %>
|
8
8
|
<%- end -%>
|
9
|
-
<%- relations[:belongs_to].each do |
|
10
|
-
-%> belongs_to <%=
|
9
|
+
<%- relations[:belongs_to].each do |table_name, foreign_key|
|
10
|
+
-%> belongs_to <%= LegacyData::TableClassNameMapper.class_name_for(table_name).underscore.to_sym.inspect %>, :foreign_key => <%= foreign_key.inspect %>
|
11
11
|
<%- end -%>
|
12
12
|
|
13
13
|
# Constraints
|
data/legacy_data.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{legacy_data}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Alex Rothenberg"]
|
12
|
-
s.date = %q{2009-09-
|
12
|
+
s.date = %q{2009-09-20}
|
13
13
|
s.description = %q{Create ActiveRecord models from an existing database}
|
14
14
|
s.email = %q{alex@alexrothenberg.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -30,7 +30,9 @@ Gem::Specification.new do |s|
|
|
30
30
|
"lib/active_record/connection_adapters/oracle_enhanced_adapter.rb",
|
31
31
|
"lib/legacy_data.rb",
|
32
32
|
"lib/legacy_data/schema.rb",
|
33
|
-
"
|
33
|
+
"lib/legacy_data/table_class_name_mapper.rb",
|
34
|
+
"spec/legacy_data/schema_spec.rb",
|
35
|
+
"spec/legacy_data/table_class_name_mapper_spec.rb",
|
34
36
|
"spec/spec_helper.rb"
|
35
37
|
]
|
36
38
|
s.has_rdoc = true
|
@@ -40,7 +42,8 @@ Gem::Specification.new do |s|
|
|
40
42
|
s.rubygems_version = %q{1.3.2}
|
41
43
|
s.summary = %q{Create ActiveRecord models from an existing database}
|
42
44
|
s.test_files = [
|
43
|
-
"spec/
|
45
|
+
"spec/legacy_data/schema_spec.rb",
|
46
|
+
"spec/legacy_data/table_class_name_mapper_spec.rb",
|
44
47
|
"spec/spec_helper.rb"
|
45
48
|
]
|
46
49
|
|
data/lib/legacy_data.rb
CHANGED
data/lib/legacy_data/schema.rb
CHANGED
@@ -2,13 +2,41 @@ module LegacyData
|
|
2
2
|
class Schema
|
3
3
|
attr_reader :table_name
|
4
4
|
|
5
|
-
def
|
5
|
+
def self.analyze(options={})
|
6
|
+
analyzed_schema = []
|
7
|
+
|
8
|
+
@tables = {}
|
9
|
+
if options[:table_name]
|
10
|
+
@tables[options[:table_name]] = :pending
|
11
|
+
else
|
12
|
+
LegacyData::Schema.tables.each {|table| @tables[table] = :pending }
|
13
|
+
end
|
14
|
+
|
15
|
+
while table_name = next_table_to_process
|
16
|
+
# puts " Tables: #{@tables.inspect}"
|
17
|
+
@tables[table_name] = analyze_table(table_name)
|
18
|
+
|
19
|
+
[:has_some, :belongs_to].each do |relation_type|
|
20
|
+
associated_tables = @tables[table_name][:relations][relation_type].keys.map(&:to_s)
|
21
|
+
associated_tables.each {|associated_table| @tables[associated_table] = :pending if @tables[associated_table].nil? }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
@tables.values
|
25
|
+
end
|
26
|
+
def self.analyze_table table_name
|
27
|
+
new(table_name).analyze_table
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.next_table_to_process
|
31
|
+
@tables.keys.detect {|table_name| @tables[table_name] == :pending }
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(table_name)
|
6
35
|
@table_name = table_name
|
7
|
-
@naming_convention = naming_convention
|
8
|
-
@naming_convention = /^#{naming_convention.gsub('*', '(.*)')}$/i if @naming_convention.is_a? String
|
9
36
|
end
|
10
37
|
|
11
38
|
def analyze_table
|
39
|
+
puts "analyzing #{table_name} => #{class_name}"
|
12
40
|
{ :table_name => table_name,
|
13
41
|
:class_name => class_name,
|
14
42
|
:primary_key => primary_key,
|
@@ -21,16 +49,8 @@ module LegacyData
|
|
21
49
|
connection.tables.select {|table_name| table_name =~ name_pattern }.sort
|
22
50
|
end
|
23
51
|
|
24
|
-
|
25
|
-
|
26
52
|
def class_name
|
27
|
-
class_name_for(self.table_name)
|
28
|
-
end
|
29
|
-
|
30
|
-
def class_name_for table
|
31
|
-
table =~ @naming_convention
|
32
|
-
stripped_table_name = $1 || table
|
33
|
-
ActiveRecord::Base.class_name(stripped_table_name.downcase.pluralize)
|
53
|
+
TableClassNameMapper.class_name_for(self.table_name)
|
34
54
|
end
|
35
55
|
|
36
56
|
def primary_key
|
@@ -49,8 +69,7 @@ module LegacyData
|
|
49
69
|
|
50
70
|
belongs_to = {}
|
51
71
|
connection.foreign_keys_for(table_name).each do |relation|
|
52
|
-
|
53
|
-
belongs_to[class_name] = relation.second.downcase.to_sym
|
72
|
+
belongs_to[relation.first.downcase] = relation.second.downcase.to_sym
|
54
73
|
end
|
55
74
|
belongs_to
|
56
75
|
end
|
@@ -59,8 +78,7 @@ module LegacyData
|
|
59
78
|
|
60
79
|
has_some = {}
|
61
80
|
connection.foreign_keys_of(table_name).each do |relation|
|
62
|
-
|
63
|
-
has_some[class_name] = relation.second.downcase.to_sym
|
81
|
+
has_some[relation.first.downcase] = relation.second.downcase.to_sym
|
64
82
|
end
|
65
83
|
has_some
|
66
84
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module LegacyData
|
2
|
+
class TableClassNameMapper
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_accessor :naming_convention, :dictionary
|
6
|
+
def naming_convention= naming_convention
|
7
|
+
@naming_convention = (naming_convention || '*').gsub('*', '(.*)')
|
8
|
+
end
|
9
|
+
|
10
|
+
def dictionary
|
11
|
+
@dictionary ||= load_dictionary
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.naming_convention= value
|
15
|
+
instance.naming_convention = value
|
16
|
+
end
|
17
|
+
def self.load_dictionary
|
18
|
+
instance.load_dictionary
|
19
|
+
end
|
20
|
+
def self.save_dictionary
|
21
|
+
instance.save_dictionary
|
22
|
+
end
|
23
|
+
def self.dictionary_file_name
|
24
|
+
instance.dictionary_file_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_dictionary
|
28
|
+
@dictionary = nil
|
29
|
+
YAML.load_file(dictionary_file_name) || {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def save_dictionary
|
33
|
+
File.open(dictionary_file_name, 'w') do |out|
|
34
|
+
YAML.dump(dictionary, out)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def dictionary_file_name
|
39
|
+
File.join(RAILS_ROOT, 'app', 'models', 'table_mappings.yaml')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.class_name_for table_name
|
43
|
+
instance.lookup_class_name(table_name) || instance.compute_class_name(table_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def lookup_class_name table_name
|
47
|
+
dictionary[table_name]
|
48
|
+
end
|
49
|
+
|
50
|
+
def compute_class_name table_name
|
51
|
+
table_name =~ /#{naming_convention}/i
|
52
|
+
stripped_table_name = $1 || table_name
|
53
|
+
dictionary[table_name] = ActiveRecord::Base.class_name(stripped_table_name.downcase.pluralize)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -1,6 +1,29 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe LegacyData::Schema do
|
4
|
+
describe 'following associations' do
|
5
|
+
it 'should analyze all tables when not given a table to start with' do
|
6
|
+
LegacyData::Schema.should_receive(:tables).and_return(['posts', 'comments'])
|
7
|
+
LegacyData::Schema.should_receive(:analyze_table).with('posts' ).and_return(posts_analysis =mock)
|
8
|
+
LegacyData::Schema.should_receive(:analyze_table).with('comments').and_return(comments_analysis=mock)
|
9
|
+
|
10
|
+
posts_analysis.stub!( :[]).with(:relations).and_return({:belongs_to=>{}, :has_some=>{}})
|
11
|
+
comments_analysis.stub!(:[]).with(:relations).and_return({:belongs_to=>{}, :has_some=>{}})
|
12
|
+
|
13
|
+
LegacyData::Schema.analyze.should include(posts_analysis, comments_analysis)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should find the associated comments when starting with posts' do
|
17
|
+
LegacyData::Schema.should_receive(:analyze_table).with('posts' ).and_return(posts_analysis =mock)
|
18
|
+
LegacyData::Schema.should_receive(:analyze_table).with('comments').and_return(comments_analysis=mock)
|
19
|
+
|
20
|
+
posts_analysis.stub!( :[]).with(:relations).and_return({:belongs_to=>{ }, :has_some=>{:comments=>:posts_id}})
|
21
|
+
comments_analysis.stub!(:[]).with(:relations).and_return({:belongs_to=>{:posts=>:posts_id}, :has_some=>{ }})
|
22
|
+
|
23
|
+
LegacyData::Schema.analyze(:table_name=>'posts').should include(posts_analysis, comments_analysis)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
4
27
|
before :each do
|
5
28
|
LegacyData::Schema.instance_variable_set('@conn', nil)
|
6
29
|
ActiveRecord::Base.stub(:connection).and_return(@connection=stub(:connection))
|
@@ -19,14 +42,14 @@ describe LegacyData::Schema do
|
|
19
42
|
describe 'analyzing a table' do
|
20
43
|
before :each do
|
21
44
|
@schema = LegacyData::Schema.new('some_table')
|
45
|
+
@schema.stub!(:puts)
|
22
46
|
end
|
23
47
|
|
24
|
-
it 'should have all the information about the table' do
|
25
|
-
@schema.
|
26
|
-
@schema.
|
27
|
-
@schema.
|
28
|
-
@schema.
|
29
|
-
|
48
|
+
it 'should have all the information about the table' do
|
49
|
+
@schema.stub!(:class_name)
|
50
|
+
@schema.stub!(:primary_key)
|
51
|
+
@schema.stub!(:relations)
|
52
|
+
@schema.stub!(:constraints)
|
30
53
|
@schema.analyze_table.keys.should include(:table_name, :class_name, :primary_key, :relations, :constraints)
|
31
54
|
end
|
32
55
|
|
@@ -35,32 +58,10 @@ describe LegacyData::Schema do
|
|
35
58
|
end
|
36
59
|
|
37
60
|
it 'should have the correct class name' do
|
38
|
-
|
61
|
+
LegacyData::TableClassNameMapper.should_receive(:class_name_for).with('some_table').and_return('SomeClass')
|
39
62
|
@schema.class_name.should == 'SomeClass'
|
40
63
|
end
|
41
64
|
|
42
|
-
it 'should handle tables with a singular name that ends with s' do
|
43
|
-
@schema.class_name_for('ADDRESS').should == 'Address'
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'should handle tables with an irregular pluralization name' do
|
47
|
-
@schema.class_name_for('TBPERSON').should == 'Tbperson'
|
48
|
-
end
|
49
|
-
|
50
|
-
describe 'ignore the table prefix naming convention when figuring out the model name' do
|
51
|
-
before :each do
|
52
|
-
@schema = LegacyData::Schema.new('some_table', /^TB(.*)$/)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'should strip off the prefix' do
|
56
|
-
@schema.class_name_for('TBPERSON').should == 'Person'
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'should work on tables that do not have the prefix' do
|
60
|
-
@schema.class_name_for('PERSON').should == 'Person'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
65
|
it 'should have the correct primary_key' do
|
65
66
|
@connection.should_receive(:pk_and_sequence_for).with('some_table').and_return(['PK', 'SEQ'])
|
66
67
|
@schema.primary_key.should == 'PK'
|
@@ -74,7 +75,7 @@ describe LegacyData::Schema do
|
|
74
75
|
it 'should get all "has_some" (has_many and has_one) relationships when my primary key is the foreign key in another table ' do
|
75
76
|
@connection.should_receive(:respond_to?).with(:foreign_keys_of).and_return(true)
|
76
77
|
@connection.should_receive(:foreign_keys_of).and_return([['OTHER_TABLE', 'PK'], ['THE_TABLE', 'PK']])
|
77
|
-
@schema.has_some_relations.should == {
|
78
|
+
@schema.has_some_relations.should == {'other_table' => :pk, 'the_table' => :pk}
|
78
79
|
end
|
79
80
|
|
80
81
|
it 'should give no "belongs_to" when the adapter does not support foreign keys' do
|
@@ -85,7 +86,7 @@ describe LegacyData::Schema do
|
|
85
86
|
it 'should get all "belongs_to" relationships when a foreign key is in my table' do
|
86
87
|
@connection.should_receive(:respond_to?).with(:foreign_keys_for).and_return(true)
|
87
88
|
@connection.should_receive(:foreign_keys_for).and_return([['OTHER_TABLE', 'FK_1'], ['THE_TABLE', 'the_table_id']])
|
88
|
-
@schema.belongs_to_relations.should == {
|
89
|
+
@schema.belongs_to_relations.should == {'other_table' => :fk_1, 'the_table' => :the_table_id}
|
89
90
|
end
|
90
91
|
|
91
92
|
it 'should have the correct constraints'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe LegacyData::TableClassNameMapper do
|
4
|
+
describe 'computing class names without an input dictionary' do
|
5
|
+
before :each do
|
6
|
+
LegacyData::TableClassNameMapper.instance.stub!(:load_dictionary).and_return({})
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should handle tables with a singular name that ends with s' do
|
10
|
+
LegacyData::TableClassNameMapper.class_name_for('ADDRESS').should == 'Address'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should handle tables with an irregular pluralization name' do
|
14
|
+
LegacyData::TableClassNameMapper.class_name_for('TBPERSON').should == 'Tbperson'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should work on tables that do not have the prefix' do
|
18
|
+
LegacyData::TableClassNameMapper.class_name_for('PERSON').should == 'Person'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alexrothenberg-legacy_data
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Rothenberg
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-20 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -55,7 +55,9 @@ files:
|
|
55
55
|
- lib/active_record/connection_adapters/oracle_enhanced_adapter.rb
|
56
56
|
- lib/legacy_data.rb
|
57
57
|
- lib/legacy_data/schema.rb
|
58
|
-
-
|
58
|
+
- lib/legacy_data/table_class_name_mapper.rb
|
59
|
+
- spec/legacy_data/schema_spec.rb
|
60
|
+
- spec/legacy_data/table_class_name_mapper_spec.rb
|
59
61
|
- spec/spec_helper.rb
|
60
62
|
has_rdoc: true
|
61
63
|
homepage: http://github.com/alexrothenberg/legacy_data
|
@@ -85,5 +87,6 @@ signing_key:
|
|
85
87
|
specification_version: 3
|
86
88
|
summary: Create ActiveRecord models from an existing database
|
87
89
|
test_files:
|
88
|
-
- spec/
|
90
|
+
- spec/legacy_data/schema_spec.rb
|
91
|
+
- spec/legacy_data/table_class_name_mapper_spec.rb
|
89
92
|
- spec/spec_helper.rb
|