是否可以验证分配给 Ruby 中 Proc 的方法数量?

发布于 2024-10-05 14:44:01 字数 881 浏览 4 评论 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

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

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

I'm currently working on a DSL in relation to accounting. What I'd like to be able to do is:

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

Currently, this executes the following method:

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

...Which in turn executes the block on the AccountantHelper class, calling the "credit" and "debit" methods respectively:

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

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

(Please hold back any fire about using class_eval() -- this is only a prototype after all!)

The goal is for the block to act as a transaction, ensuring that if the entire block can't be executed successfully, then none of it should. However in addition to this, it should also verify the integrity of the data passed into the block. In this case I need to verify that there is both a "credit" and a "debit" method within the block (in double-entry accounting, for every credit there must also be at least one debit, and vice versa). Currently I could call:

accountant do
  credit @account_1, @amount
end

...And the code will execute without any errors. This would be a bad thing as there is no corresponding "debit" to keep the accounts in balance.

Is it possible to verify what gets passed into the block? Or am I heading down the wrong path here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

晨与橙与城 2024-10-12 14:44:01

我想您可以将您的贷方和借方操作设为“惰性”,以便在验证后由包装器方法执行它们。这是一个概念证明,与您的类似,但没有元编程部分,为了清楚起见,跳过了:

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}

I guess you can make your credit and debit actions "lazy", so that they are executed by the wrapper method, after the validation. Here's a proof of concept, similar to yours, but without metaprogramming part, skipped for clarity:

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}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文