Search code examples
ruby-on-railscron

Rails Query a List for a CRON Job


I'm a complete novice with CRON jobs but I think I have that set up correctly.

Ultimately what I'm trying to do is send an email every day at 8:00 am to users (and a couple others) that have not logged in within the last 3 days, have not received the email, AND are marked as active OR temp as a status.

So from querying the db in console I know that I can do:

  • first = User.where(status: 'active').or(User.where(status: 'temp'))
  • second = first.where("last_login_at < ? ", Time.now-3.days)
  • third = second.where(notified: false)

That's not certainly clean but I was struggling with finding a contained query that grabbed all that data. Is there a cleaner way to do this query?

I believe I have my cron job set up correctly using a runner. I have whenever installed and in my schedule.rb I have:

every 1.day, at: '8:00 am' do
 runner 'ReminderMailer.agent_mailer.deliver'
end

So under app > mailer I created ReminderMailer

class ReminderMailer < ApplicationMailer
 helper ReminderHelper

 def agent_reminder(user)
  @user = user
  mail(to: email_recipients(user), subject: 'This is your reminder')
 end

 def email_recipients(agent)
  email_address = ''
  email_addresses += agent.notification_emails + ',' if agent.notification_emails
  email_addresses += agent.manager
  email_address += agent.email
 end
end

Where I'm actually struggling is where I should put my queries to send to the mailer, which is why I built a ReminderHelper.

module ReminderHelper

 def applicable_agents(user)
  agent = []
  first = User.where(status: 'active').or(User.where(status: 'temp'))
  second = first.where("last_login_at < ? ", Time.now-3.days)
  third = second.where(notified: false)
  agent << third
  return agent
 end
end

EDIT: So I know I could in theory do a chain of where queries. There's gotta be a better way right?

So what I need help on is: do I have the right structure in place? Is there a cleaner way to query this data in ActiveRecord for the CRON job? Is there a way to test this?


Solution

  • Try combining them together as if understand the conditions correct

    • Have not logged in within the last 3 days,
    • Have not received the email
    • Are marked as active OR temp as a status
    User.where("last_login_at < ? ", 3.days.ago).
         where(notified: false).
         where(status: ['active', temp])
    
    
    module ReminderHelper
     def applicable_agents(user)
       User.where("last_login_at < ? ", 3.days.ago).
         where(notified: false).
         where(status: ['active', temp])
     end
    end
    

    You don't need to add/ assign them to array. Because this relation is already like an array. You can use .to_a if you need array. If you just want to iterate over them then users.each should work fine.

    Update

    class User
    
        scope :not_notified, -> { where(notified: false) }
        scope :active_or_temp, -> { where(status: ['active', 'temmp']) }
        scope :last_login_in, -> (default_days = 3) { where("last_login_at < ?", default_days.days.ago) }
    
    end
    

    and then use

        User.not_notified.active_or_temp.last_login_in(3)
    

    Instead of Time.now-3.days it's better to use 3.days.ago because it keeps time zone also in consideration and avoids unnecessary troubles and failing test cases.

    Additionally you can create small small scopes and combine them. More read on scopes https://guides.rubyonrails.org/active_record_querying.html