rmre 0.0.1

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.
@@ -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
+