неопределенный метод API-запрос `map' [закрыт]

Я следовал руководству по интеграции стороннего API с Ruby on Rails, но получаю сообщение об ошибке.

undefined method `map' for

{"number"=>12} permitted: false>:ActionController::Parameters

который указывает на request.rb

query_string = query.map{|k,v| "#{k}=#{v}"}.join("&")

Полный код

recipes_controller.rb

class RecipesController < ApplicationController

  def index
    @tag = query.fetch(:tags, 'all')
    @refresh_params = refresh_params
    @recipes, @errors = Spoonacular::Recipe.random(query, clear_cache)
  end

  def show
    @recipe = Spoonacular::Recipe.find(params[:id])
  end

  private
   def query
     params.permit(:query).fetch(:query, {})
   end

  def clear_cache
    params[:clear_cache].present?
  end

  def refresh_params
    refresh = { clear_cache: true }
    refresh.merge!({ query: query }) if query.present?
    refresh
  end
end

app/services/spoonacular/recipes.rb

module Spoonacular
  class Recipe < Base
    attr_accessor :aggregate_likes,
                  :dairy_free,
                  :gluten_free,
                  :id,
                  :image,
                  :ingredients,
                  :instructions,
                  :ready_in_minutes,
                  :title,
                  :vegan,
                  :vegetarian

    MAX_LIMIT = 12
    CACHE_DEFAULTS = { expires_in: 7.days, force: false }

    def self.random(query = {}, clear_cache)
      cache = CACHE_DEFAULTS.merge({ force: clear_cache })
      response = Spoonacular::Request.where('recipes/random', cache, query.merge({ number: MAX_LIMIT }))
      recipes = response.fetch('recipes', []).map { |recipe| Recipe.new(recipe) }
      [ recipes, response[:errors] ]
    end

    def self.find(id)
      response = Spoonacular::Request.get("recipes/#{id}/information", CACHE_DEFAULTS)
      Recipe.new(response)
    end

    def initialize(args = {})
      super(args)
      self.ingredients = parse_ingredients(args)
      self.instructions = parse_instructions(args)
    end

    def parse_ingredients(args = {})
      args.fetch("extendedIngredients", []).map { |ingredient| Ingredient.new(ingredient) }
    end

    def parse_instructions(args = {})
      instructions = args.fetch("analyzedInstructions", [])
      if instructions.present?
        steps = instructions.first.fetch("steps", [])
        steps.map { |instruction| Instruction.new(instruction) }
      else
        []
      end
    end
  end
end

приложение/services/spoonacular/base.rb

module Spoonacular
  class Base
    attr_accessor :errors

    def initialize(args = {})
      args.each do |name, value|
        attr_name = name.to_s.underscore
        send("#{attr_name}=", value) if respond_to?("#{attr_name}=")
      end
    end
  end
end

приложение/services/spoonacular/request.rb

module Spoonacular
  class Request
    class << self
      def where(resource_path, cache, query = {}, options = {})
        response, status = get_json(resource_path, cache, query)
        status == 200 ? response : errors(response)
      end

      def get(id, cache)
        response, status = get_json(id, cache)
        status == 200 ? response : errors(response)
      end

      def errors(response)
        error = { errors: { status: response["status"], message: response["message"] } }
        response.merge(error)
      end

      def get_json(root_path, cache, query = {})
        query_string = query.map{|k,v| "#{k}=#{v}"}.join("&")
        path = query.empty?? root_path : "#{root_path}?#{query_string}"
        response =  Rails.cache.fetch(path, expires_in: cache[:expires_in], force: cache[:force]) do
          api.get(path)
        end
        [JSON.parse(response.body), response.status]
      end

      def api
        Connection.api
      end
    end
  end
end

app/services/spoonacular/connection.rb

require 'faraday'
require 'json'
module Spoonacular
  class Connection
    BASE = 'https://spoonacular-recipe-food-nutrition-v1.p.mashape.com'

    def self.api
      Faraday.new(url: BASE) do |faraday|
        faraday.response :logger
        faraday.adapter Faraday.default_adapter
        faraday.headers['Content-Type'] = 'application/json'
        faraday.headers['X-Mashape-Key'] ='key'
      end
    end
  end
