Skip to content

Instantly share code, notes, and snippets.

@deepfryed
Created July 29, 2012 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deepfryed/3196621 to your computer and use it in GitHub Desktop.
Save deepfryed/3196621 to your computer and use it in GitHub Desktop.
migrations & hstore support
#!/usr/bin/env ruby
require 'swift'
require 'swift/adapter/postgres'
require 'swift/schema_migration'
require 'fileutils'
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('-d', '--db name', String) {|value| options[:db] = value}
opt.on('-h', '--host name', String) {|value| options[:host] = value}
opt.on('-p', '--port port', Integer) {|value| options[:port] = value}
opt.on('-u', '--user name', String) {|value| options[:user] = value}
opt.on('-P', '--password secret', String) {|value| options[:password] = value}
end.parse!
path = options.delete(:path) || File.join(Dir.pwd, 'db/migrations')
db = Swift.setup :default, Swift::Adapter::Postgres, options
case ARGV.shift
when 'run', 'up'
Swift::SchemaMigration.new(db, path: path).up
when 'down'
Swift::SchemaMigration.new(db, path: path).down
when 'reset'
Swift::SchemaMigration.new(db, path: path).reset
when 'new'
name = ARGV.shift
file = File.join path, "#{Time.now.utc.strftime('%Y%m%d%H%M%S')}-#{name}"
raise "missing name" unless name
raise "missing or invalid extension (only .rb allowed)" unless %r{^.rb$}.match(File.extname(name))
IO.write file, DATA.read
$stderr.puts "created:"
$stdout.puts " #{file.sub(File.join(path, '/'), '')}"
# Open migration in EDITOR
if editor = ENV['EDITOR']
Kernel.exec "#{editor} #{file}"
end
else
puts "Invalid arguments: #{$0} [up|down|new] [name]"
end
exit 0
__END__
def up
end
def down
end
require 'logger'
require 'swift/migrations'
module Swift
class SchemaMigration
attr_reader :db, :path
def initialize db, options = {}
@db = db
@path = options.fetch(:path)
end
def setup!
begin
db.execute('select * from schema_migrations limit 1')
rescue => e
db.execute 'create table schema_migrations(id serial primary key, name text, created_at timestamp)'
end
end
def existing_migrations
Swift.db.execute('select name from schema_migrations order by name desc').map {|r| r[:name]}
end
# runs the scope within the scope of current transaction
def migrate_up file
klassify(file).new.up
end
def migrate_down file
klassify(file).new.down
end
def klassify file
klass = Class.new
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
#{File.read(file)}
def execute *args
Swift.db.execute(*args)
end
RUBY
klass
end
def up
setup!
logger = Logger.new($stdout, 0)
migrated = existing_migrations
db.transaction do |db|
Dir[File.join(path, '*.{rb}')].sort.each do |file|
name = File.basename(file)
next if migrated.include?(name)
logger.info "migrating #{name}"
migrate_up(file)
db.execute('insert into schema_migrations(name, created_at) values(?, ?)', name, Time.now)
end
end
logger.info "done"
end
def down
setup!
logger = Logger.new($stdout, 0)
name = existing_migrations.first
file = File.join(path, "#{name}")
logger.info "migrating down #{name} using #{file}"
migrate_down(file)
db.execute('delete from schema_migrations where name = ?', name)
logger.info "done"
end
def reset
setup!
logger = Logger.new($stdout, 0)
existing_migrations.each do |name|
file = File.join(path, "#{name}")
logger.info "migrating down #{name} using #{file}"
migrate_down(file)
db.execute('delete from schema_migrations where name = ?', name)
end
logger.info "done"
end
end # SchemaMigration
end # Swift
require 'hstore'
module Swift
module Type
class HStore < Attribute
def define_record_methods record
record.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}; ::HStore.parse(tuple.fetch(:#{field}, {})) end
def #{name}= value; tuple.store(:#{field}, ::HStore.dump(value)) end
RUBY
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment