Setter method for Hash value

Hi

I guess there is a simple solution to this, but right now, I can’t come
up
with it.

I want to wrap two external functions to behave like a regular hash.

  class MyClass
    def props
      some_data = SomeClass::get_prop
      some_data.to_h
    end
    def props[key]=(value)       # Not a valid construct
      SomeClass::set_prop(key, value)
    end
  end

  props["timeout"]
  =>"10"

  props["timeout"]="20"
  => nil

Now, the getter method works perfectly, but I don’t know how to declare
the
setter method :o(

Anyone?

Kind regards,
Rolf

class MyClass
def []=(key, value)
SomeClass::set_prop(key, value)
end
end

On Fri, Feb 4, 2011 at 12:12 AM, Rolf P. [email protected]
wrote:

def props[key]=(value) # Not a valid construct

just a crude example,

class C
def initialize
@h={}
end
def
@h[index]
end
def []=(index,value)
@h[index]=value
end
def h
@h
end
end
#=> nil
c=C.new
#=> #<C:0x8873e8c @h={}>
c[:a]=1
#=> 1
c[:b]=2
#=> 2
c[:a]
#=> 1
c.h
#=> {:a=>1, :b=>2}
c
#=> #<C:0x8873e8c @h={:a=>1, :b=>2}>

best regards -botp

Try calling it props= for the method name itself.

Something like

def props=(key, value) maybe?

You might want to have a look at
http://www.ruby-doc.org/stdlib/libdoc/forwardable/rdoc/index.html

cheers

Rolf P. wrote in post #979431:

    def props[key]=(value)       # Not a valid construct

def []=(key,value)

  props["timeout"]="20"
  => nil

foo = MyClass.new
foo[“timeout”]=“20”

I see I have forgotten something when extracting code to a more generic
example: making an instance of the class…
So the corrected version:

  class MyClass
    def props
      some_data = SomeClass::get_prop
      some_data.to_h
    end
    def props[key]=(value)       # Not a valid construct
      SomeClass::set_prop(key, value)
    end
  end

  my_instance = MyClass.new
  my_instance.props["timeout"]
  =>"10"

  my_instance.props["timeout"]="20"
  => nil

-Rolf

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.
Under the hood, the implementation calls external methods to retrieve or
store the given values for a given key.

Kind regards,
-Rolf

On Fri, Feb 4, 2011 at 11:22 AM, botp [email protected] wrote:

sorry, ignore the previous unclear post…

try again,

class MyClass
def props
some_data = SomeClass::get_prop
self
end
def []=(key,value)
p “i was called”
SomeClass::set_prop(key, value)
end
end
#=> nil

class SomeClass
class << self
@@h={}
def get_prop
@@h
end
def set_prop key,value
@@h[key]=value
end
def show
@@h
end
end
end
#=> nil

c=MyClass.new
#=> #MyClass:0x8887b94
c.props
#=> #MyClass:0x8887b94
c.props[:a]=1
“i was called”
#=> 1
c.props[:b]=2
“i was called”
#=> 2
c.props
#=> #MyClass:0x8887b94
SomeClass::show
#=> {:a=>1, :b=>2}

best regards -botp

Rolf P. 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.

Thanks for the input, botp.
Your example does the job allright, but designwise, I don’t like that
the
[]= method is made available for MyClass itself.
However, I’ll take whatever works for now! :o)

Best regards,
Rolf

On Fri, Feb 4, 2011 at 2:52 AM, Rolf P. [email protected] wrote:

I’m trying to make getter and setter methods for a virtual hash property
called props. Under the hood, the implementation calls external methods to
retrieve or
store the given values for a given key.

objects receive methods wc return objects, wc in turn can receive
another method, and so fort on the chain…

eg,

class MyClass
def props
some_data = SomeClass::get_prop
end
def []=(key,value)
SomeClass::set_prop(key, value)
end
end
#=> nil

class SomeClass
class << self
@@h={}
def get_prop
@@h
end
def set_prop key,value
@@h[key]=value
end
def show
@@h
end
end
end
#=> nil

c=MyClass.new
#=> #MyClass:0x888f6b4
c.props
#=> {}
c.props[:a]=1
#=> 1
c.props[:b]=2
#=> 2
c.props
#=> {:a=>1, :b=>2}
SomeClass::show
#=> {:a=>1, :b=>2}

best regards -botp

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 methodprops=’ 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?
: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

What about this:

class MyClass
def props
@props = DataClass::get_props()
Props.new @props
end

def props=(h)
DataClass::set_props h
end
end

class Props
def initialize h
@h = h
end

def
DataClass::get_prop key
end

def []=(key,value)
DataClass::set_prop key,value
end

def inspect; @h.inspect; end
def to_s; @h.to_s; end
end

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

Jesus.

Hi Jesus

It does seem to be working. At least the example “requirements” that I
defined does give the expected response.
I have to admit, I need to study this a bit, to figure out HOW it is
working, though. ;o)

I will try to incorporate this into the actual code now, and check if it
still holds.

Thanks.

Best regards,
Rolf

