Delayed Job - Find by object id - ruby-on-rails

I have using Rails + DelayedJob Mongoid. I have this model:
class User
def run
...
end
end
I create new Dejayed::Job object:
user = Use.create
user.delay.run
How I can I do something like this?
Delajed::Job.where(object_id: user.id)

I am not sure if this will work in your case.
If you have the delayed_job object id, you can simply find it like normal activerecord objects.
# find a job by id
job = Delayed::Job.find(params[:id])
Now when you dont have the id. Then add something unique during the creation of the job itself
in users_controller.rb
user = User.create
in user.rb
def delayed_run
my_job = MyJob.new('xyz')
job = Delayed::Job.enqueue(run, 0, 1.hour.from_now)
job.user_id = self.id
end
So after this, you can find your delayed job object from the unique user_id
Delayed::Job.where(user_id: 5)

Based on #aelor answer, finally I had to add field object_id to Job class:
# config/initializers/dejayed_job.rb
module Delayed
module Backend
module Mongoid
class Job
include ::Mongoid::Document
field :object_id
end
end
end
end
Then it works ok:
user = Use.create
user.delay(object_id: user.id).run
Delayed::Job.where(object_id: 5).first
=> #<Delayed::Backend::Mongoid::Job _id: 551a5116726a750e08000000

Related

Create resource from model when User signs up

