Skip to content

Commit 0893a3a

Browse files
author
Michael Bleigh
committed
Adds before/after filters. Closes ruby-grape#58
1 parent 1eb2379 commit 0893a3a

File tree

5 files changed

+119
-18
lines changed

5 files changed

+119
-18
lines changed

README.markdown

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ Grape APIs are Rack applications that are created by subclassing `Grape::API`. B
4848
)
4949
end
5050
end
51+
52+
resource :account do
53+
before{ authenticate! }
54+
55+
get '/private' do
56+
"Congratulations, you found the secret!"
57+
end
58+
end
5159
end
5260

5361
This would create a Rack application that could be used like so (in a Rackup config.ru file):

lib/grape/api.rb

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def helpers(&block)
150150
m
151151
end
152152
end
153-
153+
154154
# Add an authentication type to the API. Currently
155155
# only `:http_basic`, `:http_digest` and `:oauth2` are supported.
156156
def auth(type = nil, options = {}, &block)
@@ -160,7 +160,7 @@ def auth(type = nil, options = {}, &block)
160160
settings[:auth]
161161
end
162162
end
163-
163+
164164
# Add HTTP Basic authorization to the API.
165165
#
166166
# @param [Hash] options A hash of options.
@@ -175,7 +175,7 @@ def http_digest(options = {}, &block)
175175
options[:opaque] ||= "secret"
176176
auth :http_digest, options, &block
177177
end
178-
178+
179179
# Defines a route that will be recognized
180180
# by the Grape API.
181181
#
@@ -190,17 +190,17 @@ def http_digest(options = {}, &block)
190190
# end
191191
def route(methods, paths = ['/'], route_options = {}, &block)
192192
methods = Array(methods)
193-
193+
194194
paths = ['/'] if ! paths || paths == []
195195
paths = Array(paths)
196-
197-
endpoint = build_endpoint(&block)
198-
196+
197+
endpoint = build_endpoint(&block)
198+
199199
endpoint_options = {}
200200
endpoint_options[:version] = /#{version.join('|')}/ if version
201-
201+
202202
route_options ||= {}
203-
203+
204204
methods.each do |method|
205205
paths.each do |path|
206206

@@ -226,13 +226,23 @@ def route(methods, paths = ['/'], route_options = {}, &block)
226226
end
227227
end
228228
end
229+
230+
def before(&block)
231+
settings_stack.last[:befores] ||= []
232+
settings_stack.last[:befores] << block
233+
end
229234

235+
def after(&block)
236+
settings_stack.last[:afters] ||= []
237+
settings_stack.last[:afters] << block
238+
end
239+
230240
def get(paths = ['/'], options = {}, &block); route('GET', paths, options, &block) end
231241
def post(paths = ['/'], options = {}, &block); route('POST', paths, options, &block) end
232242
def put(paths = ['/'], options = {}, &block); route('PUT', paths, options, &block) end
233243
def head(paths = ['/'], options = {}, &block); route('HEAD', paths, options, &block) end
234244
def delete(paths = ['/'], options = {}, &block); route('DELETE', paths, options, &block) end
235-
245+
236246
def namespace(space = nil, &block)
237247
if space || block_given?
238248
nest(block) do
@@ -296,7 +306,13 @@ def nest(*blocks, &block)
296306
instance_eval &block
297307
end
298308
end
299-
309+
310+
def aggregate_setting(key)
311+
settings_stack.inject([]) do |befores, settings|
312+
befores += (settings[key] || [])
313+
end
314+
end
315+
300316
def build_endpoint(&block)
301317
b = Rack::Builder.new
302318
b.use Grape::Middleware::Error,
@@ -313,7 +329,10 @@ def build_endpoint(&block)
313329
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
314330
middleware.each{|m| b.use *m }
315331

316-
endpoint = Grape::Endpoint.generate(&block)
332+
befores = aggregate_setting(:befores)
333+
afters = aggregate_setting(:afters)
334+
335+
endpoint = Grape::Endpoint.generate({:befores => befores, :afters => afters}, &block)
317336
endpoint.send :include, helpers
318337
b.run endpoint
319338

lib/grape/endpoint.rb

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@ module Grape
77
# on the instance level of this class may be called
88
# from inside a `get`, `post`, etc. block.
99
class Endpoint
10-
def self.generate(&block)
10+
def self.generate(options = {}, &block)
1111
c = Class.new(Grape::Endpoint)
1212
c.class_eval do
1313
@block = block
14+
@options = options
1415
end
1516
c
1617
end
1718

1819
class << self
19-
attr_accessor :block
20+
attr_accessor :block, :options
2021
end
2122

2223
def self.call(env)
@@ -73,15 +74,43 @@ def header(key = nil, val = nil)
7374
@header
7475
end
7576
end
76-
77+
78+
# Allows you to define the response body as something other than the
79+
# return value.
80+
#
81+
# @example
82+
# get '/body' do
83+
# body "Body"
84+
# "Not the Body"
85+
# end
86+
#
87+
# GET /body # => "Body"
88+
def body(value = nil)
89+
if value
90+
@body = value
91+
else
92+
@body
93+
end
94+
end
95+
7796
def call(env)
7897
@env = env
7998
@header = {}
8099
@request = Rack::Request.new(@env)
81-
100+
101+
run_filters self.class.options[:befores]
82102
response_text = instance_eval &self.class.block
83-
84-
[status, header, [response_text]]
103+
run_filters self.class.options[:afters]
104+
105+
[status, header, [body || response_text]]
106+
end
107+
108+
protected
109+
110+
def run_filters(filters)
111+
(filters || []).each do |filter|
112+
instance_eval &filter
113+
end
85114
end
86115
end
87116
end

lib/grape/middleware/filter.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Grape
2+
module Middleware
3+
# This is a simple middleware for adding before and after filters
4+
# to Grape APIs. It is used like so:
5+
#
6+
# use Grape::Middleware::Filter, :before => lambda{ do_something }, after: => lambda{ do_something }
7+
class Filter < Base
8+
def before
9+
app.instance_eval &options[:before] if options[:before]
10+
end
11+
12+
def after
13+
app.instance_eval &options[:after] if options[:after]
14+
end
15+
end
16+
end
17+
end

spec/grape/endpoint_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,32 @@ def memoized
111111
get '/hello?howdy=yo'
112112
last_response.body.should == 'yo'
113113
end
114+
115+
context 'filters' do
116+
describe 'before filters' do
117+
it 'should run the before filter if set' do
118+
subject.before{ env['before_test'] = "OK" }
119+
subject.get('/before_test'){ env['before_test'] }
120+
121+
get '/before_test'
122+
last_response.body.should == "OK"
123+
end
124+
end
125+
126+
describe 'after filters' do
127+
it 'should override the response body if it sets it' do
128+
subject.after{ body "after" }
129+
subject.get('/after_test'){ "during" }
130+
get '/after_test'
131+
last_response.body.should == 'after'
132+
end
133+
134+
it 'should not override the response body with its return' do
135+
subject.after{ "after" }
136+
subject.get('/after_test'){ "body" }
137+
get '/after_test'
138+
last_response.body.should == "body"
139+
end
140+
end
141+
end
114142
end

0 commit comments

Comments
 (0)