ro-crate 0.4.10 → 0.4.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +13 -13
  3. data/README.md +39 -0
  4. data/lib/ro_crate/model/crate.rb +48 -6
  5. data/lib/ro_crate/model/data_entity.rb +21 -8
  6. data/lib/ro_crate/model/directory.rb +5 -7
  7. data/lib/ro_crate/model/entity.rb +41 -4
  8. data/lib/ro_crate/model/entry.rb +2 -2
  9. data/lib/ro_crate/model/file.rb +6 -4
  10. data/lib/ro_crate/model/metadata.rb +9 -1
  11. data/lib/ro_crate/model/organization.rb +1 -1
  12. data/lib/ro_crate/model/preview.rb +3 -15
  13. data/lib/ro_crate/model/preview_generator.rb +40 -0
  14. data/lib/ro_crate/model/remote_entry.rb +1 -12
  15. data/lib/ro_crate/reader.rb +61 -23
  16. data/lib/ro_crate/writer.rb +4 -4
  17. data/lib/ro_crate.rb +2 -1
  18. data/ro_crate.gemspec +3 -3
  19. data/test/crate_test.rb +58 -3
  20. data/test/directory_test.rb +21 -21
  21. data/test/entity_test.rb +117 -3
  22. data/test/fixtures/biobb_hpc_workflows-condapack.zip +0 -0
  23. data/test/fixtures/conflicting_data_directory/info.txt +1 -0
  24. data/test/fixtures/conflicting_data_directory/nested.txt +1 -0
  25. data/test/fixtures/misc_data_entity_crate/ro-crate-metadata.json +33 -0
  26. data/test/fixtures/ro-crate-galaxy-sortchangecase/LICENSE +176 -0
  27. data/test/fixtures/ro-crate-galaxy-sortchangecase/README.md +6 -0
  28. data/test/fixtures/ro-crate-galaxy-sortchangecase/ro-crate-metadata.json +140 -0
  29. data/test/fixtures/ro-crate-galaxy-sortchangecase/sort-and-change-case.ga +118 -0
  30. data/test/fixtures/ro-crate-galaxy-sortchangecase/test/test1/input.bed +3 -0
  31. data/test/fixtures/ro-crate-galaxy-sortchangecase/test/test1/output_exp.bed +3 -0
  32. data/test/fixtures/ro-crate-galaxy-sortchangecase/test/test1/sort-and-change-case-test.yml +8 -0
  33. data/test/fixtures/sparse_directory_crate/ro-crate-preview.html +60 -59
  34. data/test/fixtures/workflow-0.2.0/ro-crate-metadata.jsonld +5 -5
  35. data/test/fixtures/workflow-0.2.0.zip +0 -0
  36. data/test/reader_test.rb +110 -58
  37. data/test/test_helper.rb +5 -1
  38. data/test/writer_test.rb +70 -2
  39. metadata +26 -8
@@ -103,8 +103,12 @@ module ROCrate
103
103
  entry == ROCrate::Metadata::IDENTIFIER_1_0 }
104
104
 
105
105
  if metadata_file
106
- entities = entities_from_metadata(::File.read(::File.join(source, metadata_file)))
107
- build_crate(entities, source)
106
+ metadata_json = ::File.read(::File.join(source, metadata_file))
107
+ metadata = JSON.parse(metadata_json)
108
+ entities = entities_from_metadata(metadata)
109
+ context = metadata['@context']
110
+
111
+ build_crate(entities, source, context: context)
108
112
  else
109
113
  raise 'No metadata found!'
110
114
  end
@@ -113,10 +117,9 @@ module ROCrate
113
117
  ##
114
118
  # Extracts all the entities from the @graph of the RO-Crate Metadata.
115
119
  #
116
- # @param metadata_json [String] A string containing the metadata JSON.
120
+ # @param metadata [Hash] A Hash containing the parsed metadata JSON.
117
121
  # @return [Hash{String => Hash}] A Hash of all the entities, mapped by their @id.
118
- def self.entities_from_metadata(metadata_json)
119
- metadata = JSON.parse(metadata_json)
122
+ def self.entities_from_metadata(metadata)
120
123
  graph = metadata['@graph']
121
124
 
122
125
  if graph
@@ -129,6 +132,7 @@ module ROCrate
129
132
  # Do some normalization...
130
133
  entities[ROCrate::Metadata::IDENTIFIER] = extract_metadata_entity(entities)
131
134
  raise "No metadata entity found in @graph!" unless entities[ROCrate::Metadata::IDENTIFIER]
135
+ entities[ROCrate::Preview::IDENTIFIER] = extract_preview_entity(entities)
132
136
  entities[ROCrate::Crate::IDENTIFIER] = extract_root_entity(entities)
