Search code examples
ruby-on-rails-4sidekiqruby-on-rails-4.1

Rails 4 Sidekiq uninitialized constant error


I have problem with my application, when trying to send emails in the background I get error uninitialized constant AnswersController::LazyDoer , I don't have slightest clue why isn't it working, any suggestions?

My worker is in app/workers/lazy_doer.rb

Here is my controller:

class AnswersController < ApplicationController
  before_action :authenticate_user!
  before_action :set_question, except: [:adding_likes,:accept]

  def create
    @answer = Answer.new(answer_params)
    @answer.user_id = current_user.id
    @answer.question_id = @question.id
    @question_owner = User.find(@question.user_id)
    
    if @answer.save
      LazyDoer.perform_async(@question_owner,current_user,@answer,@question)
      redirect_to question_path(@question), notice: "Answer was successfully created."
    else    
      render(:template => "questions/show", alert: "There was an error when adding answer.")
      
    end
  end

Here you have my worker:

class LazyDoer
  include Sidekiq::Worker
  sidekiq_options retry: false
  
  def perform(question_owner,current_user,answer,question)
    @question_owner = question_owner
    @current_user = current_user
    @answer = answer
    @question = question
    UserMailer.send_email(@question_owner,@current_user,@answer,@question).deliver
  end
end

EDIT:

I made my LazyDoer worker fully operational , but now I have problem with sending email via it. What's most important , MAILER WORKS PERFECTLY WITHOUT SIDEKIQ. Here's the error inside sidekiq:

2014-07-30T19:28:38.479Z 4317 TID-amn3w LazyDoer JID-3e465606b1d5728181002af0 INFO: start
2014-07-30T19:28:38.480Z 4317 TID-amn3w LazyDoer JID-3e465606b1d5728181002af0 INFO: fail: 0.001 sec
2014-07-30T19:28:38.481Z 4317 TID-amn3w WARN: {"retry"=>false, "queue"=>"default", "class"=>"LazyDoer", "args"=>["someemail", "someemail", "#<Answer:0x000000045fd148>", "#<Question:0x000000045fe728>"], "jid"=>"3e465606b1d5728181002af0", "enqueued_at"=>1406748518.4762628}
2014-07-30T19:28:38.481Z 4317 TID-amn3w WARN: undefined method `email' for "someemail":String
2014-07-30T19:28:38.481Z 4317 TID-amn3w WARN: /home/mateusz/Pulpit/Aptana3_Workspace/challenge_app/app/mailers/user_mailer.rb:9:in `send_email'

And here you have my mailer:

class UserMailer < ActionMailer::Base
  default from: "someemail"
  
  def send_email(question_owner,cur_user,answer,question)
    @question_owner = question_owner
    @cur_user = cur_user
    @answer = answer
    @question = question
    mail(to: @question_owner.email, subject: "Answer added to your question:")
  end
  
  def accepted_email(user,answer,question)
    @user = user
    @answer = answer
    @question = question
    mail(to: @user.email, subject: "Your answer has been accepted")
  end
end

Solution

  • I have solution, the problem with Sidekiq error was that since it's using nosql database which is redis, redis can't properly understand complex rails data, like for example ActiveRecord Models, if you are trying to send to your worker let's say , whole user with every single attribute he has - this will not work in redis, data is way too complex. Solution is simple, look exactly at your generated email view and mailer.rb and see exactly which attributes you need, then when you need to call your worker send him ONLY those attributes, don't send whole ActiveRecord Models.

    Here you have fixed worker:

    class LazyDoer
      include Sidekiq::Worker
      sidekiq_options retry: false
      
      def perform(question_owner_email,current_user_name,answer_contents,question_title)
        UserMailer.send_email(question_owner_email,current_user_name,answer_contents,question_title).deliver
      end
    end
    

    Fixed controller (MOST IMPORTANT):

    class AnswersController < ApplicationController
      before_action :authenticate_user!
      before_action :set_question, except: [:adding_likes,:accept]
    
      def create
        @answer = Answer.new(answer_params)
        @answer.user_id = current_user.id
        @answer.question_id = @question.id
        @question_owner = User.find(@question.user_id)
        
        if @answer.save
          LazyDoer.perform_async(@question_owner.email,current_user.name,@answer.contents,@question.title)
          #DLA MAILERA BEZ SIDEKIQ UserMailer.send_email(@question_owner,current_user,@answer,@question).deliver
          redirect_to question_path(@question), notice: "Answer was successfully created."
        else    
          #redirect_to question_path(@question), alert: "There was an error when adding answer."
          render(:template => "questions/show", alert: "There was an error when adding answer.")
        end
      end
    end
    

    Fixed user mailer:

    class UserMailer < ActionMailer::Base
      default from: "someemail"
      
      def send_email(question_owner_email,cur_user_name,answer_contents,question_title)
        @question_owner_email = question_owner_email
        @cur_user_name = cur_user_name
        @answer_contents = answer_contents
        @question_title = question_title
        mail(to: @question_owner_email, subject: "Answer added to your question:")
      end
    end
    

    Fixed email view (with use of slim template language instead of erb):

    doctype html
    html
        head
            meta content="text/html; charset=UTF-8" http-equiv="Content-Type"
        body
            h1 Your question #{@question_title} has been answered
            p   
                |
                    Answered by #{@cur_user_name}
                    <br />
                    The answer content is:
                    <br />
                    #{@answer_contents}
            
            p Accept or like the answer if it was useful for you.