I'm trying to create a resource Mission when a new user is signed up. Mission has a foreign key Location as well.
class User < ApplicationRecord
after_create :create_mission
private
def create_mission
Mission.create(user: self, location_id: 1, name: 'Mission 1')
end
end
But this code doesn't work unfortunately. How can I solve it?
How about this:
class User < ApplicationRecord
after_create :create_mission
private
def create_mission
missions.create! location_id: 1, name: 'Mission 1'
end
end
Now your error could be visible with create!. Try this.
I do not recommend you to create relations in callbacks. It will hurt you if you will need to create user in another action or in console. Better to create it in controller after creation (in some service object in the future)
def create
if #user.save
#user.missions.create(...)
end
end
And you can use debugger to check errors dynamically (shipped with rails https://github.com/deivid-rodriguez/byebug, just insert 'debugger' in your code), probably you have some validation error.

Can't find Delayed Job record in database

I'm new to Delayed Jobs and am not sure if I'm doing this correctly.
Worker
class BillingFailedEmailWorker < BaseWorker
attr_accessor :car
def initialize(car)
#car = car
end
def billing_failed_for_subscriptions
StripeMailer::billing_failed_for_subscriptions(car.owner.email).deliver
end
def perform
self.class.new(car).delay(
run_at: DateTime.now + 1.week,
queue: "Failed Subscription"
).billing_failed_for_subscriptions
end
end
Mailer
class StripeMailer < ActionMailer::Base
default from: Settings.default_from_email
def billing_failed_for_subscriptions(email)
mail to: email, subject: 'Autobrain Subscription Renewal Failed'
end
end
billing_failed_for_subscriptions also obviously corresponds to the correct mailer view.
My question is when I run BillingFailedEmailWorker.new(Car.last).perform I see the command execute correctly in Delayed::Backend::ActiveRecord::Job but when I run Delayed::Backend::ActiveRecord::Job.last or Delayed::Job.last I don't see the job I just created.
Could this be a matter of my Rails environment being in development or is there something I'm missing?
Instead of using
StripeMailer::billing_failed_for_subscriptions(car.owner.email).deliver
use
StripeMailer::billing_failed_for_subscriptions(car.owner.email).deliver_later
Then in the console you can check it as Delayed::Job.last

Sidekiq: ArgumentError: When assigning attributes, you must pass a hash as an argument

I guess this question is common with Rails 4, but my situation is different.
I am using Sidekiq to delay the creation of jobs; think this is possible as with simple data, it works. By means of simple data:
def perform
Foo.create(bar: "staff")
end
Here's my data with issues:
supports_controller.rb:
def create
params = support_params // seems to be issues here?
DelayedJobs.perform_in(1.minutes, current_user.id, params)
...
end
private
def support_params
params.require(:support).permit(:foo1, :foo2, :foo3)
end
app/workers/delayed_jobs.rb:
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
support = u.supports.build(params)
Support.create(support) // create and save to db
end
end
Via web (localhost:3000/sidekiq/scheduled, I see the details. Great. But after a minute it goes to retries with the error. Any help on this one?
EDIT:
In the sidekiq web argument:
40, {"foo1"=>"a", "foo2"=>"b", "foo3"=>"c"}
Why is that the user_id (40) is outside?
The problem isn't with Sidekiq; it's an ActiveRecord problem with this line:
Support.create(support)
create only takes a hash, but you're giving it a Support.
This should work:
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
u.supports.create!(params) # `create!` will raise an error if the save fails; allowing you to catch invalid params
end
end
Protip: you can eliminate Sidekiq as a suspect by running the body of your perform method in a Rails console. You'll see that you get the same error even when Sidekiq isn't involved.
I suggest that you call save method on support object because when you are using build method it returns a new instance of support so you need only to save it.
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
support = u.supports.build(params)
support.save // save to db
end
end
In your controller try to change params to:
def create
params = params[:support]

Recommended practice for passing current user to model

Given a model Orderstatus with attributes private_status:string, and private_status_history:json(I'm using Postgresql's json). I would like to record each status transition, together with the user who made the change.
Ideally it would be something like:
class Orderstatus < ActiveRecord::Base
after_save :track_changes
def track_changes
changes = self.changes
if self.private_status_changed?
self.private_status_history_will_change!
self.private_status_history.append({
type: changes[:private_status],
user: current_user.id
})
end
end
end
class OrderstatusController <ApplicationController
def update
if #status.update_attributes(white_params)
# Good response
else
# Bad response
end
end
end
#Desired behaviour (process not run with console)
status = Orderstatus.new(private_status:'one')
status.private_status #=> 'one'
status.private_status_history #=> []
status.update_attributes({:private_status=>'two'}) #=>true
status.private_status #=> 'two'
status.private_status_history #=> [{type:['one','two'],user:32]
What would be the recommended practice to achieve this? Apart from the usual one using Thread. Or maybe, any suggestion to refactor the structure of the app?
So, I finally settled for this option ( I hope it's not alarming to anyone :S)
class Orderstatus < ActiveRecord::Base
after_save :track_changes
attr_accessor :modifying_user
def track_changes
changes = self.changes
if self.private_status_changed?
newchange = {type:changes[:private_status],user: modifying_user.id}
self.update_column(:private_status_history,
self.private_status_history.append(newchange))
end
end
end
class OrderstatusController <ApplicationController
def update
#status.modifying_user = current_user # <---- HERE!
if #status.update_attributes(white_params)
# Good response
else
# Bad response
end
end
end
Notes:
- I pass the from the Controller to the Model through an instance attribute modifying_user of the class Orderstatus. That attribute is ofc not saved to the db.
- Change of method to append new changes to the history field. I.e. attr_will_change! + save to update_column + append

Rails - Delayed job completion

I have a web application based on ruby on rails, which uses this delayed job gem.
I have a function that triggers a delayed job which in turn triggers several other delayed jobs. Is there a way, nest case being an event that can indicate that all the jobs related to a parent have completed? Or do I just have to work on whatever data is available when I try to retrieve docuemnts ?
Example:
def create_vegetable
#..... creates some vegetable
end
def create_vegetable_asynchronously id
Farm.delay(priority: 30, owner: User.where("authentication.auth_token" => token).first, class_name: "Farm", job_name:create "create_vegetable_asynchronously").create_vegetable(id)
end
def create_farm_asynchronously data
data.each do |vegetable|
create_vegetable_asynchronously vegetable.id
end
end
handle_asynchronously :create_farm_asynchoronously
maybe a little overkill, but you can explicitly organize your jobs hierarchically and employ before hook to pass on the parent job id to subtasks.
something like:
class Job < Delayed::Job
belongs_to :parent, class: 'Job' #also add db column for parent_id
end
class CreateVegetable
def initialize(id)
#id = id
end
def perform
Farm.create_vegetable(id)
end
end
class CreateFarm
def initialize(vegetable_ids,owner_id)
#vegetable_ids = vegetable_ids
#owner_id = owner_id
end
def before(job)
#job_id = job.id
end
def perform
#vegetable_ids.each { |id| Job.enqueue CreateVegetable.new(id), priority: 30, owner_id = #owner_id, :parent_id = #job_id }
end
end
Then when you launch create farm, somehow remember the job id.
def create_farm_asynchronously data
owner_id = User.where("authentications.auth_token" => token).first.id
#parent = Job.enqueue CreateFarm.new(data.map(&:id), owner_id)
end
then you can check sub-jobs by:
Job.where(parent_id: #parent.id)

Resources