Automatically create instance of a model every day with rails - ruby-on-rails

I am writing an app with 2 models, User and Daily. Each user has_many dailies. I am trying to create a Daily report for each user every day at midnight. How do I do this in rails? In Backbone, I would just find the time between now and midnight, set a timeout for that amount of time, and then create a new model, and call that function recursively. I looked at Rails: Reset a model attribute at specific time each day and saw the suggestion to use whenever and cron, but I wasn't sure if there was a better way to accomplish what I am trying to do.
A temporary solution I came up with was to create a new Daily when a user visits the home page if the current user doesn't already have a Daily with today's date. This has the problem of only creating the report for a day if the user visits the homepage on that day. If the user navigates around the homepage, the report won't be created, and I think it's too intrusive a solution to put that check in every action. Is there a way in rails to automate this task?
Edit: Another idea I had was to put an after_create into my Daily class, and then use the same solution as my Backbone solution. Is that the optimal way to accomplish this?

You should check out the gem whenever.
It allows you to define tasks in a very readable DSL in a config/schedule.rb file like this: (examples taken from gem readme):
every 3.hours do
runner "MyModel.some_process"
rake "my:rake:task"
command "/usr/bin/my_great_command"
end
every 1.day, :at => '4:30 am' do
runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end
every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
runner "SomeModel.ladeeda"
end
every :sunday, :at => '12pm' do # Use any day of the week or :weekend, :weekday
runner "Task.do_something_great"
end
every '0 0 27-31 * *' do
command "echo 'you can use raw cron syntax too'"
end
# run this task only on servers with the :app role in Capistrano
# see Capistrano roles section below
every :day, :at => '12:20am', :roles => [:app] do
rake "app_server:task"
end

I think it's a background or asynchronous function, so you would better not place the logic in views or the client side. It's not reliable. The easiest way is cron however it's OS dependent. Cron also has other disadvantages. If you want a pure ruby solution, I suggest gem clockwork. See details: https://github.com/tomykaira/clockwork

Related

How do you run a rake task at midnight of every single time zone?

I want to perform some actions on a user when it's midnight in his or her region. Since I want to support time zones instead of basing everything on server time, I want to cover all the possible midnights of every time zone.
The idea I had so far is to create a rake task that is triggered every hour with cron, and the rake task tries to figure out what time zone just ticked over to midnight. This is a pretty unreliable approach, especially when a time zone enters DST.
Are there any other more reliable ways to accomplish this?
You can use the tzinfo gem to navigate through different timezones and it supports DST. The Whenever gem makes it easy to write your cron jobs in Ruby. I assume your User model has a timezone column which follows The Time Zone Database naming.
So, your schedule.rb would be something like this:
require "tzinfo"
def midnight_in_timezone(timezone)
# assumes your server runs in utc
TZInfo::Timezone.get(timezone.name).local_to_utc(Time.parse('00:00am')).strftime('%H:%M%p')
end
set :output, "/var/log/cron_log.log"
TZInfo::Timezone.all.each do |timezone|
every :day, :at => midnight_in_timezone(timezone) do
# pass in timezone so you can perform your task
# for users who match => User.where(timezone: timezone)
runner "SomeModel.run_your_midnight_task('#{timezone}')"
end
end

How to move scheduler.rake into a more reliable background job

