[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

OpenStruct#update ?

T. Onoma

11/14/2004 8:42:00 PM

How 'bout an OpenStruct#update for adding values after initialization. Or is
there another way to do?

Thanks,
T.


71 Answers

dblack

11/14/2004 8:51:00 PM

0

T. Onoma

11/14/2004 9:07:00 PM

0

On Sunday 14 November 2004 03:42 pm, trans. (T. Onoma) wrote:
| How 'bout an OpenStruct#update for adding values after initialization. Or
| is there another way to do?

Hmm.. I also just had another related thought. I might also be useful if a
normal object could be "OpenStruct'd". I have a need for it at the moment.
Currently I take an object in as input and then dynamically define instance
vars with accessors on it as required, but often no initial object is given
in which case an OpenStruct is much easier to use. I'd rather just have the
OpenStruct capability from the get go.

T.


T. Onoma

11/14/2004 9:14:00 PM

0

Hi David,

On Sunday 14 November 2004 03:51 pm, David A. Black wrote:
| On Mon, 15 Nov 2004, trans. (T. Onoma) wrote:
| > How 'bout an OpenStruct#update for adding values after initialization. Or
| > is there another way to do?
|
| OpenStruct needs to be very conservative about what methods it has,
| since the whole point of it is to allow you to make up arbitrarily
| named members.

Why so conservative? So they don't overwrite standard object methods? I
wondered why accessors weren't used. I suppose a hash is faster too. Hek,
maybe '@' itself should be hash and forget about it ;)

| But I'm also not sure what you need to do that you can't do. The
| OpenStruct object should let you add values indefinitely.

This is what I mean:

o = OpenStruct.new( foo_hash )

# later ...

o.update( bar_hash )

The reason is because I am modifying and using the object on the fly.

Thanks,
T.


dblack

11/14/2004 9:28:00 PM

0

T. Onoma

11/14/2004 10:20:00 PM

0

On Sunday 14 November 2004 04:27 pm, David A. Black wrote:
| > Why so conservative? So they don't overwrite standard object methods? I
| > wondered why accessors weren't used. I suppose a hash is faster too. Hek,
| > maybe '@' itself should be hash and forget about it ;)
|
| I'm assuming so. Actually here's what happens if you try:
|
| irb(main):020:0> o = OpenStruct.new
| => <OpenStruct>
| irb(main):021:0> o.class = 1
| => 1
| irb(main):022:0> o.class
| => OpenStruct

Ouch. I think that may be a bit too conservative. It's better that someone can
shoot themselves in the foot rather than having someone else do it for them.
I just got stung with:

require 'yaml'

q = YAML.load %Q{
---
x: 10
y: 20
...
}

o = OpenStruct.new( q )

o.instance_eval { x } #=> 10
o.instance_eval { y } #=> nil


| > | But I'm also not sure what you need to do that you can't do. The
| > | OpenStruct object should let you add values indefinitely.
| >
| > This is what I mean:
| >
| > o = OpenStruct.new( foo_hash )
| >
| > # later ...
| >
| > o.update( bar_hash )
| >
| > The reason is because I am modifying and using the object on the fly.
|
| It's easy to write:
|
| o = OpenStruct.new({ :a => 1 })
| def o.update(h); h.each {|k,v| send("#{k}=",v)}; end
| o.update({ :a => 2})
| p o.a
|
| # => 2

Cool. although I prefer to build it in. I looked at the ostruct.rb code. Very
simple. All the "safety" logic is in #method_missing, so this is fine:

require 'ostruct'
class OpenStruct
# Insert/update hash data on the fly.
def update( hash )
if hash
for k,v in hash
@table[k.to_sym] = v
end
end
end
end

Seems a reasonable addition to standard lib, yes?

Thanks,
T.


Yukihiro Matsumoto

11/14/2004 11:17:00 PM

0

Hi,

It's amazing that OpenStruct which is a proof of concept tiny toy
attracts so many users.

