[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

using YAML as config

Larry Edelstein

2/12/2007 1:22:00 AM

Hi all -

I want to write a config file in YAML. In this file I will store a
small graph of objects in an array:

- !mydomain,mydate/Company
name: TuesdayCo
subthings:
- !mydomain,mydate/Person
name: Ruby Tuesday
- !mydomain,mydate/Person
name: Tuesday Weld
- !mydomain,myDate
name: FridayCo
subthings:
etc.

Then I want to read in this file and get an array of Company objects,
each of which has an array of Person objects. I'm defining these
classes myself. I've tried using add_domain_type and hoped to get
instances of my classes, but instead I got instances of
YAML::DomainType.

How should I do this?

-larry

--
Posted via http://www.ruby-....

13 Answers

Joel VanderWerf

2/12/2007 3:44:00 AM

0

Larry Edelstein wrote:
> Hi all -
>
> I want to write a config file in YAML. In this file I will store a
> small graph of objects in an array:
>
> - !mydomain,mydate/Company
> name: TuesdayCo
> subthings:
> - !mydomain,mydate/Person
> name: Ruby Tuesday
> - !mydomain,mydate/Person
> name: Tuesday Weld
> - !mydomain,myDate
> name: FridayCo
> subthings:
> etc.
>
> Then I want to read in this file and get an array of Company objects,
> each of which has an array of Person objects. I'm defining these
> classes myself. I've tried using add_domain_type and hoped to get
> instances of my classes, but instead I got instances of
> YAML::DomainType.
>
> How should I do this?

Here's the only way I know to do this (I'm not sure if add_domain_type
is another way):

require 'yaml'

# This assumes that all your objects will serialize as hashes ("maps")
module Base
def to_yaml( opts = {} )
YAML::quick_emit( object_id, opts ) do |out|
out.map( taguri, to_yaml_style ) do |map|
to_yaml_properties.each do |m|
map.add( m, send( m ) )
end
end
end
end

module BaseModuleMethods
def yaml_new( klass, tag, val )
unless Hash === val
raise YAML::TypeError, "Invalid #{self}: " + val.inspect
## do more error checking here, if desired
end

name, type = YAML.read_type_class( tag, self )
st = type.new
val.each do |k,v|
st.send( "#{k}=", v )
end
st
end
end

def self.included mod
mod.extend BaseModuleMethods
end
end

class Company
include Base

attr_accessor :name, :subthings
def initialize name = nil
@name = name
end

yaml_as "tag:my.domain,2007:Company"

def to_yaml_properties
["name", "subthings"]
end
end

class Person
include Base

attr_accessor :name
def initialize name = nil
@name = name
end

yaml_as "tag:my.domain,2007:Person"

def to_yaml_properties
["name"]
end
end

co = Company.new "TuesdayCo"
co.subthings = [
Person.new("Ruby Tuesday"),
Person.new("Tuesday Weld")
]

co_yaml = co.to_yaml
puts co_yaml
# ==>
# --- !my.domain,2007/Company
# name: TuesdayCo
# subthings:
# - !my.domain,2007/Person
# name: Ruby Tuesday
# - !my.domain,2007/Person
# name: Tuesday Weld

co2 = YAML.load(co_yaml)
p co2

# ==>
#<Company:0xb7ce3cfc @subthings=[#<Person:0xb7ce44cc @name="Ruby
Tuesday">, #<Person:0xb7ce3ef0 @name="Tuesday Weld">], @name="TuesdayCo">


# Check that error-checking works by
# giving the parser an array instead of a hash:
s = <<END
--- !my.domain,2007/Company
- 1
- 2
END

begin
p YAML.load(s)
rescue YAML::TypeError => ex
puts ex.message # Invalid Company: [1, 2]
end

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

_why

2/12/2007 4:31:00 AM

0

On Mon, Feb 12, 2007 at 10:21:54AM +0900, Larry Edelstein wrote:
>
> - !mydomain,mydate/Company
> name: TuesdayCo
> subthings:
> - !mydomain,mydate/Person
> name: Ruby Tuesday
> - !mydomain,mydate/Person
> name: Tuesday Weld
> - !mydomain,myDate
> name: FridayCo
> subthings:
> etc.
> [...]
>
> How should I do this?

Heya, larry! The easy way is like this:

class Company
yaml_as 'tag:mydomain.com,2007:Company'
end

class Person
yaml_as 'tag:mydomain.com,2007:Person'
end

_why

Larry Edelstein

2/12/2007 10:10:00 PM

0

Wow Joel - that's a lot of code and a lot to digest. I'm going through
it. Your response and _why's use the yaml_as attribute - I've never
even seen that before.

--
Posted via http://www.ruby-....

Joel VanderWerf

2/12/2007 10:22:00 PM

0

Larry Edelstein wrote:
> Wow Joel - that's a lot of code and a lot to digest. I'm going through
> it. Your response and _why's use the yaml_as attribute - I've never
> even seen that before.

It's nice to know that _why's much simpler code works. The other stuff I
wrote is how I've handled validation and other customization before. I
wonder if _why has a better way to do that too...

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

rhubarb

5/7/2007 7:14:00 PM

