oracle-model-generator 0.1.0

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/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ = 0.1.0 - 6-Oct-2010
2
+ * Initial release.
@@ -0,0 +1,8 @@
1
+ CHANGES
2
+ MANIFEST
3
+ README
4
+ oracle-model-generator.gemspec
5
+ Rakefile
6
+ bin/omg
7
+ lib/oracle/model/generator.rb
8
+ test/test_oracle_model_generator.rb
data/README ADDED
@@ -0,0 +1,68 @@
1
+ = Description
2
+ A library for generating an ActiveRecord model from an existing Oracle table.
3
+ This will install an "omg" executable that you can use from the command line.
4
+
5
+ = Synopsis
6
+ Using the command line tool:
7
+
8
+ omg -d your_database -t your_table -u some_user -p some_password
9
+
10
+ Results in a file called "your_table.rb". This is an ActiveRecord model
11
+ declaration, with all validations, primary keys, table name and belongs_to
12
+ relationships defined.
13
+
14
+ = Requirements
15
+ == Must Have
16
+ * ruby-oci8
17
+ * getopt
18
+
19
+ == Optional
20
+ If you want to be able to avoid specifying a username and password on the
21
+ command line then you will need the dbi-dbrc library.
22
+
23
+ If you want your models to support multiple primary keys, then you will
24
+ need to install the composite_primary_keys library.
25
+
26
+ If you want date format validations, then you will need to install the
27
+ validates_timeliness library.
28
+
29
+ = What this library doesn't do
30
+ I do not attempt to set has_many or has_one relationships. There's no good
31
+ way to determine that relationship (one or many?). Besides, in practice I
32
+ find that most people set custom has_xxx relationships that go over and
33
+ above what's set in the Oracle database anyway for purposes of their
34
+ application.
35
+
36
+ I also do not go out of my way to get the model name correct with regards
37
+ to singular vs plural. I do a simple guess that covers most cases, but
38
+ complex cases will break it. It's much easier for you to rename a class or
39
+ file name than it is for me to get this 100% correct.
40
+
41
+ = Author's Comments
42
+ I chose not to patch legacy_data because I have no interest in supporting
43
+ other vendors other than Oracle with this library. By focusing only on
44
+ Oracle I could take advantage of ruby-oci8 features. In addition, I have no
45
+ interest in making this a Rails plugin, and I needed the support of multiple
46
+ primary keys.
47
+
48
+ = Future Plans
49
+ Add support for views.
50
+
51
+ = Known Issues
52
+ None known. If you find any issues, please report them on the github project
53
+ page at http://www.github.com/djberg96/oracle-model-generator.
54
+
55
+ = Warranty
56
+ This package is provided "as is" and without any express or
57
+ implied warranties, including, without limitation, the implied
58
+ warranties of merchantability and fitness for a particular purpose.
59
+
60
+ = Copyright
61
+ (C) 2010, Daniel J. Berger
62
+ All Rights Reserved
63
+
64
+ = License
65
+ Artistic 2.0
66
+
67
+ = Author
68
+ Daniel J. Berger
@@ -0,0 +1,30 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rbconfig'
4
+ include Config
5
+
6
+ namespace 'gem' do
7
+ desc 'Remove any old gem files'
8
+ task :clean do
9
+ Dir['*.gem'].each{ |f| File.delete(f) }
10
+ end
11
+
12
+ desc 'Create the oracle-model-generator gem'
13
+ task :create => :clean do
14
+ spec = eval(IO.read('oracle-model-generator.gemspec'))
15
+ Gem::Builder.new(spec).build
16
+ end
17
+
18
+ desc 'Install the oracle-model-generator gem'
19
+ task :install => [:create] do
20
+ file = Dir["oracle-model-generator*.gem"].last
21
+ sh "gem install #{file}"
22
+ end
23
+ end
24
+
25
+ Rake::TestTask.new do |t|
26
+ t.warning = true
27
+ t.verbose = true
28
+ end
29
+
30
+ task :default => :test
data/bin/omg ADDED
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'oracle/model/generator'
4
+ require 'dbi/dbrc'
5
+ require 'getopt/long'
6
+
7
+ opts = Getopt::Long.getopts(
8
+ ['--help', '-h'],
9
+ ['--table', '-t', Getopt::REQUIRED],
10
+ ['--view', '-v', Getopt::REQUIRED],
11
+ ['--user', '-u', Getopt::REQUIRED],
12
+ ['--password', '-p', Getopt::REQUIRED],
13
+ ['--database', '-d', Getopt::REQUIRED],
14
+ ['--output', '-o', Getopt::REQUIRED]
15
+ )
16
+
17
+ def help
18
+ %Q{
19
+ Available options for the Oracle Model Generator are:
20
+
21
+ -h, --help => Display the help text you're looking at now.
22
+ -t, --table => The name of the table you wish to model.
23
+ -v, --view => The name of the view you wish to model.
24
+ -o, --output => The name of the file to create.
25
+ -u, --user => The user used to establish a connection to the database.
26
+ -p, --password => The password used to establish a connection to the database.
27
+
28
+ If no user or password are supplied, then OMG will attempt to glean that
29
+ information using a combination of the database name and your .dbrc file.
30
+ If that cannot be found, then an error is raised.
31
+
32
+ If no output file is supplied then the file generated will match the name
33
+ of the table, minus the 's' if present, with a .rb extension. This is lazy,
34
+ but it is orders of magnitude easier for you to rename a file than it is
35
+ for me to deal with all possible permutations. Note that the output file
36
+ name is also used as the basis for the class name
37
+
38
+ Examples:
39
+
40
+ # Create a User model for the users table.
41
+ omg -d some_database -u scott -p tiger -t users
42
+
43
+ # Same thing, using dbi-dbrc behind the scenes
44
+ omg -d some_database -t users
45
+
46
+ # Create a Lily model for the lilies table, and specify the output file.
47
+ omg -d some_database -u scott -p tiger -t lilies -o lily.rb
48
+ }
49
+ end
50
+
51
+ if opts['h']
52
+ puts help
53
+ exit!
54
+ end
55
+
56
+ unless opts['database']
57
+ puts "You must specify a database."
58
+ exit!
59
+ end
60
+
61
+ unless opts['table'] || opts['view']
62
+ puts "You must specify a table or view."
63
+ exit!
64
+ end
65
+
66
+ user = opts['user']
67
+ pass = opts['password']
68
+
69
+ unless user && pass
70
+ begin
71
+ dbrc = DBI::DBRC.new(opts['database'], user)
72
+ user = dbrc.user
73
+ pass = dbrc.passwd
74
+ rescue DBI::DBRC::Error
75
+ msg = "No user or password provided, and no dbrc entry found for '"
76
+ msg << opts['database'] + "'."
77
+ puts msg
78
+ exit!
79
+ end
80
+ end
81
+
82
+ table = opts['table']
83
+ view = opts['view']
84
+
85
+ if table && view
86
+ puts "You cannot specify both a table and a view."
87
+ exit!
88
+ end
89
+
90
+ connection = OCI8.new(user, pass, opts['database'])
91
+ omg = Oracle::Model::Generator.new(connection)
92
+ omg.generate(table || view, view)
93
+
94
+ ofile = opts['o'] || table + '.rb'
95
+
96
+ File.open(ofile, 'w') do |fh|
97
+ fh.puts "class #{omg.table} < ActiveRecord::Base"
98
+ fh.puts " set_table_name :#{table}"
99
+
100
+ if omg.primary_keys.size > 1
101
+ fh.puts "\n # Requires the composite-primary-keys library"
102
+ fh.puts " set_primary_keys " + omg.primary_keys.inspect
103
+ else
104
+ fh.puts "\n set_primary_key " + omg.primary_keys.first.to_sym.inspect
105
+ end
106
+
107
+ fh.puts "\n # Table relationships\n\n"
108
+ omg.belongs_to.uniq.each{ |table|
109
+ fh.puts " belongs_to :#{table.downcase}"
110
+ }
111
+
112
+ fh.puts "\n # Validations\n\n"
113
+
114
+ # Character fields, size
115
+ omg.column_info.each{ |col|
116
+ data_type = col.data_type.to_s
117
+ if ['char', 'varchar', 'varchar2'].include?(data_type)
118
+ validation = "validates_size_of :#{col.name.downcase}, :maximum => #{col.data_size}"
119
+ validation << ", :allow_blank => #{col.nullable?}" if col.nullable?
120
+ fh.puts " #{validation}"
121
+ end
122
+ }
123
+
124
+ fh.puts # Line break
125
+
126
+ # Fields that must be present
127
+ omg.column_info.each{ |col|
128
+ unless col.nullable?
129
+ validation = "validates_presence_of :#{col.name.downcase}"
130
+ fh.puts " #{validation}"
131
+ end
132
+ }
133
+
134
+ fh.puts # Line break
135
+
136
+ # Numeric fields
137
+ omg.column_info.each{ |col|
138
+ if col.data_type.to_s == 'number'
139
+ max = ("9" * (col.precision - col.scale)).to_i
140
+
141
+ validation = "validates_numericality_of :#{col.name.downcase}"
142
+ validation << ", :less_than => #{max + 1}, :greater_than => -#{max + 1}"
143
+
144
+ if col.scale == 0
145
+ validation << ", :only_integer => true"
146
+ end
147
+
148
+ fh.puts " #{validation}"
149
+ end
150
+ }
151
+
152
+ fh.puts # Line break
153
+ header_printed = false
154
+
155
+ # Date fields
156
+ omg.column_info.each{ |col|
157
+ data_type = col.data_type.to_s
158
+
159
+ if ['date', 'time'].include?(data_type)
160
+ if data_type == 'date'
161
+ validation = "validates_date :#{col.name.downcase}"
162
+ end
163
+
164
+ if data_type == 'timestamp'
165
+ validation = "validates_time :#{col.name.downcase}"
166
+ end
167
+
168
+ unless header_printed
169
+ fh.puts " # Requires the validates_timeliness library"
170
+ end
171
+
172
+ fh.puts " #{validation}"
173
+ end
174
+ }
175
+
176
+ fh.puts "end"
177
+ end
178
+
179
+ puts "\nFile '#{ofile}' generated\n\n"
@@ -0,0 +1,118 @@
1
+ require 'oci8'
2
+
3
+ module Oracle
4
+ module Model
5
+ class Generator
6
+ VERSION = '0.1.0'
7
+
8
+ attr_reader :connection
9
+ attr_reader :constraints
10
+ attr_reader :foreign_keys
11
+ attr_reader :belongs_to
12
+ attr_reader :table
13
+ attr_reader :view
14
+ attr_reader :dependencies
15
+ attr_reader :column_info
16
+ attr_reader :primary_keys
17
+
18
+ # Example:
19
+ #
20
+ # connection = Oracle::Connection.new(user, password, database)
21
+ # ogenerator = Oracle::Model::Generator.new(connection)
22
+ # ogenerator.generate('users')
23
+ #
24
+ def initialize(connection)
25
+ @connection = connection
26
+ @constraints = []
27
+ @primary_keys = []
28
+ @foreign_keys = []
29
+ @dependencies = []
30
+ @belongs_to = []
31
+ @column_info = []
32
+ @table = nil
33
+ end
34
+
35
+ def generate(table, view = false)
36
+ @table = table.split('_').map{ |e| e.downcase.capitalize }.join
37
+ @view = view
38
+ get_constraints(table) unless view
39
+ get_foreign_keys unless view
40
+ get_column_info(table) unless view
41
+ get_primary_keys
42
+ end
43
+
44
+ private
45
+
46
+ def get_column_info(table_name)
47
+ table = @connection.describe_table(table_name)
48
+ table.columns.each{ |col| @column_info << col }
49
+ end
50
+
51
+ # Returns an array of primary keys.
52
+ #
53
+ def get_primary_keys
54
+ @constraints.each{ |hash|
55
+ if hash['CONSTRAINT_TYPE'] == 'P'
56
+ @primary_keys << hash['COLUMN_NAME'].downcase
57
+ end
58
+ }
59
+ end
60
+
61
+ def get_foreign_keys
62
+ @constraints.each{ |hash|
63
+ if hash['CONSTRAINT_TYPE'] == 'R'
64
+ @foreign_keys << hash['R_CONSTRAINT_NAME']
65
+ end
66
+ }
67
+
68
+ get_belongs_to()
69
+ end
70
+
71
+ def get_belongs_to
72
+ @foreign_keys.each{ |fk|
73
+ @belongs_to << find_fk_table(fk)
74
+ }
75
+ end
76
+
77
+ def find_fk_table(fk)
78
+ sql = %Q{
79
+ select table_name
80
+ from all_constraints
81
+ where constraint_name = '#{fk}'
82
+ }
83
+
84
+ begin
85
+ cursor = @connection.exec(sql)
86
+ fk = cursor.fetch.first
87
+ ensure
88
+ cursor.close if cursor
89
+ end
90
+
91
+ fk
92
+ end
93
+
94
+ # Get a list of constraints for a given table.
95
+ #
96
+ def get_constraints(table_name)
97
+ sql = %Q{
98
+ select *
99
+ from all_cons_columns a, all_constraints b
100
+ where a.owner = b.owner
101
+ and a.constraint_name = b.constraint_name
102
+ and a.table_name = b.table_name
103
+ and b.table_name = '#{table_name.upcase}'
104
+ }
105
+
106
+ begin
107
+ cursor = @connection.exec(sql)
108
+ while rec = cursor.fetch_hash
109
+ @constraints << rec
110
+ end
111
+ ensure
112
+ cursor.close if cursor
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'oracle/model/generator')
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'oracle-model-generator'
5
+ gem.version = '0.1.0'
6
+ gem.author = 'Daniel J. Berger'
7
+ gem.license = 'Artistic 2.0'
8
+ gem.email = 'djberg96@gmail.com'
9
+ gem.homepage = 'http://www.github.com/djberg96/oracle-model-generator'
10
+ gem.summary = 'A Ruby interface for determining protocol information'
11
+ gem.test_file = 'test/test_oracle_model_generator.rb'
12
+ gem.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
+
14
+ gem.executables = "omg"
15
+ gem.rubyforge_project = 'N/A'
16
+ gem.extra_rdoc_files = %w[CHANGES README MANIFEST]
17
+
18
+ gem.add_dependency('ruby-oci8')
19
+ gem.add_dependency('getopt', '>= 1.4.0')
20
+ gem.add_development_dependency('test-unit', '>= 2.1.1')
21
+
22
+ gem.description = <<-EOF
23
+ The oracle-model-generator library allows you to generate an ActiveRecord
24
+ model from an existing Oracle table or view.
25
+ EOF
26
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ gem 'test-unit'
3
+ require 'test/unit'
4
+ require 'oracle/model/generator'
5
+
6
+ class TC_Oracle_Model_Generator < Test::Unit::TestCase
7
+ def setup
8
+ @username = 'hr'
9
+ @password = 'hr'
10
+ @database = 'xe'
11
+ @generator = nil
12
+ @connection = OCI8.new(@username, @password, @database)
13
+ end
14
+
15
+ test "version number is correct" do
16
+ assert_equal('0.1.0', Oracle::Model::Generator::VERSION)
17
+ end
18
+
19
+ test "constructor accepts an oci8 connection object" do
20
+ assert_nothing_raised{ @generator = Oracle::Model::Generator.new(@connection) }
21
+ end
22
+
23
+ test "generate method basic functionality" do
24
+ assert_nothing_raised{ @generator = Oracle::Model::Generator.new(@connection) }
25
+ assert_respond_to(@generator, :generate)
26
+ end
27
+
28
+ test "generate method works with a table name or view" do
29
+ assert_nothing_raised{ @generator = Oracle::Model::Generator.new(@connection) }
30
+ assert_nothing_raised{ @generator.generate('employees') }
31
+ assert_nothing_raised{ @generator.generate('emp_details_view', true) }
32
+ end
33
+
34
+ def teardown
35
+ @username = nil
36
+ @password = nil
37
+ @database = nil
38
+ @generator = nil
39
+ @connection.logoff if @connection
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oracle-model-generator
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Daniel J. Berger
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-06 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: ruby-oci8
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: getopt
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 7
44
+ segments:
45
+ - 1
46
+ - 4
47
+ - 0
48
+ version: 1.4.0
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: test-unit
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 9
60
+ segments:
61
+ - 2
62
+ - 1
63
+ - 1
64
+ version: 2.1.1
65
+ type: :development
66
+ version_requirements: *id003
67
+ description: " The oracle-model-generator library allows you to generate an ActiveRecord\n model from an existing Oracle table or view.\n"
68
+ email: djberg96@gmail.com
69
+ executables:
70
+ - omg
71
+ extensions: []
72
+
73
+ extra_rdoc_files:
74
+ - CHANGES
75
+ - README
76
+ - MANIFEST
77
+ files:
78
+ - Rakefile
79
+ - README
80
+ - oracle-model-generator.gemspec
81
+ - lib/oracle/model/generator.rb
82
+ - lib/oracle_model_generator.rb
83
+ - CHANGES
84
+ - test/test_oracle_model_generator.rb
85
+ - MANIFEST
86
+ - bin/omg
87
+ has_rdoc: true
88
+ homepage: http://www.github.com/djberg96/oracle-model-generator
89
+ licenses:
90
+ - Artistic 2.0
91
+ post_install_message:
92
+ rdoc_options: []
93
+
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ requirements: []
115
+
116
+ rubyforge_project: N/A
117
+ rubygems_version: 1.3.7
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: A Ruby interface for determining protocol information
121
+ test_files:
122
+ - test/test_oracle_model_generator.rb