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,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
 
3
5
  require 'praxis/extensions/field_selection'
4
6
 
5
- module Praxis
7
+ module Praxis
6
8
  module Plugins
7
9
  module MapperPlugin
8
10
  include Praxis::PluginConcern
@@ -15,7 +17,7 @@ module Praxis
15
17
  #
16
18
  # The plugin accepts only 1 configuration option thus far, which you can set inside the same block as:
17
19
  # application.config.mapper.debug_queries = true
18
- # when debug_queries is set to true, the system will output information about the expanded fields
20
+ # when debug_queries is set to true, the system will output information about the expanded fields
19
21
  # and associations that the system ihas calculated necessary to pull from the DB, based on the requested
20
22
  # API fields, API filters and `property` dependencies defined in the domain models (i.e., resources)
21
23
  class Plugin < Praxis::Plugin
@@ -32,7 +34,7 @@ module Praxis
32
34
  def prepare_config!(node)
33
35
  node.attributes do
34
36
  attribute :debug_queries, Attributor::Boolean, default: false,
35
- description: 'Weather or not to log debug information about queries executed in the build_query automation module'
37
+ description: 'Weather or not to log debug information about queries executed in the build_query automation module'
36
38
  end
37
39
  end
38
40
  end
@@ -45,27 +47,27 @@ module Praxis
45
47
  include Praxis::Extensions::FieldExpansion
46
48
  end
47
49
 
48
- def build_query(base_query) # rubocop:disable Metrics/AbcSize
49
- domain_model = self.media_type&.domain_model
50
- raise "No domain model defined for #{self.name}. Cannot use the attribute filtering helpers without it" unless domain_model
51
-
52
- filters = request.params.filters if request.params&.respond_to?(:filters)
50
+ def build_query(base_query)
51
+ domain_model = media_type&.domain_model
52
+ raise "No domain model defined for #{name}. Cannot use the attribute filtering helpers without it" unless domain_model
53
+
54
+ filters = request.params.filters if request.params.respond_to?(:filters)
53
55
  # Handle filters
54
- base_query = domain_model.craft_filter_query( base_query , filters: filters )
56
+ base_query = domain_model.craft_filter_query(base_query, filters: filters)
55
57
  # Handle field and nested field selection
56
58
  base_query = domain_model.craft_field_selection_query(base_query, selectors: selector_generator.selectors)
57
59
  # handle pagination and ordering if the pagination extention is included
58
- base_query = domain_model.craft_pagination_query(base_query, pagination: _pagination) if self.respond_to?(:_pagination)
60
+ base_query = domain_model.craft_pagination_query(base_query, pagination: _pagination) if respond_to?(:_pagination)
59
61
 
60
62
  base_query
61
63
  end
62
64
 
63
65
  def selector_generator
64
- return unless self.media_type.respond_to?(:domain_model) &&
65
- self.media_type.domain_model < Praxis::Mapper::Resource
66
+ return unless media_type.respond_to?(:domain_model) &&
67
+ media_type.domain_model < Praxis::Mapper::Resource
66
68
 
67
69
  @selector_generator ||= \
68
- Praxis::Mapper::SelectorGenerator.new.add(self.media_type.domain_model, self.expanded_fields)
70
+ Praxis::Mapper::SelectorGenerator.new.add(media_type.domain_model, expanded_fields)
69
71
  end
70
72
  end
71
73
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
  require 'praxis/extensions/pagination'
3
5
 
@@ -6,7 +8,7 @@ require 'praxis/extensions/pagination'
6
8
  # When combined with the MapperPlugin, there is no extra configuration that needs to be done for
7
9
  # the system to appropriately identify the pagination and order parameters in the API, and translate
8
10
  # that in to the appropriate queries to fetch.
9
- #
11
+ #
10
12
  # To use this plugin without the MapperPlugin (probably a rare case), one can apply the appropriate
11
13
  # clauses onto a query, by directly calling (in the controller) the `craft_pagination_query` method
12
14
  # of the domain_model associated to the controller's mediatype.
@@ -17,7 +19,7 @@ require 'praxis/extensions/pagination'
17
19
  # objs = domain_model.craft_pagination_query(base_query, pagination: _pagination)
18
20
  # display(objs)
19
21
  # end
