ActiveRecord事前回调

时间:2017-08-08 18:30:13

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord

问题:我正在寻找一个在DB事务开始之前执行的ActiveRecord(Rails 3.2)回调钩子。

如果它不存在(我认为它不存在),那么我正在寻找任何关于猴子修补ActiveRecord的建议,以便我们自己实现这样的方法。

我尝试过什么

看过ActiveRecord文档并玩弄代码后,似乎只有'after_commit'才能保证在事务之外执行。其他所有内容(“before_validation”,“before_save”,“before_commit”)都在事务中运行。我试过覆盖'交易'方法:

VERSION BUILD=9030808 RECORDER=FX
SET !ERRORIGNORE YES
SET !DATASOURCE "C:/Users/Tester/Desktop/file_name.csv"
SET !DATASOURCE_COLUMNS 1
SET !TIMEOUT_STEP 30
SET !EXTRACTDIALOG NO
SET !LOOP 1
SET !DATASOURCE_LINE {{!LOOP}}
URL GOTO=https://openflights.org/html/apsearch
SET !EXTRACT NULL
TAG POS=1 TYPE=INPUT:CHECKBOX FORM=NAME:searchform ATTR=NAME:iatafilter CONTENT=NO
TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:searchform ATTR=NAME:iata CONTENT={{!COL1}}
TAG POS=1 TYPE=INPUT:BUTTON FORM=NAME:searchform ATTR=*
TAG POS=6 TYPE=INPUT:BUTTON ATTR=*
WAIT SECONDS=1
TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:searchform ATTR=NAME:iata EXTRACT=TXT
TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:searchform ATTR=NAME:y EXTRACT=TXT
TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:searchform ATTR=NAME:x EXTRACT=TXT
SAVEAS TYPE=EXTRACT FOLDER="C:\\Users\\Tester\\Desktop" FILE=coordinates.csv

但这很复杂,因为事务是一个类方法,并且必须在每个模型实例中注册回调。最重要的是,它不能保证在数据库事务之外运行,因为它可以嵌套:

class User < ActiveRecord::Base
  included do |base|
    base.instance_eval do
    def before_transaction
      ### DO STUFF
    end

    def transaction(options = {}, &block)
       before_transaction { super(options, &block) }
    end
  end
end

根据ActiveRecord的实现方式,不确定我想要什么是可能的,因为可能无法知道另一个事务是否已经开始并随后调用回调。无论如何,我认为这里值得一提。

背景

我正在尝试将现有的Rails模型(让我们称之为用户)'同步'到位于elswhere的用户服务。用户服务应该充当真相的来源。

每当Rails用户模型发生更新时,我想在保存到DB(只有Rails项目使用)之前验证+首先将更改提交给用户服务。然后,Rails项目的DB用作直写缓存(为什么数据库用于缓存?遗留代码的原因)。

如果我们可以在写入数据库之前调用服务,我们不必担心回滚事务(并处理回滚失败时会发生什么!)。但是,我对在开放数据库事务中调用服务犹豫不决,这就是为什么我要研究是否可以建立一个“before_transaction”回调。

1 个答案:

答案 0 :(得分:0)

为什么不编写一个处理所有这些功能的服务对象?

class UserService
  attr_accessor :user

  def initialize(user, params)
    @user = user
    @params = params
  end

  def save
    external_result = API.call_to_external

    if external_result
      @user.update(@params)
    end
  end
end

user_service = UserService.new(user, params)
user_service.save