【问题标题】:Organizing API-Calls in callbacks在回调中组织 API 调用
【发布时间】:2014-11-05 06:45:17
【问题描述】:

我们正在维护几个 Rails 应用程序,它们都提出了类似的问题,我们没有真正好的解决方案:所有这些应用程序都包含需要在其生命周期中对外部服务进行 API 调用的模型。

可能的情况:

  • 成功创建后,用户订阅了 Newsletter-subscriber-list
  • 优惠价格在更新后与外部购物系统同步
  • 产品更新后在搜索索引中更新

我们体验到的不是是一个好的解决方案:将这些调用添加到模型的after_*callbacks。由于这会快速破坏测试,因此所有工厂现在都必须处理 api 调用。

我正在寻找一种组织这些 API 调用的好方法。你们是怎么做到的?

我们提出的想法,我认为这并不理想:

  • 将这些回调移动到控制器。现在他们在创建对象时很容易被遗忘
  • 生成一个异步工作器来处理 api 调用。然后每个(甚至是小型应用程序)都需要延迟作业队列的开销,例如 sidekiq。

【问题讨论】:

    标签: ruby-on-rails ruby design-patterns architecture refactoring


    【解决方案1】:

    如果您关心测试,您可以put the callback methods into a separate class 并在测试期间模拟回调类。这是一个使用 RSpec 的示例,给定以下 FooFooCallbacks 类:

    class Foo < ActiveRecord::Base
      after_save FooCallbacks
    end
    
    class FooCallbacks
      def self.after_save
        fail "Call to external API"
      end
    end
    

    您可以编写并成功运行这样的规范:

    describe Foo do
    
      before do
        allow(FooCallbacks).to receive(:after_save)
      end
    
      it "should not invoke real APIs" do
        Foo.create
      end
    
    end
    

    【讨论】:

      【解决方案2】:

      在得到建议之后,我现在就是这样做的:

      Foo:

      class Foo < ActiveRecord::Base
        before_save Foo::DataSync
      end
      

      Foo:DataSync看起来像这样:

      class Foo::DataSync
        def self.before_save(foo)
           ...do the API-Calls...
        end
      end
      

      现在为了在 rspec 中进行测试,我添加了这个:

      spec_helper.rb

      config.before(:each) do
        Foo::DataSync.stub(:before_save)
      end
      

      请注意,config.before(:suite) 将不起作用,因为此时未加载 Foo:DataSync

      现在foo_spec.rb 仅包含以下内容:

      describe Foo do
        let(:foo) {create(:foo)}
      
        it "will sync its data before every save" do
          expect(Foo::DataSync).to receive(:before_save).with(foo)
      
          foo.save
        end
      end
      

      Foo::DataSync 可以这样测试:

      describe Foo::DataSync do
        let!(:foo) {create(:foo)}
      
        before do
          Foo::DataSync.unstub(:before_save)
        end
      
        after do
          Foo::DataSync.stub(:before_save)
        end
      
        describe "#before_save" do
          ...my examples...
        end
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-08-21
        • 1970-01-01
        • 2019-07-31
        • 2023-02-24
        • 1970-01-01
        • 1970-01-01
        • 2019-03-16
        相关资源
        最近更新 更多