20
- #
22
+ #
21
23
  # This plugin accepts configuration about the default behavior of pagination.
22
24
  # Any of these configs can individually be overidden when defining each Pagination/Order parameters
23
25
  # in any of the Endpoint actions.
@@ -33,12 +35,12 @@ require 'praxis/extensions/pagination'
33
35
  # disallow_paging_by_default: true, # Default false
34
36
  # # Disallows the use of the cursor type pagination mode when true (i.e., using 'by=' or 'from=' parameter)
35
37
  # disallow_cursor_by_default: true, # Default false
36
- # # The default mode params to use
38
+ # # The default mode params to use
37
39
  # paging_default_mode: {by: :uuid}, # Default {by: :uid}
38
40
  # # Weather or not to enforce that all requested sort fields are part of the media_type attributes
39
41
  # # when false (not enforced) only the first field would be checked
40
42
  # sorting: {
41
- # enforce_all_fields: false # Default true
43
+ # enforce_all_fields: false # Default true
42
44
  # }
43
45
  # end
44
46
  # end
@@ -77,7 +79,7 @@ module Praxis
77
79
  end
78
80
 
79
81
  def setup!
80
- self.config.each do |name, val|
82
+ config.each do |name, val|
81
83
  if name == :sorting
82
84
  val.each do |ordername, orderval|
83
85
  Praxis::Types::OrderingParams.send(ordername, orderval)
@@ -85,7 +87,7 @@ module Praxis
85
87
  else
86
88
  Praxis::Types::PaginationParams.send(name, val)
87
89
  end
88
- end
90
+ end
89
91
  end
90
92
  end
91
93
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'praxis/plugin'
2
4
  require 'praxis/plugin_concern'
3
5
 
@@ -7,40 +9,42 @@ module Praxis
7
9
  include Praxis::PluginConcern
8
10
 
9
11
  class Plugin < Praxis::Plugin
10
-
11
12
  def setup!
12
13
  require 'praxis/dispatcher'
13
14
  enable_action_controller_instrumentation
14
15
  end
15
16
 
16
17
  private
18
+
17
19
  def enable_action_controller_instrumentation
18
20
  Praxis::Dispatcher.class_eval do
19
21
  # Wrap the original action dispatch with a method that instruments rails-expected bits...
20
22
  alias_method :orig_instrumented_dispatch, :instrumented_dispatch
21
23
 
22
- def instrumented_dispatch( praxis_payload )
24
+ def instrumented_dispatch(praxis_payload)
23
25
  rails_payload = {
24
- :controller => controller.class.name,
25
- :action => action.name,
26
- :params => ( (request.params) ? request.params.dump : {} ),
27
- :method => request.verb,
28
- :path => (request.fullpath rescue "unknown")
26
+ controller: controller.class.name,
27
+ action: action.name,
28
+ params: (request.params ? request.params.dump : {}),
29
+ method: request.verb,
30
+ path: begin
31
+ request.fullpath
32
+ rescue StandardError
33
+ 'unknown'
34
+ end
29
35
  }
30
- Praxis::Notifications.instrument("start_processing.action_controller", rails_payload.dup)
36
+ Praxis::Notifications.instrument('start_processing.action_controller', rails_payload.dup)
31
37
 
32
38
  Praxis::Notifications.instrument 'process_action.action_controller' do |data|
33
- begin
34
- res = orig_instrumented_dispatch(praxis_payload)
35
- # TODO: also add the db_runtime and view_runtime values...
36
- data[:status] = res[0]
37
- res
38
- ensure
39
- # Append DB runtime to payload
40
- #data[:db_runtime] = 999
41
- # Append rendering time to payload
42
- #data[:view_runtime] = 123
43
- end
39
+ res = orig_instrumented_dispatch(praxis_payload)
40
+ # TODO: also add the db_runtime and view_runtime values...
41
+ data[:status] = res[0]
42
+ res
43
+
44
+ # Append DB runtime to payload
45
+ # data[:db_runtime] = 999
46
+ # Append rendering time to payload
47
+ # data[:view_runtime] = 123
44
48
  end
45
49
  end
46
50
  end
@@ -59,30 +63,33 @@ module Praxis
59
63
  # Avoid using them explicitly in your controllers though. Use request.params object instead, as they are
