OpenStruct#update ?

T

trans. (T. Onoma)

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

Thanks,
T.
 
D

David A. Black

Hi --

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.

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.


David
 
T

trans. (T. Onoma)

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

trans. (T. Onoma)

Hi David,

| > 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.
 
D

David A. Black

Hi --

Hi David,

| > 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 ;)

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

(Modularize and error-check as required :)


David
 
T

trans. (T. Onoma)

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.
 
Y

Yukihiro Matsumoto

Hi,

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

In message "Re: OpenStruct#update ?"

|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.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: OpenStruct#update ?"

|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.
 
D

David A. Black

Hi --

Hi,

In message "Re: OpenStruct#update ?"

|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].

What about =-methods?

require 'ostruct'

def x=(y)
puts "In x=()"
end

self.x = 1 # In x=()

o = OpenStruct.new
o.x = 1 # Should this print "In x=()" ?

Probably a marginal case :)


David
 
T

trans. (T. Onoma)

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)"
| |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

trans. (T. Onoma)

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
 
F

Florian Gross

trans. (T. Onoma) said:
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

h.each do |k,v|
class << o; self; end.send:)attr_accessor, k)
o.k = v
end

Maybe .meta_class and .meta_class_eval should really be part of Standard
Ruby...
 
T

trans. (T. Onoma)

| > 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
|
| h.each do |k,v|
| class << o; self; end.send:)attr_accessor, k)
| o.k = v
| end

Nice. I still perfer to use instance_variable_set, but the send :attr_accessor
is a nice improvement. Thanks.

| Maybe .meta_class and .meta_class_eval should really be part of Standard
| Ruby...

I have them in my own lib as #virtual_class. So I could use that as well.
Speaking of which...

Matz, can we get the official word on what the name for these is going to be?
Is it meta class, singleton class, virtual class or ... ? (personally I like
instance class). I would like my lib to be able to conform to the "standard".

Thanks,
T.
 
R

Robert Klemme

Florian Gross said:
h.each do |k,v|
class << o; self; end.send:)attr_accessor, k)
o.k = v
end

Are you sure, this works? IMHO this is more efficient:

class << o; self; end.send:)attr_accessor, *h.keys)
h.each do |k,v|
o.send("#{k}=", v)
end

But it would be even better to check for existing methods in order to not
overwrite existing methods:

cl = class << o; self; end
im = cl.instance_methods
h.each do |k,v|
cl.send:)attr_reader, k) unless im.include?(k.to_s)
cl.send:)attr_writer, k) unless im.include?("#{k}=")
o.send("#{k}=", v)
end

Kind regards

robert
 
D

David A. Black

Hi --

h.each do |k,v|
class << o; self; end.send:)attr_accessor, k)
o.k = v
end

Maybe .meta_class and .meta_class_eval should really be part of Standard
Ruby...

I've got an RCR in for singleton_class. (I know, the terminology is
different, but it's the same idea :) I still don't know what the
perfect word would be for That Class.) I don't think a separate
_class_eval method would be necessary or good. As with every other
class, you could just call class_eval on the class object.

I firmly believe that having a singleton_class method (or whatever
it's called) would make it much, much easier for people to understand
the whole design. See <http://www.rcrchive.net/rcr/show/231>.


David
 
F

Florian Gross

Robert said:
Are you sure, this works? IMHO this is more efficient:

class << o; self; end.send:)attr_accessor, *h.keys)
h.each do |k,v|
o.send("#{k}=", v)
end

You are right of course. I also like how you moved the attr_accessor out
of the loop -- nice idea.
But it would be even better to check for existing methods in order to not
overwrite existing methods:

cl = class << o; self; end
im = cl.instance_methods
h.each do |k,v|
cl.send:)attr_reader, k) unless im.include?(k.to_s)
cl.send:)attr_writer, k) unless im.include?("#{k}=")
o.send("#{k}=", v)
end

Hm, I think that would still overwrite methods of o's class and
inherited ones.

What about this?

accessors = h.keys - o.methods
class << o; self; end.send:)attr_accessor, *accessors)
h.each do |key, value|
o.send("#{key}=", value) if accessors.include?(key)
end
 
F

Florian Gross

David said:
I've got an RCR in for singleton_class. (I know, the terminology is
different, but it's the same idea :) I still don't know what the
perfect word would be for That Class.) I don't think a separate
_class_eval method would be necessary or good. As with every other
class, you could just call class_eval on the class object.

I firmly believe that having a singleton_class method (or whatever
it's called) would make it much, much easier for people to understand
the whole design. See <http://www.rcrchive.net/rcr/show/231>.

Hm, looks like I already voted on it. I can live with it being named
#singleton_class, #meta_class or even #virtual_class -- I don't really
know what name would be best. I'm just in favor of #meta_class because
it avoids the name clash with the singleton pattern. (Which seems to be
obsolete in Ruby anyway -- it seems to be the case that everywhere where
you would use it you can just as well use a Module...)

I guess it's probably best to let matz decide the name.
 
T

trans. (T. Onoma)

On Monday 15 November 2004 07:38 am, Robert Klemme wrote:
| |
| > trans. (T. Onoma) wrote:
| > > 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
| >
| > h.each do |k,v|
| > class << o; self; end.send:)attr_accessor, k)
| > o.k = v
| > end
|
| Are you sure, this works? IMHO this is more efficient:

Actually the example is pulled inside out from the real method I use which is
a method of class Object (where it does work). So no the above probably
doesn't work as give --but was just intended to give approx. notion of what
I'm doing.

| class << o; self; end.send:)attr_accessor, *h.keys)
| h.each do |k,v|
| o.send("#{k}=", v)
| end
|
| But it would be even better to check for existing methods in order to not
| overwrite existing methods:
|
| cl = class << o; self; end
| im = cl.instance_methods
| h.each do |k,v|
| cl.send:)attr_reader, k) unless im.include?(k.to_s)
| cl.send:)attr_writer, k) unless im.include?("#{k}=")
| o.send("#{k}=", v)
| end

I'm not sure I want that. Hmm... It's a sticky issue depending largely on the
particular use case. I think it makes sense for general use, so yes --I'll
add something like that, thanks. Should it raise an error instead?

But in the context of an OpenStruct, no. OpenStruct focus is on data.

T.
 
D

David A. Black

Hi --

Hm, looks like I already voted on it. I can live with it being named
#singleton_class, #meta_class or even #virtual_class -- I don't really
know what name would be best. I'm just in favor of #meta_class because
it avoids the name clash with the singleton pattern. (Which seems to be
obsolete in Ruby anyway -- it seems to be the case that everywhere where
you would use it you can just as well use a Module...)

I guess it's probably best to let matz decide the name.

Yeah, as long as he doesn't choose 'virtual' :)


David

P.S. Yes, I know this is going to the list :)
 
T

trans. (T. Onoma)

On Monday 15 November 2004 08:45 am, David A. Black wrote:
|
| Yeah, as long as he doesn't choose 'virtual' :)

He he. Seems like all the readily floated possibilities have poor
connotations. I've heard complaints about all three: 'virtual', 'meta', and
of course, 'singleton'. So what do others think of 'instance' class? Or more
to the point 'special' class -- a specialton ;)

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top