Rolf P. wrote in post #979601:
MyClass should have a “virtual hash property”, props.
I want to interact with props, like it was a regular hash, but under the
hood, it should perform calls towards the external library.
Then simply return an object with those characteristics. This is Ruby,
remember 
With MyClass I should be able to to the following:
c = MyClass.new
(1)
c.props # under the hood, this will call SomeClass::get_props() and
convert
the response to a hash
=> {:a=>1, :b=>2, :c=>3}
So you are now saying you must return an actual hash at this point?
Then that is a separate object, and any mutations made will be local to
that hash.
(2)
c.props[:a] # This should make a call to SomeClass::get_prop(“a”)
=> 1
No it won’t, if c.props returned a Hash. So your requirements (1) and
(2) are contradictory.
I suggest you go with option 2: return some object, which when you call
the [] method on that object, it in turn calls SomeClass.get_prop(xxx).
That’s the “proxy” object I was talking about before.
(3)
c.props={:a=>“a”, :b=>“b”, :c=>“c”} # This should make a call to e.g.
SomeClass::set_props(“a”, “a”, “b”, “b”, “c”, “c”)
=> {:a=>“a”, :b=>“b”, :c=>“c”}
That’s easy enough. Your c object can have a props=(h) method which
iterates over h, or turns it into an Array, and calls set_props
accordingly.
(4)
c.props[:d] = “d” # This should make a call to SomeClass::set_prop(“d”)
=> “d”
That’s a different requirement: that the object returned by c.props have
specific behaviour when its []= method is called, similar to (2).
However (3) and (4) are not contradictory. You can implement both if you
wish.
(5)
c.props # Again, make a vall to SomeClass::get_props(), to check
that
new data is stored.
=> {:a=>“a”, :b=>“b”, :c=>“c”, :d=>“d”}
That is the same as (1), which contradicts (2).
Actually, you can have an almost-Hash, by either subclassing Hash, or by
defining singleton methods on a Hash object. Then it can be a Hash which
does magic things when you assign to it. This may or may not be good
design - I try to avoid magic if possible
h = {“foo”=>1, “bar”=>2}
def h.[]=(key,val)
puts “Hah, tricked you, you tried to set #{key} to #{val}!”
end
h
h[“foo”]
h[“foo”]=999
By setting an instance variable on the Hash (more magic) you can
remember which object you want to proxy []= to.
class DataClass
@@data = {:a=>1, :b=>2, :c=>3}
Try to avoid class variables. An instance variable of the class (@data)
would work just as well here. @@var has really strange semantics which I
can never entirely remember, and once you’ve been bitten by them, you’ll
choose to use instance variables as well.
Note that an instance variable of the class is not the same as an
instance variable of an instance of that class.
Just change @@data to @data throughout.
(1) just returns
#PropsClass:0x1418748
Yep, it’s returning that object, not a hash. You can choose between
returning an object with Hash-like properties, or an actual Hash.
and (3) returns
test_props.rb:35:in <main>': undefined method
props=’ for
#<MyClass:0x14202d8 @props=#PropsClass:0x14202c0> (NoMethodError)
You simply forgot to define this method. You have:
def props
@props
end
but you also need:
def props=(val)
# do stuff, e.g. @props.h.replace(val)
# except you haven't made h an accessor; I suggest
# you do, otherwise you need something ugly:
@props.instance_variable_get(:@h).replace(val)
end
I don’t have a method that redefines = in the PropsClass. (This method
cannot be redefined, right?).
foo.bar = baz
is a method called “bar=”, and you can define that.
B.