ro-crate 0.4.8 → 0.4.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) 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/contextual_entity.rb +2 -14
  5. data/lib/ro_crate/model/crate.rb +52 -6
  6. data/lib/ro_crate/model/data_entity.rb +6 -5
  7. data/lib/ro_crate/model/directory.rb +3 -5
  8. data/lib/ro_crate/model/entity.rb +65 -6
  9. data/lib/ro_crate/model/entry.rb +2 -2
  10. data/lib/ro_crate/model/file.rb +2 -4
  11. data/lib/ro_crate/model/metadata.rb +9 -1
  12. data/lib/ro_crate/model/organization.rb +1 -1
  13. data/lib/ro_crate/model/preview.rb +3 -15
  14. data/lib/ro_crate/model/preview_generator.rb +40 -0
  15. data/lib/ro_crate/model/remote_entry.rb +1 -12
  16. data/lib/ro_crate/reader.rb +77 -20
  17. data/lib/ro_crate/writer.rb +4 -4
  18. data/lib/ro_crate.rb +2 -1
  19. data/ro_crate.gemspec +3 -3
  20. data/test/crate_test.rb +37 -3
  21. data/test/directory_test.rb +21 -21
  22. data/test/entity_test.rb +135 -0
  23. data/test/fixtures/biobb_hpc_workflows-condapack.zip +0 -0
  24. data/test/fixtures/conflicting_data_directory/info.txt +1 -0
  25. data/test/fixtures/conflicting_data_directory/nested.txt +1 -0
  26. data/test/fixtures/nested_directory.zip +0 -0
  27. data/test/fixtures/ro-crate-galaxy-sortchangecase/LICENSE +176 -0
  28. data/test/fixtures/ro-crate-galaxy-sortchangecase/README.md +6 -0
  29. data/test/fixtures/ro-crate-galaxy-sortchangecase/ro-crate-metadata.json +133 -0
  30. data/test/fixtures/ro-crate-galaxy-sortchangecase/sort-and-change-case.ga +118 -0
  31. data/test/fixtures/ro-crate-galaxy-sortchangecase/test/test1/input.bed +3 -0
  32. data/test/fixtures/ro-crate-galaxy-sortchangecase/test/test1/output_exp.bed +3 -0
  33. data/test/fixtures/ro-crate-galaxy-sortchangecase/test/test1/sort-and-change-case-test.yml +8 -0
  34. data/test/fixtures/sparse_directory_crate/ro-crate-preview.html +60 -59
  35. data/test/reader_test.rb +83 -40
  36. data/test/test_helper.rb +5 -1
  37. data/test/writer_test.rb +59 -2
  38. metadata +26 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7003f224766d4445498db499257b246ea2dbfb842f0f5e0c8d04471ccea4d299
4
- data.tar.gz: f62e7c90a54f26aa6b18cf98310c998ada8c6836975c997f551b2b1ab7d85e29
3
+ metadata.gz: d9f081110d1e5b5e94674cb53d07262fbce50ec3f63437d5c4c1ecda8b04f835
4
+ data.tar.gz: 0cd710a9cd86063e706fd9cf338e5fdf55f5bc6993f306af4f50d6f9191a0d87
5
5
  SHA512:
6
- metadata.gz: f63fa2c3b9d88ec76f20e8986986d81878dc8bbb82bedd4c2c862ca6fca7f021ad84309678abb12ec6de7fb498a87711409f3f64809aba4c09c5b00676c60aac
7
- data.tar.gz: 16616f82e91760e9a6ff3eff1199c327143401086b0f9c577908e28d023e27828b7e36eeffc65333ec3aa440697b8f18cddd9406185f116ccc59ff238c98b1e9
6
+ metadata.gz: 8ce573fbd259108edbb528e24f0d9647cfee4454378d57a4b3392aaeb0dea80385b2828221a0979caa4733be0e2108a4a535725846f576c9ac97866f46911e47
7
+ data.tar.gz: ffc7aafbdccb3f4897ad2acae726714d7a313f02fd87e5c96173da2b12860dfa1318b9b323dde751e3ac10c9a66cf29effea383037bc4622eaf5113dd01bd426
data/Gemfile.lock CHANGED
@@ -1,31 +1,31 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ro-crate (0.4.8)
5
- addressable (~> 2.7.0)
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.7.0)
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
- docile (1.3.1)
15
+ docile (1.3.5)
16
16
  hashdiff (1.0.1)
17
- json (2.3.1)
18
- power_assert (0.4.1)
19
- public_suffix (4.0.3)
17
+ power_assert (1.1.3)
18
+ public_suffix (4.0.6)
20
19
  rake (13.0.0)
