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.
- checksums.yaml +4 -4
- data/.rubocop.yml +54 -0
- data/.simplecov +3 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +2 -79
- data/Gemfile +5 -1
- data/Guardfile +6 -4
- data/LICENSE +0 -2
- data/MAINTAINERS.md +1 -0
- data/README.md +15 -22
- data/Rakefile +4 -2
- data/bin/praxis +55 -58
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
- data/lib/praxis/action_definition.rb +65 -95
- data/lib/praxis/api_definition.rb +21 -29
- data/lib/praxis/api_general_info.rb +55 -66
- data/lib/praxis/application.rb +15 -32
- data/lib/praxis/blueprint.rb +80 -73
- data/lib/praxis/bootloader.rb +24 -33
- data/lib/praxis/bootloader_stages/environment.rb +5 -10
- data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
- data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
- data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
- data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
- data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
- data/lib/praxis/bootloader_stages/routing.rb +5 -8
- data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
- data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
- data/lib/praxis/callbacks.rb +12 -11
- data/lib/praxis/collection.rb +11 -14
- data/lib/praxis/config.rb +17 -28
- data/lib/praxis/config_hash.rb +2 -1
- data/lib/praxis/controller.rb +7 -6
- data/lib/praxis/dispatcher.rb +34 -42
- data/lib/praxis/docs/open_api/info_object.rb +11 -8
- data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
- data/lib/praxis/docs/open_api/operation_object.rb +7 -4
- data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
- data/lib/praxis/docs/open_api/paths_object.rb +11 -9
- data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
- data/lib/praxis/docs/open_api/response_object.rb +24 -18
- data/lib/praxis/docs/open_api/responses_object.rb +3 -1
- data/lib/praxis/docs/open_api/schema_object.rb +61 -29
- data/lib/praxis/docs/open_api/server_object.rb +5 -2
- data/lib/praxis/docs/open_api/tag_object.rb +9 -6
- data/lib/praxis/docs/open_api_generator.rb +114 -150
- data/lib/praxis/endpoint_definition.rb +60 -77
- data/lib/praxis/error_handler.rb +2 -2
- data/lib/praxis/exception.rb +2 -0
- data/lib/praxis/exceptions/config.rb +3 -1
- data/lib/praxis/exceptions/config_load.rb +2 -0
- data/lib/praxis/exceptions/config_validation.rb +3 -1
- data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
- data/lib/praxis/exceptions/invalid_response.rb +3 -1
- data/lib/praxis/exceptions/invalid_trait.rb +3 -1
- data/lib/praxis/exceptions/stage_not_found.rb +3 -1
- data/lib/praxis/exceptions/validation.rb +4 -3
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
- data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
- data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
- data/lib/praxis/extensions/attribute_filtering.rb +3 -1
- data/lib/praxis/extensions/field_expansion.rb +6 -4
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
- data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
- data/lib/praxis/extensions/field_selection.rb +3 -1
- data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
- data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
- data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
- data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
- data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
- data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
- data/lib/praxis/extensions/pagination.rb +10 -15
- data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
- data/lib/praxis/extensions/rails_compat.rb +2 -0
- data/lib/praxis/extensions/rendering.rb +12 -12
- data/lib/praxis/field_expander.rb +8 -9
- data/lib/praxis/file_group.rb +8 -12
- data/lib/praxis/finalizable.rb +1 -0
- data/lib/praxis/handlers/json.rb +5 -2
- data/lib/praxis/handlers/plain.rb +2 -1
- data/lib/praxis/handlers/www_form.rb +6 -3
- data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
- data/lib/praxis/mapper/active_model_compat.rb +13 -10
- data/lib/praxis/mapper/resource.rb +196 -181
- data/lib/praxis/mapper/selector_generator.rb +106 -112
- data/lib/praxis/mapper/sequel_compat.rb +70 -67
- data/lib/praxis/media_type.rb +2 -2
- data/lib/praxis/media_type_identifier.rb +26 -22
- data/lib/praxis/middleware_app.rb +18 -15
- data/lib/praxis/multipart/parser.rb +46 -51
- data/lib/praxis/multipart/part.rb +78 -110
- data/lib/praxis/notifications.rb +2 -4
- data/lib/praxis/plugin.rb +11 -18
- data/lib/praxis/plugin_concern.rb +12 -15
- data/lib/praxis/plugins/mapper_plugin.rb +15 -13
- data/lib/praxis/plugins/pagination_plugin.rb +8 -6
- data/lib/praxis/plugins/rails_plugin.rb +33 -28
- data/lib/praxis/renderer.rb +11 -15
- data/lib/praxis/request.rb +48 -44
- data/lib/praxis/request_stages/action.rb +4 -6
- data/lib/praxis/request_stages/load_request.rb +2 -4
- data/lib/praxis/request_stages/request_stage.rb +19 -23
- data/lib/praxis/request_stages/response.rb +4 -6
- data/lib/praxis/request_stages/validate.rb +3 -5
- data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
- data/lib/praxis/request_stages/validate_payload.rb +25 -28
- data/lib/praxis/request_superclassing.rb +3 -3
- data/lib/praxis/resource_definition.rb +1 -0
- data/lib/praxis/response.rb +24 -26
- data/lib/praxis/response_definition.rb +77 -122
- data/lib/praxis/response_template.rb +11 -15
- data/lib/praxis/responses/http.rb +23 -44
- data/lib/praxis/responses/internal_server_error.rb +18 -21
- data/lib/praxis/responses/multipart_ok.rb +4 -9
- data/lib/praxis/responses/validation_error.rb +8 -15
- data/lib/praxis/route.rb +8 -10
- data/lib/praxis/router/rack.rb +13 -7
- data/lib/praxis/router/simple.rb +10 -5
- data/lib/praxis/router.rb +27 -34
- data/lib/praxis/routing_config.rb +52 -29
- data/lib/praxis/simple_media_type.rb +5 -8
- data/lib/praxis/stage.rb +17 -25
- data/lib/praxis/tasks/api_docs.rb +17 -16
- data/lib/praxis/tasks/console.rb +3 -1
- data/lib/praxis/tasks/environment.rb +2 -0
- data/lib/praxis/tasks/routes.rb +26 -24
- data/lib/praxis/tasks.rb +3 -1
- data/lib/praxis/trait.rb +37 -46
- data/lib/praxis/types/fuzzy_hash.rb +13 -14
- data/lib/praxis/types/media_type_common.rb +11 -10
- data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
- data/lib/praxis/types/multipart_array.rb +100 -115
- data/lib/praxis/validation_handler.rb +5 -3
- data/lib/praxis/version.rb +3 -1
- data/lib/praxis.rb +4 -5
- data/praxis.gemspec +22 -21
- data/spec/functional_spec.rb +44 -56
- data/spec/praxis/action_definition_spec.rb +39 -48
- data/spec/praxis/api_definition_spec.rb +45 -47
- data/spec/praxis/api_general_info_spec.rb +28 -29
- data/spec/praxis/application_spec.rb +18 -14
- data/spec/praxis/blueprint_spec.rb +33 -34
- data/spec/praxis/bootloader_spec.rb +32 -30
- data/spec/praxis/callbacks_spec.rb +37 -37
- data/spec/praxis/collection_spec.rb +18 -25
- data/spec/praxis/config_hash_spec.rb +5 -4
- data/spec/praxis/config_spec.rb +27 -26
- data/spec/praxis/controller_spec.rb +8 -9
- data/spec/praxis/endpoint_definition_spec.rb +25 -32
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
- data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
- data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
- data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
- data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
- data/spec/praxis/extensions/rendering_spec.rb +9 -9
- data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
- data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
- data/spec/praxis/field_expander_spec.rb +6 -5
- data/spec/praxis/file_group_spec.rb +3 -1
- data/spec/praxis/handlers/json_spec.rb +6 -5
- data/spec/praxis/mapper/resource_spec.rb +39 -29
- data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
- data/spec/praxis/media_type_identifier_spec.rb +13 -10
- data/spec/praxis/media_type_spec.rb +12 -12
- data/spec/praxis/middleware_app_spec.rb +23 -22
- data/spec/praxis/multipart/parser_spec.rb +7 -9
- data/spec/praxis/notifications_spec.rb +4 -4
- data/spec/praxis/plugin_concern_spec.rb +5 -6
- data/spec/praxis/renderer_spec.rb +10 -9
- data/spec/praxis/request_spec.rb +38 -41
- data/spec/praxis/request_stages/action_spec.rb +14 -15
- data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
- data/spec/praxis/request_stages/validate_spec.rb +3 -1
- data/spec/praxis/response_definition_spec.rb +79 -92
- data/spec/praxis/response_spec.rb +35 -40
- data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
- data/spec/praxis/responses/validation_error_spec.rb +17 -18
- data/spec/praxis/route_spec.rb +4 -7
- data/spec/praxis/router_spec.rb +69 -79
- data/spec/praxis/routing_config_spec.rb +15 -14
- data/spec/praxis/stage_spec.rb +56 -53
- data/spec/praxis/trait_spec.rb +17 -17
- data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
- data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
- data/spec/praxis/types/multipart_array_spec.rb +33 -48
- data/spec/spec_app/app/concerns/authenticated.rb +5 -5
- data/spec/spec_app/app/concerns/basic_api.rb +3 -1
- data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
- data/spec/spec_app/app/controllers/base_class.rb +6 -5
- data/spec/spec_app/app/controllers/instances.rb +31 -34
- data/spec/spec_app/app/controllers/volumes.rb +6 -6
- data/spec/spec_app/app/responses/multipart.rb +1 -2
- data/spec/spec_app/app/responses/other_response.rb +2 -2
- data/spec/spec_app/config/environment.rb +19 -6
- data/spec/spec_app/config.ru +4 -3
- data/spec/spec_app/design/api.rb +13 -15
- data/spec/spec_app/design/media_types/instance.rb +6 -6
- data/spec/spec_app/design/media_types/volume.rb +2 -1
- data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
- data/spec/spec_app/design/resources/instances.rb +11 -17
- data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
- data/spec/spec_app/design/resources/volumes.rb +4 -5
- data/spec/spec_helper.rb +11 -13
- data/spec/support/be_deep_equal_matcher.rb +5 -0
- data/spec/support/spec_authorization_plugin.rb +7 -12
- data/spec/support/spec_blueprints.rb +5 -4
- data/spec/support/spec_complex_authentication_plugin.rb +17 -34
- data/spec/support/spec_endpoint_definitions.rb +2 -3
- data/spec/support/spec_media_types.rb +28 -35
- data/spec/support/spec_resources.rb +22 -16
- data/spec/support/spec_simple_authentication_plugin.rb +5 -9
- data/tasks/loader.thor +4 -2
- data/tasks/thor/app.rb +7 -5
- data/tasks/thor/example.rb +23 -22
- data/tasks/thor/model.rb +7 -7
- data/tasks/thor/scaffold.rb +23 -23
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
- data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
- metadata +72 -84
- data/MAINTAINERS +0 -2
- data/TODO.md +0 -25
- data/spec/praxis/api_resource_spec.rb +0 -0
- data/spec/praxis/dispatcher_spec.rb +0 -0
- data/spec/spec_app/app/responses/bulk_response.rb +0 -0
|
@@ -1,28 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
class RoutingConfig
|
|
5
|
+
attr_reader :route, :version, :base
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
attr_reader :version
|
|
6
|
-
attr_reader :base
|
|
7
|
-
|
|
8
|
-
def initialize(version:'n/a'.freeze, base: '', prefix:[], &block)
|
|
7
|
+
def initialize(version: 'n/a', base: '', prefix: [], &block)
|
|
9
8
|
@version = version
|
|
10
9
|
@base = base
|
|
11
10
|
@prefix_segments = Array(prefix)
|
|
12
11
|
|
|
13
12
|
@route = nil
|
|
14
13
|
|
|
15
|
-
if block_given?
|
|
16
|
-
instance_eval(&block)
|
|
17
|
-
end
|
|
14
|
+
instance_eval(&block) if block_given?
|
|
18
15
|
end
|
|
19
16
|
|
|
20
17
|
def clear!
|
|
21
18
|
@prefix_segments = []
|
|
22
19
|
end
|
|
23
20
|
|
|
24
|
-
def prefix(prefix=nil)
|
|
25
|
-
return @prefix_segments.join.gsub('//','/') if prefix.nil?
|
|
21
|
+
def prefix(prefix = nil)
|
|
22
|
+
return @prefix_segments.join.gsub('//', '/') if prefix.nil?
|
|
26
23
|
|
|
27
24
|
case prefix
|
|
28
25
|
when ''
|
|
@@ -34,28 +31,54 @@ module Praxis
|
|
|
34
31
|
end
|
|
35
32
|
end
|
|
36
33
|
|
|
37
|
-
def options(path, opts={})
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def post(path, opts={}) add_route 'POST', path, opts end
|
|
41
|
-
def put(path, opts={}) add_route 'PUT', path, opts end
|
|
42
|
-
def delete(path, opts={}) add_route 'DELETE', path, opts end
|
|
43
|
-
def trace(path, opts={}) add_route 'TRACE', path, opts end
|
|
44
|
-
def connect(path, opts={}) add_route 'CONNECT', path, opts end
|
|
45
|
-
def patch(path, opts={}) add_route 'PATCH', path, opts end
|
|
46
|
-
def any(path, opts={}) add_route 'ANY', path, opts end
|
|
34
|
+
def options(path, opts = {})
|
|
35
|
+
add_route 'OPTIONS', path, opts
|
|
36
|
+
end
|
|
47
37
|
|
|
48
|
-
|
|
38
|
+
def get(path, opts = {})
|
|
39
|
+
add_route 'GET', path, opts
|
|
40
|
+
end
|
|
49
41
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
def head(path, opts = {})
|
|
43
|
+
add_route 'HEAD', path, opts
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def post(path, opts = {})
|
|
47
|
+
add_route 'POST', path, opts
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def put(path, opts = {})
|
|
51
|
+
add_route 'PUT', path, opts
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def delete(path, opts = {})
|
|
55
|
+
add_route 'DELETE', path, opts
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def trace(path, opts = {})
|
|
59
|
+
add_route 'TRACE', path, opts
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def connect(path, opts = {})
|
|
63
|
+
add_route 'CONNECT', path, opts
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def patch(path, opts = {})
|
|
67
|
+
add_route 'PATCH', path, opts
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def any(path, opts = {})
|
|
71
|
+
add_route 'ANY', path, opts
|
|
58
72
|
end
|
|
59
73
|
|
|
74
|
+
ABSOLUTE_PATH_REGEX = %r{^//}.freeze
|
|
75
|
+
|
|
76
|
+
def add_route(verb, path, options = {})
|
|
77
|
+
path = prefix + path unless path =~ ABSOLUTE_PATH_REGEX
|
|
78
|
+
prefixed_path = path.gsub('//', '/')
|
|
79
|
+
path = (base + path).gsub('//', '/')
|
|
80
|
+
pattern = Mustermann.new(path, **{ ignore_unknown_options: true }.merge(options))
|
|
81
|
+
@route = Route.new(verb, pattern, version, prefixed_path: prefixed_path, **options)
|
|
82
|
+
end
|
|
60
83
|
end
|
|
61
84
|
end
|
|
@@ -1,29 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
# Stripped-down representation of an Internet Media Type where the structure and content of the
|
|
4
5
|
# type are unknown, or are defined externally to the Praxis application.
|
|
5
6
|
#
|
|
6
7
|
# @see Praxis::MediaType
|
|
7
8
|
# @see Praxis::Types::MediaTypeCommon
|
|
8
9
|
SimpleMediaType = Struct.new(:identifier) do
|
|
9
|
-
|
|
10
10
|
def name
|
|
11
11
|
self.class.name
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.id
|
|
15
|
-
|
|
15
|
+
'Praxis-SimpleMediaType'
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def id
|
|
19
19
|
self.class.id
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{name: name, family: "string", id: id, identifier: identifier}
|
|
22
|
+
def describe(*)
|
|
23
|
+
{ name: name, family: 'string', id: id, identifier: identifier }
|
|
25
24
|
end
|
|
26
|
-
|
|
27
25
|
end
|
|
28
|
-
|
|
29
26
|
end
|
data/lib/praxis/stage.rb
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
class Stage
|
|
4
|
-
|
|
5
|
-
attr_reader :name
|
|
6
|
-
attr_reader :context
|
|
7
|
-
attr_reader :stages
|
|
8
|
-
attr_reader :before_callbacks
|
|
9
|
-
attr_reader :after_callbacks
|
|
5
|
+
attr_reader :name, :context, :stages, :before_callbacks, :after_callbacks
|
|
10
6
|
|
|
11
7
|
def application
|
|
12
8
|
context
|
|
13
9
|
end
|
|
14
10
|
|
|
15
|
-
def initialize(name, context, **
|
|
11
|
+
def initialize(name, context, **_opts)
|
|
16
12
|
@name = name
|
|
17
13
|
@context = context
|
|
18
|
-
@before_callbacks =
|
|
19
|
-
@after_callbacks =
|
|
20
|
-
@deferred_callbacks = Hash.new do |hash,stage|
|
|
21
|
-
hash[stage] = {before: [], after:[]}
|
|
14
|
+
@before_callbacks = []
|
|
15
|
+
@after_callbacks = []
|
|
16
|
+
@deferred_callbacks = Hash.new do |hash, stage|
|
|
17
|
+
hash[stage] = { before: [], after: [] }
|
|
22
18
|
end
|
|
23
|
-
@stages =
|
|
19
|
+
@stages = []
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
def run
|
|
27
|
-
execute_callbacks(
|
|
23
|
+
execute_callbacks(before_callbacks)
|
|
28
24
|
execute
|
|
29
|
-
execute_callbacks(
|
|
25
|
+
execute_callbacks(after_callbacks)
|
|
30
26
|
end
|
|
31
27
|
|
|
32
28
|
def setup!
|
|
@@ -34,22 +30,20 @@ module Praxis
|
|
|
34
30
|
end
|
|
35
31
|
|
|
36
32
|
def setup_deferred_callbacks!
|
|
37
|
-
@deferred_callbacks.
|
|
33
|
+
@deferred_callbacks.each_key do |stage_name|
|
|
38
34
|
callbacks = @deferred_callbacks.delete stage_name
|
|
39
35
|
callbacks[:before].each do |(*stage_path, block)|
|
|
40
|
-
|
|
36
|
+
before(stage_name, *stage_path, &block)
|
|
41
37
|
end
|
|
42
38
|
|
|
43
39
|
callbacks[:after].each do |(*stage_path, block)|
|
|
44
|
-
|
|
40
|
+
after(stage_name, *stage_path, &block)
|
|
45
41
|
end
|
|
46
42
|
end
|
|
47
43
|
end
|
|
48
44
|
|
|
49
45
|
def execute
|
|
50
|
-
@stages.each
|
|
51
|
-
stage.run
|
|
52
|
-
end
|
|
46
|
+
@stages.each(&:run)
|
|
53
47
|
end
|
|
54
48
|
|
|
55
49
|
def execute_callbacks(callbacks)
|
|
@@ -65,7 +59,7 @@ module Praxis
|
|
|
65
59
|
def before(*stage_path, &block)
|
|
66
60
|
if stage_path.any?
|
|
67
61
|
stage_name = stage_path.shift
|
|
68
|
-
stage = stages.find { |
|
|
62
|
+
stage = stages.find { |s| s.name == stage_name }
|
|
69
63
|
if stage
|
|
70
64
|
stage.before(*stage_path, &block)
|
|
71
65
|
else
|
|
@@ -79,7 +73,7 @@ module Praxis
|
|
|
79
73
|
def after(*stage_path, &block)
|
|
80
74
|
if stage_path.any?
|
|
81
75
|
stage_name = stage_path.shift
|
|
82
|
-
stage = stages.find { |
|
|
76
|
+
stage = stages.find { |s| s.name == stage_name }
|
|
83
77
|
if stage
|
|
84
78
|
stage.after(*stage_path, &block)
|
|
85
79
|
else
|
|
@@ -89,7 +83,5 @@ module Praxis
|
|
|
89
83
|
@after_callbacks << block
|
|
90
84
|
end
|
|
91
85
|
end
|
|
92
|
-
|
|
93
|
-
|
|
94
86
|
end
|
|
95
87
|
end
|
|
@@ -1,42 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
namespace :praxis do
|
|
3
4
|
namespace :docs do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
task :generate => [:environment] do |t, args|
|
|
5
|
+
desc 'Generate OpenAPI 3 docs for a Praxis App'
|
|
6
|
+
task generate: [:environment] do |_t, _args|
|
|
7
7
|
require 'fileutils'
|
|
8
8
|
|
|
9
9
|
Praxis::Blueprint.caching_enabled = false
|
|
10
|
-
generator = Praxis::Docs::OpenApiGenerator.
|
|
10
|
+
generator = Praxis::Docs::OpenApiGenerator.instance
|
|
11
|
+
generator.configure_root(Dir.pwd)
|
|
11
12
|
generator.save!
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
desc
|
|
15
|
-
task :
|
|
16
|
-
require 'webrick'
|
|
15
|
+
desc 'Preview (and Generate) OpenAPI 3 docs for a Praxis App'
|
|
16
|
+
task preview: [:generate] do |_t, _args|
|
|
17
|
+
require 'webrick'
|
|
17
18
|
docs_port = 9090
|
|
18
|
-
root = Dir.pwd
|
|
19
|
+
root = "#{Dir.pwd}/docs/openapi/"
|
|
19
20
|
wb = Thread.new do
|
|
20
|
-
s = WEBrick::HTTPServer.new(:
|
|
21
|
+
s = WEBrick::HTTPServer.new(Port: docs_port, DocumentRoot: root)
|
|
21
22
|
trap('INT') { s.shutdown }
|
|
22
23
|
s.start
|
|
23
24
|
end
|
|
24
25
|
# If there is only 1 version we'll feature it and open the browser onto it
|
|
25
26
|
versions = Dir.children(root)
|
|
26
|
-
featured_version =
|
|
27
|
+
featured_version = versions.size < 2 ? "#{versions.first}/" : ''
|
|
27
28
|
`open http://localhost:#{docs_port}/#{featured_version}`
|
|
28
29
|
wb.join
|
|
29
30
|
end
|
|
30
|
-
desc
|
|
31
|
-
task :
|
|
32
|
-
docs_root = Dir.pwd
|
|
33
|
-
zip_file = Dir.pwd
|
|
31
|
+
desc 'Generate and package all OpenApi Docs into a zip, ready for a Web server (like S3...) to present it'
|
|
32
|
+
task package: [:generate] do |_t, _args|
|
|
33
|
+
docs_root = "#{Dir.pwd}/docs/openapi/"
|
|
34
|
+
zip_file = "#{Dir.pwd}/docs/openapi.zip"
|
|
34
35
|
`rm -f #{zip_file}`
|
|
35
36
|
# NOTE: This assumes the "zip" utility is installed, supporting the recursive flag.
|
|
36
37
|
`zip -r #{zip_file} #{docs_root}`
|
|
37
38
|
puts
|
|
38
39
|
puts "Left packaged API docs in #{zip_file}"
|
|
39
|
-
puts
|
|
40
|
+
puts ' --> To view the docs, unzip the file under a web server (or S3...) and access the index.hml files from a browser'
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
end
|
data/lib/praxis/tasks/console.rb
CHANGED
data/lib/praxis/tasks/routes.rb
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
namespace :praxis do
|
|
3
4
|
desc 'List routes, format=json or table, default table'
|
|
4
|
-
task :routes, [:format] => [:environment] do |
|
|
5
|
+
task :routes, [:format] => [:environment] do |_t, args|
|
|
5
6
|
require 'terminal-table'
|
|
6
7
|
|
|
7
|
-
table = Terminal::Table.new title:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
table = Terminal::Table.new title: 'Routes',
|
|
9
|
+
headings: %w[
|
|
10
|
+
Version Path Verb
|
|
11
|
+
Endpoint Action Implementation Options
|
|
12
|
+
]
|
|
12
13
|
|
|
13
14
|
rows = []
|
|
14
15
|
Praxis::Application.instance.endpoint_definitions.each do |endpoint_definition|
|
|
15
16
|
endpoint_definition.actions.each do |name, action|
|
|
16
17
|
method = begin
|
|
17
|
-
|
|
18
|
-
rescue
|
|
18
|
+
endpoint_definition.controller.instance_method(name)
|
|
19
|
+
rescue StandardError
|
|
19
20
|
nil
|
|
20
21
|
end
|
|
21
22
|
|
|
@@ -24,31 +25,32 @@ namespace :praxis do
|
|
|
24
25
|
row = {
|
|
25
26
|
resource: endpoint_definition.name,
|
|
26
27
|
action: name,
|
|
27
|
-
implementation: method_name
|
|
28
|
+
implementation: method_name
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
warn "Warning: No routes defined for #{endpoint_definition.name}##{name}."
|
|
32
|
-
rows << row
|
|
33
|
-
else
|
|
31
|
+
if action.route
|
|
34
32
|
route = action.route
|
|
35
33
|
rows << row.merge({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
version: route.version,
|
|
35
|
+
verb: route.verb,
|
|
36
|
+
path: route.path,
|
|
37
|
+
options: route.options
|
|
38
|
+
})
|
|
39
|
+
else
|
|
40
|
+
warn "Warning: No routes defined for #{endpoint_definition.name}##{name}."
|
|
41
|
+
rows << row
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
format_type = args[:format] || 'table'
|
|
46
|
+
case format_type
|
|
47
|
+
when 'json'
|
|
46
48
|
puts JSON.pretty_generate(rows)
|
|
47
|
-
when
|
|
49
|
+
when 'table'
|
|
48
50
|
rows.each do |row|
|
|
49
|
-
formatted_options = row[:options].map{|(k,v)| "#{k}:#{v
|
|
51
|
+
formatted_options = row[:options].map { |(k, v)| "#{k}:#{v}" }.join("\n")
|
|
50
52
|
row_data = row.values_at(:version, :path, :verb, :resource,
|
|
51
|
-
|
|
53
|
+
:action, :implementation)
|
|
52
54
|
row_data << formatted_options
|
|
53
55
|
table.add_row(row_data)
|
|
54
56
|
end
|
data/lib/praxis/tasks.rb
CHANGED
data/lib/praxis/trait.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
class Trait
|
|
4
|
-
attr_reader :name
|
|
5
|
-
attr_reader :attribute_groups
|
|
5
|
+
attr_reader :name, :attribute_groups
|
|
6
6
|
|
|
7
7
|
def initialize(&block)
|
|
8
8
|
@name = nil
|
|
@@ -11,21 +11,24 @@ module Praxis
|
|
|
11
11
|
@routing = nil
|
|
12
12
|
@other = []
|
|
13
13
|
|
|
14
|
-
@attribute_groups = Hash.new do |h,k|
|
|
14
|
+
@attribute_groups = Hash.new do |h, k|
|
|
15
15
|
h[k] = []
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
if block_given?
|
|
19
|
-
self.instance_eval(&block)
|
|
20
|
-
end
|
|
18
|
+
instance_eval(&block) if block_given?
|
|
21
19
|
end
|
|
22
20
|
|
|
23
21
|
def method_missing(name, *args, &block)
|
|
24
22
|
@other << [name, args, block]
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
def
|
|
25
|
+
def respond_to_missing?(*)
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def description(desc = nil)
|
|
28
30
|
return @description if desc.nil?
|
|
31
|
+
|
|
29
32
|
@description = desc
|
|
30
33
|
end
|
|
31
34
|
|
|
@@ -37,22 +40,20 @@ module Praxis
|
|
|
37
40
|
@attribute_groups[name] << block
|
|
38
41
|
end
|
|
39
42
|
|
|
40
|
-
def headers(*
|
|
41
|
-
create_group(:headers
|
|
43
|
+
def headers(*_args, &block)
|
|
44
|
+
create_group(:headers, &block)
|
|
42
45
|
end
|
|
43
46
|
|
|
44
|
-
def params(*
|
|
45
|
-
create_group(:params
|
|
47
|
+
def params(*_args, &block)
|
|
48
|
+
create_group(:params, &block)
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def payload(*args, &block)
|
|
49
|
-
type,
|
|
52
|
+
type, _opts = args
|
|
50
53
|
|
|
51
|
-
if type && !(type < Attributor::Hash)
|
|
52
|
-
raise 'payload in a trait with non-hash (or model or struct) is not supported'
|
|
53
|
-
end
|
|
54
|
+
raise 'payload in a trait with non-hash (or model or struct) is not supported' if type && !(type < Attributor::Hash)
|
|
54
55
|
|
|
55
|
-
create_group(:payload
|
|
56
|
+
create_group(:payload, &block)
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def routing(&block)
|
|
@@ -60,26 +61,24 @@ module Praxis
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
def describe
|
|
63
|
-
desc = {description: @description}
|
|
64
|
+
desc = { description: @description }
|
|
64
65
|
desc[:name] = @name if @name
|
|
65
66
|
desc[:responses] = @responses if @responses.any?
|
|
66
67
|
|
|
67
|
-
if @routing
|
|
68
|
-
desc[:routing] = ConfigHash.new(&@routing).to_hash
|
|
69
|
-
end
|
|
68
|
+
desc[:routing] = ConfigHash.new(&@routing).to_hash if @routing
|
|
70
69
|
|
|
71
70
|
@attribute_groups.each_with_object(desc) do |(name, blocks), hash|
|
|
72
71
|
type_class = if name == :headers
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
# Headers are special:
|
|
73
|
+
# Keys are strings, they have a special DSL, and are case insensitive
|
|
74
|
+
hash_opts = {
|
|
75
|
+
dsl_compiler: ActionDefinition::HeadersDSLCompiler,
|
|
76
|
+
case_insensitive_load: true
|
|
77
|
+
}
|
|
78
|
+
Attributor::Hash.of(key: String).construct(proc {}, **hash_opts)
|
|
79
|
+
else
|
|
80
|
+
Attributor::Hash.construct(proc {})
|
|
81
|
+
end
|
|
83
82
|
blocks.each do |block|
|
|
84
83
|
type_class.construct(block)
|
|
85
84
|
end
|
|
@@ -89,7 +88,6 @@ module Praxis
|
|
|
89
88
|
desc
|
|
90
89
|
end
|
|
91
90
|
|
|
92
|
-
|
|
93
91
|
def apply!(target)
|
|
94
92
|
@attribute_groups.each do |name, blocks|
|
|
95
93
|
blocks.each do |block|
|
|
@@ -97,27 +95,20 @@ module Praxis
|
|
|
97
95
|
end
|
|
98
96
|
end
|
|
99
97
|
|
|
100
|
-
if @routing
|
|
101
|
-
target.routing(&@routing)
|
|
102
|
-
end
|
|
98
|
+
target.routing(&@routing) if @routing
|
|
103
99
|
|
|
104
100
|
@responses.each do |name, args|
|
|
105
101
|
target.response(name, **args)
|
|
106
102
|
end
|
|
103
|
+
return unless @other.any?
|
|
107
104
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
target.send(name,*args)
|
|
114
|
-
end
|
|
105
|
+
@other.each do |name, args, block|
|
|
106
|
+
if block
|
|
107
|
+
target.send(name, *args, &block)
|
|
108
|
+
else
|
|
109
|
+
target.send(name, *args)
|
|
115
110
|
end
|
|
116
111
|
end
|
|
117
112
|
end
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
113
|
end
|
|
122
|
-
|
|
123
114
|
end
|
|
@@ -1,35 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module Types
|
|
3
|
-
|
|
4
5
|
class FuzzyHash
|
|
5
|
-
def initialize(value={})
|
|
6
|
+
def initialize(value = {})
|
|
6
7
|
@hash = {}
|
|
7
8
|
@regexes = []
|
|
8
9
|
update(value)
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def update(value)
|
|
12
|
-
value.each do |
|
|
13
|
-
self[
|
|
13
|
+
value.each do |key, val|
|
|
14
|
+
self[key] = val
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
self
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
def []=(
|
|
20
|
-
case
|
|
20
|
+
def []=(key, val)
|
|
21
|
+
case key
|
|
21
22
|
when Regexp
|
|
22
|
-
@regexes <<
|
|
23
|
+
@regexes << key
|
|
23
24
|
end
|
|
24
|
-
@hash[
|
|
25
|
+
@hash[key] = val
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def [](
|
|
28
|
-
return @hash[
|
|
28
|
+
def [](key)
|
|
29
|
+
return @hash[key] if @hash.key?(key)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
key = key.to_s
|
|
31
32
|
@regexes.each do |regex|
|
|
32
|
-
return @hash[regex] if regex.match(
|
|
33
|
+
return @hash[regex] if regex.match(key)
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
nil
|
|
@@ -42,8 +43,6 @@ module Praxis
|
|
|
42
43
|
def respond_to_missing?(*args)
|
|
43
44
|
@hash.respond_to?(*args)
|
|
44
45
|
end
|
|
45
|
-
|
|
46
46
|
end
|
|
47
|
-
|
|
48
47
|
end
|
|
49
48
|
end
|
|
@@ -1,43 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module Types
|
|
3
|
-
|
|
4
5
|
# Traits that are shared by MediaType and SimpleMediaType.
|
|
5
6
|
module MediaTypeCommon
|
|
6
7
|
extend ::ActiveSupport::Concern
|
|
7
8
|
|
|
8
9
|
module ClassMethods
|
|
9
10
|
def as_json_schema(**args)
|
|
10
|
-
the_type = @attribute
|
|
11
|
+
the_type = @attribute&.type || member_type
|
|
11
12
|
the_type.as_json_schema(args)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def json_schema_type
|
|
15
|
-
the_type = @attribute
|
|
16
|
+
the_type = @attribute&.type || member_type
|
|
16
17
|
the_type.json_schema_type
|
|
17
18
|
end
|
|
18
|
-
|
|
19
|
-
def description(text=nil)
|
|
19
|
+
|
|
20
|
+
def description(text = nil)
|
|
20
21
|
@description = text if text
|
|
21
22
|
@description
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
def display_name(
|
|
25
|
+
def display_name(string = nil)
|
|
25
26
|
unless string
|
|
26
|
-
return
|
|
27
|
+
return @display_name ||= name.split('::').last # Best guess at a display name?
|
|
27
28
|
end
|
|
29
|
+
|
|
28
30
|
@display_name = string
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
# Get or set the identifier of this media type.
|
|
32
34
|
#
|
|
33
35
|
# @return [MediaTypeIdentifier] the string-representation of this type's identifier
|
|
34
|
-
def identifier(identifier=nil)
|
|
36
|
+
def identifier(identifier = nil)
|
|
35
37
|
return @identifier unless identifier
|
|
38
|
+
|
|
36
39
|
@identifier = MediaTypeIdentifier.load(identifier)
|
|
37
40
|
end
|
|
38
41
|
end
|
|
39
|
-
|
|
40
42
|
end
|
|
41
|
-
|
|
42
43
|
end
|
|
43
44
|
end
|