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
data/lib/grape/api.rb
CHANGED
|
@@ -1,214 +1,178 @@
|
|
|
1
|
-
|
|
2
|
-
require 'rack/auth/basic'
|
|
3
|
-
require 'logger'
|
|
1
|
+
# frozen_string_literal: true
|
|
4
2
|
|
|
5
3
|
module Grape
|
|
6
|
-
# The API class is the primary entry point for
|
|
7
|
-
#
|
|
8
|
-
# class in order to build an API.
|
|
4
|
+
# The API class is the primary entry point for creating Grape APIs. Users
|
|
5
|
+
# should subclass this class in order to build an API.
|
|
9
6
|
class API
|
|
7
|
+
# Class methods that we want to call on the API rather than on the API object
|
|
8
|
+
NON_OVERRIDABLE = %i[call call! configuration compile! inherited recognize_path].freeze
|
|
9
|
+
|
|
10
|
+
class Boolean
|
|
11
|
+
def self.build(val)
|
|
12
|
+
return nil if val != true && val != false
|
|
13
|
+
|
|
14
|
+
new
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Instance
|
|
19
|
+
Boolean = Grape::API::Boolean
|
|
20
|
+
end
|
|
21
|
+
|
|
10
22
|
class << self
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def
|
|
36
|
-
@
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def version(*new_versions, &block)
|
|
59
|
-
new_versions.any? ? nest(block){ set(:version, new_versions) } : settings[:version]
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Specify the default format for the API's
|
|
63
|
-
# serializers. Currently only `:json` is
|
|
64
|
-
# supported.
|
|
65
|
-
def default_format(new_format = nil)
|
|
66
|
-
new_format ? set(:default_format, new_format.to_sym) : settings[:default_format]
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Add helper methods that will be accessible from any
|
|
70
|
-
# endpoint within this namespace (and child namespaces).
|
|
71
|
-
#
|
|
72
|
-
# class ExampleAPI
|
|
73
|
-
# helpers do
|
|
74
|
-
# def current_user
|
|
75
|
-
# User.find_by_id(params[:token])
|
|
76
|
-
# end
|
|
77
|
-
# end
|
|
78
|
-
# end
|
|
79
|
-
def helpers(&block)
|
|
23
|
+
extend Forwardable
|
|
24
|
+
attr_accessor :base_instance, :instances
|
|
25
|
+
|
|
26
|
+
delegate_missing_to :base_instance
|
|
27
|
+
def_delegators :base_instance, :new, :configuration
|
|
28
|
+
|
|
29
|
+
# This is the interface point between Rack and Grape; it accepts a request
|
|
30
|
+
# from Rack and ultimately returns an array of three values: the status,
|
|
31
|
+
# the headers, and the body. See [the rack specification]
|
|
32
|
+
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
|
33
|
+
# NOTE: This will only be called on an API directly mounted on RACK
|
|
34
|
+
def_delegators :instance_for_rack, :call, :compile!
|
|
35
|
+
|
|
36
|
+
# When inherited, will create a list of all instances (times the API was mounted)
|
|
37
|
+
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
|
38
|
+
def inherited(api)
|
|
39
|
+
super
|
|
40
|
+
|
|
41
|
+
api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
|
|
42
|
+
api.override_all_methods!
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Initialize the instance variables on the remountable class, and the base_instance
|
|
46
|
+
# an instance that will be used to create the set up but will not be mounted
|
|
47
|
+
def initial_setup(base_instance_parent)
|
|
48
|
+
@instances = []
|
|
49
|
+
@setup = []
|
|
50
|
+
@base_parent = base_instance_parent
|
|
51
|
+
@base_instance = mount_instance
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Redefines all methods so that are forwarded to add_setup and be recorded
|
|
55
|
+
def override_all_methods!
|
|
56
|
+
(base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
|
|
57
|
+
define_singleton_method(method_override) do |*args, &block|
|
|
58
|
+
add_setup(method: method_override, args: args, block: block)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Configure an API from the outside. If a block is given, it'll pass a
|
|
64
|
+
# configuration hash to the block which you can use to configure your
|
|
65
|
+
# API. If no block is given, returns the configuration hash.
|
|
66
|
+
# The configuration set here is accessible from inside an API with
|
|
67
|
+
# `configuration` as normal.
|
|
68
|
+
def configure
|
|
69
|
+
config = @base_instance.configuration
|
|
80
70
|
if block_given?
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
set(:helpers, m)
|
|
71
|
+
yield config
|
|
72
|
+
self
|
|
84
73
|
else
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
config
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The remountable class can have a configuration hash to provide some dynamic class-level variables.
|
|
79
|
+
# For instance, a description could be done using: `desc configuration[:description]` if it may vary
|
|
80
|
+
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
|
|
81
|
+
# too much, you may actually want to provide a new API rather than remount it.
|
|
82
|
+
def mount_instance(configuration: nil)
|
|
83
|
+
Class.new(@base_parent).tap do |instance|
|
|
84
|
+
instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
|
|
85
|
+
instance.base = self
|
|
86
|
+
replay_setup_on(instance)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# Replays the set up to produce an API as defined in this class, can be called
|
|
93
|
+
# on classes that inherit from Grape::API
|
|
94
|
+
def replay_setup_on(instance)
|
|
95
|
+
@setup.each do |setup_step|
|
|
96
|
+
replay_step_on(instance, **setup_step)
|
|
90
97
|
end
|
|
91
98
|
end
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if type
|
|
97
|
-
set(:auth, {:type => type.to_sym, :proc => block}.merge(options))
|
|
99
|
+
|
|
100
|
+
def instance_for_rack
|
|
101
|
+
if never_mounted?
|
|
102
|
+
base_instance
|
|
98
103
|
else
|
|
99
|
-
|
|
104
|
+
mounted_instances.first
|
|
100
105
|
end
|
|
101
106
|
end
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
auth :http_basic, options, &block
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Defines a route that will be recognized
|
|
113
|
-
# by the Grape API.
|
|
114
|
-
#
|
|
115
|
-
# @param methods [HTTP Verb(s)] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
|
|
116
|
-
# @param paths [String(s)] One or more strings representing the URL segment(s) for this route.
|
|
117
|
-
# @param block [Proc] The code to be executed
|
|
118
|
-
def route(methods, paths, &block)
|
|
119
|
-
methods = Array(methods)
|
|
120
|
-
paths = ['/'] if paths == []
|
|
121
|
-
paths = Array(paths)
|
|
122
|
-
endpoint = build_endpoint(&block)
|
|
123
|
-
|
|
124
|
-
methods.each do |method|
|
|
125
|
-
paths.each do |path|
|
|
126
|
-
path = Rack::Mount::Strexp.compile(compile_path(path))
|
|
127
|
-
route_set.add_route(endpoint,
|
|
128
|
-
:path_info => path,
|
|
129
|
-
:request_method => (method.to_s.upcase unless method == :any)
|
|
130
|
-
)
|
|
131
|
-
end
|
|
107
|
+
|
|
108
|
+
# Adds a new stage to the set up require to get a Grape::API up and running
|
|
109
|
+
def add_setup(step)
|
|
110
|
+
@setup << step
|
|
111
|
+
last_response = nil
|
|
112
|
+
@instances.each do |instance|
|
|
113
|
+
last_response = replay_step_on(instance, **step)
|
|
132
114
|
end
|
|
115
|
+
|
|
116
|
+
refresh_mount_step if step[:method] != :mount
|
|
117
|
+
last_response
|
|
133
118
|
end
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
set(:namespace, space.to_s) if space
|
|
119
|
+
|
|
120
|
+
# Updating all previously mounted classes in the case that new methods have been executed.
|
|
121
|
+
def refresh_mount_step
|
|
122
|
+
@setup.each do |setup_step|
|
|
123
|
+
next if setup_step[:method] != :mount
|
|
124
|
+
|
|
125
|
+
refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
|
|
126
|
+
@setup << refresh_mount_step
|
|
127
|
+
@instances.each do |instance|
|
|
128
|
+
replay_step_on(instance, **refresh_mount_step)
|
|
145
129
|
end
|
|
146
|
-
else
|
|
147
|
-
Rack::Mount::Utils.normalize_path(settings_stack.map{|s| s[:namespace]}.join('/'))
|
|
148
130
|
end
|
|
149
131
|
end
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def scope(name = nil, &block)
|
|
159
|
-
nest(block)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
protected
|
|
163
|
-
|
|
164
|
-
# Execute first the provided block, then each of the
|
|
165
|
-
# block passed in. Allows for simple 'before' setups
|
|
166
|
-
# of settings stack pushes.
|
|
167
|
-
def nest(*blocks, &block)
|
|
168
|
-
blocks.reject!{|b| b.nil?}
|
|
169
|
-
if blocks.any?
|
|
170
|
-
settings_stack << {}
|
|
171
|
-
instance_eval &block if block_given?
|
|
172
|
-
blocks.each{|b| instance_eval &b}
|
|
173
|
-
settings_stack.pop
|
|
132
|
+
|
|
133
|
+
def replay_step_on(instance, method:, args:, block:)
|
|
134
|
+
return if skip_immediate_run?(instance, args)
|
|
135
|
+
|
|
136
|
+
eval_args = evaluate_arguments(instance.configuration, *args)
|
|
137
|
+
response = instance.send(method, *eval_args, &block)
|
|
138
|
+
if skip_immediate_run?(instance, [response])
|
|
139
|
+
response
|
|
174
140
|
else
|
|
175
|
-
|
|
141
|
+
evaluate_arguments(instance.configuration, response).first
|
|
176
142
|
end
|
|
177
143
|
end
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
reset!
|
|
144
|
+
|
|
145
|
+
# Skips steps that contain arguments to be lazily executed (on re-mount time)
|
|
146
|
+
def skip_immediate_run?(instance, args)
|
|
147
|
+
instance.base_instance? &&
|
|
148
|
+
(any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) })
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def any_lazy?(args)
|
|
152
|
+
args.any? { |argument| argument.try(:lazy?) }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def evaluate_arguments(configuration, *args)
|
|
156
|
+
args.map do |argument|
|
|
157
|
+
if argument.try(:lazy?)
|
|
158
|
+
argument.evaluate_from(configuration)
|
|
159
|
+
elsif argument.is_a?(Hash)
|
|
160
|
+
argument.transform_values { |value| evaluate_arguments(configuration, value).first }
|
|
161
|
+
elsif argument.is_a?(Array)
|
|
162
|
+
evaluate_arguments(configuration, *argument)
|
|
163
|
+
else
|
|
164
|
+
argument
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def never_mounted?
|
|
170
|
+
mounted_instances.empty?
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def mounted_instances
|
|
174
|
+
instances - [base_instance]
|
|
175
|
+
end
|
|
176
|
+
end
|
|
213
177
|
end
|
|
214
|
-
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module ContentTypes
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Content types are listed in order of preference.
|
|
8
|
+
DEFAULTS = {
|
|
9
|
+
xml: 'application/xml',
|
|
10
|
+
serializable_hash: 'application/json',
|
|
11
|
+
json: 'application/json',
|
|
12
|
+
binary: 'application/octet-stream',
|
|
13
|
+
txt: 'text/plain'
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
MIME_TYPES = Grape::ContentTypes::DEFAULTS.except(:serializable_hash).invert.freeze
|
|
17
|
+
|
|
18
|
+
def content_types_for(from_settings)
|
|
19
|
+
from_settings.presence || DEFAULTS
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def mime_types_for(from_settings)
|
|
23
|
+
return MIME_TYPES if from_settings == Grape::ContentTypes::DEFAULTS
|
|
24
|
+
|
|
25
|
+
from_settings.each_with_object({}) do |(k, v), types_without_params|
|
|
26
|
+
# remove optional parameter
|
|
27
|
+
types_without_params[v.split(';', 2).first] = k
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
class Cookies
|
|
5
|
+
extend Forwardable
|
|
6
|
+
|
|
7
|
+
DELETED_COOKIES_ATTRS = {
|
|
8
|
+
max_age: '0',
|
|
9
|
+
value: '',
|
|
10
|
+
expires: Time.at(0)
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def_delegators :cookies, :[], :each
|
|
14
|
+
|
|
15
|
+
def initialize(rack_cookies)
|
|
16
|
+
@cookies = rack_cookies
|
|
17
|
+
@send_cookies = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def response_cookies
|
|
21
|
+
return unless @send_cookies
|
|
22
|
+
|
|
23
|
+
send_cookies.each do |name|
|
|
24
|
+
yield name, cookies[name]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def []=(name, value)
|
|
29
|
+
cookies[name] = value
|
|
30
|
+
send_cookies << name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
|
|
34
|
+
def delete(name, **opts)
|
|
35
|
+
self.[]=(name, opts.merge(DELETED_COOKIES_ATTRS))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def cookies
|
|
41
|
+
return @cookies unless @cookies.is_a?(Proc)
|
|
42
|
+
|
|
43
|
+
@cookies = @cookies.call.with_indifferent_access
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def send_cookies
|
|
47
|
+
@send_cookies ||= Set.new
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DryTypes
|
|
5
|
+
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
|
6
|
+
# a container in this case. Check documentation for more information
|
|
7
|
+
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
|
|
8
|
+
include Dry.Types()
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DSL
|
|
5
|
+
module API
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
include Grape::DSL::Validations
|
|
9
|
+
include Grape::DSL::Callbacks
|
|
10
|
+
include Grape::DSL::Configuration
|
|
11
|
+
include Grape::DSL::Helpers
|
|
12
|
+
include Grape::DSL::Middleware
|
|
13
|
+
include Grape::DSL::RequestResponse
|
|
14
|
+
include Grape::DSL::Routing
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DSL
|
|
5
|
+
# Blocks can be executed before or after every API call, using `before`, `after`,
|
|
6
|
+
# `before_validation` and `after_validation`.
|
|
7
|
+
#
|
|
8
|
+
# Before and after callbacks execute in the following order:
|
|
9
|
+
#
|
|
10
|
+
# 1. `before`
|
|
11
|
+
# 2. `before_validation`
|
|
12
|
+
# 3. _validations_
|
|
13
|
+
# 4. `after_validation`
|
|
14
|
+
# 5. _the API call_
|
|
15
|
+
# 6. `after`
|
|
16
|
+
#
|
|
17
|
+
# Steps 4, 5 and 6 only happen if validation succeeds.
|
|
18
|
+
module Callbacks
|
|
19
|
+
extend ActiveSupport::Concern
|
|
20
|
+
|
|
21
|
+
include Grape::DSL::Configuration
|
|
22
|
+
|
|
23
|
+
module ClassMethods
|
|
24
|
+
# Execute the given block before validation, coercion, or any endpoint
|
|
25
|
+
# code is executed.
|
|
26
|
+
def before(&block)
|
|
27
|
+
namespace_stackable(:befores, block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Execute the given block after `before`, but prior to validation or
|
|
31
|
+
# coercion.
|
|
32
|
+
def before_validation(&block)
|
|
33
|
+
namespace_stackable(:before_validations, block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Execute the given block after validations and coercions, but before
|
|
37
|
+
# any endpoint code.
|
|
38
|
+
def after_validation(&block)
|
|
39
|
+
namespace_stackable(:after_validations, block)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Execute the given block after the endpoint code has run.
|
|
43
|
+
def after(&block)
|
|
44
|
+
namespace_stackable(:afters, block)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Allows you to specify a something that will always be executed after a call
|
|
48
|
+
# API call. Unlike the `after` block, this code will run even on
|
|
49
|
+
# unsuccesful requests.
|
|
50
|
+
# @example
|
|
51
|
+
# class ExampleAPI < Grape::API
|
|
52
|
+
# before do
|
|
53
|
+
# ApiLogger.start
|
|
54
|
+
# end
|
|
55
|
+
# finally do
|
|
56
|
+
# ApiLogger.close
|
|
57
|
+
# end
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# This will make sure that the ApiLogger is opened and closed around every
|
|
61
|
+
# request
|
|
62
|
+
# @param ensured_block [Proc] The block to be executed after every api_call
|
|
63
|
+
def finally(&block)
|
|
64
|
+
namespace_stackable(:finallies, block)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DSL
|
|
5
|
+
module Configuration
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
include Grape::DSL::Settings
|
|
10
|
+
include Grape::DSL::Logger
|
|
11
|
+
include Grape::DSL::Desc
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|