Where should I put my cronjobs in rails? - ruby-on-rails

Is there a special place for cronjobs in rails? I couldn't find information on this subject, the only examples out there are to execute Model.some_method
I created a class Crawler, with a method run, and tried different places for it (including app/models), but I keep getting this error:
$ rails runner -e development 'Crawler.run'
/usr/local/lib/ruby/gems/1.8/gems/railties-3.2.1/lib/rails/commands/runner.rb:53: undefined method `run' for Crawler:Class (NoMethodError)
from /usr/local/lib/ruby/gems/1.8/gems/railties-3.2.1/lib/rails/commands.rb:64:in `eval'
from /usr/local/lib/ruby/gems/1.8/gems/railties-3.2.1/lib/rails/commands/runner.rb:53
from /usr/local/lib/ruby/gems/1.8/gems/railties-3.2.1/lib/rails/commands.rb:64:in `require'
from /usr/local/lib/ruby/gems/1.8/gems/railties-3.2.1/lib/rails/commands.rb:64
from script/rails:6:in `require'
from script/rails:6
class Crawler
def run
puts 'bla'
end
end
As you can see, it can't find the method 'run'.
If I put it inside /scripts I get this: uninitialized constant Crawler (NameError), so this is probably not what I want.
Any ideas?
note. I'm on rails 3

You're calling an instance method on class level.
Rewrite like this:
class Crawler
def self.run
puts 'bla'
end
end
To answer your initial question, there is no real dedicated place but I usually create a crons folder under app.
Regarding rake tasks, they are supposed to live in /lib but it's really up to you.

I encourage you to use the whenever gem. It allows you to set up tasks in a schedule.rb file as follows:
every 3.hours do
runner "MyModel.some_process"
rake "my:rake:task"
command "/usr/bin/my_great_command"
end

Related

rails aborted! Don't know how to build task 'task_name'

I was creating a quick one-off task:
require 'yaml'
task generate_permissions_yaml: :environment do
permissions = []
Permission.order(:title).each do |permission|
permissions << {
title: permission.title,
code: permission.code,
description: permission.description
}
end
puts permissions.to_yaml
end
When I ran it with rails generate_permissions_yaml, I got this error:
rails aborted!
Don't know how to build task 'generate_permissions_yaml' (see --tasks)
/bundle/gems/railties-5.0.2/lib/rails/commands/rake_proxy.rb:14:in `block in run_rake_task'
/bundle/gems/railties-5.0.2/lib/rails/commands/rake_proxy.rb:11:in `run_rake_task'
/bundle/gems/railties-5.0.2/lib/rails/commands/commands_tasks.rb:51:in `run_command!'
/bundle/gems/railties-5.0.2/lib/rails/commands.rb:18:in `<top (required)>'
As it turns out, it was due to the way that I named the file. It should have a .rake extension, not .rb.
I renamed this:
lib/tasks/generate_permissions_yaml.rb
To this:
lib/tasks/generate_permissions_yaml.rake
I uncovered this after trying to run rails g task generate_permissions_yaml and seeing that it generated a file with a .rake extension.
For newbies - this is a common error if you use a space instead of a colon in a rake command. For example if you enter:
rake db migrate
instead of:
rake db:migrate
The correct format has a colon, not a space

whenever gem schedule.rb file: doesn't recognize RAILS_ROOT variable

In schedule.rb file, the statement:
require "#{RAILS_ROOT}/config/environment.rb"
every "10 10 2 * * *" do
command "mysqldump -u #{#db_username} -p#{#db_password} --single-transaction #{#db_name} > #{#backup_Path}/#{#db_name}.sql 2> log/error_crontab.log"
end
When i try to execute the whenever cmd from terminal, getting the following error:
config/schedule.rb:48:in `initialize': uninitialized constant Whenever::JobList::RAILS_ROOT (NameError)
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/lib/whenever/job_list.rb:19:in `instance_eval'
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/lib/whenever/job_list.rb:19:in `initialize'
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/lib/whenever.rb:16:in `new'
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/lib/whenever.rb:16:in `cron'
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/lib/whenever/command_line.rb:40:in `run'
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/lib/whenever/command_line.rb:7:in `execute'
from /usr/local/lib/ruby/gems/1.9.1/gems/whenever-0.7.0/bin/whenever:38:in `<top (required)>'
from /usr/local/bin/whenever:19:in `load'
from /usr/local/bin/whenever:19:in `<main>'
i am using the require statement to get the dynamic values from the form to schedule the job. Please help to solve this issue?
Note: i have seen the following stackoverflow queries:
How to detect Rails environment inside whenever
Following this thread to get dynamic values, but facing problem with require statement.
Rails - Whenever gem - Dynamic values
Ruby/Rails - Whenever gem - Loop cron tasks
config file in schedule.rb with Rails Whenever gem?
Whenever doesn't require or depend on Rails at all, so when it runs, RAILS_ROOT is not defined, however because whenever's schedule.rb is generally kept in /config/schedule.rb, we can make an assumption that it is in a rails project, and set our own RAILS_ROOT like this:
# in schedule.rb
RAILS_ROOT = File.dirname(__FILE__) + '/..'
Edit: in the case that you actually need Rails loaded, do this:
# in schedule.rb
# this will require config/environment and load your entire rails environment
require File.expand_path(File.dirname(__FILE__) + "/environment")
The whenever developer already answered this question, check this out https://github.com/javan/whenever/issues/81
Javan Whenever no longer attempts to load your Rails environment. However, it does automatically set a path variable to the directory whenever was executed from. This should work just the same:
set :output, "#{path}/log/cron.log"
In Rails 4 try with:
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
in your schedule.rb file.
This way you also have access to all your active-record models and initializers.

