ro-crate 0.4.5 → 0.4.10
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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/ro_crate/model/contextual_entity.rb +2 -14
- data/lib/ro_crate/model/crate.rb +15 -6
- data/lib/ro_crate/model/data_entity.rb +1 -1
- data/lib/ro_crate/model/directory.rb +3 -3
- data/lib/ro_crate/model/entity.rb +24 -2
- data/lib/ro_crate/model/file.rb +3 -1
- data/lib/ro_crate/reader.rb +70 -8
- data/ro_crate.gemspec +1 -1
- data/test/entity_test.rb +21 -0
- data/test/fixtures/nested_directory.zip +0 -0
- data/test/fixtures/sparse_directory_crate.zip +0 -0
- data/test/fixtures/sparse_directory_crate/fish/data/binary.jpg +0 -0
- data/test/fixtures/sparse_directory_crate/fish/data/info.txt +1 -0
- data/test/fixtures/sparse_directory_crate/fish/data/nested.txt +1 -0
- data/test/fixtures/sparse_directory_crate/fish/info.txt +1 -0
- data/test/fixtures/sparse_directory_crate/fish/root.txt +1 -0
- data/test/fixtures/sparse_directory_crate/listed_file.txt +1 -0
- data/test/fixtures/sparse_directory_crate/ro-crate-metadata.jsonld +32 -0
- data/test/fixtures/sparse_directory_crate/ro-crate-preview.html +66 -0
- data/test/fixtures/sparse_directory_crate/unlisted_file.txt +1 -0
- data/test/reader_test.rb +64 -0
- data/test/writer_test.rb +20 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb6b4eba94b7190b7888cf3b1ddc8a8c6d0b464834ff48c45d9b50e6b13844ce
|
4
|
+
data.tar.gz: 70e0079caadfa3f17745e918f8109ec5908a8aa0c87d80aeaebfc55fdc03a9a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62c9875be216987ceaca7e47f92112ba744109e4df9f8dcbba9023211ef3fc278f74f8ce7421523a0f443000f251627735eb01ce2357e9e526e36c23818bbba0
|
7
|
+
data.tar.gz: 0b5be2eedc7a045ccf25db4e3d800107f30d00b62e1e4b5bdb1deed1eed941897f46621bef9fa9a012891bc058857de82d6afebeb122153f8ffc944b8ca06a7a
|
data/Gemfile.lock
CHANGED
@@ -3,21 +3,9 @@ module ROCrate
|
|
3
3
|
# A class to represent a "Contextual Entity" within an RO-Crate.
|
4
4
|
# Contextual Entities are used to describe and provide context to the Data Entities within the crate.
|
5
5
|
class ContextualEntity < Entity
|
6
|
-
def self.
|
6
|
+
def self.format_local_id(id)
|
7
7
|
i = super
|
8
|
-
|
9
|
-
uri = URI(id)
|
10
|
-
rescue ArgumentError
|
11
|
-
uri = nil
|
12
|
-
end
|
13
|
-
|
14
|
-
if uri&.absolute?
|
15
|
-
i
|
16
|
-
elsif i.start_with?('#')
|
17
|
-
i
|
18
|
-
else
|
19
|
-
"##{i}"
|
20
|
-
end
|
8
|
+
i.start_with?('#') ? i : "##{i}"
|
21
9
|
end
|
22
10
|
|
23
11
|
##
|
data/lib/ro_crate/model/crate.rb
CHANGED
@@ -8,6 +8,11 @@ module ROCrate
|
|
8
8
|
properties(%w[name datePublished author license identifier distribution contactPoint publisher description url hasPart])
|
9
9
|
|
10
10
|
def self.format_id(id)
|
11
|
+
i = super(id)
|
12
|
+
i.end_with?('/') ? i : "#{i}/"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.format_local_id(id)
|
11
16
|
return id if id == IDENTIFIER
|
12
17
|
super
|
13
18
|
end
|
@@ -217,15 +222,19 @@ module ROCrate
|
|
217
222
|
|
218
223
|
alias_method :own_entries, :entries
|
219
224
|
##
|
220
|
-
#
|
221
|
-
# and the value is an Entry where the source data can be read.
|
225
|
+
# # The RO-Crate's "payload" of the crate - a map of all the files/directories contained in the RO-Crate, where the
|
226
|
+
# key is the destination path within the crate and the value is an Entry where the source data can be read.
|
222
227
|
#
|
223
228
|
# @return [Hash{String => Entry}>]
|
224
229
|
def entries
|
225
|
-
entries
|
226
|
-
|
227
|
-
|
228
|
-
|
230
|
+
# Gather a map of entries, starting from the crate itself, then any directory data entities, then finally any
|
231
|
+
# file data entities. This ensures in the case of a conflict, the more "specific" data entities take priority.
|
232
|
+
entries = own_entries
|
233
|
+
non_self_entities = default_entities.reject { |e| e == self }
|
234
|
+
sorted_entities = (non_self_entities | data_entities).sort_by { |e| e.is_a?(ROCrate::Directory) ? 0 : 1 }
|
235
|
+
|
236
|
+
sorted_entities.each do |entity|
|
237
|
+
entity.entries.each do |path, entry|
|
229
238
|
entries[path] = entry
|
230
239
|
end
|
231
240
|
end
|
@@ -3,7 +3,7 @@ module ROCrate
|
|
3
3
|
# A class to represent a "Data Entity" within an RO-Crate.
|
4
4
|
# Data Entities are the actual physical files and directories within the Crate.
|
5
5
|
class DataEntity < Entity
|
6
|
-
def self.
|
6
|
+
def self.format_local_id(id)
|
7
7
|
super.chomp('/')
|
8
8
|
end
|
9
9
|
|
@@ -4,7 +4,7 @@ module ROCrate
|
|
4
4
|
class Directory < DataEntity
|
5
5
|
properties(%w[name contentSize dateModified encodingFormat identifier sameAs])
|
6
6
|
|
7
|
-
def self.
|
7
|
+
def self.format_local_id(id)
|
8
8
|
super + '/'
|
9
9
|
end
|
10
10
|
|
@@ -30,8 +30,8 @@ module ROCrate
|
|
30
30
|
end
|
31
31
|
|
32
32
|
##
|
33
|
-
#
|
34
|
-
# and the value is an Entry where the source data can be read.
|
33
|
+
# The "payload" of this directory - a map of all the files/directories, where the key is the destination path
|
34
|
+
# within the crate and the value is an Entry where the source data can be read.
|
35
35
|
#
|
36
36
|
# @return [Hash{String => Entry}>]
|
37
37
|
def entries
|
@@ -29,12 +29,34 @@ module ROCrate
|
|
29
29
|
end
|
30
30
|
|
31
31
|
##
|
32
|
-
# Format the given ID with rules appropriate for this type.
|
32
|
+
# Format the given ID with rules appropriate for this type if it is local/relative, leave as-is if absolute.
|
33
|
+
#
|
34
|
+
# @param id [String] The candidate ID to be formatted.
|
35
|
+
# @return [String] The formatted ID.
|
36
|
+
def self.format_id(id)
|
37
|
+
begin
|
38
|
+
uri = URI(id)
|
39
|
+
rescue ArgumentError, URI::InvalidURIError
|
40
|
+
uri = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
if uri&.absolute?
|
44
|
+
id
|
45
|
+
else
|
46
|
+
format_local_id(id)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Format the given local ID with rules appropriate for this type.
|
33
52
|
# For example:
|
34
53
|
# * contextual entities MUST be absolute URIs, or begin with: #
|
35
54
|
# * files MUST NOT begin with ./
|
36
55
|
# * directories MUST NOT begin with ./ (except for the crate itself), and MUST end with /
|
37
|
-
|
56
|
+
#
|
57
|
+
# @param id [String] The candidate local ID to be formatted.
|
58
|
+
# @return [String] The formatted local ID.
|
59
|
+
def self.format_local_id(id)
|
38
60
|
Addressable::URI.escape(id.sub(/\A\.\//, '')) # Remove initial ./ if present
|
39
61
|
end
|
40
62
|
|
data/lib/ro_crate/model/file.rb
CHANGED
@@ -52,7 +52,9 @@ module ROCrate
|
|
52
52
|
end
|
53
53
|
|
54
54
|
##
|
55
|
-
# A map
|
55
|
+
# The "payload". A map with a single key and value, of the relative filepath within the crate, to the source on disk
|
56
|
+
# where the actual bytes of the file can be read. Blank if remote.
|
57
|
+
#
|
56
58
|
# (for compatibility with Directory#entries)
|
57
59
|
#
|
58
60
|
# @return [Hash{String => Entry}>] The key is the location within the crate, and the value is an Entry.
|
data/lib/ro_crate/reader.rb
CHANGED
@@ -3,29 +3,67 @@ module ROCrate
|
|
3
3
|
# A class to handle reading of RO-Crates from Zip files or directories.
|
4
4
|
class Reader
|
5
5
|
##
|
6
|
-
# Reads an RO-Crate from a directory
|
6
|
+
# Reads an RO-Crate from a directory or zip file.
|
7
7
|
#
|
8
|
-
# @param source [String, ::File, Pathname] The
|
8
|
+
# @param source [String, ::File, Pathname, #read] The location of the zip or directory, or an IO-like object containing a zip.
|
9
9
|
# @param target_dir [String, ::File, Pathname] The target directory where the crate should be unzipped (if its a Zip file).
|
10
10
|
# @return [Crate] The RO-Crate.
|
11
11
|
def self.read(source, target_dir: Dir.mktmpdir)
|
12
12
|
raise "Not a directory!" unless ::File.directory?(target_dir)
|
13
|
-
|
13
|
+
begin
|
14
|
+
is_dir = ::File.directory?(source)
|
15
|
+
rescue TypeError
|
16
|
+
is_dir = false
|
17
|
+
end
|
18
|
+
|
19
|
+
if is_dir
|
14
20
|
read_directory(source)
|
15
21
|
else
|
16
22
|
read_zip(source, target_dir: target_dir)
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
26
|
+
##
|
27
|
+
# Extract the contents of the given Zip file/data to the given directory.
|
28
|
+
#
|
29
|
+
# @param source [String, ::File, Pathname, #read] The location of the zip file, or an IO-like object.
|
30
|
+
# @param target [String, ::File, Pathname] The target directory where the file should be unzipped.
|
31
|
+
def self.unzip_to(source, target)
|
32
|
+
source = Pathname.new(::File.expand_path(source)) if source.is_a?(String)
|
33
|
+
|
34
|
+
if source.is_a?(Pathname) || source.respond_to?(:path)
|
35
|
+
unzip_file_to(source, target)
|
36
|
+
else
|
37
|
+
unzip_io_to(source, target)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Extract the given Zip file data to the given directory.
|
43
|
+
#
|
44
|
+
# @param source [#read] An IO-like object containing a Zip file.
|
45
|
+
# @param target [String, ::File, Pathname] The target directory where the file should be unzipped.
|
46
|
+
def self.unzip_io_to(io, target)
|
47
|
+
Dir.chdir(target) do
|
48
|
+
Zip::InputStream.open(io) do |input|
|
49
|
+
while (entry = input.get_next_entry)
|
50
|
+
unless ::File.exist?(entry.name) || entry.name_is_directory?
|
51
|
+
FileUtils::mkdir_p(::File.dirname(entry.name))
|
52
|
+
::File.binwrite(entry.name, input.read)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
20
59
|
##
|
21
60
|
# Extract the contents of the given Zip file to the given directory.
|
22
61
|
#
|
23
62
|
# @param source [String, ::File, Pathname] The location of the zip file.
|
24
63
|
# @param target [String, ::File, Pathname] The target directory where the file should be unzipped.
|
25
|
-
def self.
|
26
|
-
source = ::File.expand_path(source)
|
64
|
+
def self.unzip_file_to(file_or_path, target)
|
27
65
|
Dir.chdir(target) do
|
28
|
-
Zip::File.open(
|
66
|
+
Zip::File.open(file_or_path) do |zipfile|
|
29
67
|
zipfile.each do |entry|
|
30
68
|
unless ::File.exist?(entry.name)
|
31
69
|
FileUtils::mkdir_p(::File.dirname(entry.name))
|
@@ -40,13 +78,16 @@ module ROCrate
|
|
40
78
|
# Reads an RO-Crate from a zip file. It first extracts the Zip file to a temporary directory, and then calls
|
41
79
|
# #read_directory.
|
42
80
|
#
|
43
|
-
# @param source [String, ::File, Pathname] The location of the zip file.
|
81
|
+
# @param source [String, ::File, Pathname, #read] The location of the zip file, or an IO-like object.
|
44
82
|
# @param target_dir [String, ::File, Pathname] The target directory where the crate should be unzipped.
|
45
83
|
# @return [Crate] The RO-Crate.
|
46
84
|
def self.read_zip(source, target_dir: Dir.mktmpdir)
|
47
85
|
unzip_to(source, target_dir)
|
48
86
|
|
49
|
-
|
87
|
+
# Traverse the unzipped directory to try and find the crate's root
|
88
|
+
root_dir = detect_root_directory(target_dir)
|
89
|
+
|
90
|
+
read_directory(root_dir)
|
50
91
|
end
|
51
92
|
|
52
93
|
##
|
@@ -55,6 +96,8 @@ module ROCrate
|
|
55
96
|
# @param source [String, ::File, Pathname] The location of the directory.
|
56
97
|
# @return [Crate] The RO-Crate.
|
57
98
|
def self.read_directory(source)
|
99
|
+
raise "Not a directory!" unless ::File.directory?(source)
|
100
|
+
|
58
101
|
source = ::File.expand_path(source)
|
59
102
|
metadata_file = Dir.entries(source).detect { |entry| entry == ROCrate::Metadata::IDENTIFIER ||
|
60
103
|
entry == ROCrate::Metadata::IDENTIFIER_1_0 }
|
@@ -107,6 +150,7 @@ module ROCrate
|
|
107
150
|
crate.metadata.properties = entity_hash.delete(ROCrate::Metadata::IDENTIFIER)
|
108
151
|
preview_properties = entity_hash.delete(ROCrate::Preview::IDENTIFIER)
|
109
152
|
crate.preview.properties = preview_properties if preview_properties
|
153
|
+
crate.add_all(source, false)
|
110
154
|
extract_data_entities(crate, source, entity_hash).each do |entity|
|
111
155
|
crate.add_data_entity(entity)
|
112
156
|
end
|
@@ -211,5 +255,23 @@ module ROCrate
|
|
211
255
|
raise "Metadata entity does not reference any root entity" unless root_id
|
212
256
|
entities.delete(root_id)
|
213
257
|
end
|
258
|
+
|
259
|
+
##
|
260
|
+
# Finds an RO-Crate's root directory (where `ro-crate-metdata.json` is located) within a given directory.
|
261
|
+
#
|
262
|
+
# @param source [String, ::File, Pathname] The location of the directory.
|
263
|
+
# @return [Pathname, nil] The path to the root, or nil if not found.
|
264
|
+
def self.detect_root_directory(source)
|
265
|
+
Pathname(source).find do |entry|
|
266
|
+
if entry.file?
|
267
|
+
name = entry.basename.to_s
|
268
|
+
if name == ROCrate::Metadata::IDENTIFIER || name == ROCrate::Metadata::IDENTIFIER_1_0
|
269
|
+
return entry.parent
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
nil
|
275
|
+
end
|
214
276
|
end
|
215
277
|
end
|
data/ro_crate.gemspec
CHANGED
data/test/entity_test.rb
CHANGED
@@ -70,4 +70,25 @@ class EntityTest < Test::Unit::TestCase
|
|
70
70
|
assert_equal({ '@id' => '#fred' }, person.reference)
|
71
71
|
assert_equal(person.canonical_id, crate.author.canonical_id)
|
72
72
|
end
|
73
|
+
|
74
|
+
test 'format various IDs' do
|
75
|
+
assert_equal "#Hello%20World/Goodbye%20World", ROCrate::ContextualEntity.format_id('#Hello World/Goodbye World')
|
76
|
+
assert_equal "#Hello%20World/Goodbye%20World", ROCrate::ContextualEntity.format_id('Hello World/Goodbye World')
|
77
|
+
assert_equal "#%F0%9F%98%8A", ROCrate::ContextualEntity.format_id("😊")
|
78
|
+
|
79
|
+
assert_equal "test123/hello.txt", ROCrate::File.format_id('./test123/hello.txt')
|
80
|
+
assert_equal "test123/hello.txt", ROCrate::File.format_id('./test123/hello.txt/')
|
81
|
+
assert_equal "http://www.data.com/my%20data.txt", ROCrate::File.format_id('http://www.data.com/my%20data.txt')
|
82
|
+
assert_equal "http://www.data.com/my%20data.txt/", ROCrate::File.format_id('http://www.data.com/my%20data.txt/'), 'Should not modify absolute URI for DataEntity'
|
83
|
+
|
84
|
+
assert_equal "my%20directory/", ROCrate::Directory.format_id('my directory')
|
85
|
+
assert_equal "my%20directory/", ROCrate::Directory.format_id('my directory/')
|
86
|
+
assert_equal 'http://www.data.com/my%20directory', ROCrate::Directory.format_id('http://www.data.com/my%20directory'), 'Should not modify absolute URI for DataEntity'
|
87
|
+
assert_equal 'http://www.data.com/my%20directory/', ROCrate::Directory.format_id('http://www.data.com/my%20directory/'), 'Should not modify absolute URI for DataEntity'
|
88
|
+
|
89
|
+
assert_equal "./", ROCrate::Crate.format_id('./')
|
90
|
+
assert_equal "cool%20crate/", ROCrate::Crate.format_id('./cool crate')
|
91
|
+
assert_equal "http://www.data.com/my%20crate/", ROCrate::Crate.format_id('http://www.data.com/my%20crate'), 'Crate ID should end with /'
|
92
|
+
assert_equal "http://www.data.com/my%20crate/", ROCrate::Crate.format_id('http://www.data.com/my%20crate/')
|
93
|
+
end
|
73
94
|
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
5678
|
@@ -0,0 +1 @@
|
|
1
|
+
I'm nested
|
@@ -0,0 +1 @@
|
|
1
|
+
1234
|
@@ -0,0 +1 @@
|
|
1
|
+
I'm at the root
|
@@ -0,0 +1 @@
|
|
1
|
+
123
|
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"@context": "https://w3id.org/ro/crate/1.0/context",
|
3
|
+
"@graph": [
|
4
|
+
{
|
5
|
+
"@id": "ro-crate-metadata.jsonld",
|
6
|
+
"@type": "CreativeWork",
|
7
|
+
"about": {
|
8
|
+
"@id": "./"
|
9
|
+
}
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"@id": "ro-crate-preview.html",
|
13
|
+
"@type": "CreativeWork",
|
14
|
+
"about": {
|
15
|
+
"@id": "./"
|
16
|
+
}
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"@id": "./",
|
20
|
+
"@type": "Dataset",
|
21
|
+
"hasPart": [
|
22
|
+
{
|
23
|
+
"@id": "listed_file.txt"
|
24
|
+
}
|
25
|
+
]
|
26
|
+
},
|
27
|
+
{
|
28
|
+
"@id": "listed_file.txt",
|
29
|
+
"@type": "File"
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>New RO-Crate</title>
|
5
|
+
<script type="application/ld+json">{
|
6
|
+
"@context": "https://w3id.org/ro/crate/1.1/context",
|
7
|
+
"@graph": [
|
8
|
+
{
|
9
|
+
"@id": "ro-crate-metadata.jsonld",
|
10
|
+
"@type": "CreativeWork",
|
11
|
+
"about": {
|
12
|
+
"@id": "./"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"@id": "ro-crate-preview.html",
|
17
|
+
"@type": "CreativeWork",
|
18
|
+
"about": {
|
19
|
+
"@id": "./"
|
20
|
+
}
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"@id": "./",
|
24
|
+
"@type": "Dataset",
|
25
|
+
"hasPart": [
|
26
|
+
{
|
27
|
+
"@id": "fish/"
|
28
|
+
}
|
29
|
+
]
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"@id": "fish/",
|
33
|
+
"@type": "Dataset"
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}</script>
|
37
|
+
<meta name="generator" content="https://github.com/fbacall/ro-crate-ruby">
|
38
|
+
<meta name="keywords" content="RO-Crate">
|
39
|
+
</head>
|
40
|
+
<body>
|
41
|
+
<h1>New RO-Crate</h1>
|
42
|
+
|
43
|
+
<p>
|
44
|
+
|
45
|
+
</p>
|
46
|
+
<dl>
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
</dl>
|
52
|
+
|
53
|
+
<h2>Contents</h2>
|
54
|
+
<ul>
|
55
|
+
|
56
|
+
<li id="__data_entity_fish/">
|
57
|
+
|
58
|
+
<strong>fish/</strong>
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
</li>
|
63
|
+
|
64
|
+
</ul>
|
65
|
+
</body>
|
66
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
456
|
data/test/reader_test.rb
CHANGED
@@ -163,4 +163,68 @@ class ReaderTest < Test::Unit::TestCase
|
|
163
163
|
assert_equal 'http://example.com/external_ref.txt', ext_file.id
|
164
164
|
assert_equal 'file contents', ext_file.source.read
|
165
165
|
end
|
166
|
+
|
167
|
+
test 'reading from directory with unlisted files' do
|
168
|
+
crate = ROCrate::Reader.read_directory(fixture_file('sparse_directory_crate').path)
|
169
|
+
|
170
|
+
assert_equal 11, crate.entries.count
|
171
|
+
assert crate.entries['listed_file.txt']
|
172
|
+
assert crate.entries['unlisted_file.txt']
|
173
|
+
assert crate.entries['fish']
|
174
|
+
assert_equal '1234', crate.entries['fish/info.txt'].source.read.chomp
|
175
|
+
refute crate.entries['fish/root.txt'].directory?
|
176
|
+
assert crate.entries['fish/data'].directory?
|
177
|
+
assert crate.entries['fish/data/info.txt']
|
178
|
+
refute crate.entries['fish/data/nested.txt'].remote?
|
179
|
+
assert crate.entries['fish/data/binary.jpg']
|
180
|
+
assert_equal ['./', 'listed_file.txt', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
181
|
+
end
|
182
|
+
|
183
|
+
test 'reading from a zip with unlisted files' do
|
184
|
+
crate = ROCrate::Reader.read_zip(fixture_file('sparse_directory_crate.zip').path)
|
185
|
+
|
186
|
+
assert_equal 11, crate.entries.count
|
187
|
+
assert crate.entries['listed_file.txt']
|
188
|
+
assert crate.entries['unlisted_file.txt']
|
189
|
+
assert crate.entries['fish']
|
190
|
+
assert_equal '1234', crate.entries['fish/info.txt'].source.read.chomp
|
191
|
+
refute crate.entries['fish/root.txt'].directory?
|
192
|
+
assert crate.entries['fish/data'].directory?
|
193
|
+
assert crate.entries['fish/data/info.txt']
|
194
|
+
refute crate.entries['fish/data/nested.txt'].remote?
|
195
|
+
assert crate.entries['fish/data/binary.jpg']
|
196
|
+
assert_equal ['./', 'listed_file.txt', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
197
|
+
end
|
198
|
+
|
199
|
+
test 'reading a zip from various object types' do
|
200
|
+
string_io = StringIO.new
|
201
|
+
string_io.write(::File.read(fixture_file('sparse_directory_crate.zip').path))
|
202
|
+
string_io.rewind
|
203
|
+
assert string_io.is_a?(StringIO)
|
204
|
+
assert_equal 11, ROCrate::Reader.read_zip(string_io).entries.count
|
205
|
+
|
206
|
+
path = Pathname.new(fixture_file('sparse_directory_crate.zip').path)
|
207
|
+
assert path.is_a?(Pathname)
|
208
|
+
assert_equal 11, ROCrate::Reader.read_zip(path).entries.count
|
209
|
+
|
210
|
+
file = ::File.open(fixture_file('sparse_directory_crate.zip').path)
|
211
|
+
assert file.is_a?(::File)
|
212
|
+
assert_equal 11, ROCrate::Reader.read_zip(file).entries.count
|
213
|
+
|
214
|
+
string = fixture_file('sparse_directory_crate.zip').path
|
215
|
+
assert string.is_a?(String)
|
216
|
+
assert_equal 11, ROCrate::Reader.read_zip(string).entries.count
|
217
|
+
end
|
218
|
+
|
219
|
+
test 'reading from zip where the crate root is nested somewhere within' do
|
220
|
+
crate = ROCrate::Reader.read_zip(fixture_file('nested_directory.zip'))
|
221
|
+
|
222
|
+
assert crate.entries['fish/info.txt']
|
223
|
+
assert_equal '1234', crate.entries['fish/info.txt'].source.read.chomp
|
224
|
+
assert crate.entries['fish/root.txt']
|
225
|
+
assert crate.entries['fish/data/info.txt']
|
226
|
+
assert crate.entries['fish/data/nested.txt']
|
227
|
+
assert crate.entries['fish/data/binary.jpg']
|
228
|
+
assert_equal ['./', 'fish/', 'ro-crate-metadata.json', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
229
|
+
end
|
166
230
|
end
|
data/test/writer_test.rb
CHANGED
@@ -112,4 +112,24 @@ class WriterTest < Test::Unit::TestCase
|
|
112
112
|
assert_equal 2529, ::File.size(::File.join(dir, 'data', 'binary.jpg'))
|
113
113
|
end
|
114
114
|
end
|
115
|
+
|
116
|
+
test 'reading and writing out a directory crate produces an identical crate' do
|
117
|
+
fixture = fixture_file('sparse_directory_crate').path
|
118
|
+
Dir.mktmpdir do |dir|
|
119
|
+
dir = ::File.join(dir, 'new_directory')
|
120
|
+
crate = ROCrate::Reader.read(fixture)
|
121
|
+
|
122
|
+
ROCrate::Writer.new(crate).write(dir)
|
123
|
+
expected_files = Dir.chdir(fixture) { Dir.glob('**/*') }
|
124
|
+
actual_files = Dir.chdir(dir) { Dir.glob('**/*') }
|
125
|
+
assert_equal expected_files, actual_files
|
126
|
+
expected_files.each do |file|
|
127
|
+
next if file == 'ro-crate-metadata.jsonld' # Expected context gets updated.
|
128
|
+
next if file == 'ro-crate-preview.html' # RO-Crate preview format changed
|
129
|
+
abs_file_path = ::File.join(fixture, file)
|
130
|
+
next if ::File.directory?(abs_file_path)
|
131
|
+
assert_equal ::File.read(abs_file_path), ::File.read(::File.join(dir, file)), "#{file} didn't match"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
115
135
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ro-crate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Finn Bacall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -165,8 +165,19 @@ files:
|
|
165
165
|
- test/fixtures/directory_crate/ro-crate-preview.html
|
166
166
|
- test/fixtures/file with spaces.txt
|
167
167
|
- test/fixtures/info.txt
|
168
|
+
- test/fixtures/nested_directory.zip
|
168
169
|
- test/fixtures/spaces/file with spaces.txt
|
169
170
|
- test/fixtures/spaces/ro-crate-metadata.jsonld
|
171
|
+
- test/fixtures/sparse_directory_crate.zip
|
172
|
+
- test/fixtures/sparse_directory_crate/fish/data/binary.jpg
|
173
|
+
- test/fixtures/sparse_directory_crate/fish/data/info.txt
|
174
|
+
- test/fixtures/sparse_directory_crate/fish/data/nested.txt
|
175
|
+
- test/fixtures/sparse_directory_crate/fish/info.txt
|
176
|
+
- test/fixtures/sparse_directory_crate/fish/root.txt
|
177
|
+
- test/fixtures/sparse_directory_crate/listed_file.txt
|
178
|
+
- test/fixtures/sparse_directory_crate/ro-crate-metadata.jsonld
|
179
|
+
- test/fixtures/sparse_directory_crate/ro-crate-preview.html
|
180
|
+
- test/fixtures/sparse_directory_crate/unlisted_file.txt
|
170
181
|
- test/fixtures/workflow-0.2.0.zip
|
171
182
|
- test/fixtures/workflow-0.2.0/Dockerfile
|
172
183
|
- test/fixtures/workflow-0.2.0/README.md
|