2010-11-28 44 views
0

Je travaille actuellement sur un DSL en relation avec la comptabilité. Ce que je voudrais être en mesure de faire est:Est-il possible de vérifier le nombre de méthodes assignées à un Proc dans Ruby?

accountant do 
    credit @account_1, [email protected] 
    debit @account_2, @amount 
end 

Actuellement, il exécute la méthode suivante:

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

... Ce qui, à son tour exécute le bloc sur la classe AccountantHelper, appelant la « crédit » et « débit » méthodes respectivement:

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

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

(S'il vous plaît retenir un feu sur l'utilisation class_eval() - ceci est seulement un prototype après tout!)

L'objectif est que le bloc agisse comme une transaction, en s'assurant que si le bloc entier ne peut pas être exécuté avec succès, alors il ne le devrait pas. Cependant, en plus de cela, il devrait également vérifier l'intégrité des données transmises dans le bloc. Dans ce cas, je dois vérifier qu'il existe à la fois une méthode de «crédit» et une méthode de «débit» (en comptabilité en partie double, pour chaque crédit, il doit y avoir au moins un débit, et vice versa). Actuellement, je pourrais appeler:

accountant do 
    credit @account_1, @amount 
end 

... Et le code s'exécutera sans aucune erreur. Ce serait une mauvaise chose car il n'y a pas de «débit» correspondant pour maintenir l'équilibre des comptes.

Est-il possible de vérifier ce qui est passé dans le bloc? Ou est-ce que je suis sur le mauvais chemin ici?

Répondre

2

Je suppose que vous pouvez rendre vos actions credit et debit "paresseuses", afin qu'elles soient exécutées par la méthode wrapper, après la validation. Voici une preuve de concept, semblable à la vôtre, mais sans partie métaprogrammation, sauté pour plus de clarté:

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} 
+0

Merci, c'est exactement ce que je dois :) J'avais pensé d'abord que je devais vérifier que les deux un « crédit » et la déclaration de «débit» est définie dans le bloc, mais je n'ai pas pensé à simplement vérifier que les montants totalisent zéro. C'est une solution simple qui vérifie l'intégrité du bloc en une fois. – 6twenty