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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +57 -1
  3. data/bin/console +0 -1
  4. data/lib/relaton/3gpp/bibliography.rb +82 -7
  5. data/lib/relaton/3gpp/data_fetcher.rb +51 -3
  6. data/lib/relaton/3gpp/docidentifier.rb +114 -0
  7. data/lib/relaton/3gpp/item.rb +6 -0
  8. data/lib/relaton/3gpp/parser.rb +1 -1
  9. data/lib/relaton/3gpp/processor.rb +4 -1
  10. data/lib/relaton/3gpp.rb +5 -1
  11. data/lib/relaton/adobe/bibdata.rb +8 -0
  12. data/lib/relaton/adobe/bibitem.rb +8 -0
  13. data/lib/relaton/adobe/bibliography.rb +92 -0
  14. data/lib/relaton/adobe/docidentifier.rb +49 -0
  15. data/lib/relaton/adobe/doctype.rb +14 -0
  16. data/lib/relaton/adobe/ext.rb +32 -0
  17. data/lib/relaton/adobe/item.rb +15 -0
  18. data/lib/relaton/adobe/item_base.rb +18 -0
  19. data/lib/relaton/adobe/item_data.rb +6 -0
  20. data/lib/relaton/adobe/processor.rb +45 -0
  21. data/lib/relaton/adobe/util.rb +8 -0
  22. data/lib/relaton/adobe.rb +37 -0
  23. data/lib/relaton/bib/model/address.rb +2 -2
  24. data/lib/relaton/bib/model/docidentifier.rb +24 -9
  25. data/lib/relaton/bib/model/localized_string.rb +1 -1
  26. data/lib/relaton/bib/model/structured_identifier.rb +10 -9
  27. data/lib/relaton/bib/sanitizer.rb +202 -6
  28. data/lib/relaton/bib.rb +0 -2
  29. data/lib/relaton/bipm/bibliography.rb +159 -10
  30. data/lib/relaton/bipm/data_fetcher.rb +26 -2
  31. data/lib/relaton/bipm/data_outcomes_parser.rb +3 -4
  32. data/lib/relaton/bipm/id_parser.rb +5 -4
  33. data/lib/relaton/bipm/model/structured_identifier.rb +21 -0
  34. data/lib/relaton/bipm/processor.rb +2 -2
  35. data/lib/relaton/bipm/rawdata_bipm_metrologia/fetcher.rb +2 -4
  36. data/lib/relaton/bipm/si_brochure_parser.rb +5 -3
  37. data/lib/relaton/bipm.rb +7 -1
  38. data/lib/relaton/bsi/bibliography.rb +115 -43
  39. data/lib/relaton/bsi/hit.rb +14 -0
  40. data/lib/relaton/bsi/hit_collection.rb +15 -16
  41. data/lib/relaton/bsi/model/docidentifier.rb +99 -1
  42. data/lib/relaton/bsi/processor.rb +1 -0
  43. data/lib/relaton/calconnect/bibliography.rb +12 -14
  44. data/lib/relaton/calconnect/data_fetcher.rb +77 -9
  45. data/lib/relaton/calconnect/docidentifier.rb +80 -0
  46. data/lib/relaton/calconnect/hit_collection.rb +65 -57
  47. data/lib/relaton/calconnect/model/item.rb +7 -0
  48. data/lib/relaton/calconnect/processor.rb +7 -1
  49. data/lib/relaton/calconnect.rb +11 -1
  50. data/lib/relaton/ccsds/data/fetcher.rb +17 -12
  51. data/lib/relaton/ccsds/data/parser.rb +1 -1
  52. data/lib/relaton/ccsds/hit_collection.rb +6 -1
  53. data/lib/relaton/ccsds/model/docidentifier.rb +121 -0
  54. data/lib/relaton/ccsds/model/item.rb +2 -0
  55. data/lib/relaton/cen/bibliography.rb +75 -47
  56. data/lib/relaton/cen/hit.rb +16 -1
  57. data/lib/relaton/cen/hit_collection.rb +59 -11
  58. data/lib/relaton/cen/model/docidentifier.rb +92 -1
  59. data/lib/relaton/cen/processor.rb +13 -8
  60. data/lib/relaton/cen/scraper.rb +13 -5
  61. data/lib/relaton/cen.rb +1 -0
  62. data/lib/relaton/cie/data_fetcher.rb +215 -30
  63. data/lib/relaton/cie/processor.rb +3 -1
  64. data/lib/relaton/cie/scrapper.rb +15 -2
  65. data/lib/relaton/cie.rb +2 -1
  66. data/lib/relaton/core/data_fetcher.rb +150 -3
  67. data/lib/relaton/core/governor.rb +320 -0
  68. data/lib/relaton/core/pacer.rb +134 -0
  69. data/lib/relaton/core/processor.rb +19 -0
  70. data/lib/relaton/core/request_error.rb +14 -0
  71. data/lib/relaton/core.rb +3 -0
  72. data/lib/relaton/db/registry.rb +41 -1
  73. data/lib/relaton/doi/crossref.rb +19 -2
  74. data/lib/relaton/doi/parser.rb +109 -15
  75. data/lib/relaton/easc/bibdata.rb +8 -0
  76. data/lib/relaton/easc/bibitem.rb +8 -0
  77. data/lib/relaton/easc/bibliography.rb +95 -0
  78. data/lib/relaton/easc/docidentifier.rb +100 -0
  79. data/lib/relaton/easc/doctype.rb +14 -0
  80. data/lib/relaton/easc/ext.rb +44 -0
  81. data/lib/relaton/easc/item.rb +13 -0
  82. data/lib/relaton/easc/item_base.rb +18 -0
  83. data/lib/relaton/easc/item_data.rb +6 -0
  84. data/lib/relaton/easc/processor.rb +46 -0
  85. data/lib/relaton/easc/util.rb +8 -0
  86. data/lib/relaton/easc.rb +35 -0
  87. data/lib/relaton/ecma/bibliography.rb +93 -25
  88. data/lib/relaton/ecma/data_fetcher.rb +71 -12
  89. data/lib/relaton/ecma/docidentifier.rb +124 -0
  90. data/lib/relaton/ecma/item.rb +2 -0
  91. data/lib/relaton/ecma/memento_parser.rb +1 -1
  92. data/lib/relaton/ecma/page_fetcher.rb +15 -3
  93. data/lib/relaton/ecma/parser_common.rb +2 -2
  94. data/lib/relaton/ecma/processor.rb +4 -1
  95. data/lib/relaton/ecma/standard_parser.rb +2 -2
  96. data/lib/relaton/ecma.rb +10 -1
  97. data/lib/relaton/etsi/bibliography.rb +67 -2
  98. data/lib/relaton/etsi/data_fetcher.rb +43 -4
  99. data/lib/relaton/etsi/processor.rb +3 -1
  100. data/lib/relaton/etsi.rb +2 -1
  101. data/lib/relaton/gb/bibliography.rb +55 -29
  102. data/lib/relaton/gb/docidentifier.rb +58 -9
  103. data/lib/relaton/gb/processor.rb +3 -0
  104. data/lib/relaton/gb/scraper.rb +27 -10
  105. data/lib/relaton/gost/bibdata.rb +8 -0
  106. data/lib/relaton/gost/bibitem.rb +8 -0
  107. data/lib/relaton/gost/bibliography.rb +107 -0
  108. data/lib/relaton/gost/docidentifier.rb +80 -0
  109. data/lib/relaton/gost/doctype.rb +16 -0
  110. data/lib/relaton/gost/ext.rb +46 -0
  111. data/lib/relaton/gost/item.rb +15 -0
  112. data/lib/relaton/gost/item_base.rb +18 -0
  113. data/lib/relaton/gost/item_data.rb +6 -0
  114. data/lib/relaton/gost/processor.rb +49 -0
  115. data/lib/relaton/gost/util.rb +8 -0
  116. data/lib/relaton/gost.rb +36 -0
  117. data/lib/relaton/iala/bibdata.rb +8 -0
  118. data/lib/relaton/iala/bibitem.rb +8 -0
  119. data/lib/relaton/iala/bibliography.rb +146 -0
  120. data/lib/relaton/iala/docidentifier.rb +89 -0
  121. data/lib/relaton/iala/doctype.rb +18 -0
  122. data/lib/relaton/iala/ext.rb +32 -0
  123. data/lib/relaton/iala/item.rb +21 -0
  124. data/lib/relaton/iala/item_base.rb +18 -0
  125. data/lib/relaton/iala/item_data.rb +6 -0
  126. data/lib/relaton/iala/processor.rb +43 -0
  127. data/lib/relaton/iala/relation.rb +7 -0
  128. data/lib/relaton/iala/util.rb +8 -0
  129. data/lib/relaton/iala.rb +35 -0
  130. data/lib/relaton/iana/bibliography.rb +67 -14
  131. data/lib/relaton/iana/data_fetcher.rb +35 -5
  132. data/lib/relaton/iana/processor.rb +3 -1
  133. data/lib/relaton/iana.rb +12 -1
  134. data/lib/relaton/iec/data_fetcher.rb +7 -1
  135. data/lib/relaton/iec/hit_collection.rb +1 -1
  136. data/lib/relaton/iec/model/docidentifier.rb +9 -5
  137. data/lib/relaton/iec/model/ext.rb +2 -2
  138. data/lib/relaton/iec/processor.rb +1 -0
  139. data/lib/relaton/ieee/bibliography.rb +25 -3
  140. data/lib/relaton/ieee/data_fetcher.rb +158 -17
  141. data/lib/relaton/ieee/idams_parser.rb +18 -11
  142. data/lib/relaton/ieee/processor.rb +4 -1
  143. data/lib/relaton/ieee/rawbib_id_parser.rb +291 -86
  144. data/lib/relaton/ieee.rb +2 -1
  145. data/lib/relaton/ietf/data_fetcher.rb +295 -12
  146. data/lib/relaton/ietf/processor.rb +7 -3
  147. data/lib/relaton/ietf/rfc/entry.rb +39 -3
  148. data/lib/relaton/ietf/scraper.rb +69 -36
  149. data/lib/relaton/ietf.rb +4 -1
  150. data/lib/relaton/iho/bibliography.rb +1 -1
  151. data/lib/relaton/iho/docidentifier.rb +1 -1
  152. data/lib/relaton/index/file_io.rb +11 -11
  153. data/lib/relaton/index/file_storage.rb +6 -1
  154. data/lib/relaton/index/pool.rb +6 -1
  155. data/lib/relaton/index/shard_source.rb +201 -0
  156. data/lib/relaton/index/type.rb +63 -12
  157. data/lib/relaton/index.rb +2 -1
  158. data/lib/relaton/iso/bibliography.rb +20 -15
  159. data/lib/relaton/iso/data_fetcher.rb +3 -3
  160. data/lib/relaton/iso/data_parser.rb +17 -3
  161. data/lib/relaton/iso/hit_collection.rb +27 -15
  162. data/lib/relaton/iso/item_data.rb +22 -0
  163. data/lib/relaton/iso/model/docidentifier.rb +24 -12
  164. data/lib/relaton/iso/processor.rb +1 -0
  165. data/lib/relaton/iso/scraper.rb +19 -3
  166. data/lib/relaton/itu/bibliography.rb +9 -4
  167. data/lib/relaton/itu/data_crawler_r.rb +664 -0
  168. data/lib/relaton/itu/data_fetcher.rb +496 -50
  169. data/lib/relaton/itu/data_merge_r.rb +149 -0
  170. data/lib/relaton/itu/data_parser_r.rb +163 -89
  171. data/lib/relaton/itu/data_parser_t.rb +228 -0
  172. data/lib/relaton/itu/family_cache.rb +177 -0
  173. data/lib/relaton/itu/governor.rb +56 -0
  174. data/lib/relaton/itu/hit.rb +9 -3
  175. data/lib/relaton/itu/hit_collection.rb +258 -86
  176. data/lib/relaton/itu/model/docidentifier.rb +67 -1
  177. data/lib/relaton/itu/model/structured_identifier.rb +19 -0
  178. data/lib/relaton/itu/processor.rb +10 -4
  179. data/lib/relaton/itu/pubid.rb +27 -5
  180. data/lib/relaton/itu/recommendation_fields.rb +334 -0
  181. data/lib/relaton/itu/recommendation_parser.rb +18 -149
  182. data/lib/relaton/itu/scraper.rb +13 -3
  183. data/lib/relaton/itu.rb +2 -1
  184. data/lib/relaton/jcgm/bibdata.rb +8 -0
  185. data/lib/relaton/jcgm/bibitem.rb +8 -0
  186. data/lib/relaton/jcgm/bibliography.rb +97 -0
  187. data/lib/relaton/jcgm/data_fetcher.rb +81 -0
  188. data/lib/relaton/jcgm/docidentifier.rb +102 -0
  189. data/lib/relaton/jcgm/doctype.rb +12 -0
  190. data/lib/relaton/jcgm/ext.rb +23 -0
  191. data/lib/relaton/jcgm/item.rb +20 -0
  192. data/lib/relaton/jcgm/item_base.rb +18 -0
  193. data/lib/relaton/jcgm/item_data.rb +6 -0
  194. data/lib/relaton/jcgm/meetings_parser.rb +175 -0
  195. data/lib/relaton/jcgm/processor.rb +71 -0
  196. data/lib/relaton/jcgm/relation.rb +9 -0
  197. data/lib/relaton/jcgm/structured_identifier.rb +40 -0
  198. data/lib/relaton/jcgm/util.rb +8 -0
  199. data/lib/relaton/jcgm.rb +24 -0
  200. data/lib/relaton/jis/bibliography.rb +8 -10
  201. data/lib/relaton/jis/data_fetcher.rb +21 -19
  202. data/lib/relaton/jis/docidentifier.rb +104 -5
  203. data/lib/relaton/jis/hit.rb +18 -23
  204. data/lib/relaton/jis/hit_collection.rb +19 -18
  205. data/lib/relaton/jis/processor.rb +1 -1
  206. data/lib/relaton/jis.rb +2 -3
  207. data/lib/relaton/logger/channels/gh_issue.rb +78 -13
  208. data/lib/relaton/nist/data_fetcher.rb +63 -13
  209. data/lib/relaton/nist/docidentifier.rb +165 -0
  210. data/lib/relaton/nist/item.rb +2 -0
  211. data/lib/relaton/nist/item_base.rb +16 -0
  212. data/lib/relaton/nist/mods_parser.rb +38 -12
  213. data/lib/relaton/nist/processor.rb +2 -1
  214. data/lib/relaton/nist/relation.rb +3 -0
  215. data/lib/relaton/nist/scraper.rb +6 -3
  216. data/lib/relaton/oasis/bibliography.rb +147 -6
  217. data/lib/relaton/oasis/data_fetcher.rb +41 -5
  218. data/lib/relaton/oasis/data_parser_utils.rb +37 -3
  219. data/lib/relaton/oasis/docidentifier.rb +54 -0
  220. data/lib/relaton/oasis/item.rb +3 -0
  221. data/lib/relaton/oasis/processor.rb +7 -1
  222. data/lib/relaton/oasis.rb +14 -1
  223. data/lib/relaton/ogc/data_fetcher.rb +23 -2
  224. data/lib/relaton/ogc/docidentifier.rb +105 -0
  225. data/lib/relaton/ogc/hit_collection.rb +78 -3
  226. data/lib/relaton/ogc/processor.rb +2 -1
  227. data/lib/relaton/ogc.rb +5 -1
  228. data/lib/relaton/oiml/bibliography.rb +90 -15
  229. data/lib/relaton/oiml/docidentifier.rb +18 -3
  230. data/lib/relaton/omg/docidentifier.rb +67 -0
  231. data/lib/relaton/omg/item.rb +1 -0
  232. data/lib/relaton/omg/processor.rb +1 -0
  233. data/lib/relaton/omg/scraper.rb +61 -16
  234. data/lib/relaton/omg.rb +1 -0
  235. data/lib/relaton/plateau/bibliography.rb +10 -3
  236. data/lib/relaton/plateau/data_fetcher.rb +25 -2
  237. data/lib/relaton/plateau/handbook_parser.rb +8 -1
  238. data/lib/relaton/plateau/hit.rb +10 -2
  239. data/lib/relaton/plateau/hit_collection.rb +31 -11
  240. data/lib/relaton/plateau/processor.rb +3 -1
  241. data/lib/relaton/plateau/technical_report_parser.rb +8 -1
  242. data/lib/relaton/plateau.rb +2 -1
  243. data/lib/relaton/sdo/config.rb +34 -0
  244. data/lib/relaton/sdo/fetcher.rb +52 -0
  245. data/lib/relaton/sdo/logo.rb +95 -0
  246. data/lib/relaton/sdo/name.rb +26 -0
  247. data/lib/relaton/sdo/organization.rb +71 -0
  248. data/lib/relaton/sdo/store.rb +49 -0
  249. data/lib/relaton/sdo.rb +29 -0
  250. data/lib/relaton/version.rb +1 -1
  251. data/lib/relaton/w3c/bibliography.rb +132 -12
  252. data/lib/relaton/w3c/data_fetcher.rb +194 -16
  253. data/lib/relaton/w3c/data_parser.rb +3 -3
  254. data/lib/relaton/w3c/docidentifier.rb +48 -0
  255. data/lib/relaton/w3c/governor.rb +32 -0
  256. data/lib/relaton/w3c/item.rb +3 -0
  257. data/lib/relaton/w3c/pubid.rb +12 -0
  258. data/lib/relaton/w3c/safe_realize.rb +110 -21
  259. data/lib/relaton/w3c.rb +12 -1
  260. data/lib/relaton/xsf/bibliography.rb +61 -1
  261. data/lib/relaton/xsf/data_fetcher.rb +55 -5
  262. data/lib/relaton/xsf/docidentifier.rb +46 -0
  263. data/lib/relaton/xsf/hit_collection.rb +31 -3
  264. data/lib/relaton/xsf/item.rb +6 -0
  265. data/lib/relaton/xsf/processor.rb +1 -0
  266. data/lib/relaton/xsf.rb +5 -1
  267. data/lib/relaton.rb +42 -0
  268. metadata +135 -24
  269. data/lib/relaton/ieee/pub_id.rb +0 -161
  270. data/lib/relaton/index/id_number.rb +0 -30
