Multiple Route Params in Grape - ruby

How do you get multiple route params in Grape to work in grape?
I can make this route work:
.../api/company/:cid
But when I try this:
.../api/company/:cid/members
.../api/company/:cid/members/:mid
I get errors.
Here's the code that works.
resource 'company' do
params do
optional :start_date, type: Date, desc: "Start date of range."
optional :end_date, type: Date, desc: "End date of range."
end
route_param :cid do
get do
{company_id: params[:cid]}
end
end

class API::Company < Grape::API
resource :company do
route_param :cid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid
# process get
end
resources :members do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/'
# process get
end
route_param :mid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/:mid'
# process get
end
end
end
end
end
end
You can do that that way. Or you can create two different resources file and mount Members to Company. Like so:
# api/company.rb
class API::Company < Grape::API
resource :company do
route_param :cid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid
# process get
end
mount API::Members
end
end
end
# api/member.rb
class API::Member < Grape::API
resources :members do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/'
# process get
end
route_param :mid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/:mid'
# process get
end
end
end
Hope that helps

Another way to do that is using a regexp to validate the ids.
resource 'company' :requirements => { :id => /[0-9]*/, :mid => /[0-9]*/ } do
get '/:id' do
# returns company
end
get ":id/members" do
members = Company.find_by_id(params[:id]).members
present members, :with => Members::MemberResponseEntity
end
get ":id/members/:mid" do
member = Member.find_by_id(params[:mid])
present member, :with => Members::MemberResponseEntity
end
end

Related

Routing Error: No route matches [GET] "/api/v1/courts/by_coordinates/41.89400864"

I have this error when I try to search in url by coordinates (saved as :latitude and :longitude in db using geocoder gem):
No route matches [GET] "/api/v1/courts/by_coordinates/41.89400864"
Here the code that I'm using.
module API
module V1
class Courts < Grape::API
version 'v1' # path-based versioning by default
format :json # We don't like xml anymore
resource :courts do
desc "Courts index"
get do
courts = Court.all
present courts, with: API::Entities::Court
end
desc "Search by address or city"
params do
requires :address, :type => String, :desc => "Court Address"
end
get "by_address/:address" do
courts = Court.near(params[:address])
present courts, with: API::Entities::Court
end
desc "Search by address or city and range"
params do
requires :address, :type => String, :desc => "Court Address"
requires :distance, :type => Integer, :desc => "Range of Distance"
end
get "by_address/:address/:distance" do
courts = Court.near(params[:address], params[:distance])
present courts.sort_by(&:distance), with: API::Entities::Court
end
desc "Search by coordinates"
params do
requires :latitude, :type => BigDecimal, :desc => "Court Latitude"
end
get "by_coordinates/:latitude" do
courts = Court.near(params[:latitude])
present courts, with: API::Entities::Court
end
end
end
end
end
I don't understand why if I use the research by address it works, but if I simply change the address with latitude params it stops to work.
Someone can halp me, please? Thank you in advance!

Hanami parameters whitelisting

Following the hanami docs, in order to block a admin parameter inside an action, I can use the following configuration:
params do
required(:email).filled
required(:address).schema do
required(:country).filled
end
end
def call(params)
puts params[:email] # => "alice#example.org"
puts params[:address][:country] # => "Italy"
puts params[:admin] # => nil
end
But this does not work for nested parameters, i.e.:
params do
required(:email).filled
required(:address).schema do
required(:country).filled
end
end
def call(params)
puts params[:email] # => "alice#example.org"
puts params[:address] # => { country: "Italy", admin: true }
puts params[:address][:admin] # => true
end
I was able to solve this by using select to filter out the undesirable parameters with a private method, but this does not seems like the Hanami way. What would be the proper way to do this whitelisting of nested parameters?
I have never had this issue when using Hanami Validations. Within the app directory there should be a validations folder which should have the same directory structure as your controllers, views, templates etc. Your validation file should look something like this:
# apps/web/validations/users/create.rb
module Web
module Validations
module Users
class Create < Web::Action::Params
predicates Web::Validations::CommonPredicates
validations do
required(:email).filled
required(:address).schema do
required(:country).filled
end
end
end
end
end
end
And then your controller should set the params to be filtered through the validation:
module Web
module Controllers
module Users
class Create
include Web::Action
params Web::Validations::Users::Create
def call(params); end
end
end
end
end

Mongo object's id as dirname for file upload in Sinatra/Mongoid

