Group posts by Year - Rails - ruby-on-rails

I have model called "Shoes" that belongs to the a model called "History". I want to show all of the shoes in my History show.html.erb page, but I want to group them by their release date. (I have an attribute called release for that).
I'm using this example from Railscasts:
shoes_controller
def index
#shoes = Shoe.all(:order => 'release, name')
#release_year = #shoes.group_by { |t| t.release.beginning_of_year }
end
Shoes index.html.erb view
<% #release_year.each do |release, shoes| %>
<% shoes.each do |shoe| %>
<%= shoe.name %>
<% end %>
<% end %>
I want to be able to do this but on my History show.html page, how can i accomplish this?
What I have so far:
History show.html.erb
<% #history.shoes.each do |shoe| %>
<div class="shoe">
<%= shoe.name %>
</div>
<% end %>
Thanks.

Perhaps you may want to create a _shoes_by_year partial in the shoe views, and then render the shoe collection:
views/shoes/_shoe.html.erb
<div class=shoe>
<%= shoe.name %>
</div>
views/shoes/_shoes_by_year.html.erb
<% shoes_by_year.each do |release_year, shoes| %>
<div>
<h3><%= release_year.year %></h3>
<%= render 'shoes/shoe', collection: shoes %>
</div>
<% end %>
histories_controller.rb
def show
...
#shoes = #history.shoes.group_by { |shoe| shoe.release.at_beginning_of_year }
end
/views/history/show.html.erb
...
<%= render 'shoes/shoes_by_year', object => #shoes %>

Related

One to many relationship fom_for take the last 3 from all the model attributes

I have one to many relationship(oferta have many sigs) and I want to show the last three sigs from all the oferta
I tried this code but it show the last 3 sigs from each oferta
In home index.erb
<% #oferta.each do |o| %>
<% if o.sigs.exists? %>
<% for item in o.sigs.order("created_at asc").last(3).each %>
<div class="col-md-4 col-sm-4">
<div class="coll">
<br>
<%= link_to item do %>
<%= image_tag item.image.url(), skip_pipeline: true ,id: "img",height: "200px"%>
<% end %>
<h4><%=link_to item.name,item %></h4>
<p id="comment"><%= item.comment %></p>
<%= link_to "read more..", item %>
<p id="price"><%= item.price %></p>
</div>
</div>
<% end %>
<% end %>
<% end %>
In the controller
def index
#oferta = Ofertum.unscoped.first(3)
end
In ofertum model
has_many :sigs
In sig model
belongs_to :ofertum
You can do it in both ways,
#last_3_sigs = Sigs.last(3)
for getting latest records first use this
#last_3_sigs = Sig.order(created_at: :desc).limit(3)

Combine 2 models into one array, paginate, and force 1 partial

