serrano 0.5.0 → 1.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +30 -0
- data/.gitignore +3 -1
- data/CHANGELOG.md +26 -0
- data/CODE_OF_CONDUCT.md +2 -2
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/README.md +21 -7
- data/Rakefile +12 -8
- data/bin/serrano +96 -98
- data/lib/serrano.rb +112 -63
- data/lib/serrano/cn.rb +4 -6
- data/lib/serrano/cnrequest.rb +36 -22
- data/lib/serrano/error.rb +2 -0
- data/lib/serrano/faraday.rb +16 -15
- data/lib/serrano/filterhandler.rb +26 -28
- data/lib/serrano/filters.rb +91 -85
- data/lib/serrano/helpers/configuration.rb +2 -2
- data/lib/serrano/request.rb +31 -29
- data/lib/serrano/request_cursor.rb +59 -58
- data/lib/serrano/styles.rb +14 -9
- data/lib/serrano/utils.rb +19 -17
- data/lib/serrano/version.rb +3 -1
- data/serrano.gemspec +41 -28
- metadata +75 -48
- data/.travis.yml +0 -8
- data/Gemfile.lock +0 -64
- data/lib/serrano/constants.rb +0 -36
- data/lib/serrano/cursor_testing.rb +0 -52
data/lib/serrano.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
1
4
|
require "serrano/version"
|
2
5
|
require "serrano/request"
|
3
6
|
require "serrano/request_cursor"
|
@@ -6,8 +9,8 @@ require "serrano/cnrequest"
|
|
6
9
|
require "serrano/filters"
|
7
10
|
require "serrano/styles"
|
8
11
|
|
9
|
-
require
|
10
|
-
require
|
12
|
+
require "rexml/document"
|
13
|
+
require "rexml/xpath"
|
11
14
|
|
12
15
|
# @!macro serrano_params
|
13
16
|
# @param offset [Fixnum] Number of record to start at, any non-negative integer up to 10,000
|
@@ -19,10 +22,10 @@ require 'rexml/xpath'
|
|
19
22
|
# @param sort [String] Field to sort on, one of score, relevance,
|
20
23
|
# updated (date of most recent change to metadata - currently the same as deposited),
|
21
24
|
# deposited (time of most recent deposit), indexed (time of most recent index),
|
22
|
-
# published (publication date), published-print (print publication date),
|
23
|
-
# published-online (online publication date), issued (issued date (earliest known publication date)),
|
24
|
-
# is-referenced-by-count (number of times this DOI is referenced by other Crossref DOIs), or
|
25
|
-
# references-count (number of references included in the references section of the document
|
25
|
+
# published (publication date), published-print (print publication date),
|
26
|
+
# published-online (online publication date), issued (issued date (earliest known publication date)),
|
27
|
+
# is-referenced-by-count (number of times this DOI is referenced by other Crossref DOIs), or
|
28
|
+
# references-count (number of references included in the references section of the document
|
26
29
|
# identified by this DOI). Note: If the API call includes a query, then the sort
|
27
30
|
# order will be by the relevance score. If no query is included, then the sort order
|
28
31
|
# will be by DOI update date.
|
@@ -32,7 +35,7 @@ require 'rexml/xpath'
|
|
32
35
|
# @param select [String/Array(String)] Crossref metadata records can be
|
33
36
|
# quite large. Sometimes you just want a few elements from the schema. You can "select"
|
34
37
|
# a subset of elements to return. This can make your API calls much more efficient. Not
|
35
|
-
# clear yet which fields are allowed here.
|
38
|
+
# clear yet which fields are allowed here.
|
36
39
|
# @param verbose [Boolean] Print request headers to stdout. Default: false
|
37
40
|
|
38
41
|
# @!macro cursor_params
|
@@ -60,11 +63,10 @@ require 'rexml/xpath'
|
|
60
63
|
# - oauth [Hash] A hash with OAuth details
|
61
64
|
|
62
65
|
# @!macro field_queries
|
63
|
-
# @param [Hash<Object>] args Field queries, as named parameters. See
|
66
|
+
# @param [Hash<Object>] args Field queries, as named parameters. See
|
64
67
|
# https://github.com/CrossRef/rest-api-doc/blob/master/rest_api.md#field-queries
|
65
68
|
# Field query parameters mut be named, and must start with `query_`. Any dashes or
|
66
69
|
# periods should be replaced with underscores. The options include:
|
67
|
-
# - query_title: Query title and subtitle
|
68
70
|
# - query_container_title: Query container-title aka. publication name
|
69
71
|
# - query_author: Query author given and family names
|
70
72
|
# - query_editor: Query editor given and family names
|
@@ -108,7 +110,7 @@ require 'rexml/xpath'
|
|
108
110
|
# searching full text, or even abstracts of articles, but only what is
|
109
111
|
# available in the data that is returned to you. That is, they search
|
110
112
|
# article titles, authors, etc. For some discussion on this, see
|
111
|
-
# https://
|
113
|
+
# https://gitlab.com/crossref/issues/issues/101
|
112
114
|
#
|
113
115
|
#
|
114
116
|
# The Polite Pool
|
@@ -119,13 +121,13 @@ require 'rexml/xpath'
|
|
119
121
|
# information, then they will send you to a separate pool of machines,
|
120
122
|
# with better control the performance of these machines because they can
|
121
123
|
# block abusive users.
|
122
|
-
#
|
124
|
+
#
|
123
125
|
# We have been using `https` in `serrano` for a while now, so that's good
|
124
126
|
# to go. To get into the Polite Pool, also set your `mailto` email address
|
125
127
|
# with `Serrano.configuration` (example below), or set as an environment
|
126
128
|
# variable with the name `CROSSREF_EMAIL` and your mailto will be set
|
127
|
-
# for each request automatically.
|
128
|
-
#
|
129
|
+
# for each request automatically.
|
130
|
+
#
|
129
131
|
# require 'serrano'
|
130
132
|
# Serrano.configuration do |config|
|
131
133
|
# config.mailto = "foo@bar.com"
|
@@ -138,6 +140,12 @@ require 'rexml/xpath'
|
|
138
140
|
# limit is 50 requests per second. Look for the headers `X-Rate-Limit-Limit`
|
139
141
|
# and `X-Rate-Limit-Interval` in requests to see what the current rate
|
140
142
|
# limits are.
|
143
|
+
#
|
144
|
+
#
|
145
|
+
# URL Encoding
|
146
|
+
# We do URL encoding of DOIs for you for all methods except `Serrano.citation_count`
|
147
|
+
# which doesn't work if you encode DOIs beforehand. We use `ERB::Util.url_encode`
|
148
|
+
# to encode.
|
141
149
|
|
142
150
|
module Serrano
|
143
151
|
extend Configuration
|
@@ -193,7 +201,7 @@ module Serrano
|
|
193
201
|
#
|
194
202
|
# # sample
|
195
203
|
# Serrano.works(sample: 2)
|
196
|
-
#
|
204
|
+
#
|
197
205
|
# # select - pass an array or a comma separated string
|
198
206
|
# Serrano.works(query: "ecology", select: "DOI,title", limit: 30)
|
199
207
|
# Serrano.works(query: "ecology", select: ["DOI","title"], limit: 30)
|
@@ -222,10 +230,11 @@ module Serrano
|
|
222
230
|
# Serrano.works(select: ['DOI', 'title'], limit: 3)
|
223
231
|
def self.works(ids: nil, query: nil, filter: nil, offset: nil,
|
224
232
|
limit: nil, sample: nil, sort: nil, order: nil, facet: nil,
|
225
|
-
select: nil, options: nil, verbose: false, cursor: nil,
|
233
|
+
select: nil, options: nil, verbose: false, cursor: nil,
|
226
234
|
cursor_max: 5000, **args)
|
227
235
|
|
228
|
-
|
236
|
+
assert_valid_filters(filter) if filter
|
237
|
+
RequestCursor.new("works", ids, query, filter, offset,
|
229
238
|
limit, sample, sort, order, facet, select, nil, nil, options,
|
230
239
|
verbose, cursor, cursor_max, args).perform
|
231
240
|
end
|
@@ -281,8 +290,9 @@ module Serrano
|
|
281
290
|
select: nil, works: false, options: nil, verbose: false,
|
282
291
|
cursor: nil, cursor_max: 5000, **args)
|
283
292
|
|
284
|
-
|
285
|
-
|
293
|
+
assert_valid_filters(filter) if filter
|
294
|
+
RequestCursor.new("members", ids, query, filter, offset,
|
295
|
+
limit, sample, sort, order, facet, select, works, nil,
|
286
296
|
options, verbose, cursor, cursor_max, args).perform
|
287
297
|
end
|
288
298
|
|
@@ -317,19 +327,20 @@ module Serrano
|
|
317
327
|
# items.collect{ |z| z['DOI'] }[0,50]
|
318
328
|
#
|
319
329
|
# # field queries
|
320
|
-
# ## query.
|
321
|
-
# res = Serrano.prefixes(ids: "10.1016", works: true,
|
330
|
+
# ## query.bibliographic
|
331
|
+
# res = Serrano.prefixes(ids: "10.1016", works: true, query_bibliographic: 'cell biology')
|
322
332
|
# res[0]['message']['items'].collect { |x| x['title'] }
|
323
333
|
#
|
324
334
|
# # select certain fields
|
325
|
-
# Serrano.prefixes(ids: "10.1016", works: true, select: ['DOI', 'title'], limit: 3)
|
335
|
+
# Serrano.prefixes(ids: "10.1016", works: true, select: ['DOI', 'title'], limit: 3)
|
326
336
|
def self.prefixes(ids:, filter: nil, offset: nil,
|
327
337
|
limit: nil, sample: nil, sort: nil, order: nil, facet: nil,
|
328
338
|
select: nil, works: false, options: nil, verbose: false,
|
329
339
|
cursor: nil, cursor_max: 5000, **args)
|
330
340
|
|
331
|
-
|
332
|
-
|
341
|
+
assert_valid_filters(filter) if filter
|
342
|
+
RequestCursor.new("prefixes", ids, nil, filter, offset,
|
343
|
+
limit, sample, sort, order, facet, select, works, nil,
|
333
344
|
options, verbose, cursor, cursor_max, args).perform
|
334
345
|
end
|
335
346
|
|
@@ -374,13 +385,14 @@ module Serrano
|
|
374
385
|
# res[0]['message']['items'].collect { |x| x['author'][0]['family'] }
|
375
386
|
#
|
376
387
|
# # select certain fields
|
377
|
-
# Serrano.funders(ids: "10.13039/100000001", works: true, select: ['DOI', 'title'], limit: 3)
|
388
|
+
# Serrano.funders(ids: "10.13039/100000001", works: true, select: ['DOI', 'title'], limit: 3)
|
378
389
|
def self.funders(ids: nil, query: nil, filter: nil, offset: nil,
|
379
390
|
limit: nil, sample: nil, sort: nil, order: nil, facet: nil,
|
380
391
|
select: nil, works: false, options: nil, verbose: false,
|
381
392
|
cursor: nil, cursor_max: 5000, **args)
|
382
393
|
|
383
|
-
|
394
|
+
assert_valid_filters(filter) if filter
|
395
|
+
RequestCursor.new("funders", ids, query, filter, offset,
|
384
396
|
limit, sample, sort, order, facet, select, works, nil, options,
|
385
397
|
verbose, cursor, cursor_max, args).perform
|
386
398
|
end
|
@@ -426,13 +438,14 @@ module Serrano
|
|
426
438
|
# res[0]['message']['items'].collect { |x| x['container-title'] }
|
427
439
|
#
|
428
440
|
# # select certain fields
|
429
|
-
# Serrano.journals(ids: "2167-8359", works: true, select: ['DOI', 'title'], limit: 3)
|
441
|
+
# Serrano.journals(ids: "2167-8359", works: true, select: ['DOI', 'title'], limit: 3)
|
430
442
|
def self.journals(ids: nil, query: nil, filter: nil, offset: nil,
|
431
443
|
limit: nil, sample: nil, sort: nil, order: nil, facet: nil,
|
432
444
|
select: nil, works: false, options: nil, verbose: false,
|
433
445
|
cursor: nil, cursor_max: 5000, **args)
|
434
446
|
|
435
|
-
|
447
|
+
assert_valid_filters(filter) if filter
|
448
|
+
RequestCursor.new("journals", ids, query, filter, offset,
|
436
449
|
limit, sample, sort, order, facet, select, works, nil, options,
|
437
450
|
verbose, cursor, cursor_max, args).perform
|
438
451
|
end
|
@@ -448,7 +461,7 @@ module Serrano
|
|
448
461
|
# @param select [String/Array(String)] Crossref metadata records can be
|
449
462
|
# quite large. Sometimes you just want a few elements from the schema. You can "select"
|
450
463
|
# a subset of elements to return. This can make your API calls much more efficient. Not
|
451
|
-
# clear yet which fields are allowed here.
|
464
|
+
# clear yet which fields are allowed here.
|
452
465
|
# @return [Array] An array of hashes
|
453
466
|
#
|
454
467
|
# @example
|
@@ -470,11 +483,11 @@ module Serrano
|
|
470
483
|
# res[0]['message']['items'].collect { |x| x['container-title'] }
|
471
484
|
#
|
472
485
|
# # select certain fields
|
473
|
-
# Serrano.types(ids: "journal", works: true, select: ['DOI', 'title'], limit: 3)
|
486
|
+
# Serrano.types(ids: "journal", works: true, select: ['DOI', 'title'], limit: 3)
|
474
487
|
def self.types(ids: nil, offset: nil, limit: nil, select: nil, works: false,
|
475
488
|
options: nil, verbose: false, cursor: nil, cursor_max: 5000, **args)
|
476
489
|
|
477
|
-
RequestCursor.new(
|
490
|
+
RequestCursor.new("types", ids, nil, nil, offset,
|
478
491
|
limit, nil, nil, nil, nil, select, works, nil, options,
|
479
492
|
verbose, cursor, cursor_max, args).perform
|
480
493
|
end
|
@@ -482,9 +495,28 @@ module Serrano
|
|
482
495
|
##
|
483
496
|
# Search the licenses route
|
484
497
|
#
|
485
|
-
# @!macro serrano_params
|
486
498
|
# @!macro serrano_options
|
487
499
|
# @param query [String] A query string
|
500
|
+
# @param offset [Fixnum] Number of record to start at, any non-negative integer up to 10,000
|
501
|
+
# @param limit [Fixnum] Number of results to return. Not relavant when searching with specific dois.
|
502
|
+
# Default: 20. Max: 1000
|
503
|
+
# @param sample [Fixnum] Number of random results to return. when you use the sample parameter,
|
504
|
+
# the limit and offset parameters are ignored. This parameter only used when works requested.
|
505
|
+
# Max: 100.
|
506
|
+
# @param sort [String] Field to sort on, one of score, relevance,
|
507
|
+
# updated (date of most recent change to metadata - currently the same as deposited),
|
508
|
+
# deposited (time of most recent deposit), indexed (time of most recent index),
|
509
|
+
# published (publication date), published-print (print publication date),
|
510
|
+
# published-online (online publication date), issued (issued date (earliest known publication date)),
|
511
|
+
# is-referenced-by-count (number of times this DOI is referenced by other Crossref DOIs), or
|
512
|
+
# references-count (number of references included in the references section of the document
|
513
|
+
# identified by this DOI). Note: If the API call includes a query, then the sort
|
514
|
+
# order will be by the relevance score. If no query is included, then the sort order
|
515
|
+
# will be by DOI update date.
|
516
|
+
# @param order [String] Sort order, one of 'asc' or 'desc'
|
517
|
+
# @param facet [Boolean/String] Include facet results OR a query (e.g., `license:*`) to facet by
|
518
|
+
# license. Default: false
|
519
|
+
# @param verbose [Boolean] Print request headers to stdout. Default: false
|
488
520
|
# @return [Array] An array of hashes
|
489
521
|
#
|
490
522
|
# @example
|
@@ -496,7 +528,7 @@ module Serrano
|
|
496
528
|
limit: nil, sample: nil, sort: nil, order: nil,
|
497
529
|
facet: nil, options: nil, verbose: false)
|
498
530
|
|
499
|
-
Request.new(
|
531
|
+
Request.new("licenses", nil, query, nil, offset,
|
500
532
|
limit, sample, sort, order, facet, nil, nil, nil, options, verbose).perform
|
501
533
|
end
|
502
534
|
|
@@ -512,9 +544,8 @@ module Serrano
|
|
512
544
|
# Serrano.registration_agency(ids: '10.1371/journal.pone.0033693')
|
513
545
|
# Serrano.registration_agency(ids: ['10.1007/12080.1874-1746','10.1007/10452.1573-5125', '10.1111/(issn)1442-9993'])
|
514
546
|
def self.registration_agency(ids:, options: nil, verbose: false)
|
515
|
-
|
516
|
-
|
517
|
-
nil, nil, nil, nil, nil, false, true, options, verbose).perform
|
547
|
+
Request.new("works", ids, nil, nil, nil,
|
548
|
+
nil, nil, nil, nil, nil, nil, false, true, options, verbose).perform
|
518
549
|
end
|
519
550
|
|
520
551
|
##
|
@@ -535,10 +566,9 @@ module Serrano
|
|
535
566
|
# Serrano.random_dois(sample: 10)
|
536
567
|
# Serrano.random_dois(sample: 100)
|
537
568
|
def self.random_dois(sample: 10, options: nil, verbose: false)
|
538
|
-
|
539
|
-
tmp = Request.new('works', nil, nil, nil, nil,
|
569
|
+
tmp = Request.new("works", nil, nil, nil, nil,
|
540
570
|
nil, sample, nil, nil, nil, nil, false, nil, options, verbose).perform
|
541
|
-
tmp[
|
571
|
+
tmp["message"]["items"].collect { |x| x["DOI"] }
|
542
572
|
end
|
543
573
|
|
544
574
|
##
|
@@ -564,7 +594,7 @@ module Serrano
|
|
564
594
|
# Serrano.content_negotiation(ids: "10.1126/science.169.3946.635", format: "crossref-xml")
|
565
595
|
# Serrano.content_negotiation(ids: "10.1126/science.169.3946.635", format: "text")
|
566
596
|
#
|
567
|
-
# # return
|
597
|
+
# # return a bibentry type
|
568
598
|
# Serrano.content_negotiation(ids: "10.1126/science.169.3946.635", format: "bibentry")
|
569
599
|
# Serrano.content_negotiation(ids: "10.6084/m9.figshare.97218", format: "bibentry")
|
570
600
|
#
|
@@ -577,30 +607,32 @@ module Serrano
|
|
577
607
|
# Serrano.content_negotiation(ids: "10.1126/science.169.3946.635", format: "text", style: "oikos")
|
578
608
|
#
|
579
609
|
# # example with many DOIs
|
580
|
-
# dois =
|
581
|
-
# Serrano.content_negotiation(dois, format: "text", style: "apa")
|
610
|
+
# dois = Serrano.random_dois(sample: 2)
|
611
|
+
# Serrano.content_negotiation(ids: dois, format: "text", style: "apa")
|
582
612
|
#
|
583
613
|
# # Using DataCite DOIs
|
584
|
-
#
|
585
|
-
#
|
586
|
-
#
|
587
|
-
#
|
614
|
+
# doi = "10.1126/science.169.3946.635"
|
615
|
+
# Serrano.content_negotiation(ids: doi, format: "text")
|
616
|
+
# Serrano.content_negotiation(ids: doi, format: "crossref-xml")
|
617
|
+
# Serrano.content_negotiation(ids: doi, format: "crossref-tdm")
|
588
618
|
#
|
589
619
|
# ## But most do work
|
590
|
-
# Serrano.content_negotiation(ids:
|
591
|
-
# Serrano.content_negotiation(ids:
|
592
|
-
# Serrano.content_negotiation(ids:
|
593
|
-
# Serrano.content_negotiation(ids:
|
594
|
-
# Serrano.content_negotiation(ids:
|
595
|
-
# Serrano.content_negotiation(ids:
|
596
|
-
# Serrano.content_negotiation(ids:
|
597
|
-
# Serrano.content_negotiation(ids:
|
620
|
+
# Serrano.content_negotiation(ids: doi, format: "datacite-xml")
|
621
|
+
# Serrano.content_negotiation(ids: doi, format: "rdf-xml")
|
622
|
+
# Serrano.content_negotiation(ids: doi, format: "turtle")
|
623
|
+
# Serrano.content_negotiation(ids: doi, format: "citeproc-json")
|
624
|
+
# Serrano.content_negotiation(ids: doi, format: "ris")
|
625
|
+
# Serrano.content_negotiation(ids: doi, format: "bibtex")
|
626
|
+
# Serrano.content_negotiation(ids: doi, format: "bibentry")
|
627
|
+
# Serrano.content_negotiation(ids: doi, format: "bibtex")
|
598
628
|
#
|
599
629
|
# # many DOIs
|
600
|
-
# dois = ['10.5167/UZH-30455','10.5167/UZH-49216','10.5167/UZH-503',
|
630
|
+
# dois = ['10.5167/UZH-30455','10.5167/UZH-49216','10.5167/UZH-503',
|
631
|
+
# '10.5167/UZH-38402','10.5167/UZH-41217']
|
601
632
|
# x = Serrano.content_negotiation(ids: dois)
|
602
633
|
# puts x
|
603
|
-
def self.content_negotiation(ids:, format: "bibtex", style:
|
634
|
+
def self.content_negotiation(ids:, format: "bibtex", style: "apa", locale: "en-US")
|
635
|
+
ids = Array(ids).map { |x| ERB::Util.url_encode(x) }
|
604
636
|
CNRequest.new(ids, format, style, locale).perform
|
605
637
|
end
|
606
638
|
|
@@ -619,17 +651,16 @@ module Serrano
|
|
619
651
|
# Serrano.citation_count(doi: "10.1016/j.fbr.2012.01.001")
|
620
652
|
# # DOI not found
|
621
653
|
# Serrano.citation_count(doi: "10.1016/j.fbr.2012")
|
622
|
-
def self.citation_count(doi:, url: "
|
654
|
+
def self.citation_count(doi:, url: "https://www.crossref.org/openurl/",
|
623
655
|
key: "cboettig@ropensci.org", options: nil)
|
624
656
|
|
625
|
-
args = {
|
626
|
-
opts = args.delete_if { |
|
627
|
-
conn = Faraday.new(:
|
628
|
-
res = conn.get
|
657
|
+
args = {id: "doi:" + doi, pid: key, noredirect: true}
|
658
|
+
opts = args.delete_if { |_k, v| v.nil? }
|
659
|
+
conn = Faraday.new(url: url, request: options)
|
660
|
+
res = conn.get "", opts
|
629
661
|
x = res.body
|
630
662
|
oc = REXML::Document.new("<doc>#{x}</doc>")
|
631
|
-
|
632
|
-
return value
|
663
|
+
REXML::XPath.first(oc, "//query").attributes["fl_count"].to_i
|
633
664
|
end
|
634
665
|
|
635
666
|
# Get csl styles
|
@@ -639,7 +670,25 @@ module Serrano
|
|
639
670
|
# @example
|
640
671
|
# Serrano.csl_styles
|
641
672
|
def self.csl_styles
|
642
|
-
|
673
|
+
fetch_styles
|
643
674
|
end
|
644
675
|
|
676
|
+
def self.assert_valid_filters(filters)
|
677
|
+
unless filters.is_a? Hash
|
678
|
+
raise ArgumentError, <<~ERR
|
679
|
+
Filters must be provided as a hash, like:
|
680
|
+
|
681
|
+
Serrano.works(query: "something", filters: { has_abstract: true })
|
682
|
+
ERR
|
683
|
+
end
|
684
|
+
|
685
|
+
filters.each do |name, _|
|
686
|
+
filter_strings = Filters.names.map(&:to_s)
|
687
|
+
next if filter_strings.include?(name.to_s)
|
688
|
+
|
689
|
+
raise ArgumentError, <<~ERR
|
690
|
+
The filter "#{name}" is not a valid filter. Please run `Serrano.filters.filters` to see all valid filters.
|
691
|
+
ERR
|
692
|
+
end
|
693
|
+
end
|
645
694
|
end
|
data/lib/serrano/cn.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "serrano/version"
|
2
4
|
require "serrano/cnrequest"
|
3
5
|
|
4
6
|
##
|
5
7
|
# ContentNegotiation - Content Negotiation class
|
6
8
|
#
|
7
|
-
# @see
|
9
|
+
# @see https://citation.crosscite.org/docs.html for details
|
8
10
|
module Serrano
|
9
|
-
|
10
11
|
class ContentNegotiation
|
11
|
-
|
12
12
|
attr_accessor :ids
|
13
13
|
attr_accessor :format
|
14
14
|
attr_accessor :style
|
@@ -22,9 +22,7 @@ module Serrano
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def cn
|
25
|
-
CNRequest.new(
|
25
|
+
CNRequest.new(ids, format, style, locale).perform
|
26
26
|
end
|
27
|
-
|
28
27
|
end
|
29
|
-
|
30
28
|
end
|
data/lib/serrano/cnrequest.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "faraday"
|
2
4
|
require "faraday_middleware"
|
3
5
|
require "multi_json"
|
4
6
|
require "serrano/error"
|
5
|
-
require "serrano/
|
6
|
-
require
|
7
|
-
|
7
|
+
require "serrano/utils"
|
8
|
+
require "serrano/helpers/configuration"
|
9
|
+
|
10
|
+
CN_FORMAT_HEADERS = {"rdf-xml" => "application/rdf+xml",
|
11
|
+
"turtle" => "text/turtle",
|
12
|
+
"citeproc-json" => "transform/application/vnd.citationstyles.csl+json",
|
13
|
+
"text" => "text/x-bibliography",
|
14
|
+
"ris" => "application/x-research-info-systems",
|
15
|
+
"bibtex" => "application/x-bibtex",
|
16
|
+
"crossref-xml" => "application/vnd.crossref.unixref+xml",
|
17
|
+
"datacite-xml" => "application/vnd.datacite.datacite+xml",
|
18
|
+
"bibentry" => "application/x-bibtex",
|
19
|
+
"crossref-tdm" => "application/vnd.crossref.unixsd+xml"}.freeze
|
8
20
|
|
9
21
|
##
|
10
22
|
# Serrano::CNRequest
|
@@ -12,12 +24,16 @@ require 'serrano/helpers/configuration'
|
|
12
24
|
# Class to perform HTTP requests to the Crossref API
|
13
25
|
module Serrano
|
14
26
|
class CNRequest #:nodoc:
|
15
|
-
|
16
27
|
attr_accessor :ids
|
17
28
|
attr_accessor :format
|
18
29
|
attr_accessor :style
|
19
30
|
attr_accessor :locale
|
20
31
|
|
32
|
+
CN_FORMATS = %w[rdf-xml turtle citeproc-json
|
33
|
+
citeproc-json-ish text ris bibtex
|
34
|
+
crossref-xml datacite-xml bibentry
|
35
|
+
crossref-tdm].freeze
|
36
|
+
|
21
37
|
def initialize(ids, format, style, locale)
|
22
38
|
self.ids = ids
|
23
39
|
self.format = format
|
@@ -26,37 +42,35 @@ module Serrano
|
|
26
42
|
end
|
27
43
|
|
28
44
|
def perform
|
29
|
-
|
45
|
+
unless CN_FORMATS.include? format
|
30
46
|
raise "format not one of accepted types"
|
31
47
|
end
|
32
48
|
|
33
|
-
|
49
|
+
conn = Faraday.new "https://doi.org/" do |c|
|
34
50
|
c.use FaradayMiddleware::FollowRedirects
|
35
51
|
c.adapter :net_http
|
36
52
|
end
|
37
53
|
|
38
|
-
if
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
return make_request(self.ids, self.format, self.style, self.locale)
|
54
|
+
if ids.length == 1
|
55
|
+
self.ids = ids[0] if ids.class == Array
|
56
|
+
make_request(conn, ids, format, style, locale)
|
43
57
|
else
|
44
58
|
coll = []
|
45
|
-
Array(
|
46
|
-
coll << make_request(x,
|
59
|
+
Array(ids).each do |x|
|
60
|
+
coll << make_request(conn, x, format, style, locale)
|
47
61
|
end
|
48
|
-
|
62
|
+
coll
|
49
63
|
end
|
50
64
|
end
|
51
65
|
end
|
52
66
|
end
|
53
67
|
|
54
|
-
def make_request(ids, format, style, locale)
|
55
|
-
type =
|
68
|
+
def make_request(conn, ids, format, style, locale)
|
69
|
+
type = CN_FORMAT_HEADERS.select { |x, _| x.include? format }.values[0]
|
56
70
|
|
57
71
|
if format == "citeproc-json"
|
58
|
-
endpt = "
|
59
|
-
cr_works = Faraday.new(:
|
72
|
+
endpt = "https://api.crossref.org/works/" + ids + "/" + type
|
73
|
+
cr_works = Faraday.new(url: endpt)
|
60
74
|
cr_works.headers[:user_agent] = make_ua
|
61
75
|
cr_works.headers["X-USER-AGENT"] = make_ua
|
62
76
|
res = cr_works.get
|
@@ -65,15 +79,15 @@ def make_request(ids, format, style, locale)
|
|
65
79
|
type = type + "; style = " + style + "; locale = " + locale
|
66
80
|
end
|
67
81
|
|
68
|
-
res =
|
82
|
+
res = conn.get { |req|
|
69
83
|
req.url ids
|
70
|
-
req.headers[
|
84
|
+
req.headers["Accept"] = type
|
71
85
|
req.headers[:user_agent] = make_ua
|
72
86
|
req.headers["X-USER-AGENT"] = make_ua
|
73
|
-
|
87
|
+
}
|
74
88
|
end
|
75
89
|
|
76
|
-
|
90
|
+
res.body
|
77
91
|
end
|
78
92
|
|
79
93
|
# parser <- cn_types[[self.format]]
|