是否有可能验证分配给Ruby in Proc的方法数量?

时间:2010-11-28 22:43:13

标签: ruby transactions dsl block

我目前正在研究与会计相关的DSL。我希望能做的是:

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

目前,这会执行以下方法:

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

...它依次执行AccountantHelper类的块,分别调用“credit”和“debit”方法:

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

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

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

1 个答案:

答案 0 :(得分: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}