Rails: Grouping Partials Based on an Attribute - ruby-on-rails

I've got an application that uses a calendar, and I'd like to provide a calendar summary on login. The summary should be formatted so it shows events for Today, Tomorrow, and This Week, like this:
Events For:
Today
Event 1
Tomorrow
Event 2
Event 3
Event 4
This Week
Event 5
Event 6
Event 7
Event 8
How do I render the partials so that they are grouped together in the right way, based on their date?

First one could add a model method for_dates(start_date,end_date) which would contain:
where([:date_column >= ? and :date_column <= ?, start_date, end_date])
Then use:
Model.for_dates(Date.today, Date.today)
Model.for_dates(Date.today+1, Date.today+1)
Default 'week' is Sunday to Monday. Add an offset if you wish different days, e.g.
Monday to Friday is
Model.for_dates(Date.today.beginning_of_week+1, Date.today.end_of_week+1)

Supposing your model is called Event and it has a date attribute, and your collection of these is called #events, here's one idea: Put this in your view...
<ul>
<%= render #events, locals: { events: #events } %>
</ul>
...then you would have a partial called _event.html.erb that looks like this:
<% last_event = event_counter == 0 ? nil : events[event_counter - 1]
next_event = events[event_counter + 1]
%>
<% if last_event && last_event.date != event.date
# add a new date header and start a new nested list of events
%>
<li><h3><%= event.date %></h3>
<ul>
<% end %>
<li><%= link_to event %></li>
<% if next_event && next_event.date != event.date
# end the nested list and make way for the next date header and set of events
%>
</ul>
</li>
<% end %>
What's happing is in the render call we're passing in the whole #events collection as a local called events. Then inside the partial we use the automatically-generated event_counter method to look up the previous (events[event_counter - 1]) and next (events[event_counter + 1]) events in the collection. Then if the date of the current event is different from the date of last_event (meaning it's the first one for that date) we start a new set with a new date heading, and if the date of event is different from the date of next_event (i.e. it's the last one for that date) we end the set.
It's a little ugly, and there are more elegant ways to do it for sure, but it gets the job done.

Related

How to get Date only on a DateTime attribute? working on a calendar

I am working with a Calendar and I think that calendar deals only with date without a time.
Note: requested_date is a DateTime Attribute. I want to get all reservations with requested date and covert it to date only:
users_controller.rb
def myprofile
#reservation = Reservation.all
#resv_by_date = #reservation.group_by(&:requested_date) <-- make requested_date to date only
end
myprofile.html.erb
<div id="admin">
<%= calendar do |date| %>
<%= date.day %><br>
<% #resv_by_date.each do |reservation| %>
<%= reservation.requested_date.to_date %><br>
<% end %>
<% end %>
</div>
(this month 'May') requested_date is existing on my database
image output after the solution of the 1st answer
I don't think you want to use a group on your ActiveRecord relation not group_by which is a Ruby method.
Also, the method to get a Date from a DateTime object is datetime.to_date:
#reservations_by_date = Reservation.select(:requested_date).group(:requested_date)
#reservations_by_date.each { |reservation| puts reservation.requested_date.to_date }
Normally I'd advice you to try to group the data in the DB already (using group) for performance reasons instead of ruby code (group_by). But in your specific case it seems that you indeed need to retrieve all reservations in the given time period (e.g. month) and just group them by the date so that you can display all of them in the calendar view. In that case, you indeed have to group in ruby code.
You can do that simply by updating the block in group_by:
#reservations_by_date = Reservation.all.group_by { |res| res.requested_date.to_date }
Note that most probably you'll want to narrow down the select only to the given time period (e.g. the displayed month) first, using where conditions, I skipped that part from this answer.
The grouping above will create a hash of the reservations where the keys will be the dates (not datetimes) and the values will be arrays of reservations in the given date. In the calendar view, you can simply browse once you've accessed them using the date key:
<% calendar do |date| %>
<%= date.day %><br>
<% #reservations_by_date[date].each do |reservation| %>
<%= reservation.name ... or whatever %><br>
<% end %>

Rails view segment dependent on udpated_at/created_at time?

I can do this in a view to add labels to news stories:
<% if article.urgency == 'majorbreaking' && article.updated_at.today? %>
<span class="breaking">
…
It works out of the box with 'today', but how do I do the same thing with (say) "last six hours" (in a view, I know how to to it with scopes and controllers to select the articles, but this is after selection, just for the view).
Thanks.
Because this is after selection, we can simply use like this:
article.updated_at >= 6.hours.ago
Simple & pretty
You can minimize the hard-coded logic that your view is processing and potential DRY up your code (if in fact you want to process multiple time spans) by creating a method in your article model. For example:
class Article < ActiveRecord::Base
def recent?(hours_ago=24)
created_at >= hours_ago.hours.ago
end
end
Then in your view:
For within the last day:
<% if article.urgency == 'majorbreaking' && article.recent? %>
<span class="breaking">
For within the last 6 hours:
<% if article.urgency == 'majorbreaking' && article.recent?(6) %>
<span class="breaking">
If you want to replace that at_today with the last 6 hours use:
<% if article.urgency == 'majorbreaking' && article.updated_at > (DateTime.now - 6.hours) %>
<span class="breaking">
…
check out time helpers
http://api.rubyonrails.org/classes/DateTime.html
http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html

Adding dates to the database that can be used to specify an active week

