Find and display nearest date in RoR - ruby-on-rails

I am new to ruby on rails and I'm not sure where to start with this. I have a model for users, and one for projects. Users have many projects, and projects have one user. There is an end_date column in the projects table (as well as a name column).
What I want to do is find the project with the nearest end_date and display it's name and end date on the user's show page.
I tried putting this code in the projects controller, but I do not know if it is working, because I don't know how to access it and display the project name in the view.
def next_deadline(after = DateTime.now, limit = 1)
find(:all, :conditions => ['end_date > ?', after], :limit => limit)
end
Any help would be appreciated. Let me know if more information is needed.

As #Dan mentioned, you do need the :order clause to get the first one, but you should add it to your query and not replace the :conditions (otherwise you'll get the project with the earliest end_date irrespective of your after argument). The way you're defining this method is a bit off though. It should be defined in your Project model (and definitely not the controller) as a class method, or, what I think is a better approach, as a scope. In Rails < 3 (which it seems that you're using):
class Project < ActiveRecord::Base
named_scope :next_deadline, Proc.new { |after = DateTime.now, limit = 1| {:conditions => ['end_date > ?', after], :order => "end_date ASC", :limit => limit} }
...
end
Or in Rails >= 3:
class Project < ActiveRecord::Base
scope :next_deadline, Proc.new { |after = DateTime.now, limit = 1| where('end_date > ?', after).order("end_date ASC").limit(limit) }
...
end
Also, you can always test this kind of code using the Rails console: script/console in Rails < 3, rails c in Rails >= 3.

#projects = Project.find_by_sql("SELECT projects.* FROM projects
JOIN users ON users.id = projects.user_id AND projects.user_id = " + #user.id.to_s + "
WHERE projects.end_date > now()
ORDER BY projects.end_date ASC
LIMIT " + limit)
or
#projects = Project.where(:user_id => #user.id)
.where("end_date > ?", DateTime.now)
.order("end_date ASC")

You want to use :order, not :conditions.
Model.find(:all , :order => "end_date ASC")
Then the first result will be the item with the closest end_date

As Dan said, the condition you wrote won't get the nearest end date, but the dates that are greater than today, or the date passed in as a parameter.
In your User model you could write
def next_deadline_project
self.projects.first
end
as long as you give projects a default scope that orders records by end_date
In order to show information on the view you must set it in an instance variable in the User's controller show method. Instance variables are passed to views and you can access them to display the data.
#project = next_deadline_project
And in your show.html.erb you can use something like:
<%= #project.name %> - <%= #project.end_date %>

Related

Rails query with condition in count

I'm having a little trouble with a query in Rails.
Actually my problem is:
I want to select all users which do not have any user_plans AND his role.name is equals to default... OR has user_plans and all user_plans.expire_date are lower than today
user has_many roles
user has_many user_plans
users = User.where(gym_id: current_user.id).order(:id)
#users = []
for u in users
if u.roles.pluck(:name).include?('default')
add = true
for up in u.user_plans
if up.end_date > DateTime.now.to_date
add = false
end
end
if add
#users << u
end
end
end
This code up here, is doing exactly what I need, but with multiple queries.
I don't know how to build this in just one query.
I was wondering if it is possible to do something like
where COUNT(user_plans.expire_date < TODAY) == 0
User.joins(:user_plans, :roles).where("roles.name = 'default' OR user_plans.expire_date < ?", Date.today)
Should work, not tested, but should give you some idea you can play with (calling .distinct at the end may be necessary)
There is also where OR in Rails 5:
User.joins(:user_plans, :roles).where(roles: { name: 'default' }).or(
User.joins(:user_plans).where('user_plans.expire_date < ?', Date.today)
)
FYI: Calling .joins on User will only fetch those users who have at least one user_plan (in other words: will not fetch those who have no plans)

Rails 4 - How to get specific records based on where conditions

I have a Sale model with an :offer_end column with a date data type. I would like to display specific records of Sale where :offer_end >= Date.today. I tried to get this to work in the controller but im not sure what is the correct syntax to achieve this. This is what im currently doing which isnt working:
def index
#shops = Shop.all
#sales = Sale.where("offer_end >= Date.today", {offer_end: params[:offer_end]})
end
First of all you can't pass the Date.today as a string to the query, it will be passed to the database and it won't understand it.
The query should be something like this
#sale = Sale.where('offer_end > ?', Date.today)
The Date.today will be evaluated then passed as a value to the query.
You could replace the Date.today with any date object or date string, which in your case seems to be in theparams[:offer_end]
#sale = Sale.where('offer_end > ?', params[:offer_end])
You can use scope for these type of operations:
class Sale < ActiveRecord::Base
scope :get_products, ->(date){where(" sales.offer_end >= ? ", date)}
end
In controller you can use this scope as below:
#sales = Sale.get_products(params[:offer_end])
or you can use it directly in controller:
#sales = Sale.where("offer_end >= ?", Date.today)
and you can use params[:offer_end] instead of Date.today

