cirneco 0.7.4 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -4
  3. data/lib/cirneco/api.rb +8 -8
  4. data/lib/cirneco/base.rb +1 -1
  5. data/lib/cirneco/doi.rb +20 -9
  6. data/lib/cirneco/utils.rb +204 -128
  7. data/lib/cirneco/version.rb +1 -1
  8. data/lib/cirneco/work.rb +19 -7
  9. data/spec/api_spec.rb +10 -10
  10. data/spec/doi_spec.rb +29 -27
  11. data/spec/fixtures/cool-dois-minted.html +404 -0
  12. data/spec/fixtures/cool-dois-minted.html.md +99 -0
  13. data/spec/fixtures/cool-dois-missing-metadata.html +356 -0
  14. data/spec/fixtures/cool-dois-no-json-ld.html +352 -0
  15. data/spec/fixtures/cool-dois.html +404 -0
  16. data/spec/fixtures/cool-dois.html.md +1 -0
  17. data/spec/fixtures/index.html +271 -0
  18. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/get/should_get_all_dois_by_prefix.yml +499 -4
  19. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_hide_for_all_urls.yml +44 -0
  20. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_hide_for_url.yml +44 -0
  21. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_hide_metadata_for_work.yml +8 -4
  22. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_mint_and_hide_for_all_urls.yml +130 -0
  23. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_mint_and_hide_for_url.yml +130 -0
  24. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_mint_for_all_urls.yml +130 -0
  25. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_mint_for_url.yml +130 -0
  26. data/spec/fixtures/vcr_cassettes/Cirneco_DataCenter/mint_and_hide_DOIs/should_post_metadata_for_work.yml +25 -20
  27. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/MDS_DOI_API/get/should_get_all_dois.yml +499 -4
  28. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/MDS_DOI_API/get/should_get_doi.yml +8 -4
  29. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/MDS_DOI_API/put/should_put_doi.yml +8 -4
  30. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/mint_and_hide_DOIs/hides_a_doi.yml +44 -0
  31. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/mint_and_hide_DOIs/hides_dois_for_list_of_urls.yml +44 -0
  32. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/mint_and_hide_DOIs/mints_a_doi.yml +130 -0
  33. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/mint_and_hide_DOIs/mints_and_hides_a_doi.yml +130 -0
  34. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/mint_and_hide_DOIs/mints_and_hides_dois_for_list_of_urls.yml +130 -0
  35. data/spec/fixtures/vcr_cassettes/Cirneco_Doi/mint_and_hide_DOIs/mints_dois_for_list_of_urls.yml +130 -0
  36. data/spec/fixtures/vcr_cassettes/Cirneco_Work/DOI_API/get/should_get_all_dois.yml +499 -4
  37. data/spec/fixtures/vcr_cassettes/Cirneco_Work/DOI_API/get/should_get_doi.yml +8 -4
  38. data/spec/fixtures/vcr_cassettes/Cirneco_Work/DOI_API/put/should_put_doi.yml +8 -4
  39. data/spec/fixtures/vcr_cassettes/Cirneco_Work/Media_API/get/should_get_media.yml +8 -4
  40. data/spec/fixtures/vcr_cassettes/Cirneco_Work/Media_API/post/should_post_media.yml +8 -4
  41. data/spec/fixtures/vcr_cassettes/Cirneco_Work/Metadata_API/delete/should_delete_metadata.yml +8 -4
  42. data/spec/fixtures/vcr_cassettes/Cirneco_Work/Metadata_API/get/should_get_metadata.yml +10 -5
  43. data/spec/fixtures/vcr_cassettes/Cirneco_Work/Metadata_API/post/should_post_metadata.yml +11 -6
  44. data/spec/utils_spec.rb +105 -48
  45. data/spec/work_spec.rb +4 -4
  46. metadata +20 -2
data/lib/cirneco/work.rb CHANGED
@@ -11,7 +11,7 @@ module Cirneco
11
11
  include Cirneco::Api
12
12
  include Cirneco::Utils
13
13
 
14
- attr_accessor :doi, :url, :creators, :title, :publisher, :publication_year, :resource_type, :version, :related_identifiers, :rights_list, :descriptions, :contributors, :date_issued, :subjects, :media, :username, :password, :validation_errors
14
+ attr_accessor :doi, :url, :creators, :title, :publisher, :publication_year, :resource_type, :version, :alternate_identifier, :related_identifiers, :rights_list, :descriptions, :contributors, :date_issued, :date_created, :date_updated, :subjects, :media, :username, :password, :validation_errors
15
15
 
16
16
  def initialize(metadata, **options)
17
17
  @doi = metadata.fetch("doi", nil)
@@ -24,9 +24,12 @@ module Cirneco
24
24
  @version = metadata.fetch("version", nil)
25
25
  @rights_list = metadata.fetch("rights_list", nil)
26
26
  @date_issued = metadata.fetch("date_issued", nil)
27
+ @date_created = metadata.fetch("date_created", nil)
28
+ @date_updated = metadata.fetch("date_updated", nil)
27
29
  @subjects = metadata.fetch("subjects", nil)
28
30
  @descriptions = metadata.fetch("descriptions", nil)
29
31
  @contributors = metadata.fetch("contributors", nil)
32
+ @alternate_identifier = metadata.fetch("alternate_identifier", nil)
30
33
  @related_identifiers = metadata.fetch("related_identifiers", nil)
31
34
 
32
35
  @media = options.fetch(:media, nil)
@@ -52,6 +55,7 @@ module Cirneco
52
55
  insert_publisher(xml)
53
56
  insert_publication_year(xml)
