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 クラスのブロックを実行し、それぞれ「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() の使用についてはご遠慮ください -- 結局、これはプロトタイプにすぎません!)

目標は、ブロックがトランザクションとして機能することであり、ブロック全体が正常に実行されない場合は、いずれも正常に実行されないようにすることです。ただし、これに加えて、ブロックに渡されたデータの整合性も検証する必要があります。この場合、ブロック内に「貸方」と「借方」の両方の方法があることを確認する必要があります (複式簿記では、すべての貸方に対して少なくとも 1 つの借方が必要であり、その逆も同様です)。現在、私は呼び出すことができます:

accountant do
  credit @account_1, @amount
end

...そして、コードはエラーなしで実行されます。アカウントの残高を維持するための対応する「借方」がないため、これは悪いことです。

ブロックに渡されるものを確認することは可能ですか? それとも、ここで間違った道を進んでいますか?

4

1 に答える 1

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}
于 2010-11-28T23:04:49.323 に答える