relaton 3.0.0.pre.alpha.1 → 3.0.0.pre.alpha.2

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +57 -1
  3. data/bin/console +0 -1
  4. data/lib/relaton/3gpp/bibliography.rb +82 -7
  5. data/lib/relaton/3gpp/data_fetcher.rb +51 -3
  6. data/lib/relaton/3gpp/docidentifier.rb +114 -0
  7. data/lib/relaton/3gpp/item.rb +6 -0
  8. data/lib/relaton/3gpp/parser.rb +1 -1
  9. data/lib/relaton/3gpp/processor.rb +4 -1
  10. data/lib/relaton/3gpp.rb +5 -1
  11. data/lib/relaton/adobe/bibdata.rb +8 -0
  12. data/lib/relaton/adobe/bibitem.rb +8 -0
  13. data/lib/relaton/adobe/bibliography.rb +92 -0
  14. data/lib/relaton/adobe/docidentifier.rb +49 -0
  15. data/lib/relaton/adobe/doctype.rb +14 -0
  16. data/lib/relaton/adobe/ext.rb +32 -0
  17. data/lib/relaton/adobe/item.rb +15 -0
  18. data/lib/relaton/adobe/item_base.rb +18 -0
  19. data/lib/relaton/adobe/item_data.rb +6 -0
  20. data/lib/relaton/adobe/processor.rb +45 -0
  21. data/lib/relaton/adobe/util.rb +8 -0
  22. data/lib/relaton/adobe.rb +37 -0
  23. data/lib/relaton/bib/model/address.rb +2 -2
  24. data/lib/relaton/bib/model/docidentifier.rb +24 -9
  25. data/lib/relaton/bib/model/localized_string.rb +1 -1
  26. data/lib/relaton/bib/model/structured_identifier.rb +10 -9
  27. data/lib/relaton/bib/sanitizer.rb +202 -6
  28. data/lib/relaton/bib.rb +0 -2
  29. data/lib/relaton/bipm/bibliography.rb +159 -10
  30. data/lib/relaton/bipm/data_fetcher.rb +26 -2
  31. data/lib/relaton/bipm/data_outcomes_parser.rb +3 -4
  32. data/lib/relaton/bipm/id_parser.rb +5 -4
  33. data/lib/relaton/bipm/model/structured_identifier.rb +21 -0
  34. data/lib/relaton/bipm/processor.rb +2 -2
  35. data/lib/relaton/bipm/rawdata_bipm_metrologia/fetcher.rb +2 -4
  36. data/lib/relaton/bipm/si_brochure_parser.rb +5 -3
  37. data/lib/relaton/bipm.rb +7 -1
  38. data/lib/relaton/bsi/bibliography.rb +115 -43
  39. data/lib/relaton/bsi/hit.rb +14 -0
  40. data/lib/relaton/bsi/hit_collection.rb +15 -16
  41. data/lib/relaton/bsi/model/docidentifier.rb +99 -1
  42. data/lib/relaton/bsi/processor.rb +1 -0
  43. data/lib/relaton/calconnect/bibliography.rb +12 -14
  44. data/lib/relaton/calconnect/data_fetcher.rb +77 -9
  45. data/lib/relaton/calconnect/docidentifier.rb +80 -0
  46. data/lib/relaton/calconnect/hit_collection.rb +65 -57
  47. data/lib/relaton/calconnect/model/item.rb +7 -0
  48. data/lib/relaton/calconnect/processor.rb +7 -1
  49. data/lib/relaton/calconnect.rb +11 -1
  50. data/lib/relaton/ccsds/data/fetcher.rb +17 -12
  51. data/lib/relaton/ccsds/data/parser.rb +1 -1
  52. data/lib/relaton/ccsds/hit_collection.rb +6 -1
  53. data/lib/relaton/ccsds/model/docidentifier.rb +121 -0
  54. data/lib/relaton/ccsds/model/item.rb +2 -0
  55. data/lib/relaton/cen/bibliography.rb +75 -47
  56. data/lib/relaton/cen/hit.rb +16 -1
  57. data/lib/relaton/cen/hit_collection.rb +59 -11
  58. data/lib/relaton/cen/model/docidentifier.rb +92 -1
  59. data/lib/relaton/cen/processor.rb +13 -8
  60. data/lib/relaton/cen/scraper.rb +13 -5
  61. data/lib/relaton/cen.rb +1 -0
  62. data/lib/relaton/cie/data_fetcher.rb +215 -30
  63. data/lib/relaton/cie/processor.rb +3 -1
  64. data/lib/relaton/cie/scrapper.rb +15 -2
  65. data/lib/relaton/cie.rb +2 -1
  66. data/lib/relaton/core/data_fetcher.rb +150 -3
  67. data/lib/relaton/core/governor.rb +320 -0
  68. data/lib/relaton/core/pacer.rb +134 -0
  69. data/lib/relaton/core/processor.rb +19 -0
  70. data/lib/relaton/core/request_error.rb +14 -0
  71. data/lib/relaton/core.rb +3 -0
  72. data/lib/relaton/db/registry.rb +41 -1
  73. data/lib/relaton/doi/crossref.rb +19 -2
  74. data/lib/relaton/doi/parser.rb +109 -15
  75. data/lib/relaton/easc/bibdata.rb +8 -0
  76. data/lib/relaton/easc/bibitem.rb +8 -0
  77. data/lib/relaton/easc/bibliography.rb +95 -0
  78. data/lib/relaton/easc/docidentifier.rb +100 -0
  79. data/lib/relaton/easc/doctype.rb +14 -0
  80. data/lib/relaton/easc/ext.rb +44 -0
  81. data/lib/relaton/easc/item.rb +13 -0
  82. data/lib/relaton/easc/item_base.rb +18 -0
  83. data/lib/relaton/easc/item_data.rb +6 -0
  84. data/lib/relaton/easc/processor.rb +46 -0
  85. data/lib/relaton/easc/util.rb +8 -0
  86. data/lib/relaton/easc.rb +35 -0
  87. data/lib/relaton/ecma/bibliography.rb +93 -25
  88. data/lib/relaton/ecma/data_fetcher.rb +71 -12
  89. data/lib/relaton/ecma/docidentifier.rb +124 -0
  90. data/lib/relaton/ecma/item.rb +2 -0
  91. data/lib/relaton/ecma/memento_parser.rb +1 -1
  92. data/lib/relaton/ecma/page_fetcher.rb +15 -3
  93. data/lib/relaton/ecma/parser_common.rb +2 -2
  94. data/lib/relaton/ecma/processor.rb +4 -1
  95. data/lib/relaton/ecma/standard_parser.rb +2 -2
  96. data/lib/relaton/ecma.rb +10 -1
  97. data/lib/relaton/etsi/bibliography.rb +67 -2
  98. data/lib/relaton/etsi/data_fetcher.rb +43 -4
  99. data/lib/relaton/etsi/processor.rb +3 -1
  100. data/lib/relaton/etsi.rb +2 -1
  101. data/lib/relaton/gb/bibliography.rb +55 -29
  102. data/lib/relaton/gb/docidentifier.rb +58 -9
  103. data/lib/relaton/gb/processor.rb +3 -0
  104. data/lib/relaton/gb/scraper.rb +27 -10
  105. data/lib/relaton/gost/bibdata.rb +8 -0
  106. data/lib/relaton/gost/bibitem.rb +8 -0
  107. data/lib/relaton/gost/bibliography.rb +107 -0
  108. data/lib/relaton/gost/docidentifier.rb +80 -0
  109. data/lib/relaton/gost/doctype.rb +16 -0
  110. data/lib/relaton/gost/ext.rb +46 -0
  111. data/lib/relaton/gost/item.rb +15 -0
  112. data/lib/relaton/gost/item_base.rb +18 -0
  113. data/lib/relaton/gost/item_data.rb +6 -0
  114. data/lib/relaton/gost/processor.rb +49 -0
  115. data/lib/relaton/gost/util.rb +8 -0
  116. data/lib/relaton/gost.rb +36 -0
  117. data/lib/relaton/iala/bibdata.rb +8 -0
  118. data/lib/relaton/iala/bibitem.rb +8 -0
  119. data/lib/relaton/iala/bibliography.rb +146 -0
  120. data/lib/relaton/iala/docidentifier.rb +89 -0
  121. data/lib/relaton/iala/doctype.rb +18 -0
  122. data/lib/relaton/iala/ext.rb +32 -0
  123. data/lib/relaton/iala/item.rb +21 -0
  124. data/lib/relaton/iala/item_base.rb +18 -0
  125. data/lib/relaton/iala/item_data.rb +6 -0
  126. data/lib/relaton/iala/processor.rb +43 -0
  127. data/lib/relaton/iala/relation.rb +7 -0
  128. data/lib/relaton/iala/util.rb +8 -0
  129. data/lib/relaton/iala.rb +35 -0
  130. data/lib/relaton/iana/bibliography.rb +67 -14
  131. data/lib/relaton/iana/data_fetcher.rb +35 -5
  132. data/lib/relaton/iana/processor.rb +3 -1
  133. data/lib/relaton/iana.rb +12 -1
  134. data/lib/relaton/iec/data_fetcher.rb +7 -1
  135. data/lib/relaton/iec/hit_collection.rb +1 -1
  136. data/lib/relaton/iec/model/docidentifier.rb +9 -5
  137. data/lib/relaton/iec/model/ext.rb +2 -2
  138. data/lib/relaton/iec/processor.rb +1 -0
  139. data/lib/relaton/ieee/bibliography.rb +25 -3
  140. data/lib/relaton/ieee/data_fetcher.rb +158 -17
  141. data/lib/relaton/ieee/idams_parser.rb +18 -11
  142. data/lib/relaton/ieee/processor.rb +4 -1
  143. data/lib/relaton/ieee/rawbib_id_parser.rb +291 -86
  144. data/lib/relaton/ieee.rb +2 -1
  145. data/lib/relaton/ietf/data_fetcher.rb +295 -12
  146. data/lib/relaton/ietf/processor.rb +7 -3
  147. data/lib/relaton/ietf/rfc/entry.rb +39 -3
  148. data/lib/relaton/ietf/scraper.rb +69 -36
  149. data/lib/relaton/ietf.rb +4 -1
  150. data/lib/relaton/iho/bibliography.rb +1 -1
  151. data/lib/relaton/iho/docidentifier.rb +1 -1
  152. data/lib/relaton/index/file_io.rb +11 -11
  153. data/lib/relaton/index/file_storage.rb +6 -1
  154. data/lib/relaton/index/pool.rb +6 -1
  155. data/lib/relaton/index/shard_source.rb +201 -0
  156. data/lib/relaton/index/type.rb +63 -12
  157. data/lib/relaton/index.rb +2 -1
  158. data/lib/relaton/iso/bibliography.rb +20 -15
  159. data/lib/relaton/iso/data_fetcher.rb +3 -3
  160. data/lib/relaton/iso/data_parser.rb +17 -3
  161. data/lib/relaton/iso/hit_collection.rb +27 -15
  162. data/lib/relaton/iso/item_data.rb +22 -0
  163. data/lib/relaton/iso/model/docidentifier.rb +24 -12
  164. data/lib/relaton/iso/processor.rb +1 -0
  165. data/lib/relaton/iso/scraper.rb +19 -3
  166. data/lib/relaton/itu/bibliography.rb +9 -4
  167. data/lib/relaton/itu/data_crawler_r.rb +664 -0
  168. data/lib/relaton/itu/data_fetcher.rb +496 -50
  169. data/lib/relaton/itu/data_merge_r.rb +149 -0
  170. data/lib/relaton/itu/data_parser_r.rb +163 -89
  171. data/lib/relaton/itu/data_parser_t.rb +228 -0
  172. data/lib/relaton/itu/family_cache.rb +177 -0
  173. data/lib/relaton/itu/governor.rb +56 -0
  174. data/lib/relaton/itu/hit.rb +9 -3
  175. data/lib/relaton/itu/hit_collection.rb +258 -86
  176. data/lib/relaton/itu/model/docidentifier.rb +67 -1
  177. data/lib/relaton/itu/model/structured_identifier.rb +19 -0
  178. data/lib/relaton/itu/processor.rb +10 -4
  179. data/lib/relaton/itu/pubid.rb +27 -5
  180. data/lib/relaton/itu/recommendation_fields.rb +334 -0
  181. data/lib/relaton/itu/recommendation_parser.rb +18 -149
  182. data/lib/relaton/itu/scraper.rb +13 -3
  183. data/lib/relaton/itu.rb +2 -1
  184. data/lib/relaton/jcgm/bibdata.rb +8 -0
  185. data/lib/relaton/jcgm/bibitem.rb +8 -0
  186. data/lib/relaton/jcgm/bibliography.rb +97 -0
  187. data/lib/relaton/jcgm/data_fetcher.rb +81 -0
  188. data/lib/relaton/jcgm/docidentifier.rb +102 -0
  189. data/lib/relaton/jcgm/doctype.rb +12 -0
  190. data/lib/relaton/jcgm/ext.rb +23 -0
  191. data/lib/relaton/jcgm/item.rb +20 -0
  192. data/lib/relaton/jcgm/item_base.rb +18 -0
  193. data/lib/relaton/jcgm/item_data.rb +6 -0
  194. data/lib/relaton/jcgm/meetings_parser.rb +175 -0
  195. data/lib/relaton/jcgm/processor.rb +71 -0
  196. data/lib/relaton/jcgm/relation.rb +9 -0
  197. data/lib/relaton/jcgm/structured_identifier.rb +40 -0
  198. data/lib/relaton/jcgm/util.rb +8 -0
  199. data/lib/relaton/jcgm.rb +24 -0
  200. data/lib/relaton/jis/bibliography.rb +8 -10
  201. data/lib/relaton/jis/data_fetcher.rb +21 -19
  202. data/lib/relaton/jis/docidentifier.rb +104 -5
  203. data/lib/relaton/jis/hit.rb +18 -23
  204. data/lib/relaton/jis/hit_collection.rb +19 -18
  205. data/lib/relaton/jis/processor.rb +1 -1
  206. data/lib/relaton/jis.rb +2 -3
  207. data/lib/relaton/logger/channels/gh_issue.rb +78 -13
  208. data/lib/relaton/nist/data_fetcher.rb +63 -13
  209. data/lib/relaton/nist/docidentifier.rb +165 -0
  210. data/lib/relaton/nist/item.rb +2 -0
  211. data/lib/relaton/nist/item_base.rb +16 -0
  212. data/lib/relaton/nist/mods_parser.rb +38 -12
  213. data/lib/relaton/nist/processor.rb +2 -1
  214. data/lib/relaton/nist/relation.rb +3 -0
  215. data/lib/relaton/nist/scraper.rb +6 -3
  216. data/lib/relaton/oasis/bibliography.rb +147 -6
  217. data/lib/relaton/oasis/data_fetcher.rb +41 -5
  218. data/lib/relaton/oasis/data_parser_utils.rb +37 -3
  219. data/lib/relaton/oasis/docidentifier.rb +54 -0
  220. data/lib/relaton/oasis/item.rb +3 -0
  221. data/lib/relaton/oasis/processor.rb +7 -1
  222. data/lib/relaton/oasis.rb +14 -1
  223. data/lib/relaton/ogc/data_fetcher.rb +23 -2
  224. data/lib/relaton/ogc/docidentifier.rb +105 -0
  225. data/lib/relaton/ogc/hit_collection.rb +78 -3
  226. data/lib/relaton/ogc/processor.rb +2 -1
  227. data/lib/relaton/ogc.rb +5 -1
  228. data/lib/relaton/oiml/bibliography.rb +90 -15
  229. data/lib/relaton/oiml/docidentifier.rb +18 -3
  230. data/lib/relaton/omg/docidentifier.rb +67 -0
  231. data/lib/relaton/omg/item.rb +1 -0
  232. data/lib/relaton/omg/processor.rb +1 -0
  233. data/lib/relaton/omg/scraper.rb +61 -16
  234. data/lib/relaton/omg.rb +1 -0
  235. data/lib/relaton/plateau/bibliography.rb +10 -3
  236. data/lib/relaton/plateau/data_fetcher.rb +25 -2
  237. data/lib/relaton/plateau/handbook_parser.rb +8 -1
  238. data/lib/relaton/plateau/hit.rb +10 -2
  239. data/lib/relaton/plateau/hit_collection.rb +31 -11
  240. data/lib/relaton/plateau/processor.rb +3 -1
  241. data/lib/relaton/plateau/technical_report_parser.rb +8 -1
  242. data/lib/relaton/plateau.rb +2 -1
  243. data/lib/relaton/sdo/config.rb +34 -0
  244. data/lib/relaton/sdo/fetcher.rb +52 -0
  245. data/lib/relaton/sdo/logo.rb +95 -0
  246. data/lib/relaton/sdo/name.rb +26 -0
  247. data/lib/relaton/sdo/organization.rb +71 -0
  248. data/lib/relaton/sdo/store.rb +49 -0
  249. data/lib/relaton/sdo.rb +29 -0
  250. data/lib/relaton/version.rb +1 -1
  251. data/lib/relaton/w3c/bibliography.rb +132 -12
  252. data/lib/relaton/w3c/data_fetcher.rb +194 -16
  253. data/lib/relaton/w3c/data_parser.rb +3 -3
  254. data/lib/relaton/w3c/docidentifier.rb +48 -0
  255. data/lib/relaton/w3c/governor.rb +32 -0
  256. data/lib/relaton/w3c/item.rb +3 -0
  257. data/lib/relaton/w3c/pubid.rb +12 -0
  258. data/lib/relaton/w3c/safe_realize.rb +110 -21
  259. data/lib/relaton/w3c.rb +12 -1
  260. data/lib/relaton/xsf/bibliography.rb +61 -1
  261. data/lib/relaton/xsf/data_fetcher.rb +55 -5
  262. data/lib/relaton/xsf/docidentifier.rb +46 -0
  263. data/lib/relaton/xsf/hit_collection.rb +31 -3
  264. data/lib/relaton/xsf/item.rb +6 -0
  265. data/lib/relaton/xsf/processor.rb +1 -0
  266. data/lib/relaton/xsf.rb +5 -1
  267. data/lib/relaton.rb +42 -0
  268. metadata +135 -24
  269. data/lib/relaton/ieee/pub_id.rb +0 -161
  270. data/lib/relaton/index/id_number.rb +0 -30
