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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class SetHeader
2
4
  def initialize(app, header, value)
3
5
  @app = app
@@ -8,12 +10,25 @@ class SetHeader
8
10
  def call(env)
9
11
  status, headers, body = @app.call(env)
10
12
  headers[@header] = @value
11
- [status, headers,body]
13
+ [status, headers, body]
12
14
  end
13
15
  end
14
16
 
15
- Praxis::Application.configure do |application|
17
+ class LowBudgetMutex
18
+ include Singleton
19
+
20
+ attr_reader :after_app_controllers
16
21
 
22
+ def initialize
23
+ @after_app_controllers = nil
24
+ end
25
+
26
+ def after_app_controllers_called
27
+ @after_app_controllers = :worked
28
+ end
29
+ end
30
+
31
+ Praxis::Application.configure do |application|
17
32
  application.middleware SetHeader, 'Spec-Middleware', 'used'
18
33
 
19
34
  application.bootloader.use SimpleAuthenticationPlugin, config_file: 'config/authentication.yml'
@@ -26,13 +41,12 @@ Praxis::Application.configure do |application|
26
41
 
27
42
  # Silly callback code pieces to test that the deferred callbacks work even for sub-stages
28
43
  application.bootloader.after :app, :controllers do
29
- $after_app_controllers = :worked
44
+ LowBudgetMutex.instance.after_app_controllers_called
30
45
  end
31
46
  application.bootloader.after :app do
32
- raise "After sub-stage hooks not working!" unless $after_app_controllers == :worked
47
+ raise 'After sub-stage hooks not working!' unless LowBudgetMutex.instance.after_app_controllers == :worked
33
48
  end
34
49
 
35
-
36
50
  application.layout do
37
51
  layout do
38
52
  map :initializers, 'config/initializers/**/*'
@@ -49,5 +63,4 @@ Praxis::Application.configure do |application|
49
63
  end
50
64
  end
51
65
  end
52
-
53
66
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pp'
2
4
  require 'json'
3
5
 
@@ -5,13 +7,12 @@ require 'bundler/setup'
5
7
 
6
8
  require 'pry'
7
9
 
8
- $:.unshift File.expand_path('lib', __dir__)
10
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
9
11
 
10
12
  require 'praxis'
11
13
 
12
-
13
14
  application = Praxis::Application.instance
14
15
 
15
16
  application.setup
16
17
 
17
- run application
18
+ run application
@@ -1,5 +1,6 @@
1
- Praxis::ApiDefinition.define do
1
+ # frozen_string_literal: true
2
2
 
3
+ Praxis::ApiDefinition.define do
3
4
  response_template :other_response do |media_type:|
4
5
  status 200
5
6
  media_type media_type
@@ -10,33 +11,30 @@ Praxis::ApiDefinition.define do
10
11
  media_type 'multipart/form-data'
11
12
  end
12
13
 
13
-
14
-
15
14
  trait :authenticated do
16
15
  headers do
17
- key "Authorization", String, required: false
16
+ key 'Authorization', String, required: false
18
17
  end
19
18
  end
20
19
 
21
20
  info do # applies to all API infos
22
- name "Spec App"
23
- title "A simple App to do some simple integration testing"
24
- description "This example API should really be replaced by a set of more full-fledged example apps in the future"
21
+ name 'Spec App'
22
+ title 'A simple App to do some simple integration testing'
23
+ description 'This example API should really be replaced by a set of more full-fledged example apps in the future'
25
24
 
26
- base_path "/api"
25
+ base_path '/api'
27
26
  produces 'json'
28
- #version_with :path
29
- #base_path "/v:api_version"
27
+ # version_with :path
28
+ # base_path "/v:api_version"
30
29
 
31
30
  # Custom attributes (for OpenApi, for example)
32
- termsOfService "http://example.com/tos"
31
+ termsOfService 'http://example.com/tos'
33
32
  contact name: 'Joe', email: 'joe@email.com'
34
- license name: "Apache 2.0",
35
- url: "https://www.apache.org/licenses/LICENSE-2.0.html"
33
+ license name: 'Apache 2.0',
34
+ url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
36
35
  end
37
36
 
38
37
  info '1.0' do # Applies to 1.0 version (and inherits everything else form the global one)
39
- description "A simple 1.0 App"
38
+ description 'A simple 1.0 App'
40
39
  end
41
-
42
40
  end
@@ -1,19 +1,19 @@
1
- class Instance < Praxis::MediaType
1
+ # frozen_string_literal: true
2
2
 
