本文介绍了Ruby on Rails - ActiveRecord::Relation count 方法错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个应用程序,允许用户相互发送关于报价"的消息.

我想我可以节省一些工作并使用 .

就是看不出我做错了什么!可能会重做我的测试来解决这个问题.或者放弃 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 表>receipts,实质上是计算其中包含重复 conversationsreceipts 的数量.

另一方面,self.mailbox.conversations.all.count 首先获取应用 select 的记录,它得到唯一的 conversations然后数数.

self.mailbox.conversations.all == self.mailbox.conversations 因为他们都使用 select 查询数据库.

要解决您的问题,您可以使用 sending_user.mailbox.conversations.all.countsending_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

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 方法错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 07:03