rails way of doing if/else logic - ruby-on-rails

I have following code which seems to work fine:
<%if group.employees.count > 0%>
<td><%= link_to group.employees.count.to_s, {:action => 'index', :controller => 'employees'}, {:id=>group.id}%></td>
<%else%>
<td><%= link_to "Add Employee", {:action => 'new', :controller => 'employees'}%></td>
<%end%>
I'm just wondering how this can be written in more rails friendly way using unless?

I would (with dylanfm's advice in the comment underneath) write it like this:
<% if group.employees.present? %>
<td><%= link_to group.employees.count.to_s, employees_path, { :id=> "group_#{group.id}" }%></td>
<% else %>
<td><%= link_to "Add Employee", new_employee_path %></td>
<% end %>
Here I've used the employees_path and new_employee_path methods which are generated by using the routing helpers in config/routes.rb. You can read more about routing by reading the Routing from the Outside In guide.

I consider it unreadable to use unless with else. Would be better to stick with what you have.

You have an if-else branch (two possible different outcomes), so using unless would require you to change it to an unless-else branch, which is hard to understand. If-else is the way to go in this situation.
If you only had a single condition, i.e.
if foo != 0
do_something(bar)
end
It could be rewritten as
do_something(bar) unless foo == 0

You can try this way:
<% if group.employees.any? %>
<td><%= link_to group.employees.count, employees_path, { :id=> dom_id(group) } %></td>
<% else %>
<td><%= link_to "Add Employee", new_employee_path %></td>
<% end %>

You can use helper and content_tag to generate html instead of ugly erb grammar.
def foo
if group.employees.count > 0
content_tag(...)
else
content_tag(...)
end
end
then in your view
<%= foo %>

Tumtu's way is the best, but I would even put the td tags outside the if statements. I would never use unless with else, and certainly is not a "rails way" to use unless.

Related

Pass .each loop reference object in Ruby on Rails

I am writing a code in ruby on rails where I am creating dynamic rows in table which involves a .each loop. I want to pass the .each loop reference object but it gives me an error.
Following is the code:
<% pworkflows.workflow_executions_list.each do |wf| %>
<tr>
<td><%= wf.execution_status %></td>
<td>
<% if(wf.start_timestamp != nil) %>
<%= wf.start_timestamp.localtime; %> UTC
<% end %>
</td>
<td><%= wf.close_status %></td>
<td><%= wf.execution.run_id %></td>
<td><%= button_to "Details",{ :controller => "pages", :action => "mainpage",:rulesetinstance=>rInsId, :ndetails=>wf} %></td>
</tr>
<% end %>
:ndetails=>wf gives an error. wf is not being recognized as a correct syntax to send.
Please suggest a way.
the error being:
undefined local variable or method `id' for #<ComRuleManagement::WorkflowExecutionObject:0x00003da1751528>
When you do this
<%= button_to "Details",{ :controller => "pages", :action => "mainpage",:rulesetinstance=>rInsId, :ndetails=>wf} %>
you are building an html tag. (since button_to is an html helper). The extra options you pass through, in this instance ":rulesetinstance" and ":ndetails" will be used to make extra attributes in the element, like rulesetinstance="123". However, if you pass the wf object through, then rails will call to_s on it, and you'll end up with something that looks like this ndetails="#<Wf:0x7f518dfc6e68>". This is almost certainly not what you want in your html element. Should you be calling another method of the wf object instead?

undefined method `name' for nil:NilClass

I have this problem on te line
<td><%= h box.manufacturer.name %></td>
of
<% #boxes.each do |box| %>
<tr>
<td><%= h box.manufacturer.name %></td>
<td><%= h box.model %></td>
<td><%= link_to 'Mostrar', :action => 'show', :id => box %></td>
<td><%= link_to 'Editar', :action => 'edit', :id => box %></td>
<td><%= button_to 'Eliminar', { :action => 'destroy', :id => box },
:confirm => "Are you sure you want to delete box #{box.model}?" %></td>
manufacturers is a table with field called name,its supposed that an object manufacturer has member called name ,isn it?
It's quite clear one of your box records doesn't have its associated manufacturer. If it's acceptable, you can use Object#try method, like this:
<%= box.manufacturer.try(:name) %>
If it's not, you should think of adding proper validation to Box model:
validates_presence_of :manufacturer
I didn't use h helper because in Rails >= 3.0 (which you probably use) untrusted content is escaped automatically.
It means your one of your boxes doesn't have a manufacturer.
You can prevent an exception like this by using try:
box.try(:manufacturer).try(:name)
If the manufacturer doesn't exist, it will return nil instead of throwing an exception
Yes, but in your case you seem to have a box with no manufacturer.
<%= h box.manufacturer.name if box.manufacturer %>
Or
<%= h box.manufacturer.try(:name) %>
The proposed solutions fix the issue but don't address the bug in architecture. I recommend learning the Tell Don't Ask principle.
Your views shouldn't do any checks on nil as it is not their responsibility. You should always try to avoid long method chains as it will always fail on certain step.
Refactor your application like this:
box.rb:
def manufacturer_name
manufacturer.try(:name)
end
view:
<td><%= h box.manufacturer_name %></td>
I don't think it's possible if the box is nil. If it is - then I recommend removing nils from the array before iterating over it in views like this:
controller:
#boxes = box_retrieving_method_with_nils.compact

Link_to another view ruby on rails

I am trying to use the link_to feature to link one view to another.
The view i am calling link_to is app/views/instructors/show.html.erb and that snippet of code looks like this (namely, the second to last line of it)
<% provide(:title, #instructor.login) %>
<% courses = Course.where(:instructor_ID => #instructor.id) %>
<div class="span2">
<h1 align=center ><%= #instructor.login %></h1>
<%= link_to "Add course", new_course_path(:instructor_ID\
=> #instructor.id), :class => "btn" %>
<br>
<br>
<%= link_to "Remove course", delete_course_path(courses), :class => "btn"%>
</div>
The view I am trying to link to is is app/views/courses/show_all.html.erb and looks like this:
<% #courses.each do |course| %>
<tr>
<td><%= course.course_name %></td>
<td><%= course.instructor_ID %></td>
<td><%= link_to 'Show', course %></td>
<td><%= link_to 'Edit', edit_course_path(course) %></td>
<td><%= link_to 'Destroy', course, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
</tr>
delete_course_path routes to app/views/courses/show_all.html.erb shown above. When I try the code above, I get the following error:
undefined method `each' for nil:NilClass
At this line:
<% #courses.each do |course| %>
Any ideas what i'm missing in my link_to?
In your show_all action, you should define a #courses instance variables. This is
<% courses = Course.where(:instructor_ID => #instructor.id) %>
not passed to show_all.html.erb.
An instance variables is a variable passed from action of controller to the view corresponding.
I suppose when you show page of instructor, your route will like this: /instructors/:id, so maybe in your show_all action of instructor controller, you need something like:
def show_all
#courses = Course.where(instructor_ID: params[:id])
render 'courses/show_all'
end
This means that #courses is nil. Did you set it in your show_all action of your controller? E.g.
def show_all
#courses = Course.all
end
Also, in your show view, you set courses to a collection of Course objects, but your "Remove course" link looks like you only want to delete one course. Why do you use the delete_course route to link to your show_all view?

If Statement within Views in Rails 3

Ok so i have been starting to get used to rails 3 over the past few days and have got a project in the works to test things out on. Is it possible to do the following or what would you suggest is the best way to only allow post authors to edit their posts.
<% if post.author_id == current_user.id %>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
<% end %>
Recommendation: Don't compare ids - compare objects.
<% if post.author == current_user %>
Optional: Consider using a plugin (only if necessary) like cancan to make it even more descriptive.
<% if can? :edit, post %>

I am having trouble with my first project in ruby on rails

Here's my index action in the books controller: http://pastebin.com/XdtGRQKV
Here's the view for the action i just mentioned: http://pastebin.com/nQFy400m
Here's the result without being logged in: http://i.imgur.com/rQoiw.jpg
Here's the result when i'm logged in with the user 'admin': http://i.imgur.com/E1CUr.jpg
So the problem is that, in the view, before line 25 the 'user' variable seems to be empty ( or not loaded), and after line 25 the variable 'user' has the expected values.
I have tried initializing a variable in the index method of the books controller but get exactly the same results.
Thanks in advance!
BTW had to make the links text because of stackoverflow limit.
This:
user = User.find_by_id(session[:user_id])
should be in controller, not in view (MVC!) like this:
#user = User.find_by_id(session[:user_id])
Then in your view, as #Voyta answered, use <%= #user.username %>. Code inside <% %> is evaluated, but not rendered, so if you want to put result in your html, you need to add =.
And all yours if user and if user.admin == 1 would look much better this way:
<% if user %>
<td><%= link_to 'Show', book %></td>
<% if user.admin == 1 %>
<td><%= link_to 'Edit', edit_book_path(book) %></td>
<td><%= link_to 'Delete', book, :confirm => 'Are you sure?', :method => :delete %></td>
<% end
end
%>
If you use if in single line like here:
<%
if user
if user.admin == 1
%>
<%= link_to 'New book', new_book_path %>
<%
end
end
%>
You can write the same like this:
<%= link_to 'New book', new_book_path if user && user.admin == 1 %>
You didn't output user.username. It should be <%= user.username %>, not <% user.username %>

Resources