2

How to do the following in Ruby?

It does not work as expected because of pass-by-value params.

def validate! msg
  if msg.is_a?(String)
    msg = [ msg ]
  end
end

#replace could be used if msg were being replaced with another string.

B Seven
  • 44,484
  • 66
  • 240
  • 385
  • 2
    what do you mean by `How to do the following in Ruby?`? Isn't that already a ruby code? – Raj Feb 23 '17 at 23:03
  • @emaillenin - I mean `msg` is not updated. I edited the question. – B Seven Feb 23 '17 at 23:04
  • 2
    Explain what you're trying to accomplish. It sounds a bit like you're trying to make the msg _into_ an array. If so, then this is not possible. There are some objects with methods that can mutate the object, but even in these cases, you cannot mutate an object "into" a different class. The correct course of action is to just use a new variable and set it equal to `[msg]`. Doing that with a method is fine. Also, `String` needs to be capitalized or that won't run. – Glyoko Feb 23 '17 at 23:07
  • return `msg` and update in the caller code – Raj Feb 23 '17 at 23:09
  • Possible duplicate of ['pass parameter by reference' in Ruby?](http://stackoverflow.com/questions/161510/pass-parameter-by-reference-in-ruby) – Brad Werth Feb 23 '17 at 23:14
  • It's somewhat related, but I don't know if it qualifies as a duplicate. In any case, @emaillenin, I would strongly caution against using the answer in that link. It technically works, but it's a really bad practice to get into. That answer involves passing the current context to the receiving method, and essentially circumvents any benefits you get from having different scopes. Like with any other programming language, there are certain practices that you should adhere to write the most maintainable code. In Ruby the "correct way to do things" would be to just create a new variable. – Glyoko Feb 23 '17 at 23:18
  • Yeah, the real answer is basically, you don't. Also semi-related: http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value and (less so, but sortof similar) http://stackoverflow.com/questions/3660563/why-doesnt-ruby-support-i-or-i-increment-decrement-operators – Brad Werth Feb 23 '17 at 23:20
  • Thanks for all the comments. The goal was to mutate the msg argument. – B Seven Feb 23 '17 at 23:32

2 Answers2

2

As the other answer has pointed out, you can't mutate in place a String into an Array.
You can reset the pointer with your defined method. But you can actually do one better by using #Array:

msg = 'string'
msg = Array(msg)
#=> msg = ['string']

msg = ['array']
msg = Array(msg)
#=> msg = ['array']
Mickey Sheu
  • 758
  • 7
  • 16
1

While some classes have instance methods that allow you to mutate objects, you cannot mutate an object into another class. In this case, you cannot mutate a String object into an Array object.

As you have it, your code will return [msg] if the message is a string. Otherwise it would return nil. To make the method always return an array, you could use a ternary like so:

def validate! msg
  msg.is_a?(String) ? [msg] : msg
end

# ...

my_msg = "message"
my_validated_message = validate!(my_msg) # => ["message"]

my_already_valid_message = ["different message"]
my_revalidated_message = validate!(my_already_valid_message) # => ["different message"]
Glyoko
  • 2,071
  • 1
  • 14
  • 28