-
Notifications
You must be signed in to change notification settings - Fork 1
Rails upgrade notes
This page contains a bunch of issues we've run into when upgrading Rails applications and how to fix them. For the most part each issue contain a findable error message or code snippet.
- Upgrade
rails
gem to the most recent version in the current line. Ie if the project is currently on 4.1.x, lock it to 4.1.16. You can find the newest versions on https://rubygems.org/gems/rails/versions. - Run the test suite and make sure there are no deprecation warnings. If there are any, they should be fixed, preferably in separate steps/pull requests.
- Upgrade
rails
gem to the most recent version in the next line. Ie if the project is currently on 4.1.16, lock it to 4.2.11.1. You can find the newest versions on https://rubygems.org/gems/rails/versions. If this is blocked by other incompatible gems, abort and go update each of those gems in a separate branch. - If synvert has a snippet for your particular upgrade, run it now. Ie going from Rails 4.1 to 4.2:
synvert --run rails/upgrade_4_1_to_4_2
. - Get a list of all changes to default files and new default files from RailsDiff for your particular upgrade. Go through them one by one, merging the changes into your existing files. You might be able to automate this using
rake rails:update
/rails app:update
making sure to not overwrite existing files. - Consult the official upgrade guides for your particular upgrade. Go through each change, making the applicable changes.
- Consult the known issues we've encountered for your particular upgrade below. Go through each change, making the applicable changes.
- Done?
- RailsDiff: Show changes in the default generated files, these changes will usually have to be manually merged.
- Synvert: Automatically apply common syntax changes to an entire project.
ActiveRecord::Relation defines a bunch of methods on associations, among others an #index
so that one can say
model.tickers.index(ticker)
to find the position of ticker
in the list of index.tickers
(ie 0 if ticker
is the first record).
However, if you also define a scope
with that name:
scope :index, -> { where(:kind => INDEX) }
to return all records where kind == "INDEX"
that results in
index.tickers.index(ticker)
being ambigious, because it would no longer return the position but rather another list of records.
Prior to Rails 5.2 we've been able to clobber the ActiveRecord::Relation#index
method, but starting with Rails 5.2, scope
checks for name clashes:
> You tried to define a scope named "index" on the model "Ticker", but ActiveRecord::Relation
> already defined an instance method with the same name.
The list of instance methods that are defined like this by ActiveRecord::Relation is
delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
:to_sentence, :to_formatted_s, :as_json,
:shuffle, :split, :slice, :index, :rindex, to: :records
(from https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/delegation.rb#L41).
Rename the scope to something that doesn't clash. In the above case we went for
scope :is_index, -> { where(:kind => INDEX) }
Delayed::DeserializationError: Job failed to load: undefined method `allocate' for "ActiveModel::Attribute::FromDatabase":String
Don't serialize "rich" objects (ie objects like full ActiveRecord::Base
instances) - only pass simple, Ruby instances like String
, Integer
etc, which we can reliably deserialize.
ActiveSupport::DeprecationException: DEPRECATION WARNING: Passing string to be evaluated in :if and :unless conditional options is deprecated and will be removed in Rails 5.2 without replacement. Pass a symbol for an instance method, or a lambda, proc or block, instead
Pass the conditional as a lambda or block instead
From: validates :some_attribute, :presence => true, :if => "person_identifier.nil?"
To: validates :some_attribute, :presence => true, :if => lambda { person_identifier.nil? }
ActiveRecord::RecordInvalid: Validation failed: Some association must exist
Optional belongs_to
associations must be declared as such.
Example: belongs_to :some_association, :optional => true
super: no superclass method `xhr' for #RSpec::ExampleGroups:SomeController::WhenNotLoggedIn::HandlingGETTerms:0x007f8766c19540
xhr :action
in specs doesn't work anymore.
Change to, for example, get :action, :xhr => true
ActiveRecord::HasManyThroughOrderError: Cannot have a has_many :through association 'Ticker#indices' which goes through 'Ticker#index_components' before the through association is defined.
has_many :through
associations needs to be defined after their source association. So for all places where you have associations defined like
class Car < ApplicationRecord
has_many :tires, :through => :wheels
has_many :wheels
end
swap the associations around so they become
class Car < ApplicationRecord
has_many :wheels
has_many :tires, :through => :wheels
end
Doesn't work:
render :text => "OK"
The above likely causes ActionView::MissingTemplate
as ActionView doesn't realise it has rendered and thus continues the rendering process and looks for a template.
Works:
render :plain => "OK"
If you are sending to_hash
to the params
object anywhere, it'd likely need to be changed to to_unsafe_hash
:
DEPRECATION WARNING: #to_hash unexpectedly ignores parameter filtering, and will change to enforce it in Rails 5.1. Enable
raise_on_unfiltered_parameters
to respect parameter filtering, which is the default in new applications. For the existing deprecated behaviour, call #to_unsafe_h instead.
This ensures that all parameters are included in the returned hash instead of just the permitted ones.
default value for spam is true
self def.default_scope { where(:spam => false) }
BookingEvent.new.spam?
= false
Don't use default scopes. Else ensure that new instances are created with the proper values.
The default_scope is also applied while creating/building a record. It is not applied while updating a record.
undefined method `to_sentence' for #Index::ActiveRecord_AssociationRelation:0x007fbf5712a930
Looks like Associations no longer are Arrays, and/or to_sentence
is no longer mixed into AssociationRelation. The simple solution is to simple convert the relation to an array:
virtual_portfolio.indices.display_order.to_a.to_sentence
Attempting to generate a URL from non-sanitized request parameters! An attacker can inject malicious data into the generated URL, such as changing the host. Whitelist and sanitize passed parameters to be secure.
Dont pass params
directly to a URL builder, for example in:
= simple_form_for :timeframe, :url => params do |form|
Instead, be explicit about the params you actually allow, for example:
= simple_form_for :timeframe, :url => params.permit(:controller, :action) do |form|
undefined method `shift' for #Movie::ActiveRecord_AssociationRelation:0x00000114e49e30
Dont use array mutating methods directly on ActiveRecord relations, as they are not allowed anymore. This also includes map!
, delete_if
, compact!
and similar methods. If needed, convert them to arrays first (.to_a
)
When running rake db:schema:load
:
ActiveRecord::StatementInvalid: Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX `index_annoying_closings_on_ip_address` ON `annoying_closings` (`ip_address`)
Switch the schema dump to :sql
:
config.active_record.schema_format = :sql
It seems the dump generated by rake db:schema:dump
doesn't include the limit/length of the index, so rake db:schema:load
cannot load the dump.
.../active_record/dynamic_matchers.rb:55:in `method_missing': undefined method `read_inheritable_attribute' for #<Class:0x007fcde359d900> (NoMethodError)
Rewrite to use class_attribute
ActionView::Template::Error: undefined method `call' for :raise:Symbol
Change
config.active_support.deprecation = :raise
to
config.active_support.deprecation = :stderr
This isn't quite the same behavior, though.
NoMethodError: undefined method `update_details' for #ActionView::LookupContext:0x007ff0bea09dd8
Upgrade prototype-rails to ~> 3.2.1.
-
sendmail, exim doesn't work: https://github.com/gitlabhq/gitlabhq/issues/4866
-
Rename all
Mailer.deliver_foo
toMailer.foo.deliver
-
Rename mail templates with
foo.erb
to match their actual content type, i.e.foo.html.erb
orfoo.text.erb
. -
Change
@body["foo"] = "bar"
to@foo = "bar"
in mailers -
Remove all
default_scope
s with :order - they will override all order clauses