ActiveResource задыхается при запросе json на freebase

У меня есть настройка ActiveResource для использования API-интерфейса freebase в json, и он должен работать нормально, за исключением того, что возврат json freebase вызывает взрыв ActiveResource.

NoMethodError: undefined method `collect!' for #<Hash:0x007fd674831dd0>

Как я могу определить собственный анализатор json, чтобы исправить все, что не так?

class Freebase < ActiveResource::Base
  self.site = "https://www.googleapis.com/"
  self.format = :json

  def self.search(word)
    self.find(:all, :from => "/freebase/v1/search/", :params => { :query => word })
  end

   #https://www.googleapis.com/freebase/v1/search?query=nirvana

  #Freebase.get('search', :query => 'nirvana')

end

Json возвращается:

https://www.googleapis.com/freebase/v1/search?query=nirvana

{"status":"200 OK","result":[{"mid":"/m/05b3c","name":"Nirvana","notable":{"name":"Belief","id":"/religion/belief"},"lang":"en","score":67.540009},{"mid":"/m/0b1zz","name":"Nirvana","notable":{"name":"Musical Artist","id":"/music/artist"},"lang":"en","score":64.311432},{"mid":"/m/092bf5","name":"Buddhism","notable":{"name":"Religion","id":"/religion/religion"},"lang":"en","score":33.647118},{"mid":"/m/02_6qh","name":"Nirvana","notable":{"name":"Film","id":"/film/film"},"lang":"en","score":30.068491},{"mid":"/m/01h89tx","name":"Nirvana","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":27.799274},{"mid":"/m/01rn9fm","name":"Nirvana","notable":{"name":"Musical Group","id":"/music/musical_group"},"lang":"en","score":27.445602},{"mid":"/m/015k7","name":"Gautama Buddha","notable":{"name":"Deity","id":"/religion/deity"},"lang":"en","score":24.129679},{"mid":"/m/01rkx5","name":"Mahayana Mahaparinirvana Sutra","lang":"en","score":22.359026},{"mid":"/m/03d7q7v","name":"Nirvana","lang":"en","score":21.034473},{"mid":"/m/055ym7w","name":"Nirvana bootleg recordings","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":19.241596},{"mid":"/m/0122_j","name":"Nevermind","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":18.366383},{"mid":"/m/04n7mt","name":"Nirvana fallacy","lang":"en","score":17.212397},{"mid":"/m/0484q","name":"Kurt Cobain","notable":{"name":"Musician","id":"/m/09jwl"},"lang":"en","score":16.594929},{"mid":"/m/027_k8j","name":"Nirvana","lang":"en","score":16.336584},{"mid":"/m/0285c","name":"Dave Grohl","notable":{"name":"Musician","id":"/m/09jwl"},"lang":"en","score":16.115103},{"mid":"/m/068shv","name":"Smells Like Nirvana","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":15.350652},{"mid":"/m/01kq85c","name":"Manic Nirvana","notable":{"name":"Musical Album","id":"/music/album"},"lang":"en","score":15.275189},{"mid":"/m/0437sc","name":"Lithium","notable":{"name":"Composition","id":"/music/song"},"lang":"en","score":14.637386},{"mid":"/m/055kh1","name":"Mechanus","lang":"en","score":14.621847},{"mid":"/m/01f1vf","name":"Lucifer","notable":{"name":"Fictional Character","id":"/fictional_universe/fictional_character"},"lang":"en","score":13.504528}],"cursor":20,"cost":11,"hits":3104}

Использование Rails 3.2.1

ОБНОВИТЬ:

Нашел эту проблему, но до сих пор не уверен, как ее преодолеть. https://github.com/rails/rails/issues/2318


person ere    schedule 10.03.2012    source источник
comment
Код, которым вы поделились, не включает призыв к сбору! что вызывает вашу ошибку. Пожалуйста, предоставьте более точную информацию о вашей проблеме.   -  person Jon    schedule 10.03.2012
comment
Я делаю (или пытаюсь) что-то похожее на этот пример: github.com/barrettclark/wordnik/blob/master/app/models/api/ Что работает. Может я что-то не так делаю?   -  person ere    schedule 10.03.2012
comment
похоже на: github.com/rails/rails/issues/2318   -  person ere    schedule 10.03.2012


Ответы (2)


Проблема в том, что возвращаемый JSON формируется не так, как ожидает ActiveResource. ARes не ожидает всех возвращаемых метаданных, он ожидает только то, что находится в results части ответа.

Чтобы быть явным, вы получаете обратно:

{"status":"200 OK","result":[{"mid":"/m/05b3c","name":"Nirvana","notable":{"name" ...

Но ARes хочет:

[{"mid":"/m/05b3c","name":"Nirvana","notable":{"name"...

Самое простое (и, возможно, самое грязное) решение, которое я могу придумать, — перезаписать закрытый метод ActiveResource::Base#find_every в вашей модели следующим образом:

class Freebase < ActiveResource::Base

  self.site = "https://www.googleapis.com/"
  self.format = :json

  def self.search(word)
    self.find(:all, :from => "/freebase/v1/search/", :params => { :query => word })
  end

  private

  def self.find_every(options)
    begin
      case from = options[:from]
      when Symbol
        instantiate_collection(get(from, options[:params]))
      when String
        path = "#{from}#{query_string(options[:params])}"
        instantiate_collection(format.decode(connection.get(path, headers).body['result']) || [])
      else
        prefix_options, query_options = split_options(options[:params])
        path = collection_path(prefix_options, query_options)
        instantiate_collection( (format.decode(connection.get(path, headers).body['result']) || []), prefix_options )
      end
    rescue ActiveResource::ResourceNotFound
      # Swallowing ResourceNotFound exceptions and return nil - as per
      # ActiveRecord.
      nil
    end
  end

end

Единственное изменение, которое я сделал, это вызовы методов .body теперь .body['result']. С этим дополнением #instantiate_collection теперь будет получать массив, как ожидается, а не хэш.

В конечном счете, хотя это должно устранить ошибку, я не уверен, что это решит все ваши проблемы, поскольку вам могут понадобиться некоторые данные, которые теряются при использовании этого метода. Мое предложение состояло бы в том, чтобы отказаться от ActiveResource, если вы можете, и использовать что-то вроде RestClient и построить свою модель вокруг этого. Другая причина такого подхода заключается в том, что семантика в ее нынешнем виде нарушена. Вы не получите обратно коллекцию Freebases. Вы используете Freebase API для получения результатов поиска.

person Derek Harmel    schedule 10.03.2012
comment
Да, я начал приходить к такому же выводу. Я надеялся на магию ActiveResource, но обнаружил, что колодец пуст. Вопрос, который у меня есть сейчас, заключается в том, как я могу создавать методы в стиле рельсов из возвращенного хэша? - person ere; 11.03.2012

Я последовал совету Дерека и отказался от ActiveResource...

для всех остальных я использовал Faraday и Faraday_Middleware.

class Freebase

  def self.search(id)
    connection = Faraday.new 'https://www.googleapis.com/freebase/v1' do |conn|

      conn.adapter Faraday.default_adapter
      conn.use FaradayMiddleware::ParseJson
      #conn.use Faraday::Response::Mashify
    end

    response = connection.get do |req|
      req.url('search', :query => id, :limit => 10)#, :filter => '(any namespace:/wikipedia/en_id namespace:/authority/imdb/title)')#  #:filter => '(any namespace:/wikipedia/en_id namespace:/authority/imdb/title namespace:/authority/netflix/movie)'  , :with => 'commons'
    end

  end

Предположительно, вы можете использовать несколько встроенных модулей для создания методов в стиле рельсов из возвращаемого хэша, но я еще не разобрался с этой частью. ActiveResource кажется хорошим только в других приложениях Rails или маршрутах RESTFUL, которые идеально имитируют его.

person ere    schedule 10.03.2012