60
64
  # the Praxis ones that have been validated and coerced into the types you've defined.
61
65
  def params
62
- self.request.parameters
66
+ request.parameters
63
67
  end
64
68
 
65
69
  # Allow accessing the response headers from the controller
66
70
  def headers
67
- self.response.headers
71
+ response.headers
68
72
  end
69
73
 
70
74
  def session
71
- self.request.session
75
+ request.session
72
76
  end
73
77
 
74
78
  # Allow setting the status and body of the response from the controller itself.
75
79
  def status=(code)
76
- self.response.status = code
80
+ response.status = code
77
81
  end
78
82
 
79
83
  def response_body=(body)
80
- #TODO: @_rendered = true # Necessary to know if to stop filter chain or not...
81
- self.response.body = body
84
+ # TODO: @_rendered = true # Necessary to know if to stop filter chain or not...
85
+ response.body = body
82
86
  end
83
87
 
84
88
  def head(status, options = {})
85
- options, status = status, nil if status.is_a?(Hash)
89
+ if status.is_a?(Hash)
90
+ options = status
91
+ status = nil
92
+ end
86
93
  status ||= options.delete(:status) || :ok
87
94
  location = options.delete(:location)
88
95
  content_type = options.delete(:content_type)
@@ -96,9 +103,7 @@ module Praxis
96
103
  response.content_type = content_type if content_type
97
104
  response
98
105
  end
99
-
100
106
  end
101
-
102
107
  end
103
108
  end
104
109
  end
@@ -2,12 +2,10 @@
2
2
 
3
3
  module Praxis
4
4
  class Renderer
5
- attr_reader :include_nil
6
- attr_reader :cache
5
+ attr_reader :include_nil, :cache
7
6
 
8
7
  class CircularRenderingError < StandardError
9
- attr_reader :object
10
- attr_reader :context
8
+ attr_reader :object, :context
11
9
 
12
10
  def initialize(object, context)
13
11
  @object = object
@@ -35,18 +33,16 @@ module Praxis
35
33
  def render(object, fields, context: Attributor::DEFAULT_ROOT_CONTEXT)
36
34
  if object.is_a? Praxis::Blueprint
37
35
  @cache[object._cache_key][fields] ||= _render(object, fields, context: context)
38
- else
39
- if object.class < Attributor::Collection
40
- object.each_with_index.collect do |sub_object, i|
41
- sub_context = context + ["at(#{i})"]
42
- render(sub_object, fields, context: sub_context)
43
- end
44
- else
45
- _render(object, fields, context: context)
36
+ elsif object.class < Attributor::Collection
37
+ object.each_with_index.collect do |sub_object, i|
38
+ sub_context = context + ["at(#{i})"]
39
+ render(sub_object, fields, context: sub_context)
46
40
  end
41
+ else
42
+ _render(object, fields, context: context)
47
43
  end
48
44
  rescue SystemStackError
49
- raise CircularRenderingError.new(object, context)
45
+ raise CircularRenderingError.new(object, context)
50
46
  end
51
47
 
52
48
  def _render(object, fields, context: Attributor::DEFAULT_ROOT_CONTEXT)
@@ -62,12 +58,12 @@ module Praxis
62
58
  fields.each_with_object({}) do |(key, subfields), hash|
63
59
  begin
64
60
  value = object._get_attr(key)
65
- rescue => e
61
+ rescue StandardError => e
66
62
  raise Attributor::DumpError.new(context: context, name: key, type: object.class, original_exception: e)
67
63
  end
68
64
 
69
65
  if value.nil?
70
- hash[key] = nil if self.include_nil
66
+ hash[key] = nil if include_nil
71
67
  next
72
68
  end
73
69
 
@@ -1,18 +1,19 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  class Request < Praxis.request_superclass
4
5
  attr_reader :env, :query