@@ -0,0 +1,32 @@
1
+ module Relaton
2
+ module Adobe
3
+ # Adobe bibliographic item extension. Adds Adobe-specific structured
4
+ # metadata on top of the shared Relaton::Bib::Ext so the fields
5
+ # round-trip through both XML and YAML without per-repo merge hacks.
6
+ # Doctype is referenced lazily via autoload from lib/relaton/adobe.rb.
7
+ class Ext < Bib::Ext
8
+ attribute :doctype, Doctype
9
+ attribute :urn, :string
10
+ attribute :webpage, :string
11
+ attribute :tech_note_number, :string
12
+ attribute :source_repo_path, :string
13
+ attribute :publication_slug, :string
14
+
15
+ xml do
16
+ map_element "urn", to: :urn
17
+ map_element "webpage", to: :webpage
18
+ map_element "tech_note_number", to: :tech_note_number
19
+ map_element "source_repo_path", to: :source_repo_path
20
+ map_element "publication_slug", to: :publication_slug
21
+ end
22
+
23
+ key_value do
24
+ map_element "urn", to: :urn
25
+ map_element "webpage", to: :webpage
26
+ map_element "tech_note_number", to: :tech_note_number
27
+ map_element "source_repo_path", to: :source_repo_path
28
+ map_element "publication_slug", to: :publication_slug
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ module Relaton
2
+ module Adobe
3
+ # Adobe bibliographic item. Re-declares +ext+ to use the typed Ext
4
+ # subclass so Adobe-specific fields round-trip natively. Sub-files
5
+ # (ItemData, Ext, Docidentifier) are loaded lazily via the autoload
6
+ # entries declared in lib/relaton/adobe.rb.
7
+ class Item < Bib::Item
8
+ model ItemData
9
+
10
+ attribute :ext, Ext
11
+ attribute :docidentifier, Docidentifier, collection: true,
12
+ initialize_empty: true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Relaton
2
+ module Adobe
3
+ class ItemBase < Lutaml::Model::Serializable
4
+ include Bib::NamespaceHelper
5
+
6
+ attr_accessor :type
7
+
8
+ model ItemData
9
+
10
+ instance_exec(&Bib::ItemShared::ATTRIBUTES)
11
+
12
+ xml do
13
+ map_attribute "type", to: :type
14
+ instance_exec(&Bib::ItemShared::XML_BODY)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ module Relaton
2
+ module Adobe
3
+ class ItemData < Bib::ItemData
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,45 @@
1
+ require "relaton/core/processor"
2
+
3
+ module Relaton
4
+ module Adobe
5
+ # Relaton processor for the Adobe flavor. Registered by
6
+ # Relaton::Registry when the unified gem loads (see
7
+ # lib/relaton/registry.rb). Supports `relaton fetch adobe ...` once
8
+ # the relaton-data-adobe dataset is installed.
9
+ class Processor < Core::Processor
10
+ attr_reader :idtype
11
+
12
+ def initialize
13
+ @short = :relaton_adobe
14
+ @prefix = "Adobe"
15
+ @defaultprefix = %r{^(?:Adobe|ATN)}
16
+ @idtype = "Adobe"
17
+ end
18
+
19
+ def get(code, date, opts)
20
+ require_relative "../adobe"
21
+ Bibliography.get(code, date, opts)
22
+ end
23
+
24
+ def from_xml(xml)
25
+ require_relative "../adobe"
26
+ Item.from_xml xml
27
+ end
28
+
29
+ def from_yaml(yaml)
30
+ require_relative "../adobe"
31
+ Item.from_yaml(yaml)
32
+ end
33
+
34
+ def grammar_hash
35
+ require_relative "../adobe"
36
+ @grammar_hash ||= ::Relaton::Adobe.grammar_hash
37
+ end
38
+
39
+ def remove_index_file
40
+ require_relative "../adobe"
41
+ Relaton::Index.find_or_create(:adobe, url: true, file: "#{INDEXFILE}.yaml").remove_file
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,8 @@
1
+ module Relaton
2
+ module Adobe
3
+ module Util
4
+ extend Relaton::Bib::Util
5
+ PROGNAME = "relaton-adobe".freeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "relaton/bib"
4
+ require "relaton/index"
5
+ require "pubid"
6
+ require "relaton/version"
7
+
8
+ # Relaton::Adobe is the Adobe (font tech notes, character collections,
9
+ # PostScript Language, etc.) flavor of the unified relaton gem. Adds
10
+ # typed Ext fields (urn, webpage, tech_note_number, source_repo_path,
11
+ # publication_slug) so they round-trip natively through YAML and XML
12
+ # without per-repo merge hacks.
13
+ module Relaton
14
+ module Adobe
15
+ INDEXFILE = "index-v2".freeze
16
+
17
+ class Error < StandardError; end
18
+
19
+ autoload :Doctype, "relaton/adobe/doctype"
20
+ autoload :Ext, "relaton/adobe/ext"
21
+ autoload :ItemData, "relaton/adobe/item_data"
22
+ autoload :ItemBase, "relaton/adobe/item_base"
23
+ autoload :Item, "relaton/adobe/item"
24
+ autoload :Bibitem, "relaton/adobe/bibitem"
25
+ autoload :Bibdata, "relaton/adobe/bibdata"
26
+ autoload :Docidentifier, "relaton/adobe/docidentifier"
27
+ autoload :Util, "relaton/adobe/util"
28
+ autoload :Bibliography, "relaton/adobe/bibliography"
29
+ autoload :Processor, "relaton/adobe/processor"
30
+
31
+ # Returns hash of XML grammar
32
+ # @return [String]
33
+ def self.grammar_hash
34
+ Digest::MD5.hexdigest Relaton::VERSION
35
+ end
36
+ end
37
+ end
@@ -6,7 +6,7 @@ module Relaton
6
6
  attribute :state, :string