0

So I come to the part of my little app where I want to load and save a
config file. I want it to be human read/writeable too though - it's not
just for serialization. In fact, usually a human will write it, my
little app will read it, and may adjust it and write it out anew.

So in xml it would look like this
<channels>
<channel>
<name>foo</name>
<id>3</id>
</channel>
<channel>
â?¦
</channels>

I don't care about channels that's just a random root node, and I might
do it with attbs instead of elements, so:

<channel name="foo" id="3"/>

So I thought, lets use YAML instead - it's even easier to read and
write. Google, google, read blogs, docs, google, google, more docs, more
blogs, google to_yaml, fxri: hack hack, google to_yaml_type, fxri: hack,
hack, hack, google, google, yaml_as, hack.

Argh! Seems like day later, and I'm ready to go back to xml. But I'm
sure there's a simple way to do what I want, so I'll post here first and
hope. See, my class is Channel, like so:

class Channel; attr_accessor :name, :id; end

and want my array of channels to look something like this

- Channel
name: foo
id: 3
- Channel
name: bar
id: 4
â?¦

But I just can't work out how to simply the yaml output to this stage.
And I don't want to override a dozen methods and do my own hash mapping.
The easiest way - thanks to why for posting this in the forum - seems to
be to use yaml_as:

class Channel
yaml_as "tag:foobar,2007:Channel"
end

but then I get each entry as

--- !foolbar,2007/Channel
name: foo
id: 3

This is fairly simple, but remember that I want my entries to be
human-writeable the first time. That line of "---!foobar,2007â?¦" is just
way too machiney for us poor humans to get right every time.

I can simplify the taguri like this
yaml_as "tag::Channel"

to get

--- !Channel
...

and that's about as simple as I can get it without it failing to write
yaml. But loading it back in will fail to build the object - it will
give ma a YAML::DynamicType instead - which is what it always gives if
it doesn't recognize the object.
I guess the load method needs a valid taguri

(Side note - I'd never heard of a taguri before, but when I went to read
about it at http://www.t... I was _very_ pleasantly surprised at
how quickly I was able to grok the whole thing by reading Sandro Hawke's
four-paragraph tutorial about his dog Taiko. Way to go Sandro - if I'd
been directed to the RFC first it'd be all over)

So anyway, the fact is that while taguris are great for distinguishing
Taiko from his master's descendents' pooches, and while it's very
simple, it's still overkill for me. I don't need a unique ID. I know
what "--- Channel" means in my little app. I don't want to submit my
Channel types to some great panoply of random little types each with
their own special pet name. I just want to damn well read and write it.

I've even considered parsing the text in to_yaml and YAML.load and
gsubbing "!foobar, 2007/" out and in again. But there's got to be a
better way. So far that better way is looking like xml. but there's got
to be a better rubyful way.

What is it?

--
Posted via http://www.ruby-....

Greg Donald

5/7/2007 7:29:00 PM

0

On 5/7/07, Rover Rhubarb <rover.rhubarb@gmail.com> wrote:
> So far that better way is looking like xml. but there's got
> to be a better rubyful way.
>
> What is it?

json?

http://json.ruby...



--
Greg Donald
http://des...

ara.t.howard

5/7/2007 9:05:00 PM

0

_why

5/7/2007 9:25:00 PM

0

On Tue, May 08, 2007 at 04:14:24AM +0900, Rover Rhubarb wrote:
> I can simplify the taguri like this
> yaml_as "tag::Channel"
>
> to get
>
> --- !Channel
> ...
>
> and that's about as simple as I can get it without it failing to write
> yaml. But loading it back in will fail to build the object - it will
> give ma a YAML::DynamicType instead - which is what it always gives if
> it doesn't recognize the object.

Try:

class Channel
yaml_as "x-private:Channel"
end

And you get:

--- !!Channel
id: 3
name: foo

G'luck!!

_why

rhubarb

5/7/2007 9:26:00 PM

0

Let me add:

- I meant DomainType, not dynamic type

- The use of yaml_as is redundant since I can get the same effect
without it - where my objects are dumped as

- !ruby/object:Channel

instead of my own silly name.

Still not good enough: how do I get rid of the !ruby/object
machine-talk. (Is it machine-talk, or was YAML designed by bushmen of
the Kalahari, where the scripting language of choice is "click-ruby")


--
Posted via http://www.ruby-....

James Gray

5/7/2007 9:49:00 PM

0

On May 7, 2007, at 2:14 PM, Rover Rhubarb wrote:

> and want my array of channels to look something like this
>
> - Channel
> name: foo
> id: 3
> - Channel
> name: bar
> id: 4

Hope this helps:

class Channel < Struct.new(:name, :id)
def self.parse(io)
io.inject(Array.new) do |channels, line|
if line =~ /\bChannel\b/
channels << new
elsif not channels.empty? and line =~ /\A\s+(name|id):\s+(.+)/
channels.last.send("#{$1}=", $2)
end
channels
end
end
end

if __FILE__ == $PROGRAM_NAME
p Channel.parse(DATA)
end

__END__
- Channel
name: foo
id: 3
- Channel
name: bar
id: 4

James Edward Gray II