0

I followed tutorial how to integrate 3rd party api with a ruby on rails but I get an error

undefined method `map' for

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

which points to request.rb

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

Full code

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 

app/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 

app/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 

Thank you for any help.

6
  • Include paths to these files in your question. Also did you try to move Request under Spoonacular::Recipe namespace? And if it shouldnt be there did you try to refer to global scope with ::Request.where(...)? Commented Apr 24, 2018 at 6:41
  • I bet path to request.rb contains /spoonacular/recipe/ so rails tries to resolve Request constant based on that Commented Apr 24, 2018 at 9:08
  • So did ::Request.where(...) work? Also I would suggest to move request.rb and connection.rb to app/services/spoonacular/ namespace. In that case you call it like Spoonacular::Request Commented Apr 24, 2018 at 12:22
  • You mean like this? response = ::Request.where('recipes/random', cache, query.merge({ number: MAX_LIMIT })) I moved all files to spoonacular folder and now I get error --> Unable to autoload constant Spoonacular::Request Commented Apr 24, 2018 at 13:32
  • Did you encircle class Request with module Spoonacular after you move request.rb? Commented Apr 24, 2018 at 13:35

1 Answer 1

1

You have 2 separate errors here.

uninitialized constant Spoonacular::Recipe::Request

This one you can fix by explicitly setting top-level scope for Request class:

::Request.where(...) 

It applies if you keep Request file in app/spoonacular/request.rb. But I suggest to move it to app/services/spoonacular/ where all your other spoonacular related classes are. So in this case you need to encircle class Request in module Spoonacular. After that you can call it like that:

Spoonacular::Request.where(...) 

Same goes for class Connection.

SO answer about scope resolution operator


undefined method `map' for {"number"=>12} permitted: false>:ActionController::Parameters

This one comes from private query method in recipes_controller.rb. params is ActionController::Parameters object and in order to retrieve values from it you need to permit them first:

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

Now it should return Hash object.

Here is detailed answer on SO about that

RubyOnRails Guide about strong params

Sign up to request clarification or add additional context in comments.

8 Comments

I made changes like you said, but still same problem. I updated question.
@Kristis I dont really see what you have changed
I made changes in controller query method and in recipe.rb file self.random method. Also, I encircled connection and request class in module Spoonacular.
@Kristis There is still old version of query method in your question.
@Kristis Check out RubyOnRails Guide about strong params. It will help you understand your second issue
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.