praxis 2.0.pre.17 → 2.0.pre.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/.simplecov +3 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +19 -0
  6. data/CONTRIBUTING.md +2 -79
  7. data/Gemfile +5 -1
  8. data/Guardfile +6 -4
  9. data/LICENSE +0 -2
  10. data/MAINTAINERS.md +1 -0
  11. data/README.md +15 -22
  12. data/Rakefile +4 -2
  13. data/bin/praxis +55 -58
  14. data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
  15. data/lib/praxis/action_definition.rb +65 -95
  16. data/lib/praxis/api_definition.rb +21 -29
  17. data/lib/praxis/api_general_info.rb +55 -66
  18. data/lib/praxis/application.rb +15 -32
  19. data/lib/praxis/blueprint.rb +80 -73
  20. data/lib/praxis/bootloader.rb +24 -33
  21. data/lib/praxis/bootloader_stages/environment.rb +5 -10
  22. data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
  23. data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
  24. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
  25. data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
  26. data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
  27. data/lib/praxis/bootloader_stages/routing.rb +5 -8
  28. data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
  29. data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
  30. data/lib/praxis/callbacks.rb +12 -11
  31. data/lib/praxis/collection.rb +11 -14
  32. data/lib/praxis/config.rb +17 -28
  33. data/lib/praxis/config_hash.rb +2 -1
  34. data/lib/praxis/controller.rb +7 -6
  35. data/lib/praxis/dispatcher.rb +34 -42
  36. data/lib/praxis/docs/open_api/info_object.rb +11 -8
  37. data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
  38. data/lib/praxis/docs/open_api/operation_object.rb +7 -4
  39. data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
  40. data/lib/praxis/docs/open_api/paths_object.rb +11 -9
  41. data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
  42. data/lib/praxis/docs/open_api/response_object.rb +24 -18
  43. data/lib/praxis/docs/open_api/responses_object.rb +3 -1
  44. data/lib/praxis/docs/open_api/schema_object.rb +61 -29
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +9 -6
  47. data/lib/praxis/docs/open_api_generator.rb +114 -150
  48. data/lib/praxis/endpoint_definition.rb +60 -77
  49. data/lib/praxis/error_handler.rb +2 -2
  50. data/lib/praxis/exception.rb +2 -0
  51. data/lib/praxis/exceptions/config.rb +3 -1
  52. data/lib/praxis/exceptions/config_load.rb +2 -0
  53. data/lib/praxis/exceptions/config_validation.rb +3 -1
  54. data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
  55. data/lib/praxis/exceptions/invalid_response.rb +3 -1
  56. data/lib/praxis/exceptions/invalid_trait.rb +3 -1
  57. data/lib/praxis/exceptions/stage_not_found.rb +3 -1
  58. data/lib/praxis/exceptions/validation.rb +4 -3
  59. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
  60. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
  61. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
  62. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
  63. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
  64. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
  65. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
  66. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
  67. data/lib/praxis/extensions/attribute_filtering.rb +3 -1
  68. data/lib/praxis/extensions/field_expansion.rb +6 -4
  69. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
  70. data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
  71. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
  72. data/lib/praxis/extensions/field_selection.rb +3 -1
  73. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
  74. data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
  75. data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
  76. data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
  77. data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
  78. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
  79. data/lib/praxis/extensions/pagination.rb +10 -15
  80. data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
  81. data/lib/praxis/extensions/rails_compat.rb +2 -0
  82. data/lib/praxis/extensions/rendering.rb +12 -12
  83. data/lib/praxis/field_expander.rb +8 -9
  84. data/lib/praxis/file_group.rb +8 -12
  85. data/lib/praxis/finalizable.rb +1 -0
  86. data/lib/praxis/handlers/json.rb +5 -2
  87. data/lib/praxis/handlers/plain.rb +2 -1
  88. data/lib/praxis/handlers/www_form.rb +6 -3
  89. data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
  90. data/lib/praxis/mapper/active_model_compat.rb +13 -10
  91. data/lib/praxis/mapper/resource.rb +196 -181
  92. data/lib/praxis/mapper/selector_generator.rb +106 -112
  93. data/lib/praxis/mapper/sequel_compat.rb +70 -67
  94. data/lib/praxis/media_type.rb +2 -2
  95. data/lib/praxis/media_type_identifier.rb +26 -22
  96. data/lib/praxis/middleware_app.rb +18 -15
  97. data/lib/praxis/multipart/parser.rb +46 -51
  98. data/lib/praxis/multipart/part.rb +78 -110
  99. data/lib/praxis/notifications.rb +2 -4
  100. data/lib/praxis/plugin.rb +11 -18
  101. data/lib/praxis/plugin_concern.rb +12 -15
  102. data/lib/praxis/plugins/mapper_plugin.rb +15 -13
  103. data/lib/praxis/plugins/pagination_plugin.rb +8 -6
  104. data/lib/praxis/plugins/rails_plugin.rb +33 -28
  105. data/lib/praxis/renderer.rb +11 -15
  106. data/lib/praxis/request.rb +48 -44
  107. data/lib/praxis/request_stages/action.rb +4 -6
  108. data/lib/praxis/request_stages/load_request.rb +2 -4
  109. data/lib/praxis/request_stages/request_stage.rb +19 -23
  110. data/lib/praxis/request_stages/response.rb +4 -6
  111. data/lib/praxis/request_stages/validate.rb +3 -5
  112. data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -28
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +24 -26
  117. data/lib/praxis/response_definition.rb +77 -122
  118. data/lib/praxis/response_template.rb +11 -15
  119. data/lib/praxis/responses/http.rb +23 -44
  120. data/lib/praxis/responses/internal_server_error.rb +18 -21
  121. data/lib/praxis/responses/multipart_ok.rb +4 -9
  122. data/lib/praxis/responses/validation_error.rb +8 -15
  123. data/lib/praxis/route.rb +8 -10
  124. data/lib/praxis/router/rack.rb +13 -7
  125. data/lib/praxis/router/simple.rb +10 -5
  126. data/lib/praxis/router.rb +27 -34
  127. data/lib/praxis/routing_config.rb +52 -29
  128. data/lib/praxis/simple_media_type.rb +5 -8
  129. data/lib/praxis/stage.rb +17 -25
  130. data/lib/praxis/tasks/api_docs.rb +17 -16
  131. data/lib/praxis/tasks/console.rb +3 -1
  132. data/lib/praxis/tasks/environment.rb +2 -0
  133. data/lib/praxis/tasks/routes.rb +26 -24
  134. data/lib/praxis/tasks.rb +3 -1
  135. data/lib/praxis/trait.rb +37 -46
  136. data/lib/praxis/types/fuzzy_hash.rb +13 -14
  137. data/lib/praxis/types/media_type_common.rb +11 -10
  138. data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
  139. data/lib/praxis/types/multipart_array.rb +100 -115
  140. data/lib/praxis/validation_handler.rb +5 -3
  141. data/lib/praxis/version.rb +3 -1
  142. data/lib/praxis.rb +4 -5
  143. data/praxis.gemspec +22 -21
  144. data/spec/functional_spec.rb +44 -56
  145. data/spec/praxis/action_definition_spec.rb +39 -48
  146. data/spec/praxis/api_definition_spec.rb +45 -47
  147. data/spec/praxis/api_general_info_spec.rb +28 -29
  148. data/spec/praxis/application_spec.rb +18 -14
  149. data/spec/praxis/blueprint_spec.rb +33 -34
  150. data/spec/praxis/bootloader_spec.rb +32 -30
  151. data/spec/praxis/callbacks_spec.rb +37 -37
  152. data/spec/praxis/collection_spec.rb +18 -25
  153. data/spec/praxis/config_hash_spec.rb +5 -4
  154. data/spec/praxis/config_spec.rb +27 -26
  155. data/spec/praxis/controller_spec.rb +8 -9
  156. data/spec/praxis/endpoint_definition_spec.rb +25 -32
  157. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
  158. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
  159. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
  160. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
  161. data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
  162. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
  163. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
  164. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
  165. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
  166. data/spec/praxis/extensions/rendering_spec.rb +9 -9
  167. data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
  168. data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
  169. data/spec/praxis/field_expander_spec.rb +6 -5
  170. data/spec/praxis/file_group_spec.rb +3 -1
  171. data/spec/praxis/handlers/json_spec.rb +6 -5
  172. data/spec/praxis/mapper/resource_spec.rb +39 -29
  173. data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
  174. data/spec/praxis/media_type_identifier_spec.rb +13 -10
  175. data/spec/praxis/media_type_spec.rb +12 -12
  176. data/spec/praxis/middleware_app_spec.rb +23 -22
  177. data/spec/praxis/multipart/parser_spec.rb +7 -9
  178. data/spec/praxis/notifications_spec.rb +4 -4
  179. data/spec/praxis/plugin_concern_spec.rb +5 -6
  180. data/spec/praxis/renderer_spec.rb +10 -9
  181. data/spec/praxis/request_spec.rb +38 -41
  182. data/spec/praxis/request_stages/action_spec.rb +14 -15
  183. data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
  184. data/spec/praxis/request_stages/validate_spec.rb +3 -1
  185. data/spec/praxis/response_definition_spec.rb +79 -92
  186. data/spec/praxis/response_spec.rb +35 -40
  187. data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
  188. data/spec/praxis/responses/validation_error_spec.rb +17 -18
  189. data/spec/praxis/route_spec.rb +4 -7
  190. data/spec/praxis/router_spec.rb +69 -79
  191. data/spec/praxis/routing_config_spec.rb +15 -14
  192. data/spec/praxis/stage_spec.rb +56 -53
  193. data/spec/praxis/trait_spec.rb +17 -17
  194. data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
  195. data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
  196. data/spec/praxis/types/multipart_array_spec.rb +33 -48
  197. data/spec/spec_app/app/concerns/authenticated.rb +5 -5
  198. data/spec/spec_app/app/concerns/basic_api.rb +3 -1
  199. data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
  200. data/spec/spec_app/app/controllers/base_class.rb +6 -5
  201. data/spec/spec_app/app/controllers/instances.rb +31 -34
  202. data/spec/spec_app/app/controllers/volumes.rb +6 -6
  203. data/spec/spec_app/app/responses/multipart.rb +1 -2
  204. data/spec/spec_app/app/responses/other_response.rb +2 -2
  205. data/spec/spec_app/config/environment.rb +19 -6
  206. data/spec/spec_app/config.ru +4 -3
  207. data/spec/spec_app/design/api.rb +13 -15
  208. data/spec/spec_app/design/media_types/instance.rb +6 -6
  209. data/spec/spec_app/design/media_types/volume.rb +2 -1
  210. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
  211. data/spec/spec_app/design/resources/instances.rb +11 -17
  212. data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
  213. data/spec/spec_app/design/resources/volumes.rb +4 -5
  214. data/spec/spec_helper.rb +11 -13
  215. data/spec/support/be_deep_equal_matcher.rb +5 -0
  216. data/spec/support/spec_authorization_plugin.rb +7 -12
  217. data/spec/support/spec_blueprints.rb +5 -4
  218. data/spec/support/spec_complex_authentication_plugin.rb +17 -34
  219. data/spec/support/spec_endpoint_definitions.rb +2 -3
  220. data/spec/support/spec_media_types.rb +28 -35
  221. data/spec/support/spec_resources.rb +22 -16
  222. data/spec/support/spec_simple_authentication_plugin.rb +5 -9
  223. data/tasks/loader.thor +4 -2
  224. data/tasks/thor/app.rb +7 -5
  225. data/tasks/thor/example.rb +23 -22
  226. data/tasks/thor/model.rb +7 -7
  227. data/tasks/thor/scaffold.rb +23 -23
  228. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
  229. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
  230. metadata +72 -84
  231. data/MAINTAINERS +0 -2
  232. data/TODO.md +0 -25
  233. data/spec/praxis/api_resource_spec.rb +0 -0
  234. data/spec/praxis/dispatcher_spec.rb +0 -0
  235. data/spec/spec_app/app/responses/bulk_response.rb +0 -0
