database_loader 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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in database_loader.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ Database Loader
2
+ ===============
3
+
4
+ Load SQL views, materialized views, grants, etc. into database.
5
+
6
+ For tables, use Rails migrations.
7
+
8
+ Currently it works only with **oracle_enahanced** adapter.
9
+
10
+ ## Installation
11
+
12
+ Include the gem in your Gemfile:
13
+
14
+ gem "database_loader"
15
+
16
+ In Rails 2.x you also need to add this to you Rakefile
17
+
18
+ require "database_loader/tasks"
19
+
20
+ ## Structure
21
+
22
+ rails_app/
23
+ db/
24
+ migrate/
25
+ sql/
26
+ local/
27
+ views/
28
+ report_v.sql
29
+ ext/
30
+ packages/
31
+ sync_api.sql
32
+ views/
33
+ users_v.sql
34
+ materialized_views/
35
+ indexes/
36
+ functions/
37
+ scripts/
38
+ grants/
39
+
40
+ ## Load generated SQL into database
41
+
42
+ rake db:sql:load:local
43
+ rake db:sql:load:ext
44
+
45
+ ## Preview generated SQL
46
+
47
+ rake db:sql:dump:local
48
+ rake db:sql:dump:ext
49
+
50
+ ## Packaging a new release
51
+
52
+ In case you cannot directly access your external database,
53
+ you can generate a package containing all your SQL files and send it to DBA.
54
+
55
+ rake db:sql:package:local NAME="v1"
56
+ rake db:sql:package:ext NAME="v1"
57
+
58
+ ## Configuration
59
+
60
+ config/initializers/database_loader.rb
61
+
62
+ # Types that contain SQL files.
63
+ DatabaseLoader.types = [ :views, :materialized_views, :indexes, :packages, :functions, :scripts, :grants ]
64
+
65
+ # Use :erb, :erubis or :liquid to render SQL files.
66
+ DatabaseLoader.template_engine = :erb
67
+
68
+ # Path to store generated tar packages.
69
+ DatabaseLoader.package_path = "/tmp"
70
+
71
+ # Path to SH template that will be used to generate deployement script (see examples/template.sh)
72
+ DatabaseLoader.template_path = Rails.root.join("db", "sql", "template.sh")
73
+
74
+ ## Setting up database connections
75
+
76
+ Convention is to create additional configurations for
77
+ each environment and append the external schema name.
78
+
79
+ config/database.yml
80
+
81
+ development:
82
+ username: local
83
+ password: ...
84
+
85
+ development_ext:
86
+ username: external
87
+ password: ...
88
+
89
+
90
+ ## TODO
91
+
92
+ * SQL dependency handling.
93
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "database_loader/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "database_loader"
7
+ s.version = DatabaseLoader::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Edgars Beigarts"]
10
+ s.email = ["1@wb4.lv"]
11
+ s.homepage = "http://github.com/ebeigarts/database_loader"
12
+ s.description = %q{Load SQL views, materialized views, grants, etc. into database}
13
+ s.summary = s.description
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+ echo "usage: $0 username password"
3
+
4
+ MYDIR=`pwd`
5
+
6
+ for sql_dir in views materialized_views packages indexes scripts do
7
+ do
8
+ if test -r ${MYDIR}/${sql_dir}
9
+ then
10
+ for sql_file in ${MYDIR}/${sql_dir}/*
11
+ do
12
+ if test -r ${sql_file}
13
+ then
14
+ echo create ${sql_dir} ${sql_file}
15
+ sqlplus -s $1/$1 @${sql_file}
16
+ fi
17
+ done
18
+ fi
19
+ done
@@ -0,0 +1,8 @@
1
+ module DatabaseLoader
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ DatabaseLoader.set_schemas
5
+ require "database_loader/tasks"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ module DatabaseLoader
2
+ class RubyFile < SqlFile
3
+ def render
4
+ "-- Cannot dump ruby file"
5
+ end
6
+ alias_method :to_s, :render
7
+
8
+ def excerpt
9
+ "#{read.first(60)} ..."
10
+ end
11
+
12
+ def statements
13
+ [self]
14
+ end
15
+
16
+ def execute
17
+ content = read
18
+ ActiveRecord::Schema.define do
19
+ instance_eval(content)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module DatabaseLoader
2
+ class SqlFile
3
+ attr_accessor :path, :username
4
+
5
+ def initialize(path)
6
+ self.path = path
7
+ end
8
+
9
+ def type
10
+ path.split(/[\/\\]/)[-2].to_sym
11
+ end
12
+
13
+ def name
14
+ File.basename(path)
15
+ end
16
+
17
+ def read
18
+ File.read(path)
19
+ end
20
+
21
+ def render
22
+ Template.new(read).render("username" => username)
23
+ end
24
+ alias_method :to_s, :render
25
+
26
+ def statements
27
+ render.split(/\r?\n\/\r?\n/).reject(&:blank?).map do |str|
28
+ SqlStatement.new(str)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ module DatabaseLoader
2
+ class SqlStatement
3
+ attr_accessor :contents
4
+
5
+ def initialize(contents)
6
+ self.contents = contents
7
+ end
8
+
9
+ def excerpt
10
+ text = contents.gsub(/\-\-[^\r\n]*/, "").squish
11
+ if text.size > 60
12
+ "#{text.first(60)} ..."
13
+ else
14
+ text
15
+ end
16
+ end
17
+
18
+ def execute
19
+ ActiveRecord::Base.connection.execute(contents)
20
+ end
21
+
22
+ def to_s
23
+ contents
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,136 @@
1
+ require "database_loader"
2
+
3
+ namespace :db do
4
+ namespace :sql do
5
+ namespace :load do
6
+ DatabaseLoader.schemas.each do |schema|
7
+ desc "Load SQL statements in #{schema}"
8
+ task schema do
9
+ DatabaseLoader.types.each do |type|
10
+ Rake::Task["db:sql:load:#{schema}:#{type}"].invoke
11
+ end
12
+ end
13
+ namespace schema do
14
+ DatabaseLoader.types.each do |type|
15
+ desc "Load SQL #{type.to_s.singularize} statements in #{schema}"
16
+ task type => :environment do
17
+ DatabaseLoader.connect_as(schema) do |connection_config|
18
+ DatabaseLoader.enable_dbms_output
19
+ DatabaseLoader.files(schema, type, ENV['NAME']).each do |file|
20
+ puts "* #{file.type}/#{file.name} ".ljust(80, "-")
21
+ file.username = connection_config[:username]
22
+ file.statements.each do |statement|
23
+ puts " #{statement.excerpt}"
24
+ begin
25
+ statement.execute
26
+ DatabaseLoader.dbms_output do |line|
27
+ puts "! DBMS_OUTPUT: #{line}"
28
+ end
29
+ rescue => e
30
+ $stderr.puts e.message
31
+ end
32
+ end
33
+ end
34
+ DatabaseLoader.invalid_objects.each do |h|
35
+ puts "! #{h['name']} #{h['type']} #{h['status']}"
36
+ end
37
+ DatabaseLoader.errors.each do |h|
38
+ puts "! #{h['name']} #{h['type']} #{h['attribute']} at line #{h['line']}: #{h['text']}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ namespace :dump do
48
+ DatabaseLoader.schemas.each do |schema|
49
+ desc "Dump SQL statements in #{schema}"
50
+ task schema do
51
+ DatabaseLoader.types.each do |type|
52
+ Rake::Task["db:sql:dump:#{schema}:#{type}"].invoke
53
+ end
54
+ end
55
+ namespace schema do
56
+ DatabaseLoader.types.each do |type|
57
+ desc "Dump SQL #{type.to_s.singularize} statements in #{schema}"
58
+ task type => :environment do
59
+ connection_config = ActiveRecord::Base.connection.raw_connection.instance_variable_get("@config").symbolize_keys
60
+ DatabaseLoader.files(schema, type, ENV['NAME']).each do |file|
61
+ file.username = connection_config[:username]
62
+ puts "-- #{file.type}/#{file.name} ".ljust(80, "-")
63
+ puts file.to_s
64
+ puts
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ namespace :package do
73
+ DatabaseLoader.schemas.each do |schema|
74
+ desc "Package SQL files for remote installation in #{schema}. rake db:sql:package:#{schema} NAME=CM0000072322 COMMIT=master."
75
+ task schema => :environment do
76
+ unless ENV['NAME'].present?
77
+ STDERR.puts "You have to specify NAME environment"
78
+ end
79
+ if ENV['COMMIT']
80
+ files = `git diff --diff-filter=ACM --name-only #{ENV['COMMIT']} ./db/sql/#{schema}`.strip.split(/\n/).sort
81
+ else
82
+ files = `find ./db/sql/#{schema} -type f`.strip.split("\n").sort
83
+ end
84
+ connection_config = ActiveRecord::Base.connection.raw_connection.instance_variable_get("@config").symbolize_keys
85
+ pkg_name = ENV['NAME']
86
+ pkg_dir = File.join(DatabaseLoader.package_path, connection_config[:username].to_s.downcase)
87
+ # Package SQL files
88
+ mkdir_p(File.join(pkg_dir, pkg_name))
89
+ Dir[File.join(pkg_dir, pkg_name) + "/*/"].each do |dir|
90
+ rm_rf(dir)
91
+ end
92
+ DatabaseLoader.types.each do |type|
93
+ DatabaseLoader.files(schema, type, "*").each do |file|
94
+ next unless files.any? { |f| f.include?(file.name) }
95
+ file.username = connection_config[:username]
96
+ puts "-- #{file.type}/#{file.name} ".ljust(80, "-")
97
+ sql = file.to_s
98
+ sql += "\nEXIT\n" # tell sqlplus to exit
99
+ # puts sql
100
+ FileUtils.mkdir_p(File.join(pkg_dir, pkg_name, file.type.to_s))
101
+ File.open(File.join(pkg_dir, pkg_name, file.type.to_s, file.name), "wb") { |f| f.write sql }
102
+ end
103
+ end
104
+ # Generate shell script
105
+ sh_content = DatabaseLoader::Template.new(File.read(DatabaseLoader.template_path)).render({
106
+ "cm_number" => pkg_name.gsub(/[^\d]/, ''),
107
+ "application" => connection_config[:username]
108
+ })
109
+ File.open(File.join(pkg_dir, pkg_name, "#{pkg_name}.sh"), "wb") { |f| f.write(sh_content) }
110
+ # Generate summary
111
+ if ENV['COMMIT']
112
+ File.open(File.join(pkg_dir, pkg_name, "#{pkg_name}.txt"), "wb") do |f|
113
+ f.write(`git diff --stat #{ENV['COMMIT']} ./db/sql/#{schema}`)
114
+ end
115
+ File.open(File.join(pkg_dir, pkg_name, "#{pkg_name}.diff"), "wb") do |f|
116
+ f.write(`git diff #{ENV['COMMIT']} ./db/sql/#{schema}`)
117
+ end
118
+ end
119
+ # Try to open the folder for manual reviewing
120
+ system `open #{File.join(pkg_dir, pkg_name)}`
121
+ # Cleanup & compress
122
+ puts "Press any key to continue"; STDIN.getc
123
+ Dir.chdir(pkg_dir)
124
+ sh "tar cf #{pkg_name}.tar #{pkg_name}"
125
+ ### print
126
+ puts
127
+ # puts "# export TWO_TASK=..."
128
+ puts "Instructions for #{pkg_name}:"
129
+ puts "$ tar vxf #{pkg_name}.tar"
130
+ puts "$ cd #{pkg_name}"
131
+ puts "$ sh #{pkg_name}.sh <#{connection_config[:username]}_user> <#{connection_config[:username]}_pwd> <#{schema}_user> <#{schema}_pwd>"
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,27 @@
1
+ module DatabaseLoader
2
+ class Template
3
+ attr_accessor :contents
4
+
5
+ def initialize(contents)
6
+ self.contents = contents
7
+ end
8
+
9
+ def render(options = {})
10
+ case DatabaseLoader.template_engine
11
+ when :liquid
12
+ require "liquid"
13
+ Liquid::Template.parse(contents).render(options)
14
+ when :erb
15
+ require "erb"
16
+ struct = OpenStruct.new(options)
17
+ ERB.new(contents).result(struct.send(:binding))
18
+ when :erubis
19
+ require "erubis"
20
+ struct = OpenStruct.new(options)
21
+ Erubis::Eruby.new(contents).result(struct.send(:binding))
22
+ else
23
+ contents
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module DatabaseLoader
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,123 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require "tmpdir"
4
+ require "ostruct"
5
+
6
+ require "database_loader/version"
7
+
8
+ module DatabaseLoader
9
+ def self.set_schemas
10
+ if Rails.root
11
+ self.schemas = Dir[File.expand_path("#{Rails.root}/db/sql/*")].select { |d| File.directory?(d) }.map { |d| File.basename(d).to_sym }
12
+ else
13
+ self.schemas = [:local]
14
+ end
15
+ end
16
+
17
+ # Schemas that contain SQL files. :local will be used to load in SQL files in current database.
18
+ mattr_accessor :schemas
19
+ self.set_schemas
20
+
21
+ # Folders that contain SQL files.
22
+ mattr_accessor :types
23
+ self.types = [ :views, :materialized_views, :indexes, :packages, :functions, :scripts, :grants ]
24
+
25
+ # Use :erb, :erubis or :liquid to render SQL files.
26
+ mattr_accessor :template_engine
27
+ self.template_engine = :erb
28
+
29
+ # Path to store generated deployement packages.
30
+ mattr_accessor :package_path
31
+ self.package_path = Dir.tmpdir
32
+
33
+ # Path to SH template that will be used to generate deployement script.
34
+ mattr_accessor :template_path
35
+ self.template_path = File.dirname(__FILE__) + "/../../examples"
36
+
37
+ def self.files(schema, type = nil, name = nil)
38
+ type ||= "**"
39
+ name ||= "*"
40
+ files = []
41
+ Dir[File.expand_path("#{Rails.root}/db/sql/#{schema}/#{type}")].each do |dir_path|
42
+ if DatabaseLoader.types.include?(File.basename(dir_path).to_sym)
43
+ Dir["#{dir_path}/#{name}.sql"].sort.each do |path|
44
+ files << SqlFile.new(path)
45
+ end
46
+ Dir["#{dir_path}/#{name}.rb"].sort.each do |path|
47
+ files << RubyFile.new(path)
48
+ end
49
+ end
50
+ end
51
+ files
52
+ end
53
+
54
+ def self.invalid_objects
55
+ ActiveRecord::Base.connection.select_all(%{
56
+ SELECT object_name AS name, object_type AS type, status
57
+ FROM user_objects
58
+ WHERE object_name LIKE '#{ActiveRecord::Base.table_name_prefix[0..-2].upcase}%'
59
+ AND status = 'INVALID'
60
+ AND object_type like 'PACKAGE%'
61
+ })
62
+ end
63
+
64
+ def self.errors
65
+ ActiveRecord::Base.connection.select_all(%{
66
+ SELECT *
67
+ FROM USER_ERRORS
68
+ WHERE name LIKE '#{ActiveRecord::Base.table_name_prefix[0..-2].upcase}%'
69
+ ORDER BY name, type, sequence
70
+ })
71
+ end
72
+
73
+ def self.enable_dbms_output
74
+ plsql.dbms_output.enable(10_000)
75
+ end
76
+
77
+ def self.dbms_output
78
+ loop do
79
+ result = plsql.dbms_output.get_line(:line => '', :status => 0)
80
+ break unless result[:status] == 0
81
+ yield result[:line]
82
+ end
83
+ end
84
+
85
+ def self.connect_as(schema)
86
+ begin
87
+ connection_config = ActiveRecord::Base.connection.raw_connection.instance_variable_get("@config").symbolize_keys
88
+ connection_name = nil
89
+ # When running rake test|spec RAILS_ENV is development by default,
90
+ # so we need to guess config from current connection.
91
+ ActiveRecord::Base.configurations.each do |name, config|
92
+ # current configuration?
93
+ if connection_config.symbolize_keys == config.symbolize_keys
94
+ # current configuration has also _apps configuration?
95
+ if ActiveRecord::Base.configurations.any? { |other_name, _| other_name.to_s == "#{name}_#{schema}" }
96
+ connection_name = name
97
+ break
98
+ end
99
+ end
100
+ end
101
+ if schema != :local
102
+ unless connection_name
103
+ raise "Missing database.yml configuration for <RAILS_ENV>_#{schema}."
104
+ end
105
+ puts "* Loading SQL statements in #{connection_name.downcase}_apps"
106
+ ActiveRecord::Base.establish_connection("#{connection_name}_#{schema}".to_sym)
107
+ end
108
+ yield connection_config
109
+ ensure
110
+ if schema != :local
111
+ # Set back the original connection
112
+ ActiveRecord::Base.establish_connection(connection_name)
113
+ end
114
+ end
115
+ end
116
+
117
+ autoload :SqlFile, 'database_loader/sql_file'
118
+ autoload :RubyFile, 'database_loader/ruby_file'
119
+ autoload :SqlStatement, 'database_loader/sql_statement'
120
+ autoload :Template, 'database_loader/template'
121
+ end
122
+
123
+ require "database_loader/railtie" if defined?(::Rails::Railtie)
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: database_loader
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Edgars Beigarts
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-18 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Load SQL views, materialized views, grants, etc. into database
23
+ email:
24
+ - 1@wb4.lv
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.md
35
+ - Rakefile
36
+ - database_loader.gemspec
37
+ - examples/template.sh
38
+ - lib/database_loader.rb
39
+ - lib/database_loader/railtie.rb
40
+ - lib/database_loader/ruby_file.rb
41
+ - lib/database_loader/sql_file.rb
42
+ - lib/database_loader/sql_statement.rb
43
+ - lib/database_loader/tasks.rb
44
+ - lib/database_loader/template.rb
45
+ - lib/database_loader/version.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/ebeigarts/database_loader
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.4.1
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Load SQL views, materialized views, grants, etc. into database
80
+ test_files: []
81
+