I want to combine data from two different models, combine it into one array and use the will_paginate gem to paginate the results in my view on that collection. I'm already using require 'will_paginate/array' to allow for a normal, non-ActiveRecord array and its all working properly.
My problem is that its using two different partials to render the entries based on which model the data originated from. How do I force it render one specific partial?
controller
def sample_action
page = params[:page] || 1
#collection = []
#car.messages.select {|msg| #collection << msg}
Alerts.where(car_id: #car.id).select {|alerts| #collection << alerts}
#collection = #collection.sort{|a,b| b[:created_at] <=> a[:created_at]}
#collection = #collection.paginate(:page => page, :per_page => 30)
respond_to do |format|
format.html { render :sample_action }
end
end
view
<% if #collection.count > 0 %>
<div class="row">
<div class="col-xs-12">
<div id="car-alerts" class="car-alerts-all">
<%= render #collection -%>
</div>
</div>
</div>
<%= will_paginate #collection -%>
<% else %>
<p class="no-alerts-message">Your car is healthy</p>
<% end %>
car partial
Car - <%= collection.id %>
<br>
alert partial
Alert - <%= collection.id %>
<br>
I would use the fact that you basically just want to output the model name and do this:
<% if #collection.count > 0 %>
<div class="row">
<div class="col-xs-12">
<div id="car-alerts" class="car-alerts-all">
<%- #collection.each do |obj| -%>
<%= obj.class.to_s -%> - <%= obj.id %>
</div>
</div>
</div>
<%= will_paginate #collection -%>
<% else %>
<p class="no-alerts-message">Your car is healthy</p>
<% end %>
but it would break easily if you wanted to add labels different to class names.
You need to iterate over each item in the collection and have a conditional based on the model type, so for example:
<div id="car-alerts" class="car-alerts-all">
<% #collection.each do |car_or_alert| %>
<%=
if car_or_alert.is_a?(Alert)
render :alert #you need to add locals here as well, but it unclear what locals you are using
else
render :car #see above
end %>
<% end %>
</div>
Turns out it was as simple as setting the partial from render
<% if #collection.count > 0 %>
<div class="row">
<div class="col-xs-12">
<div id="car-alerts" class="car-alerts-all">
<%= render collection: #collection, partial: 'car/partial_name' -%>
</div>
</div>
</div>
<%= will_paginate #collection -%>
<% else %>
<p class="no-alerts-message">Your car is healthy</p>
<% end %>
This being the key change -
<%= render collection: #collection, partial: 'car/partial_name' -%>

Indent every child of a depth first search (ruby on rails)?

I am new to rails, and I was wondering if there was a simple (or not simple) way to indent every child of a DPS?
I have a model called "Comment" that has_many :comments and belongs_to :comment. In my view I've implemented a DPS to display each comment and each comment on that comment, and each comment on that comment, etc.
My code looks like this:
<div class=feed>
<% #comments.each do |comment| %>
<% if comment.comment_id == nil # display all original comments %>
<!-- subject -->
<div class="subject">
<%= comment.subject %>:
</div>
<!-- create array of replies -->
<% replies = Array.new %>
<% replies.push(comment) %>
<% while replies.any? %>
<% reply = replies[0] %>
<% replies.delete_at(0) %>
<!--- name -->
<div class="comment">
<%= User.find(reply.user_id).name %>
<!-- comment -->
<%= reply.body %>
<% if user_signed_in? %>
<%= link_to "reply", new_comment_comment_path(reply.id) %>
<% end %>
</div>
<% reply.comments.each do |further_replies| %>
<% replies.push(further_replies) %>
<% end %>
<br>
<% end %>
<br>
<% end %>
<% end %>
</div>
where I push each comment onto "replies" and visit each reply one by one.
Is there a good way to indent each child comment?
Thanks!
You can use act_as_tree structure, in that case your model will be like -
class Comment
has_many :replies, class_name: 'Comment', foreign_key :comment_id
belongs_to :user
end
Your html code will be very simple in that case like
<div class=feed>
<% #comments.each do |comment| %>
<div class="subject">
<%= comment.subject %>:
</div>
<% comment.replies.each do |reply| %>
<div class="comment">
<%= reply.user..name %>
<%= reply.body %>
<% if user_signed_in? %>
<%= link_to "reply", new_comment_comment_path(reply.id) %>
<% end %>
</div>
<br>
<% end %>
<br>
<% end %>
</div>
I figured out a different approach that worked well. I implemented a recursive depth first search in "CommentsController" that returned a hash of { comments => amount_to_indent }, where comments are in the order that they were visited. In the view file iterated though the hash, using comment and amount_of_indent where appropriate.
class CommentsController < ApplicationController
def index
#comments = Comment.all
# depth first search
#visited = Hash.new
#comments.each do |comment|
if !comment.comment_id # has not parent comment
indent = 0
comment_array = DFS(comment, #visited, indent)
end
end
end
def DFS(comment, visited, indent)
visited[comment] = indent # add elements in order in which they are visited
comment.comments.each do |reply|
DFS(reply, visited, indent + 4)
end
visited
end
And in the view file:
<div class="feed">
<% #visited.each do |reply, indent| %>
<!-- display subject of all parent comments -->
<div class="subject">
<% if !reply.comment_id? %>
<%= reply.subject %>:
<% end %>
</div>
<!--- name and content -->
<div class="comment">
<!-- preceed comment with indent -->
<%= raw(('&nbsp') * indent) %>
<%= User.find(reply.user_id).name + ":" + reply.body %>
<% if user_signed_in? %>
<%= link_to "reply", new_comment_comment_path(reply.id) %>
<!--%= link_to 'delete', response, method: :delete, data: { confirm: 'Are you sure?' } %-->
<% end %>
</div>
<% end %>
</div>
If anyone runs into the same issue I did!

Rails element if first time appearance

I have a model Post with :mark, :text
I have a list with my post
<% #posts.each do |p| %>
# todo
<% if p.mark? %>
<%= p.mark %> <%= sweet_thing(p.text) %>
<% else %>
<%= sweet_thing(p.text) %>
<% end %>
<% end %>
I need to show p.mark name instead #todo where p.mark first time appearance.
Final txt example:
Audi
Audi, text-text-text-text.
Audi, text-text-text-text.
Audi, text-text-text-text.
Ford
Ford, text-text-text-text.
Ford, text-text-text-text.
Ford, text-text-text-text.
Ford, text-text-text-text.
UPDATE
My txt render in controller
def txt_receiver
#posts = Post.where("created_at >= ?", 7.days.ago.utc).find(:all, order: "mark, LOWER(post)")
render "txt_for_newspapper", formats: ["text"]
end
An obvious solution is to keep track of seen marks.
<% seen_marks = {} %>
<% #posts.each do |p| %>
<% unless seen_marks[p.mark] %>
<%= p.mark %>
<% seen_marks[p.mark] = true %>
<% end %>
# rest of your code
<% end %>
A better solution (I think) involves grouping posts by their mark and then outputting in groups. But I'm not sure whether it will match your logic regarding missing marks.
<% #posts.group_by(&:mark).each do |mark, posts| %>
<%= mark %>
<% posts.each do |p| %>
<%= p.mark if mark %> <%= sweet_thing(p.text) %>
<% end %>
<% end %>

How can I refactor out needing so many for-loops in rails?

I need help refactoring this multi-loop thing. Here is what I have:
Campaign has_many Contacts
Campaign also has many Models which are templates: (Email, Call, and Letter).
Because I am looking for overdue on each, I created an array called Event which I'd like to loop through that contains ['email', 'call', 'letter'].
I need a list of all the Emails, Calls and Letters that are "overdue" for every Contact that belongs to a Campaign. Overdue is determined by a from_today method which looks at the date the Contact was entered in the system and the number of days that needs to pass for any given Event. from_today() outputs the number of days from today that the Event should be done for a given Contact.
Here is what I've done, it works for all Emails in a Campaign across all contacts. I was going to try to create another each do loop to change the class names.
Wasn't sure where to begin: named_scope, push some things into a method, etcetera, or -- minimum to be able to dynamically change the class names so at least it loops three times across the different events rather than repeating the code three times:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<% #events.each do |event| %>
<%= event %>
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% for contact in campaign.contacts.find(:all, :order => "date_entered ASC" ) %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Just to add to Patrick's answer, I would also use the :collection option of render to simplify this a bit further, e.g. have a partial _contact.html.erb to render each contact:
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
and then render the contacts collection with
= render :partial => "contact", :collection => #contacts
I also wouldn't do a find in the view, instead I would setup all the variables in the controller, and probably move all the conditional code into a helper. It's preferable to keep as much logic as possible out of the views.
I'd put the output for each resource into a partial, like so:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<%= render 'events', :events => campaign.events %>
<% end %>
then in app/views/campaigns/_events.html.erb
<% events.each do |event| %>
<%= event %>
<%= render 'emails', :emails => event.emails %>
<% end %>
then in app/views/campaigns/_emails.html.erb
<% emails.each do |email| %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<%= render 'contacts', :contacts => email.contacts.all(:order => "date_entered ASC", :email => email) %>
<% end %>
then in app/views/campaigns/_contacts.html.erb
<% contacts.each do |contact| %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>

Resources