bfs 0.7.3 → 0.8.1

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: 6195cd2787fbdb31ce0b8af2eb251effe1b7e3389f9932c5d33162b608b76d03
4
- data.tar.gz: 22e24de8c867056c68beb0d49672400915dd4247660430e0a21bdf5b132dbdbe
3
+ metadata.gz: 20fe1fe66fa9d9d5bc0bbee1e912a505eed9a19bfc26bfc7c7d4a37aa7cb11bc
4
+ data.tar.gz: 6ed8b53369f1cdd1258ff371599b29658b0232f5c05f4c6b1425120520d7e0a2
5
5
  SHA512:
6
- metadata.gz: 17810380ac6d3452247c63057bc06e11c5d0e831afff661638c09be8f9ea0c3ec586b39d563b46a059d979c66a1752699f2633a01d2ea8ae47357c8fbd7d2f2a
7
- data.tar.gz: 9843329935c5561d2df1ac6e957168a68ce2d76033541e34288e7b6f7da898a1ca64da63508d3d79cfc9922d899958a86423056557c9159fde086cb003be5fd4
6
+ metadata.gz: 50d32db0f8b0014c06bdacd7f418dc26bca5bc1aae773d45128b2fc9cd02990a36833b05a0957972df0fb52a12f1258385ec2f5987bff7de07afa47f381208f8
7
+ data.tar.gz: 6654baa91816121fbea28ea73f854f47b5477325a5c2f1c5e588a81fcab9df60659cbbf760dac48f75e3456bc08af966f807706e1f694470602ef26956ad1fa5
data/lib/bfs.rb CHANGED
@@ -4,6 +4,8 @@ require 'cgi'
4
4
  module BFS
5
5
  class FileInfo < Hash
6
6
  def initialize(**attrs)
7
+ super(nil)
8
+
7
9
  update(size: 0, mtime: Time.at(0), mode: 0, metadata: {})
8
10
  update(attrs)
9
11
  end
@@ -36,10 +38,23 @@ module BFS
36
38
  def self.register(*schemes, &resolver)
37
39
  @registry ||= {}
38
40
  schemes.each do |scheme|
41
+ scheme = scheme.to_s
42
+ raise(ArgumentError, "scheme #{scheme} is already registered") if @registry.key?(scheme)
43
+
39
44
  @registry[scheme] = resolver
40
45
  end
41
46
  end
42
47
 
48
+ def self.unregister(*schemes)
49
+ @registry ||= {}
50
+ schemes.each do |scheme|
51
+ scheme = scheme.to_s
52
+ raise(ArgumentError, "scheme #{scheme} is not registered") unless @registry.key?(scheme)
53
+
54
+ @registry.delete(scheme)
55
+ end
56
+ end
57
+
43
58
  def self.resolve(url, &block)
44
59
  url = url.is_a?(::URI) ? url.dup : URI.parse(url)
45
60
  rsl = @registry[url.scheme]
data/lib/bfs/blob.rb CHANGED
@@ -32,6 +32,9 @@ module BFS
32
32
  end
33
33
 
34
34
  # Creates the blob and opens it for writing.
35
+ # If a block is passed the writer is automatically committed in the end.
36
+ # If no block is passed, you must manually call #commit to persist the
37
+ # result.
35
38
  def create(**opts, &block)
36
39
  @bucket.create(path, **opts, &block)
37
40
  end
data/lib/bfs/bucket/fs.rb CHANGED
@@ -42,14 +42,9 @@ module BFS
42
42
  full = @root.join(norm_path(path))
43
43
  FileUtils.mkdir_p(full.dirname.to_s)
44
44
 
45
- temp = BFS::TempWriter.new(full, encoding: encoding, perm: perm) {|t| FileUtils.mv t, full.to_s }
46
- return temp unless block
47
-
48
- begin
49
- yield temp
50
- ensure
51
- temp.close
52
- end
45
+ BFS::Writer.new(full, encoding: encoding, perm: perm) do |temp|
46
+ FileUtils.mv temp, full.to_s
47
+ end.perform(&block)
53
48
  end
54
49
 
55
50
  # Opens an existing file for reading
@@ -1,5 +1,6 @@
1
1
  require 'bfs'
2
2
  require 'stringio'
3
+ require 'delegate'
3
4
 
4
5
  module BFS
5
6
  module Bucket
@@ -7,6 +8,21 @@ module BFS
7
8
  class InMem < Abstract
8
9
  Entry = Struct.new(:io, :mtime, :content_type, :metadata)
9
10
 
11
+ class Writer < DelegateClass(::StringIO)
12
+ include BFS::Writer::Mixin
13
+
14
+ def initialize(encoding:, &on_commit)
15
+ @on_commit = on_commit
16
+
17
+ sio = StringIO.new
18
+ sio.set_encoding(encoding)
19
+ super sio
20
+ end
21
+
22
+ alias close! close
23
+ alias commit_ref __getobj__
24
+ end
25
+
10
26
  def initialize(**opts)
