rmre 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ *.gem
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Boško Ivanišević
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ = Rails Models Reverse Engineering
2
+
3
+ Rmre is utility gem for creating Ruby on Rails, at the moment only ActiveRecord, models
4
+ for legacy databases. Besides creating all models it sets proper table name and primary
5
+ key if tables and columns naming doesn't follow Rails convention. It also tries to read
6
+ all foreign keys data from a database if DBE is MySql, PostgreSQL, Oracle or MS SQL and
7
+ sets models relationships on a basic level through belongs_to and has_many declarations.
8
+
9
+ = How to use
10
+
11
+ Rmre is very simple to use:
12
+
13
+ rmre -a mysql -d my_database -u my_username -p my_password -o /path/where/models/will/be/created
14
+
15
+ That's all! Of course there is standard help which you can print at any time:
16
+
17
+ rmre --help
18
+
19
+ or
20
+
21
+ rmre -h
22
+
23
+ I believe that command line options are self explanatory especially if you are familliar
24
+ with Ruby on Rails database handling. Apart from Ruby on Rails related options there are
25
+ several that should be explained.
26
+
27
+ == MS SQL options
28
+
29
+ Options:
30
+
31
+ -m or --mode
32
+ -n or --dsn
33
+
34
+ are user for setting ODBC specific arguments. First one must be used with MS SQL and must
35
+ be set to ODBC and the second one is data source name.
36
+
37
+ == General options
38
+
39
+ Otput directory can be set with:
40
+
41
+ -o /path/to/target/directory
42
+ --out /path/to/target/directory
43
+
44
+ otherwise Rmre will create models in the directory where it is started.
45
+
46
+ Rmre can also filter tables for which it will create models. Filtering is very basic and
47
+ it mathes whether name of the table starts with passed patterns:
48
+
49
+ -i ec_,vi_
50
+ --include ic_,vi_
51
+
52
+ with create models only for tables names with prefixes ec_ or vi_.
53
+
54
+ = TODO
55
+
56
+ * Improve filtering
57
+ * Write more tests
58
+ * Foreign key support for other DBEs (firebird, SQLite, Sybase,...)
59
+ * Probably much more which I cannot remember right now
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'optparse'
4
+ require 'rmre'
5
+
6
+ options = {:db => {}}
7
+ optparse = OptionParser.new do |opts|
8
+ opts.on('-h', '--help', 'Display this screen') do
9
+ puts opts
10
+ exit
11
+ end
12
+
13
+ options[:db][:adapter] = 'sqlite3'
14
+ opts.on('-a', '--adapter [ADAPTER]', 'ActiveRecord adapter to use (default SQLite3)') do |a|
15
+ options[:db][:adapter] = a
16
+ end
17
+
18
+ opts.on('-d', '--database DATABASE', 'Name of database') do |d|
19
+ options[:db][:database] = d
20
+ end
21
+
22
+ options[:db][:username] = ''
23
+ opts.on('-u', '--user [USER]', 'Databse connection username (default empty)') do |u|
24
+ options[:db][:username] = u
25
+ end
26
+
27
+ options[:db][:password] = ''
28
+ opts.on('-p', '--pass [PASS]', 'Database connection password (default empty)') do |p|
29
+ options[:db][:password] = p
30
+ end
31
+
32
+ opts.on('-m', '--mode [MODE]', 'MS SQL conenction mode (ODBC only)') do |m|
33
+ options[:db][:mode] = m
34
+ end
35
+
36
+ opts.on('-n', '--dsn [DSN]', 'ODBC DSN name (MS SQL only)') do |n|
37
+ options[:db][:dsn] = n
38
+ end
39
+
40
+ options[:out_path] = File.expand_path(File.join(Dir.tmpdir, "rmre_models"))
41
+ opts.on('-o', '--out [PATH]', 'Path where models will be generated (default <TMP>/rmre_models)') do |p|
42
+ options[:out_path] = p
43
+ end
44
+
45
+ opts.on('-s', '--host [HOST]', 'IP address or name of the host') do |s|
46
+ options[:db][:host] = s
47
+ end
48
+
49
+ opts.on('-i', '--include pattern1,pattern2', Array, 'Include prefixes') do |i|
50
+ options[:include] = i
51
+ end
52
+ end
53
+
54
+ optparse.parse!
55
+
56
+ generator = Rmre::Generator.new(options[:db], options[:out_path], options[:include])
57
+ generator.connect
58
+ generator.create_models(generator.connection.tables)
@@ -0,0 +1 @@
1
+ require 'rmre/generator'
@@ -0,0 +1,178 @@
1
+ require "tmpdir"
2
+ require "fileutils"
3
+ require "active_record"
4
+
5
+ module Rmre
6
+ class Generator
7
+ attr_reader :connection
8
+ attr_reader :output_path
9
+
10
+ SETTINGS_ROOT = File.expand_path('../../../../db', __FILE__)
11
+
12
+ def initialize(options, out_path, include)
13
+ @connection_options = options
14
+ @connection = nil
15
+ @output_path = out_path
16
+ @include_prefixes = include
17
+ end
18
+
19
+ def connect
20
+ return if @connection_options.empty?
21
+
22
+ ActiveRecord::Base.establish_connection(@connection_options)
23
+ @connection = ActiveRecord::Base.connection
24
+ end
25
+
26
+ def create_models(tables)
27
+ return unless tables.is_a? Array
28
+
29
+ FileUtils.mkdir_p(@output_path) if !Dir.exists?(@output_path)
30
+
31
+ tables.each do |table_name|
32
+ create_model(table_name) if process?(table_name)
33
+ end
34
+ end
35
+
36
+ def create_model(table_name)
37
+ File.open(File.join(output_path, "#{table_name}.rb"), "w") do |file|
38
+ constraints = []
39
+
40
+ foreign_keys.each do |fk|
41
+ src = constraint_src(table_name, fk)
42
+ constraints << src unless src.nil?
43
+ end
44
+
45
+ file.write generate_model_source(table_name, constraints)
46
+ end
47
+ end
48
+
49
+ def process?(table_name)
50
+ return true if @include_prefixes.nil? || @include_prefixes.empty?
51
+
52
+ @include_prefixes.each do |prefix|
53
+ return true if table_name =~ /^#{prefix}/
54
+ end
55
+
56
+ false
57
+ end
58
+
59
+ def foreign_keys
60
+ @foreign_keys ||= fetch_foreign_keys
61
+ end
62
+
63
+ private
64
+ def fetch_foreign_keys
65
+ fk = []
66
+ case @connection_options[:adapter]
67
+ when 'mysql'
68
+ fk = mysql_foreign_keys
69
+ when 'postgresql'
70
+ fk = psql_foreign_keys
71
+ when 'sqlserver'
72
+ fk = mssql_foreign_keys
73
+ when 'oracle_enhanced'
74
+ fk = oracle_foreign_keys
75
+ end
76
+ fk
77
+ end
78
+
79
+ def constraint_src(table_name, fk={})
80
+ src = nil
81
+ if fk['from_table'] == table_name
82
+ src = "belongs_to :#{fk['to_table']}, :class_name => '#{fk['to_table'].classify}', :foreign_key => :#{fk['from_column']}"
83
+ elsif fk['to_table'] == table_name
84
+ src = "has_many :#{fk['from_table'].pluralize}, :class_name => '#{fk['from_table'].classify}'"
85
+ end
86
+ src
87
+ end
88
+
89
+ def generate_model_source(table_name, constraints)
90
+ src = "class #{table_name.classify} < ActiveRecord::Base\n"
91
+ primary_key = connection.primary_key(table_name)
92
+ src << " set_primary_key :#{primary_key}\n" unless "id" == primary_key || primary_key.nil?
93
+ src << " set_table_name '#{table_name}'\n" unless table_name == table_name.classify.tableize
94
+ src << " #{constraints.join("\n ")}"
95
+ src << "\nend\n"
96
+ end
97
+
98
+ def mysql_foreign_keys
99
+ sql = <<-SQL
100
+ select
101
+ table_name as from_table,
102
+ column_name as from_column,
103
+ referenced_table_name as to_table,
104
+ referenced_column_name as to_column
105
+ from information_schema.KEY_COLUMN_USAGE
106
+ where referenced_table_schema like '%'
107
+ and constraint_schema = '#{@connection_options[:database]}'
108
+ and referenced_table_name is not null
109
+ SQL
110
+ connection.select_all(sql)
111
+ end
112
+
113
+ def psql_foreign_keys
114
+ sql = <<-SQL
115
+ SELECT tc.table_name as from_table,
116
+ kcu.column_name as from_column,
117
+ ccu.table_name AS to_table,
118
+ ccu.column_name AS to_column
119
+ FROM information_schema.table_constraints tc
120
+ LEFT JOIN information_schema.key_column_usage kcu
121
+ ON tc.constraint_catalog = kcu.constraint_catalog
122
+ AND tc.constraint_schema = kcu.constraint_schema
123
+ AND tc.constraint_name = kcu.constraint_name
124
+
125
+ LEFT JOIN information_schema.referential_constraints rc
126
+ ON tc.constraint_catalog = rc.constraint_catalog
127
+ AND tc.constraint_schema = rc.constraint_schema
128
+ AND tc.constraint_name = rc.constraint_name
129
+ LEFT JOIN information_schema.constraint_column_usage ccu
130
+ ON rc.unique_constraint_catalog = ccu.constraint_catalog
131
+ AND rc.unique_constraint_schema = ccu.constraint_schema
132
+ AND rc.unique_constraint_name = ccu.constraint_name
133
+ WHERE tc.table_name like '%'
134
+ AND tc.constraint_type = 'FOREIGN KEY';
135
+ SQL
136
+ connection.select_all(sql)
137
+ end
138
+
139
+ def mssql_foreign_keys
140
+ sql = <<-SQL
141
+ SELECT C.TABLE_NAME [from_table],
142
+ KCU.COLUMN_NAME [from_column],
143
+ C2.TABLE_NAME [to_table],
144
+ KCU2.COLUMN_NAME [to_column]
145
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
146
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU
147
+ ON C.CONSTRAINT_SCHEMA = KCU.CONSTRAINT_SCHEMA
148
+ AND C.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME
149
+ INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
150
+ ON C.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
151
+ AND C.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
152
+ INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C2
153
+ ON RC.UNIQUE_CONSTRAINT_SCHEMA = C2.CONSTRAINT_SCHEMA
154
+ AND RC.UNIQUE_CONSTRAINT_NAME = C2.CONSTRAINT_NAME
155
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
156
+ ON C2.CONSTRAINT_SCHEMA = KCU2.CONSTRAINT_SCHEMA
157
+ AND C2.CONSTRAINT_NAME = KCU2.CONSTRAINT_NAME
158
+ AND KCU.ORDINAL_POSITION = KCU2.ORDINAL_POSITION
159
+ WHERE C.CONSTRAINT_TYPE = 'FOREIGN KEY'
160
+ SQL
161
+ connection.select_all(sql)
162
+ end
163
+
164
+ def oracle_foreign_keys
165
+ fk = []
166
+ connection.tables.each do |table|
167
+ connection.foreign_keys(table).each do |oracle_fk|
168
+ table_fk = { 'from_table' => oracle_fk.from_table,
169
+ 'from_column' => oracle_fk.options[:column],
170
+ 'to_table' => oracle_fk.to_table,
171
+ 'to_column' => oracle_fk.options[:primary_key] }
172
+ fk << table_fk
173
+ end
174
+ end
175
+ fk
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,3 @@
1
+ module Rmre
2
+ VERSION = "0.0.1" unless defined?(::Rmre::VERSION)
3
+ end
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ require 'rmre/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "rmre"
8
+ s.version = Rmre::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Bosko Ivanisevic"]
11
+ s.email = ["bosko.ivanisevic@gmail.com"]
12
+ s.homepage = "http://github.com/bosko/rmre"
13
+ s.summary = %q{The easiest way to create ActiveRecord models for legacy database}
14
+ s.description = %q{Rmre creates ActiveRecord models for legacy database with all constraints found.}
15
+
16
+ s.required_rubygems_version = ">= 1.3.6"
17
+ s.rubyforge_project = "rmre"
18
+
19
+ s.add_development_dependency "rspec"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+
26
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
27
+ s.rdoc_options << '--title' << 'Rmre -- Rails Models Reverse Engineering' <<
28
+ '--main' << 'README.rdoc'
29
+ end
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+
3
+ module Rmre
4
+ describe Generator do
5
+ let(:settings) do |sett|
6
+ sett = {:db => {:adapter => 'some_adapter',
7
+ :database => 'db',
8
+ :username => 'user',
9
+ :password => 'pass'},
10
+ :out_path => '/tmp/gne-test',
11
+ :include => ['incl1_', 'incl2_']}
12
+ end
13
+
14
+ let(:generator) { Generator.new(settings[:db], settings[:out_path], settings[:include]) }
15
+ let(:tables) { %w(incl1_tbl1 incl1_tbl2 incl2_tbl1 user processes) }
16
+
17
+ it "should flag table inv_plan for processing" do
18
+ generator.process?('incl1_tbl1').should be_true
19
+ end
20
+
21
+ it "should not flag table shkprocesses for processing" do
22
+ generator.process?('processes').should be_false
23
+ end
24
+
25
+ it "should process three tables from the passed array of tables" do
26
+ generator.stub(:create_model)
27
+
28
+ generator.should_receive(:create_model).exactly(3).times
29
+ generator.create_models(tables)
30
+ end
31
+
32
+ it "should contain set_table_name 'incl1_tbl1' in generated source" do
33
+ generator.stub_chain(:connection, :primary_key).and_return("id")
34
+ generator.send(:generate_model_source, 'incl1_tbl1', []).should match(/set_table_name \'incl1_tbl1\'/)
35
+ end
36
+
37
+ it "should create three model files" do
38
+ generator.stub_chain(:connection, :primary_key).and_return("id")
39
+ generator.stub(:foreign_keys).and_return([])
40
+ generator.create_models(tables)
41
+ Dir.glob(File.join(generator.output_path, "*.rb")).should have(3).items
42
+ end
43
+ end
44
+ end
@@ -0,0 +1 @@
1
+ require 'rmre'
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rmre
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Bosko Ivanisevic
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-30 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description: Rmre creates ActiveRecord models for legacy database with all constraints found.
34
+ email:
35
+ - bosko.ivanisevic@gmail.com
36
+ executables:
37
+ - rmre
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ - LICENSE.txt
43
+ files:
44
+ - .gitignore
45
+ - LICENSE.txt
46
+ - README.rdoc
47
+ - bin/rmre
48
+ - lib/rmre.rb
49
+ - lib/rmre/generator.rb
50
+ - lib/rmre/version.rb
51
+ - rmre.gemspec
52
+ - spec/rmre/generator_spec.rb
53
+ - spec/spec_helper.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/bosko/rmre
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --title
61
+ - Rmre -- Rails Models Reverse Engineering
62
+ - --main
63
+ - README.rdoc
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 1
81
+ - 3
82
+ - 6
83
+ version: 1.3.6
84
+ requirements: []
85
+
86
+ rubyforge_project: rmre
87
+ rubygems_version: 1.3.7
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: The easiest way to create ActiveRecord models for legacy database
91
+ test_files: []
92
+