使用actioncable做的notification(GoRails教学,2课)

GoRails视频系列:

1.   用actioncable建立Notifications

2.  见博客:

3.  非认证/登陆user不能使用actioncable

 


 

用ActionCable 建立 Realtime Notifications

在离线保存的全栈文件/我的练习/ajax网页应用/ajax-template /Cable_Notifictaion分支:

git:https://github.com/chentianwei411/actionCable-use

 

相关连接:

http://www.cnblogs.com/chentianwei/p/9296887.html

https://www.cnblogs.com/chentianwei/p/8690304.html


知识重点:

 

Gemfile:

redis; devise;  puma。

 

config/cable.yml

development:
  adapter: redis
  url: redis://localhost:6379/1

Here bundle install  ->  rails server.

 

安装好Devise,  rails g devise:install, 然后rails g devise User

生成Notifications mode:

rails g model Notification user:references recipient_id: integer action notifiable_type notifiable_id: integer

 

app/models/user.rb

has_many :notifications, as: :recipient

app/models/notification.rb

belongs_to :user
belongs_to :recipient, class_name: "User"
belongs_to :notifiable, polymorphic: true

Now restart:  rails server. ⚠️这里的使用技巧。一个notification里存两个user_id, 使用了别名as

 

新知识:

polymorphic: true

指定关联是一个多态关联。👇是图片,后一张是has_many :through

建立多态关联的要点是链接model需要声明XXX_type:string,和XXX_id:integer 并加上索引.(具体见Guide)

belongs_to ,imageable, polymorphic: true

 

has_many :through

has_many :through

 

 

 


 

app/views/main/index.html.erb

 

<div id="notifications">
</div>

 

⬆️用于播放通知信息的webpage.

 

然后正式开始建立Cable:

第一步:建立频道。
rails g channel Notifications

 

app/channels/notifications_channel.rb

# Be sure to restart your server when you modify this file. 
class NotificationsChannel < ApplicationCable::Channel
  def subscribed
    stream_from "notifications:#{current_user.id}"
  end

  def unsubscribed
    stop_all_streams
  end
end

Now 建立链接,让current_user可以使用cable。

app/channels/application_cable/connection.rb

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verfied_user
    end

    protected

    def find_verfied_user
      if current_user = env['warden'].user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

链接的作用是保证只有登陆的账号才能链接:

官方使用的是:verified_user = User.find_by(id: cookies.signed[:user_id])

 

第2步:设置接收的信息和位置。(80template不会用generator生成,需要自己建立)

app/assets/javascripts/channels/notifications.js

App.notifications = App.cable.subscriptions.create("NotificationsChannel", {
  connected: function() {
  },

  disconnected: function() {
  },

  received: function(data) {
    $("#notifications").prepend(data.html);
  }
});

 

第三步创建broadcast

rails console:输入

ActionCable.server.broadcast "notifications:1", {html: "<div>Hello world</div>"}

这行放哪都行。这是给自己发送信息。

 

实际:给其他用户发送信息。需要创建一条notification:

Notification.create(recipient: User.first, user: User.last, action: "followed", notifiable: User.first)

⚠️ notifiable是多态关联的列的合集,包括type和🆔:

notifiable_type: "User"

notifiable_id: 1 (1是第一个用户的🆔号)

如此,后一个用户会接收前一个用户的信息。

之后做2件事情,一个是建立一个接收的地方partial。另一个是发布广播。

可以把这些工作放到后台,用到puma和activeJob。

 

rails g job NotificationRelay

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.recipient_id}", html: html end end

 

ActionController::Renderer让你可以直接使用render渲染模版,无需再使用controller action。

ApplicationController.renderer.render template: '...'
#简写:
ApplicationController.render template: '...'

#当在一个controller内渲染时,可以使用相同的options:
FooController.render :action, locals: { ... }, assigns: { ... }

#实例:
address = Address.last #一个实例对象。
AddressesController.render(address) #调用view/addresses/_address.html.erb
解释: Render templates with any options
from ActionController::Base#render_to_string

⚠️,partial的地址取了notification记录中的属性,实际未必需要这么麻烦把?

 

 

现在Job会广播渲染的view,这个view会显示在👆的接收之处(第2步)。

在rails console

notification = Notification.first
NotificationRelayJob.perform_later(notification)

Now,成功渲染了

 

什么方法是最好激活job的方法:?添加一个after_commit的hook

app/models/notification.rb

after_commit -> { NotificationRelayJob.perform_later(self) }

 

非认证/登陆user不能使用actioncable

 

在channels/application_cable/connection.rb中已经有拒绝非验证的链接.

如果不是当前用户则拒绝链接。

def find_verfied_user
  if current_user = env['warden'].user
   current_user
  else
   logger.error "An unauthorized connection attempt was rejected"
     raise UnauthorizedError
     reject_unauthorized_connection
   end
end

 

但问题是JavaScript不知道一个用户是否登陆了。在JS端我们需要弄明白如何决定一个用户是否登陆。

最简单的是, 包括一个<meta>在HTML <head>中。

<% if user_signed_in? %>
  <%= tag :meta, name: "current-user", data: {id: current_user.id} %>
<% end %>

这里使用了tag帮助方法。这个tag在head中,JS现在可以决定是否初始化一个ActionCable连接。

当然还需要进行判断,在notifications.js中进行

if ($("meta[name='current-user']").length > 0) {
  // subscription code ommitted
}

改为vallina javscript:

document.getElementByName('current-user').length > 0

也可以写个函数在里面,这样可以自己决定是否验证。

// shouldConnect: function() {
//    return document.getElementsByName("current-user").length > 0;
// }


 

 

不知道如何把vallina javascript转化为coffeescript:

http://js2.coffee/

自动转化工具。

 

posted @ 2018-07-13 21:35  Mr-chen  阅读(455)  评论(0编辑  收藏  举报