I am in the process of creating an competition app that needs to be pretty much autonomous. The app has a resource called 'Weeks' and each week has many 'Entries'. Users can add an entry to the week, and then they can be voted on.
The week has a database column called 'start_date' and if I populate this with a string, eg "16-03-2015" then on the Show view I can pull in the weeks params and compare the string to todays date and set whether the vote buttons are active or not.
In my weeks controller I use a before action called set_dates and I set a couple of variables
def set_dates
#today = Date.today
#weekstart = Date.parse(#week.active_date)
#weekend = Date.parse(#week.active_date)+7
end
I can then set if and else statements on the view to show the vote buttons if the week is 'active', ie. todays date is within the active_date + 7 days
<% #entries.each do |entry| %>
<div class="entry">
<%= image_tag(entry.photo(:thumb)) %>
<%= entry.cached_votes_up %>
<% if (#weekstart..#weekend).include? #today %>
<%= link_to "Upvote", upvote_week_entry_path(entry.week_id, entry) %>
<%= link_to "Downvote", downvote_week_entry_path(entry.week_id, entry) %>
<% end %>
</div>
<% end %>
This was fine until I realised I needed to have an 'active week' outside of the show view. I want to put a link into the Navigation Bar in the application_layout to 'This Weeks Comp', and I need this week to point to whichever week voting is active on. But without finding the week by its ID, then cross referencing it's active_date, I am unsure how to do this. Any help would be greatly appreciated.
Thanks!
If anyone else is find this looking to solve a similar problem, I solved the issue by adding an end_date column to the weeks resource, and then assigned a variable to an arel query in the application controller
App-Controller
before_action :active_week
protected
def active_week
#aweek = Week.where("? BETWEEN start_date AND end_date", Date.today)
#active = #aweek.first
end
Application Layout
<%= link_to "This Week", #active %>

Rails - How to pass data from view or url to helper

In Rails I'm using code from Railscasts #213 revised to build a calendar. I'm using it to display Event hours for each day.
The helper code sets the background-color for today, so you can easily see which day is today.
I have it working so that you can click on a day and a table will show detail records of Events for that day.
This is the code that is executed when the user clicks on a day in the calendar:
<div><%= link_to 'View' , events_index7_path(:date_selected => date), :class => 'btn btn-mini'%></div>
That puts the selected day into the url, like this:
.../events/index7?date_selected=2013-11-22
And the view tests for the day_selected like this:
<% current_user.events.where(:event_date => #date_selected).each do |event| %>
I would like to set the css background-color for the date_selected
I'm trying to add the dayselect class to the helper:
def day_classes(day)
classes = []
classes << "today" if day == Date.today
classes << "dayselect" if day == #date_selected
classes << "notmonth" if day.month != date.month
classes.empty? ? nil : classes.join(" ")
end
But, it doesn't recognize #date_selected
I also tried this:
classes << "dayselect" if day == params[:date_selected]
UPDATE1:
This is a pic of the calendar so you better understand what I'm doing:
UPDATE2
View code for the calendar:
<%= calendar #date do |date| %>
<%= date.day %>
<% hours = current_user.events.where(:event_date => date).sum(:hours) %>
<% if hours != 0 %>
<div class="sumhours"><%= hours %></div>
<% else %>
<div> - </div>
<% end %>
<div><%= link_to 'View' , events_index7_path(:date_selected => date), :class => 'btn btn-mini'%></div>
UPDATE3
If I stop execution at the helper code to set the class, I find this:
Request parameters
{"date_selected"=>"2013-11-21", "controller"=>"events", "action"=>"index7"}
How do I access the date_selected parameter?
UPDATE4
This will highlight a specific date converted from a string:
classes << "dayselect" if day == DateTime.strptime("2013-11-18", "%Y-%m-%d")
But, if I used this:
classes << "dayselect" if day == DateTime.strptime(params[:date_selected], "%Y-%m-%d")
I get:
undefined local variable or method `params' for #<CalendarHelper
I couldn't figure out how to do it with Rails, so I used jQuery.
Used pure.js to get the url.
Here's the jQuery (Coffeescript):
url = $.url().param("date_selected")
$("#monthcalendar td").each ->
$(this).addClass "dayselect" if $(this).attr('class') == url

How do I remove days from date range?

I have a calendaring application I am working on in Rails 3. I am gathering a 'to' and 'from' date from the user and creating a date, and creating events based on that range. Here is the code:
date_range = (params[:from_date]..params[:to_date]).to_a
date_range.each do |r|
params[:orientation][:class_date] = r
#orientation = Orientation.new(params[:orientation])
#orientation.save
end
This works like a charm. Now I want to add check boxes in the view for Monday - Friday so the user can specify which days the want a repeating event to be created for. My thinking is to expand on the logic above and create a date range with the 'to' and 'from' dates, then remove dates based on check boxes (days) the user checks. If the user checks 'Mon', 'Wed', 'Fri', I would create my date range, then remove all Thursday and Friday dates from the range. Can anyone suggest any methods in Ruby that might be handy for dealing with this?
You could add checkboxes in your form to ignore the selected date and then ignore each day in the loop:
# view
<% Date::DAYNAMES.each_with_index do |day_name, wday| %>
<%= check_box_tag "ignored_days[]", wday %>
<%= day_name %>
<% end %>
# controller
date_range.each do |r|
next if params[:ignored_days].include? r.wday.to_s #added edit here
params[:orientation][:class_date] = r
#orientation = Orientation.new(params[:orientation])
#orientation.save
end
Hope this helps!

Resources