ipfs-api 0.1.0 → 0.4.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 +5 -5
- data/README.md +38 -30
- data/Rakefile +4 -6
- data/examples/basic.rb +8 -0
- data/ipfs-api.gemspec +5 -9
- data/lib/ipfs-api/connection.rb +65 -44
- data/lib/ipfs-api/io.rb +45 -22
- data/lib/ipfs-api/upload.rb +2 -2
- data/lib/ipfs-api/version.rb +1 -1
- data/lib/ipfs-api.rb +3 -3
- data/test/samples.rb +24 -2
- data/test/test_cmd_add.rb +2 -2
- data/test/test_cmd_cat.rb +2 -2
- data/test/test_cmd_get.rb +27 -5
- data/test/test_cmd_id.rb +7 -4
- data/test/test_cmd_ls.rb +20 -4
- data/test/test_io.rb +60 -10
- data/test/test_upload.rb +2 -2
- metadata +8 -11
- data/test/common.rb +0 -4
- data/test/test_cmd_name.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9bd8f7653d00434d0ee809fa1c0225c5ea983ccc53c61157932cf10953f00679
|
4
|
+
data.tar.gz: 93e65ff69174cb7a06f8cd63c3a4277055c8d8e5d9759a1715ac6d083f5c750a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99d6311039ea27fbb777af8211b34a17e6b1458c9c45336bdaa3f634d2aab21afc4cee6ada77068eab51ad93a8c2a347925ee419bead1f3a2c8fa3d7327146d3
|
7
|
+
data.tar.gz: fbfaadb695eb7fab708573778e794652c13cae4432418ecade50b78fd70feb68b399375fc9af8da67c770060581203c6005037e9daa092a6f8a49c6168294e74
|
data/README.md
CHANGED
@@ -1,58 +1,66 @@
|
|
1
1
|
# Overview
|
2
2
|
|
3
|
-
|
3
|
+
IPFS-API for Ruby is a client library to access the [Interplanetary Filesystem (IPFS)](https://ipfs.io) from Ruby.
|
4
4
|
|
5
5
|
You can find more examples in the
|
6
6
|
[examples directory](https://github.com/hjoest/ruby-ipfs-api/tree/master/examples).
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
10
|
-
|
10
|
+
You need an [IPFS daemon](https://ipfs.io/docs/install/) running
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
ipfs daemon
|
13
|
+
|
14
|
+
To install this gem, run
|
15
|
+
|
16
|
+
gem install ipfs-api
|
15
17
|
|
16
18
|
or simply add this line to your ``Gemfile``
|
17
19
|
|
18
|
-
|
19
|
-
gem 'ipfs-api', '~> 0.1.0'
|
20
|
-
```
|
20
|
+
gem 'ipfs-api', '~> 0.4.0'
|
21
21
|
|
22
22
|
## Basic examples
|
23
23
|
|
24
24
|
This example will add a directory to *IPFS*. The directory ``data``
|
25
25
|
must exist or otherwise an ``Errno::ENOENT`` error will be raised.
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
require 'ipfs-api'
|
28
|
+
|
29
|
+
ipfs = IPFS::Connection.new
|
30
|
+
ipfs.add Dir.new('data')
|
31
|
+
|
32
|
+
Afterwards, we can retrieve what we put in.
|
33
|
+
|
34
|
+
require 'ipfs-api'
|
35
|
+
|
36
|
+
ipfs = IPFS::Connection.new
|
37
|
+
|
38
|
+
# retrieve contents of a file
|
39
|
+
print ipfs.cat('QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG')
|
29
40
|
|
30
|
-
|
31
|
-
ipfs.
|
32
|
-
```
|
41
|
+
# retrieve the whole directory and make a copy of it in ``copy-of-data``
|
42
|
+
ipfs.get('QmSh4Xjoy16v6XmnREE1yCrPM1dnizZc2h6LfrqXsnbBV7', 'copy-of-data')
|
33
43
|
|
34
44
|
## Advanced
|
35
45
|
|
36
46
|
Dynamically add folders and files to *IPFS*, without creating them
|
37
47
|
on the local file system:
|
38
48
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
```
|
49
|
+
require 'ipfs-api'
|
50
|
+
|
51
|
+
ipfs = IPFS::Connection.new
|
52
|
+
folder = IPFS::Upload.folder('test') do |test|
|
53
|
+
test.add_file('hello.txt') do |fd|
|
54
|
+
fd.write 'Hello'
|
55
|
+
end
|
56
|
+
test.add_file('world.txt') do |fd|
|
57
|
+
fd.write 'World'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
ipfs.add folder do |node|
|
61
|
+
# display each uploaded node:
|
62
|
+
print "#{node.name}: #{node.hash}\n" if node.finished?
|
63
|
+
end
|
56
64
|
|
57
65
|
## License
|
58
66
|
|
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ PKG_NAME = 'ipfs-api'
|
|
11
11
|
PKG_VERSION = IPFS::VERSION
|
12
12
|
AUTHORS = ['Holger Joest']
|
13
13
|
EMAIL = 'holger@joest.org'
|
14
|
-
HOMEPAGE = 'http://ruby-ipfs-api
|
14
|
+
HOMEPAGE = 'http://hjoest.github.io/ruby-ipfs-api'
|
15
15
|
SUMMARY = 'Interplanetary File System for Ruby'
|
16
16
|
DESCRIPTION = 'This is a client library to access the IPFS from Ruby'
|
17
17
|
RDOC_OPTIONS = [ '--title', SUMMARY, '--quiet', '--main', 'lib/ipfs-api.rb' ]
|
@@ -32,7 +32,7 @@ RDoc::Task.new do |rd|
|
|
32
32
|
rd.options = RDOC_OPTIONS
|
33
33
|
end
|
34
34
|
|
35
|
-
CLEAN.include [ "*.gem*", "pkg", "rdoc" ]
|
35
|
+
CLEAN.include [ "*.gem*", "pkg", "rdoc", "test/tmp" ]
|
36
36
|
|
37
37
|
spec = Gem::Specification.new do |s|
|
38
38
|
s.name = PKG_NAME
|
@@ -40,7 +40,6 @@ spec = Gem::Specification.new do |s|
|
|
40
40
|
s.authors = AUTHORS
|
41
41
|
s.email = EMAIL
|
42
42
|
s.homepage = HOMEPAGE
|
43
|
-
s.rubyforge_project = PKG_NAME
|
44
43
|
s.summary = SUMMARY
|
45
44
|
s.description = DESCRIPTION
|
46
45
|
s.platform = Gem::Platform::RUBY
|
@@ -49,10 +48,9 @@ spec = Gem::Specification.new do |s|
|
|
49
48
|
s.executables = []
|
50
49
|
s.files = PKG_FILES
|
51
50
|
s.test_files = []
|
52
|
-
s.has_rdoc = true
|
53
51
|
s.extra_rdoc_files = RDOC_FILES
|
54
52
|
s.rdoc_options = RDOC_OPTIONS
|
55
|
-
s.required_ruby_version = ">=
|
53
|
+
s.required_ruby_version = ">= 2.0.0"
|
56
54
|
end
|
57
55
|
|
58
56
|
# also keep the gemspec up to date each time we package a tarball or gem
|
@@ -78,7 +76,7 @@ namespace :gem do
|
|
78
76
|
spec.instance_variables.sort.each do |ivar|
|
79
77
|
value = spec.instance_variable_get(ivar)
|
80
78
|
name = ivar.to_s.split("@").last
|
81
|
-
next if skip_fields.include?(name) || value.nil? ||
|
79
|
+
next if skip_fields.include?(name) || value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
82
80
|
if name == "dependencies"
|
83
81
|
value.each do |d|
|
84
82
|
dep, *ver = d.to_s.split(" ")
|
data/examples/basic.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
require 'ipfs-api'
|
2
2
|
|
3
3
|
ipfs = IPFS::Connection.new
|
4
|
+
|
5
|
+
# add a directory
|
4
6
|
ipfs.add Dir.new('data')
|
7
|
+
|
8
|
+
# retrieve contents of a file
|
9
|
+
print ipfs.cat('QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG')
|
10
|
+
|
11
|
+
# retrieve the whole directory
|
12
|
+
ipfs.get('QmSh4Xjoy16v6XmnREE1yCrPM1dnizZc2h6LfrqXsnbBV7', 'copy-of-data')
|
data/ipfs-api.gemspec
CHANGED
@@ -20,29 +20,25 @@ Gem::Specification.new do |s|
|
|
20
20
|
"lib/ipfs-api/io.rb",
|
21
21
|
"lib/ipfs-api/upload.rb",
|
22
22
|
"lib/ipfs-api/version.rb",
|
23
|
-
"test/common.rb",
|
24
23
|
"test/samples.rb",
|
25
24
|
"test/test_cmd_add.rb",
|
26
25
|
"test/test_cmd_cat.rb",
|
27
26
|
"test/test_cmd_get.rb",
|
28
27
|
"test/test_cmd_id.rb",
|
29
28
|
"test/test_cmd_ls.rb",
|
30
|
-
"test/test_cmd_name.rb",
|
31
29
|
"test/test_io.rb",
|
32
30
|
"test/test_upload.rb"]
|
33
|
-
s.full_name = "ipfs-api-0.
|
34
|
-
s.
|
35
|
-
s.homepage = "http://ruby-ipfs-api.github.io"
|
31
|
+
s.full_name = "ipfs-api-0.4.0"
|
32
|
+
s.homepage = "http://hjoest.github.io/ruby-ipfs-api"
|
36
33
|
s.licenses = ["MIT"]
|
37
34
|
s.name = "ipfs-api"
|
38
35
|
s.platform = "ruby"
|
39
36
|
s.rdoc_options = ["--title", "Interplanetary File System for Ruby", "--quiet", "--main", "lib/ipfs-api.rb"]
|
40
37
|
s.require_paths = ["lib"]
|
41
|
-
s.required_ruby_version = ">=
|
38
|
+
s.required_ruby_version = ">= 2.0.0"
|
42
39
|
s.required_rubygems_version = ">= 0"
|
43
|
-
s.
|
44
|
-
s.rubygems_version = "2.4.8"
|
40
|
+
s.rubygems_version = "3.1.4"
|
45
41
|
s.specification_version = 4
|
46
42
|
s.summary = "Interplanetary File System for Ruby"
|
47
|
-
s.version = "0.
|
43
|
+
s.version = "0.4.0"
|
48
44
|
end
|
data/lib/ipfs-api/connection.rb
CHANGED
@@ -11,66 +11,70 @@ module IPFS
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def add nodes, &block
|
14
|
-
|
15
|
-
|
16
|
-
'Content-Disposition' => 'form-data: name="files"',
|
17
|
-
'Content-Type' => "multipart/form-data; boundary=#{boundaries[0]}"
|
18
|
-
}
|
19
|
-
walker = Upload::TreeWalker.depth_first(nodes)
|
14
|
+
boundary = generate_boundary
|
15
|
+
tree_walker = Upload::TreeWalker.depth_first(nodes)
|
20
16
|
node_map = {}
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
rescue StopIteration
|
27
|
-
depth = -1
|
28
|
-
walker = nil
|
29
|
-
end
|
30
|
-
while boundaries.size > depth+1 && boundary = boundaries.shift
|
31
|
-
buf << %Q{\
|
32
|
-
--#{boundary}--\r\n\
|
17
|
+
producer = IO::StreamProducer.new do |buf|
|
18
|
+
buf << %Q{\
|
19
|
+
--#{boundary}\r\n\
|
20
|
+
Content-Disposition: file; filename="root"\r\n\
|
21
|
+
Content-Type: application/x-directory\r\n\
|
33
22
|
\r\n\
|
34
23
|
\r\n\
|
35
24
|
}
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
Content-
|
43
|
-
Content-Type: multipart/mixed; boundary=#{boundaries[0]}\r\n\
|
25
|
+
tree_walker.each do |node, depth|
|
26
|
+
node_map[node.path] = node
|
27
|
+
if node.folder?
|
28
|
+
buf << %Q{\
|
29
|
+
--#{boundary}\r\n\
|
30
|
+
Content-Disposition: file; filename="root#{node.path.gsub('/', '%2F')}"\r\n\
|
31
|
+
Content-Type: application/x-directory\r\n\
|
44
32
|
\r\n\
|
45
33
|
\r\n\
|
46
34
|
}
|
47
|
-
|
48
|
-
|
49
|
-
--#{
|
50
|
-
Content-Disposition: file; filename="#{node.path}"\r\n\
|
35
|
+
elsif node.file?
|
36
|
+
buf << %Q{\
|
37
|
+
--#{boundary}\r\n\
|
38
|
+
Content-Disposition: file; filename="root#{node.path.gsub('/', '%2F')}"\r\n\
|
51
39
|
Content-Type: application/octet-stream\r\n\
|
52
40
|
\r\n\
|
53
41
|
#{node.content}\r\n\
|
54
42
|
}
|
55
|
-
|
56
|
-
|
43
|
+
else
|
44
|
+
raise "Unknown node type: #{node}"
|
45
|
+
end
|
57
46
|
end
|
47
|
+
buf << %Q{\
|
48
|
+
--#{boundary}\r\n\
|
49
|
+
}
|
58
50
|
end
|
59
|
-
|
51
|
+
headers = {
|
52
|
+
'Content-Type' => "multipart/form-data; boundary=#{boundary}"
|
53
|
+
}
|
54
|
+
stream = producer.stream
|
55
|
+
uploaded_nodes = []
|
60
56
|
post("add?encoding=json&r=true&progress=true", stream, headers) do |chunk|
|
61
57
|
next if chunk.empty?
|
62
|
-
upload =
|
58
|
+
upload = nil
|
59
|
+
begin
|
60
|
+
upload = JSON.parse(chunk)
|
61
|
+
rescue JSON::ParserError
|
62
|
+
end
|
63
|
+
next if upload.nil?
|
63
64
|
path, bytes, hash = ['Name', 'Bytes', 'Hash'].map { |p| upload[p] }
|
65
|
+
next if not path.start_with?('root/')
|
66
|
+
path = path[4..-1]
|
64
67
|
node = node_map[path]
|
68
|
+
next if not node
|
65
69
|
node.bytes = bytes if bytes
|
66
70
|
node.hash = hash if hash
|
67
71
|
if block_given?
|
68
72
|
block.call(node)
|
69
73
|
elsif hash
|
70
|
-
|
74
|
+
uploaded_nodes << node
|
71
75
|
end
|
72
76
|
end
|
73
|
-
block_given? ? nil :
|
77
|
+
block_given? ? nil : uploaded_nodes
|
74
78
|
end
|
75
79
|
|
76
80
|
def cat path
|
@@ -81,13 +85,18 @@ Content-Type: application/octet-stream\r\n\
|
|
81
85
|
result
|
82
86
|
end
|
83
87
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
88
|
+
def get path, destination = nil
|
89
|
+
producer = IO::StreamProducer.new do |buf|
|
90
|
+
post("get?arg=#{CGI.escape(path)}") do |chunk|
|
91
|
+
buf << chunk
|
92
|
+
end
|
93
|
+
buf.close
|
94
|
+
end
|
95
|
+
if destination.nil?
|
96
|
+
return producer.stream
|
97
|
+
else
|
98
|
+
return IO::Tar.extract(producer.stream, destination)
|
89
99
|
end
|
90
|
-
result
|
91
100
|
end
|
92
101
|
|
93
102
|
def id
|
@@ -137,9 +146,21 @@ Content-Type: application/octet-stream\r\n\
|
|
137
146
|
@connection = connection
|
138
147
|
end
|
139
148
|
|
140
|
-
def resolve
|
149
|
+
def resolve id = nil
|
150
|
+
@connection.instance_exec(self) do
|
151
|
+
if id
|
152
|
+
JSON.parse(post("name/resolve?arg=#{CGI.escape(id)}").body)['Path']
|
153
|
+
else
|
154
|
+
JSON.parse(post('name/resolve').body)['Path']
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def publish node, key = nil
|
160
|
+
params = "arg=#{CGI.escape(node.hash)}"
|
161
|
+
params << "&key=#{CGI.escape(key)}" if key
|
141
162
|
@connection.instance_exec(self) do
|
142
|
-
JSON.parse(post(
|
163
|
+
JSON.parse(post("name/publish?#{params}").body)['Name']
|
143
164
|
end
|
144
165
|
end
|
145
166
|
|
data/lib/ipfs-api/io.rb
CHANGED
@@ -1,38 +1,61 @@
|
|
1
|
-
require '
|
1
|
+
require 'tempfile'
|
2
|
+
require 'rubygems/package'
|
2
3
|
|
3
4
|
module IPFS; end
|
4
5
|
module IPFS::IO # :nodoc:
|
5
6
|
|
6
|
-
class
|
7
|
+
class StreamProducer # :nodoc:
|
7
8
|
|
8
9
|
def initialize &block
|
9
10
|
@block = block
|
10
|
-
@stream = StringIO.new
|
11
|
-
fetch_data
|
12
11
|
end
|
13
12
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
s = [length - q, s].min if !length.nil?
|
21
|
-
outbuf[q, s] = @stream.string[@p, s]
|
22
|
-
@p += s
|
23
|
-
q += s
|
24
|
-
break if q == length
|
25
|
-
fetch_data if @stream.size == @p
|
13
|
+
def stream
|
14
|
+
io = Tempfile.new('ruby-ipfs')
|
15
|
+
begin
|
16
|
+
@block.call io
|
17
|
+
ensure
|
18
|
+
io.close
|
26
19
|
end
|
27
|
-
|
20
|
+
File.open(io.path, 'r')
|
28
21
|
end
|
29
22
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
end
|
24
|
+
|
25
|
+
module Tar # :nodoc:
|
26
|
+
|
27
|
+
def extract stream, destination
|
28
|
+
Gem::Package::TarReader.new(stream) do |tar|
|
29
|
+
path = nil
|
30
|
+
tar.each do |entry|
|
31
|
+
if entry.full_name == '././@LongLink'
|
32
|
+
path = File.join(destination, entry.read.strip)
|
33
|
+
next
|
34
|
+
end
|
35
|
+
path ||= File.join(destination, entry.full_name)
|
36
|
+
if entry.directory?
|
37
|
+
if File.exist?(path) and not File.directory?(path)
|
38
|
+
raise IOError.new("Not a directory: #{path}")
|
39
|
+
end
|
40
|
+
FileUtils.mkdir_p path, :mode => entry.header.mode, :verbose => false
|
41
|
+
elsif entry.file?
|
42
|
+
if File.exist?(path) and not File.file?(path)
|
43
|
+
raise IOError.new("Not a file: #{path}")
|
44
|
+
end
|
45
|
+
File.open path, "wb" do |fd|
|
46
|
+
while (chunk = entry.read(1024))
|
47
|
+
fd.write chunk
|
48
|
+
end
|
49
|
+
end
|
50
|
+
FileUtils.chmod entry.header.mode, path, :verbose => false
|
51
|
+
end
|
52
|
+
path = nil
|
53
|
+
end
|
35
54
|
end
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
module_function :extract
|
36
59
|
|
37
60
|
end
|
38
61
|
|
data/lib/ipfs-api/upload.rb
CHANGED
data/lib/ipfs-api/version.rb
CHANGED
data/lib/ipfs-api.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# =
|
1
|
+
# =IPFS API for Ruby
|
2
2
|
# License:: MIT (see the LICENSE file)
|
3
|
-
# Website:: https://ruby-ipfs-api
|
3
|
+
# Website:: https://hjoest.github.io/ruby-ipfs-api
|
4
4
|
#
|
5
5
|
# ==Introduction
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# IPFS API for Ruby is an IPFS[https://ipfs.io] library for Ruby.
|
8
8
|
#
|
9
9
|
require 'ipfs-api/version'
|
10
10
|
require 'ipfs-api/io'
|
data/test/samples.rb
CHANGED
@@ -1,9 +1,25 @@
|
|
1
1
|
require 'tmpdir'
|
2
|
-
require '
|
2
|
+
require 'minitest/autorun'
|
3
|
+
begin
|
4
|
+
Minitest.const_get('Test')
|
5
|
+
rescue
|
6
|
+
module Minitest
|
7
|
+
Test = Unit::TestCase
|
8
|
+
end
|
9
|
+
end
|
10
|
+
if ENV['DEBUG']
|
11
|
+
require 'byebug'
|
12
|
+
else
|
13
|
+
def byebug; end
|
14
|
+
end
|
15
|
+
|
16
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
17
|
require 'ipfs-api/upload'
|
4
18
|
|
5
19
|
module Samples
|
6
20
|
|
21
|
+
TEMP_DIR_PREFIX = 'ruby-ipfs-api-unit-test-'
|
22
|
+
|
7
23
|
include IPFS
|
8
24
|
|
9
25
|
def some_virtual_folders
|
@@ -61,7 +77,7 @@ module Samples
|
|
61
77
|
module_function :some_virtual_folders
|
62
78
|
|
63
79
|
def some_filesystem_folders
|
64
|
-
Dir.mktmpdir(
|
80
|
+
Dir.mktmpdir(TEMP_DIR_PREFIX) do |root|
|
65
81
|
a1 = File.join(root, 'a1')
|
66
82
|
Dir.mkdir(a1, 0755)
|
67
83
|
b1 = File.join(a1, 'b1')
|
@@ -81,4 +97,10 @@ module Samples
|
|
81
97
|
end
|
82
98
|
module_function :some_filesystem_folders
|
83
99
|
|
100
|
+
def some_byte_sequences
|
101
|
+
s = ('a'..'z').to_a.join
|
102
|
+
[ s ] * 17
|
103
|
+
end
|
104
|
+
module_function :some_byte_sequences
|
105
|
+
|
84
106
|
end
|
data/test/test_cmd_add.rb
CHANGED
data/test/test_cmd_cat.rb
CHANGED
data/test/test_cmd_get.rb
CHANGED
@@ -1,18 +1,40 @@
|
|
1
|
-
$:.unshift File.expand_path('
|
1
|
+
$:.unshift File.expand_path('..', __FILE__)
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'samples'
|
4
4
|
require 'ipfs-api'
|
5
5
|
|
6
6
|
include IPFS
|
7
7
|
|
8
8
|
class CommandGetTest < Minitest::Test
|
9
9
|
|
10
|
-
def
|
10
|
+
def test_get_as_tar_stream
|
11
11
|
ipfs = Connection.new
|
12
12
|
Samples.some_virtual_folders do |fixture, expectation|
|
13
13
|
ipfs.add fixture
|
14
|
-
|
15
|
-
|
14
|
+
hash = 'QmedYJNEKn656faSHaMv5UFVkgfSzwYf9u4zsYoXqgvnch'
|
15
|
+
stream = ipfs.get(hash)
|
16
|
+
tar = stream.read
|
17
|
+
# take some samples of the TAR archive
|
18
|
+
assert_equal 3072, tar.length
|
19
|
+
[ 0x101, 0x301, 0x501 ].each do |seek|
|
20
|
+
assert_equal 'ustar', tar[seek..seek+4]
|
21
|
+
end
|
22
|
+
[ 0x0, 0x200, 0x400 ].each do |seek|
|
23
|
+
assert_equal hash, tar[seek..seek+45]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_get_and_extract
|
29
|
+
ipfs = Connection.new
|
30
|
+
Samples.some_virtual_folders do |fixture, expectation|
|
31
|
+
ipfs.add fixture
|
32
|
+
hash = 'QmedYJNEKn656faSHaMv5UFVkgfSzwYf9u4zsYoXqgvnch'
|
33
|
+
Dir.mktmpdir(Samples::TEMP_DIR_PREFIX) do |target|
|
34
|
+
ipfs.get hash, target
|
35
|
+
actual = File.read(File.join(target, hash, 'b1/hello.txt'))
|
36
|
+
assert_equal "Hello World!\n", actual
|
37
|
+
end
|
16
38
|
end
|
17
39
|
end
|
18
40
|
|
data/test/test_cmd_id.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
$:.unshift File.expand_path('
|
1
|
+
$:.unshift File.expand_path('..', __FILE__)
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'samples'
|
4
4
|
require 'ipfs-api'
|
5
5
|
|
6
6
|
include IPFS
|
@@ -10,8 +10,11 @@ class CommandIdTest < Minitest::Test
|
|
10
10
|
def test_id
|
11
11
|
ipfs = Connection.new
|
12
12
|
id = ipfs.id
|
13
|
-
|
14
|
-
|
13
|
+
if id.start_with?('Qm')
|
14
|
+
assert_equal 46, id.size
|
15
|
+
else
|
16
|
+
assert_equal 52, id.size
|
17
|
+
end
|
15
18
|
end
|
16
19
|
|
17
20
|
end
|
data/test/test_cmd_ls.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
$:.unshift File.expand_path('
|
1
|
+
$:.unshift File.expand_path('..', __FILE__)
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'samples'
|
4
4
|
require 'ipfs-api'
|
5
5
|
|
6
6
|
include IPFS
|
@@ -12,6 +12,7 @@ class CommandLsTest < Minitest::Test
|
|
12
12
|
Samples.some_virtual_folders do |fixture, expectation|
|
13
13
|
ipfs.add fixture
|
14
14
|
actual = ipfs.ls('QmcsmfcY8SQzNxJQYGZMHLXCkeTgxDBhASDPJyVEGi8Wrv')
|
15
|
+
actual = streamline_result(actual)
|
15
16
|
expectation = {
|
16
17
|
'Objects' => [
|
17
18
|
{
|
@@ -20,13 +21,13 @@ class CommandLsTest < Minitest::Test
|
|
20
21
|
{
|
21
22
|
'Name' => 'foo.txt',
|
22
23
|
'Hash' => 'QmTz3oc4gdpRMKP2sdGUPZTAGRngqjsi99BPoztyP53JMM',
|
23
|
-
'Size' =>
|
24
|
+
'Size' => 4,
|
24
25
|
'Type' => 2
|
25
26
|
},
|
26
27
|
{
|
27
28
|
'Name' => 'hello.txt',
|
28
29
|
'Hash' => 'QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG',
|
29
|
-
'Size' =>
|
30
|
+
'Size' => 13,
|
30
31
|
'Type' => 2
|
31
32
|
}
|
32
33
|
]
|
@@ -37,4 +38,19 @@ class CommandLsTest < Minitest::Test
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
41
|
+
# At some point, around version 0.4.2, a new property "Target" was introduced
|
42
|
+
# to the "Links" objects. In order to match the test expectation, and still not
|
43
|
+
# fail for older versions, we just remove these here whenever they're empty.
|
44
|
+
private
|
45
|
+
def streamline_result result
|
46
|
+
result['Objects'].each do |object|
|
47
|
+
object['Links'].each do |link|
|
48
|
+
if link['Target'] == ''
|
49
|
+
link.delete('Target')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
40
56
|
end
|
data/test/test_io.rb
CHANGED
@@ -1,20 +1,70 @@
|
|
1
|
-
$:.unshift File.expand_path('
|
1
|
+
$:.unshift File.expand_path('..', __FILE__)
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'samples'
|
4
4
|
require 'ipfs-api/io'
|
5
5
|
|
6
6
|
include IPFS::IO
|
7
7
|
|
8
|
-
class
|
8
|
+
class StreamProducerTest < Minitest::Test
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def setup
|
11
|
+
@parts = Samples.some_byte_sequences
|
12
|
+
enum = @parts.each
|
13
|
+
producer = StreamProducer.new do |writer|
|
14
|
+
enum.each do |part|
|
15
|
+
writer << part
|
16
|
+
end
|
15
17
|
end
|
16
|
-
|
17
|
-
|
18
|
+
@reader = producer.stream
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_read_byte_wise
|
22
|
+
result = ''
|
23
|
+
while (byte = @reader.read(1))
|
24
|
+
result << byte
|
25
|
+
assert_equal result.size, @reader.pos
|
26
|
+
end
|
27
|
+
assert_eof @reader
|
28
|
+
assert_equal @parts.join, result
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_read_larger_chunks
|
32
|
+
result = ''
|
33
|
+
while (chunk = @reader.read(199))
|
34
|
+
result << chunk
|
35
|
+
assert_equal result.size, @reader.pos
|
36
|
+
if chunk.size < 199
|
37
|
+
assert_eof @reader
|
38
|
+
else
|
39
|
+
assert_not_eof @reader
|
40
|
+
end
|
41
|
+
end
|
42
|
+
assert_eof @reader
|
43
|
+
assert_equal @parts.join, result
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_different_variants_of_read
|
47
|
+
chunk = @reader.read(199)
|
48
|
+
assert_equal 199, chunk.size
|
49
|
+
assert_not_eof @reader
|
50
|
+
chunk = @reader.read(0)
|
51
|
+
assert_equal 0, chunk.size
|
52
|
+
assert_not_eof @reader
|
53
|
+
chunk = @reader.read
|
54
|
+
assert_equal 243, chunk.size
|
55
|
+
assert_eof @reader
|
56
|
+
chunk = @reader.read
|
57
|
+
assert_equal 0, chunk.size
|
58
|
+
assert_eof @reader
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def assert_eof stream
|
63
|
+
assert stream.eof?, "Stream should have reached end-of-file"
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_not_eof stream
|
67
|
+
assert !stream.eof?, "Stream should not yet have reached end-of-file"
|
18
68
|
end
|
19
69
|
|
20
70
|
end
|
data/test/test_upload.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ipfs-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Holger Joest
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This is a client library to access the IPFS from Ruby
|
14
14
|
email: holger@joest.org
|
@@ -30,21 +30,19 @@ files:
|
|
30
30
|
- lib/ipfs-api/io.rb
|
31
31
|
- lib/ipfs-api/upload.rb
|
32
32
|
- lib/ipfs-api/version.rb
|
33
|
-
- test/common.rb
|
34
33
|
- test/samples.rb
|
35
34
|
- test/test_cmd_add.rb
|
36
35
|
- test/test_cmd_cat.rb
|
37
36
|
- test/test_cmd_get.rb
|
38
37
|
- test/test_cmd_id.rb
|
39
38
|
- test/test_cmd_ls.rb
|
40
|
-
- test/test_cmd_name.rb
|
41
39
|
- test/test_io.rb
|
42
40
|
- test/test_upload.rb
|
43
|
-
homepage: http://ruby-ipfs-api
|
41
|
+
homepage: http://hjoest.github.io/ruby-ipfs-api
|
44
42
|
licenses:
|
45
43
|
- MIT
|
46
44
|
metadata: {}
|
47
|
-
post_install_message:
|
45
|
+
post_install_message:
|
48
46
|
rdoc_options:
|
49
47
|
- "--title"
|
50
48
|
- Interplanetary File System for Ruby
|
@@ -57,16 +55,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
55
|
requirements:
|
58
56
|
- - ">="
|
59
57
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
58
|
+
version: 2.0.0
|
61
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
60
|
requirements:
|
63
61
|
- - ">="
|
64
62
|
- !ruby/object:Gem::Version
|
65
63
|
version: '0'
|
66
64
|
requirements: []
|
67
|
-
|
68
|
-
|
69
|
-
signing_key:
|
65
|
+
rubygems_version: 3.1.4
|
66
|
+
signing_key:
|
70
67
|
specification_version: 4
|
71
68
|
summary: Interplanetary File System for Ruby
|
72
69
|
test_files: []
|
data/test/common.rb
DELETED
data/test/test_cmd_name.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
$:.unshift File.expand_path('../..', __FILE__)
|
2
|
-
|
3
|
-
require 'test/common'
|
4
|
-
require 'ipfs-api'
|
5
|
-
|
6
|
-
include IPFS
|
7
|
-
|
8
|
-
class CommandNameTest < Minitest::Test
|
9
|
-
|
10
|
-
def test_name_resolve
|
11
|
-
ipfs = Connection.new
|
12
|
-
resolved = ipfs.name.resolve
|
13
|
-
assert resolved.start_with?('/ipfs/Qm')
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|