How 'bout an OpenStruct#update for adding values after initialization. Or is
there another way to do?
Thanks,
T.
How 'bout an OpenStruct#update for adding values after initialization. Or is
there another way to do?
Thanks,
T.
Hi --
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.
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
--
David A. Black
dblack@wobblini.net
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.
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?
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.
On Sunday 14 November 2004 03:51 pm, David A. Black wrote:
On Mon, 15 Nov 2004, trans. (T. Onoma) wrote:
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.
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
On Mon, 15 Nov 2004, trans. (T. Onoma) wrote:
On Sunday 14 November 2004 03:51 pm, David A. Black wrote:
> On Mon, 15 Nov 2004, trans. (T. Onoma) wrote:
--
David A. Black
dblack@wobblini.net
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.
>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.
On Sunday 14 November 2004 06:16 pm, Yukihiro Matsumoto wrote:
In message "Re: OpenStruct#update ?" > > on Mon, 15 Nov 2004 06:07:11 +0900, "trans. (T. Onoma)" <transami@runbox.com> writes:
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.
On Sunday 14 November 2004 06:16 pm, Yukihiro Matsumoto wrote:
Can you be more specific, preferably with code example?
matz.
------------
#
# = 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
Yes, no, maybe?
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.
--- Original Message ---
On Sunday 14 November 2004 06:16 pm, Yukihiro Matsumoto wrote:
------------
#
# = ostruct.rb: OpenStruct implementation
#
# Author:: Yukihiro Matsumoto
# Contributor:: 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
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{
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 itI'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
---
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.
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.
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
Maybe .meta_class and .meta_class_eval should really be part of Standard Ruby...
[...]
While you are at it, please consider
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/3687
and the other msgs in that thread.
On Wed, Nov 17, 2004 at 08:53:11PM +0900, trans. (T. Onoma) wrote:
Yes, no, maybe?
--- Original Message ---
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.
--
Hassle-free packages for Ruby?
RPA is available from http://www.rubyarchive.org/
Hi --
Hi,
>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
> => OpenStructI'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
On Mon, 15 Nov 2004, Yukihiro Matsumoto wrote:
In message "Re: OpenStruct#update ?" > on Mon, 15 Nov 2004 06:27:51 +0900, "David A. Black" <dblack@wobblini.net> writes:
--
David A. Black
dblack@wobblini.net
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
> endh.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.
On Monday 15 November 2004 06:48 am, Florian Gross wrote:
"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag
news:2vrj8eF2oj365U1@uni-berlin.de...
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
> endh.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
Hi --
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
> endh.each do |k,v|
class << o; self; end.send(:attr_accessor, k)
o.k = v
endMaybe .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
On Mon, 15 Nov 2004, Florian Gross wrote:
--
David A. Black
dblack@wobblini.net
Okay, thanks. So what constitutes compatible marshal data?
T.
On Wednesday 17 November 2004 07:04 am, Mauricio Fernández wrote:
While you are at it, please consider
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/3687
and the other msgs in that thread.
Robert Klemme wrote:
"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag
h.each do |k,v|
class << o; self; end.send(:attr_accessor, k)
o.k = v
endAre 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
David A. Black wrote:
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>\.
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.