Skip to content

Commit

Permalink
Initial psql-cm gem progress, generation portion working.
Browse files Browse the repository at this point in the history
  • Loading branch information
wayneeseguin committed Apr 18, 2012
0 parents commit 0bf370a
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
@@ -0,0 +1,9 @@
.idea/
.DS_Store
*.log
config/appfirst.yml
config/database.yml
*.gem
.rbx
.idea/
sql/
21 changes: 21 additions & 0 deletions LICENCE
@@ -0,0 +1,21 @@
Copyright (c) 2009 Wayne E. Seguin

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

4 changes: 4 additions & 0 deletions README.md
@@ -0,0 +1,4 @@
# PostgreSQL Change Management Tool



48 changes: 48 additions & 0 deletions Rakefile
@@ -0,0 +1,48 @@
lib = File.expand_path('../lib/', __FILE__)

$:.unshift lib unless $:.include?(lib)

def psqlcm(params = {})
command = ["psql-cm"]
command << "--database #{params["database"] ? params["database"] : 'psqlcm_development'}"
command << '-D' if ENV['debug']
command << params[:actions].split

$stdout.puts command.join(' ') if ENV['debug']
exec command.join(' ')
end

desc "Build the psql-cm gem."
task :build do
%x{gem build psql-cm.gemspec}
end

