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,921 @@
1
+ module Relaton
2
+ module Doi
3
+ class Parser
4
+ COUNTRIES = %w[USA].freeze
5
+
6
+ TYPES = {
7
+ "book-chapter" => "inbook",
8
+ "book-part" => "inbook",
9
+ "book-section" => "inbook",
10
+ "book-series" => "book",
11
+ "book-set" => "book",
12
+ "book-track" => "inbook",
13
+ "component" => "misc",
14
+ "database" => "dataset",
15
+ "dissertation" => "thesis",
16
+ "edited-book" => "book",
17
+ "grant" => "misc",
18
+ "journal-article" => "article",
19
+ "journal-issue" => "article",
20
+ "journal-volume" => "journal",
21
+ "monograph" => "book",
22
+ "other" => "misc",
23
+ "peer-review" => "article",
24
+ "posted-content" => "dataset",
25
+ "proceedings-article" => "inproceedings",
26
+ "proceedings-series" => "proceedings",
27
+ "reference-book" => "book",
28
+ "reference-entry" => "inbook",
29
+ "report-component" => "techreport",
30
+ "report-series" => "techreport",
31
+ "report" => "techreport",
32
+ }.freeze
33
+
34
+ REALATION_TYPES = {
35
+ "is-cited-by" => "isCitedIn",
36
+ "belongs-to" => "related",
37
+ "is-child-of" => "includedIn",
38
+ "is-expression-of" => "expressionOf",
39
+ "has-expression" => "hasExpression",
40
+ "is-manifestation-of" => "manifestationOf",
41
+ "is-manuscript-of" => "draftOf",
42
+ "has-manuscript" => "hasDraft",
43
+ "is-preprint-of" => "draftOf",
44
+ "has-preprint" => "hasDraft",
45
+ "is-replaced-by" => "obsoletedBy",
46
+ "replaces" => "obsoletes",
47
+ "is-translation-of" => "translatedFrom",
48
+ "has-translation" => "hasTranslation",
49
+ "is-version-of" => "editionOf",
50
+ "has-version" => "hasEdition",
51
+ "is-based-on" => "updates",
52
+ "is-basis-for" => "updatedBy",
53
+ "is-comment-on" => "commentaryOf",
54
+ "has-comment" => "hasCommentary",
55
+ "is-continued-by" => "hasSuccessor",
56
+ "continues" => "successorOf",
57
+ "is-derived-from" => "derives",
58
+ "has-derivation" => "derivedFrom",
59
+ "is-documented-by" => "describedBy",
60
+ "documents" => "describes",
61
+ "is-part-of" => "partOf",
62
+ "has-part" => "hasPart",
63
+ "is-review-of" => "reviewOf",
64
+ "has-review" => "hasReview",
65
+ "references" => "cites",
66
+ "is-referenced-by" => "isCitedIn",
67
+ "requires" => "hasComplement",
68
+ "is-required-by" => "complementOf",
69
+ "is-supplement-to" => "complementOf",
70
+ "is-supplemented-by" => "hasComplement",
71
+ }.freeze
72
+
73
+ ATTRS = %i[type fetched title docidentifier date source abstract contributor place
74
+ ext relation extent series medium].freeze
75
+
76
+ # Matches content that looks like markup. Same shape as the tag detector
77
+ # of `Relaton::Bib::Sanitizer`, so both agree on what to parse.
78
+ TAG_RE = %r{<[a-zA-Z/!?]}
79
+
80
+ CROSSREF_API_URL = "https://api.crossref.org/works?query=%{query}&filter=%{filter}".freeze
81
+ MAX_RETRIES = 3
82
+ RATE_LIMITED = "429".freeze
83
+ MAX_RETRY_DELAY = 60
84
+
85
+ #
86
+ # Initialize instance.
87
+ #
88
+ # @param [Hash] src The source hash.
89
+ #
90
+ def initialize(src)
91
+ @src = src
92
+ @item = {}
93
+ end
94
+
95
+ #
96
+ # Initialize instance and parse the source hash.
97
+ #
98
+ # @param [Hash] src The source hash.
99
+ #
100
+ # @return [Bib::ItemData] The bibitem.
101
+ #
102
+ def self.parse(src)
103
+ new(src).parse
104
+ end
105
+
106
+ #
107
+ # Parse the source hash.
108
+ #
109
+ # @return [Bib::ItemData] The bibitem.
110
+ #
111
+ def parse
112
+ ATTRS.each { |m| @item[m] = send "parse_#{m}" }
113
+ create_bibitem @src["DOI"], @item
114
+ end
115
+
116
+ #
117
+ # Create a bibitem from the bibitem hash.
118
+ #
119
+ # @param [String] doi The DOI.
120
+ # @param [Hash] bibitem The bibitem hash.
121
+ #
122
+ # @return [Bib::ItemData] The bibitem.
123
+ #
124
+ def create_bibitem(doi, bibitem) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
125
+ doctype_content = @src["type"]
126
+ case doi
127
+ when /\/nist/
128
+ bibitem[:ext] = Nist::Ext.new(doctype: Nist::Doctype.new(content: doctype_content))
129
+ Nist::ItemData.new(**bibitem)
130
+ when /\/rfc\d+/
131
+ bibitem[:ext] = Ietf::Ext.new(doctype: Ietf::Doctype.new(content: doctype_content))
132
+ Ietf::ItemData.new(**bibitem)
133
+ when /\/0026-1394\//
134
+ bibitem[:ext] = Bipm::Ext.new(doctype: Bipm::Doctype.new(content: doctype_content))
135
+ Bipm::ItemData.new(**bibitem)
136
+ when /\/ieee/
137
+ bibitem[:ext] = Ieee::Ext.new(doctype: Ieee::Doctype.new(content: doctype_content))
138
+ Ieee::ItemData.new(**bibitem)
139
+ else Bib::ItemData.new(**bibitem)
140
+ end
141
+ end
142
+
143
+ #
144
+ # Parse the type.
145
+ #
146
+ # @return [String] The type.
147
+ #
148
+ def parse_type
149
+ TYPES[@src["type"]] || @src["type"]
150
+ end
151
+
152
+ #
153
+ # Parse the ext element with doctype.
154
+ #
155
+ # @return [Bib::Ext] The ext element.
156
+ #
157
+ def parse_ext
158
+ Bib::Ext.new doctype: Bib::Doctype.new(content: @src["type"])
159
+ end
160
+
161
+ #
162
+ # Parse the fetched date.
163
+ #
164
+ # @return [String] The fetched date.
165
+ #
166
+ def parse_fetched
167
+ Date.today.to_s
168
+ end
169
+
170
+ #
171
+ # Parse titles from the source hash.
172
+ #
173
+ # @return [Array<Bib::Title>] The titles.
174
+ #
175
+ def parse_title # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
176
+ if @src["title"].is_a?(Array) && @src["title"].any?
177
+ main_sub_titles
178
+ elsif @src["project"].is_a?(Array) && @src["project"].any?
179
+ project_titles
180
+ elsif @src["container-title"].is_a?(Array) && @src["container-title"].size > 1
181
+ @src["container-title"][0..-2].map { |t| create_title t }
182
+ else []
183
+ end
184
+ end
185
+
186
+ #
187
+ # Parse main and subtitle from the source hash.
188
+ #
189
+ # @return [Array<Bib::Title>] The titles.
190
+ #
191
+ def main_sub_titles
192
+ title = @src["title"].map { |t| create_title t }
193
+ Array(@src["subtitle"]).each { |t| title << create_title(t, "subtitle") }
194
+ Array(@src["short-title"]).each { |t| title << create_title(t, "short") }
195
+ title
196
+ end
197
+
198
+ #
199
+ # Fetch titles from the projects.
200
+ #
201
+ # @return [Array<Bib::Title>] The titles.
202
+ #
203
+ def project_titles
204
+ Array(@src["project"]).reduce([]) do |memo, proj|
205
+ memo + Array(proj["project-title"]).map { |t| create_title t["title"] }
206
+ end
207
+ end
208
+
209
+ #
210
+ # Create a title from the title and type.
211
+ #
212
+ # @param [String] title The title content.
213
+ # @param [String] type The title type. Defaults to "main".
214
+ #
215
+ # @return [Bib::Title] The title.
216
+ #
217
+ def create_title(title, type = "main")
218
+ cnt = str_cleanup normalize_markup(title)
219
+ Bib::Title.new type: type, content: cnt, script: "Latn"
220
+ end
221
+
222
+ #
223
+ # Prepare raw Crossref markup for the relaton-bib sanitizer.
224
+ #
225
+ # Crossref returns JATS markup, and it sometimes encodes that markup as
226
+ # HTML entities, so decode them before the content reaches a model
227
+ # attribute. The markup also carries the `jats:` namespace prefix, and
228
+ # sometimes an `xlink:` prefix on an attribute, which Relaton never
229
+ # declares. Removing those prefixes is `Relaton::Bib::Sanitizer`'s job:
230
+ # every `Bib::Title` and `Bib::Abstract` runs the sanitizer on
231
+ # assignment, and it declares each undeclared prefix on a wrapper
232
+ # element so the markup parses and maps to the basicdoc set. An
233
+ # undeclared prefix that survives into the output makes
234
+ # relaton-render return nil, and isodoc then raises. See
235
+ # metanorma-pdfa#99.
236
+ #
237
+ # Upstream relaton-doi carries its own prefix removal, because the
238
+ # sanitizer fix is not in a released relaton-bib. Here both ship in one
239
+ # gem, so this method is the entity decode only.
240
+ #
241
+ # Decode only content that carries no markup of its own. Crossref
242
+ # escapes an ampersand and a literal angle bracket *inside* real
243
+ # markup, so an unconditional decode turns `<jats:p>Smith &amp;
244
+ # Jones</jats:p>` into markup that no longer parses. The sanitizer
245
+ # would then give up on it and an unescapable string would reach the
246
+ # output -- the metanorma-pdfa#99 failure again, on the far more
247
+ # common input.
248
+ #
249
+ # @param [String, nil] str The raw Crossref content.
250
+ #
251
+ # @return [String, nil] The content, with the HTML entities decoded
252
+ # when it holds no markup.
253
+ #
254
+ def normalize_markup(str)
255
+ return str if str.nil? || str.match?(TAG_RE)
256
+
257
+ CGI.unescapeHTML(str)
258
+ end
259
+
260
+ #
261
+ # Parse a docidentifier from the source hash.
262
+ #
263
+ # @return [Array<Bib::Docidentifier>] The docidentifier.
264
+ #
265
+ def parse_docidentifier
266
+ %w[DOI ISBN ISSN].each_with_object([]) do |type, obj|
267
+ prm = type == "DOI"
268
+ Array(@src[type]).each do |id|
269
+ t = issn_type(type, id)
270
+ obj << Bib::Docidentifier.new(type: t, content: id, primary: prm)
271
+ end
272
+ end
273
+ end
274
+
275
+ #
276
+ # Create an ISSN type if it's an ISSN ID.
277
+ #
278
+ # @param [String] type identifier type
279
+ # @param [String] id identifier
280
+ #
281
+ # @return [String] identifier type
282
+ #
283
+ def issn_type(type, id)
284
+ return type unless type == "ISSN"
285
+
286
+ t = @src["issn-type"]&.find { |it| it["value"] == id }&.dig("type")
287
+ t ? "issn.#{t}" : type.downcase
288
+ end
289
+
290
+ #
291
+ # Parce dates from the source hash.
292
+ #
293
+ # @return [Array<Bib::Date>] The dates.
294
+ #
295
+ def parse_date # rubocop:disable Metrics/CyclomaticComplexity
296
+ dates = %w[issued published approved].each_with_object([]) do |type, obj|
297
+ next unless @src.dig(type, "date-parts")&.first&.compact&.any?
298
+
299
+ obj << Bib::Date.new(type: type, at: date_type(type))
300
+ end
301
+ if dates.none?
302
+ dates << Bib::Date.new(type: "created", at: date_type("created"))
303
+ end
304
+ dates
305
+ end
306
+
307
+ #
308
+ # Join date parts into a string.
309
+ #
310
+ # @param [String] type The date type.
311
+ #
312
+ # @return [String] The date string.
313
+ #
314
+ def date_type(type)
315
+ @src[type]["date-parts"][0].map { |d| d.to_s.rjust(2, "0") }.join "-"
316
+ end
317
+
318
+ #
319
+ # Parse source URIs from the source hash.
320
+ #
321
+ # @return [Array<Bib::Uri>] The source URIs.
322
+ #
323
+ def parse_source # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
324
+ disprefered_links = %w[similarity-checking text-mining]
325
+ links = []
326
+ if @src["URL"]
327
+ links << Bib::Uri.new(type: "DOI", content: @src["URL"])
328
+ end
329
+ [@src["link"], @src.dig("resource", "primary")].flatten.compact.each do |l|
330
+ next if disprefered_links.include? l["intended-application"]
331
+
332
+ type = case l["URL"]
333
+ when /\.pdf$/ then "pdf"
334
+ # when /\/rfc\d+$|iopscience\.iop\.org|ieeexplore\.ieee\.org/
335
+ else "src"
336
+ end
337
+ links << Bib::Uri.new(type: type, content: l["URL"]) # if type
338
+ end
339
+ links
340
+ end
341
+
342
+ #
343
+ # Parse abstract from the source hash.
344
+ #
345
+ # @return [Array<Bib::LocalizedMarkedUpString>] The abstract.
346
+ #
347
+ def parse_abstract
348
+ return [] unless @src["abstract"]
349
+
350
+ abstract = Bib::Abstract.new(
351
+ content: normalize_markup(@src["abstract"]),
352
+ language: "en",
353
+ script: "Latn",
354
+ )
355
+ [abstract]
356
+ end
357
+
358
+ #
359
+ # Parse contributors from the source hash.
360
+ #
361
+ # @return [Array<Bib::Contributor>] The contributors.
362
+ #
363
+ def parse_contributor
364
+ contribs = author_investigators
365
+ contribs += authors_editors_translators
366
+ contribs += contribs_from_parent(contribs)
367
+ contribs << contributor(org_publisher, "publisher")
368
+ contribs += org_aurhorizer
369
+ contribs + org_enabler
370
+ end
371
+
372
+ #
373
+ # Create authors investigators from the source hash.
374
+ #
375
+ # @return [Array<Bib::Contributor>] The authors investigators.
376
+ #
377
+ def author_investigators
378
+ Array(@src["project"]).reduce([]) do |memo, proj|
379
+ memo + create_investigators(proj, "lead-investigator") +
380
+ create_investigators(proj, "investigator")
381
+ end
382
+ end
383
+
384
+ #
385
+ # Create investigators from the project.
386
+ #
387
+ # @param [Hash] project The project hash.
388
+ # @param [String] type The investigator type. "lead-investigator" or "investigator".
389
+ #
390
+ # @return [Array<Bib::Contributor>] The investigators.
391
+ #
392
+ def create_investigators(project, type)
393
+ description = type.gsub("-", " ")
394
+ Array(project[type]).map do |inv|
395
+ contributor(create_person(inv), "author", description)
396
+ end
397
+ end
398
+
399
+ #
400
+ # Create authors editors translators from the source hash.
401
+ #
402
+ # @return [Array<Bib::Contributor>] The authors editors translators.
403
+ #
404
+ def authors_editors_translators
405
+ %w[author editor translator].each_with_object([]) do |type, a|
406
+ @src[type]&.each do |c|
407
+ contrib = if c["family"]
408
+ create_person(c)
409
+ else
410
+ create_org(str_cleanup(c["name"]))
411
+ end
412
+ a << contributor(contrib, type)
413
+ end
414
+ end
415
+ end
416
+
417
+ #
418
+ # Fetch authors and editors from parent if they are not present in the book part.
419
+ #
420
+ # @param [Array<Bib::Contributor>] contribs present contributors
421
+ #
422
+ # @return [Array<Bib::Contributor>] contributors with authors and editors from parent
423
+ #
424
+ def contribs_from_parent(contribs) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
425
+ return [] unless %w[inbook inproceedings dataset].include?(parse_type) && @src["container-title"]
426
+
427
+ has_authors = contribs.any? { |c| c.role&.any? { |r| r.type == "author" } }
428
+ has_editors = contribs.any? { |c| c.role&.any? { |r| r.type == "editor" } }
429
+ return [] if has_authors && has_editors
430
+
431
+ create_authors_editors(has_authors, "author")
432
+ end
433
+
434
+ #
435
+ # Fetch parent item from Crossref.
436
+ #
437
+ # @return [Hash, nil] parent item
438
+ #
439
+ def parent_item
440
+ @parent_item ||= begin
441
+ query = CGI.escape [@src["container-title"][0], fetch_year].compact.join("+")
442
+ filter = "type:#{%w[book book-set edited-book monograph reference-book].join ',type:'}"
443
+ items = fetch_crossref(query: query, filter: filter)
444
+ items&.detect { |i| i["title"].include? @src["container-title"][0] }
445
+ end
446
+ end
447
+
448
+ #
449
+ # Create authors and editors from parent item.
450
+ #
451
+ # @param [Boolean] has true if authors or editors are present in the book part
452
+ # @param [String] type "author" or "editor"
453
+ #
454
+ # @return [Array<Bib::Contributor>] authors or editors
455
+ #
456
+ def create_authors_editors(has, type)
457
+ return [] if has || !parent_item
458
+
459
+ Array(parent_item[type]).map { |a| contributor(create_person(a), type) }
460
+ end
461
+
462
+ #
463
+ # Cerate an organization publisher from the source hash.
464
+ #
465
+ # @return [Bib::Organization] The organization.
466
+ #
467
+ def org_publisher
468
+ pbr = @src["institution"]&.detect do |i|
469
+ @src["publisher"].include?(i["name"]) ||
470
+ i["name"].include?(@src["publisher"])
471
+ end
472
+ a = pbr["acronym"]&.first if pbr
473
+ create_org(str_cleanup(@src["publisher"]), a)
474
+ end
475
+
476
+ #
477
+ # Clean up trailing punctuation and whitespace from a string.
478
+ #
479
+ # @param [String] str The string to clean up.
480
+ #
481
+ # @return [String] The cleaned up string.
482
+ #
483
+ def str_cleanup(str)
484
+ str.strip.sub(/[,\/\s]+$/, "").sub(/\s:$/, "")
485
+ end
486
+
487
+ #
488
+ # Create an organization with properly typed name and abbreviation.
489
+ #
490
+ # @param [String] name The organization name.
491
+ # @param [String, nil] abbreviation The organization abbreviation.
492
+ #
493
+ # @return [Bib::Organization] The organization.
494
+ #
495
+ def create_org(name, abbreviation = nil)
496
+ n = [Bib::TypedLocalizedString.new(content: normalize_markup(name))]
497
+ a = if abbreviation
498
+ Bib::LocalizedString.new(content: normalize_markup(abbreviation))
499
+ end
500
+ Bib::Organization.new name: n, abbreviation: a
501
+ end
502
+
503
+ #
504
+ # Parse authorizer contributor from the source hash.
505
+ #
506
+ # @return [Array<Bib::Contributor>] The authorizer contributor.
507
+ #
508
+ def org_aurhorizer
509
+ return [] unless @src["standards-body"]
510
+
511
+ name, acronym = @src["standards-body"].values_at("name", "acronym")
512
+ [contributor(create_org(name, acronym), "authorizer")]
513
+ end
514
+
515
+ #
516
+ # Parse enabler contributor from the source hash.
517
+ #
518
+ # @return [Array<Bib::Contributor>] The enabler contributor.
519
+ #
520
+ def org_enabler
521
+ Array(@src["project"]).each_with_object([]) do |proj, memo|
522
+ proj["funding"].each do |f|
523
+ memo << create_enabler(f.dig("funder", "name"))
524
+ end
525
+ end + Array(@src["funder"]).map { |f| create_enabler f["name"] }
526
+ end
527
+
528
+ #
529
+ # Create enabler contributor with type "enabler".
530
+ #
531
+ # @param [String] name The funder name.
532
+ #
533
+ # @return [Bib::Contributor] The enabler contributor.
534
+ #
535
+ def create_enabler(name)
536
+ contributor(create_org(name), "enabler")
537
+ end
538
+
539
+ #
540
+ # Create contributor from an entity and a role type.
541
+ #
542
+ # @param [Bib::Person, Bib::Organization] entity The entity.
543
+ # @param [String] type The role type.
544
+ #
545
+ # @return [Bib::Contributor] The contributor.
546
+ #
547
+ def contributor(entity, type, descriprion = nil)
548
+ desc = descriprion ? [Bib::LocalizedMarkedUpString.new(content: descriprion)] : nil
549
+ role = [Bib::Contributor::Role.new(type: type, description: desc)]
550
+ if entity.is_a?(Bib::Person)
551
+ Bib::Contributor.new(role: role, person: entity)
552
+ else
553
+ Bib::Contributor.new(role: role, organization: entity)
554
+ end
555
+ end
556
+
557
+ #
558
+ # Create a person from a person hash.
559
+ #
560
+ # @param [Hash] person The person hash.
561
+ #
562
+ # @return [Bib::Person] The person.
563
+ #
564
+ def create_person(person)
565
+ Bib::Person.new(
566
+ name: create_person_name(person),
567
+ affiliation: create_affiliation(person),
568
+ identifier: person_id(person),
569
+ )
570
+ end
571
+
572
+ #
573
+ # Create person affiliations from a person hash.
574
+ #
575
+ # @param [Hash] person The person hash.
576
+ #
577
+ # @return [Array<Bib::Affiliation>] The affiliations.
578
+ #
579
+ def create_affiliation(person)
580
+ (person["affiliation"] || []).map do |a|
581
+ Bib::Affiliation.new organization: create_org(a["name"])
582
+ end
583
+ end
584
+
585
+ #
586
+ # Create a person full name from a person hash.
587
+ #
588
+ # @param [Hash] person The person hash.
589
+ #
590
+ # @return [Bib::FullName] The full name.
591
+ #
592
+ def create_person_name(person)
593
+ surname = titlecase(person["family"])
594
+ sn = Bib::LocalizedString.new(content: surname, language: "en", script: "Latn")
595
+ Bib::FullName.new(
596
+ surname: sn, forename: forename(person), addition: nameaddition(person),
597
+ completename: completename(person), prefix: nameprefix(person)
598
+ )
599
+ end
600
+
601
+ #
602
+ # Capitalize the first letter of each word in a string except for words that
603
+ # are 2 letters or less.
604
+ #
605
+ # @param [<Type>] str <description>
606
+ #
607
+ # @return [<Type>] <description>
608
+ #
609
+ def titlecase(str)
610
+ str.split.map do |s|
611
+ if s.size > 2 && s.upcase == s && !/\.&/.match?(s)
612
+ s.capitalize
613
+ else
614
+ s
615
+ end
616
+ end.join " "
617
+ end
618
+
619
+ #
620
+ # Create a person name prefix from a person hash.
621
+ #
622
+ # @param [Hash] person The person hash.
623
+ #
624
+ # @return [Array<Bib::LocalizedString>] The name prefix.
625
+ #
626
+ def nameprefix(person)
627
+ return [] unless person["prefix"]
628
+
629
+ [Bib::LocalizedString.new(content: person["prefix"], language: "en", script: "Latn")]
630
+ end
631
+
632
+ #
633
+ # Create a complete name from a person hash.
634
+ #
635
+ # @param [Hash] person The person hash.
636
+ #
637
+ # @return [Bib::LocalizedString] The complete name.
638
+ #
639
+ def completename(person)
640
+ return unless person["name"]
641
+
642
+ Bib::LocalizedString.new(content: person["name"], language: "en", script: "Latn")
643
+ end
644
+
645
+ #
646
+ # Create a forename from a person hash.
647
+ #
648
+ # @param [Hash] person The person hash.
649
+ #
650
+ # @return [Array<Bib::FullNameType::Forename>] The forename.
651
+ #
652
+ def forename(person)
653
+ return [] unless person["given"]
654
+
655
+ fname = titlecase(person["given"])
656
+ [Bib::FullNameType::Forename.new(content: fname, language: "en", script: "Latn")]
657
+ end
658
+
659
+ #
660
+ # Create an addition from a person hash.
661
+ #
662
+ # @param [Hash] person The person hash.
663
+ #
664
+ # @return [Array<Bib::LocalizedString>] The addition.
665
+ #
666
+ def nameaddition(person)
667
+ return [] unless person["suffix"]
668
+
669
+ [Bib::LocalizedString.new(content: person["suffix"], language: "en", script: "Latn")]
670
+ end
671
+
672
+ #
673
+ # Create a person identifier from a person hash.
674
+ #
675
+ # @param [Hash] person The person hash.
676
+ #
677
+ # @return [Array<Bib::Person::Identifier>] The person identifier.
678
+ #
679
+ def person_id(person)
680
+ return [] unless person["ORCID"]
681
+
682
+ [Bib::Person::Identifier.new(type: "orcid", content: person["ORCID"])]
683
+ end
684
+
685
+ #
686
+ # Parse a place from the source hash.
687
+ #
688
+ # @return [Array<Bib::Place>] The place.
689
+ #
690
+ def parse_place # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
691
+ pub_location = @src["publisher-location"] || fetch_location
692
+ return [] unless pub_location
693
+
694
+ pls1, pls2 = pub_location.split(", ")
695
+ pls1 = str_cleanup pls1
696
+ pls2 &&= str_cleanup pls2
697
+ if COUNTRIES.include? pls2
698
+ country = Bib::Place::RegionType.new(content: pls2)
699
+ [Bib::Place.new(city: pls1, country: [country])]
700
+ elsif pls2 && pls2 == pls2&.upcase
701
+ region = Bib::Place::RegionType.new(content: pls2)
702
+ [Bib::Place.new(city: pls1, region: [region])]
703
+ elsif pls1 == pls2 || pls2.nil? || pls2.empty?
704
+ [Bib::Place.new(city: pls1)]
705
+ else
706
+ [Bib::Place.new(city: pls1), Bib::Place.new(city: pls2)]
707
+ end
708
+ end
709
+
710
+ #
711
+ # Fetch location from container.
712
+ #
713
+ # @return [String, nil] The location.
714
+ #
715
+ def fetch_location
716
+ title = @item[:title].first&.content
717
+ qparts = [title, fetch_year, @src["publisher"]]
718
+ query = CGI.escape qparts.compact.join("+").gsub(" ", "+")
719
+ filter = "type:#{%w[book-chapter book-part book-section book-track].join(',type:')}"
720
+ items = fetch_crossref(query: query, filter: filter)
721
+ items&.detect do |i|
722
+ i["publisher-location"] && i["container-title"].include?(title)
723
+ end&.dig("publisher-location")
724
+ end
725
+
726
+ #
727
+ # Parse relations from the source hash.
728
+ #
729
+ # @return [Array<Bib::Relation>] The relations.
730
+ #
731
+ def parse_relation # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
732
+ rels = included_in_relation
733
+ @src["relation"].each_with_object(rels) do |(k, v), a|
734
+ type, desc = relation_type k
735
+ Array(v).each do |r|
736
+ rel_item = Crossref.get_by_id r["id"]
737
+ title = rel_item["title"].map { |t| create_title t }
738
+ docid = Bib::Docidentifier.new(content: r["id"], type: "DOI")
739
+ bib = create_bibitem r["id"], title: title, docidentifier: [docid]
740
+ a << Bib::Relation.new(type: type, description: desc, bibitem: bib)
741
+ end
742
+ end
743
+ end
744
+
745
+ #
746
+ # Transform crossref relation type to relaton relation type.
747
+ #
748
+ # @param [String] crtype The crossref relation type.
749
+ #
750
+ # @return [Array<String>] The relaton relation type and description.
751
+ #
752
+ def relation_type(crtype)
753
+ type = REALATION_TYPES[crtype] || begin
754
+ desc = Bib::LocalizedMarkedUpString.new(content: crtype)
755
+ "related"
756
+ end
757
+ [type, desc]
758
+ end
759
+
760
+ #
761
+ # Create included in relation.
762
+ #
763
+ # @return [Array<Bib::Relation>] The relations.
764
+ #
765
+ def included_in_relation
766
+ types = %w[
767
+ book book-chapter book-part book-section book-track dataset journal-issue
768
+ journal-value proceedings-article reference-entry report-component
769
+ ]
770
+ return [] unless @src["container-title"] && types.include?(@src["type"])
771
+
772
+ @src["container-title"].map do |ct|
773
+ contrib = create_authors_editors false, "editor"
774
+ title = Bib::Title.new content: normalize_markup(ct)
775
+ bib = Bib::ItemBase.new(title: [title], contributor: contrib)
776
+ Bib::Relation.new(type: "includedIn", bibitem: bib)
777
+ end
778
+ end
779
+
780
+ #
781
+ # Fetch year from the source hash.
782
+ #
783
+ # @return [String] The year.
784
+ #
785
+ def fetch_year
786
+ d = @src["published"] || @src["approved"] || @src["created"]
787
+ d["date-parts"][0][0]
788
+ end
789
+
790
+ #
791
+ # Parse an extent from the source hash.
792
+ #
793
+ # @return [Array<Bib::Extent>] The extent.
794
+ #
795
+ def parse_extent # rubocop:disable Metrics/AbcSize
796
+ extent = []
797
+ extent << Bib::Locality.new(type: "volume", reference_from: @src["volume"]) if @src["volume"]
798
+ extent << Bib::Locality.new(type: "issue", reference_from: @src["issue"]) if @src["issue"]
799
+ if @src["page"]
800
+ from, to = @src["page"].split("-")
801
+ extent << Bib::Locality.new(type: "page", reference_from: from, reference_to: to)
802
+ end
803
+ extent.any? ? [Bib::Extent.new(locality: extent)] : []
804
+ end
805
+
806
+ #
807
+ # Parse a series from the source hash.
808
+ #
809
+ # @return [Array<Bib::Series>] The series.
810
+ #
811
+ def parse_series # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
812
+ types = %w[inbook incollection inproceedings]
813
+ return [] if !@src["container-title"] || types.include?(@item[:type]) || @src["type"] == "report-component"
814
+
815
+ con_ttl = if main_sub_titles.any? || project_titles.any?
816
+ @src["container-title"]
817
+ elsif @src["container-title"].size > 1
818
+ sct = @src["short-container-title"]&.last
819
+ abbrev = Bib::LocalizedString.new(content: sct) if sct
820
+ @src["container-title"][-1..-1]
821
+ else []
822
+ end
823
+ con_ttl.map do |ct|
824
+ title = Bib::Title.new content: normalize_markup(ct)
825
+ Bib::Series.new title: [title], abbreviation: abbrev
826
+ end
827
+ end
828
+
829
+ #
830
+ # Parse a medium from the source hash.
831
+ #
832
+ # @return [Bib::Medium, nil] The medium.
833
+ #
834
+ def parse_medium
835
+ genre = @src["degree"]&.first
836
+ return unless genre
837
+
838
+ Bib::Medium.new genre: genre
839
+ end
840
+
841
+ #
842
+ # Fetch data from Crossref API with retry logic.
843
+ #
844
+ # @param [String] query The query string.
845
+ # @param [String] filter The filter string.
846
+ #
847
+ # @return [Array<Hash>, nil] Items array from response or nil for 4xx responses
848
+ # other than 429.
849
+ #
850
+ # @raise [Relaton::RequestError] If request fails after retries, or if the
851
+ # rate limit does not clear.
852
+ #
853
+ def fetch_crossref(query:, filter:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
854
+ url = format(CROSSREF_API_URL, query: query, filter: filter)
855
+ retries = 0
856
+ throttled = 0
857
+ begin
858
+ resp = Crossref.agent.get url
859
+ JSON.parse(resp.body).dig("message", "items")
860
+ rescue Mechanize::ResponseCodeError => e
861
+ # 429 must be handled before the 4xx nil return: a throttle is not
862
+ # "no such record". Returning nil here made a rate-limited lookup look
863
+ # like a missing document, so callers such as #parent_item silently
864
+ # produced an incomplete item instead of failing.
865
+ if e.response_code == RATE_LIMITED
866
+ throttled += 1
867
+ if throttled <= MAX_RETRIES
868
+ # Cap the backoff itself, not just the header value: escalating by
869
+ # attempt would otherwise let a single `Retry-After: 60` stall one
870
+ # lookup for 60+120+180s.
871
+ sleep [retry_delay(e) * throttled, MAX_RETRY_DELAY].min
872
+ retry
873
+ end
874
+ raise Relaton::RequestError,
875
+ "Crossref rate limit not cleared after #{MAX_RETRIES} retries: #{url}"
876
+ end
877
+ return nil if e.response_code.start_with?("4")
878
+
879
+ raise Relaton::RequestError, "Crossref request failed: #{e.response_code} #{e.page.body}"
880
+ rescue Net::HTTP::Persistent::Error, Net::HTTPError, Errno::ECONNREFUSED => e
881
+ retries += 1
882
+ retry if retries <= MAX_RETRIES
883
+ raise Relaton::RequestError, "Crossref network error after #{MAX_RETRIES} retries: #{e.message}"
884
+ rescue JSON::ParserError => e
885
+ raise Relaton::RequestError, "Crossref JSON parsing error: #{e.message}"
886
+ end
887
+ end
888
+
889
+ #
890
+ # Base seconds to wait before retrying a throttled Crossref request.
891
+ # Prefers Retry-After, falls back to Crossref's X-Rate-Limit-Interval
892
+ # (formatted "1s", hence the digit scan). An empty-bodied 429 carries
893
+ # neither header, so this never assumes one is present.
894
+ #
895
+ # @param [Mechanize::ResponseCodeError] err The rate-limit error.
896
+ #
897
+ # @return [Integer] Delay in seconds, at least 1 and at most MAX_RETRY_DELAY.
898
+ #
899
+ def retry_delay(err)
900
+ headers = err.page&.response || {}
901
+ secs = retry_after_seconds(headers["retry-after"]) ||
902
+ headers["x-rate-limit-interval"].to_s[/\d+/].to_i
903
+ secs.positive? ? [secs, MAX_RETRY_DELAY].min : 1
904
+ end
905
+
906
+ #
907
+ # Parse a Retry-After header value, which RFC 9110 allows to be either
908
+ # delta-seconds or an HTTP-date. Only the numeric form is usable as a
909
+ # delay: scanning digits out of a date would pick up the day-of-month,
910
+ # silently turning a long server-mandated backoff into a few seconds.
911
+ #
912
+ # @param [String, nil] value The raw header value.
913
+ #
914
+ # @return [Integer, nil] Seconds, or nil if absent or in the date form.
915
+ #
916
+ def retry_after_seconds(value)
917
+ value.to_s.strip.match?(/\A\d+\z/) ? value.to_i : nil
918
+ end
919
+ end
920
+ end
921
+ end