11
27
  super(**opts.dup)
12
28
  @files = {}
@@ -43,18 +59,9 @@ module BFS
43
59
  # @option opts [String] :content_type Custom content type.
44
60
  # @option opts [Hash] :metadata Metadata key-value pairs.
45
61
  def create(path, encoding: self.encoding, content_type: nil, metadata: nil, **_opts, &block)
46
- io = StringIO.new
47
- io.set_encoding(encoding)
48
-
49
- entry = Entry.new(io, Time.now, content_type, norm_meta(metadata))
50
- @files[norm_path(path)] = entry
51
- return io unless block
52
-
53
- begin
54
- yield(io)
55
- ensure
56
- io.close
57
- end
62
+ Writer.new(encoding: encoding) do |wio|
63
+ @files[norm_path(path)] = Entry.new(wio, Time.now, content_type, norm_meta(metadata))
64
+ end.perform(&block)
58
65
  end
59
66
 
60
67
  # Opens an existing file for reading
data/lib/bfs/helpers.rb CHANGED
@@ -2,21 +2,45 @@ require 'tempfile'
2
2
  require 'delegate'
3
3
 
4
4
  module BFS
5
- class TempWriter < DelegateClass(::Tempfile)
6
- def initialize(name, tempdir: nil, perm: nil, **opts, &closer)
7
- @closer = closer
8
- @tempfile = ::Tempfile.new(File.basename(name.to_s), tempdir, **opts)
9
- @tempfile.chmod(perm) if perm
10
- super @tempfile
5
+ class Writer < DelegateClass(::Tempfile)
6
+ module Mixin
7
+ def perform
8
+ return self unless block_given?
9
+
10
+ begin
11
+ yield self
12
+ commit
13
+ ensure
14
+ discard
15
+ end
16
+ end
17
+
18
+ def commit
19
+ close
20
+ return false if @on_commit.nil?
21
+
22
+ @on_commit.call(commit_ref)
23
+ true
24
+ ensure
25
+ discard
26
+ end
27
+
28
+ def discard
29
+ @on_commit = nil
30
+ close!
31
+ end
11
32
  end
12
33
 
13
- def close
14
- return if closed?
34
+ include Mixin
35
+
36
+ def initialize(name, tempdir: nil, perm: nil, **opts, &on_commit)
37
+ @on_commit = on_commit
15
38
 
16
- path = @tempfile.path
17
- @tempfile.close
18
- @closer&.call(path)
19
- @tempfile.unlink
39
+ tempfile = ::Tempfile.new(File.basename(name.to_s), tempdir, **opts)
40
+ tempfile.chmod(perm) if perm
41
+ super tempfile
20
42
  end
43
+
44
+ alias commit_ref path
21
45
  end
22
46
  end
@@ -2,12 +2,15 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe BFS::Blob, core: true do
4
4
  describe 'default' do
5
+ subject { described_class.new('memtest://bucket/path/to/file.txt') }
6
+
5
7
  let(:bucket) { BFS::Bucket::InMem.new }
8
+
6
9
  before { allow(BFS).to receive(:resolve).and_return(bucket) }
7
- subject { described_class.new('memtest://bucket/path/to/file.txt') }
10
+
8
11
  after { subject.close }
9
12
 
10
- it 'should move' do
13
+ it 'moves' do
11
14
  expect(subject.path).to eq('path/to/file.txt')
12
15
  expect { subject.mv('/to/other/path.txt') }.to raise_error(BFS::FileNotFound)
13
16
 
@@ -16,7 +19,7 @@ RSpec.describe BFS::Blob, core: true do
16
19
  expect(subject.path).to eq('to/other/path.txt')
17
20
  end
18
21
 
19
- it 'should write/read' do
22
+ it 'write/reads' do
20
23
  expect { subject.read }.to raise_error(BFS::FileNotFound)
21
24
  subject.write('TESTDATA', content_type: 'text/plain', metadata: { 'x-key' => 'val' })
22
25
 
@@ -36,12 +39,14 @@ RSpec.describe BFS::Blob, core: true do
36
39
  end
37
40
 
38
41
  describe 'file system' do
42
+ subject { described_class.new("file:///#{path}") }
43
+
39
44
  let(:tmpdir) { Dir.mktmpdir }
40
45
  let(:path) { "#{tmpdir}/path/to/file.txt".sub('/', '') }
41
- after { FileUtils.rm_rf tmpdir }
42
- subject { described_class.new("file:///#{path}") }
43
46
 
44
- it 'should move' do
47
+ after { FileUtils.rm_rf tmpdir }
48
+
49
+ it 'moves' do
45
50
  expect(subject.path).to eq(path)
46
51
  expect { subject.mv("#{tmpdir}/to/other/path.txt") }.to raise_error(BFS::FileNotFound)
47
52
 
@@ -54,7 +59,7 @@ RSpec.describe BFS::Blob, core: true do
54
59
  ]
