[Note: parts of this message were removed to make it a legal post.]
Hei Brian
Thanks for the answer.
I do have a solution now which works for c.props[:key] and c.props[:key] =
"value", but that solves just part of my problem.
I realize that I need to elaborate on the "requirements" a bit further.
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.
SomeClass (renamed to DataClass in code below) just represents a sample
external library with some functions getting or setting some internal data:
* get_props() # Gets all the properties, data format is not
important, could be a comma separated list. The receiver should handle the
conversion to Hash.
* get_prop(key) # Gets one value for a given key
* set_props(data) # Set all the properties. Again, data format is not
important.
* set_prop(key, value) # Set one particular property.
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}
(2)
c.props[:a] # This should make a call to SomeClass::get_prop("a")
=> 1
(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"}
(4)
c.props[:d] = "d" # This should make a call to SomeClass::set_prop("d")
=> "d"
(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"}
Now, following your first suggestion, Brian, I came up with the following
code, where I use a DataClass which represents the external interface, and
which happens to operate on Hash data, to make it simple:
class DataClass
@@data = {:a=>1, :b=>2, :c=>3}
def self.get_props()
@@data
end
def self.get_prop(key)
@@data[key]
end
def self.set_props(data)
@@data = data
end
def self.set_prop(key, value)
@@data[key] = value
end
end
class PropsClass
def [](key)
DataClass::get_prop(key)
end
def []=(key, value)
DataClass::set_prop(key, value)
end
end
class MyClass
def initialize
@props = PropsClass.new
end
def props
@props
end
end
Now this works for (2) and (4), but not for (1) and (3).
(1) just returns
#<PropsClass:0x1418748>
and (3) returns
test_props.rb:35:in `<main>': undefined method `props=' for
#<MyClass:0x14202d8 @props=#<PropsClass:0x14202c0>> (NoMethodError)
The reason is obvious. I haven't even found a way to call get_props and
set_props.
Clearly, something is missing.
I don't have a method that redefines = in the PropsClass. (This method
cannot be redefined, right?).
And according to my requirements, c.props should not return the PropsClass
instance, but rather call and return a value from a PropsClass function,
which I assume is not possible to do either.
So, I don't understand how (if possible) to go about achieving what I
want.... Maybe this is a dead end?
(
About your other suggestion, delegates, I have to study this a bit more. I'm
still a low-level Ruby programmer
)
Best regards,
Rolf
Rolf Pedersen wrote in post #979464:
Hi Brian (and others who have contributed with suggestions along the
same
line)
It's not the class MyClass that I'd like to behave like a Hash.
I'm trying to make getter and setter methods for a virtual hash property
called props.
Then the 'props' object which you return needs methods [] and []=
defined, which do whatever you want them to do.
I see you are using these methods on the class itself (not instances of
the class):
SomeClass::set_prop(key, value)
In that case, you can define them as class methods:
class SomeClass
def self.[](key)
get_prop(key)
end
def self.[]=(key,value)
set_prop(key,value)
end
end
And then simply return the class itself:
class MyClass
def props
SomeClass
end
end
If you were using *instances* of SomeClass, then personally I would just
define methods [] and []= as instance methods. Depending on how
Hash-like you want this object to be, you may want to define some other
methods too.
If there is some particular reason why you can't do that, then you would
have to return a proxy object, whose [] and []= methods are relayed to
get_prop and set_prop on the underlying SomeClass object.
If you still want props to return an actual Hash, then it will have to
be a Hash which has been modified so that []= performs the operation on
the underlying object.
You can use delegate.rb, or you can explicitly delegate those methods
which you want to. Here is some code from CouchTiny which wraps a Hash:
module CouchTiny
# A class which delegates a small subset of methods to a Hash (to keep
# the space as clear as possible for accessor names)
class DelegateDoc
attr_accessor :doc
def initialize(doc = {})
@doc = doc.to_hash
end
def to_hash
@doc
end
def ==(other)
@doc == other # .to_hash is done by Hash#==
end
def to_json(*args)
@doc.to_json(*args)
end
alias
rig_respond_to? :respond_to?
def respond_to?(*m)
orig_respond_to?(*m) || @doc.respond_to?(*m)
end
def [](k)
@doc[k.to_s]
end
def []=(k,v)
@doc[k.to_s] = v
end
def key?(k)
@doc.key?(k)
end
def has_key?(k)
@doc.has_key?(k)
end
def delete(k)
@doc.delete(k)
end
def merge!(h)
@doc.merge!(h)
end
def update(h)
@doc.update(h)
end
def dup
self.class.new(@doc.dup)
end
#def method_missing(m, *args, &block)
# @doc.__send__(m, *args, &block)
#end
end
end
Regards,
Brian.