Skip to content

Rails/FindByOrAssignmentMemoization autocorrect behavior leads to unintuitive test failures #1530

@liangmicha

Description

@liangmicha

In our codebase, we have some code that like this:

def user =  @user ||= User.find_by(id: params.require("user_id"))

This code currently fails the Rails/FindByOrAssignmentMemoization rubocop rule.

Expected behavior

I expect that running rubocop -A will lead to changes that make sense, i.e the following, per the
rails style guide

  def user
      return @user if defined?(@user)

      @user = User.find_by(id: params.require("user_id"))
    end

Actual behavior

Instead, the result is:

    if defined?(@user)
      attr_reader :user
    end

    @user = User.find_by(id: params.require("user_id"))

This leads to a really difficult to understand issue when running bundle exec rspec for any tests on circleCI (but rspec works locally):

An error occurred while loading ./spec/services/recover_care_pathway_answers_spec.rb.
Failure/Error: require File.expand_path("../config/environment", __dir__)

FrozenError:
  can't modify frozen Array: ["/home/circleci/project/lib", "/home/circleci/project/app/channels", "/home/circleci/project/app/components", "/home/circleci/project/app/concerns", "/home/circleci/project/app/config", "/home/circleci/project/app/controllers", "/home/circleci/project/app/dashboards", "/home/circleci/project/app/fields", "/home/circleci/project/app/helpers", "/home/circleci/project/app/jobs", "/home/circleci/project/app/mailers", "/home/circleci/project/app/middleware", "/home/circleci/project/app/models", "/home/circleci/project/app/policies", "/home/circleci/project/app/revocation_strategies", "/home/circleci/project/app/serializers", "/home/circleci/project/app/services", "/home/circleci/project/app/validators", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/solid_cache-1.0.7/app/jobs", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/solid_cache-1.0.7/app/models", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/devise-4.9.4/app/controllers", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/devise-4.9.4/app/helpers", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/devise-4.9.4/app/mailers", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/administrate-0.20.1/app/controllers", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/administrate-0.20.1/app/controllers/concerns", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/administrate-0.20.1/app/helpers", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/activestorage-7.2.2.1/app/controllers", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/activestorage-7.2.2.1/app/controllers/concerns", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/activestorage-7.2.2.1/app/jobs", "/home/circleci/project/vendor/bundle/ruby/3.4.0/gems/activestorage-7.2.2.1/app/models"]
$ rubocop -A --debug
For /Users/m/<redacted>/backend: configuration from /Users/m/<redacted>/backend/.rubocop.yml
Default configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-1.80.2/config/default.yml
Plugin configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-rails-2.33.3/config/default.yml
Plugin configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-rspec-3.7.0/config/default.yml
Plugin configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-rspec_rails-2.31.0/config/default.yml
Plugin configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-performance-1.26.0/config/default.yml
Plugin configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-capybara-2.22.1/config/default.yml
Plugin configuration from /Users/m/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/rubocop-factory_bot-2.27.1/config/default.yml
Inheriting configuration from /Users/m/<redacted>/backend/.rubocop_todo.yml
Use parallel by default.
Running parallel inspection

Offenses:

app/controllers/admin/dashboard_role_creation_codes_controller.rb:35:5: C: [Corrected] Style/AmbiguousEndlessMethodDefinition: Avoid using if statements with endless methods.
    def user = return @user if defined?(@user)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:35:5: C: [Corrected] Style/MultilineIfModifier: Favor a normal if-statement over a modifier clause in a multiline statement.
    def user ...
    ^^^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:35:5: C: [Corrected] Style/TrivialAccessors: Use attr_reader to define trivial reader methods.
    def user
    ^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:35:16: C: [Corrected] Rails/FindByOrAssignmentMemoization: Avoid memoizing find_by results with ||=.
    def user = @user ||= User.find_by(id: params.require("user_id"))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:35:16: C: [Corrected] Style/RedundantReturn: Redundant return detected.
    def user = return @user if defined?(@user)
               ^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:36:3: C: [Corrected] Layout/IndentationWidth: Use 2 (not -2) spaces for indentation.
  return @user
  ^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:36:3: C: [Corrected] Style/RedundantReturn: Redundant return detected.
  return @user
  ^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:36:7: C: [Corrected] Style/TrivialAccessors: Use attr_reader to define trivial reader methods.
      def user
      ^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:37:1: W: [Corrected] Layout/DefEndAlignment: end at 37, 0 is not aligned with def at 35, 4.
end if defined?(@user)
^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:37:1: C: [Corrected] Layout/IndentationConsistency: Inconsistent indentation detected.
@user = User.find_by(id: params.require("user_id"))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:37:1: C: [Corrected] Layout/IndentationWidth: Use 2 (not -2) spaces for indentation.
@user = User.find_by(id: params.require("user_id"))
^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:39:3: C: [Corrected] Layout/IndentationWidth: Use 2 (not 6) spaces for indentation.
        @user = User.find_by(id: params.require("user_id"))
  ^^^^^^
app/controllers/admin/dashboard_role_creation_codes_controller.rb:39:9: C: [Corrected] Layout/IndentationConsistency: Inconsistent indentation detected.
        @user = User.find_by(id: params.require("user_id"))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

3154 files inspected, 13 offenses detected, 13 offenses corrected
Finished in 1.0526759999920614 seconds


Steps to reproduce the problem

See above.

RuboCop version

1.80.2 (using Parser 3.3.9.0, Prism 1.4.0, rubocop-ast 1.46.0, analyzing as Ruby 3.4, running on ruby 3.4.2) [arm64-darwin24]
  - rubocop-rails 2.33.3
  - rubocop-rspec 3.7.0
  - rubocop-rspec_rails 2.31.0
  - rubocop-performance 1.26.0
  - rubocop-capybara 2.22.1
  - rubocop-factory_bot 2.27.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions