2013-05-11 1 views
0

Рассмотрим этот кодRails: включить модуль, но сохранить имя модуля?

module Auth 

    def sign_in(user) 
    #some stuff 
    session[:user_id] = user.id 
    end 

end 

Теперь я хочу, чтобы включить это в мой контроллер приложения.

ApplicationController < ActionController::Base 
    include Auth 
end 

Это делает метод sign_in во всех моих контроллеров. Теперь, чтобы понять, что это не действие контроллера, я хотел бы сохранить имя, так что мои контроллеры чтения

def sign_user_in 
    # Some stuff 
    Auth.sign_in(@user) 
end 

Это, очевидно, не работает, потому что Rails будет искать метод класса в Auth модуль. Итак, вопрос в том ... Можно ли включить модуль в контроллер, сохранить его имя или пространство имен, но все же получить доступ к той же области, что и контроллер? (в этом случае переменная сеанса).

До сих пор наименее плохой способ я придумал, это остановить, включая модуль и в ApplicationController и вместо того, чтобы передать экземпляр контроллера приложения вместе при вызове метода аутентификации, как это:

def current_user(controller) 
    User.find(controller.session[:user_id]) 
end 

Вызов этого метода от контроллера с использованием self в качестве аргумента.

+1

ИМО, которая была бы худшим из обоих миров: все опасности воздействия с ни одно из преимуществ одного пространства имен. –

+0

Преимущества для одного пространства имен? О чем ты говоришь? У меня есть набор методов, которые работают с экземплярами контроллера, но я не хочу, чтобы мой контроллер приложений увеличивался. Кроме того, мне нравится контекст метода, который предоставляет константа. Я думаю, что это улучшает читаемость. Я знаю, что я могу использовать префиксы вместо этого и делать 'auth_method_name', но я действительно думаю, что' Auth.method_name' выглядит лучше. JavaScript предлагает это благодаря контексту выполнения, но как это делает Ruby? Если бы я подумал, что есть что-то полезное в отношении «single namespacing», я бы программировал PHP. –

+0

TL; DR. Вы явно сказали, что хотите получить доступ к 'session' изнутри mixin, но только в mixin, а не в контроллере. Опасности воздействия, без преимуществ. Если вы собираетесь разделить проблемы, * отделите их * и используйте служебный объект. Простое перемещение функциональности в mixin не делает контроллер менее жирным: переместите его в изолированный класс * does *. –

ответ

1

Попробуйте это?

Использует фактический класс для всех функциональных возможностей, а контроллер имеет экземпляр этого класса; в основном тот же самый код, который вы имеете выше - см current_user, но вам нужно только передать экземпляр контроллера в один раз, а не на каждом вызове метода

module Auth 
    # this method is 'mixed' into controller (self) 
    def initialize_authorizer 
    @authorizer = ::Auth::Authorizer(self) 
    end 

    # this class doesn't need to be in this namespace (module), put it where ever makes sense 
    class Authorizer 
    def initialize(controller) 
     @controller = controller 
    end 

    attr_reader :controller 

    def sign_in(user) 
     #some stuff 
     controller.session[:user_id] = user.id 
    end 

    def current_user 
     User.find(controller.session[:user_id]) 
    end   
    end 
end 

ApplicationController < ActionController::Base 
    include Auth 

    before_filter :initialize_authorizer 
end 

def sign_user_in 
    # Some stuff 
    @authorizer.sign_in(@user) 
end  
+0

Это интересный подход, спасибо. :-) –

+0

Мне очень нравится идея использования переменной экземпляра. С вашим решением мне не нужно пространство имен. Я могу создать расширение контроллера как класс с именем auth, а затем настроить его для использования в контроллере приложения с использованием параметра before_filter. –