pfooti
pfooti

Reputation: 2704

Calling class methods from mixed in module in ruby

The scenario: I have a couple of ActiveRecord models in my rails system that all need to be controlled via an access control list. I have a nice little ACL implementation that does what I want, but right now the check-access calls are all duplicated in each controlled object type (document, user, etc).

My intuition is to pull that shared code into a module and use it with a mixin. I'm not sure this is possible (or what the right syntax is), because the mixed-in module has calls to ActiveRecord::Base methods - there's scope and has_many definitions.

The example of what I'd like to accomplish is here:

class Document < ActiveRecord::Base
  include Controlled
end

module Controlled 
  has_many :acls, as: :controlled
  scope :accessible, ->(uid, level){where("BUNCH OF SQL HERE")}
  def access_convenience_methods
    #stuff to provide easy access to authorization checks
  end
end

And then I'd have a few other models that derive from ActiveRecord::Base that include Controlled.

It's the has_many and scope calls in the module that are causing heartache - I can't call them from within the mixed-in module, apparently this context doesn't have access to the outer class methods.

Any advice is welcome.

Upvotes: 0

Views: 513

Answers (2)

pfooti
pfooti

Reputation: 2704

Aha, this is clearly a ruby newbie failure here - I need to put the has_many and other one-off calls inside an included block. It seems like ActiveSupport::Concern is precisely the right thing to use here:

module Controlled 
  extend ActiveSupport::Concern
  included do
    has_many :acls, as: :controlled
    scope :accessible, ->(uid, level){where("BUNCH OF SQL HERE")}
  end
  def access_convenience_methods
    #stuff to provide easy access to authorization checks
  end
end

Upvotes: 0

Leonid Shevtsov
Leonid Shevtsov

Reputation: 14179

You are correct in that you can't just call class methods from the module like that.

Nowadays the boilerplate code required to do this has been wrapped into ActiveSupport::Concern; it does exactly what you want.

[EDIT]: I also suggest you should study the boilerplate code itself, as it's pretty short and readable and a good example of Ruby metaprogramming.

Upvotes: 3

Related Questions