I'm working on Sinatra site which allows user to upload files. I got this action route:
post '/upload' do
params.delete 'submit'
UsrUpld.new(
:name => params[:name]
:created_at => Time.now
).save
params[:photos].each do |photo|
File.open('public/uploads/' + UsrUpld.id + '/' + photo[:filename], 'w') do |file|
file.write(photo[:tempfile].read)
end
end
redirect '/index'
end
I think this should create document in MongoDB with two fields name and created_at, the take file from POST request and write it in public/uploads/ID/FILE.jpg. But Pow returns me undefined method `id' for UsrUpld:Class. How to ask object's id in route with Mongoid?
Thank you in advance.
To ask an id, object should placed in variable which should contain exactly this object, so code should look like this:
post '/upload' do
params.delete 'submit'
u = UsrUpld.new(
:name => params[:name],
:created_at => Time.now
)
u.save
params[:photos].each do |photo|
unless File.exists?('public/media/' + u.id)
Dir.mkdir('public/media/' + u.id)
end
File.open('public/uploads/' + u.id + '/' + photo[:filename], 'w') do |file|
file.write(photo[:tempfile].read)
end
end
redirect '/index'
end
Also dir should exist before opening the file, thats why Dir.mkdir line added.

Syntax for declaring resource paths in Grape

I am looking for clarification on the syntax to declare API resource paths in Grape. The example below declares the resource paths "/items", "/items/:id", "/objects", and "/objects/:id". What I do not undestand is why the definition for "/items/:id" returns null?
class API < Grape::API
format :json
default_format :json
rescue_from :all, backtrace: true
resource :items do
desc "Returns an array of all items."
get do
ITEMS.find.to_a
end
desc "Returns an item by its id."
get '/:id' do
# hardcoding the document id returns the correct document
# ITEMS.find_one( "item_id" => 2519 )
# using the path parameter :id returns null, why???
ITEMS.find_one( "item_id" => params[:id] )
end
end
resource :objects do
desc "Returns an array of all objects."
get do
OBJECTS.find.to_a
end
##
# using the route_param syntax correctly returns the document
# resource path /objects/:id
##
desc "Returns an object by its id."
params do
requires :id, type: Integer
end
route_param :id do
get do
OBJECTS.find_one( "object_id" => params[:id] )
end
end
end
end
Your use of resource and route methods is ok.
You have a problem with parameter processing - params[:id] is a String by default. Your example hard-coded value that works is aFixnum (integer).
Probably your (not shown) code that queries the list on ITEMS is looking up integer values.
You could use ITEMS.find_one( "item_id" => params[:id].to_i ) to convert the param inline.
However, you probably should use a params description block to get Grape to convert for you, as you already are for OBJECTS:
desc "Returns an item by its id."
params do
requires :id, type: Integer
end
get '/:id' do
ITEMS.find_one( "item_id" => params[:id] )
end

mongoid document to_json including all embedded documents without ':include' on each one

Given an arbitrary mongoid document how do i convert it to JSON and include any embedded structures without specifically including those structures in my to_json statement.
For example:
#!/usr/bin/env ruby
require 'mongoid'
require 'json'
require 'pp'
class Doc
include Mongoid::Document
include Mongoid::Timestamps
field :doc_specific_info , type: String
embeds_many :persons
end
class Person
include Mongoid::Document
field :role , type: String
field :full_name , type: String
embeds_many :addresses
embedded_in :Doc
end
class Address
include Mongoid::Document
field :full_address , type: String
end
doc = Doc.new
doc.doc_specific_info = "TestReport"
p = Person.new
p.role = 'buyer'
p.full_name = 'JOHN DOE'
doc.persons << p
a = Address.new
a.full_address = '1234 nowhere ville'
doc.persons.first.addresses << a
# THIS STATEMENT
pp JSON.parse(doc.to_json(:include => { :persons => { :include => :addresses } } ) )
# GIVES ME
# {"_id"=>"4ee0d30fab1b5c5743000001",
# "created_at"=>nil,
# "doc_specific_info"=>"TestReport",
# "updated_at"=>nil,
# "persons"=>
# [{"_id"=>"4ee0d30fab1b5c5743000002",
# "full_name"=>"JOHN DOE",
# "role"=>"buyer",
# "addresses"=>
# [{"_id"=>"4ee0d30fab1b5c5743000003",
# "full_address"=>"1234 nowhere ville"}]}]}
# THIS STATEMENT
pp JSON.parse(doc.to_json() )
# GIVES ME
# {"_id"=>"4ee0d2f8ab1b5c573f000001",
# "created_at"=>nil,
# "doc_specific_info"=>"TestReport",
# "updated_at"=>nil}
So what I want is a statement something like this:
# FOR A STATEMENT LIKE THIS
pp JSON.parse(doc.to_json( :everything } ) )
# TO GIVE ME THE COMPLETE DOCUMENT LIKE SO:
# {"_id"=>"4ee0d30fab1b5c5743000001",
# "created_at"=>nil,
# "doc_specific_info"=>"TestReport",
# "updated_at"=>nil,
# "persons"=>
# [{"_id"=>"4ee0d30fab1b5c5743000002",
# "full_name"=>"JOHN DOE",
# "role"=>"buyer",
# "addresses"=>
# [{"_id"=>"4ee0d30fab1b5c5743000003",
# "full_address"=>"1234 nowhere ville"}]}]}
Does such a statement exist? If not then is my only alternative recusing the structure of the document and producing the proper includes myself? If there is another way to visualize the whole document that would be better?
This was answered by rubish in the forum but he didn't post an answer so I am doing that.
The answer is to use "doc.as_document.as_json" which will give you the whole document.
pp doc.as_document.as_json
You can override the #to_json method in your document to add all include.
class Person
def to_json(*args)
super(args.merge({:include => { :persons => { :include => :addresses } } } )
end
end
Now you can have all by doing
person.to_json()
If you want return the complete with only :everything option you can do :
class Person
def to_json(*args)
if args[0] == :everything
super(args.merge({:include => { :persons => { :include => :addresses } } } )
else
super(args)
end
end
end

Resources