Class: RuboCop::Cop::Rails::FindByOrAssignmentMemoization

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Defined in:
lib/rubocop/cop/rails/find_by_or_assignment_memoization.rb

Overview

Avoid memoizing ‘find_by` results with `||=`.

It is common to see code that attempts to memoize ‘find_by` result by `||=`, but `find_by` may return `nil`, in which case it is not memoized as intended.

Examples:

# bad - exclusively doing memoization
def current_user
  @current_user ||= User.find_by(id: session[:user_id])
end

# good
def current_user
  return @current_user if defined?(@current_user)

  @current_user = User.find_by(id: session[:user_id])
end

# bad - method contains other code
def current_user
  @current_user ||= User.find_by(id: session[:user_id])
  @current_user.do_something
end

# good
def current_user
  if defined?(@current_user)
    @current_user
  else
    @current_user = User.find_by(id: session[:user_id])
  end
  @current_user.do_something
end

Constant Summary collapse

MSG =
'Avoid memoizing `find_by` results with `||=`.'
RESTRICT_ON_SEND =
i[find_by].freeze

Instance Method Summary collapse

Instance Method Details

#on_def(node) ⇒ Object

When a method body contains only memoization, the correction can be more succinct.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rubocop/cop/rails/find_by_or_assignment_memoization.rb', line 58

def on_def(node)
  find_by_or_assignment_memoization(node.body) do |varible_name, find_by|
    add_offense(node.body) do |corrector|
      corrector.replace(
        node.body,
        "          return \#{varible_name} if defined?(\#{varible_name})\n\n          \#{varible_name} = \#{find_by.source}\n        RUBY\n      )\n\n      correct_to_regular_method_definition(corrector, node) if node.endless?\n    end\n  end\nend\n".rstrip

#on_send(node) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rubocop/cop/rails/find_by_or_assignment_memoization.rb', line 75

def on_send(node)
  assignment_node = node.parent
  find_by_or_assignment_memoization(assignment_node) do |varible_name, find_by|
    next if assignment_node.each_ancestor(:if).any?

    add_offense(assignment_node) do |corrector|
      corrector.replace(
        assignment_node,
        "          if defined?(\#{varible_name})\n            \#{varible_name}\n          else\n            \#{varible_name} = \#{find_by.source}\n          end\n        RUBY\n      )\n    end\n  end\nend\n".rstrip