Grape DSL Extension

241 views
Skip to first unread message

Alejandro Wainzinger

unread,
Dec 17, 2014, 8:36:34 AM12/17/14
to ruby-...@googlegroups.com
The Grape DSL on endpoints has great things like "desc" and "params". What would be a simple way to add one's own DSL function?

The use case here is throttling. Let's suppose you want to throttle only some endpoints, and customize it in certain ways:

desc "This endpoint does things"
throttle daily: 3, except: ->(user) { user.is_admin? }
params do
  requires
:param1, type: String, desc: "The param"
end

get "/:param1" do
  {result: "You sent #{param1}"}
end

The "throttle" DSL doesn't exist, but I'd like to build something similar to that. In this case it would limit a user (as customly defined by the system elsewhere, not by IP address) to 3 requests per day, but exclude admin users from this. I might not use the "except" part but I just wanted to show the nice kind of things this type of DSL could offer. Having the throttling shown at the level of the endpoint also serves as nice documentation.

The rack-throttle middleware can do the trick with a custom throttle strategy defined, but it needs information on the endpoints, which I think a "throttle" DSL statement from the endpoints could give.

Any good ideas for how to go about this? Thanks!

Daniel Doubrovkine

unread,
Dec 17, 2014, 1:19:20 PM12/17/14
to ruby-...@googlegroups.com
In 0.9.0 we had a hacky way of doing this:

module Grape
module Extensions
module AdminExtension
[:admin, :user].each do |role|
define_method role do |value|
@last_description ||= {}
@last_description.merge!("role_#{role}".to_sym => value)
value
end
end
Grape::API.extend self
end
end
end

This would add a role_admin or role_user to the desc which can be examined at runtime:

module Api
module Middleware
class UserRoleMiddleware < Grape::Middleware::Base
def before
if route_options[:role_admin]
authenticated_admin
elsif route_options[:role_user]
authenticated_user
end
end
private
def route_options
env['api.endpoint'].options[:route_options]
end
end
end
end


This gets cleaner in 0.10.0 (HEAD) by using route_setting:

module Grape
module Extensions
module AdminExtension
[:admin, :user].each do |role|
define_method role do |value|
route_setting :role, "role_#{role}".to_sym => value
value
end
end
Grape::API.extend self
end
end
end

And the middleware that uses it via route_setting.

module Api
module V2
module Middleware
class UserRoleMiddleware < Grape::Middleware::Base

def before
if role[:role_admin]
authenticated_admin
elsif role[:role_user]
authenticated_user
end
end
private
def role
env['api.endpoint'].route_setting(:role) || {}
end
end
end
end
end

I really would love to see a blog post detailing this, I will probably write one when 0.10.0 is released if nobody beats me to it.


--
You received this message because you are subscribed to the Google Groups "Grape Framework Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-grape+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--

Daniel Doubrovkine

unread,
Dec 17, 2014, 1:21:03 PM12/17/14
to ruby-...@googlegroups.com
Oh and to use my role example you do "admin true" or "user true" after API params, you obviously can define any method (we tried with role :admin instead for example).  

Alejandro Wainzinger

unread,
Dec 18, 2014, 8:25:16 AM12/18/14
to ruby-...@googlegroups.com
Ah, I searched through the Google Group for DSL and found nothing, and didn't notice the UPGRADING had been updated to show this, sorry about that.

So in the example in 0.9.0, Grape::Extensions::AdminExtension would be loaded somewhere before the Grape::API is loaded, in a project's lib/ or so. For Api::Middleware::UserRoleMiddleware, do you have to register the middleware somewhere, or is just reopening Api::Middleware like this sufficient?
...

Daniel Doubrovkine

unread,
Dec 18, 2014, 12:03:43 PM12/18/14
to ruby-...@googlegroups.com
We mount Grape in Rails, so we put the extension in config/initializers/grape/extensions and add the middleware in the API via `use`.

--
You received this message because you are subscribed to the Google Groups "Grape Framework Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-grape+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alejandro Wainzinger

unread,
Dec 20, 2014, 3:59:21 PM12/20/14
to ruby-...@googlegroups.com
Got it. I tried with 0.10.0 the way you had it laid out. Within the API I did something like:

module Api
 
class Users < Grape::API

   
use ::Api::Middleware::ThrottleMiddleware

   
# ... endpoints

    desc
"My method"
    throttle
true
   
get "/" do
     
{"success": true}
   
end
 
end
end


But it can't find the throttle method. I defined it the same way you defined "admin" and "user" methods. I loaded the extension via config/initializers similar as you (and ensured it's loading by putting a puts in there first). Did I get something wrong?

Alejandro Wainzinger

unread,
Dec 20, 2014, 5:50:50 PM12/20/14
to ruby-...@googlegroups.com
Ok, turned out I had several issues:
* Padrino was outdated (0.11.4, newest is 0.12.4), so wouldn't work with 0.10.0
* Grape mounted in Padrino no longer cascading properly, so I bumped Grape higher up in the mount() calls

Now the extension's throttle method on the endpoint is called, but when I hit the endpoint, it appears not to call what's in the "before" function defined in your example. I suspect the middleware is not being mixed in properly somehow, will keep trying.
...

Alejandro Wainzinger

unread,
Dec 20, 2014, 5:53:47 PM12/20/14
to ruby-...@googlegroups.com
Ok, resolved. Please excuse the large amount of spam. It turns out that the "use" statement must go in the root level Grape API, before mounting. Then it finally loads.

Thanks!

Daniel Doubrovkine

unread,
Dec 22, 2014, 12:43:52 AM12/22/14
to ruby-...@googlegroups.com
If you want to apply some of this to https://github.com/dblock/grape-on-padrino, that'd be great.

--
You received this message because you are subscribed to the Google Groups "Grape Framework Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-grape+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alejandro Wainzinger

unread,
Dec 22, 2014, 1:53:06 AM12/22/14
to ruby-...@googlegroups.com
Sure, do you have anything specific in mind? This particular one with throttling I made into a gem since it fits well into several projects I work on with Grape (not just within Padrino), but I can add an example of the general "adding a DSL" pattern that you showed me here. What do you think should be applied?
...

Daniel Doubrovkine

unread,
Dec 22, 2014, 1:41:22 PM12/22/14
to ruby-...@googlegroups.com
First, bring that project up-to-date with Grape's recent release. Then add something creative :) I tend to reticulate splines.

--
You received this message because you are subscribed to the Google Groups "Grape Framework Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-grape+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alejandro Wainzinger

unread,
Dec 23, 2014, 2:36:17 PM12/23/14
to ruby-...@googlegroups.com
Hahahah, ok, will do my best and send out a PR or two.
...

Henry Tseng

unread,
Jan 4, 2023, 11:40:22 PM1/4/23
to Grape Framework Discussion
Is there a new way of extending the Grape DSL now?  

Daniel D.

unread,
Jan 5, 2023, 1:04:51 PM1/5/23
to ruby-...@googlegroups.com
I think it's pretty easy now by extending Grape::API. See https://github.com/ruby-grape/grape/blob/d1b8e865677659dd30e60324120fa72e7ba1a9fa/lib/grape/dsl/api.rb#L16 for how all DSL extensions are added. So Grape::DSL::API.include MyCustomDSL should work.





--
You received this message because you are subscribed to the Google Groups "Grape Framework Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-grape+...@googlegroups.com.


--

dB. | Moscow - Geneva - Seattle - New York

Henry Tseng

unread,
Jan 6, 2023, 10:03:45 PM1/6/23
to Grape Framework Discussion
Is it possible to extend specifically Grape::Validations::ParamsScope and adds more functionality?  

Daniel D.

unread,
Jan 8, 2023, 1:47:55 AM1/8/23
to ruby-...@googlegroups.com
There's no new way to do what the OP was trying to do. What's your scenario? Open a feature request and describe that?

Reply all
Reply to author
Forward
0 new messages