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.
Related
In my rails application, i want to run model function using whenever gem.
My schedule.rb file like,
set :environment, 'development'
set :output, "#{path}/log/cron.log"
set :job_template, "bash -l -c ':job'"
job_type :runner, "cd :path && bin/rails runner -e :environment ':task' :output"
every 2.minutes do
runner "Book.jobrun"
end
every 2.minutes do
command "echo 'rink4'"
end
And book.rb model like,
self.jobrun
Rails.logger.info "cronjob is running"
end
But when i run cronjob, in cron.log file showing,
bin/rails:3: undefined method `require_relative' for main:Object (NoMethodError)
How i need to set ruby path or rails path?
Try running which ruby from cron. Is it as you expect? If not, cron is likly defaulting to system Ruby which in your case is probably version 1.8.7 and doesn't have the require_relative function.
Check which ruby executble you're using with which ruby. And use this in the shebang at the top of your bin/rails. for instance
bin/rails
#!/Users/<username>/.rvm/rubies/ruby-2.2.4/bin/ruby.
My cron job works fine on my local machine after running whenever -w, after deploy to my VPS, I get this error, release 20150415044915 doesn't exist. any idea?
I looked at my crontab -e, the path also looks fine where 20150502114703 is the correct release:
0 1 * * 1 /bin/bash -l -c 'cd /home/hey_production/releases/20150502114703 && bin/rails runner ....
Error Log:
/usr/local/rvm/gems/ruby-2.1.3/gems/bundler-1.7.3/lib/bundler/definition.rb:22:in `build': /home/hey_production/releases/20150415044915/Gemfile not found (Bundler::GemfileNotFound)
from /usr/local/rvm/gems/ruby-2.1.3/gems/bundler-1.7.3/lib/bundler.rb:154:in `definition'
from /usr/local/rvm/gems/ruby-2.1.3/gems/bundler-1.7.3/lib/bundler.rb:117:in `setup'
from /usr/local/rvm/gems/ruby-2.1.3/gems/bundler-1.7.3/lib/bundler/setup.rb:17:in `<top (required)>'
from /usr/local/rvm/rubies/ruby-2.1.3/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:135:in `require'
from /usr/local/rvm/rubies/ruby-2.1.3/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:135:in `rescue in require'
from /usr/local/rvm/rubies/ruby-2.1.3/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:144:in `require'
from bin/rails:14:in `<main>'
Basically the environment variable is missing that tells the cron where to look for a Gemfile. so you need to add that variable in your environment at the time when cron tries to run this.
You can do that in Your schedule.rb:
env BUNDLE_GEMFILE, ENV["/home/hey_production/current/Gemfile"]
or directly inside crontab file with the command crontab -e(before the cron entries):
BUNDLE_GEMFILE="/home/hey_production/current/Gemfile"
Hope it helps.
EDIT
Forgot the symbol above in schedule.rb
The line in schedule.rb should be like this.
env :BUNDLE_GEMFILE, ENV["/#{path}/Gemfile"]
or
env :BUNDLE_GEMFILE, ENV["/home/hey_production/current/Gemfile"]
As a follow up on previous answer, set the env variable in whenever inside schedule.rb before the schedule blocks like:
def production?
#environment == 'production'
end
set :output, {:error => '/home/current/log/cron_error.log', :standard => '/home/current/log/cron.log'}
every 2.hour, roles: [:utility] do
runner "/home/current/lib/cron_jobs/launch_pending_emails.rb"
end
and inside your deploy.rb environment specific file ie: staging.rb set the env:
set :whenever_roles, [:utility]
set :whenever_environment, defer { stage }
set(:whenever_command) { "STAGE=#{stage} bundle exec whenever" }
require 'whenever/capistrano'
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.
I have managed to get a cron job to run a rake task by doing the following:
cd /home/myusername/approotlocation/ && /usr/bin/rake sendnewsletter RAILS_ENV=development
i have checked with which ruby and which rake to make sure the paths are correct (from bash)
the job looks like it wants to run as i get the following email from the cron daemon when it completes
Missing these required gems:
chronic
whenever
searchlogic
adzap-ar_mailer
twitter
gdata
bitly
ruby-recaptcha
You're running:
ruby 1.8.7.22 at /usr/bin/ruby
rubygems 1.3.5 at /home/myusername/gems, /usr/lib/ruby/gems/1.8
Run `rake gems:install` to install the missing gems.
(in /home/myusername/approotlocation)
my custom rake file within lib/tasks is as follows:
task :sendnewsletter => :environment do
require 'rubygems'
require 'chronic'
require 'whenever'
require 'searchlogic'
require 'adzap-ar_mailer'
require 'twitter'
require 'gdata'
require 'bitly'
require 'ruby-recaptcha'
#recipients = Subscription.all(:conditions => {:active => true})
for user in #recipients
Email.send_later(:deliver_send_newsletter,user)
end
end
with or without the require items, it still gives me the same error ...
can anyone shed some light on this? or alternatively advise me on how to make a custom file within the script directory that will run this function (I already have a cron job working that will run and process all my delayed_jobs.
After Jonathans suggestion below I ran
env
as a cron job on its own and received the following output:
SHELL=/bin/sh
MAILTO=myemailaddress
USER=myusername
PATH=/usr/bin:/bin
PWD=/home/myusername
SHLVL=1
HOME=/home/myusername
LOGNAME= myusername
_=/usr/bin/env
does this mean it's not loading the ruby files properly?
....
also took Jonathans advice and produced the following cron.sh file
#!/bin/sh
date
echo "Executing Matenia Rake Task"
PATH=/usr/bin:/bin
cd /home/myusername/approotlocation/
rake sendnewsletter
still getting the missing gems notice ...
Cheers!
Easiest way to fix this (but kind of a shotgun approche) is from your shell type
env | grep PATH
Then take this output and add it your crontab for that user
so it would look something like this
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
42 6 * * * root job1
47 6 * * 7 root job2
52 6 1 * * root job3
Just make sure your gems's are inside of that path
When cron runs, it executes with a very minimal environment. Try running a cron job that just does env or which ruby, and you may see that your PATH is not the same as your interactive shell path. You'll need to specifically set the PATH in .bash_profile or another shell startup file.
I got around all of this by making a custom script/file and running that through cron. I dont know how, but it managed to work ...
Thanks for all the efforts.
I am using a shell script to run some runner scripts in my Ruby on Rails app. I need to run it on the production database, but the following:
#!/bin/bash
/usr/bin/ruby RAILS_ENV=production ../script/runner ../lib/tasks.rb
gives an error:
/usr/bin/ruby: No such file or directory -- RAILS_ENV=production (LoadError)
I have tried to force it in config/environment.rb
ENV['RAILS_ENV'] ||= 'production'
or even
ENV['RAILS_ENV'] = 'production'
but even with that it still runs in development environment.
Update: I can force the scripts to connect to the right database by editing the config/database.yml file, but I wonder what's the proper way of doing it.
The help on the command line for script/runner gives you your answer.
script/runner -e production Model.method
If that's your command, the order of your arguments is your biggest problem.
/usr/bin/ruby RAILS_ENV=production ../script/runner ../lib/tasks.rb
Is different than.
/usr/bin/ruby ../script/runner ../lib/tasks.rb RAILS_ENV=production
The second example is looking for the file, the first one is setting a runtime variable while ruby interpreting it as the file you want to run.
If you redo your script like this:
#!/bin/bash
RAILS_ENV=production
/usr/bin/ruby ../script/runner ../lib/tasks.rb
...that will make it stick for the lifetime of the script. To make it stick for the lifetime of the shell's session, change it to
#!/bin/bash
export RAILS_ENV=production
/usr/bin/ruby ../script/runner ../lib/tasks.rb
You can set the environment variable like this :
RAILS_ENV=production /usr/bin/ruby ../script/runner ../lib/tasks.rb
RAILS_ENV=production script/rails runner 'user = User.find(:first, :conditions => {:admin => true}) ; user.password, user.password_confirmation = "mypasswd"; user.save!'
it worked for me