Ruby on Rails - ActiveRecord::Relation count 方法错误? [英] Ruby on Rails - ActiveRecord::Relation count method is wrong?
问题描述
我正在编写一个应用程序,允许用户相互发送关于报价"的消息.
我想我可以节省一些工作并使用 .
就是看不出我做错了什么!可能会重做我的测试来解决这个问题.或者放弃 gem 并编写我自己的.
见这个 Rails 3:Relation.count 和 Relation.all.count 之间的区别
简而言之,当您将 count 应用于查询时,Rails 会忽略 select
列(如果有多个).这是因为
SQL 的 COUNT 只允许一列或更少列作为参数.
来自 Mailbox
代码
作用域 :participant, lambda {|participant|选择('不同的对话.*').where('notifications.type'=> Message.name).订单(conversations.updated_at DESC").joins(:receipts).merge(Receipt.recipient(participant))}
self.mailbox.conversations.count
忽略 select('DISTINCT对话.*')
并用 计算
,实质上是计算其中包含重复 join
表>receiptsconversations
的 receipts
的数量.
另一方面,self.mailbox.conversations.all.count
首先获取应用 select
的记录,它得到唯一的 conversations
然后数数.
self.mailbox.conversations.all == self.mailbox.conversations
因为他们都使用 select 查询数据库.
要解决您的问题,您可以使用 sending_user.mailbox.conversations.all.count
或 sending_user.mailbox.conversations.group('conversations.id').length
I'm writing an application that allows users to send one another messages about an 'offer'.
I thought I'd save myself some work and use the Mailboxer gem.
I'm following a test driven development approach with RSpec. I'm writing a test that should ensure that only one Conversation
is allowed per offer. An offer belongs_to
two different users (the user that made the offer, and the user that received the offer).
Here is my failing test:
describe "after a message is sent to the same user twice" do
before do
2.times { sending_user.message_user_regarding_offer! offer, receiving_user, random_string }
end
specify { sending_user.mailbox.conversations.count.should == 1 }
end
So before the test runs a user sending_user
sends a message to the receiving_user twice. The message_user_regarding_offer!
looks like this:
def message_user_regarding_offer! offer, receiver, body
conversation = offer.conversation
if conversation.nil?
self.send_message(receiver, body, offer.conversation_subject)
else
self.reply_to_conversation(conversation, body)
# I put a binding.pry here to examine in console
end
offer.create_activity key: PublicActivityKeys.message_received, owner: self, recipient: receiver
end
On the first iteration in the test (when the first message is sent) the conversation
variable is nil
therefore a message is sent and a conversation is created between the two users.
On the second iteration the conversation created in the first iteration is returned and the user replies to that conversation, but a new conversation isn't created.
This all works, but the test fails and I cannot understand why!
When I place a pry binding in the code in the location specified above I can examine what is going on... now riddle me this:
self.mailbox.conversations[0]
returns a Conversation
instance
self.mailbox.conversations[1]
returns nil
self.mailbox.conversations
clearly shows a collection containing ONE object.
self.mailbox.conversations.count
returns 2?!
What is going on there? the count
method is incorrect and my test is failing...
What am I missing? Or is this a bug?!
EDIT
offer.conversation
looks like this:
def conversation
Conversation.where({subject: conversation_subject}).last
end
and offer.conversation_subject
:
def conversation_subject
"offer-#{self.id}"
end
EDIT 2 - Showing the first and second iteration in pry
Also...
Conversation.all.count
returns 1!
and:
Conversation.all == self.mailbox.conversations
returns true
and
Conversation.all.count == self.mailbox.conversations.count
returns false
How can that be if the arrays are equal? I don't know what's going on here, blown hours on this now. Think it's a bug?!
EDIT 3
From the source of the Mailboxer gem...
def conversations(options = {})
conv = Conversation.participant(@messageable)
if options[:mailbox_type].present?
case options[:mailbox_type]
when 'inbox'
conv = Conversation.inbox(@messageable)
when 'sentbox'
conv = Conversation.sentbox(@messageable)
when 'trash'
conv = Conversation.trash(@messageable)
when 'not_trash'
conv = Conversation.not_trash(@messageable)
end
end
if (options.has_key?(:read) && options[:read]==false) || (options.has_key?(:unread) && options[:unread]==true)
conv = conv.unread(@messageable)
end
conv
end
The reply_to_convesation
code is available here -> http://rubydoc.info/gems/mailboxer/frames.
Just can't see what I'm doing wrong! Might rework my tests to get around this. Or ditch the gem and write my own.
see this Rails 3: Difference between Relation.count and Relation.all.count
In short Rails ignores the select
columns (if more than one) when you apply count to the query. This is because
SQL's COUNT allows only one or less columns as parameters.
From Mailbox
code
scope :participant, lambda {|participant|
select('DISTINCT conversations.*').
where('notifications.type'=> Message.name).
order("conversations.updated_at DESC").
joins(:receipts).merge(Receipt.recipient(participant))
}
self.mailbox.conversations.count
ignores the select('DISTINCT conversations.*')
and counts the join
table with receipts
, essentially counting number of receipts
with duplicate conversations
in it.
On the other hand, self.mailbox.conversations.all.count
first gets the records applying the select
, which gets unique conversations
and then counts it.
self.mailbox.conversations.all == self.mailbox.conversations
since both of them query the db with the select.
To solve your problem you can use sending_user.mailbox.conversations.all.count
or sending_user.mailbox.conversations.group('conversations.id').length
这篇关于Ruby on Rails - ActiveRecord::Relation count 方法错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!