relaton-nist 1.20.4 → 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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +1 -1
  4. data/CLAUDE.md +96 -0
  5. data/README.adoc +71 -73
  6. data/grammars/basicdoc.rng +1559 -671
  7. data/grammars/biblio-standoc.rng +107 -46
  8. data/grammars/biblio.rng +1010 -375
  9. data/grammars/relaton-nist.rng +13 -33
  10. data/lib/relaton/nist/bibdata.rb +7 -0
  11. data/lib/relaton/nist/bibitem.rb +7 -0
  12. data/lib/relaton/nist/bibliography.rb +168 -0
  13. data/lib/relaton/nist/comment_period.rb +15 -0
  14. data/lib/relaton/nist/data_fetcher.rb +74 -0
  15. data/lib/relaton/nist/date.rb +7 -0
  16. data/lib/relaton/nist/doctype.rb +7 -0
  17. data/lib/relaton/nist/ext.rb +20 -0
  18. data/lib/relaton/nist/hit.rb +18 -0
  19. data/lib/relaton/nist/hit_collection.rb +286 -0
  20. data/lib/relaton/nist/item.rb +17 -0
  21. data/lib/relaton/nist/item_data.rb +6 -0
  22. data/lib/relaton/nist/mods_parser.rb +249 -0
  23. data/lib/relaton/nist/processor.rb +66 -0
  24. data/lib/relaton/nist/pubs_export.rb +69 -0
  25. data/lib/relaton/nist/relation.rb +7 -0
  26. data/lib/relaton/nist/scraper.rb +290 -0
  27. data/lib/relaton/nist/tech_pubs_parser.rb +321 -0
  28. data/lib/relaton/nist/util.rb +8 -0
  29. data/lib/relaton/nist/version.rb +5 -0
  30. data/lib/relaton/nist.rb +30 -0
  31. data/relaton_nist.gemspec +9 -7
  32. metadata +61 -32
  33. data/lib/relaton_nist/comment_period.rb +0 -49
  34. data/lib/relaton_nist/data_fetcher.rb +0 -97
  35. data/lib/relaton_nist/document_relation.rb +0 -5
  36. data/lib/relaton_nist/document_status.rb +0 -24
  37. data/lib/relaton_nist/hash_converter.rb +0 -51
  38. data/lib/relaton_nist/hit.rb +0 -42
  39. data/lib/relaton_nist/hit_collection.rb +0 -301
  40. data/lib/relaton_nist/mods_parser.rb +0 -223
  41. data/lib/relaton_nist/nist_bibliographic_item.rb +0 -128
  42. data/lib/relaton_nist/nist_bibliography.rb +0 -189
  43. data/lib/relaton_nist/processor.rb +0 -58
  44. data/lib/relaton_nist/pubs_export.rb +0 -65
  45. data/lib/relaton_nist/scrapper.rb +0 -265
  46. data/lib/relaton_nist/tech_pubs_parser.rb +0 -308
  47. data/lib/relaton_nist/util.rb +0 -6
  48. data/lib/relaton_nist/version.rb +0 -3
  49. data/lib/relaton_nist/xml_parser.rb +0 -51
  50. data/lib/relaton_nist.rb +0 -29
  51. /data/lib/{relaton_nist → relaton/nist}/series.yaml +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d05e892d5c1bb5b67b8165cd69cbc431b8d6f7ea443b3acb98baf66b9edfba0
4
- data.tar.gz: 419aac532181bbe1b52e5eab80b657a8fb2b645282b0f88bdcd1c7cdca337118
3
+ metadata.gz: c03d23fadbcef92134d86d7df992c92c185202d0385d0a788944a281e69dd2c4
4
+ data.tar.gz: 05ea6538b1c9383a3ce1df1636bedfc4b044115d335b09d34b72a8ca60ca858c
5
5
  SHA512:
