1

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?

Eyeslandic
  • 14,553
  • 13
  • 41
  • 54
Alvaro Alday
  • 343
  • 3
  • 19
  • Since it is a scheduled job what you describe, I would use the whenever gem or add cron jobs yourself. The cronjob can run the rake task for you. – Hackman Jun 18 '19 at 21:52
  • So is it even worth it to have it as a rake task? should I just make a class to handle that? – Alvaro Alday Jun 18 '19 at 21:58
  • I always recommend [Sidekiq](https://sidekiq.org) for asynchronous job processing but depending on your needs this may be overkill. – anothermh Jun 18 '19 at 22:09
  • 1
    What do you mean by **get out of hand**? I'm using **sidekiq** & **whenever**, and I think this couple is okay – quyetdc Jun 19 '19 at 03:22
  • `whenever` supports a way to run different schedule files manually by command `whenever --load-file config/my_schedule.rb` and then `whenever --update-crontab`. So, you can organize your scheduler – quyetdc Jun 19 '19 at 03:29
  • @quyetdc That sounds very interesting I'm worried that the schedule.rb file might get bloated with logic, but I could use what you say to prevent that from happening. Could you elaborate on how to organize your schedules when using whenever? – Alvaro Alday Jun 19 '19 at 03:33
  • I think it depends on your application. we can think about the responsibility of the schedule file, like: conversation scheduler, statistic scheduler, marketing scheduler, etc. – quyetdc Jun 19 '19 at 03:41
  • Right now I have a single report aimed to be sent to an admin managing the tenant's users, what they're worried about is knowing each user's (and team of users), average response time and message count. so there are at least 3 responsibilities but I only need a single report. That and the overall design are my biggest headaches. – Alvaro Alday Jun 19 '19 at 03:56

1 Answers1

3

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.

spickermann
  • 100,941
  • 9
  • 101
  • 131