5
- attr_accessor :route_params, :action
6
-
7
- PATH_VERSION_PREFIX = "/v".freeze
8
- CONTENT_TYPE_NAME = 'CONTENT_TYPE'.freeze
9
- PATH_INFO_NAME = 'PATH_INFO'.freeze
10
- REQUEST_METHOD_NAME = 'REQUEST_METHOD'.freeze
11
- QUERY_STRING_NAME = 'QUERY_STRING'.freeze
12
- API_VERSION_HEADER_NAME = "HTTP_X_API_VERSION".freeze
13
- API_VERSION_PARAM_NAME = 'api_version'.freeze
14
- API_NO_VERSION_NAME = 'n/a'.freeze
15
- VERSION_USING_DEFAULTS = [:header, :params].freeze
6
+ attr_accessor :route_params, :action, :headers, :params, :payload
7
+
8
+ PATH_VERSION_PREFIX = '/v'
9
+ CONTENT_TYPE_NAME = 'CONTENT_TYPE'
10
+ PATH_INFO_NAME = 'PATH_INFO'
11
+ REQUEST_METHOD_NAME = 'REQUEST_METHOD'
12
+ QUERY_STRING_NAME = 'QUERY_STRING'
13
+ API_VERSION_HEADER_NAME = 'HTTP_X_API_VERSION'
14
+ API_VERSION_PARAM_NAME = 'api_version'
15
+ API_NO_VERSION_NAME = 'n/a'
16
+ VERSION_USING_DEFAULTS = %i[header params].freeze
16
17
 
17
18
  def initialize(env)
18
19
  @env = env
@@ -45,10 +46,9 @@ module Praxis
45
46
  @env[PATH_INFO_NAME]
46
47
  end
47
48
 
48
- attr_accessor :headers, :params, :payload
49
-
50
49
  def params_hash
51
50
  return {} if params.nil?
51
+
52
52
  params.attributes
53
53
  end
54
54
 
@@ -65,17 +65,15 @@ module Praxis
65
65
  end
66
66
 
67
67
  def raw_payload
68
- @raw_payload ||= begin
69
- if (input = env['rack.input'.freeze].read)
70
- env['rack.input'.freeze].rewind
71
- input
72
- end
73
- end
68
+ @raw_payload ||= if (input = env['rack.input'].read)
69
+ env['rack.input'].rewind
70
+ input
71
+ end
74
72
  end
75
73
 
76
74
  def coalesce_inputs!
77
- self.raw_params
78
- self.raw_payload
75
+ raw_params
76
+ raw_payload
79
77
  end
80
78
 
81
79
  def self.path_version_prefix
@@ -83,14 +81,14 @@ module Praxis
83
81
  end
84
82
 
85
83
  # DEPRECATED: remove with EndpointDefinition.version using: :path
