Rails uses a “shifted” “semantic” “versioning” which pretty much comes down to the following. Major version: “we’ll most definitely break everything you ever depended on, half of them without warning.” Minor version: “we’ll probably break many stuff you depend on, some of them without warning.” Patch version: “we might accidentally some core APIs, but we promise it’s not intentional (or documented).” Knowing that, I still embarked on the grand endeavor of upgrading from Ruby on Rails 6.0.4.1 to 6.1.4.1. What could possibly go wrong, right?
No more autoloading during startup
Let’s start with an easy one. This one was at least documented with the introduction of the Zeitwerk code loader. It has a relatively small blast radius as well: previously autoloaded constants will stop being autoloaded during application boot and have to be manually require
d. The error message is clear and if you read the docs when switching to Zeitwerk (which you probably have, considering how many things that broke) it shouldn’t come as a surprise.
Before
config.middleware.use DevWebpackProxy, ssl_verify_none: true
After
require './lib/middleware/dev_webpack_proxy'
config.middleware.use DevWebpackProxy, ssl_verify_none: true
add_template_helper
disappeared
If you started working with Rails with or before version 3, you might have some instances of add_template_helper
in your code. Well, with Rails 6.1, no more. Someone considered it “private” and deleted it without deprecation warning. Luckily there is an equivalent replacement for it.
Before
add_template_helper(MyAwesomeHelper)
After
helper MyAwesomeHelper
fixture_file_upload
broken
fixture_file_upload
is often used in tests that focus on some uploading functionality. With Rails 6.1, it suddenly turns into a sea of NoMethodError: undefined method 'file_fixture_path'
errors. Did they delete it? Turns out they did, but only from a default API. It’s still available, but it now takes an additional include
to get it to work.
There is also a new deprecation warning about using file paths relative to file_fixture_path
. “Passing a path to fixture_file_upload
relative to fixture_path
is deprecated. In Rails 6.2, the path needs to be relative to file_fixture_path
.” Might as well fix it now while we’re at it though. The warning says how to fix it as well. How kind.
Before
fixture_file_upload('images/test_image.png', 'image/png')
After
include ActionDispatch::TestProcess::FixtureFile
fixture_file_upload('../images/test_image.png', 'image/png')
where.not
changed from NOR to NAND
This is a big and potentially very painful change. If you had the policy not to test “obvious” code (because that’d be a test for the framework, right?), this might bite you one too late. Before, where.not
would behave like NOR, eg if you gave it multiple conditions it’d only work if all the (negative) conditions were fulfilled (NOR(A, B) = AND(NOT(A), NOT(B))
). This changes to NAND, meaning it works if any of the (negative) conditions are fulfilled (NAND(A, B) = OR(NOT(A), NOT(B))
). The problem with this isn’t even the difficulty of the fix, but that without thorough test coverage you won’t even notice it (and having a where.not
in a scope for example you won’t even see the deprecation warnings). For example if you wanted users who are not admins with godmode (= not admin and not godmode), you’ll need to change your code…
Before
where.not(role: ROLE::ADMIN, status: STATUS::GODMODE)
After
where.not(role: ROLE::ADMIN).where.not(status: STATUS::GODMODE)
has_many
refuses source
without through
I couldn’t find anything about this. I figure it wasn’t supposed to work in the first place, and now it’s actively erroring. Luckily for me the only usage in our source code was in a relatively simple place, so just removing the source
parameter “fixed” the issue. I haven’t looked into how to work around this if the specified source
had complex filtering conditions on it for example.
Before
has_many :active_items, -> { where status: STATUS::ACTIVE }, class_name: 'Item', source: :items
After
has_many :active_items, -> { where status: STATUS::ACTIVE }, class_name: 'Item'
re/order
refuses raw SQL
There were supposed to be deprecation warnings for this along the lines of “Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s)”. If you were lucky enough to see the deprecation warning, it told you to wrap “known safe” values in Arel.sql
. Hopefully you were only passing in “known safe” values anyway so that works.
Before
reorder('rand()')
After
reorder(Arel.sql('rand()'))
Bonus: don’t exit transaction
block with break
Another new deprecation warning in Rails 6.1! “Using `return`, `break` or `throw` to exit a transaction block is deprecated without replacement.” Now I was already warmed up (and it only occurred once in the codebase) so I fixed it along the way.
Before
ActiveRecord::Base.transaction do
break if commit_changes
raise ActiveRecord::Rollback
end
After
ActiveRecord::Base.transaction do
unless commit_changes
raise ActiveRecord::Rollback
end
end