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,17 +1,17 @@
1
- module Praxis
2
-
1
+ # frozen_string_literal: true
3
2
 
3
+ module Praxis
4
4
  module BootloaderStages
5
-
6
5
  class Routing < Stage
7
6
  class Target
8
7
  attr_reader :action
8
+
9
9
  def initialize(application, controller, action)
10
10
  @application = application
11
11
  @controller = controller
12
12
  @action = action
13
13
  end
14
-
14
+
15
15
  def call(request)
16
16
  request.action = @action
17
17
  dispatcher = Dispatcher.current(application: @application)
@@ -19,7 +19,7 @@ module Praxis
19
19
  dispatcher.dispatch(@controller, @action, request)
20
20
  end
21
21
  end
22
-
22
+
23
23
  def execute
24
24
  application.controllers.each do |controller|
25
25
  controller.definition.actions.each do |action_name, action|
@@ -29,14 +29,11 @@ module Praxis
29
29
  end
30
30
  end
31
31
 
32
-
33
32
  def target_factory(controller, action_name)
34
33
  action = controller.definition.actions.fetch(action_name)
35
34
 
36
35
  Target.new(application, controller, action)
37
36
  end
38
-
39
37
  end
40
-
41
38
  end
42
39
  end
@@ -1,17 +1,10 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  module BootloaderStages
4
-
5
5
  class SubgroupLoader < Stage
6
-
7
6
  attr_writer :order
8
7
 
9
- def initialize(name, application)
10
- super
11
- # always finalize Taylor after loading app code.
12
-
13
- end
14
-
15
8
  def order
16
9
  @order ||= application.file_layout[name] == [] ? [] : application.file_layout[name].groups.keys
17
10
  end
@@ -23,6 +16,5 @@ module Praxis
23
16
  setup_deferred_callbacks!
24
17
  end
25
18
  end
26
-
27
19
  end
28
20
  end
@@ -1,28 +1,26 @@
1
- module Praxis
2
-
1
+ # frozen_string_literal: true
3
2
 
3
+ module Praxis
4
4
  module BootloaderStages
5
-
6
5
  class WarnUnloadedFiles < Stage
7
6
  @enabled = true
8
7
 
9
- def self.enabled=(enabled)
10
- @enabled = enabled
8
+ class << self
9
+ attr_writer :enabled
11
10
  end
12
11
 
13
- def self.enabled
14
- @enabled
12
+ class << self
13
+ attr_reader :enabled
15
14
  end
16
15
 
17
16
  def execute
18
17
  return unless self.class.enabled
19
18
 
20
- if application.file_layout[:app] == []
21
- return
22
- end
19
+ return if application.file_layout[:app] == []
23
20
 
24
21
  base = application.file_layout[:app].base
25
22
  return unless base.exist?
23
+
26
24
  file_enum = base.find.to_a
27
25
  files = file_enum.select do |file|
28
26
  path = file.relative_path_from(base)
@@ -30,17 +28,15 @@ module Praxis
30
28
  end
31
29
 
32
30
  missing = Set.new(files) - application.loaded_files
33
- if missing.any?
34
- msg = "The following application files under #{base} were not loaded:\n"
35
- missing.each do |file|
36
- path = file.relative_path_from(base)
37
- msg << " * #{path}\n"
38
- end
39
- warn msg
31
+ return unless missing.any?
32
+
33
+ msg = "The following application files under #{base} were not loaded:\n"
34
+ missing.each do |file|
35
+ path = file.relative_path_from(base)
36
+ msg << " * #{path}\n"
40
37
  end
38
+ warn msg
41
39
  end
42
-
43
-
44
40
  end
45
41
  end
46
42
  end
@@ -1,33 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Callbacks
3
5
  extend ::ActiveSupport::Concern
4
6
 
5
7
  included do
6
8
  class_attribute :before_callbacks, :after_callbacks, :around_callbacks
7
- self.before_callbacks = Hash.new
8
- self.after_callbacks = Hash.new
9
- self.around_callbacks = Hash.new
9
+ self.before_callbacks = ({})
10
+ self.after_callbacks = ({})
11
+ self.around_callbacks = ({})
10
12
  end
11
-
13
+
12
14
  module ClassMethods
13
-
14
15
  def before(*stage_path, **conditions, &block)
15
16
  stage_path = [:action] if stage_path.empty?
16
- before_callbacks[stage_path] ||= Array.new
17
+ before_callbacks[stage_path] ||= []
17
18
  before_callbacks[stage_path] << [conditions, block]
18
19
  end
19
-
20
+
20
21
  def after(*stage_path, **conditions, &block)
