Friday, March 10, 2023

This Week in Rails: Improve custom namespace autoloading, Object#with and more!

Posted by greg

Hi, this is Greg, bringing you the latest changes in the Rails codebase.

Lockdown rails app in production for security
Current Dockerfile generated by Rails runs as a non-root user which prevents modification of the operating system but leaves wide open all gems and the application itself. This change locks down the application gems and only opens up access to the following directories: db, log, storage, tmp.

Improve support for custom namespaces
This patch improves support for custom root namespaces, and consolidates a direction in the design of the integration of Zeitwerk in Rails. There is a great explanation of the why on how on the pull request.

Add class name to ActiveModel::MissingAttributeError error message
When an attribute is missing the previous message was unclear about which class is missing the attribute, especially when there are multiple classes that could miss the attribute. By adding the class name to the error message, debugging is easier.

Fix nullifying association with composite query constraints
Given a models setup like:

class BlogPost < ApplicationRecord
  query_constraints :blog_id, :id

  has_many :comments, query_constraints: [:blog_id, :blog_post_id]
end

class Comment < ApplicationRecord
  query_constraints :blog_id, :blog_post_id

  belongs_to :blog_post, query_constraints: [:blog_id, :blog_post_id]
end

With this change it is now possible to nullify both blog_post.comments = [] and comment.blog_post = nil association which should result in nullification of all parts of the composite query constraints, meaning blog_id and blog_post_id being nil on the affected comments.

Make job display_name failsafe
The display_name method is used by a delayed job to log information about it, including failure messages. Whenever a job class is moved or deleted, the instances still scheduled cannot be constantized anymore, caused display_name and hence the log method to raise an exception. In certain cases, e.g. when logging happens in a rescue block, this may have terminated the entire delayed job worker. With this change, the worker handles failed jobs gracefully and continues work, all with appropriate log output.

Allow both include and where on PostgreSQL add_index
Recently, support was added for the include option when creating an index in PostgreSQL, however, when using this option in conjunction with the where option - the INCLUDE and WHERE segments where in the incorrect order causing the migration to error. This Pull Request changes there order of the INCLUDE and WHERE segments when adding an index so that the query is valid. It also updates the order they are expected in the create index statement when dumping the schema.

Expand rails route search to all table content
This pull request expands the search field on the rails/info/routes page to also search:

  • Route name (with or without a _path and _url extension)
  • HTTP Verb (eg. GET/POST/PUT etc.)
  • Controller#Action

Before this change, the search field was restricted to the route paths.

Enhance has_secure_password to also generate a password_salt method

With this change, has_secure_password generates an #{attribute}_salt method that returns the salt used to compute the password digest. The salt will change whenever the password is changed, so it can be used to create single-use password reset tokens with generates_token_for:

class User < ActiveRecord::Base
  has_secure_password
  generates_token_for :password_reset, expires_in: 15.minutes do
    password_salt&.last(10)
  end
end

Make irb a railties dependency
This pull request adds irb to the dependencies of railties so users with older versions of Ruby can benefit from the latest version of irb, instead of being limited to the version bundled with their Ruby installation.

Implement Object#with
This pull request adds Object#with to set and restore public attributes around a block:

client.timeout # => 5
client.with(timeout: 1) do
  client.timeout # => 1
end
client.timeout # => 5

More examples and details about this change can be found on the pull request.

You can view the whole list of changes here. We had 30 contributors to the Rails codebase this past week!

Until next time!

Subscribe to get these updates mailed to you.