-
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-ruby --run rails/upgrade_4_1_to_4_2
. - Use
rake rails:update
/rails app:update
making sure to not overwrite existing files. This will add new files and remove deprecated ones. - 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.
- 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.
- Run the test suite again, everything should be green.
- Fix any deprecation warnings that might have appeared.
- Restart the application and go through the most important flows, veriyfing they still work.
- 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.
If you get an error message on startup resembling
NoMethodError: undefined method 'assets' for #<Rails::Application::Configuration:0x000000013af16f18 ...>
you need to add sprockets-rails
to the Gemfile.
See https://github.com/rails/rails/issues/43793 for details.
#add_template_helper
was deleted in Rails 6.1 (supposedly it was private), resulting in:
NoMethodError: undefined method `add_template_helper' for NotificationsMailer:Class
Replace
add_template_helper EmailHelpers
with
helper EmailHelpers
This is supposedly a side effect of upgrading Sprockets to v4. Some manifest file, probably at app/assets/config/manifest.js
is pointing at a non-existing folder.
If you're still using Sprockets for those kinds of assets, update the folder reference to the location of your assets, otherwise just remove the line.
ArgumentError: wrong number of arguments (given 2, expected 1)
.../gems/actionview-6.0.1/lib/action_view/template.rb:315:in `compile'
Upgrade rspec-rails to at least 4.0, using the beta versions if necessary:
In Gemfile:
gem "rspec-rails", :github => "rspec/rspec-rails", :tag => "v4.0.0.beta3"
ActiveModel::UnknownAttributeError:
unknown attribute 'message_checksum' for ActionMailbox::InboundEmail.
You probably have an old database schema action_mailbox_inbound_emails
. The table should look like
create_table "action_mailbox_inbound_emails", force: :cascade do |t|
t.integer "status", default: 0, null: false
t.string "message_id", null: false
t.string "message_checksum", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["message_id", "message_checksum"], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true
end
Construct a new migration to bring your schema up to date. In the case where the current schema is
create_table "action_mailbox_inbound_emails", force: :cascade do |t|
t.integer "status", default: 0, null: false
t.string "message_id"
t.datetime "created_at", precision: 6
t.datetime "updated_at", precision: 6
end
the resulting migration should look something like
# frozen_string_literal: true
class UpdateActionMailboxInboundEmails < ActiveRecord::Migration[6.0]
def change
change_column_null(
:action_mailbox_inbound_emails,
:message_id, false
)
add_column(
:action_mailbox_inbound_emails,
:message_checksum,
:string
)
add_index(
:action_mailbox_inbound_emails,
[:message_id, :message_checksum],
:name => "index_action_mailbox_inbound_emails_uniqueness",
:unique => true
)
end
end
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