21
22
  stage_path = [:action] if stage_path.empty?
22
- after_callbacks[stage_path] ||= Array.new
23
+ after_callbacks[stage_path] ||= []
23
24
  after_callbacks[stage_path] << [conditions, block]
24
25
  end
25
-
26
+
26
27
  def around(*stage_path, **conditions, &block)
27
28
  stage_path = [:action] if stage_path.empty?
28
- around_callbacks[stage_path] ||= Array.new
29
+ around_callbacks[stage_path] ||= []
29
30
  around_callbacks[stage_path] << [conditions, block]
30
31
  end
31
32
  end
32
33
  end
33
- end
34
+ end
@@ -1,28 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  class Collection < Attributor::Collection
3
5
  include Types::MediaTypeCommon
4
6
 
5
7
  def self.of(type)
6
- if defined?(type::Collection)
7
- return type::Collection
8
- end
8
+ return type::Collection if defined?(type::Collection)
9
9
 
10
10
  klass = super
11
11
  klass.anonymous_type
12
12
 
13
- if type < Praxis::Types::MediaTypeCommon
14
- klass.member_type type
15
- type.const_set :Collection, klass
16
- else
17
- raise "Praxis::Collection.of() for non-MediaTypes is unsupported. Use Attributor::Collection.of() instead."
18
- end
13
+ raise 'Praxis::Collection.of() for non-MediaTypes is unsupported. Use Attributor::Collection.of() instead.' unless type < Praxis::Types::MediaTypeCommon
19
14
 
15
+ klass.member_type type
16
+ type.const_set :Collection, klass
20
17
  end
21
18
 
22
- def self.member_type(type=nil)
19
+ def self.member_type(type = nil)
23
20
  unless type.nil?
24
21
  @member_type = type
25
- self.identifier(type.identifier + ';type=collection') unless type.identifier.nil?
22
+ identifier("#{type.identifier};type=collection") unless type.identifier.nil?
26
23
  end
27
24
 
28
25
  @member_type
@@ -36,11 +33,11 @@ module Praxis
36
33
  :array
37
34
  end
38
35
 
39
- def self.as_json_schema(**args)
40
- the_type = @attribute && @attribute.type || member_type
36
+ def self.as_json_schema(**_args)
37
+ the_type = @attribute&.type || member_type
41
38
  {
42
39
  type: json_schema_type,
43
- items: { '$ref': "#/components/schemas/#{the_type.id}" }
40
+ items: the_type.as_json_schema
44
41
  }
45
42
  end
46
43
  end
data/lib/praxis/config.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  class Config
3
5
  include Attributor::Type
@@ -10,7 +12,7 @@ module Praxis
10
12
  end
11
13
 
12
14
  # You can define the configuration in different ways
13
- #
15
+ #
14
16
  # Add a key to the top struct
15
17
  # define do
16
18
  # attribute :added_to_top, String
@@ -28,46 +30,36 @@ module Praxis
28
30
  # attribute :two String
29
31
  # end
30
32
  # end
31
-
33
+
32
34
  # ...or using this way too (equivalent)
33
35
  # define(:app) do
34
36
  # attribute :two, String
35
37
  # end
36
38
  # You can also define a key to be a non-Struct type
37
39
  # define :app, Attributor::Hash
38
-
39
- def define(key=nil, type=Attributor::Struct, **opts, &block)
40
- if key.nil? && type != Attributor::Struct
41
- raise Exceptions::InvalidConfiguration.new(
42
- "You cannot define the top level configuration with a non-Struct type. Got: #{type.inspect}"
43
- )
44
- end
45
-
40
+
41
+ def define(key = nil, type = Attributor::Struct, **opts, &block)
42
+ raise Exceptions::InvalidConfiguration, "You cannot define the top level configuration with a non-Struct type. Got: #{type.inspect}" if key.nil? && type != Attributor::Struct
43
+
46
44
  case key
47
45
  when String, Symbol, NilClass
48
46
 
49
47
  top = key.nil? ? @attribute : @attribute.attributes[key]
50
- if top #key defined...redefine
51
- unless type == Attributor::Struct && top.type < Attributor::Struct
52
- raise Exceptions::InvalidConfiguration.new(
53
- "Incompatible type received for extending configuration key [#{key}]. Got type #{type.name}"
54
- )
55
- end
48
+ if top # key defined...redefine
49
+ raise Exceptions::InvalidConfiguration, "Incompatible type received for extending configuration key [#{key}]. Got type #{type.name}" unless type == Attributor::Struct && top.type < Attributor::Struct
50
+
56
51
  top.options.merge!(opts)
57
52
  top.type.attributes(**opts, &block)
