dbcompile 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.
- data/Gemfile +12 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +46 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/example/compile.yml +16 -0
- data/lib/dbcompile/construct.rb +59 -0
- data/lib/dbcompile/function.rb +16 -0
- data/lib/dbcompile/transaction.rb +83 -0
- data/lib/dbcompile/trigger.rb +16 -0
- data/lib/dbcompile/view.rb +31 -0
- data/lib/dbcompile.rb +14 -0
- data/lib/tasks/dbcompile.rake +7 -0
- metadata +98 -0
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rdoc", "~> 3.12"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.8.4"
|
12
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Tyler Lesmann
|
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.
|
data/README.markdown
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
DbCompile
|
2
|
+
---------
|
3
|
+
|
4
|
+
Adds *rake db:compile* to your rails project tasks. This task can be used to
|
5
|
+
compile views, functions, triggers, and more!
|
6
|
+
|
7
|
+
### Install
|
8
|
+
|
9
|
+
script/plugin install git://github.com/redhatcat/dbcompile.git
|
10
|
+
|
11
|
+
### Setup
|
12
|
+
|
13
|
+
Configuration is contained in one file, **RAILS_ROOT/db/compile.yml**.
|
14
|
+
In here, you describe the statement and dependencies. You can fine an example
|
15
|
+
with comments [here](http://github.com/redhatcat/dbcompile/blob/master/example/compile.yml).
|
16
|
+
|
17
|
+
The SQL scripts themselves are places in several directories under **RAILS_ROOT/db**.
|
18
|
+
|
19
|
+
* RAILS_ROOT/db/functions contains function scripts.
|
20
|
+
* RAILS_ROOT/db/triggers contains trigger scripts.
|
21
|
+
* RAILS_ROOT/db/views contains view scripts.
|
22
|
+
|
23
|
+
All of these scripts, with the exception of views, should be self replacing,
|
24
|
+
e.g. you will want to use statements like *CREATE OR REPLACE FUNCTION*.
|
25
|
+
|
26
|
+
**Note on views**: View scripts should be written as everything after the
|
27
|
+
*AS* keyword, i.e. write views as SELECTs. DbCompile will magically wrap
|
28
|
+
the SELECT with CREATE OR REPLACE VIEW #{script_name} AS #{sql_source}.
|
29
|
+
|
30
|
+
### Adding new SQL constructs
|
31
|
+
|
32
|
+
If you want something beyond a function, trigger, or view, DbCompile is easy to
|
33
|
+
extend. Simply create a new class that extends *DbCompile::Construct* and
|
34
|
+
require it in *lib/dbcompile/transaction.rb*. See the source of
|
35
|
+
[lib/dbcompile/view.rb](http://github.com/redhatcat/dbcompile/blob/master/lib/dbcompile/view.rb)
|
36
|
+
and
|
37
|
+
[lib/dbcompile/construct.rb](http://github.com/redhatcat/dbcompile/blob/master/lib/dbcompile/construct.rb)
|
38
|
+
for reference.
|
39
|
+
|
40
|
+
### How the dependencies work
|
41
|
+
|
42
|
+
The dependencies of a contruct determine the order of (re)creation. The
|
43
|
+
consequences of not stating dependencies in the case of a view:
|
44
|
+
|
45
|
+
* A view may fail being created entirely.
|
46
|
+
* A view may be created, then deleted by DROP CASCADE of another view it depends on.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "dbcompile"
|
18
|
+
gem.homepage = "http://github.com/redhatcat/dbcompile"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Rails plugin for loading various SQL constructs, like views, functions, and triggers}
|
21
|
+
gem.description = %Q{Rails plugin for loading various SQL constructs, like views, functions, and triggers}
|
22
|
+
gem.email = "redhatcat@gmail.com"
|
23
|
+
gem.authors = ["Tyler Lesmann"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rdoc/task'
|
29
|
+
Rake::RDocTask.new do |rdoc|
|
30
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
31
|
+
|
32
|
+
rdoc.rdoc_dir = 'rdoc'
|
33
|
+
rdoc.title = "dbcompile #{version}"
|
34
|
+
rdoc.rdoc_files.include('README*')
|
35
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
36
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/example/compile.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
views: # Statement type
|
2
|
+
asset_dashboard: # Script name.
|
3
|
+
# This will be translated into a filename. Here it is db/views/asset_dashboard.sql.
|
4
|
+
# On views, the script name will also be the name of the view.
|
5
|
+
# This view has no dependencies.
|
6
|
+
functions:
|
7
|
+
asset_immutable:
|
8
|
+
check_asset_integrity:
|
9
|
+
functions: # This function depends on the existence of the following functions:
|
10
|
+
- asset_immutable
|
11
|
+
views: # This function depends on the existence of the following views:
|
12
|
+
- asset_dashboard
|
13
|
+
triggers:
|
14
|
+
check_asset_integrity:
|
15
|
+
functions:
|
16
|
+
- check_asset_integrity
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module DbCompile
|
2
|
+
# Anything that can be expressed in SQL
|
3
|
+
class Construct
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :path
|
6
|
+
attr_accessor :root_path
|
7
|
+
attr_accessor :dependencies
|
8
|
+
|
9
|
+
def initialize(name, root_path)
|
10
|
+
@name = name
|
11
|
+
@root_path = root_path
|
12
|
+
build_path
|
13
|
+
end
|
14
|
+
|
15
|
+
# Override this to set path the SQL source
|
16
|
+
def build_path
|
17
|
+
end
|
18
|
+
|
19
|
+
# Execute the source to create contruct in database
|
20
|
+
def execute
|
21
|
+
ActiveRecord::Base.connection.execute(source)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return the SQL source. Do any magic wrapping here.
|
25
|
+
def source
|
26
|
+
f = File.open(File.join(root_path, path))
|
27
|
+
data = f.read
|
28
|
+
f.close
|
29
|
+
data
|
30
|
+
end
|
31
|
+
|
32
|
+
# Override to verify the existence of the construct
|
33
|
+
# Return true for verified successs
|
34
|
+
# Return false for verified failure
|
35
|
+
# Return nil otherwise
|
36
|
+
def verify
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# checks to see if one object exists based on the sql string
|
41
|
+
# generated by an individual construct
|
42
|
+
#
|
43
|
+
def does_one_exist?(sql)
|
44
|
+
result = ActiveRecord::Base.connection.execute(sql)
|
45
|
+
case ActiveRecord::Base.connection.class.to_s
|
46
|
+
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
47
|
+
return result.num_tuples == 1
|
48
|
+
when "ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter"
|
49
|
+
row_count= 0
|
50
|
+
while f = result.fetch
|
51
|
+
row_count += 1
|
52
|
+
end
|
53
|
+
return row_count == 1
|
54
|
+
else
|
55
|
+
return result.length == 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DbCompile
|
2
|
+
class Function < Construct
|
3
|
+
def build_path
|
4
|
+
@path = File.join('functions', "#{name}.sql")
|
5
|
+
end
|
6
|
+
|
7
|
+
def verify
|
8
|
+
sql = nil
|
9
|
+
case ActiveRecord::Base.connection.class.to_s
|
10
|
+
when 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
|
11
|
+
sql = "SELECT proname FROM pg_proc WHERE proname = '#{name.downcase}';"
|
12
|
+
end
|
13
|
+
return does_one_exist?(sql) if sql
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'dbcompile/construct.rb'
|
2
|
+
require 'dbcompile/function.rb'
|
3
|
+
require 'dbcompile/trigger.rb'
|
4
|
+
require 'dbcompile/view.rb'
|
5
|
+
|
6
|
+
module DbCompile
|
7
|
+
class CircularDependenciesException < Exception; end
|
8
|
+
|
9
|
+
# Encapsulates entire transaction and dependency checking
|
10
|
+
class Transaction
|
11
|
+
def initialize(path)
|
12
|
+
@path = path
|
13
|
+
@run_queue = []
|
14
|
+
@deps_queue = []
|
15
|
+
@manifest = YAML::load_file(File.join(path, 'compile.yml'))
|
16
|
+
|
17
|
+
@manifest.each{ |construct_name, data|
|
18
|
+
if data
|
19
|
+
data.each{ |object_name, dependencies|
|
20
|
+
install_dependencies(construct_name, object_name)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def install_dependencies(construct_name, object_name)
|
27
|
+
if @deps_queue.include? [construct_name, object_name]
|
28
|
+
raise CircularDependenciesException.new(
|
29
|
+
"Dependency of #{construct_name} #{object_name} referenced it as a dependency!")
|
30
|
+
end
|
31
|
+
@deps_queue << [construct_name, object_name]
|
32
|
+
if not @run_queue.include? [construct_name, object_name]
|
33
|
+
dependencies = @manifest[construct_name][object_name]
|
34
|
+
if dependencies
|
35
|
+
dependencies.each{ |dep_construct_name, name_list|
|
36
|
+
name_list.each{ |dep_object_name|
|
37
|
+
install_dependencies(dep_construct_name, dep_object_name)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
@run_queue << [construct_name, object_name]
|
42
|
+
end
|
43
|
+
@deps_queue.pop
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_contruct(construct_name, object_name)
|
47
|
+
klass = "DbCompile::#{construct_name.singularize.camelize}".constantize
|
48
|
+
klass.new(object_name, @path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute
|
52
|
+
@run_queue.each{ |construct_name, object_name|
|
53
|
+
construct = build_contruct(construct_name, object_name)
|
54
|
+
msg = "Compiling #{construct.path}"
|
55
|
+
puts msg
|
56
|
+
ActiveRecord::Base.logger.info msg
|
57
|
+
construct.execute
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def verify
|
62
|
+
msg = "Verifying compilation"
|
63
|
+
puts msg
|
64
|
+
ActiveRecord::Base.logger.info msg
|
65
|
+
no_errors = true
|
66
|
+
@run_queue.each{ |construct_name, object_name|
|
67
|
+
construct = build_contruct(construct_name, object_name)
|
68
|
+
case construct.verify
|
69
|
+
when nil
|
70
|
+
msg = "#{construct_name.capitalize} #{object_name} could not be verified."
|
71
|
+
when true
|
72
|
+
msg = "#{construct_name.capitalize} #{object_name} successfully created."
|
73
|
+
when false
|
74
|
+
msg = "#{construct_name.capitalize} #{object_name} creation failed."
|
75
|
+
no_errors = false
|
76
|
+
end
|
77
|
+
puts msg
|
78
|
+
ActiveRecord::Base.logger.info msg
|
79
|
+
}
|
80
|
+
return no_errors
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DbCompile
|
2
|
+
class Trigger < Construct
|
3
|
+
def build_path
|
4
|
+
@path = File.join('triggers', "#{name}.sql")
|
5
|
+
end
|
6
|
+
|
7
|
+
def verify
|
8
|
+
sql = nil
|
9
|
+
case ActiveRecord::Base.connection.class.to_s
|
10
|
+
when 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
|
11
|
+
sql = "SELECT * FROM pg_trigger WHERE tgname = '#{name}';"
|
12
|
+
end
|
13
|
+
does_one_exist?(sql)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DbCompile
|
2
|
+
class View < Construct
|
3
|
+
def build_path
|
4
|
+
@path = File.join('views', "#{name}.sql")
|
5
|
+
end
|
6
|
+
|
7
|
+
def source
|
8
|
+
case ActiveRecord::Base.connection.class.to_s
|
9
|
+
when "ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter"
|
10
|
+
"CREATE OR REPLACE VIEW #{name} AS #{super}"
|
11
|
+
else
|
12
|
+
"DROP VIEW IF EXISTS #{name} CASCADE; CREATE VIEW #{name} AS #{super}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def verify
|
17
|
+
sql = nil
|
18
|
+
case ActiveRecord::Base.connection.class.to_s
|
19
|
+
when 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
|
20
|
+
sql = "SELECT viewname FROM pg_catalog.pg_views WHERE viewname = '#{name}'"
|
21
|
+
when "ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter"
|
22
|
+
sql = "SELECT lower(view_name) FROM user_views WHERE lower(view_name) = lower('#{name}')"
|
23
|
+
else
|
24
|
+
raise "data dictionary query for adapter #{ActiveRecord::Base.connection.class.to_s} not defined"
|
25
|
+
end
|
26
|
+
require 'ckuru-tools'
|
27
|
+
ckebug 0, sql
|
28
|
+
return does_one_exist?(sql)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/dbcompile.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'dbcompile/transaction.rb'
|
2
|
+
|
3
|
+
module DbCompile
|
4
|
+
def self.build_transaction(path=nil)
|
5
|
+
if not path
|
6
|
+
path = File.join(RAILS_ROOT, 'db')
|
7
|
+
end
|
8
|
+
transaction = Transaction.new(path)
|
9
|
+
transaction.execute
|
10
|
+
if not transaction.verify
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dbcompile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tyler Lesmann
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rdoc
|
16
|
+
requirement: &12005620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.12'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *12005620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: bundler
|
27
|
+
requirement: &12003800 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *12003800
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &12002880 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *12002880
|
47
|
+
description: Rails plugin for loading various SQL constructs, like views, functions,
|
48
|
+
and triggers
|
49
|
+
email: redhatcat@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE.txt
|
54
|
+
- README.markdown
|
55
|
+
files:
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE.txt
|
58
|
+
- README.markdown
|
59
|
+
- Rakefile
|
60
|
+
- VERSION
|
61
|
+
- example/compile.yml
|
62
|
+
- lib/dbcompile.rb
|
63
|
+
- lib/dbcompile/construct.rb
|
64
|
+
- lib/dbcompile/function.rb
|
65
|
+
- lib/dbcompile/transaction.rb
|
66
|
+
- lib/dbcompile/trigger.rb
|
67
|
+
- lib/dbcompile/view.rb
|
68
|
+
- lib/tasks/dbcompile.rake
|
69
|
+
homepage: http://github.com/redhatcat/dbcompile
|
70
|
+
licenses:
|
71
|
+
- MIT
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
hash: 1816481755782367811
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 1.8.15
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: Rails plugin for loading various SQL constructs, like views, functions, and
|
97
|
+
triggers
|
98
|
+
test_files: []
|