54
57
  insert_resource_type(xml)
58
+ insert_alternate_identifiers(xml)
55
59
  insert_subjects(xml)
56
60
  insert_contributors(xml)
57
61
  insert_dates(xml)
@@ -119,16 +123,24 @@ module Cirneco
119
123
  xml.resourceType(resource_type[:value], 'resourceTypeGeneral' => resource_type[:resource_type_general])
120
124
  end
121
125
 
122
- def insert_dates(xml)
123
- return xml unless date_issued.present?
126
+ def insert_alternate_identifiers(xml)
127
+ return xml unless alternate_identifier.present?
124
128
 
129
+ xml.alternateIdentifiers do
130
+ xml.alternateIdentifier(alternate_identifier, 'alternateIdentifierType' => "Local accession number")
131
+ end
132
+ end
133
+
134
+ def insert_dates(xml)
125
135
  xml.dates do
126
- insert_date(xml)
136
+ insert_date(xml, date_created, 'Created') if date_created.present?
137
+ insert_date(xml, date_issued, 'Issued') if date_issued.present?
138
+ insert_date(xml, date_updated, 'Updated') if date_updated.present?
127
139
  end
128
140
  end
129
141
 
130
- def insert_date(xml)
131
- xml.date(date_issued, 'dateType' => 'Issued')
142
+ def insert_date(xml, date, date_type)
143
+ xml.date(date, 'dateType' => date_type)
132
144
  end
133
145
 
134
146
  def insert_subjects(xml)
@@ -198,7 +210,7 @@ module Cirneco
198
210
  end
199
211
 
200
212
  def validation_errors
201
- @validation_errors ||= schema.validate(Nokogiri::XML(data)).map { |error| error.to_s }
213
+ @validation_errors ||= OpenStruct.new(body: { "errors" => schema.validate(Nokogiri::XML(data)).map { |error| { "title" => error.to_s } } })
202
214
  end
203
215
  end
204
216
  end
data/spec/api_spec.rb CHANGED
@@ -28,6 +28,15 @@ describe Cirneco::Work, vcr: true, :order => :defined do
28
28
  password: password) }
29
29
 
30
30
  describe "Metadata API" do
31
+ context "post" do
32
+ it 'should post metadata' do
33
+ response = subject.post_metadata(subject.data, options)
34
+ expect(response.body["data"]).to eq("OK (10.5072/0000-03VC)")
35
+ expect(response.status).to eq(201)
36
+ expect(response.headers["Location"]).to eq("http://mds-sandbox.datacite.org/metadata/10.5072/0000-03VC")
37
+ end
38
+ end
39
+
31
40
  context "get" do
32
41
  it 'should get metadata' do
33
42
  response = subject.get_metadata(doi, options)
@@ -42,15 +51,6 @@ describe Cirneco::Work, vcr: true, :order => :defined do
42
51
  expect(response.status).to eq(200)
43
52
  end
44
53
  end
45
-
46
- context "post" do
47
- it 'should post metadata' do
48
- response = subject.post_metadata(subject.data, options)
49
- expect(response.body["data"]).to eq("OK (10.5072/0000-03VC)")
50
- expect(response.status).to eq(201)
51
- expect(response.headers["Location"]).to eq("https://mds.test.datacite.org/metadata/10.5072/0000-03VC")
52
- end
53
- end
54
54
  end
55
55
 
56
56
  describe "DOI API" do
@@ -66,7 +66,7 @@ describe Cirneco::Work, vcr: true, :order => :defined do
66
66
  it 'should get all dois' do
67
67
  response = subject.get_dois(options)
68
68
  dois = response.body["data"]
69
- expect(dois.length).to eq(13)
69
+ expect(dois.length).to eq(504)
70
70
  expect(dois.first).to eq("10.23725/0000-03VC")
71
71
  end
72
72
 
data/spec/doi_spec.rb CHANGED
@@ -10,13 +10,14 @@ describe Cirneco::Doi do
10
10
  let(:prefix) { ENV['PREFIX'] }
11
11
  let(:doi) { "10.5072/0000-03VC" }
12
12
  let(:url) { "http://www.datacite.org" }
13
- let(:filename) { 'cool-dois.html.md' }
13
+ let(:filename) { 'cool-dois.html' }
14
14
  let(:filepath) { fixture_path + filename }
15
15
  let(:username) { ENV['MDS_USERNAME'] }
16
16
  let(:password) { ENV['MDS_PASSWORD'] }
17
17
  let(:csl) { "spec/fixtures/apa.csl" }
18
18
  let(:bibliography) { "spec/fixtures/bibliography.yaml" }
19
19
  let(:api_options) { { username: username, password: password, sandbox: true } }
20
+ let(:mint_options) { { username: username, password: password, sandbox: true, source_path: "/spec/fixtures/", csl: csl, bibliography: bibliography } }
20
21
 
21
22
  describe "MDS DOI API", vcr: true, :order => :defined do
22
23
  context "put" do
@@ -28,8 +29,8 @@ describe Cirneco::Doi do
28
29
 
29
30
  context "get" do
30
31
  it 'should get all dois' do
31
- subject.options = api_options
32
- expect { subject.get "all" }.to output("10.23725/0000-03VC\n10.23725/0000-0A53\n10.23725/GQZDGNZW\n10.23725/MDS-CLIENT-RUBY-TEST\n10.5072/0000-03VC\n10.5438/0001\n10.5438/0002\n10.5438/0003\n10.5438/0004\n10.5438/0005\n10.5438/0006\n10.5438/EXAMPLE-FULL\n10.5438/MDS-CLIENT-RUBY-TEST\n").to_stdout
32
+ subject.options = api_options.merge(limit: 3)
33
+ expect { subject.get "all" }.to output("10.23725/0000-03VC\n10.23725/0000-0A53\n10.23725/GQZDGNZW\n").to_stdout
33
34
  end
