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,15 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Types
3
5
  class MultipartArray < Attributor::Collection
4
6
  class PartDefinition
5
-
6
- attr_accessor :payload_attribute
7
- attr_accessor :headers_attribute
8
- attr_accessor :filename_attribute
9
-
7
+ attr_accessor :payload_attribute, :headers_attribute, :filename_attribute
10
8
 
11
9
  def initialize(&block)
12
- self.instance_eval(&block)
10
+ instance_eval(&block)
13
11
  end
14
12
 
15
13
  def update_attribute(attribute, options, block)
@@ -17,17 +15,17 @@ module Praxis
17
15
  attribute.type.attributes(**options, &block)
18
16
  end
19
17
 
20
- def create_attribute(type=Attributor::Struct, **opts, &block)
18
+ def create_attribute(type = Attributor::Struct, **opts, &block)
21
19
  # TODO: how do we want to handle any referenced types?
22
- return Attributor::Attribute.new(type, opts, &block)
20
+ Attributor::Attribute.new(type, opts, &block)
23
21
  end
24
22
 
25
- def payload(type=Attributor::Struct, **opts, &block)
26
- #return @payload if !block_given? && type == Attributor::Struct
23
+ def payload(type = Attributor::Struct, **opts, &block)
24
+ # return @payload if !block_given? && type == Attributor::Struct
27
25
  @payload_attribute = create_attribute(type, **opts, &block)
28
26
  end
29
27
 
30
- def header(name, val=nil, **options)
28
+ def header(name, val = nil, **options)
31
29
  block = proc { header(name, val, **options) }
32
30
 
33
31
  if @headers_attribute
@@ -35,17 +33,16 @@ module Praxis
35
33
  else
36
34
  type = Attributor::Hash.of(key: String)
37
35
  @headers_attribute = create_attribute(type,
38
- dsl_compiler: Praxis::ActionDefinition::HeadersDSLCompiler,
39
- case_insensitive_load: false, # :(
40
- allow_extra: true,
41
- &block)
36
+ dsl_compiler: Praxis::ActionDefinition::HeadersDSLCompiler,
37
+ case_insensitive_load: false, # :(
38
+ allow_extra: true,
39
+ &block)
42
40
  end
43
41
  end
44
42
 
45
- def filename(type=String, **opts)
43
+ def filename(type = String, **opts)
46
44
  @filename_attribute = create_attribute(type, **opts)
47
45
  end
48
-
49
46
  end
50
47
  end
51
48
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'praxis/types/multipart_array/part_definition'
2
4
 
3
5
  module Praxis
@@ -12,6 +14,7 @@ module Praxis
12
14
  @identifier = MediaTypeIdentifier.load('multipart/form-data').freeze
13
15
 
14
16
  def self.inherited(klass)
17
+ super
15
18
  klass.instance_eval do
16
19
  @attributes = FuzzyHash.new
17
20
  @saved_blocks = []
@@ -26,38 +29,32 @@ module Praxis
26
29
  @part_attribute = nil
27
30
 
28
31
  @identifier = MediaTypeIdentifier.load('multipart/form-data').freeze
29
-
30
32
  end
31
33
  end
32
34
 
33
35
  class << self
34
- attr_reader :attributes
35
- attr_reader :multiple
36
- attr_reader :options
37
- attr_reader :identifier
36
+ attr_reader :attributes, :multiple, :options, :identifier
38
37
  end
39
38
 
40
39
  def self.constructable?
41
40
  true
42
41
  end
43
42
 
44
- def self.construct(constructor_block, _options={})
43
+ def self.construct(constructor_block, _options = {})
45
44
  Class.new(self, &constructor_block)
46
45
  end
47
46
 
48
- def self.name_type(type=nil)
47
+ def self.name_type(type = nil)
49
48
  return @name_type if type.nil?
50
49
 
51
50
  @name_type = Attributor.resolve_type type
52
51
  end
53
52
 
54
- def self.payload_type(type=nil, **opts, &block)
53
+ def self.payload_type(type = nil, **opts, &block)
55
54
  if type.nil?
56
- if block_given?
57
- type = Attributor::Struct
58
- else
59
- return @payload_type
60
- end
55
+ return @payload_type unless block_given?
56
+
57
+ type = Attributor::Struct
61
58
  end
62
59
  @payload_type = Attributor.resolve_type(type)
63
60
  @payload_attribute = Attributor::Attribute.new(@payload_type, **opts, &block)
@@ -70,13 +67,13 @@ module Praxis
70
67
  end
71
68
 
72
69
  def self.part_attribute
73
- @part_attribute ||= Attributor::Attribute.new(Praxis::MultipartPart, payload_attribute: self.payload_attribute)
70
+ @part_attribute ||= Attributor::Attribute.new(Praxis::MultipartPart, payload_attribute: payload_attribute)
74
71
  end
75
72
 
76
- def self.part(name, payload_type=nil, multiple: false, filename: false, **opts, &block)
73
+ def self.part(name, payload_type = nil, multiple: false, filename: false, **opts, &block)
77
74
  @attributes.default_proc = nil
78
75
 
79
- if name.kind_of?(Regexp)
76
+ if name.is_a?(Regexp)
80
77
  raise 'part with regexp name may not take :multiple option' if multiple
81
78
  raise 'part with regexp name may not be required' if opts[:required] == true
82
79
  end
@@ -85,9 +82,7 @@ module Praxis
85
82
 
86
83
  compiler = Attributor::DSLCompiler.new(self, **opts)
87
84
 
88
- if filename
89
- filename_attribute = compiler.define('filename', String, required: true)
90
- end
85
+ filename_attribute = compiler.define('filename', String, required: true) if filename
91
86
 
92
87
  if block_given?
93
88
  definition = PartDefinition.new(&block)
@@ -95,35 +90,31 @@ module Praxis
95
90
  header_attribute = definition.headers_attribute
96
91
  filename_attribute = definition.filename_attribute || filename_attribute
97
92
 
98
- self.attributes[name] = compiler.define(name, Praxis::MultipartPart,
99
- payload_attribute: payload_attribute,
100
- headers_attribute: header_attribute,
101
- filename_attribute: filename_attribute
102
- )
93
+ attributes[name] = compiler.define(name, Praxis::MultipartPart,
94
+ payload_attribute: payload_attribute,
95
+ headers_attribute: header_attribute,
96
+ filename_attribute: filename_attribute)
103
97
  else
104
98
  payload_attribute = compiler.define(name, payload_type || self.payload_type, **opts)
105
- self.attributes[name] = compiler.define(name, Praxis::MultipartPart,
106
- payload_attribute: payload_attribute,
107
- filename_attribute: filename_attribute
108
- )
99
+ attributes[name] = compiler.define(name, Praxis::MultipartPart,
100
+ payload_attribute: payload_attribute,
101
+ filename_attribute: filename_attribute)
109
102
  end
110
103
  end
111
104
 
112
- def self.file(name, payload_type=nil, filename: nil, **opts, &block)
113
- self.part(name, payload_type, filename: true, **opts, &block)
105
+ def self.file(name, payload_type = nil, **opts, &block)
106
+ part(name, payload_type, filename: true, **opts, &block)
114
107
  end
115
108
 
116
- def self.load(value, context=Attributor::DEFAULT_ROOT_CONTEXT, content_type:nil)
117
- return value if value.kind_of?(self) || value.nil?
109
+ def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, content_type: nil)
110
+ return value if value.is_a?(self) || value.nil?
118
111
 
119
- if value.kind_of?(::String) && content_type.nil?
120
- raise ArgumentError, "content_type is required to load values of type String for #{Attributor.type_name(self)}"
121
- end
112
+ raise ArgumentError, "content_type is required to load values of type String for #{Attributor.type_name(self)}" if value.is_a?(::String) && content_type.nil?
122
113
 
123
- parser = Praxis::MultipartParser.new({'Content-Type' => content_type}, value)
114
+ parser = Praxis::MultipartParser.new({ 'Content-Type' => content_type }, value)
124
115
  preamble, parts = parser.parse
125
116
 
126
- instance = self.new
117
+ instance = new
127
118
  instance.push(*parts)
128
119
 
129
120
  instance.preamble = preamble
@@ -132,22 +123,23 @@ module Praxis
132
123
  instance
133
124
  end
134
125
 
135
- def self.example(context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
136
- example = self.new
126
+ def self.example(context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
127
+ example = new
137
128
 
138
- self.attributes.each do |name, attribute|
139
- next if name.kind_of? Regexp
140
- sub_context = self.generate_subcontext(context, name)
129
+ attributes.each do |name, attribute|
130
+ next if name.is_a? Regexp
131
+
132
+ sub_context = generate_subcontext(context, name)
141
133
 
142
134
  part = attribute.example(sub_context)
143
135
  part.name = name
144
136
  example.push part
145
137
 
146
- if self.multiple.include? name
147
- part = attribute.example(sub_context + ['2'])
148
- part.name = name
149
- example.push part
150
- end
138
+ next unless multiple.include? name
139
+
140
+ part = attribute.example(sub_context + ['2'])
141
+ part.name = name
142
+ example.push part
151
143
  end
152
144
 
153
145
  example
@@ -174,36 +166,35 @@ module Praxis
174
166
  # profileImage: # Part 3 (an image)
175
167
  # type: string
176
168
  # format: binary
177
- #
178
- # NOTE: not sure if this
179
- def self.as_openapi_request_body( attribute_options: {} )
169
+ #
170
+ # NOTE: not sure if this
171
+ def self.as_openapi_request_body(attribute_options: {})
180
172
  hash = { type: json_schema_type }
181
- opts = self.options.merge( attribute_options )
173
+ opts = options.merge(attribute_options)
182
174
  hash[:description] = opts[:description] if opts[:description]
183
175
  hash[:default] = opts[:default] if opts[:default]
184
176
 
185
- unless self.attributes.empty?
177
+ unless attributes.empty?
186
178
  props = {}
187
179
  encoding = {}
188
- self.attributes.each do |part_name, part_attribute|
180
+ attributes.each do |part_name, part_attribute|
189
181
  part_example = part_attribute.example
190
182
  key_to_use = part_name.is_a?(Regexp) ? part_name.source : part_name
191
-
192
- part_info = {}
183
+
193
184
  if (payload_attribute = part_attribute.options[:payload_attribute])
194
185
  props[key_to_use] = payload_attribute.as_json_schema(example: part_example.payload)
195
186
  end
196
- #{
187
+ # {
197
188
  # contentType: 'fff',
198
189
  # headers: {
199
190
  # custom1: 'safd'
200
191
  # }
201
- if (headers_attribute = part_attribute.options[:headers_attribute])
202
- # Does this 'Content-Type' string check work?...can it be a symbol? what does it mean anyway?
203
- encoding[key_to_use][:contentType] = headers_attribute['Content-Type'] if headers_attribute['Content-Type']
204
- # TODO?rethink? ...is this correct?: att a 'headers' key with some header schemas if this part have some
205
- encoding[key_to_use]['headers'] = headers_attribute.as_json_schema(example: part_example.headers)
206
- end
192
+ next unless (headers_attribute = part_attribute.options[:headers_attribute])
193
+
194
+ # Does this 'Content-Type' string check work?...can it be a symbol? what does it mean anyway?
195
+ encoding[key_to_use][:contentType] = headers_attribute['Content-Type'] if headers_attribute['Content-Type']
196
+ # TODO?rethink? ...is this correct?: att a 'headers' key with some header schemas if this part have some
197
+ encoding[key_to_use]['headers'] = headers_attribute.as_json_schema(example: part_example.headers)
207
198
  end
208
199
 
209
200
  hash[:properties] = props
@@ -212,27 +203,27 @@ module Praxis
212
203
  hash
213
204
  end
214
205
 
215
- def self.as_json_schema( shallow: false, example: nil, attribute_options: {} )
206
+ def self.as_json_schema(attribute_options: {}, **_other)
216
207
  as_openapi_request_body(attribute_options: attribute_options)
217
208
  end
218
209
 
219
- def self.describe(shallow=true, example: nil)
210
+ def self.describe(shallow = true, example: nil)
220
211
  type_name = Attributor.type_name(self)
221
212
  hash = {
222
213
  name: type_name.gsub(Attributor::MODULE_PREFIX_REGEX, ''),
223
- family: self.family,
224
- id: self.id
214
+ family: family,
215
+ id: id
225
216
  }
226
217
  hash[:example] = example if example
227
218
 
228
- hash[:part_name] = {type: name_type.describe(true)}
219
+ hash[:part_name] = { type: name_type.describe(true) }
229
220
 
230
221
  unless shallow
231
- hash[:attributes] = {} if self.attributes.keys.any? { |name| name.kind_of? String}
232
- hash[:pattern_attributes] = {} if self.attributes.keys.any? { |name| name.kind_of? Regexp}
222
+ hash[:attributes] = {} if attributes.keys.any? { |name| name.is_a? String }
223
+ hash[:pattern_attributes] = {} if attributes.keys.any? { |name| name.is_a? Regexp }
233
224
 
234
225
  if hash.key?(:attributes) || hash.key?(:pattern_attributes)
235
- self.describe_attributes(shallow, example: example).each do |name, sub_hash|
226
+ describe_attributes(shallow, example: example).each do |name, sub_hash|
236
227
  case name
237
228
  when String
238
229
  hash[:attributes][name] = sub_hash
@@ -241,38 +232,30 @@ module Praxis
241
232
  end
242
233
  end
243
234
  else
244
- hash[:part_payload] = {type: payload_type.describe(true)}
235
+ hash[:part_payload] = { type: payload_type.describe(true) }
245
236
  end
246
237
  end
247
238
  hash
248
239
  end
249
240
 
250
- def self.describe_attributes(shallow=true, example: nil)
251
- self.attributes.each_with_object({}) do |(part_name, part_attribute), parts|
241
+ def self.describe_attributes(shallow = true, example: nil)
242
+ attributes.each_with_object({}) do |(part_name, part_attribute), parts|
252
243
  sub_example = example.part(part_name) if example
253
- if sub_example && self.multiple.include?(part_name)
254
- sub_example = sub_example.first
255
- end
244
+ sub_example = sub_example.first if sub_example && multiple.include?(part_name)
256
245
 
257
246
  sub_hash = part_attribute.describe(shallow, example: sub_example)
258
247
 
259
-
260
248
  if (options = sub_hash.delete(:options))
261
249
  sub_hash[:options] = {}
262
- if self.multiple.include?(part_name)
263
- sub_hash[:options][:multiple] = true
264
- end
250
+ sub_hash[:options][:multiple] = true if multiple.include?(part_name)
265
251
 
266
- if (payload_attribute = options.delete :payload_attribute)
267
- if (required = payload_attribute.options[:required])
268
- sub_hash[:options][:required] = true
269
- end
252
+ if (payload_attribute = options.delete :payload_attribute) && payload_attribute.options[:required]
253
+ sub_hash[:options][:required] = true
270
254
  end
271
255
  end
272
256
 
273
257
  sub_hash[:type] = MultipartPart.describe(shallow, example: sub_example, options: part_attribute.options)
274
258
 
275
-
276
259
  parts[part_name] = sub_hash
277
260
  end
278
261
  end
@@ -281,14 +264,13 @@ module Praxis
281
264
  attr_reader :content_type
282
265
 
283
266
  def initialize(content_type: self.class.identifier.to_s)
267
+ super()
284
268
  self.content_type = content_type
285
269
  end
286
270
 
287
271
  def content_type=(content_type)
288
272
  @content_type = MediaTypeIdentifier.load(content_type)
289
- if @content_type.parameters.get('boundary').nil?
290
- @content_type.parameters.set 'boundary', 'Boundary_puppies'
291
- end
273
+ @content_type.parameters.set 'boundary', 'Boundary_puppies' if @content_type.parameters.get('boundary').nil?
292
274
  @content_type
293
275
  end
294
276
 
@@ -298,9 +280,7 @@ module Praxis
298
280
 
299
281
  def push(*parts, context: Attributor::DEFAULT_ROOT_CONTEXT)
300
282
  part, *rest = parts
301
- if rest.any?
302
- return self.push(part, context: context).push(*rest, context:context)
303
- end
283
+ return push(part, context: context).push(*rest, context: context) if rest.any?
304
284
 
305
285
  original_context = context
306
286
 
@@ -313,12 +293,10 @@ module Praxis
313
293
  # payload_attribute, otherwise we constrain the parts
314
294
  # to the defined names.
315
295
  attribute = if self.class.attributes.empty?
316
- self.class.part_attribute
317
- elsif (default_thingy = self.class.attributes[key])
318
- default_thingy
319
- else
320
- nil
321
- end
296
+ self.class.part_attribute
297
+ elsif (default_thingy = self.class.attributes[key])
298
+ default_thingy
299
+ end
322
300
 
323
301
  if attribute
324
302
  part.attribute = attribute
@@ -327,11 +305,11 @@ module Praxis
327
305
  return self << part
328
306
  elsif self.class.options[:case_insensitive_load]
329
307
  name = self.class.attributes.keys.find do |k|
330
- k.kind_of?(String) && key.downcase == k.downcase
308
+ k.is_a?(String) && key.downcase == k.downcase
331
309
  end
332
310
  if name
333
311
  part.name = name
334
- return self.push(part, context: original_context)
312
+ return push(part, context: original_context)
335
313
  end
336
314
  end
337
315
 
@@ -342,30 +320,39 @@ module Praxis
342
320
  if self.class.multiple.include?(name)
343
321
  self.select { |i| i.name == name }
344
322
  else
345
- self.find { |i| i.name == name }
323
+ find { |i| i.name == name }
346
324
  end
347
325
  end
348
326
 
349
- def validate(context=Attributor::DEFAULT_ROOT_CONTEXT)
350
- errors = self.each_with_index.each_with_object([]) do |(part, idx), errors|
327
+ def part?(name)
328
+ any? { |i| i.name == name }
329
+ end
330
+
331
+ def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
332
+ errors = each_with_index.each_with_object([]) do |(part, idx), error_list|
351
333
  sub_context = if part.name
352
- self.class.generate_subcontext(context, part.name)
353
- else
354
- context + ["at(#{idx})"]
355
- end
334
+ self.class.generate_subcontext(context, part.name)
335
+ else
336
+ context + ["at(#{idx})"]
337
+ end
356
338
 
357
- errors.push *part.validate(sub_context)
339
+ error_list.push(*part.validate(sub_context))
358
340
  end
359
341
 
360
342
  self.class.attributes.each do |name, attribute|
361
343
  payload_attribute = attribute.options[:payload_attribute]
362
- next unless payload_attribute.options[:required]
363
- next if self.part(name)
364
344
 
365
- sub_context = self.class.generate_subcontext(context, name)
366
- errors.push *payload_attribute.validate_missing_value(sub_context)
345
+ if !part?(name)
346
+ if payload_attribute.options[:required]
347
+ sub_context = self.class.generate_subcontext(context, name)
348
+ errors.push "Attribute #{Attributor.humanize_context(sub_context)} is required"
349
+ end
350
+ # Return, don't bother checking nullability as it hasn't been provided
351
+ elsif !self.part(name) && !Attribute.nullable_attribute?(payload_attribute.options)
352
+ sub_context = self.class.generate_subcontext(context, name)
353
+ errors.push "Attribute #{Attributor.humanize_context(sub_context)} is not nullable"
354
+ end
367
355
  end
368
-
369
356
  errors
370
357
  end
371
358
 
@@ -376,15 +363,13 @@ module Praxis
376
363
  def dump(**opts)
377
364
  boundary = content_type.parameters.get 'boundary'
378
365
 
379
- parts = self.collect do |part|
366
+ parts = collect do |part|
380
367
  part.dump(**opts)
381
368
  end
382
369
 
383
370
  all_entities = parts.join("\r\n--#{boundary}\r\n")
384
371
  "--#{boundary}\r\n#{all_entities}\r\n--#{boundary}--\r\n"
385
372
  end
386
-
387
-
388
373
  end
389
374
  end
390
375
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  class ValidationHandler
3
-
4
5
  # Should return the Response to send back
5
- def handle!(summary:, request:, stage:, errors: nil, exception: nil, **opts)
6
+ def handle!(summary:, errors: nil, exception: nil, **opts)
7
+ opts.delete(:request)
8
+ opts.delete(:stage)
6
9
  Responses::ValidationError.new(summary: summary, errors: errors, exception: exception, **opts)
7
10
  end
8
-
9
11
  end
10
12
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
- VERSION = '2.0.pre.17'
4
+ VERSION = '2.0.pre.21'
3
5
  end
data/lib/praxis.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
4
  require 'attributor'
3
5
 
@@ -5,7 +7,7 @@ require 'active_support/concern'
5
7
  require 'praxis/request_superclassing'
6
8
  require 'active_support/inflector'
7
9
 
8
- $:.unshift File.dirname(__FILE__)
10
+ $LOAD_PATH.unshift File.dirname(__FILE__)
9
11
 
10
12
  require 'mime'
11
13
  module MIME
@@ -15,7 +17,6 @@ module MIME
15
17
  end
16
18
 
17
19
  module Praxis
18
-
19
20
  autoload :ActionDefinition, 'praxis/action_definition'
20
21
  autoload :ApiGeneralInfo, 'praxis/api_general_info'
21
22
  autoload :ApiDefinition, 'praxis/api_definition'
@@ -28,7 +29,7 @@ module Praxis
28
29
  autoload :ErrorHandler, 'praxis/error_handler'
29
30
  autoload :ValidationHandler, 'praxis/validation_handler'
30
31
  autoload :Exception, 'praxis/exception'
31
- autoload :FileGroup,'praxis/file_group'
32
+ autoload :FileGroup, 'praxis/file_group'
32
33
  autoload :Plugin, 'praxis/plugin'
33
34
  autoload :PluginConcern, 'praxis/plugin_concern'
34
35
  autoload :Request, 'praxis/request'
@@ -50,7 +51,6 @@ module Praxis
50
51
  autoload :Blueprint, 'praxis/blueprint'
51
52
  autoload :FieldExpander, 'praxis/field_expander'
52
53
  autoload :Renderer, 'praxis/renderer'
53
-
54
54
 
55
55
  autoload :Notifications, 'praxis/notifications'
56
56
  autoload :MiddlewareApp, 'praxis/middleware_app'
@@ -143,5 +143,4 @@ module Praxis
143
143
  require 'praxis/responses/internal_server_error'
144
144
  require 'praxis/responses/validation_error'
145
145
  require 'praxis/responses/multipart_ok'
146
-
147
146
  end
data/praxis.gemspec CHANGED
@@ -1,36 +1,38 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
 
4
6
  require 'praxis/version'
5
7
 
6
8
  Gem::Specification.new do |spec|
7
- spec.name = "praxis"
9
+ spec.name = 'praxis'
8
10
  spec.version = Praxis::VERSION
9
- spec.authors = ["Josep M. Blanquer","Dane Jensen"]
11
+ spec.authors = ['Josep M. Blanquer', 'Dane Jensen']
10
12
  spec.summary = 'Building APIs the way you want it.'
11
13
 
12
- spec.email = ["blanquer@gmail.com","dane.jensen@gmail.com"]
14
+ spec.email = ['blanquer@gmail.com', 'dane.jensen@gmail.com']
13
15
 
14
- spec.homepage = "https://github.com/praxis/praxis"
15
- spec.license = "MIT"
16
- spec.required_ruby_version = ">=2.1"
16
+ spec.homepage = 'https://github.com/praxis/praxis'
17
+ spec.license = 'MIT'
18
+ spec.required_ruby_version = '>=2.5'
17
19
 
18
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
19
21
  spec.files = `git ls-files -z`.split("\x0")
20
22
  spec.bindir = 'bin'
21
23
  spec.executables << 'praxis'
22
24
 
23
- spec.add_dependency 'rack', '>= 1'
24
- spec.add_dependency 'mustermann', '>=1.1', '<=2'
25
25
  spec.add_dependency 'activesupport', '>= 3'
26
+ spec.add_dependency 'attributor', '>= 6.0'
26
27
  spec.add_dependency 'mime', '~> 0'
27
- spec.add_dependency 'attributor', '>= 5.5'
28
- spec.add_dependency 'thor'
28
+ spec.add_dependency 'mustermann', '>=1.1', '<=2'
29
+ spec.add_dependency 'rack', '>= 1'
29
30
  spec.add_dependency 'terminal-table', '~> 1.4'
31
+ spec.add_dependency 'thor'
30
32
 
31
33
  spec.add_development_dependency 'bundler'
32
34
  spec.add_development_dependency 'rake', '>= 12.3.3'
33
-
35
+
34
36
  if RUBY_PLATFORM !~ /java/
35
37
  spec.add_development_dependency 'pry'
36
38
  spec.add_development_dependency 'pry-byebug'
@@ -39,17 +41,16 @@ Gem::Specification.new do |spec|
39
41
  else
40
42
  spec.add_development_dependency 'jdbc-sqlite3'
41
43
  end
42
- spec.add_development_dependency 'rspec', '~> 3'
43
- spec.add_development_dependency 'rspec-its', '~> 1'
44
- spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
44
+ spec.add_development_dependency 'coveralls'
45
+ spec.add_development_dependency 'fuubar', '~> 2'
45
46
  spec.add_development_dependency 'guard', '~> 2'
46
- spec.add_development_dependency 'guard-rspec', '~> 4'
47
47
  spec.add_development_dependency 'guard-bundler', '~> 2'
48
+ spec.add_development_dependency 'guard-rspec', '~> 4'
48
49
  spec.add_development_dependency 'rack-test', '~> 0'
49
- spec.add_development_dependency 'simplecov', '~> 0'
50
- spec.add_development_dependency 'fuubar', '~> 2'
51
- spec.add_development_dependency 'coveralls'
50
+ spec.add_development_dependency 'rspec', '~> 3'
51
+ spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
52
+ spec.add_development_dependency 'rspec-its', '~> 1'
52
53
  # Just for the query selector extensions etc...
54
+ spec.add_development_dependency 'activerecord', '> 4', '< 7'
53
55
  spec.add_development_dependency 'sequel', '~> 5'
54
- spec.add_development_dependency 'activerecord', '> 4'
55
56
  end