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
data/lib/praxis/blueprint.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Praxis
|
|
4
|
-
class Blueprint
|
|
5
|
-
|
|
4
|
+
class Blueprint
|
|
6
5
|
# Simple helper class that can parse the `attribute :foobar` dsl into
|
|
7
6
|
# an equivalent structure hash. Example:
|
|
8
7
|
# do
|
|
@@ -13,22 +12,25 @@ module Praxis
|
|
|
13
12
|
# end
|
|
14
13
|
# is parsed as: { one: true, complex: { sub1: true} }
|
|
15
14
|
class FieldsetParser
|
|
16
|
-
def initialize(
|
|
15
|
+
def initialize(&block)
|
|
17
16
|
@hash = nil
|
|
18
17
|
@block = block
|
|
19
18
|
end
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
def attribute(name, **args, &block)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
unless args.empty?
|
|
22
|
+
raise "Default fieldset definitions do not accept parameters (got: #{args})" \
|
|
23
|
+
"If you're upgrading from a previous version of Praxis and still using the view :default " \
|
|
24
|
+
"block syntax, make sure you don't use any view: X parameters when you define the attributes " \
|
|
25
|
+
'(expand them explicitly if you want deeper structure)' \
|
|
26
|
+
"The offending view with parameters is defined in:\n#{Kernel.caller.first}"
|
|
27
|
+
end
|
|
27
28
|
@hash[name] = block_given? ? FieldsetParser.new(&block).fieldset : true
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
def fieldset
|
|
31
32
|
return @hash if @hash
|
|
33
|
+
|
|
32
34
|
# Lazy eval
|
|
33
35
|
@hash = {}
|
|
34
36
|
instance_eval(&@block)
|
|
@@ -36,18 +38,18 @@ module Praxis
|
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
include Attributor::Type
|
|
41
|
+
include Attributor::Container
|
|
39
42
|
include Attributor::Dumpable
|
|
40
43
|
|
|
41
44
|
extend Finalizable
|
|
42
45
|
|
|
43
|
-
@@caching_enabled = false
|
|
46
|
+
@@caching_enabled = false # rubocop:disable Style/ClassVars
|
|
44
47
|
|
|
45
48
|
attr_reader :validating
|
|
46
49
|
attr_accessor :object
|
|
47
50
|
|
|
48
51
|
class << self
|
|
49
|
-
attr_reader :attribute
|
|
50
|
-
attr_reader :options
|
|
52
|
+
attr_reader :attribute, :options
|
|
51
53
|
attr_accessor :reference
|
|
52
54
|
end
|
|
53
55
|
|
|
@@ -65,14 +67,14 @@ module Praxis
|
|
|
65
67
|
def self.new(object)
|
|
66
68
|
# TODO: do we want to allow the identity map thing in the object?...maybe not.
|
|
67
69
|
if @@caching_enabled
|
|
68
|
-
return
|
|
69
|
-
blueprint =
|
|
70
|
+
return cache[object] ||= begin
|
|
71
|
+
blueprint = allocate
|
|
70
72
|
blueprint.send(:initialize, object)
|
|
71
73
|
blueprint
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
blueprint =
|
|
77
|
+
blueprint = allocate
|
|
76
78
|
blueprint.send(:initialize, object)
|
|
77
79
|
blueprint
|
|
78
80
|
end
|
|
@@ -83,15 +85,11 @@ module Praxis
|
|
|
83
85
|
|
|
84
86
|
def self.attributes(opts = {}, &block)
|
|
85
87
|
if block_given?
|
|
86
|
-
raise 'Redefining Blueprint attributes is not currently supported' if
|
|
88
|
+
raise 'Redefining Blueprint attributes is not currently supported' if const_defined?(:Struct, false)
|
|
87
89
|
|
|
88
|
-
if opts.key?(:reference) && opts[:reference] !=
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
opts[:reference] = self.reference # pass the reference Class down
|
|
92
|
-
else
|
|
93
|
-
opts[:reference] = self
|
|
94
|
-
end
|
|
90
|
+
raise "Reference mismatch in #{inspect}. Given :reference option #{opts[:reference].inspect}, while using #{reference.inspect}" if opts.key?(:reference) && opts[:reference] != reference
|
|
91
|
+
|
|
92
|
+
opts[:reference] = (reference || self)
|
|
95
93
|
|
|
96
94
|
@options.merge!(opts)
|
|
97
95
|
@block = block
|
|
@@ -99,13 +97,14 @@ module Praxis
|
|
|
99
97
|
return @attribute
|
|
100
98
|
end
|
|
101
99
|
|
|
102
|
-
raise "@attribute not defined yet for #{
|
|
100
|
+
raise "@attribute not defined yet for #{name}" unless @attribute
|
|
103
101
|
|
|
104
102
|
@attribute.attributes
|
|
105
103
|
end
|
|
106
104
|
|
|
107
105
|
def self.domain_model(klass = nil)
|
|
108
106
|
return @domain_model if klass.nil?
|
|
107
|
+
|
|
109
108
|
@domain_model = klass
|
|
110
109
|
end
|
|
111
110
|
|
|
@@ -118,16 +117,16 @@ module Praxis
|
|
|
118
117
|
when self
|
|
119
118
|
value
|
|
120
119
|
when nil, Hash, String
|
|
121
|
-
if (value =
|
|
122
|
-
|
|
120
|
+
if (value = attribute.load(value, context, **options))
|
|
121
|
+
new(value)
|
|
123
122
|
end
|
|
124
123
|
else
|
|
125
|
-
if value.is_a?(
|
|
124
|
+
if value.is_a?(domain_model) || value.is_a?(self::Struct)
|
|
126
125
|
# Wrap the value directly
|
|
127
|
-
|
|
126
|
+
new(value)
|
|
128
127
|
else
|
|
129
128
|
# Wrap the object inside the domain_model
|
|
130
|
-
|
|
129
|
+
new(domain_model.new(value))
|
|
131
130
|
end
|
|
132
131
|
end
|
|
133
132
|
end
|
|
@@ -141,7 +140,7 @@ module Praxis
|
|
|
141
140
|
end
|
|
142
141
|
|
|
143
142
|
def self.caching_enabled=(caching_enabled)
|
|
144
|
-
@@caching_enabled = caching_enabled
|
|
143
|
+
@@caching_enabled = caching_enabled # rubocop:disable Style/ClassVars
|
|
145
144
|
end
|
|
146
145
|
|
|
147
146
|
# Fetch current blueprint cache, scoped by this class
|
|
@@ -154,29 +153,28 @@ module Praxis
|
|
|
154
153
|
end
|
|
155
154
|
|
|
156
155
|
def self.valid_type?(value)
|
|
157
|
-
value.is_a?(self) || value.is_a?(
|
|
156
|
+
value.is_a?(self) || value.is_a?(attribute.type)
|
|
158
157
|
end
|
|
159
158
|
|
|
160
159
|
def self.example(context = nil, **values)
|
|
161
160
|
context = case context
|
|
162
161
|
when nil
|
|
163
|
-
["#{
|
|
162
|
+
["#{name}-#{values.object_id}"]
|
|
164
163
|
when ::String
|
|
165
164
|
[context]
|
|
166
165
|
else
|
|
167
166
|
context
|
|
168
167
|
end
|
|
169
168
|
|
|
170
|
-
|
|
169
|
+
new(attribute.example(context, values: values))
|
|
171
170
|
end
|
|
172
171
|
|
|
173
172
|
def self.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil)
|
|
174
|
-
raise ArgumentError, "Invalid context received (nil) while validating value of type #{
|
|
173
|
+
raise ArgumentError, "Invalid context received (nil) while validating value of type #{name}" if context.nil?
|
|
174
|
+
|
|
175
175
|
context = [context] if context.is_a? ::String
|
|
176
176
|
|
|
177
|
-
unless value.is_a?(self)
|
|
178
|
-
raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{self.name} for an object of type #{value.class.name}."
|
|
179
|
-
end
|
|
177
|
+
raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{name} for an object of type #{value.class.name}." unless value.is_a?(self)
|
|
180
178
|
|
|
181
179
|
value.validate(context)
|
|
182
180
|
end
|
|
@@ -187,13 +185,14 @@ module Praxis
|
|
|
187
185
|
@block_for_default_fieldset = block
|
|
188
186
|
end
|
|
189
187
|
|
|
190
|
-
def self.view(name, **
|
|
188
|
+
def self.view(name, **_options, &block)
|
|
191
189
|
unless name == :default
|
|
192
190
|
raise "[ERROR] Views are no longer supported. Please use fully expanded fields when rendering.\n" \
|
|
193
191
|
"NOTE that defining the :default view is deprecated, but still temporarily allowed, as an alias to define the default_fieldset.\n" \
|
|
194
192
|
"A view for name #{name} is attempted to be defined in:\n#{Kernel.caller.first}"
|
|
195
193
|
end
|
|
196
|
-
raise
|
|
194
|
+
raise 'Cannot define the default fieldset through the default view unless a block is passed' unless block_given?
|
|
195
|
+
|
|
197
196
|
puts "[DEPRECATED] default fieldsets should be defined through `default_fieldset` instead of using the view :default block.\n" \
|
|
198
197
|
"A default view is attempted to be defined in:\n#{Kernel.caller.first}"
|
|
199
198
|
default_fieldset(&block)
|
|
@@ -219,45 +218,45 @@ module Praxis
|
|
|
219
218
|
# Internal finalize! logic
|
|
220
219
|
def self._finalize!
|
|
221
220
|
if @block
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
define_attribute!
|
|
222
|
+
define_readers!
|
|
224
223
|
# Don't blindly override a the default fieldset if the MediaType wants to define it on its own
|
|
225
224
|
if @block_for_default_fieldset
|
|
226
|
-
parse_default_fieldset(@block_for_default_fieldset)
|
|
225
|
+
parse_default_fieldset(@block_for_default_fieldset)
|
|
227
226
|
else
|
|
228
|
-
|
|
227
|
+
generate_default_fieldset!
|
|
229
228
|
end
|
|
230
|
-
|
|
229
|
+
resolve_domain_model!
|
|
231
230
|
end
|
|
232
231
|
super
|
|
233
232
|
end
|
|
234
233
|
|
|
235
234
|
def self.resolve_domain_model!
|
|
236
|
-
return unless
|
|
235
|
+
return unless domain_model.is_a?(String)
|
|
237
236
|
|
|
238
|
-
@domain_model =
|
|
237
|
+
@domain_model = domain_model.constantize
|
|
239
238
|
end
|
|
240
239
|
|
|
241
240
|
def self.define_attribute!
|
|
242
241
|
@attribute = Attributor::Attribute.new(Attributor::Struct, @options, &@block)
|
|
243
242
|
@block = nil
|
|
244
243
|
@attribute.type.anonymous_type true
|
|
245
|
-
|
|
244
|
+
const_set(:Struct, @attribute.type)
|
|
246
245
|
end
|
|
247
246
|
|
|
248
247
|
def self.define_readers!
|
|
249
|
-
|
|
248
|
+
attributes.each do |name, _attribute|
|
|
250
249
|
name = name.to_sym
|
|
251
250
|
|
|
252
251
|
# Don't redefine existing methods
|
|
253
|
-
next if
|
|
252
|
+
next if instance_methods.include? name
|
|
254
253
|
|
|
255
254
|
define_reader! name
|
|
256
255
|
end
|
|
257
256
|
end
|
|
258
257
|
|
|
259
258
|
def self.define_reader!(name)
|
|
260
|
-
attribute =
|
|
259
|
+
attribute = attributes[name]
|
|
261
260
|
# TODO: profile and optimize
|
|
262
261
|
# because we use the attribute in the reader,
|
|
263
262
|
# it's likely faster to use define_method here
|
|
@@ -265,6 +264,7 @@ module Praxis
|
|
|
265
264
|
define_method(name) do
|
|
266
265
|
value = @object.__send__(name)
|
|
267
266
|
return value if value.nil? || value.is_a?(attribute.type)
|
|
267
|
+
|
|
268
268
|
attribute.load(value)
|
|
269
269
|
end
|
|
270
270
|
end
|
|
@@ -274,15 +274,16 @@ module Praxis
|
|
|
274
274
|
|
|
275
275
|
@default_fieldset = {}
|
|
276
276
|
attributes.each do |name, attr|
|
|
277
|
-
the_type =
|
|
277
|
+
the_type = attr.type < Attributor::Collection ? attr.type.member_type : attr.type
|
|
278
278
|
next if the_type < Blueprint
|
|
279
|
-
|
|
279
|
+
|
|
280
|
+
# NOTE: we won't try to expand fields here, as we want to be lazy (and we're expanding)
|
|
280
281
|
# every time a request comes in anyway. This could be an optimization we do at some point
|
|
281
282
|
# or we can 'memoize it' to avoid trying to expand it over an over...
|
|
282
283
|
@default_fieldset[name] = true
|
|
283
284
|
end
|
|
284
285
|
end
|
|
285
|
-
|
|
286
|
+
|
|
286
287
|
def initialize(object)
|
|
287
288
|
@object = object
|
|
288
289
|
@validating = false
|
|
@@ -291,18 +292,14 @@ module Praxis
|
|
|
291
292
|
# By default we'll use the object identity, to avoid rendering the same object twice
|
|
292
293
|
# Override, if there is a better way cache things up
|
|
293
294
|
def _cache_key
|
|
294
|
-
|
|
295
|
+
object
|
|
295
296
|
end
|
|
296
297
|
|
|
297
298
|
# Render the wrapped data with the given fields (or using the default fieldset otherwise)
|
|
298
|
-
def render(fields: self.class.default_fieldset, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **
|
|
299
|
-
|
|
299
|
+
def render(fields: self.class.default_fieldset, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **_opts)
|
|
300
300
|
# Accept a simple array of fields, and transform it to a 1-level hash with true values
|
|
301
|
-
if fields.is_a? Array
|
|
302
|
-
fields = fields.each_with_object({}) { |field, hash| hash[field] = true }
|
|
303
|
-
end
|
|
301
|
+
fields = fields.each_with_object({}) { |field, hash| hash[field] = true } if fields.is_a? Array
|
|
304
302
|
|
|
305
|
-
expanded = Praxis::FieldExpander.new.expand(self, fields)
|
|
306
303
|
renderer.render(self, fields, context: context)
|
|
307
304
|
end
|
|
308
305
|
|
|
@@ -313,26 +310,36 @@ module Praxis
|
|
|
313
310
|
end
|
|
314
311
|
|
|
315
312
|
def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
|
|
316
|
-
raise ArgumentError, "Invalid context received (nil) while validating value of type #{
|
|
313
|
+
raise ArgumentError, "Invalid context received (nil) while validating value of type #{name}" if context.nil?
|
|
314
|
+
|
|
317
315
|
context = [context] if context.is_a? ::String
|
|
318
|
-
keys_with_values = []
|
|
319
316
|
|
|
320
317
|
raise 'validation conflict' if @validating
|
|
318
|
+
|
|
321
319
|
@validating = true
|
|
322
320
|
|
|
323
321
|
errors = []
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
322
|
+
keys_provided = []
|
|
323
|
+
|
|
324
|
+
self.class.attributes.each do |key, attribute|
|
|
325
|
+
sub_context = self.class.generate_subcontext(context, key)
|
|
326
|
+
value = _get_attr(key)
|
|
327
|
+
keys_provided << key if @object.key?(key)
|
|
328
|
+
|
|
329
|
+
next if value.respond_to?(:validating) && value.validating # really, it's a thing with sub-attributes
|
|
328
330
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
+
# Isn't this handled by the requirements validation? NO! we might want to combine
|
|
332
|
+
errors.concat ["Attribute #{Attributor.humanize_context(sub_context)} is required."] if attribute.options[:required] && !@object.key?(key)
|
|
333
|
+
if @object[key].nil?
|
|
334
|
+
errors.concat ["Attribute #{Attributor.humanize_context(sub_context)} is not nullable."] if !Attributor::Attribute.nullable_attribute?(attribute.options) && @object.key?(key) # It is only nullable if there's an explicite null: true (undefined defaults to false)
|
|
335
|
+
# No need to validate the attribute further if the key wasn't passed...(or we would get nullable errors etc..cause the attribute has no
|
|
336
|
+
# context if its containing key was even passed (and there might not be a containing key for a top level attribute anyways))
|
|
337
|
+
else
|
|
338
|
+
errors.concat attribute.validate(value, sub_context)
|
|
331
339
|
end
|
|
332
|
-
errors.concat(sub_attribute.validate(value, sub_context))
|
|
333
340
|
end
|
|
334
|
-
self.class.attribute.type.requirements.each do |
|
|
335
|
-
validation_errors =
|
|
341
|
+
self.class.attribute.type.requirements.each do |requirement|
|
|
342
|
+
validation_errors = requirement.validate(keys_provided, context)
|
|
336
343
|
errors.concat(validation_errors) unless validation_errors.empty?
|
|
337
344
|
end
|
|
338
345
|
errors
|
|
@@ -342,14 +349,14 @@ module Praxis
|
|
|
342
349
|
|
|
343
350
|
# generic semi-private getter used by Renderer
|
|
344
351
|
def _get_attr(name)
|
|
345
|
-
|
|
352
|
+
send(name)
|
|
346
353
|
end
|
|
347
354
|
|
|
348
355
|
# Delegates the json-schema methods to the underlying attribute/member_type
|
|
349
356
|
def self.as_json_schema(**args)
|
|
350
357
|
@attribute.type.as_json_schema(args)
|
|
351
358
|
end
|
|
352
|
-
|
|
359
|
+
|
|
353
360
|
def self.json_schema_type
|
|
354
361
|
@attribute.type.json_schema_type
|
|
355
362
|
end
|
data/lib/praxis/bootloader.rb
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
class Bootloader
|
|
4
|
-
|
|
5
|
-
attr_reader :application
|
|
6
|
-
attr_reader :stages
|
|
5
|
+
attr_reader :application, :stages
|
|
7
6
|
|
|
8
7
|
def initialize(application)
|
|
9
8
|
@application = application
|
|
10
|
-
@stages =
|
|
9
|
+
@stages = []
|
|
11
10
|
|
|
12
11
|
setup_stages!
|
|
13
12
|
end
|
|
@@ -26,7 +25,7 @@ module Praxis
|
|
|
26
25
|
|
|
27
26
|
# then setup plugins
|
|
28
27
|
stages << BootloaderStages::PluginLoader.new(:plugins, application)
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
# then the initializers. as it is their job to ensure monkey patches and other
|
|
31
30
|
# config is in place first.
|
|
32
31
|
stages << BootloaderStages::FileLoader.new(:initializers, application)
|
|
@@ -50,24 +49,18 @@ module Praxis
|
|
|
50
49
|
Praxis::Blueprint.finalize!
|
|
51
50
|
Praxis::EndpointDefinition.finalize!
|
|
52
51
|
end
|
|
53
|
-
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
def delete_stage(stage_name)
|
|
57
|
-
|
|
58
|
-
stages.delete(stage)
|
|
59
|
-
else
|
|
60
|
-
raise Exceptions::StageNotFound.new(
|
|
61
|
-
"Cannot remove stage with name #{stage_name}, stage does not exist."
|
|
62
|
-
)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
55
|
+
raise Exceptions::StageNotFound, "Cannot remove stage with name #{stage_name}, stage does not exist." unless (stage = stages.find { |s| s.name == stage_name })
|
|
65
56
|
|
|
57
|
+
stages.delete(stage)
|
|
58
|
+
end
|
|
66
59
|
|
|
67
60
|
def before(*stage_path, &block)
|
|
68
61
|
stage_name = stage_path.shift
|
|
69
62
|
the_stage = stages.find { |stage| stage.name == stage_name }
|
|
70
|
-
raise Exceptions::StageNotFound
|
|
63
|
+
raise Exceptions::StageNotFound, "Error running a before block for stage #{stage_name}" unless the_stage
|
|
71
64
|
|
|
72
65
|
the_stage.before(*stage_path, &block)
|
|
73
66
|
end
|
|
@@ -75,36 +68,37 @@ module Praxis
|
|
|
75
68
|
def after(*stage_path, &block)
|
|
76
69
|
stage_name = stage_path.shift
|
|
77
70
|
the_stage = stages.find { |stage| stage.name == stage_name }
|
|
78
|
-
raise Exceptions::StageNotFound
|
|
71
|
+
raise Exceptions::StageNotFound, "Error running an after block for stage #{stage_name}" unless the_stage
|
|
79
72
|
|
|
80
73
|
the_stage.after(*stage_path, &block)
|
|
81
74
|
end
|
|
82
75
|
|
|
83
|
-
def use(plugin
|
|
76
|
+
def use(plugin, **options, &block)
|
|
84
77
|
if plugin.ancestors.include?(PluginConcern)
|
|
85
78
|
plugin.setup!
|
|
86
79
|
plugin = plugin::Plugin
|
|
87
80
|
end
|
|
88
81
|
|
|
89
82
|
instance = if plugin.ancestors.include?(Singleton)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
plugin.instance
|
|
84
|
+
elsif plugin.is_a?(Class)
|
|
85
|
+
plugin.new
|
|
86
|
+
else
|
|
87
|
+
plugin
|
|
88
|
+
end
|
|
96
89
|
|
|
97
90
|
instance.application = application
|
|
98
91
|
instance.options.merge!(options)
|
|
99
92
|
instance.block = block if block_given?
|
|
100
93
|
|
|
101
94
|
config_key = if instance.config_key.nil?
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
raise "Cannot use plugin: #{plugin}. It does not have a config_key defined, and its class does not have a name" unless instance.class.name
|
|
96
|
+
|
|
97
|
+
# Default the config key based on the full class name transformed to snake case (and joining modules with '_')
|
|
98
|
+
instance.class.name.to_s.split('::').collect(&:underscore).join('_').to_sym
|
|
99
|
+
else
|
|
100
|
+
instance.config_key
|
|
101
|
+
end
|
|
108
102
|
|
|
109
103
|
if application.plugins.key?(instance.config_key)
|
|
110
104
|
used_in = application.plugins[config_key].class
|
|
@@ -128,8 +122,5 @@ module Praxis
|
|
|
128
122
|
stage.run
|
|
129
123
|
end
|
|
130
124
|
end
|
|
131
|
-
|
|
132
125
|
end
|
|
133
|
-
|
|
134
|
-
|
|
135
126
|
end
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
module BootloaderStages
|
|
4
|
-
|
|
5
5
|
class Environment < Stage
|
|
6
|
-
|
|
7
6
|
# require environment files. we will require 2 files:
|
|
8
7
|
# 1) the environment.rb file - generic stuff for all environments
|
|
9
8
|
# 2) "Deployer.environment".rb - environment specific stuff
|
|
10
9
|
def execute
|
|
11
10
|
setup_initial_config!
|
|
12
11
|
|
|
13
|
-
env_file = application.root
|
|
14
|
-
require env_file if File.
|
|
12
|
+
env_file = application.root / 'config/environment.rb'
|
|
13
|
+
require env_file if File.exist? env_file
|
|
15
14
|
|
|
16
|
-
unless application.file_layout
|
|
17
|
-
setup_default_layout!
|
|
18
|
-
end
|
|
15
|
+
setup_default_layout! unless application.file_layout
|
|
19
16
|
end
|
|
20
17
|
|
|
21
18
|
def setup_default_layout!
|
|
@@ -52,8 +49,6 @@ module Praxis
|
|
|
52
49
|
end
|
|
53
50
|
end
|
|
54
51
|
end
|
|
55
|
-
|
|
56
52
|
end
|
|
57
|
-
|
|
58
53
|
end
|
|
59
54
|
end
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
3
|
+
module Praxis
|
|
4
4
|
module BootloaderStages
|
|
5
|
-
|
|
6
5
|
class FileLoader < Stage
|
|
7
|
-
|
|
8
6
|
attr_reader :path
|
|
9
7
|
|
|
10
8
|
def initialize(name, application, path: nil)
|
|
@@ -16,6 +14,7 @@ module Praxis
|
|
|
16
14
|
application.file_layout[*path].each do |file|
|
|
17
15
|
next if application.loaded_files.include?(file)
|
|
18
16
|
next unless file.extname == '.rb'
|
|
17
|
+
|
|
19
18
|
require file
|
|
20
19
|
application.loaded_files << file
|
|
21
20
|
end
|
|
@@ -24,8 +23,6 @@ module Praxis
|
|
|
24
23
|
def callback_args
|
|
25
24
|
application.file_layout[*path]
|
|
26
25
|
end
|
|
27
|
-
|
|
28
26
|
end
|
|
29
27
|
end
|
|
30
|
-
|
|
31
28
|
end
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module BootloaderStages
|
|
3
5
|
class PluginConfigLoad < Stage
|
|
4
|
-
|
|
5
6
|
def execute
|
|
6
7
|
application.plugins.each do |config_key, plugin|
|
|
7
8
|
context = [plugin.class.name]
|
|
8
9
|
value = plugin.load_config!
|
|
9
10
|
object = plugin.config_attribute.load(value, context)
|
|
10
11
|
|
|
11
|
-
if object
|
|
12
|
-
application.config.send("#{config_key}=", object)
|
|
13
|
-
end
|
|
12
|
+
application.config.send("#{config_key}=", object) if object
|
|
14
13
|
|
|
15
|
-
plugin.config = application.config.send(
|
|
14
|
+
plugin.config = application.config.send(config_key.to_s)
|
|
16
15
|
end
|
|
17
16
|
end
|
|
18
|
-
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module BootloaderStages
|
|
3
5
|
class PluginConfigPrepare < Stage
|
|
4
|
-
|
|
5
6
|
def execute
|
|
6
7
|
application.plugins.each do |config_key, plugin|
|
|
7
8
|
attribute = Attributor::Attribute.new(Attributor::Struct) {}
|
|
@@ -12,7 +13,6 @@ module Praxis
|
|
|
12
13
|
application.config.class.attributes[config_key] = plugin.config_attribute
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
|
-
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
3
|
+
module Praxis
|
|
4
4
|
module BootloaderStages
|
|
5
|
-
|
|
6
5
|
class PluginLoader < Stage
|
|
7
|
-
|
|
8
|
-
def initialize(name, context,**opts)
|
|
6
|
+
def initialize(name, context, **opts)
|
|
9
7
|
super
|
|
10
8
|
|
|
11
9
|
stages << PluginConfigPrepare.new(:prepare, context, parent: self)
|
|
12
10
|
stages << PluginConfigLoad.new(:load, context, parent: self)
|
|
13
11
|
stages << PluginSetup.new(:setup, context, parent: self)
|
|
14
12
|
end
|
|
15
|
-
|
|
16
13
|
end
|
|
17
|
-
|
|
18
14
|
end
|
|
19
15
|
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module BootloaderStages
|
|
3
5
|
class PluginSetup < Stage
|
|
4
|
-
|
|
5
6
|
def execute
|
|
6
|
-
application.plugins.each do |
|
|
7
|
+
application.plugins.each do |_config_key, plugin|
|
|
7
8
|
plugin.setup!
|
|
8
9
|
end
|
|
9
10
|
end
|
|
10
|
-
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|