@@ -1,7 +1,7 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  module Responses
4
-
5
5
  # Descriptions from http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
6
6
 
7
7
  # Standard response for successful HTTP requests.
@@ -20,7 +20,6 @@ module Praxis
20
20
  self.status = 201
21
21
  end
22
22
 
23
-
24
23
  # The request has been accepted for processing, but the
25
24
  # processing has not been completed. The request might or might
26
25
  # not eventually be acted upon, as it might be disallowed when
@@ -29,7 +28,6 @@ module Praxis
29
28
  self.status = 202
30
29
  end
31
30
 
32
-
33
31
  # The server successfully processed the request, but is not
34
32
  # returning any content. Usually used as a response to
35
33
  # a successful delete request.
@@ -37,73 +35,61 @@ module Praxis
37
35
  self.status = 204
38
36
  end
39
37
 
40
-
41
38
  # Indicates multiple options for the resource that the client may follow. It, for instance, could be used to present different format options for video, list files with different extensions, or word sense disambiguation.
42
39
  class MultipleChoices < Praxis::Response
43
40
  self.status = 300
44
41
  end
45
42
 
46
-
47
43
  # This and all future requests should be directed to the given URI.
48
44
  class MovedPermanently < Praxis::Response
49
45
  self.status = 301
50
46
  end
51
47
 
52
-
53
48
  # This is an example of industry practice contradicting the standard. The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"),[5] but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added self.status = codes 303 and 307 to distinguish between the two behaviours.[6] However, some Web applications and frameworks use the 302 self.status = code as if it were the 303.[7]
54
49
  class Found < Praxis::Response
55
50
  self.status = 302
56
51
  end
57
52
 
58
-
59
53
  # The response to the request can be found under another URI using a GET method. When received in response to a POST (or PUT/DELETE), it should be assumed that the server has received the data and the redirect should be issued with a separate GET message.
60
54
  class SeeOther < Praxis::Response
61
55
  self.status = 303
62
56
  end
63
57
 
64
-
65
58
  # Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-Match. This means that there is no need to retransmit the resource, since the client still has a previously-downloaded copy.
66
59
  class NotModified < Praxis::Response
67
60
  self.status = 304
68
61
  end
69
62
 
70
-
71
63
  # In this case, the request should be repeated with another URI; however, future requests should still use the original URI. In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. For instance, a POST request should be repeated using another POST request.[10]
72
64
  class TemporaryRedirect < Praxis::Response
73
65
  self.status = 307
74
66
  end
75
67
 
76
-
77
68
  # The request cannot be fulfilled due to bad syntax.
78
69
  class BadRequest < Praxis::Response