34
35
 
35
36
  it 'should get doi' do
@@ -44,6 +45,13 @@ describe Cirneco::Doi do
44
45
  end
45
46
  end
46
47
 
48
+ context "accession_number" do
49
+ it 'generates an accession_number' do
50
+ subject.options = { number: number }
51
+ expect { subject.accession_number }.to output("MS-123\n").to_stdout
52
+ end
53
+ end
54
+
47
55
  context "base32" do
48
56
  it 'generates a doi' do
49
57
  subject.options = { number: number, prefix: prefix }
@@ -69,45 +77,39 @@ describe Cirneco::Doi do
69
77
  end
70
78
  end
71
79
 
72
- context "mint and hide DOIs", :order => :defined do
80
+ context "mint and hide DOIs", vcr: true, :order => :defined do
73
81
  it 'mints a doi' do
74
- subject.options = { csl: csl, bibliography: bibliography }
82
+ subject.options = mint_options
75
83
  expect { subject.mint filepath }.to output("DOI 10.5072/0000-03VC minted for #{filename}\n").to_stdout
76
84
  end
77
85
 
78
86
  it 'hides a doi' do
79
- subject.options = { csl: csl, bibliography: bibliography }
80
- expect { subject.hide filepath }.to output("DOI 10.5072/0000-03VC hidden for #{filename}\n").to_stdout
87
+ filename = 'cool-dois-minted.html'
88
+ filepath = fixture_path + filename
89
+ subject.options = mint_options.merge(filepath: filepath)
90
+ expect { subject.hide filepath }.to output("DOI 10.5072/0000-03WD hidden for #{filename}\n").to_stdout
81
91
  end
82
92
 
83
93
  it 'mints and hides a doi' do
84
- subject.options = { csl: csl, bibliography: bibliography }
94
+ subject.options = mint_options
85
95
  expect { subject.mint_and_hide filepath }.to output("DOI 10.5072/0000-03VC minted and hidden for #{filename}\n").to_stdout
86
96
  end
87
97
 
88
- it 'mints dois for contents of a folder' do
89
- subject.options = { csl: csl, bibliography: bibliography }
90
- expect { subject.mint fixture_path }.to output("DOI 10.5072/0000-03VC minted for #{filename}\n").to_stdout
91
- end
92
-
93
- it 'hides dois for contents of a folder' do
94
- subject.options = { csl: csl, bibliography: bibliography }
95
- expect { subject.hide fixture_path }.to output("DOI 10.5072/0000-03VC hidden for #{filename}\n").to_stdout
96
- end
97
-
98
- it 'mints and hides dois for contents of a folder' do
99
- subject.options = { csl: csl, bibliography: bibliography }
100
- expect { subject.mint_and_hide fixture_path }.to output("DOI 10.5072/0000-03VC minted and hidden for #{filename}\n").to_stdout
98
+ it 'mints dois for list of urls' do
99
+ subject.options = mint_options
100
+ expect { subject.mint [filepath] }.to output("DOI 10.5072/0000-03VC minted for #{filename}\n").to_stdout
101
101
  end
102
102
 
103
- it 'should ignore non-markdown file for mint file' do
104
- filename = 'cool-dois.yml'
105
- expect { subject.mint fixture_path + filename }.to output("File #{filename} ignored: not a markdown file\n").to_stdout
103
+ it 'hides dois for list of urls' do
104
+ filename = 'cool-dois-minted.html'
105
+ filepath = fixture_path + filename
106
+ subject.options = mint_options.merge(filepath: filepath)
107
+ expect { subject.hide [filepath] }.to output("DOI 10.5072/0000-03WD hidden for #{filename}\n").to_stdout
106
108
  end
107
109
 
108
- it 'should ignore non-markdown file for hide file' do
109
- filename = 'cool-dois.yml'
110
- expect { subject.hide fixture_path + filename }.to output("File #{filename} ignored: not a markdown file\n").to_stdout
110
+ it 'mints and hides dois for list of urls' do
111
+ subject.options = mint_options
112
+ expect { subject.mint_and_hide [filepath] }.to output("DOI 10.5072/0000-03VC minted and hidden for #{filename}\n").to_stdout
111
113
  end
112
114
  end
113
115
  end
