I don't understand why my rake task is not running from within a resque worker. Running
rake :send_this_email
from the console works fine, I just want to run it as a cron job (as follows) but something is not working proplerly while invoking the rake task from within the worker.
My rescue_schedule.yml
send_this_email:
cron: "*/2 * * * *"
class: SendThisEmailWorker
args:
description: "Send email when condition defined in rake task is met"
My send_this_email_worker.rb in workers directory, where the problem must be if I can manually call the rake task myself from the console?
require 'rake'
module SendThisEmailWorker
#queue = :send_this_email
def self.perform
Rake::Task["send_this_email"].invoke
end
end
When I start my dev server this send_this_email rake task should run every 2 minutes correct? It's not and the resque admin panel shows it as a job in the queue. What am I missing here?
Thanks for your attention.
UPDATED from gerep comment
require 'rake'
module SendThisEmailWorker
#queue = :send_this_email
def self.perform
puts "Hi from the console, I'm started"
Rake::Task["send_this_email"].invoke
end
end
Only require 'rake' is not enough. For example if you do
Rake::Task.tasks #list down all task
You will get []
You need to tell your worker class to load tasks.
Try this
require 'rake'
Rake::Task.clear # necessary to avoid tasks being loaded several times in dev mode
YOUR_APP_NAME::Application.load_tasks
module SendThisEmailWorker
#queue = :send_this_email
def self.perform
puts "Hi from the console, I'm started"
Rake::Task["send_this_email"].invoke
end
end
YOUR_APP_NAME is the name of your app and can be found at config/application.rb
Related
I have a rake task test that I setup following the only examples I could find online.
It looks like this:
require 'test_helper'
require 'minitest/mock'
require 'rake'
class TestScrapeWelcome < ActiveSupport::TestCase
def setup
Rake.application.init
Rake.application.load_rakefile
#task = Rake::Task['scrape:scrape']
#task.reenable
end
def teardown
Rake::Task.clear
end
test "scraping text and sending to elasticsearch" do
mocked_client = Minitest::Mock.new
get_fixtures.each_with_index do |arg,i|
mocked_client.expect :index, :return_value, [index: "test", type: 'welcome', id: i, body: arg]
end
Elasticsearch::Model.stub :client, mocked_client do
#task.invoke
end
assert mocked_client.verify
end
private
def get_fixtures
(0..11).map { |i|
File.read("test/fixtures/scrape/index_#{i}.json")
}
end
end
But after the task runs once it starts running again without me doing anything (puts prints before and after #task.invoke show that the task is only run the once).
Turns out that rake is already required and initialized when the test runs so all of the following lines need to be removed or the task gets defined twice and runs twice even if you only invoke it once.
require 'minitest/mock'
require 'rake'
...
Rake.application.init
Rake.application.load_rakefile
Updated answer for rails 5.1 (using minitest):
I found I needed the following to load tasks once and only once:
MyAppName::Application.load_tasks if Rake::Task.tasks.empty?
Alternatively add MyAppName::Application.load_tasks to your test_helper, if you don't mind tasks being loaded even when running individual tests that don't need them.
(Replace MyAppName with your application name)
I've tried #iheggie answer but it worked in a way that indeed tests were run once but any other task was breaking with Don't know how to build task '<task_name_like_db_migrate>'.
I'm on Rails 3.2 still. It turned out that there were couple tasks loaded beforehand so the Rake::Task.tasks.empty? was never true and all other useful tasks were not loaded. I've fiddled with it and this version of it works for me right now:
Rake::Task.clear if Rails.env.test?
MyAppName::Application.load_tasks
Hope this helps anyone.
A solution that works for testing the tasks of a Gem that has been made a Railtie so it can add tasks to the Rails app:
Don't define the Railtie in test mode when you're also defining a Rails::Application class in spec_helper.rb (which allows your tests to call Rails.application.load_tasks). Otherwise the Rake file will be loaded once as a Railtie and once as an Engine:
class Railtie < Rails::Railtie
rake_tasks do
load 'tasks/mygem.rake'
end
end unless Rails.env.test? # Without this condition tasks under test are run twice
Another solution would be to put a condition in the Rake file to skip the task definitions if the file has already been loaded.
I'm trying to user rake and rufus, both of which I am new to. I want to have Rufus call my rake task but I am getting the following error. Don't know how to build task 'inbox:process_inbox'
lib/tasks/inbox_tasks.rb
namespace :inbox do
task :process_inbox do
logger = Logger.new(Rails.root.to_s + "/log/scheduler.log")
logger.info "Rufus Here!"
end
end
rufus_scheduler.rb
require 'rufus-scheduler'
require 'rake'
scheduler = Rufus::Scheduler.new
scheduler.every '10s', :first_at => Time.now + 3 do
Rake::Task["inbox:process_inbox"]
end
As #jmettraux (the creator of rufus-scheduler!) has already answered, the problem is that the rake task is defined in a .rb file instead of .rake file.
Adding some more details to help in the future.
While creating a new rake task, you could get the rails generator to automatically create the file with appropriate structure.
Example: Running
> rails g task inbox process_inbox
create lib/tasks/inbox.rake
will create a file named lib/tasks/inbox.rake with content:
namespace :inbox do
desc "TODO"
task process_inbox: :environment do
end
end
Having a DESC in the task definition is important; that allows for verifying that the rake task is defined and available, by running either rake -T inbox or rake -T | grep inbox
> rake -T inbox
rake inbox:process_inbox # TODO
Could this one help?
How to build task 'db:populate' (renaming inbox_tasks.rb to inbox_tasks.rake)
(did a simple https://www.google.com/?#q=rails+don%27t+know+how+to+build+task ...)
I am trying to understand how to execute custom code with clockwork. This is the example lib/clock.rb file that Heroku uses in its devcenter document.
require File.expand_path('../../config/boot', __FILE__)
require File.expand_path('../../config/environment', __FILE__)
require 'clockwork'
include Clockwork
every(4.minutes, 'Queueing interval job') { Delayed::Job.enqueue IntervalJob.new }
every(1.day, 'Queueing scheduled job', :at => '14:17') { Delayed::Job.enqueue ScheduledJob.new }
What is IntervalJob and ScheduledJob? Where are these files supposed to be located? I want to run my own custom job that has access to my database records.
EDIT
This is my /lib/clock.rb
require 'clockwork'
require './config/boot'
require './config/environment'
module Clockwork
handler do |job|
puts "Running #{job}"
end
every(2.minutes, 'Filtering Streams') { Delayed::Job.enqueue FilterJob.new}
end
This is my /lib/filter_job.rb
class FilterJob
def perform
#streams = Stream.all
#streams.each do |stream|
# manipulating stream properties
end
end
end
I get the error:
uninitialized constant Clockwork::FilterJob (NameError)
/app/lib/clock.rb:11:in `block in <module:Clockwork>'
You need to do the following:
Firstly install the clockwork gem.
In your lib folder create a clock.rb
require 'clockwork'
require './config/boot'
require './config/environment'
module Clockwork
handler do |job|
puts "Running #{job}"
end
every(1.day, 'Creating Cycle', :at => '22:00') { Delayed::Job.enqueue CyclePlannerJob.new}
end
In the example your provided IntervalJob and ScheduledJob, are delayed jobs. Clockwork triggers them on the time specified. I am calling the CyclePlannerJob, this is what my file looks like.
lib/cycle_planner_job.rb
class CyclePlannerJob
def perform
CyclePlanner.all.each do |planner|
if Time.now.in_time_zone("Eastern Time (US & Canada)").to_date.send("#{planner.start_day.downcase}?")
planner.create_cycle
end
end
end
end
In my example everyday at 10pm, I am running the CyclePlanner job, which runs the delayed job I have setup. Similar to the Heroku example.
Bare in mind to use this you need to setup the clock work and delayed jobs on your Heroku app in the dashboard.
Also your Procfile should look like this.
worker: bundle exec rake jobs:work
clock: bundle exec clockwork lib/clock.rb
Let me know if you have any questions, I can go into more detail if needed.
Looks like name space issue. Move your filter_job.rb to models directory and try.
I'm creating a DripEmail campaign for my app and using resque scheduler to schedule the tasks.
I've sceduled a static job, which runs every day at a specific time and collect the user's list based on the drip settings and sends out an emailer to them.
This is my resque job user_follow_up.rb
class UserFollowUp
#queue = :user_follow_up
def self.perform
User.each do |u|
# Send the emailers to only those who are not converted
if !user.is_converted and Date.today <= user.next_email
stage(u)
end
end
end
end
This is my scheduler.yml
UserFollowUp:
cron: "0 16 * * *"
I have 2 resque workers, one has my default set of tasks and the other for scheduler.
rake environment resque:work QUEUE=publish_story,accept_story,image_queue,Mango_mailer
and
rake environment resque:scheduler QUEUE=user_follow_up
When I open the resque admin interface, I'm able to see my static job detected in the list. I clicked the Queue Now button to test it. It properly enqueues the task to the queue, but doesn't execute. It keeps these tasks in the pending queue forever.
This is my resque.rake, it's required
require 'resque/tasks'
require 'resque/scheduler/tasks'
require 'resque/scheduler/server'
require 'active_record'
require 'mongoid'
require 'action_controller/railtie'
require 'active_support/buffered_logger'
# load the Rails app all the time
namespace :resque do
puts 'Loading Rails environment for Resque'
task :setup => :environment do
# The schedule doesn't need to be stored in a YAML, it just needs to
# be a hash. YAML is usually the easiest.
Resque.schedule = YAML.load_file("#{Rails.root}/config/scheduler.yml")
Resque::Scheduler.dynamic = true
Resque.logger.info 'Resque Scheduler Initialized!'
Resque.before_first_fork do
# Open the new separate log file
logfile = File.open(File.join(Rails.root, 'log', 'resque.log'), 'a')
# Activate file synchronization
logfile.sync = true
# Create a new buffered logger
Resque.logger = ActiveSupport::BufferedLogger.new(logfile)
Resque.logger.level = Logger::INFO
Resque.logger.info 'Resque Logger Initialized!'
puts 'Resque Logger Initialized!'
end
end
task 'resque:pool:setup' do
Resque::Pool.after_prefork do |job|
Resque.redis.client.reconnect
end
end
end
And this is my Gemfile
gem 'resque', github: 'resque/resque' , branch: '1-x-stable'
gem 'resque_mailer', github: 'zapnap/resque_mailer'
gem 'resque-scheduler'
I'm not sure what is the issue here. Please help me with this fix.
I'd like to run a rake task (apn:notifications:deliver from the apn_on_rails gem) from a delayed_job. In other words, I'd like enqueue a delayed job which will call the apn:notifications:deliver rake task.
I found this code http://pastie.org/157390 from http://geminstallthat.wordpress.com/2008/02/25/run-rake-tasks-with-delayedjob-dj/.
I added this code as DelayedRake.rb to my lib directory:
require 'rake'
require 'fileutils'
class DelayedRake
def initialize(task, options = {})
#task = task
#options = options
end
##
# Called by Delayed::Job.
def perform
FileUtils.cd RAILS_ROOT
#rake = Rake::Application.new
Rake.application = #rake
### Load all the Rake Tasks.
Dir[ "./lib/tasks/**/*.rake" ].each { |ext| load ext }
#options.stringify_keys!.each do |key, value|
ENV[key] = value
end
begin
#rake[#task].invoke
rescue => e
RAILS_DEFAULT_LOGGER.error "[ERROR]: task \"#{#task}\" failed. #{e}"
end
end
end
Everything runs fine until the delayed_job runs and it complains:
[ERROR]: task "apn:notifications:deliver" failed. Don't know how to build task 'apn:notifications:deliver'
How do I let it know about apn_on_rails? I'd tried require 'apn_on_rails_tasks' at the top of DelayedRake which didn't do anything. I also tried changing the directory of rake tasks to ./lib/tasks/*.rake
I'm somewhat new to Ruby/Rails. This is running on 2.3.5 on heroku.
Why don't do just a system call ?
system "rake apn:notifications:deliver"
I believe it's easier if you call it as a separate process. See 5 ways to run commands from Ruby.
def perform
`rake -f #{Rails.root.join("Rakefile")} #{#task}`
end
If you want to capture any errors, you should capture STDERR as shown in the article.