I am using grape redtful-api. I am Unable to inherit common_params in Grape. I defined common
_params in class API1 and called it in API2 throws the error. How can I change the code to make this work?
module Example
class API1 < Grape::API
version 'v1'
format :json
prefix :api
resource :exc1 do
common_params = proc do
requires :param1
requires :param2
end
params(&common_params)
get :params_by_pair do
p1 = params[:param1]
p2 = params[:param2]
response = "https://www.example1.com/#{p1}_#{p2}"
end
end
end
end
module Example
class API2 < API1
version 'v1', using: :header, vendor: 'twitter'
format :json
prefix :api
resource :exc2 do
params(&common_params)
get :params_by_pair do
p1 = params[:param1]
p2 = params[:param2]
response = "https://www.example2.com/#{p1}_#{p2}"
end
end
end
end
The issue doesn't have much to do with Grape but rather the way variables' scope works in Ruby. common_params is just a local, it won't survive the end of the scope. You could make it work by using a class instance variable or similar but let's not go there. The way you're supposed to share helpers across different grapes is through a dedicated module.
module Example
module SharedHelpers
extend Grape::API::Helpers
params :common_params do
requires :param1
requires :param2
end
end
end
And now in the different grapes you need to "include" the module and use the helper.
module Example
class API1 < Grape::API
helpers SharedHelpers # !!!
version 'v1'
format :json
prefix :api
resource :exc1 do
params do
use :common_params # !!!
end
get :params_by_pair do
...
end
end
end
end
To use the helpers in the API2 grape, use the same technique.
Related
As a learning exercise I am making a simplified Rails-like MVC framework.
Here is a simplified version of the flow. The framework and app are combined here.
1) Initiate a new Rack application.
# config.ru
require_relative 'config/application.rb'
run Application.new
2) Instantiate a Rack request object, then instantiate a new Router class instance, passing the request object, and call the resolve method.
# config/application.rb
require_relative '../lib/router.rb'
class Application
def call(env)
request = Rack::Request.new(env)
Router.new(request).resolve
end
end
3) When the Router instance is created, I am building a #routes variable assigned to a hash of the application's routes and their corresponding controller and action. Like Rails this is a block. The draw and match methods are defined in this same Router class. Draw runs the block and the match method adds each route to the #routes hash. This works fine.
# lib/router.rb
class Router
attr_reader :request, :routes
def initialize(request)
#request = request
#routes = {}
draw do
match "/", "pages#home"
match "/contact", "pages#contact"
match "/about", "pages#about"
end
end
def draw(&block)
instance_eval(&block)
end
def match(url, controller_action)
#routes["#{url}"] = controller_action
end
# more code... def resolve, etc.
end
Like Rails I want the draw block in it's own file so the routes can be set in the app's config/routes.rb file and manipulated in the framework's lib/router.rb file. How do I do this? Everything I've tried throws errors.
Although it's not so pretty like Rails you can try to adjust it to your case:
File routes.rb:
class Routes
def self.routes
Proc.new do
match 'url', 'foo#bar'
match 'url2', 'foo#baz'
end
end
end
File router.rb:
require_relative './routes'
class Router
attr_reader :routes
def initialize
#routes = {}
draw Routes.routes
end
def draw(block)
instance_eval(&block)
end
def match(url, handler)
#routes[url] = handler
end
end
So you can have defined in a separated file routes:
p Router.new.routes # => {"url"=>"foo#bar", "url2"=>"foo#baz"}
I have api set up like this:
class Dashboard < Api
def self.inherited(subclass)
super
subclass.instance_eval do
prefix 'dashboard'
#...
end
end
def self.company_id(path)
':company_id' + path
end
helpers do
def current_company
#current_company ||= Company.find(params[:company_id]) if params[:company_id]
end
end
end
Problem: I inherit class Employee from Dashboard, and what I want to achieve: resource, which inherits from Dashboard, should be accessed by it's namespace '/dashboard/companies/:company_id/employees', with current_company working correct.
I feel tiring each time to provide full route instead of namespace convenience:
get 'companies/:company_id/employees'
#...
end
But this won't give result needed:
namespace :companies do
namespace :employees do
...
end
end
From what I undertand, you're looking for dynamic namespace, isn't it? You can define dynamic namespaces using a string instead of just a symbol. In the given string, each :something part indicates a parameter, same thing as what you may be used to in Rails or Sinatra routes syntax. In the endpoints, you'll then be able to access params[:something] as you usually do.
For instance, in your case, you might use something like this :
namespace 'companies/:company_id' do
namespace :employees do
get do
# Will respond with the available params (containing :company_id)
body params
end
end
end
I'm using grape for restful service. As I know, :resource will define a path in url. For example:
module Sample
module V1
module Order
class GetCheckInListApi < ApplicationApi
resource :check_in_list do
get root: :data do
# business code here
end
end
end
end
end
end
The url for example, will be: localhost:8000/api/v1/check_in_list. I want the url should be: localhost:8000/api/v1/check_in/list. How can I do in Grape ?
Thanks
Does the resource has to be a :check_in_list? You could change it to :chect_in and add a /list route:
class GetCheckInListApi < ApplicationApi
resource :check_in do
get '/list' do
# business code here
end
end
end
I'm using grape gem; ref https://github.com/intridea/grape.
Could you show me how to build named path like "twitter_api_v1_statuses_path" ?
My code is as follows
module Twitter
class API < Grape::API
version 'v1', using: :header, vendor: 'twitter'
format :json
prefix :api
resource :statuses do
desc "Return a public timeline."
get :public_timeline do
Status.limit(20)
end
end
end
I'm assuming that you want an URL like this http://yourdomain.com/api/v1/statuses/public_timeline. In this scenario, you have only one problem in your API class and it's related to the versioning strategy you've chosen. The :header strategy search for the API version in a specific header and that's not what you're looking for. Change it to :path.
module Twitter
class API < Grape::API
version 'v1', using: :path, vendor: 'twitter'
format :json
prefix :api
resource :statuses do
desc "Return a public timeline."
get :public_timeline do
Status.limit(20)
end
end
end
end
Objective: Use grape Shared Params from a helper module, without having to add the syntax helpers Helper::Module on every mounted API.
Example code that works:
# /app/api/v1/helpers.rb
module V1
module Helpers
extend Grape::API::Helpers
params :requires_authentication_params do
requires :user_email, type: String
requires :authentication_token, type: String
end
end
end
# /app/api/api.rb
class API < Grape::API
format :json
version 'v1', using: :path
mount V1::A
mount V1::B
end
# /app/api/v1/a.rb
module V1
class A < Grape::API
helpers V1::Helpers
desc 'SampleA'
params do
use :requires_authentication_params
end
get 'sample_a/url' do
#...
end
end
end
# /app/api/v1/B.rb
module V1
class B < Grape::API
helpers V1::Helpers
desc 'SampleB'
params do
use :requires_authentication_params
end
get 'sample_b/url' do
#...
end
end
end
The problem arise when I try to move the helpers V1::Helpers call from A and B to the API class that mounts them, throwing the exception:
block (2 levels) in use': Params :requires_authentication_params not found! (RuntimeError)
As an interesting note, the module does get included, because if I add any instance method to the class V1::Helpers I can use them inside A and B.
So the question, What would be the best solution to DRY this and follow best practices?
What if you include V1::Helpers on API and then make A and B inherit from API? E.g:
# /app/api/api.rb
class API < Grape::API
include V1::Helpers
format :json
version 'v1', using: :path
mount V1::A
mount V1::B
end
class A < API
# ...
end
class B < API
# ...
end