I am using Application Record to simplify shared logic throughout an application.
Here's an example that writes a scope for a boolean and its inverse. This works well:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def self.boolean_scope(attr, opposite = nil)
scope(attr, -> { where("#{attr}": true) })
scope(opposite, -> { where("#{attr}": false) }) if opposite.present?
end
end
class User < ApplicationRecord
boolean_scope :verified, :unverified
end
class Message < ApplicationRecord
boolean_scope :sent, :pending
end
My Application Record class got long enough it made sense for me to break it up into individual modules and load those as needed.
Here's my attempted solution:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include ScopeHelpers
end
module ScopeHelpers
def self.boolean_scope(attr, opposite = nil)
scope(attr, -> { where("#{attr}": true) })
scope(opposite, -> { where("#{attr}": false) }) if opposite.present?
end
end
class User < ApplicationRecord
boolean_scope :verified, :unverified
end
class Message < ApplicationRecord
boolean_scope :sent, :pending
end
In this case, I don't get a load error, but boolean_scope
is then undefined on User
and Message
.
Is there a way to ensure the included modules are loaded at the appropriate time and available to Application Record and its inheriting models?
I've also attempted to have the models include the modules directly and that did not fix the issue.
module ScopeHelpers
def self.boolean_scope(attr, opposite = nil)
scope(attr, -> { where("#{attr}": true) })
scope(opposite, -> { where("#{attr}": false) }) if opposite.present?
end
end
class User < ApplicationRecord
include ScopeHelpers
boolean_scope :verified, :unverified
end
class Message < ApplicationRecord
include ScopeHelpers
boolean_scope :sent, :pending
end
As an alternative to @Pavan's answer, you can do this:
module ScopeHelpers
extend ActiveSupport::Concern # to handle ClassMethods submodule
module ClassMethods
def boolean_scope(attr, opposite = nil)
scope(attr, -> { where(attr => true) })
scope(opposite, -> { where(attr => false) }) if opposite.present?
end
end
end
# then use it as usual
class ApplicationRecord < ActiveRecord::Base
include ScopeHelpers
...
end
In this case, I don't get a load error, but boolean_scope is then undefined on User and Message
The problem is include
add methods on an instance of a class. You need to use extend
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend ScopeHelpers
end
Now you can call it like User.boolean_scope
. Below is the example for include vs extend
module Foo
def foo
puts 'heyyyyoooo!'
end
end
class Bar
include Foo
end
Bar.new.foo # heyyyyoooo!
Bar.foo # NoMethodError: undefined method ‘foo’ for Bar:Class
class Baz
extend Foo
end
Baz.foo # heyyyyoooo!
Baz.new.foo # NoMethodError: undefined method ‘foo’ for #<Baz:0x1e708>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With