dis 2.0.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7c60e34e3af85dabdb54a45edefc614356194ece8f98fefcc85f9245efbba4d
4
- data.tar.gz: e9f450a4147cb7af0812f1755a0ecab8c0aea07c4c6cd425e1bb8fc0a9eb2476
3
+ metadata.gz: b78f0ff03e09c43d839c8ba8e119c6548ae518469d17d6646767c987f36f6a15
4
+ data.tar.gz: 3c9d88b627d1eadb871f926a2552e9761eb007442543db0aa65f88623e00f168
5
5
  SHA512:
6
- metadata.gz: ddc5e8de5bb8d432d0c16a96c5d3fdae656dc2c5051dc9f728b2b6e7021679a9c9ec16b6be36d63bdc838a38e6fd965a649d56267fba4ec644b9f57aae1a41e3
7
- data.tar.gz: c82397e472d58508bb953afb5480721ec6f23aa18e70da28b67ba2edcb92fba55315eeeb4879509079ae6883cbc2aab1caa13832f30a4408c76828b13a5d9e98
6
+ metadata.gz: 7987a1138a986daa040447ff607a07b6bf077a0ea0450274dafbff3b977cca1fee9bdf2121847c9492ffe75fe12b7bb11fc8a056240d473a69129e205ca821af
7
+ data.tar.gz: 888c70668ce77b47451359d5cfe953793488eb1238749eaa0f7151e6779ed105ff6a1941cd9b8e3d69419c24a349c022fc857cea443bb48b48f102f4711507bf
data/README.md CHANGED
@@ -125,8 +125,7 @@ document.with_data_file { |path| Vips::Image.new_from_file(path.to_s).avg }
125
125
 
126
126
  `open_data` returns an open file, valid until you close it — even if
127
127
  the content is deleted or evicted from a cache layer meanwhile. Use it
128
- when the reader outlives the current call stack, such as a response
129
- body handed to the web server.
128
+ when the reader outlives the current call stack.
130
129
 
131
130
  ```ruby
132
131
  document.open_data { |file| file.read }
@@ -134,6 +133,27 @@ document.open_data { |file| file.read }
134
133
  file = document.open_data # caller closes it
135
134
  ```
136
135
 
136
+ ### Sending data from a controller
137
+
138
+ Include `Dis::Controller` and use `send_dis_data` to stream a record's
139
+ data to the client. It works like `send_file`, but reads through an
140
+ open descriptor rather than a path, so the response is unaffected if
141
+ the content is evicted or deleted while it is being written.
142
+
143
+ ```ruby
144
+ class DocumentsController < ApplicationController
145
+ include Dis::Controller
146
+
147
+ def show
148
+ send_dis_data(Document.find(params[:id]), disposition: "inline")
149
+ end
150
+ end
151
+ ```
152
+
153
+ `filename` and `content_type` default to the record's own metadata.
154
+ The full set of options is `filename`, `content_type`, `disposition`
155
+ and `status`.
156
+
137
157
  ## Layers
138
158
 
