|
|
 |
 |
 |
 |
Ruby Programming Language
|
 |
 |
 |
 |
 |
 |
 |
 |
YAML and ruby classes
I need to create some objects of different (custom) classes, in different pages of my site, so I made some code that takes an array of hashes and create the objects. the hashes are like this one: h = { :class => Class1, :param1 => 'foo', :param2 => 'bar', etc } and basically I do object = h[:class].new(h) everything works fine as long as the hash is defined inside the source code. however in some case I need to create the same object in two different file. to avoid writing the same hashes twice I though about putting them in a YAML file but this method doesn't work anymore because the class name is converted to a string instead of a reference to the class, using !ruby/object creates the object but all the code inside the initialize method seems to be never executed (or the instance variables ovverrided after the inzialize method) so my objects don't work, ClassName.to_yaml returns an error, etc... is there a way to do what I want to do? putting the ashes in a file and loading them as a source AFAIK creates other problem due to the sandboxing made by mod_ruby, that's why I tried with yaml. -- Deh! Impiacciami, imploroti sgabazzone rampante!
Tirsdag den 13. Marts 2007 skrev Matteo Cavalleri:
> I need to create some objects of different (custom) classes, in > different pages of my site, so I made some code that takes an array of > hashes and create the objects. the hashes are like this one: > h = { :class => Class1, :param1 => 'foo', :param2 => 'bar', etc } > and basically I do > object = h[:class].new(h) > everything works fine as long as the hash is defined inside the source > code. however in some case I need to create the same object in two > different file. to avoid writing the same hashes twice I though about > putting them in a YAML file but this method doesn't work anymore because > the class name is converted to a string instead of a reference to the > class, using !ruby/object creates the object but all the code inside the > initialize method seems to be never executed (or the instance variables > ovverrided after the inzialize method) so my objects don't work, > ClassName.to_yaml returns an error, etc... > is there a way to do what I want to do? putting the ashes in a file and > loading them as a source AFAIK creates other problem due to the > sandboxing made by mod_ruby, that's why I tried with yaml.
Object.const_get can convert a String to a constant, so if you do: Object.const_get(h[:class].to_s).new(h) it should work, no matter if the hash comes from source or yaml br. Chr.
On Mar 13, 2007, at 9:50 AM, Matteo Cavalleri wrote:
> I need to create some objects of different (custom) classes, in > different pages of my site, so I made some code that takes an array of > hashes and create the objects. the hashes are like this one: > h = { :class => Class1, :param1 => 'foo', :param2 => 'bar', etc } > and basically I do > object = h[:class].new(h) > everything works fine as long as the hash is defined inside the source > code. however in some case I need to create the same object in two > different file. to avoid writing the same hashes twice I though about > putting them in a YAML file but this method doesn't work anymore > because > the class name is converted to a string instead of a reference to the > class, using !ruby/object creates the object but all the code > inside the > initialize method seems to be never executed (or the instance > variables > ovverrided after the inzialize method) so my objects don't work, > ClassName.to_yaml returns an error, etc... > is there a way to do what I want to do? putting the ashes in a file > and > loading them as a source AFAIK creates other problem due to the > sandboxing made by mod_ruby, that's why I tried with yaml.
It seems to me this is a fundamental problem with YAML. Even if you were trying to put all the data in one file, I don't believe the YAML spec. addresses writing object references instead of objects. This is especially an issue when your objects have circular references. This caused me to use XML instead of YAML for a recent Java project because the Java XStream library handles serializing and deserializing Java objects that have circular references. If there is a YAML solution to this, I'd love to hear about it!
On Wed, 14 Mar 2007, Mark Volkmann wrote: > It seems to me this is a fundamental problem with YAML. Even if you were > trying to put all the data in one file, I don't believe the YAML spec. > addresses writing object references instead of objects. This is especially an > issue when your objects have circular references. > This caused me to use XML instead of YAML for a recent Java project because > the Java XStream library handles serializing and deserializing Java objects > that have circular references. > If there is a YAML solution to this, I'd love to hear about it!
harp:~ > ruby -r yaml -e' h = {}; h[:h] = h; y h ' &id001 :h: *id001 regards. -a -- be kind whenever possible... it is always possible. - the dalai lama
On Mar 13, 2007, at 10:28 AM, ara.t.how@noaa.gov wrote:
> On Wed, 14 Mar 2007, Mark Volkmann wrote: >> It seems to me this is a fundamental problem with YAML. Even if >> you were trying to put all the data in one file, I don't believe >> the YAML spec. addresses writing object references instead of >> objects. This is especially an issue when your objects have >> circular references. >> This caused me to use XML instead of YAML for a recent Java >> project because the Java XStream library handles serializing and >> deserializing Java objects that have circular references. >> If there is a YAML solution to this, I'd love to hear about it! > harp:~ > ruby -r yaml -e' h = {}; h[:h] = h; y h ' > &id001 > :h: *id001
My apologies! Apparently the problem is with the Java implementation of YAML that I was using and not with YAML itself. Here's a more full example that demonstrates YAML doing the right thing with multiple references to the same object and with circular references. --- require 'yaml' class Person attr_accessor :name, :spouse, :address def to_s "\n#{name} is married to #{spouse.name} and lives at\n#{address}" end end class Address attr_accessor :street, :city, :state, :zip def to_s "#{street}\n#{city}, #{state} #{zip}" end end a = Address.new a.street = "644 Glen Summit" a.city = "St. Charles" a.state = "MO" a.zip = 63304 p1 = Person.new p1.name = "Mark Volkmann" p1.address = a p2 = Person.new p2.name = "Tami Volkmann" p2.address = a p1.spouse = p2 p2.spouse = p1 people = [p1, p2] yaml_string = YAML::dump(people) puts yaml_string new_people = YAML::load(yaml_string) puts new_people --- The output is --- - &id002 !ruby/object:Person address: &id001 !ruby/object:Address city: St. Charles state: MO street: 644 Glen Summit zip: 63304 name: Mark Volkmann spouse: &id003 !ruby/object:Person address: *id001 name: Tami Volkmann spouse: *id002 - *id003 Mark Volkmann is married to Tami Volkmann and lives at 644 Glen Summit St. Charles, MO 63304 Tami Volkmann is married to Mark Volkmann and lives at 644 Glen Summit St. Charles, MO 63304
Christian Surlykke <christ @surlykke.dk> wrote: > Object.const_get can convert a String to a constant, so if you do: > Object.const_get(h[:class].to_s).new(h) > it should work, no matter if the hash comes from source or yaml
the idea was excellent but it seems it doens't work if the class I need is defined in another class namespace, e.g. irb(main):117:0> Object.const_get('CGI::Cookie') NameError: wrong constant name CGI::Cookie anyway thanks for the help -- Deh! Impiacciami, imploroti sgabazzone rampante!
Mark Volkmann <m @ociweb.com> wrote: > This caused me to use XML instead of YAML for a recent Java project mh... if I don't find a solution with yaml I'll try xml with ruby. thanks. -- Deh! Impiacciami, imploroti sgabazzone rampante!
On Tue, 13 Mar 2007 15:47:13 +0100, Matteo Cavalleri wrote: > I need to create some objects of different (custom) classes, in > different pages of my site, so I made some code that takes an array of > hashes and create the objects. the hashes are like this one: > h = { :class => Class1, :param1 => 'foo', :param2 => 'bar', etc } > and basically I do > object = h[:class].new(h) > everything works fine as long as the hash is defined inside the source > code. however in some case I need to create the same object in two > different file. to avoid writing the same hashes twice I though about > putting them in a YAML file but this method doesn't work anymore because > the class name is converted to a string instead of a reference to the > class, using !ruby/object creates the object but all the code inside the > initialize method seems to be never executed (or the instance variables > ovverrided after the inzialize method) so my objects don't work, > ClassName.to_yaml returns an error, etc... > is there a way to do what I want to do? putting the ashes in a file and > loading them as a source AFAIK creates other problem due to the > sandboxing made by mod_ruby, that's why I tried with yaml.
If you reference the class object directly, you can't dump it to yaml, so try using a string instead, and running eval on the string to get the class object. h = { :class => 'Class1', :param1 => 'foo', :param2 => 'bar', etc } object = eval(h[:class]).new(h) This gives you the following YAML: --- :class: Class1 :param1: foo :param2: bar -- Ken Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/
Alle marted 13 marzo 2007, Matteo Cavalleri ha scritto: > Christian Surlykke <christ @surlykke.dk> wrote: > > Object.const_get can convert a String to a constant, so if you do: > > Object.const_get(h[:class].to_s).new(h) > > it should work, no matter if the hash comes from source or yaml > the idea was excellent but it seems it doens't work if the class I need > is defined in another class namespace, e.g. > irb(main):117:0> Object.const_get('CGI::Cookie') > NameError: wrong constant name CGI::Cookie > anyway thanks for the help
Can't you store the class name (using Class#name) instead of the class itself in the yaml file? This way, since Class#name returns the full 'path', including modules, you can then do something like: name=h[:class] name.split('::').inject(Object){|res, c| c.const_get(c)}.new h (taken from the solutions of the ruby quiz 133 (http://www.rubyquiz.com/quiz113.html)) I hope this helps Stefano
Ken Bloom <kbl @gmail.com> wrote: > h = { :class => 'Class1', :param1 => 'foo', :param2 => 'bar', etc } > object = eval(h[:class]).new(h) it works! thanks :) -- Deh! Impiacciami, imploroti sgabazzone rampante!
Stefano Crocco <stefano.cro @alice.it> wrote: > name.split('::').inject(Object){|res, c| c.const_get(c)}.new h this is pretty complicated for my current knowledge of ruby :D the method suggested by ken bloom works and is simpler for me, but thanks anyway for you help! :) -- Deh! Impiacciami, imploroti sgabazzone rampante!
Matteo Cavalleri wrote: > I need to create some objects of different (custom) classes, in > different pages of my site, so I made some code that takes an array of > hashes and create the objects. the hashes are like this one: > h = { :class => Class1, :param1 => 'foo', :param2 => 'bar', etc } > and basically I do > object = h[:class].new(h) > everything works fine as long as the hash is defined inside the source > code. however in some case I need to create the same object in two > different file. to avoid writing the same hashes twice I though about > putting them in a YAML file but this method doesn't work anymore because > the class name is converted to a string instead of a reference to the > class, using !ruby/object creates the object but all the code inside the > initialize method seems to be never executed (or the instance variables > ovverrided after the inzialize method) so my objects don't work, > ClassName.to_yaml returns an error, etc... > is there a way to do what I want to do? putting the ashes in a file and > loading them as a source AFAIK creates other problem due to the > sandboxing made by mod_ruby, that's why I tried with yaml.
It is possible to extend YAML to serialize classes: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/177604 It seems to still work with ruby-1.8.6. It lets you do this: yy = [Enumerable, Comparable, String, File].to_yaml puts yy p YAML.load(yy) with output: --- - !ruby/module Enumerable - !ruby/module Comparable - !ruby/class String - !ruby/class File [Enumerable, Comparable, String, File] However, if you are hand-editing the YAML file, you may find that a class name string is more convenient than this notation. YYAMLMMV -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
Joel VanderWerf <v @path.berkeley.edu> wrote: > However, if you are hand-editing the YAML file, you may find that a > class name string is more convenient than this notation. I think i'll go for the class name string :) but thanks for the link to the extension. as I'm still learning ruby it was very interesting. -- Deh! Impiacciami, imploroti sgabazzone rampante!
On Wed, Mar 14, 2007 at 01:25:06AM +0900, Matteo Cavalleri wrote: > Stefano Crocco <stefano.cro @alice.it> wrote: > > name.split('::').inject(Object){|res, c| c.const_get(c)}.new h > this is pretty complicated for my current knowledge of ruby :D the > method suggested by ken bloom works and is simpler for me, but thanks > anyway for you help! :)
To get at Foo::Bar::Baz you need to do klass = Object.const_get("Foo").const_get("Bar").const_get("Baz") So that one-liner above is really another way of writing: name = "Foo::Bar::Baz" names = name.split("::") klass = Object while n = names.shift klass = klass.const_get(n) end
|
 |
 |
 |
 |
|