3
+ class Instance < Praxis::MediaType
3
4
  identifier 'application/vnd.acme.instance'
4
5
 
5
6
  attributes do
6
7
  attribute :id, Integer
7
- attribute :name, String,
8
- example: /[:first_name:]/,
9
- regexp: /^\w+$/
8
+ attribute :name, String,
9
+ example: /[:first_name:]/,
10
+ regexp: /^\w+$/
10
11
 
11
12
  attribute :href, String
12
13
 
13
- attribute :root_volume, Volume
14
+ attribute :root_volume, Volume, null: true
14
15
 
15
16
  attribute :volumes, Volume::Collection
16
-
17
17
  end
18
18
 
19
19
  default_fieldset do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Volume < Praxis::MediaType
2
4
  identifier 'application/vnd.acme.volume'
3
5
 
@@ -23,5 +25,4 @@ class Volume < Praxis::MediaType
23
25
 
24
26
  identifier 'application/vnd.acme.volumes'
25
27
  end
26
-
27
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class VolumeSnapshot < Praxis::MediaType
2
4
  identifier 'application/json'
3
5
 
@@ -26,5 +28,4 @@ class VolumeSnapshot < Praxis::MediaType
26
28
  attribute :href
27
29
  end
28
30
  end
29
-
30
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ApiResources
2
4
  class Instances
3
5
  include Praxis::EndpointDefinition
@@ -28,8 +30,8 @@ module ApiResources
28
30
 
29
31
  headers do
30
32
  # BOTH ARE EQUIVALENT
31
- #key "FOO", String, required: true
32
- header "FOO", /bar/
33
+ # key "FOO", String, required: true
34
+ header 'FOO', /bar/
33
35
  key 'Account-Id', Integer, required: false, min: 0
34
36
  end
35
37
 
@@ -37,7 +39,6 @@ module ApiResources
37
39
  attribute :response_content_type, String, default: 'application/vnd.acme.instance;type=collection'
38
40
  end
39
41
 
40
-
41
42
  response :ok, Praxis::Collection.of(Instance)
42
43
  end
43
44
 
@@ -57,13 +58,12 @@ module ApiResources
57
58
  attribute :create_identity_map, Attributor::Boolean, default: false
58
59
  end
59
60
 
60
- payload required: false do
61
+ payload required: false, null: true do
61
62
  attribute :something, String
62
- attribute :optional, String, default: "not given"
63
+ attribute :optional, String, default: 'not given'
63
64
  end
64
65
  end
65
66
 
66
-
67
67
  action :bulk_create do
68
68
  routing do
69
69
  post ''
@@ -77,11 +77,11 @@ module ApiResources
77
77
  response :multipart_ok, Praxis::Types::MultipartArray do
78
78
  name_type Integer
79
79
 
80
- part /\d+/ do
80
+ part(/\d+/) do
81
81
  header 'Status', '201'
82
82
  header 'Content-Type', /^#{Instance.identifier}/
83
83
  header 'Location', Attributor::URI,
84
- example: proc { |part| ApiResources::Instances.to_href(cloud_id: part[:value].cloud_id, id: part[:value].id) }
84
+ example: proc { |part| ApiResources::Instances.to_href(cloud_id: part[:value].cloud_id, id: part[:value].id) }
85
85
 
86
86
  payload Hash do
87
87
  attribute :key, Integer, required: true
@@ -90,16 +90,14 @@ module ApiResources
90
90
  attribute :name
91
91
  end
92
92
  end
93
-
94
93
  end
95
94
  end
96
95
 
97
-
98
96
  # Using a hash param for parts
99
- #response :multipart_ok, parts: {
97
+ # response :multipart_ok, parts: {
100
98
  # like: :created,
101
99
  # location: /\/instances\//
102
- #}
100
+ # }
103
101
 
104
102
  # Using a block for parts to defin a sub-request
105
103
  # sub_request = proc do
@@ -140,7 +138,7 @@ module ApiResources
140
138
  attribute :id
141
139
  end
142
140
 
143
- payload required: false do
141
+ payload required: false, null: true do
144
142
  attribute :when, DateTime
145
143
  end
146
144
 
@@ -157,7 +155,6 @@ module ApiResources
157
155
  attribute :id
158
156
  end
159
157
 
160
-
161
158
  response :ok, media_type: 'application/json'
162
159
  end
163
160
 
@@ -178,7 +175,6 @@ module ApiResources
178
175
  response :ok
179
176
  end
180
177
 
181
-
182
178
  action :exceptional do
183
179
  routing do
184
180
  prefix '//clouds/:cloud_id/otherinstances'
@@ -229,7 +225,5 @@ module ApiResources
229
225
  # key 'destination', String, required: true
230
226
  # match /file-.+/, FileUpload
231
227
  # end
232
-
233
228
  end
234
-
235
229
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'volumes'
2
4
 
3
5
  module ApiResources
@@ -7,8 +9,8 @@ module ApiResources
7
9
  media_type VolumeSnapshot
8
10
 
9
11
  version '1.0'
10
-
11
- parent Volumes, :id => :volume_id
12
+
13
+ parent Volumes, id: :volume_id
12
14
  prefix '/snapshots'
13
15
 
14
16
  action :index do
@@ -21,7 +23,6 @@ module ApiResources
21
23
  params do
22
24
  attribute :volume_id, Integer, description: 'id of parent volume'
23
25
  end
24
-
25
26
  end
26
27
 
27
28
  action :show do
@@ -32,8 +33,6 @@ module ApiResources
32
33
  params do
33
34
  attribute :id
34
35
  end
35
-
36
36
  end
37
-
38
37
  end
39
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ApiResources
2
4
  class Volumes
3
5
  include Praxis::EndpointDefinition
@@ -7,10 +9,10 @@ module ApiResources
7
9
  prefix '/clouds/:cloud_id/volumes'
8
10
 
9
11
  trait :authenticated
10
-
12
+
11
13
  action_defaults do
12
14
  params do
13
- attribute :cloud_id, Integer, description: "id of the cloud"
15
+ attribute :cloud_id, Integer, description: 'id of the cloud'
14
16
  end
15
17
  end
16
18
 
@@ -35,9 +37,6 @@ module ApiResources
35
37
  attribute :junk, String, default: ''
36
38
  attribute :some_date, DateTime, default: DateTime.parse('2012-12-21')
37
39
  end
38
-
39
40
  end
40
-
41
41
  end
42
-
43
42
  end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'coveralls'
2
4
  Coveralls.wear!
3
5
 
4
- $:.unshift File.expand_path(__dir__)
5
- $:.unshift File.expand_path('../lib',__dir__)
6
- $:.unshift File.expand_path('support',__dir__)
6
+ $LOAD_PATH.unshift File.expand_path(__dir__)
7
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
8
+ $LOAD_PATH.unshift File.expand_path('support', __dir__)
7
9
 
8
10
  require 'bundler'
9
11
  Bundler.setup :default, :test
@@ -13,9 +15,6 @@ RSpec.configure do |config|
13
15
  config.run_all_when_everything_filtered = true
14
16
  end
15
17
 
16
- require 'simplecov'
17
- SimpleCov.start 'praxis'
18
-
19
18
  require 'pry'
20
19
  require 'pry-byebug'
21
20
 
@@ -26,17 +25,17 @@ require 'rack/test'
26
25
  require 'rspec/its'
27
26
  require 'rspec/collection_matchers'
28
27
 
29
- Dir["#{File.dirname(__FILE__)}/../lib/praxis/plugins/*.rb"].each do |file|
28
+ Dir["#{File.dirname(__FILE__)}/../lib/praxis/plugins/*.rb"].sort.each do |file|
30
29
  require file
31
30
  end
32
31
 
33
-
34
- Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
32
+ Dir["#{File.dirname(__FILE__)}/support/*.rb"].sort.each do |file|
35
33
  require file
36
34
  end
37
35
 
38
36
  def suppress_output
39
- original_stdout, original_stderr = $stdout.clone, $stderr.clone
37
+ original_stdout = $stdout.clone
38
+ original_stderr = $stderr.clone
40
39
  $stderr.reopen File.new('/dev/null', 'w')
41
40
  $stdout.reopen File.new('/dev/null', 'w')
42
41
  yield
@@ -51,12 +50,12 @@ RSpec.configure do |config|
51
50
  config.before(:suite) do
52
51
  Praxis::Mapper::Resource.finalize!
53
52
  Praxis::Blueprint.caching_enabled = true
54
- Praxis::Application.instance.setup(root:'spec/spec_app')
53
+ Praxis::Application.instance.setup(root: 'spec/spec_app')
55
54
  end
56
55
 
57
56
  config.before(:each) do
58
57
  Praxis::Blueprint.cache = Hash.new do |hash, key|
59
- hash[key] = Hash.new
58
+ hash[key] = {}
60
59
  end
61
60
  end
62
61
 
@@ -65,4 +64,3 @@ RSpec.configure do |config|
65
64
  Praxis::Application.instance.logger.level = 2 # warn
66
65
  end
67
66
  end
68
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copied verbatim from: https://github.com/amogil/rspec-deep-ignore-order-matcher
2
4
 
3
5
  RSpec::Matchers.define :be_deep_equal do |expected|
@@ -18,6 +20,7 @@ RSpec::Matchers.define :be_deep_equal do |expected|
18
20
  def match?(actual, expected)
19
21
  return arrays_match?(actual, expected) if expected.is_a?(Array) && actual.is_a?(Array)
20
22
  return hashes_match?(actual, expected) if expected.is_a?(Hash) && actual.is_a?(Hash)
23
+
21
24
  expected == actual
22
25
  end
23
26
 
@@ -26,6 +29,7 @@ RSpec::Matchers.define :be_deep_equal do |expected|
26
29
  actual.each do |a|
27
30
  index = exp.find_index { |e| match? a, e }
28
31
  return false if index.nil?
32
+
29
33
  exp.delete_at(index)
30
34
  end
31
35
  exp.empty?
@@ -33,6 +37,7 @@ RSpec::Matchers.define :be_deep_equal do |expected|
33
37
 
34
38
  def hashes_match?(actual, expected)
35
39
  return false unless actual.keys.sort == expected.keys.sort
40
+
36
41
  actual.each { |key, value| return false unless match? value, expected[key] }
37
42
  true
38
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
 
3
5
  module AuthorizationPlugin
@@ -11,7 +13,8 @@ module AuthorizationPlugin
11
13
  end
12
14
 
13
15
  def initialize
14
- @options = {config_file: 'config/authorization.yml'}
16
+ super
17
+ @options = { config_file: 'config/authorization.yml' }
15
18
  end
16
19
 
17
20
  def prepare_config!(node)
@@ -30,7 +33,6 @@ module AuthorizationPlugin
30
33
 
31
34
  (request.action.required_abilities - abilities).empty?
32
35
  end
33
-
34
36
  end
35
37
 
36
38
  module Request
@@ -43,33 +45,27 @@ module AuthorizationPlugin
43
45
  extend ActiveSupport::Concern
44
46
 
45
47
  included do
46
-
47
48
  before :action do |controller|
48
49
  verify_abilities(controller.request)
49
50
  end
50
-
51
51
  end
52
52
 
53
-
54
53
  module ClassMethods
55
54
  def verify_abilities(request)
56
55
  return true unless request.action.required_abilities
57
56
 
58
57
  authorized = AuthorizationPlugin::Plugin.instance.authorized?(request)
59
58
 
60
- unless authorized
61
- return Praxis::Responses::Forbidden.new
62
- end
59
+ return Praxis::Responses::Forbidden.new unless authorized
63
60
  end
64
61
  end
65
62
 
66
63
  def subject
67
- #p [self, :subject]
64
+ # p [self, :subject]
68
65
  end
69
66
  end
70
67
 
71
68
  module EndpointDefinition
72
-
73
69
  end
74
70
 
75
71
  module ActionDefinition
@@ -77,6 +73,7 @@ module AuthorizationPlugin
77
73
 
78
74
  included do
79
75
  attr_accessor :required_abilities
76
+
80
77
  decorate_docs do |action, docs|
81
78
  docs[:required_abilities] = action.required_abilities
82
79
  end
@@ -90,6 +87,4 @@ module AuthorizationPlugin
90
87
  requires_authentication true
91
88
  end
92
89
  end
93
-
94
-
95
90
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class PersonBlueprint < Praxis::Blueprint
3
4
  attributes do
4
5
  attribute :name, String, example: /[:first_name:]/
@@ -9,8 +10,8 @@ class PersonBlueprint < Praxis::Blueprint
9
10
  attribute :full_name, FullName
10
11
  attribute :aliases, Attributor::Collection.of(FullName)
11
12
 
12
- attribute :address, AddressBlueprint, example: proc { |person, context| AddressBlueprint.example(context, resident: person) }
13
- attribute :work_address, AddressBlueprint
13
+ attribute :address, AddressBlueprint, null: true, example: proc { |person, context| AddressBlueprint.example(context, resident: person) }
14
+ attribute :work_address, AddressBlueprint, null: true
14
15
 
15
16
  attribute :prior_addresses, Attributor::Collection.of(AddressBlueprint)
16
17
  attribute :parents do
@@ -21,7 +22,7 @@ class PersonBlueprint < Praxis::Blueprint
21
22
  attribute :tags, Attributor::Collection.of(String)
