Use only the first image if there are multiples - ruby

I want to get an understanding of how to achieve the following. I have a simple CMS that will allow you to upload portfolio information/images. The user can upload one image or multiple images.
To show each image I use a .each and iterate through all the images for each portfolio...
<!-- Project Feed -->
<div class="project-feed clearfix">
<% #portfolios.each do |p| %>
<div class="one-third project-item"><!-- add data filter class here to enable selection of sectors-->
<% p.images.each do |i| %>
<%= image_tag(i.photo.url(:portfolio_square)) %>
<div class="overlay">
<!-- Small image bottom right of overlay, click and can navigate through all images
<%= image_tag(i.photo.url(:portfolio_large), :class => 'lightbox-link folio', :rel => 'gallery') %> -->
<%= link_to p do %>
<h5><%= p.title %> </h5>
<% end %>
<p>
<%= p.sectors.map(&:name).join(", ") %>
</p>
</div><!--/overlay-->
<h4><%= p.title %></h4>
</div>
<% end %>
<% end %>
My problem is that if there are multiple images assigned to the portfolio (let's say Portfolio 3 has 4 images associated with it) then they all show.
So I want to achieve this layout:
Portfolio 1 Portfolio 2 Portfolio 3
as opposed to
Portfolio 1 Portfolio 2 Portfolio 3
Portfolio 3 Portfolio 3 Portfolio 3
How can I grab only one image if there are multiples? Would this be .first? though I am unsure of the syntax and how it would fit in with my code. Or would I create a separate block and put it in an if statement if the image count is more than 1?

You can use first like this:
<%= image_tag(p.images.first.photo.url(:portfolio_square)) %>
If you do this you don't need:
<% p.images.each do |i| %>

If I understand correctly, you want to change the behavior of p.images.each to take only the first element, in that case, the minimal impact change on your code could be:
<% p.images.take(1).each do |i| %>

Related

How to reuse ERB templating code in function?

I have this piece of code inside ERB template to render all articles in list.
<% #list.each do |a| %>
<article class="my-panel">
<header>
<h2><%= a[:name] %></h2>
<time datetime="<%= to_datetime(a[:time]) %>"><%= time_description(a[:time]) %></time>
</header>
... more stuff cut out
</article>
<% end %>
Now I am going to have to change it to something like this:
<% #list[content_splitter.before_ad_range].each do |a| %>
<%= render_article(a) %>
<% end %>
<%= AdCreator.between_content_ad %>
<% #list[content_splitter.after_ad_range].each do |a| %>
<%= render_article(a) %>
<% end %>
I thought it would be nice to have render_article defined in template as opposed to having html clutter my ruby code. But when I move that code inside function I get an error.
This is the function:
<% def render_article(a) %>
<article class="my-panel">
<header>
<h2><%= a[:name] %></h2>
<time datetime="<%= to_datetime(a[:time]) %>"><%= time_description(a[:time]) %></time>
</header>
<div class="image">
<img alt="" src="<%= rel_url_to a[:img_url_1x] %>" srcset="<%= rel_url_to a[:img_url_2x] %> 2x, <%= rel_url_to a[:img_url_3x] %> 3x">
</div>
<div class="text">
<%= a[:article_text] %>
</div>
</article>
<% end %>
This is the error:
undefined local variable or method `_erbout' for #<Html::FrontPage:0x0055fb94005c68>
Line of code producing this error is:
self.class.instance_variable_get(:#renderer).result(binding)
Why is this happening? How to find more informative error?
How to fix this? Can I avoid moving this obviously html dominant code into ruby helper file?
PS. I suspect problem is that functions to_datetime and time_description can't be accessed from inside ERB function.
I know that function render_article does get called because if I change it's signature to remove parameter I get an error
wrong number of arguments (given 1, expected 0)
# (erb):45:in `render_article'
What you are trying to do is not technically impossible, but rather difficult and not recommended as can be seen by reading comments and answers to this question.
As your error message points out your def won't quite happen in the context you "probably" expect and you certainly won't be able to "freely use erb features" while defining the method (since your context is completely different from what you expect).
The "railsy" way to do this is adding a helper or using a partial, but both come with their drawbacks. .erb files (as most templating-languages) do not "factor" well. If you want to factor things somewhat more freely you should look at the fortitude gem which provides what is basically a ruby-DSL for html which factors pretty easily. However, that is a rather drastic change from what you are probably used to.
If you really want to define a method inside an .erb-file then you would have to do it entirely within a single pair of <% ... %> brackets where you will have only access to your params, not your context. You would have to return what is basically a String in order to be able to use it in <%= ... %> and pay a hell of a lot of attention to escaping rules for everything to make it through. This is most probably more trouble than it is worth (but easy enough to do in fortitude :-).

Retrieve data using for loop in Ruby

I have a question about using foreach loops in Ruby.
I want to display documents and am using a foreach loop in order to display these documents. It returns an error with the i variable inside of data["response"]["docs"][i]["topic"] which is a JSON string I am iterating over.
I do not understand why that is. Can anyone tell me what I am doing wrong?
If I simply do data["response"]["docs"][0]["topic"] it works fine but not with the i. Why is that?
<%
(0..10).each do |i|
%>
<%= i %> <br/>
<%= data["response"]["docs"][i]["topic"] %>
<%
end
%>
My question is, how many items are there in data["response"]["docs"]? Are there exactly 11? Either way I would use the following code instead:
<% data["response"]["docs"].each_with_index do |item, index| %>
<%= index %>
<br/>
<%= item["topic"] %>
<% end %>
This iterates over the data["response"]["docs"] no matter how many there are (whether is is 1 doc or 20 docs) and stores the value in the variable named item. The each_with_index function gives you the index as well, stored in index, so you can display it later. If you only want the first 11 use:
<% data["response"]["docs"].first(11).each_with_index do |item, index| %>
This will grab a maximum of 11 doc items.
It's hard to tell what might be going wrong because you haven't posted the error, but if you're using a 10-element array, you want to do:
(0..9).each do |i|
With 0-based indexes, you should only use the range from 0-9, rather than 0-10. You may be getting an error because you're trying to access an element that isn't there (i.e. at index 10).
Even better is:
<% data["response"]["docs"].each do |document| %>
<%= document["topic"] %>
<% end %>
or if you need to print the index:
<% data["response"]["docs"].each_with_index do |document, index| %>
<%= index %> <br/>
<%= document["topic"] %>
<% end %>

Splitting ruby content with in_groups_of

I am using middleman's blog to output the summary of all blog posts on a page like so:
<ul>
<% page_articles.each_with_index do |article, i| %>
<% unless article.data['tags'].include?('featured') %>
<li class="Project">
<h3><%= link_to article.title, article.url %></h3>
</li>
<% end %>
<% end %>
</ul>
This works well, but I need to break after 3 entries like so:
<ul>
<li class="Project"><li>
<li class="Project"><li>
<li class="Project"><li>
</ul>
<div class="break"></div>
<ul>
<li class="Project"><li>
<li class="Project"><li>
<li class="Project"><li>
</ul>
From research, it seems that ruby's in_groups_of seems like the way to go here, but cannot get the syntax working properly. Here is what I have:
<ul>
<% page_articles.in_groups_of(3, false).each_with_index do |article, i| %>
<% unless article.data['tags'].include?('featured') %>
<li class="Project">
<h3><%= link_to article.title, article.url %></h3>
</li>
<% end %>
<% end %>
</ul>
It is returning an undefined method 'in_groups_of'. Also, it seems like the unless logic should be applied before it is split into groups of three. Any thoughts on ways to improve this?
Array#in_groups_of is an activesupport extension added by Rails to allow you to split and access equal groups (Array) with a fixed number of elements (by default nil will fill the uneven split. e.g.
require "active_support/core_ext/array"
a = (1..5).to_a
a.in_groups_of(2)
#=> [[1,2],[3,4],[5,nil]]
When you pass the second argument (fill_with) as false it no longer returns equal groups but rather splits the elements into groups of n elements and when the split is uneven the last group will be the remaining elements regardless of n.
require "active_support/core_ext/array"
a = (1..5).to_a
a.in_groups_of(2,false)
#=> [[1,2],[3,4],[5]]
This non-equal grouping functionality is actually available without the use of activesupport through Enumberable#each_slice
a = (1..5).to_a
a.each_slice(2).to_a
#=> [[1,2],[3,4],[5]]
require "active_support/core_ext/array"
a.in_groups_of(2,false) == a.each_slice(2).to_a
#=> true
Since you are specifying the second argument as false in this case instead of require-ing functionality you don't need I would recommend using Enumerable#each_slice as follows:
<ul>
<% page_articles.each_slice(3).with_index do |article, i| %>
<% unless article.data['tags'].include?('featured') %>
<li class="Project">
<h3><%= link_to article.title, article.url %></h3>
</li>
<% end %>
<% end %>
</ul>
This will function as requested without any additional overhead as Enumerable is part of the ruby core.
Array#in_groups_of is not part of core Ruby, but added by Rails as part of Active Support Core extensions.
You can use Active Support extensions by requiring it.
require "active_support/core_ext/array"

Sorting in rails app

I created an app that gives points to students. I want to sort the points in descending order. This is my code.
<% #users.each do |user| %>
<li> <%= user.points%></li>
<%end%>
This code outputs the points from each student, but when I try to add sort! after user.points, it gives me an error. I am not sure where I should do this.
You can go for:
<% #users.sort_by{|u| u.points}.each do |user| %>
<li> <%= user.points%></li>
<%end%>
Or you can go for:
#users=User.order(:points)
Try this:
<% #users.sort_by{|u| u.points}.reverse.each do |user| %>
<li> <%= user.points%></li>
<% end %>
Note: I have used reverese as you have mentioned I want to sort the points in descending order. But It's always good practise to assign data to object in controller as per your requirement.
Do it in controller like:
#users = User.order(points: :desc) # this will fetch users in descending order on basis of points
then in view simply use:
<% #users.each do |user| %>
<li> <%= user.points%></li>
<%end%>
for more info regarding Order
The documentation for Active Record .order is what you need.
http://apidock.com/rails/ActiveRecord/QueryMethods/order
For example:
User.order(:points)
would provide you with users ordered by points. Do the ordering from your controller rather in your view.

Ruby if else syntax

I have an app where users can sign up with Facebook, in which case I take the Facebook image, or they can sign up using regular authentication, in which case they're uploading a photo using CarrierWave gem.
Because of some sort of conflict, I had to create a column in the database (:image)to store the url to the Facebook image, and then a different column (:dimage) to store the url for the image if they signed up using the non-facebook sign up.
So when it comes time to display the user, I'm checking to see if there's an :image, and, if not, then displaying the other :dimage.
However, I don't want to require them to upload an image, in which case, I want to display an anon image (here represented by the rails.png). However, the Rails.png isn't showing up (just a broken image path), so I'm assuming there's some sort of error with my if else syntax because the image_tag("rails.png") is taken straight from the api
<% if user.image %>
<%= image_tag user.image %>
<% elsif user.dimage %>
<%= image_tag user.dimage_url(:thumb).to_s %>
<% else %>
<%= image_tag("rails.png") %>
<% end %>
The generated html on the rails.png
<img alt="Assets" src="/assets/">
Use the present? method to check if the attribute is not empty:
<% if user.image.present? %>
<%= image_tag user.image %>
<% elsif user.dimage.present? %>
<%= image_tag user.dimage_url(:thumb).to_s %>
<% else %>
<%= image_tag("rails.png") %>
<% end %>
You'll save yourself a headache or two because in Ruby the only falsehoods are false and nil. This means that the empty string "" will actually return true.
You need to look at look into user.image.nil? user.image.empty? and user.image.blank? depending on what you're using and apply the same to the rest of your conditional. You need to find out specifically what your looking for or the if else won't give you what you want. I would try empty first which checks if something is nil or is an empty string.
When you create a new rails application, rails stores its logo in PROJECT_PATH/app/assets/images folder. Could you check if that image exists?

Resources