是否可以覆盖内置的Ruby方法?

时间:2018-03-16 01:18:43

标签: ruby-on-rails ruby

我正在研究一个必须通过rpsec测试的问题。问题是该方法使用与内置ruby方法.count

相同的名称

鉴于我无法更改rspec测试,是否可以覆盖.count以表现不同?如果没有,是否有更好的方法来解决这个问题?

这是我试图传递的rspec测试

subject = FinancialSummary.one_day(user: user, currency: :usd)
expect(subject.count(:deposit)).to eq(2)

我的代码:

class FinancialSummary

  def self.one_day(user: user, currency: currency)
    one_day_range = Date.today.beginning_of_day..Date.today.end_of_day
    find_transaction(user.id, currency).where(created_at: one_day_range)
  end

  def self.find_transaction(user_id, currency)
    Transaction.where(user_id: user_id,
                      amount_currency: currency.to_s.upcase
                      )
  end
end

输出:

    [#<Transaction:0x00007f9b39c2e9b8
      id: 1,
      user_id: 1,
      amount_cents: 1,
      amount_currency: "USD",
      category: "deposit",
      created_at: Sat, 10 Mar 2018 18:46:53 UTC +00:00,
      updated_at: Sat, 10 Mar 2018 18:46:53 UTC +00:00>,
     #<Transaction:0x00007f9b3d0dbc38
      id: 2,
      user_id: 1,
      amount_cents: 2000,
      amount_currency: "USD",
      category: "deposit",
      created_at: Sat, 10 Mar 2018 18:47:43 UTC +00:00,
      updated_at: Sat, 10 Mar 2018 18:47:43 UTC +00:00>,
     #<Transaction:0x00007f9b3d0b3fa8
      id: 7,
      user_id: 1,
      amount_cents: 1200,
      amount_currency: "USD",
      category: "withdraw",
      created_at: Mon, 05 Mar 2018 02:22:42 UTC +00:00,
      updated_at: Tue, 06 Mar 2018 18:48:20 UTC +00:00>]

它打印出来,我认为是正确的信息,直到测试尝试通过count category: 'deposit'进行交易。然后我收到此错误消息:

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: deposit: SELECT COUNT(deposit) FROM "transactions" WHERE "transactions"."user_id" = ? AND "transactions"."amount_currency" = ?

编辑更多信息

2 个答案:

答案 0 :(得分:1)

在撰写此答案时可能会做出一些假设,并且可能会根据更新的规范进行修改

覆盖count是一个坏主意,因为查看或使用您的代码的其他人不会知道这不是他们所了解和理解的计数。 而是考虑为此创建一个范围,如

class FinancialSummary < ApplicationRecord
  scope :one_day, ->(user:,currency:) { where(user: user, currency: currency) }  #clearly already a scope
  scope :transaction_type, ->(transaction_type:) { where(category: transaction_type) }
end 

然后测试成为

subject = FinancialSummary.one_day(user: user, currency: :usd)
expect(subject.transaction_type(:deposit).count).to eq(2)

SQL现在变为:

SELECT COUNT(*) 
FROM 
  "transactions" 
WHERE 
  "transactions"."user_id" = ? 
   AND "transactions"."amount_currency" = "usd"
   AND "transactions"."category" = "deposit"

仍然非常易懂且易于阅读,无需破坏我们明确使用的计数方法。

答案 1 :(得分:0)

目前还不清楚计数消息被发送到哪个对象,因为我不知道FinancialSummary.one_day(user: user, currency: :usd)返回什么,但似乎你说count是一个方法,无论它返回什么,你无法改变。 FinancialSummary.one_day(user: user, currency: :usd).class返回什么?

也许一种解决方案是通过添加alias_method :count, :account_count然后在您的测试调用expect(subject.account_count(:deposit)).to eq(2)

中为该对象添加别名

如果你可以在你的问题中发布FinancialSummary#one_day方法会更容易。