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. But is this not the case? I'm confused because a method accessible in the controller
isn't recognised when moved outside the file. Is there some way I could require the gem to run the code below in the command
file instead of the controller
?
I'm using an authentication gem called sorcery
to implement an OAuth login system. There are some methods like login_from(provider)
that the gem provides, which can be accessed from my controller just fine.
# 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
. 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.
That would be an incorrect impression.
As you can see, Sorcery::Engine
causes ActionController::Base
to include methods defined in Sorcery::Controller
, here:
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:
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:
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:
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
.
Is there some way I could require the gem to run the code below in the command file instead of the controller?
Maybe. Maybe not. As you can see, login_from
is using other sorcery
methods and methods inherited from ActionController::Base
. So, you would need to make sure all of those methods are available in Authentication::LoginCommand
in order for login_from
to behave appropriately.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.