6
- metadata.gz: 28f8c667337a14db4429205bdb7514f8990609dc9dd3c52d62e25b175e4e6c0a3c9984e049f01f709f22378995c8f5a74e57d9496d36e2c67cf9f1e4c42376dc
7
- data.tar.gz: 92f1e0eaeb4a82a37da3cf951a99af009a397909caef0b654b51463e741476523d8257532e4f72e80d161027695ace88539df7c6c693ad176c300753154a2738
6
+ metadata.gz: 7f20c86344e122030a98651429bbf43559b2d0955ebbd5697f071c20421996f2bad161b0967bc6171888251bcbd77d3b8c615803d17b93de08162734a7a79c71
7
+ data.tar.gz: fd93a125142aa2274f8bb0d00825cde4d21ba701529e112c998c61b3f698a36506d44f449b80072dde7270c9dfb01cb1b5fe066df0be8da3edae499abcfd36ec
data/.gitignore CHANGED
@@ -15,3 +15,4 @@
15
15
  pubs-export.json
16
16
  Gemfile.lock
17
17
  .byebug_history
18
+ .claude/
data/.rubocop.yml CHANGED
@@ -7,6 +7,6 @@ require: rubocop-rails
7
7
  inherit_from:
8
8
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
9
9
  AllCops:
10
- TargetRubyVersion: 3.1
10
+ TargetRubyVersion: 3.2
11
11
  Rails:
12
12
  Enabled: false
data/CLAUDE.md ADDED
@@ -0,0 +1,96 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ relaton-nist is a Ruby gem that retrieves and manages NIST (National Institute of Standards and Technology) bibliographic metadata as part of the Relaton ecosystem. Data sources include the NIST Cybersecurity Resource Center (CSRC) and the NIST Library (MARC21/MODS XML).
8
+
9
+ ## Development Commands
10
+
11
+ ```bash
12
+ # Install dependencies
13
+ bundle install
14
+
15
+ # Run all tests (any of these)
16
+ rake
17
+ rake spec
18
+ bundle exec rspec
19
+
20
+ # Run a single test file
21
+ bundle exec rspec spec/relaton/nist/item_spec.rb
22
+
23
+ # Run a specific test by line number
24
+ bundle exec rspec spec/relaton/nist/item_spec.rb:10
25
+
26
+ # Interactive console with gem loaded
27
+ bin/console
28
+
29
+ # Lint (uses Ribose OSS rubocop config)
30
+ bundle exec rubocop
31
+ ```
32
+
33
+ ## Architecture
34
+
35
+ ### Module Structure
36
+
37
+ All classes live under `Relaton::Nist` (entry point: `require 'relaton/nist'`).
38
+
39
+ ### Lutaml-Model Serialization
40
+
41
+ All data models inherit from `Lutaml::Model::Serializable` and use declarative attribute/mapping DSL for XML and YAML serialization. The base bibliographic types come from `relaton-bib` (aliased as `Bib`).
42
+
43
+ **Core model hierarchy**:
44
+ - `Bib::Item` → `Relaton::Nist::Item` (`model ItemData`)
45
+ - `Relaton::Nist::Bibitem` (includes `Bib::BibitemShared`)
46
+ - `Relaton::Nist::Bibdata` (includes `Bib::BibdataShared`)
47
+ - `Bib::ItemData` → `Relaton::Nist::ItemData`
48
+
49
+ **NIST-specific extensions** (under `lib/relaton/nist/`):
50
+ - `Ext` — extension block holding doctype, comment period (extends `Bib::Ext`)
51
+ - `Date` — adds `abandoned` and `superseded` date types (extends `Bib::Date`)
52
+ - `Relation` — adds `obsoletedBy`, `supersedes`, `supersededBy` relation types (extends `Bib::Relation`)
53
+ - `CommentPeriod` — from/to/extended date ranges (extends `Lutaml::Model::Serializable`)
54
+ - `Doctype` — currently only `standard` (extends `Bib::Doctype`)
55
+
56
+ **Search/retrieval layer** (extends `relaton-core`):
57
+ - `Bibliography` — class methods `search(text, year, opts)` and `get(code, year, opts)`
58
+ - `HitCollection` < `Core::HitCollection` — searches GitHub index + CSRC JSON
59
+ - `Hit` < `Core::Hit` — lazily resolves items via `Scraper`
60
+ - `Processor` < `Core::Processor` — Relaton plugin interface (`get`, `fetch_data`, `from_xml`, `from_yaml`)
61
+
62
+ **Data fetching/parsing**:
63
+ - `DataFetcher` < `Core::DataFetcher` — fetches NIST Tech Pubs MODS XML from GitHub releases
64
+ - `ModsParser` — maps MODS XML (via `loc_mods`) to `ItemData`
65
+ - `Scraper` — fetches items from GitHub YAML or CSRC JSON
66
+ - `PubsExport` — singleton; caches CSRC pubs-export zip with thread-safe daily updates
67
+
68
+ ### Serialization Round-Trip Pattern
69
+
70
+ Models support `from_xml`/`to_xml` and `from_yaml`/`to_yaml`. Tests verify round-trip fidelity by parsing a fixture, re-serializing, and comparing output to input.
71
+
72
+ ### XML Schema Validation
73
+
74
+ RNG (Relax NG) schemas in `grammars/` validate XML output. Tests use the `jing` gem:
75
+ ```ruby
76
+ schema = Jing.new("grammars/relaton-nist-compile.rng")
77
+ errors = schema.validate(file)
78
+ ```
79
+ `relaton-nist-compile.rng` is the top-level schema that includes `relaton-nist.rng` and `basicdoc.rng`.
80
+
81
+ ### HTTP Recording
82
+
83
+ Tests use VCR with WebMock. Cassettes are stored in `spec/vcr_cassettes/` and re-record every 7 days.
84
+
85
+ ## Key Dependencies
86
+
87
+ - `relaton-bib` — base bibliographic models and shared mixins
88
+ - `relaton-core` — base classes for Processor, HitCollection, Hit, DataFetcher
89
+ - `loc_mods` — MODS (Metadata Object Description Schema) XML parsing
90
+ - `pubid` — NIST publication ID parsing
91
+ - `relaton-index` — index/search utilities
92
+ - `mechanize` — HTTP fetching for data sources
93
+
94
+ ## Code Style
95
+
96
+ RuboCop config inherits from the Ribose OSS style guide. Target Ruby version is 3.1.
data/README.adoc CHANGED
@@ -98,7 +98,7 @@ Or install it yourself as:
98
98
 
