grape 1.3.1 → 1.5.1
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/CHANGELOG.md +82 -1
- data/LICENSE +1 -1
- data/README.md +104 -21
- data/UPGRADING.md +265 -39
- data/lib/grape.rb +2 -2
- data/lib/grape/api.rb +2 -2
- data/lib/grape/api/instance.rb +29 -28
- data/lib/grape/dsl/helpers.rb +1 -0
- data/lib/grape/dsl/inside_route.rb +69 -36
- data/lib/grape/dsl/parameters.rb +7 -3
- data/lib/grape/dsl/routing.rb +2 -4
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +8 -6
- data/lib/grape/http/headers.rb +1 -0
- data/lib/grape/middleware/base.rb +2 -1
- data/lib/grape/middleware/error.rb +10 -12
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/stack.rb +21 -8
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/path.rb +2 -2
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router.rb +30 -43
- data/lib/grape/router/attribute_translator.rb +26 -5
- data/lib/grape/router/route.rb +3 -22
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +11 -8
- data/lib/grape/util/lazy_value.rb +1 -0
- data/lib/grape/util/reverse_stackable_values.rb +3 -1
- data/lib/grape/util/stackable_values.rb +3 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +8 -6
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types.rb +6 -5
- data/lib/grape/validations/types/array_coercer.rb +14 -5
- data/lib/grape/validations/types/build_coercer.rb +5 -8
- data/lib/grape/validations/types/custom_type_coercer.rb +14 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
- data/lib/grape/validations/types/file.rb +15 -13
- data/lib/grape/validations/types/json.rb +40 -36
- data/lib/grape/validations/types/primitive_coercer.rb +11 -5
- data/lib/grape/validations/types/set_coercer.rb +6 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/as.rb +1 -1
- data/lib/grape/validations/validators/base.rb +4 -5
- data/lib/grape/validations/validators/coerce.rb +3 -10
- data/lib/grape/validations/validators/default.rb +3 -5
- data/lib/grape/validations/validators/except_values.rb +1 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/instance_spec.rb +50 -0
- data/spec/grape/api_spec.rb +75 -0
- data/spec/grape/dsl/inside_route_spec.rb +182 -33
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +0 -521
- data/spec/grape/entity_spec.rb +6 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
- data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +1 -1
- data/spec/grape/middleware/stack_spec.rb +3 -1
- data/spec/grape/path_spec.rb +4 -4
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
- data/spec/grape/validations/params_scope_spec.rb +26 -0
- data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +65 -5
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +317 -29
- data/spec/grape/validations/validators/default_spec.rb +170 -0
- data/spec/grape/validations/validators/except_values_spec.rb +1 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +290 -18
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/spec_helper.rb +0 -10
- data/spec/support/chunks.rb +14 -0
- data/spec/support/versioned_helpers.rb +3 -5
- metadata +18 -8
@@ -19,11 +19,11 @@ module Grape
|
|
19
19
|
rescue_subclasses: true, # rescue subclasses of exceptions listed
|
20
20
|
rescue_options: {
|
21
21
|
backtrace: false, # true to display backtrace, true to let Grape handle Grape::Exceptions
|
22
|
-
original_exception: false
|
22
|
+
original_exception: false # true to display exception
|
23
23
|
},
|
24
24
|
rescue_handlers: {}, # rescue handler blocks
|
25
25
|
base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class
|
26
|
-
all_rescue_handler: nil
|
26
|
+
all_rescue_handler: nil # rescue handler block to rescue from all exceptions
|
27
27
|
}
|
28
28
|
end
|
29
29
|
|
@@ -38,15 +38,15 @@ module Grape
|
|
38
38
|
error_response(catch(:error) do
|
39
39
|
return @app.call(@env)
|
40
40
|
end)
|
41
|
-
rescue Exception =>
|
41
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
42
42
|
handler =
|
43
|
-
rescue_handler_for_base_only_class(
|
44
|
-
rescue_handler_for_class_or_its_ancestor(
|
45
|
-
rescue_handler_for_grape_exception(
|
46
|
-
rescue_handler_for_any_class(
|
43
|
+
rescue_handler_for_base_only_class(e.class) ||
|
44
|
+
rescue_handler_for_class_or_its_ancestor(e.class) ||
|
45
|
+
rescue_handler_for_grape_exception(e.class) ||
|
46
|
+
rescue_handler_for_any_class(e.class) ||
|
47
47
|
raise
|
48
48
|
|
49
|
-
run_rescue_handler(handler,
|
49
|
+
run_rescue_handler(handler, e)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -65,15 +65,13 @@ module Grape
|
|
65
65
|
message = error[:message] || options[:default_message]
|
66
66
|
headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }
|
67
67
|
headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
|
68
|
-
backtrace = error[:backtrace] || error[:original_exception]
|
68
|
+
backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
|
69
69
|
original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
|
70
70
|
rack_response(format_message(message, backtrace, original_exception), status, headers)
|
71
71
|
end
|
72
72
|
|
73
73
|
def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
|
74
|
-
if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
|
75
|
-
message = ERB::Util.html_escape(message)
|
76
|
-
end
|
74
|
+
message = ERB::Util.html_escape(message) if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
|
77
75
|
Rack::Response.new([message], status, headers)
|
78
76
|
end
|
79
77
|
|
@@ -36,9 +36,9 @@ module Grape
|
|
36
36
|
def build_formatted_response(status, headers, bodies)
|
37
37
|
headers = ensure_content_type(headers)
|
38
38
|
|
39
|
-
if bodies.is_a?(Grape::
|
40
|
-
Grape::
|
41
|
-
resp.body = bodies.
|
39
|
+
if bodies.is_a?(Grape::ServeStream::StreamResponse)
|
40
|
+
Grape::ServeStream::SendfileResponse.new([], status, headers) do |resp|
|
41
|
+
resp.body = bodies.stream
|
42
42
|
end
|
43
43
|
else
|
44
44
|
# Allow content-type to be explicitly overwritten
|
@@ -6,11 +6,12 @@ module Grape
|
|
6
6
|
# It allows to insert and insert after
|
7
7
|
class Stack
|
8
8
|
class Middleware
|
9
|
-
attr_reader :args, :block, :klass
|
9
|
+
attr_reader :args, :opts, :block, :klass
|
10
10
|
|
11
|
-
def initialize(klass, *args, &block)
|
11
|
+
def initialize(klass, *args, **opts, &block)
|
12
12
|
@klass = klass
|
13
|
-
@args
|
13
|
+
@args = args
|
14
|
+
@opts = opts
|
14
15
|
@block = block
|
15
16
|
end
|
16
17
|
|
@@ -30,6 +31,18 @@ module Grape
|
|
30
31
|
def inspect
|
31
32
|
klass.to_s
|
32
33
|
end
|
34
|
+
|
35
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
|
36
|
+
def use_in(builder)
|
37
|
+
block ? builder.use(klass, *args, **opts, &block) : builder.use(klass, *args, **opts)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
def use_in(builder)
|
41
|
+
args = self.args
|
42
|
+
args += [opts] unless opts.empty?
|
43
|
+
block ? builder.use(klass, *args, &block) : builder.use(klass, *args)
|
44
|
+
end
|
45
|
+
end
|
33
46
|
end
|
34
47
|
|
35
48
|
include Enumerable
|
@@ -57,9 +70,9 @@ module Grape
|
|
57
70
|
middlewares[i]
|
58
71
|
end
|
59
72
|
|
60
|
-
def insert(index, *args, &block)
|
73
|
+
def insert(index, *args, **kwargs, &block)
|
61
74
|
index = assert_index(index, :before)
|
62
|
-
middleware = self.class::Middleware.new(*args, &block)
|
75
|
+
middleware = self.class::Middleware.new(*args, **kwargs, &block)
|
63
76
|
middlewares.insert(index, middleware)
|
64
77
|
end
|
65
78
|
|
@@ -70,8 +83,8 @@ module Grape
|
|
70
83
|
insert(index + 1, *args, &block)
|
71
84
|
end
|
72
85
|
|
73
|
-
def use(*args, &block)
|
74
|
-
middleware = self.class::Middleware.new(*args, &block)
|
86
|
+
def use(*args, **kwargs, &block)
|
87
|
+
middleware = self.class::Middleware.new(*args, **kwargs, &block)
|
75
88
|
middlewares.push(middleware)
|
76
89
|
end
|
77
90
|
|
@@ -90,7 +103,7 @@ module Grape
|
|
90
103
|
def build(builder = Rack::Builder.new)
|
91
104
|
others.shift(others.size).each(&method(:merge_with))
|
92
105
|
middlewares.each do |m|
|
93
|
-
m.
|
106
|
+
m.use_in(builder)
|
94
107
|
end
|
95
108
|
builder
|
96
109
|
end
|
@@ -3,11 +3,12 @@
|
|
3
3
|
module Rack
|
4
4
|
module Accept
|
5
5
|
module Header
|
6
|
+
ALLOWED_CHARACTERS = %r{^([a-z*]+)\/([a-z0-9*\&\^\-_#\$!.+]+)(?:;([a-z0-9=;]+))?$}.freeze
|
6
7
|
class << self
|
7
8
|
# Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
|
8
9
|
def parse_media_type(media_type)
|
9
10
|
# see http://tools.ietf.org/html/rfc6838#section-4.2 for allowed characters in media type names
|
10
|
-
m = media_type
|
11
|
+
m = media_type&.match(ALLOWED_CHARACTERS)
|
11
12
|
m ? [m[1], m[2], m[3] || ''] : []
|
12
13
|
end
|
13
14
|
end
|
data/lib/grape/path.rb
CHANGED
@@ -42,11 +42,11 @@ module Grape
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def namespace?
|
45
|
-
namespace
|
45
|
+
namespace&.match?(/^\S/) && namespace != '/'
|
46
46
|
end
|
47
47
|
|
48
48
|
def path?
|
49
|
-
raw_path
|
49
|
+
raw_path&.match?(/^\S/) && raw_path != '/'
|
50
50
|
end
|
51
51
|
|
52
52
|
def suffix
|
data/lib/grape/request.rb
CHANGED
data/lib/grape/router.rb
CHANGED
@@ -7,30 +7,12 @@ module Grape
|
|
7
7
|
class Router
|
8
8
|
attr_reader :map, :compiled
|
9
9
|
|
10
|
-
class Any < AttributeTranslator
|
11
|
-
attr_reader :pattern, :regexp, :index
|
12
|
-
def initialize(pattern, regexp, index, **attributes)
|
13
|
-
@pattern = pattern
|
14
|
-
@regexp = regexp
|
15
|
-
@index = index
|
16
|
-
super(attributes)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class NormalizePathCache < Grape::Util::Cache
|
21
|
-
def initialize
|
22
|
-
@cache = Hash.new do |h, path|
|
23
|
-
normalized_path = +"/#{path}"
|
24
|
-
normalized_path.squeeze!('/')
|
25
|
-
normalized_path.sub!(%r{/+\Z}, '')
|
26
|
-
normalized_path = '/' if normalized_path.empty?
|
27
|
-
h[path] = -normalized_path
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
10
|
def self.normalize_path(path)
|
33
|
-
|
11
|
+
path = +"/#{path}"
|
12
|
+
path.squeeze!('/')
|
13
|
+
path.sub!(%r{/+\Z}, '')
|
14
|
+
path = '/' if path == ''
|
15
|
+
path
|
34
16
|
end
|
35
17
|
|
36
18
|
def self.supported_methods
|
@@ -39,18 +21,20 @@ module Grape
|
|
39
21
|
|
40
22
|
def initialize
|
41
23
|
@neutral_map = []
|
24
|
+
@neutral_regexes = []
|
42
25
|
@map = Hash.new { |hash, key| hash[key] = [] }
|
43
26
|
@optimized_map = Hash.new { |hash, key| hash[key] = // }
|
44
27
|
end
|
45
28
|
|
46
29
|
def compile!
|
47
30
|
return if compiled
|
48
|
-
@union = Regexp.union(@
|
31
|
+
@union = Regexp.union(@neutral_regexes)
|
32
|
+
@neutral_regexes = nil
|
49
33
|
self.class.supported_methods.each do |method|
|
50
34
|
routes = map[method]
|
51
35
|
@optimized_map[method] = routes.map.with_index do |route, index|
|
52
36
|
route.index = index
|
53
|
-
|
37
|
+
Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
|
54
38
|
end
|
55
39
|
@optimized_map[method] = Regexp.union(@optimized_map[method])
|
56
40
|
end
|
@@ -62,8 +46,8 @@ module Grape
|
|
62
46
|
end
|
63
47
|
|
64
48
|
def associate_routes(pattern, **options)
|
65
|
-
|
66
|
-
@neutral_map <<
|
49
|
+
@neutral_regexes << Regexp.new("(?<_#{@neutral_map.length}>)#{pattern.to_regexp}")
|
50
|
+
@neutral_map << Grape::Router::AttributeTranslator.new(**options, pattern: pattern, index: @neutral_map.length)
|
67
51
|
end
|
68
52
|
|
69
53
|
def call(env)
|
@@ -107,37 +91,34 @@ module Grape
|
|
107
91
|
response = yield(input, method)
|
108
92
|
|
109
93
|
return response if response && !(cascade = cascade?(response))
|
110
|
-
|
94
|
+
last_neighbor_route = greedy_match?(input)
|
111
95
|
|
112
|
-
# If
|
96
|
+
# If last_neighbor_route exists and request method is OPTIONS,
|
113
97
|
# return response by using #call_with_allow_headers.
|
114
|
-
return call_with_allow_headers(
|
115
|
-
env,
|
116
|
-
neighbor.allow_header,
|
117
|
-
neighbor.endpoint
|
118
|
-
) if neighbor && method == Grape::Http::Headers::OPTIONS && !cascade
|
98
|
+
return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Grape::Http::Headers::OPTIONS && !cascade
|
119
99
|
|
120
100
|
route = match?(input, '*')
|
121
|
-
|
101
|
+
|
102
|
+
return last_neighbor_route.endpoint.call(env) if last_neighbor_route && cascade && route
|
122
103
|
|
123
104
|
if route
|
124
105
|
response = process_route(route, env)
|
125
106
|
return response if response && !(cascade = cascade?(response))
|
126
107
|
end
|
127
108
|
|
128
|
-
|
109
|
+
return call_with_allow_headers(env, last_neighbor_route) if !cascade && last_neighbor_route
|
110
|
+
|
111
|
+
nil
|
129
112
|
end
|
130
113
|
|
131
114
|
def process_route(route, env)
|
132
|
-
|
133
|
-
routing_args = env[Grape::Env::GRAPE_ROUTING_ARGS]
|
134
|
-
env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(routing_args, route, input)
|
115
|
+
prepare_env_from_route(env, route)
|
135
116
|
route.exec(env)
|
136
117
|
end
|
137
118
|
|
138
119
|
def make_routing_args(default_args, route, input)
|
139
120
|
args = default_args || { route_info: route }
|
140
|
-
args.merge(route.params(input))
|
121
|
+
args.merge(route.params(input) || {})
|
141
122
|
end
|
142
123
|
|
143
124
|
def extract_input_and_method(env)
|
@@ -168,9 +149,15 @@ module Grape
|
|
168
149
|
@neutral_map.detect { |route| last_match["_#{route.index}"] }
|
169
150
|
end
|
170
151
|
|
171
|
-
def call_with_allow_headers(env,
|
172
|
-
env
|
173
|
-
|
152
|
+
def call_with_allow_headers(env, route)
|
153
|
+
prepare_env_from_route(env, route)
|
154
|
+
env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header.join(', ').freeze
|
155
|
+
route.endpoint.call(env)
|
156
|
+
end
|
157
|
+
|
158
|
+
def prepare_env_from_route(env, route)
|
159
|
+
input, = *extract_input_and_method(env)
|
160
|
+
env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(env[Grape::Env::GRAPE_ROUTING_ARGS], route, input)
|
174
161
|
end
|
175
162
|
|
176
163
|
def cascade?(response)
|
@@ -4,19 +4,40 @@ module Grape
|
|
4
4
|
class Router
|
5
5
|
# this could be an OpenStruct, but doesn't work in Ruby 2.3.0, see https://bugs.ruby-lang.org/issues/12251
|
6
6
|
class AttributeTranslator
|
7
|
-
attr_reader :attributes
|
7
|
+
attr_reader :attributes
|
8
8
|
|
9
|
-
|
9
|
+
ROUTE_ATTRIBUTES = %i[
|
10
|
+
prefix
|
11
|
+
version
|
12
|
+
settings
|
13
|
+
format
|
14
|
+
description
|
15
|
+
http_codes
|
16
|
+
headers
|
17
|
+
entity
|
18
|
+
details
|
19
|
+
requirements
|
20
|
+
request_method
|
21
|
+
namespace
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
ROUTER_ATTRIBUTES = %i[pattern index].freeze
|
25
|
+
|
26
|
+
def initialize(**attributes)
|
10
27
|
@attributes = attributes
|
11
|
-
|
12
|
-
|
28
|
+
end
|
29
|
+
|
30
|
+
(ROUTER_ATTRIBUTES + ROUTE_ATTRIBUTES).each do |attr|
|
31
|
+
define_method attr do
|
32
|
+
attributes[attr]
|
33
|
+
end
|
13
34
|
end
|
14
35
|
|
15
36
|
def to_h
|
16
37
|
attributes
|
17
38
|
end
|
18
39
|
|
19
|
-
def method_missing(method_name, *args)
|
40
|
+
def method_missing(method_name, *args)
|
20
41
|
if setter?(method_name[-1])
|
21
42
|
attributes[method_name[0..-1]] = *args
|
22
43
|
else
|
data/lib/grape/router/route.rb
CHANGED
@@ -12,12 +12,13 @@ module Grape
|
|
12
12
|
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/.freeze
|
13
13
|
FIXED_NAMED_CAPTURES = %w[format version].freeze
|
14
14
|
|
15
|
-
attr_accessor :pattern, :translator, :app, :index, :
|
15
|
+
attr_accessor :pattern, :translator, :app, :index, :options
|
16
16
|
|
17
17
|
alias attributes translator
|
18
18
|
|
19
19
|
extend Forwardable
|
20
20
|
def_delegators :pattern, :path, :origin
|
21
|
+
delegate Grape::Router::AttributeTranslator::ROUTE_ATTRIBUTES => :attributes
|
21
22
|
|
22
23
|
def method_missing(method_id, *arguments)
|
23
24
|
match = ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
|
@@ -31,26 +32,7 @@ module Grape
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def respond_to_missing?(method_id, _)
|
34
|
-
ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
|
35
|
-
end
|
36
|
-
|
37
|
-
%i[
|
38
|
-
prefix
|
39
|
-
version
|
40
|
-
settings
|
41
|
-
format
|
42
|
-
description
|
43
|
-
http_codes
|
44
|
-
headers
|
45
|
-
entity
|
46
|
-
details
|
47
|
-
requirements
|
48
|
-
request_method
|
49
|
-
namespace
|
50
|
-
].each do |method_name|
|
51
|
-
define_method method_name do
|
52
|
-
attributes.public_send method_name
|
53
|
-
end
|
35
|
+
ROUTE_ATTRIBUTE_REGEXP.match?(method_id.to_s)
|
54
36
|
end
|
55
37
|
|
56
38
|
def route_method
|
@@ -67,7 +49,6 @@ module Grape
|
|
67
49
|
method_s = method.to_s
|
68
50
|
method_upcase = Grape::Http::Headers.find_supported_method(method_s) || method_s.upcase
|
69
51
|
|
70
|
-
@suffix = options[:suffix]
|
71
52
|
@options = options.merge(method: method_upcase)
|
72
53
|
@pattern = Pattern.new(pattern, **options)
|
73
54
|
@translator = AttributeTranslator.new(**options, request_method: method_upcase)
|
@@ -1,22 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Grape
|
4
|
-
module
|
5
|
-
# A simple class used to identify responses which represent files and do not
|
4
|
+
module ServeStream
|
5
|
+
# A simple class used to identify responses which represent streams (or files) and do not
|
6
6
|
# need to be formatted or pre-read by Rack::Response
|
7
|
-
class
|
8
|
-
attr_reader :
|
7
|
+
class StreamResponse
|
8
|
+
attr_reader :stream
|
9
9
|
|
10
|
-
# @param
|
11
|
-
def initialize(
|
12
|
-
@
|
10
|
+
# @param stream [Object]
|
11
|
+
def initialize(stream)
|
12
|
+
@stream = stream
|
13
13
|
end
|
14
14
|
|
15
15
|
# Equality provided mostly for tests.
|
16
16
|
#
|
17
17
|
# @return [Boolean]
|
18
18
|
def ==(other)
|
19
|
-
|
19
|
+
stream == other.stream
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|