Frage

In my Sinatra app my model defines a HABTM relationship between Users and Notifications. I am trying to define a couple of scopes, one for all Notifications associated with no Users called unread and one that returns all Notifications that are 'unread' by a particular User.

class Notification < ActiveRecord::Base
  has_and_belongs_to_many :users

  scope :unread, ->{
    Notification.joins("LEFT JOIN notifications_users ON notifications.id = notifications_users.notification_id").
                  where("notifications_users.user_id IS NULL").uniq
  }

  scope :unread_by, ->(u){
    Notification.joins("LEFT JOIN notifications_users ON notifications.id = notifications_users.notification_id").
                  where("notifications_users.user_id <> ?", u.id).uniq
  }

The unread scope works fine but the unread_by scope is not giving me the results I expect.

it "should know which notifications have not yet been read by anyone, or by a particular user" do
  n1 = Notification.create!(title: 'test 1', text: 'this is some longer text about the notification')
  n2 = Notification.create!(title: 'test 2', text: 'this is also some longer text about the notification')
  Notification.unread.must_include(n1)
  Notification.unread.must_include(n2)
  @user1.read(n1)
  Notification.unread.wont_include(n1)
  Notification.unread.must_include(n2)
  Notification.unread_by(@user1).wont_include(n1)
  Notification.unread_by(@user1).must_include(n2) # => fails
  Notification.unread_by(@user2).must_include(n1)
  Notification.unread_by(@user2).must_include(n2) # => fails
end

I suspect my query logic is flawed but I've been staring at this for too long and I'm just not seeing it. What am I missing?

War es hilfreich?

Lösung 2

Okay this fixed it, but it doesn't seem super-elegant to me.

scope :unread_by, ->(u){
  Notification.joins("LEFT JOIN notifications_users ON notifications.id = notifications_users.notification_id").
                where("notifications_users.user_id <> ? OR notifications_users.user_id IS NULL", u.id).uniq
}

It works though. Yay - Thanks all for your help, it pointed me in the right direction.

Andere Tipps

For ActiveRecord 4, you should be using the new style query interface inside the scope blocks

http://guides.rubyonrails.org/active_record_querying.html

Completely untested, but the following should be a guide a to how to structure the query: (note that each method is chained to the one above it)

scope :unread, {
    joins( "LEFT JOIN notifications_users ON notifications.id = notifications_users.notification_id").
    where("notifications_users.user_id IS NULL").
    select("DISTINCT notifications.*")
}

scope :unread_by, ->(u){
    joins("LEFT JOIN notifications_users ON notifications.id = notifications_users.notification_id").
    where(["notifications_users.user_id IS ?", u.id]).
    select("DISTINCT notifications.*")
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top