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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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