grape 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (142) hide show
  1. data/.gitignore +13 -1
  2. data/.rspec +1 -1
  3. data/.travis.yml +1 -0
  4. data/Gemfile +10 -0
  5. data/Guardfile +15 -0
  6. data/README.markdown +472 -67
  7. data/grape.gemspec +4 -4
  8. data/lib/grape.rb +17 -5
  9. data/lib/grape/api.rb +227 -124
  10. data/lib/grape/cookies.rb +41 -0
  11. data/lib/grape/endpoint.rb +256 -27
  12. data/lib/grape/entity.rb +227 -0
  13. data/lib/grape/middleware/auth/oauth2.rb +1 -2
  14. data/lib/grape/middleware/base.rb +59 -6
  15. data/lib/grape/middleware/error.rb +15 -6
  16. data/lib/grape/middleware/filter.rb +17 -0
  17. data/lib/grape/middleware/formatter.rb +25 -31
  18. data/lib/grape/middleware/versioner.rb +20 -20
  19. data/lib/grape/middleware/versioner/header.rb +59 -0
  20. data/lib/grape/middleware/versioner/path.rb +42 -0
  21. data/lib/grape/route.rb +23 -0
  22. data/lib/grape/util/hash_stack.rb +100 -0
  23. data/lib/grape/version.rb +1 -1
  24. data/spec/grape/api_spec.rb +651 -162
  25. data/spec/grape/endpoint_spec.rb +216 -18
  26. data/spec/grape/entity_spec.rb +320 -0
  27. data/spec/grape/middleware/auth/basic_spec.rb +2 -2
  28. data/spec/grape/middleware/auth/digest_spec.rb +4 -6
  29. data/spec/grape/middleware/exception_spec.rb +1 -0
  30. data/spec/grape/middleware/formatter_spec.rb +81 -27
  31. data/spec/grape/middleware/versioner/header_spec.rb +148 -0
  32. data/spec/grape/middleware/versioner/path_spec.rb +40 -0
  33. data/spec/grape/middleware/versioner_spec.rb +6 -34
  34. data/spec/grape/util/hash_stack_spec.rb +133 -0
  35. data/spec/shared/versioning_examples.rb +77 -0
  36. data/spec/spec_helper.rb +11 -3
  37. data/spec/support/basic_auth_encode_helpers.rb +4 -0
  38. data/spec/support/rack_patch.rb +25 -0
  39. data/spec/support/versioned_helpers.rb +34 -0
  40. metadata +140 -241
  41. data/.yardoc/checksums +0 -13
  42. data/.yardoc/objects/Grape.dat +0 -0
  43. data/.yardoc/objects/Grape/API.dat +0 -0
  44. data/.yardoc/objects/Grape/API/auth_c.dat +0 -0
  45. data/.yardoc/objects/Grape/API/build_endpoint_c.dat +0 -0
  46. data/.yardoc/objects/Grape/API/call_c.dat +0 -0
  47. data/.yardoc/objects/Grape/API/compile_path_c.dat +0 -0
  48. data/.yardoc/objects/Grape/API/default_format_c.dat +0 -0
  49. data/.yardoc/objects/Grape/API/delete_c.dat +0 -0
  50. data/.yardoc/objects/Grape/API/get_c.dat +0 -0
  51. data/.yardoc/objects/Grape/API/group_c.dat +0 -0
  52. data/.yardoc/objects/Grape/API/head_c.dat +0 -0
  53. data/.yardoc/objects/Grape/API/helpers_c.dat +0 -0
  54. data/.yardoc/objects/Grape/API/http_basic_c.dat +0 -0
  55. data/.yardoc/objects/Grape/API/inherited_c.dat +0 -0
  56. data/.yardoc/objects/Grape/API/logger_c.dat +0 -0
  57. data/.yardoc/objects/Grape/API/namespace_c.dat +0 -0
  58. data/.yardoc/objects/Grape/API/nest_c.dat +0 -0
  59. data/.yardoc/objects/Grape/API/post_c.dat +0 -0
  60. data/.yardoc/objects/Grape/API/prefix_c.dat +0 -0
  61. data/.yardoc/objects/Grape/API/put_c.dat +0 -0
  62. data/.yardoc/objects/Grape/API/reset_21_c.dat +0 -0
  63. data/.yardoc/objects/Grape/API/resource_c.dat +0 -0
  64. data/.yardoc/objects/Grape/API/resources_c.dat +0 -0
  65. data/.yardoc/objects/Grape/API/route_c.dat +0 -0
  66. data/.yardoc/objects/Grape/API/route_set_c.dat +0 -0
  67. data/.yardoc/objects/Grape/API/scope_c.dat +0 -0
  68. data/.yardoc/objects/Grape/API/set_c.dat +0 -0
  69. data/.yardoc/objects/Grape/API/settings_c.dat +0 -0
  70. data/.yardoc/objects/Grape/API/settings_stack_c.dat +0 -0
  71. data/.yardoc/objects/Grape/API/version_c.dat +0 -0
  72. data/.yardoc/objects/Grape/Endpoint.dat +0 -0
  73. data/.yardoc/objects/Grape/Endpoint/block_3D_c.dat +0 -0
  74. data/.yardoc/objects/Grape/Endpoint/block_c.dat +0 -0
  75. data/.yardoc/objects/Grape/Endpoint/call_c.dat +0 -0
  76. data/.yardoc/objects/Grape/Endpoint/call_i.dat +0 -0
  77. data/.yardoc/objects/Grape/Endpoint/env_i.dat +0 -0
  78. data/.yardoc/objects/Grape/Endpoint/error_21_i.dat +0 -0
  79. data/.yardoc/objects/Grape/Endpoint/generate_c.dat +0 -0
  80. data/.yardoc/objects/Grape/Endpoint/header_i.dat +0 -0
  81. data/.yardoc/objects/Grape/Endpoint/params_i.dat +0 -0
  82. data/.yardoc/objects/Grape/Endpoint/request_i.dat +0 -0
  83. data/.yardoc/objects/Grape/Endpoint/status_i.dat +0 -0
  84. data/.yardoc/objects/Grape/Endpoint/version_i.dat +0 -0
  85. data/.yardoc/objects/Grape/Middleware.dat +0 -0
  86. data/.yardoc/objects/Grape/Middleware/Auth.dat +0 -0
  87. data/.yardoc/objects/Grape/Middleware/Auth/Basic.dat +0 -0
  88. data/.yardoc/objects/Grape/Middleware/Auth/Basic/authenticator_i.dat +0 -0
  89. data/.yardoc/objects/Grape/Middleware/Auth/Basic/basic_request_i.dat +0 -0
  90. data/.yardoc/objects/Grape/Middleware/Auth/Basic/before_i.dat +0 -0
  91. data/.yardoc/objects/Grape/Middleware/Auth/Basic/credentials_i.dat +0 -0
  92. data/.yardoc/objects/Grape/Middleware/Auth/Basic/initialize_i.dat +0 -0
  93. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2.dat +0 -0
  94. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/before_i.dat +0 -0
  95. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/default_options_i.dat +0 -0
  96. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/error_out_i.dat +0 -0
  97. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/parse_authorization_header_i.dat +0 -0
  98. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/token_class_i.dat +0 -0
  99. data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/verify_token_i.dat +0 -0
  100. data/.yardoc/objects/Grape/Middleware/Base.dat +0 -0
  101. data/.yardoc/objects/Grape/Middleware/Base/after_i.dat +0 -0
  102. data/.yardoc/objects/Grape/Middleware/Base/app_i.dat +0 -0
  103. data/.yardoc/objects/Grape/Middleware/Base/before_i.dat +0 -0
  104. data/.yardoc/objects/Grape/Middleware/Base/call_21_i.dat +0 -0
  105. data/.yardoc/objects/Grape/Middleware/Base/call_i.dat +0 -0
  106. data/.yardoc/objects/Grape/Middleware/Base/default_options_i.dat +0 -0
  107. data/.yardoc/objects/Grape/Middleware/Base/env_i.dat +0 -0
  108. data/.yardoc/objects/Grape/Middleware/Base/initialize_i.dat +0 -0
  109. data/.yardoc/objects/Grape/Middleware/Base/options_i.dat +0 -0
  110. data/.yardoc/objects/Grape/Middleware/Base/request_i.dat +0 -0
  111. data/.yardoc/objects/Grape/Middleware/Base/response_i.dat +0 -0
  112. data/.yardoc/objects/Grape/Middleware/Error.dat +0 -0
  113. data/.yardoc/objects/Grape/Middleware/Error/call_21_i.dat +0 -0
  114. data/.yardoc/objects/Grape/Middleware/Error/error_response_i.dat +0 -0
  115. data/.yardoc/objects/Grape/Middleware/Formatter.dat +0 -0
  116. data/.yardoc/objects/Grape/Middleware/Formatter/CONTENT_TYPES.dat +0 -0
  117. data/.yardoc/objects/Grape/Middleware/Formatter/after_i.dat +0 -0
  118. data/.yardoc/objects/Grape/Middleware/Formatter/before_i.dat +0 -0
  119. data/.yardoc/objects/Grape/Middleware/Formatter/content_types_i.dat +0 -0
  120. data/.yardoc/objects/Grape/Middleware/Formatter/default_options_i.dat +0 -0
  121. data/.yardoc/objects/Grape/Middleware/Formatter/encode_json_i.dat +0 -0
  122. data/.yardoc/objects/Grape/Middleware/Formatter/encode_txt_i.dat +0 -0
  123. data/.yardoc/objects/Grape/Middleware/Formatter/format_from_extension_i.dat +0 -0
  124. data/.yardoc/objects/Grape/Middleware/Formatter/format_from_header_i.dat +0 -0
  125. data/.yardoc/objects/Grape/Middleware/Formatter/headers_i.dat +0 -0
  126. data/.yardoc/objects/Grape/Middleware/Formatter/mime_array_i.dat +0 -0
  127. data/.yardoc/objects/Grape/Middleware/Formatter/mime_types_i.dat +0 -0
  128. data/.yardoc/objects/Grape/Middleware/Prefixer.dat +0 -0
  129. data/.yardoc/objects/Grape/Middleware/Prefixer/before_i.dat +0 -0
  130. data/.yardoc/objects/Grape/Middleware/Prefixer/prefix_i.dat +0 -0
  131. data/.yardoc/objects/Grape/Middleware/Versioner.dat +0 -0
  132. data/.yardoc/objects/Grape/Middleware/Versioner/before_i.dat +0 -0
  133. data/.yardoc/objects/Grape/Middleware/Versioner/default_options_i.dat +0 -0
  134. data/.yardoc/objects/Grape/MiddlewareStack.dat +0 -0
  135. data/.yardoc/objects/Grape/MiddlewareStack/initialize_i.dat +0 -0
  136. data/.yardoc/objects/Grape/MiddlewareStack/stack_i.dat +0 -0
  137. data/.yardoc/objects/Grape/MiddlewareStack/to_app_i.dat +0 -0
  138. data/.yardoc/objects/Grape/MiddlewareStack/use_i.dat +0 -0
  139. data/.yardoc/objects/root.dat +0 -0
  140. data/.yardoc/proxy_types +0 -2
  141. data/Gemfile.lock +0 -52
  142. data/autotest/discover.rb +0 -1
@@ -10,22 +10,22 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "https://github.com/intridea/grape"
11
11
  s.summary = %q{A simple Ruby framework for building REST-like APIs.}
12
12
  s.description = %q{A Ruby framework for rapid API development with great conventions.}
13
+ s.license = "MIT"
13
14
 
14
15
  s.rubyforge_project = "grape"
15
16
 
16
17
  s.add_runtime_dependency 'rack'
17
18
  s.add_runtime_dependency 'rack-mount'
18
- s.add_runtime_dependency 'rack-jsonp'
19
+ # s.add_runtime_dependency 'rack-jsonp'
19
20
  s.add_runtime_dependency 'multi_json'
20
21
  s.add_runtime_dependency 'multi_xml'
22
+ s.add_runtime_dependency 'hashie', '~> 1.2'
21
23
 
22
24
  s.add_development_dependency 'rake'
23
25
  s.add_development_dependency 'maruku'
24
26
  s.add_development_dependency 'yard'
25
27
  s.add_development_dependency 'rack-test'
26
- s.add_development_dependency 'rspec', '~> 2.6.0'
27
- s.add_development_dependency 'json_pure'
28
- s.add_development_dependency 'ZenTest'
28
+ s.add_development_dependency 'rspec', '~> 2.9'
29
29
  s.add_development_dependency 'bundler'
30
30
 
31
31
  s.files = `git ls-files`.split("\n")
@@ -2,23 +2,35 @@ require 'rack'
2
2
  require 'rack/builder'
3
3
 
4
4
  module Grape
5
- autoload :API, 'grape/api'
6
- autoload :Endpoint, 'grape/endpoint'
5
+ autoload :API, 'grape/api'
6
+ autoload :Endpoint, 'grape/endpoint'
7
7
  autoload :MiddlewareStack, 'grape/middleware_stack'
8
- autoload :Client, 'grape/client'
9
-
8
+ autoload :Client, 'grape/client'
9
+ autoload :Route, 'grape/route'
10
+ autoload :Entity, 'grape/entity'
11
+ autoload :Cookies, 'grape/cookies'
12
+
10
13
  module Middleware
11
14
  autoload :Base, 'grape/middleware/base'
12
15
  autoload :Prefixer, 'grape/middleware/prefixer'
13
16
  autoload :Versioner, 'grape/middleware/versioner'
14
17
  autoload :Formatter, 'grape/middleware/formatter'
15
18
  autoload :Error, 'grape/middleware/error'
16
-
19
+
17
20
  module Auth
18
21
  autoload :OAuth2, 'grape/middleware/auth/oauth2'
19
22
  autoload :Basic, 'grape/middleware/auth/basic'
20
23
  autoload :Digest, 'grape/middleware/auth/digest'
21
24
  end
25
+
26
+ module Versioner
27
+ autoload :Path, 'grape/middleware/versioner/path'
28
+ autoload :Header, 'grape/middleware/versioner/header'
29
+ end
30
+ end
31
+
32
+ module Util
33
+ autoload :HashStack, 'grape/util/hash_stack'
22
34
  end
23
35
  end
24
36
 
@@ -5,47 +5,69 @@ require 'logger'
5
5
 
6
6
  module Grape
7
7
  # The API class is the primary entry point for
8
- # creating Grape APIs. Users should subclass this
8
+ # creating Grape APIs.Users should subclass this
9
9
  # class in order to build an API.
10
10
  class API
11
11
  class << self
12
12
  attr_reader :route_set
13
-
14
- def logger
15
- @logger ||= Logger.new($STDOUT)
13
+ attr_reader :versions
14
+ attr_reader :routes
15
+ attr_reader :settings
16
+ attr_writer :logger
17
+ attr_reader :endpoints
18
+ attr_reader :mountings
19
+ attr_reader :instance
20
+
21
+ def logger(logger = nil)
22
+ if logger
23
+ @logger = logger
24
+ else
25
+ @logger ||= Logger.new($stdout)
26
+ end
16
27
  end
17
-
28
+
18
29
  def reset!
19
- @settings = [{}]
30
+ @settings = Grape::Util::HashStack.new
20
31
  @route_set = Rack::Mount::RouteSet.new
21
- @prototype = nil
32
+ @endpoints = []
33
+ @mountings = []
34
+ @routes = nil
22
35
  end
23
-
36
+
37
+ def compile
38
+ @instance = self.new
39
+ end
40
+
41
+ def change!
42
+ @instance = nil
43
+ end
44
+
24
45
  def call(env)
25
- logger.info "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
26
- route_set.freeze.call(env)
46
+ compile unless instance
47
+ call!(env)
27
48
  end
28
-
29
- # Settings are a stack, so when we
30
- # want to access them they are merged
31
- # in the order pushed.
32
- def settings
33
- @settings.inject({}){|f,h| f.merge!(h); f}
49
+
50
+ def call!(env)
51
+ instance.call(env)
34
52
  end
35
-
36
- def settings_stack
37
- @settings
53
+
54
+ # Set a configuration value for this namespace.
55
+ #
56
+ # @param key [Symbol] The key of the configuration variable.
57
+ # @param value [Object] The value to which to set the configuration variable.
58
+ def set(key, value)
59
+ settings[key.to_sym] = value
38
60
  end
39
-
40
- # Set a configuration value for this
61
+
62
+ # Add to a configuration value for this
41
63
  # namespace.
42
64
  #
43
65
  # @param key [Symbol] The key of the configuration variable.
44
66
  # @param value [Object] The value to which to set the configuration variable.
45
- def set(key, value)
46
- @settings.last[key.to_sym] = value
67
+ def imbue(key, value)
68
+ settings.imbue(key, value)
47
69
  end
48
-
70
+
49
71
  # Define a root URL prefix for your entire
50
72
  # API.
51
73
  def prefix(prefix = nil)
@@ -57,11 +79,11 @@ module Grape
57
79
  # @example API with legacy support.
58
80
  # class MyAPI < Grape::API
59
81
  # version 'v2'
60
- #
82
+ #
61
83
  # get '/main' do
62
84
  # {:some => 'data'}
63
85
  # end
64
- #
86
+ #
65
87
  # version 'v1' do
66
88
  # get '/main' do
67
89
  # {:legacy => 'data'}
@@ -69,23 +91,48 @@ module Grape
69
91
  # end
70
92
  # end
71
93
  #
72
- def version(*new_versions, &block)
73
- new_versions.any? ? nest(block){ set(:version, new_versions) } : settings[:version]
94
+ def version(*args, &block)
95
+ if args.any?
96
+ options = args.pop if args.last.is_a? Hash
97
+ options ||= {}
98
+ options = {:using => :path}.merge!(options)
99
+ @versions = versions | args
100
+ nest(block) do
101
+ set(:version, args)
102
+ set(:version_options, options)
103
+ end
104
+ end
74
105
  end
75
-
76
- # Specify the default format for the API's
77
- # serializers. Currently only `:json` is
78
- # supported.
106
+
107
+ # Add a description to the next namespace or function.
108
+ def desc(description, options = {})
109
+ @last_description = options.merge(:description => description)
110
+ end
111
+
112
+ # Specify the default format for the API's serializers.
113
+ # May be `:json` or `:txt` (default).
79
114
  def default_format(new_format = nil)
80
115
  new_format ? set(:default_format, new_format.to_sym) : settings[:default_format]
81
116
  end
82
117
 
118
+ # Specify the format for the API's serializers.
119
+ # May be `:json` or `:txt`.
120
+ def format(new_format = nil)
121
+ new_format ? set(:format, new_format.to_sym) : settings[:format]
122
+ end
123
+
83
124
  # Specify the format for error messages.
84
125
  # May be `:json` or `:txt` (default).
85
126
  def error_format(new_format = nil)
86
127
  new_format ? set(:error_format, new_format.to_sym) : settings[:error_format]
87
128
  end
88
129
 
130
+ # Specify additional content-types, e.g.:
131
+ # content_type :xls, 'application/vnd.ms-excel'
132
+ def content_type(key, val)
133
+ settings.imbue(:content_types, key.to_sym => val)
134
+ end
135
+
89
136
  # Specify the default status code for errors.
90
137
  def default_error_status(new_status = nil)
91
138
  new_status ? set(:default_error_status, new_status) : settings[:default_error_status]
@@ -105,17 +152,54 @@ module Grape
105
152
  # @overload rescue_from(*exception_classes, options = {})
106
153
  # @param [Array] exception_classes A list of classes that you want to rescue, or
107
154
  # the symbol :all to rescue from all exceptions.
155
+ # @param [Block] block Execution block to handle the given exception.
108
156
  # @param [Hash] options Options for the rescue usage.
109
157
  # @option options [Boolean] :backtrace Include a backtrace in the rescue response.
110
- def rescue_from(*args)
111
- set(:rescue_options, args.pop) if args.last.is_a?(Hash)
158
+ def rescue_from(*args, &block)
159
+ if block_given?
160
+ args.each do |arg|
161
+ imbue(:rescue_handlers, { arg => block })
162
+ end
163
+ end
164
+ imbue(:rescue_options, args.pop) if args.last.is_a?(Hash)
112
165
  set(:rescue_all, true) and return if args.include?(:all)
113
- set(:rescued_errors, args)
166
+ imbue(:rescued_errors, args)
167
+ end
168
+
169
+ # Allows you to specify a default representation entity for a
170
+ # class. This allows you to map your models to their respective
171
+ # entities once and then simply call `present` with the model.
172
+ #
173
+ # @example
174
+ # class ExampleAPI < Grape::API
175
+ # represent User, :with => Entity::User
176
+ #
177
+ # get '/me' do
178
+ # present current_user # :with => Entity::User is assumed
179
+ # end
180
+ # end
181
+ #
182
+ # Note that Grape will automatically go up the class ancestry to
183
+ # try to find a representing entity, so if you, for example, define
184
+ # an entity to represent `Object` then all presented objects will
185
+ # bubble up and utilize the entity provided on that `represent` call.
186
+ #
187
+ # @param model_class [Class] The model class that will be represented.
188
+ # @option options [Class] :with The entity class that will represent the model.
189
+ def represent(model_class, options)
190
+ raise ArgumentError, "You must specify an entity class in the :with option." unless options[:with] && options[:with].is_a?(Class)
191
+ imbue(:representations, model_class => options[:with])
114
192
  end
115
193
 
116
194
  # Add helper methods that will be accessible from any
117
195
  # endpoint within this namespace (and child namespaces).
118
196
  #
197
+ # When called without a block, all known helpers within this scope
198
+ # are included.
199
+ #
200
+ # @param mod [Module] optional module of methods to include
201
+ # @param &block [Block] optional block of methods to include
202
+ #
119
203
  # @example Define some helpers.
120
204
  # class ExampleAPI < Grape::API
121
205
  # helpers do
@@ -124,20 +208,21 @@ module Grape
124
208
  # end
125
209
  # end
126
210
  # end
127
- def helpers(&block)
128
- if block_given?
129
- m = settings_stack.last[:helpers] || Module.new
130
- m.class_eval &block
131
- set(:helpers, m)
211
+ def helpers(mod = nil, &block)
212
+ if block_given? || mod
213
+ mod ||= settings.peek[:helpers] || Module.new
214
+ mod.class_eval &block if block_given?
215
+ set(:helpers, mod)
132
216
  else
133
- m = Module.new
134
- settings_stack.each do |s|
135
- m.send :include, s[:helpers] if s[:helpers]
217
+ mod = Module.new
218
+ settings.stack.each do |s|
219
+ mod.send :include, s[:helpers] if s[:helpers]
136
220
  end
137
- m
221
+ change!
222
+ mod
138
223
  end
139
224
  end
140
-
225
+
141
226
  # Add an authentication type to the API. Currently
142
227
  # only `:http_basic`, `:http_digest` and `:oauth2` are supported.
143
228
  def auth(type = nil, options = {}, &block)
@@ -147,7 +232,7 @@ module Grape
147
232
  settings[:auth]
148
233
  end
149
234
  end
150
-
235
+
151
236
  # Add HTTP Basic authorization to the API.
152
237
  #
153
238
  # @param [Hash] options A hash of options.
@@ -159,10 +244,25 @@ module Grape
159
244
 
160
245
  def http_digest(options = {}, &block)
161
246
  options[:realm] ||= "API Authorization"
162
- options[:opaque] ||= "secret"
247
+ options[:opaque] ||= "secret"
163
248
  auth :http_digest, options, &block
164
249
  end
