Mixin application.rb problem

Hello all.

I'm trying to implement my own role-based access scheme, and I've hit a
bit of a snag. It may be that my idea is just plain wrong, and if so,
I'd appreciate gentle instruction as to how to make it more sound.

My intent is to do something like

class MyController < ApplicationController
    permit_access(:admin)
    permit_access(:normal_user, :only => [:this, :that])

    before_filter :require_access

    def this
    end

    def that
    end

    def the_other
    end

    private

    def require_access
        redirect_to error_path unless verify_access(@current_user)
    end
end

The point of this endeavor is to more easily allow multiple roles to the
same action. To that end, I created a module called RoleAccess that
defines a permit_access method and a verify_access method. The idea is
that I'd have a hash of controllers, actions, and roles that would be
populated by permit_access and inspected by verify_access. The problem
is that permit_access is a class method and verify_access is an instance
method, so my module looks like

module RoleAccess
    module ClassMethods
        def permit_access
        end
    end

    module InstanceMethods
        def verify_access
        end
    end
end

and I can't figure out how to have a hash shared between
ClassMethods::permit_access and InstanceMethods::verify_access. First,
can this be done? If so, will someone please point me to some sample
code that illustrates how to do it. I've been looking through various
pieces of Rails source and scouring the internet, which has brought me
to the point where I am, but I can't quite get over the hump.

Peace,
Phillip

Use your favourite class variable pattern and then do
self.class.permitted_actions or whatever you call it.

Fred

Use your favourite class variable pattern and then do
self.class.permitted_actions or whatever you call it.

Fred

Hi Fred.

Funny enough, when I initially wrote "Hello all", I mused to myself that
I should probably just write "Hello Fred". Heh heh.

Thanks for the instruction. You are a quite a help to a lot of people. I
was watching the Ruby Hero awards the other day, and was thinking that
you deserve one of those as well. Cheers to you!

