ro-crate 0.4.11 → 0.4.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -6
- data/README.md +2 -0
- data/lib/ro_crate/model/crate.rb +38 -6
- data/lib/ro_crate/model/data_entity.rb +3 -2
- data/lib/ro_crate/model/directory.rb +2 -2
- data/lib/ro_crate/model/entity.rb +41 -4
- data/lib/ro_crate/model/entry.rb +2 -2
- data/lib/ro_crate/model/file.rb +1 -1
- data/lib/ro_crate/model/remote_entry.rb +1 -12
- data/lib/ro_crate/reader.rb +4 -3
- data/lib/ro_crate/writer.rb +4 -4
- data/lib/ro_crate.rb +1 -1
- data/ro_crate.gemspec +2 -2
- data/test/crate_test.rb +25 -3
- data/test/directory_test.rb +21 -21
- data/test/entity_test.rb +114 -0
- data/test/fixtures/biobb_hpc_workflows-condapack.zip +0 -0
- data/test/fixtures/conflicting_data_directory/info.txt +1 -0
- data/test/fixtures/conflicting_data_directory/nested.txt +1 -0
- data/test/reader_test.rb +53 -46
- data/test/test_helper.rb +5 -1
- data/test/writer_test.rb +37 -0
- metadata +15 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9f081110d1e5b5e94674cb53d07262fbce50ec3f63437d5c4c1ecda8b04f835
|
4
|
+
data.tar.gz: 0cd710a9cd86063e706fd9cf338e5fdf55f5bc6993f306af4f50d6f9191a0d87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ce573fbd259108edbb528e24f0d9647cfee4454378d57a4b3392aaeb0dea80385b2828221a0979caa4733be0e2108a4a535725846f576c9ac97866f46911e47
|
7
|
+
data.tar.gz: ffc7aafbdccb3f4897ad2acae726714d7a313f02fd87e5c96173da2b12860dfa1318b9b323dde751e3ac10c9a66cf29effea383037bc4622eaf5113dd01bd426
|
data/Gemfile.lock
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ro-crate (0.4.
|
5
|
-
addressable (
|
4
|
+
ro-crate (0.4.12)
|
5
|
+
addressable (>= 2.7, < 2.9)
|
6
6
|
rubyzip (~> 2.0.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
addressable (2.
|
11
|
+
addressable (2.8.0)
|
12
12
|
public_suffix (>= 2.0.2, < 5.0)
|
13
13
|
crack (0.4.3)
|
14
14
|
safe_yaml (~> 1.0.0)
|
15
15
|
docile (1.3.5)
|
16
16
|
hashdiff (1.0.1)
|
17
|
-
power_assert (
|
18
|
-
public_suffix (4.0.
|
17
|
+
power_assert (1.1.3)
|
18
|
+
public_suffix (4.0.6)
|
19
19
|
rake (13.0.0)
|
20
20
|
rubyzip (2.0.0)
|
21
21
|
safe_yaml (1.0.5)
|
@@ -25,7 +25,7 @@ GEM
|
|
25
25
|
simplecov_json_formatter (~> 0.1)
|
26
26
|
simplecov-html (0.12.3)
|
27
27
|
simplecov_json_formatter (0.1.2)
|
28
|
-
test-unit (3.2.
|
28
|
+
test-unit (3.2.9)
|
29
29
|
power_assert
|
30
30
|
webmock (3.8.3)
|
31
31
|
addressable (>= 2.3.6)
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# ro-crate-ruby
|
2
2
|
|
3
|
+
![Tests](https://github.com/ResearchObject/ro-crate-ruby/actions/workflows/tests.yml/badge.svg)
|
4
|
+
|
3
5
|
This is a WIP gem for creating, manipulating and reading RO-Crates (conforming to version 1.1 of the specification).
|
4
6
|
|
5
7
|
* RO-Crate - https://researchobject.github.io/ro-crate/
|
data/lib/ro_crate/model/crate.rb
CHANGED
@@ -25,6 +25,15 @@ module ROCrate
|
|
25
25
|
super(self, nil, id, properties)
|
26
26
|
end
|
27
27
|
|
28
|
+
##
|
29
|
+
# Lookup an Entity using the given ID (in this Entity's crate).
|
30
|
+
#
|
31
|
+
# @param id [String] The ID to query.
|
32
|
+
# @return [Entity, nil]
|
33
|
+
def dereference(id)
|
34
|
+
entities.detect { |e| e.canonical_id == crate.resolve_id(id) } if id
|
35
|
+
end
|
36
|
+
|
28
37
|
##
|
29
38
|
# Create a new file and add it to the crate.
|
30
39
|
#
|
@@ -229,21 +238,21 @@ module ROCrate
|
|
229
238
|
entity.class.new(crate, entity.id, entity.raw_properties)
|
230
239
|
end
|
231
240
|
|
232
|
-
alias_method :
|
241
|
+
alias_method :own_payload, :payload
|
233
242
|
##
|
234
|
-
#
|
235
|
-
# key is the
|
243
|
+
# The file payload of the RO-Crate - a map of all the files/directories contained in the RO-Crate, where the
|
244
|
+
# key is the path relative to the crate's root, and the value is an Entry where the source data can be read.
|
236
245
|
#
|
237
246
|
# @return [Hash{String => Entry}>]
|
238
|
-
def
|
247
|
+
def payload
|
239
248
|
# Gather a map of entries, starting from the crate itself, then any directory data entities, then finally any
|
240
249
|
# file data entities. This ensures in the case of a conflict, the more "specific" data entities take priority.
|
241
|
-
entries =
|
250
|
+
entries = own_payload
|
242
251
|
non_self_entities = default_entities.reject { |e| e == self }
|
243
252
|
sorted_entities = (non_self_entities | data_entities).sort_by { |e| e.is_a?(ROCrate::Directory) ? 0 : 1 }
|
244
253
|
|
245
254
|
sorted_entities.each do |entity|
|
246
|
-
entity.
|
255
|
+
entity.payload.each do |path, entry|
|
247
256
|
entries[path] = entry
|
248
257
|
end
|
249
258
|
end
|
@@ -255,6 +264,29 @@ module ROCrate
|
|
255
264
|
binding
|
256
265
|
end
|
257
266
|
|
267
|
+
##
|
268
|
+
# Remove the entity from the RO-Crate.
|
269
|
+
#
|
270
|
+
# @param entity [Entity, String] The entity or ID of an entity to remove from the crate.
|
271
|
+
# @param remove_orphaned [Boolean] Should linked contextual entities also be removed from the crate they are left
|
272
|
+
# dangling (nothing else is linked to them)?
|
273
|
+
#
|
274
|
+
# @return [Entity, nil] The entity that was deleted, or nil if nothing was deleted.
|
275
|
+
def delete(entity, remove_orphaned: true)
|
276
|
+
entity = dereference(entity) if entity.is_a?(String)
|
277
|
+
return unless entity
|
278
|
+
|
279
|
+
deleted = data_entities.delete(entity) || contextual_entities.delete(entity)
|
280
|
+
|
281
|
+
if deleted && remove_orphaned
|
282
|
+
crate_entities = crate.linked_entities(deep: true)
|
283
|
+
to_remove = (entity.linked_entities(deep: true) - crate_entities)
|
284
|
+
to_remove.each(&:delete)
|
285
|
+
end
|
286
|
+
|
287
|
+
deleted
|
288
|
+
end
|
289
|
+
|
258
290
|
private
|
259
291
|
|
260
292
|
def full_entry_path(relative_path)
|
@@ -24,12 +24,13 @@ module ROCrate
|
|
24
24
|
end
|
25
25
|
|
26
26
|
##
|
27
|
-
#
|
27
|
+
# The payload of all the files/directories associated with this DataEntity, mapped by their relative file path.
|
28
28
|
#
|
29
29
|
# @return [Hash{String => Entry}>] The key is the location within the crate, and the value is an Entry.
|
30
|
-
def
|
30
|
+
def payload
|
31
31
|
{}
|
32
32
|
end
|
33
|
+
alias_method :entries, :payload
|
33
34
|
|
34
35
|
##
|
35
36
|
# A disk-safe filepath based on the ID of this DataEntity.
|
@@ -28,11 +28,11 @@ module ROCrate
|
|
28
28
|
end
|
29
29
|
|
30
30
|
##
|
31
|
-
# The
|
31
|
+
# The payload of this directory - a map of all the files/directories, where the key is the destination path
|
32
32
|
# within the crate and the value is an Entry where the source data can be read.
|
33
33
|
#
|
34
34
|
# @return [Hash{String => Entry}>]
|
35
|
-
def
|
35
|
+
def payload
|
36
36
|
entries = {}
|
37
37
|
entries[filepath.chomp('/')] = @entry if @entry
|
38
38
|
|
@@ -129,16 +129,26 @@ module ROCrate
|
|
129
129
|
# @param id [String] The ID to query.
|
130
130
|
# @return [Entity, nil]
|
131
131
|
def dereference(id)
|
132
|
-
crate.
|
132
|
+
crate.dereference(id)
|
133
133
|
end
|
134
|
-
|
135
134
|
alias_method :get, :dereference
|
136
135
|
|
136
|
+
##
|
137
|
+
# Remove this entity from the RO-Crate.
|
138
|
+
#
|
139
|
+
# @param remove_orphaned [Boolean] Should linked contextual entities also be removed from the crate (if nothing else is linked to them)?
|
140
|
+
#
|
141
|
+
# @return [Entity, nil] This entity, or nil if nothing was deleted.
|
142
|
+
def delete(remove_orphaned: true)
|
143
|
+
crate.delete(self, remove_orphaned: remove_orphaned)
|
144
|
+
end
|
145
|
+
|
137
146
|
def id
|
138
147
|
@properties['@id']
|
139
148
|
end
|
140
149
|
|
141
150
|
def id=(id)
|
151
|
+
@canonical_id = nil
|
142
152
|
@properties['@id'] = self.class.format_id(id)
|
143
153
|
end
|
144
154
|
|
@@ -190,13 +200,13 @@ module ROCrate
|
|
190
200
|
#
|
191
201
|
# @return [Addressable::URI]
|
192
202
|
def canonical_id
|
193
|
-
crate.resolve_id(id)
|
203
|
+
@canonical_id ||= crate.resolve_id(id)
|
194
204
|
end
|
195
205
|
|
196
206
|
##
|
197
207
|
# Is this entity local to the crate or an external reference?
|
198
208
|
#
|
199
|
-
# @return [
|
209
|
+
# @return [Boolean]
|
200
210
|
def external?
|
201
211
|
crate.canonical_id.host != canonical_id.host
|
202
212
|
end
|
@@ -226,6 +236,33 @@ module ROCrate
|
|
226
236
|
@properties.has_type?(type)
|
227
237
|
end
|
228
238
|
|
239
|
+
##
|
240
|
+
# Gather a list of entities linked to this one through its properties.
|
241
|
+
# @param deep [Boolean] If false, only consider direct links, otherwise consider transitive links.
|
242
|
+
# @param linked [Hash{String => Entity}] Discovered entities, mapped by their ID, to avoid loops when recursing.
|
243
|
+
# @return [Array<Entity>]
|
244
|
+
def linked_entities(deep: false, linked: {})
|
245
|
+
properties.each do |key, value|
|
246
|
+
value = [value] if value.is_a?(JSONLDHash)
|
247
|
+
|
248
|
+
if value.is_a?(Array)
|
249
|
+
value.each do |v|
|
250
|
+
if v.is_a?(JSONLDHash) && !linked.key?(v['@id'])
|
251
|
+
entity = v.dereference
|
252
|
+
linked[entity.id] = entity if entity
|
253
|
+
if deep
|
254
|
+
entity.linked_entities(deep: true, linked: linked).each do |e|
|
255
|
+
linked[e.id] = e
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
linked.values.compact
|
264
|
+
end
|
265
|
+
|
229
266
|
private
|
230
267
|
|
231
268
|
def default_properties
|
data/lib/ro_crate/model/entry.rb
CHANGED
@@ -14,10 +14,10 @@ module ROCrate
|
|
14
14
|
end
|
15
15
|
|
16
16
|
##
|
17
|
-
# Write the source to the destination via a buffer.
|
17
|
+
# Write the entry's source to the destination via a buffer.
|
18
18
|
#
|
19
19
|
# @param dest [#write] An IO-like destination to write to.
|
20
|
-
def
|
20
|
+
def write_to(dest)
|
21
21
|
input = source
|
22
22
|
input = input.open('rb') if input.is_a?(Pathname)
|
23
23
|
while (buff = input.read(4096))
|
data/lib/ro_crate/model/file.rb
CHANGED
@@ -2,7 +2,7 @@ module ROCrate
|
|
2
2
|
##
|
3
3
|
# A class to represent a reference within an RO-Crate, to a remote file held on the internet somewhere.
|
4
4
|
# It handles the actual reading/writing of bytes.
|
5
|
-
class RemoteEntry
|
5
|
+
class RemoteEntry < Entry
|
6
6
|
attr_reader :uri
|
7
7
|
|
8
8
|
##
|
@@ -13,17 +13,6 @@ module ROCrate
|
|
13
13
|
@uri = uri
|
14
14
|
end
|
15
15
|
|
16
|
-
def write(dest)
|
17
|
-
raise 'Cannot write to a remote entry!'
|
18
|
-
end
|
19
|
-
|
20
|
-
##
|
21
|
-
# Read from the source.
|
22
|
-
#
|
23
|
-
def read
|
24
|
-
source.read
|
25
|
-
end
|
26
|
-
|
27
16
|
##
|
28
17
|
# @return [IO] An IO object for the remote resource.
|
29
18
|
#
|
data/lib/ro_crate/reader.rb
CHANGED
@@ -181,9 +181,10 @@ module ROCrate
|
|
181
181
|
crate.metadata.properties = entity_hash.delete(ROCrate::Metadata::IDENTIFIER)
|
182
182
|
crate.metadata.context = context
|
183
183
|
preview_properties = entity_hash.delete(ROCrate::Preview::IDENTIFIER)
|
184
|
-
|
185
|
-
|
186
|
-
|
184
|
+
preview_path = ::File.join(source, ROCrate::Preview::IDENTIFIER)
|
185
|
+
preview_path = ::File.exists?(preview_path) ? Pathname.new(preview_path) : nil
|
186
|
+
if preview_properties || preview_path
|
187
|
+
crate.preview = ROCrate::Preview.new(crate, preview_path, preview_properties || {})
|
187
188
|
end
|
188
189
|
crate.add_all(source, false)
|
189
190
|
end
|
data/lib/ro_crate/writer.rb
CHANGED
@@ -16,14 +16,14 @@ module ROCrate
|
|
16
16
|
# @param overwrite [Boolean] Whether or not to overwrite existing files.
|
17
17
|
def write(dir, overwrite: true)
|
18
18
|
FileUtils.mkdir_p(dir) # Make any parent directories
|
19
|
-
@crate.
|
19
|
+
@crate.payload.each do |path, entry|
|
20
20
|
fullpath = ::File.join(dir, path)
|
21
21
|
next if !overwrite && ::File.exist?(fullpath)
|
22
22
|
next if entry.directory?
|
23
23
|
FileUtils.mkdir_p(::File.dirname(fullpath))
|
24
24
|
temp = Tempfile.new('ro-crate-temp')
|
25
25
|
begin
|
26
|
-
entry.
|
26
|
+
entry.write_to(temp)
|
27
27
|
temp.close
|
28
28
|
FileUtils.mv(temp, fullpath)
|
29
29
|
ensure
|
@@ -38,9 +38,9 @@ module ROCrate
|
|
38
38
|
# @param destination [String, ::File] The destination where to write the RO-Crate zip.
|
39
39
|
def write_zip(destination)
|
40
40
|
Zip::File.open(destination, Zip::File::CREATE) do |zip|
|
41
|
-
@crate.
|
41
|
+
@crate.payload.each do |path, entry|
|
42
42
|
next if entry.directory?
|
43
|
-
zip.get_output_stream(path) { |s| entry.
|
43
|
+
zip.get_output_stream(path) { |s| entry.write_to(s) }
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
data/lib/ro_crate.rb
CHANGED
@@ -10,8 +10,8 @@ require 'ro_crate/json_ld_hash'
|
|
10
10
|
require 'ro_crate/model/entity'
|
11
11
|
require 'ro_crate/model/data_entity'
|
12
12
|
require 'ro_crate/model/file'
|
13
|
-
require 'ro_crate/model/remote_entry'
|
14
13
|
require 'ro_crate/model/entry'
|
14
|
+
require 'ro_crate/model/remote_entry'
|
15
15
|
require 'ro_crate/model/directory'
|
16
16
|
require 'ro_crate/model/metadata'
|
17
17
|
require 'ro_crate/model/preview_generator'
|
data/ro_crate.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'ro-crate'
|
3
|
-
s.version = '0.4.
|
3
|
+
s.version = '0.4.12'
|
4
4
|
s.summary = 'Create, manipulate, read RO-Crates.'
|
5
5
|
s.authors = ['Finn Bacall']
|
6
6
|
s.email = 'finn.bacall@manchester.ac.uk'
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.homepage = 'https://github.com/ResearchObject/ro-crate-ruby'
|
9
9
|
s.require_paths = ['lib']
|
10
10
|
s.licenses = ['MIT']
|
11
|
-
s.add_runtime_dependency 'addressable', '
|
11
|
+
s.add_runtime_dependency 'addressable', '>= 2.7', '< 2.9'
|
12
12
|
s.add_runtime_dependency 'rubyzip', '~> 2.0.0'
|
13
13
|
s.add_development_dependency 'rake', '~> 13.0.0'
|
14
14
|
s.add_development_dependency 'test-unit', '~> 3.2.3'
|
data/test/crate_test.rb
CHANGED
@@ -215,7 +215,7 @@ class CrateTest < Test::Unit::TestCase
|
|
215
215
|
crate = ROCrate::Crate.new
|
216
216
|
entities = crate.add_all(fixture_file('directory').path, include_hidden: true)
|
217
217
|
|
218
|
-
paths = crate.
|
218
|
+
paths = crate.payload.keys
|
219
219
|
assert_equal 11, paths.length
|
220
220
|
assert_includes paths, 'data'
|
221
221
|
assert_includes paths, 'root.txt'
|
@@ -250,7 +250,7 @@ class CrateTest < Test::Unit::TestCase
|
|
250
250
|
|
251
251
|
assert_empty entities
|
252
252
|
|
253
|
-
paths = crate.
|
253
|
+
paths = crate.payload.keys
|
254
254
|
assert_equal 11, paths.length
|
255
255
|
assert_includes paths, 'data'
|
256
256
|
assert_includes paths, 'root.txt'
|
@@ -278,7 +278,7 @@ class CrateTest < Test::Unit::TestCase
|
|
278
278
|
crate = ROCrate::Crate.new
|
279
279
|
entities = crate.add_all(fixture_file('directory').path)
|
280
280
|
|
281
|
-
paths = crate.
|
281
|
+
paths = crate.payload.keys
|
282
282
|
assert_equal 8, paths.length
|
283
283
|
assert_includes paths, 'data'
|
284
284
|
assert_includes paths, 'root.txt'
|
@@ -305,4 +305,26 @@ class CrateTest < Test::Unit::TestCase
|
|
305
305
|
|
306
306
|
assert_equal "5678\n", crate.dereference('data/info.txt').source.read
|
307
307
|
end
|
308
|
+
|
309
|
+
test 'can delete entities' do
|
310
|
+
crate = ROCrate::Crate.new
|
311
|
+
file = crate.add_file(StringIO.new(''), 'file')
|
312
|
+
person = crate.add_person('#bob', { name: 'Bob' })
|
313
|
+
file.author = person
|
314
|
+
|
315
|
+
assert crate.delete(file)
|
316
|
+
assert_not_include crate.entities, file
|
317
|
+
assert_not_include crate.entities, person
|
318
|
+
end
|
319
|
+
|
320
|
+
test 'can delete entities by id' do
|
321
|
+
crate = ROCrate::Crate.new
|
322
|
+
file = crate.add_file(StringIO.new(''), 'file')
|
323
|
+
person = crate.add_person('#bob', { name: 'Bob' })
|
324
|
+
file.author = person
|
325
|
+
|
326
|
+
assert crate.delete('file')
|
327
|
+
assert_not_include crate.entities, file
|
328
|
+
assert_not_include crate.entities, person
|
329
|
+
end
|
308
330
|
end
|
data/test/directory_test.rb
CHANGED
@@ -5,41 +5,41 @@ class DirectoryTest < Test::Unit::TestCase
|
|
5
5
|
crate = ROCrate::Crate.new
|
6
6
|
crate.add_directory(fixture_file('directory'))
|
7
7
|
|
8
|
-
|
8
|
+
payload = crate.payload
|
9
9
|
base_path = ::File.dirname(fixture_file('directory'))
|
10
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/info.txt')),
|
11
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')),
|
12
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')),
|
13
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')),
|
14
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')),
|
15
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')),
|
10
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/info.txt')), payload['directory/info.txt'].path
|
11
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')), payload['directory/root.txt'].path
|
12
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')), payload['directory/data'].path
|
13
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')), payload['directory/data/info.txt'].path
|
14
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')), payload['directory/data/nested.txt'].path
|
15
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')), payload['directory/data/binary.jpg'].path
|
16
16
|
end
|
17
17
|
|
18
18
|
test 'adding directory via path' do
|
19
19
|
crate = ROCrate::Crate.new
|
20
20
|
crate.add_directory(fixture_file('directory').path.to_s)
|
21
21
|
|
22
|
-
|
22
|
+
payload = crate.payload
|
23
23
|
base_path = ::File.dirname(fixture_file('directory'))
|
24
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/info.txt')),
|
25
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')),
|
26
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')),
|
27
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')),
|
28
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')),
|
29
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')),
|
24
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/info.txt')), payload['directory/info.txt'].path
|
25
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')), payload['directory/root.txt'].path
|
26
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')), payload['directory/data'].path
|
27
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')), payload['directory/data/info.txt'].path
|
28
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')), payload['directory/data/nested.txt'].path
|
29
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')), payload['directory/data/binary.jpg'].path
|
30
30
|
end
|
31
31
|
|
32
32
|
test 'adding to given path' do
|
33
33
|
crate = ROCrate::Crate.new
|
34
34
|
crate.add_directory(fixture_file('directory').path.to_s, 'fish')
|
35
35
|
|
36
|
-
|
36
|
+
payload = crate.payload
|
37
37
|
base_path = ::File.dirname(fixture_file('directory'))
|
38
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/info.txt')),
|
39
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')),
|
40
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')),
|
41
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')),
|
42
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')),
|
43
|
-
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')),
|
38
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/info.txt')), payload['fish/info.txt'].path
|
39
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')), payload['fish/root.txt'].path
|
40
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')), payload['fish/data'].path
|
41
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')), payload['fish/data/info.txt'].path
|
42
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')), payload['fish/data/nested.txt'].path
|
43
|
+
assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')), payload['fish/data/binary.jpg'].path
|
44
44
|
end
|
45
45
|
end
|
data/test/entity_test.rb
CHANGED
@@ -91,4 +91,118 @@ class EntityTest < Test::Unit::TestCase
|
|
91
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
92
|
assert_equal "http://www.data.com/my%20crate/", ROCrate::Crate.format_id('http://www.data.com/my%20crate/')
|
93
93
|
end
|
94
|
+
|
95
|
+
test 'linked entities' do
|
96
|
+
crate = ROCrate::Crate.new
|
97
|
+
file = crate.add_file(StringIO.new(''), 'file')
|
98
|
+
person = crate.add_person('#bob', { name: 'Bob' })
|
99
|
+
file.author = person
|
100
|
+
|
101
|
+
linked = file.linked_entities
|
102
|
+
assert_include linked, person
|
103
|
+
assert_equal 1, linked.length
|
104
|
+
|
105
|
+
linked = crate.linked_entities
|
106
|
+
assert_include linked, file
|
107
|
+
assert_equal 1, linked.length
|
108
|
+
|
109
|
+
linked = crate.linked_entities(deep: true)
|
110
|
+
assert_include linked, file
|
111
|
+
assert_include linked, person
|
112
|
+
assert_equal 2, linked.length
|
113
|
+
end
|
114
|
+
|
115
|
+
test 'deleting entities removes linked entities' do
|
116
|
+
crate = ROCrate::Crate.new
|
117
|
+
file = crate.add_file(StringIO.new(''), 'file')
|
118
|
+
person = crate.add_person('#bob', { name: 'Bob' })
|
119
|
+
file.author = person
|
120
|
+
|
121
|
+
assert file.delete
|
122
|
+
assert_not_include crate.entities, file
|
123
|
+
assert_not_include crate.entities, person
|
124
|
+
end
|
125
|
+
|
126
|
+
test 'deleting entities does not remove dangling entities if option set' do
|
127
|
+
crate = ROCrate::Crate.new
|
128
|
+
file = crate.add_file(StringIO.new(''), 'file')
|
129
|
+
person = crate.add_person('#bob', { name: 'Bob' })
|
130
|
+
file.author = person
|
131
|
+
|
132
|
+
assert file.delete(remove_orphaned: false)
|
133
|
+
assert_not_include crate.entities, file
|
134
|
+
assert_include crate.entities, person
|
135
|
+
end
|
136
|
+
|
137
|
+
test 'creating files in various ways' do
|
138
|
+
stub_request(:get, 'http://example.com/external_ref.txt').to_return(status: 200, body: 'file contents')
|
139
|
+
|
140
|
+
crate = ROCrate::Crate.new
|
141
|
+
|
142
|
+
f1 = nil
|
143
|
+
Dir.chdir(fixture_dir) { f1 = ROCrate::File.new(crate, 'data.csv') }
|
144
|
+
refute f1.source.remote?
|
145
|
+
refute f1.source.directory?
|
146
|
+
assert_equal 20, f1.source.read.length
|
147
|
+
t = Tempfile.new('tf1')
|
148
|
+
f1.source.write_to(t)
|
149
|
+
t.rewind
|
150
|
+
assert_equal 20, t.read.length
|
151
|
+
|
152
|
+
f2 = ROCrate::File.new(crate, fixture_file('info.txt'), { author: crate.add_person('bob', name: 'Bob').reference })
|
153
|
+
refute f2.source.remote?
|
154
|
+
refute f2.source.directory?
|
155
|
+
assert f2.source.read
|
156
|
+
assert_equal 6, f2.source.read.length
|
157
|
+
t = Tempfile.new('tf2')
|
158
|
+
f2.source.write_to(t)
|
159
|
+
t.rewind
|
160
|
+
assert_equal 6, t.read.length
|
161
|
+
|
162
|
+
f3 = ROCrate::File.new(crate, 'http://example.com/external_ref.txt')
|
163
|
+
assert f3.source.remote?
|
164
|
+
refute f3.source.directory?
|
165
|
+
assert f3.source.read
|
166
|
+
assert_equal 13, f3.source.read.length
|
167
|
+
t = Tempfile.new('tf3')
|
168
|
+
f3.source.write_to(t)
|
169
|
+
t.rewind
|
170
|
+
assert_equal 13, t.read.length
|
171
|
+
|
172
|
+
f3 = ROCrate::File.new(crate, 'http://example.com/external_ref.txt')
|
173
|
+
assert f3.source.remote?
|
174
|
+
refute f3.source.directory?
|
175
|
+
assert f3.source.read
|
176
|
+
assert_equal 13, f3.source.read.length
|
177
|
+
t = Tempfile.new('tf3')
|
178
|
+
f3.source.write_to(t)
|
179
|
+
t.rewind
|
180
|
+
assert_equal 13, t.read.length
|
181
|
+
end
|
182
|
+
|
183
|
+
test 'assigning and checking type' do
|
184
|
+
crate = ROCrate::Crate.new
|
185
|
+
file = ROCrate::File.new(crate, 'data.csv')
|
186
|
+
|
187
|
+
assert file.has_type?('File')
|
188
|
+
|
189
|
+
file.type = ['File', 'Workflow']
|
190
|
+
|
191
|
+
assert file.has_type?('Workflow')
|
192
|
+
assert file.has_type?('File')
|
193
|
+
refute file.has_type?('Banana')
|
194
|
+
end
|
195
|
+
|
196
|
+
test 'inspecting object truncates very long property list' do
|
197
|
+
crate = ROCrate::Crate.new
|
198
|
+
entity = ROCrate::ContextualEntity.new(crate, 'hello')
|
199
|
+
|
200
|
+
assert entity.inspect.start_with?("<#ROCrate::ContextualEntity arcp://uuid,")
|
201
|
+
|
202
|
+
entity['veryLong'] = ('123456789a' * 100)
|
203
|
+
|
204
|
+
ins = entity.inspect
|
205
|
+
assert ins.length < 1000
|
206
|
+
assert ins.end_with?('...>')
|
207
|
+
end
|
94
208
|
end
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
abcd
|
@@ -0,0 +1 @@
|
|
1
|
+
No, I am nested!
|
data/test/reader_test.rb
CHANGED
@@ -88,12 +88,12 @@ class ReaderTest < Test::Unit::TestCase
|
|
88
88
|
test 'reading from zip with directories' do
|
89
89
|
crate = ROCrate::Reader.read_zip(fixture_file('directory.zip'))
|
90
90
|
|
91
|
-
assert crate.
|
92
|
-
assert_equal '1234', crate.
|
93
|
-
assert crate.
|
94
|
-
assert crate.
|
95
|
-
assert crate.
|
96
|
-
assert crate.
|
91
|
+
assert crate.payload['fish/info.txt']
|
92
|
+
assert_equal '1234', crate.payload['fish/info.txt'].source.read.chomp
|
93
|
+
assert crate.payload['fish/root.txt']
|
94
|
+
assert crate.payload['fish/data/info.txt']
|
95
|
+
assert crate.payload['fish/data/nested.txt']
|
96
|
+
assert crate.payload['fish/data/binary.jpg']
|
97
97
|
assert_equal ['./', 'fish/', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
98
98
|
end
|
99
99
|
|
@@ -101,22 +101,22 @@ class ReaderTest < Test::Unit::TestCase
|
|
101
101
|
Dir.mktmpdir('test-1234-banana') do |dir|
|
102
102
|
crate = ROCrate::Reader.read_zip(fixture_file('directory.zip'), target_dir: dir)
|
103
103
|
|
104
|
-
assert crate.
|
105
|
-
assert crate.
|
104
|
+
assert crate.payload['fish/info.txt']
|
105
|
+
assert crate.payload['fish/info.txt'].source.to_s.include?('/test-1234-banana')
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
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.
|
113
|
-
assert crate.
|
114
|
-
assert_equal '1234', crate.
|
115
|
-
refute crate.
|
116
|
-
assert crate.
|
117
|
-
assert crate.
|
118
|
-
refute crate.
|
119
|
-
assert crate.
|
112
|
+
assert crate.payload.values.all? { |e| e.is_a?(ROCrate::Entry) }
|
113
|
+
assert crate.payload['fish/info.txt']
|
114
|
+
assert_equal '1234', crate.payload['fish/info.txt'].source.read.chomp
|
115
|
+
refute crate.payload['fish/root.txt'].directory?
|
116
|
+
assert crate.payload['fish/data'].directory?
|
117
|
+
assert crate.payload['fish/data/info.txt']
|
118
|
+
refute crate.payload['fish/data/nested.txt'].remote?
|
119
|
+
assert crate.payload['fish/data/binary.jpg']
|
120
120
|
assert_equal ['./', 'fish/', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
121
121
|
end
|
122
122
|
|
@@ -162,37 +162,38 @@ class ReaderTest < Test::Unit::TestCase
|
|
162
162
|
assert ext_file.source.is_a?(ROCrate::RemoteEntry)
|
163
163
|
assert_equal 'http://example.com/external_ref.txt', ext_file.id
|
164
164
|
assert_equal 'file contents', ext_file.source.read
|
165
|
+
assert crate.preview.source.source.is_a?(ROCrate::PreviewGenerator)
|
165
166
|
end
|
166
167
|
|
167
168
|
test 'reading from directory with unlisted files' do
|
168
169
|
crate = ROCrate::Reader.read_directory(fixture_file('sparse_directory_crate').path)
|
169
170
|
|
170
|
-
assert_equal 11, crate.
|
171
|
-
assert crate.
|
172
|
-
assert crate.
|
173
|
-
assert crate.
|
174
|
-
assert_equal '1234', crate.
|
175
|
-
refute crate.
|
176
|
-
assert crate.
|
177
|
-
assert crate.
|
178
|
-
refute crate.
|
179
|
-
assert crate.
|
171
|
+
assert_equal 11, crate.payload.count
|
172
|
+
assert crate.payload['listed_file.txt']
|
173
|
+
assert crate.payload['unlisted_file.txt']
|
174
|
+
assert crate.payload['fish']
|
175
|
+
assert_equal '1234', crate.payload['fish/info.txt'].source.read.chomp
|
176
|
+
refute crate.payload['fish/root.txt'].directory?
|
177
|
+
assert crate.payload['fish/data'].directory?
|
178
|
+
assert crate.payload['fish/data/info.txt']
|
179
|
+
refute crate.payload['fish/data/nested.txt'].remote?
|
180
|
+
assert crate.payload['fish/data/binary.jpg']
|
180
181
|
assert_equal ['./', 'listed_file.txt', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
181
182
|
end
|
182
183
|
|
183
184
|
test 'reading from a zip with unlisted files' do
|
184
185
|
crate = ROCrate::Reader.read_zip(fixture_file('sparse_directory_crate.zip').path)
|
185
186
|
|
186
|
-
assert_equal 11, crate.
|
187
|
-
assert crate.
|
188
|
-
assert crate.
|
189
|
-
assert crate.
|
190
|
-
assert_equal '1234', crate.
|
191
|
-
refute crate.
|
192
|
-
assert crate.
|
193
|
-
assert crate.
|
194
|
-
refute crate.
|
195
|
-
assert crate.
|
187
|
+
assert_equal 11, crate.payload.count
|
188
|
+
assert crate.payload['listed_file.txt']
|
189
|
+
assert crate.payload['unlisted_file.txt']
|
190
|
+
assert crate.payload['fish']
|
191
|
+
assert_equal '1234', crate.payload['fish/info.txt'].source.read.chomp
|
192
|
+
refute crate.payload['fish/root.txt'].directory?
|
193
|
+
assert crate.payload['fish/data'].directory?
|
194
|
+
assert crate.payload['fish/data/info.txt']
|
195
|
+
refute crate.payload['fish/data/nested.txt'].remote?
|
196
|
+
assert crate.payload['fish/data/binary.jpg']
|
196
197
|
assert_equal ['./', 'listed_file.txt', 'ro-crate-metadata.jsonld', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
197
198
|
end
|
198
199
|
|
@@ -201,30 +202,30 @@ class ReaderTest < Test::Unit::TestCase
|
|
201
202
|
string_io.write(::File.read(fixture_file('sparse_directory_crate.zip').path))
|
202
203
|
string_io.rewind
|
203
204
|
assert string_io.is_a?(StringIO)
|
204
|
-
assert_equal 11, ROCrate::Reader.read_zip(string_io).
|
205
|
+
assert_equal 11, ROCrate::Reader.read_zip(string_io).payload.count
|
205
206
|
|
206
207
|
path = Pathname.new(fixture_file('sparse_directory_crate.zip').path)
|
207
208
|
assert path.is_a?(Pathname)
|
208
|
-
assert_equal 11, ROCrate::Reader.read_zip(path).
|
209
|
+
assert_equal 11, ROCrate::Reader.read_zip(path).payload.count
|
209
210
|
|
210
211
|
file = ::File.open(fixture_file('sparse_directory_crate.zip').path)
|
211
212
|
assert file.is_a?(::File)
|
212
|
-
assert_equal 11, ROCrate::Reader.read_zip(file).
|
213
|
+
assert_equal 11, ROCrate::Reader.read_zip(file).payload.count
|
213
214
|
|
214
215
|
string = fixture_file('sparse_directory_crate.zip').path
|
215
216
|
assert string.is_a?(String)
|
216
|
-
assert_equal 11, ROCrate::Reader.read_zip(string).
|
217
|
+
assert_equal 11, ROCrate::Reader.read_zip(string).payload.count
|
217
218
|
end
|
218
219
|
|
219
220
|
test 'reading from zip where the crate root is nested somewhere within' do
|
220
221
|
crate = ROCrate::Reader.read_zip(fixture_file('nested_directory.zip'))
|
221
222
|
|
222
|
-
assert crate.
|
223
|
-
assert_equal '1234', crate.
|
224
|
-
assert crate.
|
225
|
-
assert crate.
|
226
|
-
assert crate.
|
227
|
-
assert crate.
|
223
|
+
assert crate.payload['fish/info.txt']
|
224
|
+
assert_equal '1234', crate.payload['fish/info.txt'].source.read.chomp
|
225
|
+
assert crate.payload['fish/root.txt']
|
226
|
+
assert crate.payload['fish/data/info.txt']
|
227
|
+
assert crate.payload['fish/data/nested.txt']
|
228
|
+
assert crate.payload['fish/data/binary.jpg']
|
228
229
|
assert_equal ['./', 'fish/', 'ro-crate-metadata.json', 'ro-crate-preview.html'], crate.entities.map(&:id).sort
|
229
230
|
end
|
230
231
|
|
@@ -251,4 +252,10 @@ class ReaderTest < Test::Unit::TestCase
|
|
251
252
|
}
|
252
253
|
], context
|
253
254
|
end
|
255
|
+
|
256
|
+
test 'existing preview is used even if not mentioned in metadata' do
|
257
|
+
crate = ROCrate::Reader.read_zip(fixture_file('biobb_hpc_workflows-condapack.zip').path)
|
258
|
+
assert crate.preview.source.source.is_a?(Pathname)
|
259
|
+
assert_equal 80526, crate.preview.source.read.length
|
260
|
+
end
|
254
261
|
end
|
data/test/test_helper.rb
CHANGED
@@ -6,5 +6,9 @@ require 'ro_crate'
|
|
6
6
|
require 'webmock/test_unit'
|
7
7
|
|
8
8
|
def fixture_file(name, *args)
|
9
|
-
::File.open(::File.join(
|
9
|
+
::File.open(::File.join(fixture_dir, name), *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def fixture_dir
|
13
|
+
::File.join(::File.dirname(__FILE__), 'fixtures')
|
10
14
|
end
|
data/test/writer_test.rb
CHANGED
@@ -47,8 +47,12 @@ class WriterTest < Test::Unit::TestCase
|
|
47
47
|
end
|
48
48
|
|
49
49
|
test 'writing to zip' do
|
50
|
+
# Remote entries should not be written, so this 500 error should not affect anything.
|
51
|
+
stub_request(:get, 'http://example.com/external_ref.txt').to_return(status: 500)
|
52
|
+
|
50
53
|
crate = ROCrate::Crate.new
|
51
54
|
crate.add_file(fixture_file('info.txt'))
|
55
|
+
crate.add_file('http://example.com/external_ref.txt')
|
52
56
|
crate.add_file(fixture_file('data.csv'), 'directory/data.csv')
|
53
57
|
|
54
58
|
Tempfile.create do |file|
|
@@ -57,6 +61,7 @@ class WriterTest < Test::Unit::TestCase
|
|
57
61
|
Zip::File.open(file) do |zipfile|
|
58
62
|
assert zipfile.file.exist?(ROCrate::Metadata::IDENTIFIER)
|
59
63
|
assert zipfile.file.exist?(ROCrate::Preview::IDENTIFIER)
|
64
|
+
refute zipfile.file.exist?('external_ref.txt')
|
60
65
|
assert_equal 6, zipfile.file.size('info.txt')
|
61
66
|
assert_equal 20, zipfile.file.size('directory/data.csv')
|
62
67
|
end
|
@@ -152,4 +157,36 @@ class WriterTest < Test::Unit::TestCase
|
|
152
157
|
end
|
153
158
|
end
|
154
159
|
end
|
160
|
+
|
161
|
+
test 'writing with conflicting paths in payload obeys specificity rules' do
|
162
|
+
crate = ROCrate::Crate.new
|
163
|
+
|
164
|
+
# Payload from crate
|
165
|
+
crate.add_all(fixture_file('directory').path, false)
|
166
|
+
Dir.mktmpdir do |dir|
|
167
|
+
ROCrate::Writer.new(crate).write(dir)
|
168
|
+
|
169
|
+
assert_equal "5678\n", ::File.read(::File.join(dir, 'data', 'info.txt'))
|
170
|
+
end
|
171
|
+
|
172
|
+
# Payload from crate + directory
|
173
|
+
crate.add_directory(fixture_file('conflicting_data_directory').path.to_s, 'data')
|
174
|
+
Dir.mktmpdir do |dir|
|
175
|
+
ROCrate::Writer.new(crate).write(dir)
|
176
|
+
|
177
|
+
assert_equal 'abcd', ::File.read(::File.join(dir, 'data', 'info.txt')), 'Directory payload should take priority over Crate.'
|
178
|
+
assert_equal "No, I am nested!\n", ::File.read(::File.join(dir, 'data', 'nested.txt')), 'Directory payload should take priority over Crate.'
|
179
|
+
assert ::File.exist?(::File.join(dir, 'data', 'binary.jpg'))
|
180
|
+
end
|
181
|
+
|
182
|
+
# Payload from crate + directory + file
|
183
|
+
crate.add_file(StringIO.new('xyz'), 'data/info.txt')
|
184
|
+
Dir.mktmpdir do |dir|
|
185
|
+
ROCrate::Writer.new(crate).write(dir)
|
186
|
+
|
187
|
+
assert_equal 'xyz', ::File.read(::File.join(dir, 'data', 'info.txt')), 'File payload should take priority over Crate and Directory.'
|
188
|
+
assert_equal "No, I am nested!\n", ::File.read(::File.join(dir, 'data', 'nested.txt')), 'Directory payload should take priority over Crate.'
|
189
|
+
assert ::File.exist?(::File.join(dir, 'data', 'binary.jpg'))
|
190
|
+
end
|
191
|
+
end
|
155
192
|
end
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
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.12
|
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-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.7'
|
20
|
+
- - "<"
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
22
|
+
version: '2.9'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.7'
|
30
|
+
- - "<"
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
32
|
+
version: '2.9'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rubyzip
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,6 +152,9 @@ files:
|
|
146
152
|
- test/crate_test.rb
|
147
153
|
- test/directory_test.rb
|
148
154
|
- test/entity_test.rb
|
155
|
+
- test/fixtures/biobb_hpc_workflows-condapack.zip
|
156
|
+
- test/fixtures/conflicting_data_directory/info.txt
|
157
|
+
- test/fixtures/conflicting_data_directory/nested.txt
|
149
158
|
- test/fixtures/crate-spec1.1/file with spaces.txt
|
150
159
|
- test/fixtures/crate-spec1.1/ro-crate-metadata.json
|
151
160
|
- test/fixtures/data.csv
|