79
70
  self.status = 400
80
71
  end
81
72
 
82
-
83
73
  # Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource. See Basic access authentication and Digest access authentication.
84
74
  class Unauthorized < Praxis::Response
85
75
  self.status = 401
86
76
  end
87
77
 
88
-
89
78
  # The request was a valid request, but the server is refusing to respond to it. Unlike a 401 Unauthorized response, authenticating will make no difference.
90
79
  class Forbidden < Praxis::Response
91
80
  self.status = 403
92
81
  end
93
82
 
94
-
95
83
  # The requested resource could not be found but may be available again in the future. Subsequent requests by the client are permissible.
96
84
  class NotFound < Praxis::Response
97
85
  self.status = 404
98
86
  end
99
87
 
100
-
101
88
  # A request was made of a resource using a request method not supported by that resource; for example, using GET on a form which requires data to be presented via POST, or using PUT on a read-only resource.
102
89
  class MethodNotAllowed < Praxis::Response
103
90
  self.status = 405
104
91
  end
105
92
 
106
-
107
93
  # The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
108
94
  class NotAcceptable < Praxis::Response
109
95
  self.status = 406
@@ -119,55 +105,48 @@ module Praxis
119
105
  self.status = 409
120
106
  end
121
107
 
122
-
123
108
  # The server does not meet one of the preconditions that the requester put on the request.
124
109
  class PreconditionFailed < Praxis::Response
125
110
  self.status = 412
126
111
  end
127
112
 
128
-
129
113
  # The request was well-formed but was unable to be followed due to semantic errors.[3]
130
114
  class UnprocessableEntity < Praxis::Response
131
115
  self.status = 422
132
116
  end
133
117
 
134
118
  ApiDefinition.define do |api|
