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,540 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+ require "fileutils"
5
+ require "ferrum"
6
+ require "nokogiri"
7
+ require "pubid"
8
+ require "relaton/index"
9
+ require "relaton/bib"
10
+ require "relaton/core/data_fetcher"
11
+ require_relative "../cie"
12
+
13
+ module Relaton
14
+ module Cie
15
+ # Thin Ferrum-backed HTTP agent that mimics the Mechanize#get interface
16
+ # used elsewhere in DataFetcher. Drives headless Chrome with stealth
17
+ # tweaks so the CIE catalogue host (Cloudflare-protected accuristech)
18
+ # serves real HTML instead of a "Just a moment..." challenge.
19
+ class BrowserAgent
20
+ UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " \
21
+ "(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
22
+ CHALLENGE_MARKERS = ["Just a moment", "challenge-platform"].freeze
23
+ MAX_CHALLENGE_WAIT = 30
24
+
25
+ # Raised when the Cloudflare challenge ("Just a moment…") is still showing
26
+ # after MAX_CHALLENGE_WAIT seconds. It's a retriable signal (see
27
+ # DataFetcher::RETRIABLE_ERRORS): rather than parse the challenge HTML as a
28
+ # document, the caller (#time_req) backs the worker off and retries.
29
+ class ChallengeError < StandardError; end
30
+
31
+ def initialize
32
+ @browser = Ferrum::Browser.new(
33
+ headless: true,
34
+ timeout: 90,
35
+ process_timeout: 90,
36
+ window_size: [1366, 768],
37
+ browser_options: {
38
+ "disable-blink-features" => "AutomationControlled",
39
+ "disable-quic" => nil,
40
+ "no-sandbox" => nil
41
+ }
42
+ )
43
+ @browser.headers.set(
44
+ "Accept-Language" => "en-US,en;q=0.9",
45
+ "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9," \
46
+ "image/webp,*/*;q=0.8",
47
+ "User-Agent" => UA
48
+ )
49
+ # Pre-mask the most common headless-Chrome tells before any page JS runs.
50
+ @browser.evaluate_on_new_document(<<~JS)
51
+ Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
52
+ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
53
+ Object.defineProperty(navigator, 'plugins', { get: () => [1,2,3,4,5] });
54
+ window.chrome = { runtime: {} };
55
+ JS
56
+ end
57
+
58
+ def get(url)
59
+ @browser.go_to(url)
60
+ wait_for_challenge
61
+ Nokogiri::HTML(@browser.body)
62
+ end
63
+
64
+ def quit
65
+ @browser&.quit
66
+ ensure
67
+ @browser = nil
68
+ end
69
+
70
+ private
71
+
72
+ def wait_for_challenge
73
+ MAX_CHALLENGE_WAIT.times do
74
+ return unless CHALLENGE_MARKERS.any? { |m| @browser.body.include?(m) }
75
+
76
+ sleep 1
77
+ end
78
+ raise ChallengeError, "Cloudflare challenge did not clear after #{MAX_CHALLENGE_WAIT}s"
79
+ end
80
+ end
81
+
82
+ class DataFetcher < Relaton::Core::DataFetcher
83
+ URL = "https://www.techstreet.com/cie/searches/31156444?page=1&per_page=100"
84
+
85
+ # Default worker-pool size for the parallel detail-fetch phase. Kept small
86
+ # so N stealth Chrome instances don't burst requests fast enough to trip
87
+ # Cloudflare; tune via RELATON_CIE_CONCURRENCY (see .concurrency).
88
+ DEFAULT_CONCURRENCY = 5
89
+
90
+ # Per-worker pacing bounds (seconds). Each worker starts at BASE_GAP and
91
+ # doubles its own gap up to MAX_GAP on trouble (see Pacing / #time_req).
92
+ BASE_GAP = 1
93
+ MAX_GAP = 32
94
+
95
+ # Number of detail-fetch worker threads. Tunable via env var so
96
+ # relaton-data-cie's crawler workflow can dial it up for speed or down to
97
+ # lighten load on techstreet without a code change. Never below 1.
98
+ def self.concurrency
99
+ [(ENV["RELATON_CIE_CONCURRENCY"] || DEFAULT_CONCURRENCY).to_i, 1].max
100
+ end
101
+
102
+ def initialize(output, format)
103
+ super
104
+ # Guards the shared bookkeeping (@files, @errors, @seen) and the index
105
+ # mutation while workers fetch detail pages in parallel. The slow
106
+ # agent.get runs outside this lock; only the cheap build+write is held.
107
+ @mutex = Mutex.new
108
+ # output file => catalogue position of the hit that last claimed it, so a
109
+ # duplicate primary id resolves to the same last-by-position winner the
110
+ # serial crawl would pick, regardless of worker completion order.
111
+ @seen = {}
112
+ end
113
+
114
+ # Factory for a fresh stealth browser agent. The pool builds one per worker
115
+ # (each its own Chrome, each with the full UA/header/`navigator` masking);
116
+ # #agent memoizes a single one for the serial listing phase. Specs stub
117
+ # this so one double can back every worker.
118
+ def build_agent
119
+ BrowserAgent.new
120
+ end
121
+
122
+ def agent
123
+ @agent ||= build_agent
124
+ end
125
+
126
+ # Per-worker adaptive request pacing. Replaces the old single global 4 s
127
+ # gap: each worker owns one Pacing, starts at BASE_GAP, and doubles its gap
128
+ # (capped at MAX_GAP) whenever a request hits trouble (a Cloudflare
129
+ # challenge or a Ferrum/socket error), so a struggling worker slows itself
130
+ # without throttling the healthy ones.
131
+ class Pacing
132
+ def initialize(base: BASE_GAP, max: MAX_GAP)
133
+ @base = base
134
+ @max = max
135
+ @gap = base
136
+ @last = nil
137
+ end
138
+
139
+ attr_reader :gap
140
+
141
+ # Sleep off whatever remains of this worker's gap since its last request,
142
+ # then stamp the clock (in ensure, mirroring the old #time_req).
143
+ def throttle
144
+ sleep [@gap - (Time.now - @last), 0].max if @last
145
+ ensure
146
+ @last = Time.now
147
+ end
148
+
149
+ # Exponential backoff, capped at @max. Called before a retry on trouble.
150
+ def backoff
151
+ @gap = [@gap * 2, @max].min
152
+ end
153
+ end
154
+
155
+ def index
156
+ @index ||= Index.find_or_create :cie, file: "#{INDEXFILE}.yaml",
157
+ pubid_class: ::Pubid::Cie::Identifier
158
+ end
159
+
160
+ # Parse a docidentifier string into a Pubid::Cie::Identifier, or nil if
161
+ # pubid can't parse it or it won't survive the structured index — so a
162
+ # single bad id never aborts the crawl or corrupts index-v2. The caller
163
+ # (#write_file) records the skip.
164
+ #
165
+ # The guard mirrors the read side's acceptance test
166
+ # (Index::FileIO#id_supported?, `from_hash(to_hash) == to_hash`): the
167
+ # loader raises InvalidIndexError and rejects the WHOLE index on the first
168
+ # id that doesn't round-trip, so an id that serializes but can't be
169
+ # deserialized back must be dropped here rather than poison every lookup.
170
+ def pubid(id)
171
+ pid = ::Pubid::Cie.parse id
172
+ hash = pid.to_hash
173
+ return nil unless ::Pubid::Cie::Identifier.from_hash(hash).to_hash == hash
174
+
175
+ pid
176
+ rescue StandardError
177
+ nil
178
+ end
179
+
180
+ def log_error(msg)
181
+ Util.error msg
182
+ end
183
+
184
+ # @param hit [Nokogiri::HTML::Document]
185
+ # @param doc [Mechanize::Page]
186
+ # @return [Array<Relaton::Bib::Docidentifier>]
187
+ def fetch_docid(hit, doc)
188
+ code, code2 = parse_code hit, doc
189
+ docid = []
190
+ if code && !code.strip.empty?
191
+ docid << Bib::Docidentifier.new(type: "CIE", content: code, primary: true)
192
+ @errors[:docid_1] &&= false
193
+ else
194
+ @errors[:docid_1] &&= true
195
+ end
196
+ if code2 && !code2.strip.empty?
197
+ type2 = code2.match(/\w+/).to_s
198
+ docid << Relaton::Bib::Docidentifier.new(type: type2, content: code2.strip)
199
+ @errors[:docid_2] &&= false
200
+ else
201
+ @errors[:docid_2] &&= true
202
+ end
203
+ isbn = doc.at('//h3[contains(.,"ISBN")]/following-sibling::span')&.text
204
+ if isbn && !isbn.strip.empty?
205
+ docid << Bib::Docidentifier.new(type: "ISBN", content: isbn)
206
+ @errors[:docid_isbn] &&= false
207
+ else
208
+ @errors[:docid_isbn] &&= true
209
+ end
210
+ docid
211
+ end
212
+
213
+ def parse_code(hit, doc = nil)
214
+ code = hit.at("h3/a").text.strip.squeeze(" ").sub(/\u25b9/, "").gsub(" / ", "/")
215
+ c2idx = %r{(?:\(|/)(?<c2>(?:ISO|IEC)\s[^()]+)} =~ code
216
+ code = code[0...c2idx].strip if c2idx
217
+ [primary_code(code, doc), c2]
218
+ end
219
+
220
+ def primary_code(code, doc = nil)
221
+ /^(?<code1>[^(]+)(?:\((?<code2>[a-zA-Z]+\d+,(?:\sPages)?[^)]+))?/ =~ code
222
+ if code1&.match?(/^CIE/)
223
+ parse_cie_code code1, code2, doc
224
+ elsif (pcode = doc&.at('//h3[.="Product Code(s):"]/following-sibling::span'))
225
+ "CIE #{pcode.text.strip.match(/[^,]+/)}"
226
+ else
227
+ num = code.match(/(?<=\()\w{2}\d+,.+(?=\))/).to_s.gsub(/,(?=\s)/, "").gsub(/,(?=\S)/, " ")
228
+ "CIE #{num}"
229
+ end
230
+ end
231
+
232
+ def parse_cie_code(code1, code2, doc = nil) # rubocop:disable Metrics/CyclomaticComplexity
233
+ code = code1.size > 25 && code2 ? "CIE #{code2.sub(/,(\sPages)?/, '')}" : code1
234
+ add = doc&.at("//hgroup/h2")&.text&.match(/(Add)endum\s(\d+)$/)
235
+ return code unless add
236
+
237
+ "#{code} #{add[1]} #{add[2]}"
238
+ end
239
+
240
+ def fetch_docnumber(hit)
241
+ parse_code(hit).first.sub(/^CIE\s(?:ISO\s)?/, "")
242
+ end
243
+
244
+ # @param doc [Mechanize::Page]
245
+ # @return [Array<Relaton::Bib::Title>]
246
+ def fetch_title(doc)
247
+ t = doc.at("//hgroup/h2/text()", "//hgroup/h1/text()")
248
+ unless t && !t.text.strip.empty?
249
+ @errors[:title] &&= true
250
+ return []
251
+ end
252
+
253
+ result = Bib::Title.from_string t.text.strip
254
+ @errors[:title] &&= result.empty?
255
+ result
256
+ end
257
+
258
+ # @param doc [Mechanize::Page]
259
+ # @return [Array<Relaton::Bib::Date>]
260
+ def fetch_date(doc)
261
+ result = doc.xpath("//h3[.='Published:']/following-sibling::span").map do |d|
262
+ pd = d.text.strip
263
+ on = pd.match?(/^\d{4}(?:[^-]|$)/) ? pd : Date.strptime(pd, "%m/%d/%Y").strftime("%Y-%m-%d")
264
+ Bib::Date.new(type: "published", at: on)
265
+ end
266
+ @errors[:date] &&= result.empty?
267
+ result
268
+ end
269
+
270
+ # @param doc [Mechanize::Page]
271
+ # @return [String]
272
+ def fetch_edition(doc)
273
+ ed = doc.at("//h3[.='Edition:']/following-sibling::span")
274
+ @errors[:edition] &&= true
275
+ return unless ed
276
+
277
+ content = ed.text.slice(/^\d+(?=(st|nd|rd|th))/)
278
+ if content
279
+ @errors[:edition] = false
280
+ Bib::Edition.new(content: content)
281
+ end
282
+ end
283
+
284
+ # @param doc [Mechanize::Page]
285
+ # @return [Array<Relaton::Cie::Relation>]
286
+ def fetch_relation(doc) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
287
+ rels = doc.xpath('//section[@class="history"]/ol/li[not(contains(@class,"selected-product"))]').map do |rel|
288
+ ref = rel.at("a")
289
+ url = "https://www.techstreet.com#{ref[:href]}"
290
+ title = Bib::Title.from_string ref.at('p/span[@class="title"]').text
291
+ did = ref.at("h3").text
292
+ docid = [Bib::Docidentifier.new(type: "CIE", content: did, primary: true)]
293
+ on = ref.at("p/time")
294
+ date = [Bib::Date.new(type: "published", at: on[:datetime])]
295
+ source = [Bib::Uri.new(type: "src", content: url)]
296
+ bibitem = ItemData.new docidentifier: docid, title: title, source: source, date: date
297
+ type = ref.at('//li/i[contains(@class,"historical")]') ? "updates" : "updatedBy"
298
+ Bib::Relation.new(type: type, bibitem: bibitem)
299
+ end
300
+ @errors[:relation] &&= rels.empty?
301
+ rels
302
+ end
303
+
304
+ # @param url [String]
305
+ # @return [Array<Relaton::Bib::Uri>]
306
+ def fetch_source(url)
307
+ @errors[:source] &&= url.nil? || url.empty?
308
+ return [] if url.nil? || url.empty?
309
+
310
+ [Bib::Uri.new(type: "src", content: url)]
311
+ end
312
+
313
+ # @param doc [Mechanize::Page]
314
+ # @return [Array<Relaton::Bib::LocalizedMarkedUpString>]
315
+ def fetch_abstract(doc)
316
+ content = doc.at('//div[contains(@class,"description")]')&.text&.strip
317
+ if content.nil? || content.empty?
318
+ @errors[:abstract] &&= true
319
+ return []
320
+ end
321
+
322
+ result = [Bib::Abstract.new(content: content, language: "en", script: "Latn")]
323
+ @errors[:abstract] &&= result.empty?
324
+ result
325
+ end
326
+
327
+ # @param doc [Mechanize::Page]
328
+ # @return [Array<Relaton::Bib::Contributor>]
329
+ def fetch_contributor(doc) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity
330
+ authors = doc.xpath('//hgroup/p[not(@class="pub_date")]').text.gsub "\"", ""
331
+ contribs = []
332
+ until authors.empty?
333
+ /^(?<sname1>\S+(?:\sder?\s)?[^\s,]+)
334
+ (?:,?\s(?<sname2>[\w-]{2,})(?=,\s+\w\.))?
335
+ (?:,?\s(?<fname>W-T\.[\w-]{2,})(?!,\s+\w\.))?
336
+ (?:(?:\s?,\s?|\s)(?<init>(?:\w(?:\s?\.|\s|,|$)[\s-]?)+))?
337
+ (?:(?:[,;]\s*|\s+|\.|(?<=\s))(?:and\s)?)?/x =~ authors
338
+ raise StandardError, "Author name not found in \"#{authors}\"" unless $LAST_MATCH_INFO
339
+
340
+ authors.sub! $LAST_MATCH_INFO.to_s, ""
341
+ sname = [sname1, sname2].compact.join " "
342
+ surname = Bib::LocalizedString.new content: sname, language: "en", script: "Latn"
343
+ forename = []
344
+ forename << Bib::FullNameType::Forename.new(content: fname, language: "en", script: "Latn") if fname
345
+ (init&.strip || "").split(/(?:,|\.)(?:-|\s)?/).each do |int|
346
+ forename << Bib::FullNameType::Forename.new(content: "", initial: int.strip, language: "en", script: "Latn")
347
+ end
348
+ fullname = Bib::FullName.new surname: surname, forename: forename
349
+ person = Bib::Person.new name: fullname
350
+ role = Bib::Contributor::Role.new type: "author"
351
+ contribs << Bib::Contributor.new(person: person, role: [role])
352
+ @errors[:contributor_author] &&= contribs.empty?
353
+ end
354
+ org_name = Bib::TypedLocalizedString.new(content: "Commission Internationale de L'Eclairage")
355
+ abbrev = Bib::LocalizedString.new content: "CIE"
356
+ org_uri = Bib::Uri.new content: "cie.co.at"
357
+ org = Bib::Organization.new(name: [org_name], abbreviation: abbrev, uri: [org_uri])
358
+ org_role = Bib::Contributor::Role.new type: "publisher"
359
+ contribs << Bib::Contributor.new(organization: org, role: [org_role])
360
+ end
361
+
362
+ def fetch_ext
363
+ Ext.new(doctype: fetch_doctype, flavor: "cie")
364
+ end
365
+
366
+ def fetch_doctype
367
+ Bib::Doctype.new(content: "document")
368
+ end
369
+
370
+ # @param bib [RelatonCie::BibliographicItem]
371
+ # @param pos [Integer, nil] the hit's catalogue position, used only by the
372
+ # parallel pool to keep a deterministic last-by-position winner when two
373
+ # hits map to the same output file. nil (direct calls) keeps the
374
+ # historical warn-and-overwrite behavior.
375
+ def write_file(bib, pos = nil) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
376
+ id = bib.docidentifier[0].content
377
+ file = output_file id
378
+ # Index every distinct id (the serial crawl indexes each one, even two
379
+ # that resolve to the same file), before the content-dedup gate below.
380
+ pid = pubid id
381
+ if pid
382
+ index.add_or_update pid, file
383
+ else
384
+ Util.warn { "Unparseable id `#{id}` was not indexed (#{file})" }
385
+ end
386
+ # Only the last-by-position hit writes the shared file's content.
387
+ return if superseded? file, pos
388
+
389
+ if @files.include? file
390
+ Util.warn do
391
+ "File #{file} exists. Docid: #{bib.docidentifier[0].content}\n" \
392
+ "Link: #{bib.source.detect { |l| l.type == 'src' }.content}"
393
+ end
394
+ else @files << file
395
+ end
396
+ File.write file, serialize(bib), encoding: "UTF-8"
397
+ end
398
+
399
+ # True when another hit with a *later* catalogue position has already
400
+ # claimed `file`. The serial crawl is last-writer-wins, so that later hit
401
+ # is canonical and this (earlier or duplicate) content write must be
402
+ # dropped — making the output independent of worker completion order. With
403
+ # pos nil there's no position to compare, so nothing is superseded.
404
+ def superseded?(file, pos)
405
+ return false if pos.nil?
406
+
407
+ prev = @seen[file]
408
+ return true if prev && prev >= pos
409
+
410
+ @seen[file] = pos
411
+ false
412
+ end
413
+
414
+ def to_xml(bib) = bib.to_xml(bibdata: true)
415
+ def to_yaml(bib) = bib.to_yaml
416
+ def to_bibxml(bib) = bib.to_rfcxml
417
+
418
+ # Fetch and store one document. The slow detail-page load
419
+ # (`worker_agent.get`, throttled by this worker's `pacing`) runs outside
420
+ # the lock — that's the parallel win; the cheap build + write is done under
421
+ # @mutex so @files/@errors/@seen and the index stay consistent across
422
+ # workers. Called directly (single stubbed agent) by specs and from the
423
+ # pool with a per-worker agent/pacing/position.
424
+ #
425
+ # @param hit [Nokogiri::HTML::Element]
426
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
427
+ def parse_page(hit, pos = nil, worker_agent = agent, pacing = nil)
428
+ url = hit.at('h3/a')[:href]
429
+ doc = time_req(pacing) { worker_agent.get url }
430
+ @mutex.synchronize do
431
+ item = ItemData.new(
432
+ type: "standard", source: fetch_source(url), docnumber: fetch_docnumber(hit),
433
+ docidentifier: fetch_docid(hit, doc), title: fetch_title(doc),
434
+ abstract: fetch_abstract(doc), date: fetch_date(doc),
435
+ edition: fetch_edition(doc), contributor: fetch_contributor(doc),
436
+ relation: fetch_relation(doc), language: "en", script: "Latn",
437
+ ext: fetch_ext
438
+ )
439
+ write_file item, pos
440
+ end
441
+ rescue StandardError => e
442
+ Util.error do
443
+ "Document: #{url}\n#{e.message}\n#{e.backtrace}"
444
+ end
445
+ end
446
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
447
+
448
+ def fetch(_source = nil)
449
+ process_hits collect_hits
450
+ index.save
451
+ report_errors
452
+ ensure
453
+ @agent&.quit
454
+ end
455
+
456
+ # Phase 1 (serial): walk the search-result pages, following the "next"
457
+ # link, and collect every //li[@data-product] hit into one list (~12 page
458
+ # loads at per_page=100, so serial pacing here is cheap). The detail-page
459
+ # fetches happen in #process_hits.
460
+ def collect_hits(url = URL, hits = [])
461
+ result = time_req(listing_pacing) { agent.get url }
462
+ hits.concat result.xpath("//li[@data-product]").to_a
463
+ np = result.at '//a[@class="next_page"]'
464
+ return hits unless np
465
+
466
+ next_href = np[:href]
467
+ next_url = next_href.start_with?("http") ? next_href : "https://www.techstreet.com#{next_href}"
468
+ collect_hits next_url, hits
469
+ end
470
+
471
+ # Phase 2 (parallel): fan the collected hits out across a bounded worker
472
+ # pool. Each worker owns its own stealth browser agent and its own adaptive
473
+ # pacing. The agents are built up front on this thread so a Chrome-launch
474
+ # failure aborts fast (before any hit is enqueued and before the index is
475
+ # saved) rather than leaving a dead worker that can't drain the queue; and
476
+ # every created agent is quit in the ensure even if enqueuing or a worker
477
+ # raises. Hits carry their catalogue position so #write_file keeps a
478
+ # deterministic winner for any duplicate output file — byte-identical
479
+ # output regardless of completion order.
480
+ def process_hits(hits)
481
+ return if hits.empty?
482
+
483
+ n = self.class.concurrency
484
+ agents = []
485
+ queue = SizedQueue.new(n * 2)
486
+ n.times { agents << build_agent }
487
+ workers = agents.map { |worker_agent| spawn_worker(queue, worker_agent) }
488
+ hits.each_with_index { |hit, pos| queue << [hit, pos] }
489
+ n.times { queue << nil } # poison pills
490
+ workers.each(&:join)
491
+ ensure
492
+ agents&.each(&:quit)
493
+ end
494
+
495
+ # One pool worker: drains the queue with its own browser agent + pacing
496
+ # until the poison pill (nil). parse_page swallows per-document errors, so
497
+ # the thread runs to the pill; the agent is quit by #process_hits.
498
+ def spawn_worker(queue, worker_agent)
499
+ pacing = Pacing.new
500
+ Thread.new do
501
+ while (item = queue.pop)
502
+ hit, pos = item
503
+ parse_page hit, pos, worker_agent, pacing
504
+ end
505
+ end
506
+ end
507
+
508
+ # Pacing for the serial listing phase (Phase 1). Also the fallback pacing
509
+ # for #time_req when no per-worker pacing is passed (direct calls / specs).
510
+ def listing_pacing
511
+ @listing_pacing ||= Pacing.new
512
+ end
513
+
514
+ RETRIABLE_ERRORS = [
515
+ SocketError,
516
+ Ferrum::TimeoutError,
517
+ Ferrum::PendingConnectionsError,
518
+ Ferrum::StatusError,
519
+ BrowserAgent::ChallengeError
520
+ ].freeze
521
+
522
+ # Run a throttled request through the given worker's pacing, retrying up to
523
+ # 4 times on a retriable error (challenge / Ferrum / socket) and backing
524
+ # that worker's gap off before each retry.
525
+ def time_req(pacing = listing_pacing)
526
+ pacing ||= listing_pacing
527
+ tries = 0
528
+ begin
529
+ tries += 1
530
+ pacing.throttle
531
+ yield
532
+ rescue *RETRIABLE_ERRORS => e
533
+ pacing.backoff
534
+ retry if tries < 4
535
+ raise e
536
+ end
537
+ end
538
+ end
539
+ end
540
+ end
@@ -0,0 +1,7 @@
1
+ module Relaton
2
+ module Cie
3
+ class Ext < Bib::Ext
4
+ def get_schema_version = Relaton.schema_versions["relaton-model-cie"]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "ext"
2
+
3
+ module Relaton
4
+ module Cie
5
+ class Item < Bib::Item
6
+ model ItemData
7
+
8
+ attribute :ext, Ext
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module Relaton
2
+ module Cie
3
+ class ItemData < Bib::ItemData
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,68 @@
1
+ require "relaton/core/processor"
2
+
3
+ module Relaton
4
+ module Cie
5
+ class Processor < Relaton::Core::Processor
6
+ def initialize # rubocop:disable Lint/MissingSuper
7
+ @short = :relaton_cie
8
+ @prefix = "CIE"
9
+ @defaultprefix = /^CIE(-|\s)/
10
+ @idtype = "CIE"
11
+ @datasets = %w[cie-techstreet]
12
+ end
13
+
14
+ # @param code [String]
15
+ # @param date [String, NilClass] year
16
+ # @param opts [Hash]
17
+ # @return [Relaton::Cie::ItemData, nil]
18
+ def get(code, date, opts)
19
+ require_relative "../cie"
20
+ Bibliography.get(code, date, opts)
21
+ end
22
+
23
+ #
24
+ # Fetch all the docukents from a source
25
+ #
26
+ # @param [String] _source source name
27
+ # @param [Hash] opts
28
+ # @option opts [String] :output directory to output documents
29
+ # @option opts [String] :format
30
+ #
31
+ def fetch_data(_source, opts)
32
+ require_relative "data_fetcher"
33
+ DataFetcher.fetch(**opts)
34
+ end
35
+
36
+ # @param xml [String]
37
+ # @return [Relaton::Cie::ItemData]
38
+ def from_xml(xml)
39
+ require_relative "../cie"
40
+ Item.from_xml xml
41
+ end
42
+
43
+ # @param hash [String]
44
+ # @return [Relaton::Cie::ItemData]
45
+ def from_yaml(yaml)
46
+ require_relative "../cie"
47
+ Item.from_yaml yaml
48
+ end
49
+
50
+ # Returns hash of XML grammar
51
+ # @return [String]
52
+ def grammar_hash
53
+ require_relative "../cie"
54
+ @grammar_hash ||= Cie.grammar_hash
55
+ end
56
+
57
+ #
58
+ # Remove index file
59
+ #
60
+ def remove_index_file
61
+ require_relative "../cie"
62
+ Relaton::Index.find_or_create(
63
+ :cie, url: true, file: "#{INDEXFILE}.yaml"
64
+ ).remove_file
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,52 @@
1
+ require "mechanize"
2
+
3
+ module Relaton
4
+ module Cie
5
+ module Scrapper
6
+ ENDPOINT = "https://raw.githubusercontent.com/relaton/relaton-data-cie/refs/heads/v2/".freeze
7
+
8
+ class << self
9
+ # @param code [String]
10
+ # @return [Relaton::Cie::ItemData]
11
+ def scrape_page(code)
12
+ # An unrecognized reference raises; like ISO and 3GPP we let it
13
+ # propagate -- relaton-cli rescues Pubid::Errors::Error and renders
14
+ # "... is not a recognized standards identifier". Partial refs
15
+ # (`CIE 001`, `CIE 15`) parse, so nothing valid is lost.
16
+ pubid = ::Pubid::Cie.parse code
17
+ index = Index.find_or_create :cie, url: "#{ENDPOINT}#{INDEXFILE}.zip", file: "#{INDEXFILE}.yaml",
18
+ pubid_class: ::Pubid::Cie::Identifier
19
+ # Pass the parsed pubid (not the raw String) so index-v2 narrows
20
+ # candidates by number via binary search before the block runs; the
21
+ # block keeps the broad substring match the string index gave.
22
+ # Rows are Pubid::Cie::Identifier objects (not Comparable), so pick by
23
+ # the string form.
24
+ needle = pubid.to_s
25
+ row = index.search(pubid) { |r| r[:id].to_s.include?(needle) }
26
+ .min_by { |r| r[:id].to_s }
27
+ return unless row
28
+
29
+ parse_page "#{ENDPOINT}#{row[:file]}", code
30
+ end
31
+
32
+ private
33
+
34
+ # @param url [String]
35
+ # @param code [String]
36
+ # @retrurn [Relato::Cie::ItemData]
37
+ def parse_page(url, code)
38
+ resp = Mechanize.new.get url
39
+ Item.from_yaml(resp.body).tap { |item| item.fetched = Date.today.to_s }
40
+ rescue Mechanize::ResponseCodeError => e
41
+ return if e.response_code == "404"
42
+
43
+ raise Relaton::RequestError, "No document found for #{code} reference. #{e.message}"
44
+ rescue Mechanize::RedirectLimitReachedError, Timeout::Error,
45
+ Mechanize::UnauthorizedError, Mechanize::UnsupportedSchemeError,
46
+ Mechanize::ResponseReadError, Mechanize::ChunkedTerminationError => e
47
+ raise Relaton::RequestError, "No document found for #{code} reference. #{e.message}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end