58
53
  else
59
54
  @attribute.attributes[key] = Attributor::Attribute.new(type, opts, &block)
60
55
  end
61
56
  else
62
- raise Exceptions::InvalidConfiguration.new(
63
- "Defining a configuration key requires a String or a Symbol key. Got: #{key.inspect}"
64
- )
57
+ raise Exceptions::InvalidConfiguration, "Defining a configuration key requires a String or a Symbol key. Got: #{key.inspect}"
65
58
  end
66
-
67
59
  end
68
60
 
69
61
  def set(config)
70
- context = ['Application', 'config']
62
+ context = %w[Application config]
71
63
 
72
64
  begin
73
65
  @value = @attribute.load(config, context, recurse: true)
@@ -77,17 +69,14 @@ module Praxis
77
69
 
78
70
  errors = @attribute.validate(@value, context)
79
71
 
80
- unless errors.empty?
81
- raise Exceptions::ConfigValidation.new(errors: errors)
82
- end
72
+ raise Exceptions::ConfigValidation.new(errors: errors) unless errors.empty?
83
73
  end
84
74
 
85
75
  def get
86
- @value ||= begin
87
- context = ['Application','config'].freeze
88
- @attribute.load({},context, recurse: true)
76
+ @value ||= begin # rubocop:disable Naming/MemoizedInstanceVariableName
77
+ context = %w[Application config].freeze
78
+ @attribute.load({}, context, recurse: true)
89
79
  end
90
80
  end
91
-
92
81
  end
93
82
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # TODO: move to Praxis proper...it's not used here
3
4
  module Praxis
4
5
  class ConfigHash < BasicObject
@@ -22,7 +23,7 @@ module Praxis
22
23
  true
23
24
  end
24
25
 
25
- def method_missing(name, value, *rest, &block) # rubocop:disable Style/MethodMissing
26
+ def method_missing(name, value, *rest, &block)
26
27
  if (existing = @hash[name])
27
28
  if block
28
29
  existing << [value, block]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
  require 'active_support/all'
3
5
 
@@ -24,11 +26,11 @@ module Praxis
24
26
  end
25
27
 
26
28
  def id
27
- self.name.gsub('::'.freeze,'-'.freeze)
29
+ name.gsub('::', '-')
28
30
  end
29
31
  end
30
32
 
31
- def initialize(request, response=Responses::Ok.new)
33
+ def initialize(request, response = Responses::Ok.new)
32
34
  @request = request
33
35
  @response = response
34
36
  end
@@ -38,12 +40,11 @@ module Praxis
38
40
  end
39
41
 
40
42
  def media_type
41
- if (response_definition = self.request.action.responses[self.response.name])
42
- return response_definition.media_type
43
+ if (response_definition = request.action.responses[response.name])
44
+ response_definition.media_type
43
45
  else
44
- self.definition.media_type
46
+ definition.media_type
45
47
  end
46
48
  end
47
-
48
49
  end
49
50
  end
@@ -1,35 +1,33 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  CONTEXT_FOR = {
4
- params: [Attributor::AttributeResolver::ROOT_PREFIX, "params".freeze],
5
- headers: [Attributor::AttributeResolver::ROOT_PREFIX, "headers".freeze],
6
- payload: [Attributor::AttributeResolver::ROOT_PREFIX, "payload".freeze]
5
+ params: [Attributor::ROOT_PREFIX, 'params'],
6
+ headers: [Attributor::ROOT_PREFIX, 'headers'],
7
+ payload: [Attributor::ROOT_PREFIX, 'payload']
7
8
  }.freeze
8
9
 
9
10
  class Dispatcher
10
- attr_reader :controller
11
- attr_reader :action
12
- attr_reader :request
13
- attr_reader :application
11
+ attr_reader :controller, :action, :request, :application
14
12
 
15
- @deferred_callbacks = Hash.new do |hash,stage|
16
- hash[stage] = {before: [], after:[]}
13
+ @deferred_callbacks = Hash.new do |hash, stage|
14
+ hash[stage] = { before: [], after: [] }
17
15
  end
18
16
 
19
17
  class << self
20
18
  attr_reader :deferred_callbacks
21
19
  end
22
20
 
23
- def self.before(*stage_path, **conditions, &block)
21
+ def self.before(*_stage_path, **conditions, &block)
24
22
  @deferred_callbacks[:before] << [conditions, block]
25
23
  end
26
24
 
27
- def self.after(*stage_path, **conditions, &block)
25
+ def self.after(*_stage_path, **conditions, &block)
28
26
  @deferred_callbacks[:after] << [conditions, block]
29
27
  end
30
28
 
31
29
  def self.current(thread: Thread.current, application: Application.instance)
32
- thread[:praxis_dispatcher] ||= self.new(application: application)
30
+ thread[:praxis_dispatcher] ||= new(application: application)
33
31
  end
34
32
 
35
33
  def initialize(application: Application.instance)
@@ -43,20 +41,18 @@ module Praxis
43
41
  @stages << RequestStages::Validate.new(:validate, self)
44
42
  @stages << RequestStages::Action.new(:action, self)
45
43
  @stages << RequestStages::Response.new(:response, self)
46
- @stages.each do |s|
47
- s.setup!
48
- end
44
+ @stages.each(&:setup!)
49
45
  setup_deferred_callbacks!
50
46
  end
51
47
 
52
48
  def setup_deferred_callbacks!
53
49
  self.class.deferred_callbacks.each do |stage_name, callbacks|
54
50
  callbacks[:before].each do |(*stage_path, block)|
55
- self.before(stage_name, *stage_path, &block)
51
+ before(stage_name, *stage_path, &block)
56
52
  end
57
53
 
58
54
  callbacks[:after].each do |(*stage_path, block)|
59
- self.after(stage_name, *stage_path, &block)
55
+ after(stage_name, *stage_path, &block)
60
56
  end
61
57
  end
62
58
  end
@@ -76,38 +72,35 @@ module Praxis
76
72
  @action = action
77
73
  @request = request
78
74
 
79
- payload = {request: request, response: nil, controller: @controller}
80
-
81
- instrumented_dispatch( payload )
75
+ payload = { request: request, response: nil, controller: @controller }
82
76
 
77
+ instrumented_dispatch(payload)
83
78
  ensure
84
79
  @controller = nil
85
80
  @action = nil
86
81
  @request = nil
87
82
  end
88
83
 
89
- def instrumented_dispatch( payload )
90
- Notifications.instrument 'praxis.request.all'.freeze, payload do
91
- begin
92
- # the response stage must be the final stage in the list
93
- *stages, response_stage = @stages
94
-
95
- stages.each do |stage|
96
- result = stage.run
97
- case result
98
- when Response
99
- controller.response = result
100
- break
101
- end
84
+ def instrumented_dispatch(payload)
85
+ Notifications.instrument 'praxis.request.all', payload do
86
+ # the response stage must be the final stage in the list
87
+ *stages, response_stage = @stages
88
+
89
+ stages.each do |stage|
90
+ result = stage.run
91
+ case result
92
+ when Response
93
+ controller.response = result
94
+ break
102
95
  end
96
+ end
103
97
 
104
- response_stage.run
98
+ response_stage.run
105
99
 
106
- payload[:response] = controller.response
107
- controller.response.finish
108
- rescue => e
109
- @application.error_handler.handle!(request, e)
110
- end
100
+ payload[:response] = controller.response
101
+ controller.response.finish
102
+ rescue StandardError => e
103
+ @application.error_handler.handle!(request, e)
111
104
  end
112
105
  end
113
106
 
@@ -116,9 +109,8 @@ module Praxis
116
109
  return unless Praxis::Blueprint.caching_enabled?
117
110
 
118
111
  Praxis::Blueprint.cache = Hash.new do |hash, key|
119
- hash[key] = Hash.new
112
+ hash[key] = {}
120
113
  end
121
114
  end
122
-
123
115
  end
124
116
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class InfoObject
5
7
  attr_reader :info, :version
6
- def initialize(version: , api_definition_info: )
8
+
9
+ def initialize(version:, api_definition_info:)
7
10
  @version = version
8
11
  @info = api_definition_info
9
12
  raise "OpenApi docs require a 'Title' for your API." unless info.title
@@ -11,12 +14,12 @@ module Praxis
11
14
 
12
15
  def dump
13
16
  data = { version: version }
