Gustav Bertram
Gustav Bertram

Reputation: 14911

Does @x ||= work like return @x if @x.present?

Is this:

def x?
  return @x if @x.present?

  @x = #boolean calculation
end

Equivalent to this for boolean values of @x?

def x?
  @x ||= #boolean calculation
end

Upvotes: 2

Views: 235

Answers (3)

Jay-Ar Polidario
Jay-Ar Polidario

Reputation: 6603

TL;DR: No

Just to add more examples to the answers already above:

def x1?(new_value)
  return @x if @x.present?

  @x = new_value
end

def x2?(new_value)
  @x ||= new_value
end

say @x was a Number:

@x = 123
x1?('new value')
@x
# => 123

@x = 123
x2?('new value')
@x
# => 123

# 123 == 123, so this works for `Number`

but let's say @x was an empty Array:

@x = []
x1?('new value')
@x
# => 'new value'

@x = []
x2?('new value')
@x
# => []

# 'new value' != [], so it doesn't work for empty Array.

^ and there are all other "types" that this doesn't work as well not just empty Array, some of which have already been answered by others here.

Upvotes: 0

fphilipe
fphilipe

Reputation: 10054

It depends.

@x ||= value

is equivalent to

@x = @x || value

which assigns value to @x only if @x is falsy. In Ruby, only false and nil are falsy.

Further, #present? is a concept from Rails (see doc).

Note though that depending on the value that you expect to store in @x, it might be equivalent. #present? is simply the negation of #blank? (also a Rails concept). #blank? is defined on Object as follows:

def blank?
  respond_to?(:empty?) ? !!empty? : !self
end

Thus, the behavior of the two snippets you posted is guaranteed to be equivalent when @x contains a value that doesn't define its own #present?, #blank?, or #empty? methods.

Now, FalseClass and NilClass both define #blank?:

def blank?
  true
end

TrueClass also defines #blank?:

def blank?
  false
end

But this is just an optimization as the default implementation from Object would result in the same values.

Therefore, false and nil will return false for #present? and true will return true for #present?.

From this we conclude that, in the specific case of storing boolean values in @x, the behavior of the two snippets is equivalent.

Upvotes: 0

engineersmnky
engineersmnky

Reputation: 29478

You should not use either option for memoizing boolean values as both will recalculate if @x is false.

present? is a special kind of rails check that equates to !blank? and false.blank? #=> true but even if this was not a boolean check present? and || are not equivalent. For objects that implement empty? blank defers to that so something that is empty? is also blank? and thus is not present?.

"".present? #=> false
"" || true #=> "" 
[].present? #=> false
[] || true #=> [] 
false.present? #=> false
false || true #=> true

@x ||= some_logic equates to @x = @x || some_logic where obviously if @x is false some_logic will fire.

If you just want to see if @x has already been determined to be a value (e.g. not nil) then you could replace this with

def x? 
  return @x unless @x.nil? 
  @x = some_logic
end

Upvotes: 6

Related Questions