Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby On Rails : Sidekiq : Instance variable @organisation not available in mailer-layout

I have used sidekiq and redis-server to send email in background..
Problem: Its ok when used sync method to send email. i.e. in applicants_controller.rb

UserMailer.notify_applicant_assignment(current_assigned_user.id, applicant, workflow_step).deliver

However, when I use delay method to send email i.e.

in applicants_controller.rb

UserMailer.delay.notify_applicant_assignment(current_assigned_user.id, applicant, workflow_step)   

I get the following error undefined method 'background_color' for nil:NilClass in /layouts/user_mailer.html.erb:17:

Code inside mailers/user_mailer.rb

class UserMailer < ActionMailer::Base
  include Sidekiq::Worker
  default from: CommonConstants::DO_NOT_REPLY_ADDRESS
  layout 'user_mailer'
    def notify_applicant_assignment(user_id, applicant_id, workflow_step_id)
       @user = User.find(user_id)
       @organization = @user.organization
       @applicant = Applicant.find(applicant_id)
       @url = root_url + 'applicants/' + @applicant.id.to_s
       @workflow_step = WorkflowStep.find(workflow_step_id)
       mail(to: @user.email, subject: 'Applicant Assigned.')
    end
end

Code inside layouts/user_mailer.html.erb

<body style="background:#f4f4f4;">
<table width="100%" bgcolor="<%= @organization.background_color %>" cellpadding="0" cellspacing="0">
  <tr>
    <td>
      <table align="center" width="730" cellpadding="0" cellspacing="0" >

Error I got in sidekiq console

2015-03-24T08:58:14Z 5595 TID-cjors WARN: {"retry"=>true, "queue"=>"default", "class"=>"Sidekiq::Extensions::DelayedMailer", "args"=>["---\n- !ruby/class 'UserMailer'\n- :notify_applicant_assignment\n- - 4\n  - '9'\n  - '9'\n"], "jid"=>"4421abf04e7e6864c7ee9fd8", "enqueued_at"=>1427187124.323067, "error_message"=>"undefined method `background_color' for nil:NilClass", "error_class"=>"ActionView::Template::Error", "failed_at"=>1427187124.3466575, "retry_count"=>4, "retried_at"=>1427187494.9246943}
2015-03-24T08:58:14Z 5595 TID-cjors WARN: undefined method `background_color' for nil:NilClass
2015-03-24T08:58:14Z 5595 TID-cjors WARN: /home/leapfrog/projects/ATS/app/views/layouts/user_mailer.html.erb:17:in `_app_views_layouts_user_mailer_html_erb__235114594899105140_70586860'
/home/leapfrog/.rvm/gems/ruby-2.1.1/gems/actionpack-4.0.4/lib/action_view/template.rb:143:in `block in render'
/home/leapfrog/.rvm/gems/ruby-2.1.1/gems/activesupport-4.0.4/lib/active_support/notifications.rb:161:in `instrument'
/home/leapfrog/.rvm/gems/ruby-2.1.1/gems/actionpack-4.0.4/lib/action_view/template.rb:141:in `render'
/home/leapfrog/.rvm/gems/ruby-2.1.1/gems/actionpack-4.0.4/lib/action_view/renderer/template_renderer.rb:61:in `render_with_layout'
/
like image 379
illusionist Avatar asked Mar 24 '15 09:03

illusionist


1 Answers

It's very possible that the delay extension work differently then you would expect. They are different that the regular async job.

In the documentation, it is clearly stated that:

I strongly recommend avoiding delaying methods on instances. This stores object state in Redis which can get out of date, causing stale data problems.

I wouldn't be surprised if the delay extension stores the unrendered template and the mail parameters (subject, to, from, etc') rather then calling the method itself for an outdated instance.

Also, please notice that notify_applicant_assignment isn't defined as a class method (it looks like an instance method), although the Sidekiq documentation states that delay should be applied to class methods.

Any class method can be delayed via the same methods as above:

I would recommend making the notify_applicant_assignment a class method and trying again (notice the self in def self.notify_applicant_assignment vs. def notify_applicant_assignment).

class UserMailer < ActionMailer::Base
  include Sidekiq::Worker
  default from: CommonConstants::DO_NOT_REPLY_ADDRESS
  layout 'user_mailer'
    def self.notify_applicant_assignment(user_id, applicant_id, workflow_step_id)
       @user = User.find(user_id)
       @organization = @user.organization
       @applicant = Applicant.find(applicant_id)
       @url = root_url + 'applicants/' + @applicant.id.to_s
       @workflow_step = WorkflowStep.find(workflow_step_id)
       mail(to: @user.email, subject: 'Applicant Assigned.')
    end
end

Alternatively, I would recommend also using the standard async engine in applicants_controller.rb:

UserMailer.notify_applicant_assignment_async(current_assigned_user.id, applicant, workflow_step)

Please let me know how it goes, so I can research some more if there's still a need.

Good luck!

like image 189
Myst Avatar answered Oct 02 '22 12:10

Myst