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 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
@@ -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
@@ -0,0 +1,7 @@
1
+ namespace :db do
2
+ desc "compile based on the contents of #{RAILS_ROOT}/db/compile.yml"
3
+ task :compile => 'environment' do
4
+ require 'dbcompile'
5
+ DbCompile.build_transaction
6
+ end
7
+ 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: []