Refactoring code out of the view - ruby-on-rails

I have the following view code which displays events, along with headers informing the user if the following events occured today, yesterday, or in the past week.
It all works fine, but I would prefer to refactor it out of the view, I can't put it in a helper because it needs to maintain the variables state between loop iterations. Any idea how to tidy this up? thanks
<% displayed_week_already = false %>
<% displayed_yesterday_already = false %>
<% displayed_today_already = false %>
<% #events.each do |event| %>
<%= event.title &>
<% if (Time.now - 1.week) > event.created_at && !displayed_week_already %>
<% displayed_week_already = true %>
Events in past week
<% elsif (Time.now - 1.day) > event.created_at && (Time.now - 2.day) < event.created_at && !displayed_yesterday_already %>
<% displayed_yesterday_already = true %>
Events yesterday
<% elsif (Time.now - 1.day) < event.created_at && !displayed_today_already %>
<% displayed_today_already = true %>
Events in past day
<% end %>
<% end %>
Expected output would be:
Events in past day
John created a new artice
Dan commented on article 1
Sue deleted article 2
Events yesterday
Sue created a new article
Events in past week
Carol was promoted
Dan cerated a new article

I might do something like the following. I am leaving out the implemented methods defined on the model itself.
existing view
<%= render "events", :title => "Events in past day", :events => from_today(#events) %>
<%= render "events", :title => "Events yesterday", :events => from_yesterday(#events) %>
<%= render "events", :title => "Events in past week", :events => from_this_week(#events) %>
_events.html.erb
<% if events.present? %>
<%= title %>
<% events.each do |event| %>
<%= event.title %>
<% end %>
<% end %>
app/helpers/event_display_helper.rb
module EventDisplayHelper
def from_today(events)
events.select{|x| x.happened_today?}
end
def from_yesterday(events)
events.select{|x| x.happened_yesterday?}
end
def from_this_week(events)
events.select{|x| x.happened_this_week_before_yesterday?}
end
end

You could easily do this in the model, with named scopes (I'm assuming Rails 3 because that's what I know best). You'll need to define them yourself but they're pretty simple.
#last_week = #event.last_week
#yesterday = #event.yesterday
#today = #event.today
Then just iterate over each of the groups in your view. Should make it a lot cleaner and you won't need so much logic, as it's where it actually belongs.

Related

How to group_by different attributes?

I'm trying to create a timeline for #challenges. For those challenges that have a :deadline I want them to be organized on the timeline according to their :deadline and for those challenge that have a :date_started I want them to be organized on the timeline according to their :date_started.
If a challenge has a :deadline then date_started is nil and vice versa.
example
2016
February
CHALLENGE 1 (Deadline: 1st)
CHALLENGE 2 (Date_Started: 3rd)
CHALLENGE 3 (Deadline: 18th)
controller
#challenges = current_user.challenges
#challenges_timeline = #challenges.group_by { |t| t.deadline.beginning_of_year + t.date_started.beginning_of_year }
view
<% #challenges_timeline.sort.each do |year, challenges| %>
<%= year.strftime('%Y') %>
<% challenges.group_by { |t| t.deadline.beginning_of_month + t.date_started.beginning_of_month }.sort.each do |month, challenges| %>
<%= month.strftime('%B') %>
<% for challenge in challenges %>
<% if challenge.deadline.present? %>
<%= challenge.deadline %>: <%= challenge.action %>
<% end %>
<% if challenge.date_started.present? %>
<%= challenge.date_started %>: <%= challenge.action %>
<% end %>
<% end %>
<% end %>
<% end %>
Change your schema:
If the two dates negate each other, perhaps you should have just one date field, and another field to show whether the date is the beginning or the end. That would allow you to group on the database level, saving some processing time.
Add instance method
A simple instance method such as
def timeline_date
deadline || date_started
end
would allow simple grouping such as
challenges.group_by { |c| c.timeline_date.beginning_of_month }
Use || directly in the group_by block.

Rails keeps ignoring an IF statement inside the view

I am writing an application where I want to list blog posts according to date they were created on the main index page. So lets say if I have 10 blog posts that were created today I would want to have smth like this come on the index page of the app..
Today:
POST 1
POST 2
POST 3
... so on
However, I have this...
Today:
POST 1
Today:
POST 2
Today:
POST 3
... so on
The following is my code..
index.html.erb
<% #posts.reverse.each do |post| %>
<% flag = true %>
<% if post.date.day < Time.now.day &&
post.date.day >= (Time.now.day - 1.day) %>
<!-- This IF statement gets ignored -->
<% if flag == true %>
<%= "Today" %>
<% flag = false %>
<br />
<% end %>
<tr>
<td><p>IMAGINE THIS SENTENCE IS ONE POST</p></td>
</tr>
<% end %>
<% end %>
The if statement that gets ignored inside the code acts as a flag, so that it should execute only once per group of posts with the same date. But, Rails seems to completely ignore it for some reason. If someone could help me correct this issue or nudge me in the right direction, your help would be greatly appreciated.
It's not that the if is being ignored, but that on each post the flag is being set to true again.
The first time you set the flag has to be outside of the iteration, like this:
<% flag = true %>
<% #posts.reverse.each do |post| %>
<% if post.date.day < Time.now.day &&
post.date.day >= (Time.now.day - 1.day) %>
<% if flag == true %>
<%= "Today" %>
<% flag = false %>
<br />
<% end %>
<tr>
<td><p>IMAGINE THIS SENTENCE IS ONE POST</p></td>
</tr>
<% end %>
<% end %>

If two DateTime stamps are the same in Rails

I have created a simple appointment system, and I now need to display something inside a loop if there's two or more appointments with the same date and time. The appointments are displayed in order of time, so they're just appearing one after the other.
Controller
def index
#todays_apps = current_user.appointments.order(time ASC)
end
View
<% #todays_apps.each do |app| %>
<%= app.business_name %>
<%= app.business_address %>
<%= app.time %>
<% end %>
I'm looking to display a message or icon the appointment shares a date and time with another appointment. Tried a collection of things with no luck.
You can group your collection by time and modify your iteration accordingly. You can group it like
#todays_apps.group_by(&:time)
The outcome will be something like
=> { timestamp1 => [app1,app2], timestamp2 => [app3], timestamp3 => [app4]}
Or you can try a quick hacky way like:
<% previous_time = nil %>
<% #todays_apps.each do |app| %>
<%= app.business_name %>
<%= app.business_address %>
<%= 'Your message or class or anything' if previous_time == app.time %>
<%= previous_time = app.time %>
<% end %>
Try Like this:
controller:
def index
#appointments = current_user.appointments.order("time ASC")
#todays_apps = #appointments.group_by(&:time)
end
View:
<% #todays_apps.each do |time, appointments| %>
<%= time %>
<% appointments.each do |appointment| %>
<%= appointment.business_name %>
<%= appointment.business_address %>
<% end %>
<% end %>
It will list all the appointments for particular time.
Thanks

listing data dependant on associated database

Wasn't sure how to title this question but here it goes.
I have two tables, Hotdesks and Bookings. Users can book a hotdesk.
In my controller I have an array #eastdesks containing all desks with a location of "East".
What I would like to do is to check which of these desks have been booked and display a list of available desks. This is my controller:
#eastdesk = Hotdesk.order("code ASC").where(:location => "East")
#booking = Booking.all
And this is my code so far:
<% #eastdesk.each do |ed| %>
<% #booking.each do |b| %>
<% if b.date == Date.today && b.type == "Hot Desk" %>
<% if b.desk == ed.code %>
<%= ed.code %> (Booked)
<% end %>
<% end %>
<% end %>
<% end %>
This ONLY shows those that have been booked.
I appreciate it is very ugly because I have been playing around. It is quite confusing but the t tables are associated vie the 'desk' column in bookings and the 'code' column in eastdesk. (i.e. booking with desk 553 will link to the desk with code 553) Hope that makes sense. Thanks for the help
EDIT ----
At the moment there are 2 desks in #eastdesk array (E90 and E23). The E90 desk has been booked for today and the E23 desk has not been booked at all. For the code above I am getting the following output:
E90 (Booked)
What I want to show is the desks that have NOT been booked. My Hotdesk model consists of:
has_many :bookings
I was thinking of adding the following to my bookings model but I am not sure as there ar different types of bookings and not all of them belong to a hot desk.
belongs_to :hotdesks
You can create an array holding 'Desk Codes' from booking table and use 'include?' method to check if this array contains the 'desk' value of #eastdeck.
Possible Solution:
#booking.map { |b| #booking_codes.push b.desk } // Creating a temporary array holding 'desk' from #booking
View:
<% #eastdesk.each do |ed| %>
<% #booking.each do |b| %>
<% if b.date == Date.today && b.type == "Hot Desk" && #booking_codes.include? b.desk %>
<%= ed.code %> (Booked)
<% end %>
<% end %>
<% end %>
PS: This may not be the nicest solution, but it may help :)

Rails: two attributes diffrent tables in view

I have been trying trying to use two attributes from two different models so they can be used with the calendar_for method provided by the table_builder plugin in the index view. I have been through the Rails guides for http://guides.rubyonrails.org/active_record_querying.html#retrieving-multiple-objects and posts such as Ruby on Rails: How to join two tables however i must be doing something wrong.
My models are as follows:
Class Event < ActiveRecord::Base
belongs_to :user
and
class User < ActiveRecord::Base
User has_many :events
The different ways I have tried in the controller (not all at once) are:
#event.user.name
#users = User.joins(:event).where(:event => {:event_date => true})
#users = User.where(:event => :event_date)
Amoung others, my view looks like:
my view code:
<% calendar_for #users, :year => #date.year, :month => #date.month do |calendar| %>
<%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %>
<% calendar.day(:day_method => :created_at) do |date, users| %>
<%= date.day %>
<ul>
<% for user in users %>
<li><%= link_to h(user.name), user %></li>
<% end %>
</ul>
<% end %>
<% end %>
</div>
I have tried changing the variables in the view accordingly however to no avail. I would like to show the users name and a link to the user on the specific day that their event is booked.
Two issues I see, though I'm not familiar with the calendar library.
First, make sure your query in the controller returns something useful. Of the three lines you gave us, the first doesn't even search, it calls methods on an undefined variable. The second is close to working, but you are searching for a date and matching it to true... How about:
#users = User.joins(:event).where('events.event_date is not null')
Furthermore, if you have a date range, you might include that in the search:
#users = User.joins(:event).where('events.event_date > ? and events.event_date < ?', start_date, end_date)
Next in the view, you aren't consistent with your variable naming. The controller sets up the #users variable, which you access once, but then later you are missing the # in front of it, which is not the same thing. I don't know what the calendar part wants as input, but at least the for loop should be:
for user in #users
That said, for loops are not very rubyish. The ruby way is to use each:
#users.each do |user|
...
end
or even better, to make all of your links:
<ul>
<%= #users.collect {|user| content_tag(:li, link_to h(user.name), user).join } %>
</ul>
edit
Based on more information, I think you are starting at the wrong place. Let's start with events.
Event.joins(:users).where('events.event_date is not null')
<% calendar_for #events, :year => #date.year, :month => #date.month do |calendar| %>
<%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %>
<% calendar.day(:day_method => :event_date) do |date, events| %>
<%= date.day %>
<ul>
<% for event in events %>
<li><%= link_to h(event.user.name), event.user %></li>
<% end %>
</ul>
<% end %>
<% end %>

Resources