relaton 3.0.0.pre.alpha.16 → 3.0.0.pre.alpha.18
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 +38 -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/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 +50 -3
- data/lib/relaton/db/CLAUDE.md +373 -0
- data/lib/relaton/db.rb +19 -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 +89 -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/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 +83 -2
|
@@ -67,15 +67,32 @@ module Relaton
|
|
|
67
67
|
# both sides of the equality, not only from the hits.
|
|
68
68
|
parts << :stage if refid.stage
|
|
69
69
|
compare_refid = refid.stage ? refid.exclude(:stage) : refid
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
# A stage-typed row exists now (the CSRC supplement): when the
|
|
71
|
+
# reference names a stage and a hit carries it, the exact-stage
|
|
72
|
+
# match wins outright — the stage-excluded comparison below stays
|
|
73
|
+
# as the family fallback for stages the dataset still lacks.
|
|
74
|
+
exact = []
|
|
75
|
+
if refid.stage
|
|
76
|
+
exact = @array.select do |item|
|
|
77
|
+
::Pubid::Nist::Identifier.parse(item.hit[:code]) == refid
|
|
78
|
+
rescue StandardError
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
arr = exact
|
|
83
|
+
if exact.empty?
|
|
84
|
+
arr = @array.select do |item|
|
|
85
|
+
pubid = ::Pubid::Nist::Identifier.parse(item.hit[:code])
|
|
86
|
+
pubid.exclude(*parts) == compare_refid
|
|
87
|
+
rescue StandardError
|
|
88
|
+
item.hit[:code] == ref
|
|
89
|
+
end
|
|
75
90
|
end
|
|
76
91
|
rescue StandardError
|
|
77
92
|
arr = @array.select { |item| item.hit[:code] == ref }
|
|
78
93
|
ensure
|
|
94
|
+
# The ensure is the one exit, so every branch sets arr: a `return`
|
|
95
|
+
# before it is overridden here (relaton#314).
|
|
79
96
|
dup = self.dup
|
|
80
97
|
dup.array = arr
|
|
81
98
|
return dup
|
|
@@ -108,6 +125,76 @@ module Relaton
|
|
|
108
125
|
parts
|
|
109
126
|
end
|
|
110
127
|
|
|
128
|
+
# The id of a pubs-export row: from its DOI (or its docidentifier),
|
|
129
|
+
# with the stage and the update its uri carries. A class method, so the
|
|
130
|
+
# scraper derives a related row's id the same way.
|
|
131
|
+
#
|
|
132
|
+
# @param json [Hash] pubs-export row
|
|
133
|
+
# @return [String]
|
|
134
|
+
def self.pubs_export_id(json) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
135
|
+
if json && json["doi"]
|
|
136
|
+
json["doi"].sub(/^10.6028\//, "")
|
|
137
|
+
else
|
|
138
|
+
json["docidentifier"]
|
|
139
|
+
end => id
|
|
140
|
+
|
|
141
|
+
# Not sub!: the row belongs to the shared PubsExport data.
|
|
142
|
+
id = id.sub(/(?:-draft\d*|\.\wpd)$/, "")
|
|
143
|
+
id = id.gsub(".", " ").sub(/-Add(\d*)$/, ' Add.\1') if id.match?(/-Add\d*$/)
|
|
144
|
+
# Normalize to space-separated form so pubid 2.x parses as
|
|
145
|
+
# parsed_format=:short and renders without dots; also force the
|
|
146
|
+
# "NIST " prefix so publisher_was_parsed is set.
|
|
147
|
+
parse_input = id.gsub(/\bNIST\./, "NIST ")
|
|
148
|
+
parse_input = "NIST #{parse_input}" unless parse_input.start_with?("NIST ")
|
|
149
|
+
pid = ::Pubid::Nist::Identifier.parse(parse_input)
|
|
150
|
+
|
|
151
|
+
# Canonicalize edition spelling: DOI-derived codes are already short
|
|
152
|
+
# ("…r2"), but no-DOI drafts come from the verbose docidentifier
|
|
153
|
+
# ("… Rev. 2") and pubid preserves that spelling via original_prefix.
|
|
154
|
+
# Drop it so every code renders the canonical short form.
|
|
155
|
+
pid.edition.original_prefix = nil if pid.respond_to?(:edition) && pid.edition
|
|
156
|
+
|
|
157
|
+
# Stage: URI is authoritative, fall back to iteration. "final" => no stage.
|
|
158
|
+
uri_stage = json["uri"] && json["uri"][%r{/(final|ipd|fpd|\dpd)(?:-\(\d+\))?(?:/|$)}, 1]
|
|
159
|
+
stage_src = uri_stage || json["iteration"]
|
|
160
|
+
case stage_src
|
|
161
|
+
when nil, "final"
|
|
162
|
+
# no stage — "final" means published
|
|
163
|
+
when "fpd"
|
|
164
|
+
pid.stage = ::Pubid::Nist::Components::Stage.new id: "f", type: "pd"
|
|
165
|
+
when /\A(\w)pd\z/
|
|
166
|
+
pid.stage = ::Pubid::Nist::Components::Stage.new id: Regexp.last_match(1), type: "pd"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
/\/upd(?<upd>\d+)\// =~ json["uri"]
|
|
170
|
+
# In pubid 2.x render paths the Update is read from
|
|
171
|
+
# update_component (Components::Update), not the legacy :update
|
|
172
|
+
# slot — assigning to :update would render as "-upd/Upd2" via the
|
|
173
|
+
# elsif fallback path in Base#to_short_style.
|
|
174
|
+
pid.update_component = ::Pubid::Nist::Components::Update.new(number: upd) if upd
|
|
175
|
+
pid.to_s
|
|
176
|
+
rescue StandardError
|
|
177
|
+
id += " #{json["iteration"]}" if json["iteration"] && json["iteration"] != "final"
|
|
178
|
+
id
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
#
|
|
182
|
+
# Build hit data from a CSRC pubs-export row. Shared with
|
|
183
|
+
# DataFetcher#fetch_csrc, so a crawled row and a runtime hit carry the
|
|
184
|
+
# same code.
|
|
185
|
+
#
|
|
186
|
+
# @param json [Hash] pubs-export row
|
|
187
|
+
#
|
|
188
|
+
# @return [Hash] hit data
|
|
189
|
+
#
|
|
190
|
+
def hit_data(json) # rubocop:disable Metrics/AbcSize
|
|
191
|
+
/(?<series>(?<=-)\w+$)/ =~ json["series"]
|
|
192
|
+
title = [json["title-main"], json["title-sub"]].compact.join " - "
|
|
193
|
+
release_date = parse_date(json["published-date"], str: false)
|
|
194
|
+
{ code: pubs_export_id(json), series: series.upcase, title: title, url: json["uri"],
|
|
195
|
+
status: json["status"], release_date: release_date, json: json }
|
|
196
|
+
end
|
|
197
|
+
|
|
111
198
|
private
|
|
112
199
|
|
|
113
200
|
def code_parts(code) # rubocop:disable Metrics/MethodLength
|
|
@@ -196,22 +283,45 @@ module Relaton
|
|
|
196
283
|
# @return [self] sorted hits collection
|
|
197
284
|
#
|
|
198
285
|
def sort_hits!
|
|
286
|
+
ref_stage = begin
|
|
287
|
+
st = ::Pubid::Nist::Identifier.parse(@reference)&.stage
|
|
288
|
+
st && st.id.to_s + st.type.to_s # the feed iteration form ("ipd")
|
|
289
|
+
rescue StandardError
|
|
290
|
+
nil
|
|
291
|
+
end
|
|
199
292
|
@array.sort! do |a, b|
|
|
293
|
+
# A stage-carrying reference prefers the hit whose stage matches
|
|
294
|
+
# (the ipd draft sorts before the final it drafts for); a
|
|
295
|
+
# stage-less reference keeps the stage-less hits first (relaton#146).
|
|
296
|
+
cmp = hit_stage_rank(a, ref_stage) <=> hit_stage_rank(b, ref_stage)
|
|
297
|
+
next cmp unless cmp.zero?
|
|
298
|
+
|
|
200
299
|
base_a, upd_a = base_and_update(a.hit[:code])
|
|
201
300
|
base_b, upd_b = base_and_update(b.hit[:code])
|
|
202
301
|
|
|
203
|
-
cmp = base_a <=> base_b
|
|
302
|
+
cmp = base_a.to_s <=> base_b.to_s
|
|
204
303
|
next cmp unless cmp.zero?
|
|
205
304
|
|
|
206
305
|
# Same base code: prefer higher /UpdN (latest update wins).
|
|
207
306
|
cmp = upd_b <=> upd_a
|
|
208
307
|
next cmp unless cmp.zero?
|
|
209
308
|
|
|
210
|
-
b.hit[:release_date] <=> a.hit[:release_date]
|
|
309
|
+
cmp = b.hit[:release_date] <=> a.hit[:release_date]
|
|
310
|
+
next cmp unless cmp
|
|
311
|
+
cmp
|
|
211
312
|
end
|
|
212
313
|
self
|
|
213
314
|
end
|
|
214
315
|
|
|
316
|
+
# The hit's stage id ("i" of an ipd, "f" of an fpd), or nil for a
|
|
317
|
+
# stage-less code. A parse failure reads as stage-less.
|
|
318
|
+
def hit_stage_rank(hit, ref_stage)
|
|
319
|
+
# The stage the hit carries: the CSRC feed's iteration field ("ipd",
|
|
320
|
+
# "fpd", "2pd") — the code itself strips stage suffixes.
|
|
321
|
+
hit_stage = hit.hit[:json].is_a?(Hash) ? hit.hit[:json]["iteration"] : nil
|
|
322
|
+
hit_stage == ref_stage ? 0 : 1 # rank 0 = the stage the reference names
|
|
323
|
+
end
|
|
324
|
+
|
|
215
325
|
# Split a code like "NIST FIPS 140-2/Upd2" into ["NIST FIPS 140-2", 2].
|
|
216
326
|
# Codes without an update suffix return update 0.
|
|
217
327
|
def base_and_update(code)
|
|
@@ -285,61 +395,13 @@ module Relaton
|
|
|
285
395
|
#
|
|
286
396
|
# @return [Array<Relaton::Nist::Hit>] hits
|
|
287
397
|
#
|
|
288
|
-
def from_json
|
|
398
|
+
def from_json
|
|
289
399
|
Util.info "Fetching from csrc.nist.gov ...", key: @reference
|
|
290
|
-
select_data.map
|
|
291
|
-
/(?<series>(?<=-)\w+$)/ =~ h["series"]
|
|
292
|
-
title = [h["title-main"], h["title-sub"]].compact.join " - "
|
|
293
|
-
release_date = parse_date(h["published-date"], str: false)
|
|
294
|
-
Hit.new({ code: pubs_export_id(h), series: series.upcase, title: title, url: h["uri"],
|
|
295
|
-
status: h["status"], release_date: release_date, json: h }, self)
|
|
296
|
-
end
|
|
400
|
+
select_data.map { |h| Hit.new(hit_data(h), self) }
|
|
297
401
|
end
|
|
298
402
|
|
|
299
|
-
def pubs_export_id(json)
|
|
300
|
-
|
|
301
|
-
json["doi"].sub(/^10.6028\//, "")
|
|
302
|
-
else
|
|
303
|
-
json["docidentifier"]
|
|
304
|
-
end => id
|
|
305
|
-
|
|
306
|
-
id.sub!(/(?:-draft\d*|\.\wpd)$/, "")
|
|
307
|
-
id = id.gsub(".", " ").sub(/-Add(\d*)$/, ' Add.\1') if id.match?(/-Add\d*$/)
|
|
308
|
-
# Normalize to space-separated form so pubid 2.x parses as
|
|
309
|
-
# parsed_format=:short and renders without dots; also force the
|
|
310
|
-
# "NIST " prefix so publisher_was_parsed is set.
|
|
311
|
-
parse_input = id.gsub(/\bNIST\./, "NIST ")
|
|
312
|
-
parse_input = "NIST #{parse_input}" unless parse_input.start_with?("NIST ")
|
|
313
|
-
pid = ::Pubid::Nist::Identifier.parse(parse_input)
|
|
314
|
-
|
|
315
|
-
# Canonicalize edition spelling: DOI-derived codes are already short
|
|
316
|
-
# ("…r2"), but no-DOI drafts come from the verbose docidentifier
|
|
317
|
-
# ("… Rev. 2") and pubid preserves that spelling via original_prefix.
|
|
318
|
-
# Drop it so every code renders the canonical short form.
|
|
319
|
-
pid.edition.original_prefix = nil if pid.respond_to?(:edition) && pid.edition
|
|
320
|
-
|
|
321
|
-
# Stage: URI is authoritative, fall back to iteration. "final" => no stage.
|
|
322
|
-
uri_stage = json["uri"] && json["uri"][%r{/(final|ipd|fpd|\dpd)(?:-\(\d+\))?(?:/|$)}, 1]
|
|
323
|
-
stage_src = uri_stage || json["iteration"]
|
|
324
|
-
case stage_src
|
|
325
|
-
when nil, "final"
|
|
326
|
-
# no stage — "final" means published
|
|
327
|
-
when "fpd"
|
|
328
|
-
pid.stage = ::Pubid::Nist::Components::Stage.new id: "f", type: "pd"
|
|
329
|
-
when /\A(\w)pd\z/
|
|
330
|
-
pid.stage = ::Pubid::Nist::Components::Stage.new id: Regexp.last_match(1), type: "pd"
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
/\/upd(?<upd>\d+)\// =~ json["uri"]
|
|
334
|
-
# In pubid 2.x render paths the Update is read from
|
|
335
|
-
# update_component (Components::Update), not the legacy :update
|
|
336
|
-
# slot — assigning to :update would render as "-upd/Upd2" via the
|
|
337
|
-
# elsif fallback path in Base#to_short_style.
|
|
338
|
-
pid.update_component = ::Pubid::Nist::Components::Update.new(number: upd) if upd
|
|
339
|
-
pid.to_s
|
|
340
|
-
rescue StandardError
|
|
341
|
-
id += " #{json["iteration"]}" if json["iteration"] && json["iteration"] != "final"
|
|
342
|
-
id
|
|
403
|
+
def pubs_export_id(json)
|
|
404
|
+
self.class.pubs_export_id json
|
|
343
405
|
end
|
|
344
406
|
|
|
345
407
|
#
|
data/lib/relaton/nist/scraper.rb
CHANGED
|
@@ -105,16 +105,16 @@ module Relaton
|
|
|
105
105
|
# Fetch dates
|
|
106
106
|
# @param doc [Hash]
|
|
107
107
|
# @param release_date [Date]
|
|
108
|
-
# @return [Array<
|
|
108
|
+
# @return [Array<Date>]
|
|
109
109
|
def fetch_dates(doc, release_date) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
110
|
-
dates = [
|
|
110
|
+
dates = [Date.new(type: "published", at: release_date.to_s)]
|
|
111
111
|
|
|
112
112
|
issued = safe_parse_date doc["issued-date"]
|
|
113
113
|
updated = safe_parse_date doc["updated-date"]
|
|
114
|
-
dates <<
|
|
114
|
+
dates << Date.new(type: "updated", at: updated.to_s) if updated
|
|
115
115
|
obsoleted = safe_parse_date doc["obsoleted-date"]
|
|
116
|
-
dates <<
|
|
117
|
-
dates <<
|
|
116
|
+
dates << Date.new(type: "obsoleted", at: obsoleted.to_s) if obsoleted
|
|
117
|
+
dates << Date.new(type: "issued", at: issued.to_s) if issued
|
|
118
118
|
dates
|
|
119
119
|
end
|
|
120
120
|
|
|
@@ -247,14 +247,28 @@ module Relaton
|
|
|
247
247
|
def fetch_relations_json(hit) # rubocop:disable Metrics/AbcSize
|
|
248
248
|
doc = hit[:json]
|
|
249
249
|
relations = doc["supersedes"].map do |r|
|
|
250
|
-
doc_relation "supersedes", hit
|
|
250
|
+
doc_relation "supersedes", related_id(r, hit), r["uri"]
|
|
251
251
|
end
|
|
252
252
|
|
|
253
253
|
relations + doc["superseded-by"].map do |r|
|
|
254
|
-
doc_relation "updates", hit
|
|
254
|
+
doc_relation "updates", related_id(r, hit), r["uri"]
|
|
255
255
|
end
|
|
256
256
|
end
|
|
257
257
|
|
|
258
|
+
# The related document's own id. The hit's code is the record's own
|
|
259
|
+
# id, and a relation that names it points the record at itself. The
|
|
260
|
+
# entry's docidentifier drops the update and the part ("SP 800-162"
|
|
261
|
+
# for each of 800-162, /Upd1 and /Upd2), so derive the id the way the
|
|
262
|
+
# record's own is derived: from the related feed row (its DOI), or
|
|
263
|
+
# from the entry's uri when the feed lacks that row.
|
|
264
|
+
def related_id(rel, hit)
|
|
265
|
+
row = PubsExport.instance.data.find { |d| d["uri"] == rel["uri"] }
|
|
266
|
+
return HitCollection.pubs_export_id(row) if row
|
|
267
|
+
return hit[:code] if rel["docidentifier"].to_s.strip.empty?
|
|
268
|
+
|
|
269
|
+
HitCollection.pubs_export_id(rel)
|
|
270
|
+
end
|
|
271
|
+
|
|
258
272
|
# @param type [String]
|
|
259
273
|
# @param ref [String]
|
|
260
274
|
# @param uri [String]
|
|
@@ -0,0 +1,251 @@
|
|
|
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-oasis is a Ruby gem for retrieving and serializing OASIS Standards bibliographic metadata. It is part of the Relaton family of gems and implements the IsoBibliographicItem model for OASIS standards. Uses the lutaml-model pattern on the `lutaml-integration` branch.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bundle exec rake spec # Run all tests (default rake task)
|
|
13
|
+
bundle exec rake rubocop # Run linter
|
|
14
|
+
bundle exec rspec # Run tests directly
|
|
15
|
+
bundle exec rspec spec/relaton/oasis/bibitem_spec.rb # Run a single test file
|
|
16
|
+
bin/console # Interactive IRB with gem loaded
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
### Class Hierarchy (under `Relaton::Oasis`)
|
|
22
|
+
|
|
23
|
+
All model classes use the lutaml-model pattern from `relaton-bib`:
|
|
24
|
+
|
|
25
|
+
- **ItemData** (`lib/relaton/oasis/item_data.rb`) — Data model class extending `Bib::ItemData`
|
|
26
|
+
- **Item** (`lib/relaton/oasis/item.rb`) — Base class extending `Bib::Item`, uses `ItemData` model, adds `ext` attribute of type `Ext`
|
|
27
|
+
- **Bibitem** (`lib/relaton/oasis/bibitem.rb`) — Extends `Item`, includes `Bib::BibitemShared` for individual bibliography entries
|
|
28
|
+
- **Bibdata** (`lib/relaton/oasis/bibdata.rb`) — Extends `Item`, includes `Bib::BibdataShared` for complete bibliography records
|
|
29
|
+
- **Ext** (`lib/relaton/oasis/ext.rb`) — Extends `Bib::Ext`, OASIS-specific metadata: `doctype`, `technology_area`, `schema_version`
|
|
30
|
+
- **Doctype** (`lib/relaton/oasis/doctype.rb`) — Extends `Bib::Doctype`, valid values: `specification`, `memorandum`, `resolution`, `standard`
|
|
31
|
+
- **Docidentifier** (`lib/relaton/oasis/docidentifier.rb`) — Extends
|
|
32
|
+
`Bib::Docidentifier`, parses its `content` into a `Pubid::Oasis::Identifier`
|
|
33
|
+
exposed as `#pubid`; see **Identifiers and the index**
|
|
34
|
+
|
|
35
|
+
### Bibliography (under `Relaton::Oasis`)
|
|
36
|
+
|
|
37
|
+
- **Bibliography** (`lib/relaton/oasis/bibliography.rb`) — Module with `search`/`get` class methods for fetching OASIS standards from the relaton-data-oasis GitHub repository. Delegates to private methods: `find_index_entry`, `fetch_yaml`, `parse_item`
|
|
38
|
+
|
|
39
|
+
### Data Fetching (under `Relaton::Oasis`)
|
|
40
|
+
|
|
41
|
+
These classes scrape https://www.oasis-open.org/standards/ to produce YAML/XML data files. Loaded separately via `require "relaton/oasis/data_fetcher"`.
|
|
42
|
+
|
|
43
|
+
- **DataFetcher** (`lib/relaton/oasis/data_fetcher.rb`) — Extends `Core::DataFetcher`, orchestrates fetching all OASIS standards
|
|
44
|
+
- **DataParser** (`lib/relaton/oasis/data_parser.rb`) — Parses individual OASIS document nodes into `ItemData`
|
|
45
|
+
- **DataPartParser** (`lib/relaton/oasis/data_part_parser.rb`) — Parses multi-part OASIS document nodes into `ItemData`
|
|
46
|
+
- **DataParserUtils** (`lib/relaton/oasis/data_parser_utils.rb`) — Shared module included by both parsers; provides contributor parsing, docid construction, retry logic, and doctype detection
|
|
47
|
+
|
|
48
|
+
### Identifiers and the index
|
|
49
|
+
|
|
50
|
+
#### Pubid-backed docidentifier
|
|
51
|
+
|
|
52
|
+
`Relaton::Oasis::Docidentifier` parses its `content` into a
|
|
53
|
+
`Pubid::Oasis::Identifier` kept in `@pubid`, while the lutaml `content`
|
|
54
|
+
attribute stays a plain **string** for serialization. Parsing is **soft**:
|
|
55
|
+
`content=` lazily requires pubid and rescues `LoadError`/`StandardError`, so a
|
|
56
|
+
missing gem or non-OASIS content leaves `@pubid` nil rather than raising. The
|
|
57
|
+
stored content carries the `OASIS ` publisher token, which is
|
|
58
|
+
`Pubid::Oasis::Identifier#to_s`'s default rendering, so a bare slug
|
|
59
|
+
(`"amqp-core"`) is *not* an OASIS printed id and leaves `@pubid` nil.
|
|
60
|
+
|
|
61
|
+
The scrapers still compose the slug by hand (`DataParserUtils#parse_docid`,
|
|
62
|
+
`#parse_spec`, `#parse_part`, `#parse_errata`, `DataParser#title_to_docid`) —
|
|
63
|
+
pubid parses the result, it does not replace those heuristics.
|
|
64
|
+
|
|
65
|
+
**`remove_part!` / `remove_date!` / `to_all_parts!` stay the inherited no-ops,
|
|
66
|
+
deliberately.** `Pubid::Oasis::Renderer` echoes the verbatim slug held in
|
|
67
|
+
`original`, so clearing `part`, `version` or `stage` cannot change the printed
|
|
68
|
+
id — the mutation would be invisible — and rewriting `original` would invent a
|
|
69
|
+
reference form OASIS does not publish. OASIS slugs are free-form with an
|
|
70
|
+
inconsistent internal structure, and no OASIS citation drops a part or a
|
|
71
|
+
version; there is no "all parts" or "most recent" spelling to render. Since
|
|
72
|
+
`dfbd26c72` those defaults are no-ops rather than `NotImplementedError` raises,
|
|
73
|
+
so `Bib::ItemData#to_all_parts` and `#to_most_recent_reference` return the item
|
|
74
|
+
unchanged instead of blowing up. Do not "finish" this class by adding them.
|
|
75
|
+
(Contrast `Relaton::Ogc::Docidentifier`, whose components really do render.)
|
|
76
|
+
|
|
77
|
+
#### `index-v2`, written and read
|
|
78
|
+
|
|
79
|
+
`INDEXFILE = "index-v2"` is the only index this flavor touches: pubid-keyed
|
|
80
|
+
rows (`_type: pubid:oasis:standard`), published by `relaton-data-oasis` on its
|
|
81
|
+
`v2` branch — the same branch `Bibliography::ENDPOINT` reads.
|
|
82
|
+
|
|
83
|
+
The producer and the consumer pass `pubid_class: ::Pubid::Oasis::Identifier`:
|
|
84
|
+
|
|
85
|
+
| Call site | Why it needs `pubid_class:` |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `DataFetcher#index` | `FileIO#save` calls `to_hash` only for instances of it — without it the crawl writes v1-shaped rows under a v2 name, silently |
|
|
88
|
+
| `Bibliography#index` | without it the rows stay raw hashes, `FileIO#sorted` is false, and every lookup scans all 605 rows |
|
|
89
|
+
|
|
90
|
+
`Processor#remove_index_file` passes `url: true` and `file:`, and no
|
|
91
|
+
`pubid_class:`: the delete never reads the index (see
|
|
92
|
+
`lib/relaton/index/CLAUDE.md`). It used to omit `url: true`, so `Db#clear` on an
|
|
93
|
+
empty pool deleted `./index-v2.yaml` in the working directory and kept the
|
|
94
|
+
cache.
|
|
95
|
+
|
|
96
|
+
`Bibliography#index` also passes `file:`, which the pre-migration code omitted.
|
|
97
|
+
`file:` names the cache file (`Type` falls back to `index.yaml` without it), so
|
|
98
|
+
the consumer and `remove_index_file` must pass the same one, or `Db#clear`
|
|
99
|
+
leaves the consumer's cache in place.
|
|
100
|
+
|
|
101
|
+
**This gem never writes `index-v1`.** `relaton-data-oasis`'s crawler derives it
|
|
102
|
+
from the v2 rows, for relaton v2 consumers only.
|
|
103
|
+
|
|
104
|
+
#### Resolving a reference
|
|
105
|
+
|
|
106
|
+
`Bibliography#find_index_entry` is the pubid `best_match` shape:
|
|
107
|
+
|
|
108
|
+
- **`#parse_ref` supplies the publisher token.** An OASIS printed id starts
|
|
109
|
+
with `OASIS `, and pubid's grammar requires it, but callers write bare slugs
|
|
110
|
+
(`mqtt-v5.0`) and `Db#fetch` passes the reference through verbatim. Adding
|
|
111
|
+
the token is normalization, not identification, so it happens in the flavor —
|
|
112
|
+
the same place W3C normalizes a URL or a leading `TR-`.
|
|
113
|
+
- **The parsed pubid is passed to `Type#search`, never the string.** Search
|
|
114
|
+
narrows only for a non-`String` argument, so passing text would disable the
|
|
115
|
+
binary search however the index was built. `pubid_class:` alone fixes
|
|
116
|
+
nothing; both had to change together.
|
|
117
|
+
- **Selection is pubid's asymmetric subset match** (`Pubid::SubsetMatch`):
|
|
118
|
+
`pubid === row[:id]`, the reference on the left. A component the reference
|
|
119
|
+
omits (`version`, `stage`, `part`, `label`) matches any value; a component it
|
|
120
|
+
states must be equal. `number` is always stated: it is the specification name,
|
|
121
|
+
the whole identity of an OASIS record and the key the index bsearches on.
|
|
122
|
+
`#ignored` survives only for `#ranking_key`, which counts the components the
|
|
123
|
+
caller did not ask for. Measured over the whole fixture index (605 rows,
|
|
124
|
+
6,795 comparisons): the subset match and the old
|
|
125
|
+
`matches?(…, ignore: #ignored)` agree on every pair.
|
|
126
|
+
- **There is no substring fallback.** After normalization the only unparseable
|
|
127
|
+
inputs are a blank string and one over pubid's 1000-character cap, and a
|
|
128
|
+
substring scan for `""` matches every row — worse than a miss. An
|
|
129
|
+
unparseable reference raises `Pubid::Errors::ParseError`
|
|
130
|
+
(`spec/oasis/relaton/oasis/bibliography_spec.rb` pins it), so a malformed
|
|
131
|
+
identifier stays distinct from an absent document.
|
|
132
|
+
|
|
133
|
+
##### Ordering: `#ranking_key`
|
|
134
|
+
|
|
135
|
+
Ignoring a component means "don't care", so a loose reference also matches the
|
|
136
|
+
more specific rows. The key, in order: an **exact printed id**; then the
|
|
137
|
+
**newest version**, segments compared as integers; then the **least specific**
|
|
138
|
+
row; then the **later stage revision** (digits only — OASIS stage letters have
|
|
139
|
+
no ranking this code may invent); then the printed id, so the result never
|
|
140
|
+
depends on index order.
|
|
141
|
+
|
|
142
|
+
The exact-match rule is not decoration. Five records are a bare specification
|
|
143
|
+
name that also has versioned siblings — `OASIS EDXL`, `OData`, `OSLC`, `SAML`,
|
|
144
|
+
`WSS` — and without it three of them answered with their newest sibling instead
|
|
145
|
+
of themselves. Published ids are unique (0 duplicates in 605 rows), so at most
|
|
146
|
+
one row can score there.
|
|
147
|
+
|
|
148
|
+
Verified over the whole published index: **all 605 ids resolve to their own
|
|
149
|
+
record**, with and without the publisher token.
|
|
150
|
+
|
|
151
|
+
##### One behaviour is deliberately lost
|
|
152
|
+
|
|
153
|
+
v1 matched by substring, so `OASIS amqp` resolved to some `amqp-core` record.
|
|
154
|
+
v2 matches identifiers, so a partial specification name no longer resolves.
|
|
155
|
+
That is the same semantic change IANA recorded, and
|
|
156
|
+
`spec/oasis/relaton/oasis/bibliography_spec.rb` asserts it so it stays a
|
|
157
|
+
decision rather than a surprise.
|
|
158
|
+
|
|
159
|
+
#### Unparseable ids are reported, in two places on purpose
|
|
160
|
+
|
|
161
|
+
`DataFetcher#add_to_index` indexes `docid.pubid` and, when it is nil, records
|
|
162
|
+
the reason in `@errors` and skips the row rather than indexing it unparsed:
|
|
163
|
+
`Relaton::Index` rejects the WHOLE index if a single row fails to deserialize,
|
|
164
|
+
and its sort calls `.root.number` on every id. The data file is still written,
|
|
165
|
+
so a document is unindexed, never lost.
|
|
166
|
+
|
|
167
|
+
`DataParserUtils#record_unparseable_id` records the **same** thing at the moment
|
|
168
|
+
the id is built. That is not redundant: the part ids that only ever become a
|
|
169
|
+
relation's `formattedref` (`DataParser#parse_relation`,
|
|
170
|
+
`DataPartParser#parse_relation`) never reach `save_doc`, so an index-time hook
|
|
171
|
+
alone cannot see them. Both entries are keyed on the **id string**, so a
|
|
172
|
+
document that is saved reports one line — the fetcher's, which also names the
|
|
173
|
+
output file. Keep the two keys identical.
|
|
174
|
+
|
|
175
|
+
Both routes rely on `Core::DataFetcher#report_errors` treating a **String**
|
|
176
|
+
value in `@errors` as the message, logged verbatim at `:error`, which is the
|
|
177
|
+
level the `GhIssue` channel subscribes to. So each entry becomes one line of the
|
|
178
|
+
crawl's "Error fetching documents" GitHub issue, and the key serves only to
|
|
179
|
+
de-duplicate. W3C and 3GPP use exactly this `@errors` route; ISO, ITU and NIST
|
|
180
|
+
reach the same issue through a side list plus a `report_errors` override.
|
|
181
|
+
OASIS is the only flavor that also records from its parser, for the
|
|
182
|
+
`formattedref` reason above.
|
|
183
|
+
|
|
184
|
+
#### The index key
|
|
185
|
+
|
|
186
|
+
`Index::Type#candidates_by_number` bsearches on `id.root.number.to_s`, and
|
|
187
|
+
`FileIO` sorts by that same key. `Pubid::Oasis::Identifier#number` holds the
|
|
188
|
+
**specification name** — "OSLC-CoreShapes", "STIX", "amqp-core" — so every
|
|
189
|
+
version, stage and part of one specification shares a bucket, the same shape as
|
|
190
|
+
an IETF draft slug or an IANA registry slug.
|
|
191
|
+
|
|
192
|
+
This needed a pubid change: the attribute was called `spec` and `number` was
|
|
193
|
+
never set, so every row keyed on `""` — one bucket, bsearch degraded to a
|
|
194
|
+
linear scan, silently, exactly the IANA and W3C trap. pubid `cfe8ee84` renames
|
|
195
|
+
it, with **no alias**, and it ships in the released `2.0.0.alpha.10` that
|
|
196
|
+
`relaton.gemspec` pins — so this flavor needs nothing from the `Gemfile`'s
|
|
197
|
+
temporary pubid `main` pin, unlike the nine listed there.
|
|
198
|
+
`spec/oasis/relaton/oasis/pubid_contract_spec.rb` is the standing guard: it
|
|
199
|
+
fails against any pubid that does not set `number`.
|
|
200
|
+
|
|
201
|
+
The flavor is indifferent to pubid's parse-failure class, which moved twice
|
|
202
|
+
during this work — `RuntimeError`, then `Parslet::ParseFailed`, and in
|
|
203
|
+
alpha.10 `Pubid::Errors::ParseError` (a `Parslet::ParseFailed`) with
|
|
204
|
+
`Pubid::Errors::InvalidInputError` (an `ArgumentError`) for a non-String or an
|
|
205
|
+
over-long input. `Docidentifier#content=` and `Bibliography#parse_ref` both
|
|
206
|
+
rescue `StandardError`, which every one of those descends from.
|
|
207
|
+
|
|
208
|
+
Measured over all 605 published rows, written through
|
|
209
|
+
`Relaton::Oasis::Docidentifier` into a `pubid_class:`-configured index and read
|
|
210
|
+
back: 605 rows round-trip byte-identically, 0 unparseable, 0 keying on `""`,
|
|
211
|
+
309 buckets with the largest 25, and a `STIX` lookup narrows to 25 of 605.
|
|
212
|
+
|
|
213
|
+
### Serialization
|
|
214
|
+
|
|
215
|
+
Supports XML (with RELAX NG schema validation via `grammars/`) and YAML round-trip serialization. Classes use `from_xml`/`to_xml` and `from_yaml`/`to_yaml` class methods inherited from the lutaml-model base.
|
|
216
|
+
|
|
217
|
+
### Test Conventions
|
|
218
|
+
|
|
219
|
+
Tests are round-trip based: parse a fixture file, serialize back, and compare output to input. XML tests also validate against the RELAX NG schema (`../../grammar/relaton-oasis-compile.rng`) using the `ruby-jing` gem — so the suite needs a Java runtime on `PATH`. Fixtures live in `spec/oasis/fixtures/`. VCR cassettes record HTTP interactions in `spec/oasis/vcr_cassettes/`. Private methods are tested via `send(:method_name, ...)`.
|
|
220
|
+
|
|
221
|
+
## Style
|
|
222
|
+
|
|
223
|
+
RuboCop config inherits from the Ribose OSS style guide. Target Ruby version: 3.4. Rails cops are required but disabled.
|
|
224
|
+
|
|
225
|
+
## Testing
|
|
226
|
+
|
|
227
|
+
Run the suite with `bundle exec rake spec:oasis` from the repo root; specs live
|
|
228
|
+
in `spec/oasis/` and run self-contained (see the root `CLAUDE.md`).
|
|
229
|
+
|
|
230
|
+
- **Index fixture:** `spec/oasis/fixtures/index-v2.zip` — the whole published
|
|
231
|
+
index (605 rows, verbatim), seeded into the `Relaton::Index` pool by
|
|
232
|
+
`spec/oasis/support/webmock.rb` in `before(:each)`, not only
|
|
233
|
+
`before(:suite)`: `Index::Pool#type` replaces the pooled entry whenever
|
|
234
|
+
`actual?` says no, and `DataFetcher#index` asks for the same `:oasis` type
|
|
235
|
+
with a `file:` and no `url:`. One producer example would otherwise evict the
|
|
236
|
+
fixture and send every later consumer example to a real download. The pool
|
|
237
|
+
key is `type.upcase.to_sym`, so `:oasis` is pooled as `:OASIS`.
|
|
238
|
+
- It is built **with `pubid_class:`**, and `type.index` runs once offline to
|
|
239
|
+
force the deserialize and sort. Without that the suite would pass while
|
|
240
|
+
exercising something the runtime never does.
|
|
241
|
+
- Refresh it with `bundle exec rake spec:update_index_oasis`
|
|
242
|
+
(`tasks/index_fixture_oasis.rb`), which copies the published rows verbatim
|
|
243
|
+
and refuses a v1 source rather than writing a fixture `Relaton::Index` would
|
|
244
|
+
reject wholesale.
|
|
245
|
+
- **The fixture must be written with `File.binwrite`.** The zip entry comes
|
|
246
|
+
back `ASCII-8BIT`, and one published row carries a non-breaking space —
|
|
247
|
+
`OpenC2-MQTT-v1.0]<NBSP>-CS01`, a malformed upstream "cite as" that pubid
|
|
248
|
+
keeps verbatim in `original`. `File.write(..., encoding: "UTF-8")`
|
|
249
|
+
transcodes and raises `Encoding::UndefinedConversionError` on it. Same
|
|
250
|
+
invariant as `Index::FileStorage#write`; OGC's copy of this fixture code gets
|
|
251
|
+
away with the transcode only because its rows are pure ASCII.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
= Relaton::Oasis: retrieve OASIS Standards for bibliographic use using the BibliographicItem model
|
|
2
|
+
|
|
3
|
+
RelatonOasis 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 OASIS Standards from https://www.oasis-open.org/standards/ and access such metadata through the `Relaton::Oasis::ItemData` object.
|
|
6
|
+
|
|
7
|
+
== Usage
|
|
8
|
+
|
|
9
|
+
=== Get standard using reference.
|
|
10
|
+
|
|
11
|
+
All the OASIS documents are parsed from https://www.oasis-open.org/standards/ and stored in a GitHub repository https://github.com/relaton/relaton-data-oasis. You can find all possible references on the index page https://relaton.github.io/relaton-data-oasis/.
|
|
12
|
+
|
|
13
|
+
[source,ruby]
|
|
14
|
+
----
|
|
15
|
+
require 'relaton/oasis'
|
|
16
|
+
=> true
|
|
17
|
+
|
|
18
|
+
item = Relaton::Oasis::Bibliography.get "OASIS amqp-core"
|
|
19
|
+
[relaton-oasis] INFO: (OASIS amqp-core) Fetching from Relaton repository ...
|
|
20
|
+
[relaton-oasis] INFO: (OASIS amqp-core) Found: `OASIS amqp-core`
|
|
21
|
+
=> #<Relaton::Oasis::ItemData:0x0000000120db0fa8
|
|
22
|
+
...
|
|
23
|
+
----
|
|
24
|
+
|
|
25
|
+
=== XML serialization
|
|
26
|
+
|
|
27
|
+
[source,ruby]
|
|
28
|
+
----
|
|
29
|
+
item.to_xml
|
|
30
|
+
=> "<bibitem id="OASISamqpcore" type="standard" schema-version="v1.4.1">
|
|
31
|
+
<fetched>2026-02-27</fetched>
|
|
32
|
+
<title language="en" script="Latn" type="main">Advanced Message Queueing Protocol (AMQP) v1.0</title>
|
|
33
|
+
<docidentifier type="OASIS" primary="true">OASIS amqp-core</docidentifier>
|
|
34
|
+
...
|
|
35
|
+
</bibitem>"
|
|
36
|
+
----
|
|
37
|
+
|
|
38
|
+
With argument `bibdata: true` it outputs XML wrapped by `bibdata` element and adds flavour `ext` element.
|
|
39
|
+
|
|
40
|
+
[source,ruby]
|
|
41
|
+
----
|
|
42
|
+
item.to_xml bibdata: true
|
|
43
|
+
=> "<bibdata type="standard" schema-version="v1.4.1">
|
|
44
|
+
<fetched>2026-02-27</fetched>
|
|
45
|
+
<title language="en" script="Latn" type="main">Advanced Message Queueing Protocol (AMQP) v1.0</title>
|
|
46
|
+
<docidentifier type="OASIS" primary="true">OASIS amqp-core</docidentifier>
|
|
47
|
+
...
|
|
48
|
+
<ext schema-version="v1.0.1">
|
|
49
|
+
<doctype>standard</doctype>
|
|
50
|
+
<flavor>oasis</flavor>
|
|
51
|
+
<technology-area>Messaging</technology-area>
|
|
52
|
+
</ext>
|
|
53
|
+
</bibdata>"
|
|
54
|
+
----
|
|
55
|
+
|
|
56
|
+
=== Typed links
|
|
57
|
+
|
|
58
|
+
Some OASIS documents have `src` type source link.
|
|
59
|
+
|
|
60
|
+
[source,ruby]
|
|
61
|
+
----
|
|
62
|
+
item = Relaton::Oasis::Bibliography.get "OASIS amqp-core-types-v1.0-Pt1"
|
|
63
|
+
[relaton-oasis] INFO: (OASIS amqp-core-types-v1.0-Pt1) Fetching from Relaton repository ...
|
|
64
|
+
[relaton-oasis] INFO: (OASIS amqp-core-types-v1.0-Pt1) Found: `OASIS amqp-core-types-v1.0-Pt1`
|
|
65
|
+
=> #<Relaton::Oasis::ItemData:0x0000000123ff12d8
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
item.source[0].type
|
|
69
|
+
=> "src"
|
|
70
|
+
|
|
71
|
+
item.source[0].content
|
|
72
|
+
=> "http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html"
|
|
73
|
+
----
|
|
74
|
+
|
|
75
|
+
=== Create bibliographic item from XML
|
|
76
|
+
|
|
77
|
+
[source,ruby]
|
|
78
|
+
----
|
|
79
|
+
Relaton::Oasis::Item.from_xml File.read("spec/fixtures/oasis_bibdata.xml")
|
|
80
|
+
=> #<Relaton::Oasis::ItemData:0x0000000123ffeb18
|
|
81
|
+
...
|
|
82
|
+
----
|
|
83
|
+
|
|
84
|
+
=== Create bibliographic item from YAML
|
|
85
|
+
[source,ruby]
|
|
86
|
+
----
|
|
87
|
+
Relaton::Oasis::Item.from_yaml File.read("spec/fixtures/item.yaml")
|
|
88
|
+
=> #<Relaton::Oasis::ItemData:0x000000012119a290
|
|
89
|
+
...
|
|
90
|
+
----
|
|
91
|
+
|
|
92
|
+
=== Fetch data
|
|
93
|
+
|
|
94
|
+
The OASIS documents can be fetched from https://www.oasis-open.org/standards/ and converted into RelatonXML/BibXML/BibYAML formats:
|
|
95
|
+
|
|
96
|
+
The method `Relaton::Oasis::DataFetcher.fetch(output: "data", format: "yaml")` fetches and converts all the documents from the https://www.oasis-open.org/standards/ and saves them to the `./data` folder in YAML format.
|
|
97
|
+
|
|
98
|
+
Arguments:
|
|
99
|
+
|
|
100
|
+
- `output` - folder to save documents (default './data').
|
|
101
|
+
- `format` - the format in which the documents are saved. Possible formats are: `yaml`, `xml`, `bibxml` (default `yaml`).
|
|
102
|
+
|
|
103
|
+
[source,ruby]
|
|
104
|
+
----
|
|
105
|
+
require "relaton/oasis/data_fetcher"
|
|
106
|
+
|
|
107
|
+
Relaton::Oasis::DataFetcher.fetch
|
|
108
|
+
Started at: 2022-03-18 18:34:06 +0100
|
|
109
|
+
Stopped at: 2022-03-18 18:34:12 +0100
|
|
110
|
+
Done in: 5 sec.
|
|
111
|
+
=> nil
|
|
112
|
+
----
|
|
113
|
+
|
|
114
|
+
=== Logging
|
|
115
|
+
|
|
116
|
+
RelatonOasis 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.
|
|
117
|
+
|
|
118
|
+
== Development
|
|
119
|
+
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
The index test fixture used by the tests is `spec/oasis/fixtures/index-v2.zip`, a verbatim copy of the published `index-v2.zip` from the https://github.com/relaton/relaton-data-oasis[relaton-data-oasis] repository. Refresh it with `rake spec:update_index_oasis`.
|
|
123
|
+
|
|
124
|
+
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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
125
|
+
|
|
126
|
+
== Contributing
|
|
127
|
+
|
|
128
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/relaton/relaton-oasis.
|
|
129
|
+
|
|
130
|
+
== License
|
|
131
|
+
|
|
132
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|