relaton 3.0.2 → 3.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f15745a534625b27b3d013d7b3d6edd7e0185f1bcc1c545f7b44dc6059d9d113
4
- data.tar.gz: ac15b5259abb3c980cd6bee9d10c5f2f0ae12fea955478ca28f7da82027ab6f6
3
+ metadata.gz: a774b326943089257e3aef6e5402fc1e2d53d4ff557e8b19f845a62d64b9a99b
4
+ data.tar.gz: 665fa378d2380f79a4e2952df1ff3d0f7e1de6d91ead262d75261555a4170e5c
5
5
  SHA512:
6
- metadata.gz: de3f2c7182f6a2b1737a54e9168bcab856dbc81fe169f293b5b932f0224e8cb95187f506829230068ebc5308884e89a918515b03838f969c9c9870020442efff
7
- data.tar.gz: a4bfe1d14667d8907eecd43fe880b02e7d15ceab20d2c6302c8f5c220d6c4398b3adefa056ff8815639686ad85674713c5c3a4759bd5ad57ffdd1e42b9bc39e1
6
+ metadata.gz: bfa2af357a4e288f0ce97fb82ff78ac58b39bc8f8fc8659584ebb29a4568a50175137b8b8cd32b6710aff65b8413cb4ab0d1ea674918187edd9a74592e331a6c
7
+ data.tar.gz: e2ad6a9822a6ffa5f9a625bb4095260b81b3906fcff8ba0cf4fbdcea0d543f9e2b14133c7dd29dad0f165c26713b16fc0f06ebfc06d134384d4d0f280ee873b1
data/lib/relaton/bipm.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require "zip"
2
2
  require "fileutils"
3
- require "parslet"
4
3
  require "relaton/bib"
5
4
  require "relaton/index"
6
5
  require "relaton/core"
@@ -67,7 +67,7 @@ module Relaton
67
67
  # @return [Pubid::Identifier, nil]
68
68
  def cache_pubid(ref)
69
69
  pubid_class&.parse(ref)
70
- rescue ArgumentError, ::Pubid::Errors::Error, Parslet::ParseFailed
70
+ rescue ArgumentError, ::Pubid::Errors::Error
71
71
  # relaton-bib carries the log facade; core specs load without it.
72
72
  require "relaton/bib"
73
73
  Relaton::Bib::Util.info "does not parse as this flavor's pubid; " \
@@ -221,7 +221,7 @@ module Relaton
221
221
  require "pubid"
222
222
  parsed = ::Pubid.parse reference
223
223
  parsed if parsed.to_s == reference
224
- rescue ArgumentError, ::Pubid::Errors::Error, Parslet::ParseFailed
224
+ rescue ArgumentError, ::Pubid::Errors::Error
225
225
  nil
226
226
  end
227
227
 
data/lib/relaton/db.rb CHANGED
@@ -226,7 +226,7 @@ module Relaton
226
226
  # reference they cannot read is not-found, not a failure (relaton#412).
227
227
  def unreadable_reference?(error)
228
228
  (defined?(Pubid::Errors::Error) && error.is_a?(Pubid::Errors::Error)) ||
229
- (defined?(Parslet::ParseFailed) && error.is_a?(Parslet::ParseFailed))
229
+ false
230
230
  end
231
231
 
232
232
  # The flavor routing picks for `fetch_async`'s per-flavor queue; nil
@@ -389,7 +389,7 @@ module Relaton
389
389
  return unless docid&.content
390
390
 
391
391
  cache_key docid.content, nil, {}, stdclass
392
- rescue ::Pubid::Errors::Error, Parslet::ParseFailed
392
+ rescue ::Pubid::Errors::Error
393
393
  nil
394
394
  end
395
395
 
@@ -456,7 +456,7 @@ module Relaton
456
456
  def copublisher_query(code, opts, stdclass, publisher)
457
457
  query = begin
458
458
  @registry[stdclass].query_pubid(code, opts)
459
- rescue ::Pubid::Errors::Error, Parslet::ParseFailed
459
+ rescue ::Pubid::Errors::Error
460
460
  nil
461
461
  end
462
462
  return query if query
