bfs 0.7.2 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20aaadf82c4feef66bb9f7ca3a50c2da292e5b107d69d5e055e139cea1aca352
4
- data.tar.gz: aef5f242aa8e8b22cb940ac20b280650ba4d499025a5cc3efa620b8c6f375d7e
3
+ metadata.gz: 3de22f9cab6ba21d594854a2fcec49cc215b7c10c6f6b5b9cb1528ba6ce96b59
4
+ data.tar.gz: cc3b427ff41229556b7b39a9d0ad5d61afdd0439fa984aafadd0aa9635ac4664
5
5
  SHA512:
6
- metadata.gz: 77722ad5d90e91f1abbaac4dfb633ad355de949c6a9e23cf383690de426666344e4431fa3a2da0490f7b065744e0786709beb8f18e1cd8ad2ade12ede3e895c7
7
- data.tar.gz: dc0fe99895a120ebcf6e4d6f5cb2f62fc01d2caa62ac2a985a336e9f77bc186f0edeb3ae17899bec22753c61ba63dc09b8c8c61f209808c02202e6aa141d32c8
6
+ metadata.gz: 569ac3d4e157eebf08f948eab831f214a900d29a96028a703d541d3fa7de7fdcde6a2cb11413ff802c459ff163cff15feb00c3477abcae7b18d77c302255d529
7
+ data.tar.gz: 2804530bcc9aaf60f1b475a6948b357242dc96e15788835e34d6cc1989ec7bda8e0bd66372427ce4aa9d5b923eda5468a9c33087b716ac3ca379f391a6192f43
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]
@@ -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
@@ -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
@@ -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
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS::Blob do
3
+ RSpec.describe BFS::Blob, core: true do
4
4
  describe 'default' do
5
5
  let(:bucket) { BFS::Bucket::InMem.new }
6
6
  before { allow(BFS).to receive(:resolve).and_return(bucket) }
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS::Bucket::Abstract do
3
+ RSpec.describe BFS::Bucket::Abstract, core: true do
4
4
  it 'should open with a block' do
5
5
  sub_class = Class.new(described_class) do
6
6
  def close
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS::Bucket::FS do
3
+ RSpec.describe BFS::Bucket::FS, core: true do
4
4
  let(:tmpdir) { Dir.mktmpdir }
5
5
  after { FileUtils.rm_rf tmpdir }
6
6
  subject { described_class.new(tmpdir) }
@@ -15,6 +15,7 @@ RSpec.describe BFS::Bucket::FS do
15
15
  bucket = BFS.resolve("file://#{tmpdir}")
16
16
  expect(bucket).to be_instance_of(described_class)
17
17
  expect(bucket.ls.to_a).to eq(['test.txt'])
18
+ bucket.close
18
19
  end
19
20
 
20
21
  it 'should support custom perms on #initialize' do
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS::Bucket::InMem do
3
+ RSpec.describe BFS::Bucket::InMem, core: true do
4
4
  it_behaves_like 'a bucket'
5
5
  end
@@ -1,22 +1,28 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS::TempWriter do
4
- let(:closer) { proc {} }
5
- subject { described_class.new 'test', &closer }
6
-
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
3
+ RSpec.describe BFS::Writer, core: true do
4
+ let(:on_commit) { proc { true } }
5
+ subject { described_class.new 'test', &on_commit }
11
6
 
12
7
  it 'should support custom params' do
13
- subject = described_class.new 'test', perm: 0o640, &closer
8
+ subject = described_class.new 'test', perm: 0o640, &on_commit
14
9
  expect(subject.stat.mode).to eq(0o100640)
15
- expect(subject.close).to be_truthy
10
+ expect(subject.commit).to be(true)
11
+ end
12
+
13
+ it 'should execute a on_commit block' do
14
+ expect(on_commit).to receive(:call).with(subject.path).once
15
+ expect(subject.commit).to be(true)
16
+ expect(subject.commit).to be(false)
17
+ end
18
+
19
+ it 'may skip on_commit block' do
20
+ expect(on_commit).not_to receive(:call)
21
+ expect(subject.discard).to be(true)
16
22
  end
17
23
 
18
- it 'should exectute a closer block' do
19
- expect(closer).to receive(:call).with(subject.path)
20
- expect(subject.close).to be_truthy
24
+ it 'should not auto-commit on close' do
25
+ expect(on_commit).not_to receive(:call)
26
+ expect(subject.close).to be_nil
21
27
  end
22
28
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe BFS do
3
+ RSpec.describe BFS, core: true do
4
4
  it 'should resolve' do
5
5
  bucket = BFS.resolve("file://#{Dir.tmpdir}")
6
6
  expect(bucket).to be_instance_of(BFS::Bucket::FS)
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.2
4
+ version: 0.8.0
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-02 00:00:00.000000000 Z
11
+ date: 2020-12-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Minimalist abstraction for bucket storage
14
14
  email: dimitrij@blacksquaremedia.com