relaton 3.0.7 → 3.1.0

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/bin/relaton +20 -0
  3. data/lib/relaton/3gpp/processor.rb +1 -1
  4. data/lib/relaton/3gpp.rb +3 -3
  5. data/lib/relaton/adobe/processor.rb +1 -1
  6. data/lib/relaton/adobe.rb +3 -3
  7. data/lib/relaton/bib/item_data.rb +2 -2
  8. data/lib/relaton/bib/model/docidentifier.rb +17 -2
  9. data/lib/relaton/bib.rb +2 -2
  10. data/lib/relaton/bibcollection.rb +146 -0
  11. data/lib/relaton/bibdata.rb +92 -0
  12. data/lib/relaton/bipm/data_outcomes_parser.rb +6 -1
  13. data/lib/relaton/bipm/processor.rb +1 -1
  14. data/lib/relaton/bipm.rb +3 -3
  15. data/lib/relaton/bsi/processor.rb +1 -1
  16. data/lib/relaton/bsi.rb +1 -1
  17. data/lib/relaton/calconnect/data_fetcher.rb +2 -2
  18. data/lib/relaton/calconnect/model/item.rb +1 -1
  19. data/lib/relaton/calconnect/processor.rb +1 -1
  20. data/lib/relaton/calconnect.rb +2 -2
  21. data/lib/relaton/ccsds/data/fetcher.rb +1 -1
  22. data/lib/relaton/ccsds/model/item.rb +1 -1
  23. data/lib/relaton/ccsds/processor.rb +1 -1
  24. data/lib/relaton/ccsds/util.rb +1 -1
  25. data/lib/relaton/ccsds.rb +1 -1
  26. data/lib/relaton/cen/processor.rb +1 -1
  27. data/lib/relaton/cen.rb +2 -2
  28. data/lib/relaton/cie/data_fetcher.rb +3 -3
  29. data/lib/relaton/cie/processor.rb +1 -1
  30. data/lib/relaton/cie.rb +2 -2
  31. data/lib/relaton/cli/base_convertor.rb +134 -0
  32. data/lib/relaton/cli/command.rb +396 -0
  33. data/lib/relaton/cli/data_fetcher.rb +20 -0
  34. data/lib/relaton/cli/frontend/dist/404.html +18 -0
  35. data/lib/relaton/cli/frontend/dist/app.iife.js +1 -0
  36. data/lib/relaton/cli/frontend/dist/style.css +3 -0
  37. data/lib/relaton/cli/frontend/index.html +14 -0
  38. data/lib/relaton/cli/frontend/package-lock.json +2945 -0
  39. data/lib/relaton/cli/frontend/package.json +29 -0
  40. data/lib/relaton/cli/frontend/public/404.html +18 -0
  41. data/lib/relaton/cli/frontend/src/App.test.ts +501 -0
  42. data/lib/relaton/cli/frontend/src/App.vue +506 -0
  43. data/lib/relaton/cli/frontend/src/app.ts +90 -0
  44. data/lib/relaton/cli/frontend/src/components/DocumentDetail.test.ts +142 -0
  45. data/lib/relaton/cli/frontend/src/components/DocumentDetail.vue +228 -0
  46. data/lib/relaton/cli/frontend/src/components/DocumentRow.vue +88 -0
  47. data/lib/relaton/cli/frontend/src/components/Icon.test.ts +44 -0
  48. data/lib/relaton/cli/frontend/src/components/Icon.vue +55 -0
  49. data/lib/relaton/cli/frontend/src/components/Pagination.vue +26 -0
  50. data/lib/relaton/cli/frontend/src/dev/main.ts +100 -0
  51. data/lib/relaton/cli/frontend/src/dev/sample-data.json +71 -0
  52. data/lib/relaton/cli/frontend/src/lib/filter.test.ts +88 -0
  53. data/lib/relaton/cli/frontend/src/lib/filter.ts +95 -0
  54. data/lib/relaton/cli/frontend/src/lib/hydrate.test.ts +102 -0
  55. data/lib/relaton/cli/frontend/src/lib/hydrate.ts +55 -0
  56. data/lib/relaton/cli/frontend/src/lib/shards.test.ts +319 -0
  57. data/lib/relaton/cli/frontend/src/lib/shards.ts +183 -0
  58. data/lib/relaton/cli/frontend/src/lib/types.ts +148 -0
  59. data/lib/relaton/cli/frontend/src/lib/url.test.ts +151 -0
  60. data/lib/relaton/cli/frontend/src/lib/url.ts +111 -0
  61. data/lib/relaton/cli/frontend/src/shims-vue.d.ts +5 -0
  62. data/lib/relaton/cli/frontend/src/styles/index.css +27 -0
  63. data/lib/relaton/cli/frontend/src/test/fetch.ts +58 -0
  64. data/lib/relaton/cli/frontend/tsconfig.json +20 -0
  65. data/lib/relaton/cli/frontend/vite.config.ts +42 -0
  66. data/lib/relaton/cli/frontend/vitest.config.ts +11 -0
  67. data/lib/relaton/cli/frontend_assets.rb +58 -0
  68. data/lib/relaton/cli/full_text_search.rb +98 -0
  69. data/lib/relaton/cli/i18n.yaml +1 -0
  70. data/lib/relaton/cli/index_item_normalizer.rb +380 -0
  71. data/lib/relaton/cli/index_site_generator.rb +1043 -0
  72. data/lib/relaton/cli/relaton_file.rb +290 -0
  73. data/lib/relaton/cli/subcommand_collection.rb +263 -0
  74. data/lib/relaton/cli/subcommand_db.rb +81 -0
  75. data/lib/relaton/cli/templates/_document.liquid +111 -0
  76. data/lib/relaton/cli/templates/_index.liquid +61 -0
  77. data/lib/relaton/cli/templates/index/page.liquid +20 -0
  78. data/lib/relaton/cli/templates/index-style.css +1017 -0
  79. data/lib/relaton/cli/util.rb +8 -0
  80. data/lib/relaton/cli/xml_convertor.rb +47 -0
  81. data/lib/relaton/cli/xml_to_html_renderer.rb +117 -0
  82. data/lib/relaton/cli/yaml_convertor.rb +102 -0
  83. data/lib/relaton/cli.rb +103 -0
  84. data/lib/relaton/core/processor.rb +1 -1
  85. data/lib/relaton/core.rb +1 -1
  86. data/lib/relaton/db/registry.rb +1 -1
  87. data/lib/relaton/db.rb +1 -1
  88. data/lib/relaton/doi/CLAUDE.md +1 -1
  89. data/lib/relaton/doi/parser.rb +2 -1
  90. data/lib/relaton/doi/processor.rb +1 -1
  91. data/lib/relaton/doi.rb +4 -4
  92. data/lib/relaton/easc/processor.rb +1 -1
  93. data/lib/relaton/easc.rb +3 -3
  94. data/lib/relaton/ecma/data_fetcher.rb +1 -1
  95. data/lib/relaton/ecma/processor.rb +1 -1
  96. data/lib/relaton/ecma.rb +2 -2
  97. data/lib/relaton/element_finder.rb +29 -0
  98. data/lib/relaton/etsi/data_fetcher.rb +2 -2
  99. data/lib/relaton/etsi/item.rb +1 -1
  100. data/lib/relaton/etsi/processor.rb +2 -2
  101. data/lib/relaton/etsi.rb +1 -1
  102. data/lib/relaton/evs/processor.rb +1 -1
  103. data/lib/relaton/evs.rb +2 -2
  104. data/lib/relaton/gb/CLAUDE.md +1 -1
  105. data/lib/relaton/gb/bibliography.rb +1 -1
  106. data/lib/relaton/gb/processor.rb +1 -1
  107. data/lib/relaton/gb.rb +2 -2
  108. data/lib/relaton/gost/processor.rb +1 -1
  109. data/lib/relaton/gost.rb +3 -3
  110. data/lib/relaton/iala/processor.rb +1 -1
  111. data/lib/relaton/iala.rb +3 -3
  112. data/lib/relaton/iana/data_fetcher.rb +1 -1
  113. data/lib/relaton/iana/processor.rb +1 -1
  114. data/lib/relaton/iana.rb +2 -2
  115. data/lib/relaton/iec/data_fetcher.rb +1 -1
  116. data/lib/relaton/iec/processor.rb +1 -1
  117. data/lib/relaton/iec.rb +2 -2
  118. data/lib/relaton/ieee/processor.rb +1 -1
  119. data/lib/relaton/ieee.rb +2 -2
  120. data/lib/relaton/ietf/CLAUDE.md +1 -1
  121. data/lib/relaton/ietf/data_fetcher.rb +1 -1
  122. data/lib/relaton/ietf/processor.rb +1 -1
  123. data/lib/relaton/ietf.rb +2 -2
  124. data/lib/relaton/iho/CLAUDE.md +3 -3
  125. data/lib/relaton/iho/bibliography.rb +41 -27
  126. data/lib/relaton/iho/hash_parser_v1.rb +1 -1
  127. data/lib/relaton/iho/processor.rb +4 -3
  128. data/lib/relaton/iho.rb +2 -2
  129. data/lib/relaton/index.rb +1 -1
  130. data/lib/relaton/isbn/processor.rb +1 -1
  131. data/lib/relaton/isbn.rb +1 -1
  132. data/lib/relaton/iso/hash_parser_v1.rb +1 -1
  133. data/lib/relaton/iso/processor.rb +1 -1
  134. data/lib/relaton/iso.rb +3 -3
  135. data/lib/relaton/itu/processor.rb +1 -1
  136. data/lib/relaton/itu.rb +3 -3
  137. data/lib/relaton/jcgm/processor.rb +1 -1
  138. data/lib/relaton/jcgm.rb +2 -2
  139. data/lib/relaton/jis/processor.rb +1 -1
  140. data/lib/relaton/jis.rb +2 -2
  141. data/lib/relaton/nist/CLAUDE.md +11 -1
  142. data/lib/relaton/nist/README.adoc +7 -1
  143. data/lib/relaton/nist/hit_collection.rb +25 -1
  144. data/lib/relaton/nist/mods_parser.rb +32 -8
  145. data/lib/relaton/nist/processor.rb +1 -1
  146. data/lib/relaton/nist/pubs_export.rb +69 -11
  147. data/lib/relaton/nist.rb +2 -2
  148. data/lib/relaton/oasis/processor.rb +1 -1
  149. data/lib/relaton/oasis.rb +2 -2
  150. data/lib/relaton/ogc/CLAUDE.md +1 -1
  151. data/lib/relaton/ogc/processor.rb +1 -1
  152. data/lib/relaton/ogc.rb +2 -2
  153. data/lib/relaton/oiml/processor.rb +1 -1
  154. data/lib/relaton/oiml.rb +2 -2
  155. data/lib/relaton/omg/CLAUDE.md +1 -1
  156. data/lib/relaton/omg/processor.rb +1 -1
  157. data/lib/relaton/omg.rb +1 -1
  158. data/lib/relaton/plateau/CLAUDE.md +1 -1
  159. data/lib/relaton/plateau/data_fetcher.rb +1 -1
  160. data/lib/relaton/plateau/processor.rb +1 -1
  161. data/lib/relaton/plateau.rb +2 -2
  162. data/lib/relaton/render/general.rb +319 -0
  163. data/lib/relaton/render/iso690/csl_import.rb +129 -0
  164. data/lib/relaton/render/iso690/element.rb +153 -0
  165. data/lib/relaton/render/iso690/elements/access.rb +44 -0
  166. data/lib/relaton/render/iso690/elements/authorizer.rb +45 -0
  167. data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
  168. data/lib/relaton/render/iso690/elements/component_part.rb +97 -0
  169. data/lib/relaton/render/iso690/elements/creator.rb +267 -0
  170. data/lib/relaton/render/iso690/elements/edition.rb +97 -0
  171. data/lib/relaton/render/iso690/elements/extent.rb +71 -0
  172. data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
  173. data/lib/relaton/render/iso690/elements/location.rb +73 -0
  174. data/lib/relaton/render/iso690/elements/medium.rb +32 -0
  175. data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
  176. data/lib/relaton/render/iso690/elements/production.rb +111 -0
  177. data/lib/relaton/render/iso690/elements/pub_date.rb +63 -0
  178. data/lib/relaton/render/iso690/elements/series.rb +66 -0
  179. data/lib/relaton/render/iso690/elements/size.rb +45 -0
  180. data/lib/relaton/render/iso690/elements/status.rb +30 -0
  181. data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
  182. data/lib/relaton/render/iso690/elements/title.rb +56 -0
  183. data/lib/relaton/render/iso690/elements/updated.rb +38 -0
  184. data/lib/relaton/render/iso690/elements.rb +67 -0
  185. data/lib/relaton/render/iso690/fields.rb +142 -0
  186. data/lib/relaton/render/iso690/i18n/en.yml +48 -0
  187. data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
  188. data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
  189. data/lib/relaton/render/iso690/i18n.rb +83 -0
  190. data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
  191. data/lib/relaton/render/iso690/index_renderer.rb +57 -0
  192. data/lib/relaton/render/iso690/kinds.rb +53 -0
  193. data/lib/relaton/render/iso690/renderer.rb +150 -0
  194. data/lib/relaton/render/iso690/rules/bipm.rb +186 -0
  195. data/lib/relaton/render/iso690/rules/jis.rb +103 -0
  196. data/lib/relaton/render/iso690/rules/nist.rb +384 -0
  197. data/lib/relaton/render/iso690/rules.rb +227 -0
  198. data/lib/relaton/render/iso690/style/locale.rb +59 -0
  199. data/lib/relaton/render/iso690/style/name_form.rb +87 -0
  200. data/lib/relaton/render/iso690/style/scheme.rb +33 -0
  201. data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
  202. data/lib/relaton/render/iso690/style/template_map.rb +40 -0
  203. data/lib/relaton/render/iso690/style/type_template.rb +45 -0
  204. data/lib/relaton/render/iso690/style.rb +227 -0
  205. data/lib/relaton/render/iso690/styles/apa.yml +45 -0
  206. data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
  207. data/lib/relaton/render/iso690/styles/chicago.yml +49 -0
  208. data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
  209. data/lib/relaton/render/iso690/styles/jis-en.yml +68 -0
  210. data/lib/relaton/render/iso690/styles/jis-ja.yml +67 -0
  211. data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
  212. data/lib/relaton/render/iso690/styles/mla.yml +56 -0
  213. data/lib/relaton/render/iso690/template.rb +248 -0
  214. data/lib/relaton/render/iso690.rb +28 -0
  215. data/lib/relaton/sae/CLAUDE.md +89 -0
  216. data/lib/relaton/sae/bibdata.rb +8 -0
  217. data/lib/relaton/sae/bibitem.rb +8 -0
  218. data/lib/relaton/sae/bibliography.rb +128 -0
  219. data/lib/relaton/sae/data_fetcher.rb +219 -0
  220. data/lib/relaton/sae/doi_code.rb +83 -0
  221. data/lib/relaton/sae/item.rb +10 -0
  222. data/lib/relaton/sae/item_data.rb +6 -0
  223. data/lib/relaton/sae/processor.rb +62 -0
  224. data/lib/relaton/sae/util.rb +9 -0
  225. data/lib/relaton/sae.rb +37 -0
  226. data/lib/relaton/un/CLAUDE.md +1 -1
  227. data/lib/relaton/un/processor.rb +1 -1
  228. data/lib/relaton/un.rb +1 -1
  229. data/lib/relaton/version.rb +1 -1
  230. data/lib/relaton/w3c/bibliography.rb +1 -1
  231. data/lib/relaton/w3c/data_fetcher.rb +1 -1
  232. data/lib/relaton/w3c/processor.rb +1 -1
  233. data/lib/relaton/w3c.rb +2 -2
  234. data/lib/relaton/xsf/data_fetcher.rb +1 -1
  235. data/lib/relaton/xsf/processor.rb +1 -1
  236. data/lib/relaton/xsf.rb +2 -2
  237. data/lib/relaton-cli.rb +17 -0
  238. data/lib/relaton-render.rb +12 -0
  239. data/lib/relaton.rb +3 -2
  240. metadata +203 -23
