grape 1.0.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (284) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +346 -39
  3. data/LICENSE +1 -1
  4. data/README.md +658 -90
  5. data/UPGRADING.md +472 -17
  6. data/grape.gemspec +17 -6
  7. data/lib/grape/api/helpers.rb +2 -0
  8. data/lib/grape/api/instance.rb +279 -0
  9. data/lib/grape/api.rb +132 -176
  10. data/lib/grape/config.rb +34 -0
  11. data/lib/grape/content_types.rb +34 -0
  12. data/lib/grape/cookies.rb +4 -0
  13. data/lib/grape/dsl/api.rb +2 -0
  14. data/lib/grape/dsl/callbacks.rb +22 -0
  15. data/lib/grape/dsl/configuration.rb +2 -0
  16. data/lib/grape/dsl/desc.rb +44 -12
  17. data/lib/grape/dsl/headers.rb +2 -0
  18. data/lib/grape/dsl/helpers.rb +11 -6
  19. data/lib/grape/dsl/inside_route.rb +116 -36
  20. data/lib/grape/dsl/logger.rb +2 -0
  21. data/lib/grape/dsl/middleware.rb +12 -3
  22. data/lib/grape/dsl/parameters.rb +34 -16
  23. data/lib/grape/dsl/request_response.rb +13 -8
  24. data/lib/grape/dsl/routing.rb +19 -12
  25. data/lib/grape/dsl/settings.rb +22 -4
  26. data/lib/grape/dsl/validations.rb +24 -4
  27. data/lib/grape/eager_load.rb +20 -0
  28. data/lib/grape/endpoint.rb +66 -57
  29. data/lib/grape/error_formatter/base.rb +2 -0
  30. data/lib/grape/error_formatter/json.rb +6 -4
  31. data/lib/grape/error_formatter/txt.rb +10 -3
  32. data/lib/grape/error_formatter/xml.rb +6 -4
  33. data/lib/grape/error_formatter.rb +4 -2
  34. data/lib/grape/exceptions/base.rb +22 -14
  35. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -1
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -1
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -1
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -1
  40. data/lib/grape/exceptions/invalid_response.rb +11 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -1
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -1
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -1
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -1
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -1
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -1
  47. data/lib/grape/exceptions/missing_option.rb +2 -1
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -1
  49. data/lib/grape/exceptions/unknown_options.rb +2 -1
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -1
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -1
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -1
  53. data/lib/grape/exceptions/validation.rb +5 -4
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +16 -13
  56. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  57. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  58. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  59. data/lib/grape/extensions/hash.rb +2 -0
  60. data/lib/grape/extensions/hashie/mash.rb +2 -0
  61. data/lib/grape/formatter/json.rb +3 -0
  62. data/lib/grape/formatter/serializable_hash.rb +4 -1
  63. data/lib/grape/formatter/txt.rb +2 -0
  64. data/lib/grape/formatter/xml.rb +3 -0
  65. data/lib/grape/formatter.rb +5 -3
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/locale/en.yml +3 -1
  68. data/lib/grape/middleware/auth/base.rb +7 -7
  69. data/lib/grape/middleware/auth/dsl.rb +2 -0
  70. data/lib/grape/middleware/auth/strategies.rb +2 -0
  71. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  72. data/lib/grape/middleware/base.rb +12 -7
  73. data/lib/grape/middleware/error.rb +75 -61
  74. data/lib/grape/middleware/filter.rb +2 -0
  75. data/lib/grape/middleware/formatter.rb +17 -13
  76. data/lib/grape/middleware/globals.rb +2 -0
  77. data/lib/grape/middleware/helpers.rb +12 -0
  78. data/lib/grape/middleware/stack.rb +15 -5
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  80. data/lib/grape/middleware/versioner/header.rb +13 -9
  81. data/lib/grape/middleware/versioner/param.rb +4 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  83. data/lib/grape/middleware/versioner/path.rb +5 -1
  84. data/lib/grape/middleware/versioner.rb +2 -0
  85. data/lib/grape/namespace.rb +15 -3
  86. data/lib/grape/parser/json.rb +3 -1
  87. data/lib/grape/parser/xml.rb +3 -1
  88. data/lib/grape/parser.rb +4 -2
  89. data/lib/grape/path.rb +16 -3
  90. data/lib/grape/presenters/presenter.rb +2 -0
  91. data/lib/grape/request.rb +19 -9
  92. data/lib/grape/router/attribute_translator.rb +41 -8
  93. data/lib/grape/router/pattern.rb +22 -18
  94. data/lib/grape/router/route.rb +16 -30
  95. data/lib/grape/router.rb +37 -28
  96. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  97. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  98. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  99. data/lib/grape/util/base_inheritable.rb +43 -0
  100. data/lib/grape/util/cache.rb +20 -0
  101. data/lib/grape/util/endpoint_configuration.rb +8 -0
  102. data/lib/grape/util/env.rb +19 -17
  103. data/lib/grape/util/inheritable_setting.rb +3 -3
  104. data/lib/grape/util/inheritable_values.rb +7 -25
  105. data/lib/grape/util/json.rb +2 -0
  106. data/lib/grape/util/lazy_block.rb +27 -0
  107. data/lib/grape/util/lazy_object.rb +43 -0
  108. data/lib/grape/util/lazy_value.rb +99 -0
  109. data/lib/grape/util/registrable.rb +2 -0
  110. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  111. data/lib/grape/util/stackable_values.rb +21 -34
  112. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  113. data/lib/grape/util/xml.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +16 -6
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  116. data/lib/grape/validations/params_scope.rb +128 -66
  117. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  118. data/lib/grape/validations/types/array_coercer.rb +65 -0
  119. data/lib/grape/validations/types/build_coercer.rb +52 -46
  120. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  121. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  122. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  123. data/lib/grape/validations/types/file.rb +22 -18
  124. data/lib/grape/validations/types/invalid_value.rb +24 -0
  125. data/lib/grape/validations/types/json.rb +47 -39
  126. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  127. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  128. data/lib/grape/validations/types/set_coercer.rb +40 -0
  129. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  130. data/lib/grape/validations/types.rb +26 -38
  131. data/lib/grape/validations/validator_factory.rb +8 -11
  132. data/lib/grape/validations/validators/all_or_none.rb +8 -12
  133. data/lib/grape/validations/validators/allow_blank.rb +4 -2
  134. data/lib/grape/validations/validators/as.rb +12 -0
  135. data/lib/grape/validations/validators/at_least_one_of.rb +7 -12
  136. data/lib/grape/validations/validators/base.rb +23 -15
  137. data/lib/grape/validations/validators/coerce.rb +47 -28
  138. data/lib/grape/validations/validators/default.rb +7 -6
  139. data/lib/grape/validations/validators/exactly_one_of.rb +10 -22
  140. data/lib/grape/validations/validators/except_values.rb +4 -2
  141. data/lib/grape/validations/validators/multiple_params_base.rb +19 -10
  142. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -17
  143. data/lib/grape/validations/validators/presence.rb +4 -1
  144. data/lib/grape/validations/validators/regexp.rb +5 -2
  145. data/lib/grape/validations/validators/same_as.rb +27 -0
  146. data/lib/grape/validations/validators/values.rb +29 -9
  147. data/lib/grape/validations.rb +2 -0
  148. data/lib/grape/version.rb +3 -1
  149. data/lib/grape.rb +107 -73
  150. data/spec/grape/api/custom_validations_spec.rb +6 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +116 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +49 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +775 -65
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +3 -1
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +23 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +11 -1
  178. data/spec/grape/dsl/parameters_spec.rb +12 -9
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +13 -1
  181. data/spec/grape/dsl/settings_spec.rb +4 -2
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +848 -0
  184. data/spec/grape/endpoint_spec.rb +75 -515
  185. data/spec/grape/entity_spec.rb +19 -11
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -1
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -1
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -1
  194. data/spec/grape/exceptions/unknown_options_spec.rb +3 -2
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -1
  196. data/spec/grape/exceptions/validation_errors_spec.rb +17 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +6 -4
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +4 -3
  210. data/spec/grape/middleware/exception_spec.rb +205 -55
  211. data/spec/grape/middleware/formatter_spec.rb +130 -15
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +15 -1
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +10 -2
  216. data/spec/grape/middleware/versioner/param_spec.rb +4 -2
  217. data/spec/grape/middleware/versioner/path_spec.rb +4 -2
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +9 -7
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +4 -2
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +15 -13
  227. data/spec/grape/util/stackable_values_spec.rb +16 -14
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +3 -1
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +364 -7
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +558 -133
  242. data/spec/grape/validations/validators/default_spec.rb +212 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +6 -3
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +73 -19
  250. data/spec/grape/validations_spec.rb +403 -53
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +78 -18
  255. data/spec/spec_helper.rb +13 -2
  256. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +3 -1
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +176 -130
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -1
  267. data/Gemfile +0 -34
  268. data/Gemfile.lock +0 -229
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -70
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -36
  275. data/gemfiles/multi_xml.gemfile +0 -36
  276. data/gemfiles/rack_1.5.2.gemfile +0 -36
  277. data/gemfiles/rack_edge.gemfile +0 -36
  278. data/gemfiles/rails_3.gemfile +0 -37
  279. data/gemfiles/rails_4.gemfile +0 -36
  280. data/gemfiles/rails_5.gemfile +0 -36
  281. data/gemfiles/rails_edge.gemfile +0 -36
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -0,0 +1,473 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'shared/versioning_examples'
5
+
6
+ describe Grape::API do
7
+ subject(:a_remounted_api) { Class.new(Grape::API) }
8
+ let(:root_api) { Class.new(Grape::API) }
9
+
10
+ def app
11
+ root_api
12
+ end
13
+
14
+ describe 'remounting an API' do
15
+ context 'with a defined route' do
16
+ before do
17
+ a_remounted_api.get '/votes' do
18
+ '10 votes'
19
+ end
20
+ end
21
+
22
+ context 'when mounting one instance' do
23
+ before do
24
+ root_api.mount a_remounted_api
25
+ end
26
+
27
+ it 'can access the endpoint' do
28
+ get '/votes'
29
+ expect(last_response.body).to eql '10 votes'
30
+ end
31
+ end
32
+
33
+ context 'when mounting twice' do
34
+ before do
35
+ root_api.mount a_remounted_api => '/posts'
36
+ root_api.mount a_remounted_api => '/comments'
37
+ end
38
+
39
+ it 'can access the votes in both places' do
40
+ get '/posts/votes'
41
+ expect(last_response.body).to eql '10 votes'
42
+ get '/comments/votes'
43
+ expect(last_response.body).to eql '10 votes'
44
+ end
45
+ end
46
+
47
+ context 'when mounting on namespace' do
48
+ before do
49
+ stub_const('StaticRefToAPI', a_remounted_api)
50
+ root_api.namespace 'posts' do
51
+ mount StaticRefToAPI
52
+ end
53
+
54
+ root_api.namespace 'comments' do
55
+ mount StaticRefToAPI
56
+ end
57
+ end
58
+
59
+ it 'can access the votes in both places' do
60
+ get '/posts/votes'
61
+ expect(last_response.body).to eql '10 votes'
62
+ get '/comments/votes'
63
+ expect(last_response.body).to eql '10 votes'
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'with dynamic configuration' do
69
+ context 'when mounting an endpoint conditional on a configuration' do
70
+ subject(:a_remounted_api) do
71
+ Class.new(Grape::API) do
72
+ get 'always' do
73
+ 'success'
74
+ end
75
+
76
+ given configuration[:mount_sometimes] do
77
+ get 'sometimes' do
78
+ 'sometimes'
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ it 'mounts the endpoints only when configured to do so' do
85
+ root_api.mount({ a_remounted_api => 'with_conditional' }, with: { mount_sometimes: true })
86
+ root_api.mount({ a_remounted_api => 'without_conditional' }, with: { mount_sometimes: false })
87
+
88
+ get '/with_conditional/always'
89
+ expect(last_response.body).to eq 'success'
90
+
91
+ get '/with_conditional/sometimes'
92
+ expect(last_response.body).to eq 'sometimes'
93
+
94
+ get '/without_conditional/always'
95
+ expect(last_response.body).to eq 'success'
96
+
97
+ get '/without_conditional/sometimes'
98
+ expect(last_response.status).to eq 404
99
+ end
100
+ end
101
+
102
+ context 'when using an expression derived from a configuration' do
103
+ subject(:a_remounted_api) do
104
+ Class.new(Grape::API) do
105
+ get(mounted { "api_name_#{configuration[:api_name]}" }) do
106
+ 'success'
107
+ end
108
+ end
109
+ end
110
+
111
+ before do
112
+ root_api.mount a_remounted_api, with: {
113
+ api_name: 'a_name'
114
+ }
115
+ end
116
+
117
+ it 'mounts the endpoint with the name' do
118
+ get 'api_name_a_name'
119
+ expect(last_response.body).to eq 'success'
120
+ end
121
+
122
+ it 'does not mount the endpoint with a null name' do
123
+ get 'api_name_'
124
+ expect(last_response.body).not_to eq 'success'
125
+ end
126
+
127
+ context 'when the expression lives in a namespace' do
128
+ subject(:a_remounted_api) do
129
+ Class.new(Grape::API) do
130
+ namespace :base do
131
+ get(mounted { "api_name_#{configuration[:api_name]}" }) do
132
+ 'success'
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ it 'mounts the endpoint with the name' do
139
+ get 'base/api_name_a_name'
140
+ expect(last_response.body).to eq 'success'
141
+ end
142
+
143
+ it 'does not mount the endpoint with a null name' do
144
+ get 'base/api_name_'
145
+ expect(last_response.body).not_to eq 'success'
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'when executing a standard block within a `mounted` block with all dynamic params' do
151
+ subject(:a_remounted_api) do
152
+ Class.new(Grape::API) do
153
+ mounted do
154
+ desc configuration[:description] do
155
+ headers configuration[:headers]
156
+ end
157
+ get configuration[:endpoint] do
158
+ configuration[:response]
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ let(:api_endpoint) { 'custom_endpoint' }
165
+ let(:api_response) { 'custom response' }
166
+ let(:endpoint_description) { 'this is a custom API' }
167
+ let(:headers) do
168
+ {
169
+ 'XAuthToken' => {
170
+ 'description' => 'Validates your identity',
171
+ 'required' => true
172
+ }
173
+ }
174
+ end
175
+
176
+ it 'mounts the API and obtains the description and headers definition' do
177
+ root_api.mount a_remounted_api, with: {
178
+ description: endpoint_description,
179
+ headers: headers,
180
+ endpoint: api_endpoint,
181
+ response: api_response
182
+ }
183
+ get api_endpoint
184
+ expect(last_response.body).to eq api_response
185
+ expect(a_remounted_api.instances.last.endpoints.first.options[:route_options][:description])
186
+ .to eq endpoint_description
187
+ expect(a_remounted_api.instances.last.endpoints.first.options[:route_options][:headers])
188
+ .to eq headers
189
+ end
190
+ end
191
+
192
+ context 'when executing a custom block on mount' do
193
+ subject(:a_remounted_api) do
194
+ Class.new(Grape::API) do
195
+ get 'always' do
196
+ 'success'
197
+ end
198
+
199
+ mounted do
200
+ configuration[:endpoints].each do |endpoint_name, endpoint_response|
201
+ get endpoint_name do
202
+ endpoint_response
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ it 'mounts the endpoints only when configured to do so' do
210
+ root_api.mount a_remounted_api, with: { endpoints: { 'api_name' => 'api_response' } }
211
+ get 'api_name'
212
+ expect(last_response.body).to eq 'api_response'
213
+ end
214
+ end
215
+
216
+ context 'when the configuration is part of the arguments of a method' do
217
+ subject(:a_remounted_api) do
218
+ Class.new(Grape::API) do
219
+ get configuration[:endpoint_name] do
220
+ 'success'
221
+ end
222
+ end
223
+ end
224
+
225
+ it 'mounts the endpoint in the location it is configured' do
226
+ root_api.mount a_remounted_api, with: { endpoint_name: 'some_location' }
227
+ get '/some_location'
228
+ expect(last_response.body).to eq 'success'
229
+
230
+ get '/different_location'
231
+ expect(last_response.status).to eq 404
232
+
233
+ root_api.mount a_remounted_api, with: { endpoint_name: 'new_location' }
234
+ get '/new_location'
235
+ expect(last_response.body).to eq 'success'
236
+ end
237
+
238
+ context 'when the configuration is the value in a key-arg pair' do
239
+ subject(:a_remounted_api) do
240
+ Class.new(Grape::API) do
241
+ version 'v1', using: :param, parameter: configuration[:version_param]
242
+ get 'endpoint' do
243
+ 'version 1'
244
+ end
245
+
246
+ version 'v2', using: :param, parameter: configuration[:version_param]
247
+ get 'endpoint' do
248
+ 'version 2'
249
+ end
250
+ end
251
+ end
252
+
253
+ it 'takes the param from the configuration' do
254
+ root_api.mount a_remounted_api, with: { version_param: 'param_name' }
255
+
256
+ get '/endpoint?param_name=v1'
257
+ expect(last_response.body).to eq 'version 1'
258
+
259
+ get '/endpoint?param_name=v2'
260
+ expect(last_response.body).to eq 'version 2'
261
+
262
+ get '/endpoint?wrong_param_name=v2'
263
+ expect(last_response.body).to eq 'version 1'
264
+ end
265
+ end
266
+ end
267
+
268
+ context 'on the DescSCope' do
269
+ subject(:a_remounted_api) do
270
+ Class.new(Grape::API) do
271
+ desc 'The description of this' do
272
+ tags ['not_configurable_tag', configuration[:a_configurable_tag]]
273
+ end
274
+ get 'location' do
275
+ 'success'
276
+ end
277
+ end
278
+ end
279
+
280
+ it 'mounts the endpoint with the appropiate tags' do
281
+ root_api.mount({ a_remounted_api => 'integer' }, with: { a_configurable_tag: 'a configured tag' })
282
+ end
283
+ end
284
+
285
+ context 'on the ParamScope' do
286
+ subject(:a_remounted_api) do
287
+ Class.new(Grape::API) do
288
+ params do
289
+ requires configuration[:required_param], type: configuration[:required_type]
290
+ end
291
+
292
+ get 'location' do
293
+ 'success'
294
+ end
295
+ end
296
+ end
297
+
298
+ it 'mounts the endpoint in the location it is configured' do
299
+ root_api.mount({ a_remounted_api => 'string' }, with: { required_param: 'param_key', required_type: String })
300
+ root_api.mount({ a_remounted_api => 'integer' }, with: { required_param: 'param_integer', required_type: Integer })
301
+
302
+ get '/string/location', param_key: 'a'
303
+ expect(last_response.body).to eq 'success'
304
+
305
+ get '/string/location', param_integer: 1
306
+ expect(last_response.status).to eq 400
307
+
308
+ get '/integer/location', param_integer: 1
309
+ expect(last_response.body).to eq 'success'
310
+
311
+ get '/integer/location', param_integer: 'a'
312
+ expect(last_response.status).to eq 400
313
+ end
314
+
315
+ context 'on dynamic checks' do
316
+ subject(:a_remounted_api) do
317
+ Class.new(Grape::API) do
318
+ params do
319
+ optional :restricted_values, values: -> { [configuration[:allowed_value], 'always'] }
320
+ end
321
+
322
+ get 'location' do
323
+ 'success'
324
+ end
325
+ end
326
+ end
327
+
328
+ it 'can read the configuration on lambdas' do
329
+ root_api.mount a_remounted_api, with: { allowed_value: 'sometimes' }
330
+ get '/location', restricted_values: 'always'
331
+ expect(last_response.body).to eq 'success'
332
+ get '/location', restricted_values: 'sometimes'
333
+ expect(last_response.body).to eq 'success'
334
+ get '/location', restricted_values: 'never'
335
+ expect(last_response.status).to eq 400
336
+ end
337
+ end
338
+ end
339
+
340
+ context 'when the configuration is read within a namespace' do
341
+ before do
342
+ a_remounted_api.namespace 'api' do
343
+ params do
344
+ requires configuration[:required_param]
345
+ end
346
+ get "/#{configuration[:path]}" do
347
+ '10 votes'
348
+ end
349
+ end
350
+ root_api.mount a_remounted_api, with: { path: 'votes', required_param: 'param_key' }
351
+ root_api.mount a_remounted_api, with: { path: 'scores', required_param: 'param_key' }
352
+ end
353
+
354
+ it 'will use the dynamic configuration on all routes' do
355
+ get 'api/votes', param_key: 'a'
356
+ expect(last_response.body).to eql '10 votes'
357
+ get 'api/scores', param_key: 'a'
358
+ expect(last_response.body).to eql '10 votes'
359
+ get 'api/votes'
360
+ expect(last_response.status).to eq 400
361
+ end
362
+ end
363
+
364
+ context 'a very complex configuration example' do
365
+ before do
366
+ top_level_api = Class.new(Grape::API) do
367
+ remounted_api = Class.new(Grape::API) do
368
+ get configuration[:endpoint_name] do
369
+ configuration[:response]
370
+ end
371
+ end
372
+
373
+ expression_namespace = mounted { configuration[:namespace].to_s * 2 }
374
+ given(mounted { configuration[:should_mount_expressed] != false }) do
375
+ namespace expression_namespace do
376
+ mount remounted_api, with: { endpoint_name: configuration[:endpoint_name], response: configuration[:endpoint_response] }
377
+ end
378
+ end
379
+ end
380
+ root_api.mount top_level_api, with: configuration_options
381
+ end
382
+
383
+ context 'when the namespace should be mounted' do
384
+ let(:configuration_options) do
385
+ {
386
+ should_mount_expressed: true,
387
+ namespace: 'bang',
388
+ endpoint_name: 'james',
389
+ endpoint_response: 'bond'
390
+ }
391
+ end
392
+
393
+ it 'gets a response' do
394
+ get 'bangbang/james'
395
+ expect(last_response.body).to eq 'bond'
396
+ end
397
+ end
398
+
399
+ context 'when should be mounted is nil' do
400
+ let(:configuration_options) do
401
+ {
402
+ should_mount_expressed: nil,
403
+ namespace: 'bang',
404
+ endpoint_name: 'james',
405
+ endpoint_response: 'bond'
406
+ }
407
+ end
408
+
409
+ it 'gets a response' do
410
+ get 'bangbang/james'
411
+ expect(last_response.body).to eq 'bond'
412
+ end
413
+ end
414
+
415
+ context 'when it should not be mounted' do
416
+ let(:configuration_options) do
417
+ {
418
+ should_mount_expressed: false,
419
+ namespace: 'bang',
420
+ endpoint_name: 'james',
421
+ endpoint_response: 'bond'
422
+ }
423
+ end
424
+
425
+ it 'gets a response' do
426
+ get 'bangbang/james'
427
+ expect(last_response.body).not_to eq 'bond'
428
+ end
429
+ end
430
+ end
431
+
432
+ context 'when the configuration is read in a helper' do
433
+ subject(:a_remounted_api) do
434
+ Class.new(Grape::API) do
435
+ helpers do
436
+ def printed_response
437
+ configuration[:some_value]
438
+ end
439
+ end
440
+
441
+ get 'location' do
442
+ printed_response
443
+ end
444
+ end
445
+ end
446
+
447
+ it 'will use the dynamic configuration on all routes' do
448
+ root_api.mount(a_remounted_api, with: { some_value: 'response value' })
449
+
450
+ get '/location'
451
+ expect(last_response.body).to eq 'response value'
452
+ end
453
+ end
454
+
455
+ context 'when the configuration is read within the response block' do
456
+ subject(:a_remounted_api) do
457
+ Class.new(Grape::API) do
458
+ get 'location' do
459
+ configuration[:some_value]
460
+ end
461
+ end
462
+ end
463
+
464
+ it 'will use the dynamic configuration on all routes' do
465
+ root_api.mount(a_remounted_api, with: { some_value: 'response value' })
466
+
467
+ get '/location'
468
+ expect(last_response.body).to eq 'response value'
469
+ end
470
+ end
471
+ end
472
+ end
473
+ end