Learning erb

I'm playing around with erb, so I fully understand what I'm documenting. I still have one nagging question...

<% ... %> vs. <%= ... %>

The Pickaxe II says the following about the first one:

"Insert the given Ruby code at this point in the generated program. If it outputs anything, include the output in the result."

It's the last line that's confusing me. If I feed erb:

% a = 99
<%= a %> bottles of beer...

I get:

99 bottles of beer...

If I change that to:

% a = 99
<% a %> bottles of beer...

I get:

  bottles of beer...

I can of course use:

% a = 99
<% print a %> bottles of beer...

to get back to:

99 bottles of beer...

But I'm skeptical about HOW that is actually happening. It looks like a happy accident that they just both go to STDIN to me, especially if I view the source:

_erbout = ''; a = 99
  print a ; _erbout.concat " bottles of beer...\n"
_erbout

My a output doesn't really end up in _erbout, like the rest of the code. Because of that, I assume I would have trouble here if I was generating this programatically with ERB objects. Let's see:

% cat erb_test.txt
% a = 99
<% print a %> bottles of beer...
% ruby -r erb -e 'res = ERB.new(ARGF.read, 0, "%"); puts "Answer is:" + res.result' erb_test.txt
99Answer is: bottles of beer...

Yeah, that's ugly.

So, what the heck does "If it outputs anything, include the output in the result." really mean? I'm confused.

James Edward Gray II

"Insert the given Ruby code at this point in the generated program. If it outputs anything, include the output in the result."

It's the last line that's confusing me. If I feed erb:

[...]

% a = 99
<%= a %> bottles of beer...
99 bottles of beer...

% a = 99
<% a %> bottles of beer...
bottles of beer...

% a = 99
<% print a %> bottles of beer...
99 bottles of beer...

[...]

So, what the heck does "If it outputs anything, include the output in the result." really mean? I'm confused.

I'm not sure what your question is. Perhaps this final test will make the end result (if not the magic mechanism) clear:

a = 99
b = 12
puts ERB.new( "<%= print a; b %> bottles of beer" ).result( binding )
#=> 9912 bottles of beer

puts ERB.new( "<%= puts a; b %> bottles of beer" ).result( binding )
#=> 99
#=> 12 bottles of beer

a) Without the =, run the ruby code. Any strings output by print, puts, etc. replace the block.
b) With the =, as above, except that the return value of the block should have .to_s called on it and appended to any output.

Thanks for doing the work to document this, James!

···

On Oct 26, 2004, at 6:21 PM, James Edward Gray II wrote:

Maybe I'm not either. Thanks for trying to help me.

I believe I actually understand what's going on, but the short blurb in the back of the Pickaxe II confuses me.

Let me try asking in one more way to see if it changes the answers I'm getting. Under what circumstances will <% ... %> add to the output?

Thanks.

James Edward Gray II

P.S. erb may be pretty simple, but dang I'm doing some cool stuff with it over here! Madlibs anyone? <laughs>

P.P.S. Is it just me or is Ruby's StdLib of excellent quality, especially compared to say Perl's StdLib. I'm constantly awed by what I find hiding in standard Ruby.

···

On Oct 26, 2004, at 9:32 PM, Gavin Kistner wrote:

I'm not sure what your question is.

I think what he's saying is that <%= "34" %> will output a 34 to the
result, but <% puts 34 %> will output that stream to STDOUT. So if he
were calling ERb#result, he'd get the result of the former in its
return value but the second statement's output wouldn't go into the
result string.

Bill

···

On Wed, 27 Oct 2004 12:25:18 +0900, James Edward Gray II <james@grayproductions.net> wrote:

On Oct 26, 2004, at 9:32 PM, Gavin Kistner wrote:

> I'm not sure what your question is.

Maybe I'm not either. Thanks for trying to help me.

I believe I actually understand what's going on, but the short blurb in
the back of the Pickaxe II confuses me.

Let me try asking in one more way to see if it changes the answers I'm
getting. Under what circumstances will <% ... %> add to the output?

Thanks.

James Edward Gray II

P.S. erb may be pretty simple, but dang I'm doing some cool stuff with
it over here! Madlibs anyone? <laughs>

P.P.S. Is it just me or is Ruby's StdLib of excellent quality,
especially compared to say Perl's StdLib. I'm constantly awed by what
I find hiding in standard Ruby.

