bfs 0.1.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 +7 -0
- data/bfs.gemspec +19 -0
- data/lib/bfs.rb +22 -0
- data/lib/bfs/bucket.rb +7 -0
- data/lib/bfs/bucket/abstract.rb +64 -0
- data/lib/bfs/bucket/fs.rb +79 -0
- data/lib/bfs/bucket/in_mem.rb +65 -0
- data/lib/bfs/errors.rb +10 -0
- data/lib/bfs/helpers.rb +25 -0
- data/spec/bfs/bucket/fs_spec.rb +9 -0
- data/spec/bfs/bucket/in_mem_spec.rb +5 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 40e2210b5438bc6bcbdb01204c8a573b674a80f94fdb5dee0cab48b989f77c5a
|
4
|
+
data.tar.gz: 558aab1337ec9959c8eac6c6d13c9b2fbbd2692243a1fbd0d3d5ebe9b4ef7c21
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e7047002ef910561427ddd5048f52a1460e1a15ca9752ae4497f0988fcdf0117fe17aa797b9c0946d231b4a58e62e94fb8d980ea327cc82323246e174fa5af0f
|
7
|
+
data.tar.gz: ff34edc0f19c435a1f520750beae893934c1921330fdaa3f105231d1082f749588c47e295b2891986c7820d21e97a3be7799060b0a067a4b979cc1cf39fd7069
|
data/bfs.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'bfs'
|
3
|
+
s.version = File.read(File.expand_path('../.version', __dir__)).strip
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
|
6
|
+
s.licenses = ['Apache-2.0']
|
7
|
+
s.summary = 'Multi-platform cloud bucket adapter'
|
8
|
+
s.description = 'Minimalist abstraction for bucket storage'
|
9
|
+
|
10
|
+
s.authors = ['Dimitrij Denissenko']
|
11
|
+
s.email = 'dimitrij@blacksquaremedia.com'
|
12
|
+
s.homepage = 'https://github.com/bsm/bfs.rb'
|
13
|
+
|
14
|
+
s.executables = []
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
s.required_ruby_version = '>= 2.2.0'
|
19
|
+
end
|
data/lib/bfs.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module BFS
|
4
|
+
FileInfo = Struct.new(:path, :size, :mtime)
|
5
|
+
|
6
|
+
def self.register(scheme, &resolver)
|
7
|
+
@registry ||= {}
|
8
|
+
@registry[scheme] = resolver
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.resolve(url)
|
12
|
+
url = URI.parse(url) unless url.is_a?(::URI)
|
13
|
+
rsl = @registry[url.scheme]
|
14
|
+
raise ArgumentError, "Unable to resolve #{url}, scheme #{url.scheme} is not registered" unless rsl
|
15
|
+
|
16
|
+
rsl.call(url)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'bfs/helpers'
|
21
|
+
require 'bfs/bucket'
|
22
|
+
require 'bfs/errors'
|
data/lib/bfs/bucket.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'bfs'
|
2
|
+
|
3
|
+
module BFS
|
4
|
+
module Bucket
|
5
|
+
class Abstract
|
6
|
+
# Lists the contents of a bucket using a glob pattern
|
7
|
+
def ls(_pattern='**', _opts={})
|
8
|
+
raise 'not implemented'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Info returns the info for a single file
|
12
|
+
def info(_path, _opts={})
|
13
|
+
raise 'not implemented'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Creates a new file and opens it for writing
|
17
|
+
def create(_path, _opts={})
|
18
|
+
raise 'not implemented'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Opens an existing file for reading
|
22
|
+
# May raise BFS::FileNotFound
|
23
|
+
def open(_path, _opts={})
|
24
|
+
raise 'not implemented'
|
25
|
+
end
|
26
|
+
|
27
|
+
# Deletes a file.
|
28
|
+
def rm(_path, _opts={})
|
29
|
+
raise 'not implemented'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Shortcut method to read the contents of a file into memory
|
33
|
+
def read(path, opts={})
|
34
|
+
open(path, opts, &:read)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Shortcut method to write data to path
|
38
|
+
def write(path, data, opts={})
|
39
|
+
create(path, opts) {|f| f.write data }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Copies src to dst
|
43
|
+
def cp(src, dst, opts={})
|
44
|
+
open(src, opts) do |r|
|
45
|
+
create(dst, opts) do |w|
|
46
|
+
IO.copy_stream(r, w)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Moves src to dst
|
52
|
+
def mv(src, dst, _opts={})
|
53
|
+
cp(src, dst)
|
54
|
+
rm(src)
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def norm_path(path)
|
60
|
+
path.gsub(File::SEPARATOR, '/').sub(%r{^/+}, '')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'bfs'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module BFS
|
6
|
+
module Bucket
|
7
|
+
# FS buckets are operating on the file system
|
8
|
+
class FS < Abstract
|
9
|
+
def initialize(root, _opts={})
|
10
|
+
@root = Pathname.new(root.to_s)
|
11
|
+
@prefix = "#{@root}/"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Lists the contents of a bucket using a glob pattern
|
15
|
+
def ls(pattern='**/*', _opts={})
|
16
|
+
Pathname.glob(@root.join(pattern)).select(&:file?).map do |name|
|
17
|
+
name.to_s.sub(@prefix, '')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Info returns the info for a single file
|
22
|
+
def info(path, _opts={})
|
23
|
+
name = @root.join(norm_path(path))
|
24
|
+
path = name.to_s.sub(@prefix, '')
|
25
|
+
BFS::FileInfo.new(path, name.size, name.mtime)
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
raise BFS::FileNotFound, path
|
28
|
+
end
|
29
|
+
|
30
|
+
# Creates a new file and opens it for writing
|
31
|
+
def create(path, _opts={}, &block)
|
32
|
+
name = @root.join(norm_path(path))
|
33
|
+
FileUtils.mkdir_p(name.dirname.to_s)
|
34
|
+
|
35
|
+
temp = BFS::TempWriter.new(name) {|t| FileUtils.mv t, name.to_s }
|
36
|
+
return temp unless block
|
37
|
+
|
38
|
+
begin
|
39
|
+
yield temp
|
40
|
+
ensure
|
41
|
+
temp.close
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Opens an existing file for reading
|
46
|
+
def open(path, opts={}, &block)
|
47
|
+
path = norm_path(path)
|
48
|
+
name = @root.join(path)
|
49
|
+
name.open('r', opts, &block)
|
50
|
+
rescue Errno::ENOENT
|
51
|
+
raise BFS::FileNotFound, path
|
52
|
+
end
|
53
|
+
|
54
|
+
# Deletes a file.
|
55
|
+
def rm(path, _opts={})
|
56
|
+
name = @root.join(norm_path(path))
|
57
|
+
FileUtils.rm_f name.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
# Copies a file.
|
61
|
+
def cp(src, dst, _opts={})
|
62
|
+
src = norm_path(src)
|
63
|
+
dst = norm_path(dst)
|
64
|
+
FileUtils.cp @root.join(src).to_s, @root.join(dst).to_s
|
65
|
+
rescue Errno::ENOENT
|
66
|
+
raise BFS::FileNotFound, src
|
67
|
+
end
|
68
|
+
|
69
|
+
# Moves a file.
|
70
|
+
def mv(src, dst, _opts={})
|
71
|
+
src = norm_path(src)
|
72
|
+
dst = norm_path(dst)
|
73
|
+
FileUtils.mv @root.join(src).to_s, @root.join(dst).to_s
|
74
|
+
rescue Errno::ENOENT
|
75
|
+
raise BFS::FileNotFound, src
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'bfs'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module BFS
|
5
|
+
module Bucket
|
6
|
+
# InMem buckets are useful for tests
|
7
|
+
class InMem < Abstract
|
8
|
+
Entry = Struct.new(:io, :mtime)
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@files = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Lists the contents of a bucket using a glob pattern
|
15
|
+
def ls(pattern='**/*', _opts={})
|
16
|
+
@files.each_key.select do |key|
|
17
|
+
File.fnmatch?(pattern, key, File::FNM_PATHNAME)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Info returns the file info
|
22
|
+
def info(path, _opts={})
|
23
|
+
path = norm_path(path)
|
24
|
+
raise BFS::FileNotFound, path unless @files.key?(path)
|
25
|
+
|
26
|
+
entry = @files[path]
|
27
|
+
BFS::FileInfo.new(path, entry.io.size, entry.mtime)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Creates a new file and opens it for writing
|
31
|
+
def create(path, _opts={}, &block)
|
32
|
+
io = StringIO.new
|
33
|
+
@files[norm_path(path)] = Entry.new(io, Time.now)
|
34
|
+
return io unless block
|
35
|
+
|
36
|
+
begin
|
37
|
+
yield(io)
|
38
|
+
ensure
|
39
|
+
io.close
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Opens an existing file for reading
|
44
|
+
def open(path, _opts={}, &block)
|
45
|
+
path = norm_path(path)
|
46
|
+
raise BFS::FileNotFound, path unless @files.key?(path)
|
47
|
+
|
48
|
+
io = @files[path].io
|
49
|
+
io.reopen(io.string)
|
50
|
+
return io unless block
|
51
|
+
|
52
|
+
begin
|
53
|
+
yield(io)
|
54
|
+
ensure
|
55
|
+
io.close
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Deletes a file.
|
60
|
+
def rm(path, _opts={})
|
61
|
+
@files.delete(norm_path(path))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/bfs/errors.rb
ADDED
data/lib/bfs/helpers.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module BFS
|
4
|
+
class TempWriter
|
5
|
+
def initialize(name, &closer)
|
6
|
+
@closer = closer
|
7
|
+
@tempfile = ::Tempfile.new(File.basename(name.to_s))
|
8
|
+
end
|
9
|
+
|
10
|
+
def path
|
11
|
+
@tempfile.path
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(data)
|
15
|
+
@tempfile.write(data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def close
|
19
|
+
path = @tempfile.path
|
20
|
+
@tempfile.close
|
21
|
+
@closer.call(path) if @closer
|
22
|
+
@tempfile.unlink
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bfs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dimitrij Denissenko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Minimalist abstraction for bucket storage
|
14
|
+
email: dimitrij@blacksquaremedia.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- bfs.gemspec
|
20
|
+
- lib/bfs.rb
|
21
|
+
- lib/bfs/bucket.rb
|
22
|
+
- lib/bfs/bucket/abstract.rb
|
23
|
+
- lib/bfs/bucket/fs.rb
|
24
|
+
- lib/bfs/bucket/in_mem.rb
|
25
|
+
- lib/bfs/errors.rb
|
26
|
+
- lib/bfs/helpers.rb
|
27
|
+
- spec/bfs/bucket/fs_spec.rb
|
28
|
+
- spec/bfs/bucket/in_mem_spec.rb
|
29
|
+
homepage: https://github.com/bsm/bfs.rb
|
30
|
+
licenses:
|
31
|
+
- Apache-2.0
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 2.2.0
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.7.6
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: Multi-platform cloud bucket adapter
|
53
|
+
test_files:
|
54
|
+
- spec/bfs/bucket/fs_spec.rb
|
55
|
+
- spec/bfs/bucket/in_mem_spec.rb
|