alexrothenberg-legacy_data 0.0.6 → 0.0.7

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