Ah, you're right. I was confused because of the limited test case I was doing.

These two tests make clear that what you say is true:

Test 1:
  require 'erb'
  a = 99
  puts ERB.new( "<% print a %> bottles of beer. <% print a %> I say." ).result( binding )
  #=> 9999 bottles of beer. I say.

Test 2:
  require 'erb'
  a = 99
  ERB.new( "<% print a %> bottles of beer. <% print a %> I say." ).result( binding )
  #=> 9999

Now I understand James' question. The above is NOT what I would have expected, and at odds with the Pickaxe quote. It means that the following code does not work like the equivalent would in something like ASP:

  require 'erb'
  erb = ERB.new <<ENDERB
  <h1>Hello, World</h1>
  
  <p>I like cheese because:</p>
  <ol>
  <%
  reasons = [ "It's bad for me", "It is tasty", "And so yes" ]
  reasons.each{ |reason|
    puts "\t<li>#{reason}</li>"
  }
  %>
  </ol>
  Done.
  ENDERB
  
  puts erb.result( binding )

Er...in fact, the above doesn't run at all, oddly.
  tmp.rb:12: undefined local variable or method `reason' for main:Object (NameError)
  
Changing the block to:
  <%
  3.times{ puts "<li>Just because.</li>" }
  %>

produces the final output:
  #=> <li>Just because.</li>
  #=> <h1>Hello, World</h1>
  #=>
  #=> <p>I like cheese because:</p>
  #=> <ol>
  #=>
  #=> </ol>
  #=> Done.

...which is clearly not what is desired. Perhaps there's some way to run IRB that hijacks STDOUT that I don't know about.

Finally, to be clear, the following DOES work:
  require 'erb'
  erb = ERB.new <<ENDERB
  <h1>Hello, World</h1>
  
  <p>I like cheese because:</p>
  <ol>
    <%
    reasons = [ "It's bad for me", "It is tasty", "And so yes" ]
    reasons.each{ |reason|
    %>
      <li><%=reason%></li><%
    }
    %>
  </ol>
  Done.
  ENDERB
  
  puts erb.result( binding )

outputting:
  #=> <h1>Hello, World</h1>
  #=>
  #=> <p>I like cheese because:</p>
  #=> <ol>
  #=>
  #=> <li>It's bad for me</li>
  #=> <li>It is tasty</li>
  #=> <li>And so yes</li>
  #=> </ol>
  #=> Done.

···

On Oct 26, 2004, at 10:06 PM, Bill Atkins wrote:

I think what he's saying is that <%= "34" %> will output a 34 to the
result, but <% puts 34 %> will output that stream to STDOUT. So if he
were calling ERb#result, he'd get the result of the former in its
return value but the second statement's output wouldn't go into the
result string.

Of course, I meant *E*RB, not IRB.

···

On Oct 27, 2004, at 6:39 AM, Gavin Kistner wrote:

...which is clearly not what is desired. Perhaps there's some way to run IRB that hijacks STDOUT that I don't know about.

Oh good, I'm not going crazy. Thanks for playing around with this a little Gavin. I'll look deeper into the examples you provided...

James Edward Gray II

···

On Oct 27, 2004, at 7:39 AM, Gavin Kistner wrote:

Now I understand James' question. The above is NOT what I would have expected, and at odds with the Pickaxe quote.

  require 'erb'
  erb = ERB.new <<ENDERB
  <h1>Hello, World</h1>
  
  <p>I like cheese because:</p>
  <ol>
  <%
  reasons = [ "It's bad for me", "It is tasty", "And so yes" ]
  reasons.each{ |reason|
    puts "\t<li>#{reason}</li>"
  }
  %>
  </ol>
  Done.
  ENDERB
  
  puts erb.result( binding )

Er...in fact, the above doesn't run at all, oddly.
  tmp.rb:12: undefined local variable or method `reason' for main:Object (NameError)

It works in standalone erb (with disjointed printing, of course), which is what ultimately led me to the error. The problem is your heredoc. It's trying to substitute #{reason}, before it ever makes it to to the ERB object. To fix, change:

  erb = ERB.new <<ENDERB

to:

  erb = ERB.new <<'ENDERB'

Hope that helps.

James Edward Gray II

···

On Oct 27, 2004, at 7:39 AM, Gavin Kistner wrote: