0

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

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

目前,这执行以下方法:

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

...依次执行 AccountantHelper 类上的块,分别调用“贷方”和“借方”方法:

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

...并且代码将在没有任何错误的情况下执行。这将是一件坏事,因为没有相应的“借方”来保持账户平衡。

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

4

1 回答 1

2

我猜你可以让你的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}
于 2010-11-28T23:04:49.323 回答