我正在开发一个与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文件中抛出'raise',就像这样:
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="*">
知道为什么会这样吗?我正在努力学习我做错了什么,特别是因为我不确定我是否会以正确的方式解决这个问题。这是我的一些代码,用于更多上下文。提前谢谢!
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
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
答案 0 :(得分:0)
options= {
:access_token => soundcloud_access_token,
:refresh_token => soundcloud_refresh_token,
:expires_at => soundcloud_expires_at
}.merge(options)
这是如何工作的?
您尝试设置options
本地var(与方法中的options
参数重复),然后您尝试合并{{1 }}?看起来像是一个自我引用的循环......
方式强>
其次,你提到错误是由这一行引起的:
options
我在文档中看不到这种方法? exchange_token(:code => params[:code])
方法显然是在这个方法的某个地方引起的 - 我们需要知道它被调用的位置,所以我们可以修复它!
我可以看到一个merge!
的提及,我在上面提到过
<强>更新强>
感谢发布代码!
我认为merge
是对的 - 但我会保留此代码以向您展示我所看到的内容。
我只能发表意见,因为我之前从未使用过这个宝石 - 在宝石代码中有两次调用@mischa
:
merge!
我看看这个:
<强> PARAMS 强>
params hash在本地填充如下:
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
)。这意味着您必须确保将正确的代码传递给系统
<强> @options 强>
引用 else
但未声明
我想它是在宝石代码的另一部分声明的,这意味着你需要确保它在那里。这可能会在宝石初始化时设置(当您在SC和您的应用之间设置身份验证时为I.E)
如果您没有正确设置@options
,则可能意味着您没有正确调用gem,因此错误。
尝试@options
修复,看看是否可以解决问题。
<强>初始化程序强>
需要注意的事项 - 在系统中包含宝石的惯例是使用mischa
如果您设置了一个初始化程序,它创建了一个名为SOUNDCLOUD的新常量,那么您可以在整个应用程序中引用它(解决您在此处遇到的任何错误)
如果你想要
,我可以为你写一些代码答案 1 :(得分:0)
@RickPeck我认为非常接近,因为问题实际上在于这段代码excerpt:
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?
都不会返回true。
代码的相关选项为code flow:
def options_for_code_flow_present? !!(@options[:code] && @options[:redirect_uri]) end
希望options
同时拥有:code
和 :redirect_uri
。在您的代码中,您只传递:code
。添加一个:redirect_uri
,你应该很高兴。
@Mischa建议可能会为您解决这个问题,因为您设置:redirect_uri
时nil
为{{1}} ...