2011/2/4 Jess Gabriel y Galn [email protected]

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 :slight_smile:

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 methodprops=’ 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.

Hi Jesus

2011/2/4 Jess Gabriel y Galn [email protected]

1.- Calls on the instance of MyClass: props and props=
can implement whatever methods you need to behave like a hash. As an
I completely agree. I was in a rush when I wrote my previous post, so
irb(main):037:2> end
irb(main):046:0> c.props
=> 55
irb(main):052:0> c.props
=> {:b=>0, :a=>55, :c=>5, :d=>6}

I hope it’s clearer now. Let me know if you need further help
understanding this.

Jesus.

Clear as crystal. Just what I needed! :o)
Appreciate all the help I got on this.

Best regards,
Rolf

On Fri, Feb 4, 2011 at 5:03 PM, Rolf P. [email protected] wrote:

Hi Jesus

It does seem to be working. At least the example “requirements” that I
defined does give the expected response.
I have to admit, I need to study this a bit, to figure out HOW it is
working, though. ;o)

You have two sets of requirements:

1.- Calls on the instance of MyClass: props and props=
2.- Calls on the object returned by the previous props calls: [] and []=

You need to forward these 4 calls to another class. The first two are
easy: we just implement the MyClass#props and MyClass#props= to do
what we want.

For the other two, you need a proxy object that can delegate [] and
[]= to DataClass too. For this to work, MyClass#props needs to wrap
the actual hash returned by DataClass with a wrapper object (Props).
As you need this proxy object to have some hash like behaviour, you
can implement whatever methods you need to behave like a hash. As an
example, apart from [] and []= I have implemented inspect and to_s.

On Fri, Feb 4, 2011 at 3:45 PM, Brian C. [email protected]
wrote:

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.

I completely agree. I was in a rush when I wrote my previous post, so
I left it as it is. Using instance variables on the DataClass object
is cleaner:

irb(main):030:0> class DataClass
irb(main):031:1> @data = {:a=>1, :b=>2, :c=>3}
irb(main):032:1> def self.get_props()
irb(main):033:2> @data
irb(main):034:2> end
irb(main):035:1> def self.get_prop(key)
irb(main):036:2> @data[key]
irb(main):037:2> end
irb(main):038:1> def self.set_props(data)
irb(main):039:2> @data = data
irb(main):040:2> end
irb(main):041:1> def self.set_prop(key, value)
irb(main):042:2> @data[key] = value
irb(main):043:2> end
irb(main):044:1> end
=> nil
irb(main):045:0> c = MyClass.new
=> #MyClass:0xb7881b40
irb(main):046:0> c.props
=> {:b=>2, :a=>1, :c=>3}
irb(main):047:0> c.props = {:a => 3, :b => 0, :c => 5, :d => 6}
=> {:b=>0, :a=>3, :c=>5, :d=>6}
irb(main):048:0> c.props
=> {:b=>0, :a=>3, :c=>5, :d=>6}
irb(main):049:0> c.props[:a]
=> 3
irb(main):050:0> c.props[:a] = 55
=> 55
irb(main):051:0> c.props[:a]
=> 55
irb(main):052:0> c.props
=> {:b=>0, :a=>55, :c=>5, :d=>6}

I hope it’s clearer now. Let me know if you need further help
understanding this.

Jesus.

2011/2/4 Jess Gabriel y Galn [email protected]

For the other two, you need a proxy object that can delegate [] and
[]= to DataClass too. For this to work, MyClass#props needs to wrap
the actual hash returned by DataClass with a wrapper object (Props).
As you need this proxy object to have some hash like behaviour, you
can implement whatever methods you need to behave like a hash. As an
example, apart from [] and []= I have implemented inspect and to_s.

Instead of implementing other methods to make the wrapper object behave
more
like a Hash object, why not let Props inherit Hash, and just redefine
those
methods that I need to behave differently? Perhaps like this:

class Props < Hash
def initialize h
replace h
end
def

end
def []=(key, value)

end
end

Best regards,
-Rolf

On Sun, Feb 6, 2011 at 10:23 PM, Rolf P. [email protected]
wrote:

Instead of implementing other methods to make the wrapper object behave more
def []=(key, value)

end
end

Hi,

In fact I was going to follow up the discussion with a further idea.
What I sent has a flaw, and it’s the fact that the Props object gets
initialized with the hash, while ideally it should delegate also all
reads to the DataClass. It doesn’t make sense to have a reference to
the resulting hash. This way, if the hash in DataClass is modified by
other means, the new values will be available to all instances of
Props (in the current implementation this might be true, because we
are returning references to the same hash, but this can be a weak
point). It’s cleaner this way, in my opinion:

class MyClass
def props
Props.new
end

def props=(h)
DataClass::set_props h
end
end

class Props
def
DataClass::get_prop key
end

def []=(key,value)
DataClass::set_prop key,value
end

def inspect; DataClass::get_props.inspect; end
def to_s; DataClass::get_props.to_s; end
end

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

As for your question about inheriting from Hash: read a bit about
Inheritance vs Composition. It’s generally considered that Composition
is more flexible.

Jesus.