Rails 4.0 and sprockets no longer heed the config.assets.compress directive.
Instead, the JavaScript and CSS compressor must be specified explicitly with
config.assets.js_compressor and config.assets.css_compressor,
respectively.
No error or deprecation warning is raised: assets will simply not be
minified if the Rails 3 config.assets.compress = true directive is left
intact.
Unminified assets may cause pages to load more sluggishly.
When upgrading a Rails 3 application to Rails 4, make sure to adjust
config/environments/production.rb as shown:
Widgets::Application.configure do
# ...
# Compress JavaScripts and CSS
# config.assets.compress = true <- remove the Rails 3 setting
config.assets.js_compressor = :uglifier
config.assets.css_compressor = :sass
end
Both Rails 3 and 4 use uglifier and sass-rails in the default Gemfile, so
these compressors are available unless you have removed them.
With the new settings added, assets will again minify when compiled in
production.
If you are interested in more upgrading steps and checklists, check out the
handbook I am writing: Upgrading to Rails 4.
During the development of Rails 4, many features that were present in earlier
versions of Rails were removed from Rails itself and extracted to gems.
Extracting features to gems slims down Rails itself, and allows Rails to take a
different direction to solve certain problems. For example,
strong_parameters in Rails 4
is recommended over attr_accessible and attr_protected from Rails 3
when an application needs to protect itself from mass-assignment
vulnerabilities.
Furthermore, some of the extracted gems have new maintainers. These fresh
maintainers might respond more quickly to bug and feature requests than the
Rails core team feasibly can.
Unfortunately, existing applications that are upgraded to Rails 4 may need to
pull in several new gems in order to perform properly. I have compiled a list
of these extracted gems and the features they provide. If your application uses
any of the listed features, you'll want to pull them into Gemfile while
upgrading to Rails 4.
The book I'm writing, Upgrading to Rails
4, goes into more detail about these
extractions as well as new features in Rails 4.
| Gem |
Description |
|
protected_attributes
|
Because Rails 4 recommends strong_parameters for
mass-assignment protection, attr_accessible and
attr_protected have been extracted. I expect that most
upgraded applications will need this gem, as the transition to
strong_parameters can be tedious and error-prone.
|
|
activeresource
|
While the ActiveRecord-like abstraction over a RESTful API has always
shipped as a gem, it is no longer included with Rails by default.
Include it explicitly if your application requires it.
|
actionpack-action_caching
actionpack-page_caching
|
Rails 4 includes many improvements to fragment caching, but action
and page caching have been extracted. If your application uses action
or page caching, be sure to pull these gems in explicitly.
|
|
activerecord-session_store
|
The ability to store session data in a database table has been
extracted in Rails 4. If your application uses the ActiveRecord
session store, include this gem.
|
|
rails-observers
|
Rails no longer encourages the use of observers, separate objects
that can react to lifecycle events of ActiveRecord models. If your
application uses observers, make sure to include this gem.
|
|
actionpack-xml_parser
|
Following security vulnerabilities involving inbound XML, Rails
extracts the ability to accept XML input to a gem. If your application
accepts XML in a request body (note: this is distinct from
responding with XML), you will need to pull in this gem.
|
|
rails-perftest
|
Rails 4 extracts ActionDispatch::PerformanceTest. If
your application includes performance tests (usually located in
test/performance), add this gem to your bundle.
|
|
actionview-encoded_mail_to
|
Rails previously included a little-known feature to obfuscate email
addresses in hyperlinks, either with HTML entities or JavaScript
code. If your application uses the encode option with
mail_to, include this gem.
|
If you're interested in more details about upgrading to Rails 4, please
consider buying my Upgrading to Rails 4
handbook.
An Upgrading to Rails 4 reader emailed me
about this quote from the book:
Rails 3 uses digitally signed cookies as the default store for sessions.
Digitally signed cookies cannot be easily tampered with, but users can read
the data that is being saved.
He asked:
Would you say that's something trivial to do, or would it involve some decent
amount of work? Just by accessing the Rails 3 cookies from my browser's
inspector, I can't seem to find a way to read the content from them.
Great question! Let's try.
For this example, I setup a Rails application that sets a session key to a
constant value:
class ApplicationController < ActionController::Base
before_filter :add_value_to_session
private
def add_value_to_session
session[:message] = "Hello World!"
end
end
If I hit the Rails application with curl -i (include headers), I see:
$ curl -i http://localhost:3000/
HTTP/1.1 200 OK
Set-Cookie: _railsapp_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJWYxZGNlMmNmYjFmYjBkODQ0NTc1ZWE3OTBjZmJmNTZkBjsAVEkiDG1lc3NhZ2UGOwBGSSIRSGVsbG8gV29ybGQhBjsARg%3D%3D--decd8e233744e2ba5a80481426e41d72a6685986; path=/; HttpOnly
The cookie data looks obfuscated, but in reality it's shrouded only by Base64
encoding.
Rack's session cookie methods can easily decode it:
# decode.rb
require 'rack'
puts Rack::Session::Cookie::Base64::Marshal.new.decode(ARGV[0])
Running decode.rb with the cookie's data results in:
$ ruby decode.rb BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJWYxZGNlMmNmYjFmYjBkODQ0NTc1ZWE3OTBjZmJmNTZkBjsAVEkiDG1lc3NhZ2UGOwBGSSIRSGVsbG8gV29ybGQhBjsARg%3D%3D--decd8e233744e2ba5a80481426e41d72a6685986
{"session_id"=>"f1dce2cfb1fb0d844575ea790cfbf56d", "message"=>"Hello World!"}
Our message, alongside a generated session identifier, is available without
the Rails application's secret session token.
Attempting to tamper with the data, repackage it, and send it back to the
server would fail because the digital signature would not check out, but
the data is at least readable.
Rails 4 will feature an encrypted cookie store, where data is both digitally
verified and unreadable to end users. I write about this (and more, of course!)
in Upgrading to Rails 4.
In closing, remember:
- The default Rails 3 session store (cookie store) allows users to read
the contents of the session. They may not, however, change or tamper with the
session data.
- Do not store data in the session that would be sensitive even if the data
were read by an end user or an attacker sniffing the traffic.
- Switch to the encrypted cookie store when Rails 4 ships.
Background
Writing full-stack tests is important. I go into detail about this in my
Acceptance Testing slide
deck.
In a Rails application, RSpec request
specs
are often used for the task of writing full-stack tests. In capybara 1.x,
request specs both wrap Rails' IntegrationTest framework and mix in
capybara, a tool for driving web
applications (often via headless web browsers).
Confusion
The fact that request specs both wrap a Rails IntegrationTest and have
capybara methods mixed in is confusing. José Valim goes into depth about the
problems in his blog post entitled "Improving the integration between capybara
and
RSpec".
If you've ever confused get (Rails) with visit (capybara) or
response with page, or attempted to explain the difference to a newcomer,
you know it is odd and often frustrating.
The RSpec and capybara teams worked together to alleviate the confusion
in capybara 2.x.
Changes
Upon upgrading to capybara 2.0, capybara will not be available by default in
RSpec request specs. Instead, a new type of spec--the feature
spec--has
been created for use with capybara.
To upgrade to capybara 2.0, you'll need to do a few things:
- Upgrade rspec-rails to 2.12.0 or greater
- Move any tests that use capybara from spec/requests to spec/features.
Capybara tests use the
visit method and usually assert against page.
Alternatively, you can keep using capybara in request specs, but you'll need
to manually mix in the
methods.
Examples and Recommendations
The rest of this piece has some of my opinions, but opinions I've had good
success with in the applications I develop.
Use RSpec request specs to test interactions with your application as a HTTP
API. To do so, use methods like get, post, put, delete and assert
against response:
# spec/requests/widget_api_spec.rb
describe "widget API" do
it "allows API clients to create widgets" do
post widgets_url, widget: { name: "Awesome Widget" }, format: "json"
expect(response.status).to eq(201) # "Created"
end
end
Use RSpec feature specs (with capybara) to test your application as a user
might interact with it. To do so, use methods like visit and assert against
page. You may also use feature instead of describe and scenario
instead of it to increase readability:
# spec/features/widget_management_spec.rb
feature "widget management" do
scenario "creating a new widget" do
visit root_url
click_link "New Widget"
fill_in "Name", with: "Awesome Widget"
click_button "Create Widget"
expect(page).to have_text("Widget was successfully created.")
end
end
Further Reading
Shoutouts
The capybara team, especially Jonas Nicklas,
were pivotal in making these changes happen. In fact, my own role was small
comparatively. Thank you Jonas!
Thanks to Darren Coxall, Jim
Hodgson, and David
Chelimsky for reviewing this post.
JRubyConf 2012 was a blast. Thanks
Nick and all the others who made it happen.
I enjoyed many of the talks, including Adventures on the Golden
Path by Jeff
Casimir.
Admittedly, I do not agree with all of his ideas or his implementations, but
one that struck me as a good idea and low hanging fruit was the confusion
among the rails and rake commands:
Beginners and advanced users get tripped up by this one all the time.
So, I tried whipping up a gem that delegates rake generate, rake console,
etc.. to their respective rails command.
It's called rake-rails:
# Gemfile
gem 'rake-rails'
Profit! You can now run ALL the things with rake.
$ rake generate migration add_name_to_users name:string
$ rake console staging
# ...
For efficiency freaks: it's still pretty fast and does not involve loading
the Rails environment multiple times; it simply delegates to rails/cli as
quickly as possible, just like the rails command does.
Try it out and let me know what you think! Everything is up on
GitHub.