Rails: How to use includes with conditions?

I have a ressource Room that has_many reservations. Likewise the ressource Reservation belongs_to a room.
My reservation model has a starts_at and an ends_at attribute.
In my index action for the Room controller I want to get all the Rooms and include all the reservations for a certain date(the date is given as a parameter).
I have tried this code, but it doesn't seem to work as intended.
#rooms = Room.includes(:reservations).where("reservations.starts_at" => params[:date])
I am aware that this code does not take into account that a room could be reserved for multiple days, but I had to start somewhere.
SUM UP
Based on a date the action should return all the rooms, but only include the reservations that is relevant for that date.
EDIT
This is what I ended up doing.
controllers/rooms_controller.rb
def index
#rooms = Room.includes(:reservations)
#date = params[:date] ||= Date.today
end
views/rooms/index.html.haml
- #rooms.each do |room|
- room.reservations.relevant(#date).each do |reservation|
= reservation.id
models/reservation.rb
def self.relevant(date = Date.today)
if date.blank?
date = Date.today
end
where(
'(starts_at BETWEEN ? AND ?) OR (ends_at BETWEEN ? AND ?)',
date.to_date.beginning_of_day, date.to_date.end_of_day,
date.to_date.beginning_of_day, date.to_date.end_of_day
)
end
It works alright, but the view is talking to the model I think?
If your where conditions refer to another table then you also need to need to specify references as well as includes. e.g.
#rooms = Room.includes(:reservations).
where("reservations.starts_at" => params[:date]).
references(:reservations)
See the API documentation here.

Active Record Query Using Associated Model in Find Clause

I'm having a blonde moment and probably a brain freeze.
In my rails3 app, I have users and tasks. My users have many tasks...
I have due and overdue tasks as follows:
#due = Task.find(:all, :conditions => ["dueddate >= ? AND AND status = ?", Date.today, false], :include => :taskcategories, :order => "dueddate asc")
What I want to do in my tasks view, is list the users with due tasks...
For some reason, I can't get my head around it. I have tried this, but it's not working:
#task = Task.all
#user = User.find(:all, :conditions => ["#task.dueddate <= ? AND
#task.status = ?", Date.today + 7.days, false])
I'm sure this is easy, can anyone help me!!?
I guess this should work
updated
User.joins(:tasks)
.where("tasks.dueddate <= ? AND tasks.status = ?", Date.today + 7.days, false).group(:id)
This should work with SQLite and MySQL. However, PostgreSQL requires that you supply all the columns of the table. If it's a small table, you could simply type the names. Or you could add this method to the model:
def self.column_list
self.column_names.collect { |c| "#{self.to_s.pluralize.downcase}.#{c}"}.join(",")
end
and change .group(:id) to .group(User.column_list)

How to add Previous / Next links in show view?

I'm trying to add Prevous/Next links in my show view. Here is the model:
Position
belongs_to :skill
Skill
has_many :positions, :order => 'salary desc, id desc'
position/show view:
<%= link_to("Previous", #position.previous) if #position.previous %>
<%= link_to("Next", #position.next) if #position.next %>
position.rb (new lines added for readibility)
def next
self.class
.where("skill_id = ? AND salary <= ? AND id < ?", skill_id, salary, id)
.order("salary desc, id desc").first
end
This doesn't do what I want. Records should be ordered first by salary, and than by id.
I think will_paginate won't help me because it's only for collections (won't work in the show view)
You should look at the paginate gem it does all the logic for you and creates your view links as well https://github.com/mislav/will_paginate
Ok, try looking at this gem: http://metautonomo.us/projects/metawhere/
It seems the issue is your SQL mojo isn't quite on target, but as I have absolutely no SQL mojo I don't know why your approach isn't working.
Metawhere lets you use very precise ruby code for generating complex SQL queries. From the project homepage it gives this example of complex order clauses:
Article.order(
:title.desc,
:comments => [:created_at.asc, :updated_at]
).joins(:comments)
So, I think if you were using metawhere you could simply add this to your where clause:
... :order => [:id.desc, :salary.desc] ...
or chain it on as a .order().
I hope that helps!
I found the solution :) It's rather long, but it works:
where("skill_id = ? AND salary = ? AND id < ?
OR
skill_id = ? AND salary < ?",
skill_id, salary, id, skill_id, salary
)
.order("salary desc, id desc").first

Resources