azure-blob 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ff6c7e8f9d7ec11e85bcb1ea543b9344a9db958fea0d9451221c5be161e0d8b
4
- data.tar.gz: fc1a13d7c8f39303c5985de80b7790a378f6a1901b22e4f8cce1eb85789aaeb4
3
+ metadata.gz: 046a2472eb232c6579a189ea2621c495348c43487d0963d71c7ff47283da59f0
4
+ data.tar.gz: 18793a16bea30be351fcd78a3e5cdd91375f1df54db1477632851e7ed0a6540b
5
5
  SHA512:
6
- metadata.gz: eabce34cd075764436fc473b550451fadf2f764a731464345ba2542a7e1ca5f5e5d44286b94b19d11eda554fde4e4a17faaf40eb8ad3ed2e9d8bab798ba8e2bb
7
- data.tar.gz: 793a8c163e2b7d489bfd8d4a643ba6c466d043652e5cce3f6ee2798142bfb36ce41e0881028712b57ab45eb357704af3c64d47e66c53d631ee949a50d22c310f
6
+ metadata.gz: c2881fd3b9681468cf9639b3049876773e15de1853915b48dd22cca0dd34815c60198f45768017de736a475ac1e58e295921f9081e27ea002c40b0518e7e8993
7
+ data.tar.gz: 7f877ab5f4c5c94f5204088270d6fb2fc6d8b78d4ac88892ec79ee0176bfad30f91f4666176b9c25f0a74b4d3cdbbd30625b4541e3a2b1e9aceb33a7f599a542
data/README.md CHANGED
@@ -1,6 +1,17 @@
1
1
  # AzureBlob
2
2
 
3
- WIP This gem is built as a replacement of azure-storage-blob (deprecated) in Active Storage
3
+ This gem was built to replace azure-storage-blob (deprecated) in Active Storage, but was written to be Rails agnostic.
4
+
5
+
6
+ # Active Storage
7
+
8
+ ## Migration
9
+ To migrate from azure-storage-blob to azure-blob:
10
+
11
+ 1- Replace `azure-storage-blob` in your Gemfile with `azure-blob`
12
+ 2- Run `bundle install`
13
+ 3- change the `AzureStorage` service to `AzureBlob` in your Active Storage config (`config/storage.yml`)
14
+ 4- Restart or deploy the app.
4
15
 
5
16
  ## License
