Skip to content

Instantly share code, notes, and snippets.

@sax
Last active March 5, 2022 17:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sax/a730ec5e3a1a768ef162 to your computer and use it in GitHub Desktop.
Save sax/a730ec5e3a1a768ef162 to your computer and use it in GitHub Desktop.
Overwrite Rails 3 databases rake task to remove method pollution on Object
# The built-in Rails database rake task is completely crazy, and defines methods on Object. This causes
# problems with Makara, which uses a delegator pattern along with method_missing.
require 'active_support/core_ext/object/inclusion'
require 'active_record'
class Object
remove_method :configs_for_environment
remove_method :create_database
remove_method :current_config
remove_method :database_url_config
remove_method :drop_database_and_rescue
remove_method :drop_database
remove_method :firebird_db_string
remove_method :mysql_creation_options
remove_method :session_table_name
remove_method :set_firebird_env
remove_method :set_psql_env
end
Rake::TaskManager.class_eval do
def remove_task(task_name)
@tasks.delete(task_name.to_s)
end
end
Rake.application.remove_task('db:load_config')
Rake.application.remove_task('db:create:all')
Rake.application.remove_task('db:create')
Rake.application.remove_task('db:drop:all')
Rake.application.remove_task('db:drop')
Rake.application.remove_task('db:migrate')
Rake.application.remove_task('db:_dump')
Rake.application.remove_task('db:migrate:redo')
Rake.application.remove_task('db:migrate:reset')
Rake.application.remove_task('db:migrate:up')
Rake.application.remove_task('db:migrate:down')
Rake.application.remove_task('db:migrate:status')
Rake.application.remove_task('db:rollback')
Rake.application.remove_task('db:forward')
Rake.application.remove_task('db:reset')
Rake.application.remove_task('db:charset')
Rake.application.remove_task('db:collation')
Rake.application.remove_task('db:version')
Rake.application.remove_task('db:abort_if_pending_migrations')
Rake.application.remove_task('db:setup')
Rake.application.remove_task('db:seed')
Rake.application.remove_task('db:fixtures:load')
Rake.application.remove_task('db:fixtures:identify')
Rake.application.remove_task('db:schema:dump')
Rake.application.remove_task('db:schema:load')
Rake.application.remove_task('db:schema:load_if_ruby')
Rake.application.remove_task('db:structure:dump')
Rake.application.remove_task('db:structure:load')
Rake.application.remove_task('db:structure:load_if_sql')
Rake.application.remove_task('db:test:load')
Rake.application.remove_task('db:test:load_structure')
Rake.application.remove_task('db:test:clone')
Rake.application.remove_task('db:test:clone_structure')
Rake.application.remove_task('db:test:purge')
Rake.application.remove_task('db:test:prepare')
Rake.application.remove_task('db:sessions:create')
Rake.application.remove_task('db:sessions:clear')
Rake.application.remove_task('railties:install:migrations')
require 'active_support/core_ext/string/filters'
module ActiveRecord
module Tasks # :nodoc:
class DatabaseAlreadyExists < StandardError; end # :nodoc:
class DatabaseNotSupported < StandardError; end # :nodoc:
class PostgreSQLDatabaseTasks # :nodoc:
DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
delegate :connection, :establish_connection, :clear_active_connections!,
to: ActiveRecord::Base
def initialize(configuration)
@configuration = configuration
end
def create(master_established = false)
establish_master_connection unless master_established
connection.create_database configuration['database'],
configuration.merge('encoding' => encoding)
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
if /database .* already exists/ === error.message
raise DatabaseAlreadyExists
else
raise
end
end
def drop
establish_master_connection
connection.drop_database configuration['database']
end
def charset
connection.encoding
end
def collation
connection.collation
end
def purge
clear_active_connections!
drop
create true
end
def structure_dump(filename)
set_psql_env
search_path = case ActiveRecord::Base.dump_schemas
when :schema_search_path
configuration['schema_search_path']
when :all
nil
when String
ActiveRecord::Base.dump_schemas
end
args = ['-i', '-s', '-x', '-O', '-f', filename]
unless search_path.blank?
args << search_path.split(',').map do |part|
"--schema=#{part.strip}"
end.join(' ')
end
args << configuration['database']
run_cmd('pg_dump', args, 'dumping')
File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
end
def structure_load(filename)
set_psql_env
args = ['-q', '-f', filename, configuration['database']]
run_cmd('psql', args, 'loading')
end
private
def configuration
@configuration
end
def encoding
configuration['encoding'] || DEFAULT_ENCODING
end
def establish_master_connection
establish_connection configuration.merge(
'database' => 'postgres',
'schema_search_path' => 'public'
)
end
def set_psql_env
ENV['PGHOST'] = configuration['host'] if configuration['host']
ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
end
def run_cmd(cmd, args, action)
fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
end
def run_cmd_error(cmd, args, action)
msg = "failed to execute:\n"
msg << "#{cmd} #{args.join(' ')}\n\n"
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
msg
end
end
# <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
# logic behind common tasks used to manage database and migrations.
#
# The tasks defined here are used with Rake tasks provided by Active Record.
#
# In order to use DatabaseTasks, a few config values need to be set. All the needed
# config values are set by Rails already, so it's necessary to do it only if you
# want to change the defaults or when you want to use Active Record outside of Rails
# (in such case after configuring the database tasks, you can also use the rake tasks
# defined in Active Record).
#
# The possible config values are:
#
# * +env+: current environment (like Rails.env).
# * +database_configuration+: configuration of your databases (as in +config/database.yml+).
# * +db_dir+: your +db+ directory.
# * +fixtures_path+: a path to fixtures directory.
# * +migrations_paths+: a list of paths to directories with migrations.
# * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
# * +root+: a path to the root of the application.
#
# Example usage of +DatabaseTasks+ outside Rails could look as such:
#
# include ActiveRecord::Tasks
# DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
# DatabaseTasks.db_dir = 'db'
# # other settings...
#
# DatabaseTasks.create_current('production')
module DatabaseTasks
extend self
attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
attr_accessor :database_configuration
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
def register_task(pattern, task)
@tasks ||= {}
@tasks[pattern] = task
end
register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
def db_dir
@db_dir ||= Rails.application.config.paths["db"].first
end
def migrations_paths
@migrations_paths ||= Rails.application.paths['db/migrate'].to_a
end
def fixtures_path
@fixtures_path ||= if ENV['FIXTURES_PATH']
File.join(root, ENV['FIXTURES_PATH'])
else
File.join(root, 'test', 'fixtures')
end
end
def root
@root ||= Rails.root
end
def env
@env ||= Rails.env
end
def seed_loader
@seed_loader ||= Rails.application
end
def current_config(options = {})
options.reverse_merge! :env => env
if options.has_key?(:config)
@current_config = options[:config]
else
@current_config ||= ActiveRecord::Base.configurations[options[:env]]
end
end
def create(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).create
rescue DatabaseAlreadyExists
$stderr.puts "#{configuration['database']} already exists"
rescue Exception => error
$stderr.puts error, *(error.backtrace)
$stderr.puts "Couldn't create database for #{configuration.inspect}"
end
def create_all
each_local_configuration { |configuration| create configuration }
end
def create_current(environment = env)
each_current_configuration(environment) { |configuration|
create configuration
}
ActiveRecord::Base.establish_connection(environment.to_sym)
end
def drop(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).drop
rescue ActiveRecord::NoDatabaseError
$stderr.puts "Database '#{configuration['database']}' does not exist"
rescue Exception => error
$stderr.puts error, *(error.backtrace)
$stderr.puts "Couldn't drop #{configuration['database']}"
end
def drop_all
each_local_configuration { |configuration| drop configuration }
end
def drop_current(environment = env)
each_current_configuration(environment) { |configuration|
drop configuration
}
end
def migrate
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
scope = ENV['SCOPE']
verbose_was, Migration.verbose = Migration.verbose, verbose
Migrator.migrate(Migrator.migrations_paths, version) do |migration|
scope.blank? || scope == migration.scope
end
ensure
Migration.verbose = verbose_was
end
def charset_current(environment = env)
charset ActiveRecord::Base.configurations[environment]
end
def charset(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).charset
end
def collation_current(environment = env)
collation ActiveRecord::Base.configurations[environment]
end
def collation(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).collation
end
def purge(configuration)
class_for_adapter(configuration['adapter']).new(configuration).purge
end
def purge_all
each_local_configuration { |configuration|
purge configuration
}
end
def purge_current(environment = env)
each_current_configuration(environment) { |configuration|
purge configuration
}
ActiveRecord::Base.establish_connection(environment.to_sym)
end
def structure_dump(*arguments)
configuration = arguments.first
filename = arguments.delete_at 1
class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
end
def structure_load(*arguments)
configuration = arguments.first
filename = arguments.delete_at 1
class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
end
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
file ||= schema_file(format)
case format
when :ruby
check_schema_file(file)
ActiveRecord::Base.establish_connection(configuration)
load(file)
when :sql
check_schema_file(file)
structure_load(configuration, file)
else
raise ArgumentError, "unknown format #{format.inspect}"
end
end
def load_schema_for(*args)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
This method was renamed to `#load_schema` and will be removed in the future.
Use `#load_schema` instead.
MSG
load_schema(*args)
end
def schema_file(format = ActiveRecord::Base.schema_format)
case format
when :ruby
File.join(db_dir, "schema.rb")
when :sql
File.join(db_dir, "structure.sql")
end
end
def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
if File.exist?(file || schema_file(format))
load_schema_current(format, file, environment)
end
end
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
each_current_configuration(environment) { |configuration|
load_schema configuration, format, file
}
ActiveRecord::Base.establish_connection(environment.to_sym)
end
def check_schema_file(filename)
unless File.exist?(filename)
message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
Kernel.abort message
end
end
def load_seed
if seed_loader
seed_loader.load_seed
else
raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
"loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" +
"Seed loader should respond to load_seed method"
end
end
private
def class_for_adapter(adapter)
key = @tasks.keys.detect { |pattern| adapter[pattern] }
unless key
raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
end
@tasks[key]
end
def each_current_configuration(environment)
environments = [environment]
# add test environment only if no RAILS_ENV was specified.
environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
configurations = ActiveRecord::Base.configurations.values_at(*environments)
configurations.compact.each do |configuration|
yield configuration unless configuration['database'].blank?
end
end
def each_local_configuration
ActiveRecord::Base.configurations.each_value do |configuration|
next unless configuration['database']
if local_database?(configuration)
yield configuration
else
$stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
end
end
end
def local_database?(configuration)
configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
end
end
end
end
db_namespace = namespace :db do
task :load_config do
ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration ||
YAML.load_file('config/database.yml') || {}
ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
end
namespace :create do
task :all => :load_config do
ActiveRecord::Tasks::DatabaseTasks.create_all
end
end
desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV, it defaults to creating the development and test databases.'
task :create => [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.create_current
end
namespace :drop do
task :all => :load_config do
ActiveRecord::Tasks::DatabaseTasks.drop_all
end
end
desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV, it defaults to dropping the development and test databases.'
task :drop => [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.drop_current
end
namespace :purge do
task :all => :load_config do
ActiveRecord::Tasks::DatabaseTasks.purge_all
end
end
# desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
task :purge => [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.purge_current
end
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
task :migrate => [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.migrate
db_namespace['_dump'].invoke
end
# IMPORTANT: This task won't dump the schema if ActiveRecord::Base.dump_schema_after_migration is set to false
task :_dump do
# if ActiveRecord::Base.dump_schema_after_migration
# case ActiveRecord::Base.schema_format
# when :ruby then db_namespace["schema:dump"].invoke
# when :sql then db_namespace["structure:dump"].invoke
# else
# raise "unknown schema format #{ActiveRecord::Base.schema_format}"
# end
# end
# Allow this task to be called as many times as required. An example is the
# migrate:redo task, which calls other two internally that depend on this one.
db_namespace["structure:dump"].invoke
db_namespace['_dump'].reenable
end
namespace :migrate do
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
task :redo => [:environment, :load_config] do
if ENV['VERSION']
db_namespace['migrate:down'].invoke
db_namespace['migrate:up'].invoke
else
db_namespace['rollback'].invoke
db_namespace['migrate'].invoke
end
end
# desc 'Resets your database using your migrations for the current environment'
task :reset => ['db:drop', 'db:create', 'db:migrate']
# desc 'Runs the "up" for a given migration VERSION.'
task :up => [:environment, :load_config] do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
db_namespace['_dump'].invoke
end
# desc 'Runs the "down" for a given migration VERSION.'
task :down => [:environment, :load_config] do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required - To go down one migration, run db:rollback' unless version
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
db_namespace['_dump'].invoke
end
desc 'Display status of migrations'
task :status => [:environment, :load_config] do
unless ActiveRecord::SchemaMigration.table_exists?
abort 'Schema migrations table does not exist yet.'
end
db_list = ActiveRecord::SchemaMigration.normalized_versions
file_list =
ActiveRecord::Migrator.migrations_paths.flat_map do |path|
# match "20091231235959_some_name.rb" and "001_some_name.rb" pattern
Dir.foreach(path).grep(/^(\d{3,})_(.+)\.rb$/) do
version = ActiveRecord::SchemaMigration.normalize_migration_number($1)
status = db_list.delete(version) ? 'up' : 'down'
[status, version, $2.humanize]
end
end
db_list.map! do |version|
['up', version, '********** NO FILE **********']
end
# output
puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
puts "-" * 50
(db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
end
puts
end
end
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
task :rollback => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
db_namespace['_dump'].invoke
end
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
task :forward => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
db_namespace['_dump'].invoke
end
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
task :reset => [ 'db:drop', 'db:setup' ]
# desc "Retrieves the charset for the current environment's database"
task :charset => [:environment, :load_config] do
puts ActiveRecord::Tasks::DatabaseTasks.charset_current
end
# desc "Retrieves the collation for the current environment's database"
task :collation => [:environment, :load_config] do
begin
puts ActiveRecord::Tasks::DatabaseTasks.collation_current
rescue NoMethodError
$stderr.puts 'Sorry, your database adapter is not supported yet. Feel free to submit a patch.'
end
end
desc 'Retrieves the current schema version number'
task :version => [:environment, :load_config] do
puts "Current version: #{ActiveRecord::Migrator.current_version}"
end
# desc "Raises an error if there are pending migrations"
task :abort_if_pending_migrations => [:environment, :load_config] do
pending_migrations = ActiveRecord::Migrator.open(ActiveRecord::Migrator.migrations_paths).pending_migrations
if pending_migrations.any?
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
pending_migrations.each do |pending_migration|
puts ' %4d %s' % [pending_migration.version, pending_migration.name]
end
abort %{Run `rake db:migrate` to update your database then try again.}
end
end
desc 'Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)'
task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed]
desc 'Loads the seed data from db/seeds.rb'
task :seed do
db_namespace['abort_if_pending_migrations'].invoke
ActiveRecord::Tasks::DatabaseTasks.load_seed
end
namespace :fixtures do
desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task :load => [:environment, :load_config] do
require 'active_record/fixtures'
base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path
fixtures_dir = if ENV['FIXTURES_DIR']
File.join base_dir, ENV['FIXTURES_DIR']
else
base_dir
end
fixture_files = if ENV['FIXTURES']
ENV['FIXTURES'].split(',')
else
# The use of String#[] here is to support namespaced fixtures
Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }
end
ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files)
end
# desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task :identify => [:environment, :load_config] do
require 'active_record/fixtures'
label, id = ENV['LABEL'], ENV['ID']
raise 'LABEL or ID required' if label.blank? && id.blank?
puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label
base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path
Dir["#{base_dir}/**/*.yml"].each do |file|
if data = YAML::load(ERB.new(IO.read(file)).result)
data.each_key do |key|
key_id = ActiveRecord::FixtureSet.identify(key)
if key == label || key_id == id.to_i
puts "#{file}: #{key} (#{key_id})"
end
end
end
end
end
end
namespace :schema do
desc 'Creates a db/schema.rb file that is portable against any DB supported by AR'
task :dump => [:environment, :load_config] do
require 'active_record/schema_dumper'
filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb')
File.open(filename, "w:utf-8") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
db_namespace['schema:dump'].reenable
end
desc 'Loads a schema.rb file into the database'
task :load => [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA'])
end
task :load_if_ruby => ['db:create', :environment] do
db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
end
namespace :cache do
desc 'Creates a db/schema_cache.dump file.'
task :dump => [:environment, :load_config] do
con = ActiveRecord::Base.connection
filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
con.schema_cache.clear!
con.tables.each { |table| con.schema_cache.add(table) }
open(filename, 'wb') { |f| f.write(Marshal.dump(con.schema_cache)) }
end
desc 'Clears a db/schema_cache.dump file.'
task :clear => [:environment, :load_config] do
filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
FileUtils.rm(filename) if File.exist?(filename)
end
end
end
namespace :structure do
def database_url_config
@database_url_config ||=
ActiveRecord::Base::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
end
def current_config(options = {})
options = { :env => Rails.env }.merge! options
if options[:config]
@current_config = options[:config]
else
@current_config ||= if ENV['DATABASE_URL']
database_url_config
else
ActiveRecord::Base.configurations[options[:env]]
end
end
end
def set_psql_env(config)
ENV['PGHOST'] = config['host'] if config['host']
ENV['PGPORT'] = config['port'].to_s if config['port']
ENV['PGPASSWORD'] = config['password'].to_s if config['password']
ENV['PGUSER'] = config['username'].to_s if config['username']
end
desc 'Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql'
task :dump => [:environment, :load_config] do
next if %w(production demo staging).include?(Rails.env)
config = current_config
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case config['adapter']
when /mysql/, 'oci', 'oracle'
ActiveRecord::Base.establish_connection(config)
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
when /postgresql/
set_psql_env(config)
search_path = config['schema_search_path']
unless search_path.blank?
search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
end
`pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(config['database'])}`
raise 'Error dumping database' if $?.exitstatus == 1
File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
when /sqlite/
dbfile = config['database']
`sqlite3 #{dbfile} .schema > #{filename}`
when 'sqlserver'
`smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U`
when "firebird"
set_firebird_env(config)
db_string = firebird_db_string(config)
sh "isql -a #{db_string} > #{filename}"
else
raise "Task not supported by '#{config['adapter']}'"
end
if ActiveRecord::Base.connection.supports_migrations?
File.open(filename, "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
end
db_namespace['structure:dump'].reenable
end
desc "Recreates the databases from the structure.sql file"
task :load => [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA'])
end
task :load_if_sql => ['db:create', :environment] do
db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
end
end
namespace :test do
task :deprecated do
Rake.application.top_level_tasks.grep(/^db:test:/).each do |task|
$stderr.puts "WARNING: #{task} is deprecated. The Rails test helper now maintains " \
"your test schema automatically, see the release notes for details."
end
end
# desc "Recreate the test database from the current schema"
task :load => %w(db:test:purge) do
case ActiveRecord::Base.schema_format
when :ruby
db_namespace["test:load_schema"].invoke
when :sql
db_namespace["test:load_structure"].invoke
end
end
# desc "Recreate the test database from an existent schema.rb file"
task :load_schema => %w(db:test:purge) do
begin
should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
ActiveRecord::Schema.verbose = false
ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
ensure
if should_reconnect
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
end
end
end
# desc "Recreate the test database from an existent structure.sql file"
task :load_structure => %w(db:test:purge) do
ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA']
end
# desc "Recreate the test database from a fresh schema"
task :clone => %w(db:test:deprecated environment) do
case ActiveRecord::Base.schema_format
when :ruby
db_namespace["test:clone_schema"].invoke
when :sql
db_namespace["test:clone_structure"].invoke
end
end
# desc "Recreate the test database from a fresh schema.rb file"
task :clone_schema => %w(db:test:deprecated db:schema:dump db:test:load_schema)
# desc "Recreate the test database from a fresh structure.sql file"
task :clone_structure => %w(db:test:deprecated db:structure:dump db:test:load_structure)
# desc "Empty the test database"
task :purge => %w(environment load_config) do
ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
end
# desc 'Check for pending migrations and load the test schema'
task :prepare => %w(environment load_config) do
unless ActiveRecord::Base.configurations.blank?
db_namespace['test:load'].invoke
end
end
end
end
namespace :railties do
namespace :install do
# desc "Copies missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2"
task :migrations => :'db:load_config' do
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map(&:strip)
railties = {}
Rails.application.migration_railties.each do |railtie|
next unless to_load == :all || to_load.include?(railtie.railtie_name)
if railtie.respond_to?(:paths) && (path = railtie.paths['db/migrate'].first)
railties[railtie.railtie_name] = path
end
end
on_skip = Proc.new do |name, migration|
puts "NOTE: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists."
end
on_copy = Proc.new do |name, migration|
puts "Copied migration #{migration.basename} from #{name}"
end
ActiveRecord::Migration.copy(ActiveRecord::Migrator.migrations_paths.first, railties,
:on_skip => on_skip, :on_copy => on_copy)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment