Skip to content

Commit 4a2febb

Browse files
author
Michael Bleigh
committed
Endpoints are smarter, build themselves from settings.
* Endpoints are now instances, not classes, and instantiate on route. * Moved endpoint building logic into the Endpoint itself. * Moved #aggregate_setting into the Endpoint * This gets us much closer to mountable APIs
1 parent c19c978 commit 4a2febb

File tree

6 files changed

+200
-159
lines changed

6 files changed

+200
-159
lines changed

lib/grape/api.rb

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -347,48 +347,20 @@ def nest(*blocks, &block)
347347
end
348348
end
349349

350-
def aggregate_setting(key)
351-
settings.stack.inject([]) do |aggregate, frame|
352-
aggregate += (frame[key] || [])
353-
end
354-
end
355-
356350
def build_endpoint(&block)
357-
b = Rack::Builder.new
358-
b.use Grape::Middleware::Error,
359-
:default_status => settings[:default_error_status] || 403,
360-
:rescue_all => settings[:rescue_all],
361-
:rescued_errors => settings[:rescued_errors],
362-
:format => settings[:error_format] || :txt,
363-
:rescue_options => settings[:rescue_options],
364-
:rescue_handlers => settings[:rescue_handlers] || {}
365-
366-
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
367-
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
368-
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
369-
370-
if settings[:version]
371-
b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]), {
372-
:versions => settings[:version],
373-
:version_options => settings[:version_options]
374-
}
375-
end
376-
377-
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
378-
middleware.each{|m| b.use *m }
379-
380-
befores = aggregate_setting(:befores)
381-
afters = aggregate_setting(:afters)
382-
representations = settings[:representations] || {}
383-
384-
endpoint = Grape::Endpoint.generate({
385-
:befores => befores,
386-
:afters => afters,
387-
:representations => representations
388-
}, &block)
389-
endpoint.send :include, helpers
390-
b.run endpoint
391-
b.to_app
351+
# befores = aggregate_setting(:befores)
352+
# afters = aggregate_setting(:afters)
353+
# representations = settings[:representations] || {}
354+
355+
# endpoint = Grape::Endpoint.generate({
356+
# :befores => befores,
357+
# :afters => afters,
358+
# :representations => representations
359+
# }, &block)
360+
# endpoint.send :include, helpers
361+
# b.run endpoint
362+
# b.to_app
363+
Grape::Endpoint.new(settings.clone, {}, &block)
392364
end
393365

394366
def inherited(subclass)

lib/grape/endpoint.rb

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,34 @@ module Grape
88
# on the instance level of this class may be called
99
# from inside a `get`, `post`, etc. block.
1010
class Endpoint
11-
def self.generate(options = {}, &block)
12-
c = Class.new(Grape::Endpoint)
13-
c.class_eval do
14-
@block = block
15-
@options = options
16-
end
17-
c
11+
attr_accessor :block, :options, :settings
12+
attr_reader :env, :request
13+
14+
def initialize(settings, options = {}, &block)
15+
@settings = settings
16+
@block = block
17+
@options = options
1818
end
19-
20-
class << self
21-
attr_accessor :block, :options
19+
20+
def call(env)
21+
dup.call!(env)
2222
end
23-
24-
def self.call(env)
25-
new.call(env)
23+
24+
def call!(env)
25+
builder = build_middleware
26+
builder.run lambda{|env| self.run(env) }
27+
builder.call(env)
2628
end
27-
28-
attr_reader :env, :request
29-
29+
3030
# The parameters passed into the request as
3131
# well as parsed from URL segments.
3232
def params
3333
@params ||= Hashie::Mash.new.deep_merge(request.params).deep_merge(env['rack.routing_args'] || {})
3434
end
35-
35+
3636
# The API version as specified in the URL.
3737
def version; env['api.version'] end
38-
38+
3939
# End the request and display an error to the
4040
# end user with the specified message.
4141
#
@@ -44,7 +44,7 @@ def version; env['api.version'] end
4444
def error!(message, status=403)
4545
throw :error, :message => message, :status => status
4646
end
47-
47+
4848
# Set or retrieve the HTTP status code.
4949
#
5050
# @param status [Integer] The HTTP Status Code to return for this request.
@@ -59,9 +59,9 @@ def status(status = nil)
5959
else
6060
200
6161
end
62-
end
62+
end
6363
end
64-
64+
6565
# Set an individual header or retrieve
6666
# all headers that have been set.
6767
def header(key = nil, val = nil)
@@ -109,7 +109,7 @@ def present(object, options = {})
109109
entity_class = options.delete(:with)
110110

111111
object.class.ancestors.each do |potential|
112-
entity_class ||= self.class.options[:representations][potential]
112+
entity_class ||= (settings[:representations] || {})[potential]
113113
end
114114

115115
if entity_class
@@ -121,24 +121,68 @@ def present(object, options = {})
121121
end
122122
end
123123

124-
def call(env)
124+
protected
125+
126+
def run(env)
125127
@env = env
126128
@header = {}
127129
@request = Rack::Request.new(@env)
128130

129-
run_filters self.class.options[:befores]
130-
response_text = instance_eval &self.class.block
131-
run_filters self.class.options[:afters]
131+
self.extend helpers
132+
run_filters befores
133+
response_text = instance_eval &self.block
134+
run_filters afters
132135

133136
[status, header, [body || response_text]]
134137
end
135138

136-
protected
139+
def build_middleware
140+
b = Rack::Builder.new
141+
b.use Grape::Middleware::Error,
142+
:default_status => settings[:default_error_status] || 403,
143+
:rescue_all => settings[:rescue_all],
144+
:rescued_errors => settings[:rescued_errors],
145+
:format => settings[:error_format] || :txt,
146+
:rescue_options => settings[:rescue_options],
147+
:rescue_handlers => settings[:rescue_handlers] || {}
148+
149+
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
150+
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
151+
b.use Grape::Middleware::Prefixer, :prefix => settings[:root_prefix] if settings[:root_prefix]
152+
153+
if settings[:version]
154+
b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]), {
155+
:versions => settings[:version],
156+
:version_options => settings[:version_options]
157+
}
158+
end
159+
160+
b.use Grape::Middleware::Formatter, :default_format => settings[:default_format] || :json
161+
162+
aggregate_setting(:middleware).each{|m| b.use *m }
163+
164+
b
165+
end
166+
167+
def helpers
168+
m = Module.new
169+
settings.stack.each{|frame| m.send :include, frame[:helpers] if frame[:helpers]}
170+
m
171+
end
172+
173+
def aggregate_setting(key)
174+
settings.stack.inject([]) do |aggregate, frame|
175+
aggregate += (frame[key] || [])
176+
end
177+
end
137178

138179
def run_filters(filters)
139180
(filters || []).each do |filter|
140181
instance_eval &filter
141182
end
142183
end
184+
185+
def befores; aggregate_setting(:befores) end
186+
def afters; aggregate_setting(:afters) end
143187
end
144188
end

lib/grape/util/hash_stack.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ def concat(hash_stack)
8686
def to_s
8787
@stack.to_s
8888
end
89+
90+
def clone
91+
new_stack = HashStack.new
92+
stack.each do |frame|
93+
new_stack.push frame.clone
94+
end
95+
new_stack.stack.shift
96+
new_stack
97+
end
8998
end
9099
end
91-
end
100+
end

0 commit comments

Comments
 (0)