Hash.merge!(other) vs hash = hash.merge(other)

I tried to replace

self.fixture_class_names = self.fixture_class_names.merge(class_names)

with

self.fixture_class_names.merge!(class_names) # Why does this fail?

in rails source, and it failed 2 tests.

What is the difference between the two?
Could it have something to do with the fact that the fixture_class_names
hash has a block to generate values that were not assigned?
Or is this because fixture_class_names is a class attribute?

On 1 December 2011 20:57, Alexey M. [email protected] wrote:

I tried to replace

self.fixture_class_names = self.fixture_class_names.merge(class_names)

with

self.fixture_class_names.merge!(class_names) # Why does this fail?

At a guess, I’d say because “self.fixture_class_names=” is a method
that does some setting within it, while with the other syntax, you’re
trying to call a bang method on the return value of a method.

On Dec 1, 9:16pm, Michael P. [email protected] wrote:

At a guess, I’d say because “self.fixture_class_names=” is a method
that does some setting within it, while with the other syntax, you’re
trying to call a bang method on the return value of a method.

Getting very warm! self.fixture_class_names is (or at least was -
haven’t checked rails 3.1) a superclass_delegating_accessor.
This means that when you call it on a subclass it travels up the
inheritance hierarchy looking for a class where it has been set.
So if we had classes A < B < C < D, and A.fixture_class_names = x then
calling D.fixture_class_names checks D, then C, then B a lastly A
where it stops, since a value has been defined for A.
When you call C.fixture_class_names = y that doesn’t change what B
does (it checks B and then A), but it changes what D does (it checks
D, then C and returns y).
However, if you do C.fixture_class_names.merge! (without having
called C.fixture_class_names=) that will change the value that
‘belongs’ to A, thus changing what A.fixture_class_names and what
B.fixture_class_names returns

Fred

Frederick C. wrote in post #1034631:

On Dec 1, 9:16pm, Michael P. [email protected] wrote:

At a guess, I’d say because “self.fixture_class_names=” is a method
that does some setting within it, while with the other syntax, you’re
trying to call a bang method on the return value of a method.

Getting very warm! self.fixture_class_names is (or at least was -
haven’t checked rails 3.1) a superclass_delegating_accessor.
This means that when you call it on a subclass it travels up the
inheritance hierarchy looking for a class where it has been set.
So if we had classes A < B < C < D, and A.fixture_class_names = x then
calling D.fixture_class_names checks D, then C, then B a lastly A
where it stops, since a value has been defined for A.
When you call C.fixture_class_names = y that doesn’t change what B
does (it checks B and then A), but it changes what D does (it checks
D, then C and returns y).
However, if you do C.fixture_class_names.merge! (without having
called C.fixture_class_names=) that will change the value that
‘belongs’ to A, thus changing what A.fixture_class_names and what
B.fixture_class_names returns

Fred

Thanks for the explanation. fixture_class_names is in fact declared
with

class_attribute :fixture_class_names

Didn’t you mean D < C < B < A ?

Alexey.

On Dec 2, 12:42pm, Alexey M. [email protected] wrote:

Frederick C. wrote in post #1034631:

class_attribute :fixture_class_names

The implementation is a bit different but the no-no on mutating the
blah.fixture_class_names is the same

Didn’t you mean D < C < B < A ?

yes :slight_smile:

A simpler example in which merge! and merge= wouldn’t be the same
would be

class Foo
def self.bar
@bar.dup #prevent people mutating me!
end

def self.bar=(value)
@bar = value
end
end

Clearly Foo.bar.merge!(…) will not accomplish much here

Fred