合并 SoundCloud 交换令牌时获得'undefined method `merge!'



我正在开发一个与 SoundCloud 交互的应用程序,当我尝试保存我从服务器返回的exchange_token时遇到了问题(除其他外(,我真的可以使用一些帮助。

根据我得到的错误:

undefined method `merge!' for nil:NilClass

问题显然出在我的 sclouds_controller.rb 文件中的第 10 行(包括在下面(:

soundcloud_client.exchange_token(:code => params[:code])

这是在我正在使用的 SoundCloud gem 中调用一个方法。以下是 SoundCloud gem 中产生错误的行:

params.merge!(client_params)

可以在以下方法的第 23 行找到(取自 SoundCloud gem 中的 client.rb 文件(:

def exchange_token(options={})
  store_options(options)
  raise ArgumentError, 'client_id and client_secret is required to retrieve an access_token' if client_id.nil? || client_secret.nil?
  client_params = {:client_id => client_id, :client_secret => client_secret}
  params = if options_for_refresh_flow_present?
    {
      :grant_type => 'refresh_token',
      :refresh_token => refresh_token,
    }
  elsif options_for_credentials_flow_present?
    {
      :grant_type => 'password',
      :username => @options[:username],
      :password => @options[:password],
    }
  elsif options_for_code_flow_present?
    {
      :grant_type => 'authorization_code',
      :redirect_uri => @options[:redirect_uri],
      :code => @options[:code],
    }
  end
  params.merge!(client_params)
  response = handle_response(false) {
    self.class.post("https://#{api_host}#{TOKEN_PATH}", :query => params)
  }
  @options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)
  @options[:expires_at] = Time.now + response.expires_in if response.expires_in
  @options[:on_exchange_token].call(*[(self if @options[:on_exchange_token].arity == 1)].compact)
  response
end

但是,如果我像这样在我的 sclouds_controller.rb 文件中抛出"加薪":

def connected
  if params[:error].nil?
    raise
    soundcloud_client.exchange_token(:code => params[:code])

然后,在控制台中,手动粘贴以下行:

soundcloud_client.exchange_token(:code => params[:code])

我得到以下回复(对我来说,这似乎是成功的(:

$ #<SoundCloud::HashResponseWrapper access_token="xxxxx" expires_in=21599 refresh_token="xxxxx" scope="*">

知道为什么会这样吗?我试图了解我做错了什么,特别是因为我不确定我是否以正确的方式去做这件事。以下是我的一些代码,用于提供更多上下文。提前感谢!

sclouds_controller.rb:

class ScloudsController < ApplicationController
  before_action :authenticate_user!, only: [:connect, :connected]
def connect
  redirect_to soundcloud_client.authorize_url(:display => "popup")
end
def connected
  if params[:error].nil?
    soundcloud_client.exchange_token(:code => params[:code])
    unless user_signed_in?
      flash[:alert] = 's'
      redirect_to :login
    end
    current_user.update_attributes!({
       :soundcloud_access_token => soundcloud_client.access_token,
      :soundcloud_refresh_token => soundcloud_client.refresh_token,
         :soundcloud_expires_at => soundcloud_client.expires_at
    })
  end
  redirect_to soundcloud_client.redirect_uri
end
def disconnect
  login_as nil
  redirect_to root_path
end
private
  def soundcloud_client
  return @soundcloud_client if @soundcloud_client
  @soundcloud_client = User.soundcloud_client(:redirect_uri  => 'http://localhost:3000/sclouds/connected/')
  end
end

用户.rb:

class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable
attr_accessor :soundcloud_access_token, :soundcloud_refresh_token, :soundcloud_expires_at
has_one :scloud
      @SOUNDCLOUD_CLIENT_ID = 'xxxxx'
  @SOUNDCLOUD_CLIENT_SECRET = 'xxxxx'
              @REDIRECT_URI = 'xxxxx'
  def self.soundcloud_client(options={})
    options = {
          :client_id => @SOUNDCLOUD_CLIENT_ID,
      :client_secret => @SOUNDCLOUD_CLIENT_SECRET,
       :redirect_uri => @REDIRECT_URI
    }.merge(options)
    Soundcloud.new(options)
  end
  def soundcloud_client(options={})
    client = self.class.soundcloud_client(options)
    options= {
      :access_token  => soundcloud_access_token,
      :refresh_token => soundcloud_refresh_token,
      :expires_at    => soundcloud_expires_at
    }.merge(options)
    client.on_exchange_token do
      self.update_attributes!({
        :soundcloud_access_token  => client.access_token,
        :soundcloud_refresh_token => client.refresh_token,
        :soundcloud_expires_at    => client.expires_at
      })
    end
    client
  end
end
options= {
      :access_token  => soundcloud_access_token,
      :refresh_token => soundcloud_refresh_token,
      :expires_at    => soundcloud_expires_at
    }.merge(options)

这是如何工作的?

您正在尝试设置options本地 var(这是方法中 options 参数的副本(,然后您正在尝试合并options?对我来说看起来像一个自我参照循环...


方法

其次,您提到错误是由以下行引起的:

exchange_token(:code => params[:code])

我在您的文档中看不到此方法?merge!方法显然是在此方法中的某个地方引起的 - 我们需要知道它被调用的位置,以便我们可以修复它!

我可以看到一个提到merge,我在上面已经查询过了


更新

感谢您发布代码!

我认为@mischa是对的 - 但我会保留这段代码来向您展示我会看的内容。

我只能给出一个意见,因为我以前从未使用过这个 gem -gem 代码中有两个调用merge!

params.merge!(client_params)
@options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)

我会看看这个:

参数

参数哈希在本地填充,如下所示:

params = if options_for_refresh_flow_present?
    {
      :grant_type => 'refresh_token',
      :refresh_token => refresh_token,
    }
  elsif options_for_credentials_flow_present?
    {
      :grant_type => 'password',
      :username => @options[:username],
      :password => @options[:password],
    }
  elsif options_for_code_flow_present?
    {
      :grant_type => 'authorization_code',
      :redirect_uri => @options[:redirect_uri],
      :code => @options[:code],
    }
  end

这可能是问题的原因,因为它填充了elsif(而不是else(。这意味着您必须确保将正确的代码传递给系统

@options

引用@options但未声明

我想它是在 gem 代码的另一部分声明的,这意味着您需要确保它在那里。这可能会在 gem 初始化时设置(即当您在 SC 和您的应用程序之间设置身份验证时(

如果您没有正确设置@options,则可能意味着您没有正确调用 gem,从而导致错误。

尝试mischa 的修复程序,看看是否可以为您解决问题。


初始 化

需要注意的事项 - 在系统中包含 gem 的约定是使用initializer

如果您设置了一个初始值设定项来创建一个名为 SOUNDCLOUD 的新常量,则可以在整个应用程序中引用该常量(修复您在此处遇到的任何错误(

如果你愿意,我可以为你写一些代码

我认为

@RickPeck非常接近,因为问题实际上出在这段代码摘录中:

params = if options_for_refresh_flow_present?
    {
      :grant_type => 'refresh_token',
      :refresh_token => refresh_token,
    }
  elsif options_for_credentials_flow_present?
    {
      :grant_type => 'password',
      :username => @options[:username],
      :password => @options[:password],
    }
  elsif options_for_code_flow_present?
    {
      :grant_type => 'authorization_code',
      :redirect_uri => @options[:redirect_uri],
      :code => @options[:code],
    }
  end

@options填充在行store_options(options) 中。所以问题是options_for_refresh_flow_present?options_for_credentials_flow_present?options_for_code_flow_present?都不是真的。

代码

的相关选项是代码流:

def options_for_code_flow_present?
  !!(@options[:code] && @options[:redirect_uri])
end

期望options同时拥有:code:redirect_uri.在您的代码中,您只传递 :code .添加一个:redirect_uri,你应该很高兴。

@Mischa建议的内容可能会为您解决此问题,因为当您设置它时,您的:redirect_uri nil......

相关内容

最新更新