21
20
  rubyzip (2.0.0)
22
21
  safe_yaml (1.0.5)
23
- simplecov (0.16.1)
22
+ simplecov (0.21.2)
24
23
  docile (~> 1.1)
25
- json (>= 1.8, < 3)
26
- simplecov-html (~> 0.10.0)
27
- simplecov-html (0.10.2)
28
- test-unit (3.2.3)
24
+ simplecov-html (~> 0.11)
25
+ simplecov_json_formatter (~> 0.1)
26
+ simplecov-html (0.12.3)
27
+ simplecov_json_formatter (0.1.2)
28
+ test-unit (3.2.9)
29
29
  power_assert
30
30
  webmock (3.8.3)
31
31
  addressable (>= 2.3.6)
@@ -39,7 +39,7 @@ PLATFORMS
39
39
  DEPENDENCIES
40
40
  rake (~> 13.0.0)
41
41
  ro-crate!
42
- simplecov (~> 0.16.1)
42
+ simplecov (~> 0.21.2)
43
43
  test-unit (~> 3.2.3)
44
44
  webmock (~> 3.8.3)
45
45
  yard (~> 0.9.25)
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/
@@ -17,6 +19,43 @@ and run `bundle install`.
17
19
 
18
20
  ## Usage
19
21
 
22
+ This gem consists a hierarchy of classes to model RO-Crate "entities": the crate itself, data entities
23
+ (files and directory) and contextual entities (with a limited set of specializations, such as `ROCrate::Person`).
24
+ They are all descendents of the `ROCrate::Entity` class, with the `ROCrate::Crate` class representing the crate itself.
25
+
26
+ The `ROCrate::Reader` class handles reading of RO-Crates into the above model, from a Zip file or directory.
27
+
28
+ The `ROCrate::Writer` class can write out an `ROCrate::Crate` instance into a Zip file or directory.
29
+
30
+ **Note:** for performance reasons, the gem is currently not linked-data aware and will allow you to set properties that
31
+ are not semantically valid.
32
+
33
+ ### Entities
34
+ Entities correspond to entries in the `@graph` of the RO-Crate's metadata JSON-LD file. Each entity class is
35
+ basically a wrapper around a set of JSON properties, with some convenience methods for getting/setting some
36
+ commonly used properties (`crate.name = "My first crate"`).
37
+
38
+ These convenience getter/setter methods will automatically handle turning objects into references and adding them to the
39
+ `@graph` if necessary.
40
+
41
+ ##### Getting/Setting Arbitrary Properties of Entities
42
+ As well as using the pre-defined getter/setter methods, you can get/set arbitrary properties like so.
43
+
44
+ To set the "creativeWorkStatus" property of the RO-Crate itself to a string literal:
45
+ ```ruby
46
+ crate['creativeWorkStatus'] = 'work-in-progress'
47
+ ```
48
+
49
+ If you want to reference other entities in the crate, you can get a JSON-LD reference from an entity object by using the `reference` method:
50
+ ```ruby
51
+ joe = crate.add_person('joe', { name: 'Joe Bloggs' }) # Add the entity to the @graph
52
+ crate['copyrightHolder'] = joe.reference # Reference the entity from the "copyrightHolder" property
53
+ ```
54
+ and to resolve those references back to the object, use the `dereference` method:
55
+ ```ruby
56
+ joe = crate['copyrightHolder'].dereference
57
+ ```
58
+
20
59
  ### Documentation
21
60
 
