Reaching into an associated collection - ruby-on-rails-3

I have a user model, and event model, and an asset model.
A user can have multiple events.
An event can have multiple assets (images)
Now what I am trying to do is display one (any) event image on a page. Currently I have the following.
Controller
#user = current_user
#events = #user.events
View
<% #events.each do |e| %>
<li>
<% if e.assets.nil? %>
<%= image_tag("img36.jpg" , :size => "280x230") %>
<% else %>
<%=image_tag e.assets.first.path.url %>
<% end %>
<div class="bar">
<strong class="heading"><%= e.name %></strong>
<ul class="menu">
<li><a class="time" title="Time" href="#"><%= e.date %></a></li>
<li><a class="comments" title="Comments" href="#">53</a></li>
<li><a class="favourites" title="Favourites" href="#">87</a></li>
<li><a class="view" title="Views" href="#">242</a></li>
</ul>
</div>
<p><%= e.description %>more »</p>
</li>
<% end %>
"path"is the string field in the assets table that contains the image path. I'm getting the following error right now.
undefined method `path' for nil:NilClass
Any ideas? Thanks!

The problem is when there are no assets associated with an event, the value of e.assets will be an empty array, not nil. So what is happening is that e.assets passes the nil? conditional, then you take e.assets.first which is nil (because the array is empty), and then you try calling path on that which obviously doesn't work.
To fix the problem just change:
<% if e.assets.nil? %>
to:
<% if e.assets.empty? %>

Related

RoR: how can I list only microposts that have a certain attribute in one column?

Right now I am listing all of a users microposts using the code below.
<div class="span8">
<% if #user.microposts.any? %>
<h3>Purchases I am interested in (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
and the view that renders for _micropost.html.erb is as follows
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %>
<% end %>
</li>
so this works fine, however I am changing things up. Every micropost has a hidden_tag_field which is a string (and a column in the database) that is called kind. It can be either "purchase" or "sale". I want to list all of the purchase microposts in one place and all the sale microposts in another. How can I change the micropost view to do this?
Controller:
#purchases = #microposts.where(:kind => 'purchase')
#sales = #microposts.where(:kind => 'sale')
View:
<h2>Purchases</h2>
<%= render #purchases %>
<h2>Sales</h2>
<%= render #sales %>

Is it possible to pass html elements into a partial?

I have a partial that contains a header, a subheader and potentially one or more buttons. It is used on many different pages. Often the pages don't need buttons, so I just pass in the header and an optional subheader to the partial. However sometimes the pages need one or more buttons and the only way I've managed to allow for an arbitrary number of buttons to be passed in is using content_for. My partial looks like this:
<% if defined? page_title %>
<header class="pageHeader">
<div class="page-details">
<h3><%= page_title %></h3>
<% if defined? page_subtitle %>
<p><%= page_subtitle %></p>
<% end %>
</div>
<ul class="crud-menu nav-pills">
<%= content_for :page_header_buttons %>
</ul>
</header>
<% end %>
This use of content_for nasty. Is there any way I can pass the list items / buttons into this partial? How else could I deal with this situation?
You could transform this partial into a layout:
<% if defined? page_title %>
<header class="pageHeader">
<div class="page-details">
<h3><%= page_title %></h3>
<% if defined? page_subtitle %>
<p><%= page_subtitle %></p>
<% end %>
</div>
<ul class="crud-menu nav-pills">
<%= yield %>
</ul>
</header>
<% end %>
And you would render it like that:
<%= render layout: "your_partial_above", locals: { page_title: "page title } do %>
<%= render "partial_with_your_buttons" %>
<% end %>

Unable to display usernames on Assets index page

I'm trying to create an index page where all assets are displayed including a link to the user who they are assigned to, however when I use
def index
#assets = Asset.paginate(page: params[:page])
#user = User.find(params[:id])
end
It returns - couldn't find User without an ID.
index.html.erb - Assets
<%= provide(:title, 'Assets') %>
<h1>All assets</h1>
<%= will_paginate #assets %>
<ol class="assets">
<%= render #assets %>
</ol>
<%= will_paginate #assets %>
_asset.html.erb partial
<li>
<span class="id">JTC<%= asset.id %></span>
<span class="serial"><%= asset.serial_no %></span>
<span class="status">Status: <%= asset.status %></span>
<span class="type">Type: <%= asset.asset_type %><br/></span>
<span class="description">Description: <%= asset.asset_description %><br /></span>
<span class="comment"><em><%= asset.comment %></em></span>
<span class="timestamp">
<br />Added <%= time_ago_in_words(asset.created_at) %> ago. <br /> Assigned to: <%= link_to
#user.name %>
<br />Date Purchased: <%= asset.date_purchased %>
</span>
</li>
#user = User.find(params[:id]) is expecting a URL along the lines of /assets/40, where 40 is the id (and even then that wouldn't make sense, since the 40 should be the id of the asset, not the user). When you just access /assets, there is no id parameter, so it tries to basically do User.find(nil), which gives you that error.
Assuming assets belong to users, you should just get rid of the #user = User.find(params[:id]) line in your controller, and change your partial to use asset.user.name instead of #user.name.
To avoid an N+1 query situation on your users, you could update your controller to use this:
#assets = Asset.includes(:user).paginate(page: params[:page])

Refactoring - Chapter 11, Exercise 3 Rails Tutorial 2nd Edition

Anyone else working through Chapter 11 Exercises for Michael Hartl's Rails Tutorial 2nd Edition?
Chapter 11, Exercise 3 asks:
Refactor Listing 11.31 by adding partials for the code common to the following/followers pages, the Home page, and the user show page.
I'm not seeing anything worth refactoring in the homepage, user show page, or the show_follow page
If anyone came up with something worthwhile for this exercise, would love to know.
Thanks!
You can refactor the first block of code from Listing 11.31:
<section>
<%= gravatar_for #user %>
<h1><%= #user.name %></h1>
<span><%= link_to "view my profile", #user %></span>
<span><b>Microposts:</b> <%= #user.microposts.count %></span>
</section>
because it is essentially the same as the views\shared_user_info.html.erb partial used on the home page (Listing 10.32). Therefore, you can replace the block of code above with this:
<%= render 'shared/user_info' %>
Note that you will also need to add <% #user ||= current_user %> to the top of the views\shared_user_info.html.erb partial (which is the same as what was necessary to add to the stats partial in Listing 11.20).
Additionally, there is some duplication (though not exact duplication) between the feed_item + feed partials with the user + micropost partials, where depending on which page is being displayed (follow_show, home, or profile) there are one or more elements being listed (name, gravatar, admin delete link, micropost content, micropost time stamp, and micropost delete link). Those could probably be refactored too to eliminate the feed_item+feed partials and replace them with a combination of the user + micropost partials depending on the page.
I just worked through this exercise and found a solution that works.
First I changed around app/views/shared/_user_info.html.erb to use the #user variable if it is set, and the current_user variable otherwise.
app/views/shared/_user_info.html.erb:
<% if #user %>
<%= link_to gravatar_for(#user, size: 52), #user %>
<h1>
<%= #user.name %>
</h1>
<span>
<%= link_to "view my profile", #user %>
</span>
<span>
<%= pluralize(#user.microposts.count, "micropost") %>
</span>
<% else %>
<%= link_to gravatar_for(current_user, size: 52), current_user %>
<h1>
<%= current_user.name %>
</h1>
<span>
<%= link_to "view my profile", current_user %>
</span>
<span>
<%= pluralize(current_user.microposts.count, "micropost") %>
</span>
<% end %>
Then I replaced the corresponding information in app/views/users/show_follow.hmtl.erb with the partial _user_info.html.erb
app/views/users/show_follow.hmtl.erb:
<div class="row">
<aside class="span4">
<section>
<%= render 'shared/user_info' %>
</section>
<section>
<%= render 'shared/stats' %>
<% if #users.any? %>
<div class="user_avatars">
<% #users.each do |user| %>
<%= link_to gravatar_for(user, size: 30), user %>
<% end %>
</div>
<% end %>
</section>
</aside>
<div class="span8">
<h3><%= #title %></h3>
<% if #users.any? %>
<ul class="users">
<%= render #users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
I hope this answer helps anyone going through M. Hartl's tutorial.

RoR Difficulty accessing params in controller

I have an edit page with the following code, basically a dropdown and button that calls update.
<% form_tag('switch_car', :method => :put) do%>
<div class="field">
<label>Car Name:</label>
<%= select("params", ":id", #available_cars.collect {|v| [v.name, v.id]})%>
</div>
<div>
<%= submit_tag "Switch Car" %>
</div>
<% end %>
The server reads like the params is being set:
Parameters: {"utf8"=>"Γ£ô", "authenticity_token"=>"8vHXrnICaOKrGns6FfMUcd/dWo5kpNKpA8F5l5ozRkY=", "params"=>{":id"=>"9"}, "commit"=>"Switch Car"}
However, when I put the params into a session I get nothing. It seems to be always nil. Not sure what I am doing wrong? Here is code in the controller.
def update
if params[:id]
session[:car_info_id] = params[:id]
redirect_to entry_url
else
redirect_to switch_car_path
end
end
It always gets redirected to the switch_car_path so I am assuming params[:id] is always nil. When I put if params[:id] == nil it goes to the entry_url.
Thanks in advance.
you want params[:params][":id"]
Alternatively, you could put this in your view:<%= select("car_info", "id", #available_cars.collect {|v| [v.name, v.id]})%>
And then in your controller:if params[:car_info][:id]
While the other answer would work, this is probably what you'd want to be doing (using select_tag(:id) will automatically add an :id key/value to the params hash):
<% form_tag('switch_car', :method => :put) do %>
<div class="field">
<label>Car Name:</label>
<%= select_tag(:id, options_from_collection_for_select(#available_cars, "id", "name")) %>
</div>
<div>
<%= submit_tag "Switch Car" %>
</div>
<% end %>
Then you can easily access params[:id] in the controller.