The method in question that I want to use is gem
and sourced here (lines 17-42): https://github.com/rails/rails/blob/master/railties/lib/rails/generators/actions.rb
As you can see, name
is assigned to the first arg on line 19, then message
is assigned to name
on line 23 and finally message
is mutated on line 26 with <<
. This unfortunately means that the string I am passing in as the first argument is mutated outside of the method.
I have a hash of arrays and am iterating over them as follows:
groups = { foo: %w(foo, bar), bar: %w(foobar) }
groups.each do |group, gems|
gems.each do |name|
gem(name, "42")
end
end
Afterwards my hash looks like this due to the mutation inside of gem
:
groups => { foo: ["foo (42)", "bar (42)"], bar: ["foobar (42)"] }
How can I prevent these strings (and the hash and its arrays) from being mutated but without breaking the method?
You may call it with name.dup
:
gem(name.dup, "42")
Background: With gem(name)
you pass the parameter to the method. Any modification inside the called method, will change also the the original variable.
With name.dup
you make a copy of the object. This copy is modified inside the called method, but the original value is unchanged.
A warning: dup
does not work always, it is depending on the data. dup
doesn't make a deep copy. See this example:
arr = ['a', 'b']
arr.dup.map{|x| x << '1'}
p arr #["a1", "b1"]
Explanation: The array arr
is copied, but not the content inside the array.
Inside the map
you modify the data of the copied array. But the elements of the original and the copied array are the same. So you also change the content of the original array.
def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is call-by-#{baz}"
- Jörg W Mittag 2012-04-03 22:46
def foo(bar) bar << ', but those values are typically references' end; baz = 'value'; foo(baz); puts "Ruby is call-by-#{baz}"
dbenhur 2012-04-04 00:34
<<
is defined to behave? I don't understand what this has to do with being call-by-value or call-by-reference, though - Jörg W Mittag 2012-04-04 09:18
def a(p); p = 5; end; x = 4;a(x);p x
when you make a new assignment to the parameter, the original value is unchanged. But if you modify the object, you modify the original object. Maybe this wikipedia section for Call by sharing helps to understand it (and if I understand Jörg correct, the wikipedia article contains an error about ruby - knut 2012-04-04 19:18
You can't prevent the method from mutating its arguments (other than by filing a bug report, because it's something it shouldn't do).
What you can do is call the method with a clone of your string, like so:
gem(name.dup, "42")