Created
July 1, 2022 11:22
-
-
Save kaspth/190f882646985c13c9ea389d93ec26c6 to your computer and use it in GitHub Desktop.
`scope` extension to allow marking a class method as a scope.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# In Active Record, class method scopes have to remember to return `all` otherwise they're break the call chain. | |
# | |
# def self.some_scope = nil # Assume more complex conditions that would result in a branch that accidentally didn't return `all`. | |
# | |
# User.some_scope.first # => raises NoMethodError `first' for NilClass | |
# | |
# Note: Active Record ensures a `scope :some_scope, -> { nil }` returns `all` via `|| all` here: | |
# https://github.com/rails/rails/blob/c704da66de59262f4e88824589ae4eddefb6ed4a/activerecord/lib/active_record/scoping/named.rb#L181 | |
# | |
# Now, this extension allows you to mark a class method as a scope, so you don't have to remember and the code is more clearly demarcated too. | |
class User < ApplicationRecord | |
def self.scope(name, body = nil, &) | |
if body.nil? && block_given? | |
raise ArgumentError, "can't pass an extension block while calling scope on a class method" | |
end | |
body = method(name) if body.nil? | |
super | |
rescue NameError => error | |
raise unless error.message.match?(/undefined method .*? for class/) | |
raise ArgumentError, "didn't pass a class method to scope, you may need to affix `self.` in `def self.method_name`" | |
end | |
scope def self.some_scope(skip: false) | |
where(id: 1) unless skip | |
end | |
# Just to show the NameError raised for `method(name)` since this is an instance method and not a class method. | |
# scope def some_instance_scope | |
# end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment