libdoi 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/libdoi.rb +108 -0
- data/lib/libdoi/network.rb +80 -0
- data/test/test-libdoi-network.rb +36 -0
- data/test/test-libdoi.rb +81 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 43fa380332a5eb90e5256f97fa57eaf92dcb96fd
|
4
|
+
data.tar.gz: 3e2298e22fb298f9e7ebc787fb2e064ed8e9b570
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e504fdb335e4856b1b0cdd9eda88ac46e31e8fcf1d41f281a8aca18148586fa0061b95aa098feae3bd6aeb97fdef6929770c3615ddb441dd1a266dc983944762
|
7
|
+
data.tar.gz: b20963646458837690fd9e39cba33a17a44d0e0c881152c35c5fbf03e6fff752c1284e4759b5f434b397799a8e10f68bdc5830483c92c717846614e364250ced
|
data/lib/libdoi.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
class DOI
|
7
|
+
|
8
|
+
VERSION = '1.0.0'
|
9
|
+
|
10
|
+
def initialize dir, reg, dss #:nodoc:
|
11
|
+
@dir = dir
|
12
|
+
@reg = reg
|
13
|
+
@dss = dss
|
14
|
+
end
|
15
|
+
attr_reader :dir, :reg, :dss
|
16
|
+
|
17
|
+
##
|
18
|
+
# Concatenation--Returns a new DOI containing +other+ concatenated to this DOI's suffix string.
|
19
|
+
#
|
20
|
+
def + other
|
21
|
+
self.class.new @dir, @reg, @dss + other.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Append--Concatenates the given object to this DOI's suffix string.
|
26
|
+
#
|
27
|
+
def << other
|
28
|
+
@dss << other.to_s
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns a String that represents this DOI.
|
34
|
+
#
|
35
|
+
# * prefix: Prepends 'doi:' to the returned string.
|
36
|
+
#
|
37
|
+
def to_s prefix: true
|
38
|
+
(prefix ? 'doi:' : '') + "#{@dir}.#{@reg}/#{@dss}"
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Returns a URI.
|
43
|
+
#
|
44
|
+
# For example: "https://doi.org/10.1000/foo%23bar"
|
45
|
+
#
|
46
|
+
# * info: Returns an 'info:' URI instead of 'https:'
|
47
|
+
#
|
48
|
+
def to_uri info: false
|
49
|
+
if info
|
50
|
+
URI(_info_uri)
|
51
|
+
else
|
52
|
+
URI(_http_url)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class <<self
|
57
|
+
##
|
58
|
+
# Parses the given string as a DOI.
|
59
|
+
#
|
60
|
+
# Raises an ArgumentError if parsing fails.
|
61
|
+
#
|
62
|
+
def parse str
|
63
|
+
str = "#{str}"
|
64
|
+
if str =~ %r[^https?://(?:(?:dx\.)?doi\.org|doi\.acm\.org|doi\.ieeecomputersociety\.org)/+(?:doi:)?(.*)]i
|
65
|
+
# It looks like a HTTP proxy URL.
|
66
|
+
doi = CGI.unescape $1
|
67
|
+
elsif str =~ %r[^info:doi/(.*)]i
|
68
|
+
# It looks like an info URI.
|
69
|
+
doi = CGI.unescape $1
|
70
|
+
else
|
71
|
+
# It's probably a DOI string.
|
72
|
+
doi = str.sub %r[^doi:\s*]i, ''
|
73
|
+
end
|
74
|
+
|
75
|
+
# ANSI/NISO Z39.84-2005
|
76
|
+
# <http://www.niso.org/apps/group_public/download.php/6587/Syntax%20for%20the%20Digital%20Object%20Identifier.pdf>
|
77
|
+
if doi =~ %r[^(10)\.([^/]+)/(\p{Graph}(?:[^/]\p{Graph}*)?)$]
|
78
|
+
# FIXME: $2 and $3 may contain characters outside of /\p{Graph}/
|
79
|
+
new $1, $2, $3
|
80
|
+
else
|
81
|
+
raise ArgumentError, "'#{str}' is not a valid DOI string";
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
InfoURI = 'info:doi/'.freeze
|
89
|
+
HTTPURI = 'https://doi.org/'.freeze
|
90
|
+
|
91
|
+
# Returns a percent-encoded "dir.reg/dss" string.
|
92
|
+
def _uri_path
|
93
|
+
"#{@dir}.#{CGI.escape @reg}/#{CGI.escape @dss}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns an "info:doi/..." URI string.
|
97
|
+
def _info_uri
|
98
|
+
InfoURI + _uri_path
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns a "https://doi.org/..." URI string.
|
102
|
+
def _http_url
|
103
|
+
HTTPURI + _uri_path
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../libdoi'
|
4
|
+
|
5
|
+
require 'net/http'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class DOI
|
9
|
+
|
10
|
+
##
|
11
|
+
# Looks for a DOI at doi.org.
|
12
|
+
#
|
13
|
+
# Returns a URI if it finds a match, otherwise returns nil.
|
14
|
+
#
|
15
|
+
def find
|
16
|
+
_http_get(self.to_uri) do |response|
|
17
|
+
# FIXME: this is both presumptuous and intolerant
|
18
|
+
loc = response['Location']
|
19
|
+
return URI(loc) if loc
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Gets data about this DOI from CrossRef.
|
26
|
+
#
|
27
|
+
def data
|
28
|
+
uri = URI(_data_url)
|
29
|
+
_http_get(uri, 'Accept'=>JSON_Type) do |response|
|
30
|
+
return JSON.parse(response.body) if response.code.to_i == 200
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
class <<self
|
36
|
+
##
|
37
|
+
# Looks for a DOI at doi.org.
|
38
|
+
#
|
39
|
+
# Returns a URI if it finds a match, otherwise returns nil.
|
40
|
+
#
|
41
|
+
def find doi
|
42
|
+
doi = parse(doi) unless doi.is_a? DOI
|
43
|
+
doi.find
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Gets data about a DOI from CrossRef.
|
48
|
+
#
|
49
|
+
def data doi
|
50
|
+
doi = parse(doi) unless doi.is_a? DOI
|
51
|
+
doi.data
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
JSON_Type = 'application/vnd.citationstyles.csl+json'.freeze
|
58
|
+
UAString = "ruby/#{::RUBY_VERSION} libdoi/#{DOI::VERSION}".freeze
|
59
|
+
CrossRefURL = 'https://data.crossref.org/'.freeze
|
60
|
+
|
61
|
+
def _http_get uri, opts={}, &block
|
62
|
+
Net::HTTP.start(uri.host, uri.port,
|
63
|
+
:use_ssl=>uri.is_a?(URI::HTTPS),
|
64
|
+
) do |http|
|
65
|
+
query = Net::HTTP::Get.new uri, 'Accept-Encoding'=>''
|
66
|
+
query['Connection'] = 'close'
|
67
|
+
query['User-Agent'] = UAString
|
68
|
+
opts.each_pair {|k,v| query[k] = v }
|
69
|
+
http.request query, &block
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns a data.crossref.org URI string.
|
74
|
+
def _data_url
|
75
|
+
CrossRefURL + _uri_path
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'uri'
|
3
|
+
$VERBOSE = true
|
4
|
+
|
5
|
+
require_relative '../lib/libdoi/network'
|
6
|
+
class Test_libdoi_network < Test::Unit::TestCase
|
7
|
+
STRING = '10.17487/RFC8089'.freeze
|
8
|
+
|
9
|
+
URL = URI('https://www.rfc-editor.org/info/rfc8089')
|
10
|
+
|
11
|
+
FAMILY = 'Kerwin'.freeze
|
12
|
+
TITLE = 'The "file" URI Scheme'.freeze
|
13
|
+
|
14
|
+
def test_find
|
15
|
+
doi = DOI.parse STRING
|
16
|
+
url = doi.find
|
17
|
+
assert_equal( url, URL )
|
18
|
+
end
|
19
|
+
def test_find2
|
20
|
+
url = DOI.find STRING
|
21
|
+
assert_equal( url, URL )
|
22
|
+
end
|
23
|
+
def test_data
|
24
|
+
doi = DOI.parse STRING
|
25
|
+
data = doi.data
|
26
|
+
assert_equal( data['author'][0]['family'], FAMILY )
|
27
|
+
assert_equal( data['title'], TITLE )
|
28
|
+
end
|
29
|
+
def test_data2
|
30
|
+
data = DOI.data STRING
|
31
|
+
assert_equal( data['author'][0]['family'], FAMILY )
|
32
|
+
assert_equal( data['title'], TITLE )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
data/test/test-libdoi.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'uri'
|
3
|
+
$VERBOSE = true
|
4
|
+
|
5
|
+
require_relative '../lib/libdoi'
|
6
|
+
class Test_libdoi < Test::Unit::TestCase
|
7
|
+
DIR = '10'.freeze
|
8
|
+
REQ = '17487'.freeze
|
9
|
+
DSS = 'RFC8089'.freeze
|
10
|
+
|
11
|
+
STRING1 = 'doi:10.17487/RFC8089'.freeze
|
12
|
+
STRING2 = '10.17487/RFC8089'.freeze
|
13
|
+
|
14
|
+
HTTP_URL = URI('https://doi.org/10.17487/RFC8089')
|
15
|
+
INFO_URL = URI('info:doi/10.17487/RFC8089')
|
16
|
+
|
17
|
+
def test_construct
|
18
|
+
doi = DOI.new DIR.dup, REQ.dup, DSS.dup
|
19
|
+
assert_kind_of( DOI, doi )
|
20
|
+
assert_equal( DIR, doi.dir )
|
21
|
+
assert_equal( REQ, doi.reg )
|
22
|
+
assert_equal( DSS, doi.dss )
|
23
|
+
end
|
24
|
+
def test_concat
|
25
|
+
doi1 = DOI.new DIR.dup, REQ.dup, 'RFC'
|
26
|
+
doi2 = doi1 + '8089'
|
27
|
+
assert_equal( [DIR,REQ,'RFC'], [doi1.dir, doi1.reg, doi1.dss] )
|
28
|
+
assert_equal( [DIR,REQ,'RFC8089'], [doi2.dir, doi2.reg, doi2.dss] )
|
29
|
+
end
|
30
|
+
def test_append
|
31
|
+
doi1 = DOI.new DIR.dup, REQ.dup, 'RFC'
|
32
|
+
doi2 = doi1 << '8089'
|
33
|
+
assert_equal( [DIR.dup,REQ.dup,'RFC8089'], [doi1.dir, doi1.reg, doi1.dss] )
|
34
|
+
assert_same( doi1, doi2 )
|
35
|
+
end
|
36
|
+
def test_to_s
|
37
|
+
doi = DOI.new DIR.dup, REQ.dup, DSS.dup
|
38
|
+
assert_equal( STRING1, doi.to_s )
|
39
|
+
assert_equal( STRING1, doi.to_s(prefix: true) )
|
40
|
+
assert_equal( STRING2, doi.to_s(prefix: false) )
|
41
|
+
end
|
42
|
+
def test_to_uri
|
43
|
+
doi = DOI.new DIR.dup, REQ.dup, DSS.dup
|
44
|
+
uri1 = doi.to_uri
|
45
|
+
assert_kind_of( URI, uri1 )
|
46
|
+
assert_equal( HTTP_URL, uri1 )
|
47
|
+
|
48
|
+
uri2 = doi.to_uri(info: false)
|
49
|
+
assert_kind_of( URI, uri2 )
|
50
|
+
assert_equal( HTTP_URL, uri2 )
|
51
|
+
|
52
|
+
uri3 = doi.to_uri(info: true)
|
53
|
+
assert_kind_of(URI, uri3)
|
54
|
+
assert_equal( INFO_URL, uri3 )
|
55
|
+
end
|
56
|
+
def test_parse
|
57
|
+
[
|
58
|
+
'http://dx.doi.org/10.17487/RFC8089',
|
59
|
+
'https://doi.org/10.17487/RFC8089',
|
60
|
+
'http://doi.acm.org/10.17487/RFC8089',
|
61
|
+
'http://doi.ieeecomputersociety.org/10.17487/RFC8089',
|
62
|
+
'info:doi/10.17487/RFC8089',
|
63
|
+
'doi:10.17487/RFC8089',
|
64
|
+
'DOI: 10.17487/RFC8089',
|
65
|
+
'10.17487/RFC8089',
|
66
|
+
].each do |good|
|
67
|
+
doi = DOI.parse(good)
|
68
|
+
assert_kind_of( DOI, doi )
|
69
|
+
assert_equal( STRING1, doi.to_s )
|
70
|
+
end
|
71
|
+
|
72
|
+
[
|
73
|
+
'10.0001/x/abc', # DSS matching "^./" is forbidden by the spec
|
74
|
+
'https://doi.org/not-a-doi',
|
75
|
+
'garbage',
|
76
|
+
].each do |bad|
|
77
|
+
assert_raise(ArgumentError) { DOI.parse bad }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: libdoi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthew Kerwin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
== DOI Library
|
15
|
+
|
16
|
+
Parse, display, and dereference DOIs.
|
17
|
+
|
18
|
+
See the documentation at http://phluid61.github.io/libdoi/
|
19
|
+
email:
|
20
|
+
- matthew@kerwin.net.au
|
21
|
+
executables: []
|
22
|
+
extensions: []
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- lib/libdoi.rb
|
26
|
+
- lib/libdoi/network.rb
|
27
|
+
- test/test-libdoi-network.rb
|
28
|
+
- test/test-libdoi.rb
|
29
|
+
homepage: http://phluid61.github.com/libdoi
|
30
|
+
licenses:
|
31
|
+
- ISC
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.6.8
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: DOI Library
|
53
|
+
test_files:
|
54
|
+
- test/test-libdoi-network.rb
|
55
|
+
- test/test-libdoi.rb
|