link_to meta programming question

I am trying to create a dynamic menu using the following construct:

  <%- menu_list = [ 'first', 'second', 'third'] -%>

<ul>
  <%- menu_list.each do |m| -%>
    <%= link_to "List All #{m.titlecase.pluralize} <m>_path %>
  <%- end -%>
</ul>

What I cannot determine is what I have to do to replace <m> so that the
link_to has a target. I have tried string substitution ("#{m}"_path)
but that gives a compile error:

_erbout.concat(( link_to "List All #{m.titlecase.pluralize}",
("#{m}"_path) ).to_s); _erbout.concat "

I am sure that there must be a fairly straight forward way to do this.
Can someone point it out to me?

You can get it this way:

    <%= link_to "List All #{m.titlecase.pluralize}", send("#{m}_path") %>

Nevertheless in this case I tend to see repetition as a better option. Compare the above to this crystal clear

    <%= link_to "List All Firsts", first_path %>
    <%= link_to "List All Seconds", second_path %>
    <%= link_to "List All Thirds", third_path %>

If you have 35 of those then fine, but for a handful of them I find copy & paste gives better code.

-- fxn

Xavier Noria wrote:

Now I have a related question. Some of these methods may or may not
have routes depending upon when and where they are invoked. So, I have a
guard conditional set up:

<%- role_list.each do |m| -%>
  <li> <%= "#{m.titlecase}" %>

  <%= if defined? "#{m.pluralize}_path" then
          link_to "List All #{m.titlecase.pluralize}",
               send("#{m.pluralize}_path")
        else
          nil
        end %>
    </li>
  <%- end -%>

When the guard is explicit ( if defined? firsts_path then ...) this
works. But I do not know how to test for a dynamically formed method
name. How is this done in Ruby?

Yeah, defined? is not what you want in that case:

  irb(main):001:0> defined? "foo"
  => "expression"

Use respond_to? instead.

Xavier Noria wrote:

Yeah, defined? is not what you want in that case:

  irb(main):001:0> defined? "foo"
  => "expression"

Use respond_to? instead.

Works great using either idiom: Thanks!

<ul>
  <%- role_list.each do |m| -%>
    <li> <%= "#{m.titlecase}" %>
  <! guard against missing path method this way -->
    <%= link_to "List All #{m.titlecase.pluralize}",
          send("#{m.pluralize}_path") \
          if respond_to? "#{m.pluralize}_path" %>
  <!-- or this way -->
    <%= if respond_to? "#{m.pluralize}_path" then
          link_to "List All #{m.titlecase.pluralize}",
            send("#{m.pluralize}_path")
        else
          nil
        end %>
    </li>
  <%- end -%>
</ul>

Can you explain to me the different behaviours between defined? and
respond_to? in this instance. I am not clear why defined? would work
with symbolized path names but not work with the same parameterized
string.to_sym.

Thanks again.

respond_to? asks whether the object responds to some method. Since you
want to know whether self responds to the path generator that's the
one.

defined? is more generic. That's a special operator in the sense that
Ruby does *not* evaluate its argument. I don't know the whole story
(anyone?) but a posteriori defined? works at parse time or something
like that to interpret the argument expression in a special way.

For example, if SOME_CONSTANT is unknown

   defined? SOME_CONSTANT

does *not* raise NameError, it does not evaluate the argument. It
silently returns nil. If the constant was known it would return
"constant".

Check this other one for example:

   irb(main):004:0> a = "\n"
   => "\n"
   irb(main):005:0> defined? a.chop!
   => "method"
   irb(main):006:0> a
   => "\n"

You see? chop! is not being called, we are just told that a responds to chop!.

I'd like to provide a better explanation of defined? but don't have
Flanagan & Matz at hand.

defined? is a bit nuts: http://redhanded.hobix.com/inspect/methodCheckDefined.html

Xavier Noria wrote:

I'd like to provide a better explanation of defined? but don't have
Flanagan & Matz at hand.

The main point that I see here is that when I am testing for the
existence of methods then I should use respond_to? and that is
sufficient.

For more on defined? see:

http://redhanded.hobix.com/inspect/methodCheckDefined.html

I wrote too soon. I have another blank spot in my knowledge that need
filling.

This construct works:

