activestorage-openstack 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/lib/active_storage/openstack/railtie.rb +12 -0
- data/lib/active_storage/openstack/version.rb +1 -1
- data/lib/active_storage/service/open_stack_service.rb +47 -33
- data/lib/activestorage-openstack.rb +2 -0
- metadata +17 -3
- data/lib/active_storage/openstack.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 983a4b66788932c6fa1e5af33b72ba32385e5b2e9b3664e353d6a8bdc30ea70a
|
4
|
+
data.tar.gz: 54a1176eb547289bd43dd8510cdc235c9e257cb2a208024744e9651b54ac2df4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0653477a4bae7e3d31cc81400c440906939a574e3ad6e07cbd545fe9c9574edd08cff487b8071d5cba9f103691069cd17ac7f65eef6b76ae510a47c7e4dd024
|
7
|
+
data.tar.gz: 2f4c2d22fb3f5087870b44a2348c2a8bd8dcda8d373f690202a642670fd37f543f5869b2a629fe649f2a401de7691424865f50d6af0dc1856f5efc544a33acee
|
data/README.md
CHANGED
@@ -57,6 +57,12 @@ the `openstack_temp_url_key` in your configuration is mandatory for generating U
|
|
57
57
|
|
58
58
|
The next version of this plugin, will add a rails generator, or expose a method that would use the built-in method from `Fog::OpenStack::Real` to generate the key.
|
59
59
|
|
60
|
+
## `ActiveStorage::Openstack`'s Content-Type handling
|
61
|
+
|
62
|
+
OpenStack Swift handles the Content-Type of an object differently from other object storage services. You cannot overwrite the Content-Type via a temp URL. This gem will try very hard to set the right Content-Type for an object at object creation (either via server upload or direct upload) but this is wrong in some edge cases (e.g. you use direct upload and the browser provides a wrong mime type).
|
63
|
+
|
64
|
+
For these edge cases `ActiveStorage::Blob::Identifiable` downloads the first 4K of a file, identifies the content type and saves the result in the database. For `ActiveStorage::Openstack` we also need to update the Content-Type of the object. This is done automatically with a little monkey patch.
|
65
|
+
|
60
66
|
## Testing
|
61
67
|
First, run `bundle` to install the gem dependencies (both development and production)
|
62
68
|
```bash
|
@@ -1,6 +1,18 @@
|
|
1
1
|
module ActiveStorage
|
2
2
|
module Openstack
|
3
3
|
class Railtie < ::Rails::Railtie
|
4
|
+
initializer "active_storage_openstack.blob" do
|
5
|
+
ActiveSupport.on_load(:active_storage_blob) do |klass|
|
6
|
+
klass.after_commit do |blob|
|
7
|
+
# overwrite content type if identification run and
|
8
|
+
# the service responds to change_content_type
|
9
|
+
if blob.identified? && !blob.content_type.blank? &&
|
10
|
+
blob.service.respond_to?(:change_content_type)
|
11
|
+
blob.service.change_content_type(blob.key, blob.content_type)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
4
16
|
end
|
5
17
|
end
|
6
18
|
end
|
@@ -16,7 +16,10 @@ module ActiveStorage
|
|
16
16
|
|
17
17
|
def upload(key, io, checksum: nil)
|
18
18
|
instrument :upload, key: key, checksum: checksum do
|
19
|
-
params = {
|
19
|
+
params = {
|
20
|
+
'Content-Type' => guess_content_type(io),
|
21
|
+
'ETag' => convert_base64digest_to_hexdigest(checksum)
|
22
|
+
}
|
20
23
|
begin
|
21
24
|
client.put_object(container, key, io, params)
|
22
25
|
rescue Excon::Error::UnprocessableEntity
|
@@ -28,7 +31,7 @@ module ActiveStorage
|
|
28
31
|
def download(key, &block)
|
29
32
|
if block_given?
|
30
33
|
instrument :streaming_download, key: key do
|
31
|
-
object_for(key, &block)
|
34
|
+
object_for(key, &block)
|
32
35
|
end
|
33
36
|
else
|
34
37
|
instrument :download, key: key do
|
@@ -39,17 +42,21 @@ module ActiveStorage
|
|
39
42
|
|
40
43
|
def download_chunk(key, range)
|
41
44
|
instrument :download_chunk, key: key, range: range do
|
42
|
-
|
45
|
+
chunk_buffer = []
|
46
|
+
|
47
|
+
object_for(key) do |chunk|
|
48
|
+
chunk_buffer << chunk
|
49
|
+
end
|
50
|
+
|
51
|
+
chunk_buffer.join[range]
|
43
52
|
end
|
44
53
|
end
|
45
54
|
|
46
55
|
def delete(key)
|
47
56
|
instrument :delete, key: key do
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
false
|
52
|
-
end
|
57
|
+
client.delete_object(container, key)
|
58
|
+
rescue Fog::Storage::OpenStack::NotFound
|
59
|
+
false
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
@@ -65,31 +72,28 @@ module ActiveStorage
|
|
65
72
|
|
66
73
|
def exist?(key)
|
67
74
|
instrument :exist, key: key do |payload|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
payload[:exist] = false
|
73
|
-
end
|
75
|
+
answer = object_for(key)
|
76
|
+
payload[:exist] = answer.present?
|
77
|
+
rescue Fog::Storage::OpenStack::NotFound
|
78
|
+
payload[:exist] = false
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
77
|
-
def url(key, expires_in:, disposition:, filename:,
|
82
|
+
def url(key, expires_in:, disposition:, filename:, **)
|
78
83
|
instrument :url, key: key do |payload|
|
79
84
|
expire_at = unix_timestamp_expires_at(expires_in)
|
80
|
-
generated_url = client.get_object_https_url(container,
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
content_type: content_type)
|
85
|
+
generated_url = client.get_object_https_url(container, key, expire_at)
|
86
|
+
generated_url += '&inline' if disposition.to_s != 'attachment'
|
87
|
+
generated_url += "&filename=#{Fog::OpenStack.escape(filename.to_s)}" unless filename.nil?
|
88
|
+
# unfortunately OpenStack Swift cannot overwrite the content type of an object via a temp url
|
89
|
+
# so we just ignore the content_type argument here
|
86
90
|
payload[:url] = generated_url
|
87
91
|
|
88
92
|
generated_url
|
89
93
|
end
|
90
94
|
end
|
91
95
|
|
92
|
-
def url_for_direct_upload(key, expires_in:,
|
96
|
+
def url_for_direct_upload(key, expires_in:, **)
|
93
97
|
instrument :url, key: key do |payload|
|
94
98
|
expire_at = unix_timestamp_expires_at(expires_in)
|
95
99
|
generated_url = client.create_temp_url(container,
|
@@ -97,10 +101,7 @@ module ActiveStorage
|
|
97
101
|
expire_at,
|
98
102
|
'PUT',
|
99
103
|
port: 443,
|
100
|
-
scheme:
|
101
|
-
content_type: content_type,
|
102
|
-
content_length: content_length,
|
103
|
-
etag: convert_base64digest_to_hexdigest(checksum))
|
104
|
+
scheme: 'https')
|
104
105
|
|
105
106
|
payload[:url] = generated_url
|
106
107
|
|
@@ -108,24 +109,33 @@ module ActiveStorage
|
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
111
|
-
def headers_for_direct_upload(
|
112
|
+
def headers_for_direct_upload(_key, content_type:, checksum:, **)
|
112
113
|
{
|
113
114
|
'Content-Type' => content_type,
|
114
|
-
'
|
115
|
-
'Content-Length' => content_length
|
115
|
+
'ETag' => convert_base64digest_to_hexdigest(checksum)
|
116
116
|
}
|
117
117
|
end
|
118
118
|
|
119
|
-
|
119
|
+
# Non-standard method to change the content type of an existing object
|
120
|
+
def change_content_type(key, content_type)
|
121
|
+
client.post_object(container,
|
122
|
+
key,
|
123
|
+
'Content-Type' => content_type)
|
124
|
+
true
|
125
|
+
rescue Fog::Storage::OpenStack::NotFound
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
120
130
|
|
121
131
|
def object_for(key, &block)
|
122
132
|
client.get_object(container, key, &block)
|
123
133
|
end
|
124
134
|
|
125
135
|
# ActiveStorage sends a `Digest::MD5.base64digest` checksum
|
126
|
-
# OpenStack expects a `Digest::MD5.hexdigest`
|
136
|
+
# OpenStack expects a `Digest::MD5.hexdigest` ETag
|
127
137
|
def convert_base64digest_to_hexdigest(base64digest)
|
128
|
-
base64digest
|
138
|
+
base64digest&.unpack1('m0')&.unpack1('H*')
|
129
139
|
end
|
130
140
|
|
131
141
|
def unix_timestamp_expires_at(seconds_from_now)
|
@@ -136,6 +146,10 @@ module ActiveStorage
|
|
136
146
|
" bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}"
|
137
147
|
end
|
138
148
|
|
139
|
-
|
149
|
+
def guess_content_type(io)
|
150
|
+
Marcel::MimeType.for io,
|
151
|
+
name: io.try(:original_filename),
|
152
|
+
declared_type: io.try(:content_type)
|
153
|
+
end
|
140
154
|
end
|
141
155
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activestorage-openstack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chedli Bourguiba
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fog-openstack
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: marcel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: sqlite3
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -76,10 +90,10 @@ files:
|
|
76
90
|
- MIT-LICENSE
|
77
91
|
- README.md
|
78
92
|
- Rakefile
|
79
|
-
- lib/active_storage/openstack.rb
|
80
93
|
- lib/active_storage/openstack/railtie.rb
|
81
94
|
- lib/active_storage/openstack/version.rb
|
82
95
|
- lib/active_storage/service/open_stack_service.rb
|
96
|
+
- lib/activestorage-openstack.rb
|
83
97
|
- lib/tasks/activestorage/openstack_tasks.rake
|
84
98
|
homepage: https://github.com/chaadow/activestorage-openstack
|
85
99
|
licenses:
|