relaton-isbn 1.20.1 → 2.0.0.pre.alpha.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.
- checksums.yaml +4 -4
- data/.claude/settings.local.json +7 -0
- data/.rubocop.yml +1 -1
- data/CLAUDE.md +45 -0
- data/README.adoc +38 -59
- data/lib/relaton/isbn/isbn.rb +61 -0
- data/lib/relaton/isbn/open_library.rb +43 -0
- data/lib/relaton/isbn/parser.rb +88 -0
- data/lib/relaton/isbn/processor.rb +48 -0
- data/lib/relaton/isbn/util.rb +8 -0
- data/lib/relaton/isbn/version.rb +5 -0
- data/lib/relaton/isbn.rb +19 -0
- data/sig/relaton/isbn.rbs +6 -0
- metadata +47 -20
- data/lib/relaton_isbn/isbn.rb +0 -59
- data/lib/relaton_isbn/open_library.rb +0 -41
- data/lib/relaton_isbn/parser.rb +0 -69
- data/lib/relaton_isbn/processor.rb +0 -58
- data/lib/relaton_isbn/util.rb +0 -6
- data/lib/relaton_isbn/version.rb +0 -3
- data/lib/relaton_isbn.rb +0 -17
- data/sig/relaton_isbn.rbs +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d47310ca1f6271c5591fff0ec1024f005fde6924145a82fb0a802f000d705fd
|
|
4
|
+
data.tar.gz: c2bb8987f28c0d7a3f7bed4b2b02738bb3e929f43c38762c152387620609140a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8568adf632314066c5ecb1e93efce0767a07abc112b9dfd7dcf1d1618151b69901ce772a479101881c5405fde6ec1c22f609b872d731e81cb6b6c931052f12b9
|
|
7
|
+
data.tar.gz: '0148ab38709f2868769f8b51da950f257dcc5d43952b0515ef1c8044f695359bdd6ca63c1215c5f72bf87a479ae7f40f7d0e45577ab0794d6094d955f1c73989'
|
data/.rubocop.yml
CHANGED
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this gem does
|
|
6
|
+
|
|
7
|
+
Retrieves bibliographic items from the OpenLibrary API by ISBN (10 or 13 digit) and returns `Relaton::Bib::ItemData` objects. Part of the Relaton family of gems.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bundle exec rspec # run all tests
|
|
13
|
+
bundle exec rspec spec/relaton/isbn/parser_spec.rb # run single spec file
|
|
14
|
+
bundle exec rubocop # lint
|
|
15
|
+
bin/console # IRB with gem loaded
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
Request flow: `OpenLibrary.get(code)` → `Isbn.new(code).parse` (validates/normalizes ISBN) → `OpenLibrary.request_api(isbn)` (HTTP to openlibrary.org) → `Parser.parse(json)` (builds `ItemData`)
|
|
21
|
+
|
|
22
|
+
Key modules under `Relaton::Isbn`:
|
|
23
|
+
- **OpenLibrary** — API client, main entry point via `.get`
|
|
24
|
+
- **Isbn** — ISBN-10/13 validation and conversion (always normalizes to ISBN-13)
|
|
25
|
+
- **Parser** — Transforms OpenLibrary JSON into `Relaton::Bib::ItemData` with titles, contributors, dates, etc.
|
|
26
|
+
- **Processor** — Integration hook for relaton-core framework (`from_xml`, `from_yaml`, `get`)
|
|
27
|
+
- **Util** — Logging via `Relaton::Bib::Util`
|
|
28
|
+
|
|
29
|
+
## Data model (relaton-bib 2.0)
|
|
30
|
+
|
|
31
|
+
Uses `Relaton::Bib::` namespace (not the old `RelatonBib::`). Key classes: `ItemData`, `Title`, `Docidentifier` (uses `.content` not `.id`), `Uri`, `Contributor`, `Person`, `Organization`, `Date` (uses `at:` not `on:`), `Place`.
|
|
32
|
+
|
|
33
|
+
`Parser#parse` returns `Bib::ItemData` (not `Bibitem`) — `ItemData` is the plain data class; `Bibitem`/`Item` are lutaml-model serializers that wrap it.
|
|
34
|
+
|
|
35
|
+
## Testing
|
|
36
|
+
|
|
37
|
+
- **VCR** records HTTP interactions in `spec/vcr_cassettes/` (re-records every 7 days)
|
|
38
|
+
- **WebMock** disables real HTTP by default
|
|
39
|
+
- **SimpleCov** tracks coverage
|
|
40
|
+
- Fixtures in `spec/fixtures/`
|
|
41
|
+
|
|
42
|
+
## Style
|
|
43
|
+
|
|
44
|
+
- RuboCop with Ribose OSS style guide, target Ruby 3.1
|
|
45
|
+
- `rubocop-rails` is required but Rails cops are disabled (not a Rails app)
|
data/README.adoc
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
= Relaton
|
|
1
|
+
= Relaton::ISBN: retrieves bibliographic items using OpenLibrary API
|
|
2
2
|
|
|
3
3
|
image:https://img.shields.io/gem/v/relaton-isbn.svg["Gem Version", link="https://rubygems.org/gems/relaton-isbn"]
|
|
4
4
|
image:https://github.com/relaton/relaton-isbn/workflows/macos/badge.svg["Build Status (macOS)", link="https://github.com/relaton/relaton-isbn/actions?workflow=macos"]
|
|
@@ -8,11 +8,11 @@ image:https://codeclimate.com/github/relaton/relaton-isbn/badges/gpa.svg["Code C
|
|
|
8
8
|
image:https://img.shields.io/github/issues-pr-raw/relaton/relaton-isbn.svg["Pull Requests", link="https://github.com/relaton/relaton-isbn/pulls"]
|
|
9
9
|
image:https://img.shields.io/github/commits-since/relaton/relaton-isbn/latest.svg["Commits since latest",link="https://github.com/relaton/relaton-isbn/releases"]
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Relaton::Isbn is a Ruby gem that implements the
|
|
12
12
|
https://github.com/metanorma/metanorma-model-iso#iso-bibliographic-item[IsoBibliographicItem model].
|
|
13
13
|
|
|
14
14
|
You can use it to retrieve metadata of Standards from https://openlibrary.org, and
|
|
15
|
-
access such metadata through the `
|
|
15
|
+
access such metadata through the `Bibitem` object.
|
|
16
16
|
|
|
17
17
|
== Installation
|
|
18
18
|
|
|
@@ -41,25 +41,25 @@ $ gem install relaton-isbn
|
|
|
41
41
|
|
|
42
42
|
=== Retrieving bibliographic items using OpenLibrary API
|
|
43
43
|
|
|
44
|
-
To retrieve bibliographic items, use `
|
|
44
|
+
To retrieve bibliographic items, use `Relaton::Isbn::OpenLibrary.get` method with ISBN-10 or ISBN-13 as an argument. Allowed prefixes are `ISBN`, `isbn:`. Prefix and hyphens are optional. The method returns `Relaton::Bib::Bibitem` object.
|
|
45
45
|
|
|
46
46
|
[source,ruby]
|
|
47
47
|
----
|
|
48
|
-
require '
|
|
48
|
+
require 'relaton/isbn'
|
|
49
49
|
=> true
|
|
50
50
|
|
|
51
51
|
# get document by ISBN-13
|
|
52
|
-
|
|
53
|
-
[relaton-isbn] (ISBN
|
|
54
|
-
[relaton-isbn] (ISBN
|
|
55
|
-
=> #<
|
|
52
|
+
bibitem = Relaton::Isbn::OpenLibrary.get "ISBN 978-0-12-064481-0"
|
|
53
|
+
[relaton-isbn] INFO: (ISBN 978-0-12-064481-0) Fetching from OpenLibrary ...
|
|
54
|
+
[relaton-isbn] INFO: (ISBN 978-0-12-064481-0) Found: `9780120644810`
|
|
55
|
+
=> #<Relaton::Bib::Bibitem:0x0000000127ed25d8
|
|
56
56
|
...
|
|
57
57
|
|
|
58
58
|
# get document by ISBN-10
|
|
59
|
-
|
|
60
|
-
[relaton-isbn] (isbn:0120644819) Fetching from OpenLibrary ...
|
|
61
|
-
[relaton-isbn] (isbn:0120644819) Found: `9780120644810`
|
|
62
|
-
=> #<
|
|
59
|
+
Relaton::Isbn::OpenLibrary.get "isbn:0120644819"
|
|
60
|
+
[relaton-isbn] INFO: (isbn:0120644819) Fetching from OpenLibrary ...
|
|
61
|
+
[relaton-isbn] INFO: (isbn:0120644819) Found: `9780120644810`
|
|
62
|
+
=> #<Relaton::Bib::Bibitem:0x0000000127f11698
|
|
63
63
|
...
|
|
64
64
|
----
|
|
65
65
|
|
|
@@ -67,60 +67,39 @@ require 'relaton_isbn'
|
|
|
67
67
|
[source,ruby]
|
|
68
68
|
----
|
|
69
69
|
# serialize to XML
|
|
70
|
-
|
|
71
|
-
<bibitem id="9780120644810" schema-version="v1.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
<contributor>
|
|
79
|
-
<role type="author"/>
|
|
80
|
-
<person>
|
|
81
|
-
<name>
|
|
82
|
-
<completename>James Arvo</completename>
|
|
83
|
-
</name>
|
|
84
|
-
</person>
|
|
85
|
-
</contributor>
|
|
86
|
-
<contributor>
|
|
87
|
-
<role type="publisher"/>
|
|
88
|
-
<organization>
|
|
89
|
-
<name>AP Professional</name>
|
|
90
|
-
</organization>
|
|
91
|
-
</contributor>
|
|
92
|
-
<place>
|
|
93
|
-
<city>Boston</city>
|
|
94
|
-
</place>
|
|
95
|
-
<place>
|
|
96
|
-
<city>London</city>
|
|
97
|
-
</place>
|
|
98
|
-
</bibitem>
|
|
70
|
+
bibitem.to_xml
|
|
71
|
+
=> "<bibitem id="9780120644810" schema-version="v1.4.1">
|
|
72
|
+
<fetched>2026-03-04</fetched>
|
|
73
|
+
<title type="main">Graphics gems II</title>
|
|
74
|
+
<uri type="src">http://openlibrary.org/books/OL21119585M/Graphics_gems_II</uri>
|
|
75
|
+
<docidentifier type="ISBN" primary="true">9780120644810</docidentifier>
|
|
76
|
+
...
|
|
77
|
+
</bibitem>"
|
|
99
78
|
|
|
100
79
|
# serialize to bibdata XML
|
|
101
|
-
|
|
102
|
-
<bibdata schema-version="v1.
|
|
103
|
-
|
|
104
|
-
|
|
80
|
+
bibitem.to_xml bibdata: true
|
|
81
|
+
=> "<bibdata schema-version="v1.4.1">
|
|
82
|
+
<fetched>2026-03-04</fetched>
|
|
83
|
+
<title type="main">Graphics gems II</title>
|
|
84
|
+
<uri type="src">http://openlibrary.org/books/OL21119585M/Graphics_gems_II</uri>
|
|
85
|
+
<docidentifier type="ISBN" primary="true">9780120644810</docidentifier>
|
|
86
|
+
...
|
|
87
|
+
<ext>
|
|
88
|
+
<flavor>isbn</flavor>
|
|
89
|
+
</ext>
|
|
90
|
+
</bibdata>"
|
|
105
91
|
|
|
106
92
|
# serialize to hash
|
|
107
|
-
|
|
108
|
-
=>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"docid"=>[{"id"=>"9780120644810", "type"=>"ISBN", "primary"=>true}],
|
|
113
|
-
"date"=>[{"type"=>"published", "value"=>"1991"}],
|
|
114
|
-
"contributor"=>
|
|
115
|
-
[{"person"=>{"name"=>{"completename"=>{"content"=>"James Arvo"}}, "url"=>"http://openlibrary.org/authors/OL2646519A/James_Arvo"}, "role"=>[{"type"=>"author"}]},
|
|
116
|
-
{"organization"=>{"name"=>[{"content"=>"AP Professional"}]}, "role"=>[{"type"=>"publisher"}]}],
|
|
117
|
-
"revdate"=>"1991",
|
|
118
|
-
"place"=>[{"city"=>"Boston"}, {"city"=>"London"}]}
|
|
93
|
+
puts bibitem.to_yaml
|
|
94
|
+
=> "---
|
|
95
|
+
id: '9780120644810'
|
|
96
|
+
schema_version: v1.4.1
|
|
97
|
+
..."
|
|
119
98
|
----
|
|
120
99
|
|
|
121
100
|
=== Logging
|
|
122
101
|
|
|
123
|
-
|
|
102
|
+
Relaton::Isbn uses the relaton-logger gem for logging. By default, it logs to STDOUT. To change the log levels and add other loggers, read the https://github.com/relaton/relaton-logger#usage[relaton-logger] documentation.
|
|
124
103
|
|
|
125
104
|
== Development
|
|
126
105
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Relaton
|
|
2
|
+
module Isbn
|
|
3
|
+
class Isbn
|
|
4
|
+
#
|
|
5
|
+
# Create ISBN object.
|
|
6
|
+
#
|
|
7
|
+
# @param [String] isbn ISBN 13 number
|
|
8
|
+
#
|
|
9
|
+
def initialize(isbn)
|
|
10
|
+
@isbn = isbn&.delete("-")&.sub(/^ISBN[\s:]/i, "")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse
|
|
14
|
+
convert_to13
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def check?
|
|
18
|
+
case @isbn
|
|
19
|
+
when /^\d{9}[\dX]$/i then check10?
|
|
20
|
+
when /^\d{13}$/ then check13?
|
|
21
|
+
else false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def check10?
|
|
26
|
+
@isbn[9] == calc_check_digit10
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def check13?
|
|
30
|
+
@isbn[12] == calc_check_digit13
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def calc_check_digit10
|
|
34
|
+
sum = 0
|
|
35
|
+
@isbn[..-2].chars.each_with_index do |c, i|
|
|
36
|
+
sum += c.to_i * (10 - i)
|
|
37
|
+
end
|
|
38
|
+
chk = (11 - sum % 11) % 11
|
|
39
|
+
chk == 10 ? "X" : chk.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def calc_check_digit13
|
|
43
|
+
sum = 0
|
|
44
|
+
@isbn[..-2].chars.each_with_index do |c, i|
|
|
45
|
+
sum += c.to_i * (i.even? ? 1 : 3)
|
|
46
|
+
end
|
|
47
|
+
((10 - sum % 10) % 10).to_s
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def convert_to13
|
|
51
|
+
return unless check?
|
|
52
|
+
|
|
53
|
+
return @isbn if @isbn.size == 13
|
|
54
|
+
|
|
55
|
+
@isbn = "978#{@isbn}"
|
|
56
|
+
@isbn[12] = calc_check_digit13
|
|
57
|
+
@isbn
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Relaton
|
|
2
|
+
module Isbn
|
|
3
|
+
#
|
|
4
|
+
# Search ISBN in Openlibrary.
|
|
5
|
+
#
|
|
6
|
+
module OpenLibrary
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "http://openlibrary.org/api/volumes/brief/isbn/".freeze
|
|
10
|
+
|
|
11
|
+
def get(ref, _date = nil, _opts = {}) # rubocop:disable Metrics/MethodLength
|
|
12
|
+
Util.info "Fetching from OpenLibrary ...", key: ref
|
|
13
|
+
|
|
14
|
+
isbn = Isbn.new(ref).parse
|
|
15
|
+
unless isbn
|
|
16
|
+
Util.info "Incorrect ISBN.", key: ref
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
resp = request_api isbn
|
|
21
|
+
unless resp
|
|
22
|
+
Util.info "Not found.", key: ref
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
bib = Parser.parse resp
|
|
27
|
+
Util.info "Found: `#{bib.docidentifier.first.content}`", key: ref
|
|
28
|
+
bib
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def request_api(isbn)
|
|
32
|
+
uri = URI "#{ENDPOINT}#{isbn}.json"
|
|
33
|
+
response = Net::HTTP.get_response uri
|
|
34
|
+
return unless response.is_a? Net::HTTPSuccess
|
|
35
|
+
|
|
36
|
+
data = JSON.parse response.body
|
|
37
|
+
return unless data["records"]&.any?
|
|
38
|
+
|
|
39
|
+
data["records"].first.last
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Relaton
|
|
2
|
+
module Isbn
|
|
3
|
+
#
|
|
4
|
+
# OpenLibrary document parser.
|
|
5
|
+
#
|
|
6
|
+
class Parser
|
|
7
|
+
ATTRS = %i[fetched title docidentifier source contributor date place ext].freeze
|
|
8
|
+
|
|
9
|
+
def initialize(doc)
|
|
10
|
+
@doc = doc
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.parse(doc)
|
|
14
|
+
new(doc).parse
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def parse
|
|
18
|
+
args = ATTRS.each_with_object({}) { |a, h| h[a] = send(a) }
|
|
19
|
+
Bib::ItemData.new(**args)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def fetched = Date.today.to_s
|
|
25
|
+
|
|
26
|
+
def title
|
|
27
|
+
t = [Bib::Title.new(content: @doc["data"]["title"], type: "main")]
|
|
28
|
+
if @doc["data"]["subtitle"]
|
|
29
|
+
t << Bib::Title.new(content: @doc["data"]["subtitle"], type: "subtitle")
|
|
30
|
+
end
|
|
31
|
+
t
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def docidentifier
|
|
35
|
+
isbn = @doc["details"]["bib_key"].split(":").last
|
|
36
|
+
[Bib::Docidentifier.new(content: isbn, type: "ISBN", primary: true)]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def source
|
|
40
|
+
[Bib::Uri.new(content: @doc["recordURL"], type: "src")]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def contributor
|
|
44
|
+
create_authors + creaate_publishers
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def create_authors
|
|
48
|
+
@doc["data"]["authors"].map do |a|
|
|
49
|
+
name = Bib::FullName.new(
|
|
50
|
+
completename: Bib::LocalizedString.new(content: a["name"]),
|
|
51
|
+
)
|
|
52
|
+
person = Bib::Person.new(
|
|
53
|
+
name: name,
|
|
54
|
+
uri: a["url"] ? [Bib::Uri.new(content: a["url"])] : [],
|
|
55
|
+
)
|
|
56
|
+
Bib::Contributor.new(
|
|
57
|
+
person: person,
|
|
58
|
+
role: [Bib::Contributor::Role.new(type: "author")],
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def creaate_publishers
|
|
64
|
+
@doc["data"]["publishers"].map do |p|
|
|
65
|
+
org = Bib::Organization.new(
|
|
66
|
+
name: [Bib::TypedLocalizedString.new(content: p["name"])],
|
|
67
|
+
)
|
|
68
|
+
Bib::Contributor.new(
|
|
69
|
+
organization: org,
|
|
70
|
+
role: [Bib::Contributor::Role.new(type: "publisher")],
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def date
|
|
76
|
+
@doc["publishDates"].map { Bib::Date.new type: "published", at: _1 }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def place
|
|
80
|
+
@doc["data"]["publish_places"]&.map { Bib::Place.new city: _1["name"] }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def ext
|
|
84
|
+
Bib::Ext.new(flavor: "isbn")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "relaton/core/processor"
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Isbn
|
|
5
|
+
class Processor < Relaton::Core::Processor
|
|
6
|
+
attr_reader :idtype
|
|
7
|
+
|
|
8
|
+
def initialize # rubocop:disable Lint/MissingSuper
|
|
9
|
+
@short = :relaton_isbn
|
|
10
|
+
@prefix = "ISBN"
|
|
11
|
+
@defaultprefix = /^ISBN\s/
|
|
12
|
+
@idtype = "ISBN"
|
|
13
|
+
@datasets = %w[]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param code [String]
|
|
17
|
+
# @param date [String, nil] year
|
|
18
|
+
# @param opts [Hash]
|
|
19
|
+
# @return [Relaton::Bib::Bibitem]
|
|
20
|
+
def get(code, date, opts)
|
|
21
|
+
require_relative "../isbn"
|
|
22
|
+
::Relaton::Isbn::OpenLibrary.get(code, date, opts)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# @param xml [String]
|
|
27
|
+
# @return [Relaton::Bib::Bibitem]
|
|
28
|
+
def from_xml(xml)
|
|
29
|
+
require_relative "../isbn"
|
|
30
|
+
::Relaton::Bib::Bibitem.from_xml xml
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @param hash [Hash]
|
|
34
|
+
# @return [Relaton::Bib::Bibitem]
|
|
35
|
+
def from_yaml(hash)
|
|
36
|
+
require_relative "../isbn"
|
|
37
|
+
::Relaton::Bib::Bibitem.from_hash hash
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns hash of XML grammar
|
|
41
|
+
# @return [String]
|
|
42
|
+
def grammar_hash
|
|
43
|
+
require_relative "../isbn"
|
|
44
|
+
@grammar_hash ||= ::Relaton::Isbn.grammar_hash
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/relaton/isbn.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "relaton/bib"
|
|
3
|
+
require_relative "isbn/version"
|
|
4
|
+
require_relative "isbn/util"
|
|
5
|
+
require_relative "isbn/isbn"
|
|
6
|
+
require_relative "isbn/parser"
|
|
7
|
+
require_relative "isbn/open_library"
|
|
8
|
+
|
|
9
|
+
module Relaton
|
|
10
|
+
module Isbn
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Returns hash of XML reammar
|
|
14
|
+
# @return [String]
|
|
15
|
+
def grammar_hash
|
|
16
|
+
Digest::MD5.hexdigest Relaton::Isbn::VERSION + Relaton::Bib::VERSION # grammars
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
CHANGED
|
@@ -1,30 +1,57 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: relaton-isbn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0.pre.alpha.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: isoics
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.1.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.1.0
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: relaton-bib
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
16
29
|
requirements:
|
|
17
30
|
- - "~>"
|
|
18
31
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
32
|
+
version: 2.0.0.pre.alpha.4
|
|
20
33
|
type: :runtime
|
|
21
34
|
prerelease: false
|
|
22
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
36
|
requirements:
|
|
24
37
|
- - "~>"
|
|
25
38
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
27
|
-
|
|
39
|
+
version: 2.0.0.pre.alpha.4
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: relaton-core
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.0.9
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 0.0.9
|
|
54
|
+
description: 'Relaton::Isbn: retrieve publications by ISBN for bibliographic use using
|
|
28
55
|
the BibliographicItem model'
|
|
29
56
|
email:
|
|
30
57
|
- open.source@ribose.com
|
|
@@ -32,26 +59,27 @@ executables: []
|
|
|
32
59
|
extensions: []
|
|
33
60
|
extra_rdoc_files: []
|
|
34
61
|
files:
|
|
62
|
+
- ".claude/settings.local.json"
|
|
35
63
|
- ".rspec"
|
|
36
64
|
- ".rubocop.yml"
|
|
65
|
+
- CLAUDE.md
|
|
37
66
|
- LICENSE.txt
|
|
38
67
|
- README.adoc
|
|
39
68
|
- Rakefile
|
|
40
|
-
- lib/
|
|
41
|
-
- lib/
|
|
42
|
-
- lib/
|
|
43
|
-
- lib/
|
|
44
|
-
- lib/
|
|
45
|
-
- lib/
|
|
46
|
-
- lib/
|
|
47
|
-
- sig/
|
|
69
|
+
- lib/relaton/isbn.rb
|
|
70
|
+
- lib/relaton/isbn/isbn.rb
|
|
71
|
+
- lib/relaton/isbn/open_library.rb
|
|
72
|
+
- lib/relaton/isbn/parser.rb
|
|
73
|
+
- lib/relaton/isbn/processor.rb
|
|
74
|
+
- lib/relaton/isbn/util.rb
|
|
75
|
+
- lib/relaton/isbn/version.rb
|
|
76
|
+
- sig/relaton/isbn.rbs
|
|
48
77
|
homepage: https://github.com/relaton/relaton-isbn
|
|
49
78
|
licenses:
|
|
50
79
|
- MIT
|
|
51
80
|
metadata:
|
|
52
81
|
homepage_uri: https://github.com/relaton/relaton-isbn
|
|
53
82
|
source_code_uri: https://github.com/relaton/relaton-isbn
|
|
54
|
-
post_install_message:
|
|
55
83
|
rdoc_options: []
|
|
56
84
|
require_paths:
|
|
57
85
|
- lib
|
|
@@ -59,16 +87,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
59
87
|
requirements:
|
|
60
88
|
- - ">="
|
|
61
89
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: 3.
|
|
90
|
+
version: 3.2.0
|
|
63
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
92
|
requirements:
|
|
65
93
|
- - ">="
|
|
66
94
|
- !ruby/object:Gem::Version
|
|
67
95
|
version: '0'
|
|
68
96
|
requirements: []
|
|
69
|
-
rubygems_version: 3.
|
|
70
|
-
signing_key:
|
|
97
|
+
rubygems_version: 3.6.9
|
|
71
98
|
specification_version: 4
|
|
72
|
-
summary: '
|
|
73
|
-
BibliographicItem model'
|
|
99
|
+
summary: 'Relaton::Isbn: retrieve publications by ISBN for bibliographic use using
|
|
100
|
+
the BibliographicItem model'
|
|
74
101
|
test_files: []
|
data/lib/relaton_isbn/isbn.rb
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
module RelatonIsbn
|
|
2
|
-
class Isbn
|
|
3
|
-
#
|
|
4
|
-
# Create ISBN object.
|
|
5
|
-
#
|
|
6
|
-
# @param [String] isbn ISBN 13 number
|
|
7
|
-
#
|
|
8
|
-
def initialize(isbn)
|
|
9
|
-
@isbn = isbn&.delete("-")&.sub(/^ISBN[\s:]/i, "")
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def parse
|
|
13
|
-
convert_to13
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def check?
|
|
17
|
-
case @isbn
|
|
18
|
-
when /^\d{9}[\dX]$/i then check10?
|
|
19
|
-
when /^\d{13}$/ then check13?
|
|
20
|
-
else false
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def check10?
|
|
25
|
-
@isbn[9] == calc_check_digit10
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def check13?
|
|
29
|
-
@isbn[12] == calc_check_digit13
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def calc_check_digit10
|
|
33
|
-
sum = 0
|
|
34
|
-
@isbn[..-2].chars.each_with_index do |c, i|
|
|
35
|
-
sum += c.to_i * (10 - i)
|
|
36
|
-
end
|
|
37
|
-
chk = (11 - sum % 11) % 11
|
|
38
|
-
chk == 10 ? "X" : chk.to_s
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def calc_check_digit13
|
|
42
|
-
sum = 0
|
|
43
|
-
@isbn[..-2].chars.each_with_index do |c, i|
|
|
44
|
-
sum += c.to_i * (i.even? ? 1 : 3)
|
|
45
|
-
end
|
|
46
|
-
((10 - sum % 10) % 10).to_s
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def convert_to13
|
|
50
|
-
return unless check?
|
|
51
|
-
|
|
52
|
-
return @isbn if @isbn.size == 13
|
|
53
|
-
|
|
54
|
-
@isbn = "978#{@isbn}"
|
|
55
|
-
@isbn[12] = calc_check_digit13
|
|
56
|
-
@isbn
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module RelatonIsbn
|
|
2
|
-
#
|
|
3
|
-
# Search ISBN in Openlibrary.
|
|
4
|
-
#
|
|
5
|
-
module OpenLibrary
|
|
6
|
-
extend self
|
|
7
|
-
|
|
8
|
-
ENDPOINT = "http://openlibrary.org/api/volumes/brief/isbn/".freeze
|
|
9
|
-
|
|
10
|
-
def get(ref, _date = nil, _opts = {}) # rubocop:disable Metrics/MethodLength
|
|
11
|
-
Util.info "Fetching from OpenLibrary ...", key: ref
|
|
12
|
-
|
|
13
|
-
isbn = Isbn.new(ref).parse
|
|
14
|
-
unless isbn
|
|
15
|
-
Util.info "Incorrect ISBN.", key: ref
|
|
16
|
-
return
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
resp = request_api isbn
|
|
20
|
-
unless resp
|
|
21
|
-
Util.info "Not found.", key: ref
|
|
22
|
-
return
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
bib = Parser.parse resp
|
|
26
|
-
Util.info "Found: `#{bib.docidentifier.first.id}`", key: ref
|
|
27
|
-
bib
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def request_api(isbn)
|
|
31
|
-
uri = URI "#{ENDPOINT}#{isbn}.json"
|
|
32
|
-
response = Net::HTTP.get_response uri
|
|
33
|
-
return unless response.is_a? Net::HTTPSuccess
|
|
34
|
-
|
|
35
|
-
data = JSON.parse response.body
|
|
36
|
-
return unless data["records"]&.any?
|
|
37
|
-
|
|
38
|
-
data["records"].first.last
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
data/lib/relaton_isbn/parser.rb
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
module RelatonIsbn
|
|
2
|
-
#
|
|
3
|
-
# OpenLibrary document parser.
|
|
4
|
-
#
|
|
5
|
-
class Parser
|
|
6
|
-
ATTRS = %i[fetched title docid link contributor date place].freeze
|
|
7
|
-
|
|
8
|
-
def initialize(doc)
|
|
9
|
-
@doc = doc
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def self.parse(doc)
|
|
13
|
-
new(doc).parse
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def parse
|
|
17
|
-
args = ATTRS.each_with_object({}) { |a, h| h[a] = send(a) }
|
|
18
|
-
RelatonBib::BibliographicItem.new(**args)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def fetched = Date.today.to_s
|
|
24
|
-
|
|
25
|
-
def title
|
|
26
|
-
t = [RelatonBib::TypedTitleString.new(content: @doc["data"]["title"], type: "main")]
|
|
27
|
-
if @doc["data"]["subtitle"]
|
|
28
|
-
t << RelatonBib::TypedTitleString.new(content: @doc["data"]["subtitle"], type: "subtitle")
|
|
29
|
-
end
|
|
30
|
-
t
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def docid
|
|
34
|
-
isbn = @doc["details"]["bib_key"].split(":").last
|
|
35
|
-
[RelatonBib::DocumentIdentifier.new(id: isbn, type: "ISBN", primary: true)]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def link
|
|
39
|
-
[RelatonBib::TypedUri.new(content: @doc["recordURL"], type: "src")]
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def contributor
|
|
43
|
-
create_authors + creaate_publishers
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def create_authors
|
|
47
|
-
@doc["data"]["authors"].map do |a|
|
|
48
|
-
name = RelatonBib::FullName.new completename: RelatonBib::LocalizedString.new(a["name"])
|
|
49
|
-
entity = RelatonBib::Person.new name: name, url: a["url"]
|
|
50
|
-
RelatonBib::ContributionInfo.new entity: entity, role: [{ type: "author" }]
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def creaate_publishers
|
|
55
|
-
@doc["data"]["publishers"].map do |p|
|
|
56
|
-
entity = RelatonBib::Organization.new name: RelatonBib::LocalizedString.new(p["name"])
|
|
57
|
-
RelatonBib::ContributionInfo.new entity: entity, role: [{ type: "publisher" }]
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def date
|
|
62
|
-
@doc["publishDates"].map { RelatonBib::BibliographicDate.new type: "published", on: _1 }
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def place
|
|
66
|
-
@doc["data"]["publish_places"]&.map { RelatonBib::Place.new city: _1["name"] }
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require "relaton/processor"
|
|
2
|
-
|
|
3
|
-
module RelatonIsbn
|
|
4
|
-
class Processor < Relaton::Processor
|
|
5
|
-
attr_reader :idtype
|
|
6
|
-
|
|
7
|
-
def initialize # rubocop:disable Lint/MissingSuper
|
|
8
|
-
@short = :relaton_isbn
|
|
9
|
-
@prefix = "ISBN"
|
|
10
|
-
@defaultprefix = /^ISBN\s/
|
|
11
|
-
@idtype = "ISBN"
|
|
12
|
-
@datasets = %w[]
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# @param code [String]
|
|
16
|
-
# @param date [String, nil] year
|
|
17
|
-
# @param opts [Hash]
|
|
18
|
-
# @return [RelatonBib::BibliographicItem]
|
|
19
|
-
def get(code, date, opts)
|
|
20
|
-
::RelatonIsbn::OpenLibrary.get(code, date, opts)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
# @param [String] source source name (bipm-data-outcomes, bipm-si-brochure,
|
|
25
|
-
# rawdata-bipm-metrologia)
|
|
26
|
-
# @param [Hash] opts
|
|
27
|
-
# @option opts [String] :output directory to output documents
|
|
28
|
-
# @option opts [String] :format
|
|
29
|
-
#
|
|
30
|
-
# def fetch_data(source, opts)
|
|
31
|
-
# DataFetcher.fetch(source, **opts)
|
|
32
|
-
# end
|
|
33
|
-
|
|
34
|
-
# @param xml [String]
|
|
35
|
-
# @return [RelatonBib::BibliographicItem]
|
|
36
|
-
def from_xml(xml)
|
|
37
|
-
::RelatonBib::XMLParser.from_xml xml
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# @param hash [Hash]
|
|
41
|
-
# @return [RelatonBib::BibliographicItem]
|
|
42
|
-
def hash_to_bib(hash)
|
|
43
|
-
::RelatonBib::BibliographicItem.from_hash hash
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Returns hash of XML grammar
|
|
47
|
-
# @return [String]
|
|
48
|
-
def grammar_hash
|
|
49
|
-
@grammar_hash ||= ::RelatonIsbn.grammar_hash
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
# Remove index file
|
|
54
|
-
#
|
|
55
|
-
# def remove_index_file
|
|
56
|
-
# end
|
|
57
|
-
end
|
|
58
|
-
end
|
data/lib/relaton_isbn/util.rb
DELETED
data/lib/relaton_isbn/version.rb
DELETED
data/lib/relaton_isbn.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
require "net/http"
|
|
2
|
-
require "relaton_bib"
|
|
3
|
-
require_relative "relaton_isbn/version"
|
|
4
|
-
require_relative "relaton_isbn/util"
|
|
5
|
-
require_relative "relaton_isbn/isbn"
|
|
6
|
-
require_relative "relaton_isbn/parser"
|
|
7
|
-
require_relative "relaton_isbn/open_library"
|
|
8
|
-
|
|
9
|
-
module RelatonIsbn
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
# Returns hash of XML reammar
|
|
13
|
-
# @return [String]
|
|
14
|
-
def grammar_hash
|
|
15
|
-
Digest::MD5.hexdigest RelatonIsbn::VERSION + RelatonBib::VERSION # grammars
|
|
16
|
-
end
|
|
17
|
-
end
|
data/sig/relaton_isbn.rbs
DELETED