Ruby on Rails

Bundler

Bundler performs dependency resolution on the entire list of dependencies.

The install command should be invoked whenever dependencies are modified.

Gemfile

The :require option can be used to specify the gem to use when it differs from the name.

gem 'webmock', require: 'webmock/rspec'

The :path option can be used to use a local gem.

gem 'nokogiri', path: '~/code/nokogiri'

A git source repository can be used with the :git option to load a gem as long as it has a .gemspec file in its root. This can be shortened when the repository is on GitHub by using the :github option. It’s also possible to use the :ref, :branch, or :tag options to anchor it to a particular revision.

gem 'carrierwave', git: 'git@github.com:carrierwaveuploader/carrierwave.git'
gem 'carrierwave', github: 'carrierwaveuploader/carrierwave'

Gem Locking

Running the install or update commands causes Bundler to resolve the dependency tree and save it in the Gemfile.lock file. This “locks” the versions of the gems being used.

The package command can be used to package up all of the dependencies into vendor/cache. Vendored dependencies can then be installed using install --local.

Dependency scripts can be run within the application’s RubyGem’s environment using the exec command.

Rails 4 and greater automatically creates binstubs—scripts that run in the bundle’s context—for Rails executables such as bundle, rake, and rails. It’s also possible to create binstubs for a dependency using the binstubs command.

Configuration Files

Changes to configuration files require a server restart to take effect, since they’re only run at application startup.

The config/boot.rb file is for setting up Bundler and load paths.

The config/application.rb file is for loading rails gems, as well as those for the current Rails environment Rails.env, and for configuration the application. It’s a good place to store application settings. It can also be used to specifically pick “railties” to load, i.e. Rails gems. This file also loads config/boot.rb, and defines a module for the application, allowing for multiple instances of the application within the same process.

The config/application.rb file can also be used to configure generators, such as specifying what template engine to use.

The config/environment.rb file is used to load initializers. This should contain config/application.rb at the top.

Initializers

Initializers are a Rails convention for breaking up pieces of configuration into self-contained files. Initializers live in config/initializers/.

The backtrace_silencers.rb initializer allows configuring the shortening of backtraces.

The filter_parameter_logging.rb initializer allows filtering out sensitive information from logs.

The inflections.rb file can be used to configure singular↔plural transformations.

The session_store.rb initializer can be used to configure the encrypted session store’s key, though the secret key itself is stored in config/secrets.yml and the rake secret command can be used to generate one automatically.

Load Paths

Rails automatically looks in certain directories, such as any within app/. The load path can modified via config.autoload_paths.

The default log level of :debug can be changed via config.log_level.

Console

The console environment can be configured by passing a block to the console method in config/application.rb.

console do
  require 'pry'
  config.console = Pry
end

Environments

The config/environments/ directory stores configuration files specific to particular environments, such as development.rb.

Development Mode

Rails 4.1 ships with an application preloader named Spring which monitors config/ and initializers/, as well as any changed gem dependencies for changes and then automatically restarts the application when any are detected.

Development mode also enables automatic class reloading. The config.cache_classes setting. When true, Rails uses require to load files instead of load. The difference is that require caches the file so that subsequent requires are ignored, unlike load.

Code is not eagerly loaded when in development mode in order to increase startup times. This can be changed via config.eager_load.

Rails Class Loader

Rails registers a callback for missing constants so that when a previously unknown class is encountered, Rails conventions are used to attempt to locate and load the file that would contain that class. In particular, the conventions are:

  • If the class/module is not nested, convert the name to lower-snake-case:

    SourceCode becomes source_code

  • If the class/module is nested, each level of nesting represents a sub-directory and the above conversion takes places as well.

    SourceCode::PullRequest becomes source_code/pull_request

Database Configuration

The database.yml file is used to configure the database. It’s advised not to store it in version control because it may contain sensitive information as well as different development and test setups among team members.

Rails 4.1 added support for configuring Active Record with the DATABASE_URL environment variable.

Application Secrets

The secrets.yml file is for storing application secrets. It’s advised not to store any sensitive production information in it if checked into version control. Instead delegate to environment variables:

production:
  secret_key_base: ENV['SECRET_KEY_BASE']

Secrets are accessible through the Rails.application.secrets hash.

Logging

Most Rails contexts expose a logger attribute that is a reference to the logger. This is also accessible via Rails.logger.

It’s possible to list request object methods in config.log_tags to have that information show up in the logs. For example, setting [:subdomain] will prepend each request’s subdomain at the beginning of its log message.

Routing

Routes map URL paths to controller and action pairs, and they’re defined in config/routes.rb. For example, to route GET products/:id to the Products controller’s show action, one can do:

get 'products/:id', to: 'products#show'
get 'products/:id' => 'products#show'

The match method is more general, and its via: argument can take one or more HTTP methods to restrict the route to.

match 'products/:id' => 'products#show', via: :get

Segment keys are URL parameters denoted by a colon : prefix, similar to Ruby symbols, for example products/:id.

URLs to defined routes can be generated automatically using the link_to method, which takes as parameters the text to show for the link, the controller, action, and its URL parameters.

link_to "Products",
  controller: "products",
  action: "show",
  id: 1

There are also more ergonomic named routes that can be used to generate URLs from defined routes.

Optional segment keys can be defined by wrapping the segments in parentheses. For example, to require a :controller URL parameter but also have an optional :action parameter:

match ':controller(/:action)', via: :any

Redirects can be specified in a route. It can take an optional HTTP :status code or even a block that is passed the request parameters.

get '/foo', to: redirect('/bar')
get '/with_status', to: redirect('/destination', status: 302)

get '/thing', to: redirect do |request_params|
  "/api/#{request_params[:api_version]}"
end

More generally, the :to parameter takes a Rack Endpoint, which the aforementioned redirect evaluates to. A Rack endpoint can be a callable that takes an environment hash and returns a 3-length array consisting of the HTTP status code, response headers, and response body.

In fact, the "controller#action" syntax relies on the action method on the Controller to return a Rack endpoint that actually invokes the specified action.

An arbitrary Rack-based application can be dispatched to by using the mount method, using the :at option to specify the route that will map to it.

class SinatraApp < Sinatra::Base
  get '/' { 'Test' }
end

Rails.application.routes.draw do
  mount SinatraApp, at: '/hello'
end

It’s possible to respond to different formats by using the respond_to method.

def show
  @product = Product.find(params[:id])

  respond_to do |format|
    format.html
    format.json { render json: @product.to_json }
    format.any do
      # Any other format not previously handled.
    end
  end
end
January 30, 2017
18630dc — October 6, 2018