Basically... I just want to exactly move what is in scheduler.rake into a worker because I am hoping it is faster and more reliable.
What is the quickest way to do this? I am on Rails 3
Nothing fancy. Just quick.
I would recommend using the whenever gem
in your Gemfile.
gem 'whenever', :require => false
This will create an initial config/schedule.rb file for you.
every 3.hours do
# call background job
end
every 1.day, :at => '4:30 am' do
# call background job
end
every :hour do
# call background job
end
every :sunday, :at => '12pm' do
# call background job
end
every '0 0 27-31 * *' do
# call background job
end
I hope that this helps.
Happy Hacking
If you are using Heroku Scheduler then you can configure what type of dyno starts for each job.
Note that jobs ran on Scheduler should complete before next scheduled task or they will be terminated.
For more advanced clock related work please see this (but it's not trivial): https://devcenter.heroku.com/articles/scheduled-jobs-custom-clock-processes

Rails - How to auto-transfer records from table to table at a specific time?

I am pre-storing records in a table A and I want to transfer these records from table A to table B automatically at a specific time, lets say on every evening at 08:00 PM.
Any ideas on how to solve this little problem?
You could create rake task to implement your job, and then schedule it with cron, default *nix time manager. Its syntax is difficult to remember, so I prefer to use Ruby wrapper around it, gem whenever.
You can use whenever gem to run cron jobs ...for example job that runs every 5 mins
in schedule.rb
every 5.minutes do
rake "transfer_data:send_data"
end
lib/tasks/send_data.rake
#!/usr/bin/env ruby
namespace :transfer_data do
desc "Rake task to transfer data
task :send_data => :environment do
## code to transfer data from one table to other table
end
end
Execute the task using bundle exec rake transfer_data:send_data

How to automatically change record in database?

I need to automatically change a record in my database on a specific date and time.
Example: When the date and time is 01/12/2012 17:15:00 I want some record to automatically be set to 1. Is there any solution to this?
(I am using Rails 3.2.6)
Update 30.11.12 - 13:06
The real problem is that I have a datetime record in my database, when the date and time in this record exceeds todays date and time - then I want another record to be set to 1. Is there any way I can make an if statement, or something, somewhere in my controller?
I can see a few possible options here. It depends on your assumptions/constraints.
Possible assumptions (ordered by increasing difficulty to implement):
1) the data does not need to be correct in the database, but the next time somebody takes a look at the data, we fix it accordingly
2) the data does not need to be correct in the database, but e.g. within a definable time-interval
3) the data needs to be correct in the database at all times
Let me go over these in more detail
The data will appear correct, next time somebody looks at the data
This is actually pretty easy. When retrieving the data to be shown, then check if certain conditions/times have past, and set flags accordingly.
For the user it is actually not relevant when the flags were set correctly, but everytime a user will look at the data, the flags will be set correctly.
The data will be correct inside a given time-interval
A simple case is to check every hour, every half hour, once per day and set or fix the states of items that need to be changed.
To accomplish this you would use a cron job or a gem like whenever
This is a very simple approach and will assure your data will be correct in an acceptable/given interval (if your problem has such an interval of course).
The data must be set at the correct time
Here I see two options, either for each item that needs to change state at a given time, schedule a
task using the same options as before: cron or the whenever gem. This would work, but I am not entirely sure if cron is the best solution for scheduling a lot of 1-off jobs.
Alternatively, a very clean solution is to use a gem like DelayedJob. This gem is mostly used to offload tasks to the background, e.g. for tasks that need a bit more time processin. But you can also use it to execute tasks in the near future, at a very specific time.
def change_flag_when_needed
# change the flag
end
# 5.minutes.from_now will be evaluated when change_flag_when_needed is called
handle_asynchronously :in_the_future, :run_at => Proc.new { 5.minutes.from_now }
Whenever you will call change_flag_when_needed, it will evaluate the given block, which now contains 5.minutes.from_now, but could as well calculate the actual time when it should be performed.
Hope this helps.
You could use whenever https://github.com/javan/whenever to set up a cron job for you
Whenever has an easy syntax in its schedule.rb file to let you run tasks "whenever" you like
From their readme:
every 3.hours do
runner "MyModel.some_process"
rake "my:rake:task"
command "/usr/bin/my_great_command"
end
every 1.day, :at => '4:30 am' do
runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end
every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
runner "SomeModel.ladeeda"
end
every :sunday, :at => '12pm' do # Use any day of the week or :weekend, :weekday
runner "Task.do_something_great"
end
every '0 0 27-31 * *' do
command "echo 'you can use raw cron syntax too'"
end
# run this task only on servers with the :app role in Capistrano
# see Capistrano roles section below
every :day, :at => '12:20am', :roles => [:app] do
rake "app_server:task"
end
use cron job on linux server and set time for run script

Rails - Whenever gem - Dynamic values

Lets say I have a cronjob like this:
every 1.day, :at => '4:30 am' do
runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end
Although, I would like to make '1.day' more dynamic, such as changing the value via a form in an administration page.
Then it could look like this:
every Constant.find(2).value, :at => '4:30 am' do
or
#const = Constant.find(2)
every #const.span, :at => #const.time do
Can anyone come up with an idea on how to make this work?
Obviously, the reason for this would be that I could use the values stored in the database on my site, like an message saying
<%= "The next update is in less than #{#const.time}" #or something similar %>
Thank you idlefingers! It totally worked. Here's my solution:
require "#{RAILS_ROOT}/config/environment.rb"
#notification_daily = Constant.find_by_key("notification_daily_time_span")
every eval(#notification_daily.value), :at => #notification_daily.additional_data do
runner "Notification.daily"
end
#notification_weekly = Constant.find_by_key("notification_weekly_time_span")
every eval(#notification_weekly.value), :at => #notification_weekly.additional_data do
runner "Notification.weekly"
end
.value can contain for example: 1.day or :sunday
.additional_data contains the timestamp, example: 11:00am
And yes, I'm aware that I need to run --update crontab again :)
But I'll let a cronjob update itself, hehe.
I've never tried, but you should be able to do this by loading up the Rails environment in whenever so that you can use your model classes. Just use require File.dirname(__FILE__) + "./environment" (or "./application" for Rails 3) in your schedule.rb, assuming your schedule.rb is in the config dir.
However, since all whenever does is generate lines in the crontab, any changes made to any Constant would require running whenever --update-crontab again.
# In rails 4
require File.expand_path('../..//config/environment.rb', __FILE__)
# This is your table by which you will get your new value
bid_update = DynamicOfferTime.first
# Now Task As
every (bid_update.hour_value).hours do
puts "This will repeat in every #{bid_update.hour_value} hour"
end
Do not forget to update whenever
I had the same task and decided to solve it a bit another approach.
In my table have a lot of records where storing settings for schedule
(daily, monthly, weekly, time)
In schedule.rb file I added a cron job, which was running every day at 00:00 and select the records, which can be run today. And after it added to queue with sidekiq using perform_at.

Resources