99
99
  [source,sh]
100
100
  ----
101
- $ gem install relaton_nist
101
+ $ gem install relaton-nist
102
102
  ----
103
103
 
104
104
  == Usage
@@ -120,19 +120,16 @@ https://www.nist.gov/news-events/news/2021/08/nist-technical-series-publications
120
120
  ====
121
121
  [source,ruby]
122
122
  ----
123
- > require 'relaton_nist'
124
- =>
125
- true
126
-
127
- > hit_collection = RelatonNist::NistBibliography.search("NISTIR 8200")
128
- [relaton-nist] (NIST IR 8200) Fetching from csrc.nist.gov ...
129
- [relaton-nist] (NIST IR 8200) Fetching from Relaton repository ...
130
- =>
131
- <RelatonNist::HitCollection:0x00000000004b28 @ref=NIST IR 8200 @fetched=false>
132
-
133
- > item = hit_collection[0].fetch
134
- =>
135
- #<RelatonNist::NistBibliographicItem:0x007fc049aa6778
123
+ require 'relaton/nist'
124
+ => true
125
+
126
+ hit_collection = Relaton::Nist::Bibliography.search("NISTIR 8200")
127
+ [relaton-nist] INFO: (NIST IR 8200) Fetching from csrc.nist.gov ...
128
+ [relaton-nist] INFO: (NIST IR 8200) Fetching from Relaton repository ...
129
+ => <Relaton::Nist::HitCollection:0x00000000002c10 @ref=NIST IR 8200 @fetched=false>
130
+
131
+ item = hit_collection[0].item
132
+ => #<Relaton::Nist::ItemData:0x000000012c1d8008
136
133
  # ...
