Brian Candler
5/9/2007 11:07:00 AM
On Wed, May 09, 2007 at 06:05:11PM +0900, Robert Klemme wrote:
> On 08.05.2007 18:55, Brian Candler wrote:
> >On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote:
> >>Brian Candler wrote:
> >>>o1 = [:defuzz_configs, {"conf"=>"foo.txt"}]
> >>>o2 = [:defuzz_configs, {"conf"=>"foo.txt"}]
> >>>p o1 == o2 # prints true
> >>>p o1.hash == o2.hash # prints false ?? <<<<<<<<<<<
> >>That's because the Hashs in o1 and o2 are two different objects.
> >
> >So hashes and arrays don't behave the same in this regard?
> >
> >o1 = ["hello", ["world"]]
> >o2 = ["hello", ["world"]]
> >p o1 == o2
> >p o1.hash == o2.hash # prints true
> >
> >o1 = {"key"=>"hello"}
> >o2 = {"key"=>"hello"}
> >p o1 == o2
> >p o1.hash == o2.hash # prints false
> >
> >I wonder why this is?
>
> Probably because Hashes store not only key value pairs but also default
> value and an optionally block that is invoked if an element is missing
> from the Hash.
OK, then I'm missing some subtlety between hash1 == hash2 (which is true),
and hash1.eql? hash2 (which is false).
But given just arrays, and arrays of arrays, array1 == array2 and
array1.eql? array2 are both true. And so is array1.hash == array2.hash
So it appears that someone has decided that hashes cannot be used usefully
as hash keys, but I cannot see why.
> The solution
> is of course to use your own class for your keys. Since they seem to be
> pretty complex that's a good idea anyway.
I solved my problem by doing a linear search over the hash to find the
entry, which seems silly, but it works.
# Was: return @output[input]
@output.each { |(k,v)| return v if k == input }
Making "my own class" was not an option. I was building a small mock object,
and the hash key is whatever method_missing receives. If the caller of the
mock object passes in a hash, then a hash is what I get.
class Mock
def self.prepare(output)
@output = output
end
def self.[]=(input,output)
@output[input] = output
end
def self.[](input)
@output.each { |(k,v)| return v if k == input }
raise "No response for #{input.inspect}"
end
attr_reader :actions
def initialize(*args)
@actions = [[:initialize] + args]
end
def method_missing(*args)
# STDERR.puts "#{args.inspect} => #{self.class[args].inspect}"
@actions << args
self.class[args]
end
end
Mock.prepare({
[:flurble] => 'boo',
})
a = Mock.new(123)
res = a.flurble
p res
p a.actions
Yes, I know there are other mock solutions out there. I would rather just
write something which is (a) small, and (b) does exactly what I need.
Regards,
Brian.