alexrothenberg-legacy_data 0.0.8 → 0.0.9
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/Rakefile +1 -0
- data/VERSION +1 -1
- data/generators/models_from_tables/models_from_tables_generator.rb +3 -2
- data/generators/models_from_tables/templates/model.rb +7 -4
- data/legacy_data.gemspec +5 -2
- data/lib/legacy_data.rb +1 -0
- data/lib/legacy_data/schema.rb +59 -26
- data/lib/legacy_data/table_class_name_mapper.rb +8 -13
- data/lib/legacy_data/table_definition.rb +41 -0
- data/spec/legacy_data/schema_spec.rb +117 -63
- data/spec/legacy_data/table_class_name_mapper_spec.rb +77 -7
- data/spec/legacy_data/table_definition_spec.rb +66 -0
- metadata +5 -2
data/Rakefile
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.9
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../lib/legacy_data/table_definition'
|
|
1
2
|
require File.dirname(__FILE__) + '/../../lib/legacy_data/schema'
|
|
2
3
|
require File.dirname(__FILE__) + '/../../lib/legacy_data/table_class_name_mapper'
|
|
3
4
|
require File.dirname(__FILE__) + '/../../lib/active_record/connection_adapters/oracle_enhanced_adapter'
|
|
@@ -20,11 +21,11 @@ Done analyzing the tables.
|
|
|
20
21
|
LegacyData::TableClassNameMapper.load_dictionary
|
|
21
22
|
|
|
22
23
|
analyzed_tables.each do |analyzed_table|
|
|
23
|
-
analyzed_table
|
|
24
|
+
analyzed_table.class_name = LegacyData::TableClassNameMapper.class_name_for(analyzed_table[:table_name])
|
|
24
25
|
m.class_collisions :class_path, analyzed_table[:class_name]
|
|
25
26
|
m.template 'model.rb',
|
|
26
27
|
File.join('app/models', "#{analyzed_table[:class_name].underscore}.rb"),
|
|
27
|
-
:assigns => analyzed_table
|
|
28
|
+
:assigns => analyzed_table.to_hash
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
@@ -3,11 +3,14 @@ class <%= class_name -%> < ActiveRecord::Base
|
|
|
3
3
|
<%= "set_primary_key #{primary_key.to_sym.inspect}" if primary_key %>
|
|
4
4
|
|
|
5
5
|
# Relationships
|
|
6
|
-
<%- relations[:has_some].each do |table_name, foreign_key|
|
|
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 |table_name, foreign_key|
|
|
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
|
+
<%- end -%>
|
|
12
|
+
<%- relations[:has_and_belongs_to_many].each do |table_name, options|
|
|
13
|
+
-%> has_and_belongs_to_many <%= LegacyData::TableClassNameMapper.class_name_for(table_name).underscore.pluralize.to_sym.inspect %>, :foreign_key => <%= options[:foreign_key].inspect%>, :association_foreign_key => <%= options[:association_foreign_key].inspect%>, :join_table => <%= options[:join_table].inspect %>
|
|
11
14
|
<%- end -%>
|
|
12
15
|
|
|
13
16
|
# 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.9"
|
|
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-21}
|
|
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 = [
|
|
@@ -31,8 +31,10 @@ Gem::Specification.new do |s|
|
|
|
31
31
|
"lib/legacy_data.rb",
|
|
32
32
|
"lib/legacy_data/schema.rb",
|
|
33
33
|
"lib/legacy_data/table_class_name_mapper.rb",
|
|
34
|
+
"lib/legacy_data/table_definition.rb",
|
|
34
35
|
"spec/legacy_data/schema_spec.rb",
|
|
35
36
|
"spec/legacy_data/table_class_name_mapper_spec.rb",
|
|
37
|
+
"spec/legacy_data/table_definition_spec.rb",
|
|
36
38
|
"spec/spec_helper.rb"
|
|
37
39
|
]
|
|
38
40
|
s.has_rdoc = true
|
|
@@ -44,6 +46,7 @@ Gem::Specification.new do |s|
|
|
|
44
46
|
s.test_files = [
|
|
45
47
|
"spec/legacy_data/schema_spec.rb",
|
|
46
48
|
"spec/legacy_data/table_class_name_mapper_spec.rb",
|
|
49
|
+
"spec/legacy_data/table_definition_spec.rb",
|
|
47
50
|
"spec/spec_helper.rb"
|
|
48
51
|
]
|
|
49
52
|
|
data/lib/legacy_data.rb
CHANGED
data/lib/legacy_data/schema.rb
CHANGED
|
@@ -3,50 +3,76 @@ module LegacyData
|
|
|
3
3
|
attr_reader :table_name
|
|
4
4
|
|
|
5
5
|
def self.analyze(options={})
|
|
6
|
-
|
|
6
|
+
initialize_tables(options[:table_name])
|
|
7
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
8
|
while table_name = next_table_to_process
|
|
16
|
-
|
|
17
|
-
@tables[table_name] = analyze_table(table_name)
|
|
9
|
+
table_definitions[table_name] = analyze_table(table_name)
|
|
18
10
|
|
|
19
11
|
[:has_some, :belongs_to].each do |relation_type|
|
|
20
|
-
associated_tables =
|
|
21
|
-
associated_tables.each {|associated_table|
|
|
12
|
+
associated_tables = table_definitions[table_name][:relations][relation_type].keys.map(&:to_s)
|
|
13
|
+
associated_tables.each {|associated_table| add_pending_table(associated_table) }
|
|
22
14
|
end
|
|
23
15
|
end
|
|
24
|
-
|
|
16
|
+
|
|
17
|
+
remove_join_tables
|
|
25
18
|
end
|
|
26
19
|
def self.analyze_table table_name
|
|
27
20
|
new(table_name).analyze_table
|
|
28
21
|
end
|
|
29
22
|
|
|
23
|
+
|
|
24
|
+
def self.initialize_tables(table_name)
|
|
25
|
+
clear_table_definitions
|
|
26
|
+
if table_name
|
|
27
|
+
add_pending_table(table_name)
|
|
28
|
+
else
|
|
29
|
+
LegacyData::Schema.tables.each {|table| add_pending_table(table) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
def self.add_pending_table table_name
|
|
33
|
+
table_definitions[table_name] = :pending if table_definitions[table_name].nil?
|
|
34
|
+
end
|
|
30
35
|
def self.next_table_to_process
|
|
31
|
-
|
|
36
|
+
table_definitions.keys.detect {|table_name| table_definitions[table_name] == :pending }
|
|
37
|
+
end
|
|
38
|
+
def self.clear_table_definitions
|
|
39
|
+
@tables = {}
|
|
40
|
+
end
|
|
41
|
+
def self.table_definitions
|
|
42
|
+
@tables ||= {}
|
|
43
|
+
end
|
|
44
|
+
def self.next_join_table
|
|
45
|
+
table_definitions.keys.detect {|table_name| table_definitions[table_name].join_table? }
|
|
46
|
+
end
|
|
47
|
+
def self.remove_join_tables
|
|
48
|
+
join_tables, other_tables = table_definitions.values.partition &:join_table?
|
|
49
|
+
|
|
50
|
+
join_tables.each { |join_table| convert_to_habtm(join_table) }
|
|
51
|
+
|
|
52
|
+
other_tables
|
|
53
|
+
end
|
|
54
|
+
def self.convert_to_habtm join_table
|
|
55
|
+
join_table.belongs_to_tables.each do |table|
|
|
56
|
+
table_definitions[table].convert_has_many_to_habtm(join_table)
|
|
57
|
+
end
|
|
32
58
|
end
|
|
33
59
|
|
|
34
60
|
def initialize(table_name)
|
|
35
|
-
@table_name
|
|
61
|
+
@table_name = table_name
|
|
36
62
|
end
|
|
37
63
|
|
|
38
64
|
def analyze_table
|
|
39
65
|
puts "analyzing #{table_name} => #{class_name}"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
TableDefinition.new(:table_name => table_name,
|
|
67
|
+
:columns => column_names,
|
|
68
|
+
:primary_key => primary_key,
|
|
69
|
+
:relations => relations,
|
|
70
|
+
:constraints => constraints
|
|
71
|
+
)
|
|
46
72
|
end
|
|
47
73
|
|
|
48
|
-
def self.tables
|
|
49
|
-
connection.tables.
|
|
74
|
+
def self.tables
|
|
75
|
+
connection.tables.sort
|
|
50
76
|
end
|
|
51
77
|
|
|
52
78
|
def class_name
|
|
@@ -59,8 +85,9 @@ module LegacyData
|
|
|
59
85
|
end
|
|
60
86
|
|
|
61
87
|
def relations
|
|
62
|
-
{ :belongs_to
|
|
63
|
-
:has_some
|
|
88
|
+
{ :belongs_to => belongs_to_relations,
|
|
89
|
+
:has_some => has_some_relations,
|
|
90
|
+
:has_and_belongs_to_many => {}
|
|
64
91
|
}
|
|
65
92
|
end
|
|
66
93
|
|
|
@@ -92,8 +119,14 @@ module LegacyData
|
|
|
92
119
|
}
|
|
93
120
|
end
|
|
94
121
|
|
|
122
|
+
def columns
|
|
123
|
+
@columns ||= connection.columns(table_name, "#{table_name} Columns")
|
|
124
|
+
end
|
|
125
|
+
def column_names
|
|
126
|
+
columns.map(&:name)
|
|
127
|
+
end
|
|
95
128
|
def non_nullable_constraints
|
|
96
|
-
non_nullable_constraints =
|
|
129
|
+
non_nullable_constraints = columns.reject(&:null).map(&:name)
|
|
97
130
|
non_nullable_constraints.reject {|col| col == primary_key}
|
|
98
131
|
end
|
|
99
132
|
|
|
@@ -11,21 +11,16 @@ module LegacyData
|
|
|
11
11
|
@dictionary ||= load_dictionary
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def self.
|
|
15
|
-
instance.
|
|
14
|
+
def self.method_missing(method_id, *arguments, &block)
|
|
15
|
+
instance.send(method_id, *arguments, &block)
|
|
16
16
|
end
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def self.save_dictionary
|
|
21
|
-
instance.save_dictionary
|
|
22
|
-
end
|
|
23
|
-
def self.dictionary_file_name
|
|
24
|
-
instance.dictionary_file_name
|
|
17
|
+
|
|
18
|
+
def clear_dictionary
|
|
19
|
+
@dictionary = nil
|
|
25
20
|
end
|
|
26
21
|
|
|
27
22
|
def load_dictionary
|
|
28
|
-
|
|
23
|
+
clear_dictionary
|
|
29
24
|
File.exists?(dictionary_file_name) ? YAML.load_file(dictionary_file_name) : {}
|
|
30
25
|
end
|
|
31
26
|
|
|
@@ -39,8 +34,8 @@ module LegacyData
|
|
|
39
34
|
File.join(RAILS_ROOT, 'app', 'models', 'table_mappings.yml')
|
|
40
35
|
end
|
|
41
36
|
|
|
42
|
-
def
|
|
43
|
-
|
|
37
|
+
def class_name_for table_name
|
|
38
|
+
lookup_class_name(table_name) || compute_class_name(table_name)
|
|
44
39
|
end
|
|
45
40
|
|
|
46
41
|
def lookup_class_name table_name
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module LegacyData
|
|
2
|
+
class TableDefinition
|
|
3
|
+
attr_accessor :class_name, :table_name, :columns, :primary_key, :relations, :constraints
|
|
4
|
+
|
|
5
|
+
def initialize(options)
|
|
6
|
+
options.each {|key, value| self.send("#{key}=", value) }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def [] key
|
|
10
|
+
self.send(key)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_hash
|
|
14
|
+
hash = {}
|
|
15
|
+
[:class_name, :table_name, :columns, :primary_key, :relations, :constraints].each {|field| hash[field] = self.send(field) }
|
|
16
|
+
hash
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def join_table?
|
|
20
|
+
(columns.size == 2) and relations[:belongs_to] and (relations[:belongs_to].values.map(&:to_s) == columns)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def belongs_to_relations
|
|
24
|
+
return {} if relations.nil? or relations[:belongs_to].nil?
|
|
25
|
+
relations[:belongs_to]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def belongs_to_tables
|
|
29
|
+
return [] if belongs_to_relations == {}
|
|
30
|
+
belongs_to_relations.keys
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def convert_has_many_to_habtm(join_table)
|
|
34
|
+
other_table_name = join_table.belongs_to_tables.detect {|table_name| table_name != self.table_name}
|
|
35
|
+
relations[:has_and_belongs_to_many][other_table_name] = { :foreign_key =>join_table.belongs_to_relations[table_name],
|
|
36
|
+
:association_foreign_key=>join_table.belongs_to_relations[other_table_name],
|
|
37
|
+
:join_table =>join_table.table_name }
|
|
38
|
+
relations[:has_some].delete(join_table.table_name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -2,27 +2,30 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
|
2
2
|
|
|
3
3
|
describe LegacyData::Schema do
|
|
4
4
|
describe 'following associations' do
|
|
5
|
+
before :each do
|
|
6
|
+
LegacyData::Schema.stub!(:analyze_table).with('posts' ).and_return(@posts_analysis =mock(:posts, {:join_table? =>false}))
|
|
7
|
+
LegacyData::Schema.stub!(:analyze_table).with('comments').and_return(@comments_analysis=mock(:comments, {:join_table? =>false}))
|
|
8
|
+
@posts_analysis.stub!( :[]).with(:relations).and_return({:belongs_to=>{ }, :has_some=>{:comments=>:posts_id}})
|
|
9
|
+
@comments_analysis.stub!(:[]).with(:relations).and_return({:belongs_to=>{:posts=>:posts_id}, :has_some=>{ }})
|
|
10
|
+
end
|
|
11
|
+
|
|
5
12
|
it 'should analyze all tables when not given a table to start with' do
|
|
6
13
|
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
14
|
|
|
10
|
-
|
|
11
|
-
comments_analysis.stub!(:[]).with(:relations).and_return({:belongs_to=>{}, :has_some=>{}})
|
|
12
|
-
|
|
13
|
-
LegacyData::Schema.analyze.should include(posts_analysis, comments_analysis)
|
|
15
|
+
LegacyData::Schema.analyze.should include(@posts_analysis, @comments_analysis)
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
it 'should find the associated comments when starting with posts' do
|
|
17
|
-
LegacyData::Schema.
|
|
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)
|
|
19
|
+
LegacyData::Schema.analyze(:table_name=>'posts').should include(@posts_analysis, @comments_analysis)
|
|
24
20
|
end
|
|
25
21
|
end
|
|
22
|
+
|
|
23
|
+
it 'should analyze a single table' do
|
|
24
|
+
LegacyData::Schema.should_receive(:new).with('some_table').and_return(schema=mock)
|
|
25
|
+
schema.should_receive(:analyze_table).and_return(analysis=mock)
|
|
26
|
+
|
|
27
|
+
LegacyData::Schema.analyze_table('some_table').should == analysis
|
|
28
|
+
end
|
|
26
29
|
|
|
27
30
|
before :each do
|
|
28
31
|
LegacyData::Schema.instance_variable_set('@conn', nil)
|
|
@@ -34,11 +37,6 @@ describe LegacyData::Schema do
|
|
|
34
37
|
LegacyData::Schema.tables.should == ['comments', 'people', 'posts']
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
it "should get a filtered list of the tables from the database" do
|
|
38
|
-
@connection.should_receive(:tables).and_return(['comments', 'people', 'posts'])
|
|
39
|
-
LegacyData::Schema.tables(/^p/).should == ['people', 'posts']
|
|
40
|
-
end
|
|
41
|
-
|
|
42
40
|
describe 'analyzing a table' do
|
|
43
41
|
before :each do
|
|
44
42
|
@schema = LegacyData::Schema.new('some_table')
|
|
@@ -46,11 +44,16 @@ describe LegacyData::Schema do
|
|
|
46
44
|
end
|
|
47
45
|
|
|
48
46
|
it 'should have all the information about the table' do
|
|
49
|
-
@schema.stub!(:class_name)
|
|
50
|
-
@schema.stub!(:
|
|
51
|
-
@schema.stub!(:
|
|
52
|
-
@schema.stub!(:
|
|
53
|
-
@schema.
|
|
47
|
+
@schema.stub!(:class_name ).and_return(class_name =mock)
|
|
48
|
+
@schema.stub!(:column_names).and_return(columns =mock)
|
|
49
|
+
@schema.stub!(:primary_key ).and_return(primary_key=mock)
|
|
50
|
+
@schema.stub!(:relations ).and_return(relations =mock)
|
|
51
|
+
@schema.stub!(:constraints ).and_return(constraints=mock)
|
|
52
|
+
@schema.analyze_table[:table_name ].should == 'some_table'
|
|
53
|
+
@schema.analyze_table[:columns ].should == columns
|
|
54
|
+
@schema.analyze_table[:primary_key].should == primary_key
|
|
55
|
+
@schema.analyze_table[:relations ].should == relations
|
|
56
|
+
@schema.analyze_table[:constraints].should == constraints
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
it 'should have the correct table name' do
|
|
@@ -67,56 +70,107 @@ describe LegacyData::Schema do
|
|
|
67
70
|
@schema.primary_key.should == 'PK'
|
|
68
71
|
end
|
|
69
72
|
|
|
70
|
-
it 'should
|
|
71
|
-
@
|
|
72
|
-
@schema.
|
|
73
|
+
it 'should have the names of all columns' do
|
|
74
|
+
@schema.should_receive(:columns).and_return([col1=mock(:col1, :name=>'Col1'), col2=mock(:col2, :name=>'Col2')])
|
|
75
|
+
@schema.column_names.sort.should == ['Col1', 'Col2']
|
|
73
76
|
end
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
describe 'relations' do
|
|
79
|
+
it 'should have the different types of relations' do
|
|
80
|
+
@schema.stub!(:belongs_to_relations).and_return([belongs_to=mock])
|
|
81
|
+
@schema.stub!(:has_some_relations ).and_return([has_some =mock])
|
|
82
|
+
|
|
83
|
+
@schema.relations.should == {:belongs_to=>[belongs_to], :has_some=>[has_some], :has_and_belongs_to_many=>{}}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
it 'should give no "has_some" (has_many and has_one) relationships when the adapter does not support foreign keys' do
|
|
88
|
+
@connection.should_receive(:respond_to?).with(:foreign_keys_of).and_return(false)
|
|
89
|
+
@schema.has_some_relations.should == []
|
|
90
|
+
end
|
|
80
91
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
it 'should get all "has_some" (has_many and has_one) relationships when my primary key is the foreign key in another table ' do
|
|
93
|
+
@connection.should_receive(:respond_to?).with(:foreign_keys_of).and_return(true)
|
|
94
|
+
@connection.should_receive(:foreign_keys_of).and_return([['OTHER_TABLE', 'PK'], ['THE_TABLE', 'PK']])
|
|
95
|
+
@schema.has_some_relations.should == {'other_table' => :pk, 'the_table' => :pk}
|
|
96
|
+
end
|
|
85
97
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
end
|
|
98
|
+
it 'should give no "belongs_to" when the adapter does not support foreign keys' do
|
|
99
|
+
@connection.should_receive(:respond_to?).with(:foreign_keys_for).and_return(false)
|
|
100
|
+
@schema.belongs_to_relations.should == []
|
|
101
|
+
end
|
|
91
102
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
col2=stub(:null=>false, :name=>'col2'),
|
|
98
|
-
col3=stub(:null=>true, :name=>'col3'),
|
|
99
|
-
col3=stub(:null=>false, :name=>'col4')])
|
|
100
|
-
@schema.non_nullable_constraints.should == ['col2', 'col4']
|
|
103
|
+
it 'should get all "belongs_to" relationships when a foreign key is in my table' do
|
|
104
|
+
@connection.should_receive(:respond_to?).with(:foreign_keys_for).and_return(true)
|
|
105
|
+
@connection.should_receive(:foreign_keys_for).and_return([['OTHER_TABLE', 'FK_1'], ['THE_TABLE', 'the_table_id']])
|
|
106
|
+
@schema.belongs_to_relations.should == {'other_table' => :fk_1, 'the_table' => :the_table_id}
|
|
107
|
+
end
|
|
101
108
|
end
|
|
109
|
+
|
|
110
|
+
describe 'constraints' do
|
|
111
|
+
it 'should have the different types of constraints' do
|
|
112
|
+
@schema.stub!(:unique_constraints ).and_return([['unique_col'], ['col1', 'col2']])
|
|
113
|
+
@schema.stub!(:non_nullable_constraints).and_return([non_nullable=mock ])
|
|
114
|
+
@schema.stub!(:custom_constraints ).and_return([custom =mock ])
|
|
115
|
+
|
|
116
|
+
@schema.constraints.should == {:unique=>[['unique_col']], :multi_column_unique=>[['col1', 'col2']], :non_nullable=>[non_nullable], :custom=>[custom]}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'should get non-nullable constraints as all columns that do not allow null except the primary key' do
|
|
120
|
+
@schema.stub(:primary_key).and_return('col1')
|
|
121
|
+
@connection.should_receive(:columns).with('some_table', 'some_table Columns').and_return([col1=stub(:null=>false, :name=>'col1'),
|
|
122
|
+
col2=stub(:null=>false, :name=>'col2'),
|
|
123
|
+
col3=stub(:null=>true, :name=>'col3'),
|
|
124
|
+
col3=stub(:null=>false, :name=>'col4')])
|
|
125
|
+
@schema.non_nullable_constraints.should == ['col2', 'col4']
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'should get uniqueness constraints' do
|
|
129
|
+
@connection.should_receive(:indexes).with('some_table').and_return([idx1=stub(:unique=>true, :columns=>['col1']),
|
|
130
|
+
idx2=stub(:unique=>false, :columns=>['col2']),
|
|
131
|
+
idx3=stub(:unique=>true, :columns=>['col3', 'col4'])])
|
|
132
|
+
@schema.unique_constraints.should == [['col1'], ['col3', 'col4']]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'should give no custom constraints when the adapter does not support it' do
|
|
136
|
+
@connection.should_receive(:respond_to?).with(:constraints).and_return(false)
|
|
137
|
+
@schema.custom_constraints.should == []
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'should get all "belongs_to" relationships when a foreign key is in my table' do
|
|
141
|
+
@connection.should_receive(:respond_to?).with(:constraints).and_return(true)
|
|
142
|
+
@connection.should_receive(:constraints).and_return([['SomeConstraint', 'custom sql 1'], ['anotherconstraint', 'more custom sql']])
|
|
143
|
+
@schema.custom_constraints.should == {:some_constraint => 'custom sql 1', :anotherconstraint => 'more custom sql'}
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
102
147
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
148
|
+
describe 'convert join tables into HABTM relations' do
|
|
149
|
+
before :each do
|
|
150
|
+
LegacyData::Schema.clear_table_definitions
|
|
151
|
+
LegacyData::Schema.table_definitions['posts' ] = @posts_table =mock(:posts, :join_table? => false)
|
|
152
|
+
LegacyData::Schema.table_definitions['tags' ] = @tags_table =mock(:tags, :join_table? => false)
|
|
153
|
+
LegacyData::Schema.table_definitions['tag_posts'] = @tag_posts_join_table=mock(:tag_posts, :join_table? => true )
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'should remove the join table from the list of tables' do
|
|
157
|
+
@tag_posts_join_table.stub!(:belongs_to_tables).and_return(['posts', 'tags'])
|
|
158
|
+
@posts_table.should_receive(:convert_has_many_to_habtm).with(@tag_posts_join_table)
|
|
159
|
+
@tags_table.should_receive( :convert_has_many_to_habtm).with(@tag_posts_join_table)
|
|
160
|
+
|
|
161
|
+
analyzed_tables = LegacyData::Schema.remove_join_tables
|
|
162
|
+
|
|
163
|
+
analyzed_tables.should include(@posts_table, @tags_table)
|
|
164
|
+
analyzed_tables.size.should == 2
|
|
108
165
|
end
|
|
109
166
|
|
|
110
|
-
it 'should
|
|
111
|
-
|
|
112
|
-
@schema.custom_constraints.should == []
|
|
167
|
+
it 'should find the next join table' do
|
|
168
|
+
LegacyData::Schema.next_join_table.should == 'tag_posts'
|
|
113
169
|
end
|
|
114
|
-
|
|
115
|
-
it 'should
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
@schema.custom_constraints.should == {:some_constraint => 'custom sql 1', :anotherconstraint => 'more custom sql'}
|
|
170
|
+
|
|
171
|
+
it 'should know when there are no join tables' do
|
|
172
|
+
LegacyData::Schema.table_definitions.delete('tag_posts')
|
|
173
|
+
LegacyData::Schema.next_join_table.should == nil
|
|
119
174
|
end
|
|
120
|
-
|
|
121
175
|
end
|
|
122
176
|
end
|
|
@@ -1,21 +1,91 @@
|
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
2
2
|
|
|
3
3
|
describe LegacyData::TableClassNameMapper do
|
|
4
|
-
|
|
4
|
+
before :each do
|
|
5
|
+
LegacyData::TableClassNameMapper.clear_dictionary
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe 'without an input dictionary' do
|
|
5
9
|
before :each do
|
|
6
10
|
LegacyData::TableClassNameMapper.instance.stub!(:load_dictionary).and_return({})
|
|
7
11
|
end
|
|
12
|
+
|
|
13
|
+
describe 'without any naming convention' do
|
|
14
|
+
before :each do
|
|
15
|
+
LegacyData::TableClassNameMapper.naming_convention = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'should handle tables with a singular name that ends with s' do
|
|
19
|
+
LegacyData::TableClassNameMapper.class_name_for('ADDRESS').should == 'Address'
|
|
20
|
+
end
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
22
|
+
it 'should handle tables with an irregular pluralization name' do
|
|
23
|
+
LegacyData::TableClassNameMapper.class_name_for('TBPERSON').should == 'Tbperson'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'should work on tables that do not have the prefix' do
|
|
27
|
+
LegacyData::TableClassNameMapper.class_name_for('PERSON').should == 'Person'
|
|
28
|
+
end
|
|
11
29
|
end
|
|
30
|
+
|
|
31
|
+
describe "with a 'tables start with TB' naming convention" do
|
|
32
|
+
before :each do
|
|
33
|
+
LegacyData::TableClassNameMapper.naming_convention = 'TB*'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should use the wildcard portion when the naming convention applied' do
|
|
37
|
+
LegacyData::TableClassNameMapper.class_name_for('TBPERSON').should == 'Person'
|
|
38
|
+
end
|
|
12
39
|
|
|
13
|
-
|
|
14
|
-
|
|
40
|
+
it 'should use the full table name when the naming convention does not match' do
|
|
41
|
+
LegacyData::TableClassNameMapper.class_name_for('PERSON').should == 'Person'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe 'with an input dictionary' do
|
|
47
|
+
before :each do
|
|
48
|
+
LegacyData::TableClassNameMapper.instance.stub!(:load_dictionary).and_return({'some_table' => 'CustomClassName'})
|
|
15
49
|
end
|
|
16
50
|
|
|
17
|
-
it 'should
|
|
18
|
-
LegacyData::TableClassNameMapper.class_name_for('
|
|
51
|
+
it 'should use the dictionary mapping when one exists' do
|
|
52
|
+
LegacyData::TableClassNameMapper.class_name_for('some_table').should == 'CustomClassName'
|
|
19
53
|
end
|
|
54
|
+
|
|
55
|
+
it 'should use the algorithm when no dictionary mapping exists' do
|
|
56
|
+
LegacyData::TableClassNameMapper.class_name_for('ANOTHER_TABLE').should == 'AnotherTable'
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe 'persisting the dictionary' do
|
|
61
|
+
before :each do
|
|
62
|
+
RAILS_ROOT = 'test_rails_root'
|
|
63
|
+
@dictionary_file_name = LegacyData::TableClassNameMapper.dictionary_file_name
|
|
64
|
+
end
|
|
65
|
+
after :each do
|
|
66
|
+
Object.send(:remove_const, :RAILS_ROOT) if RAILS_ROOT=='test_rails_root'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'should load the dictionary from a file' do
|
|
70
|
+
File.should_receive(:exists? ).with(@dictionary_file_name).and_return(true)
|
|
71
|
+
YAML.should_receive(:load_file).with(@dictionary_file_name).and_return(dictionary_from_file=mock)
|
|
72
|
+
|
|
73
|
+
LegacyData::TableClassNameMapper.dictionary.should == dictionary_from_file
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'should give empty dictionary when file does not exist' do
|
|
77
|
+
File.should_receive(:exists? ).with(@dictionary_file_name).and_return(false)
|
|
78
|
+
|
|
79
|
+
LegacyData::TableClassNameMapper.dictionary.should == {}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'should save the dictionary to a file' do
|
|
83
|
+
File.should_receive(:open).with(@dictionary_file_name, 'w').and_yield(file=mock)
|
|
84
|
+
LegacyData::TableClassNameMapper.instance.should_receive(:dictionary).and_return(dictionary=mock)
|
|
85
|
+
YAML.should_receive(:dump).with(dictionary, file).and_return(yaml_dictionary=mock)
|
|
86
|
+
|
|
87
|
+
LegacyData::TableClassNameMapper.save_dictionary
|
|
88
|
+
end
|
|
89
|
+
|
|
20
90
|
end
|
|
21
91
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
2
|
+
|
|
3
|
+
describe LegacyData::TableDefinition do
|
|
4
|
+
[:table_name, :columns, :primary_key, :relations, :constraints].each do |option|
|
|
5
|
+
it "should save the #{option} on initialization" do
|
|
6
|
+
LegacyData::TableDefinition.new({option=>'some value'})[option].should == 'some value'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it 'should allow you to set class_name' do
|
|
10
|
+
table_definition = LegacyData::TableDefinition.new({})
|
|
11
|
+
table_definition.class_name = 'NewClassName'
|
|
12
|
+
table_definition[:class_name].should == 'NewClassName'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should reveal itself as a hash' do
|
|
16
|
+
params = {}
|
|
17
|
+
[:class_name, :table_name, :columns, :primary_key, :relations, :constraints].each { |field| params[field] = "#{field}_value" }
|
|
18
|
+
table_definition = LegacyData::TableDefinition.new(params)
|
|
19
|
+
table_definition.to_hash.should == params
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe 'join table' do
|
|
24
|
+
before :each do
|
|
25
|
+
@foreign_key_columns = ['one_table_id', 'another_table_id']
|
|
26
|
+
@belongs_to_relation = {'one_table' => :one_table_id, 'another_table' => :another_table_id}
|
|
27
|
+
end
|
|
28
|
+
it 'should be a join table when it has only 2 columns and both are foreign keys' do
|
|
29
|
+
table_definition = LegacyData::TableDefinition.new(:columns=>@foreign_key_columns, :relations=> {:belongs_to=>@belongs_to_relation})
|
|
30
|
+
table_definition.should be_join_table
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'should not be a join table when it has additional columns' do
|
|
34
|
+
table_definition = LegacyData::TableDefinition.new(:columns=>@foreign_key_columns.push(:another_column), :relations=> {:belongs_to=>@belongs_to_relation})
|
|
35
|
+
table_definition.should_not be_join_table
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'should not be a join table when it does not have the belongs_to relation' do
|
|
39
|
+
table_definition = LegacyData::TableDefinition.new(:columns=>@foreign_key_columns, :relations=> {})
|
|
40
|
+
table_definition.should_not be_join_table
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe 'creating habtm' do
|
|
44
|
+
before :each do
|
|
45
|
+
@posts = LegacyData::TableDefinition.new(:table_name=>'posts', :relations=>{:has_some=>{'tag_posts' => :posts_id}, :has_and_belongs_to_many=>{}})
|
|
46
|
+
@tags = LegacyData::TableDefinition.new(:table_name=>'tags', :relations=>{:has_some=>{'tag_posts' => :tags_id }, :has_and_belongs_to_many=>{}})
|
|
47
|
+
@tag_posts = LegacyData::TableDefinition.new(:table_name=>'tag_posts', :relations=>{:belongs_to=>{'posts' => :posts_id, 'tags' => :tags_id}})
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe 'belonging to tables' do
|
|
51
|
+
it 'should tell you when it does not' do
|
|
52
|
+
@posts.belongs_to_tables.should == []
|
|
53
|
+
end
|
|
54
|
+
it 'should tell you when it belongs to 2 tables' do
|
|
55
|
+
@tag_posts.belongs_to_tables.sort.should == ['posts', 'tags']
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
it 'should convert a has_many into a habtm' do
|
|
59
|
+
@posts.convert_has_many_to_habtm(@tag_posts)
|
|
60
|
+
|
|
61
|
+
@posts.relations[:has_some ].should == {}
|
|
62
|
+
@posts.relations[:has_and_belongs_to_many].should == {'tags' => {:foreign_key=>:posts_id, :association_foreign_key=>:tags_id, :join_table=>"tag_posts"} }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
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.9
|
|
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-21 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
@@ -56,8 +56,10 @@ files:
|
|
|
56
56
|
- lib/legacy_data.rb
|
|
57
57
|
- lib/legacy_data/schema.rb
|
|
58
58
|
- lib/legacy_data/table_class_name_mapper.rb
|
|
59
|
+
- lib/legacy_data/table_definition.rb
|
|
59
60
|
- spec/legacy_data/schema_spec.rb
|
|
60
61
|
- spec/legacy_data/table_class_name_mapper_spec.rb
|
|
62
|
+
- spec/legacy_data/table_definition_spec.rb
|
|
61
63
|
- spec/spec_helper.rb
|
|
62
64
|
has_rdoc: true
|
|
63
65
|
homepage: http://github.com/alexrothenberg/legacy_data
|
|
@@ -89,4 +91,5 @@ summary: Create ActiveRecord models from an existing database
|
|
|
89
91
|
test_files:
|
|
90
92
|
- spec/legacy_data/schema_spec.rb
|
|
91
93
|
- spec/legacy_data/table_class_name_mapper_spec.rb
|
|
94
|
+
- spec/legacy_data/table_definition_spec.rb
|
|
92
95
|
- spec/spec_helper.rb
|