Skip to content

Commit

Permalink
Added file, explicitly set file-like response object.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed Jun 3, 2015
1 parent 7a2761d commit 1505d47
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 23 deletions.
35 changes: 18 additions & 17 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
# This configuration was generated by `rubocop --auto-gen-config`
# on 2014-12-16 11:52:50 -0500 using RuboCop version 0.29.1.
# on 2015-06-03 09:20:43 -0400 using RuboCop version 0.29.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 29
# Offense count: 33
# Cop supports --auto-correct.
Lint/UnusedBlockArgument:
Enabled: false

# Offense count: 26
# Offense count: 27
# Cop supports --auto-correct.
Lint/UnusedMethodArgument:
Enabled: false

# Offense count: 35
# Offense count: 37
Metrics/AbcSize:
Max: 50
Max: 48

# Offense count: 1
# Offense count: 2
Metrics/BlockNesting:
Max: 4

# Offense count: 4
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 245
Max: 246

# Offense count: 15
# Offense count: 23
Metrics/CyclomaticComplexity:
Max: 20

# Offense count: 545
# Offense count: 676
# Configuration parameters: AllowURI, URISchemes.
Metrics/LineLength:
Max: 198

# Offense count: 42
# Offense count: 44
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 35

# Offense count: 13
# Offense count: 17
Metrics/PerceivedComplexity:
Max: 22

# Offense count: 26
# Offense count: 46
# Cop supports --auto-correct.
Style/Blocks:
Enabled: false
Expand All @@ -57,7 +57,7 @@ Style/Blocks:
Style/ClassCheck:
Enabled: false

# Offense count: 157
# Offense count: 174
Style/Documentation:
Enabled: false

Expand All @@ -73,7 +73,7 @@ Style/EachWithObject:
Style/EmptyElse:
Enabled: false

# Offense count: 14
# Offense count: 15
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Enabled: false
Expand All @@ -89,7 +89,8 @@ Style/HashSyntax:
Style/IndentArray:
Enabled: false

# Offense count: 18
# Offense count: 20
# Cop supports --auto-correct.
Style/Lambda:
Enabled: false

Expand All @@ -109,7 +110,7 @@ Style/PercentLiteralDelimiters:
Style/PredicateName:
Enabled: false

# Offense count: 9
# Offense count: 13
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/RaiseArgs:
Enabled: false
Expand All @@ -119,7 +120,7 @@ Style/RaiseArgs:
Style/RegexpLiteral:
Enabled: false

# Offense count: 4
# Offense count: 11
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/SpaceBeforeBlockBraces:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* [#952](https://github.com/intridea/grape/pull/952): Status method now raises error when called with invalid status code - [@dabrorius](https://github.com/dabrorius).
* [#957](https://github.com/intridea/grape/pull/957): Regexp validator now supports `allow_blank`, `nil` value behavior changed - [@calfzhou](https://giihub.com/calfzhou).
* [#962](https://github.com/intridea/grape/pull/962): The `default` attribute with `false` value is documented now - [@ajvondrak](https://github.com/ajvondrak).
* [#1026](https://github.com/intridea/grape/pull/1026): Added `file` method, explicitly setting a file-like response object - [@dblock](https://github.com/dblock).

#### Fixes

Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2009,7 +2009,7 @@ class API < Grape::API
end
```
You can also set the response body explicitly with `body`.
You can set the response body explicitly with `body`.
```ruby
class API < Grape::API
Expand All @@ -2023,6 +2023,28 @@ end
Use `body false` to return `204 No Content` without any data or content-type.
You can also set the response to a file-like object with `file`.
```ruby
class FileStreamer
def initialize(file_path)
@file_path = file_path
end
def each(&blk)
File.open(@file_path, 'rb') do |file|
file.each(10, &blk)
end
end
end
class API < Grape::API
get '/' do
file FileStreamer.new('file.bin')
end
end
```
## Authentication
### Basic and Digest Auth
Expand Down
16 changes: 16 additions & 0 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,22 @@ def body(value = nil)
end
end

# Allows you to define the response as a file-like object.
#
# @example
# get '/file' do
# file FileStreamer.new(...)
# end
#
# GET /file # => "contents of file"
def file(value = nil)
if value
@file = value
else
@file
end
end

# Allows you to make use of Grape Entities by setting
# the response body to the serializable hash of the
# entity provided in the `:with` option. This has the
Expand Down
6 changes: 4 additions & 2 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,13 @@ def run(env)

run_filters after_validations

response_text = @block ? @block.call(self) : nil
response_object = @block ? @block.call(self) : nil
run_filters afters
cookies.write(header)

[status, header, [body || response_text]]
# The Body commonly is an Array of Strings, the application instance itself, or a File-like object.
response_object = file || [body || response_object]
[status, header, response_object]
end

def build_middleware
Expand Down
10 changes: 7 additions & 3 deletions lib/grape/middleware/formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ def after
api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env['api.format']
formatter = Grape::Formatter::Base.formatter_for api_format, options
begin
bodymap = bodies.collect do |body|
formatter.call body, env
end
bodymap = if bodies.respond_to?(:collect)
bodies.collect do |body|
formatter.call body, env
end
else
bodies
end
rescue Grape::Exceptions::InvalidFormatter => e
throw :error, status: 500, message: e.message
end
Expand Down
16 changes: 16 additions & 0 deletions spec/grape/dsl/inside_route_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,22 @@ def initialize
end
end

describe '#file' do
describe 'set' do
before do
subject.file 'file'
end

it 'returns value' do
expect(subject.file).to eq 'file'
end
end

it 'returns default' do
expect(subject.file).to be nil
end
end

describe '#route' do
before do
subject.env['rack.routing_args'] = {}
Expand Down
14 changes: 14 additions & 0 deletions spec/grape/endpoint_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -927,4 +927,18 @@ def memoized
expect(last_response.status).to eq(406)
end
end

context 'binary' do
before do
subject.get do
file FileStreamer.new(__FILE__)
end
end

it 'suports stream objects in response' do
get '/'
expect(last_response.status).to eq 200
expect(last_response.body).to eq File.read(__FILE__)
end
end
end
11 changes: 11 additions & 0 deletions spec/support/file_streamer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class FileStreamer
def initialize(file_path)
@file_path = file_path
end

def each(&blk)
File.open(@file_path, 'rb') do |file|
file.each(10, &blk)
end
end
end

0 comments on commit 1505d47

Please sign in to comment.