Table of contents
- Introduction
- 1. ActiveStorage: Cannot generate URL
- 2. Please use #media_type instead.
- 3. undefined method 'raw_filter' For ActiveSupport::Callback
- 4. Rails couldn't find a valid model
- 5. ActiveSupport::TimeWithZone.name has been deprecated
- 6. Undefined method `clear_association_cache'
- 7. Uninitialized constant DeprecationHandlingMessageHash
- 8. Uninitialized Active record object after calling .reload
- 9. image/jpg is not a valid content type
- 10. Zlib::DataError: incorrect header check
- 11. Massing failing of tests - Bullet N+1 Queries
- 12. Fewer SQL queries are fired
- 13. Non-URL-safe CSRF tokens are deprecated. Use 6.1 defaults or above.
- 14. ArgumentError: missing keywords: :records, :associations
- 15. ExecJS::ProgramError: Unexpected token: name (FileChecksum)
- 16. Uglifier::Error: Unexpected token: keyword (const)
- 17. to_s(:format) deprecated in favor of to_fs(:format)
These are the errors which were not mentioned in Rails upgrade guide.
Introduction
I recently attempted to upgrade our long running Rails application from 6.1 to 7.0.4. On the way, I found various errors and failing tests which I have documented down here as a blog post. Feel free to skip to the part you are interested in using the Table Of Contents.
1. ActiveStorage: Cannot generate URL
Cannot generate URL for shiva_passport.jpg using Disk service, please set ActiveStorage::Current.url_options. : Cannot generate URL for shiva_passport.jpg using Disk service, please set ActiveStorage::Current.url_options.
Explanation
The error message suggests that you are trying to generate a URL for a file called "shiva_passport.jpg"
using the Disk service, but Active Storage
is unable to generate the URL because the URL options are not set.
Solution
# in test.rb and development.rb
# make sure to update host according to your need
Rails.application.routes.default_url_options = {protocol: "http",
host: "example.com", port: 80}
If this ^ does not solve your issue then try the suggestions below
class ApplicationController < ActionController::Base
# Fixes error
# Cannot generate URL for filename.jpg using Disk service,
# please set ActiveStorage::Current.url_options.
# This mainly affects development environment.
if Rails.env.development?
before_action do
ActiveStorage::Current.url_options = {protocol: request.protocol, host: request.host, port: request.port}
end
end
or simply add
class ApplicationController < ActionController::Base
include ActiveStorage::SetCurrent
...
In Rspec, if the suggestions above do not work, try this:-
module ActiveStorageCurrentUrlHelper
def set_url_options
# put this somewhere just before where you call methods to
# generate blob/attachment URL
ActiveStorage::Current.url_options = {host: 'www.example.com'} if Rails.env.test?
end
end
# example
def attachment_url(upload_obj: upload, params: {})
# see here
ActiveStorageCurrentUrlHelper.set_url_options
upload_obj.url(**params)
end
# test.rb
# this does not seem to solve the issue, though its the way to set
# url_options for routes in Rails 7
config.default_url_options = { host: 'example.com', protocol: 'http' }
Benefit of ActiveStorage::Current.url_options
This is an API you can reap the benefit of when you need to decide Asset's domain
/URL
based on request or User parameters.
2. Please use #media_type
instead.
Rails 7.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead. (called from preload at /Users/XX/Projects/XX/XX/app/modules/request_caching/lib/request_caching/request_cache_filter_helper.rb:8)
Solution
Use request.media_type
instead of request.content_type
if you are expecting "text/csv"
as a value because now it also includes the charset value.
Also, make sure you are using request.media_type
and expecting "text/csv"
because now
request.content_type #=> "text/csv; header=present; charset=utf-16"
request.media_type #=> "text/csv"
For more info please see this guide.
But, if you want to explicitly want to use content_type
then you can silence the deprecation warning by updating config like
# application.rb
# if you wish previous behaviour
config.action_dispatch.return_only_request_media_type_on_content_type = true
3. undefined method 'raw_filter' For ActiveSupport::Callback
#application undefined method raw\_filter' for #[ActiveSupport::Callbacks::Callback:0x0000000111021ec8](ActiveSupport::Callbacks::Callback:0x0000000111021ec8) Did you mean? filter
tagged allocation ticket error \*\*> undefined method \`raw\_filter' for #[ActiveSupport::Callbacks::Callback:0x0000000111021ec8](ActiveSupport::Callbacks::Callback:0x0000000111021ec8) Did you mean? filter
Explanation
It's possible that the raw_filter
error is related to the activerecord-import
gem, which is a gem that provides a fast way to bulk insert data into an ActiveRecord database.
The activerecord-import
gem uses callbacks to perform bulk inserts, and these callbacks may be triggering the raw_filter
method, which is not defined in the current version of Rails.
To resolve this issue, you can try updating the activerecord-import
gem to the latest version, which may include a fix for this issue. Alternatively, you can try disabling any callbacks related to the activerecord-import
gem and see if that resolves the issue.
To disable callbacks for a specific model, you can use the without_callback
method. For example:
MyModel.without_callback(:save, :after, :bulk_insert) do
MyModel.import(data)
end
This will disable the bulk_insert
callback for the save
action, allowing you to perform the import without triggering the raw_filter
error.
Solution
# updated the gem
$ bundle update 'activerecord-import'
4. Rails couldn't find a valid model
NameError: Rails couldn't find a valid model for Card association. Please provide the :class\_name option on the association declaration. If :class\_name is already provided, make sure it's an ActiveRecord::Base subclass.
--- Caused by: ---
# NameError:
# uninitialized constant CreditCardPayment::Card
# ./app/models/credit_card_payment.rb:375:in `stored_card?'
Explanation:
For some reason, this was not raising any error in Rails 6 but, suddenly its an issue in Rails 7. This the related part of code in rails repo.
Solution
belongs_to :card, class_name: 'CreditCardPayment', foreign_key: 'payment_id'
5. ActiveSupport::TimeWithZone.name has been deprecated
ActiveSupport::DeprecationException:
DEPRECATION WARNING: ActiveSupport::TimeWithZone.name has been deprecated and
from Rails 7.1 will use the default Ruby implementation.
You can set `config.active_support.remove_deprecated_time_with_zone_name = true`
Solution
# activate this in config/initializers/new_framework_defaults_7_0.rb
# Don't override ActiveSupport::TimeWithZone.name and use the default Ruby
# implementation.
Rails.application.config.active_support.remove_deprecated_time_with_zone_name = true
After the deployment of Rails 7, you will need to remove the file new_framework_defaults_7_0.rb
and you will need to activate 7.0
default config in application.rb
. And, note that, config.active_support.remove_deprecated_time_with_zone_name
will be removed from Rails 7.2.
6. Undefined method `clear_association_cache'
Turns out, this private API is now removed in Rails 7
- object.send(:clear_association_cache)
so you need to manually reset the instance variable
object.instance_variable_set :@association_cache, {} if object.persisted?
The reason for the removal of this method was due to improvements in the way that Rails handles caching of associations. With these improvements, the clear_association_cache
method was no longer needed and could be safely removed.
7. Uninitialized constant DeprecationHandlingMessageHash
Looks like the classes ActiveModel::DeprecationHandlingMessageHash
and ActiveModel::DeprecationHandlingMessageArray
were deprecated from rails 6.1 but now in 7.0 it's removed. So, it was raising this issue.
ActiveModel::Error was added to Rails 6.1 in #32313. Part of the change was the addition of DeprecationHandlingMessageArray which serves as a delegate for the [] method:
Explanation
In our case, we were using Yaks
gem to generate APIs responses. In order to generate response, the gem need to have data in primitive types like string
, number
, hash
, etc that can be easily converted to xml
or JSON
. So, we need to explicitly tell Yaks
how to get primitive data out of any complex data-type. Now, the constants are removed, so its raising exception.
Solution
- map_to_primitive ActiveModel::DeprecationHandlingMessageHash, &:to_h
- map_to_primitive ActiveModel::DeprecationHandlingMessageArray, &:to_a
+ map_to_primitive ActiveModel::Error, &:full_message
Need to convert instances of ActiveModel::Error
to primitive using :full_message
method to string
. No other meaning full methods are there. If you want to try then can use :details
, :message
8. Uninitialized Active record object after calling .reload
arobject.reload.reload
=> #<ArModel:0x000000011acbcdc8 not initialized>
Also facing stale object issue. calling .reload
is not making another SQL query to fetch the record from the DB.
Solution
This happened probably because we had overwritten preload
method in some models. In Rails 7, reload
method happens to call preload
method internally on the active-record object.
I just renamed our preload
method to something else like def our_preload
and the problem seems to have gone.
8.1 TypeError: no implicit conversion of String into Integer
expect(setting.reload.value[0][:description]).to eq("Old charge")
I found that in Rails 6.1 the .reload
method internally did not used to call any preload
class method on the object. So it was working before.
Rails 6.1 : github.com/rails/rails/blob/v6.1.7.2/active..
In Rails 7.0, it called .preload
class method which is causing problem.
Rails 7.0.4: github.com/rails/rails/blob/v7.0.4/activere..
calls this method below, which calls preload
method github.com/rails/rails/blob/v7.0.4/activere..
9. image/jpg is not a valid content type
DEPRECATION WARNING: image/jpg is not a valid content type, it should not be used when creating a blob, and support for it will be removed in Rails 7.1. If you want to keep supporting this content type past Rails 7.1, add it to `config.active_storage.variable_content_types`. Dismiss this warning by setting `config.active_storage.silence_invalid_content_types_warning = true`. (called from block (2 levels) in <top (required)> at /Users/username/Projects/app/spec/lib/active_storage/service/cloudfront_s3_service_spec.rb:23)
Solution
- content_type: 'image/jpg'
+ content_type: 'image/jpeg'
10. Zlib::DataError: incorrect header check
Failure/Error: expect(Rails.cache.fetch('cache-key')).to eq('value to cache')
Zlib::DataError:
incorrect header check
Explanation
We were using the following API in Rails 6.1
::Rails.cache.fetch(
opts[:cache_key],
compressed: true, # <-- has no effect in Rails 6
expires_in: opts[:expires_in],
force: opts[:force_cache_refresh]
) do
cache_value_string
end
It was working before because compressed: true
had no meaning and the actual option was compress: true
which was by default true
and everything was fine, data was compressed in the cache. See Here
When I upgraded to Rails 7.0.4, suddenly the cache compression behaviour changed from the default true
to false
. See Here
And, the actual cause of the exception is a potential bug in Rails. When compressed: true
option is used,
while storing the value, there is no issue. Compression is off
while fetching from the cache, it tries to decompress the value and raises error.
Solution
- compressed: true,
+ compress: true,
11. Massing failing of tests - Bullet N+1 Queries
After moving from bullet 6.0.0 to 7.0.7
, we are seeing a lot of N+1 query exceptions. I also tried upgrading bullet to 7.0.7
in the master branch but see no such errors. upon digging I found that, bullet has multiple modules for different version of active-record. Like this is for AR 7.0
github.com/flyerhzm/bullet/blob/7.0.7/lib/b.. github.com/flyerhzm/bullet/blob/7.0.7/lib/b..
Solution
Looking into this I came to the conclusion that these are false positives and need to be whitelisted
# helper method
def bullet_whitelist(whitelist)
whitelist.each do |name, association, type = :n_plus_one_query|
Bullet.add_safelist type: type, class_name: name, association: association
end
yield
Bullet.clear_safelist
end
# Usage
# rails_helper.rb
config.around do |example|
bullet_whitelist([
["User", :account],
["Comment", :article, :unused_eager_loading],
]) do
example.run
end
end
12. Fewer SQL queries are fired
There are several potential reasons why we might be seeing fewer SQL queries fired in Rails 7 compared to Rails 6:
Improved caching: Rails 7 includes several caching improvements that could reduce the need for SQL queries. For example, the new
action caching
feature caches entire controller actions, reducing the need to execute SQL queries on subsequent requests.Better database connection management: Rails 7 includes improvements to the way it manages database connections, which could reduce the number of unnecessary SQL queries. For example, the new
connection pooling
feature helps reduce the overhead of creating and managing database connections.Changes to the way ActiveRecord handles associations: In Rails 7, ActiveRecord has been optimized to handle associations more efficiently, which could reduce the number of SQL queries needed to load associated records.
Other performance optimizations: Rails 7 includes several other performance optimizations that could reduce the number of SQL queries needed for certain operations. For example, improvements to the way ActiveRecord handles
count
queries could reduce the need for additional SQL queries.
Overall, it's likely that a combination of these factors (and possibly others) could be responsible for the reduction in SQL queries you're seeing in Rails 7.
13. Non-URL-safe CSRF tokens are deprecated. Use 6.1 defaults or above.
Failure/Error: require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
ActiveSupport::DeprecationException:
DEPRECATION WARNING: Non-URL-safe CSRF tokens are deprecated. Use 6.1 defaults or above. (called from <top (required)> at
Solution
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.1 # less than 6.1 is not acceptable in Rails 7
14. ArgumentError: missing keywords: :records, :associations
ArgumentError: missing keywords: :records, :associations
from /Users/user/.rbenv/versions/2.7.7/lib/ruby/gems/2.7.0/gems/activerecord-7.0.4.2/lib/active_record/associations/preloader.rb:96:in `initialize'
Solution
def self.eager_load(models, includes)
# This is the old way to preload data and will have argument error in Rails 7
# ActiveRecord::Associations::Preloader.new.preload(Array(models).flatten, includes)
# The is the new way to preload data in Rails 7
ActiveRecord::Associations::Preloader.new(
records: Array(models).flatten,
associations: includes
).call
end
15. ExecJS::ProgramError: Unexpected token: name (FileChecksum)
$ bundle exec rake assets:precompile
ExecJS::ProgramError: Unexpected token: name (FileChecksum) (line: 421, col: 8, pos: 16550)
Solution
I got rid of this error by upgrading the uglifier
gem.
bundle update uglifier
16. Uglifier::Error: Unexpected token: keyword (const)
Uglifier::Error: Unexpected token: keyword (const). To use ES6 syntax, harmony mode must be enabled with Uglifier.new(:harmony => true).
--
412 SparkMD5.ArrayBuffer.hash = function(arr, raw) {
413 var hash = md51_array(new Uint8Array(arr)), ret = hex(hash);
414 return raw ? hexToBinaryString(ret) : ret;
415 };
416 return SparkMD5;
417 }));
Solution
add harmony: true
to Uglifier.new
# Compress JavaScripts and CSS
config.assets.js_compressor = Uglifier.new({
output: {
beautify: true,
preserve_line: true, # Add new lines
indent_level: 0 # Don't add indentation
},
+ harmony: true
})
Now it supports both ES5 and ES6 syntax.
17. to_s(:format) deprecated in favor of to_fs(:format)
To allow Rails applications to use this optimization in the future Rails is deprecating all the reasons we override to_s in those core classes in favour of using to_formatted_s.
You need to make sure all the deprecated API usages are updated accordingly.