86
- PATH_VERSION_MATCHER = %r{^#{self.path_version_prefix}(?<version>[^\/]+)\/}.freeze
84
+ PATH_VERSION_MATCHER = %r{^#{path_version_prefix}(?<version>[^/]+)/}.freeze
87
85
 
88
86
  def path_version_matcher
89
87
  if Application.instance.versioning_scheme == :path
90
- matcher = Mustermann.new(ApiDefinition.instance.info.base_path + '*')
91
- matcher.params(self.path)[API_VERSION_PARAM_NAME]
88
+ matcher = Mustermann.new("#{ApiDefinition.instance.info.base_path}*")
89
+ matcher.params(path)[API_VERSION_PARAM_NAME]
92
90
  else
93
- PATH_VERSION_MATCHER.match(self.path)[:version]
91
+ PATH_VERSION_MATCHER.match(path)[:version]
94
92
  end
95
93
  end
96
94
 
@@ -99,30 +97,32 @@ module Praxis
99
97
 
100
98
  Array(Application.instance.versioning_scheme).find do |mode|
101
99
  case mode
102
- when :header;
100
+ when :header
103
101
  result = env[API_VERSION_HEADER_NAME]
104
- when :params;
102
+ when :params
105
103
  result = @query[API_VERSION_PARAM_NAME]
106
- when :path;
107
- result = self.path_version_matcher
104
+ when :path
105
+ result = path_version_matcher
108
106
  else
109
107
  raise "Unknown method for retrieving the API version: #{mode}"
110
108
  end
111
109
  end
112
- return result || API_NO_VERSION_NAME
110
+ result || API_NO_VERSION_NAME
113
111
  end
114
112
 
115
113
  def load_headers(context)
116
114
  return unless action.headers
117
- defined_headers = action.precomputed_header_keys_for_rack.each_with_object(Hash.new) do |(upper, original), hash|
118
- hash[original] = self.env[upper] if self.env.has_key? upper
115
+
116
+ defined_headers = action.precomputed_header_keys_for_rack.each_with_object({}) do |(upper, original), hash|
117
+ hash[original] = env[upper] if env.key? upper
119
118
  end
120
119
  self.headers = action.headers.load(defined_headers, context)
121
120
  end
122
121
 
123
122
  def load_params(context)
124
123
  return unless action.params
125
- self.params = action.params.load(self.raw_params, context)
124
+
125
+ self.params = action.params.load(raw_params, context)
126
126
  end
127
127
 
128
128
  def load_payload(context)
@@ -130,11 +130,11 @@ module Praxis
130
130
  return if content_type.nil?
131
131
 
132
132
  raw = if (handler = Praxis::Application.instance.handlers[content_type.handler_name])
133
- handler.parse(self.raw_payload)
134
- else
135
- # TODO is this a good default?
136
- self.raw_payload
137
- end
133
+ handler.parse(raw_payload)
134
+ else
135
+ # TODO: is this a good default?
136
+ raw_payload
137
+ end
138
138
 
139
139
  self.payload = action.payload.load(raw, context, content_type: content_type.to_s)
140
140
  end
@@ -142,19 +142,25 @@ module Praxis
142
142
  def validate_headers(context)
143
143
  return [] unless action.headers
144
144
 
145
- action.headers.validate(self.headers, context)
145
+ return ["Attribute #{Attributor.humanize_context(context)} is required."] if action.headers.options[:required] == true && headers.nil?
146
+
147
+ action.headers.validate(headers, context)
146
148
  end
147
149
 
148
150
  def validate_params(context)
149
151
  return [] unless action.params
150
152
 
151
- action.params.validate(self.params, context)
153
+ return ["Attribute #{Attributor.humanize_context(context)} is required."] if action.params.options[:required] == true && params.nil?
154
+
155
+ action.params.validate(params, context)
152
156
  end
153
157
 
154
158
  def validate_payload(context)
155
159
  return [] unless action.payload
156
160
 
157
- action.payload.validate(self.payload, context)
161
+ return ["Attribute #{Attributor.humanize_context(context)} is required."] if action.payload.options[:required] == true && payload.nil?
162
+
163
+ action.payload.validate(payload, context)
158
164
  end
159
165
 
160
166
  # versions that matched all the conditions of the request (except its version)
@@ -167,7 +173,5 @@ module Praxis
167
173
  def inspect
168
174
  "'@env' => #{@env.inspect},\n'@headers' => #{@headers.inspect},\n'@params' => #{@params.inspect},\n'@query' => #{@query.inspect}"
169
175
  end
170
-
171
176
  end
172
-
173
177
  end
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module RequestStages
3
-
4
5
  class Action < RequestStage
5
-
6
6
  def execute
7
- response = Notifications.instrument 'praxis.request_stage.execute'.freeze, controller: controller do
8
- if controller.method(action.name).arity == 0
7
+ response = Notifications.instrument 'praxis.request_stage.execute', controller: controller do
8
+ if controller.method(action.name).arity.zero?
9
9
  controller.__send__(action.name)
10
10
  else
11
11
  controller.__send__(action.name, **request.params_hash)
@@ -23,8 +23,6 @@ module Praxis
23
23
  controller.response.request = request
24
24
  nil # Action cannot return its OK request, as it would indicate the end of the stage chain
25
25
  end
26
-
27
26
  end
28
-
29
27
  end
30
28
  end
@@ -1,13 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module RequestStages
3
-
4
5
  class LoadRequest < RequestStage
5
-
6
6
  def execute
7
7
  request.coalesce_inputs!
8
8
  end
9
-
10
9
  end
11
-
12
10
  end
13
11
  end
@@ -1,25 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module RequestStages
3
-
4
5
  # Special Stage what will hijack the run and execute methods to:
5
6
  # 1- Run specific controller callbacks (in addition to any normal callbacks)
6
7
  # 2- Shortcut the controller callback chain if any returns a Response object
7
8
  class RequestStage < Stage
8
-
9
- alias :dispatcher :application # it's technically application in the base Stage
9
+ alias dispatcher application # it's technically application in the base Stage
10
10
 
11
11
  def path
12
- @the_path ||= [name].freeze
12
+ @path ||= [name].freeze
13
13
  end
14
14
 
15
15
  def execute_controller_callbacks(callbacks)
16
16
  if callbacks.key?(path)
17
17
  callbacks[path].each do |(conditions, block)|
18
- if conditions.key?(:actions)
19
- next unless conditions[:actions].include? action.name
20
- end
18
+ next if conditions.key?(:actions) && !(conditions[:actions].include? action.name)
19
+
21
20
  result = block.call(controller)
22
- if result && result.kind_of?(Praxis::Response)
21
+ if result.is_a?(Praxis::Response)
23
22
  controller.response = result
24
23
  return result
25
24
  end
@@ -51,7 +50,7 @@ module Praxis
51
50
 
52
51
  def run
53
52
  # stage-level callbacks (typically empty) will never shortcut
54
- execute_callbacks(self.before_callbacks)
53
+ execute_callbacks(before_callbacks)
55
54
 
56
55
  r = execute_controller_callbacks(controller.class.before_callbacks)
57
56
  # Shortcut lifecycle if filters return non-nil value
@@ -61,7 +60,7 @@ module Praxis
61
60
  result = execute_with_around
62
61
  # Shortcut lifecycle if filters return a response
63
62
  # (non-nil but non-response-class response is ignored)
64
- if result && result.kind_of?(Praxis::Response)
63
+ if result.is_a?(Praxis::Response)
65
64
  controller.response = result
66
65
  return result
67
66
  end
@@ -72,52 +71,49 @@ module Praxis
72
71
  return r if r
73
72
 
74
73
  # stage-level callbacks (typically empty) will never shortcut
75
- execute_callbacks(self.after_callbacks)
74
+ execute_callbacks(after_callbacks)
76
75
 
77
76
  result
78
77
  end
79
78
 
80
79
  def execute_with_around
81
- cb = controller.class.around_callbacks[ path ]
82
- if cb == nil || cb.empty?
80
+ cb = controller.class.around_callbacks[path]
81
+ if cb.nil? || cb.empty?
83
82
  execute
84
83
  else
85
84
  inner_proc = proc { execute }
86
85
 
87
- applicable = cb.select do |(conditions, handler)|
88
- if conditions.has_key?(:actions)
89
- (conditions[:actions].include? action.name) ? true : false
86
+ applicable = cb.select do |(conditions, _handler)|
87
+ if conditions.key?(:actions)
88
+ conditions[:actions].include?(action.name) ? true : false
90
89
  else
91
90
  true
92
91
  end
93
92
  end
94
93
 
95
- chain = applicable.reverse.inject(inner_proc) do |blk, (conditions, handler)|
94
+ chain = applicable.reverse.inject(inner_proc) do |blk, (_conditions, handler)|
96
95
  if blk
97
- proc{ handler.call(controller,blk) }
96
+ proc { handler.call(controller, blk) }
98
97
  else
99
- proc{ handler.call }
98
+ proc { handler.call }
100
99
  end
101
100
  end
102
101
  chain.call
103
102
  end
104
103
  end
105
104
 
106
-
107
105
  def execute
108
106
  raise NotImplementedError, 'Subclass must implement Stage#execute' unless @stages.any?
109
107
 
110
108
  @stages.each do |stage|
111
109
  shortcut = stage.run
112
- if shortcut && shortcut.kind_of?(Praxis::Response)
110
+ if shortcut.is_a?(Praxis::Response)
113
111
  controller.response = shortcut
114
112
  return shortcut
115
113
  end
116
114
  end
117
115
  nil
118
116
  end
119
-
120
117
  end
121
-
122
118
  end
123
119
  end
@@ -1,8 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module RequestStages
3
-
4
5
  class Response < RequestStage
5
-
6
6
  def execute
7
7
  response = controller.response
8
8
 
@@ -15,16 +15,14 @@ module Praxis
15
15
  end
16
16
  rescue Exceptions::Validation => e
17
17
  controller.response = validation_handler.handle!(
18
- summary: "Error validating response",
18
+ summary: 'Error validating response',
19
19
  exception: e,
20
20
  request: request,
21
- stage: name,
21
+ stage: name,
22
22
  errors: e.errors
23
23
  )
24
24
  retry
25
25
  end
26
-
27
26
  end
28
-
29
27
  end
30
28
  end