【问题标题】:Rails: Include module, but maintain module name?Rails:包含模块,但维护模块名称?
【发布时间】:2013-05-11 21:42:01
【问题描述】:

考虑这段代码

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 中包含模块,而是在调用这样的 auth 方法时传递应用程序控制器实例:

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

使用 self 作为参数从控制器调用此方法有效。

【问题讨论】:

  • IMO 这将是两全其美:暴露的所有危险,没有任何单一命名空间的好处。
  • 单一命名空间的好处?你在说什么?我有一组在控制器实例上运行的方法,但我不希望我的应用程序控制器变胖。另外,我喜欢常量提供的方法上下文。我认为它提高了可读性。我知道我可以使用前缀来代替auth_method_name,但我真的认为Auth.method_name 看起来更好。 JavaScript 提供了这一点,这要归功于它的执行上下文,但是 Ruby 是如何做到的呢?如果我认为“单一命名空间”有什么好处,我会编程 PHP。
  • TL;博士。您明确表示您想从 mixin 内部访问 session - 但只能在 mixin 中,而不是在控制器中。暴露的危险,没有好处。如果您要分离关注点,请将它们分离,然后使用服务对象。简单地将功能移动到 mixin 不会让控制器变得更胖:将它移动到一个独立的类确实
  • 你太粗鲁了。如果您不想阅读我的帖子,请关闭浏览器选项卡。我希望我的控制器在运行时很胖,但在我的编辑器中可以管理。一个具有 20 种方法的应用程序控制器不响应路由器对我来说是无法管理的。这令人困惑。在这个单一命名空间中调用某个名为 sign_in 的方法的控制器方法也无济于事。它是(视图)助手吗?也许另一个控制器动作?也许它是从 ActionController::Base 继承的?真的没有什么比方法前缀更好的了吗?
  • 知道你想要,这正是为什么我认为它是两全其美的:一个胖控制器假装 Auth 是独立的,而实际上不是。看起来像是在调用类方法的代码,其实不是。

标签: ruby-on-rails ruby actioncontroller


【解决方案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    

【讨论】:

  • 我真的很喜欢使用实例变量的想法。使用您的解决方案,我不需要命名空间。我可以将控制器扩展创建为一个名为 auth 的类,然后使用 before_filter 将其设置为在应用程序控制器中使用。
【解决方案2】:

我问这个问题已经 9 年了。与此同时,我意识到这样做不是一个好主意,因为它会与语言发生冲突。

常量有自己的self,当引用一个常量时,你会期望任何方法都是类方法。除非在方法调用期间显式传递了引用,否则您不会期望它们能够访问调用对象,在这种情况下,您具有双向依赖关系,这会带来一系列问题。那将是代码异味,应该是重构软件设计的原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-25
    • 2018-09-04
    • 1970-01-01
    • 2023-03-17
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    相关资源
    最近更新 更多