7
7
  attribute :country, :string
8
8
  attribute :postcode, :string
9
- attribute :formatted_address, :string, raw: true
9
+ attribute :formatted_address, :string
10
10
 
11
11
  xml do
12
12
  root "address"
@@ -15,7 +15,7 @@ module Relaton
15
15
  map_element "state", to: :state
16
16
  map_element "country", to: :country
17
17
  map_element "postcode", to: :postcode
18
- map_element "formattedAddress", to: :formatted_address
18
+ map_element "formattedAddress", to: :formatted_address, raw: :content
19
19
  end
20
20
  end
21
21
  end
@@ -18,17 +18,32 @@ module Relaton
18
18
  map "primary", to: :primary
19
19
  end
20
20
 
21
- def remove_part!
22
- raise NotImplementedError, "`remove_part!` method not implemented in #{self.class}"
23
- end
21
+ # `Bib::ItemData#to_all_parts` and `#to_most_recent_reference` broadcast
22
+ # these three to EVERY docidentifier unconditionally, so raising here made
23
+ # both calls unusable for any flavor that had not subclassed this class —
24
+ # 13 of them had not, and both died on every item they produced. The raise
25
+ # was also `NotImplementedError`, which descends from `ScriptError`, so a
26
+ # caller's `rescue => e` did not even catch it.
27
+ #
28
+ # The default is therefore a **no-op**. An identifier that models neither
29
+ # a part nor a date has nothing to strip, and returning it unchanged is
30
+ # the correct answer for both calls.
31
+ #
32
+ # A flavor whose identifier does carry one overrides these — see
33
+ # `Relaton::Ogc::Docidentifier` or `Relaton::Iala::Docidentifier` for the
34
+ # pubid-backed shape (parse `content` into a pubid, mutate, re-render
35
+ # through a `store_content` alias so the write does not re-parse).
36
+ #
37
+ # The trade-off, recorded so it stays a choice rather than an oversight: a
38
+ # flavor that DOES carry a date but has not overridden `remove_date!` now
39
+ # returns a dated reference silently, where before it crashed loudly.
40
+ # That is a missing override in the flavor, not a defect here.
24
41
 
