I'm trying to design a reporting system that notifies administrators about user's messaging rate and response time for a customer service app.
I have a tenant class that looks like this:
class Tenant < ApplicationRecord
has_many :users
has_many :chat_messages
end
And a user class that looks like this:
class User < ApplicationRecord
belongs_to :organization
has_many :authored_conversations, class_name: 'Conversation', :as => :author
has_many :chat_messages, as: :user, dependent: :nullify
has_many :received_conversations, :as => :receiver, class_name: 'Conversation'
def conversations
authored_conversations + received_conversations
end
def response_time
# calculate the user's average response time
end
end
Right now we have to manually run a rake task to take care of business. But automating the process would be so much better.
So designed a ReportGenerator class like this:
class ReportGenerator
def initialize(org_id)
@organization = Organization.find org_id
end
def generate_report
report = Report.generate(@organization)
ReportMailer.new_report(report).deliver_later
end
end
I also set my mailer like so:
class ReportMailer < ApplicationMailer
default from: ENV["DEFAULT_MAILER_FROM"],
template_path: 'mailers/chat_message_mailer'
def new_message(report, recipient)
@report = report
@recipient = recipient
@subject = "Monthly report for #{report.created_at}"
@greeting = "Hi, #{recipient.name}"
@body = @report.body
mail(to: @recipient.email, subject: @subject)
end
end
However, I'm struggling to set up the schedule I found this example But I believe doing it that way can get out of hand very quickly. I also want to know, What is the best approach? Executing a background job or a rake task?
I would argue that you need to solve two things: a way to run the required code on a regular base and you need to find a place where to put the code.
CRON has been the default for starting and running tasks on a regular base for a long time. And the whenever
gem is a well-known and simple solution to manage CRON when deploying the application on common environments. Unless you are on an environment that doesn't support CRON or favors a different solution (Heroku, for example, prefers Scheduler) I would simply go with CRON and whenever.
Regarding where to place the code I see no need for a background processing tool like sidekiq
because running your code via CRON is already some kind of processing in the background. Furthermore, I see no benefit in implementing this in a rake task. Rake tasks are harder to test and you will need to run application code anyway to query the database and send the emails.
I would just use rails runner
to call a single method that creates and sends all emails. Perhaps something like this
rails runner "ReportGenerator.create_for_all_organisations"
with your ReportGenerator
changed like this:
class ReportGenerator
def self.create_for_all_organisations
Organization.find_each { |organization| new(organization).generate_report }
end
def initialize(organization)
@organization = organization
end
def generate_report
report = Report.generate(@organization)
ReportMailer.new_report(report).deliver_later
end
end
That avoids depending on another gem like sidekiq
and allows to have the code in your application (not as an external rake task). Having the code in your application makes testing and maintaining the code much easier.