@@ -0,0 +1,404 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <!-- (1) Optimize for mobile versions: http://goo.gl/EOpFl -->
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <!-- (1) force latest IE rendering engine: bit.ly/1c8EiC9 -->
8
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
9
+
10
+
11
+ <title>Cool DOI's</title>
12
+ <meta name="description" content="In 1998 Tim Berners-Lee coined the term cool URIs (1998), that is URIs that don’t change. We know that URLs referenced in the scholarly literature are often not cool, leading to link rot (Klein et al., 2014) and making it hard or impossible to find..." />
13
+
14
+ <meta name="HandheldFriendly" content="True" />
15
+ <meta name="MobileOptimized" content="320" />
16
+ <meta name="apple-mobile-web-app-capable" content="yes">
17
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
18
+
19
+ <!-- DublinCore Metadata -->
20
+ <meta property="dc:title" content="Cool DOI's" />
21
+ <meta property="dc:format" content="text/html" />
22
+ <meta property="dc:language" content="en" />
23
+ <meta property="dc:rights" content="CC-BY" />
24
+ <meta property="dc:source" content="DataCite Blog" />
25
+ <meta property="dc:subject" content="Scholarly Communication" />
26
+ <meta property="dc:type" content="website" />
27
+
28
+ <meta name="twitter:card" content="summary" />
29
+ <meta name="twitter:site" content="datacite" />
30
+ <meta name="twitter:title" content="Cool DOI's" />
31
+ <meta name="twitter:image" content="https://blog.datacite.org/images/2016/12/cool-dois.png" />
32
+ <meta name="twitter:description" content="In 1998 Tim Berners-Lee coined the term cool URIs (1998), that is URIs that don’t change. We know that URLs referenced in the scholarly literature are often not cool, leading to link rot (Klein et al., 2014) and making it hard or impossible to find..." />
33
+
34
+ <meta property="og:site_name" content="Cool DOI's" />
35
+ <meta property="og:description" content="In 1998 Tim Berners-Lee coined the term cool URIs (1998), that is URIs that don’t change. We know that URLs referenced in the scholarly literature are often not cool, leading to link rot (Klein et al., 2014) and making it hard or impossible to find..." />
36
+ <meta property="og:image" content="https://blog.datacite.org/images/2016/12/cool-dois.png" />
37
+ <meta property="og:type" content="blog" />
38
+
39
+ <link href="//fonts.googleapis.com/css?family=Libre+Baskerville:400,400i,700" rel="stylesheet">
40
+ <link href='//fonts.googleapis.com/css?family=Raleway:400,600,400italic,600italic' rel='stylesheet' type='text/css'>
41
+ <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css" rel="stylesheet" type='text/css'>
42
+ <link href="//localhost:4568/stylesheets/datacite.css" rel='stylesheet' type='text/css'>
43
+
44
+ <link href="/images/favicon.ico" rel="icon" type="image/ico" />
45
+
46
+ <script type="application/ld+json">
47
+ {
48
+ "@context": "http://schema.org",
49
+ "@type": "BlogPosting",
50
+ "@id": "https://doi.org/10.5072/0000-03WD",
51
+ "name": "Cool DOI's",
52
+ "alternateName":"MS-124",
53
+ "url": "https://blog.datacite.org/cool-dois/",
54
+ "author": [
55
+ {
56
+ "@type": "Person",
57
+ "@id": "http://orcid.org/0000-0003-1419-2405",
58
+ "givenName": "Martin",
59
+ "familyName": "Fenner",
60
+ "name": "Martin Fenner"
61
+ }
62
+ ],
63
+ "publisher": {
64
+ "@type": "Organization",
65
+ "name": "DataCite"
66
+ },
67
+ "dateCreated": "2016-12-15",
68
+ "datePublished": "2016-12-15",
69
+ "dateModified": "2016-12-15",
70
+ "keywords": "doi, featured",
71
+ "version": "1.0",
72
+ "description": "In 1998 Tim Berners-Lee coined the term cool URIs (1998), that is URIs that don’t change. We know that URLs referenced in the scholarly literature are often not cool, leading to link rot (Klein et al., 2014) and making it hard or impossible to find...",
73
+ "license": "https://creativecommons.org/licenses/by/4.0/",
74
+ "image": "https://blog.datacite.org/images/2016/12/cool-dois.png",
75
+ "encoding": {
76
+ "@type": "MediaObject",
77
+ "@id": "https://raw.githubusercontent.com/datacite/blog/master/source/posts/cool-dois.html.md",
78
+ "fileFormat": "text/markdown"
79
+ },
80
+ "isPartOf": {
81
+ "@type": "Blog",
82
+ "@id": "https://blog.datacite.org",
83
+ "name": "DataCite Blog"
84
+ },
85
+ "citation": [
86
+ {
87
+ "@type": "CreativeWork",
88
+ "@id": "https://www.w3.org/Provider/Style/URI"
89
+ },
90
+ {
91
+ "@type": "CreativeWork",
92
+ "@id": "https://doi.org/10.1371/journal.pone.0115253"
93
+ }
94
+ ]
95
+ }
96
+ </script>
97
+ </head>
98
+ <body>
99
+
100
+ <!-- header start -->
101
+
102
+ <div class="header" id="navtop">
103
+ <div class="navbar navbar-white navbar-static-top" role="navigation">
104
+ <div class="container-fluid">
105
+ <div class="navbar-header"
106
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
107
+ <span class="sr-only">Toggle navigation</span>
108
+ <span class="icon-bar"></span>
109
+ <span class="icon-bar"></span>
110
+ <span class="icon-bar"></span>
111
+ </button>
112
+ <a class="navbar-brand" href="/">DataCite Blog</a>
113
+ </div>
114
+ <div class="navbar-collapse collapse">
115
+ <ul class="nav navbar-nav navbar-right">
116
+ <li class="dropdown">
117
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="support">Support <span class="caret"></a>
118
+ <ul class="dropdown-menu" role="menu">
119
+ <li><a href="mailto:support@datacite.org">Email</a></li>
120
+ <li><a href="https://github.com/datacite/blog">Source Code</a></li>
121
+ </ul>
122
+ </li>
123
+ <li class="dropdown">
124
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="sites"><i class='fa fa-th'></i> <span class="caret"></span></a>
125
+ <ul class="dropdown-menu" role="menu">
126
+ <li>
127
+ <a href='https://api.datacite.org'>
128
+ <i class='fa fa-cogs fa-fw'></i>
129
+ API
130
+ </a>
131
+ </li>
132
+ <li>
133
+ <a href='https://blog.datacite.org'>
134
+ <i class='fa fa-rss fa-fw'></i>
135
+ Blog
136
+ </a>
137
+ </li>
138
+ <li>
139
+ <a href='http://citation.crosscite.org'>
140
+ <i class='fa fa-file-text-o fa-fw'></i>
141
+ Citation Formatter
142
+ </a>
143
+ </li>
144
+ <li>
145
+ <a href='https://data.datacite.org'>
146
+ <i class='fa fa-repeat fa-fw'></i>
147
+ Content Resolver
148
+ </a>
149
+ </li>
150
+ <li>
151
+ <a href='https://www.datacite.org'>
152
+ <i class='fa fa-globe fa-fw'></i>
153
+ Homepage
154
+ </a>
155
+ </li>
156
+ <li>
157
+ <a href='https://mds.datacite.org'>
158
+ <i class='fa fa-database fa-fw'></i>
159
+ MDS
160
+ </a>
161
+ </li>
162
+ <li>
163
+ <a href='https://oai.datacite.org'>
164
+ <i class='fa fa-table fa-fw'></i>
165
+ OAI-PMH
166
+ </a>
167
+ </li>
168
+ <li>
169
+ <a href='https://profiles.datacite.org'>
170
+ <i class='fa fa-user fa-fw'></i>
171
+ Profiles
172
+ </a>
173
+ </li>
174
+ <li>
175
+ <a href='https://schema.datacite.org'>
176
+ <i class='fa fa-file-code-o fa-fw'></i>
177
+ Schema
178
+ </a>
179
+ </li>
180
+ <li>
181
+ <a href='https://search.datacite.org'>
182
+ <i class='fa fa-search fa-fw'></i>
183
+ Search
184
+ </a>
185
+ </li>
186
+ <li>
187
+ <a href='https://stats.datacite.org'>
188
+ <i class='fa fa-bar-chart fa-fw'></i>
189
+ Statistics
190
+ </a>
191
+ </li>
192
+ <li>
193
+ <a href='http://status.datacite.org'>
194
+ <i class='fa fa-calendar-check-o fa-fw'></i>
195
+ Status
196
+ </a>
197
+ </li>
198
+ </ul>
199
+ </li>
200
+ </ul>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <!-- header end -->
207
+
208
+ <div class="wrapper">
209
+ <div class="section section-white">
210
+ <div class="container-fluid">
211
+ <div class="row row-section">
212
+ <div class="col-md-8 col-md-offset-2 post-content">
213
+ <a name="topofpage"></a>
214
+ <div class="post-meta">
215
+ <h1>Cool DOI's</h1>
216
+ December 15, 2016 by Martin Fenner
217
+ • <span class="post-reading-time"></span> read
218
+ <p class="doi"><a href="https://doi.org/10.5438/55E5-T5C0">https://doi.org/10.5438/55E5-T5C0</a></p>
219
+ </div>
220
+
221
+ <p>In 1998 Tim Berners-Lee coined the term cool URIs <span class="citation">(<a href="#ref-https://www.w3.org/Provider/Style/URI">1998</a>)</span>, that is URIs that don’t change. We know that URLs referenced in the scholarly literature are often not cool, leading to link rot <span class="citation">(Klein et al., <a href="#ref-https://doi.org/10.1371/journal.pone.0115253">2014</a>)</span> and making it hard or impossible to find the referenced resource.</p>
222
+ <p>Cool URIs are, of course, a fundamental principle behind DOIs, with the two important concepts <a href="https://www.doi.org/doi_handbook/3_Resolution.html"><em>resolution</em></a> (it is very hard to maintain a URL directly pointing at a resource) and <a href="https://www.doi.org/doi_handbook/6_Policies.html"><em>policies</em></a> (that all DOI registration agencies and organizations minting DOIs agree to maintain the redirection). The third essential element for DOIs, their <a href="https://www.doi.org/doi_handbook/4_Data_Model.html"><em>data model</em></a>, is not directly about persistent linking, but about the discoverability of the linked resources via standard metadata in a central index.</p>
223
+ <p>All DOIs, expressed as HTTP URI, are therefore cool URIs. So what is a cool DOI? And, furthermore, how to create and use them? To understand what a cool DOI is, we have to explain the three parts that make up a DOI:</p>
224
+ <div class="figure">
225
+ <img src="/images/2016/12/doi-parts.png" />
226
+
227
+ </div>
228
+ <h3 id="proxy">Proxy</h3>
229
+ <p>The proxy is not part of the DOI specification, but almost all scholarly DOIs that users encounter today will be expressed as HTTP URLs. DataCite recommends that all DOIs are displayed as permanent URLs, consistent with the recommendations of other DOI registration agencies, e.g. the <a href="http://www.crossref.org/02publishers/doi_display_guidelines.html">Crossref DOI display guidelines</a>. When the DOI system was originally designed, it was thought that the DOI protocol would become widely used, but that clearly has not happened and displaying DOIs as <strong>doi:10.5281/ZENODO.31780</strong> is therefore not recommended.</p>
230
+ <p>The DOI proxy enables the functionality of expressing DOIs as HTTP URIs. Users should also be aware of two these two recommendations:</p>
231
+ <ul>
232
+ <li>Use <a href="https://www.doi.org/doi_proxy/proxy_policies.html">doi.org</a> instead of dx.doi.org as DNS name</li>
233
+ <li>Use the HTTPS protocol instead of HTTP protocol</li>
234
+ </ul>
235
+ <p>Ed Pentz from Crossref makes the case for HTTPS in a <a href="http://blog.crossref.org/2016/09/new-crossref-doi-display-guidelines.html">September blog post</a>. The web, and therefore also the scholarly web, is moving to HTTPS as the default. It is important that the DOI proxy redirects to HTTPS URLs, and it will take some time until all DataCite data centers use HTTPS for the landing pages their DOIs redirects to.</p>
236
+ <p>What many users don’t know is that doi.org is not the only proxy server for DOIs. DOIs use the handle system and any handle server will resolve a DOI, just as doi.org will resolve any handle. This means that <a href="https://hdl.handle.net/10.5281/ZENODO.31780" class="uri">https://hdl.handle.net/10.5281/ZENODO.31780</a> will resolve to the landing page for that DOI and that <a href="http://doi.org/10273/BGRB5054RX05201" class="uri">http://doi.org/10273/BGRB5054RX05201</a> is a handle (for a <a href="http://www.igsn.org/">IGSN</a>) and not a DOI.</p>
237
+ <h3 id="prefix">Prefix</h3>
238
+ <p>The DOI prefix is used as a namespace so that DOIs are globally unique without requiring global coordination for every new identifier. Prefixes in the handle system and therefore for DOIs are numbers without any semantic meaning. One lesson learned with persistent identifiers is that adding meaning to the identifier (e.g. by using a prefix with the name of the data repository) is always dangerous, because – despite best intentions – all names can change over time.</p>
239
+ <p>Since the DOI prefix is a namespace to keep DOIs globally unique, there is usually no need for multiple prefixes for one organization managing DOI assignment. The tricky part is that these responsibilities can change, e.g. when an organization manages multiple repositories and one of them is migrated to another organization. It therefore makes sense to assign one prefix per list of resources that always stays together, e.g. one repository. It is possible that one prefix is managed by multiple organizations (as long as they use the same DOI registration agency), but that makes DOI management more complex.</p>
240
+ <h3 id="suffix">Suffix</h3>
241
+ <p>The suffix for a DOI can be (almost) any string. Which is both a feature and a curse. It is a feature because it gives maximal flexibility, for example when migrating existing identifiers to the DOI system. And it is a curse because it not always works well in the web context, as the list of characters allowed in a URL is limited. A good example of this are SICIs (<a href="https://en.wikipedia.org/wiki/Serial_Item_and_Contribution_Identifier">Serial Item and Contribution Identifier</a>), they were defined in 1996 before the DOI system was implemented, and could then be migrated to DOIs. Unfortunately they can contain many characters that are problematic in a URL or make it difficult to validate the DOI, as in <a href="https://doi.org/10.1002/(sici)1099-1409(199908/10)3:6/7%3C672::aid-jpp192%3E3.0.co;2-8" class="uri">https://doi.org/10.1002/(sici)1099-1409(199908/10)3:6/7&lt;672::aid-jpp192&gt;3.0.co;2-8</a>. A Crossref <a href="http://blog.crossref.org/2015/08/doi-regular-expressions.html">blog post</a> by Andrew Gilmartin gives a good overview about the characters found in DOIs and suggests the following regular expression to check for valid DOIs:</p>
242
+ <pre><code>/^10.\d{4,9}/[-._;()/:A-Z0-9]+$/i</code></pre>
243
+ <p>SICIs demonstrate two other pitfalls:</p>
244
+ <ul>
245
+ <li>they contain semantic information (ISSN, volume, number, etc.) that may change over time, and</li>
246
+ <li>they are long, difficult to transcribe, with characters not allowed in URLs, and not very human-readable.</li>
247
+ </ul>
248
+ <p>Semantic information might also lead users to expect certain functionalities. A common pattern that we see at DataCite is to include information about the version or parent in the suffix, e.g. <a href="https://doi.org/10.6084/M9.FIGSHARE.3501629.V1" class="uri">https://doi.org/10.6084/M9.FIGSHARE.3501629.V1</a> or <a href="https://doi.org/10.5061/DRYAD.0SN63/7" class="uri">https://doi.org/10.5061/DRYAD.0SN63/7</a>. While the decision on what to put into the suffix is up to each data center, we should make sure users don’t think that these are functionalities of the DOI system (e.g. that adding <strong>.V2</strong> to any DOI name will resolve to version 2 of that resource).</p>
249
+ <p>Another issue to keep in mind when assigning suffixes is that DOIs – in contrast to HTTP URIs – are case-insensitive, <a href="https://doi.org/10.5281/ZENODO.31780" class="uri">https://doi.org/10.5281/ZENODO.31780</a> and <a href="https://doi.org/10.5281/zenodo.31780" class="uri">https://doi.org/10.5281/zenodo.31780</a> are the same DOI. All DOIs are <a href="https://www.doi.org/doi_handbook/2_Numbering.html#2.4">converted to upper case</a> upon registration and DOI resolution, but DOIs are not consistently displayed in such a way.</p>
250
+ <h3 id="generating-cool-dois">Generating cool DOIs</h3>
251
+ <p>With all that, what should the ideal DOI look like? Its suffix should be:</p>
252
+ <ul>
253
+ <li>opaque without semantic information</li>
254
+ <li>work well in a web environment, avoiding characters problematic in URLs</li>
255
+ <li>short and human-readable</li>
256
+ <li>Resistant to transcription errors</li>
257
+ <li>easy to generate</li>
258
+ </ul>
259
+ <p>On Tuesday DataCite released a tool that helps generating such a suffix, an open source command line tool called <a href="https://github.com/datacite/cirneco">cirneco</a> (a lot of our open source software uses Italian dog breed names). Cirneco is a Ruby gem that can be installed via</p>
260
+ <pre><code>gem install cirneco</code></pre>
261
+ <p>Cirneco uses base32 encoding, as <a href="http://www.crockford.com/wrmg/base32.html">described</a> by Douglas Crockford. The encoding starts with a randomly generated number to guarantee uniqueness of the identifier, and then encodes the number into a string that uses all numbers and uppercase letters. It avoids the letters I, O and L as they can be confused with the letter 1 and 0, using 32 characters (and 5 checksum characters) in total. The last character is a checksum. The resulting string from cirneco always has a length of 8 characters, in groups of 4 separated by a hyphen to help with readability. The advantage of base32 encoding over using only numbers (as for example ORCID is doing) is that the resulting string becomes much more compact, the available 7 characters (plus one for the checksum) can encode 34,359,738,367 strings, compared to 10 million when only using numbers. This number is large enough that the resulting suffix will not only be unique for a given prefix, but also unique for all DOIs (there is a very small chance to get the same random number twice, but this will be rejected when trying to register the DOI).</p>
262
+ <p>Another common way to generate random strings would have been universally unique identifiers (<a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUID</a>), but they are long and not very human-readable, e.g. <a href="https://doi.org/10.4233/UUID:6D192FE2-DE18-4556-873A-D3CD56AB96A6" class="uri">https://doi.org/10.4233/UUID:6D192FE2-DE18-4556-873A-D3CD56AB96A6</a>.</p>
263
+ <p>An example DOI generated by cirneco would be</p>
264
+ <pre><code>cirneco doi generate --prefix 10.5555
265
+ 10.5555/KVTD-VPWM</code></pre>
266
+ <p>The generated DOI is short enough that it should work well in places where space is limited, providing an alternative to the <a href="http://shortdoi.org/">ShortDOI</a> service which shortens existing DOIs, but does this by adding another layer on top of the DOI proxy.</p>
267
+ <p>Another cirneco command checks that this is a valid bas32 string using the checksum</p>
268
+ <pre><code>cirneco doi check 10.5555/KVTD-VPWM
269
+ Checksum for 10.5555/KVTD-VPWM is valid</code></pre>
270
+ <p>This can be used to quickly verify a DOI, e.g. in a web form or API. The Ruby base32 encoding library used by cirneco is open source (<a href="https://github.com/datacite/base32" class="uri">https://github.com/datacite/base32</a>. I added the checksum to the existing library), and implementations of the Crockford base32 encoding pattern are available in many other languages, including <a href="https://github.com/jbittel/base32-crockford">Python</a>, <a href="https://github.com/dflydev/dflydev-base32-crockford">PHP</a>, <a href="https://www.npmjs.com/package/base32-crockford">Javascript</a>, <a href="http://stackoverflow.com/questions/22385467/crockford-base32-encoding-for-large-number-java-implementation">Java</a>, <a href="https://github.com/richardlehane/crock32">Go</a> and <a href="https://crockfordbase32.codeplex.com/">.NET</a>.</p>
271
+ <p>To answer the question raised at the beginning: a cool DOI is a DOI expressed as HTTPS URI using the doi.org proxy and using a base32-encoded suffix, for example <strong>https://doi.org/10.5555/KVTD-VPWM</strong>. This DOI works well in a web environment, is human readable, easy to parse and detect (e.g. in text mining), and can be generated using an algorithm that is well understood and supported.</p>
272
+ <div class="figure">
273
+ <img src="/images/2016/12/cool-dois.svg" />
274
+
275
+ </div>
276
+ <h3 id="references" class="unnumbered">References</h3>
277
+ <div id="refs" class="references">
278
+ <div id="ref-https://www.w3.org/Provider/Style/URI">
279
+ <p>Berners-Lee, T. (1998). Hypertext Style: Cool URIs don’t change. Retrieved from <a href="https://www.w3.org/Provider/Style/URI" class="uri">https://www.w3.org/Provider/Style/URI</a></p>
280
+ </div>
281
+ <div id="ref-https://doi.org/10.1371/journal.pone.0115253">
282
+ <p>Klein, M., Sompel, H. V. de, Sanderson, R., Shankar, H., Balakireva, L., Zhou, K., &amp; Tobin, R. (2014). Scholarly Context Not Found: One in Five Articles Suffers from Reference Rot. <em>PLOS ONE</em>, <em>9</em>(12), e115253. <a href="http://doi.org/10.1371/journal.pone.0115253" class="uri">http://doi.org/10.1371/journal.pone.0115253</a></p>
283
+ </div>
284
+ </div>
285
+
286
+ <hr width="80%">
287
+ </div>
288
+ </div>
289
+ <div class="row">
290
+ <div class="col-md-5 col-md-offset-2 post-content">
291
+ <div class="bottom-teaser cf">
292
+ <div class="isLeft">
293
+ <section class="author">
294
+ <div class="author-image" style="background-image: url(https://www.gravatar.com/avatar/434592a097e91261792ebd6b492042bc?s=250&d=mm&r=x)">Blog Logo</div>
295
+ <h4>Martin Fenner</h4>
296
+ <p class="bio">DataCite Technical Director</p>
297
+ <p class="orcid"><a href="http://orcid.org/0000-0003-1419-2405">http://orcid.org/0000-0003-1419-2405</a></p>
298
+ <div class="clearfix"></div>
299
+ <h4>Cool DOI's</h4>
300
+ <p class="published"><a href="https://doi.org/10.5438/55E5-T5C0">https://doi.org/10.5438/55E5-T5C0</a>
301
+ <p class="published"><i class="fa fa-calendar"></i> <time datetime="2016-12-15 00:00">December 15, 2016</time></p>
302
+ <p class="published"><i class="fa fa-history"></i> <a href="https://github.com/datacite/blog/commits/master/source/posts/cool-dois.html.md">History</a></p>
303
+ <p class="published">© 2016 Martin Fenner. Distributed under the terms of the <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution license</a>.</p>
304
+ <p class="published">
305
+ <i class="fa fa-tags"></i>
306
+ <a href="/index.html?tag=doi">doi</a>, <a href="/index.html?tag=featured">featured</a>
307
+ </p>
308
+ </section>
309
+ </div>
310
+ </div>
311
+
312
+ </div>
313
+ <div class="col-md-2 col-md-offset-1">
314
+ <div class="bottom-teaser cf">
315
+ <div class="isLeft">
316
+ <h5 class="index-headline featured"><span>Share on</span></h5>
317
+ <a class="icon-twitter" href="http://twitter.com/share?text=On the @datacite blog: Cool DOI's&amp;url=http://localhost:4567/cool-dois/"
318
+ onclick="window.open(this.href, 'twitter-share', 'width=550,height=255');return false;">
319
+ <i class="fa fa-twitter fa-2x"></i><span class="hidden">twitter</span>
320
+ </a>
321
+ <a class="icon-facebook" href="https://www.facebook.com/sharer.php?t=On the @datacite blog: Cool DOI's&amp;u=http://localhost:4567/cool-dois/"
322
+ onclick="window.open(this.href, 'facebook-share', 'width=550,height=255');return false;">
323
+ <i class="fa fa-facebook fa-2x"></i><span class="hidden">facebook</span>
324
+ </a>
325
+ </div>
326
+ </div>
327
+
328
+ </div>
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </div>
333
+
334
+ <!-- footer start -->
335
+
336
+ <footer class='row footer'>
337
+ <div class="container-fluid">
338
+ <div class='col-md-3 col-sm-4'>
339
+ <h4>About DataCite</h4>
340
+ <ul>
341
+ <li><a href="https://www.datacite.org/mission.html">What we do</a></a></li>
342
+ <li><a href="https://www.datacite.org/board.html">Board</a></a></li>
343
+ <li><a href="https://www.datacite.org/steering.html">Steering groups</a></a></li>
344
+ <li><a href="https://www.datacite.org/staff.html">Staff</a></a></li>
345
+ <li><a href="https://www.datacite.org/jobopportunities.html">Job opportunities</a></a></li>
346
+ </ul>
347
+ </div>
348
+ <div class='col-md-3 col-sm-4'>
349
+ <h4>Services</h4>
350
+ <ul>
351
+ <li><a href="https://www.datacite.org/dois.html">Assign DOIs</a></a></li>
352
+ <li><a href="https://www.datacite.org/search.html">Metadata search</a></a></li>
353
+ <li><a href="https://www.datacite.org/eventdata.html">Event data</a></a></li>
354
+ <li><a href="https://www.datacite.org/profiles.html">Profiles</a></a></li>
355
+ <li><a href="https://www.datacite.org/re3data.html">re3data</a></a></li>
356
+ <li><a href="https://www.datacite.org/citation.html">Citation formatter</a></a></li>
357
+ <li><a href="https://www.datacite.org/stats.html">Statistics</a></a></li>
358
+ <li><a href="https://www.datacite.org/service.html">Service status</a></a></li>
359
+ <li><a href="https://www.datacite.org/content.html">Content negotiation</a></a></li>
360
+ <li><a href="https://www.datacite.org/oaipmh.html">OAI-PMH</a></a></li>
361
+ <li><a href="https://www.datacite.org/test.html">Test environment</a></a></li>
362
+ </ul>
363
+ </div>
364
+ <div class='col-md-3 col-sm-4'>
365
+ <h4>Resources</h4>
366
+ <ul>
367
+ <li><a href="https://schema.datacite.org">Metadata schema</a></a></li>
368
+ <li><a href="https://www.datacite.org/technical.html">Technical documentation</a></a></li>
369
+ <li><a href="https://www.datacite.org/outreach.html">Outreach material</a></a></li>
370
+ <li><a href="https://www.datacite.org/events.html">Events</a></a></li>
371
+ </ul>
372
+ <h4>Community</h4>
373
+ <ul>
374
+ <li><a href="https://www.datacite.org/members.html">Members</a></a></li>
375
+ <li><a href="https://www.datacite.org/partners.html">Partners</a></a></li>
376
+ <li><a href="https://www.datacite.org/steering.html">Steering groups</a></a></li>
377
+ <li><a href="https://www.datacite.org/events.html">Events</a></a></li>
378
+ </ul>
379
+ </div>
380
+ <div class='col-md-3'>
381
+ <h4 class="share">Contact us</h4>
382
+ <a href='mailto:support@datacite.org' class="share">
383
+ <i class='fa fa-at'></i>
384
+ </a>
385
+ <a href='https://blog.datacite.org' class="share">
386
+ <i class='fa fa-rss'></i>
387
+ </a>
388
+ <a href='https://twitter.com/datacite' class="share">
389
+ <i class='fa fa-twitter'></i>
390
+ </a>
391
+ <a href='https://www.linkedin.com/company/datacite' class="share">
392
+ <i class='fa fa-linkedin'></i>
393
+ </a>
394
+ <ul class="share">
395
+ <li><a href="https://www.datacite.org/terms.html">Terms and conditions</a></a></li>
396
+ <li><a href="https://www.datacite.org/privacy.html">Privacy policy</a></a></li>
397
+ <li><a href="https://www.datacite.org/acknowledgments.html">Acknowledgements</a></a></li>
398
+ </ul>
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ </body>
404
+ </html>