jtmarmon
jtmarmon

Reputation: 6179

Rails scope on belongs_to association

Imagine a Book and a Chapter model. Each Chapter belongs_to :book, and a Book has_many :chapters. We have scopes on the Chapter, for example :very_long returns chapters with over 300 pages.

Many times we want to get all books with any chapters over 300 pages. The way we usually achieve this is like so:

# book.rb
scope :has_very_long_chapter, -> { where(id: Chapter.very_long.select(:book_id) }

However, as you can imagine, it gets pretty tedious to proxy the scope every time we want to filter Books by chapter scopes. Is there any more idiomatic or cleaner way to achieve this?

Upvotes: 2

Views: 1920

Answers (2)

nemile
nemile

Reputation: 111

To get those books you can use ActiveRecord::SpawnMethods#merge, and you don't have to use another scope:

Book.joins(:chapters).merge(Chapter.very_long) 

Upvotes: 1

Sean
Sean

Reputation: 983

I think one thing you could do would be to use joins/merge

class Book < ARBase
  scope :with_long_chapters, ->{ joins(:chapter).merge(Chapter.very_long) }
end

class Chapter < ARBase
  scope :very_long, -> { logic for querying chapters with over 300 pages }
end

EDIT(#2): A more reusable scope

class Book < ARBase
  scope :by_chapter_page_count, ->(pages){ joins(:chapter).merge(Chapter.by_page_count pages) }
  scope :with_climax, -> { joins(:chapter).merge(Chapter.by_category :climax) }

  scope :long_with_climax, -> { by_chapter_page_count(400).with_climax }
end

class Chapter < ARBase
  scope :by_page_count, ->(pages) { where("pages > ?", pages }
  scope :by_category, ->(category) { where category: category }
end

Book.by_chapter_page_count(100)

You can get fairly creative with how you write your scopes

Upvotes: 0

Related Questions