135
-
136
-
137
119
  [
138
- [ :accepted, 202, "The request has been accepted for processing, but the processing has not been completed." ],
139
- [ :no_content, 204,"The server successfully processed the request, but is not returning any content."],
140
- [ :multiple_choices, 300,"Indicates multiple options for the resource that the client may follow."],
141
- [ :moved_permanently, 301,"This and all future requests should be directed to the given URI."],
142
- [ :found, 302,"The requested resource resides temporarily under a different URI."],
143
- [ :see_other, 303,"The response to the request can be found under another URI using a GET method"],
144
- [ :not_modified, 304,"Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-Match."],
145
- [ :temporary_redirect, 307,"In this case, the request should be repeated with another URI; however, future requests should still use the original URI."],
146
- [ :bad_request, 400,"The request cannot be fulfilled due to bad syntax."],
147
- [ :unauthorized, 401,"Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided."],
148
- [ :forbidden, 403,"The request was a valid request, but the server is refusing to respond to it."],
149
- [ :not_found, 404,"The requested resource could not be found but may be available again in the future."],
150
- [ :method_not_allowed, 405,"A request was made of a resource using a request method not supported by that resource."],
151
- [ :not_acceptable, 406,"The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request."],
152
- [ :request_timeout, 408,"The server timed out waiting for the request."],
153
- [ :conflict, 409, "Indicates that the request could not be processed because of conflict in the request, such as an edit conflict in the case of multiple updates."],
154
- [ :precondition_failed, 412,"The server does not meet one of the preconditions that the requester put on the request."],
155
- [ :unprocessable_entity, 422,"The request was well-formed but was unable to be followed due to semantic errors."],
120
+ [:accepted, 202, 'The request has been accepted for processing, but the processing has not been completed.'],
121
+ [:no_content, 204, 'The server successfully processed the request, but is not returning any content.'],
122
+ [:multiple_choices, 300, 'Indicates multiple options for the resource that the client may follow.'],
123
+ [:moved_permanently, 301, 'This and all future requests should be directed to the given URI.'],
124
+ [:found, 302, 'The requested resource resides temporarily under a different URI.'],
125
+ [:see_other, 303, 'The response to the request can be found under another URI using a GET method'],
126
+ [:not_modified, 304, 'Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-Match.'],
127
+ [:temporary_redirect, 307, 'In this case, the request should be repeated with another URI; however, future requests should still use the original URI.'],
128
+ [:bad_request, 400, 'The request cannot be fulfilled due to bad syntax.'],
129
+ [:unauthorized, 401, 'Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.'],
130
+ [:forbidden, 403, 'The request was a valid request, but the server is refusing to respond to it.'],
131
+ [:not_found, 404, 'The requested resource could not be found but may be available again in the future.'],
132
+ [:method_not_allowed, 405, 'A request was made of a resource using a request method not supported by that resource.'],
133
+ [:not_acceptable, 406, 'The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.'],
134
+ [:request_timeout, 408, 'The server timed out waiting for the request.'],
135
+ [:conflict, 409, 'Indicates that the request could not be processed because of conflict in the request, such as an edit conflict in the case of multiple updates.'],
136
+ [:precondition_failed, 412, 'The server does not meet one of the preconditions that the requester put on the request.'],
137
+ [:unprocessable_entity, 422, 'The request was well-formed but was unable to be followed due to semantic errors.']
156
138
  ].each do |name, code, base_description|
157
139
  api.response_template name do |media_type: nil, location: nil, headers: nil, description: nil|
158
140
  status code
159
- description( description || base_description ) # description can "potentially" be overriden in an individual action.
141
+ description(description || base_description) # description can "potentially" be overriden in an individual action.
160
142
 
161
143
  media_type media_type if media_type
162
144
  location location if location
163
- headers&.each do |(name, value)|
164
- header(name: name, value: value)
145
+ headers&.each do |(header_name, value)|
146
+ header(name: header_name, value: value)
165
147
  end
166
148
  end
167
149
  end
168
-
169
150
  end
170
-
171
-
172
151
  end
173
152
  end
@@ -1,7 +1,7 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  module Responses
4
-
5
5
  # A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.
6
6
  class InternalServerError < Praxis::Response
7
7
  self.status = 500
@@ -9,37 +9,34 @@ module Praxis
9
9
 
10
10
  def initialize(error: nil, **opts)
11
11
  super(**opts)
12
- @headers['Content-Type'] = 'application/json' #TODO: might want an error mediatype
12
+ @headers['Content-Type'] = 'application/json' # TODO: might want an error mediatype
13
13
  @error = error
14
14
  end
15
15
 
16
16
  def format!(exception = @error)
17
- if @error
18
-
19
- if Application.instance.config.praxis.show_exceptions == true
20
- msg = {
21
- name: exception.class.name,
22
- message: exception.message,
23
- backtrace: exception.backtrace
24
- }
25
- msg[:cause] = format!(exception.cause) if exception.cause
26
- else
27
- msg = {name: 'InternalServerError', message: "Something bad happened."}
28
- end
29
-
30
- @body = msg
17
+ return unless @error
18
+
19
+ if Application.instance.config.praxis.show_exceptions == true
20
+ msg = {
21
+ name: exception.class.name,
22
+ message: exception.message,
23
+ backtrace: exception.backtrace
24
+ }
25
+ msg[:cause] = format!(exception.cause) if exception.cause
26
+ else
27
+ msg = { name: 'InternalServerError', message: 'Something bad happened.' }
31
28
  end
29
+
30
+ @body = msg
32
31
  end
33
32
  end
34
-
35
33
  end
36
34
 
37
35
  ApiDefinition.define do |api|
38
36
  api.response_template :internal_server_error do
39
- description "A generic error message, given when an unexpected condition was encountered and no more specific message is suitable."
37
+ description 'A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.'
40
38
  status 500
41
- media_type "application/json"
39
+ media_type 'application/json'
42
40
  end
43
41
  end
44
-
45
42
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Responses
3
5
  class MultipartOk < Ok
