I'm attempting to use the whenever to execute a method every 30 minutes. However I'm having some trouble setting it up.
schedule.rb
every 1.minutes do
runner "Post.winner"
end
post.rb
class Post < ActiveRecord::Base
module Post
def winner
#do some stuff that saves a new value in database
I don't think the runner is working because the field that I save a value to still shows up as nil in the console.
You most probably do not want to run a controller method (eg you have no request to serve there). Either create a runner from a class or module.
# schedule.rb
every 30.minutes do
runner "Post.winner"
end
#lib/post.rb
module Post
def self.winner
...
end
end
or a rake task in lib/tasks/
# schedule.rb
every 30.minutes do
rake post:winner
end
#lib/tasks/post.rake
namespace :post do
desc 'calculate winner'
task :winner do
...
end
end
I'm almost sure you have a model Post. Create a class method:
class Post
...
def self.winner
...
end
end
But if you are using Post.winner only for the schedule, I prefer a rake task as #xlembouras suggested.
Related
I would like to automate a task in my Rails app, create a rake task, almost all the code I need is in a controller action, I would like to just call that controller action instead of write the "same" code in my task and save some lines of code. Is that possible?
Well, for example. Say you have usecase to auto renew Stripe customers subscriptions from both Rake tasks and using client side . Now, you will write a PORO class like below:
class AutoRenewSubscription
attr_reader :coupon, :email
def initialize args = {}
#email = args[:email]
#coupon = args[:coupon]
#....
end
def run!
user_current_plan.toggle!(:auto_renew)
case action
when :resume
resume_subscription
when :cancel
cancel_subscription
end
end
#... some more code as you need.
end
You can put the class inside app/services/auto_renew_subscription.rb folder. Now this class is available globally. So, call it inside the controller like :
class SubscriptionController < ApplicationController
def create
#.. some logic
AutoRenewSubscription.new(
coupon: "VXTYRE", email: 'some#email.com'
).run!
end
end
And do call it from your rake task also :
desc "This task is to auto renew user subscriptions"
task :auto_renew => :environment do
puts "auto renew."
AutoRenewSubscription.new(
coupon: "VXTYRE", email: 'some#email.com'
).run!
end
This is what I think good way to solve your issue. Hope you will like my idea. :)
I have installed the Ruby gem Delayed_Job to run tasks in a queue, but it shows some behavior that I don't understand. Delayed_Job is using my local active_record so a very standard installation.
I have the code for a job in a file called test_job.rb in my /lib folder
class TestJob
# Create a entry in the database to track the execution of jobs
DatabaseJob = Struct.new(:text, :emails) do
def perform
# Perform Test Code
end
end
def enqueue
#enqueue the job
Delayed::Job.enqueue DatabaseJob.new('lorem ipsum...', 'test email')
end
end
When I try to call the code from a controller like this, the first time the job seems to get submitted (is listed in rake jobs:work) but it does not run:
require 'test_job'
class ExampleController < ApplicationController
def index
end
def job
# Create a new job instance
job = TestJob.new
# Enqueue the job into Delay_Job
job.enqueue
end
end
Then when I change the controller code to do what my lib class does, it works perfectly. The job does not only get submitted to the queue, but also runs and completes without failures.
require 'test_job'
class ExampleController < ApplicationController
# Create a entry in the database to track the execution of jobs
DatabaseJob = Struct.new(:text, :emails) do
def perform
# Perform Test Code
end
end
def index
end
def job
#enqueue the job
Delayed::Job.enqueue DatabaseJob.new('lorem ipsum...', 'test email')
end
end
The strange thing is that when I switch back to calling the lib job class it works without a problem. Then it does not matter whether the struct is directly defined in the controller or in the class in the lib folder.
Defining the struct inside the controller and submitting the job to the queue this way always seems to work, but afterwards also the lib class starts working and sometimes the lib class works even after a restart of the rails server.
Any ideas? Thank you very much for the help.
Best,
Bastian
I would like to have a task that simply substracts a certain value from my user model at every 5 minutes.
My code:
schedule.rb
every 5.minutes do
runner 'Site::SubstractSomething.execute'
end
app/jobs/SubstractSomething.rb
module Site
class SubstractSomething
def initialize
end
def execute
#users = ::User.all
#users.each { |user| user.update_heat }
end
end
end
method inside user model:
def update_heat
self.heat -= 10
self.save
end
then I ran:
crontab -r
whenever --update-crontab --set environment='development'
EDIT:
I have taken out the job from the namespace and seems that it did the trick.Thanks for the help
You will have to require 'SubstractSomething' in your schedule.rb and make sure that your $LOAD_PATH includes the directory it is situated in. See this question for some possibilities on how to achieve this.
I am rookie in RoR. I want to run a custom task after every order. To be more specific, I want to run a python script to update the quantity of the same prduct on another site.
So my question is, how can I trigger the script after every order?
Thanks
You can run commands like ruby does. `python commands`, note that '' between.
If Order is an ActiveRecord model, you can use an ActiveRecord callback to run code after each Order is created. For example:
class Order < ActiveRecord::Base
after_create :update_count
private
def update_count
system "python /path/to/script.py #{self.class.count}"
end
end
If the publish script is likely to take a long time, you'll want to spawn a background job to run the script instead. There are a number of Ruby libraries to manage this for you; have a look at sidekiq, resque, or delayed_job for a start.
You can add an after_filter. But this will block.
class OrdersController < ApplicationController
after_filter :update_product_count, :only => :new_order
def new_order
...
end
private
def update_product_count
system "python /path/to/update/script.py"
end
end
Or you can do it in background (I prefer delayed jobs gem)
Order model:
class Order < ActiveRecord::Base
private
def update_product_count order_id
# find products in this order and pass them to python script
products = Order.find(order_id).products.map(&:id)
system "python /path/to/update/script.py #{products}"
end
end
Orders controller:
class OrdersController < ApplicationController
def new_order
order.new
...
order.save!
Order.delay.update_product_count(order.id)
end
end
I'm using resque to do some (long time) job. And I have a few classes with the same mixed-in module for queuing. Class Service substitutes in tests, that's why it standalone and (maybe too much) complicated. So the story is when I call
Campaign.perform(user_id)
directly, everything works fine, but when I try to use queue:
Resque.enqueue(Campaign, user_id)
Job created, but seems like do nothing. At least, nothing saves into the database. Which is main task of Campaign class. I can see in resque-web-interface that jobs creates and finished, and finished (to fast, almost just after create) but no result.
I'm new in Resque and not really sure it calls it all (confused how to debug it).
Does anybody have similar problem? thanks for any help.
Module:
module Synchronisable
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def perform(user_id)
save_objects("#{self.name}::Service".constantize.get_objects(user_id))
end
protected
def save_objects(objects)
raise ArgumentError "should be implemented"
end
end
class Service
def self.get_objects(user)
raise ArgumentError "should be implemented"
end
end
end
One of the classes:
class Campaign < ActiveRecord::Base
include Synchronisable
#queue = :app
class << self
protected
def save_objects(objects)
#some stuff to save objects
end
end
class Service
def self.get_objects(user_id)
#some stuff to get objects
end
end
end
This is a very old question so not sure how rails folder structure was back then but I had the same problem and issue was with inheritance. Seems if you are using Resque your job classes shouldn't inherit from ApplicationJob.
so if your code was like this in (app/jobs/campaign_job.rb):
class Campaign < ApplicationJob
#queue = :a_job_queue
def self.perform
#some background job
end
end
then remove the inheritance i.e "< ApplicationJob"
These jobs are almost certainly failing, due to an Exception. What is resque-web showing you on the Failures tab? You can also get this from the Rails console with:
Resque.info
or
Resque::Failure.all(0)
You should run your worker like this:
nohup QUEUE=* rake resque:work & &> log/resque_worker_QUEUE.log
This will output everything you debug to "log/resque_worker_QUEUE.log" and you will be able to find out what's wrong with your Campaign class.
Try this:
env TERM_CHILD=1 COUNT=2 "QUEUE=*" bundle exec rake resque:workers