Syntax for declaring resource paths in Grape - ruby

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

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!

Can't understand Grape API route param

I am having a lot of trouble understanding Grape API, specifically route_param and how it works with just params.
Consider this code:
desc "Return a status."
params do
requires :id, type: Integer, desc: "Status id."
end
route_param :id do
get do
Status.find(param[:id])
end
end
What route does this block produce? I get that this is a get request, but why is it wrapped in route_param block? Why can't it be in params block?
Your block produces this route:
http://yourdomain.com/<resource>/<id>
Note that your code and the code below do the same thing and produce the same route:
desc "Return a status."
params do
requires :id, type: Integer, desc: "Status id."
end
get ':id' do
Status.find(params[:id])
end
You can use route_param to group methods that receive the same params, for example:
resource :categories do
route_param :id do
get do # produces the route GET /categories/:id
end
put do # produces the route PUT /categories/:id
end
end
end

Multiple Route Params in Grape

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

How to save received string parameters in array field?

How to extract and save array from string parameter? I'm trying convert string beafore_create but this doesn't work. When I comment before_create :waypoints Mongoid throw error:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"nehoT1fnza/ZW4XB4v27uZsfFjjOu/ucIhzMmMKgWPo=",
"trip"=>{
"title"=>"test",
"description"=>"test",
"waypoints"=>"[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
}
}
Completed 500 Internal Server Error in 1ms
Mongoid::Errors::InvalidType (Field was defined as a(n) Array, but received a String with the value "[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]".):
EDIT Thanks for help, now it work but I don't know whether following approach is good. I remove before_create and change parameter name from waypoints to waypoints_s and def waypoints to def waypoints_s:
#Parameters:
#"waypoints"=>"[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
"waypoints_s"=>"[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
class Trip
include Mongoid::Document
field :title, :type => String
field :description, :type => String
field :waypoints, :type => Array
#before_create :waypoints
#def waypoints=(arg)
def waypoints_s=(arg)
if (arg.is_a? Array)
##waypoints = arg
self.waypoints = arg
elsif (arg.is_a? String)
##waypoints = arg.split(',')
self.waypoints = JSON.parse(arg)
else
return false
end
end
end
class TripsController < ApplicationController
def create
#trip = Trip.create(params[:trip])
#trip.save
end
end
Parse the string as a JSON object:
require 'json'
waypoints = "[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
JSON.parse(waypoints)
=> [[52.40637, 16.92517], [52.40601, 16.925040000000003], [52.405750000000005, 16.92493], [52.40514, 16.92463], [52.404320000000006, 16.924200000000003]]
You need to use serialize http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-c-serialize
This method serialize your object to database by YAML format (let's say just text with some format).
class Trip < ActiveRecord::Base
serialize :waypoints
end
trip = Trip.create( :waypoints => [[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]])
Trip.find(trip.id).waypoints # => [[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]

MongoMapper custom validation

I have this ruby class with an array of links. As it is now I'm able to save a Paper object even if the array contains links that are not valid urls. I have a method that runs through the array and validates the urls and returns false if a url is invalid. But I want to get an error message when I try to call Paper.save. Is that possible?
class Paper
include MongoMapper::Document
key :links, Array
validates_presence_of :links
def validate_urls
reg = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix
status = []
links.each do |link|
if link.match(reg)
status.push('true')
else
if "http://#{link}".match(reg)
status.push('true')
else
status.push('false')
end
end
end
if status.include?('false')
return false
else
return true
end
end
end
If you're using MongoMapper from GitHub (which supports ActiveModel), see http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate
class Paper
include MongoMapper::Document
key :links, Array
validates_presence_of :links
validate :validate_urls
def validate_urls
reg = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix
status = []
links.each do |link|
if link.match(reg)
status.push('true')
else
if "http://#{link}".match(reg)
status.push('true')
else
status.push('false')
end
end
end
if status.include?('false')
# add errors to make the save fail
errors.add :links, 'must all be valid urls'
end
end
end
Not sure if that code works with the 0.8.6 gem but it might.
Also, it doesn't apply in this case but if it weren't an array you could smash it all into a single line:
key :link, String, :format => /your regex here/

Resources