grape 1.3.0 → 1.5.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 +4 -4
- data/CHANGELOG.md +90 -0
- data/LICENSE +1 -1
- data/README.md +104 -21
- data/UPGRADING.md +243 -39
- data/lib/grape.rb +4 -5
- data/lib/grape/api.rb +4 -4
- data/lib/grape/api/instance.rb +32 -31
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/dsl/helpers.rb +2 -1
- data/lib/grape/dsl/inside_route.rb +76 -42
- data/lib/grape/dsl/parameters.rb +4 -4
- data/lib/grape/dsl/routing.rb +8 -8
- 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/exceptions/base.rb +0 -4
- data/lib/grape/exceptions/validation_errors.rb +11 -12
- data/lib/grape/http/headers.rb +26 -0
- data/lib/grape/middleware/base.rb +3 -4
- data/lib/grape/middleware/error.rb +10 -12
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/stack.rb +19 -5
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +12 -2
- data/lib/grape/path.rb +13 -3
- data/lib/grape/request.rb +13 -8
- data/lib/grape/router.rb +26 -30
- data/lib/grape/router/attribute_translator.rb +25 -4
- data/lib/grape/router/pattern.rb +17 -16
- data/lib/grape/router/route.rb +5 -24
- 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 +15 -8
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +1 -0
- data/lib/grape/util/reverse_stackable_values.rb +2 -0
- data/lib/grape/util/stackable_values.rb +7 -20
- data/lib/grape/validations/params_scope.rb +6 -5
- 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 -12
- data/lib/grape/validations/types/json.rb +40 -36
- data/lib/grape/validations/types/primitive_coercer.rb +15 -6
- 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/validators/as.rb +1 -1
- data/lib/grape/validations/validators/base.rb +2 -4
- data/lib/grape/validations/validators/coerce.rb +4 -11
- data/lib/grape/validations/validators/default.rb +3 -5
- data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
- data/lib/grape/validations/validators/except_values.rb +1 -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 +82 -6
- data/spec/grape/dsl/inside_route_spec.rb +182 -33
- data/spec/grape/endpoint/declared_spec.rb +590 -0
- data/spec/grape/endpoint_spec.rb +0 -521
- data/spec/grape/entity_spec.rb +6 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- 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 +3 -3
- data/spec/grape/middleware/stack_spec.rb +12 -1
- data/spec/grape/path_spec.rb +4 -4
- data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
- data/spec/grape/validations/params_scope_spec.rb +26 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- 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 +329 -77
- data/spec/grape/validations/validators/default_spec.rb +170 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
- 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 +30 -30
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/spec_helper.rb +3 -10
- data/spec/support/chunks.rb +14 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/versioned_helpers.rb +3 -5
- metadata +121 -105
- data/lib/grape/util/content_types.rb +0 -28
data/lib/grape/namespace.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'grape/util/cache'
|
4
|
+
|
3
5
|
module Grape
|
4
6
|
# A container for endpoints or other namespaces, which allows for both
|
5
7
|
# logical grouping of endpoints as well as sharing common configuration.
|
@@ -25,13 +27,21 @@ module Grape
|
|
25
27
|
|
26
28
|
# (see ::joined_space_path)
|
27
29
|
def self.joined_space(settings)
|
28
|
-
|
30
|
+
settings&.map(&:space)
|
29
31
|
end
|
30
32
|
|
31
33
|
# Join the namespaces from a list of settings to create a path prefix.
|
32
34
|
# @param settings [Array] list of Grape::Util::InheritableSettings.
|
33
35
|
def self.joined_space_path(settings)
|
34
|
-
Grape::Router.normalize_path(joined_space(settings))
|
36
|
+
Grape::Router.normalize_path(JoinedSpaceCache[joined_space(settings)])
|
37
|
+
end
|
38
|
+
|
39
|
+
class JoinedSpaceCache < Grape::Util::Cache
|
40
|
+
def initialize
|
41
|
+
@cache = Hash.new do |h, joined_space|
|
42
|
+
h[joined_space] = -joined_space.join('/')
|
43
|
+
end
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
data/lib/grape/path.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'grape/util/cache'
|
4
|
+
|
3
5
|
module Grape
|
4
6
|
# Represents a path to an endpoint.
|
5
7
|
class Path
|
@@ -40,11 +42,11 @@ module Grape
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def namespace?
|
43
|
-
namespace
|
45
|
+
namespace&.match?(/^\S/) && namespace != '/'
|
44
46
|
end
|
45
47
|
|
46
48
|
def path?
|
47
|
-
raw_path
|
49
|
+
raw_path&.match?(/^\S/) && raw_path != '/'
|
48
50
|
end
|
49
51
|
|
50
52
|
def suffix
|
@@ -58,7 +60,7 @@ module Grape
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def path
|
61
|
-
Grape::Router.normalize_path(parts
|
63
|
+
Grape::Router.normalize_path(PartsCache[parts])
|
62
64
|
end
|
63
65
|
|
64
66
|
def path_with_suffix
|
@@ -71,6 +73,14 @@ module Grape
|
|
71
73
|
|
72
74
|
private
|
73
75
|
|
76
|
+
class PartsCache < Grape::Util::Cache
|
77
|
+
def initialize
|
78
|
+
@cache = Hash.new do |h, parts|
|
79
|
+
h[parts] = -parts.join('/')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
74
84
|
def parts
|
75
85
|
parts = [mount_path, root_prefix].compact
|
76
86
|
parts << ':version' if uses_path_versioning?
|
data/lib/grape/request.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'grape/util/lazy_object'
|
4
|
+
|
3
5
|
module Grape
|
4
6
|
class Request < Rack::Request
|
5
7
|
HTTP_PREFIX = 'HTTP_'
|
6
8
|
|
7
9
|
alias rack_params params
|
8
10
|
|
9
|
-
def initialize(env, options
|
11
|
+
def initialize(env, **options)
|
10
12
|
extend options[:build_params_with] || Grape.config.param_builder
|
11
13
|
super(env)
|
12
14
|
end
|
@@ -30,14 +32,17 @@ module Grape
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def build_headers
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
Grape::Util::LazyObject.new do
|
36
|
+
env.each_pair.with_object({}) do |(k, v), headers|
|
37
|
+
next unless k.to_s.start_with? HTTP_PREFIX
|
38
|
+
transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
|
39
|
+
headers[transformed_header] = v
|
40
|
+
end
|
39
41
|
end
|
40
|
-
|
42
|
+
end
|
43
|
+
|
44
|
+
def transform_header(header)
|
45
|
+
-header[5..-1].split('_').each(&:capitalize!).join('-')
|
41
46
|
end
|
42
47
|
end
|
43
48
|
end
|
data/lib/grape/router.rb
CHANGED
@@ -1,21 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'grape/router/route'
|
4
|
+
require 'grape/util/cache'
|
4
5
|
|
5
6
|
module Grape
|
6
7
|
class Router
|
7
8
|
attr_reader :map, :compiled
|
8
9
|
|
9
|
-
class Any < AttributeTranslator
|
10
|
-
attr_reader :pattern, :regexp, :index
|
11
|
-
def initialize(pattern, regexp, index, **attributes)
|
12
|
-
@pattern = pattern
|
13
|
-
@regexp = regexp
|
14
|
-
@index = index
|
15
|
-
super(attributes)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
10
|
def self.normalize_path(path)
|
20
11
|
path = +"/#{path}"
|
21
12
|
path.squeeze!('/')
|
@@ -30,18 +21,20 @@ module Grape
|
|
30
21
|
|
31
22
|
def initialize
|
32
23
|
@neutral_map = []
|
24
|
+
@neutral_regexes = []
|
33
25
|
@map = Hash.new { |hash, key| hash[key] = [] }
|
34
26
|
@optimized_map = Hash.new { |hash, key| hash[key] = // }
|
35
27
|
end
|
36
28
|
|
37
29
|
def compile!
|
38
30
|
return if compiled
|
39
|
-
@union = Regexp.union(@
|
31
|
+
@union = Regexp.union(@neutral_regexes)
|
32
|
+
@neutral_regexes = nil
|
40
33
|
self.class.supported_methods.each do |method|
|
41
34
|
routes = map[method]
|
42
35
|
@optimized_map[method] = routes.map.with_index do |route, index|
|
43
36
|
route.index = index
|
44
|
-
|
37
|
+
Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
|
45
38
|
end
|
46
39
|
@optimized_map[method] = Regexp.union(@optimized_map[method])
|
47
40
|
end
|
@@ -53,8 +46,8 @@ module Grape
|
|
53
46
|
end
|
54
47
|
|
55
48
|
def associate_routes(pattern, **options)
|
56
|
-
|
57
|
-
@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)
|
58
51
|
end
|
59
52
|
|
60
53
|
def call(env)
|
@@ -98,37 +91,34 @@ module Grape
|
|
98
91
|
response = yield(input, method)
|
99
92
|
|
100
93
|
return response if response && !(cascade = cascade?(response))
|
101
|
-
|
94
|
+
last_neighbor_route = greedy_match?(input)
|
102
95
|
|
103
|
-
# If
|
96
|
+
# If last_neighbor_route exists and request method is OPTIONS,
|
104
97
|
# return response by using #call_with_allow_headers.
|
105
|
-
return call_with_allow_headers(
|
106
|
-
env,
|
107
|
-
neighbor.allow_header,
|
108
|
-
neighbor.endpoint
|
109
|
-
) if neighbor && method == 'OPTIONS' && !cascade
|
98
|
+
return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Grape::Http::Headers::OPTIONS && !cascade
|
110
99
|
|
111
100
|
route = match?(input, '*')
|
112
|
-
|
101
|
+
|
102
|
+
return last_neighbor_route.endpoint.call(env) if last_neighbor_route && cascade && route
|
113
103
|
|
114
104
|
if route
|
115
105
|
response = process_route(route, env)
|
116
106
|
return response if response && !(cascade = cascade?(response))
|
117
107
|
end
|
118
108
|
|
119
|
-
|
109
|
+
return call_with_allow_headers(env, last_neighbor_route) if !cascade && last_neighbor_route
|
110
|
+
|
111
|
+
nil
|
120
112
|
end
|
121
113
|
|
122
114
|
def process_route(route, env)
|
123
|
-
|
124
|
-
routing_args = env[Grape::Env::GRAPE_ROUTING_ARGS]
|
125
|
-
env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(routing_args, route, input)
|
115
|
+
prepare_env_from_route(env, route)
|
126
116
|
route.exec(env)
|
127
117
|
end
|
128
118
|
|
129
119
|
def make_routing_args(default_args, route, input)
|
130
120
|
args = default_args || { route_info: route }
|
131
|
-
args.merge(route.params(input))
|
121
|
+
args.merge(route.params(input) || {})
|
132
122
|
end
|
133
123
|
|
134
124
|
def extract_input_and_method(env)
|
@@ -159,9 +149,15 @@ module Grape
|
|
159
149
|
@neutral_map.detect { |route| last_match["_#{route.index}"] }
|
160
150
|
end
|
161
151
|
|
162
|
-
def call_with_allow_headers(env,
|
163
|
-
env
|
164
|
-
|
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)
|
165
161
|
end
|
166
162
|
|
167
163
|
def cascade?(response)
|
@@ -6,17 +6,38 @@ module Grape
|
|
6
6
|
class AttributeTranslator
|
7
7
|
attr_reader :attributes, :request_method, :requirements
|
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/pattern.rb
CHANGED
@@ -2,35 +2,32 @@
|
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
4
|
require 'mustermann/grape'
|
5
|
+
require 'grape/util/cache'
|
5
6
|
|
6
7
|
module Grape
|
7
8
|
class Router
|
8
9
|
class Pattern
|
9
|
-
DEFAULT_PATTERN_OPTIONS = { uri_decode: true
|
10
|
+
DEFAULT_PATTERN_OPTIONS = { uri_decode: true }.freeze
|
10
11
|
DEFAULT_SUPPORTED_CAPTURE = %i[format version].freeze
|
11
12
|
|
12
|
-
attr_reader :origin, :path, :
|
13
|
+
attr_reader :origin, :path, :pattern, :to_regexp
|
13
14
|
|
14
15
|
extend Forwardable
|
15
16
|
def_delegators :pattern, :named_captures, :params
|
16
|
-
def_delegators
|
17
|
+
def_delegators :to_regexp, :===
|
17
18
|
alias match? ===
|
18
19
|
|
19
20
|
def initialize(pattern, **options)
|
20
21
|
@origin = pattern
|
21
22
|
@path = build_path(pattern, **options)
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@regexp = to_regexp
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_regexp
|
28
|
-
@to_regexp ||= @pattern.to_regexp
|
23
|
+
@pattern = Mustermann::Grape.new(@path, **pattern_options(options))
|
24
|
+
@to_regexp = @pattern.to_regexp
|
29
25
|
end
|
30
26
|
|
31
27
|
private
|
32
28
|
|
33
|
-
def pattern_options
|
29
|
+
def pattern_options(options)
|
30
|
+
capture = extract_capture(**options)
|
34
31
|
options = DEFAULT_PATTERN_OPTIONS.dup
|
35
32
|
options[:capture] = capture if capture.present?
|
36
33
|
options
|
@@ -43,23 +40,27 @@ module Grape
|
|
43
40
|
pattern << '*path'
|
44
41
|
end
|
45
42
|
|
46
|
-
pattern = pattern.split('/').tap do |parts|
|
43
|
+
pattern = -pattern.split('/').tap do |parts|
|
47
44
|
parts[parts.length - 1] = '?' + parts.last
|
48
45
|
end.join('/') if pattern.end_with?('*path')
|
49
46
|
|
50
|
-
|
47
|
+
PatternCache[[pattern, suffix]]
|
51
48
|
end
|
52
49
|
|
53
50
|
def extract_capture(requirements: {}, **options)
|
54
51
|
requirements = {}.merge(requirements)
|
55
|
-
|
52
|
+
DEFAULT_SUPPORTED_CAPTURE.each_with_object(requirements) do |field, capture|
|
56
53
|
option = Array(options[field])
|
57
54
|
capture[field] = option.map(&:to_s) if option.present?
|
58
55
|
end
|
59
56
|
end
|
60
57
|
|
61
|
-
|
62
|
-
|
58
|
+
class PatternCache < Grape::Util::Cache
|
59
|
+
def initialize
|
60
|
+
@cache = Hash.new do |h, (pattern, suffix)|
|
61
|
+
h[[pattern, suffix]] = -"#{pattern}#{suffix}"
|
62
|
+
end
|
63
|
+
end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
data/lib/grape/router/route.rb
CHANGED
@@ -8,16 +8,17 @@ require 'pathname'
|
|
8
8
|
module Grape
|
9
9
|
class Router
|
10
10
|
class Route
|
11
|
-
ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)
|
12
|
-
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')
|
11
|
+
ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)/.freeze
|
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
|