简体   繁体   English

Ruby gem方法只在控制器中可用? 如果是这样,如何在其他文件中使用它?

[英]Ruby gem method only available in controller? If so, how to make it available in other files?

I was under the impression that a gem's methods can be accessed anywhere in the Rails app once it's been installed with bundler. 我的印象是,一旦用捆绑器安装了gem的方法,就可以在Rails应用程序中的任何地方访问它。 But is this not the case? 但事实并非如此吗? I'm confused because a method accessible in the controller isn't recognised when moved outside the file. 我很困惑,因为当移动到文件外时,无法识别controller可访问的方法。 Is there some way I could require the gem to run the code below in the command file instead of the controller ? 有什么方法我可以要求gem在command文件而不是controller运行下面的代码?

I'm using an authentication gem called sorcery to implement an OAuth login system. 我正在使用名为sorcery的身份验证gem来实现OAuth登录系统。 There are some methods like login_from(provider) that the gem provides, which can be accessed from my controller just fine. 有一些方法,比如gem提供的login_from(provider) ,可以从我的控制器访问就好了。

# app/controllers/oauths_controller.rb
class OauthsController < ApplicationController
  skip_before_action :require_login

  def callback
    provider = params[:provider]
    if @user = login_from(provider)
    # method continues
  end

However, we're trying to take a command-query separation approach in our app, so I tried moving this process containing login_from(provider) method to a different file called app/commands/authentication/login_command.rb . 但是,我们尝试在我们的应用程序中采用命令查询分离方法,因此我尝试将包含login_from(provider)方法的此进程移动到名为app/commands/authentication/login_command.rb的其他文件中。 This resulted in the method not being recognised: 这导致该方法无法识别:

NoMethodError (undefined method 'login_from' for #<Authentication::LoginCommand:>)

Thanks in advance. 提前致谢。

I was under the impression that a gem's methods can be accessed anywhere in the Rails app once it's been installed with bundler. 我的印象是,一旦用捆绑器安装了gem的方法,就可以在Rails应用程序中的任何地方访问它。

That would be an incorrect impression. 这将是一个错误的印象。

As you can see, Sorcery::Engine causes ActionController::Base to include methods defined in Sorcery::Controller , here: 如您所见, Sorcery::Engine使ActionController::Base包含在Sorcery::Controller定义的方法,这里:

require 'sorcery'
require 'rails'

module Sorcery
  # The Sorcery engine takes care of extending ActiveRecord (if used) and ActionController,
  # With the plugin logic.
  class Engine < Rails::Engine
    config.sorcery = ::Sorcery::Controller::Config

    initializer 'extend Controller with sorcery' do
      # TODO: Should this include a modified version of the helper methods?
      if defined?(ActionController::API)
        ActionController::API.send(:include, Sorcery::Controller)
      end

      if defined?(ActionController::Base)
        ActionController::Base.send(:include, Sorcery::Controller)
        ActionController::Base.helper_method :current_user
        ActionController::Base.helper_method :logged_in?
      end
    end
  end
end

Sorcer::Controller in turn, includes a series of submodules: Sorcer::Controller依次包含一系列子模块:

module Sorcery
  module Controller
    def self.included(klass)
      klass.class_eval do
        include InstanceMethods
        Config.submodules.each do |mod|
          # FIXME: Is there a cleaner way to handle missing submodules?
          # rubocop:disable Lint/HandleExceptions
          begin
            include Submodules.const_get(mod.to_s.split('_').map(&:capitalize).join)
          rescue NameError
            # don't stop on a missing submodule.
          end
          # rubocop:enable Lint/HandleExceptions
        end
      end
      Config.update!
      Config.configure!
    end

    ...
  end
end

One of these submodules is Sourcery::Controller::Submodules::External which, when included, also includes Sourcery::Controller::Submodules::External::InstanceMethods , here: 其中一个子模块是Sourcery::Controller::Submodules::External ,当包含它时,还包括Sourcery::Controller::Submodules::External::InstanceMethods ,这里:

module Sorcery
  module Controller
    module Submodules
      # This submodule helps you login users from external auth providers such as Twitter.
      # This is the controller part which handles the http requests and tokens passed between the app and the @provider.
      module External
        def self.included(base)
          base.send(:include, InstanceMethods)
          ...
        end
      end
    end
  end
end

And, InstanceMethods contains the method login_from , here: 而且, InstanceMethods包含login_from方法,这里:

module Sorcery
  module Controller
    module Submodules
      # This submodule helps you login users from external auth providers such as Twitter.
      # This is the controller part which handles the http requests and tokens passed between the app and the @provider.
      module External
        def self.included(base)
          base.send(:include, InstanceMethods)

        module InstanceMethods
          protected

          ...

          # tries to login the user from provider's callback
          def login_from(provider_name, should_remember = false)
            sorcery_fetch_user_hash provider_name

            return unless (user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s))

            # we found the user.
            # clear the session
            return_to_url = session[:return_to_url]
            reset_sorcery_session
            session[:return_to_url] = return_to_url

            # sign in the user
            auto_login(user, should_remember)
            after_login!(user)

            # return the user
            user
          end

          ...
        end
      end
    end
  end
end

Soooo, that's a long way of saying that login_from is defined as a protected instance method on classes that inherit from ActionController::Base and is not defined as an instance method on classes that DO NOT inherit from ActionController::Base which is why you're getting the NoMethodError . Soooo,这是一个很长的路要说, login_from被定义为继承自ActionController::Base类的受保护实例方法,并且没有被定义为不从ActionController::Base继承的类的实例方法,这就是为什么你'得到NoMethodError

Is there some way I could require the gem to run the code below in the command file instead of the controller? 有什么方法我可以要求gem在命令文件而不是控制器中运行下面的代码?

Maybe. 也许。 Maybe not. 也许不吧。 As you can see, login_from is using other sorcery methods and methods inherited from ActionController::Base . 如您所见, login_from正在使用从ActionController::Base继承的其他sorcery方法和方法。 So, you would need to make sure all of those methods are available in Authentication::LoginCommand in order for login_from to behave appropriately. 因此,您需要确保所有这些方法在Authentication::LoginCommand中都可用,以便login_from正常运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使红宝石宝石可用于脚本? - How do I make a ruby gem available to my scripts? 如何使在Rails引擎的应用程序控制器中定义的方法自动适用于安装它的任何其他引擎? - How to make method defined in a rails engine's application controller automatically available for any other engines which mount it? 如何在控制器中设置变量集,在其他类中可用? - How to make a variable set in controller, available in other class? 如何在Rails 3.2的视图和控制器中使用下划线方法? - How to make underscore method available in view and controller in rails 3.2? 如何以线程安全的方式将会话数据提供给我的ruby gem? - How can I make session data available to my ruby gem in a thread safe way? 如何使我现有的ruby gem配置可用于rails-api应用程序? - How to make my existing ruby gem configuration available to my rails-api app? 如何在Rails中仅使10分钟的“编辑发布”方法可用? - How to make “edit post” method available for only 10 minutes in Rails? 在一个方法中传递时,使变量在控制器的所有方法中可用 - make variable available in all method of controller when passed in one method 如何使gem中的功能可用于Sinatra视图? - How do I make functions in a gem available to Sinatra views? devise gem如何使它的应用程序文件夹可用于Rails? - How does devise gem make its app folder available to Rails?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM