praxis 2.0.pre.16 → 2.0.pre.20

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 +22 -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 +187 -131
  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 +221 -106
  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 -47
  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 +12 -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