我目前正在研究与会计有关的DSL。我想做的是:

accountant do
  credit @account_1, -@amount
  debit  @account_2,  @amount
end

当前,这执行以下方法:

class Accountant
  def accountant &block
    AccountantHelper.class_eval(&block)
  end
end

...反过来又执行了帐户类上的块,分别称为“信用”和“借记”方法:

class AccountantHelper
  def self.credit account, amount
    account.credit amount
  end

  def self.debit account, amount
    account.debit amount
  end
end

(请放下有关使用class_eval()的任何火灾 - 毕竟这只是一个原型!)

目的是使该块充当交易,确保如果整个块无法成功执行,那么都不应。但是,除此之外,它还应该验证传递到块的数据的完整性。在这种情况下,我需要验证该块内是否有“信用”和“借记”方法(在双输入会计中,对于每个信用,也必须至少有一个借方,反之亦然)。目前我可以致电:

accountant do
  credit @account_1, @amount
end

...并且代码将执行没有任何错误。这将是一件坏事,因为没有相应的“借记”可以使帐户保持平衡。

是否可以验证什么传递到块中?还是我在这里走错了路?

有帮助吗?

解决方案

我想你可以做你的 creditdebit 操作“懒惰”,以便在验证后通过包装法执行它们。这是类似于您的概念的证明,但没有跨编程的部分,而是跳过了,为了清楚起见:

def transaction
  yield
  if @actions.map(&:last).inject(&:+) == 0
    @actions.each do |account, amount|
      @accounts[account] += amount
    end
    @actions = []
    puts 'transaction executed ok'
  else
    puts 'balance not zero, rolling back transaction'
    # rollback (effectively, do nothing)
  end
end

def credit account, amount
  @actions << [account, amount]
end

def debit account, amount
  @actions<< [account, -amount]
end

@actions = []
@accounts = {a: 0, b: 0, c: 0} # start with three blank accounts

transaction do
  credit :a, 5
  debit :b, 2
  debit :c, 3
end
#=>transaction executed ok

p @accounts
#=>{:a=>5, :b=>-2, :c=>-3}

transaction do
  credit :a, 5
  debit :b, 4
end
#=> balance not zero, rolling back transaction

p @accounts
#=> {:a=>5, :b=>-2, :c=>-3}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top