25
- def to_all_parts!
26
- raise NotImplementedError, "`to_all_parts!` method not implemented in #{self.class}"
27
- end
42
+ def remove_part!; end
28
43
 
29
- def remove_date!
30
- raise NotImplementedError, "`remove_date!` method not implemented in #{self.class}"
31
- end
44
+ def to_all_parts!; end
45
+
46
+ def remove_date!; end
32
47
  end
33
48
  end
34
49
  end
@@ -32,7 +32,7 @@ module Relaton
32
32
  end
33
33
  end
34
34
 
35
- attribute :content, :string, raw: true
35
+ attribute :content, :string
36
36
  prepend ContentSanitization
37
37
 
38
38
  xml do
@@ -32,17 +32,18 @@ module Relaton
32
32
  map_element "year", to: :year
33
33
  end
34
34
 
35
- def remove_part!
36
- raise NotImplementedError, "`remove_part!` method not implemented in #{self.class}"
37
- end
35
+ # The same broadcast reaches `ext.structuredidentifier`
36
+ # (`Bib::ItemData#ext_to_all_parts!` / `#ext_remove_date`), so these
37
+ # default to no-ops for the same reason as `Bib::Docidentifier`'s: a
38
+ # structured identifier with no part and no date has nothing to strip.
39
+ # Flavors that model one override them — see
40
+ # `Relaton::Iso::StructuredIdentifier` and `Relaton::Gb::StructuredIdentifier`.
38
41
 