137
134
  ----
138
135
  ====
@@ -146,12 +143,11 @@ revisions or editions.
146
143
  ====
147
144
  [source,ruby]
148
145
  ----
149
- > RelatonNist::NistBibliography.get("NIST IR 8200", "2018")
150
- [relaton-nist] (NIST SP 8200:2018) Fetching from csrc.nist.gov ...
151
- [relaton-nist] (NIST IR 8200:2018) Fetching from Relaton repository...
152
- [relaton-nist] (NIST IR 8200:2018) Found: `NIST IR 8200`
153
- =>
154
- #<RelatonNist::NistBibliographicItem:0x00007fab74a572c0
146
+ Relaton::Nist::Bibliography.get("NIST IR 8200", "2018")
147
+ [relaton-nist] INFO: (NIST IR 8200) Fetching from csrc.nist.gov ...
148
+ [relaton-nist] INFO: (NIST IR 8200) Fetching from Relaton repository ...
149
+ [relaton-nist] INFO: (NIST IR 8200) Found: `NIST IR 8200`
150
+ => #<Relaton::Nist::ItemData:0x000000012c5e7590
155
151
  # ...
156
152
  ----
157
153
  ====
@@ -170,14 +166,14 @@ A NIST PubID can contain these optional parameters `{ptN}{vN}{verN}{rN}{/Add}`:
170
166
  ====
171
167
  [source,ruby]
172
168
  ----
173
- item = RelatonNist::NistBibliography.get 'NIST SP 800-67r1'
174
- [relaton-nist] (NIST SP 800-67r1) Fetching from csrc.nist.gov ...
175
- [relaton-nist] (NIST SP 800-67r1) Found: `NIST SP 800-67 Rev. 1`
176
- => #<RelatonNist::NistBibliographicItem:0x00000001105acd08
169
+ item = Relaton::Nist::Bibliography.get 'NIST SP 800-67r1'
170
+ [relaton-nist] INFO: (NIST SP 800-67r1) Fetching from csrc.nist.gov ...
171
+ [relaton-nist] INFO: (NIST SP 800-67r1) Found: `NIST SP 800-67r1 fpd`
172
+ => #<Relaton::Nist::ItemData:0x000000012cf294c8
177
173
  ...
178
174
 
179
- item.docidentifier.first.id
180
- => "NIST SP 800-67 Rev. 1"
175
+ item.docidentifier.first.content
176
+ => "NIST SP 800-67r1 fpd"
181
177
  ----
182
178
  ====
183
179
 
@@ -186,14 +182,14 @@ item.docidentifier.first.id
186
182
  ====
187
183
  [source,ruby]
188
184
  ----
189
- item = RelatonNist::NistBibliography.get 'NIST SP 800-38A/Add'
190
- [relaton-nist] (NIST SP 800-38A/Add) Fetching from csrc.nist.gov ...
191
- [relaton-nist] (NIST SP 800-38A/Add) Found: `NIST SP 800-38A-Add`
192
- => #<RelatonNist::NistBibliographicItem:0x00000001105abf48
185
+ item = Relaton::Nist::Bibliography.get 'NIST SP 800-38A/Add'
186
+ [relaton-nist] INFO: (NIST SP 800-38A Add) Fetching from csrc.nist.gov ...
187
+ [relaton-nist] INFO: (NIST SP 800-38A Add) Found: `NIST SP 800-38A fpd Add`
188
+ => #<Relaton::Nist::ItemData:0x0000000124864190
193
189
  ...
194
190
 
195
- item.docidentifier.first.id
196
- => "NIST SP 800-38A-Add"
191
+ item.docidentifier.first.content
192
+ => "NIST SP 800-38A fpd Add"
197
193
  ----
198
194
  ====
199
195
 
