bolognese 0.7.2 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -1
  3. data/README.md +25 -16
  4. data/bolognese.gemspec +2 -1
  5. data/codemeta.json +39 -0
  6. data/lib/bolognese.rb +4 -0
  7. data/lib/bolognese/array.rb +11 -0
  8. data/lib/bolognese/author_utils.rb +35 -21
  9. data/lib/bolognese/bibtex.rb +4 -4
  10. data/lib/bolognese/codemeta.rb +8 -13
  11. data/lib/bolognese/crossref.rb +22 -20
  12. data/lib/bolognese/datacite.rb +61 -61
  13. data/lib/bolognese/datacite_json.rb +208 -0
  14. data/lib/bolognese/datacite_utils.rb +17 -48
  15. data/lib/bolognese/metadata.rb +83 -22
  16. data/lib/bolognese/schema_org.rb +42 -16
  17. data/lib/bolognese/utils.rb +79 -13
  18. data/lib/bolognese/version.rb +1 -1
  19. data/lib/bolognese/whitelist_scrubber.rb +45 -0
  20. data/spec/array_spec.rb +20 -0
  21. data/spec/author_utils_spec.rb +93 -9
  22. data/spec/bibtex_spec.rb +4 -4
  23. data/spec/cli_spec.rb +5 -0
  24. data/spec/codemeta_spec.rb +41 -31
  25. data/spec/crossref_spec.rb +47 -72
  26. data/spec/datacite_json_spec.rb +65 -0
  27. data/spec/datacite_spec.rb +67 -83
  28. data/spec/datacite_utils_spec.rb +9 -14
  29. data/spec/fixtures/datacite.json +49 -0
  30. data/spec/fixtures/datacite_software.json +18 -0
  31. data/spec/fixtures/vcr_cassettes/Bolognese_CLI/convert_from_id/datacite/to_datacite_json.yml +214 -0
  32. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/author_from_schema_org/with_id.yml +930 -0
  33. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/author_to_schema_org/with_id.yml +930 -0
  34. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/authors_as_string/author.yml +137 -860
  35. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/authors_as_string/no_author.yml +137 -860
  36. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/authors_as_string/single_author.yml +137 -860
  37. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/authors_as_string/with_organization.yml +137 -860
  38. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/from_schema_org/with_id.yml +930 -0
  39. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_name_identifier/has_ORCID.yml +155 -0
  40. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_name_identifier/has_no_ORCID.yml +134 -0
  41. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_one_author/has_familyName.yml +155 -0
  42. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_one_author/has_name_in_display-order.yml +186 -0
  43. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_one_author/has_name_in_display-order_with_ORCID.yml +177 -0
  44. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_one_author/has_name_in_sort-order.yml +173 -0
  45. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/get_one_author/is_organization.yml +207 -0
  46. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/is_personal_name_/has_comma.yml +207 -0
  47. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/is_personal_name_/has_family_name.yml +207 -0
  48. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/is_personal_name_/has_id.yml +207 -0
  49. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/is_personal_name_/has_no_info.yml +207 -0
  50. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/is_personal_name_/has_type_organization.yml +207 -0
  51. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/is_personal_name_/has_type_person.yml +207 -0
  52. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/sanitize/should_only_keep_specific_tags.yml +930 -0
  53. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/sanitize/should_remove_a_tags.yml +930 -0
  54. data/spec/fixtures/vcr_cassettes/Bolognese_Crossref/to_schema_org/with_id.yml +930 -0
  55. data/spec/fixtures/vcr_cassettes/Bolognese_Datacite/insert_related_identifiers/related_identifier.yml +173 -0
  56. data/spec/fixtures/vcr_cassettes/Bolognese_DataciteJson/get_metadata_as_bibtex/BlogPosting.yml +155 -0
  57. data/spec/schema_org_spec.rb +17 -14
  58. data/spec/utils_spec.rb +32 -2
  59. metadata +54 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc292f3921b5634ec479a7e392fd8f9c476b5739
4
- data.tar.gz: 33a44a56720c036829596fad78a38674dc1bcdef
3
+ metadata.gz: d9c8e7a4955f749653d35917dae9f3a459c0f2da
4
+ data.tar.gz: b646899d5e3c5502320e754d25fc9cbfdafffc51
5
5
  SHA512:
6
- metadata.gz: 14e2ba1b16d38ffdf5bb839596b7c1b73e62fa2d2a35cca89d3c615801334f26c36dc302f87080a112ea5be6165614a49816a218242c86632422c4b937c7612b
7
- data.tar.gz: 5415c8c7283392900c4558d3b732099dc47216774207ce47d369221df3c2bf096abfdb0ca79e4a955b67135288c2d2ee80375b684c43a205bf163bb68b142785
6
+ metadata.gz: 892e8ccf9ad4354b93dca5b42d7fc099707007d2187dcaab5ad066ebf5357dc6ade3664e181408e05be3cfc07b8a77f86f1abe57f1348fd6a5d9fcfe5b293b06
7
+ data.tar.gz: 064b883d4eefefb064f4dc24fb5397a2c530f2dd1a52cc15319a52487820053d67b6a68dd94ae8a1dbd35dcb21d623e3a42e9539552172c0be536945edd4daf5
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bolognese (0.7.2)
4
+ bolognese (0.8)
5
5
  activesupport (~> 4.2, >= 4.2.5)
6
6
  bibtex-ruby (~> 4.1)
7
7
  builder (~> 3.2, >= 3.2.2)
8
8
  colorize (~> 0.8.1)
9
+ loofah (~> 2.0, >= 2.0.3)
9
10
  maremma (~> 3.5)
10
11
  namae (~> 0.10.2)
11
12
  nokogiri (~> 1.6, >= 1.6.8)
@@ -43,6 +44,8 @@ GEM
43
44
  json (2.0.3)
44
45
  latex-decode (0.2.2)
45
46
  unicode (~> 0.4)
47
+ loofah (2.0.3)
48
+ nokogiri (>= 1.5.9)
46
49
  maremma (3.5.1)
47
50
  activesupport (~> 4.2, >= 4.2.5)
48
51
  addressable (>= 2.3.6)
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
+ [![Identifier](https://img.shields.io/badge/doi-10.5438%2Fn138--z3mk-fca709.svg)](https://doi.org/10.5438/n138-z3mk)
2
+ [![Gem Version](https://badge.fury.io/rb/bolognese.svg)](https://badge.fury.io/rb/bolognese)
1
3
  [![Build Status](https://travis-ci.org/datacite/bolognese.svg?branch=master)](https://travis-ci.org/datacite/bolognese)
2
4
  [![Code Climate](https://codeclimate.com/github/datacite/bolognese/badges/gpa.svg)](https://codeclimate.com/github/datacite/bolognese)
3
5
  [![Test Coverage](https://codeclimate.com/github/datacite/bolognese/badges/coverage.svg)](https://codeclimate.com/github/datacite/bolognese/coverage)
4
6
 
5
- # Bolognese
7
+ # Bolognese: a Ruby library for conversion of DOI Metadata
6
8
 
7
9
  Ruby gem and command-line utility for conversion of DOI metadata from and to different metadata formats, including [schema.org](https://schema.org).
8
10
 
@@ -36,25 +38,32 @@ Bolognese reads and/or writes these metadata formats:
36
38
  <td>Yes</td>
37
39
  </tr>
38
40
  <tr>
39
- <td><a href='http://schema.org/'>Schema.org in JSON-LD</a></td>
40
- <td>schema_org</td>
41
- <td>application/vnd.schemaorg.ld+json</td>
42
- <td>Yes</td>
43
- <td>Yes</td>
41
+ <td><a href='https://api.datacite.org/'>DataCite JSON</a></td>
42
+ <td>datacite</td>
43
+ <td>application/vnd.datacite+json</td>
44
+ <td>Yes</td>
45
+ <td>Yes</td>
44
46
  </tr>
45
47
  <tr>
46
- <td><a href='https://codemeta.github.io/'>Codemeta</a></td>
47
- <td>codemeta</td>
48
- <td>application/ld+json</td>
49
- <td>Yes</td>
50
- <td>Yes</td>
48
+ <td><a href='http://schema.org/'>Schema.org in JSON-LD</a></td>
49
+ <td>schema_org</td>
50
+ <td>application/vnd.schemaorg.ld+json</td>
51
+ <td>Yes</td>
52
+ <td>Yes</td>
51
53
  </tr>
52
54
  <tr>
53
- <td><a href='http://en.wikipedia.org/wiki/BibTeX'>BibTeX</a></td>
54
- <td>bibtex</td>
55
- <td>application/x-bibtex</td>
56
- <td>Yes</td>
57
- <td>Yes</td>
55
+ <td><a href='https://codemeta.github.io/'>Codemeta</a></td>
56
+ <td>codemeta</td>
57
+ <td>application/ld+json</td>
58
+ <td>Yes</td>
59
+ <td>Yes</td>
60
+ </tr>
61
+ <tr>
62
+ <td><a href='http://en.wikipedia.org/wiki/BibTeX'>BibTeX</a></td>
63
+ <td>bibtex</td>
64
+ <td>application/x-bibtex</td>
65
+ <td>Yes</td>
66
+ <td>Yes</td>
58
67
  </tr>
59
68
  </tbody>
60
69
  </table>
data/bolognese.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.homepage = "https://github.com/datacite/bolognese"
9
9
  s.summary = "Ruby client library for conversion of DOI Metadata"
10
10
  s.date = Date.today
11
- s.description = "Convert DOI metadata to and from Crossref and DataCite XML, as well as schema.org/JSON-LD"
11
+ s.description = "Ruby gem and command-line utility for conversion of DOI metadata from and to different metadata formats, including schema.org."
12
12
  s.require_paths = ["lib"]
13
13
  s.version = Bolognese::VERSION
14
14
  s.extra_rdoc_files = ["README.md"]
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
  # Declary dependencies here, rather than in the Gemfile
18
18
  s.add_dependency 'maremma', '~> 3.5'
19
19
  s.add_dependency 'nokogiri', '~> 1.6', '>= 1.6.8'
20
+ s.add_dependency 'loofah', '~> 2.0', '>= 2.0.3'
20
21
  s.add_dependency 'builder', '~> 3.2', '>= 3.2.2'
21
22
  s.add_dependency 'activesupport', '~> 4.2', '>= 4.2.5'
22
23
  s.add_dependency 'bibtex-ruby', '~> 4.1'
data/codemeta.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "@context": "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld",
3
+ "@type": "SoftwareSourceCode",
4
+ "@id": "https://doi.org/10.5438/N138-Z3MK",
5
+ "agents": {
6
+ "@id": "http://orcid.org/0000-0003-0077-4738",
7
+ "@type": "person",
8
+ "name": "Martin Fenner",
9
+ "affiliation": "DataCite",
10
+ "mustBeCited": true,
11
+ "isMaintainer": true,
12
+ "isRightsHolder": true
13
+ },
14
+ "identifier": "https://doi.org/10.5438/N138-Z3MK",
15
+ "codeRepository": "https://github.com/datacite/bolognese",
16
+ "dateCreated": "2017-02-13",
17
+ "datePublished": "2017-02-25",
18
+ "dateModified": "2017-02-25",
19
+ "description": "Ruby gem and command-line utility for conversion of DOI metadata from and to different metadata formats, including schema.org.",
20
+ "isAutomatedBuild": true,
21
+ "licenseId": "MIT",
22
+ "publisher": "DataCite",
23
+ "tags": [
24
+ "doi",
25
+ "metadata",
26
+ "crossref",
27
+ "datacite",
28
+ "schema.org",
29
+ "bibtex",
30
+ "codemeta"
31
+ ],
32
+ "title": "Bolognese: a Ruby library for conversion of DOI Metadata",
33
+ "programmingLanguage": {
34
+ "name": "Ruby",
35
+ "version": "≥ 2.3.3",
36
+ "URL": "https://www.ruby-lang.org"
37
+ },
38
+ "readme": "https://github.com/datacite/bolognese/blob/master/README.md"
39
+ }
data/lib/bolognese.rb CHANGED
@@ -4,14 +4,18 @@ require 'maremma'
4
4
  require 'postrank-uri'
5
5
  require 'bibtex'
6
6
  require 'colorize'
7
+ require 'loofah'
7
8
 
8
9
  require "bolognese/version"
9
10
  require "bolognese/metadata"
10
11
  require "bolognese/crossref"
11
12
  require "bolognese/datacite"
13
+ require "bolognese/datacite_json"
12
14
  require "bolognese/schema_org"
13
15
  require "bolognese/codemeta"
14
16
  require "bolognese/bibtex"
15
17
  require "bolognese/orcid"
16
18
  require "bolognese/cli"
17
19
  require "bolognese/string"
20
+ require "bolognese/array"
21
+ require "bolognese/whitelist_scrubber"
@@ -0,0 +1,11 @@
1
+ # turn array into hash or nil, depending on array size. Reverses Array.wrap,
2
+ # but uses self to allow chaining with Array.wrap
3
+ class Array
4
+ def unwrap
5
+ case self.length
6
+ when 0 then nil
7
+ when 1 then self.first
8
+ else self
9
+ end
10
+ end
11
+ end
@@ -4,25 +4,34 @@ module Bolognese
4
4
  module AuthorUtils
5
5
  # only assume personal name when using sort-order: "Turing, Alan"
6
6
  def get_one_author(author)
7
- orcid = get_name_identifier(author)
8
- author = author.fetch("creatorName", nil)
7
+ type = author.fetch("type", nil) && author.fetch("type").titleize
8
+ id = author.fetch("id", nil).presence || get_name_identifier(author)
9
+ name = author.fetch("creatorName", nil) ||
10
+ author.fetch("contributorName", nil) ||
11
+ author.fetch("name", nil)
12
+ name = cleanup_author(name)
13
+ given_name = author.fetch("givenName", nil)
14
+ family_name = author.fetch("familyName", nil)
9
15
 
10
- return { "name" => "" } if author.to_s.strip.blank?
16
+ author = { "type" => type || "Person",
17
+ "id" => id,
18
+ "name" => name,
19
+ "givenName" => given_name,
20
+ "familyName" => family_name }.compact
11
21
 
12
- author = cleanup_author(author)
13
- names = Namae.parse(author)
22
+ return author if family_name.present?
14
23
 
15
- if names.blank? || !is_personal_name?(author)
16
- { "@type" => "Agent",
17
- "@id" => orcid,
18
- "name" => author }.compact
19
- else
20
- name = names.first
24
+ if is_personal_name?(author)
25
+ names = Namae.parse(name)
26
+ parsed_name = names.first
21
27
 
22
- { "@type" => "Person",
23
- "@id" => orcid,
24
- "givenName" => name.given,
25
- "familyName" => name.family }.compact
28
+ { "type" => "Person",
29
+ "id" => id,
30
+ "name" => [parsed_name.given, parsed_name.family].join(" "),
31
+ "givenName" => parsed_name.given,
32
+ "familyName" => parsed_name.family }.compact
33
+ else
34
+ { "type" => type, "name" => name }.compact
26
35
  end
27
36
  end
28
37
 
@@ -37,15 +46,20 @@ module Bolognese
37
46
  end
38
47
 
39
48
  def is_personal_name?(author)
40
- return true if author.include?(",")
49
+ return false if author.fetch("type", "").downcase == "organization"
50
+ return true if author.fetch("type", "").downcase == "person" ||
51
+ author.fetch("id", "").start_with?("http://orcid.org") ||
52
+ author.fetch("familyName", "").present? ||
53
+ author.fetch("name", "").include?(",")
41
54
 
42
55
  # lookup given name
43
56
  #::NameDetector.name_exists?(author.split.first)
57
+ false
44
58
  end
45
59
 
46
60
  # parse array of author strings into CSL format
47
61
  def get_authors(authors)
48
- Array(authors).map { |author| get_one_author(author) }
62
+ Array.wrap(authors).map { |author| get_one_author(author) }.unwrap
49
63
  end
50
64
 
51
65
  # parse nameIdentifier from DataCite
@@ -61,12 +75,12 @@ module Bolognese
61
75
 
62
76
  def authors_as_string(authors)
63
77
  Array.wrap(authors).map do |a|
64
- if a["@type"] == "organization"
65
- "{" + a["name"] + "}"
66
- elsif a["familyName"].present?
78
+ if a["familyName"].present?
67
79
  [a["familyName"], a["givenName"]].join(", ")
68
- else
80
+ elsif a["type"] == "Person"
69
81
  a["name"]
82
+ elsif a["name"].present?
83
+ "{" + a["name"] + "}"
70
84
  end
71
85
  end.join(" and ").presence
72
86
  end
@@ -70,7 +70,7 @@ module Bolognese
70
70
  end
71
71
  end
72
72
 
73
- def name
73
+ def title
74
74
  metadata.title
75
75
  end
76
76
 
@@ -88,7 +88,7 @@ module Bolognese
88
88
 
89
89
  def is_part_of
90
90
  if metadata.journal.present?
91
- { "@type" => "Periodical",
91
+ { "type" => "Periodical",
92
92
  "name" => metadata.journal.to_s,
93
93
  "issn" => metadata.issn.to_s.presence }.compact
94
94
  else
@@ -97,11 +97,11 @@ module Bolognese
97
97
  end
98
98
 
99
99
  def description
100
- metadata.field?(:abstract) && metadata.abstract.to_s.presence
100
+ { "text" => metadata.field?(:abstract) && metadata.abstract.to_s.presence }
101
101
  end
102
102
 
103
103
  def license
104
- metadata.field?(:copyright) && metadata.copyright.to_s.presence
104
+ { "id" => metadata.field?(:copyright) && metadata.copyright.to_s.presence }
105
105
  end
106
106
  end
107
107
  end
@@ -56,7 +56,7 @@ module Bolognese
56
56
  Bolognese::Bibtex::SO_TO_BIB_TRANSLATIONS[type] || "misc"
57
57
  end
58
58
 
59
- def name
59
+ def title
60
60
  metadata.fetch("title", nil)
61
61
  end
62
62
 
@@ -65,20 +65,21 @@ module Bolognese
65
65
  end
66
66
 
67
67
  def author
68
- arr = Array.wrap(metadata.fetch("agents", nil)).map { |a| a.slice("@type", "@id", "name") }
69
- array_unwrap(arr)
68
+ authors = from_schema_org(Array.wrap(metadata.fetch("agents", nil)))
69
+ get_authors(authors)
70
70
  end
71
71
 
72
72
  def editor
73
- Array(metadata.fetch("editor", nil)).map { |a| a.except("name") }.presence
73
+ editors = from_schema_org(Array.wrap(metadata.fetch("editor", nil)))
74
+ get_authors(editors)
74
75
  end
75
76
 
76
77
  def description
77
- metadata.fetch("description", nil)
78
+ { "text" => metadata.fetch("description", nil) }
78
79
  end
79
80
 
80
81
  def license
81
- metadata.fetch("license", nil)
82
+ { "id" => metadata.fetch("license", nil) }
82
83
  end
83
84
 
84
85
  def version
@@ -122,13 +123,7 @@ module Bolognese
122
123
  end
123
124
 
124
125
  def publisher
125
- p = metadata.fetch("publisher", nil)
126
- if p.is_a?(Hash)
127
- p
128
- elsif p.is_a?(String)
129
- { "@type" => "Organization",
130
- "name" => p }
131
- end
126
+ metadata.fetch("publisher", nil)
132
127
  end
133
128
 
134
129
  def container_title
@@ -68,6 +68,7 @@ module Bolognese
68
68
  elsif id.present?
69
69
  response = Maremma.get(id, accept: "application/vnd.crossref.unixref+xml", host: true, raw: true)
70
70
  @raw = response.body.fetch("data", nil)
71
+ @raw = Nokogiri::XML(@raw, &:noblanks).to_s if @raw.present?
71
72
  end
72
73
  end
73
74
 
@@ -132,7 +133,7 @@ module Bolognese
132
133
  CR_TO_BIB_TRANSLATIONS[additional_type] || "misc"
133
134
  end
134
135
 
135
- def name
136
+ def title
136
137
  parse_attributes(bibliographic_metadata.dig("titles", "title"))
137
138
  end
138
139
 
@@ -172,23 +173,21 @@ module Bolognese
172
173
 
173
174
  def people(contributor_role)
174
175
  person = bibliographic_metadata.dig("contributors", "person_name")
175
- arr = Array.wrap(person).select { |a| a["contributor_role"] == contributor_role }.map do |a|
176
- { "@type" => "Person",
177
- "@id" => parse_attributes(a["ORCID"]),
176
+ Array.wrap(person).select { |a| a["contributor_role"] == contributor_role }.map do |a|
177
+ { "type" => "Person",
178
+ "id" => parse_attributes(a["ORCID"]),
179
+ "name" => [a["given_name"], a["surname"]].join(" "),
178
180
  "givenName" => a["given_name"],
179
181
  "familyName" => a["surname"] }.compact
180
- end
181
- array_unwrap(arr)
182
+ end.unwrap
182
183
  end
183
184
 
184
185
  def funder
185
186
  fundref = Array.wrap(program_metadata).find { |a| a["name"] == "fundref" } || {}
186
- arr = Array.wrap(fundref.fetch("assertion", [])).select { |a| a["name"] == "fundgroup" }.map do |f|
187
- { "@type" => "Organization",
188
- "@id" => normalize_id(f.dig("assertion", "assertion", "__content__")),
187
+ Array.wrap(fundref.fetch("assertion", [])).select { |a| a["name"] == "fundgroup" }.map do |f|
188
+ { "id" => normalize_id(f.dig("assertion", "assertion", "__content__")),
189
189
  "name" => f.dig("assertion", "__content__").strip }.compact
190
- end
191
- array_unwrap(arr)
190
+ end.unwrap
192
191
  end
193
192
 
194
193
  def date_published
@@ -215,7 +214,7 @@ module Bolognese
215
214
 
216
215
  def is_part_of
217
216
  if journal_metadata.present?
218
- { "@type" => "Periodical",
217
+ { "type" => "Periodical",
219
218
  "name" => journal_metadata["full_title"],
220
219
  "issn" => parse_attributes(journal_metadata.fetch("issn", nil)) }.compact
221
220
  else
@@ -229,20 +228,23 @@ module Bolognese
229
228
 
230
229
  alias_method :journal, :container_title
231
230
 
232
- def citation
233
- citations = bibliographic_metadata.dig("citation_list", "citation")
234
- Array.wrap(citations).map do |c|
235
- { "@type" => "CreativeWork",
236
- "@id" => normalize_id(c["doi"]),
231
+ def related_identifier(relation_type: nil)
232
+ references
233
+ end
234
+
235
+ def references
236
+ refs = bibliographic_metadata.dig("citation_list", "citation")
237
+ Array.wrap(refs).map do |c|
238
+ { "id" => normalize_id(c["doi"]),
239
+ "relationType" => "Cites",
237
240
  "position" => c["key"],
238
241
  "name" => c["article_title"],
239
242
  "datePublished" => c["cYear"] }.compact
240
- end.presence
243
+ end.unwrap
241
244
  end
242
245
 
243
246
  def provider
244
- { "@type" => "Organization",
245
- "name" => "Crossref" }
247
+ "Crossref"
246
248
  end
247
249
  end
248
250
  end