2

I have defined a rake task to work on the 15th and last day of the month at 8 am in schedule.rb file. I just wanted to confirm if I have done it the right way. Please have a look and suggest it.

run this task at 8am on 15th of every month

every '0 8 15 * *' do
  rake 'office:reminder', environment: ENV['RAILS_ENV']
end

run this task at 8am on the last day of every month

every '0 8 28-31 * *' do
  rake 'office:reminder', environment: ENV['RAILS_ENV']
end
Stefan
  • 109,145
  • 14
  • 143
  • 218
user12763413
  • 1,177
  • 3
  • 18
  • 53
  • 3
    `0 8 28-31 * *` will run the job at 8am on 28th, 29th, 30th and 31st. – Stefan Jan 27 '20 at 15:45
  • @Stefan how can I make it run for the last day of the month? – user12763413 Jan 27 '20 at 15:51
  • 1
    AFAIK you can't specify "last day of month" in cron. A possible workaround is to specify `0 8 * * *`, i.e. run the task every day at 8am and move the day handling into the rake task. To avoid hard-coding the values, you could pass them as arguments when invoking the task, i.e. `15` and `-1`. – Stefan Jan 27 '20 at 15:51
  • https://stackoverflow.com/a/30288148/12201472 – Marian13 Jan 27 '20 at 19:17
  • https://stackoverflow.com/a/6139310/12201472 – Marian13 Jan 27 '20 at 19:18
  • I hope, links above will help a little – Marian13 Jan 27 '20 at 19:19
  • I'm not up on cron and rake tasks, but it's ruby running the above code, right? So you could put `require 'active_support'` at the top and leverage those methods? – Beartech Jan 27 '20 at 19:38
  • There's tools like [Fugit](https://github.com/floraison/fugit) that can turn English descriptions of intervals into `cron` notation. See: `Fugit::Nat.parse`. – tadman Jan 29 '20 at 23:31

2 Answers2

0

Since cron has a very simple interface it's hard to convey a concept like "last day of month" to it without outside help. But you could shift your logic into the task:

every '0 8 28-31 * *' do
  rake 'office:end_of_month_reminder', environment: ENV['RAILS_ENV']
end

And in a new task called office:end_of_month_reminder:

if Date.today.day == Date.today.end_of_month.day
  #your task here
else
  puts "not the end of the month, skipping"
end

You would still have your first-of-the-month task. But if you wanted to roll it into one:

every '0 8 15,28-31 * *' do
  rake 'office:reminder', environment: ENV['RAILS_ENV']
end

And in your task:

if (Date.today.day == 15) || (Date.today.day == Date.today.end_of_month.day) 
  #your task here
else
  puts "not the first or last of the month, skipping"
end
Beartech
  • 6,173
  • 1
  • 18
  • 41
0

cron usually doesn't allow to specify "last day of the month". But in Ruby, you can simply use -1 to denote the month's last day:

Date.new(2020, 2, -1)
#=> Sat, 29 Feb 2020

So instead of having separate entries for specific days, you could define one entry running every day at 8am and pass the days as arguments to the rake task:

every '0 8 * * *' do
  rake 'office:reminder[15,-1]', environment: ENV['RAILS_ENV']
end

Then weithin your task, you can turn these arguments into date objects and check if any of them is equal to today's date:

namespace :office do
  task :reminder do |t, args|
    days = args.extras.map(&:to_i)
    today = Date.today
    if days.any? { |day| today == Date.new(today.year, today.month, day) }
      # send your reminder
    end
  end
end
Stefan
  • 109,145
  • 14
  • 143
  • 218