133
137
  raise "No root entity (with @id: #{entities[ROCrate::Metadata::IDENTIFIER].dig('about', '@id')}) found in @graph!" unless entities[ROCrate::Crate::IDENTIFIER]
134
138
 
@@ -139,25 +143,50 @@ module ROCrate
139
143
  end
140
144
 
141
145
  ##
142
- # Create a crate from the given set of entities.
146
+ # Create and populate crate from the given set of entities.
143
147
  #
144
148
  # @param entity_hash [Hash{String => Hash}] A Hash containing all the entities in the @graph, mapped by their @id.
145
149
  # @param source [String, ::File, Pathname] The location of the RO-Crate being read.
150
+ # @param crate_class [Class] The class to use to instantiate the crate,
151
+ # useful if you have created a subclass of ROCrate::Crate that you want to use. (defaults to ROCrate::Crate).
152
+ # @param context [nil, String, Array, Hash] A custom JSON-LD @context (parsed), or nil to use default.
146
153
  # @return [Crate] The RO-Crate.
147
- def self.build_crate(entity_hash, source)
148
- ROCrate::Crate.new.tap do |crate|
154
+ def self.build_crate(entity_hash, source, crate_class: ROCrate::Crate, context:)
155
+ crate = initialize_crate(entity_hash, source, crate_class: crate_class, context: context)
156
+
157
+ extract_data_entities(crate, source, entity_hash).each do |entity|
158
+ crate.add_data_entity(entity)
159
+ end
160
+
161
+ # The remaining entities in the hash must be contextual.
162
+ extract_contextual_entities(crate, entity_hash).each do |entity|
163
+ crate.add_contextual_entity(entity)
164
+ end
165
+
166
+ crate
167
+ end
168
+
169
+ ##
170
+ # Initialize a crate from the given set of entities.
171
+ #
172
+ # @param entity_hash [Hash{String => Hash}] A Hash containing all the entities in the @graph, mapped by their @id.
173
+ # @param source [String, ::File, Pathname] The location of the RO-Crate being read.
174
+ # @param crate_class [Class] The class to use to instantiate the crate,
175
+ # useful if you have created a subclass of ROCrate::Crate that you want to use. (defaults to ROCrate::Crate).
176
+ # @param context [nil, String, Array, Hash] A custom JSON-LD @context (parsed), or nil to use default.
177
+ # @return [Crate] The RO-Crate.
178
+ def self.initialize_crate(entity_hash, source, crate_class: ROCrate::Crate, context:)
179
+ crate_class.new.tap do |crate|
149
180
  crate.properties = entity_hash.delete(ROCrate::Crate::IDENTIFIER)
150
181
  crate.metadata.properties = entity_hash.delete(ROCrate::Metadata::IDENTIFIER)
182
+ crate.metadata.context = context
151
183
  preview_properties = entity_hash.delete(ROCrate::Preview::IDENTIFIER)
152
- crate.preview.properties = preview_properties if preview_properties
153
- crate.add_all(source, false)
154
- extract_data_entities(crate, source, entity_hash).each do |entity|
155
- crate.add_data_entity(entity)
156
- end
157
- # The remaining entities in the hash must be contextual.
158
- extract_contextual_entities(crate, entity_hash).each do |entity|
159
- crate.add_contextual_entity(entity)
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 || {})
160
188
  end
189
+ crate.add_all(source, false)
161
190
  end
162
191
  end
163
192
 
@@ -216,10 +245,10 @@ module ROCrate
216
245
  fullpath = ::File.join(source, i)
217
246
  path = Pathname.new(fullpath) if ::File.exist?(fullpath)
218
247
  end
219
- unless path
220
- warn "Missing file/directory: #{id}, skipping..."
221
- return nil
222
- end
248
+ # unless path
249
+ # warn "Missing file/directory: #{id}, skipping..."
250
+ # return nil
251
+ # end
223
252
  end
224
253
 
225
254
  entity_class.new(crate, path, decoded_id, entity_props)
@@ -229,11 +258,13 @@ module ROCrate
229
258
  ##
230
259
  # Extract the metadata entity from the entity hash, according to the rules defined here:
231
260
  # https://www.researchobject.org/ro-crate/1.1/root-data-entity.html#finding-the-root-data-entity
232
- # @return [Hash{String => Hash}] A Hash containing (hopefully) one value, the metadata entity's properties,
233
- # mapped by its @id.
261
+ # @return [nil, Hash{String => Hash}] A Hash containing (hopefully) one value, the metadata entity's properties
262
+ # mapped by its @id, or nil if nothing is found.
234
263
  def self.extract_metadata_entity(entities)
235
264
  key = entities.detect do |_, props|
