Including a module in a Rails 5 Application Record class

sea*_*vis 4 ruby metaprogramming ruby-on-rails ruby-on-rails-5

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Ser*_*sev 5

作为@Pavan答案的替代方法,您可以执行以下操作:

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
Run Code Online (Sandbox Code Playgroud)