Check for nil?, Blank? - ruby-on-rails

I have a model and for some reason I am trying to tell rails if there is nothing created then render add a new show link.
<% if #show != blank? %>
<%= link_to 'Add a new show', new_show_path %></br>
<% else %>
<%= render(:partial => 'shows/show', :locals => {:show => #profile.shows.last}) %>
<% end %>
It adds the Add a new show link but once a show is created I still only see the link and not the partial. If I create the show and put the render at top like so then I can see it but if i delete the show it returns an error.
I've tried these also
<% if #show.present? %>
<%= render(:partial => 'shows/show', :locals => {:show => #profile.shows.last}) %>
<% else %>
<%= link_to 'Add a new show', new_show_path %></br>
<% end %>
<% if #show.blank? %>
<%= link_to 'Add a new show', new_show_path %></br>
<% else %>
<%= render(:partial => 'shows/show', :locals => {:show => #profile.shows.last}) %>
<% end %>
<% if #{model} nil? %>
<%= link_to 'Add a new show', new_show_path %></br>
<% else %>
<%= render(:partial => 'shows/show', :locals => {:show => #profile.shows.last}) %>
<% end %>
and it seems to never give me what I am looking for on both ends. It ethiers shows me the link and nevers shows the partial once created or it shows the partial but when I delete it it gives me an error.
How can I tell rails that if there is no shows created to render the add new link and once there is a show created to render the partial?

Are you actually filtering this by profile? It looks like you're rendering a page for the last show of a profile. (#profile.shows.last)
<% show = #profile.shows.last %>
<% if show.blank? %>
<%= link_to 'Add a new show', new_show_path %>
<br />
<% else %>
<%= render 'shows/show', :show => show %>
<% end %>

Use the .blank? method for the global variable you're trying to tell if it's empty. If its an array or hash. Use .nil? If its supposed to be anything else.

Related

Refactoring into partials in Rails

In page1, I use code A+B+C
In page2, I use code B+C
So when I make a partial, I realy have no idea in how to deal with this.
For example, In a Post-Comment system. I want to show #comments in 2 different pages. In the comment index page,
We show the post it belongs to. And in the post show page, We only have to show the comments content.(Since there is no need to show the comment.post again)
#Comment Index Page
<% #comments.each do |comment| %>
<%= comment.post %>
<%= comment.author %>
<%= comment.content %>
<% end %>
..
#Post Show Page
<% #comments.each do |comment| %>
<%= comment.author %>
<%= comment.content %>
<% end %>
So, how do I make a partial to reuse the code? Perhaps like this? But this there more elegant way of doing this?
#Comment Index Page
<% #comments.each do |comment| %>
<%= comment.post %>
<%= render comment %>
<% end %>
#Post Show Page
<% #comments.each do |comment| %>
<%= render comment %>
<% end %>
Updated:
I adopt the local variable approach, and update my code like:
# partial
<% if include_topic %>
<div class="Topic">
<h5><%= link_to "#{comment.topic.content}", comment.topic %></h5>
</div>
<% end %>
#Index
<%= render #comments, :locals => {:include_topic => true } %>
But I get undefined local variable or method `include_topic' for #<#
I just find nowhere to debug this issue
Your partial:
<%= comment.post if include_post %>
<%= comment.author %>
<%= comment.content %>
your code:
#index page
<%= render :partial => "partial_path", :collection => #comments, :as => :comment, :locals => {:include_post => true } %>
#show page
<%= render :partial => "partial_path", :collection => #comments, :as => :comment, :locals => {:include_post => false } %>
Syntax could be much shorter but it depends whether or not you stick to rails conventions see doc.
Sidenote: I don't like 1.8.7 syntax
In the partial,
<% comments.each do |comment| %>
<%= comment.post if params[:controller] == "comments" %>
<%= comment.author %>
<%= comment.content %>
<% end %>
and now render this partial in both comments/index and posts/show pages by specifying the comments as a local variable.

current action name problem

When i am in /edit action, i see edit button but the problem is if there is an error in form validation it renders action edit and i see create button. how can i fix it?
<%= form_for(#page) do |f| %>
<% if controller.action_name =="edit" %>
<%= f.submit "Update" %>
<% else %>
<%= f.submit "Create" %>
<% end %>
<% if ["edit", "update"].include? params[:action] %>
<%= f.submit "Update" %>
<% else %>
<%= f.submit "Create" %>
<% end %>
Better solution is to extract your form as a partial and send local variable with button name to it
your edit view:
<%= render :partial => "form", :locals => { :button_label => "Edit" } %>
your create view:
<%= render :partial => "form", :locals => { :button_label => "create" } %>
your _form partial:
<%= form_for #object ... do |f| %>
...
<%= f.submit button_label %>
<% end %>
UPD
I think #idlefingers solution the best for your issue
You could just use f.submit with no arguments. This will create names like "Update Page" and "Create Page". If you want to change the wording of these, they can be set in your locale. No conditionals, no messing about with action names. Simple.
Try to do this check:
<%= form_for(#page) do |f| %>
<% if controller.action_name =~ /update|edit/ %>
<%= f.submit "Update" %>
<% else %>
<%= f.submit "Create" %>
<% end %>
My solution:
<%= form_for(#page) do |f| %>
<%= f.submit(f.object.new_record? ? "Create" : "Update") -%>
<% end %>

How do I manage if-then explosion in view files?

I apologize if this doesn't follow good question guidelines, but I hope it's well in class with How to Manage CSS Explosion and receives a similarly helpful response.
I'm familiar with some basic view prolixity mitigation strategies such as the following:
Use helpers where appropriate
Don't repeat yourself
Use partials and layouts
Feel free to suggest something if I'm missing some big idea in the above list.
Nevertheless, I still end up with having several dimensions/degrees of freedom in my view, causing a lot of if-then statements or at least ternary blocks. For instance, in something I'm currently messing with, I'm working on a header bar for a program where the view is called when three "big" variables:
Whether the user is admin
Whether the user is logged in
Whether the page being viewed belongs to the user or someone else
It ends up looking like this mess:
<% content_for :subheader do %>
<div class="row">
<% if #user %>
<% if #user == current_user %>
<%= link_to 'My programs', user_programs_path(current_user), :class => 'active' %>
<% else %>
<%= link_to "#{#user.username}'s programs", user_programs_path(#user), :class => 'active' %>
<% end %>
<%= link_to 'Browse all programs', programs_path %>
<% else %>
<% if current_user %>
<%= link_to 'My programs', user_programs_path(current_user) %>
<% end %>
<%= link_to 'Browse all programs', programs_path, :class => 'active' %>
<% end %>
<%= link_to 'New Program', new_program_path, :class => 'admin' if current_user.admin? %>
</div>
<% if #regions %>
<div class="row second">
<%= link_to 'Regional program search', request.fullpath, :class => 'active' %>
</div>
<% end %>
<% end %>
Ugly. Readable and easily accessible, but ugly. Some suggestions?
Between experience and new technologies like LESS, I've become pretty good at slimming down my CSS files, but I'm still running into explosion issues with my MVC views.
I would use helpers and model definitions to dry up your code:
class User
def possesive
self == current_user ? 'My' : "#{username}'s"
end
end
module ...Helper
def user_program_link user
if user
link_to "#{user.possesive} programs", user_programs_path(user), :class => 'active'
elsif current_user
link_to 'My programs', user_programs_path(current_user)
end
end
end
You can then simplify all the if statements for the user_program_path calls to this:
<%= user_program_link #user %>
Which would reduce your view code to:
<% content_for :subheader do %>
<div class="row">
<%= user_program_link #user %>
<% if #user %>
<%= link_to 'Browse all programs', programs_path %>
<% else %>
<%= link_to 'Browse all programs', programs_path, :class => 'active' %>
<% end %>
<%= link_to 'New Program', new_program_path, :class => 'admin' if current_user.admin? %>
</div>
<% if #regions %>
<div class="row second">
<%= link_to 'Regional program search', request.fullpath, :class => 'active' %>
</div>
<% end %>
<% end %>
Continue this process to DRY up the rest of your code as well.
Rewrite the view code as follows:
<% content_for :subheader do %>
<div class="row">
<% if #user || current_user %>
<%= link_to ((current_user == #user or #user.nil?) ? "My programs" :
"#{#user.username}'s programs"),
user_programs_path(#user || current_user),
:class => 'active' %>
<% end %>
<%= link_to 'Browse all programs', programs_path,
:class => (#user ? '' : 'active') %>
<%= link_to 'New Program', new_program_path, :class => 'admin' if current_user.admin? %>
</div>
<% if #regions %>
<div class="row second">
<%= link_to 'Regional program search', request.fullpath, :class => 'active' %>
</div>
<% end %>
<% end %>

How can I make three partials into just one in rails where the :collection is the same?

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

Ruby on Rails - Trouble looping through partial using collection

I am having issues using the :collection command for a partial within a form I am creating in rails. I would ideally like to use the :collection command, so I can easily manipulate this section in my .rjs templates (the form will submit and reload the form when the check box is changed, it's a to-do list).
This code works:
<% form_for "list[]", :url => {:action => "checkbox_update"} do |f| %>
<ul id="lists_not_completed">
<% for #list in #lists %>
<%= render :partial => #list, :locals => {:f =>f, :complete => FALSE } %>
<% end %>
</ul>
<% end %>
with the partial:
<% if #list.completed == complete %>
<li><%= f.check_box :completed %>
<%=h #list.name %>
<%= link_to 'Show', list %>
<%= link_to 'Edit', edit_list_path(list) %>
<%= link_to 'Destroy', list, :confirm => 'Are you sure?', :method => :delete %></li>
<% end %>
This code does not work, but I would like it to use this form:
<% form_for "list[]", :url => {:action => "checkbox_update"} do |f| %>
<ul id="lists_not_completed">
<%= render :partial => 'list', :collection => #lists, :locals => {:f =>f, :complete => FALSE } %>
</ul>
<% end %>
with the non-working partial:
<% if list.completed == complete %>
<li><%= f.check_box :completed %>
<%=h list.name %>
<%= link_to 'Show', list %>
<%= link_to 'Edit', edit_list_path(list) %>
<%= link_to 'Destroy', list, :confirm => 'Are you sure?', :method => :delete %></li>
<% end %>
I get the error:
object[] naming but object param and #object var don't exist or don't respond to to_param: nil. It is referring to this line: <li><%= f.check_box :completed %>. I'm not sure if why this doesn't work and have tried many, many different variations, but I can't get it working. Is the form preventing me from doing this? The form_for code is straight from the Rails Way book for listing multiple objects from one model in a form.
Any help on this would be greatly appreciated.
I think that the problem is you've not got #list defined anywhere when you're using the render :partial with a :collection.
The system is looking for #list to match the list[] when you call f.check_box
you could set #list = list in your partial to get around that. I suppose.
Tim's answer is correct, but I'd probably avoid extracting the partial within the form_for loop altogether. I suppose it's a matter of style, but I think the confusion here isn't really worth the cleanup that the partial represents in this case. I'd probably write a partial that included the whole form.

Resources