In message "Re: OpenStruct#update ?"
on Mon, 15 Nov 2004 06:07:11 +0900, "trans. (T. Onoma)" <transami@runbox.com> writes:

|Hmm.. I also just had another related thought. I might also be useful if a
|normal object could be "OpenStruct'd". I have a need for it at the moment.
|Currently I take an object in as input and then dynamically define instance
|vars with accessors on it as required, but often no initial object is given
|in which case an OpenStruct is much easier to use. I'd rather just have the
|OpenStruct capability from the get go.

Can you be more specific, preferably with code example?

matz.


Yukihiro Matsumoto

11/14/2004 11:20:00 PM

0

Hi,

In message "Re: OpenStruct#update ?"
on Mon, 15 Nov 2004 06:27:51 +0900, "David A. Black" <dblack@wobblini.net> writes:

|I'm assuming so. Actually here's what happens if you try:
|
| irb(main):020:0> o = OpenStruct.new
| => <OpenStruct>
| irb(main):021:0> o.class = 1
| => 1
| irb(main):022:0> o.class
| => OpenStruct

I'm thinking of making OpenStruct raise error for assignment to
existing method. I'm not sure whether I should prohibit private
method overriding, i.e. cases Hal pointed out in [ruby-talk:117889].

matz.


dblack

11/14/2004 11:52:00 PM

0

T. Onoma

11/15/2004 1:23:00 AM

0

On Sunday 14 November 2004 06:16 pm, Yukihiro Matsumoto wrote:
| Hi,
|
| It's amazing that OpenStruct which is a proof of concept tiny toy
| attracts so many users.

It is a very convienet way to access data that is object polymorphic.

Problem, of course, is namespace clash with core methods. What might help: 1)
a "kernelless" object class which reduces the core methods to bare minimum.
2) Have specialized core aliases like __id__ and __send__ (e.g. __class__)
for all methods considered core. These *cannot* be overridden ever. And 3)
have private and public methods have separate namespaces --could that help?
Might that even boost method lookup speeds? Just some thoughts on the matter.

On the whole I'd rather be able to override methods.

| In message "Re: OpenStruct#update ?"
|
| on Mon, 15 Nov 2004 06:07:11 +0900, "trans. (T. Onoma)"
<transami@runbox.com> writes:
| |Hmm.. I also just had another related thought. I might also be useful if a
| |normal object could be "OpenStruct'd". I have a need for it at the moment.
| |Currently I take an object in as input and then dynamically define
| | instance vars with accessors on it as required, but often no initial
| | object is given in which case an OpenStruct is much easier to use. I'd
| | rather just have the OpenStruct capability from the get go.
|
| Can you be more specific, preferably with code example?

Sure. Okay first, basically what I was doing, given object o and data in hash
h:

h.each do |k,v|
o.instance_variable_set("@#{k}", v)
o.instance_eval <<-EOS
def #{k}; @#{k}; end
def #{k}=(x); @#{k}=x; end
EOS
end

Now if o is an OpenStruct, the above is much easier, well, easier after I've
added the before mentioned #update method:

o.update( h )

But o might not be an OpenStruct --the library user may have a pre-made object
that they want to use. What might be of use then, rather then a stand alone
OpenStruct object is a way to "open" a regular object:

o.extend OpenStruct

Actually, if one could extend using a class, instead of just a module, I think
it might almost work just like that.

Thanks,
T.




T. Onoma

11/15/2004 4:35:00 AM

0

On Sunday 14 November 2004 06:16 pm, Yukihiro Matsumoto wrote:
| Can you be more specific, preferably with code example?
|
| matz.

Ah, what the hek! I pulled down 1.9 and modified. Below you will find code.
Let me know if you'd like me to send code as attachment via private mail.

Okay so the trick was simply to move everything into a mixin module (expect
the initialize method). I also changed @table to @__table__ to help prevent
possible name clash. The only other changed required was using

@__table__ ||= {}