How to detect Rails environment inside whenever

This question will probably only make sense if you know about the whenever gem for creating cron jobs.
For my app, I want to use whenever in all the environments, including testing and development.
My schedule.rb looks like this:
set :output, {
:error => "#{path}/log/error.log",
:standard => "#{path}/log/cron.log"
}
set :environment, Rails.env.to_sym
every 5.minutes do
rake 'db:activity:synchronize'
end
but it fails on Rails.env.to_sym (and the same stands for RAILS_ENV):
/home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever/job_list.rb:21:in `eval': uninitialized constant Whenever::JobList::Rails (NameError)
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever/job_list.rb:21:in `eval'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever/job_list.rb:21:in `initialize'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever.rb:15:in `new'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever.rb:15:in `cron'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever/command_line.rb:41:in `run'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/lib/whenever/command_line.rb:8:in `execute'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/gems/whenever-0.6.8/bin/whenever:38:in `<top (required)>'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/bin/whenever:19:in `load'
from /home/marius/.rvm/gems/ruby-1.9.2-p290#uxolo/bin/whenever:19:in `<main>'
So, my question basically boils down to:
How do I access the current environment, or
What should I do to use whenever in all the environments?
At least in newer version of whenever it is possible to access the environment with #environment. For example if you want whenever to only generate cron entries for some jobs in production:
case #environment
when 'production'
every 1.day, :at => '0:00 am' do
rake "some:task"
end
end
The error message suggests that Rails isn't defined. i.e the framework isn't loaded when you're asking the question what environment is rails running with.
In fact from looking at the code for Whenever it looks like rails isn't a requirement for it (i.e. You can install and run Whenever without rails even being installed on your system). Hence there's no way for Whenever to look at your rails environment (as far as i can tell)
As recommended by the gem author, the solution is to pass in the current environment as a variable:
$ whenever --set environment=test
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /bin/bash -l -c 'cd /home/marius/uxolo && RAILS_ENV=test rake db:activity:synchronize --silent >> /home/marius/uxolo/log/cron.log 2>> /home/marius/uxolo/log/error.log'
$ whenever --set environment=development
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /bin/bash -l -c 'cd /home/marius/uxolo && RAILS_ENV=development rake db:activity:synchronize --silent >> /home/marius/uxolo/log/cron.log 2>> /home/marius/uxolo/log/error.log'
And Chris Bailey is right: Whenever itself doesn't load the Rails environment.
A variation of the first answer to a similar question worked for me. Add
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
to the top of schedule.rb and you'll be able to call Rails.env to access the current Rails environment.
Note: the above path would be different if your environment.rb file isn't in /app/config
I took the implementation of Rails.env I found here (by clicking on "source"), and used it to initialize the ::Rails module at the beginning of the config/schedule.rb
eval %Q(module ::Rails
def self.env
'#{#environment}' || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
end
end
)
This creates the Rails module, and makes its environment return what you supplied as --set environment=... in the whenever command line, as the script author suggests.
However, whenever sets the #environment to production by default, so this large "or" may be not quite useful.
Now the Rails.env call in the Whenever script would work. What was more important in my case, it also worked in other scripts I included into schedule.rb, such as the one that loaded application.yml.
P.S. The eval call is used to access #environment available in the scope of the schedule.rb script from inside the definition of a module.

Rails 3 rake task can't find model in production

