我正在编写一个应用程序,允许用户发送有关“优惠”的消息。
我以为我会节省一些工作并使用Mailboxer宝石。
我正在遵循RSpec的测试驱动开发方法。我正在编写一个测试,确保每个优惠只允许一个Conversation
。优惠belongs_to
两个不同的用户(提出优惠的用户以及收到优惠的用户)。
这是我失败的测试:
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
因此,在测试运行之前,用户sending_user
会向receiver_user发送两次消息。 message_user_regarding_offer!
看起来像这样:
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
在测试的第一次迭代中(发送第一条消息时)conversation
变量为nil
,因此会发送一条消息并在两个用户之间创建一个对话。
在第二次迭代中,返回在第一次迭代中创建的对话,并且用户回复该对话,但不会创建新对话。
这一切都有效,但测试失败了,我无法理解为什么!
当我在上面指定的位置的代码中放置一个pry绑定时,我可以检查发生了什么...现在谜我:
self.mailbox.conversations[0]
会返回Conversation
个实例
self.mailbox.conversations[1]
返回nil
self.mailbox.conversations
清楚地显示了一个包含一个对象的集合。
self.mailbox.conversations.count
返回2?!
那里发生了什么? count
方法不正确,我的测试失败......
我错过了什么?或者这是一个错误?!
修改
offer.conversation
看起来像这样:
def conversation
Conversation.where({subject: conversation_subject}).last
end
和offer.conversation_subject
:
def conversation_subject
"offer-#{self.id}"
end
编辑2 - 显示pry中的第一次和第二次迭代
也...
Conversation.all.count
返回1!
和
Conversation.all == self.mailbox.conversations
返回true
和
Conversation.all.count == self.mailbox.conversations.count
返回false
如果阵列相等,那该怎么办?我不知道这里发生了什么,现在已经开始了。认为这是一个错误?!
编辑3
从邮箱宝石的来源......
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
reply_to_convesation
代码可在此处使用 - > http://rubydoc.info/gems/mailboxer/frames。
只是看不出我做错了什么!可能会修改我的测试以解决这个问题。或抛弃宝石并自己编写。
答案 0 :(得分:9)
请参阅此Rails 3: Difference between Relation.count and Relation.all.count
简而言之,当您将count应用于查询时,Rails会忽略select
列(如果不止一个)。这是因为
SQL的COUNT只允许一列或更少的列作为参数。
来自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
忽略select('DISTINCT conversations.*')
并使用join
计算receipts
表格,主要计算其中包含receipts
个conversations
的{{1}}个数。
另一方面,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
答案 1 :(得分:3)
我倾向于在代码中使用size
方法。根据ActiveRecord代码,size将使用缓存计数(如果可用),并且在通过关系创建模型并且尚未保存时返回正确的数字。
# File activerecord/lib/active_record/relation.rb, line 228
def size
loaded? ? @records.length : count
end
此here上有一个博客。
在Ruby中,#length和#size是同义词,它们都做同样的事情:它们告诉你有多少元素在数组或散列中。从技术上讲,#length是方法,而#size是它的别名。
在ActiveRecord中,有几种方法可以找出关联中有多少条记录,并且它们的工作方式存在细微差别。
post.comments.count - 使用SQL COUNT查询确定元素的数量。您还可以指定条件以仅计算关联元素的子集(例如:conditions => {:author_name =>“josh”})。如果在关联上设置计数器缓存,#count将返回该缓存值,而不是执行新查询。
post.comments.length - 这总是将关联的内容加载到内存中,然后返回加载的元素数。请注意,如果先前已加载关联,然后通过其他方式创建新注释(例如,Comment.create(...)而不是post.comments.create(...)),则不会强制更新。< / p>
post.comments.size - 这是前两个选项的组合。如果集合已经加载,它将返回其长度,就像调用#length一样。如果还没有加载,就像调用#count。
如果您不通过关联创建模型,也值得一提,因为相关模型不一定在其关联代理/集合中具有这些实例。
# do this
mailbox.conversations.build(attrs)
# or this
mailbox.conversations << Conversation.new(attrs)
# or this
mailbox.conversations.create(attrs)
# or this
mailbox.conversations.create!(attrs)
# NOT this
Conversation.new(mailbox_id: some_id, ....)
答案 2 :(得分:0)
我不知道这是否解释了发生了什么,但ActiveRecord count
方法在数据库中查询存储的记录数。 length
中的Relation
可能会有所不同,如http://archive.railsforum.com/viewtopic.php?id=6255中所述,尽管在该示例中,数据库中的记录数 less Rails数据结构中的项目。
答案 3 :(得分:0)
尝试
self.mailbox.conversations.reload; self.mailbox.conversations.count
或者
self.mailbox.reload; self.mailbox.conversations.count
或者,如果这些都不起作用,只需尝试reload
尽可能多的对象,看看是否可以让它工作(self
,mailbox
,{{ 1}}等。)。
我的猜测是内存和数据库之间存在混淆。这绝对是一个非常奇怪的错误,可能想在Rails上提出一个问题,看看为什么会出现这种情况。
答案 4 :(得分:0)
mailbox.conversations
的结果在第一次调用后被缓存。要重新加载,请写mailbox.conversations(true)