如何获取嵌套属性的user_id用于通知?

时间:2016-10-17 11:43:25

标签: ruby-on-rails ruby notifications

如何发送带有创建决斗的姓名的通知?

enter image description here

现在两个dueler's都会收到通知,但通知中列出了自己的名称notification.dueler.user.name

模型

class Dueler < ActiveRecord::Base
  belongs_to :user
  belongs_to :challenge
  belongs_to :duel
  after_save :create_notification
  has_many :notifications

private
  def create_notification
    notifications.create(
      duel:    duel,
      user:    user, # I'm not sure if or how to rewrite this line so that the notification shows the user's name  who created the duel.      
      read:    false
    )
  end
end

通知

<%= link_to notification.dueler.user.name, user_path(notification.dueler.user_id) %> challenged you to a <%= link_to "duel", notification_duel_request_path(notification, notification.duel_id) %>

rails c

Notification.find(223)
 id: 223,
 user_id: 2, # This is who created the duel
 duel_id: 112,
 dueler_id: 181>
Notification.last
 id: 224,
 user_id: 114,
 duel_id: 112,
 dueler_id: 182>
Duel.last
 id: 112,
Dueler.find(181)
 id: 181,
 user_id: 2,
 challenge_id: 302,
 duel_id: 112,
Dueler.last
 id: 182,
 user_id: 114,
 challenge_id: 410,
 duel_id: 112,

1 个答案:

答案 0 :(得分:1)

您的Dueler课程是否真的要负责通知其他参与者? - 我不这么认为。在这种情况下,它只是一个m2m的连接模型。

此外,您的模型不知道会话 - 因此它不知道两个应答者中的哪一个实际创建了决斗。虽然您可以查看duel.user,但您可能正在制作鸡蛋与鸡蛋的情景。

相反,你可能想要选择一个更理智和解耦的解决方案,而不是将所有模型变成浓稠的汤。

class User < ActiveRecord::Base
  has_many :duelers
  has_many :duels, through: :duelers
  has_many :notifications
end

class Duel < ActiveRecord::Base
  has_many :duelers
  has_many :users, through: :duelers
end

class Dueler < ActiveRecord::Base
  enum role: [:defendant, :challenger]
  belongs_to :user
  belongs_to :duel
end

class Notification < ActiveRecord::Base
  enum status: [:unread, :read, :trashed]
  belongs_to :user
end

此处,您的通知类不必了解DuelerDuel或任何游戏逻辑。它只需要收件人和消息并完成其工作。

假设您在users#show页面上有一个离散的表单来创建决斗:

<%= button_to( "Challenge to a duel", user_duels_path(user_id: @user.to_param), method: :post) %>

采取以下行动:

class Users::DuelsController < ApplicationController

  before_action :set_user

  # POST users/:user_id/duels
  def create
    @duel = Duel.new do |d|
      d.duelers.new(user: current_user, role: :challenger)
      d.duelers.new(user: @user, role: :defendant)
    end

    if @duel.save
      DuelNotifier.new(current_user, @user).send
      redirect_to @duel
    else
      redirect_to @user, alert: 'Duel could not be created'
    end
  end

  private 

  def set_user
    @user = User.find(params[:user_id])
  end
end

在应用程序通知中创建内容时,您不希望在模型层上执行此操作:

  • 模型不支持会话。
  • 回调很复杂,在被解雇时很难进行规范。
  • 该死的很难测试。
  • 模型不适合处理本地化问题。
  • 模型回调不能委托给作业
  • 模型无法向ActionCable广播,因为它们不支持会话。

而是从邮寄者的工作方式中获取一些线索:

# app/notifiers/duel_notifier.rb
class DuelNotifier 
  attr_accessor :challenger, :defendant

  def initialize(challenger, defendant)
    @challenger, @defendant = challenger, defendant
  end

  def send
    [].tap do |a|
      a.push( @challenger.notifications.create!(
        message: "You have challenged to #{defendant.name} to a duel."
      ) )
      a.push( @defendant.notifications.create!(
        message: "#{challenger.name} has challenged you to a duel."
      ) )
    end
  end
end

这只是一个完成一项工作的简单课程。我们可以通过简单直接的方式对此进行测试:

RSpec.describe DuelNotifier do
  let(:challenger) { create(:user) }
  let(:defendant) { create(:user) }
  let(:notifier) { described_class.new(challenger, defendant) } 

  it "creates two notifications" do
    expect { notifier.send }.to change(Notification, :count).by(+2)
  end

  it "creates the correct notification for the challenger" do
     notifier.send
     expect(challenger.notifications.last.message).to eq(
       "You have challenged to #{defendant.name} to a duel."
     )
  end

  it "creates the correct notification for the defendant" do
     notifier.send
     expect(defendant.notifications.last.message).to eq(
       "#{challenger.name} has challenged you to a duel."
     )
  end
end