Rails: Ordering objects created in 3 days - ruby-on-rails

I want to display the hottest posts in 3 days, so this is what I do
#posts = Post.created_in_days(3).order_by_likes.first(4)
For queries:
scope :order_by_likes, -> { order('likes IS NULL, likes DESC') }
scope :created_in_days, ->(number) {where('created_at >= ?', Time.zone.now - number.days)}
This works fine. But the problem is, sometimes there is not enough posts generate in 3 days. Maybe just 3, or even no post at all.
How can I handle this so when #posts created in 3 days is not enough, it would extend the time, till it get enough #posts? (In this case, 4 post needed).

scope :created_in_days, ->(number) do
filtered = where('created_at >= ?', Time.zone.now - number.days)
if filtered.length < 4
order(:created_at => :desc).limit(4) # takes last 4 created
else
filtered
end
end

Related

Rails Date Query for setting Expiration

I'm creating a marketplace app where sellers can list items to sell. I want to set an expiry date so listings over 30 days old do not show on the site.
I found some similar examples online but can't get this query to work.
#listings = Listing.where('created_at <= ?', Date.created_at + 30.day)
You want to query items whose created_at time is >= the current date (Date.current) - 30 days (30.day). So the query should simply be:
#listings = Listing.where('created_at >= ?', Date.current - 30.day)
You can also replace Date.current with Time.now or DateTime.now.
UPDATE: as user3334690 mentioned in a comment, it's recommended that you make this a model method since it's something that should be in the Model layer:
# app/models/listing.rb
def self.not_expired
where('created_at >= ?', Date.current - 30.day)
end
# now in controllers you can do something like
#listings = Listing.not_expired

Counting User registration for a day, week, month

I want to count the users that registered on the website on specific periods.
For example:
Today: 5
Yesterday: 7
Over last week: 28
Over last month: 101
I used this stackoveflow question that is somewhat relevant to what I want to do. But when I try to apply it it has several problems in terms of logic for what I try to do.
So what I figured out is that I should use something like:
#lastfivedays = User.where(
'created_at >= :five_days_ago',
:five_days_ago => Time.now - 5.days,
)
But where am I placing this and how do I use it in the view?
Yes I am lost on how I do something like this in Rails as I am new to this. Any guidance, tip will be extremely helpful.
Your query should go in the controller and you can then access the instance variable in the view.
Controller:
#lastfivedays = User.where(
'created_at >= :five_days_ago',
:five_days_ago => 5.days.ago,
).count
View:
Number of users who registered in last 5 days: <%= #lastfivedays %>
I would recommend using ActiveRecord Scopes
You could have something like:
class User < ActiveRecord::Base
...
scope :joined_within_five_days, -> { where('created_at >= :five_days_ago',
:five_days_ago => Time.now - 5.days,) }
...
end
Your controller could then use the approriate one. For example
#users = User.joined_within_five_days

Ambiguous table reference

This problem seems fairly simple, but I've never encountered one like this.
Here are the settings:
Post has_many :reader_links
Post has_many :readers, :through => :reader_links
I need to find out if there are readers reading a post.
#post.reader_links.where('created_at >= ?', 45.minutes.ago).any?
Works great.
#post.readers.where('created_at >= ?', 45.minutes.ago),any?
throws an ambiguous table column error because it's confused whether the created_at column means that of reader object or reader_link object. This happens because the class of a reader is actually User. How do I query readers who were created by reader_links 45 minutes ago?
I'm looking for something like..
#post.readers.where('reader_link.created_at >= ?', 45.minutes.ago)
If I get it right, you just need to specify which created_at column you're talking about:
#post.readers.where('reader_links.created_at >= ?', 45.minutes.ago).any?
You coul merge the scopes to get rid of ambigious errors, so each scope has it's own visibility range.
using meta_where:
Post.scoped & (ReaderLink.scoped & User.where(:created_at.gt => 45.minutes.ago))
without meta_where:
Post.scoped.merge(ReaderLink.scoped.merge(User.where('created_at >= ?', 45.minutes.ago))
This will result in arrays of Post objects containing the reader_links and readers data for all readers younger than 45 minutes. Please try it in the rails console.
Edit: for a single post
post_with_fresh_users = Post.where('id = ?', some_id).merge(ReaderLink.scoped.merge(User.where('created_at >= ?', 45.minutes.ago))
Edit: all fresh readers of a post (different order)
fresh_readers_for_post = User.where('created_at >= ?', 45.minutes.ago).merge(ReaderLink.scoped.merge(Post.where('id = ?', #post.id))
How it works:
http://benhoskin.gs/2012/07/04/arel-merge-a-hidden-gem

How to count "days with records" in Rails?

Rails adds and populates a created_at column for new records.
How can I use the to count the number of days that have records within a specified timeframe? (note: counting days, not counting records).
For example, say I have a Post model, how can I calculate how many days in the last year have a Post?
Since you asked for the ruby way, here it is:
Post.where('created_at >= ?', 1.year.ago).map { |p| p.created_at.beginning_of_day }.uniq.size
Update
You can put the following in your Post model
def self.number_of_days
where('created_at >= ?', 1.year.ago).map { |p| p.created_at.beginning_of_day }.uniq.size
end
Then in your controller you can do stuff like
#user.posts.number_of_days
Here's a more efficient way that delegates most of the work to the database (MySQL, not sure if it'll work on others):
Post.where('created_at >= ?', 1.year.ago).group('DATE(created_at)').length

Get number of rows associated model has with complicated join

I'm new to rails and am unsure of the best way of doing this. I have a User which "has many" Events. I want to be able to go user.active? in a view which will display whether the user has one or more associated Events created in the last 5 minutes.
I know the syntax isn't correct but this is what I've got so far:
def active?
find(:conditions => {:events => ["created_at >= ?", DateTime.now - 5.minute]}).count > 0
end
You can just do a find on the events collection:
def active?
events.where('created_at >= ?', 5.minutes.ago).count > 0
end

Resources