Skip to content

Commit

Permalink
Allow providing a custom key per blob
Browse files Browse the repository at this point in the history
Closes #37315.
  • Loading branch information
georgeclaghorn committed Sep 30, 2019
1 parent 0069400 commit 4dba136
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 13 deletions.
11 changes: 11 additions & 0 deletions activestorage/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
* You can optionally provide a custom blob key when attaching a new file:

```ruby
user.avatar.attach key: "avatars/#{user.id}.jpg",
io: io, content_type: "image/jpeg", filename: "avatar.jpg"
```

Active Storage will store the blob's data on the configured service at the provided key.

*George Claghorn*

* Replace `Blob.create_after_upload!` with `Blob.create_and_upload!` and deprecate the former.

`create_after_upload!` has been removed since it could lead to data
Expand Down
16 changes: 8 additions & 8 deletions activestorage/app/models/active_storage/blob.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,23 @@ def build_after_upload(io:, filename:, content_type: nil, metadata: nil, identif

deprecate :build_after_upload

def build_after_unfurling(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil) #:nodoc:
new(filename: filename, content_type: content_type, metadata: metadata).tap do |blob|
def build_after_unfurling(key: nil, io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil) #:nodoc:
new(key: key, filename: filename, content_type: content_type, metadata: metadata).tap do |blob|
blob.unfurl(io, identify: identify)
end
end

def create_after_unfurling!(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil) #:nodoc:
build_after_unfurling(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
def create_after_unfurling!(key: nil, io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil) #:nodoc:
build_after_unfurling(key: key, io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
end

# Creates a new blob instance and then uploads the contents of
# the given <tt>io</tt> to the service. The blob instance is going to
# be saved before the upload begins to prevent the upload clobbering another due to key collisions.
# When providing a content type, pass <tt>identify: false</tt> to bypass
# automatic content type inference.
def create_and_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil)
create_after_unfurling!(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap do |blob|
def create_and_upload!(key: nil, io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil)
create_after_unfurling!(key: key, io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap do |blob|
blob.upload_without_unfurling(io)
end
end
Expand All @@ -87,8 +87,8 @@ def create_and_upload!(io:, filename:, content_type: nil, metadata: nil, identif
# in order to produce the signed URL for uploading. This signed URL points to the key generated by the blob.
# Once the form using the direct upload is submitted, the blob can be associated with the right record using
# the signed ID.
def create_before_direct_upload!(filename:, byte_size:, checksum:, content_type: nil, metadata: nil, record: nil)
create! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil, record: nil)
create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
end

# To prevent problems with case-insensitive filesystems, especially in combination
Expand Down
9 changes: 9 additions & 0 deletions activestorage/test/models/blob_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
assert_match(/^[a-z0-9]{28}$/, create_blob.key)
end

test "create_and_upload accepts a custom key" do
key = SecureRandom.base36(28)
data = "Hello world!"
blob = create_blob key: key, data: data

assert_equal key, blob.key
assert_equal data, blob.download
end

test "create_and_upload accepts a record for overrides" do
assert_nothing_raised do
create_blob(record: User.new)
Expand Down
10 changes: 5 additions & 5 deletions activestorage/test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ class ActiveSupport::TestCase
end

private
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true, record: nil)
ActiveStorage::Blob.create_and_upload! io: StringIO.new(data), filename: filename, content_type: content_type, identify: identify, record: record
def create_blob(key: nil, data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true, record: nil)
ActiveStorage::Blob.create_and_upload! key: key, io: StringIO.new(data), filename: filename, content_type: content_type, identify: identify, record: record
end

def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil, record: nil)
def create_file_blob(key: nil, filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil, record: nil)
ActiveStorage::Blob.create_and_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type, metadata: metadata, record: record
end

def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain", record: nil)
ActiveStorage::Blob.create_before_direct_upload! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, record: record
def create_blob_before_direct_upload(key: nil, filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain", record: nil)
ActiveStorage::Blob.create_before_direct_upload! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, record: record
end

def directly_upload_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", record: nil)
Expand Down

0 comments on commit 4dba136

Please sign in to comment.