relaton 3.0.7 → 3.1.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/bin/relaton +20 -0
- data/lib/relaton/3gpp/processor.rb +1 -1
- data/lib/relaton/3gpp.rb +3 -3
- data/lib/relaton/adobe/processor.rb +1 -1
- data/lib/relaton/adobe.rb +3 -3
- data/lib/relaton/bib/item_data.rb +2 -2
- data/lib/relaton/bib/model/docidentifier.rb +17 -2
- data/lib/relaton/bib.rb +2 -2
- data/lib/relaton/bibcollection.rb +146 -0
- data/lib/relaton/bibdata.rb +92 -0
- data/lib/relaton/bipm/data_outcomes_parser.rb +6 -1
- data/lib/relaton/bipm/processor.rb +1 -1
- data/lib/relaton/bipm.rb +3 -3
- data/lib/relaton/bsi/processor.rb +1 -1
- data/lib/relaton/bsi.rb +1 -1
- data/lib/relaton/calconnect/data_fetcher.rb +2 -2
- data/lib/relaton/calconnect/model/item.rb +1 -1
- data/lib/relaton/calconnect/processor.rb +1 -1
- data/lib/relaton/calconnect.rb +2 -2
- data/lib/relaton/ccsds/data/fetcher.rb +1 -1
- data/lib/relaton/ccsds/model/item.rb +1 -1
- data/lib/relaton/ccsds/processor.rb +1 -1
- data/lib/relaton/ccsds/util.rb +1 -1
- data/lib/relaton/ccsds.rb +1 -1
- data/lib/relaton/cen/processor.rb +1 -1
- data/lib/relaton/cen.rb +2 -2
- data/lib/relaton/cie/data_fetcher.rb +3 -3
- data/lib/relaton/cie/processor.rb +1 -1
- data/lib/relaton/cie.rb +2 -2
- data/lib/relaton/cli/base_convertor.rb +134 -0
- data/lib/relaton/cli/command.rb +396 -0
- data/lib/relaton/cli/data_fetcher.rb +20 -0
- data/lib/relaton/cli/frontend/dist/404.html +18 -0
- data/lib/relaton/cli/frontend/dist/app.iife.js +1 -0
- data/lib/relaton/cli/frontend/dist/style.css +3 -0
- data/lib/relaton/cli/frontend/index.html +14 -0
- data/lib/relaton/cli/frontend/package-lock.json +2945 -0
- data/lib/relaton/cli/frontend/package.json +29 -0
- data/lib/relaton/cli/frontend/public/404.html +18 -0
- data/lib/relaton/cli/frontend/src/App.test.ts +501 -0
- data/lib/relaton/cli/frontend/src/App.vue +506 -0
- data/lib/relaton/cli/frontend/src/app.ts +90 -0
- data/lib/relaton/cli/frontend/src/components/DocumentDetail.test.ts +142 -0
- data/lib/relaton/cli/frontend/src/components/DocumentDetail.vue +228 -0
- data/lib/relaton/cli/frontend/src/components/DocumentRow.vue +88 -0
- data/lib/relaton/cli/frontend/src/components/Icon.test.ts +44 -0
- data/lib/relaton/cli/frontend/src/components/Icon.vue +55 -0
- data/lib/relaton/cli/frontend/src/components/Pagination.vue +26 -0
- data/lib/relaton/cli/frontend/src/dev/main.ts +100 -0
- data/lib/relaton/cli/frontend/src/dev/sample-data.json +71 -0
- data/lib/relaton/cli/frontend/src/lib/filter.test.ts +88 -0
- data/lib/relaton/cli/frontend/src/lib/filter.ts +95 -0
- data/lib/relaton/cli/frontend/src/lib/hydrate.test.ts +102 -0
- data/lib/relaton/cli/frontend/src/lib/hydrate.ts +55 -0
- data/lib/relaton/cli/frontend/src/lib/shards.test.ts +319 -0
- data/lib/relaton/cli/frontend/src/lib/shards.ts +183 -0
- data/lib/relaton/cli/frontend/src/lib/types.ts +148 -0
- data/lib/relaton/cli/frontend/src/lib/url.test.ts +151 -0
- data/lib/relaton/cli/frontend/src/lib/url.ts +111 -0
- data/lib/relaton/cli/frontend/src/shims-vue.d.ts +5 -0
- data/lib/relaton/cli/frontend/src/styles/index.css +27 -0
- data/lib/relaton/cli/frontend/src/test/fetch.ts +58 -0
- data/lib/relaton/cli/frontend/tsconfig.json +20 -0
- data/lib/relaton/cli/frontend/vite.config.ts +42 -0
- data/lib/relaton/cli/frontend/vitest.config.ts +11 -0
- data/lib/relaton/cli/frontend_assets.rb +58 -0
- data/lib/relaton/cli/full_text_search.rb +98 -0
- data/lib/relaton/cli/i18n.yaml +1 -0
- data/lib/relaton/cli/index_item_normalizer.rb +380 -0
- data/lib/relaton/cli/index_site_generator.rb +1043 -0
- data/lib/relaton/cli/relaton_file.rb +290 -0
- data/lib/relaton/cli/subcommand_collection.rb +263 -0
- data/lib/relaton/cli/subcommand_db.rb +81 -0
- data/lib/relaton/cli/templates/_document.liquid +111 -0
- data/lib/relaton/cli/templates/_index.liquid +61 -0
- data/lib/relaton/cli/templates/index/page.liquid +20 -0
- data/lib/relaton/cli/templates/index-style.css +1017 -0
- data/lib/relaton/cli/util.rb +8 -0
- data/lib/relaton/cli/xml_convertor.rb +47 -0
- data/lib/relaton/cli/xml_to_html_renderer.rb +117 -0
- data/lib/relaton/cli/yaml_convertor.rb +102 -0
- data/lib/relaton/cli.rb +103 -0
- data/lib/relaton/core/processor.rb +1 -1
- data/lib/relaton/core.rb +1 -1
- data/lib/relaton/db/registry.rb +1 -1
- data/lib/relaton/db.rb +1 -1
- data/lib/relaton/doi/CLAUDE.md +1 -1
- data/lib/relaton/doi/parser.rb +2 -1
- data/lib/relaton/doi/processor.rb +1 -1
- data/lib/relaton/doi.rb +4 -4
- data/lib/relaton/easc/processor.rb +1 -1
- data/lib/relaton/easc.rb +3 -3
- data/lib/relaton/ecma/data_fetcher.rb +1 -1
- data/lib/relaton/ecma/processor.rb +1 -1
- data/lib/relaton/ecma.rb +2 -2
- data/lib/relaton/element_finder.rb +29 -0
- data/lib/relaton/etsi/data_fetcher.rb +2 -2
- data/lib/relaton/etsi/item.rb +1 -1
- data/lib/relaton/etsi/processor.rb +2 -2
- data/lib/relaton/etsi.rb +1 -1
- data/lib/relaton/evs/processor.rb +1 -1
- data/lib/relaton/evs.rb +2 -2
- data/lib/relaton/gb/CLAUDE.md +1 -1
- data/lib/relaton/gb/bibliography.rb +1 -1
- data/lib/relaton/gb/processor.rb +1 -1
- data/lib/relaton/gb.rb +2 -2
- data/lib/relaton/gost/processor.rb +1 -1
- data/lib/relaton/gost.rb +3 -3
- data/lib/relaton/iala/processor.rb +1 -1
- data/lib/relaton/iala.rb +3 -3
- data/lib/relaton/iana/data_fetcher.rb +1 -1
- data/lib/relaton/iana/processor.rb +1 -1
- data/lib/relaton/iana.rb +2 -2
- data/lib/relaton/iec/data_fetcher.rb +1 -1
- data/lib/relaton/iec/processor.rb +1 -1
- data/lib/relaton/iec.rb +2 -2
- data/lib/relaton/ieee/processor.rb +1 -1
- data/lib/relaton/ieee.rb +2 -2
- data/lib/relaton/ietf/CLAUDE.md +1 -1
- data/lib/relaton/ietf/data_fetcher.rb +1 -1
- data/lib/relaton/ietf/processor.rb +1 -1
- data/lib/relaton/ietf.rb +2 -2
- data/lib/relaton/iho/CLAUDE.md +3 -3
- data/lib/relaton/iho/bibliography.rb +41 -27
- data/lib/relaton/iho/hash_parser_v1.rb +1 -1
- data/lib/relaton/iho/processor.rb +4 -3
- data/lib/relaton/iho.rb +2 -2
- data/lib/relaton/index.rb +1 -1
- data/lib/relaton/isbn/processor.rb +1 -1
- data/lib/relaton/isbn.rb +1 -1
- data/lib/relaton/iso/hash_parser_v1.rb +1 -1
- data/lib/relaton/iso/processor.rb +1 -1
- data/lib/relaton/iso.rb +3 -3
- data/lib/relaton/itu/processor.rb +1 -1
- data/lib/relaton/itu.rb +3 -3
- data/lib/relaton/jcgm/processor.rb +1 -1
- data/lib/relaton/jcgm.rb +2 -2
- data/lib/relaton/jis/processor.rb +1 -1
- data/lib/relaton/jis.rb +2 -2
- data/lib/relaton/nist/CLAUDE.md +11 -1
- data/lib/relaton/nist/README.adoc +7 -1
- data/lib/relaton/nist/hit_collection.rb +25 -1
- data/lib/relaton/nist/mods_parser.rb +32 -8
- data/lib/relaton/nist/processor.rb +1 -1
- data/lib/relaton/nist/pubs_export.rb +69 -11
- data/lib/relaton/nist.rb +2 -2
- data/lib/relaton/oasis/processor.rb +1 -1
- data/lib/relaton/oasis.rb +2 -2
- data/lib/relaton/ogc/CLAUDE.md +1 -1
- data/lib/relaton/ogc/processor.rb +1 -1
- data/lib/relaton/ogc.rb +2 -2
- data/lib/relaton/oiml/processor.rb +1 -1
- data/lib/relaton/oiml.rb +2 -2
- data/lib/relaton/omg/CLAUDE.md +1 -1
- data/lib/relaton/omg/processor.rb +1 -1
- data/lib/relaton/omg.rb +1 -1
- data/lib/relaton/plateau/CLAUDE.md +1 -1
- data/lib/relaton/plateau/data_fetcher.rb +1 -1
- data/lib/relaton/plateau/processor.rb +1 -1
- data/lib/relaton/plateau.rb +2 -2
- data/lib/relaton/render/general.rb +319 -0
- data/lib/relaton/render/iso690/csl_import.rb +129 -0
- data/lib/relaton/render/iso690/element.rb +153 -0
- data/lib/relaton/render/iso690/elements/access.rb +44 -0
- data/lib/relaton/render/iso690/elements/authorizer.rb +45 -0
- data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
- data/lib/relaton/render/iso690/elements/component_part.rb +97 -0
- data/lib/relaton/render/iso690/elements/creator.rb +267 -0
- data/lib/relaton/render/iso690/elements/edition.rb +97 -0
- data/lib/relaton/render/iso690/elements/extent.rb +71 -0
- data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
- data/lib/relaton/render/iso690/elements/location.rb +73 -0
- data/lib/relaton/render/iso690/elements/medium.rb +32 -0
- data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
- data/lib/relaton/render/iso690/elements/production.rb +111 -0
- data/lib/relaton/render/iso690/elements/pub_date.rb +63 -0
- data/lib/relaton/render/iso690/elements/series.rb +66 -0
- data/lib/relaton/render/iso690/elements/size.rb +45 -0
- data/lib/relaton/render/iso690/elements/status.rb +30 -0
- data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
- data/lib/relaton/render/iso690/elements/title.rb +56 -0
- data/lib/relaton/render/iso690/elements/updated.rb +38 -0
- data/lib/relaton/render/iso690/elements.rb +67 -0
- data/lib/relaton/render/iso690/fields.rb +142 -0
- data/lib/relaton/render/iso690/i18n/en.yml +48 -0
- data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
- data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
- data/lib/relaton/render/iso690/i18n.rb +83 -0
- data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
- data/lib/relaton/render/iso690/index_renderer.rb +57 -0
- data/lib/relaton/render/iso690/kinds.rb +53 -0
- data/lib/relaton/render/iso690/renderer.rb +150 -0
- data/lib/relaton/render/iso690/rules/bipm.rb +186 -0
- data/lib/relaton/render/iso690/rules/jis.rb +103 -0
- data/lib/relaton/render/iso690/rules/nist.rb +384 -0
- data/lib/relaton/render/iso690/rules.rb +227 -0
- data/lib/relaton/render/iso690/style/locale.rb +59 -0
- data/lib/relaton/render/iso690/style/name_form.rb +87 -0
- data/lib/relaton/render/iso690/style/scheme.rb +33 -0
- data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
- data/lib/relaton/render/iso690/style/template_map.rb +40 -0
- data/lib/relaton/render/iso690/style/type_template.rb +45 -0
- data/lib/relaton/render/iso690/style.rb +227 -0
- data/lib/relaton/render/iso690/styles/apa.yml +45 -0
- data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
- data/lib/relaton/render/iso690/styles/chicago.yml +49 -0
- data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
- data/lib/relaton/render/iso690/styles/jis-en.yml +68 -0
- data/lib/relaton/render/iso690/styles/jis-ja.yml +67 -0
- data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
- data/lib/relaton/render/iso690/styles/mla.yml +56 -0
- data/lib/relaton/render/iso690/template.rb +248 -0
- data/lib/relaton/render/iso690.rb +28 -0
- data/lib/relaton/sae/CLAUDE.md +89 -0
- data/lib/relaton/sae/bibdata.rb +8 -0
- data/lib/relaton/sae/bibitem.rb +8 -0
- data/lib/relaton/sae/bibliography.rb +128 -0
- data/lib/relaton/sae/data_fetcher.rb +219 -0
- data/lib/relaton/sae/doi_code.rb +83 -0
- data/lib/relaton/sae/item.rb +10 -0
- data/lib/relaton/sae/item_data.rb +6 -0
- data/lib/relaton/sae/processor.rb +62 -0
- data/lib/relaton/sae/util.rb +9 -0
- data/lib/relaton/sae.rb +37 -0
- data/lib/relaton/un/CLAUDE.md +1 -1
- data/lib/relaton/un/processor.rb +1 -1
- data/lib/relaton/un.rb +1 -1
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton/w3c/bibliography.rb +1 -1
- data/lib/relaton/w3c/data_fetcher.rb +1 -1
- data/lib/relaton/w3c/processor.rb +1 -1
- data/lib/relaton/w3c.rb +2 -2
- data/lib/relaton/xsf/data_fetcher.rb +1 -1
- data/lib/relaton/xsf/processor.rb +1 -1
- data/lib/relaton/xsf.rb +2 -2
- data/lib/relaton-cli.rb +17 -0
- data/lib/relaton-render.rb +12 -0
- data/lib/relaton.rb +3 -2
- metadata +203 -23
data/lib/relaton/iala.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require "pubid"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
require_relative "bib"
|
|
3
|
+
require_relative "index"
|
|
4
|
+
require_relative "version"
|
|
5
5
|
|
|
6
6
|
# Relaton::Iala is the IALA (International Organization for Marine Aids
|
|
7
7
|
# to Navigation) flavor of the unified relaton gem. Adds typed Ext fields
|
data/lib/relaton/iana.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
|
-
|
|
4
|
+
require_relative "index"
|
|
5
5
|
require "pubid"
|
|
6
|
-
|
|
6
|
+
require_relative "bib"
|
|
7
7
|
require_relative "version"
|
|
8
8
|
require_relative "iana/util"
|
|
9
9
|
require_relative "iana/item_data"
|
data/lib/relaton/iec.rb
CHANGED
|
@@ -3,8 +3,8 @@ require "net/http"
|
|
|
3
3
|
require "moxml"
|
|
4
4
|
require "pubid"
|
|
5
5
|
require "zip"
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
require_relative "index"
|
|
7
|
+
require_relative "iso"
|
|
8
8
|
require_relative "version"
|
|
9
9
|
require_relative "iec/util"
|
|
10
10
|
require_relative "iec/bibliography"
|
data/lib/relaton/ieee.rb
CHANGED
|
@@ -2,8 +2,8 @@ require "digest/md5"
|
|
|
2
2
|
require "faraday"
|
|
3
3
|
require "yaml"
|
|
4
4
|
require "pubid"
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
require_relative "index"
|
|
6
|
+
require_relative "bib"
|
|
7
7
|
require_relative "version"
|
|
8
8
|
require_relative "ieee/util"
|
|
9
9
|
require_relative "ieee/bibliography"
|
data/lib/relaton/ietf/CLAUDE.md
CHANGED
|
@@ -375,7 +375,7 @@ and the affected files come through with their real characters —
|
|
|
375
375
|
- **An oracle for `read_bibxml` must read binary.** The method is byte-verbatim (`File.binread`), so comparing it against `File.read(path, encoding: "UTF-8")` compares two different strings **on Windows only**: Ruby opens a file in text mode there and collapses CRLF to LF. The repo declares no `.gitattributes` and the GitHub Windows runner image defaults to `core.autocrlf=true`, so a fixture stored with LF is checked out CRLF — which failed all three `windows-latest` jobs while every Linux and macOS job passed. Use `File.read(path, mode: "rb", encoding: "UTF-8")`. macOS cannot reproduce this by checking the fixture out as CRLF, because its text-mode read does not translate; emulate Windows explicitly with `File.read(path, encoding: "UTF-8", universal_newline: true)`. The two `keeps CRLF line endings verbatim` examples pin the contract with `binwrite` temp files, independent of the checkout — one per branch, since the CP1252 repair rebuilds the string run by run and is where a line ending is likeliest to be lost. Note that a byte-equality oracle alone cannot catch a dropped `force_encoding`: `rfc.xml` is pure ASCII, and Ruby compares two ASCII-only strings as equal across encodings, so the example asserts `encoding` separately.
|
|
376
376
|
- **Schema validation** via `ruby-jing` against the shared RNG grammars in the repo-root `grammar/` (`Jing.new "../../grammar/relaton-ietf-compile.rng"`, relative to the suite's CWD `spec/ietf/`)
|
|
377
377
|
- **Shared examples** for org/person parsing in `bibxml_parser_spec.rb` (`parse_org`, `parse_person`)
|
|
378
|
-
- **`
|
|
378
|
+
- **`canon`** gem used for XML comparison (`be_equivalent_to` matcher)
|
|
379
379
|
|
|
380
380
|
## RuboCop
|
|
381
381
|
|
data/lib/relaton/ietf.rb
CHANGED
data/lib/relaton/iho/CLAUDE.md
CHANGED
|
@@ -55,11 +55,11 @@ In `item.rb`, `require_relative "relation"` is placed **after** the `Item` class
|
|
|
55
55
|
|
|
56
56
|
`Bibliography.search`/`.get` fetches YAML from the relaton-data-iho GitHub repo via `Relaton::Index`, then deserializes into model objects.
|
|
57
57
|
|
|
58
|
-
**Editions and series.** The pubid
|
|
58
|
+
**Editions and series (the pubid 2.0.5 component model, relaton#440).** The pubid attribute is `edition` (dotted `a.b.c`, `IHO B-12 3.0.0`; the pre-440 name `version` is gone). A reference with no edition answers with the **latest**: `recency_key` compares the segments as integers (`2.10.0` after `2.9.0`). It used to be `min_by` on the string, which returned the oldest.
|
|
59
59
|
|
|
60
|
-
**
|
|
60
|
+
**Serial locators are volumes (relaton#440 unified the year locators of relaton#407).** The yearbook year IS a volume: `IHO P-5 Vol. 2020` is canonical, the bare-year faces (`P-5 2020`, `P-5/2020`) parse as legacy aliases into the same `volume`, and the IHR is `P-1 Vol. 30` with `Issue 1` (no parens) or the short slash form `P-1/30`. A bare series query answers with the latest volume/issue. **Components can publish separately and carry their own edition:** `IHO S-66 Suppl 1 2.0.0` gives `supplement_edition`, `S-100 Part S-102 1.0.0` gives `part_edition`, annexes likewise — and the publication years in parens (`S-52 Annex A 4.0.3 (2021)`) are `*_year` metadata, not identity: a stated year pins the record, an omitted one wildcards. Matching (`pubid_match?`) treats a discriminator the query omits as a wildcard: `IHO P-1 Vol. 30` matches any issue of volume 30. The comparison runs through pubid's `#exclude` — excluding `:volume` nils the `issue`, excluding an `:appendix` nils its `appendix_subnumber` (scalar clusters) — the way BIPM's `stem_keys` does. The subdivisions (part/appendix/annex/supplement) stay strict: this flavor cannot use the `===` subset match, because pubid does not declare `subset_strict` for IHO's subdivisions and a nil `part` would read as "any part" (`IHO S-100` would answer with a part row).
|
|
61
61
|
|
|
62
|
-
**The edition reaches the index only through the committed row.** A record prints `B-12` and keeps its edition in `edition:`, so `relaton index` cannot read a version from the docid. `MachineIndex#refine` (relaton-cli) takes the committed `index-v3.yaml` row when it adds only a key in `REFINABLE_KEYS` to the parse — `version`/`edition`/`volume`/`year`/`issue` (relaton#407 added the year/issue pair for the series rows). Before that, the Pages index (generated 2026-10-29) had no `version` in any of its 325 rows, so every versioned reference answered not-found (relaton-data-iho#20). The suite did not see it: `spec/fixtures/index-v3.zip` is the committed index, which has versions. The suite's fixture
|
|
62
|
+
**The edition reaches the index only through the committed row.** A record prints `B-12` and keeps its edition in `edition:`, so `relaton index` cannot read a version from the docid. `MachineIndex#refine` (relaton-cli) takes the committed `index-v3.yaml` row when it adds only a key in `REFINABLE_KEYS` to the parse — `version`/`edition`/`volume`/`year`/`issue` (relaton#407 added the year/issue pair for the series rows). Before that, the Pages index (generated 2026-10-29) had no `version` in any of its 325 rows, so every versioned reference answered not-found (relaton-data-iho#20). The suite did not see it: `spec/fixtures/index-v3.zip` is the committed index, which has versions. The suite's fixture carries the #48-era re-keyed series rows migrated to the #440 keys (`version`→`edition`/`<component>_edition`, year locators→`volume`; `support/migrate_index_fixture.rb` did the one-shot migration) — refresh the fixture from the live published zip once relaton-data-iho round 2 republishes, and reconcile any file-name drift then.
|
|
63
63
|
|
|
64
64
|
### Test Infrastructure
|
|
65
65
|
|
|
@@ -96,31 +96,37 @@ module Relaton
|
|
|
96
96
|
)
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
# The sort key for the latest record in a bucket (relaton#407
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# key
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
99
|
+
# The sort key for the latest record in a bucket (relaton#407,
|
|
100
|
+
# reshaped by relaton#440's component model). Volume-locator series
|
|
101
|
+
# (P-1, P-5) carry a `volume` with an optional `issue` — the
|
|
102
|
+
# yearbook year IS a volume (`P-5 Vol. 2020`; the bare-year faces
|
|
103
|
+
# of the #48 re-key parse as legacy aliases), so the year locators
|
|
104
|
+
# fold into the volume ordering. Edition-keyed products carry a
|
|
105
|
+
# dotted `edition` (`B-12 3.0.0`), and a component published
|
|
106
|
+
# separately carries its own edition (`S-66 Suppl 1 2.0.0` →
|
|
107
|
+
# `supplement_edition`). Every component compares numerically
|
|
108
|
+
# segment by segment (2.10.0 after 2.9.0); a row with no
|
|
109
|
+
# discriminator at all is the oldest.
|
|
109
110
|
def recency_key(id)
|
|
110
|
-
if id.
|
|
111
|
-
[id.year.to_i]
|
|
112
|
-
elsif id.volume
|
|
111
|
+
if id.volume
|
|
113
112
|
[id.volume.to_i, id.issue.to_i]
|
|
114
113
|
else
|
|
115
|
-
id.
|
|
114
|
+
ed = id.edition || id.supplement_edition || id.part_edition ||
|
|
115
|
+
id.annex_edition || id.appendix_edition || id.volume_edition
|
|
116
|
+
ed.to_s.split(".").map(&:to_i)
|
|
116
117
|
end
|
|
117
118
|
end
|
|
118
119
|
|
|
119
|
-
# The components
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
|
|
120
|
+
# The identity components beyond the series and subdivisions
|
|
121
|
+
# (relaton#440): the base `edition` — or the component-level
|
|
122
|
+
# edition when the component publishes separately — plus the
|
|
123
|
+
# volume/issue pair. The publication years (`year`, `*_year`) are
|
|
124
|
+
# metadata, not identity: a stated year pins the record, an
|
|
125
|
+
# omitted one wildcards it.
|
|
126
|
+
DISCRIMINATOR_KEYS = %i[
|
|
127
|
+
edition volume issue
|
|
128
|
+
supplement_edition part_edition annex_edition appendix_edition volume_edition
|
|
129
|
+
].freeze
|
|
124
130
|
|
|
125
131
|
# Both `row_id` and `query` are Pubid::Iho::Identifiers::* instances.
|
|
126
132
|
# Class identity pins the series (S/P/M/B/C). Subdivision keys
|
|
@@ -132,21 +138,29 @@ module Relaton
|
|
|
132
138
|
# so `IHO S-100` would answer with a part row.
|
|
133
139
|
#
|
|
134
140
|
# The discriminator keys stay nil-tolerant: an unqualified `IHO B-11`
|
|
135
|
-
# finds the latest edition, an unqualified `IHO P-5` the latest
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
+
# finds the latest edition, an unqualified `IHO P-5` the latest
|
|
142
|
+
# volume (the yearbook year — relaton#440 folded the year
|
|
143
|
+
# locators in), and `IHO P-1 Vol. 30` any issue of that volume —
|
|
144
|
+
# while a stated component must match exactly. The comparison runs
|
|
145
|
+
# through pubid's `#exclude` (the primitive `matches?` is built
|
|
146
|
+
# on), so the rules the flavor declares there ride along: excluding
|
|
147
|
+
# `:volume` nils the `issue` with it, and excluding an appendix
|
|
148
|
+
# nils its subnumber — the scalar clusters are one unit. `stem_keys`
|
|
149
|
+
# in the BIPM flavor is the same shape.
|
|
141
150
|
def pubid_match?(row_id, query)
|
|
142
151
|
row_id.class == query.class &&
|
|
143
152
|
row_id.exclude(*wildcards(query)) == query.exclude(*wildcards(query))
|
|
144
153
|
end
|
|
145
154
|
|
|
146
155
|
# The discriminators the query leaves unset: wildcards for the
|
|
147
|
-
# search, significant whenever the query states one
|
|
156
|
+
# search, significant whenever the query states one — plus the
|
|
157
|
+
# publication years, which are stated-metadata: pinned when the
|
|
158
|
+
# query carries them, wildcarded when it does not.
|
|
148
159
|
def wildcards(query)
|
|
149
|
-
|
|
160
|
+
keys = DISCRIMINATOR_KEYS + %i[
|
|
161
|
+
year supplement_year part_year annex_year appendix_year volume_year
|
|
162
|
+
]
|
|
163
|
+
keys.select { |key| query.public_send(key).nil? }
|
|
150
164
|
end
|
|
151
165
|
end
|
|
152
166
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative "../core/processor"
|
|
2
2
|
|
|
3
3
|
module Relaton
|
|
4
4
|
module Iho
|
|
@@ -26,9 +26,10 @@ module Relaton
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# An IHO record prints `B-12` and keeps its edition in `edition`, so
|
|
29
|
-
# the edition goes on the key
|
|
29
|
+
# the edition goes on the key (relaton#408). The component model
|
|
30
|
+
# (relaton#440) renamed the pubid attribute `version` -> `edition`.
|
|
30
31
|
def item_cache_key(pubid, item)
|
|
31
|
-
with_components pubid,
|
|
32
|
+
with_components pubid, edition: item.edition&.content
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# @param xml [String]
|
data/lib/relaton/iho.rb
CHANGED
data/lib/relaton/index.rb
CHANGED
data/lib/relaton/isbn.rb
CHANGED
data/lib/relaton/iso.rb
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require "net/http"
|
|
4
4
|
require "pubid"
|
|
5
|
-
|
|
5
|
+
require_relative "index"
|
|
6
6
|
require "isoics"
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
require_relative "bib"
|
|
8
|
+
require_relative "core"
|
|
9
9
|
require_relative "version"
|
|
10
10
|
require_relative "iso/util"
|
|
11
11
|
require_relative "iso/item_data"
|
data/lib/relaton/itu.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require "mechanize"
|
|
2
2
|
require "pubid"
|
|
3
3
|
require "digest/md5"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
require_relative "index"
|
|
5
|
+
require_relative "bib"
|
|
6
|
+
require_relative "core"
|
|
7
7
|
require_relative "version"
|
|
8
8
|
require_relative "itu/util"
|
|
9
9
|
require_relative "itu/item_data"
|
data/lib/relaton/jcgm.rb
CHANGED
data/lib/relaton/jis.rb
CHANGED
data/lib/relaton/nist/CLAUDE.md
CHANGED
|
@@ -69,7 +69,8 @@ All data models inherit from `Lutaml::Model::Serializable` and use declarative a
|
|
|
69
69
|
- **`#write_file` serializes before it indexes**, so a record that fails to serialize leaves no index row naming a file that was never written. That is how a `Bib::Date` in `Scraper.fetch_dates` hid: `Nist::ItemData` takes `Nist::Date`, and YAML serialization rejects the parent class (the same strictness as the docidentifier note below).
|
|
70
70
|
- `ModsParser` — maps MODS XML (via `loc_mods`) to `ItemData`
|
|
71
71
|
- `Scraper` — fetches items from GitHub YAML or CSRC JSON
|
|
72
|
-
- `PubsExport` — singleton; caches CSRC pubs-export zip with thread-safe daily updates
|
|
72
|
+
- `PubsExport` — singleton; caches CSRC pubs-export zip with thread-safe daily updates. The cache file is `#datafile`, under `Relaton::Index.config.storage_dir` (default `Dir.home`; the `DATAFILE` constant is only that default).
|
|
73
|
+
- **A read-only file system must not stop a search (relaton-nist#57).** When the cache write fails, `#fetch_data` keeps the downloaded zip in `@buffer`, and `#unzip` reads it with `Zip::File.open_buffer`. In rubyzip 3, `open_buffer` returns the buffer, not the block value, so collect the rows in a local. The cache write goes to a temp file and is renamed into place (`#write_datafile`), so a failed write never leaves a truncated zip that later processes read as fresh. `HitCollection#search` also rescues the local-cache errors in `LOCAL_DATA_ERRORS` from `from_json` (`#from_json_or_empty`), logs a warning, and continues with `from_ga`. Do not widen that list to `SystemCallError`: `Errno::ECONNREFUSED` is one, and a CSRC outage would then answer "not found" from the index, which `Relaton::Db` can cache. Specs assert the warning with `output(...).to_stderr_from_any_process`. A `receive(:warn)` stub on `Util` is never hit: `Kernel#warn` is private, the stub stays private, and the explicit-receiver call goes to `method_missing`.
|
|
73
74
|
|
|
74
75
|
### Pubid-backed docidentifier
|
|
75
76
|
|
|
@@ -115,6 +116,15 @@ NIST-specific gotchas (why this isn't a straight copy of IEC/CCSDS):
|
|
|
115
116
|
numbered editions/revisions (`e2`, `r5`). Dates that ride the `update`
|
|
116
117
|
component (some NBS supplements) are left intact — `update` also encodes
|
|
117
118
|
non-date update codes (`/Upd2`), so stripping it would corrupt identity.
|
|
119
|
+
- **The item's `edition` comes from the DOI (relaton-nist#56).**
|
|
120
|
+
`ModsParser#parse_edition` sets `<edition number="N">N</edition>` only for a
|
|
121
|
+
numbered `e<N>` edition in the DOI (`NBS.CIRC.111e2` → 2, `11e2.1915` → 2):
|
|
122
|
+
250 of the 20,392 live MODS records. An `e<year>` edition is a date, and an
|
|
123
|
+
`r<N>` revision is not an edition, so both stay without one. The MODS
|
|
124
|
+
`<edition>` element is prose (`New version`, `Erratum`), never a number. The
|
|
125
|
+
issue's "edition 0 → 1" rule came from the old Crossref feed; it is kept for
|
|
126
|
+
a DOI `e0` (none in the live data). A DOI pubid cannot parse gives no
|
|
127
|
+
edition, not an error.
|
|
118
128
|
- **No `(all parts)` marker in `content`.** `content` is live-derived from
|
|
119
129
|
`@pubid`, so `to_all_parts!` freezes the already-stripped rendering into
|
|
120
130
|
`@raw_content` before wrapping `@pubid` in pubid's `AllParts` — matching
|
|
@@ -311,7 +311,13 @@ Relaton::Nist::Item.from_yaml File.read("spec/fixtures/item.yaml")
|
|
|
311
311
|
|
|
312
312
|
=== Fetching data
|
|
313
313
|
|
|
314
|
-
On a search, the CSRC data sources are fetched and stored in the user's cache
|
|
314
|
+
On a search, the CSRC data sources are fetched and stored in the user's cache,
|
|
315
|
+
`~/.relaton/nist/pubs-export.zip`. The cache follows
|
|
316
|
+
`Relaton::Index.config.storage_dir`, so one setting moves it together with the
|
|
317
|
+
index cache. When the cache cannot be written (for example on a read-only file
|
|
318
|
+
system such as AWS Lambda), the data stays in memory for the process. When the
|
|
319
|
+
CSRC data cannot be used at all, the search continues with the
|
|
320
|
+
`relaton-data-nist` index.
|
|
315
321
|
|
|
316
322
|
This gem uses the https://github.com/usnistgov/NIST-Tech-Pubs/releases/download/May2024/allrecords-MODS.xml dataset as NIST-Tech-Pubs data source.
|
|
317
323
|
|
|
@@ -12,6 +12,12 @@ module Relaton
|
|
|
12
12
|
|
|
13
13
|
GHNISTDATA = "https://raw.githubusercontent.com/relaton/relaton-data-nist/v2/"
|
|
14
14
|
|
|
15
|
+
# Errors of the local CSRC cache (read-only or full disk, a bad zip).
|
|
16
|
+
LOCAL_DATA_ERRORS = [
|
|
17
|
+
Errno::EROFS, Errno::EACCES, Errno::EPERM, Errno::ENOSPC,
|
|
18
|
+
Errno::ENOENT, Zip::Error
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
15
21
|
attr_reader :reference
|
|
16
22
|
attr_accessor :array
|
|
17
23
|
|
|
@@ -47,7 +53,7 @@ module Relaton
|
|
|
47
53
|
# @return [Relaton::Nist::HitCollection] hits collection
|
|
48
54
|
#
|
|
49
55
|
def search
|
|
50
|
-
@array =
|
|
56
|
+
@array = from_json_or_empty
|
|
51
57
|
@array = from_ga unless @array.any?
|
|
52
58
|
sort_hits!
|
|
53
59
|
end
|
|
@@ -400,6 +406,24 @@ module Relaton
|
|
|
400
406
|
select_data.map { |h| Hit.new(hit_data(h), self) }
|
|
401
407
|
end
|
|
402
408
|
|
|
409
|
+
#
|
|
410
|
+
# The CSRC hits, or none when the local CSRC data cannot be used: a
|
|
411
|
+
# file-system error must not stop the search, because the
|
|
412
|
+
# relaton-data-nist index can still answer (relaton-nist#57). Network
|
|
413
|
+
# errors are not rescued: they still reach Bibliography.search, which
|
|
414
|
+
# raises Relaton::RequestError, so a CSRC outage is not cached as
|
|
415
|
+
# "not found".
|
|
416
|
+
#
|
|
417
|
+
# @return [Array<Relaton::Nist::Hit>] hits
|
|
418
|
+
#
|
|
419
|
+
def from_json_or_empty
|
|
420
|
+
from_json
|
|
421
|
+
rescue *LOCAL_DATA_ERRORS => e
|
|
422
|
+
Util.warn "CSRC pubs-export data is not available (#{e.message}); " \
|
|
423
|
+
"searching the Relaton repository only", key: @reference
|
|
424
|
+
[]
|
|
425
|
+
end
|
|
426
|
+
|
|
403
427
|
def pubs_export_id(json)
|
|
404
428
|
self.class.pubs_export_id json
|
|
405
429
|
end
|
|
@@ -13,7 +13,7 @@ module Relaton
|
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
15
|
ATTRS = %i[type docidentifier title source abstract date contributor
|
|
16
|
-
relation place series].freeze
|
|
16
|
+
relation place series edition].freeze
|
|
17
17
|
|
|
18
18
|
def initialize(doc, series, errors = {})
|
|
19
19
|
@doc = doc
|
|
@@ -51,17 +51,41 @@ module Relaton
|
|
|
51
51
|
def get_id_from_str(str)
|
|
52
52
|
return if str.nil? || str.empty?
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
# parses through a path that skips UpdateCodes date lookup and drops
|
|
56
|
-
# letter suffixes. Space the leading "PUB.SERIES." prefix so it parses
|
|
57
|
-
# like the canonical short form (only the separator dots are touched,
|
|
58
|
-
# not any inside the number). Then force :human rendering.
|
|
59
|
-
spaced = str.sub(/\A(NIST|NBS)\.([A-Z][A-Za-z]*)\./, '\1 \2 ')
|
|
60
|
-
::Pubid::Nist::Identifier.parse(spaced).to_s(format: :human)
|
|
54
|
+
parse_pubid(str).to_s(format: :human)
|
|
61
55
|
rescue ::Pubid::Errors::ParseError
|
|
62
56
|
str.gsub(".", " ").sub(/^[\D]+/, &:upcase)
|
|
63
57
|
end
|
|
64
58
|
|
|
59
|
+
# DOIs arrive dotted ("NIST.HB.135e2022-upd1"); that MR-style form
|
|
60
|
+
# parses through a path that skips UpdateCodes date lookup and drops
|
|
61
|
+
# letter suffixes. Space the leading "PUB.SERIES." prefix so it parses
|
|
62
|
+
# like the canonical short form (only the separator dots are touched,
|
|
63
|
+
# not any inside the number).
|
|
64
|
+
def parse_pubid(str)
|
|
65
|
+
spaced = str.sub(/\A(NIST|NBS)\.([A-Z][A-Za-z]*)\./, '\1 \2 ')
|
|
66
|
+
::Pubid::Nist::Identifier.parse(spaced)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The edition number a DOI states ("NBS.CIRC.111e2" -> 2), relaton-nist#56.
|
|
70
|
+
# The MODS carries no edition number of its own (its <edition> is prose:
|
|
71
|
+
# "New version", "Erratum"), so the DOI is the only source. Only a
|
|
72
|
+
# numbered "e<N>" edition counts: an "e<year>" edition is a date, and an
|
|
73
|
+
# "r<N>" revision is not an edition. An edition 0 counts as 1 (the issue's
|
|
74
|
+
# second rule; no live DOI has one).
|
|
75
|
+
# @return [Bib::Edition, nil]
|
|
76
|
+
def parse_edition
|
|
77
|
+
doi = parse_doi
|
|
78
|
+
return if doi.nil? || doi.empty?
|
|
79
|
+
|
|
80
|
+
edition = parse_pubid(doi).edition
|
|
81
|
+
return unless edition&.type == "e" && edition.id.to_s.match?(/\A\d{1,3}\z/)
|
|
82
|
+
|
|
83
|
+
number = edition.id.to_i.zero? ? "1" : edition.id
|
|
84
|
+
Bib::Edition.new(number: number, content: number)
|
|
85
|
+
rescue ::Pubid::Errors::Error, ArgumentError
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
|
|
65
89
|
# @return [String]
|
|
66
90
|
def replace_wrong_doi(id)
|
|
67
91
|
case id
|