I have a rails app using sidekiq w/ redis for mailers. I have 2 workers, but for one of them I get wrong number of arguments error (ArgumentError: wrong number of arguments (5 for 2) ) and I don't know why since the other one, which is basically the same, works perfectly. Here is the code/explanation for both: Contactmailer w/ Postmanworker is the working one which sends out an email when sby submits a contact form; Taskmailer w/ Taskcreatorworker gives the error, which sends out an email when a task gets created. I've already tried with different number of arguments, as I see I have 6 args at the moment but still gives the 5 for 2 error => Prior to sidekiq, taskmailer worked as well. For first I tried to pass only 2 args @task.id and @current_user and I could call all the 6 variables that can be found in task_created.html.erb. Since then I've tried different kind and number of args. As you see below, at the moment I try to give all the 6 variables in controller and mailer as well exactly as they are called. I also show the user.rb since model is a bit tricky.
Since the contact mailer works perfectly both in dev/prod env, I likely made a mistake in the code below and it's not a server/installation problem.
tasks_controller.rb
def create
@user = current_user
@task = Task.new(task_params))
if @task.save
h = JSON.generate( {'task_assigner_first_name' => @current_user.profile.first_name,
'task_assigner_last_name' => @current_user.profile.last_name,
'task_executor_first_name' => @task.executor.profile.first_name,
'task_executor_email' => @task.executor.email,
'task_executor_id' => @task.executor_id,
'task_id' => @task.id
} )
TaskcreatorWorker.perform_async(h, 5)
#TaskMailer.task_created(current_user, @task).deliver_later
flash[:success] = "Task saved!"
redirect_to user_tasks_path(current_user)
else
render action: :new
end
end
private
def task_params
params.require(:task).permit(:executor_id, :name, :content, :deadline).merge(assigner_id: current_user.id)
end
contacts_controller.rb
def create
@contact = Contact.new(contact_params)
if @contact.save
h = JSON.generate({ 'name' => params[:contact][:name],
'email' => params[:contact][:email],
'comment' => params[:contact][:comment] })
PostmanWorker.perform_async(h, 5)
#ContactMailer.contact_email(name, email, comment).deliver_later
flash[:success] = "Message sent."
redirect_to new_contact_path
else
flash[:danger] = "Error occured."
render action: :new
end
end
private
def contact_params
params.require(:contact).permit(:name, :email, :comment)
end
taskcreator_worker.rb
class TaskcreatorWorker
include Sidekiq::Worker
def perform(h, count)
h = JSON.load(h)
TaskMailer.task_created(h['task_assigner_first_name'], h['task_assigner_last_name'], h['task_executor_first_name'], h['task_executor_email'], h['task_executor_id'], h['task_id']).deliver_later
end
end
postman_worker.rb
class PostmanWorker
include Sidekiq::Worker
def perform(h, count)
h = JSON.load(h)
ContactMailer.contact_email(h['name'],h['email'],h['comment']).deliver_later
end
end
task_mailer.rb
class TaskMailer < ActionMailer::Base
def task_created(task_assigner_first_name, task_assigner_last_name, task_executor_first_name, task_executor_email, task_executor_id, task_id)
@task.assigner.profile.first_name = task_assigner_first_name
@task.assigner.profile.last_name = task_assigner_last_name
@task.executor.profile.first_name = task_executor_first_name
@task.executor.email = task_executor_email
@task.executor_id = task_executor_id
@task.id = task_id
mail(from: '[email protected]',
to: "#{task.executor.email}",
subject: "[Faskyn] New task/favor from #{task.assigner.profile.first_name} #{task.assigner.profile.last_name}"
)
end
end
contact_mailer.rb
class ContactMailer < ActionMailer::Base
default to: '[email protected]'
def contact_email(name, email, content)
@name = name
@email = email
@content = content
mail(from: email, subject: 'Contact form message')
end
end
task_created.html.erb
<p>Hi <%= @task.executor.profile.first_name%>,</p>
<p><%= @current_user.profile.first_name %> <%= @current_user.profile.last_name %> just sent you a new task/favor.</p>
<p>You can check it out <%= link_to "here", user_task_url(@task.executor_id, @task.id) %>.</p>
<p>Cheers,<br />Faskyn Team</p>
contact_email.html.erb
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>You have received a message from the site's contact form, from <%= "#{ @name }, #{ @email}." %></p>
<p><%= @comment %></p>
</body>
</html>
user.rb
has_one :profile, dependent: :destroy
has_many :assigned_tasks, class_name: "Task", foreign_key: "assigner_id", dependent: :destroy
has_many :executed_tasks, class_name: "Task", foreign_key: "executor_id", dependent: :destroy
error message from sidekiq log:
2015-09-28T12:48:33.600Z 20264 TID-outp8berw TaskcreatorWorker JID-a0816ab8d1881e81c58569d2 INFO: start
2015-09-28T12:48:33.633Z 20264 TID-outp8berw TaskcreatorWorker JID-a0816ab8d1881e81c58569d2 INFO: fail: 0.033 sec
2015-09-28T12:48:33.635Z 20264 TID-outp8berw WARN: {"class"=>"TaskcreatorWorker", "args"=>["{\"task_assigner_first_name\":\"Szilard\",\"task_assigner_last_name\":\"Hungarian\",\"task_executor_first_name\":\"Andrew\",\"task_executor_email\":\"[email protected]\",\"task_executor_id\":3,\"task_id\":82}", 5], "retry"=>true, "queue"=>"default", "jid"=>"a0816ab8d1881e81c58569d2", "created_at"=>1443439028.7889678, "enqueued_at"=>1443444513.5961752, "error_message"=>"wrong number of arguments (5 for 2)", "error_class"=>"ArgumentError", "failed_at"=>1443439028.8198888, "retry_count"=>8, "retried_at"=>1443444513.632468}
2015-09-28T12:48:33.636Z 20264 TID-outp8berw WARN: ArgumentError: wrong number of arguments (5 for 2)
2015-09-28T12:48:33.636Z 20264 TID-outp8berw WARN: /Users/Silo/Desktop/ruby_on_rails/faskyn/app/mailers/task_mailer.rb:3:in `task_created'
2015-09-28T16:17:07.015Z 23136 TID-ow4vpw9ik TaskcreatorWorker JID-6cb35b9b42bebdbf128746c0 INFO: start
2015-09-28T16:17:08.178Z 23136 TID-ow4vpw9ik TaskcreatorWorker JID-6cb35b9b42bebdbf128746c0 INFO: fail: 1.163 sec
2015-09-28T16:17:08.180Z 23136 TID-ow4vpw9ik WARN: {"class"=>"TaskcreatorWorker", "args"=>["{\"task_assigner_first_name\":\"Szilard\",\"task_assigner_last_name\":\"Hungarian\",\"task_executor_first_name\":\"Peter\",\"task_executor_email\":\"[email protected]\",\"task_executor_id\":2,\"task_id\":83}", 5], "retry"=>true, "queue"=>"default", "jid"=>"6cb35b9b42bebdbf128746c0", "created_at"=>1443456758.3800051, "enqueued_at"=>1443457026.97385, "error_message"=>"undefined method `assigner' for nil:NilClass", "error_class"=>"NoMethodError", "failed_at"=>1443456758.398172, "retry_count"=>3, "retried_at"=>1443457028.177601}
2015-09-28T16:17:08.180Z 23136 TID-ow4vpw9ik WARN: NoMethodError: undefined method `assigner' for nil:NilClass
2015-09-28T16:17:08.180Z 23136 TID-ow4vpw9ik WARN: /Users/Silo/Desktop/ruby_on_rails/faskyn/app/mailers/task_mailer.rb:4:in `task_created'
You're using a reference to your @task
instance variable that is not set when coming from sidekiq. You need to initialize your @task
variable in the mailer method itself. Depending on the desired outcome, it can be done in several ways.
@task = Task.new
Initialize a task and all the objects it needs (assigner
and executor
) and then give them the values passed in from your worker.
@task = Task.find(task_id)
This is a better solution since then you don't need to pass all of parameters through sidekiq. Your task is already saved in the database, so just pass the id through and use it to find the task. It will have all the required data already.
tasks_controller.rb
def create
@user = current_user
@task = Task.new(task_params)
if @task.save
TaskcreatorWorker.perform_async(@task.id, @user.id)
flash[:success] = "Task saved!"
redirect_to user_tasks_path(current_user)
else
render action: :new
end
end
taskcreator_worker.rb
class TaskcreatorWorker
include Sidekiq::Worker
def perform(task_id, user_id) # Removed count since you weren't using it. Add it back if needed
TaskMailer.task_created(
Task.find(task_id),
User.find(user_id)
).deliver_now # deliver_now since we're already in a sidekiq worker
end
end
task_mailer.rb
class TaskMailer < ActionMailer::Base
def task_created(task, user)
# Initialize variables you use in your view
@task = task
@current_user = user
mail(from: '[email protected]',
to: "#{task.executor.email}",
subject: "[Faskyn] New task/favor from "\
"#{task.assigner.profile.first_name} "\
"#{task.assigner.profile.last_name}"
)
end
end