Skip to content

Commit

Permalink
Support moving files between uris
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeSouthan committed Feb 10, 2021
1 parent e9ccc10 commit c53dfe6
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ tmp/

.bundle/
.rspec_status
.yardoc/
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,9 @@ FileStorage.for("inmemory://bucket/path/").list
FileStorage.for("inmemory://bucket/path/").delete!
=> true
```

### Moving a file
```ruby
FileStorage.for("inmemory://bucket/path/file.xml").move!("inmemory://bucket/path/file2.xml")
=> "inmemory://bucket/path/file2.xml"
```
33 changes: 33 additions & 0 deletions lib/file_storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,39 @@ def delete!
true
end

# Moves the existing file to a new file path
#
# @param [String] new_key The new key to move the file to
# @return [String] A URI to the file's new path
# @example Move a file
# FileStorage.for("inmemory://bucket1/foo").move!("inmemory://bucket2/bar")
def move!(new_key)
raise ArgumentError, "Key cannot be empty" if key.empty?

new_key_ctx = FileStorage.for(new_key)

unless new_key_ctx.adapter_type == adapter_type
raise ArgumentError, "Adapter type must be the same"
end
raise ArgumentError, "Destination key cannot be empty" if new_key_ctx.key.empty?

info("Moving file", new_key: new_key, event: "move")

start = FileStorage::Timing.monotonic_now
result = adapter.move!(
bucket: bucket,
key: key,
new_bucket: new_key_ctx.bucket,
new_key: new_key_ctx.key,
)

info("Moved file",
event: "move_finished",
duration: FileStorage::Timing.monotonic_now - start)

"#{adapter_type}://#{result[:bucket]}/#{result[:key]}"
end

private

attr_reader :adapter
Expand Down
9 changes: 9 additions & 0 deletions lib/file_storage/disk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ def delete!(bucket:, key:)
true
end

def move!(bucket:, key:, new_bucket:, new_key:)
FileUtils.mv(key_path(bucket, key), key_path(new_bucket, new_key))

{
bucket: new_bucket,
key: new_key,
}
end

private

attr_reader :base_dir
Expand Down
12 changes: 12 additions & 0 deletions lib/file_storage/gcs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ def delete!(bucket:, key:)
true
end

def move!(bucket:, key:, new_bucket:, new_key:)
old_file = get_bucket(bucket).file(key)
destination_bucket = get_bucket(new_bucket)
file.copy(destination_bucket.name, new_key)
old_file.delete

{
bucket: new_bucket,
key: new_key,
}
end

private

attr_reader :storage
Expand Down
8 changes: 8 additions & 0 deletions lib/file_storage/in_memory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,13 @@ def delete!(bucket:, key:)

true
end

def move!(bucket:, key:, new_bucket:, new_key:)
@buckets[new_bucket][new_key] = @buckets.fetch(bucket).delete(key)
{
bucket: new_bucket,
key: new_key,
}
end
end
end
40 changes: 40 additions & 0 deletions spec/file_storage/disk_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,44 @@
to raise_error(Errno::ENOENT, /No such file or directory/)
end
end

describe "#move!" do
subject(:move) do
instance.move!(bucket: bucket, key: key, new_bucket: new_bucket, new_key: new_key)
end

let(:new_bucket) { "cake" }

context "when the 'existing' file doesn't exist" do
let(:key) { "foobar" }
let(:new_key) { "barbaz" }

it "raises a File error" do
expect { move }.to raise_error(Errno::ENOENT, /No such file or directory/)
end
end

context "when the file does exist" do
let(:key) { "2021-02-08/hello1" }
let(:new_key) { "2021-02-08/hello2" }
let(:content) { "world" }

before { instance.upload!(bucket: bucket, key: key, content: content) }

it "moves the file" do
move

expect(instance.download(bucket: new_bucket, key: new_key)[:content]).to eq(content)
expect { instance.download(bucket: bucket, key: key)[:content] }.
to raise_error(Errno::ENOENT, /No such file or directory/)
end

it "returns the expected payload" do
expect(move).to eq(
bucket: new_bucket,
key: new_key,
)
end
end
end
end
40 changes: 40 additions & 0 deletions spec/file_storage/in_memory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,46 @@
end
end

describe "#move!" do
subject(:move) do
instance.move!(bucket: bucket, key: key, new_bucket: new_bucket, new_key: new_key)
end

let(:new_bucket) { "cake" }

context "when the 'existing' file doesn't exist" do
let(:key) { "foobar" }
let(:new_key) { "barbaz" }

it "raises a KeyError" do
expect { move }.to raise_error(KeyError, /#{bucket}/)
end
end

context "when the file does exist" do
let(:key) { "2021-02-08/hello1" }
let(:new_key) { "2021-02-08/hello2" }
let(:content) { "world" }

before { instance.upload!(bucket: bucket, key: key, content: content) }

it "moves the file" do
move

expect(instance.download(bucket: new_bucket, key: new_key)[:content]).to eq(content)
expect { instance.download(bucket: bucket, key: key)[:content] }.
to raise_error(KeyError, /key not found/)
end

it "returns the expected payload" do
expect(move).to eq(
bucket: new_bucket,
key: new_key,
)
end
end
end

describe "#reset!" do
let(:bucket2) { "bucket2" }

Expand Down
27 changes: 27 additions & 0 deletions spec/file_storage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,32 @@
expect(described_class.for("inmemory://bucket/file1").delete!).to eq(true)
end
end

describe "#move!" do
subject(:move) { described_class.for(old_key).move!(new_key) }

let(:old_key) { "inmemory://bucket/file1" }
let(:new_key) { "inmemory://bucket2/file2" }

before { described_class.for(old_key).upload!("hello") }

it "returns the new path's uri" do
expect(move).to eq(new_key)
end

it "moves the file" do
move

expect(described_class.for(new_key).download[:content]).to eq("hello")
end

context "with a different adapter" do
let(:new_key) { "disk://bucket2/file2" }

it "raises an error" do
expect { move }.to raise_error(ArgumentError, /Adapter type/)
end
end
end
end
end

0 comments on commit c53dfe6

Please sign in to comment.