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,32 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  # stolen from Rack::Multipart::Parser
4
6
 
5
7
  module Praxis
6
8
  class MultipartParser
7
- EOL = "\r\n".freeze
8
- MULTIPART_BOUNDARY = "AaB03x".freeze
9
- MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n.freeze
10
- TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/.freeze
9
+ EOL = "\r\n"
10
+ MULTIPART_BOUNDARY = 'AaB03x'
11
+ MULTIPART = %r{\Amultipart/.*boundary="?([^";,]+)"?}n.freeze
12
+ TOKEN = %r{[^\s()<>,;:\\"/\[\]?=]+}.freeze
11
13
  CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i.freeze
12
14
  DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/.freeze
13
15
  RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i.freeze
14
16
  BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i.freeze
15
17
  BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i.freeze
16
18
  MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni.freeze
17
- MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni.freeze
19
+ MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^";]*)"?/ni.freeze
18
20
  MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni.freeze
19
21
 
20
22
  HEADER_KV = /(?<k>[^:]+): (?<v>.*)/.freeze
21
23
  UNTIL_NEWLINE = /\A([^\n]*\n)/.freeze
22
24
  TERMINAL_CRLF = /\r\n$/.freeze
23
25
 
26
+ PARAMS_BUF_SIZE = 65_536 # Same as implicitly in rack 1.x
27
+ BUFSIZE = 16_384
24
28
 
25
- PARAMS_BUF_SIZE = 65536 # Same as implicitly in rack 1.x
26
- BUFSIZE = 16384
27
-
28
- def self.parse(headers,body)
29
- self.new(headers,body).parse
29
+ def self.parse(headers, body)
30
+ new(headers, body).parse
30
31
  end
31
32
 
32
33
  def initialize(headers, body)
@@ -36,7 +37,7 @@ module Praxis
36
37
  @io << chunk
37
38
  end
38
39
  @io.rewind
39
- @parts = Array.new
40
+ @parts = []
40
41
  end
41
42
 
42
43
  def parse
@@ -46,19 +47,19 @@ module Praxis
46
47
 
47
48
  loop do
48
49
  head, filename, content_type, name, body =
49
- get_current_head_and_filename_and_content_type_and_name_and_body
50
+ current_head_and_filename_and_content_type_and_name_and_body
50
51
 
51
52
  # Save the rest.
52
- if i = @buf.index(rx)
53
+ if (i = @buf.index(rx))
53
54
  body << @buf.slice!(0, i)
54
- @buf.slice!(0, @boundary_size+2)
55
+ @buf.slice!(0, @boundary_size + 2)
55
56
 
56
- @content_length = -1 if $1 == "--"
57
+ @content_length = -1 if Regexp.last_match(1) == '--'
57
58
  end
58
59
 
59
60
  filename, data = get_data(filename, body, content_type, name, head)
60
61
 
61
- parsed_headers = head.split(EOL).each_with_object(Hash.new) do |line, hash|
62
+ parsed_headers = head.split(EOL).each_with_object({}) do |line, hash|
62
63
  match = HEADER_KV.match(line)
63
64
  k = match[:k]
64
65
  v = match[:v]
@@ -70,7 +71,7 @@ module Praxis
70
71
  @parts << part
71
72
 
72
73
  # break if we're at the end of a buffer, but not if it is the end of a field
73
- break if (@buf.empty? && $1 != EOL) || @content_length == -1
74
+ break if (@buf.empty? && Regexp.last_match(1) != EOL) || @content_length == -1
74
75
  end
75
76
 
76
77
  @io.rewind
@@ -79,6 +80,7 @@ module Praxis
79
80
  end
80
81
 
81
82
  private
83
+
82
84
  def setup_parse
83
85
  unless (match = MULTIPART.match @headers['Content-Type'])
84
86
  return false
@@ -86,13 +88,13 @@ module Praxis
86
88
 
87
89
  @boundary = "--#{match[1]}"
88
90
 
89
- @buf = ""
91
+ @buf = String.new
90
92
 
91
93
  @params = new_params
92
94
 
93
95
  @boundary_size = @boundary.bytesize + EOL.size
94
96
 
95
- if @content_length = @headers['Content-Length']
97
+ if (@content_length = @headers['Content-Length'])
96
98
  @content_length = @content_length.to_i
97
99
  @content_length -= @boundary_size
98
100
  end
@@ -119,36 +121,35 @@ module Praxis
119
121
  end
120
122
 
121
123
  def fast_forward_to_first_boundary
122
- preamble = ''
124
+ preamble = String.new
123
125
  loop do
124
126
  content = @io.read(BUFSIZE)
125
- raise EOFError, "bad content body" unless content
127
+ raise EOFError, 'bad content body' unless content
128
+
126
129
  @buf << content
127
130
 
128
131
  while @buf.gsub!(UNTIL_NEWLINE, '')
129
- read_buffer = $1
130
- if read_buffer == full_boundary
131
- return preamble.gsub!(TERMINAL_CRLF,'')
132
- else
133
- preamble << read_buffer
134
- end
132
+ read_buffer = Regexp.last_match(1)
133
+ return preamble.gsub!(TERMINAL_CRLF, '') if read_buffer == full_boundary
134
+
135
+ preamble << read_buffer
135
136
  end
136
137
 
137
- raise EOFError, "bad content body" if Rack::Utils.bytesize(@buf) >= BUFSIZE
138
+ raise EOFError, 'bad content body' if Rack::Utils.bytesize(@buf) >= BUFSIZE
138
139
  end
139
140
  end
140
141
 
141
- def get_current_head_and_filename_and_content_type_and_name_and_body
142
+ def current_head_and_filename_and_content_type_and_name_and_body
142
143
  head = nil
143
- body = ''
144
+ body = String.new
144
145
  filename = content_type = name = nil
145
146
  content = nil
146
147
 
147
148
  until head && @buf =~ rx
148
- if !head && i = @buf.index(EOL+EOL)
149
- head = @buf.slice!(0, i+2) # First \r\n
149
+ if !head && (i = @buf.index(EOL + EOL))
150
+ head = @buf.slice!(0, i + 2) # First \r\n
150
151
 
151
- @buf.slice!(0, 2) # Second \r\n
152
+ @buf.slice!(0, 2) # Second \r\n
152
153
 
153
154
  content_type = head[MULTIPART_CONTENT_TYPE, 1]
154
155
  name = head[MULTIPART_CONTENT_DISPOSITION, 1] || head[MULTIPART_CONTENT_ID, 1]
@@ -157,7 +158,7 @@ module Praxis
157
158
  filename = get_filename(head)
158
159
 
159
160
  if filename
160
- body = Tempfile.new("RackMultipart")
161
+ body = Tempfile.new('RackMultipart')
161
162
  body.binmode if body.respond_to?(:binmode)
162
163
  end
163
164
 
@@ -165,12 +166,10 @@ module Praxis
165
166
  end
166
167
 
167
168
  # Save the read body part.
168
- if head && (@boundary_size+4 < @buf.size)
169
- body << @buf.slice!(0, @buf.size - (@boundary_size+4))
170
- end
169
+ body << @buf.slice!(0, @buf.size - (@boundary_size + 4)) if head && (@boundary_size + 4 < @buf.size)
171
170
 
172
171
  content = @io.read(@content_length && BUFSIZE >= @content_length ? @content_length : BUFSIZE)
173
- raise EOFError, "bad content body" if content.nil? || content.empty?
172
+ raise EOFError, 'bad content body' if content.nil? || content.empty?
174
173
 
175
174
  @buf << content
176
175
  @content_length -= content.size if @content_length
@@ -183,34 +182,30 @@ module Praxis
183
182
  filename = nil
184
183
  if head =~ RFC2183
185
184
  filename = Hash[head.scan(DISPPARM)]['filename']
186
- filename = $1 if filename and filename =~ /^"(.*)"$/
185
+ filename = Regexp.last_match(1) if filename && filename =~ (/^"(.*)"$/)
187
186
  elsif head =~ BROKEN_QUOTED
188
- filename = $1
187
+ filename = Regexp.last_match(1)
189
188
  elsif head =~ BROKEN_UNQUOTED
190
- filename = $1
189
+ filename = Regexp.last_match(1)
191
190
  end
192
191
 
193
- if filename && filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
194
- filename = Rack::Utils.unescape(filename)
195
- end
196
- if filename && filename !~ /\\[^\\"]/
197
- filename = filename.gsub(/\\(.)/, '\1')
198
- end
192
+ filename = Rack::Utils.unescape(filename) if filename&.scan(/%.?.?/)&.all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
193
+ filename = filename.gsub(/\\(.)/, '\1') if filename && filename !~ /\\[^\\"]/
199
194
  filename
200
195
  end
201
196
 
202
- def get_data(filename, body, content_type, name, head)
197
+ def get_data(filename, body, _content_type, _name, _head)
203
198
  # filename is blank which means no file has been selected
204
- return nil if filename == ""
199
+ return nil if filename == ''
205
200
 
206
201
  # Take the basename of the upload's original filename.
207
202
  # This handles the full Windows paths given by Internet Explorer
208
203
  # (and perhaps other broken user agents) without affecting
209
204
  # those which give the lone filename.
210
- filename = filename.split(/[\/\\]/).last if filename
205
+ filename = filename.split(%r{[/\\]}).last if filename
211
206
 
212
207
  # Rewind any IO bodies so the app can read them at its leisure
213
- body.rewind if body.is_a?(IO)
208
+ body.rewind if body.is_a?(IO)
214
209
 
215
210
  [filename, body]
216
211
  end
@@ -1,22 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  class MultipartPart
3
5
  include Attributor::Type
4
6
 
5
- attr_accessor :body
6
- attr_accessor :headers
7
- attr_accessor :filename
8
- attr_accessor :name
9
- attr_accessor :payload_attribute
10
- attr_accessor :headers_attribute
11
- attr_accessor :filename_attribute
12
- attr_accessor :default_handler
7
+ attr_reader :name, :filename
8
+ attr_accessor :body, :headers, :payload_attribute, :headers_attribute, :filename_attribute, :default_handler
13
9
 
14
10
  def self.check_option!(name, definition)
15
11
  case name
16
12
  when :payload_attribute, :headers_attribute, :filename_attribute
17
- unless definition.nil? || definition.kind_of?(Attributor::Attribute)
18
- raise Attributor::AttributorException.new("Value for option #{name.inspect} must be an Attribute. Got #{definition.class.name}")
19
- end
13
+ raise Attributor::AttributorException, "Value for option #{name.inspect} must be an Attribute. Got #{definition.class.name}" unless definition.nil? || definition.is_a?(Attributor::Attribute)
20
14
  else
21
15
  return :unknown
22
16
  end
@@ -32,32 +26,30 @@ module Praxis
32
26
  :object
33
27
  end
34
28
 
35
- def self.example(context=nil, options:{})
29
+ def self.example(context = nil, options: {})
36
30
  if (payload_attribute = options[:payload_attribute])
37
31
  payload = payload_attribute.example(context + ['payload'])
38
32
  end
39
33
 
40
34
  headers = if (headers_attribute = options[:headers_attribute])
41
- headers_attribute.example(context + ['headers'])
42
- else
43
- {}
44
- end
35
+ headers_attribute.example(context + ['headers'])
36
+ else
37
+ {}
38
+ end
45
39
 
46
40
  name = options[:name]
47
41
 
48
42
  filename = if (filename_attribute = options[:filename_attribute])
49
- filename_attribute.example(context + ['filename'])
50
- else
51
- nil
52
- end
43
+ filename_attribute.example(context + ['filename'])
44
+ end
53
45
 
54
- self.new(payload, headers, name: name, filename: filename,
55
- payload_attribute: payload_attribute,
56
- headers_attribute: headers_attribute,
57
- filename_attribute: filename_attribute)
46
+ new(payload, headers, name: name, filename: filename,
47
+ payload_attribute: payload_attribute,
48
+ headers_attribute: headers_attribute,
49
+ filename_attribute: filename_attribute)
58
50
  end
59
-
60
- def self.describe(shallow=true, example: nil, options:{})
51
+
52
+ def self.describe(shallow = true, example: nil, options: {})
61
53
  hash = super(shallow, example: example)
62
54
 
63
55
  if (payload_attribute = options[:payload_attribute])
@@ -76,63 +68,49 @@ module Praxis
76
68
  hash
77
69
  end
78
70
 
79
- def initialize(body, headers={}, name: nil, filename: nil, payload_attribute: nil, headers_attribute: nil, filename_attribute: nil)
80
- @name = name
71
+ def initialize(body, headers = {}, **args)
72
+ @name = args[:name]
81
73
  @body = body
82
74
  @headers = headers
83
75
  @default_handler = Praxis::Application.instance.handlers['json']
84
76
 
85
- if content_type.nil?
86
- self.content_type = 'text/plain'
87
- end
77
+ self.content_type = 'text/plain' if content_type.nil?
88
78
 
89
- @filename = filename
79
+ @filename = args[:filename]
90
80
 
91
- @payload_attribute = payload_attribute
92
- @headers_attribute = headers_attribute
93
- @filename_attribute = filename_attribute
81
+ @payload_attribute = args[:payload_attribute]
82
+ @headers_attribute = args[:headers_attribute]
83
+ @filename_attribute = args[:filename_attribute]
94
84
 
95
85
  reset_content_disposition
96
86
  end
97
87
 
98
- alias_method :payload, :body
99
- alias_method :payload=, :body=
88
+ alias payload body
89
+ alias payload= body=
100
90
 
101
91
  def attribute=(attribute)
102
- unless self.kind_of?(attribute.type)
103
- raise ArgumentError, "invalid attribute type #{attribute.type}"
104
- end
92
+ raise ArgumentError, "invalid attribute type #{attribute.type}" unless is_a?(attribute.type)
105
93
 
106
- if attribute.options.key? :payload_attribute
107
- @payload_attribute = attribute.options[:payload_attribute]
108
- end
94
+ @payload_attribute = attribute.options[:payload_attribute] if attribute.options.key? :payload_attribute
109
95
 
110
- if attribute.options.key? :headers_attribute
111
- @headers_attribute = attribute.options[:headers_attribute]
112
- end
113
-
114
- if attribute.options.key? :filename_attribute
115
- @filename_attribute = attribute.options[:filename_attribute]
116
- end
96
+ @headers_attribute = attribute.options[:headers_attribute] if attribute.options.key? :headers_attribute
117
97
 
98
+ @filename_attribute = attribute.options[:filename_attribute] if attribute.options.key? :filename_attribute
118
99
  end
119
100
 
120
- def load_payload(context=Attributor::DEFAULT_ROOT_CONTEXT)
121
- if self.payload_attribute
122
- value = if self.payload.kind_of?(String)
123
- handler.parse(self.payload)
124
- else
125
- self.payload
126
- end
101
+ def load_payload(_context = Attributor::DEFAULT_ROOT_CONTEXT)
102
+ return unless payload_attribute
127
103
 
128
- self.payload = self.payload_attribute.load(value)
129
- end
104
+ value = if payload.is_a?(String)
105
+ handler.parse(payload)
106
+ else
107
+ payload
108
+ end
109
+ self.payload = payload_attribute.load(value)
130
110
  end
131
111
 
132
- def load_headers(context=Attributor::DEFAULT_ROOT_CONTEXT)
133
- if self.headers_attribute
134
- self.headers = self.headers_attribute.load(self.headers)
135
- end
112
+ def load_headers(_context = Attributor::DEFAULT_ROOT_CONTEXT)
113
+ self.headers = headers_attribute.load(headers) if headers_attribute
136
114
  end
137
115
 
138
116
  # Determine the content type of this response.
@@ -166,42 +144,39 @@ module Praxis
166
144
  end
167
145
  end
168
146
 
169
- def validate_headers(context=Attributor::DEFAULT_ROOT_CONTEXT)
170
- return [] unless self.headers_attribute
147
+ def validate_headers(context = Attributor::DEFAULT_ROOT_CONTEXT)
148
+ return [] unless headers_attribute
171
149
 
172
- self.headers_attribute.validate(headers, context + ['headers'])
150
+ headers_attribute.validate(headers, context + ['headers'])
173
151
  end
174
152
 
175
- def validate_payload(context=Attributor::DEFAULT_ROOT_CONTEXT)
176
- return [] unless self.payload_attribute
153
+ def validate_payload(context = Attributor::DEFAULT_ROOT_CONTEXT)
154
+ return [] unless payload_attribute
177
155
 
178
- self.payload_attribute.validate(payload, context + ['payload'])
156
+ payload_attribute.validate(payload, context + ['payload'])
179
157
  end
180
158
 
181
- def validate_filename(context=Attributor::DEFAULT_ROOT_CONTEXT)
182
- return [] unless self.filename_attribute
159
+ def validate_filename(context = Attributor::DEFAULT_ROOT_CONTEXT)
160
+ return [] unless filename_attribute
183
161
 
184
- self.filename_attribute.validate(filename, context + ['filename'])
162
+ filename_attribute.validate(filename, context + ['filename'])
185
163
  end
186
164
 
187
- def validate(context=Attributor::DEFAULT_ROOT_CONTEXT)
165
+ def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
188
166
  errors = validate_headers(context)
189
- errors.push *validate_payload(context)
190
- errors.push *validate_filename(context)
167
+ errors.push(*validate_payload(context))
168
+ errors.push(*validate_filename(context))
191
169
  end
192
170
 
193
171
  def reset_content_disposition
194
- self.headers['Content-Disposition'] = begin
172
+ headers['Content-Disposition'] = begin
195
173
  disposition = "form-data; name=#{name}"
196
- if filename
197
- disposition += "; filename=#{filename}"
198
- end
174
+ disposition += "; filename=#{filename}" if filename
199
175
 
200
176
  disposition
201
177
  end
202
178
  end
203
179
 
204
-
205
180
  def name=(name)
206
181
  @name = name
207
182
  reset_content_disposition
@@ -221,25 +196,23 @@ module Praxis
221
196
 
222
197
  # Determine an appropriate default content_type for this part given
223
198
  # the preferred handler_name, if possible.
224
- #
199
+ #
225
200
  # Considers any pre-defined set of values on the content_type attributge
226
201
  # of the headers.
227
202
  def derive_content_type(handler_name)
228
- possible_values = if self.content_type.match 'text/plain'
229
- _, content_type_attribute = self.headers_attribute && self.headers_attribute.attributes.find { |k,v| k.to_s =~ /^content[-_]{1}type$/i }
230
- if content_type_attribute && content_type_attribute.options.key?(:values)
231
- content_type_attribute.options[:values]
232
- else
233
- []
234
- end
235
- else
236
- [self.content_type]
237
- end
203
+ possible_values = if content_type.match 'text/plain'
204
+ _, content_type_attribute = headers_attribute&.attributes&.find { |k, _v| k.to_s =~ /^content[-_]{1}type$/i }
205
+ if content_type_attribute&.options&.key?(:values)
206
+ content_type_attribute.options[:values]
207
+ else
208
+ []
209
+ end
210
+ else
211
+ [content_type]
212
+ end
238
213
 
239
214
  # generic default encoding is the best we can do
240
- if possible_values.empty?
241
- return MediaTypeIdentifier.load("application/#{handler_name}")
242
- end
215
+ return MediaTypeIdentifier.load("application/#{handler_name}") if possible_values.empty?
243
216
 
244
217
  # if any defined value match the preferred handler_name, return it
245
218
  possible_values.each do |ct|
@@ -253,38 +226,33 @@ module Praxis
253
226
  # and return that one if it already corresponds to a registered handler
254
227
  # otherwise, add the encoding
255
228
  if Praxis::Application.instance.handlers.include?(pick.handler_name)
256
- return pick
229
+ pick
257
230
  else
258
- return pick + handler_name
231
+ pick + handler_name
259
232
  end
260
-
261
233
  end
262
234
 
263
235
  def dump(default_format: nil, **opts)
264
- original_content_type = self.content_type
236
+ original_content_type = content_type
265
237
 
266
- body = self.payload_attribute.dump(self.payload, **opts)
238
+ body = payload_attribute.dump(payload, **opts)
267
239
 
268
240
  body_string = case body
269
- when Hash, Array
270
- if default_format
271
- self.content_type = derive_content_type(default_format)
272
- end
241
+ when Hash, Array
242
+ self.content_type = derive_content_type(default_format) if default_format
273
243
 
274
- self.handler.generate(body)
275
- else
276
- body
277
- end
244
+ handler.generate(body)
245
+ else
246
+ body
247
+ end
278
248
 
279
- header_string = self.headers.collect do |name, value|
249
+ header_string = headers.collect do |name, value|
280
250
  "#{name}: #{value}"
281
251
  end.join("\r\n")
282
252
 
283
-
284
253
  "#{header_string}\r\n\r\n#{body_string}"
285
254
  ensure
286
255
  self.content_type = original_content_type
287
256
  end
288
-
289
257
  end
290
258
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/notifications'
2
4
 
3
5
  require 'singleton'
4
6
 
5
7
  module Praxis
6
-
7
8
  module Notifications
8
9
  include Praxis::PluginConcern
9
10
 
@@ -13,7 +14,6 @@ module Praxis
13
14
  def config_key
14
15
  :notifications # 'praxis.notifications'
15
16
  end
16
-
17
17
  end
18
18
 
19
19
  def self.publish(name, *args)
@@ -35,7 +35,5 @@ module Praxis
35
35
  def self.unsubscribe(subscriber_or_name)
36
36
  ActiveSupport::Notifications.unsubscribe(subscriber_or_name)
37
37
  end
38
-
39
-
40
38
  end
41
39
  end
data/lib/praxis/plugin.rb CHANGED
@@ -1,44 +1,37 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  # one instance is created per use.
4
5
  class Plugin
5
-
6
- attr_accessor :application
7
- attr_accessor :block
8
- attr_accessor :config
9
- attr_accessor :config_attribute
6
+ attr_accessor :application, :block, :config, :config_attribute
10
7
 
11
8
  def options
12
9
  @options ||= {}
13
10
  end
14
11
 
15
- def config_key
16
- end
12
+ def config_key; end
17
13
 
18
- def prepare_config!(node)
19
- end
14
+ def prepare_config!(node); end
20
15
 
21
16
  def load_config!
22
- return unless options.has_key?(:config_file)
17
+ return unless options.key?(:config_file)
23
18
  return {} unless (application.root + options[:config_file]).exist?
24
19
 
25
20
  YAML.load_file(application.root + options[:config_file])
26
21
  end
27
22
 
28
- def setup!
29
- end
23
+ def setup!; end
30
24
 
31
25
  def register_doc_browser_plugin(path)
32
26
  application.doc_browser_plugin_paths << File.expand_path(path)
33
27
  end
34
28
 
35
- def after(stage,&block)
36
- application.bootloader.after(stage,&block)
29
+ def after(stage, &block)
30
+ application.bootloader.after(stage, &block)
37
31
  end
38
32
 
39
- def before(stage,&block)
40
- application.bootloader.before(stage,&block)
33
+ def before(stage, &block)
34
+ application.bootloader.before(stage, &block)
41
35
  end
42
-
43
36
  end
44
37
  end
@@ -1,5 +1,6 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  module PluginConcern
4
5
  extend ::ActiveSupport::Concern
5
6
 
@@ -8,35 +9,31 @@ module Praxis
8
9
  end
9
10
 
10
11
  module ClassMethods
11
- PLUGIN_CLASSES = [
12
- :Request,
13
- :Controller,
14
- :EndpointDefinition,
15
- :ActionDefinition,
16
- :Response,
17
- :ApiGeneralInfo
18
- ]
12
+ PLUGIN_CLASSES = %i[
13
+ Request
14
+ Controller
15
+ EndpointDefinition
16
+ ActionDefinition
17
+ Response
18
+ ApiGeneralInfo
19
+ ].freeze
19
20
 
20
21
  def setup!
21
22
  return if @setup
22
23
 
23
24
  PLUGIN_CLASSES.each do |name|
24
- if self.constants.include?(name)
25
- inject!(name)
26
- end
25
+ inject!(name) if constants.include?(name)
27
26
  end
28
27
 
29
28
  @setup = true
30
29
  end
31
30
 
32
31
  def inject!(name)
33
- plugin = self.const_get(name)
32
+ plugin = const_get(name)
34
33
  praxis = Praxis.const_get(name)
35
34
 
36
35
  praxis.include(plugin)
37
36
  end
38
-
39
37
  end
40
38
  end
41
-
42
39
  end