1

Is there any reasonable way to inherit a named scope and modify it in Rails?

In the application on which I'm working, there are a great many models which have a scope named is_readable. Many of these models inherit from a handful of base models. One of the subclasses needs to add some strictures to its is_readable scope, and I want to avoid:

  1. giving the subclass' scope a new name because then the standard is_readable check could not be performed universally
  2. copy-pasting the superclass' scope definition into the subclass because then any future changes to the superclass' scope would not appear in the subclass
JellicleCat
  • 28,480
  • 24
  • 109
  • 162
  • 2
    I wonder if [this Q&A](https://stackoverflow.com/questions/4470108/when-monkey-patching-an-instance-method-can-you-call-the-overridden-method-from) might be of any help? – jvillian Sep 02 '20 at 15:18
  • @jvillian thanks. Can you clarify what might be the application? Is it just a suggestion for avoiding bad extension patterns? (I've read that thread before, but I'm not sure that I saw anything that looked like a clean solution for this application.) – JellicleCat Sep 02 '20 at 16:49
  • 1
    I could imagine a number of the directions mentioned being potential leads. I'm not sure there's a "clean answer" in that thread. I could also imagine, possibly, creating a `is_readable` class method in the subclass and calling `super`. TBH, I'm just sort of spitballing. Your question is very interesting but also a wee bit high-level and conceptual. I'm hoping someone a lot smarter than me will weight in with something really clever. – jvillian Sep 02 '20 at 17:26

1 Answers1

1

If I'm understanding you right (and I might not be), this should work:

Because a scope is really just syntactical sugar for a class method, we can use super in it.

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
    t.boolean :readable
  end

  create_table :comments, force: true do |t|
    t.boolean :readable 
    t.boolean :active
  end
end

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true 
  scope :is_readable, -> { where(readable: true) }
end

class Post < ApplicationRecord
end

class Comment < ApplicationRecord
  def self.is_readable
    super.where(active: true)
  end
end

class IsReadableTest < Minitest::Test
  def test_scope
    5.times { Post.create!(readable: true) }
    3.times { Comment.create!(readable: false, active: false) }
    2.times { Comment.create!(readable: true, active: true) }

    assert_equal 5, Post.count
    assert_equal 5, Post.is_readable.count
    assert_equal 5, Comment.count
    assert_equal 2, Comment.is_readable.count 
  end
end
Josh Brody
  • 5,153
  • 1
  • 14
  • 25