Block usage of content_tag

Hi there,

I’m learning basics with Agile Web D. with Rails, and
encountered this piece of code :

module StoreHelper

def hidden_div_if(condition, attributes = {}, &block)
if condition
attributes[“style” ] = “display: none”
end
content_tag(“div” , attributes, &block)
end

end

which is used like that :

<% hidden_div_if(@cart.items.empty?, :id => “cart” ) do %>
<%= render(:partial => “cart” , :object => @cart) %>
<% end %>

After reading some posts about blocks performance issues, I tried to
change the code for that :

module StoreHelper

def hidden_div_if(condition, attributes = {})
if condition
attributes[“style” ] = “display: none”
end
content_tag(“div” , attributes) {yield}
end

end

But the result is an error (undefined local variable or method
`_erbout’). I thought this issue had been fixed
(http://dev.rubyonrails.org/ticket/7857), but maybe I’m missing the
point.

Is my code wrong, is my rails version out-of-date, or what ?

Thanks

Arnaud J. wrote:

def hidden_div_if(condition, attributes = {}, &block)
if condition
attributes[“style” ] = “display: none”
end
content_tag(“div” , attributes, &block)

Look inside the content_tag source. Is it using capture? That takes a
&block, and it must treat the block specially to bind to its eRB
context.

<% hidden_div_if(@cart.items.empty?, :id => “cart” ) do %>
<%= render(:partial => “cart” , :object => @cart) %>
<% end %>

eRB starts by converting <%%> marks into a mishmash of strings and eRB
method calls. A simple do-end block would evaluate in the wrong
context, so capture() provides the correct context. And you can’t just
yield into it.

http://dev.rubyonrails.org/ticket/7857

Someone may correct me, but that appears to apply to your first
example, not your second. You yielded without a capture, so you
yielded in the wrong context.


Phlip
http://www.oreilly.com/catalog/9780596510657/
“Test Driven Ajax (on Rails)”
assert_xpath, assert_javascript, & assert_ajax

On Jul 20, 2007, at 7:57 , Arnaud J. wrote:

I’m learning basics with Agile Web D. with Rails, and
encountered this piece of code :

After reading some posts about blocks performance issues, I tried to
change the code for that :

As you’re just starting out with Rails, I wouldn’t worry about
performance issues. Get the basics down and then benchmark your app.
If it’s not performing adequately for your specifications, find the
bottlenecks of your system, make changes, and benchmark again.
Premature optimization at the best of times is a bad idea. As you’re
just starting out, it’s likely going to cause you much more grief
than any gains you might find.

Michael G.
grzm seespotcode net

Michael G. wrote:

As you’re just starting out with Rails, I wouldn’t worry about
performance issues.

I don’t worry about performance issues. While trying to understand
blocks & yield, I found a post about performance issues (this one :
Is a block converted to a Proc object before yield? - Ruby - Ruby-Forum), and since yield and blocks
look like ruby basics, I tried to understand a little more about the
difference between using a yield and a .call.
In the example given in the topic mentionned above it seems like you can
swap between one and another without too much changes. So, I tried to do
the same with the method I was playing with, to test my comprehension. I
wasn’t looking for the best solution, but for a didactic solution.

And I still don’t understand well why my litte {yield} trick doesn’t
work. I will try to reformulate. What is the difference between those to
methods :

def method_ampersand(&b)
some_method(&b)
end

def method_yield
some_method {yield}
end

With some_method defined like that
def some_method(&block)
#code
end

and the two methods called like that
method_ampersand() { #code to be captured }
method_yield() { #code to be captured }

It might be a question of context, but I’m still a little bit confused
about that. Note that some_method would be like our content_tag of my
first post.

Thank you for your time

Arnaud take a look at the Rails source for content_tag:
http://pastie.caboo.se/80983

  def content_tag(name, content_or_options_with_block = nil,

options = nil, &block)
if block_given?
options = content_or_options_with_block if
content_or_options_with_block.is_a?(Hash)
content = capture(&block)
concat(content_tag_string(name, content, options),
block.binding)
else
content = content_or_options_with_block
content_tag_string(name, content, options)
end
end

If you give it a block, it calls concat(content_tag_string(name,
content, options), block.binding).

module StoreHelper

def hidden_div_if(condition, attributes = {})
if condition
attributes[“style” ] = “display: none”
end
content_tag(“div” , attributes) {yield}
end

If you use content_tag(“div” , attributes) {yield} inside your
hidden_div_if(), when concat inside content_tag() will be called w/
the binding of you block: {yield}, which has the StoreHelper’s context
as to your View’s context: (the original block passed in
hidden_div_if). As a result, when concat() gets called inside
content_tag, it doesn’t have the right binding and hence missing:
“_erbout.” I hope this helps. :slight_smile:

David D. wrote:

block.binding)

the binding of you block: {yield}, which has the StoreHelper’s context
as to your View’s context: (the original block passed in
hidden_div_if). As a result, when concat() gets called inside
content_tag, it doesn’t have the right binding and hence missing:
“_erbout.” I hope this helps. :slight_smile:

Thanks - that confirms what I implied, when I was too lazy to look up
the
actual method!

The best way to use capture{} is don’t, guys! (-;


Phlip
http://www.oreilly.com/catalog/9780596510657/
“Test Driven Ajax (on Rails)”
assert_xpath, assert_javascript, & assert_ajax