bfs 0.7.3 → 0.8.1

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 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