Rails Setup Script Improvements
Rails ships with a setup script that automates bootstrapping a new application, but did you know that you’re free to edit this script? In this quick tutorial, I’ll show you how you can improve the default Rails setup script to create a more consistent and helpful onboarding experience for folks on your team.
Demo
Simply clone this repo and run ./bin/setup
.
Step 1. Update the Command That Prepares the Database
--- a/bin/setup
+++ b/bin/setup
@@ -23,7 +23,7 @@ FileUtils.chdir APP_ROOT do
# end
puts "\n== Preparing database =="
- system! "bin/rails db:prepare"
* system! "bin/rails db:reset"
puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"
What’s Going On Here?
Running bin/rails db:reset will do the following:
- Run
bin/rails db:drop
.- Run
bin/rails db:setup
.
- The bin/rails db:setup command will do the following:
- Create the database.
- Load the schema.
- Seed the database.
This ensures the database will always be in the same state after running ./bin/setup
. This is because bin/rails db:prepare
will not seed the database if the database already exists which could result in inconsistent outcomes.
Step 2. Create a Job to Seed the Database
# app/jobs/database_seeder_job.rb
class DatabaseSeederJob < ApplicationJob
queue_as :default
DEFAULT_USER_EMAIL = "user@example.com"
DEFAULT_USER_PASSWORD = "password"
def perform
ActiveRecord::Base.transaction do
create_default_user
create_users
create_posts
end
end
private
def create_default_user
User.create!(
email: DEFAULT_USER_EMAIL,
password: DEFAULT_USER_PASSWORD,
password_confirmation: DEFAULT_USER_PASSWORD,
)
end
def create_users
10.times do |i|
User.create!(
email: "user-#{i}@example.com",
password: DEFAULT_USER_PASSWORD,
password_confirmation: DEFAULT_USER_PASSWORD,
)
end
end
def create_posts
User.all.each_with_index do |user, index|
Post.create!(
title: "Title for post #{index}",
body: "Body for post #{index}",
user: user,
)
end
end
end
# db/seeds.rb
puts "\n== Seeding database =="
DatabaseSeederJob.perform_now
What’s Going On Here?
- We could run all the commands from each method inline in
db/seeds.rb
, but that script is run in isolation. By abstracting this into a job, we can call it anywhere within our application. This is important because we’ll need access to those constants when we print the instructions to the console.- Note that we call
puts
so that we’ll know when this part of the setup script is being called.
Step 3. Print Instructions When Setup is Complete
# app/jobs/post_setup_instructions_job.rb
class PostSetupInstructionsJob < ApplicationJob
queue_as :default
def perform
puts "\n== Setup complete! 🎉 =="
puts "\n👉 Run \"rails s\" to start the development server."
puts "\n== You can login with the following account =="
puts "\n🔗 #{Rails.application.routes.url_helpers.new_user_session_url(host: "localhost", port: 3000)}"
puts "\📧 Email: #{DatabaseSeederJob::DEFAULT_USER_EMAIL}"
puts "\🔐 Password: #{DatabaseSeederJob::DEFAULT_USER_PASSWORD}"
puts "\n"
end
end
# lib/tasks/post_setup_instructions.rake
namespace :post_setup_instructions do
desc "Prints instructions once setup is complete."
task perform: :environment do
PostSetupInstructionsJob.perform_now
end
end
--- a/bin/setup
+++ b/bin/setup
@@ -30,4 +30,6 @@ FileUtils.chdir APP_ROOT do
puts "\n== Restarting application server =="
system! "bin/rails restart"
-
- system! "bin/rails post_setup_instructions:perform"
end
What’s Going On Here?
- We create
PostSetupInstructionsJob
to print out the instructions for accessing our application. This allows us to access the constants we created in step 2.
- Note that we have to call
Rails.application.routes.url_helpers
to gain access to the url_for method.- You’ll also note that we pass in a
host
andport
as options to avoid raising aMissing host to link to!
error.- We create a
post_setup_instructions
task the calls thePostSetupInstructionsJob
. This is so we can call it from./bin/setup
.