获得未定义的方法`合并!'合并SoundCloud交换令牌时

时间:2014-05-14 05:50:39

标签: ruby-on-rails ruby api authentication soundcloud

我正在开发一个与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="*">

知道为什么会这样吗?我正在努力学习我做错了什么,特别是因为我不确定我是否会以正确的方式解决这个问题。这是我的一些代码,用于更多上下文。提前谢谢!

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

user.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

2 个答案:

答案 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_urinil为{{1}} ...