55
60
  end
56
61
 
57
- it 'should write/read' do
62
+ it 'write/reads' do
58
63
  expect { subject.read }.to raise_error(BFS::FileNotFound)
59
64
 
60
65
  subject.write('TESTDATA', content_type: 'text/plain', metadata: { 'x-key' => 'val' })
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe BFS::Bucket::Abstract, core: true do
4
- it 'should open with a block' do
4
+ it 'opens with a block' do
5
5
  sub_class = Class.new(described_class) do
6
6
  def close
7
7
  @closed = true
@@ -1,15 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe BFS::Bucket::FS, core: true do
4
- let(:tmpdir) { Dir.mktmpdir }
5
- after { FileUtils.rm_rf tmpdir }
6
4
  subject { described_class.new(tmpdir) }
7
5
 
6
+ let(:tmpdir) { Dir.mktmpdir }
7
+
8
+ after { FileUtils.rm_rf tmpdir }
9
+
8
10
  it_behaves_like 'a bucket',
9
11
  content_type: false,
10
12
  metadata: false
11
13
 
12
- it 'should resolve from URL' do
14
+ it 'resolves from URL' do
13
15
  File.open(File.join(tmpdir, 'test.txt'), 'wb') {|f| f.write 'TESTDATA' }
14
16
 
15
17
  bucket = BFS.resolve("file://#{tmpdir}")
@@ -18,14 +20,14 @@ RSpec.describe BFS::Bucket::FS, core: true do
18
20
  bucket.close
19
21
  end
20
22
 
21
- it 'should support custom perms on #initialize' do
23
+ it 'supports custom perms on #initialize' do
22
24
  blob = BFS::Blob.new("file://#{tmpdir}/test.txt?perm=0666")
23
25
  blob.create {|w| w.write 'foo' }
24
26
  expect(blob.info.mode).to eq(0o666)
25
27
  blob.close
26
28
  end
27
29
 
28
- it 'should support custom perms on #create' do
30
+ it 'supports custom perms on #create' do
29
31
  blob = BFS::Blob.new("file://#{tmpdir}/test.txt")
30
32
  blob.create(perm: 0o666) {|w| w.write 'foo' }
31
33
  expect(blob.info.mode).to eq(0o666)
@@ -1,22 +1,29 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS::TempWriter, core: true do
4
- let(:closer) { proc {} }
5
- subject { described_class.new 'test', &closer }
3
+ RSpec.describe BFS::Writer, core: true do
4
+ subject { described_class.new 'test', &on_commit }
6
5
 
7
- it 'should behave like a File' do
8
- missing = ::File.public_instance_methods - subject.public_methods
9
- expect(missing).to be_empty
10
- end
6
+ let(:on_commit) { proc { true } }
11
7
 
12
- it 'should support custom params' do
13
- subject = described_class.new 'test', perm: 0o640, &closer
8
+ it 'supports custom params' do
9
+ subject = described_class.new 'test', perm: 0o640, &on_commit
14
10
  expect(subject.stat.mode).to eq(0o100640)
15
- expect(subject.close).to be_truthy
11
+ expect(subject.commit).to be(true)
12
+ end
13
+
14
+ it 'executes a on_commit block' do
15
+ expect(on_commit).to receive(:call).with(subject.path).once
16
+ expect(subject.commit).to be(true)
17
+ expect(subject.commit).to be(false)
18
+ end
19
+
20
+ it 'may skip on_commit block' do
21
+ expect(on_commit).not_to receive(:call)
22
+ expect(subject.discard).to be(true)
16
23
  end
17
24
 
18
- it 'should exectute a closer block' do
19
- expect(closer).to receive(:call).with(subject.path)
20
- expect(subject.close).to be_truthy
25
+ it 'does not auto-commit on close' do
26
+ expect(on_commit).not_to receive(:call)
27
+ expect(subject.close).to be_nil
21
28
  end
22
29
  end
data/spec/bfs_spec.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe BFS, core: true do
4
- it 'should resolve' do
5
- bucket = BFS.resolve("file://#{Dir.tmpdir}")
4
+ it 'resolves' do
5
+ bucket = described_class.resolve("file://#{Dir.tmpdir}")
6
6
  expect(bucket).to be_instance_of(BFS::Bucket::FS)
7
7
  bucket.close
8
8
  end
9
9
 
10
- it 'should resolve with block' do
11
- BFS.resolve("file://#{Dir.tmpdir}") do |bucket|
10
+ it 'resolves with block' do
11
+ described_class.resolve("file://#{Dir.tmpdir}") do |bucket|
12
12
  expect(bucket).to be_instance_of(BFS::Bucket::FS)
13
13
  expect(bucket).to receive(:close)
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitrij Denissenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-10 00:00:00.000000000 Z
11
+ date: 2021-02-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Minimalist abstraction for bucket storage
14
14
  email: dimitrij@blacksquaremedia.com