@@ -225,11 +221,10 @@ In case the state is "draft" it should be:
225
221
  ====
226
222
  [source,ruby]
227
223
  ----
228
- > RelatonNist::NistBibliography.get("NIST SP 800-205 (February 2019) (IPD)")
229
- [relaton-nist] (NIST SP 800-205) Fetching from csrc.nist.gov ...
230
- [relaton-nist] (NIST SP 800-205) Found: `NIST SP 800-205 (Draft)`
231
- =>
232
- #<RelatonNist::NistBibliographicItem:0x00000001105afdc8
224
+ Relaton::Nist::Bibliography.get("NIST SP 800-205 (February 2019) (IPD)")
225
+ [relaton-nist] INFO: (NIST SP 800-205) Fetching from csrc.nist.gov ...
226
+ [relaton-nist] INFO: (NIST SP 800-205) Found: `NIST SP 800-205 ipd`
227
+ => #<Relaton::Nist::ItemData:0x000000012494fb40
233
228
  # ...
234
229
  ----
235
230
  ====
@@ -247,11 +242,10 @@ The format for FIPS publications is:
247
242
  ====
248
243
  [source,ruby]
249
244
  ----
250
- > RelatonNist::NistBibliography.get("NIST FIPS 140-3")
245
+ Relaton::Nist::Bibliography.get("NIST FIPS 140-3")
251
246
  [relaton-nist] INFO: (NIST FIPS 140-3) Fetching from csrc.nist.gov ...
252
- [relaton-nist] INFO: (NIST FIPS 140-3) Found: `NIST FIPS 140-3`
253
- =>
254
- # #<RelatonNist::NistBibliographicItem:0x000000013b68fb50
247
+ [relaton-nist] INFO: (NIST FIPS 140-3) Found: `NIST FIPS 140-3 2pd`
248
+ => #<Relaton::Nist::ItemData:0x0000000124d8dfe0
255
249
  # ...
256
250
  ----
257
251
  ====
@@ -266,12 +260,16 @@ The format for FIPS publications is:
266
260
  ====
267
261
  [source,ruby]
268
262
  ----
269
- > item.to_xml
270
- #=> "<bibitem id="NISTIR8200" type="standard" schema-version="v1.2.9">
271
- # <fetched>2023-10-16</fetched>
272
- # <title format="text/plain" language="en" script="Latn">Interagency report on the status of international cybersecurity standardization for the internet of things (IoT)</title>
273
- # ...
274
- # <bibitem>"
263
+ item.to_xml
264
+ => "<bibitem id="NISTSP80038AfpdAdd" type="standard" schema-version="v1.4.1">
265
+ <fetched>2026-02-26</fetched>
266
+ <title language="en" script="Latn">Recommendation for Block Cipher Modes of Operation - Three Variants of Ciphertext Stealing for CBC Mode</title>
267
+ <uri type="src">https://csrc.nist.gov/pubs/sp/800/38/a/sup/final</uri>
268
+ <uri type="doi">https://doi.org/10.6028/NIST.SP.800-38A-Add</uri>
269
+ <docidentifier type="NIST" primary="true">NIST SP 800-38A fpd Add</docidentifier>
270
+ <docidentifier type="DOI">NIST.SP.800-38A-Add</docidentifier>
271
+ ...
272
+ </bibitem>"
275
273
  ----
276
274
  ====
277
275
 
@@ -283,16 +281,20 @@ the `bibdata` element and adds a flavor-specific `ext` element.
283
281
  ====
284
282
  [source,ruby]
285
283
  ----