39
- def to_all_parts!
40
- raise NotImplementedError, "`to_all_parts!` method not implemented in #{self.class}"
41
- end
42
+ def remove_part!; end
42
43
 
43
- def remove_date!
44
- raise NotImplementedError, "`remove_date!` method not implemented in #{self.class}"
45
- end
44
+ def to_all_parts!; end
45
+
46
+ def remove_date!; end
46
47
  end
47
48
  end
48
49
  end
@@ -3,8 +3,13 @@ require "nokogiri"
3
3
  module Relaton
4
4
  module Bib
5
5
  # Strips inline markup not in the basicdoc PureTextElement set
6
- # (plus <p>, <eref>, <xref>, <fn>) from raw marked-up content strings.
7
- # Disallowed elements are unwrapped: tags removed, inner text kept.
6
+ # (plus <p>, <eref>, <xref>, <fn>, <link>) from raw marked-up content
7
+ # strings. Disallowed elements are unwrapped: tags removed, inner text kept.
8
+ #
9
+ # <link> (basicdoc's inline hyperlink) is admitted because it is a valid
10
+ # TextElement child of a biblionote and carries a target URL that must
11
+ # survive the from_xml/to_xml round-trip — dropping it silently loses
12
+ # every URL in amended note.display references (relaton-bib#122).
8
13
  #