desc "Build then install the psql-cm gem."
task :install => :build do
require 'psql-cm/version'
%x{gem install psql-cm-#{PSQLCM::Version}.gem}
end

task :default => :install

task :dev => :install do
Rake::Task['install'].invoke
ENV['debug'] = "true"
end

desc "Development console, builds installs then runs console"
task :console => :install do
psqlcm :actions => 'console'
end

require 'rake/testtask'

task :spec => "spec:test"

namespace :spec do
Rake::TestTask.new do |task|
task.libs.push "lib"
task.test_files = FileList['spec/*_spec.rb']
task.verbose = true
end
end
10 changes: 10 additions & 0 deletions bin/psql-cm
@@ -0,0 +1,10 @@
#!/usr/bin/env ruby

require 'psql-cm'
require 'psql-cm/cli'

::PSQLCM::CLI.parse!(ARGV)

::PSQLCM.run!(ARGV.shift)


6 changes: 6 additions & 0 deletions lib/psql-cm.rb
@@ -0,0 +1,6 @@
require 'pg'
require 'fileutils'

require_relative 'psql-cm/base'
require_relative 'psql-cm/database'

111 changes: 111 additions & 0 deletions lib/psql-cm/base.rb
@@ -0,0 +1,111 @@
module PSQLCM
class << self
def verbose(message)
$stdout.puts message if (config.verbose || config.debug)
end

def debug(message)
$stdout.puts message if config.debug
end

def halt!(message)
$stderr.puts message
exit 1
end

def config
require 'ostruct'
@config ||= OpenStruct.new
end # def self.config

def databases
@databases = db.
exec("SELECT datname as name FROM pg_database WHERE datname !~ 'template*|postgres';").
map {|row| row["name"]}

unless config.databases.empty?
@databases.select!{ |name| config.databases.include?(name) }
end
end

def schemas
@schemas = db.
exec("SELECT nspname as name FROM pg_namespace WHERE nspname !~ '^pg_.*|information_schema';").
map{|row| row["name"]}
end

def tree
return @tree if @tree
@tree = {}
databases.each do |name|
debug "tree> database: #{name}"
@config.connection["dbname"] = name and reconnect!
@tree[name] = {}
schemas.each do |schema|
debug "tree> schema: #{schema}"
@tree[name][schema] = ['base.sql', 'cm.sql']
end
end
@tree
end

def generate!
unless config.sql_path
$stdout.puts "Warning: --sql-path was not set, defaulting to $PWD/sql."
config.sql_path = "#{ENV["PWD"]}/sql"
end

FileUtils.mkdir(config.sql_path) unless File.directory?(config.sql_path)

debug "generate> sql_path: #{config.sql_path}"
Dir.chdir(config.sql_path) do
tree.each_pair do |database, hash|
debug "generate> database: #{database}"

File.directory?(File.join(config.sql_path,database)) or
FileUtils.mkdir(File.join(config.sql_path,database))

hash.each_pair do |schema, files|
debug "generate> schema: #{schema}"
File.directory?(File.join(config.sql_path,database,schema)) or
FileUtils.mkdir(File.join(config.sql_path,database,schema))

base_file = File.join(config.sql_path,database,schema,'base.sql')
cm_file = File.join(config.sql_path,database,schema,'cm.sql')

FileUtils.touch(base_file)
FileUtils.touch(cm_file)

command = "pg_dump --schema-only --no-owner --schema=#{schema} "
if File.size(base_file) > 0
command += "--file=#{cm_file} --table=psql_cm "
else
command += "--file=#{base_file} --exclude-table=psql_cm "
end
command += "#{database}"
debug "generate> #{command}"

%x[#{command}]
end
end
end
end

def run!(action = config.action, parent_id = config.parent_id)
case action
when "console"
::PSQLCM.debug "Starting Console"
require 'psql-cm/cli'
::PSQLCM::Console.run!
when "generate"
generate!
else
halt! "Action '#{action}' is not handled."
end
end

end # class << self
end

::PSQLCM.config.debug = !!ENV['DEBUG']

64 changes: 64 additions & 0 deletions lib/psql-cm/cli.rb
@@ -0,0 +1,64 @@
require 'optparse'

module PSQLCM
class CLI

class << self
def parse!(arguments)
::OptionParser.new do |options|
options.banner = "Usage: psql-cm [options]"
options.separator ""
options.separator "Specific options:"

options.on("-s", "--sql-path PATH", "Path to generate SQL cm files.") do |path|
::PSQLCM.config.sql_path = path
end

options.on("-a", "--databases NAMES", "A comma separated list of databases to cm.") do |names|
::PSQLCM.config.databases = names.split(',')
end

options.on("-u", "--uri URI", "Path to the sink database connection file.") do |uri|
::PSQLCM.configure!(uri)
end

options.on("-D", "--[no-]debug", "Output debugging information.") do |debug|
::PSQLCM.config.debug = debug.nil? ? false : true
end

options.on("-v", "--[no-]verbose", "Output verbosley.") do |verbose|
::PSQLCM.config.verbose = verbose
end

options.on_tail("-h", "--help", "Print help and exit.") do
puts options
exit 0
end

options.on_tail("--version", "Print version and exit.") do
require 'psql-cm/version'
puts ::PSQLCM::Version
exit 0
end

options.parse!(arguments)

options
end # OptionParser.new
end # def self.parse

end # class << self
end # class CLI

class Console
class << self

def run!
require 'irb'
IRB.start
end

end # class << self
end # class Console
end # module PSQLCM

41 changes: 41 additions & 0 deletions lib/psql-cm/database.rb
@@ -0,0 +1,41 @@
require 'uri'

module PSQLCM
# "postgres://{user}:{password}@{host}:{port}/{database}"
class << self
def db
return @db if @db
connect!
end

def connect!
@config.connection ||= {"dbname" => "postgres"}
@db = PG.connect(@config.connection)
end

def reconnect!
@db.close
connect!
end

def configure!(uri)
uri = URI.parse(::PSQLCM.config.uri)

query = uri.query.to_s.split('&')

timeout = query.detect { |k| k.match /connect_timeout=/ }.to_s.sub(/.*=/,'')
sslmode = query.detect { |k| k.match /sslmode=/ }.to_s.sub(/.*=/,'')

@config.connection = {
:host => uri.host,
:port => uri.port || 5432,
:dbname => "postgres", # uri.path.sub('/',''),
:user => uri.user,
:password => uri.password,
:connect_timeout => timeout.empty? ? 20 : timeout.to_i,
:sslmode => sslmode.empty? ? "disable" : sslmode # (disable|allow|prefer|require)
}.delete_if { |key, value| value.nil? }
end
end # class << self
end # module PSQLCM

4 changes: 4 additions & 0 deletions lib/psql-cm/version.rb
@@ -0,0 +1,4 @@
module PSQLCM
Version = '0.0.1'
end

26 changes: 26 additions & 0 deletions psql-cm.gemspec
@@ -0,0 +1,26 @@
lib = File.expand_path('../lib/', __FILE__)

$:.unshift lib unless $:.include?(lib)

require 'psql-cm/version'

Gem::Specification.new do |spec|
spec.platform = Gem::Platform::RUBY
spec.version = ::PSQLCM::Version
spec.name = 'psql-cm'
spec.authors = ['Wayne E. Seguin']
spec.email = ['wayneeseguin@gmail.com']
spec.homepage = 'http://rubygems.org/gems/psql-cm/'
spec.summary = 'PostgreSQL CM'
spec.description = 'PostgreSQL Change Management Tool'

spec.required_ruby_version = '~> 1.9.3'
spec.required_rubygems_version = '>= 1.8.22'
spec.add_dependency 'pg'

spec.require_path = 'lib'
spec.executables = ['psql-cm']
spec.files = Dir.glob('{bin,lib}/**/*') + %w(LICENCE README.md)
spec.test_files = Dir.glob('spec/**/*')
end

6 changes: 6 additions & 0 deletions spec/psql-cm_spec.rb
@@ -0,0 +1,6 @@
require_relative 'spec_helper'

describe PSQLCM do
before do
end
end
4 changes: 4 additions & 0 deletions spec/spec_helper.rb
@@ -0,0 +1,4 @@
require 'minitest/spec'
require 'minitest/autorun'

require 'psql-cm'

0 comments on commit 0bf370a

Please sign in to comment.