Learning erb

  • Thread starter James Edward Gray II
  • Start date
J

James Edward Gray II

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
 
G

Gavin Kistner

"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!
 
J

James Edward Gray II

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.
 
B

Bill Atkins

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
 
G

Gavin Kistner

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.

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>
#=> <li>Just because.</li>
#=> <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.
 
G

Gavin Kistner

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

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

James Edward Gray II

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

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
 
J

James Edward Gray II

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top