9
14
  # <fn> is admitted beyond strict PureTextElement because bibliographic
10
15
  # titles in real Metanorma input routinely carry footnotes (e.g. ISO
@@ -22,7 +27,7 @@ module Relaton
22
27
  module Sanitizer
23
28
  ALLOWED = %w[
24
29
  em strong sub sup tt underline strike smallcap br stem
25
- p eref xref fn
30
+ p eref xref fn link
26
31
  ].freeze
27
32
 
28
33
  # Elements whose children are non-basicdoc inline notation
@@ -36,15 +41,206 @@ module Relaton
36
41
 
37
42
  TAG_RX = %r{<[a-zA-Z/!?]}
38
43
 
44
+ # Captures a namespace prefix, on a tag or on an attribute: the
45
+ # "jats" of <jats:p> and </jats:italic>, and the "xlink" of
46
+ # xlink:href.
47
+ NS_PREFIX_RX = %r{(?:</?|\s)([A-Za-z_][\w.-]*):(?=[A-Za-z_])}
48
+
49
+ # Namespace that declares a prefix which the content leaves
50
+ # undeclared. The sanitiser removes it again before it serialises.
51
+ NS_PLACEHOLDER = "urn:x-relaton-undeclared:%s".freeze
52
+
53
+ # Element that carries the placeholder declarations. Its children
54
+ # are the sanitised content, so the element itself never reaches
55
+ # the output.
56
+ NS_WRAPPER = "relaton-sanitizer-root".freeze
57
+
58
+ # Extension that lengthens the wrapper name past a collision, and
59
+ # the pattern that measures how far the content already extends it.
60
+ NS_WRAPPER_SUFFIX = "-x".freeze
61
+ NS_WRAPPER_RX = /#{NS_WRAPPER}(?:#{NS_WRAPPER_SUFFIX})*/
62
+
63
+ # Reserved prefixes. XML declares both, so the content must not.
64
+ NS_RESERVED = %w[xml xmlns].freeze
65
+
66
+ # Serialise without the FORMAT option, so the sanitiser keeps the
67
+ # shape of element-only content instead of adding newlines and
68
+ # indent.
69
+ SAVE_OPTS = Nokogiri::XML::Node::SaveOptions::AS_XML
70
+
39
71
  def self.sanitize(content)
