在设计 SessionsController 和 RegistrationsController 之间创建共享方法的最佳实践

Best practise creating a shared method between devise SessionsController and RegistrationsController

有 3 种方法可以使用邀请令牌进入我的应用程序:

现在我感兴趣的是如何结合 Devise 处理最后两种情况,而不必重复相同的功能。

控制器覆盖由 routes.rb:

处理
devise_for :users, controllers: {
      sessions: 'users/sessions',
      registrations: 'users/registrations'
  }

覆盖会话和注册的 after_sign_in/up_path

class Users::SessionsController < Devise::SessionsController
  protected
  def after_sign_in_path(resource)
    handle_invite
    super(resource)
  end
end
class Users::RegistrationsController < Devise::RegistrationsController
  protected
  def after_sign_up_path_for(resource)
    handle_invite
    super(resource)
  end
end

我应该把handle_invite方法放在哪里?

我正在寻找一种解决方案,我可以将方法放在我的 UsersController 中,因为那似乎是放置它的正确位置。:

class UsersController < ApplicationController
  private
  def handle_invite
    # Some code getting the token and adding the user to a group
  end
end

我认为这应该可行,因为 Devise 似乎继承了这个控制器:

class Users::SessionsController < Devise::SessionsController; end
class Devise::SessionsController < DeviseController; end
class DeviseController < Devise.parent_controller.constantize; end

所以我希望 Devise.parent_controller.constantize 代表 UsersController,但由于某些原因无法从子控制器调用 handle_invite

如果您想使用 classical 继承,您必须通过配置 Devise.parent_controller 将 class 实际放置在继承链中,同时不破坏链的其余部分。

Ruby 没有多重继承。 class 只能继承单亲 class.

# config/initializers/devise.rb
config.parent_controller = 'UsersController'
class UsersController < DeviseController
  private
  def handle_invite
    # Some code getting the token and adding the user to a group
  end
end

但这并不是解决问题的最佳方法,因为 Ruby 通过模块进行水平继承:

# app/controllers/concerns/invitable.rb
module Invitable
  private

  def handle_invite
    # Some code getting the token and adding the user to a group
  end

  def after_sign_in_path(resource)
    handle_invite
    super(resource)
  end
end
# Do not use the scope resolution operator (::) for namespace definition!
# See https://github.com/rubocop-hq/ruby-style-guide#namespace-definition
module Users
  class SessionsController < ::Devise::SessionsController
    include Invitable
  end 
end
module Users
  class RegistrationsController < ::Devise::SessionsController
    include Invitable
  end 
end

这称为模块混合。在 Java 或 PHP 等其他语言中,它会被称为 特质 。模块封装了一组方法,可以包含在任何class中,也可以将模块混入其他模块中。

在 Rails 术语中,模块混合被称为 concerns - 这实际上只是包装了一个约定,即 app/controllers/concernsapp/models/concerns 目录被添加到自动加载根目录。这意味着 rails 将在那里的顶级命名空间中查找常量。

这也松散地连接到 ActiveSupport::Concern which is syntactic sugar for common ruby idioms。但是没有必要使用它,除非你真的在使用它的功能。