grape 0.1.1 → 2.4.0
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 +7 -0
- data/CHANGELOG.md +1167 -0
- data/CONTRIBUTING.md +156 -0
- data/LICENSE +1 -1
- data/README.md +4192 -0
- data/UPGRADING.md +1697 -0
- data/grape.gemspec +28 -105
- data/grape.png +0 -0
- data/lib/grape/api/helpers.rb +9 -0
- data/lib/grape/api/instance.rb +247 -0
- data/lib/grape/api.rb +158 -194
- data/lib/grape/content_types.rb +31 -0
- data/lib/grape/cookies.rb +50 -0
- data/lib/grape/dry_types.rb +10 -0
- data/lib/grape/dsl/api.rb +17 -0
- data/lib/grape/dsl/callbacks.rb +69 -0
- data/lib/grape/dsl/configuration.rb +15 -0
- data/lib/grape/dsl/desc.rb +124 -0
- data/lib/grape/dsl/headers.rb +21 -0
- data/lib/grape/dsl/helpers.rb +108 -0
- data/lib/grape/dsl/inside_route.rb +454 -0
- data/lib/grape/dsl/logger.rb +22 -0
- data/lib/grape/dsl/middleware.rb +54 -0
- data/lib/grape/dsl/parameters.rb +266 -0
- data/lib/grape/dsl/request_response.rb +171 -0
- data/lib/grape/dsl/routing.rb +248 -0
- data/lib/grape/dsl/settings.rb +179 -0
- data/lib/grape/dsl/validations.rb +57 -0
- data/lib/grape/endpoint.rb +398 -68
- data/lib/grape/env.rb +20 -0
- data/lib/grape/error_formatter/base.rb +68 -0
- data/lib/grape/error_formatter/json.rb +28 -0
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +21 -0
- data/lib/grape/error_formatter/xml.rb +11 -0
- data/lib/grape/error_formatter.rb +15 -0
- data/lib/grape/exceptions/base.rb +76 -0
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
- data/lib/grape/exceptions/invalid_formatter.rb +11 -0
- data/lib/grape/exceptions/invalid_message_body.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +11 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
- data/lib/grape/exceptions/method_not_allowed.rb +11 -0
- data/lib/grape/exceptions/missing_group_type.rb +13 -0
- data/lib/grape/exceptions/missing_mime_type.rb +11 -0
- data/lib/grape/exceptions/missing_option.rb +11 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_options.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unknown_validator.rb +11 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
- data/lib/grape/exceptions/validation.rb +25 -0
- data/lib/grape/exceptions/validation_array_errors.rb +14 -0
- data/lib/grape/exceptions/validation_errors.rb +53 -0
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
- data/lib/grape/extensions/hash.rb +27 -0
- data/lib/grape/extensions/hashie/mash.rb +24 -0
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +13 -0
- data/lib/grape/formatter/serializable_hash.rb +39 -0
- data/lib/grape/formatter/txt.rb +11 -0
- data/lib/grape/formatter/xml.rb +13 -0
- data/lib/grape/formatter.rb +17 -0
- data/lib/grape/json.rb +10 -0
- data/lib/grape/locale/en.yml +59 -0
- data/lib/grape/middleware/auth/base.rb +23 -0
- data/lib/grape/middleware/auth/dsl.rb +39 -0
- data/lib/grape/middleware/auth/strategies.rb +25 -0
- data/lib/grape/middleware/auth/strategy_info.rb +15 -0
- data/lib/grape/middleware/base.rb +89 -18
- data/lib/grape/middleware/error.rb +129 -10
- data/lib/grape/middleware/filter.rb +19 -0
- data/lib/grape/middleware/formatter.rb +124 -82
- data/lib/grape/middleware/globals.rb +14 -0
- data/lib/grape/middleware/stack.rb +109 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +127 -0
- data/lib/grape/middleware/versioner/param.rb +32 -0
- data/lib/grape/middleware/versioner/path.rb +40 -0
- data/lib/grape/middleware/versioner.rb +21 -21
- data/lib/grape/namespace.rb +46 -0
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +14 -0
- data/lib/grape/parser/xml.rb +14 -0
- data/lib/grape/parser.rb +15 -0
- data/lib/grape/path.rb +76 -0
- data/lib/grape/presenters/presenter.rb +11 -0
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +190 -0
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +81 -0
- data/lib/grape/router/route.rb +62 -0
- data/lib/grape/router.rb +190 -0
- data/lib/grape/serve_stream/file_body.rb +36 -0
- data/lib/grape/serve_stream/sendfile_response.rb +21 -0
- data/lib/grape/serve_stream/stream_response.rb +23 -0
- data/lib/grape/types/invalid_value.rb +8 -0
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +17 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +100 -0
- data/lib/grape/util/inheritable_values.rb +29 -0
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +15 -0
- data/lib/grape/util/stackable_values.rb +36 -0
- data/lib/grape/util/strict_hash_configuration.rb +108 -0
- data/lib/grape/validations/attributes_doc.rb +60 -0
- data/lib/grape/validations/attributes_iterator.rb +62 -0
- data/lib/grape/validations/contract_scope.rb +34 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +538 -0
- data/lib/grape/validations/single_attribute_iterator.rb +26 -0
- data/lib/grape/validations/types/array_coercer.rb +61 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
- data/lib/grape/validations/types/file.rb +31 -0
- data/lib/grape/validations/types/invalid_value.rb +17 -0
- data/lib/grape/validations/types/json.rb +71 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
- data/lib/grape/validations/types/primitive_coercer.rb +73 -0
- data/lib/grape/validations/types/set_coercer.rb +35 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
- data/lib/grape/validations/types.rb +213 -0
- data/lib/grape/validations/validator_factory.rb +15 -0
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +88 -0
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +37 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/length_validator.rb +49 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +60 -0
- data/lib/grape/validations.rb +21 -0
- data/lib/grape/version.rb +6 -0
- data/lib/grape/xml.rb +10 -0
- data/lib/grape.rb +81 -17
- metadata +250 -192
- data/.document +0 -5
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.rvmrc +0 -1
- data/Gemfile +0 -21
- data/Gemfile.lock +0 -58
- data/README.markdown +0 -75
- data/Rakefile +0 -70
- data/VERSION +0 -1
- data/autotest/discover.rb +0 -1
- data/lib/grape/middleware/auth/basic.rb +0 -30
- data/lib/grape/middleware/auth/oauth2.rb +0 -55
- data/lib/grape/middleware/prefixer.rb +0 -21
- data/lib/grape/middleware_stack.rb +0 -35
- data/spec/grape/api_spec.rb +0 -343
- data/spec/grape/endpoint_spec.rb +0 -104
- data/spec/grape/middleware/auth/basic_spec.rb +0 -31
- data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
- data/spec/grape/middleware/base_spec.rb +0 -63
- data/spec/grape/middleware/error_spec.rb +0 -49
- data/spec/grape/middleware/formatter_spec.rb +0 -87
- data/spec/grape/middleware/prefixer_spec.rb +0 -30
- data/spec/grape/middleware/versioner_spec.rb +0 -40
- data/spec/grape/middleware_stack_spec.rb +0 -47
- data/spec/grape_spec.rb +0 -1
- data/spec/spec_helper.rb +0 -20
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Util
|
|
5
|
+
module Lazy
|
|
6
|
+
class ValueHash < ValueEnumerable
|
|
7
|
+
def initialize(hash)
|
|
8
|
+
super
|
|
9
|
+
@value_hash = ActiveSupport::HashWithIndifferentAccess.new
|
|
10
|
+
hash.each do |key, value|
|
|
11
|
+
self[key] = value
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def evaluate
|
|
16
|
+
@value_hash.transform_values(&:evaluate)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Util
|
|
5
|
+
class MediaType
|
|
6
|
+
attr_reader :type, :subtype, :vendor, :version, :format
|
|
7
|
+
|
|
8
|
+
# based on the HTTP Accept header with the pattern:
|
|
9
|
+
# application/vnd.:vendor-:version+:format
|
|
10
|
+
VENDOR_VERSION_HEADER_REGEX = /\Avnd\.(?<vendor>[a-z0-9.\-_!^]+?)(?:-(?<version>[a-z0-9*.]+))?(?:\+(?<format>[a-z0-9*\-.]+))?\z/.freeze
|
|
11
|
+
|
|
12
|
+
def initialize(type:, subtype:)
|
|
13
|
+
@type = type
|
|
14
|
+
@subtype = subtype
|
|
15
|
+
VENDOR_VERSION_HEADER_REGEX.match(subtype) do |m|
|
|
16
|
+
@vendor = m[:vendor]
|
|
17
|
+
@version = m[:version]
|
|
18
|
+
@format = m[:format]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ==(other)
|
|
23
|
+
eql?(other)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def eql?(other)
|
|
27
|
+
self.class == other.class &&
|
|
28
|
+
other.type == type &&
|
|
29
|
+
other.subtype == subtype &&
|
|
30
|
+
other.vendor == vendor &&
|
|
31
|
+
other.version == version &&
|
|
32
|
+
other.format == format
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def hash
|
|
36
|
+
[self.class, type, subtype, vendor, version, format].hash
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
def best_quality(header, available_media_types)
|
|
41
|
+
parse(best_quality_media_type(header, available_media_types))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def parse(media_type)
|
|
45
|
+
return if media_type.blank?
|
|
46
|
+
|
|
47
|
+
type, subtype = media_type.split('/', 2)
|
|
48
|
+
return if type.blank? || subtype.blank?
|
|
49
|
+
|
|
50
|
+
new(type: type, subtype: subtype)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def match?(media_type)
|
|
54
|
+
return false if media_type.blank?
|
|
55
|
+
|
|
56
|
+
subtype = media_type.split('/', 2).last
|
|
57
|
+
return false if subtype.blank?
|
|
58
|
+
|
|
59
|
+
VENDOR_VERSION_HEADER_REGEX.match?(subtype)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def best_quality_media_type(header, available_media_types)
|
|
63
|
+
header.blank? ? available_media_types.first : Rack::Utils.best_q_match(header, available_media_types)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private_class_method :best_quality_media_type
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Util
|
|
5
|
+
module Registry
|
|
6
|
+
def register(klass)
|
|
7
|
+
short_name = build_short_name(klass)
|
|
8
|
+
return if short_name.nil?
|
|
9
|
+
|
|
10
|
+
warn "#{short_name} is already registered with class #{klass}" if registry.key?(short_name)
|
|
11
|
+
registry[short_name] = klass
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def build_short_name(klass)
|
|
17
|
+
return if klass.name.blank?
|
|
18
|
+
|
|
19
|
+
klass.name.demodulize.underscore
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def registry
|
|
23
|
+
@registry ||= {}.with_indifferent_access
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Util
|
|
5
|
+
class ReverseStackableValues < StackableValues
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
def concat_values(inherited_value, new_value)
|
|
9
|
+
return inherited_value unless new_value
|
|
10
|
+
|
|
11
|
+
new_value + inherited_value
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Util
|
|
5
|
+
class StackableValues < BaseInheritable
|
|
6
|
+
# Even if there is no value, an empty array will be returned.
|
|
7
|
+
def [](name)
|
|
8
|
+
inherited_value = inherited_values[name]
|
|
9
|
+
new_value = new_values[name]
|
|
10
|
+
|
|
11
|
+
return new_value || [] unless inherited_value
|
|
12
|
+
|
|
13
|
+
concat_values(inherited_value, new_value)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def []=(name, value)
|
|
17
|
+
new_values[name] ||= []
|
|
18
|
+
new_values[name].push value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_hash
|
|
22
|
+
keys.each_with_object({}) do |key, result|
|
|
23
|
+
result[key] = self[key]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
def concat_values(inherited_value, new_value)
|
|
30
|
+
return inherited_value unless new_value
|
|
31
|
+
|
|
32
|
+
inherited_value + new_value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Util
|
|
5
|
+
module StrictHashConfiguration
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module DSL
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def settings
|
|
13
|
+
config_context.to_hash
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def configure(&block)
|
|
17
|
+
config_context.instance_exec(&block)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class SettingsContainer
|
|
23
|
+
def initialize
|
|
24
|
+
@settings = {}
|
|
25
|
+
@contexts = {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_hash
|
|
29
|
+
@settings.to_hash
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.config_class(*args)
|
|
34
|
+
new_config_class = Class.new(SettingsContainer)
|
|
35
|
+
|
|
36
|
+
args.each do |setting_name|
|
|
37
|
+
if setting_name.respond_to? :values
|
|
38
|
+
nested_settings_methods(setting_name, new_config_class)
|
|
39
|
+
else
|
|
40
|
+
simple_settings_methods(setting_name, new_config_class)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
new_config_class
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.simple_settings_methods(setting_name, new_config_class)
|
|
48
|
+
setting_name_sym = setting_name.to_sym
|
|
49
|
+
new_config_class.class_eval do
|
|
50
|
+
define_method setting_name do |new_value|
|
|
51
|
+
@settings[setting_name_sym] = new_value
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.nested_settings_methods(setting_name, new_config_class)
|
|
57
|
+
new_config_class.class_eval do
|
|
58
|
+
setting_name.each_pair do |key, value|
|
|
59
|
+
define_method :"#{key}_context" do
|
|
60
|
+
@contexts[key] ||= Grape::Util::StrictHashConfiguration.config_class(*value).new
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
define_method key do |&block|
|
|
64
|
+
send(:"#{key}_context").instance_exec(&block)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
define_method :to_hash do
|
|
69
|
+
@settings.to_hash.merge(
|
|
70
|
+
setting_name.each_key.with_object({}) do |k, merge_hash|
|
|
71
|
+
merge_hash[k] = send(:"#{k}_context").to_hash
|
|
72
|
+
end
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.module(*args)
|
|
79
|
+
new_module = Module.new do
|
|
80
|
+
extend ActiveSupport::Concern
|
|
81
|
+
include DSL
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
new_module.tap do |mod|
|
|
85
|
+
class_mod = create_class_mod(args)
|
|
86
|
+
|
|
87
|
+
mod.const_set(:ClassMethods, class_mod)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.create_class_mod(args)
|
|
92
|
+
new_module = Module.new do
|
|
93
|
+
def config_context
|
|
94
|
+
@config_context ||= config_class.new
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
new_module.tap do |class_mod|
|
|
99
|
+
new_config_class = config_class(*args)
|
|
100
|
+
|
|
101
|
+
class_mod.send(:define_method, :config_class) do
|
|
102
|
+
@config_class ||= new_config_class
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
# Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
|
|
6
|
+
# internal API), the class only cleans up attributes to avoid junk in RAM.
|
|
7
|
+
|
|
8
|
+
class AttributesDoc
|
|
9
|
+
attr_accessor :type, :values
|
|
10
|
+
|
|
11
|
+
# @param api [Grape::API::Instance]
|
|
12
|
+
# @param scope [Validations::ParamsScope]
|
|
13
|
+
def initialize(api, scope)
|
|
14
|
+
@api = api
|
|
15
|
+
@scope = scope
|
|
16
|
+
@type = type
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def extract_details(validations)
|
|
20
|
+
details[:required] = validations.key?(:presence)
|
|
21
|
+
|
|
22
|
+
desc = validations.delete(:desc) || validations.delete(:description)
|
|
23
|
+
|
|
24
|
+
details[:desc] = desc if desc
|
|
25
|
+
|
|
26
|
+
documentation = validations.delete(:documentation)
|
|
27
|
+
|
|
28
|
+
details[:documentation] = documentation if documentation
|
|
29
|
+
|
|
30
|
+
details[:default] = validations[:default] if validations.key?(:default)
|
|
31
|
+
|
|
32
|
+
details[:min_length] = validations[:length][:min] if validations.key?(:length) && validations[:length].key?(:min)
|
|
33
|
+
details[:max_length] = validations[:length][:max] if validations.key?(:length) && validations[:length].key?(:max)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def document(attrs)
|
|
37
|
+
return if @api.namespace_inheritable(:do_not_document)
|
|
38
|
+
|
|
39
|
+
details[:type] = type.to_s if type
|
|
40
|
+
details[:values] = values if values
|
|
41
|
+
|
|
42
|
+
documented_attrs = attrs.each_with_object({}) do |name, memo|
|
|
43
|
+
memo[@scope.full_name(name)] = details
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@api.namespace_stackable(:params, documented_attrs)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def required
|
|
50
|
+
details[:required]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def details
|
|
56
|
+
@details ||= {}
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
class AttributesIterator
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
attr_reader :scope
|
|
9
|
+
|
|
10
|
+
def initialize(validator, scope, params)
|
|
11
|
+
@scope = scope
|
|
12
|
+
@attrs = validator.attrs
|
|
13
|
+
@original_params = scope.params(params)
|
|
14
|
+
@params = Array.wrap(@original_params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def each(&block)
|
|
18
|
+
do_each(@params, &block) # because we need recursion for nested arrays
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def do_each(params_to_process, parent_indicies = [], &block)
|
|
24
|
+
@scope.reset_index # gets updated depending on the size of params_to_process
|
|
25
|
+
params_to_process.each_with_index do |resource_params, index|
|
|
26
|
+
# when we get arrays of arrays it means that target element located inside array
|
|
27
|
+
# we need this because we want to know parent arrays indicies
|
|
28
|
+
if resource_params.is_a?(Array)
|
|
29
|
+
do_each(resource_params, [index] + parent_indicies, &block)
|
|
30
|
+
next
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if @scope.type == Array
|
|
34
|
+
next unless @original_params.is_a?(Array) # do not validate content of array if it isn't array
|
|
35
|
+
|
|
36
|
+
# fill current and parent scopes with correct array indicies
|
|
37
|
+
parent_scope = @scope.parent
|
|
38
|
+
parent_indicies.each do |parent_index|
|
|
39
|
+
parent_scope.index = parent_index
|
|
40
|
+
parent_scope = parent_scope.parent
|
|
41
|
+
end
|
|
42
|
+
@scope.index = index
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
yield_attributes(resource_params, @attrs, &block)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def yield_attributes(_resource_params, _attrs)
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# This is a special case so that we can ignore tree's where option
|
|
54
|
+
# values are missing lower down. Unfortunately we can remove this
|
|
55
|
+
# are the parameter parsing stage as they are required to ensure
|
|
56
|
+
# the correct indexing is maintained
|
|
57
|
+
def skip?(val)
|
|
58
|
+
val == Grape::DSL::Parameters::EmptyOptionalValue
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
class ContractScope
|
|
6
|
+
# Declare the contract to be used for the endpoint's parameters.
|
|
7
|
+
# @param api [API] the API endpoint to modify.
|
|
8
|
+
# @param contract the contract or schema to be used for validation. Optional.
|
|
9
|
+
# @yield a block yielding a new schema class. Optional.
|
|
10
|
+
def initialize(api, contract = nil, &block)
|
|
11
|
+
# When block is passed, the first arg is either schema or nil.
|
|
12
|
+
contract = Dry::Schema.Params(parent: contract, &block) if block
|
|
13
|
+
|
|
14
|
+
if contract.respond_to?(:schema)
|
|
15
|
+
# It's a Dry::Validation::Contract, then.
|
|
16
|
+
contract = contract.new
|
|
17
|
+
key_map = contract.schema.key_map
|
|
18
|
+
else
|
|
19
|
+
# Dry::Schema::Processor, hopefully.
|
|
20
|
+
key_map = contract.key_map
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
api.namespace_stackable(:contract_key_map, key_map)
|
|
24
|
+
|
|
25
|
+
validator_options = {
|
|
26
|
+
validator_class: Grape::Validations.require_validator(:contract_scope),
|
|
27
|
+
opts: { schema: contract, fail_fast: false }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
api.namespace_stackable(:validations, validator_options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
class MultipleAttributesIterator < AttributesIterator
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def yield_attributes(resource_params, _attrs)
|
|
9
|
+
yield resource_params unless skip?(resource_params)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|