rails 6.0.3
ruby 2.6.5
Mysql
railsでActionCableを使ったチャットアプリを作成しています。
しかしメッセージを送信したところで、
[ActiveJob] [MessageBroadcastJob] [b9536de0-7add-448c-8a4e-464fa65b1229] Error performing MessageBroadcastJob (Job ID: b9536de0-7add-448c-8a4e-464fa65b1229) from Async(default) in 10.09ms: ActionView::Template::Error (Devise could not find the `Warden::Proxy` instance on your request environment. Make sure that your application is loading Devise and Warden as expected and that the `Warden::Manager` middleware is present in your middleware stack. If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` module to inject the `request.env['warden']` object for you.):
このようなエラーが出てしまいます。
ちなみに、メッセージ自体は保存することができます。
個人的にはjob関係がなにか悪いところがあるかと思いました。
コードを載せておきます。
_message.html.erb
erb
1<div class="message" id="message-<%= message.id %>"> 2 <div class="usernameAndTimeBox"> 3 <% unless message.user_id.nil? %> 4 <%= link_to message.username, user_path(message.user_id),class: "message_username" %> 5 <% else %> 6 <p class="message_username"><%= message.username %></p> 7 <% end %> 8 <p class="agomessage"><%= time_ago_in_words(message.created_at) + "前" %></p> 9 </div> 10 <% if user_signed_in? %> 11 <% if message.login == true %> 12 <%# if message.user_id == current_user.id %> 13 <button id="<%= message.id %>" class="delete_btn">削除</button> 14 <%# end %> 15 <% end %> 16 <% end %> 17 <%= raw Rinku.auto_link simple_format h(message.content), class: "message_content" %> 18 <% unless message.youtube_id.nil? %> 19 <% iframe = content_tag( 20 :iframe, 21 '', 22 width: 560, 23 height: 315, 24 src: "https://www.YouTube.com/embed/#{message.youtube_id}", 25 frameborder: 0, 26 allowfullscreen: true, 27 class: "youtube-container" 28 ) %> 29 <%= content_tag(:div, iframe, class: "youtube-container2") %> 30 <% end %> 31 <% if message.image? %> 32 <%= image_tag message.image.url %> 33 <% end %> 34<!-- <div class="editBox"> 35 <div class="formchat"> 36 <%#= form_with model: message, url: message_path(message) do |f| %> 37 <%#= f.rich_text_area :content %> 38 <%#= f.submit %> 39 <%# end %> 40 </div> 41 </div> --> 42</div>
message_broadcast_job.rb
rb
1class MessageBroadcastJob < ApplicationJob 2 queue_as :default 3 4 def perform(message) 5 ActionCable.server.broadcast "room_channel_#{message.room_id}", message: render_message(message) 6 end 7 8 private 9 def render_message(message) 10 ApplicationController.renderer.render partial: 'messages/message', locals: {message: message} 11 end 12end 13
room_channel.js
js
1import consumer from "./consumer" 2 $(function(){ 3 const chatChannel = consumer.subscriptions.create({channel: "RoomChannel", room: $('#messages').data('room_id') }, { 4 connected() { 5 // Called when the subscription is ready for use on the server 6 }, 7 8 disconnected() { 9 // Called when the subscription has been terminated by the server 10 }, 11 12 received(data) { 13 // 0.01秒たったら下に移動。 14 setTimeout(function(){ 15 var submitscroll = $('#bottomGO').offset().top; 16 $('html, body').scrollTop(submitscroll); 17 },10); 18 return $('#messages').append(data['message']); 19 20 // Called when there's incoming data on the websocket for this channel 21 }, 22 23 speak: function(message) { 24 return this.perform('speak', { 25 message: message 26 }); 27 } 28 }); 29 $(document).on('keypress submit click', '[data-behavior~=room_speaker]', function(event){ 30 31 console.log(event.target.value); 32 function chatgo(){ 33 chatChannel.speak(event.target.value); 34 event.target.value = ''; 35 return event.preventDefault(); 36 } 37 if(event.shiftKey){ 38 if(event.keyCode === 13){ 39 if(event.target.value != ''){ 40 chatgo(); 41 }else if(event.target.value == '' || event.target.length <= 1000){ 42 event.preventDefault(); 43 event.target.value = event.target.value; 44 } 45 } 46 } 47 $('#submit').click(function(){ 48 if(event.target.value != ''){ 49 chatgo(); 50 }else if(event.target.value == '' || event.target.length <= 1000){ 51 event.preventDefault(); 52 } 53 }); 54 }); 55 });
message.rb
rb
1class Message < ApplicationRecord 2 belongs_to :user, optional: true 3 belongs_to :room 4 # has_rich_text :content 5 validates :content, length: {maximum: 1000 } 6 after_create_commit { MessageBroadcastJob.perform_later self } 7 mount_uploader :image, ImageUploader 8 9 validate :image_size 10 11 private 12 13 def image_size 14 if image.size > 5.megabytes 15 errors.add(:image, "容量が大きすぎます。5MB未満のファイルにしてください。") 16 end 17 end 18end 19
次はメッセージを削除するところです。
room_delete_channel.js
js
1import consumer from "./consumer" 2$(function(){ 3 const deleteChannel = consumer.subscriptions.create({channel: "RoomDeleteChannel", room: $('#messages').data('room_id') }, { 4 connected() { 5 // Called when the subscription is ready for use on the server 6 7 }, 8 9 disconnected() { 10 // Called when the subscription has been terminated by the server 11 }, 12 13 received(data) { 14 $('#message-' + data['id']).remove(); 15 // Called when there's incoming data on the websocket for this channel 16 }, 17 18 delete: function(id) { 19 console.log("idGOGOGO") 20 return this.perform('delete', { 21 id: id 22 }); 23 } 24 }); 25 $(document).on('click', '.delete_btn', function(event){ 26 deleteChannel.delete(event.target.id) 27 // return event.preventDefault(); 28 }); 29});
room_delete_channel.rb
ruby
1class RoomDeleteChannel < ApplicationCable::Channel 2 def subscribed 3 stream_from "room_delete_channel" 4 end 5 6 def unsubscribed 7 # Any cleanup needed when channel is unsubscribed 8 end 9 10 def delete(data) 11 ActionCable.server.broadcast 'room_delete_channel', id: data['id'] 12 message = Message.find_by(id: data['id'], room_id: params['room'],user_id: current_user.id,login: true) 13 message.destroy 14 end 15end
current_userとipを扱うためのファイル
connection.rb
rb
1module ApplicationCable 2 class Connection < ActionCable::Connection::Base 3 identified_by :current_user 4 5 attr_accessor :ip_addr 6 def connect 7 self.current_user = find_verified_user 8 @ip_addr = request.ip 9 end 10 11 private 12 13 def find_verified_user 14 session_key = cookies.encrypted[Rails.application.config.session_options[:key]] 15 if session_key['warden.user.user.key'].present? 16 verified_id = session_key['warden.user.user.key'][0][0] 17 verified_user = User.find_by(id: verified_id) 18 return reject_unauthorized_connection unless verified_user 19 verified_user 20 else 21 return false 22 end 23 end 24 end 25end 26
追記
ActionCableで、メッセージを保存して、
ActionJobで、再度
ApplicationController.renderer.render
を使って、読み込むのですが、
読み込んだ先のファイルで
user_signed_in? と current_userなどの deiviseが用意しているヘルパーメソッドがエラーで使えなくなりました。
調べたらApplicationController.renderer.renderはenvに直接アクセスすることができないみたいです。
このサイトを参考にして、やってみましたが、うまくいかず
ActionView::MissingTemplate (Missing template messages/message with {:locale=>[:ja], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :mpeg, :mp3, :ogg, :m4a, :webm, :mp4, :otf, :ttf, :woff, :woff2, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip, :gzip], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}. Searched in: * "/home/vagrant/chatapp/app/views" * "/home/vagrant/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/devise-4.7.1/app/views" * "/home/vagrant/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/actiontext-6.0.3/app/views" * "/home/vagrant/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/actionmailbox-6.0.3/app/views" ):
が出てしまいました。。。
修正してみたファイルを記述します。
message_broadcast_job.rb
rb
1class MessageBroadcastJob < ApplicationJob 2 queue_as :default 3 4 def perform(message) 5 ActionCable.server.broadcast "room_channel_#{message.room_id}", message: render_message(message) 6 end 7 private 8 9 def render_message(message) 10 11 # ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message }) 12 ApplicationController.render_with_signed_in_user(message.user_id, 'messages/message', locals: { message: message }) 13 end 14 15end 16
application.controller.rb
rb
1 ... 2 def self.render_with_signed_in_user(user, *args) 3 ActionController::Renderer::RACK_KEY_TRANSLATION['warden'] ||= 'warden' 4 proxy = Warden::Proxy.new({}, Warden::Manager.new({})).tap{|i| i.set_user(user, scope: :user) } 5 renderer = self.renderer.new('warden' => proxy) 6 puts "====================" + renderer.to_s 7 renderer.render(*args) 8 end 9end 10
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。