Rails applications typically run in one of three environments: development
, test
, and production
. In addition, you can create your own custom environment, such as qa
or staging
.
Each of these environments has its own configuration settings, which allows your application to behave differently based on the environment itās running in. For example, your app can log to standard output in development, whereas it might use an external logging service in production.
How does Rails know which environment to use?
To figure out the current environment, Rails uses the RAILS_ENV
environment variables. If thatās not set, it will use the RACK_ENV
variable. You can set RAILS_ENV
to one of the following values to choose the correct environment: development
test
, or production
.
To set the Rails environment, youād use the following command in a terminal (or in a .bashrc
or .zshrc
file)
$ export RAILS_ENV=production
Alternatively, you can specify the environment name by passing the -e
flag to Rails commands.
$ bin/rails server -e staging
You can also set the environment in your Ruby code. In fact, thatās what the default test_helper
does in a Rails application.
# test/test_helper.rb
ENV["RAILS_ENV"] ||= "test"
Note: If you donāt set any of the above environment variables, Rails will assume the development
environment as a default.
For each of the above environments, Rails provides a dedicated configuration file stored under the config/environments
directory. If you open a newly minted Rails application, you will notice three files in there: development.rb
, test.rb
, and production.rb
, containing the configuration settings for each environment.
Put the configuration settings common to all environments in the config/application.rb
file, and settings specific to a particular environment in the config/environments/#{env}.rb
file.
š¤ Which environment I'm using?
To figure out the current environment your application is running under, use the Rails.env
method, which returns a string containing the name of the environment.
Rails.env # "development"
Alternatively, you can use the query methods named after the environments.
Rails.env.production? # false
Rails.env.test? # true
To create and configure a new environment, say āstagingā, youād simply add a new config/environments/staging.rb
file with its own settings. This new environment is no different than other. You can start a server with bin/rails server -e staging
, launch a console with bin/rails console -e staging
, and check the environment using Rails.env.staging?
method.
But you probably knew all of that. So letās learn something new and interesting by taking a peek behind the scenes to understand how it's implemented.
Behind the Scenes of Rails Environments
The env
method is defined in the railties/lib/rails.rb
file. It returns the current Rails environment. Hereās the implementation:
# railties/lib/rails.rb
module Rails
class << self
def env
@_env ||= ActiveSupport::EnvironmentInquirer.new(
ENV["RAILS_ENV"].presence ||
ENV["RACK_ENV"].presence ||
"development"
)
end
end
end
As you can see, Rails lazily sets the value of the @_env
variable to an instance of the EnvironmentInquirer
class, passing the values of RAILS_ENV
, RACK_ENV
, and "development"
in that order.
The EnvironmentInquirer
class takes the name of the environment, i.e. "production"
, "test"
, or "development"
and does two things:
- Set the corresponding instance variable to
true
orfalse
if it matches the passed environment name. - Add query methods like
test?
orproduction?
returning the above value. This allows you to query the environment likeRails.env.development?
and so on.
Hereās the simplified source code.
# activesupport/lib/active_support/environment_inquirer.rb
module ActiveSupport
class EnvironmentInquirer < StringInquirer
DEFAULT_ENVIRONMENTS = %w[ development test production ]
def initialize(env)
DEFAULT_ENVIRONMENTS.each do |default|
instance_variable_set :"@#{default}", env == default
end
@local = in? LOCAL_ENVIRONMENTS
end
DEFAULT_ENVIRONMENTS.each do |env|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
def #{env}?
@#{env}
end
RUBY
end
end
end
In addition, it also adds a local?
query method that returns true
if the current environment is development
or test
. This is a handy shortcut if you want to skip running some code in production, or run something only in production.
Note: If you don't know how class_eval
works in Ruby, I highly recommend reading Paolo Perrotta's Metaprogramming Ruby 2.
What is a String Inquirer?
You must have noticed that the EnvironmentInquirer
class inherits from the StingInquirer
class. Whatās going on?
The StringInquirer
class provides a query- based way to compare two strings. Hereās a simple example:
def result
ActiveSupport::StringInquirer.new("success")
end
result.success? # true
env.failure? # false
This lets you query the env
method against any other environment name, such as Rails.env.staging?
or Rails.env.qa?
.
In fact, we didnāt even have to define the DEFAULT_ENVIRONMENTS
constant above and EnvironmentInquirer
would have handled the default environments. Itās an optimization for the three default environments, so this inquirer doesn't need to rely on the slower delegation through method_missing
that StringInquirer
would normally use.
To learn more about string and array inquries, check out the following posts.
Override Existing Configuration
Sometimes, you may want to create a new configuration that mimics existing configuration, with only one or two settings being different. For example, the staging environment typically matches the production environment, with only difference being the database connection.
In these cases, you can require the existing configuration and override it in-place as follows.
# config/environments/staging.rb
require_relative "production"
Rails.application.configure do
# config settings specific to
# the staging environment.
end
Bonus: Did you know about the bin/rails about
command?
$ bin/rails about
About your application's environment
Rails version 7.1.2
Ruby version ruby 3.2.3 (2024-01-18 revision 52bb2ac0a6) [x86_64-darwin21]
RubyGems version 3.4.19
Rack version 3.0.8
Middleware ActionDispatch::HostAuthorization, Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, ActionDispatch::ServerTiming, ...
Application root /Users/akshay/blog
Environment development
Database adapter sqlite3
Database schema version 20240111083326
You're welcome š
Further Reading
- Check out all the available settings for various Rails sub-frameworks on the official Rails Guides on Configuring Rails Applications.
- This article covers some of the important configuration settings that are common to most Rails applications: Configuring Rails Environments
- For an interesting read on how Basecamp used Rails environments other than the default ones, check out this article Beyond the default Rails environments from David. Itās over a decade old, but interesting nonetheless.
That's a wrap. I hope you found this article helpful and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.