$ rake db:create # create the db
$ rake db:migrate # run migrations
$ rake db:drop # delete the db
$ rake db:reset # combination of the upper three
$ rake db:schema # creates a schema file of the current database
$ rake g:migration your_migration # generates a new migration file
-
-
Save schickling/6762581 to your computer and use it in GitHub Desktop.
# config/database.yml | |
host: 'localhost' | |
adapter: 'postgresql' | |
encoding: utf-8 | |
database: 'test' |
require "active_record" | |
namespace :db do | |
db_config = YAML::load(File.open('config/database.yml')) | |
db_config_admin = db_config.merge({'database' => 'postgres', 'schema_search_path' => 'public'}) | |
desc "Create the database" | |
task :create do | |
ActiveRecord::Base.establish_connection(db_config_admin) | |
ActiveRecord::Base.connection.create_database(db_config["database"]) | |
puts "Database created." | |
end | |
desc "Migrate the database" | |
task :migrate do | |
ActiveRecord::Base.establish_connection(db_config) | |
ActiveRecord::Migrator.migrate("db/migrate/") | |
Rake::Task["db:schema"].invoke | |
puts "Database migrated." | |
end | |
desc "Drop the database" | |
task :drop do | |
ActiveRecord::Base.establish_connection(db_config_admin) | |
ActiveRecord::Base.connection.drop_database(db_config["database"]) | |
puts "Database deleted." | |
end | |
desc "Reset the database" | |
task :reset => [:drop, :create, :migrate] | |
desc 'Create a db/schema.rb file that is portable against any DB supported by AR' | |
task :schema do | |
ActiveRecord::Base.establish_connection(db_config) | |
require 'active_record/schema_dumper' | |
filename = "db/schema.rb" | |
File.open(filename, "w:utf-8") do |file| | |
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) | |
end | |
end | |
end | |
namespace :g do | |
desc "Generate migration" | |
task :migration do | |
name = ARGV[1] || raise("Specify name: rake g:migration your_migration") | |
timestamp = Time.now.strftime("%Y%m%d%H%M%S") | |
path = File.expand_path("../db/migrate/#{timestamp}_#{name}.rb", __FILE__) | |
migration_class = name.split("_").map(&:capitalize).join | |
File.open(path, 'w') do |file| | |
file.write <<-EOF | |
class #{migration_class} < ActiveRecord::Migration | |
def self.up | |
end | |
def self.down | |
end | |
end | |
EOF | |
end | |
puts "Migration #{path} created" | |
abort # needed stop other tasks | |
end | |
end |
@schickling - thanks for this. The Rakefile also needs to include require "yaml"
at the top for a clean ruby 2.3.0 project
Thanks.
Create a gem, activerecord_sans_rails
, perhaps?
@schickling: Created a gem activerecord_sans_rails for the same.
you're awesome, just found this via Google. thanks <3
just fyi, i needed to require 'yaml' to get this working.
I made a slightly larger version of this in https://github.com/jdblack/active_record_example . It includes a couple trivial modules and a small script to show how to use Schickling's work.
great gist! thanks for your work.
if you use activerecord 5.2, you may run into an error below
$ rake db:migrate
rake aborted!
NoMethodError: undefined method `migrate' for ActiveRecord::Migrator:Class
you should replace
ActiveRecord::Migrator.migrate("db/migrate/")
with
ActiveRecord::MigrationContext.new("db/migrate/").migrate
https://gist.github.com/schickling/6762581#file-rakefile-L18
If rollback is needed. (Based on activerecord 5.2)
desc "Migrate the database"
task :rollback do
ActiveRecord::Base.establish_connection(db_config)
ActiveRecord::MigrationContext.new("db/migrate/").rollback
Rake::Task["db:schema"].invoke
puts "Last migration has been reverted."
end
A few suggestions, as pointed out by my colleague @owst:
- The generated migration should have instance
up
/down
methods (or even a singlechange
method), rather than singleton class methods - Calling
abort
(to prevent the migration name argument from being run as a Rake task) causes the process to exit non-zero, indicating an error, which is not the case, so it should just beexit
- It's simpler to just use
File.write
with a string, rather than the block form, ie:
File.write(path, <<~MIGRATION_BODY)
# contents of HEREDOC
MIGRATION_BODY
Edit: Obviously I've used the Ruby 2.3+ squiggly HEREDOC there, but no real need to.
Rollback (activerecord 5.1)
desc 'Rollback the database'
task :rollback, [:steps] do |_task, args|
steps = (args[:steps] || 1).to_i
ActiveRecord::Base.establish_connection(DATABASE_CONFIG)
ActiveRecord::Migrator.rollback(['db/migrate'], steps)
Rake::Task['db:schema'].invoke
end
Also refer to Generate migrations outside Rails - Stack Overflow.
Maybe The DatabaseTasks is the best plan. Example
I have made minimal example/setup using Rails migration and active record outside Rails
https://github.com/euclid1990/rails-migration
(Support Rails >= 5.2)
🔢
anybody interested to update this for ActiveRecord 6.0.1
?
currently the error I'm getting is: NoMethodError: undefined method
migrate' for ActiveRecord::Migrator:Class`
@captainhusaynpinguin yeah it's a little more complicated. you need an instance of ActiveRecord::MigrationContext
which needs to be initialized with the path to the migration directory and ActiveRecord::SchemaMigration
which will need to have its table created. I would up getting it to work using something like this:
def schema_migrations
ActiveRecord::SchemaMigration.tap do |sm|
sm.create_table
end
end
def migration_context
ActiveRecord::MigrationContext.new(migration_directory, schema_migrations)
end
def migrate
migration_context.migrate
end
You could also do the create_table
separately before, but I found this to be a little nicer to work with.
Most of this works in the newest version of ActiveRecord, but a few things have to be in for the db:migrate task to work without silently failing. I am using ActiveRecord outside of Rails in this context.
ActiveRecord::Migrator.migrate("db/migrate/")
should be replaced with ActiveRecord::Tasks::DatabaseTasks.migrate
assuming you are wanting to use the ActiveRecord default directory ('db/migrate'). This fixes NoMethodError: undefined method migrate' for ActiveRecord::Migrator:Class
without having to customize anything. It was failing silently before, but this seems to have solved the issue on my end.
I didn't have to mess with MigrationContext at all.
Also, g:migration should have the version number attached to the generated model. You need the version number regardless of the version you are actually using (but it defaults to 4.2). Using activerecord (6.0.2.1)
for example...
Before: class #{migration_class} < ActiveRecord::Migration
After: class #{migration_class} < ActiveRecord::Migration[6.0]
https://gist.github.com/Rhoxio/ee9a855088c53d447f2eb888bd9d09a4 if you need a static reference.
The following worked for me when using a db/migrate
directory like in rails. ActiveRecord::Tasks::DatabaseTasks
is an option too, but the default values have to be changed as it defaults to requiring the Rails
object.
def establish_connection
ActiveRecord::Base.establish_connection(
{
adapter: 'postgresql',
encoding: 'unicode',
host: 'localhost',
port: 5432,
pool: 10,
username: 'postgres',
password: 'postgres',
database: 'your_database_name'
}
)
end
def clear_all_connections
ActiveRecord::Base.clear_all_connections!
end
def generate_schema
ActiveRecord::Base.connection_pool.with_connection do |connection|
File.open('db/schema.rb', 'w') do |file|
ActiveRecord::SchemaDumper.dump(connection, file)
end
end
end
def migrate_db
establish_connection
ActiveRecord::Base.connection_pool.with_connection do |connection|
connection.migration_context.migrate
end
generate_schema
clear_all_connections
end
This two fixes made it for me with active record 6.0:
namespace :db do
# ...
desc "Migrate the database"
task :migrate do
ActiveRecord::Base.establish_connection(db_config)
ActiveRecord::Base.connection.migration_context.migrate
Rake::Task["db:schema"].invoke
puts "Database migrated."
end
desc "Rollback the last migration"
task :rollback do
step = ENV["STEP"] ? ENV["STEP"].to_i : 1
ActiveRecord::Base.establish_connection(db_config)
ActiveRecord::Base.connection.migration_context.rollback(step)
Rake::Task["db:schema"].invoke
puts "Database rollback."
end
# ...
end
namespace :g do
desc "Generate migration"
task :migration do
name = ARGV[1] || raise("Specify name: rake g:migration your_migration")
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
path = File.expand_path("../db/migrate/#{timestamp}_#{name}.rb", __FILE__)
migration_class = name.split("_").map(&:capitalize).join
File.open(path, 'w') do |file|
file.write <<-EOF
class #{migration_class} < ActiveRecord::Migration[6.0]
def self.up
end
def self.down
end
end
EOF
end
puts "Migration #{path} created"
abort # needed stop other tasks
end
end
is it better to load Rake tasks instead? like in this gist:
require 'bundler/setup'
require 'active_record'
include ActiveRecord::Tasks
db_dir = File.expand_path('../db', __FILE__)
config_dir = File.expand_path('../config', __FILE__)
DatabaseTasks.env = 'development'
DatabaseTasks.db_dir = db_dir
DatabaseTasks.database_configuration = YAML.load(File.read(File.join(config_dir, 'database.yml')))
DatabaseTasks.migrations_paths = File.join(db_dir, 'migrate')
task :environment do
ActiveRecord::Base.establish_connection(
{
adapter: 'postgresql',
encoding: 'unicode',
host: 'localhost',
port: 5432,
pool: 10,
username: 'postgres',
password: 'postgres',
database: 'your_database_name'
}
)
# require_relative './config/environment'
# ActiveRecord::Base.configurations = DatabaseTasks.database_configuration
# ActiveRecord::Base.establish_connection DatabaseTasks.env
end
load 'active_record/railties/databases.rake'
@Iaian0 came here to confirm. Loading the default tasks from ActiveRecord works just fine:
load 'active_record/railties/databases.rake'
awesome...thank you for this 👍
It seems to need a Rails
constant with ActiveRecord 7.x:
rake db:create
-- create_table(:issues_issues, {:id=>:uuid, :force=>true})
-> 0.0034s
uninitialized constant primary::Rails
@root ||= Rails.root
^^^^^
With rails.7.2.1 (activerecord 7.2.0) it works like as with rails6 just fine
@schickling, thank you for sharing this!