end

Спасибо за любую помощь.


person Kristis    schedule 23.04.2018    source источник
comment
Включите пути к этим файлам в свой вопрос. Также вы пытались переместить Request в пространство имен Spoonacular::Recipe? И если этого не должно быть, вы пытались сослаться на глобальную область видимости с помощью ::Request.where(...)?   -  person Martin Zinovsky    schedule 24.04.2018
comment
Бьюсь об заклад, путь к request.rb содержит /spoonacular/recipe/, поэтому рельсы пытаются разрешить константу Request на основе этого   -  person Martin Zinovsky    schedule 24.04.2018
comment
Так ::Request.where(...) сработало? Также я бы предложил переместить request.rb и connection.rb в пространство имен app/services/spoonacular/. В этом случае вы называете это как Spoonacular::Request   -  person Martin Zinovsky    schedule 24.04.2018
comment
Вы имеете в виду вот так? response = ::Request.where('recipes/random', cache, query.merge({ number: MAX_LIMIT })) Я переместил все файлы в папку Spoonacular и теперь получаю сообщение об ошибке --> Невозможно автоматически загрузить константу Spoonacular::Request   -  person Kristis    schedule 24.04.2018
comment
Вы окружили class Request module Spoonacular после того, как переместили request.rb?   -  person Martin Zinovsky    schedule 24.04.2018
comment
Да, я сделал это сейчас. Теперь я получаю ту же ошибку, что и раньше --› неопределенный метод `map' для ‹ActionController::Parameters {число=›12} разрешено: false›:ActionController::Parameters   -  person Kristis    schedule 24.04.2018


Ответы (1)


У вас есть 2 отдельные ошибки здесь.

неинициализированная константа Spoonacular::Recipe::Request

Это можно исправить, явно установив область верхнего уровня для класса Request:

::Request.where(...)

Это применимо, если вы храните Request файл в app/spoonacular/request.rb. Но я предлагаю переместить его в app/services/spoonacular/, где находятся все остальные ваши классы, связанные с spoonacular. Так что в этом случае вам нужно окружить class Request в module Spoonacular. После этого вы можете называть это так:

Spoonacular::Request.where(...)

То же самое касается класса Connection.

SO ответьте об операторе разрешения области


неопределенный метод `map' для {"number"=>12} разрешено: false>:ActionController::Parameters

Это происходит из частного метода query в recipes_controller.rb. params — это объект ActionController::Parameters, и для извлечения из него значений вам необходимо сначала разрешить их:

def query
  params.permit(:query).to_h
end

Теперь он должен вернуть объект Hash.

Вот подробный ответ на SO об этом

Руководство RubyOnRails по сильным параметрам

person Martin Zinovsky    schedule 24.04.2018
comment
Я внес изменения, как вы сказали, но все еще та же проблема. Я обновил вопрос. - person Kristis; 24.04.2018
comment
@Kristis, я действительно не вижу, что ты изменил - person Martin Zinovsky; 24.04.2018
comment
Я внес изменения в метод запроса контроллера и в метод self.random файла recipe.rb. Кроме того, я обвел соединение и класс запроса в модуле Spoonular. - person Kristis; 24.04.2018
comment
@Kristis В вашем вопросе все еще есть старая версия метода query. - person Martin Zinovsky; 24.04.2018
comment
@Kristis Ознакомьтесь с Руководством RubyOnRails по сильным параметрам. Это поможет вам понять вашу вторую проблему - person Martin Zinovsky; 24.04.2018
comment
Я разрешил запрос с помощью этого кода --> params.permit(query: {}), но получил ту же ошибку --> неопределенный метод `map' для ‹ActionController::Parameters {number=›12} разрешено: true›:ActionController: :Параметры - person Kristis; 25.04.2018
comment
@Kristis Попробуйте преобразовать его в объект Hash с помощью query.to_h - person Martin Zinovsky; 25.04.2018
comment
@MartinZinovsky Я следую тому же руководству (очевидно) и получаю те же ошибки. Добавление to_h дает ошибку: unable to convert unpermitted parameters to hash - person Liz; 10.02.2019