Including a module in a Rails 5 Application Record class

廉价感情. 提交于 2019-12-12 11:29:28

问题


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

回答1:


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



回答2:


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>



回答3:


Your User and Message classes don't seem to be inheriting ApplicationRecord. How will they have access to ::boolean_scope?

Try this:

class User < ApplicationRecord
  boolean_scope :verified, :unverified
end

class Message < ApplicationRecord
  boolean_scope :sent, :pending
end


来源:https://stackoverflow.com/questions/44498962/including-a-module-in-a-rails-5-application-record-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!