236
- props.dig('conformsTo', '@id')&.start_with?(ROCrate::Metadata::RO_CRATE_BASE)
265
+ conforms = props['conformsTo']
266
+ conforms = [conforms] unless conforms.is_a?(Array)
267
+ conforms.compact.any? { |c| c['@id']&.start_with?(ROCrate::Metadata::RO_CRATE_BASE) }
237
268
  end&.first
238
269
 
239
270
  return entities.delete(key) if key
@@ -245,6 +276,13 @@ module ROCrate
245
276
  entities.delete(ROCrate::Metadata::IDENTIFIER_1_0))
246
277
  end
247
278
 
279
+ ##
280
+ # Extract the ro-crate-preview entity from the entity hash.
281
+ # @return [Hash{String => Hash}] A Hash containing the preview entity's properties mapped by its @id, or nil if nothing is found.
282
+ def self.extract_preview_entity(entities)
283
+ entities.delete("./#{ROCrate::Preview::IDENTIFIER}") || entities.delete(ROCrate::Preview::IDENTIFIER)
284
+ end
285
+
248
286
  ##
249
287
  # Extract the root entity from the entity hash, according to the rules defined here:
250
288
  # https://www.researchobject.org/ro-crate/1.1/root-data-entity.html#finding-the-root-data-entity
@@ -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.entries.each do |path, entry|
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.write(temp)
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.entries.each do |path, entry|
41
+ @crate.payload.each do |path, entry|
42
42
  next if entry.directory?
43
- zip.get_output_stream(path) { |s| entry.write(s) }
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,10 +10,11 @@ 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
+ require 'ro_crate/model/preview_generator'
17
18
  require 'ro_crate/model/preview'
18
19
  require 'ro_crate/model/crate'
19
20
  require 'ro_crate/model/contextual_entity'
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.10'
3
+ s.version = '0.4.14'
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,11 +8,11 @@ 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', '~> 2.7.0'
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'
15
- s.add_development_dependency 'simplecov', '~> 0.16.1'
15
+ s.add_development_dependency 'simplecov', '~> 0.21.2'
16
16
  s.add_development_dependency 'yard', '~> 0.9.25'
17
17
  s.add_development_dependency 'webmock', '~> 3.8.3'
18
18
  end
data/test/crate_test.rb CHANGED
@@ -164,6 +164,18 @@ class CrateTest < Test::Unit::TestCase
164
164
  assert_equal 2, crate1.contextual_entities.first.properties['cats']
165
165
  end
166
166
 
167
+ test 'sharing entities' do
168
+ crate = ROCrate::Crate.new
169
+ info = crate.add_file(fixture_file('info.txt'),'the_info.txt')
170
+ bob = crate.add_person('bob', name: 'Bob Jones')
171
+ crate.author = bob
172
+ info.author = bob
173
+
174
+ assert_equal [bob], crate.contextual_entities
175
+ assert_equal bob, info.author
176
+ assert_equal bob, crate.author
177
+ end
178
+
167
179
  test 'external files' do
168
180
  crate = ROCrate::Crate.new
169
181
  local = crate.add_file(fixture_file('info.txt'))
@@ -203,7 +215,7 @@ class CrateTest < Test::Unit::TestCase
203
215
  crate = ROCrate::Crate.new
204
216
  entities = crate.add_all(fixture_file('directory').path, include_hidden: true)
205
217
 
206
- paths = crate.entries.keys
218
+ paths = crate.payload.keys
207
219
  assert_equal 11, paths.length
208
220
  assert_includes paths, 'data'
209
221
  assert_includes paths, 'root.txt'
@@ -238,7 +250,7 @@ class CrateTest < Test::Unit::TestCase
238
250
 
239
251
  assert_empty entities
240
252
 
241
- paths = crate.entries.keys
253
+ paths = crate.payload.keys
242
254
  assert_equal 11, paths.length
243
255
  assert_includes paths, 'data'
244
256
  assert_includes paths, 'root.txt'
@@ -266,7 +278,7 @@ class CrateTest < Test::Unit::TestCase
266
278
  crate = ROCrate::Crate.new
267
279
  entities = crate.add_all(fixture_file('directory').path)
268
280
 
269
- paths = crate.entries.keys
281
+ paths = crate.payload.keys
270
282
  assert_equal 8, paths.length
271
283
  assert_includes paths, 'data'
272
284
  assert_includes paths, 'root.txt'
@@ -293,4 +305,47 @@ class CrateTest < Test::Unit::TestCase
293
305
 
294
306
  assert_equal "5678\n", crate.dereference('data/info.txt').source.read