@@ -64,7 +64,8 @@ All data models inherit from `Lutaml::Model::Serializable` and use declarative a
64
64
  - **Per-record resilience (mirrors the ISO flavor).** A single bad record must never abort the crawl. `#write_file` skips (never `id.content`-crashes on) a record whose MODS entry yields **no docidentifier** (e.g. a DOI stored only in `<identifier type="doi">`, which `ModsParser#parse_doi` doesn't read), and records ids that `#pubid` can't parse (new series like `NIST RB`/`NIST CHIPS`) as "not indexed" — the YAML is still written, only the index-v2 entry is skipped. Both classes accumulate in `#failures`; the `#report_errors` override emits each at `:error` (via `#log_error` → `Util.error`), which the shared `Core::DataFetcher` gh_issue machinery turns into one "Error fetching documents" GitHub issue in CI (`GITHUB_REPOSITORY` + `GITHUB_TOKEN`). `#pubid` no longer warns per-failure; reporting is centralized in `#report_errors`.
65
65
  - **Draft-stage rows come from CSRC (relaton#313).** The MODS collection has no draft records, so `#fetch_csrc` adds the CSRC pubs-export feed (`PubsExport.instance.data`) after the MODS pass. It builds each row **exactly as the runtime builds a CSRC hit** — `HitCollection#hit_data` (which calls `#pubs_export_id`) → `Scraper.parse_page` — so the crawled id and the live id are one string, with the stage, revision and update from the row's uri and DOI (`NIST FIPS 197/Upd1 ipd`, `NIST SP 800-108r1 ipd`, `NIST SP 1800-12 2pd`). Only a row whose id carries a **stage** is written; finals come from MODS, so a CSRC row never shadows a base document. On the 914-row fixture feed this writes 302 staged records. Don't go back to a hand-built `"NIST #{series} #{docnumber} #{stage}"` id: it dropped the numbered drafts (`2pd`…), the `/UpdN`, and spelled revisions `Rev. 1`. A stage that CSRC does not list (e.g. `NIST FIPS 197 fpd`) still answers through the consumer's #303 family fallback. Three more rules: `#csrc_bib` clears the `fetched` date the runtime stamps, or every draft changes each day and `Core#force_write` rewrites ~300 files per incremental crawl; CSRC lists a republished draft twice (`…/ipd` and `…/ipd-(1)`, one DOI, so one id), so each id is written once and the plain uri wins; and only the build step is rescued (to a `#failures` entry) — a write error stops the crawl, as in the MODS pass. `#pubs_export_id` must not mutate its row (`sub`, not `sub!`): the crawler and the runtime share the `PubsExport` data.
66
66
  - **A CSRC relation names the related document (relaton#314).** `Scraper#related_id` finds the related feed row by its uri and derives its id with `HitCollection.pubs_export_id` (the class method the instance method delegates to). The entry's own `docidentifier` drops the update and the part, and the hit's code is the record's own id, so neither is the related id. `HitCollection#search_filter` ends in an `ensure` that returns `arr`: every branch must set `arr`, because an earlier `return` is overridden there.
67
- - **CSWP comes from CSRC, not the NIST Library (relaton-nist#104).** The NIST Library announced CSWP data, but its MODS release (`allrecords-MODS.xml`, and the MARC `allrecords.xml`) holds **no** CSWP record — 0 on the Sept2026 release. It publishes CSWPs only as RIS/BibTeX files under `bib/` on its `nist-pages` branch. So the index has only the CSRC draft CSWP rows (`#fetch_csrc`), and a final CSWP answers from CSRC at runtime: `HitCollection#search` asks the CSRC feed (`from_json`) and asks the index (`from_ga`) only when CSRC gives no hit. That is the chosen rule — CSRC wins, no merge — because CSRC lists every numbered CSWP with more metadata (affiliations, status, supersedes, comment period). The known gap: the NIST Library also has about 35 **legacy dated ids** (`NIST CSWP 01142020`, the same paper as `CSWP 9`, from before NIST renumbered the series) and translations (`NIST.CSWP.6.spa`). CSRC lists neither, so they answer not-found; a fix needs a mapping built from those RIS files. `hit_collection_spec.rb` pins the CSRC answer.
67
+ - **CSWP comes from CSRC, not the NIST Library (relaton-nist#104).** The NIST Library announced CSWP data, but its MODS release (`allrecords-MODS.xml`, and the MARC `allrecords.xml`) holds **no** CSWP record — 0 on the Sept2026 release. It publishes CSWPs only as RIS/BibTeX files under `bib/` on its `nist-pages` branch. So the index has only the CSRC draft CSWP rows (`#fetch_csrc`), and a final CSWP answers from CSRC at runtime: `HitCollection#search` asks the CSRC feed (`from_json`) and asks the index (`from_ga`) only when CSRC gives no hit. That is the chosen rule — CSRC wins, no merge — because CSRC lists every numbered CSWP with more metadata (affiliations, status, supersedes, comment period). `hit_collection_spec.rb` pins the CSRC answer.
68
+ - **Legacy dated CSWP ids and CSWP translations come from a static table (relaton#406).** CSRC lists neither the ~30 **legacy dated ids** (`NIST CSWP 01142020`, the same paper as `CSWP 9`, from before NIST numbered the series) nor the translations (`NIST.CSWP.6.spa`). `cswp.yaml` holds both, built once from the 84 RIS files (header of the file says how), and `Cswp` reads it. `Bibliography.get` asks `Cswp.lookup` **first** — the parenthetical-date regex after it would strip `(spa)`. A legacy id fetches the numbered id through the normal CSRC path and adds the dated id as a non-primary docidentifier (`06222021-draft` → the CSRC `CSWP 19 ipd`); a translation, dated or numbered, answers an offline record from the table (`NIST CSWP 6(spa)`, the RIS title/authors/year/DOI, `language`/`script` from `Cswp::LANGUAGES`, a `translatedFrom` relation to the original). `normalize` reads `NIST CSWP 6 spa`, `NIST CSWP 6(spa)` and `NIST.CSWP.6.spa` as one key. `Processor#cache_pubid` returns nil (so `Db` hands `get` the String and does not cache) for **every legacy id** and for a translation that pubid does not render back. A legacy answer is the numbered record plus the dated id, and `Db` would store it under the numbered id's row, so a later `NIST CSWP 9` would carry `NIST CSWP 01142020`. pubid reads `04162018ar` as `04162018a`, `6 spa` as `6(spa)`, and fails on `04162018bg`/`02122014pl`. A translation answers only for its own RIS year. Not mapped, on purpose: `02042022-1`…`-5` have no SN and share the title "Cybersecurity White Paper: EO Response", so they still answer not-found. The CSRC `ipd` rows of `27`/`29`/`32` already answer, so the table leaves out their RIS files. A new translation means a new table row.
68
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).
69
70
  - `ModsParser` — maps MODS XML (via `loc_mods`) to `ItemData`
70
71
  - `Scraper` — fetches items from GitHub YAML or CSRC JSON
@@ -39,6 +39,9 @@ module Relaton
39
39
  # read back as its printed form; the pubid itself is not changed.
40
40
  ref = ref.to_s unless ref.is_a?(String)
41
41
  return fetch_ref_err(ref, year, []) if ref.match?(/\sEP$/)
42
+ # Before the parsing below, which reads "(spa)" as a date.
43
+ cswp = Cswp.lookup(ref)
44
+ return cswp_get(ref, cswp, year, opts) if cswp
42
45
 
43
46
  /^(?<code2>[^(]+)(?:\((?<date2>\w+\s(?:\d{2},\s)?\d{4})\))?\s?\(?(?:(?<=\()(?<stage>(?:I|F|\d)PD))?/ =~ ref
44
47
  stage ||= /(?<=\.)PD-\w+(?=\.)/.match(ref)&.to_s
@@ -62,6 +65,34 @@ module Relaton
62
65
 
63
66
  private
64
67
 
68
+ #
69
+ # Get a CSWP that CSRC does not list (relaton#406). A legacy dated id
70
+ # answers the numbered CSRC record, with the dated id as an extra
71
+ # docidentifier; a translation answers its record from the RIS table.
72
+ #
73
+ # @param ref [String] reference
74
+ # @param cswp [Array] the Cswp.lookup result
75
+ # @param year [String, nil] year
76
+ # @param opts [Hash] options
77
+ #
78
+ # @return [Relaton::Nist::ItemData, nil] bibliographic item
79
+ #
80
+ def cswp_get(ref, cswp, year, opts)
81
+ type, id, dated = cswp
82
+ if type == :translation
83
+ return fetch_ref_err(ref, year, [id[:year]]) if year && year.to_s != id[:year]
84
+
85
+ item = Cswp.translation_item(id)
86
+ Util.info "Found: `#{item.docidentifier.first.content}`", key: ref
87
+ return item
88
+ end
89
+
90
+ Util.info "Legacy dated CSWP id, fetching `#{id}`", key: ref
91
+ item = nistbib_get(id, year, opts)
92
+ item&.docidentifier&.<< Docidentifier.new(content: dated, type: "NIST")
93
+ item
94
+ end
95
+
65
96
  #
66
97
  # Get NIST document by reference
67
98
  #
@@ -0,0 +1,137 @@
1
+ require "yaml"
2
+
3
+ module Relaton
4
+ module Nist
5
+ #
6
+ # CSWP ids the CSRC pubs-export feed does not list (relaton#406): the
7
+ # legacy dated ids from before the series was numbered, and the
8
+ # translations. The table (`cswp.yaml`) is built from the NIST Library RIS
9
+ # files; see the header of that file.
10
+ #
11
+ # This file loads no flavor model at require time, so the processor can
12
+ # ask #lookup on the cold path. #translation_item needs the models.
13
+ #
14
+ module Cswp
15
+ # ISO 639-2 code of a translation => [ISO 639-1 code, ISO 15924 script]
16
+ LANGUAGES = {
17
+ "ara" => %w[ar Arab], "bul" => %w[bg Cyrl], "fre" => %w[fr Latn],
18
+ "gre" => %w[el Grek], "ind" => %w[id Latn], "jpn" => %w[ja Jpan],
19
+ "kor" => %w[ko Kore], "may" => %w[ms Latn], "nor" => %w[no Latn],
20
+ "pol" => %w[pl Latn], "por" => %w[pt Latn], "spa" => %w[es Latn],
21
+ "ukr" => %w[uk Cyrl]
22
+ }.freeze
23
+
24
+ class << self
25
+ # @return [Hash] the table as written in cswp.yaml
26
+ def table
27
+ @table ||= YAML.load_file File.expand_path("cswp.yaml", __dir__)
28
+ end
29
+
30
+ #
31
+ # The comparable form of a CSWP reference: no publisher, the
32
+ # separators (".", "(...)") read as spaces, upper case.
33
+ # "NIST CSWP 6(spa)", "NIST.CSWP.6.spa" and "NIST CSWP 6 spa" all
34
+ # give "CSWP 6 SPA".
35
+ #
36
+ # @param ref [String]
37
+ # @return [String]
38
+ def normalize(ref)
39
+ ref.to_s.sub(/\ANIST[.\s]+/i, "").gsub(/[().]/, " ").squeeze(" ").strip.upcase
40
+ end
41
+
42
+ #
43
+ # Find a reference in the table.
44
+ #
45
+ # @param ref [String]
46
+ # @return [Array, nil] `[:legacy, numbered id, dated id]` for a
47
+ # legacy id of an English CSWP, `[:translation, entry]` for a
48
+ # translation (dated or numbered), nil otherwise
49
+ def lookup(ref)
50
+ key = normalize ref
51
+ return unless key.start_with? "CSWP "
52
+
53
+ key = key.delete_prefix "CSWP "
54
+ dated, target = legacy[key]
55
+ entry = translations[(target || key).upcase]
56
+ return [:translation, entry] if entry
57
+
58
+ [:legacy, "NIST CSWP #{target}", "NIST CSWP #{dated}"] if target
59
+ end
60
+
61
+ #
62
+ # Build the record of a translation from its RIS fields.
63
+ #
64
+ # @param entry [Hash] a translation entry from #lookup
65
+ # @return [Relaton::Nist::ItemData]
66
+ def translation_item(entry) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
67
+ number, lang_code = entry[:id].split
68
+ language, script = LANGUAGES.fetch lang_code
69
+ id = "NIST CSWP #{number}(#{lang_code})"
70
+ ItemData.new(
71
+ type: "standard",
72
+ docidentifier: [
73
+ Docidentifier.new(content: id, type: "NIST", primary: true),
74
+ Docidentifier.new(content: entry[:doi].split("/").last, type: "DOI"),
75
+ ],
76
+ title: [Bib::Title.new(content: entry[:title], language: "en", script: "Latn")],
77
+ source: [Bib::Uri.new(type: "doi", content: "https://doi.org/#{entry[:doi]}")],
78
+ date: [Date.new(type: "published", at: entry[:year])],
79
+ contributor: entry[:authors].map { |name| author name } << publisher,
80
+ language: [language],
81
+ script: [script],
82
+ relation: [original_relation("NIST CSWP #{number}")],
83
+ place: [Bib::Place.new(city: "Gaithersburg", region: [Bib::Place::RegionType.new(iso: "MD")])],
84
+ ext: Ext.new(doctype: Doctype.new(content: "standard"), flavor: "nist"),
85
+ )
86
+ end
87
+
88
+ private
89
+
90
+ # dated id (upper case) => [dated id, numbered id]
91
+ def legacy
92
+ @legacy ||= table["legacy"].to_h { |dated, numbered| [dated.upcase, [dated, numbered]] }
93
+ end
94
+
95
+ # translation id (upper case) => entry
96
+ def translations
97
+ @translations ||= table["translations"].to_h do |id, fields|
98
+ [id.upcase, fields.transform_keys(&:to_sym).merge(id: id, authors: fields["authors"] || [])]
99
+ end
100
+ end
101
+
102
+ # @param name [String] RIS author, "Surname, Forename"
103
+ # @return [Bib::Contributor]
104
+ def author(name)
105
+ surname, forename = name.split(/,\s*/, 2)
106
+ fullname = Bib::FullName.new(
107
+ surname: Bib::LocalizedString.new(content: surname, language: "en", script: "Latn"),
108
+ forename: [Bib::FullNameType::Forename.new(content: forename, language: "en", script: "Latn")],
109
+ )
110
+ Bib::Contributor.new(
111
+ person: Bib::Person.new(name: fullname),
112
+ role: [Bib::Contributor::Role.new(type: "author")],
113
+ )
114
+ end
115
+
116
+ # @return [Bib::Contributor]
117
+ def publisher
118
+ org = Bib::Organization.new(
119
+ name: [Bib::TypedLocalizedString.new(content: "National Institute of Standards and Technology")],
120
+ abbreviation: Bib::LocalizedString.new(content: "NIST"),
121
+ )
122
+ Bib::Contributor.new(organization: org, role: [Bib::Contributor::Role.new(type: "publisher")])
123
+ end
124
+
125
+ # @param id [String] the original's id
126
+ # @return [Relaton::Nist::Relation]
127
+ def original_relation(id)
128
+ bib = ItemData.new(
129
+ formattedref: Bib::Formattedref.new(content: id),
130
+ docidentifier: [Docidentifier.new(content: id, type: "NIST", primary: true)],
131
+ )
132
+ Relation.new(type: "translatedFrom", bibitem: bib)
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,155 @@
1
+ # CSWP ids the CSRC pubs-export feed does not list (relaton#406).
2
+ #
3
+ # Source: the RIS files `bib/NIST.CSWP.*.ris` on the `nist-pages` branch of
4
+ # usnistgov/NIST-Tech-Pubs (the NIST Library MODS/MARC releases hold no CSWP).
5
+ #
6
+ # legacy: a dated id from before the series was numbered -> the numbered id,
7
+ # matched by the exact RIS title (TI). A dated translation maps to the
8
+ # numbered translation. `06222021-draft` has no numbered RIS; its title is
9
+ # the CSRC `CSWP 19` initial public draft.
10
+ # Not mapped: `02042022-1`..`-5` have no SN and share the title
11
+ # "Cybersecurity White Paper: EO Response", so no numbered id matches.
12
+ #
13
+ # translations: numbered id + ISO 639-2 language code -> the RIS fields (TI,
14
+ # PY, DO, AU). The original is the numbered id without the language code.
15
+ legacy:
16
+ "01142020": "9"
17
+ "01162020es": "10 spa"
18
+ "01162020id": "10 ind"
19
+ "01162020pt": "10 por"
20
+ "01262018": "5"
21
+ "02042020-1": "11"
22
+ "02042020-2": "11A"
23
+ "02042020-3": "11B"
24
+ "02042020-4": "11C"
25
+ "02042020-5": "11D"
26
+ "02042020-6": "11E"
27
+ "02042020-7": "11F"
28
+ "02122014": "1"
29
+ "02122014pl": "1 pol"
30
+ "02192014": "2"
31
+ "04162018": "6"
32
+ "04162018ar": "6 ara"
33
+ "04162018bg": "6 bul"
34
+ "04162018es": "6 spa"
35
+ "04162018fr": "6 fre"
36
+ "04162018id": "6 ind"
37
+ "04162018pt": "6 por"
38
+ "04212016": "4"
39
+ "04222019": "8"
40
+ "04232020": "13"
41
+ "04282021": "15"
42
+ "06032014": "3"
43
+ "06222021-draft": "19 ipd"
44
+ "09102018": "7"
45
+ "09292021": "21"
46
+ translations:
47
+ "1 pol":
48
+ title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.0 (Polish translation)
49
+ year: "2014"
50
+ doi: 10.6028/NIST.CSWP.1.pol
51
+ authors:
52
+ - Stine, Kevin
53
+ "6 ara":
54
+ title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Arabic translation)
55
+ year: "2018"
56
+ doi: 10.6028/NIST.CSWP.6.ara
57
+ authors:
58
+ - Stine, Kevin
59
+ "6 bul":
60
+ title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Bulgarian translation)
61
+ year: "2018"
62
+ doi: 10.6028/NIST.CSWP.6.bul
63
+ authors:
64
+ - Stine, Kevin
65
+ "6 fre":
66
+ title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (French Translation)
67
+ year: "2018"
68
+ doi: 10.6028/NIST.CSWP.6.fre
69
+ authors:
70
+ - Stine, Kevin
71
+ "6 gre":
72
+ title: Greek Translation of the Framework for Improving Critical Infrastructure Cybersecurity
73
+ year: "2018"
74
+ doi: 10.6028/NIST.CSWP.6.gre
75
+ authors:
76
+ - Stine, Kevin
77
+ "6 ind":
78
+ title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Indonesian Translation)
79
+ year: "2018"
80
+ doi: 10.6028/NIST.CSWP.6.ind
81
+ authors:
82
+ - Stine, Kevin
83
+ "6 jpn":
84
+ title: Japanese Translation of Framework for Improving Critical Infrastructure Cybersecurity Version 1.1 (Cybersecurity Framework)
85
+ year: "2018"
86
+ doi: 10.6028/NIST.CSWP.6.jpn
87
+ authors:
88
+ - Stine, Kevin
89
+ "6 kor":
90
+ title: Korean Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1
91
+ year: "2024"
92
+ doi: 10.6028/NIST.CSWP.6.kor
93
+ authors:
94
+ - Stine, Kevin
95
+ "6 may":
96
+ title: Malay Translation of the Framework for Improving Critical Infrastructure Cybersecurity V1.1
97
+ year: "2018"
98
+ doi: 10.6028/NIST.CSWP.6.may
99
+ authors:
100
+ - Stine, Kevin
101
+ "6 nor":
102
+ title: Norwegian Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1
103
+ year: "2018"
104
+ doi: 10.6028/NIST.CSWP.6.nor
105
+ authors:
106
+ - Stine, Kevin
107
+ "6 por":
108
+ title: Portuguese Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1 (Cybersecurity Framework)
109
+ year: "2018"
110
+ doi: 10.6028/NIST.CSWP.6.por
111
+ authors:
112
+ - Stine, Kevin
113
+ "6 spa":
114
+ title: Framework for Improving Critical Infrastructure Cybersecurity, Version 1.1 (Spanish translation)
115
+ year: "2018"
116
+ doi: 10.6028/NIST.CSWP.6.spa
117
+ authors:
118
+ - Stine, Kevin
119
+ "6 ukr":
120
+ title: Ukranian Translation of the Framework for Improving Critical Infrastructure Cybersecurity Version 1.1 (Cybersecurity Framework)
121
+ year: "2018"
122
+ doi: 10.6028/NIST.CSWP.6.ukr
123
+ authors:
124
+ - Stine, Kevin
125
+ "10 ara":
126
+ title: "NIST Privacy Framework: A Tool for Improving Privacy Through Enterprise Risk Management, Version 1.0 (Arabic translation)"
127
+ year: "2022"
128
+ doi: 10.6028/NIST.CSWP.10.ara
129
+ authors:
130
+ - Lefkovitz, Naomi
131
+ - Boeckl, Katie
132
+ "10 ind":
133
+ title: Indonesian Translation of the NIST Privacy Framework Version 1.0
134
+ year: "2020"
135
+ doi: 10.6028/NIST.CSWP.10.ind
136
+ authors:
137
+ - Lefkovitz, Naomi
138
+ "10 may":
139
+ title: "NIST Privacy Framework: A Tool for Improving Privacy Through Enterprise Risk Management, Version 1.0 (Malay translation)"
140
+ year: "2024"
141
+ doi: 10.6028/NIST.CSWP.10.may
142
+ authors:
143
+ - Lefkovitz, Naomi
144
+ "10 por":
145
+ title: Portuguese Translation of the NIST Privacy Framework Version 1.0
146
+ year: "2021"
147
+ doi: 10.6028/NIST.CSWP.10.por
148
+ authors:
149
+ - Lefkovitz, Naomi
150
+ "10 spa":
151
+ title: Spanish Translation of the NIST Privacy Framework Version 1.0
152
+ year: "2021"
153
+ doi: 10.6028/NIST.CSWP.10.spa
154
+ authors:
155
+ - Lefkovitz, Naomi
@@ -21,6 +21,26 @@ module Relaton
21
21
  Bibliography.get(code, date, opts)
22
22
  end
23
23
 
24
+ # Two kinds of Cswp table reference reach `get` as written and are not
25
+ # cached (relaton#406). A legacy dated id answers the numbered record
26
+ # with the dated id added, and Db would store that record under the
27
+ # numbered id's row, so a later "NIST CSWP 9" would carry the dated id.
28
+ # A translation that pubid reads in another form ("NIST CSWP
29
+ # 04162018ar" as "...04162018a", "NIST CSWP 6 spa" as "...6(spa)").
30
+ #
31
+ # @param ref [String]
32
+ # @return [Pubid::Nist::Identifier, nil]
33
+ def cache_pubid(ref)
34
+ pubid = super
35
+ return if pubid.nil?
36
+
37
+ require_relative "cswp"
38
+ type, = Cswp.lookup(ref)
39
+ return pubid if type.nil? || (type == :translation && pubid.to_s == ref)
40
+
41
+ nil
42
+ end
43
+
24
44
  #
25
45
  # Fetch all the documents from a source
26
46
  #
data/lib/relaton/nist.rb CHANGED
@@ -12,6 +12,7 @@ require_relative "nist/pubs_export"
12
12
  require_relative "nist/hit"
13
13
  require_relative "nist/hit_collection"
14
14
  require_relative "nist/scraper"
15
+ require_relative "nist/cswp"
15
16
  require_relative "nist/bibliography"
16
17
  # require_relative "nist/tech_pubs_parser"
17
18
 
@@ -1,3 +1,3 @@
1
1
  module Relaton
2
- VERSION = "3.0.2".freeze
2
+ VERSION = "3.0.3".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relaton
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-30 00:00:00.000000000 Z
11
+ date: 2026-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -42,30 +42,30 @@ dependencies:
42
42
  name: base64
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.3'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.3'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bibtex-ruby
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '6.2'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '6.2'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: cnccs
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: sqlite3
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '1.7'
103
+ version: '2.10'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '1.7'
110
+ version: '2.10'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: csv
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -224,16 +224,16 @@ dependencies:
224
224
  name: iso639
225
225
  requirement: !ruby/object:Gem::Requirement
226
226
  requirements:
227
- - - ">="
227
+ - - "~>"
228
228
  - !ruby/object:Gem::Version
229
- version: '0'
229
+ version: '1.4'
230
230
  type: :runtime
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
- - - ">="
234
+ - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: '0'
236
+ version: '1.4'
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: isoics
239
239
  requirement: !ruby/object:Gem::Requirement
@@ -282,20 +282,14 @@ dependencies:
282
282
  requirements:
283
283
  - - "~>"
284
284
  - !ruby/object:Gem::Version
285
- version: '0.2'
286
- - - ">="
287
- - !ruby/object:Gem::Version
288
- version: 0.2.5
285
+ version: 0.2.8
289
286
  type: :runtime
290
287
  prerelease: false
291
288
  version_requirements: !ruby/object:Gem::Requirement
292
289
  requirements:
293
290
  - - "~>"
294
291
  - !ruby/object:Gem::Version
295
- version: '0.2'
296
- - - ">="
297
- - !ruby/object:Gem::Version
298
- version: 0.2.5
292
+ version: 0.2.8
299
293
  - !ruby/object:Gem::Dependency
300
294
  name: lutaml-model
301
295
  requirement: !ruby/object:Gem::Requirement
@@ -370,30 +364,30 @@ dependencies:
370
364
  name: moxml
371
365
  requirement: !ruby/object:Gem::Requirement
372
366
  requirements:
373
- - - ">="
367
+ - - "~>"
374
368
  - !ruby/object:Gem::Version
375
- version: '0.5'
369
+ version: 0.5.174
376
370
  type: :runtime
377
371
  prerelease: false
378
372
  version_requirements: !ruby/object:Gem::Requirement
379
373
  requirements:
380
- - - ">="
374
+ - - "~>"
381
375
  - !ruby/object:Gem::Version
382
- version: '0.5'
376
+ version: 0.5.174
383
377
  - !ruby/object:Gem::Dependency
384
378
  name: leptris
385
379
  requirement: !ruby/object:Gem::Requirement
386
380
  requirements:
387
- - - ">="
381
+ - - "~>"
388
382
  - !ruby/object:Gem::Version
389
- version: 1.9.80
383
+ version: 1.9.391
390
384
  type: :runtime
391
385
  prerelease: false
392
386
  version_requirements: !ruby/object:Gem::Requirement
393
387
  requirements:
394
- - - ">="
388
+ - - "~>"
395
389
  - !ruby/object:Gem::Version
396
- version: 1.9.80
390
+ version: 1.9.391
397
391
  - !ruby/object:Gem::Dependency
398
392
  name: parallel
399
393
  requirement: !ruby/object:Gem::Requirement
@@ -410,92 +404,60 @@ dependencies:
410
404
  version: '1.26'
411
405
  - !ruby/object:Gem::Dependency
412
406
  name: parsanol
413
- requirement: !ruby/object:Gem::Requirement
414
- requirements:
415
- - - ">="
416
- - !ruby/object:Gem::Version
417
- version: 1.3.114
418
- - - "<"
419
- - !ruby/object:Gem::Version
420
- version: '1.4'
421
- type: :runtime
422
- prerelease: false
423
- version_requirements: !ruby/object:Gem::Requirement
424
- requirements:
425
- - - ">="
426
- - !ruby/object:Gem::Version
427
- version: 1.3.114
428
- - - "<"
429
- - !ruby/object:Gem::Version
430
- version: '1.4'
431
- - !ruby/object:Gem::Dependency
432
- name: parslet
433
407
  requirement: !ruby/object:Gem::Requirement
434
408
  requirements:
435
409
  - - "~>"
436
410
  - !ruby/object:Gem::Version
437
- version: 2.0.0
411
+ version: 1.3.114
438
412
  type: :runtime
439
413
  prerelease: false
440
414
  version_requirements: !ruby/object:Gem::Requirement
441
415
  requirements:
442
416
  - - "~>"
443
417
  - !ruby/object:Gem::Version
444
- version: 2.0.0
418
+ version: 1.3.114
445
419
  - !ruby/object:Gem::Dependency
446
420
  name: pubid
447
421
  requirement: !ruby/object:Gem::Requirement
448
422
  requirements:
449
- - - ">="
423
+ - - "~>"
450
424
  - !ruby/object:Gem::Version
451
425
  version: 2.0.1
452
- - - "<"
453
- - !ruby/object:Gem::Version
454
- version: '2.1'
455
426
  type: :runtime
456
427
  prerelease: false
457
428
  version_requirements: !ruby/object:Gem::Requirement
458
429
  requirements:
459
- - - ">="
430
+ - - "~>"
460
431
  - !ruby/object:Gem::Version
461
432
  version: 2.0.1
462
- - - "<"
463
- - !ruby/object:Gem::Version
464
- version: '2.1'
465
433
  - !ruby/object:Gem::Dependency
466
434
  name: yeptris
467
435
  requirement: !ruby/object:Gem::Requirement
468
436
  requirements:
469
- - - ">="
437
+ - - "~>"
470
438
  - !ruby/object:Gem::Version
471
439
  version: 0.6.51
472
440
  type: :runtime
473
441
  prerelease: false
474
442
  version_requirements: !ruby/object:Gem::Requirement
475
443
  requirements:
476
- - - ">="
444
+ - - "~>"
477
445
  - !ruby/object:Gem::Version
478
446
  version: 0.6.51
479
447
  - !ruby/object:Gem::Dependency
480
448
  name: relaton-render
481
449
  requirement: !ruby/object:Gem::Requirement
482
450
  requirements:
483
- - - ">="
484
- - !ruby/object:Gem::Version
485
- version: 3.0.0.pre.alpha.1
486
- - - "<"
451
+ - - "~>"
487
452
  - !ruby/object:Gem::Version
488
- version: '4'
453
+ version: '3.0'
489
454
  type: :runtime
490
455
  prerelease: false
491
456
  version_requirements: !ruby/object:Gem::Requirement
492
457
  requirements:
493
- - - ">="
494
- - !ruby/object:Gem::Version
495
- version: 3.0.0.pre.alpha.1
496
- - - "<"
458
+ - - "~>"
497
459
  - !ruby/object:Gem::Version
498
- version: '4'
460
+ version: '3.0'
499
461
  - !ruby/object:Gem::Dependency
500
462
  name: rfcxml
501
463
  requirement: !ruby/object:Gem::Requirement
@@ -1159,6 +1121,8 @@ files:
1159
1121
  - lib/relaton/nist/bibitem.rb
1160
1122
  - lib/relaton/nist/bibliography.rb
1161
1123
  - lib/relaton/nist/comment_period.rb
1124
+ - lib/relaton/nist/cswp.rb
1125
+ - lib/relaton/nist/cswp.yaml
1162
1126
  - lib/relaton/nist/data_fetcher.rb
1163
1127
  - lib/relaton/nist/date.rb
1164
1128
  - lib/relaton/nist/docidentifier.rb