在事务中使用 if 语句删除多个对象

Using an if statement within a transaction to delete multiple objects

我是一名初级开发人员,正在尝试编写代码来区分用户拥有的蜂鸣声和其他用户获得授权的蜂鸣声。

蜂鸣声来自用户拥有的设备。所有者可以授权其他用户使用该设备并接收他们自己的蜂鸣警报。这为我们提供了两种不同类型的蜂鸣声:拥有的蜂鸣声和授权的蜂鸣声。我希望授权用户能够同时删除多个蜂鸣声事件,并且只能删除他们自己的蜂鸣声事件,而我希望所有者能够删除多个蜂鸣声和这些蜂鸣声对应的事件。

我正在处理的应用程序非常大,并且在架构上已经设置了此功能,以便所有者仅删除单个哔哔声。

哔声事件通过一个方法与单独的应用程序对话,该应用程序通过 device_broker 方法处理事件删除。

我最后做的是一个带有 if 语句的事务,然后遍历每个迭代器,检查每一次发出的蜂鸣声是否由用户拥有或授权。

我的问题是我的 broker_mock 测试没有收到任何参数。当我在代码上使用调试器时,这些方法似乎都有效,所以我很困惑。在 each 循环中使用 if 语句是个好主意吗?还将它全部包装在一个事务中意味着如果一个小东西坏了,它就不起作用。有更好的选择吗?

感谢您提供的任何智慧或光芒。

  beeps_controller.rb

  def bulk_deletion
      BulkBeepRemover.run!(params[:beep_ids], current_user)
  end

  bulk_beep_remover.rb

  class BulkBeepRemover
    class Unauthorized < StandardError; end;
    attr_reader :beep_ids, :user, :beeps

    attr_accessor :owned_beeps, :unowned_beeps

    def self.run!(beep_ids, user)
      new(beep_ids, user).run!
    end

    def initialize(beep_ids, user)
      @beep_ids = beep_ids
      raise Unauthorized if user.nil?
      @user = user
    end

    def beeps
      @beeps ||= Beep.finished.find(beep_ids)
    end

    def device_broker
      @device_broker ||= $device_broker
    end

    attr_writer :device_broker

    def run!
      #handles deletion of the owned beeps
      Beep.transaction do
        beeps.each do |beep|
          if beep.device.is_owner?(user)
             beep.destroy
             remove_beep_event(beep.id)
          end
         # elsif beep.device.is_authorized?(user) logic goes here
        end
      end
    end

 private

  def remove_beep_event(beep_id)
    device_broker.publish('beep_deleted', { beep_id: beep_id })
  end

dings_spec.rb

  describe "POST /clients_api/beeps/bulk_deletion" do
    let(:user_id)        { user.id }

    let!(:shared_beep_1) { create(:beep, id: 33, device:       authorized_device, state: :completed) }
    let!(:shared_beep_2) { create(:beep, id: 34, device: authorized_device, state: :completed) }
    let!(:owned_beep_1)  { create(:beep, id: 35, device: owned_device, state: :completed) }
    let!(:owned_beep_2)  { create(:beep, id: 36, device: owned_device, state: :completed) }

    before do
      post "/clients_api/beeps/:beep_id/bulk_deletion",        default_params.merge(beep_ids: [shared_beep_1.id, shared_beep_2.id,     owned_beep_1.id, owned_beep_2.id], user_id: user.id)
      allow_any_instance_of(BulkBeepRemover).to receive(:device_broker).and_return(broker_mock)
    end

    context "the owner's array of beep ids" do
      let(:beep_id) { owned_beep_2.id }


      it "deletes the beeps" do
        expect { Beep.find(beep_id) }.to     raise_error(ActiveRecord::RecordNotFound)
      end

      it "publishes the owner beep_deleted event" do
        expect(broker_mock).to     have_received(:publish).with("beep_deleted", { beep_id: beep_id })
      end
    end
  end
end

您的 /clients_api/beeps/:beep_id/bulk_deletion 路由似乎没有使用 :user_id 参数。因此,没有要传递的 user_id 个参数的参数。

另外,参数名称:beep_id与测试中的用法不匹配,调用它:beep_ids;这可能会或可能不会成为问题,但值得确保名称对齐以避免冲突。

您问的是 each 块中的 if 语句,完全没有问题;这在 Ruby 代码中很常见。您已经完成了 if 语句的查找。

关于交易,你可以rescue保护交易块,但是你是否应该完全取决于几点考虑。

1) 事务块的目的很重要,从代码中看并不明显。您必须确定事务是否严格用于更新数据库的效率(以减少 SQL 调用的次数),或者它是否用于维护事务完整性。

2) 你必须弄清楚以这种方式使用 rescue 在你的组织中是否被认为是可以接受的做法;在某些组织中,这是非常禁止的。如果不允许,您必须采取措施确保发生的异常在发生之前得到避免。一种这样的方法是在执行操作之前查询执行操作是否安全,如果不安全则避免这样做。

3) 你必须知道在交易中无视删除哔哔声是否可以接受,无论出于何种原因,没有给出适当的反馈;或者,另一方面,确定适当的反馈机制是什么。悄无声息地失败的过程并不受欢迎。比如有人要求删除哔哔声,删除成功returns,则可以认为哔哔声已经被删除;如果不是,则应以某种方式通知进程的用户它不是,以便他们可以采取适当的行动,即使该行动只是向上游中继消息。

实际上,对于事务的使用以及是否可以使用 if(或 应该)的问题,您可能正在进入软件设计领域,您可能需要澄清您必须遵守的目标和限制。这就是软件开发人员的生活;每个简单的问题都有许多复杂的考虑因素。学会正确认识和应对,你将遥遥领先!