【问题标题】:Using an if statement within a transaction to delete multiple objects在事务中使用 if 语句删除多个对象
【发布时间】:2016-09-23 16:45:57
【问题描述】:

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

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

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

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

我最终做的是一个带有 if 语句的事务,然后循环遍历每个迭代器,检查通过的每个哔声是否由用户拥有或授权。

我的问题是我的 broker_mock 测试没有收到任何参数。当我在代码上使用调试器时,这些方法似乎都在工作,所以我很困惑。在每个循环中使用 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

【问题讨论】:

    标签: ruby-on-rails if-statement transactions conditional


    【解决方案1】:

    看起来您的 /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) 您必须知道是否可以忽略在交易中删除哔哔声,出于任何原因,而没有提供适当的反馈;或者,另一方面,确定正确的反馈机制是什么。静默失败的进程并没有得到很好的评价。例如,如果有人要求删除哔声,删除成功返回,则可以认为哔声已被删除;如果不是,则应该以某种方式通知流程的用户它不是,以便他们可以采取适当的行动,即使该行动只是将消息转发到上游。

    实际上,如果对事务的使用以及是否可以(或应该)使用 if 有疑问,您可能正在涉足软件设计领域,您可能需要澄清您必须处理的目标和限制。这就是软件开发者的生活;每个简单的问题都有许多复杂的考虑。学会正确地识别和处理它们,您将遥遥领先!

    【讨论】:

    • 感谢迈克尔的出色回应!感谢您的时间和建议。至于我遇到的问题,确实在我的 rspec 测试中。我在存根参数之前调用了 api。这是一个非常菜鸟没有注意到的事情。再次感谢您在交易方面给我更多启发并思考其实施。这是我的解决方法:before do allow_any_instance_of(BulkBeepRemover).to receive(:device_broker).and_return(broker_mock) post "/clients_api/beeps/:beep_id/bulk_deletion", default_params.merge(params) end
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    相关资源
    最近更新 更多