Estoy teniendo un pequeño problema con la constante ámbito en el mixin módulos. Digamos que tengo algo como esto

module Auth

  USER_KEY = "user" unless defined? USER_KEY

  def authorize
    user_id = session[USER_KEY]
  def

end

La USER_KEY constante, de forma predeterminada, el «usuario», a menos que ya está definido. Ahora puede mezclar esto en un par de lugares, pero en uno de esos lugares de la USER_KEY debe ser diferente, por lo que podría tener algo como esto

class ApplicationController < ActionController::Base

  USER_KEY = "my_user"

  include Auth

  def test_auth
    authorize
  end

end

Yo esperaría que USER_KEY sería «mi_usuario» cuando se utiliza en autorizar, puesto que ya está definido, pero aún así «usuario», tomado de la definición de módulos de USER_KEY. Alguien tiene alguna idea de cómo conseguir la autorización para utilizar la versión de las clases de USER_KEY?

InformationsquelleAutor user204078 | 2010-04-21

5 Comentarios

  1. 58

    La USER_KEY declarado (incluso condicional) en Auth es conocida a nivel mundial como Auth::USER_KEY. No hay «mixed» a la inclusión de módulos, aunque incluyendo módulos puede referencia la clave en un no-completo de la moda.

    Si quieres en cada una de ellas módulo (por ejemplo,ApplicationController) ser capaz de definir su propio USER_KEY, intente esto:

    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

    Si vas a ir a todo este problema, sin embargo, usted puede ser que también acaba de llegar a un método de clase:

    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

    o una clase de nivel descriptor de acceso:

    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
  2. 41

    Constantes no tienen alcance global en Ruby. Las constantes pueden ser visibles desde cualquier ámbito, pero debe especificar donde la constante es para ser encontrado. Al comenzar una nueva clase, módulo, o def, de comenzar un nuevo ámbito, y si quieres una constante desde otro ámbito, usted tiene que especificar dónde encontrarlo.

    X = 0
    class C
      X = 1
      module M
        X = 2
        class D
          X = 3
          puts X          # => 3
          puts C::X       # => 1
          puts C::M::X    # => 2
          puts M::X       # => 2
          puts ::X        # => 0
        end
      end
    end
    • En aras de la exhaustividad, se le olvidó X = 0 fuera de la clase y puts ::X.
    • La respuesta ha sido editada para incluir tu comentario.
    • Vale la pena señalar que si abre la clase o módulo con la taquigrafía como class C::M::D; end, dentro de que usted no será capaz de acceder a la M alcance directamente, por lo que M::X sería indefinido, sólo se podía acceder a ella como C::M::X.
    • Estoy sorprendido de que puts ::X no volver 2 (el valor de X en module M. Entonces, ¿qué hace :: decir? El «nivel superior»?
  3. 12

    Aquí una solución simple.

    Cambios:

    • No hay necesidad de comprobar la existencia de USER_KEY.
    • Intente buscar la constante en el receptor de la clase o módulo (en su caso sería el controlador). Si existe, usarlo, de lo contrario, utilice el valor predeterminado del módulo/clase (ver más abajo por lo que el valor predeterminado es).

    .

    module Auth
      USER_KEY = "user"
    
      def authorize
        user_key = self.class.const_defined?(:USER_KEY) ? self.class::USER_KEY : USER_KEY
        user_id = session[user_key]
      def
    end

    Explicación

    El comportamiento que estamos viendo no es específico para los rieles, sino que es debido a donde ruby busca constantes si no explícitamente ámbito a través de :: (lo que yo llamo el «default», más arriba). Las constantes se buscan mediante el «ámbito léxico de el código actualmente en ejecución». Esto significa que ruby busca primero la constante en la ejecución de código del módulo (o de clase), luego se mueve hacia afuera para cada una de las sucesivas adjuntando módulo (o clase) hasta que encuentre la constante definida en ese ámbito.

    En su controlador, llamada authorize. Pero cuando authorize es la ejecución, el código actualmente en ejecución es en Auth. De modo que es donde las constantes miró hacia arriba. Si Auth no han USER_KEY, pero una que encierra el módulo tiene, entonces, el que encierra uno podría ser utilizado. Ejemplo:

    module Outer
      USER_KEY = 'outer_key'
      module Auth
         # code here can access USER_KEY without specifying "Outer::"
         # ...
      end
    end

    Un caso especial de esto es el nivel superior del entorno de ejecución, el cual es tratado como pertenecientes a la clase Object.

    USER_KEY = 'top-level-key'
    module Auth
      # code here can access the top-level USER_KEY (which is actually Object::USER_KEY)
      # ...
    end

    Una de ellas es la definición de un módulo o clase con el operador de ámbito (::):

    module Outer
      USER_KEY = 'outer_key'
    end
    module Outer::Auth
      # methods here won't be able to use USER_KEY,
      # because Outer isn't lexically enclosing Auth.
      # ...
    end

    Tenga en cuenta que la constante puede ser definido mucho más tarde que el método está definido. La búsqueda sólo ocurre cuando USER_KEY se accede, por lo que esto también funciona:

    module Auth
      # don't define USER_KEY yet
      # ...
    end
    
    # you can't call authorize here or you'll get an uninitialized constant error
    
    Auth::USER_KEY = 'user'
    
    # now you can call authorize.
  4. 3

    Si su proyecto está en Rails, o al menos utiliza el ActiveSupport módulo, usted puede reducir significativamente la lógica necesaria azúcar:

    module Auth
    
      extend ActiveSupport::Concern
    
      included do
        # set a global default value
        unless self.const_defined?(:USER_KEY)
          self.const_set :USER_KEY, 'module_user'
        end
      end
    
    end
    
    class ApplicationController < ActionController::Base
      # set an application default value
      USER_KEY = "default_user"
      include Auth  
    end
    
    class SomeController < ApplicationController
      # set a value unique to a specific controller
      USER_KEY = "specific_user"
    end

    Me sorprende que nadie sugirió este enfoque, viendo cómo el OP del escenario residido dentro de una aplicación Rails…

  5. 0

    Hay una solución más simple para el OP pregunta de las otras respuestas aquí revelan que:

    module Foo
      THIS_CONST = 'foo'
    
      def show_const
        self.class::THIS_CONST
      end
    end
    
    class Bar
      include Foo
    
      THIS_CONST ='bar'
      def test_it
        show_const
      end
    end
    
    class Baz
      include Foo
    
      def test_it
        show_const
      end
    end
    
    2.3.1 :004 > r = Bar.new
     => #<Bar:0x000000008be2c8> 
    2.3.1 :005 > r.test_it
     => "bar" 
    2.3.1 :006 > z = Baz.new
     => #<Baz:0x000000008658a8> 
    2.3.1 :007 > z.test_it
     => "foo" 

    Fue @james-un-rosen la respuesta que me dio la inspiración para tratar esto. Yo no quería ir a su ruta, porque he tenido varias constantes que son compartidos entre varias clases, cada una con un valor diferente, y su método se veía como un montón de escribir.

Dejar respuesta

Please enter your comment!
Please enter your name here