If you’re in the Sinatra Active Record section of the Flatiron School, you’ve probably seen this error message a few times: “Migrations are pending. Run rake db:migrate SINATRA_ENV=test
to resolve the issue.” Well we know we need to run rake db:migrate
to create our migration tables, but SINATRA_ENV
… what’s up with that!?
First let’s go over a few files line by line so we can follow ENV["SINATRA_ENV"]
on its journey through our app. For this, I’m using the Sinatra Complex Forms Associations Lab.
Rakefile
"SINATRA_ENV"
is the key to Ruby’s ENV
hash and defines your deployment environment. This is set in your Rakefile
.
Line 1
ENV["SINATRA_ENV"] ||= "development"
In most of the labs in this section, this will be the first line of your Rakefile
. This means that if "SINATRA_ENV"
doesn’t already have a value, its value will be set equal to "development"
.
Line 3
require_relative './config/environment'
This loads our app’s environment.rb
file.
Line 4
require 'sinatra/activerecord/rake'
This loads Rake tasks from the sinatra-activerecord
gem. A custom Rake task is defined on Lines 8-10 which starts a new Pry session.
Environment.rb
Now let’s check out our config/environment.rb
file.
Line 1
ENV["SINATRA_ENV"] ||= "development"
Again, we see our "SINATRA_ENV"
being defined. In order to maintain DRY code, I removed Line 1 from the Rakefile
and all my tests for the lab still passed without any failures.
Lines 3–4
require 'bundler/setup'
Bundler.require(:default, ENV['SINATRA_ENV'])
On Lines 3 and 4 we require our gems and dependencies. Line 3 finds our Gemfile
and makes all the gems contained within (plus their dependencies) available to Ruby by adding them to the load path. On Line 4, we’re requiring all of our gems (:default
represents all gems since we didn’t create gem groups for this app) as well as our deployment environment hash to be used with the Active Record gem.
Lines 6–9
ActiveRecord::Base.establish_connection(
adapter: "sqlite3",
database: "db/#{ENV['SINATRA_ENV']}.sqlite"
)
On Lines 6 through 9, we establish our database connection. Our adapter
sets the name of the database management system (typed in all lower-case) that we’re using to hold our data and our database
sets the path to our app’s database. This path includes our deployment environment hash, which is an argument of Bundle.require
on Line 4 and will change based on our key’s value, pointing to other databases.
Line 11
require_all 'app'
This loads all other files nested under app
to run the program.
Config.ru
Lastly, let’s look at our config.ru
file.
Line 1
require './config/environment'
Again, this loads our app’s environment.rb
file. Notice the difference between this line and Line 3 in our Rakefile
? Both do the same thing, they just require the environment.rb
file in different ways.
Lines 3–5
if ActiveRecord::Migrator.needs_migration?
raise 'Migrations are pending. Run `rake db:migrate` to resolve the issue.'
end
Lines 3 through 5 check to make sure our migrations have been run. If not, the error message on Line 4 is raised.
Line 8
use Rack::MethodOverride
Rack::MethodOverride
is a piece of Sinatra Middleware that intercepts every request run by our application. It will interpret any requests with name="_method"
by translating the request to whatever is set by the value
attribute—normally PATCH
or DELETE
for purposes in our Sinatra curriculum. This line must be placed in the config.ru
file above all controllers in which you want access to the Middleware’s functionality.
Lines 9–13
Dir[File.join(File.dirname(__FILE__), "app/controllers", "*.rb")].collect {|file| File.basename(file).split(".")[0] }.reject {|file| file == "application_controller" }.each do |file|
string_class_name = file.split('_').collect { |w| w.capitalize }.join
class_name = Object.const_get(string_class_name)
use class_name
end
In Lines 9 through 13, we’re mounting our controllers nested under app/controllers
. These classes will also be loaded as Middleware. In this case, they’ll be use OwnersController
and use PetsController
. Notice how "application_controller"
is rejected? That’s because it is called on in Line 14 below.
Line 14
run ApplicationController
This line creates an instance of our ApplicationController
class that can respond to requests from a client. We can only run
one class and the rest are loaded via use
.
Bringing It All Together
So now we know what happens each time the Rakefile
, the config/environment.rb
file, and the config.ru
file is run. We still have to answer the question as to what ENV["SINATRA_ENV"]
actually is.
ENV
is a hash-like accessor for Ruby environment variables. It will change depending on what information Ruby needs. The ENV
accessor holds all types of information for Ruby as well as its corresponding frameworks and gems. In our case, Ruby needs to define a "SINATRA_ENV"
so this is added as a key to the ENV
.
When we run learn
to test our app, this triggers the gem RSpec, which will run our spec_helper.rb
file.
Line 1
ENV["SINATRA_ENV"] = "test"
As we can see on Line 1, our ENV["SINATRA_ENV"]
is being set equal to "test"
so that when we go to our environment.rb
file as directed on Line 3, ENV["SINATRA_ENV"]
is already assigned a value and, therefore, does not take on the value of "development"
. This means that on Line 4 of our environment.rb
file, the deployment environment hash dependency has the value of "test"
and on Line 8, we are establishing a connection with a different database entirely—db/test.sqlite
! This database contains seed data to match the expected test outputs.
Lines 4 through 6 require gems related to testing.
Lines 8–10
if ActiveRecord::Migrator.needs_migration?
raise 'Migrations are pending. Run `rake db:migrate SINATRA_ENV=test` to resolve the issue.'
end
Lines 8 through 10 check to make sure our migrations have been run. If not, the error message on Line 9 is raised… and now we’ve come full circle. The app will not function unless we’ve run our migrations first because we won’t have any information to display or manipulate.
The rest of our spec_helper.rb
file configures the app for testing and then runs our config.ru
file.
Shotgun
There’s one last difference I’d like to point out. In the Sinatra section, we learn about a trusty little gem called shotgun
which allows you to start your Rack-based web app with an automatic code reloading feature. It starts up a Rack server and listens for any requests so when you hit the browser’s refresh button, you can see changes made to your code that have been saved. All you have to do is type shotgun
in your terminal to start up a new session.
Starting a new shotgun
session is like starting a new rackup
session in that the first thing our app loads is our config.ru
file, which on the very first line, requires our environment.rb
file. At this point, ENV["SINATRA_ENV"]
will be set equal to "development"
and the rest of our environment.rb
file will execute, bringing us back to config.ru
. Again, we have the chance of raising an error message on Line 4 of our config.ru
file if we have not already run our migrations. However, this message simply says “Run rake db:migrate
to resolve the issue” instead of “Run rake db:migrate SINATRA_ENV=test
to resolve the issue” Why is that? It’s because of the line of code ENV["SINATRA_ENV"] ||= "development"
, which sets "development"
as our default value for the "SINATRA_ENV"
key. Since that’s the default, we don’t have to specify another "SINATRA_ENV"
in our error message.
Conclusion
ENV["SINATRA_ENV"]
started out as a mixed-up mess for me but after a lot of research and deep-diving into my code files, I came to understand its purpose throughout my programs. Hopefully this post helped you understand it a little bit better as well.