Rails: display #cars as a comma-separated list - ruby-on-rails

Based on this query:
#cars = Car.where("manufacturer_id IN ?", #mfts.select("id")).limit(30).select("id")
How can I display the cars' IDs in the view like this (or do I need to rewrite my query)?
3,2,5,12,15,24,34,63,64,65,66,85
Thanks a lot - I've looked for this but couldn't find the right question/answer.
One solution is to do:
#view
<% #cars.each do |c| %><%= c.id %>,<% end %>
I don't know if there's a better way to go about it - this obviously leaves a stray comma at the end of the list (which isn't a dealbreaker). Any more elegant solutions?

One line:
<%= #cars.map(&:id).join(",") %>

If writing &:id seems confusing, there's another way that's a little more readable.. If y'all want to access a method or attribute, it might look better to inline a block.
<%= #cars.map { |car| car.id }.join(", ") %>
P.S... another name for map is collect.. that's what it's called in Smalltalk.
Lookin' good!

With Rails 3.0+ you can now write:
<%= #cars.map { |car| car.id }.to_sentence %>
Rails will appropriately add the comments and the word 'and' between the last two elements.

Related

How do I elegantly check for presence of both the object and associated objects?

I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|

Rails output polymorphic associations

I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.

Limit each do loop

If I have the following,
<% #feed.sort_by{|t| - t.created_at.to_i}.each do |feed| %>
<% end %>
How can limit it to only show the 10 most recent results
<% #feed.sort_by{|t| - t.created_at.to_i}.first(10).each do |feed| %>
However, it's probably best to push this down into the model like this
<% #feed.recent(10).each do |feed| %>
And, in fact, if #feed comes out of a database, I'd push it down even further: it doesn't make sense to load a ton of unsorted feed entries out of the DB, then sort them and then throw most of them away. Better let the DB do the sorting and filtering.
See #Peer Allan's answer for how to do it in ActiveRecord. In ARel (IOW: Rails 3) it would probably be even simpler, something like
Feed.all.order('created_at DESC').take(10)
Array#first(n)
[1,2,3,4,5].first(3)
=> [1,2,3]
I'd do it like this:
<% #array.limit(10).each do |a| %>
I agree with the others (Jörg in particular); but if you still want to know how to limit the loop itself, break can be useful.
#array.each_with_index do |feed, i|
break if i == 10;
# ...
The following code will return 10 recent records.
#feed = #feed.sort! { |a,b| b.created_at <=> a.created_at }.take(10)
Array Reference
The created_at seems to indicate that you are using ActiveRecord in Rails to get set the #feed variable. If that is the case you are better to do this work in SQL. Its far more efficient and easier to deal with.
#feed = Feed.all(:order => 'created_at DESC', :limit => 10)
Otherwise if you really want to use the view to do this you can use first or a range
<% #feed.sort_by{|t| - t.created_at.to_i}[0..9].each do |feed| %>
<% #feed.sort_by{|t| - t.created_at.to_i}.first(10).each do |feed| %>

Ruby and Rails: Statement Modifiers in Views?

I have this code
<% if approved %>
<td>Flow Number</td>
<% end %>
and I'd like to shorten it using statement modifiers. Of course I can use
<%="<td>Flow Number</td>" if approved -%>
but is there a shorter way? I'd also like to get the markup out of quotes.
You could use "content_tag", which isn't actually shorter, but may be more appealing, keeping HTML out of your ruby blocks:
<%= content_tag :td, "Flow Number" if approved %>
Otherwise, you could consider writing a helper - which may be appealing if you need to reuse similar logic throughout the page (or over several pages).
Maybe HAML?
That'd be:
- if approved?
%td Flow Number
Not exactly what you're after I know.
Yeah, I think a helper method using content_tag internally would be the best short way.
Using a helper method, you could also yield to the desired output like this:
# in view helper
def show_if(condition, wrapper_tag)
condition ? content_tag(wrapper_tag, yield) : ''
end
# in view
<%= show_if(approved, :td) {'Flow Number'} %>
or
# in view helper
def show_if(condition)
condition ? yield : ''
end
# in view
<% show_if(approved) do %>
<td>Flow Number</td>
<% end %>
I like this last method for a nice generic way to show or hide whole blocks based on a condition. Hope that helps!

Is it best practice to achieve recursion via a partial?

I have the need to display a nested set structure in HTML. I am doing it with the following partial:
<ul<%= ' id="tree"' if depth == 0 %>>
<% items.each do |item| %>
<li id="node_<%= item.id %>"><a><%= item.name %></a>
<% if item.has_children? %>
<%= render :partial => 'tree_level', :locals => {:items => item.children, :depth => depth + 1} %>
<% end %>
</li>
<% end %>
</ul>
Is this the best place to have the code? I "feel" like there should be a to_html method on the object which dumps the entire tree structure for me, though this works.
I am not sure whether it is best practice but I used similar code for rendering project tree.
Faster alternative is to create helper method doing the same job (recursively traversing tree and adding partial strings into result string). It is a little bit PHP style :( but for such a small amount of HTML is it OK, I guess :)
Helper looks like:
def render_node(node)
res = "<ul>"
...
node.items.each {|n| res << render_node(n)}
...
res << "</ul>"
res
end
Then it is used like this:
<%=render_node ProjectTree.new%>
Well, you should realize there's a (small) overhead for using partials, so if performance is an issue, you may not want to use them this much. Otherwise I see little problem with using this.
However, you might want to use the collection-variant of partials (see "Rendering a collection of partials" on this API page, it could clean up your code a bit.

Resources