From: Rolf Pedersen Date: 2011-02-04T22:32:53+09:00 Subject: Re: Setter method for Hash value --0016368321b2221e9f049b74ebed Content-Type: text/plain; charset=ISO-8859-1 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 # and (3) returns test_props.rb:35:in `
': undefined method `props=' for #> (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? :o( About your other suggestion, delegates, I have to study this a bit more. I'm still a low-level Ruby programmer :o) Best regards, Rolf On Fri, Feb 4, 2011 at 9:56 AM, Brian Candler wrote: > 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 :orig_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. > > -- > Posted via http://www.ruby-forum.com/. > > --0016368321b2221e9f049b74ebed--