22
61
  [Click here for API documentation](https://www.researchobject.org/ro-crate-ruby/).
@@ -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.format_id(id)
6
+ def self.format_local_id(id)
7
7
  i = super
8
- begin
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
  ##
@@ -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
@@ -20,6 +25,15 @@ module ROCrate
20
25
  super(self, nil, id, properties)
21
26
  end
22
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
+
23
37
  ##
24
38
  # Create a new file and add it to the crate.
25
39
  #
@@ -163,6 +177,15 @@ module ROCrate
163
177
  @preview ||= ROCrate::Preview.new(self)
164
178
  end
165
179
 
180
+ ##
181
+ # Set the RO-Crate preview file
182
+ # @param preview [Preview] the preview to set.
183
+ #
184
+ # @return [Preview]
185
+ def preview=(preview)
186
+ @preview = claim(preview)
187
+ end
188
+
166
189
  ##
167
190
  # All the entities within the crate. Includes contextual entities, data entities, the crate itself and its metadata file.
168
191
  #
@@ -215,21 +238,21 @@ module ROCrate
215
238
  entity.class.new(crate, entity.id, entity.raw_properties)
216
239
  end
217
240
 
218
- alias_method :own_entries, :entries
241
+ alias_method :own_payload, :payload
219
242
  ##
220
- # # The RO-Crate's "payload" of the crate - a map of all the files/directories contained in the RO-Crate, where the
221
- # key is the destination path within the crate and the value is an Entry where the source data can be read.
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.
222
245
  #
223
246
  # @return [Hash{String => Entry}>]
224
- def entries
247
+ def payload
225
248
  # Gather a map of entries, starting from the crate itself, then any directory data entities, then finally any
226
249
  # file data entities. This ensures in the case of a conflict, the more "specific" data entities take priority.
227
- entries = own_entries
250
+ entries = own_payload
228
251
  non_self_entities = default_entities.reject { |e| e == self }
229
252
  sorted_entities = (non_self_entities | data_entities).sort_by { |e| e.is_a?(ROCrate::Directory) ? 0 : 1 }
230
253
 
231
254
  sorted_entities.each do |entity|
232
- entity.entries.each do |path, entry|
255
+ entity.payload.each do |path, entry|
233
256
  entries[path] = entry
234
257
  end
235
258
  end
@@ -241,6 +264,29 @@ module ROCrate
241
264
  binding
242
265
  end
243
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
+
244
290
  private
245
291
 
246
292
  def full_entry_path(relative_path)
@@ -3,7 +3,9 @@ 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.format_id(id)
6
+ properties(%w[name contentSize dateModified encodingFormat identifier sameAs author])
7
+
8
+ def self.format_local_id(id)
7
9
  super.chomp('/')
8
10
  end
9
11
 
@@ -13,8 +15,6 @@ module ROCrate
13
15
  # @return [Class]
14
16
  def self.specialize(props)
15
17
  type = props['@type']
16
- id = props['@id']
17
- abs = URI(id)&.absolute? rescue false
18
18
  type = [type] unless type.is_a?(Array)
19
19
  if type.include?('Dataset')
20
20
  ROCrate::Directory
@@ -24,12 +24,13 @@ module ROCrate
24
24
  end
25
25
 
26
26
  ##
27
- # A map of all the files/directories associated with this DataEntity.
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 entries
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.
@@ -2,9 +2,7 @@ module ROCrate
2
2
  ##
3
3
  # A data entity that represents a directory of potentially many files and subdirectories (or none).
4
4
  class Directory < DataEntity
5
- properties(%w[name contentSize dateModified encodingFormat identifier sameAs])
6
-
7
- def self.format_id(id)
5
+ def self.format_local_id(id)
8
6
  super + '/'
9
7
  end
10
8
 
@@ -30,11 +28,11 @@ module ROCrate
30
28
  end
31
29
 
32
30
  ##
33
- # The "payload" of this directory - a map of all the files/directories, where the key is the destination path
31
+ # The payload of this directory - a map of all the files/directories, where the key is the destination path
34
32
  # within the crate and the value is an Entry where the source data can be read.
35
33
  #
36
34
  # @return [Hash{String => Entry}>]
37
- def entries
35
+ def payload
38
36
  entries = {}
39
37
  entries[filepath.chomp('/')] = @entry if @entry
40
38
 
@@ -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
- def self.format_id(id)
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
 
@@ -107,16 +129,26 @@ module ROCrate
107
129
  # @param id [String] The ID to query.
108
130
  # @return [Entity, nil]
109
131
  def dereference(id)
110
- crate.entities.detect { |e| e.canonical_id == crate.resolve_id(id) } if id
132
+ crate.dereference(id)
111
133
  end
112
-
113
134
  alias_method :get, :dereference
114
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
+
115
146
  def id
116
147
  @properties['@id']
117
148
  end
118
149
 
119
150
  def id=(id)
151
+ @canonical_id = nil
120
152
  @properties['@id'] = self.class.format_id(id)
121
153
  end
122
154
 
@@ -168,13 +200,13 @@ module ROCrate
168
200
  #
169
201
  # @return [Addressable::URI]
170
202
  def canonical_id
171
- crate.resolve_id(id)
203
+ @canonical_id ||= crate.resolve_id(id)
172
204
  end
173
205
 
174
206
  ##
175
207
  # Is this entity local to the crate or an external reference?
176
208
  #
177
- # @return [boolean]
209
+ # @return [Boolean]
178
210
  def external?
179
211
  crate.canonical_id.host != canonical_id.host
180
212
  end
@@ -204,6 +236,33 @@ module ROCrate
204
236
  @properties.has_type?(type)
205
237
  end
206
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
+
207
266
  private
208
267
 
209
268
  def default_properties
@@ -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 write(dest)
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))
@@ -2,14 +2,12 @@ module ROCrate
2
2
  ##