165
-
250
+
251
+ def mount(mounts)
252
+ mounts = {mounts => '/'} unless mounts.respond_to?(:each_pair)
253
+ mounts.each_pair do |app, path|
254
+ if app.respond_to?(:inherit_settings)
255
+ app.inherit_settings(settings.clone)
256
+ end
257
+
258
+ endpoints << Grape::Endpoint.new(settings.clone,
259
+ :method => :any,
260
+ :path => path,
261
+ :app => app
262
+ )
263
+ end
264
+ end
265
+
166
266
  # Defines a route that will be recognized
167
267
  # by the Grape API.
168
268
  #
@@ -175,47 +275,49 @@ module Grape
175
275
  # {:hello => 'world'}
176
276
  # end
177
277
  # end
178
- def route(methods, paths, &block)
179
- methods = Array(methods)
180
- paths = ['/'] if paths == []
181
- paths = Array(paths)
182
- endpoint = build_endpoint(&block)
183
- options = {}
184
- options[:version] = /#{version.join('|')}/ if version
185
-
186
- methods.each do |method|
187
- paths.each do |path|
188
- path = Rack::Mount::Strexp.compile(compile_path(path), options, ['/'], true)
189
- route_set.add_route(endpoint,
190
- :path_info => path,
191
- :request_method => (method.to_s.upcase unless method == :any)
192
- )
193
- end
194
- end
278
+ def route(methods, paths = ['/'], route_options = {}, &block)
279
+ endpoint_options = {
280
+ :method => methods,
281
+ :path => paths,
282
+ :route_options => (route_options || {}).merge(@last_description || {})
283
+ }
284
+ endpoints << Grape::Endpoint.new(settings.clone, endpoint_options, &block)
285
+ @last_description = nil
195
286
  end
196
-
197
- def get(*paths, &block); route('GET', paths, &block) end
198
- def post(*paths, &block); route('POST', paths, &block) end
199
- def put(*paths, &block); route('PUT', paths, &block) end
200
- def head(*paths, &block); route('HEAD', paths, &block) end
201
- def delete(*paths, &block); route('DELETE', paths, &block) end
202
-
287
+
288
+ def before(&block)
289
+ imbue(:befores, [block])
290
+ end
291
+
292
+ def after(&block)
293
+ imbue(:afters, [block])
294
+ end
295
+
296
+ def get(paths = ['/'], options = {}, &block); route('GET', paths, options, &block) end
297
+ def post(paths = ['/'], options = {}, &block); route('POST', paths, options, &block) end
298
+ def put(paths = ['/'], options = {}, &block); route('PUT', paths, options, &block) end
299
+ def head(paths = ['/'], options = {}, &block); route('HEAD', paths, options, &block) end
300
+ def delete(paths = ['/'], options = {}, &block); route('DELETE', paths, options, &block) end
301
+ def options(paths = ['/'], options = {}, &block); route('OPTIONS', paths, options, &block) end
302
+ def patch(paths = ['/'], options = {}, &block); route('PATCH', paths, options, &block) end
303
+
203
304
  def namespace(space = nil, &block)
204
305
  if space || block_given?
205
306
  nest(block) do
206
307
  set(:namespace, space.to_s) if space
207
308
  end
208
309
  else
209
- Rack::Mount::Utils.normalize_path(settings_stack.map{|s| s[:namespace]}.join('/'))
310
+ Rack::Mount::Utils.normalize_path(settings.stack.map{|s| s[:namespace]}.join('/'))
210
311
  end
211
312
  end
212
-
313
+
213
314
  alias_method :group, :namespace
214
315
  alias_method :resource, :namespace
215
316
  alias_method :resources, :namespace
216
-
317
+ alias_method :segment, :namespace
318
+
217
319
  # Create a scope without affecting the URL.
218
- #
320
+ #
219
321
  # @param name [Symbol] Purely placebo, just allows to to name the scope to make the code more readable.
220
322
  def scope(name = nil, &block)
221
323
  nest(block)
@@ -225,77 +327,78 @@ module Grape
225
327
  # to the current namespace and any children, but
226
328
  # not parents.
227
329
  #
