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 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