286
- > item.to_xml bibdata: true
287
- =>
288
- "<bibdata type="standard" schema-version="v1.2.9">
289
- # <fetched>2023-10-16</fetched>
290
- # <title format="text/plain" language="en" script="Latn">Interagency report on the status of international cybersecurity standardization for the internet of things (IoT)</title>
291
- # ...
292
- # <ext schema-version="v1.0.0">
293
- # <doctype>standard</doctype>
294
- # </ext>
295
- # </bibdata>"
284
+ item.to_xml bibdata: true
285
+ => "<bibdata type="standard" schema-version="v1.4.1">
286
+ <fetched>2026-02-26</fetched>
287
+ <title language="en" script="Latn">Recommendation for Block Cipher Modes of Operation - Three Variants of Ciphertext Stealing for CBC Mode</title>
288
+ <uri type="src">https://csrc.nist.gov/pubs/sp/800/38/a/sup/final</uri>
289
+ <uri type="doi">https://doi.org/10.6028/NIST.SP.800-38A-Add</uri>
290
+ <docidentifier type="NIST" primary="true">NIST SP 800-38A fpd Add</docidentifier>
291
+ <docidentifier type="DOI">NIST.SP.800-38A-Add</docidentifier>
292
+ ...
293
+ <ext schema-version="v1.0.0">
294
+ <doctype>standard</doctype>
295
+ <flavor>nist</flavor>
296
+ </ext>
297
+ </bibdata>
296
298
  ----
297
299
  ====
298
300
 
@@ -318,18 +320,16 @@ They can be accessed through the following methods:
318
320
  NIST publications may have `src` and `doi` links, obtained using the `#link`
319
321
  method.
320
322
 
321
- .Accessing links of a NIST bibliographic item
323
+ .Accessing source links of a NIST bibliographic item
322
324
  [example]
323
325
  ====
324
326
  [source,ruby]
325
327
  ----
326
- item.link
327
- => [#<RelatonBib::TypedUri:0x0000000111087a98
328
- @content=#<Addressable::URI:0x8c0 URI:https://csrc.nist.gov/pubs/sp/800/38/a/sup/final>,
329
- @language=nil,
330
- @script=nil,
331
- @type="src">,
332
- #<RelatonBib::TypedUri:0x00000001110879a8 @content=#<Addressable::URI:0x8d4 URI:https://doi.org/10.6028/NIST.SP.800-38A-Add>, @language=nil, @script=nil, @type="doi">]
328
+ item.source[0].type
329
+ => "src"
330
+
331
+ item.source[0].content
332
+ => "https://csrc.nist.gov/pubs/sp/800/38/a/sup/final"
333
333
  ----
334
334
  ====
335
335
 
@@ -347,12 +347,8 @@ Offline Relaton YAML files can be loaded directly as bibliographic items.
347
347
  ====
348
348
  [source,ruby]
349
349
  ----
350
- hash = YAML.load_file 'spec/examples/nist_bib_item.yml'
351
- => {"id"=>"NISTIR 8011 Vol. 3",
352
- ...
353
-
354
- RelatonNist::NistBibliographicItem.from_hash hash
355
- => #<RelatonNist::NistBibliographicItem:0x007f8b708505b8
350
+ Relaton::Nist::Item.from_yaml File.read("spec/fixtures/item.yaml")
351
+ => #<Relaton::Nist::ItemData:0x0000000124d4ab50
356
352
  ...
357
353
  ----
358
354
  ====
@@ -371,7 +367,9 @@ The following method fetches all the documents from the NIST-Tech-Pubs dataset,
371
367
  ====
372
368
  [source,ruby]
373
369
  ----
374
- > RelatonNist::DataFetcher.fetch
370
+ require "relaton/nist/data_fetcher"
371
+
372
+ Relaton::Nist::DataFetcher.fetch
375
373
  Started at: 2021-09-01 18:01:01 +0200
376
374
  Stopped at: 2021-09-01 18:01:43 +0200
377
375
  Done in: 42 sec.
@@ -384,7 +382,7 @@ Done in: 42 sec.
384
382
  ====
385
383
  [source,ruby]
386
384
  ----
387
- > RelatonNist::DataFetcher.fetch(output: "data", format: "yaml")
385
+ > Relaton::Nist::DataFetcher.fetch(output: "data", format: "yaml")
388
386
  ----
389
387
  ====
390
388