40
72
  return content unless sanitizable?(content)
41
73
 
74
+ node = parse(content)
75
+ return content if node.nil?
76
+
77
+ sanitize_children(node)
78
+ node.children.map do |c|
79
+ c.to_xml(encoding: "UTF-8", save_with: SAVE_OPTS)
80
+ end.join
81
+ end
82
+
83
+ #
84
+ # Parse the content into a node whose children are the content.
85
+ #
86
+ # @param [String] content The raw marked-up content.
87
+ #
88
+ # @return [Nokogiri::XML::Node, nil] The node, or nil when the
89
+ # content does not parse.
90
+ #
91
+ def self.parse(content)
42
92
  fragment = Nokogiri::XML::DocumentFragment.parse(content)
43
- return content if fragment.errors.any?
93
+ return fragment if fragment.errors.empty?
94
+
95
+ parse_with_prefixes(content)
96
+ end
97
+ private_class_method :parse
98
+
99
+ #
100
+ # Parse content that uses undeclared namespace prefixes.
101
+ #
102
+ # An undeclared prefix is always a parse error, so without this the
103
+ # sanitiser gives up on exactly the third-party markup that needs
104
+ # sanitising most. Declare every prefix that the content uses on a
105
+ # wrapper element, parse, then remove the placeholder namespaces
106
+ # from the elements and from the attributes. See metanorma-pdfa#99.
107
+ #
108
+ # An undeclared prefix inside an OPAQUE <stem> goes as well. The
109
+ # sanitiser cannot keep it: an undeclared prefix in the output is
110
+ # the exact failure that this method removes. Only a namespace that
111
+ # the content declares itself survives verbatim.
112
+ #
113
+ # @param [String] content The raw marked-up content.
114
+ #
115
+ # @return [Nokogiri::XML::Element, nil] The wrapper element, or nil
116
+ # when the content uses no prefix or does not parse.
117
+ #
118
+ def self.parse_with_prefixes(content)
119
+ decl = placeholder_declarations(content) or return
120
+ name = wrapper_name(content)
121
+ doc = Nokogiri::XML "<#{name} #{decl}>#{content}</#{name}>"
122
+ return unless doc.errors.empty?
123
+
124
+ drop_placeholder_namespaces doc.root
125
+ end
126
+ private_class_method :parse_with_prefixes
127
+
128
+ #
129
+ # Declare every namespace prefix that the content uses.
130
+ #
131
+ # @param [String] content The raw marked-up content.
132
+ #
133
+ # @return [String, nil] The declarations, or nil when the content
134
+ # uses no prefix.
135
+ #
136
+ def self.placeholder_declarations(content)
137
+ prefixes = content.scan(NS_PREFIX_RX).flatten.uniq - NS_RESERVED
138
+ return if prefixes.empty?
139
+
140
+ prefixes.map do |pfx|
141
+ %(xmlns:#{pfx}="#{format NS_PLACEHOLDER, pfx}")
142
+ end.join(" ")
143
+ end
144
+ private_class_method :placeholder_declarations
145
+
146
+ #
147
+ # Name a wrapper element that the content does not close itself.
148
+ #
149
+ # Content that holds the literal end tag of the wrapper would close
150
+ # it early. The document then has more than one root, the parse
151
+ # fails, and the sanitiser gives up on content that it can handle.
152
+ #
153
+ # Extend past the longest run of the suffix that the content already
154
+ # holds, in ONE scan. Growing the name and re-testing with include?
155
+ # is quadratic: each extension re-scans the whole string, and
156
+ # content shaped like "…-root-x-x-x" forces one pass per two
157
+ # characters (measured at 229 ms for 20k characters). The sanitiser
158
+ # runs on every marked-up assignment, on third-party content.
159
+ #
160
+ # @param [String] content The raw marked-up content.
161
+ #
162
+ # @return [String] A name that the content does not contain.
163
+ #
164
+ def self.wrapper_name(content)
165
+ longest = content.scan(NS_WRAPPER_RX).map(&:size).max
166
+ return NS_WRAPPER unless longest
167
+
168
+ extra = ((longest - NS_WRAPPER.size) / NS_WRAPPER_SUFFIX.size) + 1
169
+ NS_WRAPPER + (NS_WRAPPER_SUFFIX * extra)
170
+ end
171
+ private_class_method :wrapper_name
172
+
173
+ #
174
+ # Remove the placeholder namespaces, and only those.
175
+ #
176
+ # Nokogiri's remove_namespaces! would also strip a namespace that
177
+ # the content declares itself, such as the MathML xmlns inside an
178
+ # OPAQUE <stem>, which must survive verbatim. Match the wrapper's
179
+ # own declarations, so a namespace of the content never matches,
180
+ # whatever its URI.
181
+ #
182
+ # The declarations stay on the wrapper element. Only its children
183
+ # reach the output, so the declarations never leak. Do not
184
+ # serialise the root itself.
185
+ #
186
+ # @param [Nokogiri::XML::Element] root The wrapper element.
187
+ #
188
+ # @return [Nokogiri::XML::Element] The same element.
189
+ #
190
+ def self.drop_placeholder_namespaces(root)
191
+ placeholders = root.namespace_definitions
192
+ root.traverse do |node|
193
+ node.namespace = nil if placeholders.include?(node.namespace)
194
+ next unless node.element?
195
+
196
+ drop_attribute_namespaces node, placeholders
197
+ end
198
+ root
199
+ end
200
+ private_class_method :drop_placeholder_namespaces
201
+
202
+ #
203
+ # Un-prefix the placeholder attributes of one element.
204
+ #
205
+ # Un-prefixing renames the attribute, so it can collide: an element
206
+ # carrying both target and xlink:target would keep two attributes
207
+ # called target, and Nokogiri rejects the result with "Attribute
208
+ # target redefined" -- the unparseable output this whole path
209
+ # exists to prevent. Drop the prefixed one instead.
210
+ #
211
+ # @param [Nokogiri::XML::Element] node The element.
212
+ # @param [Array<Nokogiri::XML::Namespace>] placeholders The
213
+ # wrapper's own declarations.
214
+ #
215
+ # @return [void]
216
+ #
217
+ def self.drop_attribute_namespaces(node, placeholders)
218
+ node.attribute_nodes.each do |attr|
219
+ next unless placeholders.include?(attr.namespace)
220
+
221
+ if plain_attribute?(node, attr.name) then attr.unlink
222
+ else attr.namespace = nil
223
+ end
224
+ end
225
+ end
226
+ private_class_method :drop_attribute_namespaces
44
227
 
45
- sanitize_children(fragment)
46
- fragment.children.map { |c| c.to_xml(encoding: "UTF-8") }.join
228
+ #
229
+ # Does the element already carry this attribute without a prefix?
230
+ #
231
+ # Nokogiri's Node#attribute matches on the name alone, so it finds
232
+ # the prefixed attribute itself and every un-prefixing would look
233
+ # like a collision. Match on the namespace as well.
234
+ #
235
+ # @param [Nokogiri::XML::Element] node The element.
236
+ # @param [String] name The un-prefixed attribute name.
237
+ #
238
+ # @return [Boolean] Whether the element carries it.
239
+ #
240
+ def self.plain_attribute?(node, name)
241
+ node.attribute_nodes.any? { |a| a.namespace.nil? && a.name == name }
47
242
  end
243
+ private_class_method :plain_attribute?
48
244
 
49
245
  def self.sanitizable?(content)
50
246
  content.is_a?(::String) && !content.empty? && content.match?(TAG_RX)
data/lib/relaton/bib.rb CHANGED
@@ -24,8 +24,6 @@ require_relative "bib/model/relation"
24
24
  module Relaton
25
25
  # class Error < StandardError; end
26
26
 
27
- class RequestError < StandardError; end
28
-
29
27
  class << self
30
28
  #
31
29
  # Read schema versions from file