attachie 0.0.1 → 1.0.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/README.md +48 -3
- data/attachie.gemspec +0 -1
- data/lib/attachie.rb +10 -1
- data/lib/attachie/fake_driver.rb +12 -0
- data/lib/attachie/file_driver.rb +12 -0
- data/lib/attachie/s3_driver.rb +16 -0
- data/lib/attachie/version.rb +1 -1
- data/spec/attachie/fake_driver_spec.rb +32 -18
- data/spec/attachie/file_driver_spec.rb +53 -25
- data/spec/attachie/s3_driver_spec.rb +87 -37
- data/spec/attachie_spec.rb +39 -6
- metadata +2 -17
- data/lib/attachie/swift_driver.rb +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a6322baba1a72a9869ed2cb50a2f58a890fed49a5b1fbfb57c6e358dfcce4b2
|
4
|
+
data.tar.gz: 7e95e247cf920af92fd51d32b21bf2d0a82a59af022cacc1343a2bb71612d5b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef797be58fb2573636f3cfc9de41fc1484cc8f6357488e7d1699ed5288a79b3c2ca0a51795ab825ce8941f05c69986dd84dd05b1861493ef74c8cc1afb5a1cd6
|
7
|
+
data.tar.gz: e986a15bc1a7781d4d60f620949582ae455c0dc6374c818930c7990a430bc9cbc4740d14cb2b0a60865769c084b7bc5bbce9c55f5aa1cabb0a10790704767745
|
data/README.md
CHANGED
@@ -36,6 +36,9 @@ class User
|
|
36
36
|
end
|
37
37
|
```
|
38
38
|
|
39
|
+
Please note, Attachie will interpolate colon prefixed segments like `:id` by
|
40
|
+
replacing it with the return value of the respective method.
|
41
|
+
|
39
42
|
Second, store blobs for your version:
|
40
43
|
|
41
44
|
```ruby
|
@@ -71,14 +74,15 @@ user.avatar(:icon).temp_url(expires_in: 2.days) # Must be supported by the drive
|
|
71
74
|
|
72
75
|
## Drivers
|
73
76
|
|
74
|
-
The `
|
77
|
+
The `Attachie` gem ships with the following drivers:
|
75
78
|
|
76
79
|
* `Attachie::FileDriver`: To store files on the local file system
|
77
80
|
* `Attachie::FakeDriver`: To store files in memory (for testing)
|
78
81
|
* `Attachie::S3Driver`: To store files on S3
|
79
|
-
* `Attachie::SwiftDriver`: To store files on an Openstack Swift provider
|
80
82
|
|
81
|
-
|
83
|
+
### FileDriver
|
84
|
+
|
85
|
+
To use the file driver:
|
82
86
|
|
83
87
|
```ruby
|
84
88
|
require "attachie/file_driver"
|
@@ -94,6 +98,47 @@ class User
|
|
94
98
|
end
|
95
99
|
```
|
96
100
|
|
101
|
+
### S3Driver
|
102
|
+
|
103
|
+
To use the s3 driver:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
require "attachie/s3_driver"
|
107
|
+
|
108
|
+
Attachie.default_options[:driver] = Attachie::S3Driver.new(Aws::S3::Client.new('...'))
|
109
|
+
```
|
110
|
+
|
111
|
+
### FakeDriver
|
112
|
+
|
113
|
+
To use the fake driver (useful for testing):
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
require "attachie/fake_driver"
|
117
|
+
|
118
|
+
Attachie.default_options[:driver] = Attache::FakeDriver.new
|
119
|
+
```
|
120
|
+
|
121
|
+
Drivers and other options can be set on an attachment level as well:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
class User
|
125
|
+
include Attachie
|
126
|
+
|
127
|
+
attachment :avatar, driver: MyFileDriver, versions: {
|
128
|
+
# ...
|
129
|
+
}
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
## Direct S3 Uploads
|
134
|
+
|
135
|
+
Attachie allows to presign s3 post requests like:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
user.avatar(:icon).presign_post(content_type: 'image/jpeg', ...)
|
139
|
+
# => {"fields"=>{"key"=>"path/to/object","x-amz-signature"=>"..."},"headers":{},"method"=>"post","url"=>"..."}
|
140
|
+
```
|
141
|
+
|
97
142
|
## Contributing
|
98
143
|
|
99
144
|
1. Fork it ( https://github.com/mrkamel/attachie/fork )
|
data/attachie.gemspec
CHANGED
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "swift_client"
|
22
21
|
spec.add_dependency "aws-sdk-s3"
|
23
22
|
spec.add_dependency "mime-types"
|
24
23
|
spec.add_dependency "connection_pool"
|
data/lib/attachie.rb
CHANGED
@@ -7,6 +7,7 @@ module Attachie
|
|
7
7
|
class UnknownAttachment < StandardError; end
|
8
8
|
class NoSuchVersion < StandardError; end
|
9
9
|
class InterpolationError < StandardError; end
|
10
|
+
class NotSupported < StandardError; end
|
10
11
|
|
11
12
|
def self.default_options
|
12
13
|
@default_options ||= { :protocol => "http" }
|
@@ -22,6 +23,10 @@ module Attachie
|
|
22
23
|
self.options = options
|
23
24
|
end
|
24
25
|
|
26
|
+
def presigned_post(options = {})
|
27
|
+
option(:driver).presigned_post(path, container, options)
|
28
|
+
end
|
29
|
+
|
25
30
|
def url
|
26
31
|
"#{interpolate option(:protocol)}://#{interpolate option(:host)}/#{interpolate(option(:url_prefix)).to_s + "/" if option(:url_prefix)}#{path}#{interpolate(option(:url_suffix)) if option(:url_suffix)}"
|
27
32
|
end
|
@@ -54,6 +59,10 @@ module Attachie
|
|
54
59
|
option(:driver).store(path, data_or_io, container, opts)
|
55
60
|
end
|
56
61
|
|
62
|
+
def info
|
63
|
+
option(:driver).info(path, container)
|
64
|
+
end
|
65
|
+
|
57
66
|
def store_multipart(opts = {}, &block)
|
58
67
|
option(:driver).store_multipart(path, container, opts, &block)
|
59
68
|
end
|
@@ -148,7 +157,7 @@ module Attachie
|
|
148
157
|
|
149
158
|
raise(UnknownAttachment) unless definition
|
150
159
|
|
151
|
-
Attachment.new
|
160
|
+
Attachment.new(self, name, definition)
|
152
161
|
end
|
153
162
|
|
154
163
|
module ClassMethods
|
data/lib/attachie/fake_driver.rb
CHANGED
@@ -42,6 +42,18 @@ module Attachie
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
def info(name, bucket)
|
46
|
+
{
|
47
|
+
last_modified: nil,
|
48
|
+
content_length: objects(bucket)[name].size,
|
49
|
+
content_type: MIME::Types.of(name).first&.to_s
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def presigned_post(name, bucket, options = {})
|
54
|
+
raise NotSupported, 'presigned_post is not supported in FakeDriver'
|
55
|
+
end
|
56
|
+
|
45
57
|
def store(name, data_or_io, bucket, options = {})
|
46
58
|
objects(bucket)[name] = data_or_io.respond_to?(:read) ? data_or_io.read : data_or_io
|
47
59
|
end
|
data/lib/attachie/file_driver.rb
CHANGED
@@ -65,6 +65,18 @@ module Attachie
|
|
65
65
|
true
|
66
66
|
end
|
67
67
|
|
68
|
+
def presigned_post(name, bucket, options = {})
|
69
|
+
raise NotSupported, 'presigned_post is not supported in FileDriver'
|
70
|
+
end
|
71
|
+
|
72
|
+
def info(name, bucket)
|
73
|
+
{
|
74
|
+
last_modified: File.mtime(path_for(name, bucket)),
|
75
|
+
content_type: MIME::Types.of(name).first&.to_s,
|
76
|
+
content_length: File.size(path_for(name, bucket))
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
68
80
|
def store_multipart(name, bucket, options = {}, &block)
|
69
81
|
path = path_for(name, bucket)
|
70
82
|
|
data/lib/attachie/s3_driver.rb
CHANGED
@@ -59,6 +59,12 @@ module Attachie
|
|
59
59
|
self.s3_resource = Aws::S3::Resource.new(client: s3_client)
|
60
60
|
end
|
61
61
|
|
62
|
+
def presigned_post(name, bucket, options = {})
|
63
|
+
res = s3_resource.bucket(bucket).object(name).presigned_post(options)
|
64
|
+
|
65
|
+
return { fields: res.fields, headers: {}, method: "post", url: res.url }
|
66
|
+
end
|
67
|
+
|
62
68
|
def list(bucket, prefix: nil)
|
63
69
|
return enum_for(:list, bucket, prefix: prefix) unless block_given?
|
64
70
|
|
@@ -70,6 +76,16 @@ module Attachie
|
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
79
|
+
def info(name, bucket)
|
80
|
+
object = s3_resource.bucket(bucket).object(name)
|
81
|
+
|
82
|
+
{
|
83
|
+
content_length: object.content_length,
|
84
|
+
last_modified: object.last_modified,
|
85
|
+
content_type: object.content_type
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
73
89
|
def store(name, data_or_io, bucket, options = {})
|
74
90
|
opts = options.dup
|
75
91
|
|
data/lib/attachie/version.rb
CHANGED
@@ -4,32 +4,36 @@ require File.expand_path("../../spec_helper", __FILE__)
|
|
4
4
|
RSpec.describe Attachie::FakeDriver do
|
5
5
|
let(:driver) { Attachie::FakeDriver.new }
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
after { driver.flush }
|
8
|
+
|
9
|
+
describe "#presigned_post" do
|
10
|
+
it "raises NotSupported" do
|
11
|
+
expect { driver.presigned_post("path", "bucket") }.to raise_error(Attachie::NotSupported)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#list" do
|
16
|
+
it "lists objects" do
|
9
17
|
driver.store("object1", "blob", "bucket1")
|
10
18
|
driver.store("object2", "blob", "bucket1")
|
11
19
|
driver.store("other", "blob", "bucket1")
|
12
20
|
driver.store("object", "blob", "bucket3")
|
13
21
|
|
14
22
|
expect(driver.list("bucket1", prefix: "object").to_a).to eq(["object1", "object2"])
|
15
|
-
ensure
|
16
|
-
driver.flush
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
20
|
-
|
21
|
-
|
26
|
+
describe "#store" do
|
27
|
+
it "stores a blob" do
|
22
28
|
driver.store("name", "blob", "bucket")
|
23
29
|
|
24
30
|
expect(driver.exists?("name", "bucket")).to be(true)
|
25
31
|
expect(driver.value("name", "bucket")).to eq("blob")
|
26
|
-
ensure
|
27
|
-
driver.flush
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
describe "#store_multipart" do
|
36
|
+
it "stores a blob via multipart upload" do
|
33
37
|
driver.store_multipart("name", "bucket") do |upload|
|
34
38
|
upload.upload_part("chunk1")
|
35
39
|
upload.upload_part("chunk2")
|
@@ -37,25 +41,35 @@ RSpec.describe Attachie::FakeDriver do
|
|
37
41
|
|
38
42
|
expect(driver.exists?("name", "bucket")).to be(true)
|
39
43
|
expect(driver.value("name", "bucket")).to eq("chunk1chunk2")
|
40
|
-
ensure
|
41
|
-
driver.flush
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
+
describe "#delete" do
|
48
|
+
it "deletes a blob" do
|
47
49
|
driver.store("name", "blob", "bucket")
|
48
50
|
expect(driver.exists?("name", "bucket")).to be(true)
|
49
51
|
|
50
52
|
driver.delete("name", "bucket")
|
51
53
|
expect(driver.exists?("name", "bucket")).to be(false)
|
52
|
-
ensure
|
53
|
-
driver.flush
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
|
58
|
-
|
57
|
+
describe "#temp_url" do
|
58
|
+
it "generates a temp_url" do
|
59
|
+
expect(driver.temp_url("name", "bucket")).to eq("https://example.com/bucket/name?signature=signature&expires=expires")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#info" do
|
64
|
+
it "returns info about the object" do
|
65
|
+
driver.store("name.txt", "blob", "bucket")
|
66
|
+
|
67
|
+
expect(driver.info("name.txt", "bucket")).to eq(
|
68
|
+
content_length: 4,
|
69
|
+
content_type: "text/plain",
|
70
|
+
last_modified: nil
|
71
|
+
)
|
72
|
+
end
|
59
73
|
end
|
60
74
|
end
|
61
75
|
|
@@ -4,40 +4,68 @@ require File.expand_path("../../spec_helper", __FILE__)
|
|
4
4
|
RSpec.describe Attachie::FileDriver do
|
5
5
|
let(:driver) { Attachie::FileDriver.new("/tmp/attachie") }
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
driver.
|
10
|
-
|
11
|
-
expect(driver.exists?("name", "bucket")).to be(true)
|
12
|
-
expect(driver.value("name", "bucket")).to eq("blob")
|
13
|
-
ensure
|
14
|
-
driver.delete("name", "bucket")
|
7
|
+
describe "#presigned_post" do
|
8
|
+
it "raises NotSupported" do
|
9
|
+
expect { driver.presigned_post("path", "bucket") }.to raise_error(Attachie::NotSupported)
|
15
10
|
end
|
16
11
|
end
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
describe "#store" do
|
14
|
+
it "stores a blob" do
|
15
|
+
begin
|
16
|
+
driver.store("name", "blob", "bucket")
|
17
|
+
|
18
|
+
expect(driver.exists?("name", "bucket")).to be(true)
|
19
|
+
expect(driver.value("name", "bucket")).to eq("blob")
|
20
|
+
ensure
|
21
|
+
driver.delete("name", "bucket")
|
23
22
|
end
|
23
|
+
end
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
describe" #store_multipart" do
|
27
|
+
it "stores a blob via multipart upload" do
|
28
|
+
begin
|
29
|
+
driver.store_multipart("name", "bucket") do |upload|
|
30
|
+
upload.upload_part("chunk1")
|
31
|
+
upload.upload_part("chunk2")
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(driver.exists?("name", "bucket")).to be(true)
|
35
|
+
expect(driver.value("name", "bucket")).to eq("chunk1chunk2")
|
36
|
+
ensure
|
37
|
+
driver.delete("name", "bucket")
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
42
|
+
describe "#delete" do
|
43
|
+
it "deletes a blob" do
|
44
|
+
begin
|
45
|
+
driver.store("name", "blob", "bucket")
|
46
|
+
expect(driver.exists?("name", "bucket")).to be(true)
|
36
47
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
driver.delete("name", "bucket")
|
49
|
+
expect(driver.exists?("name", "bucket")).to be(false)
|
50
|
+
ensure
|
51
|
+
driver.delete("name", "bucket")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#info" do
|
57
|
+
it "returns info about the object" do
|
58
|
+
begin
|
59
|
+
driver.store("name.txt", "blob", "bucket")
|
60
|
+
|
61
|
+
expect(driver.info("name.txt", "bucket")).to match(
|
62
|
+
content_length: 4,
|
63
|
+
content_type: "text/plain",
|
64
|
+
last_modified: anything
|
65
|
+
)
|
66
|
+
ensure
|
67
|
+
driver.delete("name.txt", "bucket")
|
68
|
+
end
|
41
69
|
end
|
42
70
|
end
|
43
71
|
end
|
@@ -11,59 +11,109 @@ RSpec.describe Attachie::S3Driver do
|
|
11
11
|
))
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
describe "#list" do
|
15
|
+
it "lists objects" do
|
16
|
+
begin
|
17
|
+
driver.store("object1", "blob", "bucket")
|
18
|
+
driver.store("object2", "blob", "bucket")
|
19
|
+
driver.store("other", "blob", "bucket")
|
20
|
+
|
21
|
+
expect(driver.list("bucket", prefix: "object").to_a).to eq(["object1", "object2"])
|
22
|
+
ensure
|
23
|
+
driver.delete("object1", "bucket")
|
24
|
+
driver.delete("object2", "bucket")
|
25
|
+
driver.delete("other", "bucket")
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
30
|
+
describe "#store" do
|
31
|
+
it "stores a blob" do
|
32
|
+
begin
|
33
|
+
driver.store("name", "blob", "bucket")
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
expect(driver.exists?("name", "bucket")).to be(true)
|
36
|
+
expect(driver.value("name", "bucket")).to eq("blob")
|
37
|
+
ensure
|
38
|
+
driver.delete("name", "bucket")
|
39
|
+
end
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
describe "#store_multipart" do
|
44
|
+
it "stores a blob via multipart upload" do
|
45
|
+
begin
|
46
|
+
driver.store_multipart("name", "bucket") do |upload|
|
47
|
+
upload.upload_part("chunk1")
|
48
|
+
upload.upload_part("chunk2")
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(driver.exists?("name", "bucket")).to be(true)
|
52
|
+
expect(driver.value("name", "bucket")).to eq("chunk1chunk2")
|
53
|
+
ensure
|
54
|
+
driver.delete("name", "bucket")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#delete" do
|
60
|
+
it "deletes a blob" do
|
61
|
+
begin
|
62
|
+
driver.store("name", "blob", "bucket")
|
63
|
+
expect(driver.exists?("name", "bucket")).to be(true)
|
64
|
+
|
65
|
+
driver.delete("name", "bucket")
|
66
|
+
expect(driver.exists?("name", "bucket")).to be(false)
|
67
|
+
ensure
|
68
|
+
driver.delete("name", "bucket")
|
44
69
|
end
|
70
|
+
end
|
71
|
+
end
|
45
72
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
driver.delete("name", "bucket")
|
73
|
+
describe "#temp_url" do
|
74
|
+
it "generates a temp_url" do
|
75
|
+
expect(driver.temp_url("name", "bucket")).to be_url
|
50
76
|
end
|
51
77
|
end
|
52
78
|
|
53
|
-
|
54
|
-
|
55
|
-
driver.
|
56
|
-
|
79
|
+
describe "#presigned_post" do
|
80
|
+
it "generates a presign response" do
|
81
|
+
expect(driver.presigned_post("path/to/object", "bucket")).to match(
|
82
|
+
fields: hash_including("key" => "path/to/object"),
|
83
|
+
headers: {},
|
84
|
+
method: "post",
|
85
|
+
url: "http://bucket.localhost:4569"
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "supports and passes additional options" do
|
90
|
+
bucket = double
|
91
|
+
object = double
|
92
|
+
|
93
|
+
allow(bucket).to receive(:object).and_return(object)
|
94
|
+
allow(object).to receive(:presigned_post).and_return(OpenStruct.new)
|
95
|
+
allow(driver.s3_resource).to receive(:bucket).and_return(bucket)
|
57
96
|
|
58
|
-
driver.
|
59
|
-
|
60
|
-
|
61
|
-
driver.delete("name", "bucket")
|
97
|
+
driver.presigned_post("path", "bucket", { key: "value" })
|
98
|
+
|
99
|
+
expect(object).to have_received(:presigned_post).with(hash_including(key: "value"))
|
62
100
|
end
|
63
101
|
end
|
64
102
|
|
65
|
-
|
66
|
-
|
103
|
+
describe "#info" do
|
104
|
+
it "returns info about the object" do
|
105
|
+
begin
|
106
|
+
driver.store("name.txt", "blob", "bucket")
|
107
|
+
|
108
|
+
expect(driver.info("name.txt", "bucket")).to match(
|
109
|
+
content_length: 4,
|
110
|
+
content_type: "text/plain",
|
111
|
+
last_modified: anything
|
112
|
+
)
|
113
|
+
ensure
|
114
|
+
driver.delete("name.txt", "bucket")
|
115
|
+
end
|
116
|
+
end
|
67
117
|
end
|
68
118
|
end
|
69
119
|
|
data/spec/attachie_spec.rb
CHANGED
@@ -17,32 +17,32 @@ class TestModel
|
|
17
17
|
end
|
18
18
|
|
19
19
|
RSpec.describe TestModel do
|
20
|
-
it "
|
20
|
+
it "interpolates the path" do
|
21
21
|
test_model = TestModel.new(filename: "file.jpg")
|
22
22
|
|
23
23
|
expect(test_model.file(:small).path).to eq("path/to/small/file.jpg")
|
24
24
|
end
|
25
25
|
|
26
|
-
it "
|
26
|
+
it "allows arbitrary version methods" do
|
27
27
|
test_model = TestModel.new(filename: "file.jpg")
|
28
28
|
|
29
29
|
expect(test_model.file(:small).attribute).to eq("value")
|
30
30
|
end
|
31
31
|
|
32
|
-
it "
|
32
|
+
it "respects the host" do
|
33
33
|
test_model = TestModel.new(filename: "file.jpg")
|
34
34
|
|
35
35
|
expect(test_model.file(:large).url).to eq("http://www.example.com/path/to/large/file.jpg")
|
36
36
|
end
|
37
37
|
|
38
|
-
it "
|
38
|
+
it "correctly uses the driver" do
|
39
39
|
test_model = TestModel.new(filename: "blob.txt")
|
40
40
|
test_model.file(:large).store "blob"
|
41
41
|
|
42
42
|
expect(test_model.file(:large).value).to eq("blob")
|
43
43
|
end
|
44
44
|
|
45
|
-
it "
|
45
|
+
it "sets updated_at" do
|
46
46
|
test_model = TestModel.new(filename: "file.jpg")
|
47
47
|
test_model.file = "file"
|
48
48
|
|
@@ -53,5 +53,38 @@ RSpec.describe TestModel do
|
|
53
53
|
|
54
54
|
expect(test_model.updated_at).to be_nil
|
55
55
|
end
|
56
|
-
end
|
57
56
|
|
57
|
+
describe "#info" do
|
58
|
+
it "returns info about the attachment" do
|
59
|
+
test_model = TestModel.new(filename: "blob.txt")
|
60
|
+
test_model.file(:large).store("blob")
|
61
|
+
|
62
|
+
expect(test_model.file(:large).info).to match(
|
63
|
+
last_modified: anything,
|
64
|
+
content_type: anything,
|
65
|
+
content_length: anything
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#presigned_post" do
|
71
|
+
let(:attachment) { TestModel.new(filename: "file.jpg").file(:large) }
|
72
|
+
let(:driver) { TestModel.attachments[:file][:driver] }
|
73
|
+
|
74
|
+
before do
|
75
|
+
allow(driver).to receive(:presigned_post)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "delegates to the driver" do
|
79
|
+
attachment.presigned_post
|
80
|
+
|
81
|
+
expect(driver).to have_received(:presigned_post).with(attachment.path, attachment.bucket, {})
|
82
|
+
end
|
83
|
+
|
84
|
+
it "passes the supplied options" do
|
85
|
+
attachment.presigned_post(key: "value")
|
86
|
+
|
87
|
+
expect(driver).to have_received(:presigned_post).with(attachment.path, attachment.bucket, { key: "value" })
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attachie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Vetter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: swift_client
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: aws-sdk-s3
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,7 +142,6 @@ files:
|
|
156
142
|
- lib/attachie/file_driver.rb
|
157
143
|
- lib/attachie/interpolation.rb
|
158
144
|
- lib/attachie/s3_driver.rb
|
159
|
-
- lib/attachie/swift_driver.rb
|
160
145
|
- lib/attachie/version.rb
|
161
146
|
- spec/attachie/fake_driver_spec.rb
|
162
147
|
- spec/attachie/file_driver_spec.rb
|
@@ -1,66 +0,0 @@
|
|
1
|
-
|
2
|
-
require "swift_client"
|
3
|
-
require "connection_pool"
|
4
|
-
|
5
|
-
module Attachie
|
6
|
-
class SwiftDriver
|
7
|
-
attr_accessor :swift_client_pool
|
8
|
-
|
9
|
-
def initialize(swift_client_pool)
|
10
|
-
self.swift_client_pool = swift_client_pool
|
11
|
-
end
|
12
|
-
|
13
|
-
def list(container, prefix: nil)
|
14
|
-
return enum_for(:list, container, prefix: prefix) unless block_given?
|
15
|
-
|
16
|
-
swift_client_pool.with do |swift_client|
|
17
|
-
swift_client.paginate_objects(container, prefix: prefix) do |response|
|
18
|
-
response.parsed_response.each do |source_object|
|
19
|
-
yield source_object["name"]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def store(name, data_or_io, container, headers = {})
|
26
|
-
swift_client_pool.with do |swift_client|
|
27
|
-
swift_client.put_object name, data_or_io, container, headers
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def value(name, container)
|
32
|
-
swift_client_pool.with do |swift_client|
|
33
|
-
swift_client.get_object(name, container).body
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def delete(name, container)
|
38
|
-
swift_client_pool.with do |swift_client|
|
39
|
-
swift_client.delete_object name, container
|
40
|
-
end
|
41
|
-
rescue SwiftClient::ResponseError => e
|
42
|
-
return true if e.code == 404
|
43
|
-
|
44
|
-
raise e
|
45
|
-
end
|
46
|
-
|
47
|
-
def exists?(name, container)
|
48
|
-
swift_client_pool.with do |swift_client|
|
49
|
-
swift_client.head_object name, container
|
50
|
-
end
|
51
|
-
|
52
|
-
true
|
53
|
-
rescue SwiftClient::ResponseError => e
|
54
|
-
return false if e.code == 404
|
55
|
-
|
56
|
-
raise e
|
57
|
-
end
|
58
|
-
|
59
|
-
def temp_url(name, container, options = {})
|
60
|
-
swift_client_pool.with do |swift_client|
|
61
|
-
swift_client.temp_url name, container, options
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|