ro-crate 0.4.4 → 0.4.9
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 +23 -13
- data/lib/ro_crate/model/data_entity.rb +1 -1
- data/lib/ro_crate/model/directory.rb +11 -8
- data/lib/ro_crate/model/entity.rb +24 -2
- data/lib/ro_crate/model/file.rb +3 -1
- data/lib/ro_crate/reader.rb +48 -7
- data/ro_crate.gemspec +1 -1
- data/test/crate_test.rb +34 -2
- data/test/entity_test.rb +21 -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 +56 -2
- data/test/writer_test.rb +20 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88f4e08570b547ac985a759af19d619747dd78a9001cd7994f314a42c014e001
|
4
|
+
data.tar.gz: e3711f54d12c1b4d6b1d68d16923dd051ae2b3a01a28a1d1e155a471bb84bfab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a468ad85761945fc782b5ccb6943e4d1b53c7bb528dba59ed19f93ab2af08017f212d93bf47f9af983b6f00fd304dd1b2edfaef5e096f04ffdb181fd75352e5
|
7
|
+
data.tar.gz: 89edb2f44a6842a7409c2e3107e76b3c401533738c32e6c18faecc6c9c29e6fd71a0d06fbe20f67fdec62564d00b3c168da36b29662ab8badfe652d923b7ab58
|
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
|
@@ -68,22 +73,23 @@ module ROCrate
|
|
68
73
|
#
|
69
74
|
# @param source_directory [String, Pathname, ::File,] The source directory that will be included in the crate.
|
70
75
|
# @param create_entities [Boolean] Whether to create data entities for the added content, or just include them anonymously.
|
76
|
+
# @param include_hidden [Boolean] Whether to include hidden files, i.e. those prefixed by a `.` (period).
|
71
77
|
#
|
72
78
|
# @return [Array<DataEntity>] Any entities that were created from the directory contents. Will be empty if `create_entities` was false.
|
73
|
-
def add_all(source_directory, create_entities = true)
|
79
|
+
def add_all(source_directory, create_entities = true, include_hidden: false)
|
74
80
|
added = []
|
75
81
|
|
76
|
-
|
77
|
-
|
78
|
-
|
82
|
+
if create_entities
|
83
|
+
list_all_files(source_directory, include_hidden: include_hidden).each do |rel_path|
|
84
|
+
source_path = Pathname.new(::File.join(source_directory, rel_path)).expand_path
|
79
85
|
if source_path.directory?
|
80
86
|
added << add_directory(source_path, rel_path)
|
81
87
|
else
|
82
88
|
added << add_file(source_path, rel_path)
|
83
89
|
end
|
84
|
-
else
|
85
|
-
populate_entries(Pathname.new(::File.expand_path(source_directory)))
|
86
90
|
end
|
91
|
+
else
|
92
|
+
populate_entries(Pathname.new(::File.expand_path(source_directory)), include_hidden: include_hidden)
|
87
93
|
end
|
88
94
|
|
89
95
|
added
|
@@ -216,16 +222,20 @@ module ROCrate
|
|
216
222
|
|
217
223
|
alias_method :own_entries, :entries
|
218
224
|
##
|
219
|
-
#
|
220
|
-
# 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.
|
221
227
|
#
|
222
228
|
# @return [Hash{String => Entry}>]
|
223
229
|
def entries
|
224
|
-
entries
|
225
|
-
|
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|
|
238
|
+
entries[path] = entry
|
229
239
|
end
|
230
240
|
end
|
231
241
|
|
@@ -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
|
|
@@ -21,7 +21,7 @@ module ROCrate
|
|
21
21
|
|
22
22
|
if source_directory
|
23
23
|
source_directory = Pathname.new(::File.expand_path(source_directory))
|
24
|
-
@entry = source_directory
|
24
|
+
@entry = Entry.new(source_directory)
|
25
25
|
populate_entries(source_directory)
|
26
26
|
crate_path = source_directory.basename.to_s if crate_path.nil?
|
27
27
|
end
|
@@ -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
|
@@ -51,13 +51,14 @@ module ROCrate
|
|
51
51
|
# Populate this directory with files/directories from a given source directory on disk.
|
52
52
|
#
|
53
53
|
# @param source_directory [Pathname] The source directory to populate from.
|
54
|
+
# @param include_hidden [Boolean] Whether to include hidden files, i.e. those prefixed by a `.` (period).
|
54
55
|
#
|
55
56
|
# @return [Hash{String => Entry}>] The files/directories that were populated.
|
56
57
|
# The key is the relative path of the file/directory, and the value is an Entry object where data can be read etc.
|
57
|
-
def populate_entries(source_directory)
|
58
|
+
def populate_entries(source_directory, include_hidden: false)
|
58
59
|
raise 'Not a directory' unless ::File.directory?(source_directory)
|
59
60
|
@directory_entries = {}
|
60
|
-
list_all_files(source_directory).each do |rel_path|
|
61
|
+
list_all_files(source_directory, include_hidden: include_hidden).each do |rel_path|
|
61
62
|
source_path = Pathname.new(::File.join(source_directory, rel_path)).expand_path
|
62
63
|
@directory_entries[rel_path] = Entry.new(source_path)
|
63
64
|
end
|
@@ -69,8 +70,10 @@ module ROCrate
|
|
69
70
|
::File.join(filepath, relative_path)
|
70
71
|
end
|
71
72
|
|
72
|
-
def list_all_files(source_directory)
|
73
|
-
|
73
|
+
def list_all_files(source_directory, include_hidden: false)
|
74
|
+
args = ['**/*']
|
75
|
+
args << ::File::FNM_DOTMATCH if include_hidden
|
76
|
+
Dir.chdir(source_directory) { Dir.glob(*args) }.reject do |path|
|
74
77
|
path == '.' || path == '..' || path.end_with?('/.')
|
75
78
|
end
|
76
79
|
end
|
@@ -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,7 +78,7 @@ 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)
|
@@ -55,6 +93,8 @@ module ROCrate
|
|
55
93
|
# @param source [String, ::File, Pathname] The location of the directory.
|
56
94
|
# @return [Crate] The RO-Crate.
|
57
95
|
def self.read_directory(source)
|
96
|
+
raise "Not a directory!" unless ::File.directory?(source)
|
97
|
+
|
58
98
|
source = ::File.expand_path(source)
|
59
99
|
metadata_file = Dir.entries(source).detect { |entry| entry == ROCrate::Metadata::IDENTIFIER ||
|
60
100
|
entry == ROCrate::Metadata::IDENTIFIER_1_0 }
|
@@ -107,6 +147,7 @@ module ROCrate
|
|
107
147
|
crate.metadata.properties = entity_hash.delete(ROCrate::Metadata::IDENTIFIER)
|
108
148
|
preview_properties = entity_hash.delete(ROCrate::Preview::IDENTIFIER)
|
109
149
|
crate.preview.properties = preview_properties if preview_properties
|
150
|
+
crate.add_all(source, false)
|
110
151
|
extract_data_entities(crate, source, entity_hash).each do |entity|
|
111
152
|
crate.add_data_entity(entity)
|
112
153
|
end
|
data/ro_crate.gemspec
CHANGED
data/test/crate_test.rb
CHANGED
@@ -201,7 +201,7 @@ class CrateTest < Test::Unit::TestCase
|
|
201
201
|
|
202
202
|
test 'can add an entire directory tree as data entities' do
|
203
203
|
crate = ROCrate::Crate.new
|
204
|
-
entities = crate.add_all(fixture_file('directory').path)
|
204
|
+
entities = crate.add_all(fixture_file('directory').path, include_hidden: true)
|
205
205
|
|
206
206
|
paths = crate.entries.keys
|
207
207
|
assert_equal 11, paths.length
|
@@ -234,7 +234,7 @@ class CrateTest < Test::Unit::TestCase
|
|
234
234
|
|
235
235
|
test 'can create an RO-Crate using content from a given directory' do
|
236
236
|
crate = ROCrate::Crate.new
|
237
|
-
entities = crate.add_all(fixture_file('directory').path, false)
|
237
|
+
entities = crate.add_all(fixture_file('directory').path, false, include_hidden: true)
|
238
238
|
|
239
239
|
assert_empty entities
|
240
240
|
|
@@ -261,4 +261,36 @@ class CrateTest < Test::Unit::TestCase
|
|
261
261
|
assert_nil crate.dereference('data/nested.txt')
|
262
262
|
assert_nil crate.dereference('.dotfile')
|
263
263
|
end
|
264
|
+
|
265
|
+
test 'can create an RO-Crate using content from a given directory, excluding hidden files' do
|
266
|
+
crate = ROCrate::Crate.new
|
267
|
+
entities = crate.add_all(fixture_file('directory').path)
|
268
|
+
|
269
|
+
paths = crate.entries.keys
|
270
|
+
assert_equal 8, paths.length
|
271
|
+
assert_includes paths, 'data'
|
272
|
+
assert_includes paths, 'root.txt'
|
273
|
+
assert_includes paths, 'info.txt'
|
274
|
+
assert_includes paths, 'data/binary.jpg'
|
275
|
+
assert_includes paths, 'data/info.txt'
|
276
|
+
assert_includes paths, 'data/nested.txt'
|
277
|
+
assert_not_includes paths, '.dotfile'
|
278
|
+
assert_not_includes paths, '.dir'
|
279
|
+
assert_not_includes paths, '.dir/test.txt'
|
280
|
+
assert_includes paths, 'ro-crate-metadata.json'
|
281
|
+
assert_includes paths, 'ro-crate-preview.html'
|
282
|
+
|
283
|
+
assert_equal 6, entities.length
|
284
|
+
assert_equal 'ROCrate::Directory', crate.dereference('data/').class.name
|
285
|
+
assert_equal 'ROCrate::File', crate.dereference('root.txt').class.name
|
286
|
+
assert_equal 'ROCrate::File', crate.dereference('info.txt').class.name
|
287
|
+
assert_equal 'ROCrate::File', crate.dereference('data/binary.jpg').class.name
|
288
|
+
assert_equal 'ROCrate::File', crate.dereference('data/info.txt').class.name
|
289
|
+
assert_equal 'ROCrate::File', crate.dereference('data/nested.txt').class.name
|
290
|
+
assert_nil crate.dereference('.dotfile')
|
291
|
+
assert_nil crate.dereference('.dir/')
|
292
|
+
assert_nil crate.dereference('.dir/test.txt')
|
293
|
+
|
294
|
+
assert_equal "5678\n", crate.dereference('data/info.txt').source.read
|
295
|
+
end
|
264
296
|
end
|
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
|
@@ -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
@@ -109,11 +109,13 @@ class ReaderTest < Test::Unit::TestCase
|
|
109
109
|
test 'reading from directory with directories' do
|
110
110
|
crate = ROCrate::Reader.read_directory(fixture_file('directory_crate').path)
|
111
111
|
|
112
|
+
assert crate.entries.values.all? { |e| e.is_a?(ROCrate::Entry) }
|
112
113
|
assert crate.entries['fish/info.txt']
|
113
114
|
assert_equal '1234', crate.entries['fish/info.txt'].source.read.chomp
|
114
|
-
|
115
|
+
refute crate.entries['fish/root.txt'].directory?
|
116
|
+
assert crate.entries['fish/data'].directory?
|
115
117
|
assert crate.entries['fish/data/info.txt']
|
116
|
-
|
118
|
+
refute crate.entries['fish/data/nested.txt'].remote?
|
117
119
|
assert crate.entries['fish/data/binary.jpg']
|
118
120
|
assert_equal ['./', 'fish/', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
119
121
|
end
|
@@ -161,4 +163,56 @@ class ReaderTest < Test::Unit::TestCase
|
|
161
163
|
assert_equal 'http://example.com/external_ref.txt', ext_file.id
|
162
164
|
assert_equal 'file contents', ext_file.source.read
|
163
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
|
164
218
|
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.9
|
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-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -167,6 +167,16 @@ files:
|
|
167
167
|
- test/fixtures/info.txt
|
168
168
|
- test/fixtures/spaces/file with spaces.txt
|
169
169
|
- test/fixtures/spaces/ro-crate-metadata.jsonld
|
170
|
+
- test/fixtures/sparse_directory_crate.zip
|
171
|
+
- test/fixtures/sparse_directory_crate/fish/data/binary.jpg
|
172
|
+
- test/fixtures/sparse_directory_crate/fish/data/info.txt
|
173
|
+
- test/fixtures/sparse_directory_crate/fish/data/nested.txt
|
174
|
+
- test/fixtures/sparse_directory_crate/fish/info.txt
|
175
|
+
- test/fixtures/sparse_directory_crate/fish/root.txt
|
176
|
+
- test/fixtures/sparse_directory_crate/listed_file.txt
|
177
|
+
- test/fixtures/sparse_directory_crate/ro-crate-metadata.jsonld
|
178
|
+
- test/fixtures/sparse_directory_crate/ro-crate-preview.html
|
179
|
+
- test/fixtures/sparse_directory_crate/unlisted_file.txt
|
170
180
|
- test/fixtures/workflow-0.2.0.zip
|
171
181
|
- test/fixtures/workflow-0.2.0/Dockerfile
|
172
182
|
- test/fixtures/workflow-0.2.0/README.md
|