alexrothenberg-legacy_data 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -29,6 +29,7 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
29
  spec.libs << 'lib' << 'spec'
30
30
  spec.pattern = 'spec/**/*_spec.rb'
31
31
  spec.rcov = true
32
+ spec.rcov_opts = ['--exclude spec,gems', '--sort coverage']
32
33
  end
33
34
 
34
35
  task :spec => :check_dependencies
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
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[:class_name] = LegacyData::TableClassNameMapper.class_name_for(analyzed_table[:table_name])
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 <%= LegacyData::TableClassNameMapper.class_name_for(table_name).underscore.pluralize.to_sym.inspect %>, :foreign_key => <%= foreign_key.inspect %>
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 <%= LegacyData::TableClassNameMapper.class_name_for(table_name).underscore.to_sym.inspect %>, :foreign_key => <%= foreign_key.inspect %>
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"
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-20}
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
@@ -1,3 +1,4 @@
1
+ require 'legacy_data/table_definition'
1
2
  require 'legacy_data/schema'
2
3
  require 'legacy_data/table_class_name_mapper'
3
4
  require 'active_record/connection_adapters/oracle_enhanced_adapter'
@@ -3,50 +3,76 @@ module LegacyData
3
3
  attr_reader :table_name
4
4
 
5
5
  def self.analyze(options={})
6
- analyzed_schema = []
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
- # puts " Tables: #{@tables.inspect}"
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 = @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? }
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
- @tables.values
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
- @tables.keys.detect {|table_name| @tables[table_name] == :pending }
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 = 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
- { :table_name => table_name,
41
- :class_name => class_name,
42
- :primary_key => primary_key,
43
- :relations => relations,
44
- :constraints => constraints
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 name_pattern=/.*/
49
- connection.tables.select {|table_name| table_name =~ name_pattern }.sort
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 => belongs_to_relations,
63
- :has_some => has_some_relations
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 = connection.columns(table_name, "#{table_name} Columns").reject(&:null).map(&:name)
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.naming_convention= value
15
- instance.naming_convention = value
14
+ def self.method_missing(method_id, *arguments, &block)
15
+ instance.send(method_id, *arguments, &block)
16
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
17
+
18
+ def clear_dictionary
19
+ @dictionary = nil
25
20
  end
26
21
 
27
22
  def load_dictionary
28
- @dictionary = nil
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 self.class_name_for table_name
43
- instance.lookup_class_name(table_name) || instance.compute_class_name(table_name)
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
- 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)
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.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)
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!(:primary_key)
51
- @schema.stub!(:relations)
52
- @schema.stub!(:constraints)
53
- @schema.analyze_table.keys.should include(:table_name, :class_name, :primary_key, :relations, :constraints)
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 give no "has_some" (has_many and has_one) relationships when the adapter does not support foreign keys' do
71
- @connection.should_receive(:respond_to?).with(:foreign_keys_of).and_return(false)
72
- @schema.has_some_relations.should == []
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
- it 'should get all "has_some" (has_many and has_one) relationships when my primary key is the foreign key in another table ' do
76
- @connection.should_receive(:respond_to?).with(:foreign_keys_of).and_return(true)
77
- @connection.should_receive(:foreign_keys_of).and_return([['OTHER_TABLE', 'PK'], ['THE_TABLE', 'PK']])
78
- @schema.has_some_relations.should == {'other_table' => :pk, 'the_table' => :pk}
79
- end
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
- it 'should give no "belongs_to" when the adapter does not support foreign keys' do
82
- @connection.should_receive(:respond_to?).with(:foreign_keys_for).and_return(false)
83
- @schema.belongs_to_relations.should == []
84
- end
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
- it 'should get all "belongs_to" relationships when a foreign key is in my table' do
87
- @connection.should_receive(:respond_to?).with(:foreign_keys_for).and_return(true)
88
- @connection.should_receive(:foreign_keys_for).and_return([['OTHER_TABLE', 'FK_1'], ['THE_TABLE', 'the_table_id']])
89
- @schema.belongs_to_relations.should == {'other_table' => :fk_1, 'the_table' => :the_table_id}
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
- it 'should have the correct constraints'
93
-
94
- it 'should get non-nullable constraints as all columns that do not allow null except the primary key' do
95
- @schema.stub(:primary_key).and_return('col1')
96
- @connection.should_receive(:columns).with('some_table', 'some_table Columns').and_return([col1=stub(:null=>false, :name=>'col1'),
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
- it 'should get uniqueness constraints' do
104
- @connection.should_receive(:indexes).with('some_table').and_return([idx1=stub(:unique=>true, :columns=>['col1']),
105
- idx2=stub(:unique=>false, :columns=>['col2']),
106
- idx3=stub(:unique=>true, :columns=>['col3', 'col4'])])
107
- @schema.unique_constraints.should == [['col1'], ['col3', 'col4']]
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 give no custom constraints when the adapter does not support it' do
111
- @connection.should_receive(:respond_to?).with(:constraints).and_return(false)
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 get all "belongs_to" relationships when a foreign key is in my table' do
116
- @connection.should_receive(:respond_to?).with(:constraints).and_return(true)
117
- @connection.should_receive(:constraints).and_return([['SomeConstraint', 'custom sql 1'], ['anotherconstraint', 'more custom sql']])
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
- describe 'computing class names without an input dictionary' do
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
- it 'should handle tables with a singular name that ends with s' do
10
- LegacyData::TableClassNameMapper.class_name_for('ADDRESS').should == 'Address'
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
- it 'should handle tables with an irregular pluralization name' do
14
- LegacyData::TableClassNameMapper.class_name_for('TBPERSON').should == 'Tbperson'
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 work on tables that do not have the prefix' do
18
- LegacyData::TableClassNameMapper.class_name_for('PERSON').should == 'Person'
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.8
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-20 00:00:00 -07:00
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