6

I'd like to have one yaml object refer to another, like so:

intro: "Hello, dear user."

registration: $intro Thanks for registering!

new_message: $intro You have a new message!

The above syntax is just an example of how it might work (it's also how it seems to work in this cpan module.)

I'm using the standard ruby yaml parser.

Is this possible?

John Bachir
  • 22,495
  • 29
  • 154
  • 227

2 Answers2

8

Some yaml objects do refer to the others:

irb> require 'yaml'
#=> true
irb> str = "hello"
#=> "hello"
irb> hash = { :a => str, :b => str }
#=> {:a=>"hello", :b=>"hello"}
irb> puts YAML.dump(hash)
---
:a: hello
:b: hello
#=> nil
irb> puts YAML.dump([str,str])
---
- hello
- hello
#=> nil
irb> puts YAML.dump([hash,hash])
---
- &id001
  :a: hello
  :b: hello
- *id001
#=> nil

Note that it doesn't always reuse objects (the string is just repeated) but it does sometimes (the hash is defined once and reused by reference).

YAML doesn't support string interpolation - which is what you seem to be trying to do - but there's no reason you couldn't encode it a bit more verbosely:

intro: Hello, dear user
registration: 
- "%s Thanks for registering!"
- intro
new_message: 
- "%s You have a new message!"
- intro

Then you can interpolate it after you load the YAML:

strings = YAML::load(yaml_str)
interpolated = {}
strings.each do |key,val|
  if val.kind_of? Array
    fmt, *args = *val
    val = fmt % args.map { |arg| strings[arg] }
  end
  interpolated[key] = val
end

And this will yield the following for interpolated:

{
  "intro"=>"Hello, dear user", 
  "registration"=>"Hello, dear user Thanks for registering!", 
  "new_message"=>"Hello, dear user You have a new message!"
}
rampion
  • 87,131
  • 49
  • 199
  • 315
  • Interesting. Two questions: [1] So dump doesn't just return a string, but looks for variables in the execution environment? [2] I'm not sure I understand what happened in the `hash` case. – John Bachir Jun 29 '10 at 18:47
  • 1
    @John: [1] dump doesn't look at variables - but it does look for repetition w/in the passed object. [2] in `YAML.dump([hash,hash])`, it had an array of hashes of symbols to strings. In its inspection of the array, it found that the two hashes were referring to the same object. So the first time it printed the hash, it gave an identifier (`&id001`) for that hash, and the second time, instead of printing the whole thing again, it referenced that identifier (`*id001`). – rampion Jun 29 '10 at 20:54
  • I see. So that doesn't really get me anywhere in my quest to re-use objects within yaml files, right? – John Bachir Jun 29 '10 at 21:10
  • @John: I don't think I understand what you mean by "re-use objects w/in yaml files." The `YAML.dump([hash,hash])` produces a yaml string, which can be written to a file. Within that string, objects are reused. – rampion Jun 29 '10 at 22:07
  • Look at my example in the original question -- I want "$intro" to be replaced with the 'intro' string when the the file is ingested. – John Bachir Jun 30 '10 at 16:18
  • @John: that's interpolation - which is different from reuse in that you want to combine an object reference and a literal string into a single string. – rampion Jun 30 '10 at 21:51
  • Well, regardless of the terminology, I am seeking an existing tool/methodology that will help me write DRY yaml :) – John Bachir Jul 01 '10 at 15:01
2

Rather than trying to use implicit references in your yaml, why don't you use substitution strings (like you show above, you need quotes though) and explicitly substitute the contents of them at parse time?

Jason S
  • 184,598
  • 164
  • 608
  • 970