Last active
March 5, 2022 17:27
-
-
Save sax/a730ec5e3a1a768ef162 to your computer and use it in GitHub Desktop.
Overwrite Rails 3 databases rake task to remove method pollution on Object
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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