6
17
 
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) David Heinemeier Hansson, 37signals LLC
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+
25
+ require "active_support/core_ext/numeric/bytes"
26
+ require "active_storage/service"
27
+
28
+ require 'azure_blob'
29
+
30
+ module ActiveStorage
31
+ # = Active Storage \Azure Storage \Service
32
+ #
33
+ # Wraps the Microsoft Azure Storage Blob Service as an Active Storage service.
34
+ # See ActiveStorage::Service for the generic API documentation that applies to all services.
35
+ class Service::AzureBlobService < Service
36
+ attr_reader :client, :container, :signer
37
+
38
+ def initialize(storage_account_name:, storage_access_key:, container:, public: false, **options)
39
+ @container = container
40
+ @public = public
41
+ @client = AzureBlob::Client.new(
42
+ account_name: storage_account_name,
43
+ access_key: storage_access_key,
44
+ container: container,
45
+ **options)
46
+ end
47
+
48
+ def upload(key, io, checksum: nil, filename: nil, content_type: nil, disposition: nil, custom_metadata: {}, **)
49
+ instrument :upload, key: key, checksum: checksum do
50
+ handle_errors do
51
+ content_disposition = content_disposition_with(filename: filename, type: disposition) if disposition && filename
52
+
53
+ client.create_block_blob(key, IO.try_convert(io) || io, content_md5: checksum, content_type: content_type, content_disposition: content_disposition, metadata: custom_metadata)
54
+ end
55
+ end
56
+ end
57
+
58
+ def download(key, &block)
59
+ if block_given?
60
+ instrument :streaming_download, key: key do
61
+ stream(key, &block)
62
+ end
63
+ else
64
+ instrument :download, key: key do
65
+ handle_errors do
66
+ io = client.get_blob(key)
67
+ io.force_encoding(Encoding::BINARY)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def download_chunk(key, range)
74
+ instrument :download_chunk, key: key, range: range do
75
+ handle_errors do
76
+ io = client.get_blob(key, start: range.begin, end: range.exclude_end? ? range.end - 1 : range.end)
77
+ io.force_encoding(Encoding::BINARY)
78
+ end
79
+ end
80
+ end
81
+
82
+ def delete(key)
83
+ instrument :delete, key: key do
84
+ client.delete_blob(key)
85
+ rescue AzureBlob::Http::FileNotFoundError
86
+ # Ignore files already deleted
87
+ end
88
+ end
89
+
90
+ def delete_prefixed(prefix)
91
+ instrument :delete_prefixed, prefix: prefix do
92
+ client.delete_prefix(prefix)
93
+ end
94
+ end
95
+
96
+ def exist?(key)
97
+ instrument :exist, key: key do |payload|
98
+ payload[:exist] = blob_for(key).present?
99
+ end
100
+ end
101
+
102
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata: {})
103
+ instrument :url, key: key do |payload|
104
+ generated_url = client.signed_uri(
105
+ key,
106
+ permissions: "rw",
107
+ expiry: format_expiry(expires_in)
108
+ ).to_s
109
+
110
+ payload[:url] = generated_url
111
+
112
+ generated_url
113
+ end
114
+ end
115
+
116
+ def headers_for_direct_upload(key, content_type:, checksum:, filename: nil, disposition: nil, custom_metadata: {}, **)
117
+ content_disposition = content_disposition_with(type: disposition, filename: filename) if filename
118
+
119
+ { "Content-Type" => content_type, "Content-MD5" => checksum, "x-ms-blob-content-disposition" => content_disposition, "x-ms-blob-type" => "BlockBlob", **custom_metadata_headers(custom_metadata) }
120
+ end
121
+
122
+ def compose(source_keys, destination_key, filename: nil, content_type: nil, disposition: nil, custom_metadata: {})
123
+ content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
124
+
125
+ client.create_append_blob(
126
+ destination_key,
127
+ content_type: content_type,
128
+ content_disposition: content_disposition,
129
+ metadata: custom_metadata,
130
+ )
131
+
132
+ source_keys.each do |source_key|
133
+ stream(source_key) do |chunk|
134
+ client.append_blob_block(destination_key, chunk)
135
+ end
136
+ end
137
+ end
138
+
139
+ private
140
+ def private_url(key, expires_in:, filename:, disposition:, content_type:, **)
141
+ client.signed_uri(
142
+ key,
143
+ permissions: "r",
144
+ expiry: format_expiry(expires_in),
145
+ content_disposition: content_disposition_with(type: disposition, filename: filename),
146
+ content_type: content_type
147
+ ).to_s
148
+ end
149
+
150
+ def public_url(key, **)
151
+ uri_for(key).to_s
152
+ end
153
+
154
+
155
+ def uri_for(key)
156
+ client.generate_uri("#{container}/#{key}")
157
+ end
158
+
159
+ def blob_for(key)
160
+ client.get_blob_properties(key)
161
+ rescue AzureBlob::Http::FileNotFoundError
162
+ false
163
+ end
164
+
165
+ def format_expiry(expires_in)
166
+ expires_in ? Time.now.utc.advance(seconds: expires_in).iso8601 : nil
167
+ end
168
+
169
+ # Reads the object for the given key in chunks, yielding each to the block.
170
+ def stream(key)
171
+ blob = blob_for(key)
172
+
173
+ chunk_size = 5.megabytes
174
+ offset = 0
175
+
176
+ raise ActiveStorage::FileNotFoundError unless blob.present?
177
+
178
+ while offset < blob.size
179
+ chunk = client.get_blob(key, start: offset, end: offset + chunk_size - 1)
180
+ yield chunk.force_encoding(Encoding::BINARY)
181
+ offset += chunk_size
182
+ end
183
+ end
184
+
185
+ def handle_errors
186
+ yield
187
+ rescue AzureBlob::Http::IntegrityError
188
+ raise ActiveStorage::IntegrityError
189
+ rescue AzureBlob::Http::FileNotFoundError
190
+ raise ActiveStorage::FileNotFoundError
191
+ end
192
+
193
+ def custom_metadata_headers(metadata)
194
+ AzureBlob::Metadata.new(metadata).headers
195
+ end
196
+ end
197
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AzureBlob
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure-blob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joé Dupuis
@@ -42,6 +42,7 @@ files:
42
42
  - devenv.lock
43
43
  - devenv.nix
44
44
  - devenv.yaml
45
+ - lib/active_storage/service/azure_blob_service.rb
45
46
  - lib/azure_blob.rb
46
47
  - lib/azure_blob/blob.rb
47
48
  - lib/azure_blob/blob_list.rb