lib/ro_crate/reader.rb in ro-crate-0.5.1 vs lib/ro_crate/reader.rb in ro-crate-0.5.2

- old
+ new

@@ -7,11 +7,10 @@ # # @param source [String, ::File, Pathname, #read] The location of the zip or directory, or an IO-like object containing a zip. # @param target_dir [String, ::File, Pathname] The target directory where the crate should be unzipped (if its a Zip file). # @return [Crate] The RO-Crate. def self.read(source, target_dir: Dir.mktmpdir) - raise "Not a directory!" unless ::File.directory?(target_dir) begin is_dir = ::File.directory?(source) rescue TypeError is_dir = false end @@ -80,10 +79,12 @@ # # @param source [String, ::File, Pathname, #read] The location of the zip file, or an IO-like object. # @param target_dir [String, ::File, Pathname] The target directory where the crate should be unzipped. # @return [Crate] The RO-Crate. def self.read_zip(source, target_dir: Dir.mktmpdir) + raise ROCrate::ReadException, "Target is not a directory!" unless ::File.directory?(target_dir) + unzip_to(source, target_dir) # Traverse the unzipped directory to try and find the crate's root root_dir = detect_root_directory(target_dir) @@ -94,25 +95,30 @@ # Reads an RO-Crate from a directory. # # @param source [String, ::File, Pathname] The location of the directory. # @return [Crate] The RO-Crate. def self.read_directory(source) - raise "Not a directory!" unless ::File.directory?(source) + raise ROCrate::ReadException, "Source is not a directory!" unless ::File.directory?(source) source = ::File.expand_path(source) metadata_file = Dir.entries(source).detect { |entry| entry == ROCrate::Metadata::IDENTIFIER || entry == ROCrate::Metadata::IDENTIFIER_1_0 } if metadata_file metadata_json = ::File.read(::File.join(source, metadata_file)) - metadata = JSON.parse(metadata_json) + begin + metadata = JSON.parse(metadata_json) + rescue JSON::ParserError => e + raise ROCrate::ReadException.new("Error parsing metadata", e) + end + entities = entities_from_metadata(metadata) context = metadata['@context'] build_crate(entities, source, context: context) else - raise 'No metadata found!' + raise ROCrate::ReadException, "No metadata found!" end end ## # Extracts all the entities from the @graph of the RO-Crate Metadata. @@ -129,18 +135,18 @@ entities[entity['@id']] = entity end # Do some normalization... entities[ROCrate::Metadata::IDENTIFIER] = extract_metadata_entity(entities) - raise "No metadata entity found in @graph!" unless entities[ROCrate::Metadata::IDENTIFIER] + raise ROCrate::ReadException, "No metadata entity found in @graph!" unless entities[ROCrate::Metadata::IDENTIFIER] entities[ROCrate::Preview::IDENTIFIER] = extract_preview_entity(entities) entities[ROCrate::Crate::IDENTIFIER] = extract_root_entity(entities) - raise "No root entity (with @id: #{entities[ROCrate::Metadata::IDENTIFIER].dig('about', '@id')}) found in @graph!" unless entities[ROCrate::Crate::IDENTIFIER] + raise ROCrate::ReadException, "No root entity (with @id: #{entities[ROCrate::Metadata::IDENTIFIER].dig('about', '@id')}) found in @graph!" unless entities[ROCrate::Crate::IDENTIFIER] entities else - raise "No @graph found in metadata!" + raise ROCrate::ReadException, "No @graph found in metadata!" end end ## # Create and populate crate from the given set of entities. @@ -196,11 +202,11 @@ # @param crate [Crate] The RO-Crate being read. # @param source [String, ::File, Pathname] The location of the RO-Crate being read. # @param entity_hash [Hash] A Hash containing all the entities in the @graph, mapped by their @id. # @return [Array<ROCrate::File, ROCrate::Directory>] The extracted DataEntity objects. def self.extract_data_entities(crate, source, entity_hash) - crate.raw_properties['hasPart'].map do |ref| + (crate.raw_properties['hasPart'] || []).map do |ref| entity_props = entity_hash.delete(ref['@id']) next unless entity_props entity_class = ROCrate::DataEntity.specialize(entity_props) entity = create_data_entity(crate, entity_class, source, entity_props) next if entity.nil? @@ -232,25 +238,25 @@ # @param entity_props [Hash] A Hash containing the entity's properties, including its @id. # @return [ROCrate::File, ROCrate::Directory, nil] The DataEntity object, # or nil if it referenced a local file that wasn't found. def self.create_data_entity(crate, entity_class, source, entity_props) id = entity_props.delete('@id') + raise ROCrate::ReadException, "Data Entity missing '@id': #{entity_props.inspect}" unless id decoded_id = URI.decode_www_form_component(id) path = nil uri = URI(id) rescue nil if uri&.absolute? path = uri decoded_id = nil - else + elsif !id.start_with?('#') [id, decoded_id].each do |i| fullpath = ::File.join(source, i) path = Pathname.new(fullpath) if ::File.exist?(fullpath) end - # unless path - # warn "Missing file/directory: #{id}, skipping..." - # return nil - # end + if path.nil? + raise ROCrate::ReadException, "Local Data Entity not found in crate: #{id}" + end end entity_class.new(crate, path, decoded_id, entity_props) end @@ -288,10 +294,10 @@ # https://www.researchobject.org/ro-crate/1.1/root-data-entity.html#finding-the-root-data-entity # @return [Hash{String => Hash}] A Hash containing (hopefully) one value, the root entity's properties, # mapped by its @id. def self.extract_root_entity(entities) root_id = entities[ROCrate::Metadata::IDENTIFIER].dig('about', '@id') - raise "Metadata entity does not reference any root entity" unless root_id + raise ROCrate::ReadException, "Metadata entity does not reference any root entity" unless root_id entities.delete(root_id) end ## # Finds an RO-Crate's root directory (where `ro-crate-metdata.json` is located) within a given directory.