22
23
  attribute :href, String
23
24
  attribute :alive, Attributor::Boolean, default: true
24
- attribute :myself, PersonBlueprint
25
+ attribute :myself, PersonBlueprint, null: true
25
26
  attribute :friends, Attributor::Collection.of(PersonBlueprint)
26
27
  attribute :metadata, Attributor::Hash
27
28
  end
@@ -44,7 +45,7 @@ class AddressBlueprint < Praxis::Blueprint
44
45
  attribute :id, Integer
45
46
  attribute :name, String
46
47
  attribute :street, String
47
- attribute :state, String, values: %w(OR CA)
48
+ attribute :state, String, values: %w[OR CA]
48
49
 
49
50
  attribute :resident, PersonBlueprint, example: proc { |address, context| PersonBlueprint.example(context, address: address) }
50
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
 
3
5
  class Authenticator
@@ -7,7 +9,7 @@ class Authenticator
7
9
  Class
8
10
  end
9
11
 
10
- def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
12
+ def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
11
13
  case value
12
14
  when Hash
13
15
  type = value.delete(:type) || value.delete('type')
@@ -15,43 +17,32 @@ class Authenticator
15
17
  when self
16
18
  value
17
19
  else
18
- raise "#{self.naem} can not load values of type #{value.class}"
20
+ raise "#{naem} can not load values of type #{value.class}"
19
21
  end
20
22
  end
21
23
 
22
- def self.validate(*args)
23
- end
24
+ def self.validate(*args); end
24
25
 
25
- def self.describe
26
- end
27
-
28
- def initialize(**options)
29
- end
26
+ def self.describe; end
30
27
 
31
- def authenticate(request)
32
- raise "sublcass must implement authenticate"
28
+ def authenticate(_request)
29
+ raise 'sublcass must implement authenticate'
33
30
  end
34
-
35
31
  end
36
32
 
37
-
38
33
  class GlobalSessionAuthenticator < Authenticator
39
-
40
- def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
41
- self.new(**value)
42
- end
43
-
44
- def self.describe
34
+ def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
35
+ new(**value)
45
36
  end
46
37
 
47
- def initialize(**options)
48
- end
38
+ def self.describe; end
49
39
 
50
40
  def authenticate(request)
51
- body = {name: 'Unauthorized'}
41
+ body = { name: 'Unauthorized' }
52
42
 
53
43
  if (session = request.env['global_session'])
54
44
  return true if session.valid?
45
+
55
46
  body[:message] = 'Invalid session.'
56
47
  else
57
48
  body[:message] = 'Missing session.'
@@ -59,10 +50,8 @@ class GlobalSessionAuthenticator < Authenticator
59
50
 
60
51
  Praxis::Responses::Unauthorized.new(body: body)
61
52
  end
62
-
63
53
  end
64
54
 
65
-
66
55
  module ComplexAuthenticationPlugin
67
56
  include Praxis::PluginConcern
68
57
 
@@ -70,24 +59,23 @@ module ComplexAuthenticationPlugin
70
59
  include Singleton
71
60
 
72
61
  def initialize
73
- @options = {config_file: 'config/authentication.yml'}
62
+ @options = { config_file: 'config/authentication.yml' }
63
+ super
74
64
  end
75
65
 
76
66
  def config_key
77
67
  :authentication
78
68
  end
79
69
 
80
- def prepare_config!(node)
70
+ def prepare_config!(_node)
81
71
  self.config_attribute = Attributor::Attribute.new(Authenticator, required: true)
82
72
  end
83
73
 
84
74
  def self.authenticate(request)
85
75
  instance.config.authenticate(request)
86
76
  end
87
-
88
77
  end
89
78
 
90
-
91
79
  module Request
92
80
  end
93
81
 
@@ -96,15 +84,11 @@ module ComplexAuthenticationPlugin
96
84
 
97
85
  included do
98
86
  before :action do |controller|
99
- if controller.request.action.authentication_required
100
- Plugin.authenticate(controller.request)
101
- end
87
+ Plugin.authenticate(controller.request) if controller.request.action.authentication_required
102
88
  end
103
89
  end
104
-
105
90
  end
106
91
 
107
-
108
92
  module ActionDefinition
109
93
  extend ActiveSupport::Concern
110
94
 
@@ -121,6 +105,5 @@ module ComplexAuthenticationPlugin
121
105
  def authentication_required
122
106
  @authentication_required ||= false
123
107
  end
124
-
125
108
  end
126
109
  end