@@ -0,0 +1,15 @@
1
+ module Relaton
2
+ module Gost
3
+ # GOST bibliographic item. Re-declares +ext+ to use the typed Ext
4
+ # subclass so GOST-specific fields round-trip natively. Sub-files
5
+ # (ItemData, Ext, Docidentifier) are loaded lazily via the autoload
6
+ # entries declared in lib/relaton/gost.rb.
7
+ class Item < Bib::Item
8
+ model ItemData
9
+
10
+ attribute :ext, Ext
11
+ attribute :docidentifier, Docidentifier, collection: true,
12
+ initialize_empty: true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Relaton
2
+ module Gost
3
+ class ItemBase < Lutaml::Model::Serializable
4
+ include Bib::NamespaceHelper
5
+
6
+ attr_accessor :type
7
+
8
+ model ItemData
9
+
10
+ instance_exec(&Bib::ItemShared::ATTRIBUTES)
11
+
12
+ xml do
13
+ map_attribute "type", to: :type
14
+ instance_exec(&Bib::ItemShared::XML_BODY)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ module Relaton
2
+ module Gost
3
+ class ItemData < Bib::ItemData
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,49 @@
1
+ require "relaton/core/processor"
2
+
3
+ module Relaton
4
+ module Gost
5
+ # Relaton processor for the GOST flavor. Registered by
6
+ # Relaton::Db::Registry when the unified gem loads (see
7
+ # lib/relaton/db/registry.rb). Supports `relaton fetch gost ...`
8
+ # once the relaton-data-gost dataset is installed.
9
+ class Processor < Core::Processor
10
+ attr_reader :idtype
11
+
12
+ def initialize
13
+ @short = :relaton_gost
14
+ @prefix = "GOST"
15
+ # Both Latin "GOST" and Cyrillic "ГОСТ" route here. The trailing
16
+ # \b keeps the prefix from swallowing longer tokens ("GOSTA …").
17
+ @defaultprefix = %r{^(?:GOST|ГОСТ)\b}
18
+ @idtype = "GOST"
19
+ end
20
+
21
+ def get(code, date, opts)
22
+ require_relative "../gost"
23
+ Bibliography.get(code, date, opts)
24
+ end
25
+
26
+ def from_xml(xml)
27
+ require_relative "../gost"
28
+ Item.from_xml xml
29
+ end
30
+
31
+ def from_yaml(yaml)
32
+ require_relative "../gost"
33
+ Item.from_yaml(yaml)
34
+ end
35
+
36
+ def grammar_hash
37
+ require_relative "../gost"
38
+ @grammar_hash ||= ::Relaton::Gost.grammar_hash
39
+ end
40
+
41
+ def remove_index_file
42
+ require_relative "../gost"
43
+ Relaton::Index.find_or_create(
44
+ :gost, url: true, file: "#{INDEXFILE}.yaml"
45
+ ).remove_file
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module Gost
3
+ module Util
4
+ extend Relaton::Bib::Util
5
+ PROGNAME = "relaton-gost".freeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "relaton/bib"
4
+ require "relaton/index"
5
+ require "pubid"
6
+ require "relaton/version"
7
+
8
+ # Relaton::Gost is the GOST (Russian federal standards — Гостстандарт)
9
+ # flavor of the unified relaton gem. Adds typed Ext fields (urn,
10
+ # webpage) so they round-trip natively through YAML and XML without
11
+ # per-repo merge hacks.
12
+ module Relaton
13
+ module Gost
14
+ INDEXFILE = "index-v2".freeze
15
+
16
+ class Error < StandardError; end
17
+
18
+ autoload :Doctype, "relaton/gost/doctype"
19
+ autoload :Ext, "relaton/gost/ext"
20
+ autoload :ItemData, "relaton/gost/item_data"
21
+ autoload :ItemBase, "relaton/gost/item_base"
22
+ autoload :Item, "relaton/gost/item"
23
+ autoload :Bibitem, "relaton/gost/bibitem"
24
+ autoload :Bibdata, "relaton/gost/bibdata"
25
+ autoload :Docidentifier, "relaton/gost/docidentifier"
26
+ autoload :Bibliography, "relaton/gost/bibliography"
27
+ autoload :Util, "relaton/gost/util"
28
+ autoload :Processor, "relaton/gost/processor"
29
+
30
+ # Returns hash of XML grammar
31
+ # @return [String]
32
+ def self.grammar_hash
33
+ Digest::MD5.hexdigest Relaton::VERSION
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module Iala
3
+ class Bibdata < Item
4
+ model ItemData
5
+ include Bib::BibdataShared
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module Iala
3
+ class Bibitem < Item
4
+ model ItemData
5
+ include Bib::BibitemShared
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,146 @@
1
+ require "net/http"
2
+
3
+ module Relaton
4
+ module Iala
5
+ module Bibliography
6
+ ENDPOINT = "https://raw.githubusercontent.com/relaton/relaton-data-iala/main/".freeze
7
+
8
+ class << self
9
+ # Search for an IALA publication by its identifier.
10
+ #
11
+ # @param text [String] the IALA reference to look up (e.g. "IALA S1070")
12
+ # @param _year [String, nil] optional edition/year filter
13
+ # @param _opts [Hash] options (unused)
14
+ # @return [Relaton::Iala::Item, nil]
15
+ def search(text, _year = nil, _opts = {})
16
+ Util.info "Fetching from Relaton repository ...", key: text
17
+ row = best_match text
18
+ unless row
19
+ Util.info "Not found.", key: text
20
+ return
21
+ end
22
+
23
+ uri = URI("#{ENDPOINT}#{row[:file]}")
24
+ resp = Net::HTTP.get_response uri
25
+ unless resp.code == "200"
26
+ raise Relaton::RequestError, "Could not access #{uri}: HTTP #{resp.code}"
27
+ end
28
+
29
+ item = Relaton::Iala::Item.from_yaml resp.body
30
+ Util.info "Found: `#{item.docidentifier.first&.content}`", key: text
31
+ item.tap { |i| i.fetched = Date.today.to_s }
32
+ rescue SocketError, Errno::EINVAL, Errno::ECONNRESET, EOFError,
33
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
34
+ Net::ProtocolError, Net::ReadTimeout, OpenSSL::SSL::SSLError,
35
+ Errno::ETIMEDOUT => e
36
+ raise Relaton::RequestError, "Could not access #{uri}: #{e.message}"
37
+ end
38
+
39
+ def get(ref, year = nil, opts = {})
40
+ search(ref, year, opts)
41
+ end
42
+
43
+ private
44
+
45
+ #
46
+ # Find the index row for a reference, newest edition first.
47
+ #
48
+ # Passing the pubid to `Index::Type#search` is what enables the binary
49
+ # search on `id.root.number` — with a block alone, or with the plain
50
+ # string this used to pass, the whole index is scanned. Selection is
51
+ # `Type#search`'s default with no block — pubid's asymmetric subset
52
+ # match (`Pubid::SubsetMatch`): the reference on the left, the row on
53
+ # the right, and a component the reference omits matches any value.
54
+ # `edition` and `language` are IALA's only
55
+ # optional components, so a bare `IALA M0001` still finds the
56
+ # `Ed 9.0 (E)` row. The document type is deliberately NOT a wildcard —
57
+ # it is the identifier's class, and `===` requires the same class. 17 of
58
+ # the 309 index numbers are shared by two types, so `R1001` and `C1001`
59
+ # must never match each other. An `Annex` row carries its base
60
+ # identifier, which `===` compares with its own subset match.
61
+ #
62
+ # @param text [String]
63
+ # @return [Hash, nil]
64
+ #
65
+ # The substring-scan fallback this used to take when parsing failed is
66
+ # gone with the rescue. It was the same conflation the convention
67
+ # rejects: an unparseable string matched a SUBSTRING of the rendered
68
+ # ids, so an ambiguous reference silently resolved to whichever row
69
+ # sorted first, instead of telling the caller the reference is not an
70
+ # identifier. ecma, w3c and xsf never had one.
71
+ def best_match(text)
72
+ pubid = parse_ref text
73
+ rows = index.search(pubid)
74
+ rows.max_by { |r| [edition_key(r[:id].edition), language_key(r[:id]), r[:file]] }
75
+ end
76
+
77
+ #
78
+ # Order key for an IALA edition.
79
+ #
80
+ # Editions are dotted version strings, so they must be compared segment
81
+ # by segment as integers rather than as text: `"10.0"` is newer than
82
+ # `"9.0"` but sorts before it as a string. No published edition has a
83
+ # segment above 9 today, so this is a latent bug rather than a live one
84
+ # — the same one already fixed for ETSI. An absent edition sorts below
85
+ # every present one.
86
+ #
87
+ # `2` and `2.0` are distinct editions in the index and compare as
88
+ # `[2] < [2, 0]`; `2.02` and `2.2` reduce to the same segments. Both
89
+ # pairs are broken by the edition text and then the file path, so a
90
+ # repeated lookup returns the same document (the index sort is not
91
+ # stable).
92
+ #
93
+ # @param edition [String, nil]
94
+ # @return [Array]
95
+ #
96
+ def edition_key(edition)
97
+ [edition.to_s.split(".").map(&:to_i), edition.to_s]
98
+ end
99
+
100
+ # Prefer the language-neutral record over its translations: it is the
101
+ # base document, and the translations are keyed off it.
102
+ #
103
+ # @param row_id [Pubid::Iala::Identifier]
104
+ # @return [Integer]
105
+ #
106
+ def language_key(row_id)
107
+ row_id.language.nil? ? 1 : 0
108
+ end
109
+
110
+ #
111
+ # Parse a user reference into a `Pubid::Iala::Identifier`, or nil.
112
+ #
113
+ # `Pubid::Iala` takes the `IALA ` publisher prefix as optional and
114
+ # zero-pads the number to its type's canonical width, so `IALA M1`,
115
+ # `M0001` and `R1016:ed2.0(F)` all parse without normalization here.
116
+ #
117
+ # @param text [String]
118
+ # @return [Pubid::Iala::Identifier, nil]
119
+ #
120
+ # An unrecognized reference **raises**; like ISO, ETSI and 3GPP we let it
121
+ # propagate. relaton-cli rescues `Pubid::Errors::Error` and renders
122
+ # `"..." is not a recognized standards identifier`
123
+ # (`gems/relaton-cli/lib/relaton/cli/command.rb:324`), and `Db#fetch`
124
+ # logs it through the `StandardError` arm at `lib/relaton/db.rb:122`.
125
+ # Rescuing here would collapse "this identifier is malformed" into "no
126
+ # such document", leaving a caller unable to tell them apart.
127
+ def parse_ref(text)
128
+ ::Pubid::Iala::Identifier.parse text.to_s.strip
129
+ end
130
+
131
+ # The index is pubid-backed: `pubid_class:` is what makes
132
+ # Relaton::Index deserialize the rows into identifiers, sort them by
133
+ # `id.root.number`, and let `Type#search` bsearch. Without it the rows
134
+ # stay raw hashes and every lookup scans all 701 of them.
135
+ def index
136
+ Relaton::Index.find_or_create(
137
+ :iala,
138
+ url: "#{ENDPOINT}#{INDEXFILE}.zip",
139
+ file: "#{INDEXFILE}.yaml",
140
+ pubid_class: ::Pubid::Iala::Identifier,
141
+ )
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,89 @@
1
+ module Relaton
2
+ module Iala
3
+ # Structured IALA document identifier. The docid string is the canonical
4
+ # form (e.g. "IALA S1070 Ed 2.0"). Defined as a subclass so future
5
+ # Pubid::Iala integration can hook in via #pubid without changing the
6
+ # public interface.
7
+ class Docidentifier < Bib::Docidentifier
8
+ attr_reader :pubid
9
+
10
+ # Capture the inherited (LocalizedMarkedUpString) content setter before
11
+ # overriding #content=, so #refresh_content! can write the re-rendered
12
+ # string back WITHOUT re-parsing (a re-parse would rebuild @pubid from
13
+ # the string, which may not even parse back — e.g. an all-parts
14
+ # wrapper's own "(all parts)" rendering — and discard the mutation).
15
+ alias_method :store_content, :content=
16
+
17
+ def initialize(attrs = {}, options = {})
18
+ pubid = attrs.is_a?(Hash) ? attrs.delete(:pubid) : nil
19
+ attrs[:content] ||= pubid.to_s if pubid
20
+ super
21
+ @pubid = pubid if pubid
22
+ end
23
+
24
+ def content=(value)
25
+ super
26
+ return unless value
27
+
28
+ @pubid = begin
29
+ # `pubid/iala` needs pubid core (Pubid::PrefixesSupport) loaded first,
30
+ # so require both. Rescue LoadError so a missing pubid gem degrades to
31
+ # a plain string, and StandardError so non-IALA/unparseable content
32
+ # just leaves @pubid nil rather than raising.
33
+ require "pubid"
34
+ require "pubid/iala"
35
+ ::Pubid::Iala.parse(value)
36
+ rescue LoadError, StandardError
37
+ nil
38
+ end
39
+ end
40
+
41
+ # IALA's Pubid::Iala::Identifier models only publisher/number/edition/
42
+ # language. It has no distinct `date` component and folds any subpart
43
+ # inline into `number` (e.g. "0103-1"), so:
44
+ # * #remove_date! maps onto `edition` — IALA's version discriminator —
45
+ # yielding the version-agnostic ("most recent") reference.
46
+ # * #remove_part! clears the (currently-unused) part/subpart attributes;
47
+ # it is a no-op for the rendered content today, but is implemented so
48
+ # it never raises and starts working automatically should pubid-iala
49
+ # ever model `part` separately.
50
+ # All three no-op safely when @pubid is nil (parse failed or the pubid gem
51
+ # is unavailable), so Bib::ItemData#to_all_parts / #to_most_recent_reference
52
+ # never raise on IALA items.
53
+
54
+ def remove_part!
55
+ clear_attr!(:part)
56
+ end
57
+
58
+ def remove_date!
59
+ clear_attr!(:edition)
60
+ end
61
+
62
+ def to_all_parts!
63
+ return if !@pubid || @pubid.all_parts?
64
+
65
+ # `#exclude` (no args) rebuilds a full independent copy — `remove_part!`
66
+ # / `remove_date!` mutate `@pubid` in place, so a bare reference here
67
+ # would lose the original edition to that mutation too.
68
+ original = @pubid.exclude
69
+ remove_part!
70
+ remove_date!
71
+ @pubid = original.to_all_parts
72
+ end
73
+
74
+ private
75
+
76
+ def clear_attr!(attr)
77
+ return unless @pubid && @pubid.respond_to?("#{attr}=")
78
+
79
+ @pubid.public_send("#{attr}=", nil)
80
+ @pubid.subpart = nil if attr == :part && @pubid.respond_to?(:subpart=)
81
+ refresh_content!
82
+ end
83
+
84
+ def refresh_content!
85
+ store_content(@pubid.to_s) if @pubid
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,18 @@
1
+ module Relaton
2
+ module Iala
3
+ # IALA publication document types. The value is carried in the
4
+ # inherited `content` attribute (e.g. `standard`, `recommendation`).
5
+ # The list mirrors the seven categories the IALA site exposes.
6
+ class Doctype < Bib::Doctype
7
+ TYPES = %w[
8
+ standard
9
+ recommendation
10
+ guideline
11
+ manual
12
+ model-course
13
+ report
14
+ resolution
15
+ ].freeze
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module Relaton
2
+ module Iala
3
+ # IALA bibliographic item extension. Adds IALA-specific structured
4
+ # metadata on top of the shared Relaton::Bib::Ext so the fields
5
+ # round-trip through both XML and YAML without per-repo merge hacks.
6
+ # Doctype is referenced lazily via autoload from lib/relaton/iala.rb.
7
+ class Ext < Bib::Ext
8
+ attribute :doctype, Doctype
9
+ attribute :urn, :string
10
+ attribute :webpage, :string
11
+ attribute :committee, :string
12
+ attribute :normative, :string
13
+ attribute :supersedes, :string, collection: true, initialize_empty: true
14
+
15
+ xml do
16
+ map_element "urn", to: :urn
17
+ map_element "webpage", to: :webpage
18
+ map_element "committee", to: :committee
19
+ map_element "normative", to: :normative
20
+ map_element "supersedes", to: :supersedes
21
+ end
22
+
23
+ key_value do
24
+ map_element "urn", to: :urn
25
+ map_element "webpage", to: :webpage
26
+ map_element "committee", to: :committee
27
+ map_element "normative", to: :normative
28
+ map_element "supersedes", to: :supersedes
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module Relaton
2
+ module Iala
3
+ # IALA bibliographic item. Re-declares +ext+ to use the typed Ext
4
+ # subclass so IALA-specific fields round-trip natively. Sub-files
5
+ # (ItemData, Ext, Docidentifier, Relation) are loaded lazily via the
6
+ # autoload entries declared in lib/relaton/iala.rb.
7
+ class Item < Bib::Item
8
+ model ItemData
9
+
10
+ attribute :ext, Ext
11
+ attribute :docidentifier, Docidentifier, collection: true,
12
+ initialize_empty: true
13
+ end
14
+ end
15
+ end
16
+
17
+ # Wire up the back-reference: an Item's relation collection contains
18
+ # Relation nodes whose bibitem is an ItemBase. Referencing
19
+ # Relaton::Iala::Relation triggers its autoload.
20
+ Relaton::Iala::Item.attribute :relation, Relaton::Iala::Relation,
21
+ collection: true, initialize_empty: true
@@ -0,0 +1,18 @@
1
+ module Relaton
2
+ module Iala
3
+ class ItemBase < Lutaml::Model::Serializable
4
+ include Bib::NamespaceHelper
5
+
6
+ attr_accessor :type
7
+
8
+ model ItemData
9
+
10
+ instance_exec(&Bib::ItemShared::ATTRIBUTES)
11
+
12
+ xml do
13
+ map_attribute "type", to: :type
14
+ instance_exec(&Bib::ItemShared::XML_BODY)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ module Relaton
2
+ module Iala
3
+ class ItemData < Bib::ItemData
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,43 @@
1
+ require "relaton/core/processor"
2
+
3
+ module Relaton
4
+ module Iala
5
+ class Processor < Core::Processor
6
+ attr_reader :idtype
7
+
8
+ def initialize
9
+ @short = :relaton_iala
10
+ @prefix = "IALA"
11
+ @defaultprefix = %r{^IALA\s}
12
+ @idtype = "IALA"
13
+ end
14
+
15
+ def get(code, date, opts)
16
+ require_relative "../iala"
17
+ Bibliography.get(code, date, opts)
18
+ end
19
+
20
+ def from_xml(xml)
21
+ require_relative "../iala"
22
+ Item.from_xml xml
23
+ end
24
+
25
+ def from_yaml(yaml)
26
+ require_relative "../iala"
27
+ Item.from_yaml(yaml)
28
+ end
29
+
30
+ def grammar_hash
31
+ require_relative "../iala"
32
+ @grammar_hash ||= ::Relaton::Iala.grammar_hash
33
+ end
34
+
35
+ def remove_index_file
36
+ require_relative "../iala"
37
+ Relaton::Index.find_or_create(
38
+ :iala, url: true, file: "#{INDEXFILE}.yaml"
39
+ ).remove_file
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ module Relaton
2
+ module Iala
3
+ class Relation < Bib::Relation
4
+ attribute :bibitem, ItemBase
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module Iala
3
+ module Util
4
+ extend Relaton::Bib::Util
5
+ PROGNAME = "relaton-iala".freeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ require "pubid"
2
+ require "relaton/bib"
3
+ require "relaton/index"
4
+ require "relaton/version"
5
+
6
+ # Relaton::Iala is the IALA (International Organization for Marine Aids
7
+ # to Navigation) flavor of the unified relaton gem. Adds typed Ext fields
8
+ # (urn, webpage, committee, normative, supersedes) so they round-trip
9
+ # natively through YAML and XML without per-repo merge hacks.
10
+ module Relaton
11
+ module Iala
12
+ INDEXFILE = "index-v2".freeze
13
+
14
+ class Error < StandardError; end
15
+
16
+ autoload :Util, "relaton/iala/util"
17
+ autoload :Doctype, "relaton/iala/doctype"
18
+ autoload :Ext, "relaton/iala/ext"
19
+ autoload :ItemData, "relaton/iala/item_data"
20
+ autoload :ItemBase, "relaton/iala/item_base"
21
+ autoload :Item, "relaton/iala/item"
22
+ autoload :Relation, "relaton/iala/relation"
23
+ autoload :Bibitem, "relaton/iala/bibitem"
24
+ autoload :Bibdata, "relaton/iala/bibdata"
25
+ autoload :Docidentifier, "relaton/iala/docidentifier"
26
+ autoload :Bibliography, "relaton/iala/bibliography"
27
+ autoload :Processor, "relaton/iala/processor"
28
+
29
+ # Returns hash of XML grammar
30
+ # @return [String]
31
+ def self.grammar_hash
32
+ Digest::MD5.hexdigest Relaton::VERSION
33
+ end
34
+ end
35
+ end