アクションケーブルによるビューのレンダリング


今週、私はアクションケーブルでWebSocketsの上で部分をレンダリングすることに取り組みました.私は私の認証と認可のライブラリに接続されている問題に遭遇しました.
私のアプリでは、アクションケーブルは、現在のユーザーを識別するために、次のコードを使用します.何か同様の工夫を持つアクションケーブル認証について多くの記事で見つけることができます.
# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = env['warden'].user || reject_unauthorized_connection
    end
  end
end
アクションケーブルでビューをレンダリングするときには、ほとんどの場合、単に現在のユーザーをローカル変数として渡すことができました.
# app/channels/my_channel.rb
class MyChannel < ApplicationCable::Channel
  def subscribed
    stream_for current_user
  end
end
<!-- partial_with_local_variable.html.erb -->
Hello <%= current_user.name %>
# any ruby file
MyChannel.broadcast_to(
  current_user,
  ApplicationController.render(
    partial: 'partial_with_local_variable',
    locals: { current_user: current_user }
  )
)

# => "Hello Brian"
しかし、Cancancanがかかわるとき、ものは毛むくじゃらになります.
<!-- partial_with_authorization.html.erb -->
<% if can?(:do_something, to_this_object) %>
  Here is the text I want to render.
<% end %>
# Any ruby file
MyChannel.broadcast_to(
  current_user,
  ApplicationController.render(partial: 'partial_with_authorization')
)

# => 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.
その問題はcan? メソッド.ビューで呼び出されると、そのメソッドの呼び出しチェーンにはcurrent_user ビューをレンダリングするコントローラのメソッド.このメソッドはコントローラに呼び出されますcurrent_user ビュー内の変数はこのエラーを防止しません.
The current_user メソッドは、変数request.env['warden'] HTTPリクエストがWardenミドルウェアを通過した結果として設定します.アクションケーブル通信はHTTPの代わりにWebSocketを介して行われるので、このミドルウェアは使用されず、変数は設定されず、ブロードキャストは失敗する.
あなたは、このポストのトップの近くに気づいたかもしれませんenv['warden'] で接続します.rbアクションケーブル接続はHTTPリクエストで確立されているので、Wardenミドルウェアを使用し、必要な環境変数がその時存在します.解決策は、そのオブジェクトを接続オブジェクトの属性として格納し、その後、WebSocketの後の通信に必要なときにレンダラに渡すことです.これを行うには、非動作ケーブル固有の記述がありますthis article , しかし、あなたは下記の私の好ましいバージョンを見ることができます.
# app/channels/application_cable/connection.rb
# updated from the version at the top of this post.
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user
    attr_reader :warden

    def connect
      self.current_user = env['warden'].user || reject_unauthorized_connection
      @warden = env['warden'] if current_user
    end
  end
end

# app/channels/my_channel.rb
# also updated from the earlier example
class MyChannel < ApplicationCable::Channel
  def subscribed
    stream_for current_user
  end

  # This method will return ApplicationController.renderer with our Warden::Proxy instance added to the default environment hash.
  def renderer
    ApplicationController.renderer.tap do |default_renderer|
      default_env = default_renderer.instance_variable_get(:@env)
      env_with_warden = default_env.merge('warden' => connection.warden)
      default_renderer.instance_variable_set(:@env, env_with_warden)
    end
  end
end

# Any ruby file
MyChannel.broadcast_to(
  current_user,
  MyChannel.renderer.render(partial: 'partial_with_authorization')
)

# => Here is the text I want to render.