relaton 3.0.0.pre.alpha.17 → 3.0.0.pre.alpha.19
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/data/equivalence/adoption-chains.yaml +59 -0
- data/lib/relaton/3gpp/CLAUDE.md +187 -0
- data/lib/relaton/3gpp/README.adoc +149 -0
- data/lib/relaton/adobe/CLAUDE.md +104 -0
- data/lib/relaton/bib/CLAUDE.md +130 -0
- data/lib/relaton/bib/README.adoc +330 -0
- data/lib/relaton/bib/model/item_shared.rb +5 -1
- data/lib/relaton/bib/model/series.rb +1 -1
- data/lib/relaton/bipm/CLAUDE.md +173 -0
- data/lib/relaton/bipm/README.adoc +352 -0
- data/lib/relaton/bipm/data_fetcher.rb +3 -2
- data/lib/relaton/bsi/CLAUDE.md +35 -0
- data/lib/relaton/bsi/README.adoc +112 -0
- data/lib/relaton/calconnect/CLAUDE.md +177 -0
- data/lib/relaton/calconnect/README.adoc +140 -0
- data/lib/relaton/ccsds/CLAUDE.md +74 -0
- data/lib/relaton/ccsds/README.adoc +154 -0
- data/lib/relaton/cen/CLAUDE.md +219 -0
- data/lib/relaton/cen/README.adoc +155 -0
- data/lib/relaton/cie/CLAUDE.md +90 -0
- data/lib/relaton/cie/README.adoc +121 -0
- data/lib/relaton/core/CLAUDE.md +140 -0
- data/lib/relaton/core/README.adoc +64 -0
- data/lib/relaton/core/data_fetcher.rb +142 -3
- data/lib/relaton/db/CLAUDE.md +373 -0
- data/lib/relaton/db.rb +21 -1
- data/lib/relaton/doi/CLAUDE.md +105 -0
- data/lib/relaton/doi/README.adoc +89 -0
- data/lib/relaton/easc/CLAUDE.md +85 -0
- data/lib/relaton/ecma/CLAUDE.md +217 -0
- data/lib/relaton/ecma/README.adoc +178 -0
- data/lib/relaton/equivalence.rb +117 -0
- data/lib/relaton/etsi/CLAUDE.md +219 -0
- data/lib/relaton/etsi/README.adoc +137 -0
- data/lib/relaton/evs/CLAUDE.md +120 -0
- data/lib/relaton/evs/README.adoc +27 -0
- data/lib/relaton/gb/CLAUDE.md +47 -0
- data/lib/relaton/gb/README.adoc +169 -0
- data/lib/relaton/gost/CLAUDE.md +100 -0
- data/lib/relaton/iala/CLAUDE.md +97 -0
- data/lib/relaton/iana/CLAUDE.md +105 -0
- data/lib/relaton/iana/README.adoc +107 -0
- data/lib/relaton/iec/CLAUDE.md +13 -0
- data/lib/relaton/iec/README.adoc +280 -0
- data/lib/relaton/iec/data_fetcher.rb +24 -8
- data/lib/relaton/iec/project_fetcher.rb +48 -0
- data/lib/relaton/iec/project_parser.rb +111 -0
- data/lib/relaton/iec.rb +5 -0
- data/lib/relaton/ieee/CLAUDE.md +176 -0
- data/lib/relaton/ieee/README.adoc +131 -0
- data/lib/relaton/ietf/CLAUDE.md +357 -0
- data/lib/relaton/ietf/README.adoc +167 -0
- data/lib/relaton/iho/CLAUDE.md +70 -0
- data/lib/relaton/iho/README.adoc +119 -0
- data/lib/relaton/index/CLAUDE.md +207 -0
- data/lib/relaton/index/README.adoc +221 -0
- data/lib/relaton/index/dataset_lock.rb +122 -0
- data/lib/relaton/index/family.rb +63 -0
- data/lib/relaton/index/type.rb +32 -0
- data/lib/relaton/index.rb +3 -0
- data/lib/relaton/isbn/CLAUDE.md +47 -0
- data/lib/relaton/isbn/README.adoc +91 -0
- data/lib/relaton/iso/CLAUDE.md +38 -0
- data/lib/relaton/iso/README.adoc +370 -0
- data/lib/relaton/iso/data_fetcher.rb +16 -1
- data/lib/relaton/itu/CLAUDE.md +685 -0
- data/lib/relaton/itu/README.adoc +188 -0
- data/lib/relaton/jcgm/CLAUDE.md +107 -0
- data/lib/relaton/jis/CLAUDE.md +64 -0
- data/lib/relaton/jis/README.adoc +167 -0
- data/lib/relaton/logger/CLAUDE.md +37 -0
- data/lib/relaton/logger/README.adoc +120 -0
- data/lib/relaton/nist/CLAUDE.md +180 -0
- data/lib/relaton/nist/README.adoc +389 -0
- data/lib/relaton/nist/bibliography.rb +17 -1
- data/lib/relaton/nist/data_fetcher.rb +60 -4
- data/lib/relaton/nist/hit_collection.rb +121 -59
- data/lib/relaton/nist/scraper.rb +21 -7
- data/lib/relaton/oasis/CLAUDE.md +251 -0
- data/lib/relaton/oasis/README.adoc +132 -0
- data/lib/relaton/ogc/CLAUDE.md +242 -0
- data/lib/relaton/ogc/README.adoc +134 -0
- data/lib/relaton/ogc/data_fetcher.rb +9 -3
- data/lib/relaton/oiml/CLAUDE.md +47 -0
- data/lib/relaton/oiml/README.adoc +70 -0
- data/lib/relaton/omg/CLAUDE.md +137 -0
- data/lib/relaton/omg/README.adoc +101 -0
- data/lib/relaton/plateau/CLAUDE.md +126 -0
- data/lib/relaton/plateau/README.adoc +139 -0
- data/lib/relaton/sdo/CLAUDE.md +81 -0
- data/lib/relaton/un/CLAUDE.md +91 -0
- data/lib/relaton/un/README.adoc +125 -0
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton/w3c/CLAUDE.md +369 -0
- data/lib/relaton/w3c/README.adoc +140 -0
- data/lib/relaton/w3c/data_fetcher.rb +1 -1
- data/lib/relaton/xsf/CLAUDE.md +189 -0
- data/lib/relaton/xsf/README.adoc +127 -0
- data/lib/relaton.rb +1 -0
- metadata +85 -4
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Development
|
|
4
|
+
|
|
5
|
+
- `bundle install` — install dependencies
|
|
6
|
+
- `bundle exec rake spec:calconnect` — run this flavor's suite
|
|
7
|
+
- `bundle exec rubocop` — lint
|
|
8
|
+
|
|
9
|
+
The root `Gemfile` git-pins pubid. Pick it up with **`bundle update pubid`**,
|
|
10
|
+
never `bundle install` — an already-locked git source does not refloat.
|
|
11
|
+
|
|
12
|
+
## Index
|
|
13
|
+
|
|
14
|
+
### The gem builds and reads `index-v2`; the data repo owns `index-v1`
|
|
15
|
+
|
|
16
|
+
`INDEXFILE` is the pubid-backed `index-v2`: rows are
|
|
17
|
+
`Pubid::Calconnect::Identifier` hashes
|
|
18
|
+
(`_type: pubid:calconnect:standard`, with `series`/`number`/`year`, plus
|
|
19
|
+
`month`/`day` for the one fully dated row). The producer
|
|
20
|
+
(`DataFetcher#index`) and the consumer (`HitCollection#index`) pass
|
|
21
|
+
`pubid_class: ::Pubid::Calconnect::Identifier`. Omitting it on the
|
|
22
|
+
**producer** writes v1-shaped rows under a v2 name, silently (`FileIO#save`
|
|
23
|
+
calls `to_hash` only for instances of `pubid_class`); omitting it on the
|
|
24
|
+
**consumer** leaves the rows raw hashes with `FileIO#sorted` false, so every
|
|
25
|
+
lookup scans all 191 rows. `Processor#remove_index_file` passes `url: true` and
|
|
26
|
+
`file:` only: the delete never reads the index (see
|
|
27
|
+
`lib/relaton/index/CLAUDE.md`).
|
|
28
|
+
|
|
29
|
+
**`index-v1` is not this gem's concern at all.** It exists only for released
|
|
30
|
+
relaton **v2** clients, and `relaton-data-calconnect` owns it end to end:
|
|
31
|
+
`build_index_v1.rb` rebuilds it there from each crawled document's own primary
|
|
32
|
+
`docidentifier` string out of `data/` — unlike relaton-data-{ecma,w3c,iana,bipm},
|
|
33
|
+
it does **not** derive it from the v2 rows. CalConnect can do that and they
|
|
34
|
+
cannot, because a v1 row id is simply the document's printed id, with no edition
|
|
35
|
+
or volume to split back out. The upshot for this side is that their v1 never
|
|
36
|
+
depends on pubid, so nothing here can break it and there is no v1 fixture and no
|
|
37
|
+
v1 assertion in this suite.
|
|
38
|
+
|
|
39
|
+
### Lookup
|
|
40
|
+
|
|
41
|
+
`HitCollection#search_index` follows the ETSI/W3C/ECMA idiom:
|
|
42
|
+
|
|
43
|
+
- **Pass the pubid, not the string.** `Type#search_candidates` narrows only when
|
|
44
|
+
the argument is not a `String`, and a block alone never narrows — so the plain
|
|
45
|
+
string this flavor used to pass disabled the binary search however the index
|
|
46
|
+
was built. `pubid_class:` alone fixes nothing; both had to change together.
|
|
47
|
+
- **Select with the subset match.** `search_index` calls
|
|
48
|
+
`index.search(pubid)` with no block, so `Index::Type#search` selects with
|
|
49
|
+
pubid's `pubid === row`. A date that the reference omits matches any value.
|
|
50
|
+
pubid declares `series` strict for CalConnect (pubid#408): the series keeps
|
|
51
|
+
`CC/CD 51016` and `CC/WD 51016` apart, and its *absence* is a value too, so
|
|
52
|
+
`CC 36010` does not match `CC/WD 36010`. Measured over the 191-row fixture:
|
|
53
|
+
`===` and the old `matches?(…, ignore: %i[year])` agree on all 406 pairs.
|
|
54
|
+
- **An unparseable reference finds nothing**, with a warning, and is a miss
|
|
55
|
+
rather than an error — it must never surface as a `Relaton::RequestError`.
|
|
56
|
+
|
|
57
|
+
**This ended a silently ambiguous substring scan.** The old string search
|
|
58
|
+
matched any row *containing* the reference: `CC/DIR 1000` answered with all five
|
|
59
|
+
`CC/DIR 1000x` documents and `CC/A 1` with every `CC/A 1xxx`. A number now
|
|
60
|
+
matches exactly, and a leading zero is significant (`CC/A 0001` is not
|
|
61
|
+
`CC/A 1`).
|
|
62
|
+
|
|
63
|
+
**Ordering is newest-first, and it has to be explicit.** The index is sorted by
|
|
64
|
+
number, so rows sharing a number arrive in no meaningful order; without
|
|
65
|
+
`#recency_key` a bare `CC/S 0601` would answer with an arbitrary one of the 2005
|
|
66
|
+
and 2006 documents. Date segments are compared as integers and negated for
|
|
67
|
+
descending order, with `row[:file]` breaking ties because the index sort is not
|
|
68
|
+
stable.
|
|
69
|
+
|
|
70
|
+
`Bibliography.get` no longer regex-splits the reference before searching: pubid
|
|
71
|
+
parses `CC/DIR 10005:2019` whole, so a dated reference narrows in the index
|
|
72
|
+
itself. The `year` **argument** still needs filtering afterwards, because an
|
|
73
|
+
undated reference reaches every year of the document — that is why
|
|
74
|
+
`bib_results_filter` stays, and why it now reads the year off `row[:id].date`
|
|
75
|
+
instead of scraping a rendered string (the old `/:(\d{4})$/` would raise
|
|
76
|
+
`TypeError` against a pubid).
|
|
77
|
+
|
|
78
|
+
### Why this flavor needs no render flags
|
|
79
|
+
|
|
80
|
+
CalConnect is the simple end of the pubid migrations, and the reason is worth
|
|
81
|
+
stating so nobody adds machinery it does not need. `Pubid::Calconnect` renders
|
|
82
|
+
the publisher by default and models no edition and no volume, so the index key
|
|
83
|
+
and the document's own printed id are **the same string**: `CC/DIR 10005:2019`
|
|
84
|
+
in the row, `CC/DIR 10005:2019` in the record. There is nothing to opt in to
|
|
85
|
+
(contrast `Relaton::ThreeGpp::Docidentifier`, which must pass
|
|
86
|
+
`with_publisher: true`) and nothing to opt out of (contrast
|
|
87
|
+
`Relaton::Ecma::Docidentifier`, which must suppress the edition and the volume).
|
|
88
|
+
`DataFetcher#index_id` therefore returns the docidentifier's pubid untouched —
|
|
89
|
+
no `dup`, no mutation.
|
|
90
|
+
|
|
91
|
+
The number is a plain `String`, not a `Components::Code`, and deliberately so:
|
|
92
|
+
it preserves leading zeros (`0001`, `0514`) and keeps a sub-number in one token
|
|
93
|
+
(`0812-1`, `0707.1`). CalConnect models **no part** — those are numbers, not a
|
|
94
|
+
number plus a part — which is why `Docidentifier#remove_part!` and
|
|
95
|
+
`#to_all_parts!` are no-ops. Do not "fix" them by splitting the number.
|
|
96
|
+
|
|
97
|
+
`Docidentifier` follows the IHO/W3C shape: `content=` calls `super` first, so
|
|
98
|
+
`content` stays the source string verbatim. That is load-bearing beyond
|
|
99
|
+
convention — `ItemData#create_id` derives the record's `id` from
|
|
100
|
+
`content.gsub(/\W+/, "")`, so a re-render would move every published id.
|
|
101
|
+
`#remove_date!` is the one real mutator (`CC/DIR 10005:2019` ->
|
|
102
|
+
`CC/DIR 10005`); it writes back through `store_content`, never `content=`,
|
|
103
|
+
because a re-parse would rebuild `@pubid` and discard the mutation.
|
|
104
|
+
|
|
105
|
+
### An unparseable docid is recorded, not just warned
|
|
106
|
+
|
|
107
|
+
`Docidentifier#parse` logs at `Util.error` and leaves `#pubid` nil — it must not
|
|
108
|
+
raise, or an already-published record would stop deserializing.
|
|
109
|
+
`DataFetcher#add_to_index` then records
|
|
110
|
+
`@errors[docid] = "Unparseable primary id … was not indexed (…)"`, which the
|
|
111
|
+
inherited `Core::DataFetcher#report_errors` logs and `Logger::Channels::GhIssue`
|
|
112
|
+
turns into a GitHub issue at the end of the crawl. (`report_errors` treats a
|
|
113
|
+
**String** value as the message; the boolean form the parsers use means "this
|
|
114
|
+
field failed for every record" and is the wrong shape here.)
|
|
115
|
+
|
|
116
|
+
The row is skipped rather than indexed unparsed, because `Relaton::Index`
|
|
117
|
+
rejects the **whole** index if one row fails to deserialize and its sort calls
|
|
118
|
+
`.root.number` on every id. `#write_doc` writes the file **before** indexing, so
|
|
119
|
+
the document is unindexed, never lost.
|
|
120
|
+
|
|
121
|
+
### Measured against the live published corpus
|
|
122
|
+
|
|
123
|
+
All 191 rows of the published index (2026-09-08): every id parses; `to_s`
|
|
124
|
+
reproduces the published string **exactly**; `from_hash(to_hash)` round-trips
|
|
125
|
+
all 191; they render **191 distinct keys**, so nothing collapses under
|
|
126
|
+
`add_or_update`; `root.number` is empty for none. Driving all 191 documents of
|
|
127
|
+
`relaton-data-calconnect`'s `data/` through `write_doc` produces 191 rows with 0
|
|
128
|
+
errors and reads back as 191 identifiers with `FileIO#sorted` true.
|
|
129
|
+
`spec/calconnect/relaton/calconnect/pubid_contract_spec.rb` pins these
|
|
130
|
+
properties offline against the committed `index-v2` fixture.
|
|
131
|
+
|
|
132
|
+
No pubid change was needed for this migration. `Pubid::Calconnect` was written
|
|
133
|
+
for it.
|
|
134
|
+
|
|
135
|
+
## Testing
|
|
136
|
+
|
|
137
|
+
- **Framework:** RSpec with VCR cassettes and WebMock.
|
|
138
|
+
- **Index fixture:** `spec/calconnect/fixtures/index-v2.zip` — the whole
|
|
139
|
+
published index (191 rows), copied verbatim, so the stored shapes are exactly
|
|
140
|
+
what the runtime deserializes. Refresh it with
|
|
141
|
+
`bundle exec rake spec:update_index_calconnect`
|
|
142
|
+
(`tasks/index_fixture_calconnect.rb`); `#build` refuses a source that is not a
|
|
143
|
+
pubid index-v2 rather than writing a fixture `Relaton::Index` would reject
|
|
144
|
+
wholesale.
|
|
145
|
+
- It is seeded into the `Relaton::Index` pool by
|
|
146
|
+
`spec/calconnect/support/webmock.rb`, built **with `pubid_class:`** — without
|
|
147
|
+
which the rows stay raw hashes, `FileIO#sorted` stays false, and
|
|
148
|
+
`Type#search` silently stops narrowing, so the suite would pass while
|
|
149
|
+
exercising something the runtime never does. It is written to a temp file and
|
|
150
|
+
read through `FileIO`, not stuffed into `@index` directly, because the
|
|
151
|
+
deserialize and the sort both happen on that read path.
|
|
152
|
+
- There is **no `index-v1` fixture**, deliberately: `index-v1` is a released
|
|
153
|
+
relaton v2 artifact that this gem neither produces nor reads (see the index
|
|
154
|
+
section). Don't reintroduce one.
|
|
155
|
+
- **The `before(:each)` re-seed is not belt-and-braces.** `Index::Pool#type`
|
|
156
|
+
replaces the pooled entry whenever `actual?` says no, and `DataFetcher#index`
|
|
157
|
+
asks for the same `:CC` slot with a *different* file (`index-v2.yaml`). With a
|
|
158
|
+
`before(:suite)` seed alone the fetcher evicts the fixture, and every later
|
|
159
|
+
example goes to the network for real — which is exactly how six examples used
|
|
160
|
+
to die with `Errno::EPERM` on `~/.relaton/cc/index-v1.yaml`. The pool key is
|
|
161
|
+
`type.upcase.to_sym`, so the producer and the consumer share one slot.
|
|
162
|
+
(The ECMA/OGC pattern.)
|
|
163
|
+
- **VCR cassettes:** `spec/calconnect/vcr_cassettes/` — index downloads are
|
|
164
|
+
ignored by VCR, matched on the constant so a version bump cannot leave the
|
|
165
|
+
filter naming the old file and let a real download through.
|
|
166
|
+
- `pubid_contract_spec.rb` checks the whole fixture corpus for the properties
|
|
167
|
+
the index key depends on: every row deserializes, one distinct rendered key
|
|
168
|
+
per row, no empty bsearch key, a `to_hash`/`from_hash` round trip, and every
|
|
169
|
+
rendered key parsing back to the same identifier. Re-run it whenever the
|
|
170
|
+
fixture is refreshed.
|
|
171
|
+
- `hit_collection_spec.rb` covers the runtime search path — narrowing,
|
|
172
|
+
ordering, and the misses. It was an empty `describe` block before this
|
|
173
|
+
migration, so the lookup had no direct coverage at all.
|
|
174
|
+
- The `bibdata`/`bibitem` round-trip examples and two `gets` examples validate
|
|
175
|
+
XML with Jing, which needs a **JVM on PATH**. Without one they fail with
|
|
176
|
+
`Jing::ExecutionError`, which is environmental, not a code defect — check
|
|
177
|
+
`java -version` before chasing it.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
= Relaton::Calconnect
|
|
2
|
+
|
|
3
|
+
Relaton::Calconnect is a Ruby gem that implements the https://github.com/metanorma/metanorma-model-iso#iso-bibliographic-item[IsoBibliographicItem model].
|
|
4
|
+
|
|
5
|
+
You can use it to retrieve metadata of Calconnect Standards from https://standards.calconnect.org, and access such metadata through the `CcBibliographicItem` object.
|
|
6
|
+
|
|
7
|
+
== Usage
|
|
8
|
+
|
|
9
|
+
=== Search for a standard using keywords
|
|
10
|
+
|
|
11
|
+
[source,ruby]
|
|
12
|
+
----
|
|
13
|
+
require 'relaton/calconnect'
|
|
14
|
+
=> true
|
|
15
|
+
|
|
16
|
+
hits = Relaton::Calconnect::Bibliography.search("CC/DIR 10005:2019")
|
|
17
|
+
=> <Relaton::Calconnect::HitCollection:0x00000000002df0 @ref=CC/DIR 10005:2019 @fetched=false>
|
|
18
|
+
|
|
19
|
+
item = hits[0].item
|
|
20
|
+
=> #<Relaton::Calconnect::ItemData:0x000000011104c998
|
|
21
|
+
...
|
|
22
|
+
----
|
|
23
|
+
|
|
24
|
+
=== XML serialization
|
|
25
|
+
|
|
26
|
+
[source,ruby]
|
|
27
|
+
----
|
|
28
|
+
item.to_xml
|
|
29
|
+
=> "<bibitem id="CC-DIR10005-2019" type="standard" schema-version="v1.5.6">
|
|
30
|
+
<title language="en">Guidelines for the implementation of the CalConnect patent policy</title>
|
|
31
|
+
<docidentifier type="CalConnect" primary="true">CC/DIR 10005:2019</docidentifier>
|
|
32
|
+
...
|
|
33
|
+
</bibitem>"
|
|
34
|
+
----
|
|
35
|
+
With argument `bibdata: true` it outputs XML wrapped by `bibdata` element and adds flavour `ext` element.
|
|
36
|
+
[source,ruby]
|
|
37
|
+
----
|
|
38
|
+
item.to_xml bibdata: true
|
|
39
|
+
=> "<bibdata type="standard" schema-version="v1.5.6">
|
|
40
|
+
<title language="en">Guidelines for the implementation of the CalConnect patent policy</title>
|
|
41
|
+
<docidentifier type="CalConnect" primary="true">CC/DIR 10005:2019</docidentifier>
|
|
42
|
+
...
|
|
43
|
+
<ext schema-version="v1.1.2">
|
|
44
|
+
<doctype abbreviation="DIR">directive</doctype>
|
|
45
|
+
<flavor>calconnect</flavor>
|
|
46
|
+
</ext>
|
|
47
|
+
</bibdata>
|
|
48
|
+
----
|
|
49
|
+
|
|
50
|
+
=== Typed links
|
|
51
|
+
|
|
52
|
+
Each Calconnect document has link types:
|
|
53
|
+
|
|
54
|
+
* `xml` - XML format
|
|
55
|
+
* `pdf` - PDF format
|
|
56
|
+
* `doc` - Microsoft Word document format
|
|
57
|
+
* `html` - HTML format
|
|
58
|
+
* `rxl` - RelatonXML format
|
|
59
|
+
|
|
60
|
+
[source,ruby]
|
|
61
|
+
----
|
|
62
|
+
item.source
|
|
63
|
+
=> [#<Relaton::Bib::Uri:0x00007ff2d4b009e0 @content=#<Addressable::URI:0x668 URI:https://standards.calconnect.org/csd/cc-10005.xml>, @type="xml">,
|
|
64
|
+
#<Relaton::Bib::Uri:0x00007ff2d4b00378 @content=#<Addressable::URI:0x67c URI:https://standards.calconnect.org/csd/cc-10005.pdf>, @type="pdf">,
|
|
65
|
+
#<Relaton::Bib::Uri:0x00007ff2d4b00058 @content=#<Addressable::URI:0x690 URI:https://standards.calconnect.org/csd/cc-10005.doc>, @type="doc">,
|
|
66
|
+
#<Relaton::Bib::Uri:0x00007ff2d4b23d28 @content=#<Addressable::URI:0x6a4 URI:https://standards.calconnect.org/csd/cc-10005.html>, @type="html">,
|
|
67
|
+
#<Relaton::Bib::Uri:0x00007ff2d4b23a08 @content=#<Addressable::URI:0x6b8 URI:https://standards.calconnect.org/csd/cc-10005.rxl>, @type="rxl">]
|
|
68
|
+
----
|
|
69
|
+
|
|
70
|
+
=== Get document by code and year
|
|
71
|
+
[source,ruby]
|
|
72
|
+
----
|
|
73
|
+
Relaton::Calconnect::Bibliography.get "CC/DIR 10005", "2019"
|
|
74
|
+
[relaton-calconnect] INFO: (CC/DIR 10005) Fetching from Relaton repository ...
|
|
75
|
+
[relaton-calconnect] INFO: (CC/DIR 10005) Found: `CC/DIR 10005:2019`
|
|
76
|
+
=> #<Relaton::Calconnect::ItemData:0x0000000111944b18
|
|
77
|
+
...
|
|
78
|
+
----
|
|
79
|
+
|
|
80
|
+
=== Create bibliographic item from XML
|
|
81
|
+
[source,ruby]
|
|
82
|
+
----
|
|
83
|
+
Relaton::Calconnect::Item.from_xml File.read('spec/fixtures/cc_dir_10005_2019.xml')
|
|
84
|
+
=> #<Relaton::Calconnect::ItemData:0x000000010ec81040
|
|
85
|
+
...
|
|
86
|
+
----
|
|
87
|
+
|
|
88
|
+
=== Create bibliographic item from YAML
|
|
89
|
+
[source,ruby]
|
|
90
|
+
----
|
|
91
|
+
Relaton::Calconnect::Item.from_yaml File.read('spec/fixtures/item.yaml')
|
|
92
|
+
=> #<Relaton::Calconnect::ItemData:0x000000010fe5e600
|
|
93
|
+
...
|
|
94
|
+
----
|
|
95
|
+
|
|
96
|
+
=== Fetch data
|
|
97
|
+
|
|
98
|
+
This gem uses the https://raw.githubusercontent.com/usnistgov/NIST-Tech-Pubs/nist-pages/xml/allrecords.xml dataset as one of the data sources.
|
|
99
|
+
|
|
100
|
+
The method `RelatonCalconnect::DataFetcher.fetch(output: "data", format: "yaml")` fetches all the documents from the dataset and saves them to the `./data` folder in YAML format.
|
|
101
|
+
Arguments:
|
|
102
|
+
|
|
103
|
+
- `output` - folder to save documents (default './data').
|
|
104
|
+
- `format` - the format in which the documents are saved. Possible formats are: `yaml`, `xml`, `bibxml` (default `yaml`).
|
|
105
|
+
|
|
106
|
+
[source,ruby]
|
|
107
|
+
----
|
|
108
|
+
require "relaton/calconnect/data_fetcher"
|
|
109
|
+
Relaton::Calconnect::DataFetcher.fetch
|
|
110
|
+
Started at: 2021-09-09 16:03:51 +0200
|
|
111
|
+
Stopped at: 2021-09-09 16:04:12 +0200
|
|
112
|
+
Done in: 20 sec.
|
|
113
|
+
=> nil
|
|
114
|
+
----
|
|
115
|
+
|
|
116
|
+
=== Logging
|
|
117
|
+
|
|
118
|
+
RelatonCalconnect 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.
|
|
119
|
+
|
|
120
|
+
== Development
|
|
121
|
+
|
|
122
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
123
|
+
|
|
124
|
+
== Index
|
|
125
|
+
|
|
126
|
+
Lookups read a pubid-backed `index-v2`, whose rows are `Pubid::Calconnect::Identifier` objects serialized as `_type: pubid:calconnect:standard`. A reference is parsed into an identifier and matched against the index structurally, so a number matches exactly rather than as a substring: `CC/DIR 1000` finds nothing, `CC/DIR 10005` finds the one document, and `CC/A 0001` is not `CC/A 1`. An undated reference reaches every year of a document and the most recent is returned first.
|
|
127
|
+
|
|
128
|
+
The legacy `index-v1` is no longer produced here; https://github.com/relaton/relaton-data-calconnect[relaton-data-calconnect] rebuilds it from each crawled document's own docidentifier for released relaton v2 clients.
|
|
129
|
+
|
|
130
|
+
To refresh the index test fixture, run `rake spec:update_index_calconnect`. This downloads the published `index-v2.zip` from the https://github.com/relaton/relaton-data-calconnect[relaton-data-calconnect] repository.
|
|
131
|
+
|
|
132
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
133
|
+
|
|
134
|
+
== Contributing
|
|
135
|
+
|
|
136
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/calconnect/relaton_calconnect.
|
|
137
|
+
|
|
138
|
+
== License
|
|
139
|
+
|
|
140
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
**Pubid-backed docidentifier.** `Relaton::Ccsds::Docidentifier`
|
|
6
|
+
(`model/docidentifier.rb`, `< Bib::Docidentifier`) parses its `content` into a
|
|
7
|
+
`Pubid::Ccsds::Identifier` kept in `@pubid`, while the lutaml `content` attribute
|
|
8
|
+
stays a plain string for serialization. It implements the base class's abstract
|
|
9
|
+
`remove_part!` / `remove_date!` / `to_all_parts!` by mutating the pubid graph
|
|
10
|
+
(walking the `base` chain for supplements/corrigenda) and re-rendering
|
|
11
|
+
via `refresh_content!`. Unparseable content falls back to the raw string. It is
|
|
12
|
+
wired into `model/item.rb` (`attribute :docidentifier, Docidentifier`) so
|
|
13
|
+
`from_xml`/`from_yaml` and `DataParser#parse_docidentifier` yield this class.
|
|
14
|
+
`remove_date!` is effectively a no-op today — CCSDS ids carry no date component —
|
|
15
|
+
but is implemented for parity with ISO/IEC. `to_all_parts!` drops the part
|
|
16
|
+
component (e.g. `CCSDS 121.0-B-3` → `CCSDS 121-B-3`), freezes that rendering
|
|
17
|
+
into `@raw_content` (since `content` is live-derived from `@pubid`), then
|
|
18
|
+
wraps `@pubid` in pubid's `AllParts`. pubid-ccsds has no dedicated `AllParts`
|
|
19
|
+
subclass — unlike pubid-iso — so the wrapper is the generic
|
|
20
|
+
`Pubid::AllPartsIdentifier`: `content` stays the plain part-stripped id, but
|
|
21
|
+
`#pubid` itself, read directly, now answers `all_parts? == true` and renders
|
|
22
|
+
WITH pubid's generic "(all parts)" marker, since it's the wrapper. This
|
|
23
|
+
mirrors `lib/relaton/iec/model/docidentifier.rb`; the shared skeleton is
|
|
24
|
+
intentionally duplicated per flavor for now (to be hoisted into
|
|
25
|
+
`Bib::Docidentifier` once every flavor's id is Pubid-backed).
|
|
26
|
+
|
|
27
|
+
**Translation relations use pubid's subset match.**
|
|
28
|
+
`Data::Fetcher#search_instance_translation` reads the document's **already
|
|
29
|
+
parsed** id from `bib.docidentifier.first.pubid` (not `content`, which it would
|
|
30
|
+
re-parse), derives the language-less reference with `pubid.exclude(:language)`,
|
|
31
|
+
and picks the branch by `bibid == pubid` — no language means an instance
|
|
32
|
+
(`#search_translations`), a language means a translation (`#search_relations`).
|
|
33
|
+
This is more correct than the old `TRRGX` string test: `content` returns
|
|
34
|
+
`@pubid.to_s`, which reorders components, so a translated corrigendum
|
|
35
|
+
(`CCSDS 320.0-B-1-S - German Translated Cor. 1`) put the language mid-string and
|
|
36
|
+
the `$`-anchored `TRRGX` missed it; `exclude(:language)` strips the language down
|
|
37
|
+
the whole base chain. `TRRGX` still serves `translation_relation_types` and
|
|
38
|
+
`DataParser#relation_type`, which match raw relation-id strings.
|
|
39
|
+
`#search_relations` and `#search_translations` receive that pubid and select
|
|
40
|
+
index rows with `row[:id].exclude(:language) == bibid_pid` — a
|
|
41
|
+
language-agnostic match, so a translated row and the instance both match while
|
|
42
|
+
another edition of the same number does not. This does **not** use `===`:
|
|
43
|
+
pubid declares CCSDS `language` `subset_strict`, so a nil reference language
|
|
44
|
+
means "has none" (a wildcard would be wrong for `HitCollection`, where a
|
|
45
|
+
language-less user reference must not reach a translation). `exclude(:language)`
|
|
46
|
+
drops the language from the row on both sides, mirroring
|
|
47
|
+
`HitCollection#rows` (`r[:id].exclude(:edition) == pubid`).
|
|
48
|
+
|
|
49
|
+
`HitCollection#rows` keeps `index.search(pubid, exact: true)` for an edition
|
|
50
|
+
reference, although `language` is strict now. CCSDS `suffix` is still a nil
|
|
51
|
+
wildcard in pubid, so the bare `index.search(pubid)` makes `CCSDS 101.0-B-4`
|
|
52
|
+
also return the historical `CCSDS 101.0-B-4-S` (260 such pairs in the index
|
|
53
|
+
fixture). The `hit_collection_spec` "historical and a translated sibling"
|
|
54
|
+
example guards this. Drop `exact: true` only after pubid declares `suffix`
|
|
55
|
+
`subset_strict` too.
|
|
56
|
+
|
|
57
|
+
`#search_relations` excludes the document's own row with
|
|
58
|
+
`row[:id] == bib.docidentifier.first.pubid` — pubid to pubid. It compared a
|
|
59
|
+
`Pubid::Ccsds::Identifier` with a `String` (`.content`) before and was always
|
|
60
|
+
false, so a re-crawl over an index that already held the document gave it a
|
|
61
|
+
relation to itself. Covered by the `#search_relations` "excludes the document's
|
|
62
|
+
own row" example.
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
- `bundle install` — install dependencies
|
|
67
|
+
- `bundle exec rake spec` — run tests
|
|
68
|
+
- `bundle exec rubocop` — lint
|
|
69
|
+
|
|
70
|
+
## Testing
|
|
71
|
+
|
|
72
|
+
- **Framework:** RSpec with VCR cassettes and WebMock
|
|
73
|
+
- **Index fixture:** `spec/fixtures/index-v1.zip` is pre-loaded into `Relaton::Index` pool in `before(:suite)` (configured in `spec/support/webmock.rb`). Run `rake spec:update_index` to refresh from relaton-data-ccsds.
|
|
74
|
+
- **VCR cassettes:** `spec/vcr_cassettes/` — index download requests are ignored by VCR (handled by fixture).
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
= Relaton::Ccsds
|
|
2
|
+
|
|
3
|
+
RelatonCcsds is a Ruby gem that implements the https://github.com/relaton/relaton-models#bibliographic-item[BibliographicItem model].
|
|
4
|
+
|
|
5
|
+
You can use it to retrieve metadata of CCSDS Standards from https://public.ccsds.org/Publications/AllPubs.aspx, and access such metadata through the `RelatonCcsds::BibliographicItem` object.
|
|
6
|
+
|
|
7
|
+
== Usage
|
|
8
|
+
|
|
9
|
+
=== Search for a standard using keywords
|
|
10
|
+
|
|
11
|
+
[source,ruby]
|
|
12
|
+
----
|
|
13
|
+
require 'relaton/ccsds'
|
|
14
|
+
=> true
|
|
15
|
+
|
|
16
|
+
hits = Relaton::Ccsds::Bibliography.search("CCSDS 230.2-G-1")
|
|
17
|
+
=> <Relaton::Ccsds::HitCollection:0x000000000016b0 @ref=CCSDS 230.2-G-1 @fetched=false>
|
|
18
|
+
|
|
19
|
+
item = hits[0].item
|
|
20
|
+
=> #<Relaton::Ccsds::ItemData:0x0000000125ae1318
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
item.docidentifier[0].content
|
|
24
|
+
=> "CCSDS 230.2-G-1"
|
|
25
|
+
|
|
26
|
+
item = Relaton::Ccsds::Bibliography.get("CCSDS 230.2-G-1")
|
|
27
|
+
[relaton-ccsds] INFO: (CCSDS 230.2-G-1) Fetching from Relaton repository ...
|
|
28
|
+
[relaton-ccsds] INFO: (CCSDS 230.2-G-1) Found: `CCSDS 230.2-G-1`.
|
|
29
|
+
=> #<Relaton::Ccsds::ItemData:0x0000000125deba40
|
|
30
|
+
...
|
|
31
|
+
----
|
|
32
|
+
|
|
33
|
+
=== XML serialization
|
|
34
|
+
|
|
35
|
+
[source,ruby]
|
|
36
|
+
----
|
|
37
|
+
item.to_xml
|
|
38
|
+
=> "<bibitem id="CCSDS2302G1" schema-version="v1.5.6">
|
|
39
|
+
<fetched>2025-12-03</fetched>
|
|
40
|
+
<title language="en" script="Latn">Next Generation Uplink</title>
|
|
41
|
+
<uri type="src">https://ccsds.org/publications/ccsdsallpubs/entry/3224/</uri>
|
|
42
|
+
<uri type="pdf">https://ccsds.org/wp-content/uploads/gravity_forms/5-448e85c647331d9cbaf66c096458bdd5/2025/01//230x2g1.pdf</uri>
|
|
43
|
+
<docidentifier type="CCSDS" primary="true">CCSDS 230.2-G-1</docidentifier>
|
|
44
|
+
...
|
|
45
|
+
</bibitem>"
|
|
46
|
+
----
|
|
47
|
+
With argument `bibdata: true` it outputs XML wrapped by `bibdata` element and adds flavour `ext` element.
|
|
48
|
+
[source,ruby]
|
|
49
|
+
----
|
|
50
|
+
item.to_xml bibdata: true
|
|
51
|
+
=> "<bibdata schema-version="v1.5.6">
|
|
52
|
+
<fetched>2025-12-03</fetched>
|
|
53
|
+
<title language="en" script="Latn">Next Generation Uplink</title>
|
|
54
|
+
<uri type="src">https://ccsds.org/publications/ccsdsallpubs/entry/3224/</uri>
|
|
55
|
+
...
|
|
56
|
+
<ext>
|
|
57
|
+
<doctype>report</doctype>
|
|
58
|
+
<technology-area>Space Link Services Area</technology-area>
|
|
59
|
+
</ext>
|
|
60
|
+
</bibdata>"
|
|
61
|
+
----
|
|
62
|
+
|
|
63
|
+
=== Typed links
|
|
64
|
+
|
|
65
|
+
All the CCSDS documents have PDF links. Some of them have additional DOC links. The gem provides a way to access these links through the `RelatonBib::TypedUri` object.
|
|
66
|
+
|
|
67
|
+
[source,ruby]
|
|
68
|
+
----
|
|
69
|
+
item.source
|
|
70
|
+
=> [#<Relaton::Bib::Uri:0x0000000126722b90 @content="https://ccsds.org/publications/ccsdsallpubs/entry/3224/", @language=nil, @locale=nil, @script=nil, @type="src">,
|
|
71
|
+
#<Relaton::Bib::Uri:0x0000000126722690
|
|
72
|
+
@content="https://ccsds.org/wp-content/uploads/gravity_forms/5-448e85c647331d9cbaf66c096458bdd5/2025/01//230x2g1.pdf",
|
|
73
|
+
@language=nil,
|
|
74
|
+
@locale=nil,
|
|
75
|
+
@script=nil,
|
|
76
|
+
@type="pdf">]
|
|
77
|
+
|
|
78
|
+
Relaton::Ccsds::Bibliography.get("CCSDS 720.6-Y-1").source
|
|
79
|
+
[relaton-ccsds] (CCSDS 720.6-Y-1) Fetching from Relaton repository ...
|
|
80
|
+
[relaton-ccsds] (CCSDS 720.6-Y-1) Found: `CCSDS 720.6-Y-1`.
|
|
81
|
+
=> [#<Relaton::Bib::Uri:0x00000001268cfba0 @content="https://ccsds.org/publications/ccsdsallpubs/entry/3160/", @language=nil, @locale=nil, @script=nil, @type="src">,
|
|
82
|
+
#<Relaton::Bib::Uri:0x00000001268cf650
|
|
83
|
+
@content="https://ccsds.org/wp-content/uploads/gravity_forms/5-448e85c647331d9cbaf66c096458bdd5/2025/01//720x6y1.doc",
|
|
84
|
+
@language=nil,
|
|
85
|
+
@locale=nil,
|
|
86
|
+
@script=nil,
|
|
87
|
+
@type="doc">,
|
|
88
|
+
#<Relaton::Bib::Uri:0x00000001268cf100
|
|
89
|
+
@content="https://ccsds.org/wp-content/uploads/gravity_forms/5-448e85c647331d9cbaf66c096458bdd5/2025/01//720x6y1.pdf",
|
|
90
|
+
@language=nil,
|
|
91
|
+
@locale=nil,
|
|
92
|
+
@script=nil,
|
|
93
|
+
@type="pdf">]
|
|
94
|
+
----
|
|
95
|
+
|
|
96
|
+
=== Create bibliographic item from XML
|
|
97
|
+
[source,ruby]
|
|
98
|
+
----
|
|
99
|
+
Relaton::Ccsds::Item.from_xml File.read('spec/fixtures/ccsds_230_2-g-1.xml')
|
|
100
|
+
=> #<Relaton::Ccsds::ItemData:0x0000000127b46680
|
|
101
|
+
...
|
|
102
|
+
----
|
|
103
|
+
|
|
104
|
+
=== Create bibliographic item from YAML
|
|
105
|
+
[source,ruby]
|
|
106
|
+
----
|
|
107
|
+
yaml = File.read 'spec/fixtures/ccsds_230_2-g-1.yaml'
|
|
108
|
+
=> ---\nschema-version: v1.2.9\nid: CCSDS230.2-G-1 ...
|
|
109
|
+
|
|
110
|
+
Relaton::Ccsds::Item.from_yaml yaml
|
|
111
|
+
=> #<Relaton::Ccsds::ItemData:0x0000000122e4caa0
|
|
112
|
+
...
|
|
113
|
+
----
|
|
114
|
+
|
|
115
|
+
=== Fetch data
|
|
116
|
+
|
|
117
|
+
This gem uses the https://ccsds.org/publications/ccsdsallpubs/ page as a data source.
|
|
118
|
+
|
|
119
|
+
The method `Relaton::Ccsds::DataFetcher.fetch(output: "data", format: "yaml")` fetches all the documents from the data source and saves them to the `./data` folder in YAML format.
|
|
120
|
+
Arguments:
|
|
121
|
+
|
|
122
|
+
- `output` - folder to save documents (default './data').
|
|
123
|
+
- `format` - the format in which the documents are saved. Possible formats are: `yaml`, `xml`, `bibxml` (default `yaml`).
|
|
124
|
+
|
|
125
|
+
[source,ruby]
|
|
126
|
+
----
|
|
127
|
+
require 'relaton/ccsds/data/fetcher'
|
|
128
|
+
Relaton::Ccsds::DataFetcher.fetch
|
|
129
|
+
Started at: 2023-08-25 22:02:06 -0400
|
|
130
|
+
(data/CCSDS-720-4-Y-1.yaml) file already exists. Trying to merge links ...
|
|
131
|
+
...
|
|
132
|
+
Done in: 40 sec.
|
|
133
|
+
=> nil
|
|
134
|
+
----
|
|
135
|
+
|
|
136
|
+
=== Logging
|
|
137
|
+
|
|
138
|
+
RelatonCcsds 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.
|
|
139
|
+
|
|
140
|
+
== Development
|
|
141
|
+
|
|
142
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
143
|
+
|
|
144
|
+
To update the index test fixture (used by tests), run `rake spec:update_index`. This downloads the latest `index-v1.zip` from the https://github.com/relaton/relaton-data-ccsds[relaton-data-ccsds] repository.
|
|
145
|
+
|
|
146
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
147
|
+
|
|
148
|
+
== Contributing
|
|
149
|
+
|
|
150
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/relaton/relaton-ccsds.
|
|
151
|
+
|
|
152
|
+
== License
|
|
153
|
+
|
|
154
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|