<ul>
  <%- role_list.each do |m| -%>
    <li>
      <%= "#{m.titlecase}" if respond_to? "#{m.pluralize}_path" %>
      <%= link_to "List All #{m.titlecase.pluralize}", \
            send("#{m.pluralize}_path") if respond_to?
"#{m.pluralize}_path" %>
      <%= link_to "Add New #{m.titlecase}", send("new_#{m}_path") \
            if respond_to? "#{m.pluralize}_path" %>
    </li>
  <%- end -%>
</ul>

and this does not:

<ul>
<%- role_list.each do |m| -%>
  <li>
  <!-- guard against missing path methods. We assume (that word again)
       that if the ms_path exists then the new_m_path does as well.-->
<%= if respond_to? "#{m.pluralize}_path" then
      "#{m.titlecase}"
      link_to "List All #{m.titlecase.pluralize}",
send("#{m.pluralize}_path")
      link_to "Add New #{m.titlecase}", send("new_#{m}_path")
    end -%>
  </li>

<%- end -%>
</ul>

All I get in the html from this is the output of the last step in the if
block:

link_to "Add New #{m.titlecase}", send("new_#{m}_path")

This indicate that I am only getting the final return value of a Ruby
method as the entire result of the if construct. This is not what I
intend or desire so is there some way of getting each step in the if
block to produce output onto the html page?

if is an expression in Ruby, but the easy way to do that in ERb is

  <% if ... %>
    this
    that
   <%= link_to ... %>
  <% end %>

Note the use of <% ... %> instead of <%= ... %>.

Me again.

Now that I have this working in a view I want to move it all out into a
pair of methods that I can call from multiple views: role_list_head and
role_list_foot. Two questions arise from this:

Where do I put these methods? In application_helper.rb,
application_controller.rb, or somewhere else?

What does the method look like? Something akin to this I expect:

@role_list_default = ['entity', 'client', 'employee', 'vendor']

def role_list_head(list=@role_list_default)
  link_to 'Main Page', :controller => 'nav_menus'
  list.each do |m|
    # guard against missing path methods.
    if respond_to? "#{m.pluralize}_path" then
      link_to "List All #{m.titlecase.pluralize}",
send("#{m.pluralize}_path")
    end
  end
  list.each do |m|
    if respond_to? "new_#{m.}_path" then
      link_to "Add New #{m.titlecase}", send("new_#{m}_path")
    end
  end
end

I know that this does not work, because I tried it. But something
somewhat like this has to be the answer.

What does the method look like? Something akin to this I expect:

@role_list_default = ['entity', 'client', 'employee', 'vendor']

This could be a constant.

def role_list_head(list=@role_list_default)
  link_to 'Main Page', :controller => 'nav_menus'
  list.each do |m|

    # guard against missing path methods.

    if respond_to? "#{m.pluralize}_path" then

      link_to "List All #{m.titlecase.pluralize}",
send("#{m.pluralize}_path")
    end
  end
  list.each do |m|
    if respond_to? "new_#{m.}_path" then

      link_to "Add New #{m.titlecase}", send("new_#{m}_path")
    end
  end
end

I know that this does not work, because I tried it. But something
somewhat like this has to be the answer.

That's view stuff so should go to a helper or to a partial. The helper
would build and return a single string with HTML so that you can use
it like this

   <%= role_list_header %>

Xavier Noria wrote:

That's view stuff so should go to a helper or to a partial. The helper
would build and return a single string with HTML so that you can use
it like this

   <%= role_list_header %>

yes, but which helper file exactly? All that I have read about helpers
indicates that they are tied to a specific controller by name. I tried
adding a dummy method to a file called lib/application_helper.rb but
when I called the method from a view I got a missing method error.

There are several options, but you can just throw it into
app/helpers/application_helper.rb. That file contains helpers that are
available everywhere.

James Byrne wrote:

I figured out what I was doing wrong in application_helper.rb. It really
helps if you put:

module ApplicationHelper
  ...
end

around your code. double sigh.

Careful, you don't need that file in lib, see the path in my previous post.

Xavier Noria wrote:

Careful, you don't need that file in lib, see the path in my previous
post.

What are the implications of each. Why one place rather than the other?

Because app/helpers is the directory where helpers are expected to be
by convention. Note that application_helper.rb is generated there by
the rails command, and it already contains a module ApplicationHelper.