14
- [
15
- :title,
16
- :description,
17
- :termsOfService,
18
- :contact,
19
- :license
17
+ %i[
18
+ title
19
+ description
20
+ termsOfService
21
+ contact
22
+ license
20
23
  ].each do |attr|
21
24
  val = info.send(attr)
22
25
  data[attr] = val if val
@@ -27,7 +30,7 @@ module Praxis
27
30
  if info.logo_url
28
31
  data[:'x-logo'] = {
29
32
  url: info.logo_url,
30
- backgroundColor: "#FFFFFF",
33
+ backgroundColor: '#FFFFFF',
31
34
  altText: info.title
32
35
  }
33
36
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class MediaTypeObject
5
7
  attr_reader :schema, :example
8
+
6
9
  def initialize(schema:, example:)
7
10
  @schema = schema
8
11
  @example = example
@@ -10,26 +13,24 @@ module Praxis
10
13
 
11
14
  def dump
12
15
  {
13
- schema: schema,
14
- example: example,
15
- # encoding: TODO SUPPORT IT maybe be great/necessary for multipart
16
+ schema: schema,
17
+ example: example
18
+ # encoding: TODO SUPPORT IT maybe be great/necessary for multipart
16
19
  }
17
20
  end
18
21
 
19
22
  # Helper to create the typical content attribute for responses and request bodies
20
- def self.create_content_attribute_helper(type: , example_payload:, example_handlers: nil)
23
+ def self.create_content_attribute_helper(type:, example_payload:, example_handlers: nil)
21
24
  # Will produce 1 example encoded with a given handler (and marking it with the given content type)
22
- unless example_handlers
23
- example_handlers = [ {'application/json' => 'json' } ]
24
- end
25
+ example_handlers ||= [{ 'application/json' => 'json' }]
25
26
  # NOTE2: we should just create a $ref here unless it's an anon mediatype...
26
- return {} if type.is_a? SimpleMediaType # NOTE: skip if it's a SimpleMediaType?? ... is that correct?
27
+ return {} if type.is_a? SimpleMediaType # NOTE: skip if it's a SimpleMediaType?? ... is that correct?
27
28
 
28
- the_schema = if type.anonymous? || ! (type < Praxis::MediaType) # Avoid referencing custom/simple Types? (i.e., just MTs)
29
- SchemaObject.new(info: type).dump_schema
30
- else
31
- { '$ref': "#/components/schemas/#{type.id}" }
32
- end
29
+ the_schema = if type.anonymous? || !(type < Praxis::MediaType) # Avoid referencing custom/simple Types? (i.e., just MTs)
30
+ SchemaObject.new(info: type).dump_schema(shallow: false, allow_ref: false)
31
+ else
32
+ SchemaObject.new(info: type).dump_schema(shallow: true, allow_ref: true)
33
+ end
33
34
 
34
35
  if example_payload
35
36
  examples_by_content_type = {}
@@ -40,16 +41,16 @@ module Praxis
40
41
  handler = Praxis::Application.instance.handlers[handler_name]
41
42
  # ReDoc is not happy to display json generated outputs when served as JSON...wtf?
42
43
  generated = handler.generate(rendered_payload)
43
- final = ( handler_name == 'json') ? JSON.parse(generated) : generated
44
+ final = handler_name == 'json' ? JSON.parse(generated) : generated
44
45
  examples_by_content_type[content_type] = final
45
46
  end
46
47
  end
47
48
 
48
49
  # Key string (of MT) , value MTObject
49
- content_hash = examples_by_content_type.each_with_object({}) do |(content_type, example_hash),accum|
50
- accum[content_type] = MediaTypeObject.new(
50
+ examples_by_content_type.transform_values do |example_hash|
51
+ MediaTypeObject.new(
51
52
  schema: the_schema, # Every MT will have the same exact type..oh well .. maybe a REF?
52
- example: example_hash,
53
+ example: example_hash
53
54
  ).dump
54
55
  end
55
56
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'parameter_object'
2
4
  require_relative 'request_body_object'
3
5
  require_relative 'responses_object'
@@ -8,6 +10,7 @@ module Praxis
8
10
  class OperationObject
9
11
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operation-object
10
12
  attr_reader :id, :url, :action, :tags
13
+
11
14
  def initialize(id:, url:, action:, tags:)
12
15
  @id = id
13
16
  @url = url
@@ -20,18 +23,18 @@ module Praxis
20
23
  all_tags = tags + action.traits
21
24
  h = {
22
25
  summary: action.name.to_s,
23
- description: action.description,
24
- #externalDocs: {}, # TODO/FIXME
26
+ # externalDocs: {}, # TODO/FIXME
25
27
  operationId: id,
26
- responses: ResponsesObject.new(responses: action.responses).dump,
28
+ responses: ResponsesObject.new(responses: action.responses).dump
27
29
  # callbacks
28
30
  # deprecated: false
29
31
  # security: [{}]
30
32
  # servers: [{}]
31
33
  }
34
+ h[:description] = action.description if action.description
32
35
  h[:tags] = all_tags.uniq unless all_tags.empty?
33
36
  h[:parameters] = all_parameters unless all_parameters.empty?
34
- h[:requestBody] = RequestBodyObject.new(attribute: action.payload ).dump if action.payload
37
+ h[:requestBody] = RequestBodyObject.new(attribute: action.payload).dump if action.payload
35
38
  h
36
39
  end
37
40
  end