oracle-model-generator 0.4.1 → 0.5.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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/{CHANGES → CHANGES.md} +14 -8
- data/DOCKER.md +152 -0
- data/Dockerfile +87 -0
- data/Gemfile +2 -0
- data/LICENSE +177 -0
- data/{MANIFEST → MANIFEST.md} +3 -3
- data/README-Docker.md +140 -0
- data/README.md +169 -0
- data/Rakefile +14 -10
- data/bin/omg +0 -0
- data/certs/djberg96_pub.pem +22 -17
- data/docker-compose.yml +41 -0
- data/lib/oracle/model/generator.rb +1 -1
- data/oracle-model-generator.gemspec +13 -14
- data/spec/oracle_model_generator_spec.rb +176 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/oracle_connection.rb +126 -0
- data.tar.gz.sig +0 -0
- metadata +60 -55
- metadata.gz.sig +0 -0
- data/README +0 -150
- data/test/test_oracle_model_generator.rb +0 -68
data/README.md
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
# MAINTAINER WANTED
|
2
|
+
|
3
|
+
As of 2019 I haven't used Oracle or this library for many years. I would like
|
4
|
+
to turn it over to someone who is. If you are interested please contact me,
|
5
|
+
and we can discuss transferring the repository.
|
6
|
+
|
7
|
+
## Description
|
8
|
+
A library for generating an ActiveRecord model from an existing Oracle table.
|
9
|
+
This will install an "omg" executable that you can use from the command line.
|
10
|
+
|
11
|
+
## Synopsis
|
12
|
+
Using the command line tool:
|
13
|
+
|
14
|
+
`omg -d your_database -t locations -u some_user -p some_password`
|
15
|
+
|
16
|
+
The above command results in a file called "location.rb". This is an
|
17
|
+
ActiveRecord model declaration, with all validations, primary keys,
|
18
|
+
table name and belongs_to relationships defined.
|
19
|
+
|
20
|
+
If your LOCATIONS table looks like this:
|
21
|
+
|
22
|
+
```sql
|
23
|
+
create table locations(
|
24
|
+
location_id number(4,0) primary key,
|
25
|
+
street_address varchar2(40),
|
26
|
+
postal_code varchar2(12),
|
27
|
+
city varchar2(30) not null
|
28
|
+
state_province varchar2(25),
|
29
|
+
country_id CHAR(2),
|
30
|
+
constraint "LOC_C_ID_FK" FOREIGN KEY (country_id)
|
31
|
+
references COUNTRIES (country_id)
|
32
|
+
)
|
33
|
+
```
|
34
|
+
|
35
|
+
The omg library will generate this:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class Location < ActiveRecord::Base
|
39
|
+
set_table_name :locations
|
40
|
+
set_primary_key :location_id
|
41
|
+
|
42
|
+
# Table relationships
|
43
|
+
|
44
|
+
belongs_to :countries
|
45
|
+
|
46
|
+
# Validations
|
47
|
+
|
48
|
+
validates :location_id, :presence => true, :numericality => {
|
49
|
+
:less_than_or_equal_to => 9999,
|
50
|
+
:greater_than_or_equal_to => -9999,
|
51
|
+
:only_integer => true
|
52
|
+
}
|
53
|
+
|
54
|
+
validates :street_address, :length => {:maximum => 40}
|
55
|
+
validates :postal_code, :length => {:maximum => 12}
|
56
|
+
validates :city, :length => {:maximum => 30}, :presence => true
|
57
|
+
validates :state_province, :length => {:maximum => 25}
|
58
|
+
validates :country_id, :length => {:maximum => 2}
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
It will also generate a corresponding test file using test-unit 2 by default.
|
63
|
+
For the above example you will see some tests like this:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class TC_Location < Test::Unit::TestCase
|
67
|
+
def setup
|
68
|
+
@location = Location.new
|
69
|
+
end
|
70
|
+
|
71
|
+
test 'table name is locations' do
|
72
|
+
assert_equal('locations', Location.table_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
test 'primary key is location_id' do
|
76
|
+
assert_equal('location_id', Location.primary_key)
|
77
|
+
end
|
78
|
+
|
79
|
+
test 'location_id basic functionality' do
|
80
|
+
assert_respond_to(@location, :location_id)
|
81
|
+
assert_nothing_raised{ @location.location_id }
|
82
|
+
assert_kind_of(Numeric, @location.location_id)
|
83
|
+
end
|
84
|
+
|
85
|
+
test 'location_id must be a number' do
|
86
|
+
@location.location_id = 'test_string'
|
87
|
+
assert_false(@location.valid?)
|
88
|
+
assert_true(@location.errors[:location_id].include?('is not a number'))
|
89
|
+
end
|
90
|
+
|
91
|
+
test 'location_id cannot exceed the value 9999' do
|
92
|
+
@location.location_id = 10000
|
93
|
+
assert_false(@location.valid?)
|
94
|
+
assert_true(@location.errors[:location_id].include?('must be less than or equal to 9999'))
|
95
|
+
end
|
96
|
+
|
97
|
+
# ... and so on.
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
## Requirements
|
102
|
+
* ruby-oci8
|
103
|
+
* getopt
|
104
|
+
|
105
|
+
## Running the specs
|
106
|
+
Run `docker-compose run --rm oracle-model-generator bundle exec rspec`.
|
107
|
+
|
108
|
+
You may need to use sudo. No guarantees on MacOS because of known issues
|
109
|
+
with the Oracle instant client.
|
110
|
+
|
111
|
+
## Optional Libraries
|
112
|
+
If you want to be able to avoid specifying a username and password on the
|
113
|
+
command line then you will need the `dbi-dbrc` library.
|
114
|
+
|
115
|
+
If you want your models to support multiple primary keys, then you will
|
116
|
+
need to install the `composite_primary_keys` library.
|
117
|
+
|
118
|
+
If you want date format validations, then you will need to install the
|
119
|
+
`validates_timeliness` library.
|
120
|
+
|
121
|
+
## What this library doesn't do
|
122
|
+
I do not attempt to set `has_many` or `has_one` relationships. There's no good
|
123
|
+
way to determine that relationship (one or many?). Besides, in practice I
|
124
|
+
find that most people set custom has_xxx relationships that go over and
|
125
|
+
above what's set in the Oracle database anyway for purposes of their
|
126
|
+
application.
|
127
|
+
|
128
|
+
I also do not go out of my way to get the model name correct with regards
|
129
|
+
to singular vs plural. I do a simple guess that covers most cases, but
|
130
|
+
complex cases will break it. It's much easier for you to rename a class or
|
131
|
+
file name than it is for me to get this 100% correct.
|
132
|
+
|
133
|
+
As of 0.3.1 there's also the `--class` option that let's you explicitly
|
134
|
+
set it if you like.
|
135
|
+
|
136
|
+
## Author's Comments
|
137
|
+
I chose not to patch the `legacy_data` library because I have no interest in
|
138
|
+
supporting other vendors other than Oracle with this library. By focusing only
|
139
|
+
on Oracle I could take advantage of ruby-oci8 features. In addition, I have no
|
140
|
+
interest in making this a Rails plugin, and I needed the support of multiple
|
141
|
+
primary keys.
|
142
|
+
|
143
|
+
## Future Plans (originally)
|
144
|
+
* Add support for views.
|
145
|
+
* Add automatic test suite generation for rspec.
|
146
|
+
* Explicitly set :foreign_key if using CPK in belongs_to relationships.
|
147
|
+
* The output could use a little formatting love.
|
148
|
+
|
149
|
+
## Acknowlegements
|
150
|
+
Thanks go to Daniel Luna for his --class patch.
|
151
|
+
|
152
|
+
## Known Issues
|
153
|
+
None known. If you find any issues, please report them on the github project
|
154
|
+
page at http://www.github.com/djberg96/oracle-model-generator.
|
155
|
+
|
156
|
+
## Warranty
|
157
|
+
This package is provided "as is" and without any express or
|
158
|
+
implied warranties, including, without limitation, the implied
|
159
|
+
warranties of merchantability and fitness for a particular purpose.
|
160
|
+
|
161
|
+
## Copyright
|
162
|
+
(C) 2010-2025 Daniel J. Berger
|
163
|
+
All Rights Reserved
|
164
|
+
|
165
|
+
## License
|
166
|
+
Apache-2.0
|
167
|
+
|
168
|
+
## Author
|
169
|
+
Daniel J. Berger
|
data/Rakefile
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
require 'rake'
|
2
|
-
require 'rake/testtask'
|
3
2
|
require 'rake/clean'
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
|
5
|
-
CLEAN.include("**/*.gem", "**/*.rbc", "**/*.log")
|
5
|
+
CLEAN.include("**/*.gem", "**/*.rbc", "**/*.log", "**/*.lock")
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.pattern = 'spec/**/*_spec.rb'
|
9
|
+
t.rspec_opts = ['--format', 'documentation', '--color']
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Run RSpec tests'
|
13
|
+
task :rspec => :spec
|
6
14
|
|
7
15
|
namespace 'gem' do
|
8
16
|
desc 'Create the oracle-model-generator gem'
|
9
17
|
task :create => :clean do
|
10
18
|
require 'rubygems/package'
|
11
|
-
spec =
|
19
|
+
spec = Gem::Specification.load('oracle-model-generator.gemspec')
|
12
20
|
spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
|
13
|
-
Gem::Package.build(spec
|
21
|
+
Gem::Package.build(spec)
|
14
22
|
end
|
15
23
|
|
16
24
|
desc 'Install the oracle-model-generator gem'
|
@@ -20,9 +28,5 @@ namespace 'gem' do
|
|
20
28
|
end
|
21
29
|
end
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
t.verbose = true
|
26
|
-
end
|
27
|
-
|
28
|
-
task :default => :test
|
31
|
+
# Set default task to run both test suites
|
32
|
+
task :default => [:spec]
|
data/bin/omg
CHANGED
File without changes
|
data/certs/djberg96_pub.pem
CHANGED
@@ -1,21 +1,26 @@
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
2
|
-
|
2
|
+
MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MREwDwYDVQQDDAhkamJl
|
3
3
|
cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
|
4
|
-
|
4
|
+
MB4XDTE4MDMxODE1MjIwN1oXDTI4MDMxNTE1MjIwN1owPzERMA8GA1UEAwwIZGpi
|
5
5
|
ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
6
|
+
bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALgfaroVM6CI06cxr0/h
|
7
|
+
A+j+pc8fgpRgBVmHFaFunq28GPC3IvW7Nvc3Y8SnAW7pP1EQIbhlwRIaQzJ93/yj
|
8
|
+
u95KpkP7tA9erypnV7dpzBkzNlX14ACaFD/6pHoXoe2ltBxk3CCyyzx70mTqJpph
|
9
|
+
75IB03ni9a8yqn8pmse+s83bFJOAqddSj009sGPcQO+QOWiNxqYv1n5EHcvj2ebO
|
10
|
+
6hN7YTmhx7aSia4qL/quc4DlIaGMWoAhvML7u1fmo53CYxkKskfN8MOecq2vfEmL
|
11
|
+
iLu+SsVVEAufMDDFMXMJlvDsviolUSGMSNRTujkyCcJoXKYYxZSNtIiyd9etI0X3
|
12
|
+
ctu0uhrFyrMZXCedutvXNjUolD5r9KGBFSWH1R9u2I3n3SAyFF2yzv/7idQHLJJq
|
13
|
+
74BMnx0FIq6fCpu5slAipvxZ3ZkZpEXZFr3cIBtO1gFvQWW7E/Y3ijliWJS1GQFq
|
14
|
+
058qERadHGu1yu1dojmFRo6W2KZvY9al2yIlbkpDrD5MYQIDAQABo3cwdTAJBgNV
|
15
|
+
HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUFZsMapgzJimzsbaBG2Tm8j5e
|
16
|
+
AzgwHQYDVR0RBBYwFIESZGpiZXJnOTZAZ21haWwuY29tMB0GA1UdEgQWMBSBEmRq
|
17
|
+
YmVyZzk2QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAW2tnYixXQtKxgGXq
|
18
|
+
/3iSWG2bLwvxS4go3srO+aRXZHrFUMlJ5W0mCxl03aazxxKTsVVpZD8QZxvK91OQ
|
19
|
+
h9zr9JBYqCLcCVbr8SkmYCi/laxIZxsNE5YI8cC8vvlLI7AMgSfPSnn/Epq1GjGY
|
20
|
+
6L1iRcEDtanGCIvjqlCXO9+BmsnCfEVehqZkQHeYczA03tpOWb6pon2wzvMKSsKH
|
21
|
+
ks0ApVdstSLz1kzzAqem/uHdG9FyXdbTAwH1G4ZPv69sQAFAOCgAqYmdnzedsQtE
|
22
|
+
1LQfaQrx0twO+CZJPcRLEESjq8ScQxWRRkfuh2VeR7cEU7L7KqT10mtUwrvw7APf
|
23
|
+
DYoeCY9KyjIBjQXfbj2ke5u1hZj94Fsq9FfbEQg8ygCgwThnmkTrrKEiMSs3alYR
|
24
|
+
ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
|
25
|
+
WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
|
21
26
|
-----END CERTIFICATE-----
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
services:
|
2
|
+
# Oracle Express Edition database for testing
|
3
|
+
oracle-db:
|
4
|
+
image: gvenzl/oracle-xe:21-slim
|
5
|
+
environment:
|
6
|
+
- ORACLE_PASSWORD=oracle
|
7
|
+
- APP_USER=hr
|
8
|
+
- APP_USER_PASSWORD=hr
|
9
|
+
ports:
|
10
|
+
- "1521:1521"
|
11
|
+
volumes:
|
12
|
+
- oracle_data:/opt/oracle/oradata
|
13
|
+
shm_size: 1g
|
14
|
+
healthcheck:
|
15
|
+
test: ["CMD", "healthcheck.sh"]
|
16
|
+
interval: 30s
|
17
|
+
timeout: 10s
|
18
|
+
retries: 5
|
19
|
+
start_period: 60s
|
20
|
+
|
21
|
+
# Oracle Model Generator testing environment
|
22
|
+
oracle-model-generator:
|
23
|
+
build: .
|
24
|
+
depends_on:
|
25
|
+
oracle-db:
|
26
|
+
condition: service_healthy
|
27
|
+
environment:
|
28
|
+
- ORACLE_HOST=oracle-db
|
29
|
+
- ORACLE_PORT=1521
|
30
|
+
- ORACLE_SID=freepdb1
|
31
|
+
- ORACLE_USER=hr
|
32
|
+
- ORACLE_PASSWORD=hr
|
33
|
+
volumes:
|
34
|
+
- .:/app
|
35
|
+
working_dir: /app
|
36
|
+
stdin_open: true
|
37
|
+
tty: true
|
38
|
+
command: ["rake"] # This will run tests with automatic setup
|
39
|
+
|
40
|
+
volumes:
|
41
|
+
oracle_data:
|
@@ -1,23 +1,22 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name
|
5
|
-
spec.version
|
6
|
-
spec.author
|
7
|
-
spec.license
|
8
|
-
spec.email
|
9
|
-
spec.homepage
|
10
|
-
spec.summary
|
11
|
-
spec.
|
12
|
-
spec.files
|
13
|
-
spec.cert_chain
|
4
|
+
spec.name = 'oracle-model-generator'
|
5
|
+
spec.version = '0.5.0'
|
6
|
+
spec.author = 'Daniel J. Berger'
|
7
|
+
spec.license = 'Apache-2.0'
|
8
|
+
spec.email = 'djberg96@gmail.com'
|
9
|
+
spec.homepage = 'http://www.github.com/djberg96/oracle-model-generator'
|
10
|
+
spec.summary = 'A Ruby interface for determining protocol information'
|
11
|
+
spec.test_files = Dir['spec/**/*.rb']
|
12
|
+
spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
13
|
+
spec.cert_chain = spec.cert_chain = ['certs/djberg96_pub.pem']
|
14
14
|
|
15
15
|
spec.executables = "omg"
|
16
|
-
spec.extra_rdoc_files = %w[CHANGES README MANIFEST]
|
17
16
|
|
18
|
-
spec.add_dependency('ruby-oci8')
|
19
|
-
spec.add_dependency('getopt')
|
20
|
-
spec.add_development_dependency('
|
17
|
+
spec.add_dependency('ruby-oci8', '~> 2.2')
|
18
|
+
spec.add_dependency('getopt', '~> 1.6')
|
19
|
+
spec.add_development_dependency('rspec', '~> 3.12')
|
21
20
|
|
22
21
|
spec.description = <<-EOF
|
23
22
|
The oracle-model-generator library allows you to generate an ActiveRecord
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/oracle_connection'
|
3
|
+
require 'oracle/model/generator'
|
4
|
+
|
5
|
+
RSpec.describe Oracle::Model::Generator do
|
6
|
+
include_context 'Oracle connection'
|
7
|
+
|
8
|
+
let(:generator) { described_class.new(connection) }
|
9
|
+
|
10
|
+
describe 'class information' do
|
11
|
+
it 'has the correct version number' do
|
12
|
+
expect(Oracle::Model::Generator::VERSION).to eq('0.5.0')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#initialize' do
|
17
|
+
it 'accepts an OCI8 connection object' do
|
18
|
+
expect { described_class.new(connection) }.not_to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'sets up instance variables correctly' do
|
22
|
+
expect(generator.connection).to eq(connection)
|
23
|
+
expect(generator.constraints).to eq([])
|
24
|
+
expect(generator.primary_keys).to eq([])
|
25
|
+
expect(generator.foreign_keys).to eq([])
|
26
|
+
expect(generator.dependencies).to eq([])
|
27
|
+
expect(generator.belongs_to).to eq([])
|
28
|
+
expect(generator.column_info).to eq([])
|
29
|
+
expect(generator.table).to be_nil
|
30
|
+
expect(generator.model).to be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#generate' do
|
35
|
+
it 'responds to the generate method' do
|
36
|
+
expect(generator).to respond_to(:generate)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'works with a table name' do
|
40
|
+
expect { generator.generate('employees') }.not_to raise_error
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'works with a view name' do
|
44
|
+
expect { generator.generate('emp_details_view', true) }.not_to raise_error
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when generating from a table' do
|
48
|
+
before { generator.generate('employees') }
|
49
|
+
|
50
|
+
it 'sets the table name in uppercase' do
|
51
|
+
expect(generator.table).to eq('EMPLOYEES')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'generates a model name from the table name' do
|
55
|
+
expect(generator.model).to eq('Employee')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'sets view to false' do
|
59
|
+
expect(generator.view).to be false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'populates column information' do
|
63
|
+
expect(generator.column_info).to be_an(Array)
|
64
|
+
expect(generator.column_info).not_to be_empty
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'finds primary keys' do
|
68
|
+
expect(generator.primary_keys).to be_an(Array)
|
69
|
+
expect(generator.primary_keys).to include('employee_id')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'populates constraints information' do
|
73
|
+
expect(generator.constraints).to be_an(Array)
|
74
|
+
expect(generator.constraints).not_to be_empty
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when generating from a view' do
|
79
|
+
before { generator.generate('emp_details_view', true) }
|
80
|
+
|
81
|
+
it 'sets the table name in uppercase' do
|
82
|
+
expect(generator.table).to eq('EMP_DETAILS_VIEW')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'generates the correct model name' do
|
86
|
+
expect(generator.model).to eq('EmpDetailsView')
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'sets view to true' do
|
90
|
+
expect(generator.view).to be true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#model' do
|
96
|
+
it 'returns the active record model name' do
|
97
|
+
generator.generate('emp_details_view', true)
|
98
|
+
expect(generator).to respond_to(:model)
|
99
|
+
expect(generator.model).to eq('EmpDetailsView')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'removes trailing s from model names' do
|
103
|
+
generator.generate('employees')
|
104
|
+
expect(generator.model).to eq('Employee')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#table' do
|
109
|
+
it 'returns the uppercased table name' do
|
110
|
+
generator.generate('emp_details_view', true)
|
111
|
+
expect(generator).to respond_to(:table)
|
112
|
+
expect(generator.table).to eq('EMP_DETAILS_VIEW')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#primary_keys' do
|
117
|
+
it 'returns an array of primary keys' do
|
118
|
+
generator.generate('employees')
|
119
|
+
expect(generator.primary_keys).to be_an(Array)
|
120
|
+
expect(generator.primary_keys).to include('employee_id')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#column_info' do
|
125
|
+
it 'returns an array of column metadata' do
|
126
|
+
generator.generate('employees')
|
127
|
+
expect(generator.column_info).to be_an(Array)
|
128
|
+
expect(generator.column_info.length).to be > 0
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#constraints' do
|
133
|
+
it 'returns an array of constraint information' do
|
134
|
+
generator.generate('employees')
|
135
|
+
expect(generator.constraints).to be_an(Array)
|
136
|
+
expect(generator.constraints).not_to be_empty
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#foreign_keys' do
|
141
|
+
it 'returns an array of foreign key names' do
|
142
|
+
generator.generate('employees')
|
143
|
+
expect(generator.foreign_keys).to be_an(Array)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#belongs_to' do
|
148
|
+
it 'returns an array of parent tables' do
|
149
|
+
generator.generate('employees')
|
150
|
+
expect(generator.belongs_to).to be_an(Array)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#dependencies' do
|
155
|
+
it 'returns an array of dependent objects' do
|
156
|
+
generator.generate('employees', true)
|
157
|
+
expect(generator).to respond_to(:dependencies)
|
158
|
+
expect(generator.dependencies).to be_an(Array)
|
159
|
+
|
160
|
+
# Only check for Hash if there are dependencies
|
161
|
+
if generator.dependencies.any?
|
162
|
+
expect(generator.dependencies.first).to be_a(Hash)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#view' do
|
168
|
+
it 'correctly identifies tables vs views' do
|
169
|
+
generator.generate('employees')
|
170
|
+
expect(generator.view).to be false
|
171
|
+
|
172
|
+
generator.generate('emp_details_view', true)
|
173
|
+
expect(generator.view).to be true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Load the library we're testing
|
2
|
+
require_relative '../lib/oracle-model-generator'
|
3
|
+
|
4
|
+
# RSpec configuration for Oracle Model Generator
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# Use the expect() syntax rather than should syntax
|
7
|
+
config.expect_with :rspec do |expectations|
|
8
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
9
|
+
end
|
10
|
+
|
11
|
+
# Use the new mock framework syntax
|
12
|
+
config.mock_with :rspec do |mocks|
|
13
|
+
mocks.verify_partial_doubles = true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Enable color output
|
17
|
+
config.color = true
|
18
|
+
|
19
|
+
# Use documentation format for output
|
20
|
+
config.formatter = :documentation
|
21
|
+
|
22
|
+
# Run specs in random order to surface order dependencies
|
23
|
+
config.order = :random
|
24
|
+
|
25
|
+
# Allow filtering tests by tags
|
26
|
+
config.filter_run_when_matching :focus
|
27
|
+
|
28
|
+
# Shared context for setting up test data
|
29
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
30
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'oci8'
|
2
|
+
|
3
|
+
# Shared context for Oracle database connection and test data
|
4
|
+
RSpec.shared_context 'Oracle connection' do
|
5
|
+
before(:context) do
|
6
|
+
@oracle_connection = begin
|
7
|
+
# In Docker environment, use the service name instead of localhost
|
8
|
+
host = ENV['ORACLE_HOST'] || 'localhost'
|
9
|
+
port = ENV['ORACLE_PORT'] || '1521'
|
10
|
+
sid = ENV['ORACLE_SID'] || 'freepdb1'
|
11
|
+
database_str = "#{host}:#{port}/#{sid}"
|
12
|
+
|
13
|
+
OCI8.new(
|
14
|
+
ENV['ORACLE_USER'] || 'hr',
|
15
|
+
ENV['ORACLE_PASSWORD'] || 'oracle',
|
16
|
+
database_str
|
17
|
+
)
|
18
|
+
rescue OCIError => e
|
19
|
+
warn "Database connection failed: #{e.message}"
|
20
|
+
warn "Make sure Oracle database is running and accessible"
|
21
|
+
warn "Connection string: #{ENV['ORACLE_HOST'] || 'localhost'}:#{ENV['ORACLE_PORT'] || '1521'}/#{ENV['ORACLE_SID'] || 'freepdb1'}"
|
22
|
+
warn "User: #{ENV['ORACLE_USER'] || 'hr'}"
|
23
|
+
raise e
|
24
|
+
end
|
25
|
+
setup_test_data
|
26
|
+
end
|
27
|
+
|
28
|
+
after(:context) do
|
29
|
+
# Cleanup test tables and views
|
30
|
+
cleanup_test_data
|
31
|
+
|
32
|
+
# Close the connection
|
33
|
+
@oracle_connection&.logoff
|
34
|
+
end
|
35
|
+
|
36
|
+
# Use a method instead of let for context-level variables
|
37
|
+
def connection
|
38
|
+
@oracle_connection
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def setup_test_data
|
44
|
+
# Create test tables and views if they don't exist
|
45
|
+
setup_sample_data
|
46
|
+
end
|
47
|
+
|
48
|
+
def cleanup_test_data
|
49
|
+
# Drop test views first (to avoid dependency issues)
|
50
|
+
begin
|
51
|
+
@oracle_connection.exec('DROP VIEW hr.employees_by_department') rescue nil
|
52
|
+
rescue => e
|
53
|
+
# Ignore errors if views don't exist
|
54
|
+
end
|
55
|
+
|
56
|
+
# Note: We don't drop HR schema tables as they might be used by other tests
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup_sample_data
|
60
|
+
# Check if employees table exists and has data
|
61
|
+
begin
|
62
|
+
cursor = @oracle_connection.parse('SELECT COUNT(*) FROM employees')
|
63
|
+
cursor.exec
|
64
|
+
count = cursor.fetch[0]
|
65
|
+
cursor.close
|
66
|
+
|
67
|
+
if count == 0
|
68
|
+
# Insert sample data into employees table
|
69
|
+
@oracle_connection.exec(<<~SQL)
|
70
|
+
INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,
|
71
|
+
hire_date, job_id, salary, commission_pct, manager_id, department_id)
|
72
|
+
VALUES (1, 'John', 'Doe', 'john.doe@example.com', '555-1234',
|
73
|
+
DATE '2020-01-15', 'IT_PROG', 5000, NULL, NULL, 60)
|
74
|
+
SQL
|
75
|
+
|
76
|
+
@oracle_connection.exec(<<~SQL)
|
77
|
+
INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,
|
78
|
+
hire_date, job_id, salary, commission_pct, manager_id, department_id)
|
79
|
+
VALUES (2, 'Jane', 'Smith', 'jane.smith@example.com', '555-5678',
|
80
|
+
DATE '2021-03-10', 'IT_PROG', 5500, NULL, 1, 60)
|
81
|
+
SQL
|
82
|
+
|
83
|
+
@oracle_connection.commit
|
84
|
+
end
|
85
|
+
|
86
|
+
# Check if departments table exists and has data
|
87
|
+
cursor = @oracle_connection.parse('SELECT COUNT(*) FROM departments')
|
88
|
+
cursor.exec
|
89
|
+
dept_count = cursor.fetch[0]
|
90
|
+
cursor.close
|
91
|
+
|
92
|
+
if dept_count == 0
|
93
|
+
# Insert sample data into departments table
|
94
|
+
@oracle_connection.exec(<<~SQL)
|
95
|
+
INSERT INTO departments (department_id, department_name, manager_id, location_id)
|
96
|
+
VALUES (60, 'IT', 1, 1700)
|
97
|
+
SQL
|
98
|
+
|
99
|
+
@oracle_connection.exec(<<~SQL)
|
100
|
+
INSERT INTO departments (department_id, department_name, manager_id, location_id)
|
101
|
+
VALUES (50, 'Shipping', NULL, 1500)
|
102
|
+
SQL
|
103
|
+
|
104
|
+
@oracle_connection.commit
|
105
|
+
end
|
106
|
+
|
107
|
+
# Create a test view if it doesn't exist
|
108
|
+
begin
|
109
|
+
@oracle_connection.exec(<<~SQL)
|
110
|
+
CREATE OR REPLACE VIEW hr.employees_by_department AS
|
111
|
+
SELECT e.employee_id, e.first_name, e.last_name, e.email,
|
112
|
+
d.department_name, d.department_id
|
113
|
+
FROM employees e
|
114
|
+
JOIN departments d ON e.department_id = d.department_id
|
115
|
+
SQL
|
116
|
+
rescue => e
|
117
|
+
# View creation might fail if tables don't exist yet, that's okay
|
118
|
+
warn "Could not create test view: #{e.message}"
|
119
|
+
end
|
120
|
+
|
121
|
+
rescue => e
|
122
|
+
warn "Could not set up sample data: #{e.message}"
|
123
|
+
# Continue with tests even if sample data setup fails
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data.tar.gz.sig
CHANGED
Binary file
|