Now, if I could pick your (or anyone else's for that matter) brain a bit
more, I'm having another problem with my access scheme. The original
post was just generalities, so I'll use specific code this time.

In application.rb, I have

class ApplicationController < ActionController::Base
  extend RoleAccess
  init_role_access

  protected

  def require_access
    redirect_to person_path unless verify_access(@current_person)
  end
end

and in an actual controller, this

class SportsController < ApplicationController
  permit_access(:system_administrator)
  permit_access(:oranization_administrator, [:index, :show, :update])
  permit_access(:sport_administrator, [:show, :update])

  before_filter :require_logged_in
  before_filter :require_access

  ...
end

and finally, lib/role_access.rb looks like

module RoleAccess
  def init_role_access
    include InstanceMethods
    extend ClassMethods
    @@access_map ||= {}
  end

  def self.access_map
    @@access_map
  end

  module ClassMethods
    def permit_access(*args)
      access_map = RoleAccess::access_map

      #debugger
      controller = self.controller_name.to_sym

Use your favourite class variable pattern and then do
self.class.permitted_actions or whatever you call it.

Fred

Hi Fred.

Funny enough, when I initially wrote "Hello all", I mused to myself
that
I should probably just write "Hello Fred". Heh heh.

Thanks for the instruction. You are a quite a help to a lot of
people. I
was watching the Ruby Hero awards the other day, and was thinking that
you deserve one of those as well. Cheers to you!

Now, if I could pick your (or anyone else's for that matter) brain a
bit
more, I'm having another problem with my access scheme. The original
post was just generalities, so I'll use specific code this time.

In application.rb, I have

class ApplicationController < ActionController::Base
extend RoleAccess
init_role_access

At this point, no methods have yet been defined so it's normal that
action_methods is empty.

You also want to be careful with @@ - since you first define
@@access_map on ApplicationController, all controllers will share it
(I wrote up some stuff on that not too long ago at http://www.spacevatican.org/2008/8/19/fun-with-class-variables)

one minor thing you could do would be to use the self.included hook on
modules. This is called when the module is included so you could do just

include RoleAccess and do your init_role_access stuff from that hook

Fred

Frederick Cheung wrote:

At this point, no methods have yet been defined so it's normal that
action_methods is empty.

Well, hm. So they aren't. Going to have to think about that one. I guess
I need to look at the filter code some more to see how Rails gets around
that.

You also want to be careful with @@ - since you first define
@@access_map on ApplicationController, all controllers will share it
(I wrote up some stuff on that not too long ago at
http://www.spacevatican.org/2008/8/19/fun-with-class-variables)

I thought of that, which is why I start by adding a key for the
controller. This sort of thing is pretty new to me, so I might be
missing something fundamental. If I just make it @access_map, will each
controller have its own? I'll read your article and see if I understand
better afterward.

one minor thing you could do would be to use the self.included hook on
modules. This is called when the module is included so you could do just

include RoleAccess and do your init_role_access stuff from that hook

I tried do this different ways, but didn't have any success. Part of my
problem might be that I'm a bit confused by extend versus include. Based
on what I've been reading, I thought that include was used to add
methods to classes whereas extend added methods to instances (objects).
The current form of

extend RoleAccess

is actually the reverse of what I thought I needed to have, so my grip
on that one is apparently pretty weak. I'll experiment with them and see
if can't get it cleaner.

Fred

Thanks, Fred. Your insight and patience are very much appreciated.

Phillip

Frederick Cheung wrote:

At this point, no methods have yet been defined so it's normal that
action_methods is empty.

Well, hm. So they aren't. Going to have to think about that one. I
guess
I need to look at the filter code some more to see how Rails gets
around
that.

Personally I'd do it when the filter runs, not ahead of time.

You also want to be careful with @@ - since you first define
@@access_map on ApplicationController, all controllers will share it
(I wrote up some stuff on that not too long ago at
http://www.spacevatican.org/2008/8/19/fun-with-class-variables)

I thought of that, which is why I start by adding a key for the
controller. This sort of thing is pretty new to me, so I might be
missing something fundamental. If I just make it @access_map, will
each
controller have its own? I'll read your article and see if I
understand
better afterward.

one minor thing you could do would be to use the self.included hook
on
modules. This is called when the module is included so you could do
just

include RoleAccess and do your init_role_access stuff from that hook

I tried do this different ways, but didn't have any success. Part of
my
problem might be that I'm a bit confused by extend versus include.
Based
on what I've been reading, I thought that include was used to add
methods to classes whereas extend added methods to instances
(objects).
The current form of

extend RoleAccess

Have a look at http://blog.jayfields.com/2006/05/ruby-extend-and-include.html
  or http://www.dcmanges.com/blog/27

Fred

Frederick Cheung wrote:

Personally I'd do it when the filter runs, not ahead of time.

Thanks for saying that. It didn't register at first what you were
talking about, but it finally sank in and I followed that route. Wow, it
really simplified things. I decided to take ApplicationController out of
the picture completely and include RoleAccess in the specific
controllers in which I need greater flexibility. Coupling that with your
suggestion made it all cleaner. Now I can just include RoleAccess in
SportsController and not worry about whether it's a class method or an
instance method. Since I am calling permit_access and verify_access from
within require_access, they are both instance methods. And that also
solves the problem of action_methods not being populated. The only
difference is now I have to call

self.class.action_methods

but that's okay. The only downside, and it's purely aesthetic, is not
having the statements about access control at the top of the controller.
When I come back to this later, or if someone else ever has to maintain
this code (which might happen), it won't be as obvious what's going on.
I suppose I could resort to that legacy construct we used to hear so
much about. Oh what was it? A 'comment', I think.

Have a look at
http://blog.jayfields.com/2006/05/ruby-extend-and-include.html
  or http://www.dcmanges.com/blog/27

Thanks again. Those resources helped. I'm going to have to put those in
Evernote so I don't forget about them.

So my module is much cleaner now, but I'm still confused by the access
to the instance variable. I now have

module RoleAccess
  @access_map ||= {}

  def self.access_map
    @access_map
  end # access_map

  private

  def permit_access(*args)
    access_map = RoleAccess::access_map
    ...
  end

  def verify_access(current_person = nil)
    access_map = RoleAccess::access_map
    ...
  end
end

I tried various ways to access @access_map without having to use that
ugly RoleAccess::access_map, but nothing worked. I tried to apply what
you wrote in the blog post you mentioned earlier, but I believe there
are some differences between classes and modules in this regard. This
approach does work, but I'd like it to be a bit cleaner.

Again, thanks for the help and patience. I really enjoy Ruby/Rails, but
it's taking me a bit of effort to wrap my head around a lot of it.

Fred

Phillip