4
-
5
- def initialize(status:self.class.status, headers:{}, body:'')
6
+ def initialize(status: self.class.status, headers: {}, body: '')
6
7
  @name = response_name
7
8
  @status = status
8
9
  @headers = headers
@@ -12,13 +13,10 @@ module Praxis
12
13
  def handle
13
14
  case @body
14
15
  when Praxis::Types::MultipartArray
15
- if @headers['Content-Type'].nil?
16
- @headers['Content-Type'] = @body.content_type
17
- end
16
+ @headers['Content-Type'] = @body.content_type if @headers['Content-Type'].nil?
18
17
  end
19
18
  end
20
19
 
21
-
22
20
  def encode!
23
21
  case @body
24
22
  when Praxis::Types::MultipartArray
@@ -36,9 +34,7 @@ module Praxis
36
34
 
37
35
  [@status, @headers, @body]
38
36
  end
39
-
40
37
  end
41
-
42
38
  end
43
39
 
44
40
  ApiDefinition.define do |api|
@@ -47,5 +43,4 @@ module Praxis
47
43
  media_type media_type
48
44
  end
49
45
  end
50
-
51
46
  end
@@ -1,43 +1,36 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  module Responses
4
-
5
5
  class ValidationError < BadRequest
6
6
  def initialize(summary:, errors: nil, exception: nil, documentation: nil, **opts)
7
7
  super(**opts)
8
- @headers['Content-Type'] = 'application/json' #TODO: might want an error mediatype
8
+ @headers['Content-Type'] = 'application/json' # TODO: might want an error mediatype
9
9
  @errors = errors
10
- unless @errors # The exception message will the the only error if no errors are passed in
11
- @errors = [exception.message] if exception && exception.message
12
- end
10
+ @errors = [exception.message] if !@errors && exception&.message # The exception message will the the only error if no errors are passed in
13
11
  @exception = exception
14
12
  @summary = summary
15
13
  @documentation = documentation
16
14
  end
17
15
 
18
16
  def format!
19
- @body = {name: 'ValidationError', summary: @summary }
17
+ @body = { name: 'ValidationError', summary: @summary }
20
18
  @body[:errors] = @errors if @errors
21
19
 
22
- if @exception && @exception.cause
23
- @body[:cause] = {name: @exception.cause.class.name, message: @exception.cause.message}
24
- end
20
+ @body[:cause] = { name: @exception.cause.class.name, message: @exception.cause.message } if @exception&.cause
25
21
 
26
22
  @body[:documentation] = @documentation if @documentation
27
23
 
28
24
  @body
29
25
  end
30
26
  end
31
-
32
27
  end
33
28
 
34
-
35
29
  ApiDefinition.define do |api|
36
30
  api.response_template :validation_error do
37
- description "An error message indicating that one or more elements of the request did not match the API specification for the action"
31
+ description 'An error message indicating that one or more elements of the request did not match the API specification for the action'
38
32
  status 400
39
- media_type "application/json"
33
+ media_type 'application/json'
40
34
  end
41
35
  end
42
-
43
36
  end
data/lib/praxis/route.rb CHANGED
@@ -1,9 +1,10 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  class Route
4
5
  attr_accessor :verb, :path, :version, :prefixed_path, :options
5
6
 
6
- def initialize(verb, path, version='n/a', prefixed_path:nil, **options)
7
+ def initialize(verb, path, version = 'n/a', prefixed_path: nil, **options)
7
8
  @verb = verb
8
9
  @path = path
9
10
  @version = version
@@ -11,8 +12,8 @@ module Praxis
11
12
  @prefixed_path = prefixed_path
12
13
  end
13
14
 
14
- def example(example_hash:{}, params:)
15
- path_param_keys = self.path.named_captures.keys.collect(&:to_sym)
15
+ def example(params:, example_hash: {})
16
+ path_param_keys = path.named_captures.keys.collect(&:to_sym)
16
17
 
17
18
  param_attributes = params ? params.attributes : {}
18
19
  query_param_keys = param_attributes.keys - path_param_keys
@@ -20,11 +21,10 @@ module Praxis
20
21
  array << p if params.attributes[p].options[:required]
21
22
  end
22
23
 
23
- path_params = example_hash.select{|k,v| path_param_keys.include? k }
24
+ path_params = example_hash.select { |k, _v| path_param_keys.include? k }
24
25
  # Let's generate the example only using required params, to avoid mixing incompatible parameters
