diff options
Diffstat (limited to 'guides/source/upgrading_ruby_on_rails.md')
-rw-r--r-- | guides/source/upgrading_ruby_on_rails.md | 435 |
1 files changed, 403 insertions, 32 deletions
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index ab8cabe48d..4ce61c282d 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -3,10 +3,12 @@ A Guide for Upgrading Ruby on Rails This guide provides steps to be followed when you upgrade your applications to a newer version of Ruby on Rails. These steps are also available in individual release guides. +-------------------------------------------------------------------------------- + General Advice -------------- -Before attempting to upgrade an existing application, you should be sure you have a good reason to upgrade. You need to balance out 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. +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. ### Test Coverage @@ -22,17 +24,204 @@ Rails generally stays close to the latest released Ruby version when it's releas TIP: 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. -Upgrading from Rails 4.0 to Rails 4.1 +### The Rake Task + +Rails provides the `rails:update` rake task. After updating the Rails version +in the Gemfile, run this rake task. +This will help you with the creation of new files and changes of old files in an +interactive session. + +```bash +$ rake rails:update + identical config/boot.rb + exist config + conflict config/routes.rb +Overwrite /myapp/config/routes.rb? (enter "h" for help) [Ynaqdh] + force config/routes.rb + conflict config/application.rb +Overwrite /myapp/config/application.rb? (enter "h" for help) [Ynaqdh] + force config/application.rb + conflict config/environment.rb +... +``` + +Don't forget to review the difference, to see if there were any unexpected changes. + +Upgrading from Rails 4.1 to Rails 4.2 ------------------------------------- -NOTE: This section is a work in progress. +### Web Console + +First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your Gemfile and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment. + +### Responders + +`respond_with` and the class-level `respond_to` methods have been extracted to the `responders` gem. To use them, simply add `gem 'responders', '~> 2.0'` to your Gemfile. Calls to `respond_with` and `respond_to` (again, at the class level) will no longer work without having included the `responders` gem in your dependencies: + +```ruby +# app/controllers/users_controller.rb + +class UsersController < ApplicationController + respond_to :html, :json + + def show + @user = User.find(params[:id]) + respond_with @user + end +end +``` + +Instance-level `respond_to` is unaffected and does not require the additional gem: + +```ruby +# app/controllers/users_controller.rb + +class UsersController < ApplicationController + def show + @user = User.find(params[:id]) + respond_to do |format| + format.html + format.json { render json: @user } + end + end +end +``` + +See [#16526](https://github.com/rails/rails/pull/16526) for more details. + +### Error handling in transaction callbacks + +Currently, Active Record suppresses errors raised +within `after_rollback` or `after_commit` callbacks and only prints them to +the logs. In the next version, these errors will no longer be suppressed. +Instead, the errors will propagate normally just like in other Active +Record callbacks. + +When you define a `after_rollback` or `after_commit` callback, 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 following configuration to your +`config/application.rb`: + + config.active_record.raise_in_transactional_callbacks = true + +See [#14488](https://github.com/rails/rails/pull/14488) and +[#16537](https://github.com/rails/rails/pull/16537) for more details. + +### Ordering of test cases + +In Rails 5.0, test cases will be executed in random order by default. In +anticipation of this change, Rails 4.2 introduced a new configuration option +`active_support.test_order` for explicitly specifying the test ordering. This +allows you to either lock down the current behavior by 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: + +```ruby +# config/environments/test.rb +Rails.application.configure do + config.active_support.test_order = :sorted # or `:random` if you prefer +end +``` + +### Serialized attributes + +When using a custom coder (e.g. `serialize :metadata, JSON`), +assigning `nil` to a serialized attribute will save it to the database +as `NULL` instead of passing the `nil` value through the coder (e.g. `"null"` +when using the `JSON` coder). + +### Production log level + +In Rails 5, the default log level for the production environment will be changed +to `:debug` (from `:info`). To preserve the current default, add the following +line to your `production.rb`: + +```ruby +# Set to `:info` to match the current default, or set to `:debug` to opt-into +# the future default. +config.log_level = :info +``` + +### `after_bundle` in Rails templates + +If you have a Rails template that adds all the files in version control, it +fails to add the generated binstubs because it gets executed before Bundler: + +```ruby +# template.rb +generate(:scaffold, "person name:string") +route "root to: 'people#index'" +rake("db:migrate") + +git :init +git add: "." +git commit: %Q{ -m 'Initial commit' } +``` + +You can now wrap the `git` calls in an `after_bundle` block. It will be run +after the binstubs have been generated. + +```ruby +# 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 +``` + +### Rails HTML Sanitizer + +There's a new choice for sanitizing HTML fragments in your applications. The +venerable html-scanner approach is now officially being deprecated in favor of +[`Rails HTML Sanitizer`](https://github.com/rails/rails-html-sanitizer). + +This means the methods `sanitize`, `sanitize_css`, `strip_tags` and +`strip_links` are backed by a new implementation. + +This new sanitizer uses [Loofah](https://github.com/flavorjones/loofah) internally. Loofah in turn uses Nokogiri, which +wraps XML parsers written in both C and Java, so sanitization should be faster +no matter which Ruby version you run. + +The new version updates `sanitize`, so it can take a `Loofah::Scrubber` for +powerful scrubbing. +[See some examples of scrubbers here](https://github.com/flavorjones/loofah#loofahscrubber). + +Two new scrubbers have also been added: `PermitScrubber` and `TargetScrubber`. +Read the [gem's readme](https://github.com/rails/rails-html-sanitizer) for more information. + +The documentation for `PermitScrubber` and `TargetScrubber` explains how you +can gain complete control over when and how elements should be stripped. + +If your application needs to use the old sanitizer implementation, include `rails-deprecated_sanitizer` in your Gemfile: + +```ruby +gem 'rails-deprecated_sanitizer' +``` + +### Rails DOM Testing +The [`TagAssertions` module](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/TagAssertions.html) (containing methods such as `assert_tag`), [has been deprecated](https://github.com/rails/rails/blob/6061472b8c310158a2a2e8e9a6b81a1aef6b60fe/actionpack/lib/action_dispatch/testing/assertions/dom.rb) in favor of the `assert_select` methods from the `SelectorAssertions` module, which has been extracted into the [rails-dom-testing gem](https://github.com/rails/rails-dom-testing). + + +### Masked Authenticity Tokens +In order to mitigate SSL attacks, `form_authenticity_token` is now masked so that it varies with each request. Thus, tokens are validated by unmasking and then decrypting. As a result, any strategies for verifying requests from non-rails forms that relied on a static session CSRF token have to take this into account. + +Upgrading from Rails 4.0 to Rails 4.1 +------------------------------------- ### CSRF protection from remote `<script>` tags Or, "whaaat my tests are failing!!!?" Cross-site request forgery (CSRF) protection now covers GET requests with -JavaScript responses, too. That prevents a third-party site from referencing +JavaScript responses, too. This prevents a third-party site from referencing your JavaScript URL and attempting to run it to extract sensitive data. This means that your functional and integration tests that use @@ -47,7 +236,7 @@ will now trigger CSRF protection. Switch to xhr :get, :index, format: :js ``` -to explicitly test an XmlHttpRequest. +to explicitly test an `XmlHttpRequest`. If you really mean to load JavaScript from remote `<script>` tags, skip CSRF protection on that action. @@ -62,7 +251,7 @@ If you want to use Spring as your application preloader you need to: NOTE: User defined rake tasks will run in the `development` environment by default. If you want them to run in other environments consult the -[Spring README](https://github.com/jonleighton/spring#rake). +[Spring README](https://github.com/rails/spring#rake). ### `config/secrets.yml` @@ -79,11 +268,14 @@ secrets, you need to: secret_key_base: production: - secret_key_base: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> ``` -2. Copy the existing `secret_key_base` from the `secret_token.rb` initializer to - `secrets.yml` under the `production` section. +2. Use your existing `secret_key_base` from the `secret_token.rb` initializer to + set the SECRET_KEY_BASE environment variable for whichever users running the + Rails application in production mode. Alternatively, you can simply copy the existing + `secret_key_base` from the `secret_token.rb` initializer to `secrets.yml` + under the `production` section, replacing '<%= ENV["SECRET_KEY_BASE"] %>'. 3. Remove the `secret_token.rb` initializer. @@ -95,9 +287,66 @@ secrets, you need to: If your test helper contains a call to `ActiveRecord::Migration.check_pending!` this can be removed. The check -is now done automatically when you `require 'test_help'`, although +is now done automatically when you `require 'rails/test_help'`, although leaving this line in your helper is not harmful in any way. +### 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: + +```ruby +Rails.application.config.action_dispatch.cookies_serializer = :hybrid +``` + +This would transparently migrate your existing `Marshal`-serialized cookies into the +new `JSON`-based format. + +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 `Hash`es will have their keys stringified. + +```ruby +class CookiesController < ApplicationController + def set_cookie + cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014 + redirect_to action: 'read_cookie' + end + + 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. + +### Flash structure changes + +Flash message keys are +[normalized to strings](https://github.com/rails/rails/commit/a668beffd64106a1e1fedb71cc25eaaa11baf0c1). They +can still be accessed using either symbols or strings. Looping through the flash +will always yield string keys: + +```ruby +flash["string"] = "a string" +flash[:symbol] = "a symbol" + +# Rails < 4.1 +flash.keys # => ["string", :symbol] + +# Rails >= 4.1 +flash.keys # => ["string", "symbol"] +``` + +Make sure you are comparing Flash message keys against strings. + ### Changes in JSON handling There are a few major changes related to JSON handling in Rails 4.1. @@ -148,10 +397,20 @@ part of the rewrite, the following features have been removed from the encoder: 2. Support for the `encode_json` hook 3. Option to encode `BigDecimal` objects as numbers instead of strings -If you application depends on one of these features, you can get them back by +If your application depends on one of these features, you can get them back by adding the [`activesupport-json_encoder`](https://github.com/rails/activesupport-json_encoder) gem to your Gemfile. +#### JSON representation of Time objects + +`#as_json` for objects with time component (`Time`, `DateTime`, `ActiveSupport::TimeWithZone`) +now returns millisecond precision by default. If you need to keep old behavior with no millisecond +precision, set the following in an initializer: + +``` +ActiveSupport::JSON::Encoding.time_precision = 0 +``` + ### Usage of `return` within inline callback blocks Previously, Rails allowed inline callback blocks to use `return` this way: @@ -162,7 +421,7 @@ class ReadOnlyModel < ActiveRecord::Base end ``` -This behaviour was never intentionally supported. Due to a change in the internals +This behavior was never intentionally supported. Due to a change in the internals of `ActiveSupport::Callbacks`, this is no longer allowed in Rails 4.1. Using a `return` statement in an inline callback block causes a `LocalJumpError` to be raised when the callback is executed. @@ -207,7 +466,7 @@ included in the newly introduced `ActiveRecord::FixtureSet.context_class`, in `test_helper.rb`. ```ruby -class FixtureFileHelpers +module FixtureFileHelpers def file_sha(path) Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path))) end @@ -217,8 +476,8 @@ ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers ### I18n enforcing available locales -Rails 4.1 now defaults the I18n option `enforce_available_locales` to `true`, -meaning that it will make sure that all locales passed to it must be declared in +Rails 4.1 now defaults the I18n option `enforce_available_locales` to `true`. This +means that it will make sure that all locales passed to it must be declared in the `available_locales` list. To disable it (and allow I18n to accept *any* locale option) add the following @@ -228,9 +487,10 @@ configuration to your application: config.i18n.enforce_available_locales = false ``` -Note that this option was added as a security measure, to ensure user input could -not be used as locale information unless previously known, so it's recommended not -to disable this option unless you have a strong reason for doing so. +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. ### Mutator methods called on Relation @@ -249,6 +509,114 @@ authors = Author.where(name: 'Hank Moody').to_a authors.compact! ``` +### Changes on Default Scopes + +Default scopes are no longer overridden by chained conditions. + +In previous versions when you defined a `default_scope` in a model +it was overridden by chained conditions in the same field. Now it +is merged like any other scope. + +Before: + +```ruby +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" = 'active' + +User.where(state: 'inactive') +# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' +``` + +After: + +```ruby +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' AND "users"."state" = 'active' + +User.where(state: 'inactive') +# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive' +``` + +To get the previous behavior it is needed to explicitly remove the +`default_scope` condition using `unscoped`, `unscope`, `rewhere` or +`except`. + +```ruby +class User < ActiveRecord::Base + default_scope { where state: 'pending' } + scope :active, -> { unscope(where: :state).where(state: 'active') } + scope :inactive, -> { rewhere state: 'inactive' } +end + +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" = 'inactive' +``` + +### Rendering content from string + +Rails 4.1 introduces `:plain`, `:html`, and `:body` options to `render`. Those +options are now the preferred way to render string-based content, as it allows +you to specify which content type you want the response sent as. + +* `render :plain` will set the content type to `text/plain` +* `render :html` will set the content type to `text/html` +* `render :body` will *not* set the content type header. + +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`. + +### 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. + +### 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. + +```ruby +# Previously in Rails 4.0 +set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff } + +# Now in Rails 4.1 +set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff } +``` + Upgrading from Rails 3.2 to Rails 4.0 ------------------------------------- @@ -320,7 +688,7 @@ 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](http://weblog.rubyonrails.org/2012/2/25/edge-rails-patch-is-the-new-primary-http-method-for-updates/) +For more on PATCH and why this change was made, see [this post](http://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates/) on the Rails blog. #### A note about media types @@ -336,7 +704,7 @@ def update respond_to do |format| format.json do # perform a partial update - @post.update params[:post] + @article.update params[:article] end format.json_patch do @@ -380,6 +748,9 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep * Rails 4.0 has changed `serialized_attributes` and `attr_readonly` to class methods only. You shouldn't use instance methods since it's now deprecated. You should change them to use class methods, e.g. `self.serialized_attributes` to `self.class.serialized_attributes`. +* When using the default coder, assigning `nil` to a serialized attribute will save it +to the database as `NULL` instead of passing the `nil` value through YAML (`"--- \n...\n"`). + * Rails 4.0 has removed `attr_accessible` and `attr_protected` feature in favor of Strong Parameters. You can use the [Protected Attributes gem](https://github.com/rails/protected_attributes) for a smooth upgrade path. * If you are not using Protected Attributes, you can remove any options related to @@ -399,7 +770,7 @@ this gem such as `whitelist_attributes` or `mass_assignment_sanitizer` options. * Rails 4.0 has deprecated `ActiveRecord::TestCase` in favor of `ActiveSupport::TestCase`. * Rails 4.0 has deprecated the old-style hash based finder API. This means that - methods which previously accepted "finder options" no longer do. + methods which previously accepted "finder options" no longer do. For example, `Book.find(:all, conditions: { name: '1984' })` has been deprecated in favor of `Book.where(name: '1984')` * All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's how you can handle the changes: @@ -424,7 +795,7 @@ Rails 4.0 extracted Active Resource to its own gem. If you still need the featur * 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`. -* Rails 4.0 has changed `ActiveModel::Serializers::JSON.include_root_in_json` default value to `false`. Now, Active Model Serializers and Active Record objects have the same default behaviour. This means that you can comment or remove the following option in the `config/initializers/wrap_parameters.rb` file: +* Rails 4.0 has changed `ActiveModel::Serializers::JSON.include_root_in_json` default value to `false`. Now, Active Model Serializers and Active Record 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: ```ruby # Disable root element in JSON by default. @@ -550,13 +921,12 @@ The order in which helpers from more than one directory are loaded has changed i ### Active Record Observer and Action Controller Sweeper -Active Record Observer and Action Controller Sweeper have been extracted to the `rails-observers` gem. You will need to add the `rails-observers` gem if you require these features. +`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. ### sprockets-rails -* `assets:precompile:primary` has been removed. Use `assets:precompile` instead. -* The `config.assets.compress` option should be changed to -`config.assets.js_compressor` like so for instance: +* `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: ```ruby config.assets.js_compressor = :uglifier @@ -564,22 +934,23 @@ config.assets.js_compressor = :uglifier ### sass-rails -* `asset-url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")` +* `asset-url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")`. Upgrading from Rails 3.1 to Rails 3.2 ------------------------------------- -If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2. +If your application is currently on any version of Rails older than 3.1.x, you +should upgrade to Rails 3.1 before attempting an update to Rails 3.2. -The following changes are meant for upgrading your application to Rails 3.2.16, -the last 3.2.x version of Rails. +The following changes are meant for upgrading your application to the latest +3.2.x version of Rails. ### Gemfile Make the following changes to your `Gemfile`. ```ruby -gem 'rails', '3.2.16' +gem 'rails', '3.2.18' group :assets do gem 'sass-rails', '~> 3.2.6' @@ -740,7 +1111,7 @@ AppName::Application.config.session_store :cookie_store, key: 'SOMETHINGNEW' or ```bash -$ rake db:sessions:clear +$ bin/rake db:sessions:clear ``` ### Remove :cache and :concat options in asset helpers references in views |