My simple rake task, stored in lib/tasks/items_spider.rake runs just fine in development. All it does is call spider! on the Item model.
namespace :items do
desc "Spider the web for data, hoorah"
task :spider => :environment do
Item.spider!
end
end
I have the :environment task as a dependency, so everything works just fine. However, when I add RAILS_ENV=production, I hit errors, both on my local server and the production server:
$ rake items:spider RAILS_ENV=production --trace
(in /home/matchu/Websites/my-rails-app)
** Invoke items:spider (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute items:spider
rake aborted!
uninitialized constant Object::Item
/home/matchu/.rvm/gems/ruby-1.9.2-preview3#rails3/gems/rake-0.8.7/lib/rake.rb:2503:in `const_missing'
/home/matchu/.rvm/gems/ruby-1.9.2-preview3#rails3/gems/rspec-core-2.0.0.beta.22/lib/rspec/core/backward_compatibility.rb:20:in `const_missing'
/home/matchu/.rvm/gems/ruby-1.9.2-preview3#rails3/gems/rspec-expectations-2.0.0.beta.22/lib/rspec/expectations/backward_compatibility.rb:6:in `const_missing'
/home/matchu/Websites/openneo-impress-items/lib/tasks/items_spider.rake:4:in `block (2 levels) in <top (required)>'
/home/matchu/.rvm/gems/ruby-1.9.2-preview3#rails3/gems/rake-0.8.7/lib/rake.rb:636:in `call'
[...trace of how rake gets to my task...]
This just seems odd to me. Apparently the models have not been loaded correctly. I'm on Rails 3.0.3, though development on this app started back when Rails 3 was in beta. How can I go about debugging this issue? Thanks!
Contrary to running your application in production, a Rake task does not eager load your entire code base. You can see it in the source:
module Rails
class Application
module Finisher
# ...
initializer :eager_load! do
if config.cache_classes && !$rails_rake_task
ActiveSupport.run_load_hooks(:before_eager_load, self)
eager_load!
end
end
# ...
end
end
end
So only if $rails_rake_task is false, will the application be eager-loaded in production. And $rails_rake_task is set to true in the :environment Rake task.
The easiest workaround is to simply require the model that you need. However, if you really need all of your application to be loaded in the Rake task, it is quite simple to load it:
Rails.application.eager_load!
The reason all of this work in development is because Rails autoloads your models in development mode. This also works from within a Rake task.
In your environment/production.rb, you should add the following:
config.dependency_loading = true if $rails_rake_task
It solved the problem for me.
(Note: this should be added AFTER the config.threadsafe! call)
The same thing happens to me in Rails 6.0.3.2.
I was trying to configure the rake file task :foo do ... end. Instead it should've been task foo: :environment do ... end.
Just found another one: I was developing on windows, deploying to Heroku. The web app and rails console worked fine, but rake tasks and even direct require could not load the model. Turned out I had absentmindedly created the model file as Model.rb instead of model.rb - system dependent case sensitivity.

DelayedJob with acts_as_ferret in production mode

I get the following error when I run my task via DelayedJob:
closed stream
/usr/lib/ruby/1.8/drb/drb.rb:961:in `select'
/usr/lib/ruby/1.8/drb/drb.rb:961:in `alive?'
/usr/lib/ruby/1.8/drb/drb.rb:1211:in `alive?'
/usr/lib/ruby/1.8/drb/drb.rb:1168:in `open'
/usr/lib/ruby/1.8/drb/drb.rb:1166:in `each'
/usr/lib/ruby/1.8/drb/drb.rb:1166:in `open'
/usr/lib/ruby/1.8/drb/drb.rb:1163:in `synchronize'
/usr/lib/ruby/1.8/drb/drb.rb:1163:in `open'
/usr/lib/ruby/1.8/drb/drb.rb:1092:in `method_missing'
/usr/lib/ruby/1.8/drb/drb.rb:1110:in `with_friend'
/usr/lib/ruby/1.8/drb/drb.rb:1091:in `method_missing'
/usr/lib/ruby/gems/1.8/gems/acts_as_ferret-0.4.3/lib/remote_index.rb:31:in `<<'
/usr/lib/ruby/gems/1.8/gems/acts_as_ferret-0.4.3/lib/instance_methods.rb:90:in `ferret_update'
...
From the error its obvious that delayed_job could not find the ferret_server. However, when I run this SAME task from console in production mode, it works fine. Any ideas how I can ensure that delayed_job has:
really loaded the production environment. I set RAILS['ENV]] = 'production' in the script/delayed_job to ensure this. (I guess this should be good)
got ferret configured via models?
This happened to me too. You need to disable ferret from indexing during the running of the delayed job.
In your worker definition (something like RAILS_ROOT/lib/worker.rb), and given a model called Post, you should include a line like this:
class Worker < Struct.new(:stuff)
def perform
Post.disable_ferret
<do some stuff>
.
.
.
end
end

Resources