我目前正在研究与会计相关的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
...代码将执行而没有任何错误。这将是一件坏事,因为没有相应的“借记”来保持帐户的平衡。
是否可以验证传递给块的内容?还是我在这里走错路?
答案 0 :(得分:2)
我猜您可以使credit
和debit
操作“懒惰”,以便在验证后通过包装器方法执行它们。这是一个概念证明,类似于你的概念,但没有元编程部分,为了清晰起见,跳过了:
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}