Sunday, 11 August 2013

How to make Ruby var= return value assigned, not value passed in?

How to make Ruby var= return value assigned, not value passed in?

pThere's a nice idiom for adding to lists stored in a hash table:/p
precode(hash[key] ||= []) lt;lt; new_value /code/pre pNow, suppose I write
a derivative hash class, like the ones found in Hashie, which does a
deep-convert of any hash I store in it. Then what I store will not be the
same object I passed to the = operator; Hash may be converted to Mash or
Clash, and arrays may be copied./p pHere's the problem. Ruby apparently
returns, from the var= method, the value passed in, not the value that's
stored. It doesn't matter what the var= method returns. The code below
demonstrates this:/p precodeclass C attr_reader :foo def foo=(value) @foo
= (value.is_a? Array) ? (value.clone) : value end end c=C.new puts
assignment: #{(c.foo ||= []) lt;lt; 5} puts c.foo is #{c.foo} puts
assignment: #{(c.foo ||= []) lt;lt; 6} puts c.foo is #{c.foo} /code/pre
poutput is/p precodeassignment: [5] c.foo is [] assignment: [6] c.foo is
[6] /code/pre pWhen I posted this as a bug to Hashie, Danielle Sucher
explained what was happening and pointed out that foo.send :bar=, 1
returns the value returned by the bar= method. (Hat tip for the research!)
So I guess I could do:/p precodec=C.new puts clunky assignment: #{(c.foo
|| c.send(:foo=, [])) lt;lt; 5} puts c.foo is #{c.foo} puts assignment:
#{(c.foo || c.send(:foo=, [])) lt;lt; 6} puts c.foo is #{c.foo} /code/pre
pwhich prints/p precodeclunky assignment: [5] c.foo is [5] assignment: [5,
6] c.foo is [5, 6] /code/pre pIs there any more elegant way to do this?/p

No comments:

Post a Comment