228
- # @param middleware_class [Class] The class of the middleware you'd like to inject.
229
- def use(middleware_class, *args)
230
- settings_stack.last[:middleware] ||= []
231
- settings_stack.last[:middleware] << [middleware_class, *args]
330
+ # @param middleware_class [Class] The class of the middleware you'd like
331
+ # to inject.
332
+ def use(middleware_class, *args, &block)
333
+ arr = [middleware_class, *args]
334
+ arr << block if block_given?
335
+ imbue(:middleware, [arr])
232
336
  end
233
337
 
234
338
  # Retrieve an array of the middleware classes
235
339
  # and arguments that are currently applied to the
236
340
  # application.
237
341
  def middleware
238
- settings_stack.inject([]){|a,s| a += s[:middleware] if s[:middleware]; a}
342
+ settings.stack.inject([]){|a,s| a += s[:middleware] if s[:middleware]; a}
343
+ end
344
+
345
+ # An array of API routes.
346
+ def routes
347
+ @routes ||= prepare_routes
348
+ end
349
+
350
+ def versions
351
+ @versions ||= []
239
352
  end
240
353
 
241
354
  protected
242
-
355
+
356
+ def prepare_routes
357
+ routes = []
358
+ endpoints.each do |endpoint|
359
+ routes.concat(endpoint.routes)
360
+ end
361
+ routes
362
+ end
363
+
243
364
  # Execute first the provided block, then each of the
244
365
  # block passed in. Allows for simple 'before' setups
245
366
  # of settings stack pushes.
246
367
  def nest(*blocks, &block)
247
368
  blocks.reject!{|b| b.nil?}
248
369
  if blocks.any?
249
- settings_stack << {}
370
+ settings.push # create a new context to eval the follow
250
371
  instance_eval &block if block_given?
251
372
  blocks.each{|b| instance_eval &b}
252
- settings_stack.pop
373
+ settings.pop # when finished, we pop the context
253
374
  else
254
375
  instance_eval &block
255
376
  end
256
377
  end
257
-
258
- def build_endpoint(&block)
259
- b = Rack::Builder.new
260
- b.use Grape::Middleware::Error,
261
- :default_status => settings[:default_error_status] || 403,
262
- :rescue_all => settings[:rescue_all],
263
- :rescued_errors => settings[:rescued_errors],
264
- :format => settings[:error_format] || :txt,
265
- :rescue_options => settings[:rescue_options]
266
- b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
267
- b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
268
- b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
269
- b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
270
- b.use Grape::Middleware::Formatter, :default_format => default_format || :json
271
- middleware.each{|m| b.use *m }
272
-
273
- endpoint = Grape::Endpoint.generate(&block)
274
- endpoint.send :include, helpers
275
- b.run endpoint
276
-
277
- b.to_app
278
- end
279
-
280
- def inherited(subclass)
378
+
379
+ def inherited(subclass)
281
380
  subclass.reset!
381
+ subclass.logger = logger.clone
282
382
  end
283
-
284
- def route_set
285
- @route_set ||= Rack::Mount::RouteSet.new
383
+
384
+ def inherit_settings(other_stack)
385
+ settings.prepend other_stack
386
+ endpoints.each{|e| e.settings.prepend(other_stack)}
286
387
  end
287
-
288
- def compile_path(path)
289
- parts = []
290
- parts << prefix if prefix
291
- parts << ':version' if version
292
- parts << namespace.to_s if namespace
293
- parts << path.to_s if path && '/' != path
294
- parts.last << '(.:format)'
295
- Rack::Mount::Utils.normalize_path(parts.join('/'))
296
- end
297
- end
298
-
299
- reset!
388
+ end
389
+
390
+ def initialize
391
+ @route_set = Rack::Mount::RouteSet.new
392
+ self.class.endpoints.each do |endpoint|
393
+ endpoint.mount_in(@route_set)
394
+ end
395
+ @route_set.freeze
396
+ end
397
+
398
+ def call(env)
399
+ @route_set.call(env)
400
+ end
401
+
402
+ reset!
300
403
  end
301
404
  end