3
3
  # A data entity that represents a single file.
4
4
  class File < DataEntity
5
- properties(%w[name contentSize dateModified encodingFormat identifier sameAs])
6
-
7
5
  ##
8
6
  # Create a new ROCrate::File. PLEASE NOTE, the new file will not be added to the crate. To do this, call
9
7
  # Crate#add_data_entity, or just use Crate#add_file.
10
8
  #
11
9
  # @param crate [Crate] The RO-Crate that owns this file.
12
- # @param source [String, Pathname, ::File, #read, URI, nil] The source on the disk (or on the internet if a URI) where this file will be read.
10
+ # @param source [String, Pathname, ::File, URI, nil, #read] The source on the disk (or on the internet if a URI) where this file will be read.
13
11
  # @param crate_path [String] The relative path within the RO-Crate where this file will be written.
14
12
  # @param properties [Hash{String => Object}] A hash of JSON-LD properties to associate with this file.
15
13
  def initialize(crate, source, crate_path = nil, properties = {})
@@ -58,7 +56,7 @@ module ROCrate
58
56
  # (for compatibility with Directory#entries)
59
57
  #
60
58
  # @return [Hash{String => Entry}>] The key is the location within the crate, and the value is an Entry.
61
- def entries
59
+ def payload
62
60
  remote? ? {} : { filepath => source }
63
61
  end
64
62
 
@@ -17,7 +17,15 @@ module ROCrate
17
17
  # @return [String] The rendered JSON-LD as a "prettified" string.
18
18
  def generate
19
19
  graph = crate.entities.map(&:properties).reject(&:empty?)
20
- JSON.pretty_generate('@context' => CONTEXT, '@graph' => graph)
20
+ JSON.pretty_generate('@context' => context, '@graph' => graph)
21
+ end
22
+
23
+ def context
24
+ @context || CONTEXT
25
+ end
26
+
27
+ def context= c
28
+ @context = c
21
29
  end
22
30
 
23
31
  private
@@ -2,7 +2,7 @@ module ROCrate
2
2
  ##
3
3
  # A contextual entity that represents an organization.
4
4
  class Organization < ContextualEntity
5
- properties(['name'])
5
+ properties(%w[name])
6
6
 
7
7
  private
8
8
 
@@ -12,26 +12,14 @@ module ROCrate
12
12
  # @return [String]
13
13
  attr_accessor :template
14
14
 
15
- def initialize(crate, properties = {})
15
+ def initialize(crate, source = nil, properties = {})
16
+ source ||= PreviewGenerator.new(self)
16
17
  @template = nil
17
- super(crate, nil, IDENTIFIER, properties)
18
- end
19
-
20
- ##
21
- # Generate the crate's `ro-crate-preview.html`.
22
- # @return [String] The rendered HTML as a string.
23
- def generate
24
- b = crate.get_binding
25
- renderer = ERB.new(template || ::File.read(DEFAULT_TEMPLATE))
26
- renderer.result(b)
18
+ super(crate, source, IDENTIFIER, properties)
27
19
  end
28
20
 
29
21
  private
30
22
 
31
- def source
32
- Entry.new(StringIO.new(generate))
33
- end
34
-
35
23
  def default_properties
36
24
  {
37
25
  '@id' => IDENTIFIER,
@@ -0,0 +1,40 @@
1
+ require 'erb'
2
+
3
+ module ROCrate
4
+ ##
5
+ # A class to handle generation of an RO-Crate's preview HTML in an IO-like way (to fit into an Entry).
6
+ class PreviewGenerator
7
+ ##
8
+ # @param preview [Preview] The RO-Crate preview object.
9
+ def initialize(preview)
10
+ @preview = preview
11
+ end
12
+
13
+ def read(*args)
14
+ io.read(*args)
15
+ end
16
+
17
+ ##
18
+ # Generate the crate's `ro-crate-preview.html`.
19
+ # @return [String] The rendered HTML as a string.
20
+ def generate
21
+ b = crate.get_binding
22
+ renderer = ERB.new(template)
23
+ renderer.result(b)
24
+ end
25
+
26
+ def template
27
+ @preview.template || ::File.read(Preview::DEFAULT_TEMPLATE)
28
+ end
29
+
30
+ def crate
31
+ @preview.crate
32
+ end
33
+
34
+ private
35
+
36
+ def io
37
+ @io ||= StringIO.new(generate)
38
+ end
39
+ end
40
+ end
@@ -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
  #