lib/briard/readers/datacite_reader.rb in briard-2.4.2 vs lib/briard/readers/datacite_reader.rb in briard-2.6.0

- old
+ new

@@ -2,340 +2,376 @@ module Briard module Readers module DataciteReader def get_datacite(id: nil, **options) - return { "string" => nil, "state" => "not_found" } unless id.present? + return { 'string' => nil, 'state' => 'not_found' } unless id.present? api_url = doi_api_url(id, options) response = Maremma.get(api_url) - attributes = response.body.dig("data", "attributes") - return { "string" => nil, "state" => "not_found" } unless attributes.present? + attributes = response.body.dig('data', 'attributes') + return { 'string' => nil, 'state' => 'not_found' } unless attributes.present? string = attributes.fetch('xml', nil) string = Base64.decode64(string) if string.present? if string.present? doc = Nokogiri::XML(string, nil, 'UTF-8', &:noblanks) # remove leading and trailing whitespace in text nodes - doc.xpath("//text()").each do |node| - if node.content =~ /\S/ + doc.xpath('//text()').each do |node| + if /\S/.match?(node.content) node.content = node.content.strip else node.remove end end - string = doc.to_xml(:indent => 2) + string = doc.to_xml(indent: 2) end - client = Array.wrap(response.body.fetch("included", nil)).find { |m| m["type"] == "clients" } - client_id = client.to_h.fetch("id", nil) - provider_id = Array.wrap(client.to_h.fetch("relationships", nil)).find { |m| m["provider"].present? }.to_h.dig("provider", "data", "id") + client = Array.wrap(response.body.fetch('included', nil)).find do |m| + m['type'] == 'clients' + end + client_id = client.to_h.fetch('id', nil) + provider_id = Array.wrap(client.to_h.fetch('relationships', nil)).find do |m| + m['provider'].present? + end.to_h.dig('provider', 'data', 'id') - content_url = attributes.fetch("contentUrl", nil) || Array.wrap(response.body.fetch("included", nil)).select { |m| m["type"] == "media" }.map do |m| - m.dig("attributes", "url") - end.compact + content_url = attributes.fetch('contentUrl', + nil) || Array.wrap(response.body.fetch('included', + nil)).select do |m| + m['type'] == 'media' + end.map do |m| + m.dig('attributes', 'url') + end.compact - { "string" => string, - "url" => attributes.fetch("url", nil), - "state" => attributes.fetch("state", nil), - "date_registered" => attributes.fetch("registered", nil), - "date_updated" => attributes.fetch("updated", nil), - "provider_id" => provider_id, - "client_id" => client_id, - "content_url" => content_url } + { 'string' => string, + 'url' => attributes.fetch('url', nil), + 'state' => attributes.fetch('state', nil), + 'date_registered' => attributes.fetch('registered', nil), + 'date_updated' => attributes.fetch('updated', nil), + 'provider_id' => provider_id, + 'client_id' => client_id, + 'content_url' => content_url } end def read_datacite(string: nil, **options) - read_options = ActiveSupport::HashWithIndifferentAccess.new(options.except(:doi, :id, :url, :sandbox, :validate, :ra)) + read_options = ActiveSupport::HashWithIndifferentAccess.new(options.except(:doi, :id, :url, + :sandbox, :validate, :ra)) doc = Nokogiri::XML(string, nil, 'UTF-8', &:noblanks) if read_options.present? - schema_version = "http://datacite.org/schema/kernel-4" + schema_version = 'http://datacite.org/schema/kernel-4' else - ns = doc.collect_namespaces.find { |k, v| v.start_with?("http://datacite.org/schema/kernel") } - schema_version = Array.wrap(ns).last || "http://datacite.org/schema/kernel-4" + ns = doc.collect_namespaces.find { |_k, v| v.start_with?('http://datacite.org/schema/kernel') } + schema_version = Array.wrap(ns).last || 'http://datacite.org/schema/kernel-4' end doc.remove_namespaces! - string = doc.to_xml(:indent => 2) + string = doc.to_xml(indent: 2) - meta = Maremma.from_xml(string).to_h.fetch("resource", {}) + meta = Maremma.from_xml(string).to_h.fetch('resource', {}) # validate only when option is set, as this step is expensive and # not needed if XML comes from DataCite MDS if options[:validate] errors = datacite_errors(xml: string, schema_version: schema_version) - return { "errors" => errors } if errors.present? + return { 'errors' => errors } if errors.present? end - if options[:doi] - id = normalize_doi(options[:doi], sandbox: options[:sandbox]) - else - id = normalize_doi(meta.dig("identifier", "__content__") || options[:id], sandbox: options[:sandbox]) - end + id = if options[:doi] + normalize_doi(options[:doi], sandbox: options[:sandbox]) + else + normalize_doi(meta.dig('identifier', '__content__') || options[:id], + sandbox: options[:sandbox]) + end - identifiers = Array.wrap(meta.dig("alternateIdentifiers", "alternateIdentifier")).map do |r| - if r["__content__"].present? - { "identifierType" => get_identifier_type(r["alternateIdentifierType"]), "identifier" => r["__content__"] } + identifiers = Array.wrap(meta.dig('alternateIdentifiers', 'alternateIdentifier')).map do |r| + if r['__content__'].present? + { 'identifierType' => get_identifier_type(r['alternateIdentifierType']), + 'identifier' => r['__content__'] } end end.compact - resource_type_general = meta.dig("resourceType", "resourceTypeGeneral") - resource_type = meta.dig("resourceType", "__content__") - schema_org = Briard::Utils::CR_TO_SO_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::DC_TO_SO_TRANSLATIONS[resource_type_general.to_s.dasherize] || "CreativeWork" + resource_type_general = meta.dig('resourceType', 'resourceTypeGeneral') + resource_type = meta.dig('resourceType', '__content__') + schema_org = Briard::Utils::CR_TO_SO_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::DC_TO_SO_TRANSLATIONS[resource_type_general.to_s.dasherize] || 'CreativeWork' types = { - "resourceTypeGeneral" => resource_type_general, - "resourceType" => resource_type, - "schemaOrg" => schema_org, - "citeproc" => Briard::Utils::CR_TO_CP_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::SO_TO_CP_TRANSLATIONS[schema_org] || "article", - "bibtex" => Briard::Utils::CR_TO_BIB_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::SO_TO_BIB_TRANSLATIONS[schema_org] || "misc", - "ris" => Briard::Utils::CR_TO_RIS_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::DC_TO_RIS_TRANSLATIONS[resource_type_general.to_s.dasherize] || "GEN" + 'resourceTypeGeneral' => resource_type_general, + 'resourceType' => resource_type, + 'schemaOrg' => schema_org, + 'citeproc' => Briard::Utils::CR_TO_CP_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::SO_TO_CP_TRANSLATIONS[schema_org] || 'article', + 'bibtex' => Briard::Utils::CR_TO_BIB_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::SO_TO_BIB_TRANSLATIONS[schema_org] || 'misc', + 'ris' => Briard::Utils::CR_TO_RIS_TRANSLATIONS[resource_type.to_s.underscore.camelcase] || Briard::Utils::DC_TO_RIS_TRANSLATIONS[resource_type_general.to_s.dasherize] || 'GEN' }.compact titles = get_titles(meta) - descriptions = Array.wrap(meta.dig("descriptions", "description")).map do |r| + descriptions = Array.wrap(meta.dig('descriptions', 'description')).map do |r| if r.blank? nil elsif r.is_a?(String) - { "description" => sanitize(r), "descriptionType" => "Abstract" } + { 'description' => sanitize(r), 'descriptionType' => 'Abstract' } elsif r.is_a?(Hash) - { "description" => sanitize(r["__content__"]), "descriptionType" => r["descriptionType"], "lang" => r["lang"] }.compact + { 'description' => sanitize(r['__content__']), + 'descriptionType' => r['descriptionType'], 'lang' => r['lang'] }.compact end end.compact - rights_list = Array.wrap(meta.dig("rightsList", "rights")).map do |r| + rights_list = Array.wrap(meta.dig('rightsList', 'rights')).map do |r| if r.blank? nil elsif r.is_a?(String) name_to_spdx(r) elsif r.is_a?(Hash) hsh_to_spdx(r) end end.compact - subjects = Array.wrap(meta.dig("subjects", "subject")).reduce([]) do |sum, subject| - if subject.is_a?(String) + subjects = Array.wrap(meta.dig('subjects', 'subject')).reduce([]) do |sum, subject| + case subject + when String sum += name_to_fos(subject) - elsif subject.is_a?(Hash) + when Hash sum += hsh_to_fos(subject) end sum - end.uniq { |s| s["subject"] } + end.uniq { |s| s['subject'] } - dates = Array.wrap(meta.dig("dates", "date")).map do |r| - if r.is_a?(Hash) && date = sanitize(r["__content__"]).presence - if Date.edtf(date).present? || Briard::Utils::UNKNOWN_INFORMATION.key?(date) - { "date" => date, - "dateType" => parse_attributes(r, content: "dateType"), - "dateInformation" => parse_attributes(r, content: "dateInformation") - }.compact - end - end + dates = Array.wrap(meta.dig('dates', 'date')).map do |r| + next unless r.is_a?(Hash) && date = sanitize(r['__content__']).presence + + next unless Date.edtf(date).present? || Briard::Utils::UNKNOWN_INFORMATION.key?(date) + + { 'date' => date, + 'dateType' => parse_attributes(r, content: 'dateType'), + 'dateInformation' => parse_attributes(r, content: 'dateInformation') }.compact end.compact - dates << { "date" => meta.fetch("publicationYear", nil), "dateType" => "Issued" } if meta.fetch("publicationYear", nil).present? && get_date(dates, "Issued").blank? - sizes = Array.wrap(meta.dig("sizes", "size")).map do |k| + if meta.fetch('publicationYear', nil).present? && get_date(dates, 'Issued').blank? + dates << { 'date' => meta.fetch('publicationYear', nil), 'dateType' => 'Issued' } + end + sizes = Array.wrap(meta.dig('sizes', 'size')).map do |k| if k.blank? nil elsif k.is_a?(String) sanitize(k).presence elsif k.is_a?(Hash) - sanitize(k["__content__"]).presence + sanitize(k['__content__']).presence end end.compact - formats = Array.wrap(meta.dig("formats", "format")).map do |k| + formats = Array.wrap(meta.dig('formats', 'format')).map do |k| if k.blank? nil elsif k.is_a?(String) sanitize(k).presence elsif k.is_a?(Hash) - sanitize(k["__content__"]).presence + sanitize(k['__content__']).presence end end.compact - .map { |s| s.to_s.squish.presence }.compact - funding_references = Array.wrap(meta.dig("fundingReferences", "fundingReference")).compact.map do |fr| - scheme_uri = parse_attributes(fr["funderIdentifier"], content: "schemeURI") - funder_identifier = parse_attributes(fr["funderIdentifier"]) - funder_identifier_type = parse_attributes(fr["funderIdentifier"], content: "funderIdentifierType") - if funder_identifier_type != "Other" - funder_identifier = !funder_identifier.to_s.start_with?("https://","http://") && scheme_uri.present? ? normalize_id(scheme_uri + funder_identifier) : normalize_id(funder_identifier) + .map { |s| s.to_s.squish.presence }.compact + funding_references = Array.wrap(meta.dig('fundingReferences', + 'fundingReference')).compact.map do |fr| + scheme_uri = parse_attributes(fr['funderIdentifier'], content: 'schemeURI') + funder_identifier = parse_attributes(fr['funderIdentifier']) + funder_identifier_type = parse_attributes(fr['funderIdentifier'], + content: 'funderIdentifierType') + if funder_identifier_type != 'Other' + funder_identifier = if !funder_identifier.to_s.start_with?('https://', + 'http://') && scheme_uri.present? + normalize_id(scheme_uri + funder_identifier) + else + normalize_id(funder_identifier) + end end { - "funderName" => fr["funderName"], - "funderIdentifier" => funder_identifier, - "funderIdentifierType" => funder_identifier_type, - "awardNumber" => parse_attributes(fr["awardNumber"]), - "awardUri" => parse_attributes(fr["awardNumber"], content: "awardURI"), - "awardTitle" => fr["awardTitle"] }.compact + 'funderName' => fr['funderName'], + 'funderIdentifier' => funder_identifier, + 'funderIdentifierType' => funder_identifier_type, + 'awardNumber' => parse_attributes(fr['awardNumber']), + 'awardUri' => parse_attributes(fr['awardNumber'], content: 'awardURI'), + 'awardTitle' => fr['awardTitle'] + }.compact end - related_identifiers = Array.wrap(meta.dig("relatedIdentifiers", "relatedIdentifier")).map do |ri| - if ri["relatedIdentifierType"] == "DOI" - rid = validate_doi(ri["__content__"].to_s.downcase) - else - rid = ri["__content__"] - end + related_identifiers = Array.wrap(meta.dig('relatedIdentifiers', + 'relatedIdentifier')).map do |ri| + rid = if ri['relatedIdentifierType'] == 'DOI' + validate_doi(ri['__content__'].to_s.downcase) + else + ri['__content__'] + end { - "relatedIdentifier" => rid, - "relatedIdentifierType" => ri["relatedIdentifierType"], - "relationType" => ri["relationType"], - "resourceTypeGeneral" => ri["resourceTypeGeneral"], - "relatedMetadataScheme" => ri["relatedMetadataScheme"], - "schemeUri" => ri["schemeURI"], - "schemeType" => ri["schemeType"] + 'relatedIdentifier' => rid, + 'relatedIdentifierType' => ri['relatedIdentifierType'], + 'relationType' => ri['relationType'], + 'resourceTypeGeneral' => ri['resourceTypeGeneral'], + 'relatedMetadataScheme' => ri['relatedMetadataScheme'], + 'schemeUri' => ri['schemeURI'], + 'schemeType' => ri['schemeType'] }.compact end - related_items = Array.wrap(meta.dig("relatedItems", "relatedItem")).map do |ri| - - rii = ri["relatedItemIdentifier"] + related_items = Array.wrap(meta.dig('relatedItems', 'relatedItem')).map do |ri| + rii = ri['relatedItemIdentifier'] relatedItemIdentifier = nil if rii - if rii["relatedItemIdentifierType"] == "DOI" - rid = validate_doi(rii["__content__"].to_s.downcase) - else - rid = rii["__content__"] - end + rid = if rii['relatedItemIdentifierType'] == 'DOI' + validate_doi(rii['__content__'].to_s.downcase) + else + rii['__content__'] + end relatedItemIdentifier = { - "relatedItemIdentifier" => rid, - "relatedItemIdentifierType" => rii["relatedItemIdentifierType"], - "relatedMetadataScheme" => rii["relatedMetadataScheme"], - "schemeURI" => rii["schemeURI"], - "schemeType" => rii["schemeType"] + 'relatedItemIdentifier' => rid, + 'relatedItemIdentifierType' => rii['relatedItemIdentifierType'], + 'relatedMetadataScheme' => rii['relatedMetadataScheme'], + 'schemeURI' => rii['schemeURI'], + 'schemeType' => rii['schemeType'] }.compact end - number = ri["number"] + number = ri['number'] if number.is_a?(String) number = number numberType = nil else - number = ri.dig("number", "__content__") - numberType = ri.dig("number", "numberType") + number = ri.dig('number', '__content__') + numberType = ri.dig('number', 'numberType') end a = { - "relationType" => ri["relationType"], - "relatedItemType" => ri["relatedItemType"], - "relatedItemIdentifier" => relatedItemIdentifier, - "creators" => get_authors(Array.wrap(ri.dig("creators", "creator"))), - "titles" => get_titles(ri), - "publicationYear" => ri["publicationYear"], - "volume" => ri["volume"], - "issue" => ri["issue"], - "number" => number, - "numberType" => numberType, - "firstPage" => ri["firstPage"], - "lastPage" => ri["lastPage"], - "publisher" => ri["publisher"], - "edition" => ri["edition"], - "contributors" => get_authors(Array.wrap(ri.dig("contributors", "contributor"))), + 'relationType' => ri['relationType'], + 'relatedItemType' => ri['relatedItemType'], + 'relatedItemIdentifier' => relatedItemIdentifier, + 'creators' => get_authors(Array.wrap(ri.dig('creators', 'creator'))), + 'titles' => get_titles(ri), + 'publicationYear' => ri['publicationYear'], + 'volume' => ri['volume'], + 'issue' => ri['issue'], + 'number' => number, + 'numberType' => numberType, + 'firstPage' => ri['firstPage'], + 'lastPage' => ri['lastPage'], + 'publisher' => ri['publisher'], + 'edition' => ri['edition'], + 'contributors' => get_authors(Array.wrap(ri.dig('contributors', 'contributor'))) }.compact end - geo_locations = Array.wrap(meta.dig("geoLocations", "geoLocation")).map do |gl| - if !gl.is_a?(Hash) || gl["geoLocationPoint"].is_a?(String) || gl["geoLocationBox"].is_a?(String) || gl["geoLocationPolygon"].is_a?(String) + geo_locations = Array.wrap(meta.dig('geoLocations', 'geoLocation')).map do |gl| + if !gl.is_a?(Hash) || gl['geoLocationPoint'].is_a?(String) || gl['geoLocationBox'].is_a?(String) || gl['geoLocationPolygon'].is_a?(String) nil else # Handle scenario where multiple geoLocationPolygons are allowed within a single geoLocation # we want to return an array if it's already an array (i.e. multiple geoLocationPolygons) # vs if it's singular just return the object # This is for backwards compatability to allow both scenarios. - if gl.dig("geoLocationPolygon").kind_of?(Array) - geoLocationPolygon = gl.dig("geoLocationPolygon").map do |glp| - Array.wrap(glp.dig("polygonPoint")).map { |glpp| { "polygonPoint" => glpp } }.compact.presence + if gl['geoLocationPolygon'].is_a?(Array) + geoLocationPolygon = gl['geoLocationPolygon'].map do |glp| + Array.wrap(glp['polygonPoint']).map do |glpp| + { 'polygonPoint' => glpp } + end.compact.presence end.compact.presence else - geoLocationPolygon = Array.wrap(gl.dig("geoLocationPolygon", "polygonPoint")).map { |glp| { "polygonPoint" => glp } }.compact.presence + geoLocationPolygon = Array.wrap(gl.dig('geoLocationPolygon', + 'polygonPoint')).map do |glp| + { 'polygonPoint' => glp } + end.compact.presence end { - "geoLocationPoint" => { - "pointLatitude" => gl.dig("geoLocationPoint", "pointLatitude"), - "pointLongitude" => gl.dig("geoLocationPoint", "pointLongitude") + 'geoLocationPoint' => { + 'pointLatitude' => gl.dig('geoLocationPoint', 'pointLatitude'), + 'pointLongitude' => gl.dig('geoLocationPoint', 'pointLongitude') }.compact.presence, - "geoLocationBox" => { - "westBoundLongitude" => gl.dig("geoLocationBox", "westBoundLongitude"), - "eastBoundLongitude" => gl.dig("geoLocationBox", "eastBoundLongitude"), - "southBoundLatitude" => gl.dig("geoLocationBox", "southBoundLatitude"), - "northBoundLatitude" => gl.dig("geoLocationBox", "northBoundLatitude") + 'geoLocationBox' => { + 'westBoundLongitude' => gl.dig('geoLocationBox', 'westBoundLongitude'), + 'eastBoundLongitude' => gl.dig('geoLocationBox', 'eastBoundLongitude'), + 'southBoundLatitude' => gl.dig('geoLocationBox', 'southBoundLatitude'), + 'northBoundLatitude' => gl.dig('geoLocationBox', 'northBoundLatitude') }.compact.presence, - "geoLocationPolygon" => geoLocationPolygon, - "geoLocationPlace" => parse_attributes(gl["geoLocationPlace"], first: true).to_s.strip.presence + 'geoLocationPolygon' => geoLocationPolygon, + 'geoLocationPlace' => parse_attributes(gl['geoLocationPlace'], + first: true).to_s.strip.presence }.compact end end.compact - state = id.present? || read_options.present? ? "findable" : "not_found" + state = id.present? || read_options.present? ? 'findable' : 'not_found' - { "id" => id, - "types" => types, - "doi" => doi_from_url(id), - "identifiers" => identifiers, - "url" => options.fetch(:url, nil).to_s.strip.presence, - "titles" => titles, - "creators" => get_authors(Array.wrap(meta.dig("creators", "creator"))), - "contributors" => get_authors(Array.wrap(meta.dig("contributors", "contributor"))), - "container" => set_container(meta), - "publisher" => parse_attributes(meta.fetch("publisher", nil), first: true).to_s.strip.presence, - "agency" => "datacite", - "funding_references" => funding_references, - "dates" => dates, - "publication_year" => parse_attributes(meta.fetch("publicationYear", nil), first: true).to_s.strip.presence, - "descriptions" => descriptions, - "rights_list" => Array.wrap(rights_list), - "version_info" => meta.fetch("version", nil).to_s.presence, - "subjects" => subjects, - "language" => parse_attributes(meta.fetch("language", nil), first: true).to_s.strip.presence, - "geo_locations" => geo_locations, - "related_identifiers" => related_identifiers, - "related_items" => related_items, - "formats" => formats, - "sizes" => sizes, - "schema_version" => schema_version, - "state" => state - }.merge(read_options) + { 'id' => id, + 'types' => types, + 'doi' => doi_from_url(id), + 'identifiers' => identifiers, + 'url' => options.fetch(:url, nil).to_s.strip.presence, + 'titles' => titles, + 'creators' => get_authors(Array.wrap(meta.dig('creators', 'creator'))), + 'contributors' => get_authors(Array.wrap(meta.dig('contributors', 'contributor'))), + 'container' => set_container(meta), + 'publisher' => parse_attributes(meta.fetch('publisher', nil), + first: true).to_s.strip.presence, + 'agency' => 'datacite', + 'funding_references' => funding_references, + 'dates' => dates, + 'publication_year' => parse_attributes(meta.fetch('publicationYear', nil), + first: true).to_s.strip.presence, + 'descriptions' => descriptions, + 'rights_list' => Array.wrap(rights_list), + 'version_info' => meta.fetch('version', nil).to_s.presence, + 'subjects' => subjects, + 'language' => parse_attributes(meta.fetch('language', nil), + first: true).to_s.strip.presence, + 'geo_locations' => geo_locations, + 'related_identifiers' => related_identifiers, + 'related_items' => related_items, + 'formats' => formats, + 'sizes' => sizes, + 'schema_version' => schema_version, + 'state' => state }.merge(read_options) end def set_container(meta) - series_information = Array.wrap(meta.dig("descriptions", "description")).find { |r| r["descriptionType"] == "SeriesInformation" }.to_h.fetch("__content__", nil) + series_information = Array.wrap(meta.dig('descriptions', 'description')).find do |r| + r['descriptionType'] == 'SeriesInformation' + end.to_h.fetch('__content__', nil) si = get_series_information(series_information) - is_part_of = Array.wrap(meta.dig("relatedIdentifiers", "relatedIdentifier")).find { |ri| ri["relationType"] == "IsPartOf" }.to_h + is_part_of = Array.wrap(meta.dig('relatedIdentifiers', 'relatedIdentifier')).find do |ri| + ri['relationType'] == 'IsPartOf' + end.to_h - if si["title"].present? || is_part_of.present? + if si['title'].present? || is_part_of.present? { - "type" => meta.dig("resourceType", "resourceTypeGeneral") == "Dataset" ? "DataRepository" : "Series", - "identifier" => is_part_of["__content__"], - "identifierType" => is_part_of["relatedIdentifierType"], - "title" => si["title"], - "volume" => si["volume"], - "issue" => si["issue"], - "firstPage" => si["firstPage"], - "lastPage" => si["lastPage"] + 'type' => if meta.dig('resourceType', 'resourceTypeGeneral') == 'Dataset' + 'DataRepository' + else + 'Series' + end, + 'identifier' => is_part_of['__content__'], + 'identifierType' => is_part_of['relatedIdentifierType'], + 'title' => si['title'], + 'volume' => si['volume'], + 'issue' => si['issue'], + 'firstPage' => si['firstPage'], + 'lastPage' => si['lastPage'] }.compact else {} end end def get_titles(meta) - titles = Array.wrap(meta.dig("titles", "title")).map do |r| + Array.wrap(meta.dig('titles', 'title')).map do |r| if r.blank? nil elsif r.is_a?(String) - { "title" => sanitize(r) } + { 'title' => sanitize(r) } else - { "title" => sanitize(r["__content__"]), "titleType" => r["titleType"], "lang" => r["lang"] }.compact + { 'title' => sanitize(r['__content__']), 'titleType' => r['titleType'], + 'lang' => r['lang'] }.compact end end.compact - - titles end - end end end