in a number of places (too bad no better way to do that). Finally I just
called the module OpenStructable --seems like a good a name as any.

Also, I found what looks to be a bug in current 1.9 version. The initialize
method wasn't calling the newly added #new_ostruct_member method. I fixed and
added #update method. Also, is #new_ostruct_member supposed to be private?
Currently it is public. If it remains public, I recommend a more generic
name. Not sure what though.

Hope you like,
T.

------------

#
# = ostruct.rb: OpenStruct implementation
#
# Author:: Yukihiro Matsumoto & Thomas Sawyer
# Documentation:: Gavin Sinclair
#
# OpenStruct allows the creation of data objects with arbitrary attributes.
# See OpenStruct for an example. OpenStruct is implemented with a resuable
# mixin module OpenStructable.
#

# OpensStructable is a mixin module which can provide OpenStruct behavior to
# any class or object.
#
# require 'ostruct'
#
# record = Object.new
# record.extend OpenStructable
#
# record.name = "John Smith"
# record.age = 70
# record.pension = 300
#
# puts record.name # -> "John Smith"
# puts record.address # -> nil
#
module OpenStructable

# Duplicate an OpenStruct object members.
def initialize_copy(orig)
super
@__table__ = @__table__.dup
end

def new_ostruct_member(name)
self.instance_eval %{
def #{name}; @__table__[:#{name}]; end
def #{name}=(x); @__table__[:#{name}] = x; end
}
end

#
# Generate additional attributes and values.
#
def update(hash)
@__table__ ||= {}
if hash
for k,v in hash
@__table__[k.to_sym] = v
new_ostruct_member(k)
end
end
end

def method_missing(mid, *args) # :nodoc:
mname = mid.id2name
len = args.length
if mname =~ /=$/
if len != 1
raise ArgumentError, "wrong number of arguments (#{len} for 1)",
caller(1)
end
if self.frozen?
raise TypeError, "can't modify frozen #{self.class}", caller(1)
end
mname.chop!
@__table__ ||= {}
@__table__[mname.intern] = args[0]
self.new_ostruct_member(mname)
elsif len == 0
@__table__ ||= {}
@__table__[mid]
else
raise NoMethodError, "undefined method `#{mname}' for #{self}",
caller(1)
end
end

#
# Remove the named field from the object.
#
def delete_field(name)
@__table__ ||= {}
@__table__.delete name.to_sym
end

#
# Returns a string containing a detailed summary of the keys and values.
#
def inspect
str = "<#{self.class}"
for k,v in (@__table__ ||= {})
str << " #{k}=#{v.inspect}"
end
str << ">"
end

def __table__ # :nodoc:
@__table__ ||= {}
end
protected :__table__

#
# Compare this object and +other+ for equality.
#
def ==(other)
return false unless(other.kind_of?(OpenStruct))
return @__table__ == other.table
end
end

#
# OpenStruct allows you to create data objects and set arbitrary attributes.
# For example:
#
# require 'ostruct'
#
# record = OpenStruct.new
# record.name = "John Smith"
# record.age = 70
# record.pension = 300
#
# puts record.name # -> "John Smith"
# puts record.address # -> nil
#
# It is like a hash with a different way to access the data. In fact, it is
# implemented with a hash, and you can initialize it with one.
#
# hash = { "country" => "Australia", :population => 20_000_000 }
# data = OpenStruct.new(hash)
#
# p data # -> <OpenStruct country="Australia" population=20000000>
#
class OpenStruct
include OpenStructable

#
# Create a new OpenStruct object. The optional +hash+, if given, will
# generate attributes and values. For example.
#
# require 'ostruct'
# hash = { "country" => "Australia", :population => 20_000_000 }
# data = OpenStruct.new(hash)
#
# p data # -> <OpenStruct country="Australia" population=20000000>
#
# By default, the resulting OpenStruct object will have no attributes.
#
def initialize(hash=nil)
update(hash)
end
end