How to make Ruby var= return value assigned, not value passed in?
ruby
Solution
c.foo ||= []
c.foo << 5
Using two lines of code isn't the end of the world, and it's easier on the eyes.
Problem
There's a nice idiom for adding to lists stored in a hash table: ``` (hash[key] ||= []) << new_value ``` Now, 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. Here'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: ``` class C attr_reader :foo def foo=(value) @foo = (value.is_a? Array) ? (value.clone) : value end end c=C.new puts "assignment: #{(c.foo ||= []) << 5}" puts "c.foo is #{c.foo}" puts "assignment: #{(c.foo ||= []) << 6}" puts "c.foo is #{c.foo}" ``` output is ``` assignment: [5] c.foo is [] assignment: [6] c.foo is [6] ``` When 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: ``` c=C.new puts "clunky assignment: #{(c.foo || c.send(:foo=, [])) << 5}" puts "c.foo is #{c.foo}" puts "assignment: #{(c.foo || c.send(:foo=, [])) << 6}" puts "c.foo is #{c.foo}" ``` which prints ``` clunky assignment: [5] c.foo is [5] assignment: [5, 6] c.foo is [5, 6] ``` Is there any more elegant way to do this?