alexrothenberg-legacy_data 0.0.6 → 0.0.7

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