139
159
  The underlying storage consists of one or more layers. Each layer
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dis
4
+ # = Dis Controller
5
+ #
6
+ # Adds {#send_dis_data} for serving stored data from a controller.
7
+ #
8
+ # class DocumentsController < ApplicationController
9
+ # include Dis::Controller
10
+ #
11
+ # def show
12
+ # send_dis_data(Document.find(params[:id]))
13
+ # end
14
+ # end
15
+ module Controller
16
+ extend ActiveSupport::Concern
17
+
18
+ included do
19
+ include ActionController::DataStreaming
20
+ end
21
+
22
+ private
23
+
24
+ # Sends the record's data to the client. Works like +send_file+,
25
+ # but reads through an open descriptor rather than a path, so the
26
+ # response is unaffected if the content is evicted or deleted
27
+ # while it is being written.
28
+ #
29
+ # +filename+ and +content_type+ default to the record's own
30
+ # metadata.
31
+ #
32
+ # @param record [Dis::Model] the record to send data from
33
+ # @param filename [String, nil] suggested filename
34
+ # @param content_type [String, nil] the content type
35
+ # @param disposition [String] +"attachment"+ or +"inline"+
36
+ # @param status [Integer] the HTTP status code
37
+ # @return [void]
38
+ # @raise [Dis::Errors::NotFoundError] if the data is not found
39
+ #
40
+ # @example
41
+ # send_dis_data(document, disposition: "inline")
42
+ def send_dis_data(record, filename: nil, content_type: nil,
43
+ disposition: "attachment", status: 200)
44
+ file = record.open_data
45
+ send_file_headers!(
46
+ { filename: filename || dis_metadata(record, :filename),
47
+ type: content_type || dis_metadata(record, :content_type),
48
+ disposition: }.compact
49
+ )
50
+ self.status = status
51
+ response.headers["Content-Length"] = file.size.to_s
52
+ self.response_body = Dis::ResponseBody.new(file)
53
+ end
54
+
55
+ def dis_metadata(record, name)
56
+ attribute = record.class.dis_attributes[name]
57
+ record[attribute] if attribute
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dis
4
+ # = Dis Response Body
5
+ #
6
+ # Rack body that streams an open file and closes it once the
7
+ # response has been sent. The file may already be unlinked, so it is
8
+ # read through the open descriptor and never by path.
9
+ class ResponseBody
10
+ CHUNK_SIZE = 16_384
11
+
12
+ delegate :closed?, to: :@file
13
+
14
+ # @param file [File] an open, readable file
15
+ def initialize(file)
16
+ @file = file
17
+ end
18
+
19
+ # Returns the entire contents as a binary string.
20
+ #
21
+ # @return [String]
22
+ def body
23
+ @file.rewind
24
+ @file.read
25
+ end
26
+
27
+ # Yields the contents in chunks.
28
+ #
29
+ # @yieldparam chunk [String] a chunk of the contents
30
+ # @return [void]
31
+ def each
32
+ @file.rewind
33
+ while (chunk = @file.read(CHUNK_SIZE))
34
+ yield chunk
35
+ end
36
+ end
37
+
38
+ # Closes the underlying file.
39
+ #
40
+ # @return [void]
41
+ def close
42
+ @file.close unless @file.closed?
43
+ end
44
+ end
45
+ end
data/lib/dis/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dis
4
- VERSION = "2.0.0"
4
+ VERSION = "2.1.0"
5
5
  end
data/lib/dis.rb CHANGED
@@ -6,6 +6,7 @@ require "fog/core"
6
6
  require "fog/local"
7
7
  require "active_job"
8
8
  require "concurrent"
9
+ require "dis/controller"
9
10
  require "dis/engine"
10
11
  require "dis/errors"
11
12
  require "dis/jobs"
@@ -13,6 +14,7 @@ require "dis/logging"
13
14
  require "dis/layer"
14
15
  require "dis/layers"
15
16
  require "dis/model"
17
+ require "dis/response_body"
16
18
  require "dis/storage"
17
19
  require "dis/validations"
18
20
 
@@ -27,6 +29,7 @@ require "dis/validations"
27
29
  # configure layers via {Dis::Storage.layers}.
28
30
  #
29
31
  # @see Dis::Model
32
+ # @see Dis::Controller
30
33
  # @see Dis::Storage
31
34
  # @see Dis::Layer
32
35
  module Dis
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dis
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
@@ -110,6 +110,7 @@ files:
110
110
  - LICENSE
111
111
  - README.md
112
112
  - lib/dis.rb
113
+ - lib/dis/controller.rb
113
114
  - lib/dis/engine.rb
114
115
  - lib/dis/errors.rb
115
116
  - lib/dis/jobs.rb
@@ -123,6 +124,7 @@ files:
123
124
  - lib/dis/model.rb
124
125
  - lib/dis/model/class_methods.rb
125
126
  - lib/dis/model/data.rb
127
+ - lib/dis/response_body.rb
126
128
  - lib/dis/storage.rb
127
129
  - lib/dis/validations.rb
128
130
  - lib/dis/validations/data_presence.rb