relaton 2.2.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 (702) hide show
  1. checksums.yaml +4 -4
  2. data/{docs/README.adoc → README.adoc} +165 -38
  3. data/bin/console +0 -1
  4. data/lib/relaton/3gpp/bibdata.rb +9 -0
  5. data/lib/relaton/3gpp/bibitem.rb +9 -0
  6. data/lib/relaton/3gpp/bibliography.rb +123 -0
  7. data/lib/relaton/3gpp/data_fetcher.rb +303 -0
  8. data/lib/relaton/3gpp/docidentifier.rb +114 -0
  9. data/lib/relaton/3gpp/doctype.rb +9 -0
  10. data/lib/relaton/3gpp/ext.rb +31 -0
  11. data/lib/relaton/3gpp/item.rb +18 -0
  12. data/lib/relaton/3gpp/item_data.rb +15 -0
  13. data/lib/relaton/3gpp/parser.rb +400 -0
  14. data/lib/relaton/3gpp/processor.rb +71 -0
  15. data/lib/relaton/3gpp/release.rb +34 -0
  16. data/lib/relaton/3gpp/util.rb +8 -0
  17. data/lib/relaton/3gpp.rb +29 -0
  18. data/lib/relaton/adobe/bibdata.rb +8 -0
  19. data/lib/relaton/adobe/bibitem.rb +8 -0
  20. data/lib/relaton/adobe/bibliography.rb +92 -0
  21. data/lib/relaton/adobe/docidentifier.rb +49 -0
  22. data/lib/relaton/adobe/doctype.rb +14 -0
  23. data/lib/relaton/adobe/ext.rb +32 -0
  24. data/lib/relaton/adobe/item.rb +15 -0
  25. data/lib/relaton/adobe/item_base.rb +18 -0
  26. data/lib/relaton/adobe/item_data.rb +6 -0
  27. data/lib/relaton/adobe/processor.rb +45 -0
  28. data/lib/relaton/adobe/util.rb +8 -0
  29. data/lib/relaton/adobe.rb +37 -0
  30. data/lib/relaton/bib/converter/asciibib/to_asciibib.rb +663 -0
  31. data/lib/relaton/bib/converter/asciibib.rb +13 -0
  32. data/lib/relaton/bib/converter/bibtex/from_bibtex.rb +245 -0
  33. data/lib/relaton/bib/converter/bibtex/to_bibtex.rb +341 -0
  34. data/lib/relaton/bib/converter/bibtex.rb +23 -0
  35. data/lib/relaton/bib/converter/bibxml/from_rfcxml.rb +386 -0
  36. data/lib/relaton/bib/converter/bibxml/from_rfcxml_referencegroup.rb +71 -0
  37. data/lib/relaton/bib/converter/bibxml/to_rfcxml.rb +308 -0
  38. data/lib/relaton/bib/converter/bibxml/to_rfcxml_referencegroup.rb +52 -0
  39. data/lib/relaton/bib/converter/bibxml.rb +51 -0
  40. data/lib/relaton/bib/hash_parser_v1.rb +767 -0
  41. data/lib/relaton/bib/item_data.rb +229 -0
  42. data/lib/relaton/bib/model/abstract.rb +16 -0
  43. data/lib/relaton/bib/model/address.rb +22 -0
  44. data/lib/relaton/bib/model/affiliation.rb +16 -0
  45. data/lib/relaton/bib/model/bibdata.rb +11 -0
  46. data/lib/relaton/bib/model/bibdata_shared.rb +12 -0
  47. data/lib/relaton/bib/model/bibitem.rb +11 -0
  48. data/lib/relaton/bib/model/bibitem_shared.rb +12 -0
  49. data/lib/relaton/bib/model/contact.rb +18 -0
  50. data/lib/relaton/bib/model/contribution_info.rb +15 -0
  51. data/lib/relaton/bib/model/contributor.rb +29 -0
  52. data/lib/relaton/bib/model/copyright.rb +27 -0
  53. data/lib/relaton/bib/model/date.rb +31 -0
  54. data/lib/relaton/bib/model/depiction.rb +16 -0
  55. data/lib/relaton/bib/model/docidentifier.rb +49 -0
  56. data/lib/relaton/bib/model/doctype.rb +14 -0
  57. data/lib/relaton/bib/model/edition.rb +14 -0
  58. data/lib/relaton/bib/model/ext.rb +39 -0
  59. data/lib/relaton/bib/model/extent.rb +16 -0
  60. data/lib/relaton/bib/model/formattedref.rb +43 -0
  61. data/lib/relaton/bib/model/full_name_type.rb +64 -0
  62. data/lib/relaton/bib/model/fullname.rb +11 -0
  63. data/lib/relaton/bib/model/ics.rb +49 -0
  64. data/lib/relaton/bib/model/image.rb +28 -0
  65. data/lib/relaton/bib/model/item.rb +96 -0
  66. data/lib/relaton/bib/model/item_base.rb +20 -0
  67. data/lib/relaton/bib/model/item_shared.rb +88 -0
  68. data/lib/relaton/bib/model/keyword.rb +30 -0
  69. data/lib/relaton/bib/model/locality.rb +18 -0
  70. data/lib/relaton/bib/model/locality_stack.rb +14 -0
  71. data/lib/relaton/bib/model/localized_string.rb +48 -0
  72. data/lib/relaton/bib/model/localized_string_attrs.rb +24 -0
  73. data/lib/relaton/bib/model/logo.rb +14 -0
  74. data/lib/relaton/bib/model/medium.rb +22 -0
  75. data/lib/relaton/bib/model/note.rb +16 -0
  76. data/lib/relaton/bib/model/organization.rb +13 -0
  77. data/lib/relaton/bib/model/organization_type.rb +42 -0
  78. data/lib/relaton/bib/model/person.rb +36 -0
  79. data/lib/relaton/bib/model/phone.rb +14 -0
  80. data/lib/relaton/bib/model/place.rb +33 -0
  81. data/lib/relaton/bib/model/price.rb +14 -0
  82. data/lib/relaton/bib/model/relation.rb +43 -0
  83. data/lib/relaton/bib/model/series.rb +34 -0
  84. data/lib/relaton/bib/model/size.rb +23 -0
  85. data/lib/relaton/bib/model/source_locality_stack.rb +14 -0
  86. data/lib/relaton/bib/model/status.rb +27 -0
  87. data/lib/relaton/bib/model/structured_identifier.rb +49 -0
  88. data/lib/relaton/bib/model/subdivision.rb +16 -0
  89. data/lib/relaton/bib/model/title.rb +56 -0
  90. data/lib/relaton/bib/model/type/plain_date.rb +16 -0
  91. data/lib/relaton/bib/model/type/string_date.rb +48 -0
  92. data/lib/relaton/bib/model/uri.rb +18 -0
  93. data/lib/relaton/bib/model/validity.rb +16 -0
  94. data/lib/relaton/bib/model/version.rb +43 -0
  95. data/lib/relaton/bib/namespace_helper.rb +21 -0
  96. data/lib/relaton/bib/sanitizer.rb +264 -0
  97. data/lib/relaton/bib/util.rb +18 -0
  98. data/lib/relaton/bib/versions.json +35 -0
  99. data/lib/relaton/bib.rb +46 -0
  100. data/lib/relaton/bipm/bibliography.rb +231 -0
  101. data/lib/relaton/bipm/converter/asciibib.rb +64 -0
  102. data/lib/relaton/bipm/data_fetcher.rb +81 -0
  103. data/lib/relaton/bipm/data_outcomes_parser.rb +656 -0
  104. data/lib/relaton/bipm/id_parser.rb +278 -0
  105. data/lib/relaton/bipm/item_data.rb +47 -0
  106. data/lib/relaton/bipm/model/bibdata.rb +9 -0
  107. data/lib/relaton/bipm/model/bibitem.rb +9 -0
  108. data/lib/relaton/bipm/model/comment_period.rb +13 -0
  109. data/lib/relaton/bipm/model/doctype.rb +12 -0
  110. data/lib/relaton/bipm/model/ext.rb +38 -0
  111. data/lib/relaton/bipm/model/item.rb +11 -0
  112. data/lib/relaton/bipm/model/structured_identifier.rb +36 -0
  113. data/lib/relaton/bipm/processor.rb +69 -0
  114. data/lib/relaton/bipm/rawdata_bipm_metrologia/affiliations.rb +111 -0
  115. data/lib/relaton/bipm/rawdata_bipm_metrologia/fetcher.rb +172 -0
  116. data/lib/relaton/bipm/rawdata_bipm_metrologia/niso_jats_parser.rb +353 -0
  117. data/lib/relaton/bipm/si_brochure_parser.rb +188 -0
  118. data/lib/relaton/bipm/util.rb +8 -0
  119. data/lib/relaton/bipm.rb +38 -0
  120. data/lib/relaton/bsi/bibliography.rb +196 -0
  121. data/lib/relaton/bsi/hit.rb +28 -0
  122. data/lib/relaton/bsi/hit_collection.rb +113 -0
  123. data/lib/relaton/bsi/item_data.rb +13 -0
  124. data/lib/relaton/bsi/model/bibdata.rb +8 -0
  125. data/lib/relaton/bsi/model/bibitem.rb +8 -0
  126. data/lib/relaton/bsi/model/docidentifier.rb +107 -0
  127. data/lib/relaton/bsi/model/doctype.rb +14 -0
  128. data/lib/relaton/bsi/model/ext.rb +17 -0
  129. data/lib/relaton/bsi/model/item.rb +17 -0
  130. data/lib/relaton/bsi/model/item_base.rb +22 -0
  131. data/lib/relaton/bsi/model/relation.rb +9 -0
  132. data/lib/relaton/bsi/processor.rb +43 -0
  133. data/lib/relaton/bsi/schema.json +24882 -0
  134. data/lib/relaton/bsi/scraper.rb +288 -0
  135. data/lib/relaton/bsi/util.rb +8 -0
  136. data/lib/relaton/bsi.rb +25 -0
  137. data/lib/relaton/calconnect/bibliography.rb +93 -0
  138. data/lib/relaton/calconnect/data_fetcher.rb +174 -0
  139. data/lib/relaton/calconnect/docidentifier.rb +80 -0
  140. data/lib/relaton/calconnect/hit.rb +14 -0
  141. data/lib/relaton/calconnect/hit_collection.rb +92 -0
  142. data/lib/relaton/calconnect/item_data.rb +12 -0
  143. data/lib/relaton/calconnect/model/bibdata.rb +8 -0
  144. data/lib/relaton/calconnect/model/bibitem.rb +8 -0
  145. data/lib/relaton/calconnect/model/doctype.rb +11 -0
  146. data/lib/relaton/calconnect/model/ext.rb +11 -0
  147. data/lib/relaton/calconnect/model/item.rb +20 -0
  148. data/lib/relaton/calconnect/processor.rb +72 -0
  149. data/lib/relaton/calconnect/scraper.rb +87 -0
  150. data/lib/relaton/calconnect/util.rb +8 -0
  151. data/lib/relaton/calconnect.rb +36 -0
  152. data/lib/relaton/ccsds/bibliography.rb +63 -0
  153. data/lib/relaton/ccsds/data/fetcher.rb +252 -0
  154. data/lib/relaton/ccsds/data/iso_references.rb +30 -0
  155. data/lib/relaton/ccsds/data/parser.rb +194 -0
  156. data/lib/relaton/ccsds/hit.rb +24 -0
  157. data/lib/relaton/ccsds/hit_collection.rb +47 -0
  158. data/lib/relaton/ccsds/item_data.rb +9 -0
  159. data/lib/relaton/ccsds/model/bibdata.rb +8 -0
  160. data/lib/relaton/ccsds/model/bibitem.rb +8 -0
  161. data/lib/relaton/ccsds/model/docidentifier.rb +121 -0
  162. data/lib/relaton/ccsds/model/doctype.rb +9 -0
  163. data/lib/relaton/ccsds/model/ext.rb +19 -0
  164. data/lib/relaton/ccsds/model/item.rb +15 -0
  165. data/lib/relaton/ccsds/processor.rb +68 -0
  166. data/lib/relaton/ccsds/util.rb +10 -0
  167. data/lib/relaton/ccsds.rb +33 -0
  168. data/lib/relaton/cen/bibliography.rb +149 -0
  169. data/lib/relaton/cen/committees.yaml +66 -0
  170. data/lib/relaton/cen/hit.rb +31 -0
  171. data/lib/relaton/cen/hit_collection.rb +116 -0
  172. data/lib/relaton/cen/item_data.rb +7 -0
  173. data/lib/relaton/cen/model/bibdata.rb +8 -0
  174. data/lib/relaton/cen/model/bibitem.rb +8 -0
  175. data/lib/relaton/cen/model/docidentifier.rb +100 -0
  176. data/lib/relaton/cen/model/ext.rb +11 -0
  177. data/lib/relaton/cen/model/item.rb +14 -0
  178. data/lib/relaton/cen/model/structured_identifier.rb +9 -0
  179. data/lib/relaton/cen/processor.rb +45 -0
  180. data/lib/relaton/cen/scraper.rb +218 -0
  181. data/lib/relaton/cen/util.rb +8 -0
  182. data/lib/relaton/cen.rb +30 -0
  183. data/lib/relaton/cie/bibdata.rb +8 -0
  184. data/lib/relaton/cie/bibitem.rb +8 -0
  185. data/lib/relaton/cie/bibliography.rb +31 -0
  186. data/lib/relaton/cie/data_fetcher.rb +540 -0
  187. data/lib/relaton/cie/ext.rb +7 -0
  188. data/lib/relaton/cie/item.rb +11 -0
  189. data/lib/relaton/cie/item_data.rb +6 -0
  190. data/lib/relaton/cie/processor.rb +68 -0
  191. data/lib/relaton/cie/scrapper.rb +52 -0
  192. data/lib/relaton/cie/util.rb +8 -0
  193. data/lib/relaton/cie.rb +29 -0
  194. data/lib/relaton/core/array_wrapper.rb +20 -0
  195. data/lib/relaton/core/data_fetcher.rb +244 -0
  196. data/lib/relaton/core/date_parser.rb +42 -0
  197. data/lib/relaton/core/governor.rb +320 -0
  198. data/lib/relaton/core/hash_keys_sybolizer.rb +19 -0
  199. data/lib/relaton/core/hit.rb +49 -0
  200. data/lib/relaton/core/hit_collection.rb +118 -0
  201. data/lib/relaton/core/pacer.rb +134 -0
  202. data/lib/relaton/core/processor.rb +67 -0
  203. data/lib/relaton/core/request_error.rb +14 -0
  204. data/lib/relaton/core/workers_pool.rb +45 -0
  205. data/lib/relaton/core.rb +12 -0
  206. data/lib/relaton/db/registry.rb +44 -4
  207. data/lib/relaton/db.rb +0 -1
  208. data/lib/relaton/doi/crossref.rb +89 -0
  209. data/lib/relaton/doi/parser.rb +921 -0
  210. data/lib/relaton/doi/processor.rb +65 -0
  211. data/lib/relaton/doi/util.rb +8 -0
  212. data/lib/relaton/doi.rb +20 -0
  213. data/lib/relaton/easc/bibdata.rb +8 -0
  214. data/lib/relaton/easc/bibitem.rb +8 -0
  215. data/lib/relaton/easc/bibliography.rb +95 -0
  216. data/lib/relaton/easc/docidentifier.rb +100 -0
  217. data/lib/relaton/easc/doctype.rb +14 -0
  218. data/lib/relaton/easc/ext.rb +44 -0
  219. data/lib/relaton/easc/item.rb +13 -0
  220. data/lib/relaton/easc/item_base.rb +18 -0
  221. data/lib/relaton/easc/item_data.rb +6 -0
  222. data/lib/relaton/easc/processor.rb +46 -0
  223. data/lib/relaton/easc/util.rb +8 -0
  224. data/lib/relaton/easc.rb +35 -0
  225. data/lib/relaton/ecma/bibdata.rb +8 -0
  226. data/lib/relaton/ecma/bibitem.rb +8 -0
  227. data/lib/relaton/ecma/bibliography.rb +149 -0
  228. data/lib/relaton/ecma/data_fetcher.rb +162 -0
  229. data/lib/relaton/ecma/data_parser.rb +49 -0
  230. data/lib/relaton/ecma/docidentifier.rb +124 -0
  231. data/lib/relaton/ecma/edition_parser.rb +80 -0
  232. data/lib/relaton/ecma/ext.rb +7 -0
  233. data/lib/relaton/ecma/item.rb +13 -0
  234. data/lib/relaton/ecma/item_data.rb +6 -0
  235. data/lib/relaton/ecma/memento_parser.rb +60 -0
  236. data/lib/relaton/ecma/page_fetcher.rb +39 -0
  237. data/lib/relaton/ecma/parser_common.rb +33 -0
  238. data/lib/relaton/ecma/processor.rb +69 -0
  239. data/lib/relaton/ecma/standard_parser.rb +134 -0
  240. data/lib/relaton/ecma/util.rb +8 -0
  241. data/lib/relaton/ecma.rb +33 -0
  242. data/lib/relaton/etsi/bibdata.rb +10 -0
  243. data/lib/relaton/etsi/bibitem.rb +10 -0
  244. data/lib/relaton/etsi/bibliography.rb +111 -0
  245. data/lib/relaton/etsi/data_fetcher.rb +167 -0
  246. data/lib/relaton/etsi/data_parser.rb +208 -0
  247. data/lib/relaton/etsi/doctype.rb +30 -0
  248. data/lib/relaton/etsi/ext.rb +31 -0
  249. data/lib/relaton/etsi/item.rb +15 -0
  250. data/lib/relaton/etsi/item_data.rb +6 -0
  251. data/lib/relaton/etsi/processor.rb +71 -0
  252. data/lib/relaton/etsi/pubid.rb +37 -0
  253. data/lib/relaton/etsi/status.rb +13 -0
  254. data/lib/relaton/etsi/util.rb +8 -0
  255. data/lib/relaton/etsi.rb +26 -0
  256. data/lib/relaton/gb/bibdata.rb +8 -0
  257. data/lib/relaton/gb/bibitem.rb +8 -0
  258. data/lib/relaton/gb/bibliography.rb +171 -0
  259. data/lib/relaton/gb/ccs.rb +14 -0
  260. data/lib/relaton/gb/committee.rb +13 -0
  261. data/lib/relaton/gb/docidentifier.rb +72 -0
  262. data/lib/relaton/gb/doctype.rb +9 -0
  263. data/lib/relaton/gb/ext.rb +36 -0
  264. data/lib/relaton/gb/gb_scraper.rb +61 -0
  265. data/lib/relaton/gb/gb_type.rb +20 -0
  266. data/lib/relaton/gb/hit.rb +48 -0
  267. data/lib/relaton/gb/hit_collection.rb +19 -0
  268. data/lib/relaton/gb/item.rb +13 -0
  269. data/lib/relaton/gb/item_data.rb +6 -0
  270. data/lib/relaton/gb/processor.rb +49 -0
  271. data/lib/relaton/gb/project_number.rb +38 -0
  272. data/lib/relaton/gb/scraper.rb +221 -0
  273. data/lib/relaton/gb/sec_scraper.rb +92 -0
  274. data/lib/relaton/gb/stage_name.rb +13 -0
  275. data/lib/relaton/gb/structured_identifier.rb +26 -0
  276. data/lib/relaton/gb/t_scraper.rb +126 -0
  277. data/lib/relaton/gb/util.rb +8 -0
  278. data/lib/relaton/gb/yaml/prefixes.yaml +200 -0
  279. data/lib/relaton/gb.rb +33 -0
  280. data/lib/relaton/gost/bibdata.rb +8 -0
  281. data/lib/relaton/gost/bibitem.rb +8 -0
  282. data/lib/relaton/gost/bibliography.rb +107 -0
  283. data/lib/relaton/gost/docidentifier.rb +80 -0
  284. data/lib/relaton/gost/doctype.rb +16 -0
  285. data/lib/relaton/gost/ext.rb +46 -0
  286. data/lib/relaton/gost/item.rb +15 -0
  287. data/lib/relaton/gost/item_base.rb +18 -0
  288. data/lib/relaton/gost/item_data.rb +6 -0
  289. data/lib/relaton/gost/processor.rb +49 -0
  290. data/lib/relaton/gost/util.rb +8 -0
  291. data/lib/relaton/gost.rb +36 -0
  292. data/lib/relaton/iala/bibdata.rb +8 -0
  293. data/lib/relaton/iala/bibitem.rb +8 -0
  294. data/lib/relaton/iala/bibliography.rb +146 -0
  295. data/lib/relaton/iala/docidentifier.rb +89 -0
  296. data/lib/relaton/iala/doctype.rb +18 -0
  297. data/lib/relaton/iala/ext.rb +32 -0
  298. data/lib/relaton/iala/item.rb +21 -0
  299. data/lib/relaton/iala/item_base.rb +18 -0
  300. data/lib/relaton/iala/item_data.rb +6 -0
  301. data/lib/relaton/iala/processor.rb +43 -0
  302. data/lib/relaton/iala/relation.rb +7 -0
  303. data/lib/relaton/iala/util.rb +8 -0
  304. data/lib/relaton/iala.rb +35 -0
  305. data/lib/relaton/iana/bibdata.rb +8 -0
  306. data/lib/relaton/iana/bibitem.rb +8 -0
  307. data/lib/relaton/iana/bibliography.rb +100 -0
  308. data/lib/relaton/iana/data_fetcher.rb +101 -0
  309. data/lib/relaton/iana/item.rb +7 -0
  310. data/lib/relaton/iana/item_data.rb +6 -0
  311. data/lib/relaton/iana/parser.rb +146 -0
  312. data/lib/relaton/iana/processor.rb +70 -0
  313. data/lib/relaton/iana/util.rb +8 -0
  314. data/lib/relaton/iana.rb +40 -0
  315. data/lib/relaton/iec/bibliography.rb +283 -0
  316. data/lib/relaton/iec/data_fetcher.rb +222 -0
  317. data/lib/relaton/iec/data_parser.rb +391 -0
  318. data/lib/relaton/iec/hit.rb +26 -0
  319. data/lib/relaton/iec/hit_collection.rb +138 -0
  320. data/lib/relaton/iec/item_data.rb +7 -0
  321. data/lib/relaton/iec/model/bibdata.rb +8 -0
  322. data/lib/relaton/iec/model/bibitem.rb +8 -0
  323. data/lib/relaton/iec/model/docidentifier.rb +135 -0
  324. data/lib/relaton/iec/model/doctype.rb +12 -0
  325. data/lib/relaton/iec/model/ext.rb +53 -0
  326. data/lib/relaton/iec/model/item.rb +20 -0
  327. data/lib/relaton/iec/model/item_base.rb +12 -0
  328. data/lib/relaton/iec/model/relation.rb +7 -0
  329. data/lib/relaton/iec/model/stage_name.rb +13 -0
  330. data/lib/relaton/iec/processor.rb +74 -0
  331. data/lib/relaton/iec/statuses.yml +199 -0
  332. data/lib/relaton/iec/util.rb +8 -0
  333. data/lib/relaton/iec.rb +98 -0
  334. data/lib/relaton/ieee/balloting_group.rb +13 -0
  335. data/lib/relaton/ieee/bibdata.rb +8 -0
  336. data/lib/relaton/ieee/bibitem.rb +8 -0
  337. data/lib/relaton/ieee/bibliography.rb +73 -0
  338. data/lib/relaton/ieee/converter/bibxml/from_rfcxml.rb +10 -0
  339. data/lib/relaton/ieee/converter/bibxml/from_rfcxml_referencegroup.rb +10 -0
  340. data/lib/relaton/ieee/converter/bibxml.rb +20 -0
  341. data/lib/relaton/ieee/data_fetcher.rb +771 -0
  342. data/lib/relaton/ieee/doctype.rb +9 -0
  343. data/lib/relaton/ieee/editorial_group.rb +19 -0
  344. data/lib/relaton/ieee/ext.rb +36 -0
  345. data/lib/relaton/ieee/idams_parser.rb +330 -0
  346. data/lib/relaton/ieee/item.rb +11 -0
  347. data/lib/relaton/ieee/item_data.rb +7 -0
  348. data/lib/relaton/ieee/processor.rb +71 -0
  349. data/lib/relaton/ieee/rawbib_id_parser.rb +753 -0
  350. data/lib/relaton/ieee/util.rb +8 -0
  351. data/lib/relaton/ieee.rb +30 -0
  352. data/lib/relaton/ietf/bibdata.rb +8 -0
  353. data/lib/relaton/ietf/bibitem.rb +8 -0
  354. data/lib/relaton/ietf/bibliography.rb +35 -0
  355. data/lib/relaton/ietf/bibxml_parser.rb +232 -0
  356. data/lib/relaton/ietf/data_fetcher.rb +561 -0
  357. data/lib/relaton/ietf/doctype.rb +9 -0
  358. data/lib/relaton/ietf/ext.rb +63 -0
  359. data/lib/relaton/ietf/item.rb +16 -0
  360. data/lib/relaton/ietf/item_base.rb +18 -0
  361. data/lib/relaton/ietf/item_data.rb +6 -0
  362. data/lib/relaton/ietf/processing_instructions.rb +79 -0
  363. data/lib/relaton/ietf/processor.rb +72 -0
  364. data/lib/relaton/ietf/relation.rb +9 -0
  365. data/lib/relaton/ietf/rfc/abstract.rb +19 -0
  366. data/lib/relaton/ietf/rfc/author.rb +21 -0
  367. data/lib/relaton/ietf/rfc/entry.rb +446 -0
  368. data/lib/relaton/ietf/rfc/entry_date.rb +21 -0
  369. data/lib/relaton/ietf/rfc/format.rb +19 -0
  370. data/lib/relaton/ietf/rfc/index.rb +46 -0
  371. data/lib/relaton/ietf/rfc/is_also.rb +21 -0
  372. data/lib/relaton/ietf/rfc/keywords.rb +19 -0
  373. data/lib/relaton/ietf/rfc/rfc_index_namespace.rb +11 -0
  374. data/lib/relaton/ietf/scraper.rb +101 -0
  375. data/lib/relaton/ietf/util.rb +8 -0
  376. data/lib/relaton/ietf/wg_name_resolver.rb +42 -0
  377. data/lib/relaton/ietf.rb +30 -0
  378. data/lib/relaton/iho/bibdata.rb +8 -0
  379. data/lib/relaton/iho/bibitem.rb +8 -0
  380. data/lib/relaton/iho/bibliography.rb +114 -0
  381. data/lib/relaton/iho/comment_period.rb +13 -0
  382. data/lib/relaton/iho/docidentifier.rb +31 -0
  383. data/lib/relaton/iho/doctype.rb +10 -0
  384. data/lib/relaton/iho/ext.rb +20 -0
  385. data/lib/relaton/iho/hash_parser_v1.rb +144 -0
  386. data/lib/relaton/iho/item.rb +20 -0
  387. data/lib/relaton/iho/item_base.rb +18 -0
  388. data/lib/relaton/iho/item_data.rb +6 -0
  389. data/lib/relaton/iho/processor.rb +54 -0
  390. data/lib/relaton/iho/relation.rb +9 -0
  391. data/lib/relaton/iho/structured_identifier.rb +20 -0
  392. data/lib/relaton/iho/util.rb +8 -0
  393. data/lib/relaton/iho.rb +27 -0
  394. data/lib/relaton/index/config.rb +52 -0
  395. data/lib/relaton/index/file_io.rb +305 -0
  396. data/lib/relaton/index/file_storage.rb +66 -0
  397. data/lib/relaton/index/pool.rb +46 -0
  398. data/lib/relaton/index/shard_source.rb +201 -0
  399. data/lib/relaton/index/type.rb +210 -0
  400. data/lib/relaton/index/util.rb +18 -0
  401. data/lib/relaton/index.rb +61 -0
  402. data/lib/relaton/isbn/isbn.rb +61 -0
  403. data/lib/relaton/isbn/open_library.rb +43 -0
  404. data/lib/relaton/isbn/parser.rb +88 -0
  405. data/lib/relaton/isbn/processor.rb +48 -0
  406. data/lib/relaton/isbn/util.rb +8 -0
  407. data/lib/relaton/isbn.rb +19 -0
  408. data/lib/relaton/iso/bibliography.rb +412 -0
  409. data/lib/relaton/iso/data_fetcher.rb +338 -0
  410. data/lib/relaton/iso/data_parser.rb +458 -0
  411. data/lib/relaton/iso/hash_parser_v1.rb +177 -0
  412. data/lib/relaton/iso/hit.rb +57 -0
  413. data/lib/relaton/iso/hit_collection.rb +196 -0
  414. data/lib/relaton/iso/item_data.rb +50 -0
  415. data/lib/relaton/iso/model/bibdata.rb +10 -0
  416. data/lib/relaton/iso/model/bibitem.rb +8 -0
  417. data/lib/relaton/iso/model/contributor.rb +6 -0
  418. data/lib/relaton/iso/model/contributor_info.rb +9 -0
  419. data/lib/relaton/iso/model/docidentifier.rb +146 -0
  420. data/lib/relaton/iso/model/doctype.rb +13 -0
  421. data/lib/relaton/iso/model/ext.rb +35 -0
  422. data/lib/relaton/iso/model/item.rb +17 -0
  423. data/lib/relaton/iso/model/item_base.rb +22 -0
  424. data/lib/relaton/iso/model/organization.rb +9 -0
  425. data/lib/relaton/iso/model/project_number.rb +22 -0
  426. data/lib/relaton/iso/model/relation.rb +9 -0
  427. data/lib/relaton/iso/model/stagename.rb +14 -0
  428. data/lib/relaton/iso/model/structured_identifier.rb +31 -0
  429. data/lib/relaton/iso/processor.rb +85 -0
  430. data/lib/relaton/iso/scraper.rb +635 -0
  431. data/lib/relaton/iso/type/pubid.rb +50 -0
  432. data/lib/relaton/iso/util.rb +8 -0
  433. data/lib/relaton/iso.rb +30 -0
  434. data/lib/relaton/itu/bibliography.rb +95 -0
  435. data/lib/relaton/itu/data_crawler_r.rb +664 -0
  436. data/lib/relaton/itu/data_fetcher.rb +550 -0
  437. data/lib/relaton/itu/data_merge_r.rb +149 -0
  438. data/lib/relaton/itu/data_parser_r.rb +214 -0
  439. data/lib/relaton/itu/data_parser_t.rb +228 -0
  440. data/lib/relaton/itu/family_cache.rb +177 -0
  441. data/lib/relaton/itu/governor.rb +56 -0
  442. data/lib/relaton/itu/hit.rb +28 -0
  443. data/lib/relaton/itu/hit_collection.rb +299 -0
  444. data/lib/relaton/itu/item_data.rb +6 -0
  445. data/lib/relaton/itu/model/approval_stage.rb +13 -0
  446. data/lib/relaton/itu/model/bibdata.rb +8 -0
  447. data/lib/relaton/itu/model/bibitem.rb +8 -0
  448. data/lib/relaton/itu/model/docidentifier.rb +75 -0
  449. data/lib/relaton/itu/model/doctype.rb +13 -0
  450. data/lib/relaton/itu/model/ext.rb +47 -0
  451. data/lib/relaton/itu/model/item.rb +13 -0
  452. data/lib/relaton/itu/model/meeting.rb +13 -0
  453. data/lib/relaton/itu/model/meeting_date.rb +15 -0
  454. data/lib/relaton/itu/model/question.rb +13 -0
  455. data/lib/relaton/itu/model/recommendation_status.rb +11 -0
  456. data/lib/relaton/itu/model/structured_identifier.rb +38 -0
  457. data/lib/relaton/itu/processor.rb +72 -0
  458. data/lib/relaton/itu/pubid.rb +199 -0
  459. data/lib/relaton/itu/radio_regulations_parser.rb +70 -0
  460. data/lib/relaton/itu/recommendation_fields.rb +334 -0
  461. data/lib/relaton/itu/recommendation_parser.rb +30 -0
  462. data/lib/relaton/itu/scraper.rb +203 -0
  463. data/lib/relaton/itu/util.rb +8 -0
  464. data/lib/relaton/itu.rb +34 -0
  465. data/lib/relaton/jcgm/bibdata.rb +8 -0
  466. data/lib/relaton/jcgm/bibitem.rb +8 -0
  467. data/lib/relaton/jcgm/bibliography.rb +97 -0
  468. data/lib/relaton/jcgm/data_fetcher.rb +81 -0
  469. data/lib/relaton/jcgm/docidentifier.rb +102 -0
  470. data/lib/relaton/jcgm/doctype.rb +12 -0
  471. data/lib/relaton/jcgm/ext.rb +23 -0
  472. data/lib/relaton/jcgm/item.rb +20 -0
  473. data/lib/relaton/jcgm/item_base.rb +18 -0
  474. data/lib/relaton/jcgm/item_data.rb +6 -0
  475. data/lib/relaton/jcgm/meetings_parser.rb +175 -0
  476. data/lib/relaton/jcgm/processor.rb +71 -0
  477. data/lib/relaton/jcgm/relation.rb +9 -0
  478. data/lib/relaton/jcgm/structured_identifier.rb +40 -0
  479. data/lib/relaton/jcgm/util.rb +8 -0
  480. data/lib/relaton/jcgm.rb +24 -0
  481. data/lib/relaton/jis/bibdata.rb +8 -0
  482. data/lib/relaton/jis/bibitem.rb +8 -0
  483. data/lib/relaton/jis/bibliography.rb +73 -0
  484. data/lib/relaton/jis/data_fetcher.rb +183 -0
  485. data/lib/relaton/jis/docidentifier.rb +120 -0
  486. data/lib/relaton/jis/doctype.rb +9 -0
  487. data/lib/relaton/jis/ext.rb +13 -0
  488. data/lib/relaton/jis/hit.rb +88 -0
  489. data/lib/relaton/jis/hit_collection.rb +128 -0
  490. data/lib/relaton/jis/item.rb +22 -0
  491. data/lib/relaton/jis/item_base.rb +26 -0
  492. data/lib/relaton/jis/item_data.rb +8 -0
  493. data/lib/relaton/jis/processor.rb +55 -0
  494. data/lib/relaton/jis/relation.rb +11 -0
  495. data/lib/relaton/jis/scraper.rb +232 -0
  496. data/lib/relaton/jis/structured_identifier.rb +13 -0
  497. data/lib/relaton/jis/util.rb +8 -0
  498. data/lib/relaton/jis.rb +32 -0
  499. data/lib/relaton/logger/channels/gh_issue.rb +143 -0
  500. data/lib/relaton/logger/config.rb +34 -0
  501. data/lib/relaton/logger/formatter_json.rb +8 -0
  502. data/lib/relaton/logger/formatter_string.rb +12 -0
  503. data/lib/relaton/logger/log.rb +64 -0
  504. data/lib/relaton/logger/log_device.rb +27 -0
  505. data/lib/relaton/logger/pool.rb +24 -0
  506. data/lib/relaton/logger.rb +18 -0
  507. data/lib/relaton/nist/bibdata.rb +8 -0
  508. data/lib/relaton/nist/bibitem.rb +8 -0
  509. data/lib/relaton/nist/bibliography.rb +191 -0
  510. data/lib/relaton/nist/comment_period.rb +15 -0
  511. data/lib/relaton/nist/data_fetcher.rb +134 -0
  512. data/lib/relaton/nist/date.rb +7 -0
  513. data/lib/relaton/nist/docidentifier.rb +165 -0
  514. data/lib/relaton/nist/doctype.rb +7 -0
  515. data/lib/relaton/nist/ext.rb +16 -0
  516. data/lib/relaton/nist/hit.rb +18 -0
  517. data/lib/relaton/nist/hit_collection.rb +362 -0
  518. data/lib/relaton/nist/item.rb +19 -0
  519. data/lib/relaton/nist/item_base.rb +16 -0
  520. data/lib/relaton/nist/item_data.rb +6 -0
  521. data/lib/relaton/nist/mods_parser.rb +302 -0
  522. data/lib/relaton/nist/processor.rb +69 -0
  523. data/lib/relaton/nist/pubs_export.rb +69 -0
  524. data/lib/relaton/nist/relation.rb +10 -0
  525. data/lib/relaton/nist/scraper.rb +293 -0
  526. data/lib/relaton/nist/series.yaml +49 -0
  527. data/lib/relaton/nist/util.rb +8 -0
  528. data/lib/relaton/nist.rb +34 -0
  529. data/lib/relaton/oasis/bibdata.rb +8 -0
  530. data/lib/relaton/oasis/bibitem.rb +8 -0
  531. data/lib/relaton/oasis/bibliography.rb +201 -0
  532. data/lib/relaton/oasis/browser_agent.rb +71 -0
  533. data/lib/relaton/oasis/data_fetcher.rb +130 -0
  534. data/lib/relaton/oasis/data_parser.rb +305 -0
  535. data/lib/relaton/oasis/data_parser_utils.rb +330 -0
  536. data/lib/relaton/oasis/data_part_parser.rb +239 -0
  537. data/lib/relaton/oasis/docidentifier.rb +54 -0
  538. data/lib/relaton/oasis/doctype.rb +7 -0
  539. data/lib/relaton/oasis/ext.rb +19 -0
  540. data/lib/relaton/oasis/item.rb +14 -0
  541. data/lib/relaton/oasis/item_data.rb +6 -0
  542. data/lib/relaton/oasis/processor.rb +74 -0
  543. data/lib/relaton/oasis/util.rb +8 -0
  544. data/lib/relaton/oasis.rb +41 -0
  545. data/lib/relaton/ogc/bibdata.rb +8 -0
  546. data/lib/relaton/ogc/bibitem.rb +8 -0
  547. data/lib/relaton/ogc/bibliography.rb +64 -0
  548. data/lib/relaton/ogc/data_fetcher.rb +131 -0
  549. data/lib/relaton/ogc/docidentifier.rb +111 -0
  550. data/lib/relaton/ogc/doctype.rb +12 -0
  551. data/lib/relaton/ogc/ext.rb +15 -0
  552. data/lib/relaton/ogc/hit.rb +18 -0
  553. data/lib/relaton/ogc/hit_collection.rb +111 -0
  554. data/lib/relaton/ogc/item.rb +13 -0
  555. data/lib/relaton/ogc/item_data.rb +6 -0
  556. data/lib/relaton/ogc/processor.rb +59 -0
  557. data/lib/relaton/ogc/scraper.rb +225 -0
  558. data/lib/relaton/ogc/util.rb +8 -0
  559. data/lib/relaton/ogc.rb +30 -0
  560. data/lib/relaton/oiml/bibdata.rb +8 -0
  561. data/lib/relaton/oiml/bibitem.rb +8 -0
  562. data/lib/relaton/oiml/bibliography.rb +169 -0
  563. data/lib/relaton/oiml/docidentifier.rb +48 -0
  564. data/lib/relaton/oiml/doctype.rb +13 -0
  565. data/lib/relaton/oiml/ext.rb +42 -0
  566. data/lib/relaton/oiml/item.rb +20 -0
  567. data/lib/relaton/oiml/item_base.rb +18 -0
  568. data/lib/relaton/oiml/item_data.rb +6 -0
  569. data/lib/relaton/oiml/processor.rb +54 -0
  570. data/lib/relaton/oiml/relation.rb +9 -0
  571. data/lib/relaton/oiml/util.rb +8 -0
  572. data/lib/relaton/oiml.rb +24 -0
  573. data/lib/relaton/omg/bibdata.rb +8 -0
  574. data/lib/relaton/omg/bibitem.rb +8 -0
  575. data/lib/relaton/omg/bibliography.rb +31 -0
  576. data/lib/relaton/omg/docidentifier.rb +67 -0
  577. data/lib/relaton/omg/ext.rb +7 -0
  578. data/lib/relaton/omg/item.rb +9 -0
  579. data/lib/relaton/omg/item_data.rb +6 -0
  580. data/lib/relaton/omg/processor.rb +35 -0
  581. data/lib/relaton/omg/scraper.rb +186 -0
  582. data/lib/relaton/omg/util.rb +8 -0
  583. data/lib/relaton/omg.rb +25 -0
  584. data/lib/relaton/plateau/bibdata.rb +8 -0
  585. data/lib/relaton/plateau/bibitem.rb +8 -0
  586. data/lib/relaton/plateau/bibliography.rb +31 -0
  587. data/lib/relaton/plateau/data_fetcher.rb +176 -0
  588. data/lib/relaton/plateau/doctype.rb +7 -0
  589. data/lib/relaton/plateau/ext.rb +23 -0
  590. data/lib/relaton/plateau/handbook_parser.rb +102 -0
  591. data/lib/relaton/plateau/hit.rb +21 -0
  592. data/lib/relaton/plateau/hit_collection.rb +79 -0
  593. data/lib/relaton/plateau/item.rb +12 -0
  594. data/lib/relaton/plateau/item_data.rb +6 -0
  595. data/lib/relaton/plateau/parser.rb +101 -0
  596. data/lib/relaton/plateau/processor.rb +51 -0
  597. data/lib/relaton/plateau/technical_report_parser.rb +98 -0
  598. data/lib/relaton/plateau/util.rb +8 -0
  599. data/lib/relaton/plateau.rb +29 -0
  600. data/lib/relaton/sdo/config.rb +34 -0
  601. data/lib/relaton/sdo/fetcher.rb +52 -0
  602. data/lib/relaton/sdo/logo.rb +95 -0
  603. data/lib/relaton/sdo/name.rb +26 -0
  604. data/lib/relaton/sdo/organization.rb +71 -0
  605. data/lib/relaton/sdo/store.rb +49 -0
  606. data/lib/relaton/sdo.rb +29 -0
  607. data/lib/relaton/un/bibdata.rb +8 -0
  608. data/lib/relaton/un/bibitem.rb +8 -0
  609. data/lib/relaton/un/bibliography.rb +48 -0
  610. data/lib/relaton/un/doctype.rb +10 -0
  611. data/lib/relaton/un/ext.rb +30 -0
  612. data/lib/relaton/un/hit.rb +56 -0
  613. data/lib/relaton/un/hit_collection.rb +61 -0
  614. data/lib/relaton/un/item.rb +11 -0
  615. data/lib/relaton/un/item_data.rb +6 -0
  616. data/lib/relaton/un/parser.rb +108 -0
  617. data/lib/relaton/un/processor.rb +38 -0
  618. data/lib/relaton/un/session.rb +25 -0
  619. data/lib/relaton/un/token_generator.rb +105 -0
  620. data/lib/relaton/un/util.rb +8 -0
  621. data/lib/relaton/un/wasm/decoder.rb +434 -0
  622. data/lib/relaton/un/wasm/instance.rb +101 -0
  623. data/lib/relaton/un/wasm/interpreter.rb +613 -0
  624. data/lib/relaton/un/wasm/memory.rb +112 -0
  625. data/lib/relaton/un/wasm/module.rb +47 -0
  626. data/lib/relaton/un/wasm.rb +17 -0
  627. data/lib/relaton/un/wasm_v_bg.wasm +0 -0
  628. data/lib/relaton/un.rb +28 -0
  629. data/lib/relaton/version.rb +1 -1
  630. data/lib/relaton/w3c/bibdata.rb +8 -0
  631. data/lib/relaton/w3c/bibitem.rb +8 -0
  632. data/lib/relaton/w3c/bibliography.rb +174 -0
  633. data/lib/relaton/w3c/data_fetcher.rb +487 -0
  634. data/lib/relaton/w3c/data_parser.rb +379 -0
  635. data/lib/relaton/w3c/docidentifier.rb +48 -0
  636. data/lib/relaton/w3c/doctype.rb +7 -0
  637. data/lib/relaton/w3c/ext.rb +11 -0
  638. data/lib/relaton/w3c/governor.rb +32 -0
  639. data/lib/relaton/w3c/item.rb +15 -0
  640. data/lib/relaton/w3c/item_data.rb +6 -0
  641. data/lib/relaton/w3c/processor.rb +49 -0
  642. data/lib/relaton/w3c/pubid.rb +85 -0
  643. data/lib/relaton/w3c/safe_realize.rb +148 -0
  644. data/lib/relaton/w3c/util.rb +8 -0
  645. data/lib/relaton/w3c.rb +36 -0
  646. data/lib/relaton/xsf/bibdata.rb +8 -0
  647. data/lib/relaton/xsf/bibitem.rb +8 -0
  648. data/lib/relaton/xsf/bibliography.rb +84 -0
  649. data/lib/relaton/xsf/data_fetcher.rb +106 -0
  650. data/lib/relaton/xsf/docidentifier.rb +46 -0
  651. data/lib/relaton/xsf/hit.rb +17 -0
  652. data/lib/relaton/xsf/hit_collection.rb +52 -0
  653. data/lib/relaton/xsf/item.rb +13 -0
  654. data/lib/relaton/xsf/item_data.rb +6 -0
  655. data/lib/relaton/xsf/processor.rb +51 -0
  656. data/lib/relaton/xsf/util.rb +9 -0
  657. data/lib/relaton/xsf.rb +34 -0
  658. data/lib/relaton-core.rb +1 -0
  659. data/lib/relaton.rb +85 -0
  660. metadata +775 -161
  661. data/.github/workflows/rake.yml +0 -14
  662. data/.github/workflows/release.yml +0 -24
  663. data/.github/workflows/rubocop.yml +0 -48
  664. data/.gitignore +0 -12
  665. data/.rspec +0 -3
  666. data/CLAUDE.md +0 -78
  667. data/Gemfile +0 -29
  668. data/Rakefile +0 -6
  669. data/docs/CHANGELOG.adoc +0 -16
  670. data/docs/VERSIONING_POLICY.adoc +0 -38
  671. data/docs/navigation.adoc +0 -6
  672. data/lib/relaton/db/version.rb +0 -5
  673. data/relaton.gemspec +0 -72
  674. data/spec/relaton/config_spec.rb +0 -10
  675. data/spec/relaton/db_cache_spec.rb +0 -51
  676. data/spec/relaton/db_spec.rb +0 -567
  677. data/spec/relaton/registry_spec.rb +0 -178
  678. data/spec/relaton/util_spec.rb +0 -3
  679. data/spec/relaton_meta_spec.rb +0 -25
  680. data/spec/relaton_spec.rb +0 -800
  681. data/spec/spec_helper.rb +0 -45
  682. data/spec/support/gb_t_20223_2006.xml +0 -33
  683. data/spec/support/iso_111111119115_1.xml +0 -0
  684. data/spec/support/iso_19115_1.xml +0 -115
  685. data/spec/support/iso_19115_2.xml +0 -95
  686. data/spec/support/rfc_8341.xml +0 -46
  687. data/spec/vcr_cassetes/api_relaton_org.yml +0 -121
  688. data/spec/vcr_cassetes/api_relaton_org_unavailable.yml +0 -140
  689. data/spec/vcr_cassetes/cc_dir_10005_2019.yml +0 -202
  690. data/spec/vcr_cassetes/cipm_meeting_43.yml +0 -1505
  691. data/spec/vcr_cassetes/gb_t_20223_2006.yml +0 -729
  692. data/spec/vcr_cassetes/iso_111111119115_1.yml +0 -12951
  693. data/spec/vcr_cassetes/iso_19115_1.yml +0 -17334
  694. data/spec/vcr_cassetes/iso_19115_1_2.yml +0 -322
  695. data/spec/vcr_cassetes/iso_19115_1_std.yml +0 -17334
  696. data/spec/vcr_cassetes/iso_19115_all_parts.yml +0 -185
  697. data/spec/vcr_cassetes/iso_19133_2005.yml +0 -144
  698. data/spec/vcr_cassetes/iso_combined_applied.yml +0 -318
  699. data/spec/vcr_cassetes/iso_combined_included.yml +0 -318
  700. data/spec/vcr_cassetes/ogc_19_025r1.yml +0 -374
  701. data/spec/vcr_cassetes/omg_ami4ccm_1_0.yml +0 -317
  702. data/spec/vcr_cassetes/rfc_8341.yml +0 -1278
