iTranslated by AI
Understanding the RuboCop Suggestion: RuboCop::Cop::Rails::Presence
While refactoring with RuboCop, I received the following suggestion in a model method.
C: [Correctable] Rails/Presence:
Use [processing to manipulate an array of IDs from another table].presence || []
instead of [processing to manipulate an array of IDs from another table].blank? ? [] : [processing to manipulate an array of IDs from another table]
variable = [processing to manipulate an array of IDs from another table] ? [] : [processing to manipulate an array of IDs from another table]
The original code was simple: if the result of processing the array was an empty array, it returned an empty array; otherwise, it returned the processed result. However, it repeated the same expression twice, which did not follow the DRY (Don't Repeat Yourself) principle.
Through this suggestion, I learned how to write it more concisely. Since it was a pattern I hadn't encountered before, I'll summarize it here.
About Short-Circuit Evaluation
In the case of ||,
If the left side of || is true, it returns true regardless of what's on the right.
If the left side of || is false, it returns true if the right side is true, and false if the right side is false.
In other words, if the left side of && is false or the left side of || is true, there's no need to look at (evaluate) the value on the right side. If the language is designed such that it doesn't evaluate the right side in these cases, it might reduce some redundancy. This specification is called "short-circuit evaluation."
In this case, if [processing to manipulate an array of IDs from another table].presence is truthy, it returns that value; otherwise, it returns the boolean value of the right side.
Actually, what it returns follows the rules described in the following article, so it will be the result of the processing or an empty array, rather than a boolean value.
const name = user.name || "Mr./Ms. Anonymous";
Me: "Ah, I think I've seen this before."
Me: "If the user's name exists, it assigns that name."
Me: "If it doesn't exist, it assigns 'Mr./Ms. Anonymous' for now. That's how it works."
Me: "You can write it cleanly without using an if statement."
In this instance, I was using a ternary operator instead of an if statement, but I was still able to write it concisely.
About the presence method
The RuboCop page contains the following description:
This cop checks code that can be written more easily using
Object#presencedefined by Active Support.
It says it checks for cases that can be written easily using the presence method. Since the presence method itself isn't explained there, let's look at another page.
Returns the receiver if it’s present otherwise returns nil.
It returns the receiver if it exists, otherwise it returns nil. The examples provided also use short-circuit evaluation.
state = params[:state] if params[:state].present?
country = params[:country] if params[:country].present?
region = state || country || 'US'
region = params[:state].presence || params[:country].presence || 'US'
As mentioned in the following article, there are various ways to write things concisely, so I want to use them proactively.
Discussion