delayed_job saving entry but not sending out email - ruby-on-rails

I am using delayed_job gem to send email upon the destroy of a record.
comments_controller.rb
def reject_appeal
#comment = Comment.find(params[:id])
if #comment.destroy
#comment.rejection(#comment)
flash[:alert] = "You have succesfully rejected the appeal."
redirect_to post(#comment.post_id)
end
end
comments.rb
def rejection(comment)
RejectAppeal.delay.notify(comment)
end
Now if I remove the delay method and just have it as RejectAppeal.notify(comment), the email gets sent out perfectly fine. But with delay i don't know what happens. I don't see anything in the delayed job lob log. Although in development log i do notice that the entry gets stored in or at least it BEGINS the action.
Any help on this? I am using delay method on several other mailer functions in this same app and they all get sent out fine but i am unsure whats wrong with this
P.S I am using Rails 4.0.1

Is the delayed job getting serialized correctly? (Check the delayed_jobs table). Is there an error running the job? The error gets stored in the last_error column if so.
It's probably because you are destroying your comment and then passing it through to the delayed job so, when the delayed job runs and tries to load the comment to do something with it, the comment can no longer be found.

Related

rails controller action contain an algorithm

I have made a rails app. Users can upload images. Once the images are saved into database. An algorithm being called to process those pictures.
For now, it was realized within a controller action like this:
def create
#post = current_user.posts.build(params)
if #post.save
flash[:success]="post created"
redirect_to root_url
image_names = []
#post.picture.each do |imgs|
image_names << imgs.url
end
my_algorithm(image_names)
else
render 'static_pages/home'
end
end
It works correctly. The problem is the page didn't show until the algorithm finishing. And the algorithm took long time. How to fix it. Or maybe call my_algorithm other places? Or delay_job?
I think you should use Active Job for that it will make your job background job
You should try the background jobs to perform that action in particular time.
You may use sidekiq gem to trigger the events in background.
Or You can refer the following URL to, do the active jobs,
http://edgeguides.rubyonrails.org/active_job_basics.html#enqueue-the-job
Refer the above url and configure the app and perform the operations in background.
I have additionally mention the one more link for scheduled jobs,
How to perform a background job now?
First of all, this should be done in a call back, here in the after_create callback of the Post model. And from the callback you can en queue one delayed job or other background job which will fire the algorithm. In front end you can show some messages like under processing in the place where you want to show the processed information.
Thanks

Delayed Jobs not picking up second ActionMailer action

My problem is pretty simple, I am trying to delay 2 ActionMailer actions on the same method, this way:
#params = params
begin
ContactMailer.delay.email_application(#params)
ContactMailer.delay.email_application_confirmation(#params)
end
The first one is being sent to the website's admin, the second one to the user submitting the form.
But only the first one is being picked up by delayed_job and added to the jobs queue. What's going on with the second one? (I tried to add begin``end for that reason but it didn't change anything).
EDIT: I should mention that, looking at the logs, nothing appears, as if the second line was completely being ignored
this fixed it...
#params = params
begin
ContactMailer.delay.email_application(#params)
end
begin
ContactMailer.delay.email_application_confirmation(#params)
end

Do delayed Sidekiq Mailer methods execute when the job is processed?

I have a typical ActionMailer with a method specifying an email delivery.
def some_email(user_id)
#user = User.find(user_id)
if #user.eligible_for_email?
mail(to: #user.email, from: "me#me.com", subject: "The Subject")
#user.email_sent = Date.today
#user.save
end
end
I want to delay the sending of this using Sidekiq so I use:
Mailer.delay_for(2.days).some_email(user.id)
The eligible_for_email method:
def eligible_for_email?
!unsubscribed? && email_sent.nil?
end
In the meantime, the user could have unsubscribed, which is why there is a method in the User model called eligible_for_email? which I can use to conditionally send the email - but obviously this condition needs to be tested just before the email is sent, not when the job is scheduled.
So the problem is that when I use Sidekiq to process this, the conditional logic doesn't seem to be run when the job is done.
Does Sidekiq work by executing the some_email method on runtime and then queuing the resulting email to be sent out two days later, thereby negating my conditional code?
Your understanding is 100% correct and that's exactly what you want to do.
I'd guess you aren't restarting Sidekiq to pick up your code changes. Sidekiq does not auto-reload changed code like Rails does.

Execute task in another thread

I'm new in Rails developing, and I have one question. There the following code:
def create
#order = current_user.orders.create!(order_params)
OrderMailer.send_order_info(#order).deliver
end
This code creates a new order, render json result and send e-mail about it. Mail sending takes some time, and I think I should do it in another thread or something similar. Please, give me advice how I can do it good. Thanks!
You should use delay the email sending. You could do it using Sidekiq, Delayed Job or Resque for example.
You will also be able to delay any other jobs with these gems.
You should look at the docs and see which one is the best for your use.
I personally use Sidekiq but Delayed Job is the easiest to install if you only want to use it for mailer.
We use Spawn for this: it's changed its name to "Spawnling" now
https://github.com/tra/spawnling
Very easy to use: (in the controller)
#user = User.create(params[:user])
spawn do
#user.do_some_slow_background_stuff
end
or, if you want to monitor whether the background process has finished yet (#spawn_id is the pid)
#user = User.create(params[:user])
spawner = spawn do
#user.do_some_slow_background_stuff
end
#spawn_id = spawner.handle

How can I abort the delivery of an ActionMailer request?

I'm running a Q&A service. One of the things admins can do is mark a question as offtopic. When they they do that an email gets sent to the person that asked the question telling them that their email is offtopic.
The email notification is sent via delayed_job:
QuestionMailer.delay.notify_asker_of_offtopic_flag question
However, on occasion someone might accidentally mark the question as offtopic or change their mind. To avoid an incorrect notification going to the person who originally asked it I want to create a short delay and evaluate whether the question is still offtopic when the mailer request runs:
Delayed call to mailer:
QuestionMailer.delay(run_at: Time.now + 3.minutes).notify_asker_of_offtopic_flag(question)
Mailer:
class QuestionMailer
...
def notify_asker_of_offtopic_flag question
if question.offtopic?
# do mailing
end
end
end
Unfortunately this isn't that simple since the if block simply causes an error which then causes delayed_job to retry the job again and again.
I'm now experimenting with some pretty roundabout methods to achieve the same end but I'd really like to find some way to abort the QuestionMailer action without triggering errors. Is this possible?
Dont delay the mailer then. Delay another class method in your Question class perhaps? Pass the id of the question and within that delayed method check if the question is still offtopic and if yes the send email synchronously.
Essentially, your notify_asker_of_offtopic_flag could be moved to your question model and then mailing is synchronous (i'm sure you'll rename your methods).
There is talk going on about preventing delivering by setting perform_deliveries to false within your mail action itself in core but i'm not 100% where or how that will end up.
#Aditya's answer was basically correct however I wanted to keep my methods on the Mailer object to keep things nice and tidy. This required a few extra hacks.
Create a new Class method in the mailer that CAN be delayed
The problem with trying to cancel an instance Mailer method is that it inherently triggers rendering and other things that stop the method from being aborted. However I would still like to keep all my Mailer logic together.
The way I did this was by using a class method instead of an instance method. This avoided all of the hooks that kick in when calling the method on an ActionMailer instance but still allowed me to keep the code tidy and together
class QuestionMailer
...
def notify_asker_of_offtopic_flag question
...
end
def self.notify_asker_of_offtopic_flag question_if question
if question.offtopic?
QuestionMailer.notify_asker_of_offtopic_flag question
end
end
end
NB fix for using delayed job
This works except for one slight hack that's necessary to deal with delayed_job.
When dealing with a Mailer, delayed_job will always call .deliver on the returned object in order to deliver the mail. This is fine when we return a mail object but in this case we're returning nil. delayed_job therefore tries to call .deliver on nil and everything fails.
In order to account for this we simply return a dummy mailer object containing a dupe .deliver method:
class QuestionMailer
...
class DummyMailer
def deliver
return true
end
end
def notify_asker_of_offtopic_flag question
# do mailing stuff
end
def self.notify_asker_of_offtopic_flag question_if question
if question.offtopic?
QuestionMailer.notify_asker_of_offtopic_flag question
else
DummyMailer.new
end
end
end

Resources