Rails 应用未通过 ActionCable 显示通知
Rails app not showing notifications through ActionCable
我有一个 todo-list rails 应用程序,允许一个用户将其他用户 public 列表标记为他们的最爱。这里的目标是:当列表的所有者创建一个新的 todo-item 时,将同一个列表标记为他们最喜欢的用户应该在不刷新页面的情况下实时收到通知。
app/assets/javascripts/channels/notification.js
App.notifications = App.cable.subscriptions.create("NotificationsChannel", {
connected: function() {
// Called when the subscription is ready for use on the server
},
disconnected: function() {
// Called when the subscription has been terminated by the server
},
received: function(data) {
// Called when there's incoming data on the websocket for this channel
$("#notifications").prepend(data.html);
}
});
我的 div 在 app/views/layouts/application.html.erb
<body>
<div class="container">
<div id="notifications">
</div>
<header>
<div class="header_inner">
<nav style="text-align: right;">
<% if user_signed_in? %>
<%= link_to 'New Todo List', new_todo_list_path %> |
<%= link_to "Sign out", destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to "Log in", new_user_session_path %>
<% end %>
</nav>
</div>
</header>
<%= yield %>
</div>
</body>
app/models/notification.rb
class Notification < ApplicationRecord
after_create_commit { NotificationRelayJob.perform_later(self) }
belongs_to :user
belongs_to :recipient, class_name: "User"
belongs_to :notifiable, polymorphic: true
end
app/models/todo_list.rb
class TodoList < ApplicationRecord
belongs_to :user
has_many :todo_items, dependent: :destroy
has_many :favorites
has_many :users, through: :favorites
validates :title, presence: true
validates :description, length: { maximum: 50 }
def public?
self.public
end
end
app/views/notifications/todo_lists/_created.html.erb
<div><%= notification.user.email %> <%= notification.action %> new item!</div>
app/channels/notifications_channel.rb
class NotificationsChannel < ApplicationCable::Channel
def subscribed
stream_from "notifications:#{current_user.id}"
end
def unsubscribed
stop_all_streams
end
end
app/jobs/notification_relay_job.rb
class NotificationRelayJob < ApplicationJob
queue_as :default
def perform(notification)
html = ApplicationController.render partial: "notifications/#{notification.notifiable_type.underscore.pluralize}/#{notification.action}", locals: {notification: notification}, formats: [:html]
ActionCable.server.broadcast "notifications:#{notification.current_user}", html: html
end
app/controllers/todo_items_controller.rb
class TodoItemsController < ApplicationController
before_action :set_todo_list
before_action :set_todo_item, except: [:create]
def create
@todo_item = @todo_list.todo_items.new(todo_item_params)
if @todo_item.save
@todo_list.users.each do |user|
@noti = Notification.create(recipient: user, user: User.last, action: "create", notifiable: @todo_list)
end
redirect_to @todo_list, notice: 'Todo item added!'
else
redirect_to @todo_list, alert: 'Please some content in the new todo item'
end
end
...
development.log
web_1 | Finished "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:28 +0000
web_1 | NotificationsChannel stopped streaming from notifications:2
web_1 | Started GET "/cable" for 172.23.0.1 at 2018-04-17 00:58:28 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Started GET "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:28 +0000
web_1 | Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
web_1 | User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | Registered connection (Z2lkOi8vZmFudGFzdGljLXN5c3RlbS9Vc2VyLzI)
web_1 | NotificationsChannel is transmitting the subscription confirmation
web_1 | NotificationsChannel is streaming from notifications:2
web_1 | Started POST "/todo_lists/3/todo_items" for 172.23.0.1 at 2018-04-17 00:58:31 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Processing by TodoItemsController#create as HTML
web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"WfKWJNbd0HiPj64/51vXsj7O/mJ7nF/U6tvwP5OQMsTLE0cgUQHXYq7D08bB3RUxuxqVTvcD6uGzWcNJqkxGQA==", "todo_item"=>{"content"=>"asd"}, "commit"=>"Create Todo item", "todo_list_id"=>"3"}
web_1 | TodoList Load (0.3ms) SELECT "todo_lists".* FROM "todo_lists" WHERE "todo_lists"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
web_1 | (0.1ms) begin transaction
web_1 | SQL (2.2ms) INSERT INTO "todo_items" ("content", "todo_list_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "asd"], ["todo_list_id", 3], ["created_at", "2018-04-17 00:58:31.708390"], ["updated_at", "2018-04-17 00:58:31.708390"]]
web_1 | (157.7ms) commit transaction
web_1 | User Load (0.4ms) SELECT "users".* FROM "users" INNER JOIN "favorites" ON "users"."id" = "favorites"."user_id" WHERE "favorites"."todo_list_id" = ? [["todo_list_id", 3]]
web_1 | User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
web_1 | (0.1ms) begin transaction
web_1 | SQL (1.5ms) INSERT INTO "notifications" ("user_id", "recipient_id", "action", "notifiable_type", "notifiable_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["user_id", 3], ["recipient_id", 1], ["action", "followed"], ["notifiable_type", "TodoList"], ["notifiable_id", 3], ["created_at", "2018-04-17 00:58:31.915523"], ["updated_at", "2018-04-17 00:58:31.915523"]]
web_1 | (97.2ms) commit transaction
web_1 | [ActiveJob] Enqueued NotificationRelayJob (Job ID: a32e8a9d-d9fd-4a1c-90c1-891c48709b4b) to Async(default) with arguments: #<GlobalID:0x00007f651054f610 @uri=#<URI::GID gid://fantastic-system/Notification/72>>
web_1 | Redirected to http://localhost:3000/todo_lists/3
web_1 | Notification Load (0.6ms) SELECT "notifications".* FROM "notifications" WHERE "notifications"."id" = ? LIMIT ? [["id", 72], ["LIMIT", 1]]
web_1 | Completed 302 Found in 359ms (ActiveRecord: 261.1ms)
web_1 |
web_1 |
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] Performing NotificationRelayJob (Job ID: a32e8a9d-d9fd-4a1c-90c1-891c48709b4b) from Async(default) with arguments: #<GlobalID:0x00007f64cc04ab18 @uri=#<URI::GID gid://fantastic-system/Notification/72>>
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] Rendered notifications/todo_lists/_followed.html.erb (4.9ms)
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] [ActionCable] Broadcasting to notifications:3: {:html=>"<div>a@a.com.br followed you!</div>\n"}
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] Performed NotificationRelayJob (Job ID: a32e8a9d-d9fd-4a1c-90c1-891c48709b4b) from Async(default) in 7.58ms
web_1 | Started GET "/todo_lists/3" for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Processing by TodoListsController#show as HTML
web_1 | Parameters: {"id"=>"3"}
web_1 | TodoList Load (0.2ms) SELECT "todo_lists".* FROM "todo_lists" WHERE "todo_lists"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
web_1 | Rendering todo_lists/show.html.erb within layouts/application
web_1 | TodoItem Load (0.2ms) SELECT "todo_items".* FROM "todo_items" WHERE "todo_items"."todo_list_id" = ? [["todo_list_id", 3]]
web_1 | User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | Rendered collection of todo_items/_todo_item.html.erb [4 times] (7.3ms)
web_1 | Rendered todo_items/_form.html.erb (2.8ms)
web_1 | Rendered todo_lists/show.html.erb within layouts/application (16.8ms)
web_1 | Completed 200 OK in 47ms (Views: 44.4ms | ActiveRecord: 1.1ms)
web_1 |
web_1 |
web_1 | Finished "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | NotificationsChannel stopped streaming from notifications:2
web_1 | Started GET "/cable" for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Started GET "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
web_1 | User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | Registered connection (Z2lkOi8vZmFudGFzdGljLXN5c3RlbS9Vc2VyLzI)
web_1 | NotificationsChannel is transmitting the subscription confirmation
web_1 | NotificationsChannel is streaming from notifications:2
我可以通过“@todo_list.users”看到所有收藏列表的用户(这将收到通知),控制台显示 NotificationChannel 正在传输和流式传输,但是当我创建时没有任何反应一个新的待办事项。我已经搜索了一段时间,但发现我不太了解 ActionCable :(.
非常感谢任何帮助。
最初,问题是您向谁发送通知。您指定了 notifications.current_user,它必须发送到 notification.recipient_id
在app/jobs/notification_relay_job.rb
更改代码
ActionCable.server.broadcast "notifications:#{notification.current_user}", html: html
到
ActionCable.server.broadcast "notifications:#{notification.recipient_id}", html: html
执行此操作后,登录发件人和收件人用户,创建通知并再次测试。
我有一个 todo-list rails 应用程序,允许一个用户将其他用户 public 列表标记为他们的最爱。这里的目标是:当列表的所有者创建一个新的 todo-item 时,将同一个列表标记为他们最喜欢的用户应该在不刷新页面的情况下实时收到通知。
app/assets/javascripts/channels/notification.js
App.notifications = App.cable.subscriptions.create("NotificationsChannel", {
connected: function() {
// Called when the subscription is ready for use on the server
},
disconnected: function() {
// Called when the subscription has been terminated by the server
},
received: function(data) {
// Called when there's incoming data on the websocket for this channel
$("#notifications").prepend(data.html);
}
});
我的 div 在 app/views/layouts/application.html.erb
<body>
<div class="container">
<div id="notifications">
</div>
<header>
<div class="header_inner">
<nav style="text-align: right;">
<% if user_signed_in? %>
<%= link_to 'New Todo List', new_todo_list_path %> |
<%= link_to "Sign out", destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to "Log in", new_user_session_path %>
<% end %>
</nav>
</div>
</header>
<%= yield %>
</div>
</body>
app/models/notification.rb
class Notification < ApplicationRecord
after_create_commit { NotificationRelayJob.perform_later(self) }
belongs_to :user
belongs_to :recipient, class_name: "User"
belongs_to :notifiable, polymorphic: true
end
app/models/todo_list.rb
class TodoList < ApplicationRecord
belongs_to :user
has_many :todo_items, dependent: :destroy
has_many :favorites
has_many :users, through: :favorites
validates :title, presence: true
validates :description, length: { maximum: 50 }
def public?
self.public
end
end
app/views/notifications/todo_lists/_created.html.erb
<div><%= notification.user.email %> <%= notification.action %> new item!</div>
app/channels/notifications_channel.rb
class NotificationsChannel < ApplicationCable::Channel
def subscribed
stream_from "notifications:#{current_user.id}"
end
def unsubscribed
stop_all_streams
end
end
app/jobs/notification_relay_job.rb
class NotificationRelayJob < ApplicationJob
queue_as :default
def perform(notification)
html = ApplicationController.render partial: "notifications/#{notification.notifiable_type.underscore.pluralize}/#{notification.action}", locals: {notification: notification}, formats: [:html]
ActionCable.server.broadcast "notifications:#{notification.current_user}", html: html
end
app/controllers/todo_items_controller.rb
class TodoItemsController < ApplicationController
before_action :set_todo_list
before_action :set_todo_item, except: [:create]
def create
@todo_item = @todo_list.todo_items.new(todo_item_params)
if @todo_item.save
@todo_list.users.each do |user|
@noti = Notification.create(recipient: user, user: User.last, action: "create", notifiable: @todo_list)
end
redirect_to @todo_list, notice: 'Todo item added!'
else
redirect_to @todo_list, alert: 'Please some content in the new todo item'
end
end
...
development.log
web_1 | Finished "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:28 +0000
web_1 | NotificationsChannel stopped streaming from notifications:2
web_1 | Started GET "/cable" for 172.23.0.1 at 2018-04-17 00:58:28 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Started GET "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:28 +0000
web_1 | Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
web_1 | User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | Registered connection (Z2lkOi8vZmFudGFzdGljLXN5c3RlbS9Vc2VyLzI)
web_1 | NotificationsChannel is transmitting the subscription confirmation
web_1 | NotificationsChannel is streaming from notifications:2
web_1 | Started POST "/todo_lists/3/todo_items" for 172.23.0.1 at 2018-04-17 00:58:31 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Processing by TodoItemsController#create as HTML
web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"WfKWJNbd0HiPj64/51vXsj7O/mJ7nF/U6tvwP5OQMsTLE0cgUQHXYq7D08bB3RUxuxqVTvcD6uGzWcNJqkxGQA==", "todo_item"=>{"content"=>"asd"}, "commit"=>"Create Todo item", "todo_list_id"=>"3"}
web_1 | TodoList Load (0.3ms) SELECT "todo_lists".* FROM "todo_lists" WHERE "todo_lists"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
web_1 | (0.1ms) begin transaction
web_1 | SQL (2.2ms) INSERT INTO "todo_items" ("content", "todo_list_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "asd"], ["todo_list_id", 3], ["created_at", "2018-04-17 00:58:31.708390"], ["updated_at", "2018-04-17 00:58:31.708390"]]
web_1 | (157.7ms) commit transaction
web_1 | User Load (0.4ms) SELECT "users".* FROM "users" INNER JOIN "favorites" ON "users"."id" = "favorites"."user_id" WHERE "favorites"."todo_list_id" = ? [["todo_list_id", 3]]
web_1 | User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
web_1 | (0.1ms) begin transaction
web_1 | SQL (1.5ms) INSERT INTO "notifications" ("user_id", "recipient_id", "action", "notifiable_type", "notifiable_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["user_id", 3], ["recipient_id", 1], ["action", "followed"], ["notifiable_type", "TodoList"], ["notifiable_id", 3], ["created_at", "2018-04-17 00:58:31.915523"], ["updated_at", "2018-04-17 00:58:31.915523"]]
web_1 | (97.2ms) commit transaction
web_1 | [ActiveJob] Enqueued NotificationRelayJob (Job ID: a32e8a9d-d9fd-4a1c-90c1-891c48709b4b) to Async(default) with arguments: #<GlobalID:0x00007f651054f610 @uri=#<URI::GID gid://fantastic-system/Notification/72>>
web_1 | Redirected to http://localhost:3000/todo_lists/3
web_1 | Notification Load (0.6ms) SELECT "notifications".* FROM "notifications" WHERE "notifications"."id" = ? LIMIT ? [["id", 72], ["LIMIT", 1]]
web_1 | Completed 302 Found in 359ms (ActiveRecord: 261.1ms)
web_1 |
web_1 |
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] Performing NotificationRelayJob (Job ID: a32e8a9d-d9fd-4a1c-90c1-891c48709b4b) from Async(default) with arguments: #<GlobalID:0x00007f64cc04ab18 @uri=#<URI::GID gid://fantastic-system/Notification/72>>
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] Rendered notifications/todo_lists/_followed.html.erb (4.9ms)
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] [ActionCable] Broadcasting to notifications:3: {:html=>"<div>a@a.com.br followed you!</div>\n"}
web_1 | [ActiveJob] [NotificationRelayJob] [a32e8a9d-d9fd-4a1c-90c1-891c48709b4b] Performed NotificationRelayJob (Job ID: a32e8a9d-d9fd-4a1c-90c1-891c48709b4b) from Async(default) in 7.58ms
web_1 | Started GET "/todo_lists/3" for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Processing by TodoListsController#show as HTML
web_1 | Parameters: {"id"=>"3"}
web_1 | TodoList Load (0.2ms) SELECT "todo_lists".* FROM "todo_lists" WHERE "todo_lists"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
web_1 | Rendering todo_lists/show.html.erb within layouts/application
web_1 | TodoItem Load (0.2ms) SELECT "todo_items".* FROM "todo_items" WHERE "todo_items"."todo_list_id" = ? [["todo_list_id", 3]]
web_1 | User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | Rendered collection of todo_items/_todo_item.html.erb [4 times] (7.3ms)
web_1 | Rendered todo_items/_form.html.erb (2.8ms)
web_1 | Rendered todo_lists/show.html.erb within layouts/application (16.8ms)
web_1 | Completed 200 OK in 47ms (Views: 44.4ms | ActiveRecord: 1.1ms)
web_1 |
web_1 |
web_1 | Finished "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | NotificationsChannel stopped streaming from notifications:2
web_1 | Started GET "/cable" for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | Cannot render console from 172.23.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1 | Started GET "/cable/" [WebSocket] for 172.23.0.1 at 2018-04-17 00:58:32 +0000
web_1 | Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
web_1 | User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
web_1 | Registered connection (Z2lkOi8vZmFudGFzdGljLXN5c3RlbS9Vc2VyLzI)
web_1 | NotificationsChannel is transmitting the subscription confirmation
web_1 | NotificationsChannel is streaming from notifications:2
我可以通过“@todo_list.users”看到所有收藏列表的用户(这将收到通知),控制台显示 NotificationChannel 正在传输和流式传输,但是当我创建时没有任何反应一个新的待办事项。我已经搜索了一段时间,但发现我不太了解 ActionCable :(.
非常感谢任何帮助。
最初,问题是您向谁发送通知。您指定了 notifications.current_user,它必须发送到 notification.recipient_id
在app/jobs/notification_relay_job.rb
更改代码
ActionCable.server.broadcast "notifications:#{notification.current_user}", html: html
到
ActionCable.server.broadcast "notifications:#{notification.recipient_id}", html: html
执行此操作后,登录发件人和收件人用户,创建通知并再次测试。