@@ -0,0 +1,134 @@
1
+ require "fileutils"
2
+ require_relative "../bibdata"
3
+ require_relative "../bibcollection"
4
+ require_relative "xml_to_html_renderer"
5
+
6
+ module Relaton
7
+ module Cli
8
+ class BaseConvertor
9
+ def initialize(file, options = {})
10
+ @file = file
11
+ @options = options
12
+ @outdir = options.fetch(:outdir, nil)
13
+ @writable = options.fetch(:write, true)
14
+ @overwrite = options.fetch(:overwrite, true)
15
+ @default_filelabel = 0
16
+
17
+ install_dependencies(options[:require] || [])
18
+ end
19
+
20
+ # @return [String] HTML
21
+ def to_html
22
+ content = convert_to_html
23
+ write_to_a_file(content)
24
+ end
25
+
26
+ # Convert to HTML
27
+ #
28
+ # This interface expect us to provide Relaton collection XML
29
+ # as XML/RXL, and necessary styels / templates then it will be
30
+ # used convert that collection to HTML.
31
+ #
32
+ # @param file [String] Relaton collection file path
33
+ # @param style [String] Stylesheet file path for styles
34
+ # @param template [String] The liquid tempalte directory
35
+ #
36
+ # @return [String] HTML
37
+ def self.to_html(file, style = nil, template = nil)
38
+ new(
39
+ file,
40
+ style: style || File.join(File.dirname(__FILE__), "templates/index-style.css"),
41
+ template: template || File.join(File.dirname(__FILE__), "templates/"),
42
+ extension: "html"
43
+ ).to_html
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :file, :outdir, :options, :writable, :overwrite
49
+
50
+ # @return [String] HTML
51
+ def convert_to_html
52
+ Relaton::Cli::XmlToHtmlRenderer.render(
53
+ xml_content(file),
54
+ stylesheet: options[:style],
55
+ liquid_dir: options[:template]
56
+ )
57
+ end
58
+
59
+ # @param file [String] path to a file
60
+ # @return [String] the file's content
61
+ # @return [String] HTML
62
+ def xml_content(file)
63
+ File.read(file, encoding: "utf-8")
64
+ end
65
+
66
+ def install_dependencies(dependencies)
67
+ dependencies.each { |dependency| require(dependency) }
68
+ end
69
+
70
+ def item_output(content, format)
71
+ case format.to_sym
72
+ when :to_yaml then content.to_yaml
73
+ when :to_xml then content.to_xml(date_format: :full, bibdata: true)
74
+ end
75
+ end
76
+
77
+ def convert_and_write(content, format)
78
+ content = convert_content(content)
79
+ write_to_a_file(item_output(content, format))
80
+ write_to_file_collection(content, format.to_sym)
81
+ end
82
+
83
+ def write_to_a_file(content, outfile = nil)
84
+ outfile ||= Pathname.new(file).sub_ext(extension).to_s
85
+
86
+ if !File.exist?(outfile) || overwrite
87
+ File.open(outfile, "w:utf-8") do |file|
88
+ file.write(content)
89
+ end
90
+ end
91
+ end
92
+
93
+ def write_to_file_collection(content, format)
94
+ if outdir && content.is_a?(Relaton::Bibcollection)
95
+ FileUtils.mkdir_p(outdir)
96
+ content.items_flattened.each do |item|
97
+ collection = collection_filename(extract_docid(item))
98
+ write_to_a_file(item_output(item, format), collection)
99
+ end
100
+ end
101
+ end
102
+
103
+ def extract_docid(item)
104
+ @default_filelabel += 1
105
+ item.docidentifier.nil? && (return @default_filelabel.to_s)
106
+ # item.docidentifier.is_a?(Array) or return @default_filelabel.to_s
107
+ item.docidentifier.empty? && (return @default_filelabel.to_s)
108
+ docidentifier_code(item.docidentifier)
109
+ end
110
+
111
+ # From http://gavinmiller.io/2016/creating-a-secure-sanitization-function/
112
+ FILENAME_BAD_CHARS = ["/", '\\', "?", '%", "*", ":', "|", '"', "<", ">",
113
+ ".", " "].freeze
114
+
115
+ def docidentifier_code(docidentifier)
116
+ return "" if docidentifier.nil?
117
+
118
+ FILENAME_BAD_CHARS.reduce(docidentifier.downcase) do |result, bad_char|
119
+ result.gsub(bad_char, "-")
120
+ end
121
+ end
122
+
123
+ def extension
124
+ @extension ||= [".", options.fetch(:extension, default_ext)].join
125
+ end
126
+
127
+ def collection_filename(identifier)
128
+ File.join(
129
+ outdir, [@options[:prefix], identifier, extension].compact.join("")
130
+ )
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,396 @@
1
+ require "date"
2
+ require "tmpdir"
3
+ require "shellwords"
4
+ require "pubid"
5
+ require_relative "relaton_file"
6
+ require_relative "xml_convertor"
7
+ require_relative "yaml_convertor"
8
+ require_relative "data_fetcher"
9
+ require_relative "index_site_generator"
10
+ require_relative "subcommand_collection"
11
+ require_relative "subcommand_db"
12
+ require "fcntl"
13
+
14
+ module Relaton
15
+ module Cli
16
+ class Command < Thor
17
+ include Relaton::Cli
18
+ class_before :relaton_config
19
+ class_option :verbose, aliases: :v, type: :boolean, desc: "Output warnings"
20
+
21
+ # Exit non-zero when Thor itself rejects an invocation (unknown argument,
22
+ # wrong arity, a raised Thor::Error). Thor's legacy default is to print the
23
+ # error and exit **0**, which in CI reads as success: a caller passing a
24
+ # removed flag would write no site at all and still go green. The Pages
25
+ # deploy in relaton/support runs unattended across ~29 data repos, so a
26
+ # silent green there publishes an empty index nobody notices.
27
+ def self.exit_on_failure?
28
+ true
29
+ end
30
+
31
+ desc "version", "Show Relaton version"
32
+
33
+ def version
34
+ Relaton::Cli.version
35
+ end
36
+
37
+ desc "types", "List the supported flavors (--type values of relaton fetch)"
38
+
39
+ def types
40
+ puts registered_types.sort.join(", ")
41
+ end
42
+
43
+ desc "fetch CODE", "Fetch Relaton XML for Standard identifier CODE"
44
+ option :type, aliases: :t,
45
+ desc: "Type of standard to get bibliographic entry for"
46
+ option :format, aliases: :f,
47
+ desc: "Output format (xml, yaml, bibtex). Default xml."
48
+ option :year, aliases: :y, type: :numeric, desc: "Year the standard was published"
49
+ option :"all-parts", type: :boolean, desc: "Fetch all parts"
50
+ option :"keep-year", type: :boolean,
51
+ desc: "Undated reference should return actual reference with year"
52
+ option :retries, aliases: :r, type: :numeric,
53
+ desc: "Number of network retries. Default 1."
54
+ option :"no-cache", type: :boolean, desc: "Ignore cache"
55
+ option :"publication-date-before",
56
+ desc: "Fetch only documents published before the specified date " \
57
+ "(e.g. 2008, 2008-02, or 2008-02-02)"
58
+ option :"publication-date-after",
59
+ desc: "Fetch only documents published after the specified date " \
60
+ "(e.g. 2002, 2002-01, or 2002-01-01)"
61
+
62
+ def fetch(code)
63
+ io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
64
+ io.puts(fetch_document(code, options) || supported_type_message)
65
+ end
66
+
67
+ desc "Metanorma-XML-File / Directory Relaton-XML-Directory",
68
+ "Export Relaton XML from a Metanorma XML file / directory"
69
+ option :extension, aliases: :x, default: "rxl", desc: "File extension of Relaton XML files, " \
70
+ "defaults to 'rxl'"
71
+
72
+ def extract(source_dir, outdir)
73
+ Relaton::Cli::RelatonFile.extract(source_dir, outdir, options)
74
+ end
75
+
76
+ desc "concatenate SOURCE-DIR COLLECTION-FILE",
77
+ "Concatenate entries in DIRECTORY (containing Relaton-XML or YAML) into a Relaton Collection"
78
+ option :title, aliases: :t, desc: "Title of resulting Relaton collection"
79
+ option :organization, aliases: :g, desc: "Organization owner of Relaton collection"
80
+ option :extension, aliases: :x, desc: "File extension of destination " \
81
+ "Relaton file, defaults to 'rxl'"
82
+
83
+ def concatenate(source_dir, outfile)
84
+ Relaton::Cli::RelatonFile.concatenate(source_dir, outfile, options)
85
+ end
86
+
87
+ desc "split Relaton-Collection-File Relaton-XML-Directory",
88
+ "Split a Relaton Collection into multiple files"
89
+ option :extension, aliases: :x, default: "rxl", desc: "File extension of Relaton XML files, " \
90
+ "defaults to 'rxl'"
91
+
92
+ def split(source, outdir)
93
+ Relaton::Cli::RelatonFile.split(source, outdir, options)
94
+ end
95
+
96
+ desc "yaml2xml YAML", "Convert Relaton YAML into Relaton Collection XML " \
97
+ "or separate files"
98
+ option :extension, aliases: :x, default: "rxl", desc: "File extension of Relaton XML files, " \
99
+ "defaults to 'rxl'"
100
+ option :prefix, aliases: :p, desc: "Filename prefix of individual " \
101
+ "Relaton XML files, defaults to empty"
102
+ option :outdir, aliases: :o, desc: "Output to the specified directory " \
103
+ "with individual Relaton Bibdata XML files"
104
+ option :require, aliases: :r, type: :array, desc: "Require LIBRARY " \
105
+ "prior to execution"
106
+ option :overwrite, aliases: :f, type: :boolean, default: false,
107
+ desc: "Overwrite the existing file"
108
+
109
+ def yaml2xml(filename)
110
+ Relaton::Cli::YAMLConvertor.to_xml(filename, options)
111
+ end
112
+
113
+ desc "xml2yaml XML", "Convert Relaton XML into Relaton Bibdata / " \
114
+ "Bibcollection YAML (and separate files)"
115
+ option :extension, aliases: :x, default: "yaml", desc: "File extension of Relaton YAML files, " \
116
+ "defaults to 'yaml'"
117
+ option :prefix, aliases: :p, desc: "Filename prefix of Relaton XML " \
118
+ "files, defaults to empty"
119
+ option :outdir, aliases: :o, desc: "Output to the specified directory " \
120
+ "with individual Relaton Bibdata YAML files"
121
+ option :require, aliases: :r, type: :array, desc: "Require LIBRARY " \
122
+ "prior to execution"
123
+ option :overwrite, aliases: :f, type: :boolean, default: false,
124
+ desc: "Overwrite the existing file"
125
+
126
+ def xml2yaml(filename)
127
+ Relaton::Cli::XMLConvertor.to_yaml(filename, options)
128
+ end
129
+
130
+ desc "xml2html RELATON-INDEX-XML", "Convert Relaton Collection XML into HTML"
131
+ option :stylesheet, aliases: :s, desc: "Stylesheet file path for " \
132
+ "rendering HTML index"
133
+ option :templatedir, aliases: :t, desc: "Liquid template directory for " \
134
+ "rendering Relaton items and collection"
135
+ option :overwrite, aliases: :f, type: :boolean, default: false,
136
+ desc: "Overwrite the existing file"
137
+
138
+ def xml2html(file, style = nil, template = nil)
139
+ Relaton::Cli::XMLConvertor.to_html(file, style, template)
140
+ end
141
+
142
+ desc "yaml2html RELATON-INDEX-YAML", "Concatenate Relaton Collection " \
143
+ "YAML into HTML"
144
+ option :stylesheet, aliases: :s, desc: "Stylesheet file path for " \
145
+ "rendering HTML index"
146
+ option :templatedir, aliases: :t, desc: "Liquid template directory for " \
147
+ "rendering Relaton items and collection"
148
+ option :overwrite, aliases: :f, type: :boolean, default: false,
149
+ desc: "Overwrite the existing file"
150
+
151
+ def yaml2html(file, style = nil, template = nil)
152
+ Relaton::Cli::YAMLConvertor.to_html(file, style, template)
153
+ end
154
+
155
+ desc "index [DATA-DIR]",
156
+ "Build a browsable HTML index site from a folder of Relaton YAML docs (default ./data)"
157
+ option :output, aliases: :o, default: "_site",
158
+ desc: "Output directory for the generated site"
159
+ option :flavor,
160
+ desc: "Build from relaton/relaton-data-<flavor> (shallow-cloned) instead of a local folder"
161
+ option :repo,
162
+ desc: "Build from a GitHub repo ORG/NAME (shallow-cloned) instead of a local folder"
163
+ option :branch, desc: "Branch to clone when --flavor/--repo is used"
164
+ option :title, aliases: :t, desc: "Index page title"
165
+ option :description,
166
+ desc: "Index page description (<meta name=\"description\"> and the header subtitle)"
167
+ option :favicon,
168
+ desc: "Favicon href for <link rel=\"icon\">, used verbatim (a relative " \
169
+ "path must be placed in the deployed site by the caller)"
170
+ option :"base-url",
171
+ desc: "Base URL for raw YAML links (defaults to the raw GitHub URL when cloning)"
172
+ option :"shard-size", type: :numeric, default: 5000,
173
+ desc: "Summary records per search-NNNN.json shard"
174
+ option :"detail-shard-size", type: :numeric, default: 500,
175
+ desc: "Detail records per detail-NNNN.json shard"
176
+ option :detail, type: :boolean, default: true,
177
+ desc: "Emit the detail shards the document panel fetches; --no-detail disables"
178
+ option :static, type: :boolean, default: true,
179
+ desc: "Also index a sibling static/ folder next to DATA-DIR (auto-detected); --no-static disables"
180
+ option :overwrite, aliases: :f, type: :boolean, default: true,
181
+ desc: "Overwrite existing output files"
182
+ option :"publish-data", type: :boolean, default: false,
183
+ desc: "Copy the corpus onto the site so document fetches " \
184
+ "share the Pages origin (for repos whose data is " \
185
+ "served only from there); --no-publish-data disables"
186
+ option :"pubid-flavor",
187
+ desc: "Flavor whose pubid Identifier parses docids and whose INDEXFILE " \
188
+ "names the published index (e.g. iso, iho, iec). Required unless " \
189
+ "--no-machine-index"
190
+ option :"machine-index", type: :boolean, default: true,
191
+ desc: "Emit the machine-consumable index (index/manifest.json, " \
192
+ "index/shard-NNNNN.json and the index-vN monolith); " \
193
+ "--no-machine-index builds the human site only"
194
+ option :"index-name",
195
+ desc: "Base name for the published index, overriding the flavor's " \
196
+ "INDEXFILE (e.g. index-v2). For a corpus that is not a relaton flavor"
197
+
198
+ def index(data_dir = nil)
199
+ with_index_source(data_dir) do |dir, default_base_url, default_title|
200
+ Relaton::Cli::IndexSiteGenerator.generate(
201
+ dir,
202
+ output: options[:output],
203
+ shard_size: options[:"shard-size"].to_i,
204
+ detail_shard_size: options[:"detail-shard-size"].to_i,
205
+ detail: options[:detail],
206
+ title: options[:title] || default_title,
207
+ description: options[:description],
208
+ favicon: options[:favicon],
209
+ base_url: options[:"base-url"] || default_base_url,
210
+ static: options[:static],
211
+ overwrite: options[:overwrite],
212
+ publish_data: options[:"publish-data"],
213
+ flavor: options[:"pubid-flavor"],
214
+ machine_index: options[:"machine-index"],
215
+ index_name: options[:"index-name"],
216
+ )
217
+ end
218
+ end
219
+
220
+ desc "convert XML", "Convert Relaton XML document"
221
+ option :format, aliases: :f, required: true,
222
+ desc: "Output format (yaml, bibtex, asciibib, citation)"
223
+ # Plain-text citation rendering via relaton-render's CitationStyle
224
+ # instances (relaton-cli#94): the style names a Style YAML, the engine
225
+ # stays style-free.
226
+ option :style, desc: "Citation style for --format citation " \
227
+ "(author-date, apa, chicago, mla, ieee-sa, lncs; a Style YAML path " \
228
+ "also works). Default: author-date"
229
+ option :output, aliases: :o, desc: "Output to the specified file"
230
+
231
+ def convert(file) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
232
+ xml = Nokogiri::XML(File.read(file, encoding: "UTF-8"))
233
+ item = Relaton::Cli.parse_xml xml
234
+ result = if /yaml|yml/.match?(options[:format])
235
+ item.to_yaml
236
+ elsif options[:format] == "citation"
237
+ # The paths that exist in every published relaton-render;
238
+ # the gem's conventional "relaton/render" entry is newer.
239
+ require_relative "../render/general"
240
+ require_relative "../render/iso690"
241
+ Relaton::Render::General
242
+ .new(style: options[:style] || "author-date")
243
+ .render(item, embedded: true)
244
+ else item.send "to_#{options[:format]}"
245
+ end
246
+ ext = case options[:format]
247
+ when "bibtex" then "bib"
248
+ when "asciibib" then "adoc"
249
+ when "citation" then "txt"
250
+ else options[:format]
251
+ end
252
+ output = options[:output] || file.sub(/(?<=\.)[^.]+$/, ext)
253
+ File.write output, result, encoding: "UTF-8"
254
+ end
255
+
256
+ desc "fetch-data SOURCE", "Fetch all the documents from a source"
257
+ option :output, aliases: :o, desc: "Output dir. Default: ./data/"
258
+ option :format, aliases: :f, desc: "Output format (yaml, xml, bibxml). Default: yaml"
259
+
260
+ def fetch_data(source)
261
+ DataFetcher.fetch source, options
262
+ end
263
+
264
+ desc "collection SUBCOMMAND", "Collection manipulations"
265
+ subcommand "collection", SubcommandCollection
266
+
267
+ desc "db SUBCOMMAND", "Cache DB manipulation"
268
+ subcommand "db", SubcommandDb
269
+
270
+ no_commands do
271
+ def relaton_config
272
+ Relaton::Logger.configure do |conf|
273
+ if options[:verbose]
274
+ conf.logger_pool[:default].add_level :info
275
+ else
276
+ conf.logger_pool[:default].remove_level :info
277
+ end
278
+ end
279
+ end
280
+
281
+ # Resolve the source folder for `index`. Locally it's just DATA-DIR
282
+ # (default ./data). With --flavor/--repo it shallow-clones the GitHub
283
+ # repo into a temp dir and yields its data/ subfolder plus sensible
284
+ # defaults for the raw-YAML base URL and page title. The temp clone is
285
+ # removed after the block returns.
286
+ def with_index_source(data_dir)
287
+ subdir = data_dir || "data"
288
+ if options[:flavor] || options[:repo]
289
+ repo = options[:repo] || "relaton/relaton-data-#{options[:flavor]}"
290
+ flavor = options[:flavor] ||
291
+ repo.split("/").last.sub(/\Arelaton-data-/, "")
292
+ Dir.mktmpdir("relaton-index-") do |tmp|
293
+ branch = clone_data_repo(repo, options[:branch], tmp)
294
+ base = "https://raw.githubusercontent.com/#{repo}/#{branch}"
295
+ yield File.join(tmp, subdir), base, "#{flavor.upcase} Index"
296
+ end
297
+ else
298
+ yield subdir, nil, nil
299
+ end
300
+ end
301
+
302
+ # Shallow-clone ORG/NAME into `dest`; return the checked-out branch name.
303
+ def clone_data_repo(repo, branch, dest)
304
+ url = "https://github.com/#{repo}.git"
305
+ cmd = ["git", "clone", "--depth", "1", "--single-branch"]
306
+ cmd += ["--branch", branch] if branch
307
+ cmd += [url, dest]
308
+ Relaton::Cli::Util.info "Cloning #{url}…"
309
+ unless system(*cmd, out: File::NULL, err: File::NULL)
310
+ raise Thor::Error,
311
+ "Failed to clone #{url}. Check the repo/branch name and that git is installed."
312
+ end
313
+ branch || `git -C #{Shellwords.escape(dest)} rev-parse --abbrev-ref HEAD`.strip
314
+ end
315
+ end
316
+ end
317
+
318
+ private
319
+
320
+ DATE_FILTER_FORMAT = /\A\d{4}(-\d{2}(-\d{2})?)?\z/
321
+
322
+ def parse_date_option(value, name)
323
+ return unless value
324
+
325
+ unless value.match?(DATE_FILTER_FORMAT)
326
+ raise ArgumentError,
327
+ "Invalid #{name}: #{value.inspect}. Expected YYYY, YYYY-MM, or YYYY-MM-DD."
328
+ end
329
+ parts = value.split("-").map(&:to_i)
330
+ Date.new(*parts.concat([1] * (3 - parts.size)))
331
+ rescue Date::Error
332
+ raise ArgumentError,
333
+ "Invalid #{name}: #{value.inspect}. Date components are out of range."
334
+ end
335
+
336
+ def validate_date_range(date_after, date_before)
337
+ return unless date_after && date_before
338
+ return if date_after < date_before
339
+
340
+ raise ArgumentError,
341
+ "Invalid date range: --publication-date-after (#{date_after}) must be before --publication-date-before (#{date_before})."
342
+ end
343
+
344
+ # @param code [String]
345
+ # @param options [Hash]
346
+ # @option options [String] :type
347
+ # @option options [String, NilClass] :format
348
+ # @option options [Integer, NilClass] :year
349
+ # @return [String, nil]
350
+ def fetch_document(code, options) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/AbcSize,Metrics/MethodLength
351
+ year = options[:year]&.to_s
352
+ dup_opts = options.dup.transform_keys { |k| k.to_s.gsub("-", "_").to_sym }
353
+ %i[publication_date_before publication_date_after].each do |key|
354
+ dup_opts[key] = parse_date_option(dup_opts[key], key.to_s.tr("_", "-").prepend("--")) if dup_opts[key]
355
+ end
356
+ validate_date_range dup_opts[:publication_date_after], dup_opts[:publication_date_before]
357
+ if (processor = Relaton::Db::Registry.instance.by_type options[:type]&.upcase)
358
+ doc = Relaton.db.fetch_std code, year, processor.short, **dup_opts
359
+ elsif options[:type] then return
360
+ else doc = Relaton.db.fetch(code, year, **dup_opts)
361
+ end
362
+ return "No matching bibliographic entry found" unless doc
363
+
364
+ serialize doc, options[:format]
365
+ rescue Relaton::UnknownReferenceError => e
366
+ # No flavor recognizes the reference: log it, return no document (the
367
+ # command then prints the supported types).
368
+ Util.warn e.message
369
+ nil
370
+ rescue Pubid::Errors::Error
371
+ %("#{code}" is not a recognized standards identifier)
372
+ rescue Relaton::RequestError => e
373
+ e.message
374
+ end
375
+
376
+ # @param doc [Relaton::Bib::ItemData]
377
+ # @param format [String]
378
+ # @return [String]
379
+ def serialize(doc, format)
380
+ case format
381
+ when "yaml", "yml" then doc.to_yaml
382
+ when "bibtex" then doc.to_bibtex
383
+ else doc.to_xml bibdata: true
384
+ end
385
+ end
386
+
387
+ def supported_type_message
388
+ ["Recognised types:", registered_types.sort.join(", ")].join(" ")
389
+ end
390
+
391
+ def registered_types
392
+ @registered_types ||=
393
+ Relaton::Db::Registry.instance.processors.each.map { |_n, pr| pr.prefix }
394
+ end
395
+ end
396
+ end
@@ -0,0 +1,20 @@
1
+ module Relaton
2
+ module Cli
3
+ module DataFetcher
4
+ def fetch(source, options)
5
+ processor = Relaton::Db::Registry.instance.find_processor_by_dataset source
6
+ unless processor
7
+ Util.warn "no processor found for `#{source}`"
8
+ return
9
+ end
10
+
11
+ opts = {}
12
+ opts[:output] = options[:output] if options[:output]
13
+ opts[:format] = options[:format] if options[:format]
14
+ processor.fetch_data source, opts
15
+ end
16
+
17
+ extend DataFetcher
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Redirecting…</title>
6
+ <script>
7
+ // GitHub Pages has no URL rewrites, so path-based document URLs
8
+ // (/doc/<id>) land on this 404 fallback. Hand the id to the app as
9
+ // ?doc=<id>, preserving the site's base path wherever it is hosted.
10
+ (function () {
11
+ var m = location.pathname.match(/\/doc\/([^/?#]+)$/);
12
+ var root = m ? location.pathname.slice(0, m.index) : location.pathname.replace(/[^/]*$/, "");
13
+ location.replace(root + "?doc=" + (m ? m[1] : ""));
14
+ })();
15
+ </script>
16
+ </head>
17
+ <body></body>
18
+ </html>