@@ -0,0 +1,550 @@
1
+ require "net/http"
2
+ require "json"
3
+ require "uri"
4
+ require "mechanize"
5
+ require_relative "../itu"
6
+ require_relative "data_parser_t"
7
+ require_relative "family_cache"
8
+
9
+ module Relaton
10
+ module Itu
11
+ class DataFetcher < Core::DataFetcher
12
+ # ITU-T recommendation index (issue relaton-itu#80). main_edition_flag=0
13
+ # returns one row per edition, including supplements; a single request
14
+ # enumerates the whole ITU-T corpus.
15
+ SEARCH_RECS_URL = "https://www.itu.int/mws/api/recommendations/searchRecs".freeze
16
+ # A browser User-Agent — www.itu.int sits behind an F5 WAF that rejects
17
+ # non-browser clients (it is what killed the old RunSearch endpoint), so
18
+ # Net::HTTP's default "Ruby" UA must not be sent.
19
+ USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " \
20
+ "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15".freeze
21
+ DEFAULT_CONCURRENCY = 8
22
+
23
+ # ITU-R crawl worker threads. Duplicated from DataCrawlerR::DEFAULT_CONCURRENCY
24
+ # rather than read from it, so this knob does not drag the whole ITU-R
25
+ # harvester into an ITU-T-only crawl — #fetch_publications requires that
26
+ # file lazily and deliberately.
27
+ DEFAULT_R_CONCURRENCY = 4
28
+
29
+
30
+ # Number of ITU-T enrichment worker threads. Each record costs ~4
31
+ # www.itu.int round-trips (~3.7 s wall clock), so a ~16k-record corpus is
32
+ # ~17 h single-threaded — past the 6 h GitHub Actions job cap. The work is
33
+ # pure I/O wait, so a small pool is close to linear. Tunable via env var so
34
+ # a run can dial it down when the F5 WAF in front of www.itu.int starts
35
+ # throttling (or up to 1 to reproduce the serial order). Never below 1.
36
+ def self.concurrency
37
+ [(ENV["RELATON_ITU_CONCURRENCY"] || DEFAULT_CONCURRENCY).to_i, 1].max
38
+ end
39
+
40
+ # Number of ITU-R crawl worker threads. Its own knob, not ITU-T's, because
41
+ # the two halves have different bottlenecks: ITU-T is latency-bound, ITU-R
42
+ # is *pacer*-bound. Throughput there is min(1/delay, concurrency/latency),
43
+ # so at the 1 s default and ITU's ~1.1 s latency two workers already
44
+ # saturate the politeness contract; the default of 4 is headroom for a slow
45
+ # page, not extra load. `RELATON_ITU_R_CONCURRENCY=1` restores a strictly
46
+ # serial crawl.
47
+ def self.r_concurrency
48
+ [(ENV["RELATON_ITU_R_CONCURRENCY"] || DEFAULT_R_CONCURRENCY).to_i, 1].max
49
+ end
50
+
51
+ # How RELATON_ITU_DELAY is spent.
52
+ #
53
+ # slot (default) it is the minimum gap between request *starts*, shared
54
+ # by the pool, so ITU's own latency counts toward it instead of
55
+ # being added on top. www.itu.int sees at most 1/delay req/s no
56
+ # matter how many workers there are.
57
+ # fixed sleep it before every request, whatever else is happening. With
58
+ # RELATON_ITU_R_CONCURRENCY=1 this reproduces the pre-pool crawler
59
+ # exactly — one request per (delay + latency) — and is the rollback
60
+ # if ITU ever objects to the new profile.
61
+ def self.pace_mode
62
+ ENV["RELATON_ITU_PACE"].to_s == "fixed" ? :fixed : :slot
63
+ end
64
+
65
+ # The ITU-T cross-row cache for a crawl. `RELATON_ITU_CACHE_ENTRIES=0`
66
+ # turns it off entirely, restoring today's exact request pattern.
67
+ def self.family_cache
68
+ entries = ENV["RELATON_ITU_CACHE_ENTRIES"]
69
+ return NullCache.instance if entries.to_s.strip == "0"
70
+
71
+ size = entries.to_i
72
+ FamilyCache.new(max_entries: size.positive? ? size : FamilyCache::DEFAULT_MAX_ENTRIES)
73
+ end
74
+
75
+ def initialize(output, format)
76
+ super
77
+ # Guards the shared bookkeeping (@files, @seen, @errors, @unparseable_ids,
78
+ # @enrichment_failures) and the index mutation while workers fetch detail
79
+ # pages in parallel. The slow HTTP runs outside this lock; only the cheap
80
+ # build+write is held.
81
+ @mutex = Mutex.new
82
+ # output file => searchRecs position of the row that claimed it, so a
83
+ # duplicate filename resolves to the same last-by-position winner the
84
+ # serial crawl picked, regardless of worker completion order.
85
+ @seen = {}
86
+ @done = 0
87
+ @enrichment_failures = 0
88
+ end
89
+
90
+ def index
91
+ @index ||= Relaton::Index.find_or_create(
92
+ :itu, file: "#{INDEXFILE}.yaml", pubid_class: ::Pubid::Itu::Identifier
93
+ )
94
+ end
95
+
96
+ def log_error(msg)
97
+ Util.error msg
98
+ end
99
+
100
+ # @param source [String, nil] "itu-t" harvests ITU-T recommendations via
101
+ # the searchRecs index (issue #80); "itu-r" (and nil, the legacy default)
102
+ # harvests ITU-R Recommendations and Reports by crawling ITU's
103
+ # server-rendered pages (issue #75, see #fetch_publications).
104
+ def fetch(source = nil)
105
+ source == "itu-t" ? fetch_recommendations : fetch_publications
106
+ index.save
107
+ report_errors
108
+ end
109
+
110
+ # ITU-R harvester. ITU decommissioned the RunSearch bulk-enumeration
111
+ # endpoint this used to page through, so enumeration walks the `/pub` +
112
+ # `/rec` pages ITU still renders server-side (`DataCrawlerR`).
113
+ #
114
+ # Two modes, because a full crawl is ~7k requests and ~4 h:
115
+ #
116
+ # * **`:full`** (default) — every edition of every document, deep. Pair it
117
+ # with wiping `data/itu-r-*` first: ITU is then the sole author of the
118
+ # result, which is the point of crawling it at all.
119
+ # * **`:top_up`** — enumerate the same documents, but deep-fetch only the
120
+ # editions the dataset does not already hold. Costs the enumeration
121
+ # (~1 request per document) plus one per genuinely new edition, so a day
122
+ # with no new publications is a small fraction of a full run.
123
+ #
124
+ # Set with `RELATON_ITU_MODE=top_up`, so the two scheduled jobs differ by
125
+ # environment rather than by code.
126
+ #
127
+ # A top-up assumes the dataset it tops up was built by a previous `:full`
128
+ # run. Against the pre-#110 dataset it would add every Report a second time
129
+ # under its `Report …` name, so the data repo runs the rebuild first and
130
+ # enables the daily job after — an ordering that belongs to the schedule, not
131
+ # to this class. That one wipe retires the old names for good, which is why
132
+ # the harvester carries no migration step.
133
+ #
134
+ # Cost, measured: the BO series of both families is ~230 requests in 484 s
135
+ # at the 1 s default — ~2.1 s per request once ITU's own latency is added.
136
+ # Lower RELATON_ITU_DELAY at your peril: 0.4 s tripped the WAF mid-run
137
+ # (503 on /rec, a 302 to notfound.aspx on /pub), 1 s did not.
138
+ #
139
+ # @param mode [Symbol] :full or :top_up
140
+ # @return [Hash] the merge tally, `collisions` being a count
141
+ def fetch_publications(mode: self.class.mode)
142
+ require_relative "data_crawler_r"
143
+ require_relative "data_merge_r"
144
+ crawler = DataCrawlerR.new delay: self.class.delay,
145
+ concurrency: self.class.r_concurrency,
146
+ pace_mode: self.class.pace_mode
147
+ stats = Hash.new 0
148
+ DataCrawlerR::FAMILIES.each_key { |family| harvest_family crawler, family, stats, mode }
149
+ Util.info "ITU-R (#{mode}): #{stats.reject { |k, _| k == :collisions }.map { |k, v| "#{k} #{v}" }.join ', '}"
150
+ # Say it out loud: a run that was throttled but recovered otherwise looks
151
+ # identical to a clean one, and a run the governor abandoned would
152
+ # otherwise be visible only as a thin corpus.
153
+ if crawler.throttle_count.positive?
154
+ Util.warn "ITU-R: #{crawler.throttle_count} rate-limit responses from www.itu.int" \
155
+ "#{'; crawl ABANDONED as rate-limited' if crawler.abandoned?}"
156
+ end
157
+ stats
158
+ ensure
159
+ crawler&.shutdown
160
+ end
161
+
162
+ # One family, series by series, so a series that fails — ITU throttles by
163
+ # path family and a blocked series raises — costs that series rather than
164
+ # the whole run.
165
+ #
166
+ # @return [void]
167
+ def harvest_family(crawler, family, stats, mode = :full)
168
+ crawler.series(family).each do |series|
169
+ items = crawler.harvest series, family: family, errors: @errors,
170
+ skip: (method(:held?) if mode == :top_up)
171
+ # `collisions` comes back as the offending pairs; the run-level tally
172
+ # keeps their count and leaves the detail to DataMergeR's own errors.
173
+ DataMergeR.write_all(items, self).each { |k, v| stats[k] += v.is_a?(Array) ? v.size : v }
174
+ Util.info "ITU-R #{family}-#{series}: #{items.size} harvested"
175
+ rescue => e # rubocop:disable Style/RescueStandardError
176
+ log_error "ITU-R #{family}-#{series} skipped: #{e.message}"
177
+ end
178
+ rescue => e # rubocop:disable Style/RescueStandardError
179
+ log_error "ITU-R #{family} series index unavailable: #{e.message}"
180
+ end
181
+
182
+ # Does the dataset already hold this edition? Answered from the level-2 row
183
+ # alone — the id and displayed code are enough to derive the docid, and so
184
+ # the filename — which is what lets a top-up decide *before* paying for the
185
+ # edition page.
186
+ #
187
+ # @param row [Hash] a level-2 edition row, with :family
188
+ # @return [Boolean]
189
+ def held?(row)
190
+ docid = DataParserR.family_docid row
191
+ docid && File.exist?(output_file(docid))
192
+ end
193
+
194
+ # Minimum seconds between ITU-R request *starts*, enforced by one
195
+ # Core::Pacer shared by the whole pool — not a sleep before each request,
196
+ # which would add to ITU's own ~1.1 s latency instead of being absorbed by
197
+ # it. www.itu.int therefore sees at most 1/delay req/s however many workers
198
+ # run. Its WAF answers 503 on `/rec` and a 302 to notfound.aspx on `/pub`
199
+ # when pushed; 0.4 s tripped it mid-run, 1 s did not.
200
+ def self.delay
201
+ (ENV["RELATON_ITU_DELAY"] || 1.0).to_f
202
+ end
203
+
204
+ # @return [Symbol] :top_up or :full
205
+ def self.mode
206
+ ENV["RELATON_ITU_MODE"].to_s == "top_up" ? :top_up : :full
207
+ end
208
+
209
+ # ITU-T harvester: one searchRecs request enumerates every edition and
210
+ # supplement; each row is then enriched with getRecHdrDetail-sourced fields
211
+ # (abstract, ISO co-id, editorial-group contributors, status), so harvested
212
+ # records match the live runtime output. Enrichment is up to 4 requests per
213
+ # record — the bulk of the crawl's cost — so the rows are spread over a
214
+ # worker pool (see .concurrency) and progress is logged.
215
+ #
216
+ # Two things keep that cost down. The rows are enqueued **grouped by
217
+ # recommendation**, and the pool shares one FamilyCache, so the two
218
+ # endpoints that answer per *recommendation* rather than per *edition*
219
+ # (`getRecEditions` and the ~90 KB `rec.aspx` page) are fetched once per
220
+ # family instead of once per edition — H.264 alone was asking ITU the same
221
+ # question 21 times. And `mode: :top_up` skips a family the dataset already
222
+ # holds entirely, decided with no HTTP at all (see #held_t?).
223
+ #
224
+ # Each worker owns its own Mechanize agent: Mechanize is not thread-safe,
225
+ # and per-worker agents also keep one worker's cookie/history state out of
226
+ # another's. Rows carry their searchRecs position so #write_file can pick a
227
+ # deterministic winner for duplicate filenames.
228
+ def fetch_recommendations(mode: self.class.mode)
229
+ # `pos` is fixed HERE, against the unreordered searchRecs result, and
230
+ # keeps exactly its current meaning. Only the enqueue ORDER changes
231
+ # below, which #write_file's max-by-position rule is already immune to —
232
+ # it has to be, since worker completion order was never deterministic.
233
+ work = search_recs.each_with_index.to_a
234
+ work = top_up_rows(work) if mode == :top_up
235
+ work = self.class.group_by_family(work)
236
+ cache = self.class.family_cache
237
+
238
+ n = self.class.concurrency
239
+ agents = Array.new(n) { rec_agent }
240
+ queue = SizedQueue.new(n * 2)
241
+ workers = agents.map { |agent| spawn_rec_worker(queue, agent, work.size, cache) }
242
+ work.each { |pair| queue << pair }
243
+ n.times { queue << nil } # poison pills
244
+ workers.each(&:join)
245
+ Util.info "ITU-T: detail cache #{cache.stats[:hits]} hits / #{cache.stats[:misses]} misses"
246
+ report_enrichment_failures work.size
247
+ ensure
248
+ agents&.each(&:shutdown)
249
+ end
250
+
251
+ # Does the dataset already hold this ITU-T edition? Answered from the
252
+ # searchRecs row alone, which is what lets a top-up decide *before* paying
253
+ # the four detail requests — the ITU-T counterpart of #held?.
254
+ #
255
+ # HTTP-free, and provably so: DataParserT.fetch_docid reads only
256
+ # `row["rec_name"]`, normalizes it with one #sub, and builds a
257
+ # Docidentifier. It takes no agent and touches none of the enrichment path.
258
+ #
259
+ # The filename it derives is the one #write_file would produce: that method
260
+ # uses `docidentifier.find(&:primary).content`, and DataParserT.parse puts
261
+ # the ITU docid first (`docid + enr[:iso]`) — which matters, because an
262
+ # enriched record's ISO co-identifier is *also* `primary: true`.
263
+ #
264
+ # @param row [Hash] a searchRecs row
265
+ # @return [Boolean]
266
+ def held_t?(row)
267
+ did = DataParserT.fetch_docid(row).first
268
+ did && File.exist?(output_file(did.content))
269
+ end
270
+
271
+ # Narrow a top-up to the rows worth re-harvesting.
272
+ #
273
+ # The unit is the **family, not the row**. A new edition changes its
274
+ # siblings' `hasEdition` relations, so topping up only the new row would
275
+ # leave the rest of the family on disk pointing at an incomplete edition
276
+ # list — a staleness the full run's wipe-and-rebuild never had. If any row
277
+ # of a family is new, every row of that family is re-harvested; the extra
278
+ # cost is one getRecHdrDetail per sibling, since the family-invariant
279
+ # fetches are shared by the cache.
280
+ #
281
+ # @param work [Array<Array(Hash, Integer)>] [row, searchRecs position]
282
+ # @return [Array<Array(Hash, Integer)>]
283
+ def top_up_rows(work)
284
+ kept = work.group_by { |row, _pos| self.class.family_key(row) }
285
+ .select { |_key, pairs| pairs.any? { |row, _pos| !held_t?(row) } }
286
+ .values.flatten(1)
287
+ Util.info "ITU-T (top_up): #{kept.size}/#{work.size} rows in families with new editions"
288
+ kept
289
+ end
290
+
291
+ # The recommendation a row is an edition of.
292
+ #
293
+ # Used **only** to decide enqueue order and top-up grouping, never as a
294
+ # cache key: cache correctness is keyed on idrecs ITU's own getRecEditions
295
+ # response supplied (see RecommendationFields#family_id), because two
296
+ # recommendations whose names normalize alike would otherwise silently
297
+ # share metadata. A wrong guess here costs a cache miss, nothing more.
298
+ #
299
+ # "H.264 (V16) (06/2026)" -> "H.264"; "H Suppl. 1 (05/1999)" -> "H Suppl. 1"
300
+ #
301
+ # @param row [Hash]
302
+ # @return [String]
303
+ def self.family_key(row)
304
+ # Cut at a bare "(" with no leading \s* — an unanchored \s* here is
305
+ # polynomial-time backtracking (CodeQL rb/polynomial-redos).
306
+ DataParserT.normalize_rec_name(row["rec_name"]).sub(/\(.*\z/m, "").strip
307
+ end
308
+
309
+ # Make each recommendation's rows contiguous, so its cache entry is still
310
+ # resident when its siblings are enriched. Every [row, pos] pair is carried
311
+ # through untouched. searchRecs already returns name-sorted rows, so on
312
+ # today's data this is close to a no-op — it is a guarantee, not a gain.
313
+ #
314
+ # @param work [Array<Array(Hash, Integer)>]
315
+ # @return [Array<Array(Hash, Integer)>]
316
+ def self.group_by_family(work)
317
+ work.group_by { |row, _pos| family_key(row) }.values.flatten(1)
318
+ end
319
+
320
+ # One pool worker: drains the queue with its own agent until the poison
321
+ # pill (nil). Per-row errors are logged and skipped, so one bad row never
322
+ # kills a worker and leaves the queue undrained.
323
+ def spawn_rec_worker(queue, agent, total, cache = NullCache.instance)
324
+ Thread.new do
325
+ while (item = queue.pop)
326
+ row, pos = item
327
+ begin
328
+ errors = Hash.new(true)
329
+ bib = DataParserT.parse(row, agent, errors, cache: cache)
330
+ if bib
331
+ # DataParserT#enrichment adds the ITU publisher unconditionally
332
+ # when it succeeds, so an empty contributor list is exactly the
333
+ # set of records whose detail fetch failed and degraded to the
334
+ # thin searchRecs shape.
335
+ count_enrichment_failure if bib.contributor.empty?
336
+ write_file bib, pos
337
+ end
338
+ merge_errors errors
339
+ rescue => e # rubocop:disable Style/RescueStandardError
340
+ Util.error "#{e.message}\n#{e.backtrace}"
341
+ end
342
+ progress total
343
+ end
344
+ end
345
+ end
346
+
347
+ # Each parse gets its own errors hash (DataParserT is per-call state, but
348
+ # the flags are AND-folded across all rows), merged back under the lock.
349
+ def merge_errors(errors)
350
+ @mutex.synchronize { errors.each { |k, v| @errors[k] &&= v } }
351
+ end
352
+
353
+ def progress(total)
354
+ done = @mutex.synchronize { @done += 1 }
355
+ Util.info "ITU-T: enriched #{done}/#{total}" if (done % 500).zero?
356
+ end
357
+
358
+ def count_enrichment_failure
359
+ @mutex.synchronize { @enrichment_failures += 1 }
360
+ end
361
+
362
+ # Enrichment is best-effort: a failed detail fetch degrades that record to
363
+ # the thin searchRecs shape instead of losing it, and the crawl runs to
364
+ # completion either way. That is right for one flaky record and dangerous
365
+ # in bulk — a WAF block would quietly republish a metadata-thin corpus — so
366
+ # say how many records lost their enrichment.
367
+ def report_enrichment_failures(total)
368
+ return if @enrichment_failures.zero?
369
+
370
+ Util.warn "ITU-T: enrichment failed for #{@enrichment_failures}/#{total} records"
371
+ end
372
+
373
+ # Mechanize agent for per-record enrichment. A browser User-Agent is
374
+ # required — www.itu.int sits behind the F5 WAF that rejects non-browser
375
+ # clients (mirrors HitCollection#agent).
376
+ #
377
+ # max_history: Mechanize retains every response it fetches, unbounded by
378
+ # default. Enrichment issues ~4 requests per record and one of them is
379
+ # the ~90 KB rec.aspx page that #fetch_workgroup parses into a DOM; over
380
+ # a 16k-record corpus (times one agent per worker) that history grows
381
+ # into the gigabytes.
382
+ # timeouts: Mechanize sets none, so a single stalled www.itu.int socket
383
+ # would park a worker indefinitely with no error and no progress. A
384
+ # timeout raises Net::{Open,Read}Timeout < Timeout::Error, which
385
+ # RecommendationFields#request_document already turns into a
386
+ # Relaton::RequestError and DataParserT#enrichment rescues — so it costs
387
+ # one thin record rather than the run.
388
+ def rec_agent
389
+ Mechanize.new.tap do |a|
390
+ a.user_agent_alias = "Mac Safari"
391
+ a.max_history = 1
392
+ a.open_timeout = 15
393
+ a.read_timeout = 60
394
+ end
395
+ end
396
+
397
+ # @param bib [Relaton::Itu::ItemData]
398
+ # @param pos [Integer, nil] source position of the row this came from, used
399
+ # only to break filename collisions deterministically (nil for the
400
+ # single-threaded ITU-R path)
401
+ def write_file(bib, pos = nil) # rubocop:disable Metrics/AbcSize
402
+ id = bib.docidentifier.find(&:primary).content
403
+ file = output_file(id)
404
+ content = serialize(bib) # outside the lock: it is the expensive part
405
+ @mutex.synchronize do
406
+ if @files.include? file
407
+ Util.warn "File #{file} exists."
408
+ # Distinct docids can sanitize to one filename, in which case the
409
+ # serial crawl left the last row's version on disk. Keep that
410
+ # outcome whatever order the workers finish in.
411
+ return if pos && @seen[file] && @seen[file] > pos
412
+ else
413
+ @files << file
414
+ end
415
+ @seen[file] = pos
416
+ index_primary(id, file)
417
+ File.write file, content, encoding: "UTF-8"
418
+ end
419
+ end
420
+
421
+ # Index records that are already on disk instead of harvesting them.
422
+ #
423
+ # ITU-R cannot be re-harvested — ITU decommissioned the bulk RunSearch
424
+ # enumeration, which is why #fetch now refuses the "itu-r" source
425
+ # (ITU_R_DISABLED, issue #75) — so the
426
+ # published ITU-R records are preserved and only re-indexed on each run of
427
+ # relaton-data-itu's crawler. Doing that here rather than in the data repo
428
+ # gives the pass the same pubid guard and the same unparseable-id reporting
429
+ # as the ITU-T harvest, instead of a copy of both living downstream. Drive
430
+ # it and #fetch off one instance so there is a single index, a single
431
+ # unparseable-id list and a single error report:
432
+ #
433
+ # fetcher = DataFetcher.new("data", "yaml")
434
+ # fetcher.index_files "data/itu-r-*.yaml"
435
+ # fetcher.fetch "itu-t"
436
+ #
437
+ # @param glob [String] e.g. "data/itu-r-*.yaml"
438
+ # @return [Integer] number of files indexed
439
+ def index_files(glob)
440
+ indexed = 0
441
+ files = Dir[glob].sort
442
+ files.each do |file|
443
+ item = Item.from_yaml(File.read(file, encoding: "UTF-8"))
444
+ id = (item.docidentifier.find(&:primary) || item.docidentifier.first)&.content
445
+ if id
446
+ indexed += 1 if index_primary(id, file)
447
+ else
448
+ unparseable_ids << ["(no docidentifier)", file]
449
+ end
450
+ rescue => e # rubocop:disable Style/RescueStandardError
451
+ Util.error "Failed to index #{file}: #{e.message}"
452
+ end
453
+ Util.info "ITU: indexed #{indexed}/#{files.size} existing records from #{glob}"
454
+ indexed
455
+ end
456
+
457
+ # Index the id's parsed pubid. If it can't be parsed/round-tripped, record
458
+ # it so #report_errors raises a tracked GitHub issue; the data file is
459
+ # still written, so the document is not lost — only unindexed until its id
460
+ # parses (mirrors Relaton::Iso::DataFetcher#index_primary).
461
+ #
462
+ # @param id [String] primary docidentifier content, e.g. "ITU-R BO.600-1"
463
+ # @param file [String] file name of the document
464
+ # @return [Boolean] whether the id was indexed
465
+ def index_primary(id, file)
466
+ if (pid = pubid(id))
467
+ index.add_or_update pid, file
468
+ true
469
+ else
470
+ unparseable_ids << [id, file]
471
+ false
472
+ end
473
+ end
474
+
475
+ def unparseable_ids
476
+ @unparseable_ids ||= []
477
+ end
478
+
479
+ # Surface unparseable ids through the shared error machinery (the
480
+ # "Error fetching documents" GitHub issue in CI). The gh_issue channel is
481
+ # registered inside #gh_issue, so log at :error after it is set up and
482
+ # before super creates the issue (mirrors
483
+ # Relaton::Iso::DataFetcher#report_errors).
484
+ def report_errors
485
+ gh_issue
486
+ unparseable_ids.each do |content, file|
487
+ log_error "Unparseable primary id `#{content}` was not indexed (#{file})"
488
+ end
489
+ super
490
+ end
491
+
492
+ # Parse an ITU docid into a Pubid::Itu identifier, or nil when it can't be
493
+ # parsed or does not round-trip losslessly. Storing the pubid object (not
494
+ # its hash) lets Relaton::Index sort the index and serialize each id to its
495
+ # `_type: pubid:itu:*` hash on save. The round-trip check mirrors the index
496
+ # loader's own Index::FileIO#id_supported? acceptance test, so an id that
497
+ # would make Relaton::Index reject the whole index is dropped at write time.
498
+ # The pinned pubid models recommendations, handbooks and questions, so the
499
+ # guard only skips the few residual forms it can't parse (e.g. "ITU-R RR").
500
+ #
501
+ # @param id [String]
502
+ # @return [::Pubid::Itu::Identifier, nil]
503
+ def pubid(id)
504
+ pid = ::Pubid::Itu.parse id
505
+ hash = pid.to_hash
506
+ return nil unless ::Pubid::Itu::Identifier.from_hash(hash).to_hash == hash
507
+
508
+ pid
509
+ rescue StandardError
510
+ nil
511
+ end
512
+
513
+ def to_yaml(bib)
514
+ bib.to_yaml
515
+ end
516
+
517
+ def to_xml(bib)
518
+ bib.to_xml bibdata: true
519
+ end
520
+
521
+ def to_bibxml(bib)
522
+ bib.to_rfcxml
523
+ end
524
+
525
+ private
526
+
527
+ # Fetch the full ITU-T recommendation index in one request.
528
+ # @return [Array<Hash>] rows from the searchRecs "Data" array
529
+ def search_recs
530
+ uri = URI(SEARCH_RECS_URL)
531
+ uri.query = URI.encode_www_form(
532
+ series: -1, type_of_text: -1, sg: -1, main_edition_flag: 0,
533
+ rows: 100_000, page: 1, status: "Z", sort_order: "asc"
534
+ )
535
+
536
+ http = Net::HTTP.new(uri.host, uri.port)
537
+ http.use_ssl = true
538
+
539
+ request = Net::HTTP::Get.new(uri)
540
+ request["Accept"] = "application/json"
541
+ request["Referer"] = "https://www.itu.int/myworkspace/"
542
+ request["User-Agent"] = USER_AGENT
543
+
544
+ response = http.request(request)
545
+ json = JSON.parse(response.body)
546
+ json["Data"] || []
547
+ end
548
+ end
549
+ end
550
+ end