295
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
330
+
331
+ test 'legacy entries method still returns same result as payload' do
332
+ crate = ROCrate::Crate.new
333
+ crate.add_all(fixture_file('directory').path)
334
+
335
+ paths = crate.entries.keys
336
+ assert_equal 8, paths.length
337
+ assert_includes paths, 'data'
338
+ assert_includes paths, 'root.txt'
339
+ assert_includes paths, 'info.txt'
340
+ assert_includes paths, 'data/binary.jpg'
341
+ assert_includes paths, 'data/info.txt'
342
+ assert_includes paths, 'data/nested.txt'
343
+ assert_not_includes paths, '.dotfile'
344
+ assert_not_includes paths, '.dir'
345
+ assert_not_includes paths, '.dir/test.txt'
346
+ assert_includes paths, 'ro-crate-metadata.json'
347
+ assert_includes paths, 'ro-crate-preview.html'
348
+
349
+ assert_equal crate.payload.keys, crate.entries.keys
350
+ end
296
351
  end
@@ -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
- entries = crate.entries
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')), entries['directory/info.txt'].path
11
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')), entries['directory/root.txt'].path
12
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')), entries['directory/data'].path
13
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')), entries['directory/data/info.txt'].path
14
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')), entries['directory/data/nested.txt'].path
15
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')), entries['directory/data/binary.jpg'].path
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
- entries = crate.entries
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')), entries['directory/info.txt'].path
25
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')), entries['directory/root.txt'].path
26
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')), entries['directory/data'].path
27
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')), entries['directory/data/info.txt'].path
28
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')), entries['directory/data/nested.txt'].path
29
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')), entries['directory/data/binary.jpg'].path
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
- entries = crate.entries
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')), entries['fish/info.txt'].path
39
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/root.txt')), entries['fish/root.txt'].path
40
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data')), entries['fish/data'].path
41
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/info.txt')), entries['fish/data/info.txt'].path
42
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/nested.txt')), entries['fish/data/nested.txt'].path
43
- assert_equal ::File.expand_path(::File.join(base_path, 'directory/data/binary.jpg')), entries['fish/data/binary.jpg'].path
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
@@ -38,11 +38,11 @@ class EntityTest < Test::Unit::TestCase
38
38
  test 'fetch appropriate class for type' do
39
39
  assert_equal ROCrate::File, ROCrate::DataEntity.specialize({ '@type' => 'File' })
40
40
  assert_equal ROCrate::File, ROCrate::DataEntity.specialize({ '@type' => ['File', 'Image'] })
41
- assert_equal ROCrate::File, ROCrate::DataEntity.specialize({ '@type' => 'SoftwareSourceCode' })
42
- assert_equal ROCrate::File, ROCrate::DataEntity.specialize({ '@type' => 'anything that isnt a directory' })
41
+ assert_equal ROCrate::DataEntity, ROCrate::DataEntity.specialize({ '@type' => 'SoftwareSourceCode' })
42
+ assert_equal ROCrate::DataEntity, ROCrate::DataEntity.specialize({ '@type' => 'anything that isnt a directory' })
43
43
  assert_equal ROCrate::Directory, ROCrate::DataEntity.specialize({ '@type' => 'Dataset' })
44
44
  assert_equal ROCrate::Directory, ROCrate::DataEntity.specialize({ '@type' => ['Dataset', 'Image'] })
45
- assert_equal ROCrate::File, ROCrate::DataEntity.specialize({ '@type' => 'Person' })
45
+ assert_equal ROCrate::DataEntity, ROCrate::DataEntity.specialize({ '@type' => 'Person' })
46
46
  assert_equal ROCrate::File, ROCrate::DataEntity.specialize({ '@type' => ['File', 'Image'], '@id' => 'http://www.external.com' })
47
47
 
48
48
  assert_equal ROCrate::Person, ROCrate::ContextualEntity.specialize({ '@type' => 'Person' })
@@ -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
@@ -0,0 +1 @@
1
+ No, I am nested!
@@ -0,0 +1,33 @@
1
+ {
2
+ "@context": [
3
+ "https://w3id.org/ro/crate/1.1/context",
4
+ {
5
+ "@vocab": "http://schema.org/"
6
+ },
7
+ {
8
+ "@base": null
9
+ }
10
+ ],
11
+ "@graph": [
12
+ {
13
+ "@id": "#collection",
14
+ "@type": "RepositoryCollection",
15
+ "name": "Test collection"
16
+ },
17
+ {
18
+ "@id": "./",
19
+ "@type": "Dataset",
20
+ "hasFile": [],
21
+ "hasPart": [{"@id": "#collection"}],
22
+ "name": "testing hasPart"
23
+ },
24
+ {
25
+ "@id": "ro-crate-metadata.json",
26
+ "@type": "CreativeWork",
27
+ "about": {
28
+ "@id": "./"
29
+ },
30
+ "identifier": "ro-crate-metadata.json"
31
+ }
32
+ ]
33
+ }