Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setter method return self not work, it's a bug? [duplicate]

Tags:

ruby

I want to iterate an array of strings, and assign each of them to a fresh instance of class User, and I expect that I will got an array of User objects:

class User
  def name=(name)
    @name = name
    self
  end
end

original_array = ["aaa", "bbb", "bbb"]
result = original_array.collect { |str| User.new.name = str }

but the result is an array of strings!

puts result.inspect            # => ["aaa", "bbb", "bbb"]
puts result === original_array # => true

I have no idea of where I went wrong?

like image 699
hbin Avatar asked May 12 '14 10:05

hbin


1 Answers

What's wrong here is that User.new.name = str returns str, so the value of str gets collected.

Why does it return str? Because, opposed to any other Ruby method, every Ruby setter method returns the passed value, regardless the returned value in the method. For more infos about this behaviour you can check this other SO answer.

Below a IRB-ready Proof of Concept:

def name=(name)
  @name = 'another value'
end

returned_value = (self.name = 'a value')

returned_value #=> 'a value'
@name          #=> 'another value'

What you want can be done in this ways:

  • This syntax is valid for any Ruby object, as it uses Object#tap:

    User.new.tap { |v| v.name = str }
    
  • If User is an ActiveRecord model, as I guess, you can use one of these slightly shorter syntaxes:

    User.new name: str
    User.new { |v| v.name = str }
    
like image 112
mdesantis Avatar answered Sep 21 '22 03:09

mdesantis