Rails encourages the use of a different environment for each stage in an application's life cycle development, testing, and production. If you've been developing web applications for a while, this is probably how you operate anyway; Rails simply formalizes these environments.
In the development environment, changes to an application's source code are immediately visible; we just reload the corresponding page in a web browser. Speed is not a critical factor in this environment.
Instead, the focus is on providing the developer with as much insight as possible into the components responsible for displaying each page. When an error occurs in the development environment, we are able to tell at a glance which line of code is responsible for the error and how that particular line was invoked.
This capability is provided by the stack trace—a comprehensive list of all the method calls leading up to the error—which is displayed when an unexpected error occurs.
In testing, we usually refresh the database with a baseline of dummy data each time a test is repeated.
This step ensures that the results of the tests are consistent and behavior is reproducible. Unit and functional testing procedures are fully automated in Rails.
When we test a Rails application, we don't view it using a traditional web browser. Instead, tests are invoked from the command line and can be run as background processes. The testing environment provides a dedicated space in which these processes can operate.
As the requirements for each of the three environments are quite different, Rails stores the configuration for each environment separately.
Application Dependencies
One of the great aspects of Rails is its community and all the gems it has created that we, as Rails developers, can use in our apps. Each gem you use in your application becomes a dependency, meaning that your app depends on it. It's likely that your apps will have a lot of dependencies.
In fact, it's such a common occurrence that Rubyists created a tool to make managing dependencies easy.
Bundler
Rails manages application dependencies using a Ruby gem called Bundler. As its homepage states, Bundler:
"provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed."
These dependencies are listed in the application Gemfile, which is found at the root of the application structure. Gems are listed by name and version.
Here is part of the Gemfile that Rails created with our application:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.0'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use SCSS for stylesheets
gem 'sass-rails', '/> 5.0'
...
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger
gem 'byebug', platform: mri
end
group :development do
# Access an IRB console on exception pages or by using <%= console %> in
gem 'web-console', '/> 2.0'
end
As you can see, everything is a gem, including Rails itself! The first line (source 'rubygems.org') tells Bundler to look for gems on the RubyGems website, where the community happens to publish gems.
Did you notice that Bundler lets you define dependencies in each environment?
group :development do
# Access an IRB console on exception pages or by using <%= console %> in
gem 'web-console'
end
The group :development block declaration tells Bundler to only load these gems in the development environment. Neat, huh?
Once the Gemfile includes all the app dependencies, running bundle install will make Bundler retrieve all the gems and pull them into the current environment:
$ bundle install
Fetching gem metadata from https://rubygems.org/.........
Fetching additional metadata from https://rubygems.org/...
Resolving dependencies...
Using rake 10.3.1
Using json 1.8.1
Installing minitest 5.3.3
Installing i18n 0.6.9
Installing thread_safe 0.3.3
Bundler is smart. Really smart. It checks all the gems, ensuring that their dependencies are met and there are no version clashes.
A version clash is when two gems require different versions of a third gem, and that can be a nightmare to handle. Thankfully, Bundler does that for you.
A successful bundle install creates another file called Gemfile.lock , which lists the exact gems and versions used in the last successful "bundle." When Rails starts up, it checks this file to load all the gem dependencies so that your app is ready to go. Any change to the Gemfile (meaning, dependencies added or removed) requires another bundle install.
Don't worry, though; Bundler is smart and will just load (or remove) the changes, check that everything is okay, and reuse gems from previous bundles. Bundler is like your Dependency Compliance Officer ensuring everyone gets along.
Finally, Bundler is not a Rails-only tool. It can be (and is) used in other Ruby projects, so you'll see it all over the Ruby landscape.
Database Configuration
By default, Rails creates a distinct database for each environment. At any given time, you might have:
- live data with which real users are interacting in the production environment.
- a partial copy of this live data to debug an error or develop new features in the development environment.
- a set of testing data that's constantly being reloaded into the testing environment.
Configuring the database for a Rails application is incredibly easy. All the critical information is contained in just one file: config/database.yml.
The Database Configuration File
The separation of environments is reflected in the Rails database configuration file database.yml.
The format of many configuration files in Ruby frameworks, such as Rails, is "YAML Ain't Markup Language" or YAML.
YAML defines data structures and object trees in a very human-readable fashion. The database.yml file that follows is a YAML file, and you can see that it defines keys and their values using colons ( : ) and whitespace (the environment values are indented under the environment name.) You will see YAML a lot in your Ruby travels. With the comments removed, the file should look like this:
default: &default
adapter: sqlite3
pool: 5
timeout: 5000
development:
<<: *default
database: db/development.sqlite3
test:
<<: default
database: db/test.sqlite3
production:
<<: default
database: db/production.sqlite3
The parameter database sets the name of the database that is to be used in each environment.
As the configuration file suggests, Rails can support multiple databases (and even different types of database engines, such as PostgreSQL for production and SQLite for development) in parallel.
Note that we're talking about different databases here, not just different tables—each database can host an arbitrary number of different tables in parallel.
Thanks for reading.