25
- query_params = example_hash.select{|k,v| required_query_param_keys.include? k }
26
- example = { verb: self.verb, url: self.path.expand(path_params.transform_values(&:to_s)), query_params: query_params }
27
-
26
+ query_params = example_hash.select { |k, _v| required_query_param_keys.include? k }
27
+ { verb: verb, url: path.expand(path_params.transform_values(&:to_s)), query_params: query_params }
28
28
  end
29
29
 
30
30
  def describe
@@ -36,7 +36,5 @@ module Praxis
36
36
  result[:options] = options if options.any?
37
37
  result
38
38
  end
39
-
40
39
  end
41
-
42
40
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Originally from:
2
4
  # https://github.com/rkh/mustermann/blob/500a603fffe0594ab842d72addcc449eedd6d5be/mustermann/lib/mustermann/router/rack.rb
3
5
  # Copied here after its removal from Mustermann proper
@@ -25,20 +27,24 @@ module Mustermann
25
27
  # # in a config.ru
26
28
  # run router
27
29
  class Rack < Simple
28
- def initialize(env_prefix: "mustermann", params_key: "#{env_prefix}.params", pattern_key: "#{env_prefix}.pattern", **options, &block)
29
- @params_key, @pattern_key = params_key, pattern_key
30
- options[:default] = [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless options.include? :default
30
+ def initialize(env_prefix: 'mustermann', params_key: "#{env_prefix}.params", pattern_key: "#{env_prefix}.pattern", **options, &block)
31
+ @params_key = params_key
32
+ @pattern_key = pattern_key
33
+ options[:default] = [404, { 'Content-Type' => 'text/plain', 'X-Cascade' => 'pass' }, ['Not Found']] unless options.include? :default
31
34
  super(**options, &block)
32
35
  end
33
36
 
34
37
  def invoke(callback, env, params, pattern)
35
- params_was, pattern_was = env[@params_key], env[@pattern_key]
36
- env[@params_key], env[@pattern_key] = params, pattern
38
+ params_was = env[@params_key]
39
+ pattern_was = env[@pattern_key]
40
+ env[@params_key] = params
41
+ env[@pattern_key] = pattern
37
42
  response = callback.call(env)
38
- response[1].each { |k,v| throw :pass if k.downcase == 'x-cascade' and v == 'pass' }
43
+ response[1].each { |k, v| throw :pass if (k.downcase == 'x-cascade') && (v == 'pass') }
39
44
  response
40
45
  ensure
41
- env[@params_key], env[@pattern_key] = params_was, pattern_was
46
+ env[@params_key] = params_was
47
+ env[@pattern_key] = pattern_was
42
48
  end
43
49
 
44
50
  def string_for(env)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Originally from:
2
4
  # https://github.com/rkh/mustermann/blob/500a603fffe0594ab842d72addcc449eedd6d5be/mustermann/lib/mustermann/router/simple.rb
3
5
  # Copied here after its removal from Mustermann proper
@@ -60,7 +62,9 @@ module Mustermann
60
62
  @map = []
61
63
  @default = default
62
64
 
63
- block.arity == 0 ? instance_eval(&block) : yield(self) if block
65
+ return unless block
66
+
67
+ block.arity.zero? ? instance_eval(&block) : yield(self)
64
68
  end
65
69
 
66
70
  # @example
@@ -74,7 +78,7 @@ module Mustermann
74
78
  # @return [#call, nil] callback for given string, if a pattern matches
75
79
  def [](string)
76
80
  string = string_for(string) unless string.is_a? String
77
- @map.detect { |p,v| p === string }[1]
81
+ @map.detect { |p, _v| p === string }[1] # rubocop:disable Style/CaseEquality
78
82
  end
79
83
 
80
84
  # @example
@@ -125,14 +129,15 @@ module Mustermann
125
129
  def call(input)
126
130
  @map.each do |pattern, callback|
127
131
  catch(:pass) do
128
- next unless params = pattern.params(string_for(input))
132
+ next unless (params = pattern.params(string_for(input)))
133
+
129
134
  return invoke(callback, input, params, pattern)
130
135
  end
131
136
  end
132
137
  @default
133
138
  end
134
139
 
135
- def invoke(callback, input, params, pattern)
140
+ def invoke(callback, input, params, _pattern)
136
141
  callback.call(input, params)
137
142
  end
138
143
 
@@ -143,4 +148,4 @@ module Mustermann
143
148
  private :invoke, :string_for
144
149
  end
145
150
  end
146
- end
151
+ end
data/lib/praxis/router.rb CHANGED
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'praxis/router/rack'
2
4
 
3
5
  module Praxis
4
-
5
6
  class Router
6
- attr_reader :request_class
7
- attr_reader :application
8
-
7
+ attr_reader :request_class, :application
9
8
 
10
9
  class VersionMatcher
11
10
  def initialize(target, version: 'n/a')
12
11
  @target = target
13
12
  @version = version
14
13
  end
14
+
15
15
  def call(request)
16
16
  if request.version == @version
17
17
  @target.call(request)
@@ -24,13 +24,13 @@ module Praxis
24
24
  end
25
25
 
26
26
  class RequestRouter < Mustermann::Router::Simple
27
- def initialize(default: nil, **options, &block)
27
+ def initialize(**options, &block)
28
28
  options[:default] = :not_found
29
29
 
30
30
  super(**options, &block)
31
31
  end
32
32
 
33
- def invoke(callback, request, params, pattern)
33
+ def invoke(callback, request, params, _pattern)
34
34
  request.route_params = params
35
35
  callback.call(request)
36
36
  end
@@ -40,7 +40,6 @@ module Praxis
40
40
  end
41
41
  end
42
42
 
43
-
44
43
  def initialize(application, request_class: Praxis::Request)
45
44
  @routes = Hash.new do |hash, verb|
46
45
  hash[verb] = RequestRouter.new
@@ -55,34 +54,32 @@ module Praxis
55
54
  # DEPRECATED: remove with EndpointDefinition.version using: :path
56
55
  path_versioning ||= (target.action.endpoint_definition.version_options[:using] == :path)
57
56
 
58
- unless path_versioning
59
- target = VersionMatcher.new(target, version: route.version)
60
- end
57
+ target = VersionMatcher.new(target, version: route.version) unless path_versioning
61
58
 
62
59
  @routes[route.verb].on(route.path, call: target)
63
60
  end
64
61
 
65
62
  def call(env_or_request)
66
63
  request = case env_or_request
67
- when Hash
68
- request_class.new(env_or_request)
69
- when request_class
70
- env_or_request
71
- else
72
- raise ArgumentError, "received #{env_or_request.class}"
73
- end
64
+ when Hash
65
+ request_class.new(env_or_request)
66
+ when request_class
67
+ env_or_request
68
+ else
69
+ raise ArgumentError, "received #{env_or_request.class}"
70
+ end
74
71
 
75
72
  verb = request.verb
76
- r = ( @routes.key?(verb) ? @routes[verb] : nil ) # Exact verb match
73
+ r = (@routes.key?(verb) ? @routes[verb] : nil) # Exact verb match
77
74
  result = r.call(request) if r
78
75
  # If we didn't have an exact verb route, or if we did but the rest or route conditions didn't match
79
- if( r == nil || result == :not_found )
76
+ if r.nil? || result == :not_found
80
77
  # Fallback to a wildcard router, if there is one registered
81
78
  result = if @routes.key?('ANY')
82
- @routes['ANY'].call(request)
83
- else
84
- :not_found
85
- end
79
+ @routes['ANY'].call(request)
80
+ else
81
+ :not_found
82
+ end
86
83
  end
87
84
 
88
85
  if result == :not_found
@@ -90,25 +87,21 @@ module Praxis
90
87
  # plus we wouldn't have tracked it as unmatched
91
88
  version = request.version
92
89
  attempted_versions = request.unmatched_versions
93
- body = "NotFound"
90
+ body = 'NotFound'
94
91
  unless attempted_versions.empty? || (attempted_versions.size == 1 && attempted_versions.first == 'n/a')
95
92
  body += if version == 'n/a'
96
- ". Your request did not specify an API version.".freeze
97
- else
98
- ". Your request specified API version = \"#{version}\"."
99
- end
93
+ '. Your request did not specify an API version.'
94
+ else
95
+ ". Your request specified API version = \"#{version}\"."
96
+ end
100
97
  pretty_versions = attempted_versions.collect(&:inspect).join(', ')
101
98
  body += " Available versions = #{pretty_versions}."
102
99
  end
103
- headers = {"Content-Type" => "text/plain"}
104
- if Praxis::Application.instance.config.praxis.x_cascade
105
- headers['X-Cascade'] = 'pass'
106
- end
100
+ headers = { 'Content-Type' => 'text/plain' }
101
+ headers['X-Cascade'] = 'pass' if Praxis::Application.instance.config.praxis.x_cascade
107
102
  result = [404, headers, [body]]
108
103
  end
109
104
  result
110
105
  end
111
-
112
106
  end
113
-
114
107
  end