views:

48

answers:

1

I have three partials that I'd like to consolidate into one. They share the same collection, but each gets passed its own :local variable. Those variables are used for specific Models, so as a result, I have three different calls to the partial and three different partials.

Here's the repetitive code:

<% for email in campaign.emails %>
      <h4><%= link_to email.title, email  %> <%= email.days %> days</h4>

         <% @contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->   

         <!-- render the information for each contact -->
         <%= render :partial => "contact_email",
                    :collection => @contacts,
                    :locals => {:email => email} %>
    <% end %>

       Calls in this Campaign:
       <% for call in campaign.calls %>
          <h4><%= link_to call.title, call  %> <%= call.days %> days</h4>
          <% @contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->      
         <!-- render the information for each contact -->
         <%= render :partial => "contact_call",
                    :collection => @contacts,
                    :locals => {:call => call} %>
       <% end %>

       Letters in this Campaign:
       <% for letter in campaign.letters %>
          <h4><%= link_to letter.title, letter  %> <%= letter.days %> days</h4>
          <% @contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->      
         <!-- render the information for each contact -->
         <%= render :partial => "contact_letter",
                    :collection => @contacts,
                    :locals => {:letter => letter} %>
       <% end %>

An example of one of the partials is as follows:

<

div id="contact_email_partial">
 <% if from_today(contact_email, email.days) < 0 %>
       <% if show_status(contact_email, email) == 'no status'%>
            <p> <%= full_name(contact_email) %>
                <% unless contact_email.statuses.empty?%>
                    (<%= contact_email.statuses.find(:last).status%>) 
                 <% end %>
                is <%= from_today(contact_email,email.days).abs%> days overdue:
                <%= do_event(contact_email, email) %>

                <%= link_to_remote "Skip Email Remote",
                                  :url => skip_contact_email_url(contact_email,email),
                                  :update => "update-area-#{contact_email.id}-#{email.id}" %>
                <span id='update-area-<%="#{contact_email.id}-#{email.id}"%>'> </span>
        <% end %>
     <% end %>
</div>

And here is the other partial...similar, eh? Need help making it DRY!

 <% if (from_today(contact_call, call.days) < 0) %>
       <% if show_status(contact_call, call) == 'no status'%>
            <p> <%= full_name(contact_call) %> 
                 <% unless contact_call.statuses.empty?%>
                    (<%= contact_call.statuses.find(:last).status%>) 
                 <% end %>
                is <%= from_today(contact_call,call.days).abs%> days overdue:
                <%= do_event(contact_call, call) %>
                <%= contact_call.phone %>
            </p>
        <% end %>
     <% end %>
A: 

For the local variables, the key you select when passing the locals hash is the name the variable will have in the partial. So you could do this in your repetitive code:

<% for email in campaign.emails %>

  <h4><%= link_to email.title, email  %> <%= email.days %> days</h4>

     <% @contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->   

     <!-- render the information for each contact -->
     <%= render :partial => "contact",
                :collection => @contacts,
                :locals => {:objt => email, 
                            :url_method => "skip_#{class}_url".to_sym } %>
<% end %>

   Calls in this Campaign:
   <% for call in campaign.calls %>
      <h4><%= link_to call.title, call  %> <%= call.days %> days</h4>
      <% @contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->      
     <!-- render the information for each contact -->
     <%= render :partial => "contact",
                :collection => @contacts,
                :locals => {:objt => call,
                            :url_method => "skip_#{class}_url".to_sym } %>
   <% end %>

   Letters in this Campaign:
   <% for letter in campaign.letters %>
      <h4><%= link_to letter.title, letter  %> <%= letter.days %> days</h4>
      <% @contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->      
     <!-- render the information for each contact -->
     <%= render :partial => "contact",
                :collection => @contacts,
                :locals => {:objt => letter,
                            :url_method => "skip_#{class}_url".to_sym } %>

   <% end %>

Since in the partial we don't really know the class of the object we need to render, I'm creating a symbol representing the method you need to call to build the URL. We'll invoke this in the partial using a bit of send magic.

Notice the partial has been renamed simply "contact" (_contact.html.erb), and I'm assigning email, call and letter in turn to a variable called objt, that will be accessible from the partial.

For the partial (_contact.html.erb):

<div id="contact_partial">
 <% if from_today(contact, objt.days) < 0 %>
       <% if show_status(contact, objt) == 'no status'%>
            <p> <%= full_name(contact) %>
                <% unless contact.statuses.empty?%>
                    (<%= contact.statuses.find(:last).status%>) 
                 <% end %>
                is <%= from_today(contact,objt.days).abs%> days overdue:
                <%= do_event(contact, objt) %>

                <%= link_to_remote "Skip Email Remote",
                                  :url => send(url_method,contact,objt), 
                                  :update => "update-area-#{contact.id}-#{objt.id}" %>
                <span id='update-area-<%="#{contact.id}-#{objt.id}"%>'> </span>
        <% end %>
     <% end %>
</div>

Notice how, instead of calling the skip_email_url method directly, we use "send" to invoke a method named in the first parameter (in this case, url_method, a local passed from the invoking view), and further, we pass this method the additional parameters (in this case, contact and objt). Just make sure the method named in url_method does effectively take two parameters.

Roadmaster
how do I make skip-contact_email_url work, because that is a set route.....which calls on a specific controller?
Angela
My best solution is to pass something like :skip_controller => :email in your locals, and then rewrite that url as url_for(:controller => skip_controller, ...) and specify the other parameters manually. It's not-as-restful but should work the same. Alternatively, some magic by doing controller.send(:method_name) and setting :method_name => :skip_contact_email_url in the locals might also work.
Roadmaster
I see, so from the partial say the full url? or is there a way to "write" the url...my version would be :url => skip_#{class}_url(contact.objt) but that doesn't seem to work....is there a way to do that?
Angela
this got me part of the way there, I ended up DRY-ing it up even further by having only a single loop and going through each class.
Angela
OK, the thing is, skip_contact_email_url, for instance, is a method, so you can't just directly do string substitution and expect it to work. What you *can* do is build your string and then convert it to a symbol. I've updated my answer to reflect this.
Roadmaster