Search code examples
ruby-on-railssidekiqactioncable

ActionView::Template::Error: undefined method 'body' when using sidekiq to render ActionCable broadcast


I'm setting up ActionCable to work with my app's messaging, and am stuck at an undefined method error that I'm not understanding.

When a message is created, the ActionCable broadcast gets delegated to a background worker:

model.rb

class Message < ApplicationRecord
 after_create_commit { MessageBroadcastWorker.perform_async self }
 validates :body, presence: true
end

message_broadcast_worker.rb

class MessageBroadcastWorker
 include Sidekiq::Worker

 def perform(message)
  ActionCable.server.broadcast('messages_channel', message: render_message(message))
 end

 private
  def render_message(message)
   ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
  end
end

The messages index page renders all the messages in a conversation:

index.html.erb

<%= render @messages %>

_message.html.erb:

<% cache message do %>
  <div><%= message.body %></div>
<% end %>

But I get this error:

2020-03-22T16:41:01.733Z pid=597 tid=owznmnngh class=MessageBroadcastWorker jid=11d9da030dd20e89cd6c774e INFO: start
2020-03-22T16:41:01.808Z pid=597 tid=owznmnngh class=MessageBroadcastWorker jid=11d9da030dd20e89cd6c774e elapsed=0.075 INFO: fail
2020-03-22T16:41:01.808Z pid=597 tid=owznmnngh WARN: {"context":"Job raised exception","job":{"retry":true,"queue":"default","class":"MessageBroadcastWorker","args":["#<Message:0x00007f9f94227d48>"],"jid":"11d9da030dd20e89cd6c774e","created_at":1584844758.799895,"enqueued_at":1584895261.7334359,"error_message":"undefined method `body' for \"#<Message:0x00007f9f94227d48>\":String","error_class":"ActionView::Template::Error","failed_at":1584844758.877783,"retry_count":11,"retried_at":1584877194.326414},"jobstr":"{\"retry\":true,\"queue\":\"default\",\"class\":\"MessageBroadcastWorker\",\"args\":[\"#<Message:0x00007f9f94227d48>\"],\"jid\":\"11d9da030dd20e89cd6c774e\",\"created_at\":1584844758.799895,\"enqueued_at\":1584895261.7334359,\"error_message\":\"undefined method `body' for \\\"#<Message:0x00007f9f94227d48>\\\":String\",\"error_class\":\"ActionView::Template::Error\",\"failed_at\":1584844758.877783,\"retry_count\":11,\"retried_at\":1584877194.326414}"}
2020-03-22T16:41:01.808Z pid=597 tid=owznmnngh WARN: ActionView::Template::Error: undefined method `body' for "#<Message:0x00007f9f94227d48>":String
2020-03-22T16:41:01.808Z pid=597 tid=owznmnngh WARN: /Users/me/code/project/app/views/messages/_message.html.erb:3:in `block in _app_views_messages__message_html_erb__359591413316599227_70291875480180'

I know what an undefined method error is but I'm having trouble reasoning why I cannot call body on message when I'm passing self to the background worker in the model.


Solution

  • Pass the id to Sidekiq instead of the active record object.

    MessageBroadcastWorker.perform_async id
    

    https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple