Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope of Constants in Ruby Modules

I'm having a little problem with constant scope in mixin modules. Let's say I have something like this

module Auth    USER_KEY = "user" unless defined? USER_KEY    def authorize     user_id = session[USER_KEY]   def  end 

The USER_KEY constant should default to "user" unless it's already defined. Now I might mix this into a couple of places, but in one of those places the USER_KEY needs to be different, so we might have something like this

class ApplicationController < ActionController::Base    USER_KEY = "my_user"    include Auth    def test_auth     authorize   end  end 

I would expect that USER_KEY would be "my_user" when used in authorize, since it's already defined, but it's still "user", taken from the modules definition of USER_KEY. Anyone have any idea how to get authorize to use the classes version of USER_KEY?

like image 943
user204078 Avatar asked Apr 21 '10 23:04

user204078


People also ask

What is the scope of a constant?

The scope of a variable or a constant determines whether the variable or the constant is known to only one procedure, all procedures in a module, or all procedures in your database. You can create variables or constants that can be used by any procedure in your database (public scope).

What are constants in Ruby?

What is a constant in Ruby? A constant is a type of variable which always starts with a capital letter. They can only be defined outside of methods, unless you use metaprogramming. Constants are used for values that aren't supposed to change, but Ruby doesn't prevent you from changing them.

Are constants global in Ruby?

Although constants look like local variables with capital letters, they have the visibility of global variables: they can be used anywhere in a Ruby program without regard to scope.

How do you use a constant in Ruby?

Ruby Constants Constants begin with an uppercase letter. Constants defined within a class or module can be accessed from within that class or module, and those defined outside a class or module can be accessed globally. Constants may not be defined within methods.


1 Answers

The USER_KEY you declared (even conditionally) in Auth is globally known as Auth::USER_KEY. It doesn't get "mixed in" to including modules, though including modules can reference the key in a non-fully-qualified fashion.

If you want each including module (e.g. ApplicationController) to be able to define its own USER_KEY, try this:

module Auth   DEFAULT_USER_KEY = 'user'   def self.included(base)     unless base.const_defined?(:USER_KEY)       base.const_set :USER_KEY, Auth::DEFAULT_USER_KEY     end   end   def authorize     user_id = session[self.class.const_get(:USER_KEY)]   end end  class ApplicationController < ActionController::Base   USER_KEY = 'my_user'   include Auth end 

If you're going to go to all this trouble, though, you might as well just make it a class method:

module Auth   DEFAULT_USER_KEY = 'user'   def self.included(base)     base.extend Auth::ClassMethods     base.send :include, Auth::InstanceMethods   end   module ClassMethods     def user_key       Auth::DEFAULT_USER_KEY     end   end   module InstanceMethods     def authorize       user_id = session[self.class.user_key]     end   end end  class ApplicationController < ActionController::Base   def self.user_key     'my_user'   end end 

or a class-level accessor:

module Auth   DEFAULT_USER_KEY = 'user'   def self.included(base)     base.send :attr_accessor :user_key unless base.respond_to?(:user_key=)     base.user_key ||= Auth::DEFAULT_USER_KEY   end   def authorize     user_id = session[self.class.user_key]   end end  class ApplicationController < ActionController::Base   include Auth   self.user_key = 'my_user' end 
like image 75
James A. Rosen Avatar answered Oct 05 '22 14:10

James A. Rosen