Reputation: 2704
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
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
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