relaton-cli 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.
- checksums.yaml +4 -4
- data/lib/relaton-cli.rb +4 -16
- metadata +11 -123
- data/.gitignore +0 -22
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/CLAUDE.md +0 -524
- data/Gemfile +0 -42
- data/Rakefile +0 -21
- data/bin/console +0 -11
- data/bin/rspec +0 -29
- data/bin/setup +0 -8
- data/docs/README.adoc +0 -589
- data/exe/relaton +0 -20
- data/frontend/dist/404.html +0 -18
- data/frontend/dist/app.iife.js +0 -1
- data/frontend/dist/style.css +0 -3
- data/i18n.yaml +0 -1
- data/lib/relaton/bibcollection.rb +0 -146
- data/lib/relaton/bibdata.rb +0 -92
- data/lib/relaton/cli/base_convertor.rb +0 -134
- data/lib/relaton/cli/command.rb +0 -390
- data/lib/relaton/cli/data_fetcher.rb +0 -20
- data/lib/relaton/cli/frontend_assets.rb +0 -58
- data/lib/relaton/cli/full_text_search.rb +0 -98
- data/lib/relaton/cli/index_item_normalizer.rb +0 -380
- data/lib/relaton/cli/index_site_generator.rb +0 -1008
- data/lib/relaton/cli/relaton_file.rb +0 -290
- data/lib/relaton/cli/subcommand_collection.rb +0 -263
- data/lib/relaton/cli/subcommand_db.rb +0 -81
- data/lib/relaton/cli/util.rb +0 -8
- data/lib/relaton/cli/version.rb +0 -11
- data/lib/relaton/cli/xml_convertor.rb +0 -47
- data/lib/relaton/cli/xml_to_html_renderer.rb +0 -117
- data/lib/relaton/cli/yaml_convertor.rb +0 -102
- data/lib/relaton/cli.rb +0 -113
- data/lib/relaton/element_finder.rb +0 -29
- data/relaton-cli.gemspec +0 -49
- data/templates/_document.liquid +0 -111
- data/templates/_index.liquid +0 -61
- data/templates/index/page.liquid +0 -20
- data/templates/index-style.css +0 -1017
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
require "fileutils"
|
|
2
|
-
require "relaton/bibdata"
|
|
3
|
-
require "relaton/bibcollection"
|
|
4
|
-
require "relaton/cli/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
|
data/lib/relaton/cli/command.rb
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
require "date"
|
|
2
|
-
require "tmpdir"
|
|
3
|
-
require "shellwords"
|
|
4
|
-
require "pubid"
|
|
5
|
-
require "relaton/cli/relaton_file"
|
|
6
|
-
require "relaton/cli/xml_convertor"
|
|
7
|
-
require "relaton/cli/yaml_convertor"
|
|
8
|
-
require "relaton/cli/data_fetcher"
|
|
9
|
-
require "relaton/cli/index_site_generator"
|
|
10
|
-
require "relaton/cli/subcommand_collection"
|
|
11
|
-
require "relaton/cli/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 "fetch CODE", "Fetch Relaton XML for Standard identifier CODE"
|
|
38
|
-
option :type, aliases: :t,
|
|
39
|
-
desc: "Type of standard to get bibliographic entry for"
|
|
40
|
-
option :format, aliases: :f,
|
|
41
|
-
desc: "Output format (xml, yaml, bibtex). Default xml."
|
|
42
|
-
option :year, aliases: :y, type: :numeric, desc: "Year the standard was published"
|
|
43
|
-
option :"all-parts", type: :boolean, desc: "Fetch all parts"
|
|
44
|
-
option :"keep-year", type: :boolean,
|
|
45
|
-
desc: "Undated reference should return actual reference with year"
|
|
46
|
-
option :retries, aliases: :r, type: :numeric,
|
|
47
|
-
desc: "Number of network retries. Default 1."
|
|
48
|
-
option :"no-cache", type: :boolean, desc: "Ignore cache"
|
|
49
|
-
option :"publication-date-before",
|
|
50
|
-
desc: "Fetch only documents published before the specified date " \
|
|
51
|
-
"(e.g. 2008, 2008-02, or 2008-02-02)"
|
|
52
|
-
option :"publication-date-after",
|
|
53
|
-
desc: "Fetch only documents published after the specified date " \
|
|
54
|
-
"(e.g. 2002, 2002-01, or 2002-01-01)"
|
|
55
|
-
|
|
56
|
-
def fetch(code)
|
|
57
|
-
io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
|
|
58
|
-
io.puts(fetch_document(code, options) || supported_type_message)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
desc "Metanorma-XML-File / Directory Relaton-XML-Directory",
|
|
62
|
-
"Export Relaton XML from a Metanorma XML file / directory"
|
|
63
|
-
option :extension, aliases: :x, default: "rxl", desc: "File extension of Relaton XML files, " \
|
|
64
|
-
"defaults to 'rxl'"
|
|
65
|
-
|
|
66
|
-
def extract(source_dir, outdir)
|
|
67
|
-
Relaton::Cli::RelatonFile.extract(source_dir, outdir, options)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
desc "concatenate SOURCE-DIR COLLECTION-FILE",
|
|
71
|
-
"Concatenate entries in DIRECTORY (containing Relaton-XML or YAML) into a Relaton Collection"
|
|
72
|
-
option :title, aliases: :t, desc: "Title of resulting Relaton collection"
|
|
73
|
-
option :organization, aliases: :g, desc: "Organization owner of Relaton collection"
|
|
74
|
-
option :extension, aliases: :x, desc: "File extension of destination " \
|
|
75
|
-
"Relaton file, defaults to 'rxl'"
|
|
76
|
-
|
|
77
|
-
def concatenate(source_dir, outfile)
|
|
78
|
-
Relaton::Cli::RelatonFile.concatenate(source_dir, outfile, options)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
desc "split Relaton-Collection-File Relaton-XML-Directory",
|
|
82
|
-
"Split a Relaton Collection into multiple files"
|
|
83
|
-
option :extension, aliases: :x, default: "rxl", desc: "File extension of Relaton XML files, " \
|
|
84
|
-
"defaults to 'rxl'"
|
|
85
|
-
|
|
86
|
-
def split(source, outdir)
|
|
87
|
-
Relaton::Cli::RelatonFile.split(source, outdir, options)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
desc "yaml2xml YAML", "Convert Relaton YAML into Relaton Collection XML " \
|
|
91
|
-
"or separate files"
|
|
92
|
-
option :extension, aliases: :x, default: "rxl", desc: "File extension of Relaton XML files, " \
|
|
93
|
-
"defaults to 'rxl'"
|
|
94
|
-
option :prefix, aliases: :p, desc: "Filename prefix of individual " \
|
|
95
|
-
"Relaton XML files, defaults to empty"
|
|
96
|
-
option :outdir, aliases: :o, desc: "Output to the specified directory " \
|
|
97
|
-
"with individual Relaton Bibdata XML files"
|
|
98
|
-
option :require, aliases: :r, type: :array, desc: "Require LIBRARY " \
|
|
99
|
-
"prior to execution"
|
|
100
|
-
option :overwrite, aliases: :f, type: :boolean, default: false,
|
|
101
|
-
desc: "Overwrite the existing file"
|
|
102
|
-
|
|
103
|
-
def yaml2xml(filename)
|
|
104
|
-
Relaton::Cli::YAMLConvertor.to_xml(filename, options)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
desc "xml2yaml XML", "Convert Relaton XML into Relaton Bibdata / " \
|
|
108
|
-
"Bibcollection YAML (and separate files)"
|
|
109
|
-
option :extension, aliases: :x, default: "yaml", desc: "File extension of Relaton YAML files, " \
|
|
110
|
-
"defaults to 'yaml'"
|
|
111
|
-
option :prefix, aliases: :p, desc: "Filename prefix of Relaton XML " \
|
|
112
|
-
"files, defaults to empty"
|
|
113
|
-
option :outdir, aliases: :o, desc: "Output to the specified directory " \
|
|
114
|
-
"with individual Relaton Bibdata YAML files"
|
|
115
|
-
option :require, aliases: :r, type: :array, desc: "Require LIBRARY " \
|
|
116
|
-
"prior to execution"
|
|
117
|
-
option :overwrite, aliases: :f, type: :boolean, default: false,
|
|
118
|
-
desc: "Overwrite the existing file"
|
|
119
|
-
|
|
120
|
-
def xml2yaml(filename)
|
|
121
|
-
Relaton::Cli::XMLConvertor.to_yaml(filename, options)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
desc "xml2html RELATON-INDEX-XML", "Convert Relaton Collection XML into HTML"
|
|
125
|
-
option :stylesheet, aliases: :s, desc: "Stylesheet file path for " \
|
|
126
|
-
"rendering HTML index"
|
|
127
|
-
option :templatedir, aliases: :t, desc: "Liquid template directory for " \
|
|
128
|
-
"rendering Relaton items and collection"
|
|
129
|
-
option :overwrite, aliases: :f, type: :boolean, default: false,
|
|
130
|
-
desc: "Overwrite the existing file"
|
|
131
|
-
|
|
132
|
-
def xml2html(file, style = nil, template = nil)
|
|
133
|
-
Relaton::Cli::XMLConvertor.to_html(file, style, template)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
desc "yaml2html RELATON-INDEX-YAML", "Concatenate Relaton Collection " \
|
|
137
|
-
"YAML into HTML"
|
|
138
|
-
option :stylesheet, aliases: :s, desc: "Stylesheet file path for " \
|
|
139
|
-
"rendering HTML index"
|
|
140
|
-
option :templatedir, aliases: :t, desc: "Liquid template directory for " \
|
|
141
|
-
"rendering Relaton items and collection"
|
|
142
|
-
option :overwrite, aliases: :f, type: :boolean, default: false,
|
|
143
|
-
desc: "Overwrite the existing file"
|
|
144
|
-
|
|
145
|
-
def yaml2html(file, style = nil, template = nil)
|
|
146
|
-
Relaton::Cli::YAMLConvertor.to_html(file, style, template)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
desc "index [DATA-DIR]",
|
|
150
|
-
"Build a browsable HTML index site from a folder of Relaton YAML docs (default ./data)"
|
|
151
|
-
option :output, aliases: :o, default: "_site",
|
|
152
|
-
desc: "Output directory for the generated site"
|
|
153
|
-
option :flavor,
|
|
154
|
-
desc: "Build from relaton/relaton-data-<flavor> (shallow-cloned) instead of a local folder"
|
|
155
|
-
option :repo,
|
|
156
|
-
desc: "Build from a GitHub repo ORG/NAME (shallow-cloned) instead of a local folder"
|
|
157
|
-
option :branch, desc: "Branch to clone when --flavor/--repo is used"
|
|
158
|
-
option :title, aliases: :t, desc: "Index page title"
|
|
159
|
-
option :description,
|
|
160
|
-
desc: "Index page description (<meta name=\"description\"> and the header subtitle)"
|
|
161
|
-
option :favicon,
|
|
162
|
-
desc: "Favicon href for <link rel=\"icon\">, used verbatim (a relative " \
|
|
163
|
-
"path must be placed in the deployed site by the caller)"
|
|
164
|
-
option :"base-url",
|
|
165
|
-
desc: "Base URL for raw YAML links (defaults to the raw GitHub URL when cloning)"
|
|
166
|
-
option :"shard-size", type: :numeric, default: 5000,
|
|
167
|
-
desc: "Summary records per search-NNNN.json shard"
|
|
168
|
-
option :"detail-shard-size", type: :numeric, default: 500,
|
|
169
|
-
desc: "Detail records per detail-NNNN.json shard"
|
|
170
|
-
option :detail, type: :boolean, default: true,
|
|
171
|
-
desc: "Emit the detail shards the document panel fetches; --no-detail disables"
|
|
172
|
-
option :static, type: :boolean, default: true,
|
|
173
|
-
desc: "Also index a sibling static/ folder next to DATA-DIR (auto-detected); --no-static disables"
|
|
174
|
-
option :overwrite, aliases: :f, type: :boolean, default: true,
|
|
175
|
-
desc: "Overwrite existing output files"
|
|
176
|
-
option :"publish-data", type: :boolean, default: false,
|
|
177
|
-
desc: "Copy the corpus onto the site so document fetches " \
|
|
178
|
-
"share the Pages origin (for repos whose data is " \
|
|
179
|
-
"served only from there); --no-publish-data disables"
|
|
180
|
-
option :"pubid-flavor",
|
|
181
|
-
desc: "Flavor whose pubid Identifier parses docids and whose INDEXFILE " \
|
|
182
|
-
"names the published index (e.g. iso, iho, iec). Required unless " \
|
|
183
|
-
"--no-machine-index"
|
|
184
|
-
option :"machine-index", type: :boolean, default: true,
|
|
185
|
-
desc: "Emit the machine-consumable index (index/manifest.json, " \
|
|
186
|
-
"index/shard-NNNNN.json and the index-vN monolith); " \
|
|
187
|
-
"--no-machine-index builds the human site only"
|
|
188
|
-
option :"index-name",
|
|
189
|
-
desc: "Base name for the published index, overriding the flavor's " \
|
|
190
|
-
"INDEXFILE (e.g. index-v2). For a corpus that is not a relaton flavor"
|
|
191
|
-
|
|
192
|
-
def index(data_dir = nil)
|
|
193
|
-
with_index_source(data_dir) do |dir, default_base_url, default_title|
|
|
194
|
-
Relaton::Cli::IndexSiteGenerator.generate(
|
|
195
|
-
dir,
|
|
196
|
-
output: options[:output],
|
|
197
|
-
shard_size: options[:"shard-size"].to_i,
|
|
198
|
-
detail_shard_size: options[:"detail-shard-size"].to_i,
|
|
199
|
-
detail: options[:detail],
|
|
200
|
-
title: options[:title] || default_title,
|
|
201
|
-
description: options[:description],
|
|
202
|
-
favicon: options[:favicon],
|
|
203
|
-
base_url: options[:"base-url"] || default_base_url,
|
|
204
|
-
static: options[:static],
|
|
205
|
-
overwrite: options[:overwrite],
|
|
206
|
-
publish_data: options[:"publish-data"],
|
|
207
|
-
flavor: options[:"pubid-flavor"],
|
|
208
|
-
machine_index: options[:"machine-index"],
|
|
209
|
-
index_name: options[:"index-name"],
|
|
210
|
-
)
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
desc "convert XML", "Convert Relaton XML document"
|
|
215
|
-
option :format, aliases: :f, required: true,
|
|
216
|
-
desc: "Output format (yaml, bibtex, asciibib, citation)"
|
|
217
|
-
# Plain-text citation rendering via relaton-render's CitationStyle
|
|
218
|
-
# instances (relaton-cli#94): the style names a Style YAML, the engine
|
|
219
|
-
# stays style-free.
|
|
220
|
-
option :style, desc: "Citation style for --format citation " \
|
|
221
|
-
"(author-date, apa, chicago, mla, ieee-sa, lncs; a Style YAML path " \
|
|
222
|
-
"also works). Default: author-date"
|
|
223
|
-
option :output, aliases: :o, desc: "Output to the specified file"
|
|
224
|
-
|
|
225
|
-
def convert(file) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
226
|
-
xml = Nokogiri::XML(File.read(file, encoding: "UTF-8"))
|
|
227
|
-
item = Relaton::Cli.parse_xml xml
|
|
228
|
-
result = if /yaml|yml/.match?(options[:format])
|
|
229
|
-
item.to_yaml
|
|
230
|
-
elsif options[:format] == "citation"
|
|
231
|
-
# The paths that exist in every published relaton-render;
|
|
232
|
-
# the gem's conventional "relaton/render" entry is newer.
|
|
233
|
-
require "relaton/render/general"
|
|
234
|
-
require "relaton/render/iso690"
|
|
235
|
-
Relaton::Render::General
|
|
236
|
-
.new(style: options[:style] || "author-date")
|
|
237
|
-
.render(item, embedded: true)
|
|
238
|
-
else item.send "to_#{options[:format]}"
|
|
239
|
-
end
|
|
240
|
-
ext = case options[:format]
|
|
241
|
-
when "bibtex" then "bib"
|
|
242
|
-
when "asciibib" then "adoc"
|
|
243
|
-
when "citation" then "txt"
|
|
244
|
-
else options[:format]
|
|
245
|
-
end
|
|
246
|
-
output = options[:output] || file.sub(/(?<=\.)[^.]+$/, ext)
|
|
247
|
-
File.write output, result, encoding: "UTF-8"
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
desc "fetch-data SOURCE", "Fetch all the documents from a source"
|
|
251
|
-
option :output, aliases: :o, desc: "Output dir. Default: ./data/"
|
|
252
|
-
option :format, aliases: :f, desc: "Output format (yaml, xml, bibxml). Default: yaml"
|
|
253
|
-
|
|
254
|
-
def fetch_data(source)
|
|
255
|
-
DataFetcher.fetch source, options
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
desc "collection SUBCOMMAND", "Collection manipulations"
|
|
259
|
-
subcommand "collection", SubcommandCollection
|
|
260
|
-
|
|
261
|
-
desc "db SUBCOMMAND", "Cache DB manipulation"
|
|
262
|
-
subcommand "db", SubcommandDb
|
|
263
|
-
|
|
264
|
-
no_commands do
|
|
265
|
-
def relaton_config
|
|
266
|
-
Relaton::Logger.configure do |conf|
|
|
267
|
-
if options[:verbose]
|
|
268
|
-
conf.logger_pool[:default].add_level :info
|
|
269
|
-
else
|
|
270
|
-
conf.logger_pool[:default].remove_level :info
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
# Resolve the source folder for `index`. Locally it's just DATA-DIR
|
|
276
|
-
# (default ./data). With --flavor/--repo it shallow-clones the GitHub
|
|
277
|
-
# repo into a temp dir and yields its data/ subfolder plus sensible
|
|
278
|
-
# defaults for the raw-YAML base URL and page title. The temp clone is
|
|
279
|
-
# removed after the block returns.
|
|
280
|
-
def with_index_source(data_dir)
|
|
281
|
-
subdir = data_dir || "data"
|
|
282
|
-
if options[:flavor] || options[:repo]
|
|
283
|
-
repo = options[:repo] || "relaton/relaton-data-#{options[:flavor]}"
|
|
284
|
-
flavor = options[:flavor] ||
|
|
285
|
-
repo.split("/").last.sub(/\Arelaton-data-/, "")
|
|
286
|
-
Dir.mktmpdir("relaton-index-") do |tmp|
|
|
287
|
-
branch = clone_data_repo(repo, options[:branch], tmp)
|
|
288
|
-
base = "https://raw.githubusercontent.com/#{repo}/#{branch}"
|
|
289
|
-
yield File.join(tmp, subdir), base, "#{flavor.upcase} Index"
|
|
290
|
-
end
|
|
291
|
-
else
|
|
292
|
-
yield subdir, nil, nil
|
|
293
|
-
end
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
# Shallow-clone ORG/NAME into `dest`; return the checked-out branch name.
|
|
297
|
-
def clone_data_repo(repo, branch, dest)
|
|
298
|
-
url = "https://github.com/#{repo}.git"
|
|
299
|
-
cmd = ["git", "clone", "--depth", "1", "--single-branch"]
|
|
300
|
-
cmd += ["--branch", branch] if branch
|
|
301
|
-
cmd += [url, dest]
|
|
302
|
-
Relaton::Cli::Util.info "Cloning #{url}…"
|
|
303
|
-
unless system(*cmd, out: File::NULL, err: File::NULL)
|
|
304
|
-
raise Thor::Error,
|
|
305
|
-
"Failed to clone #{url}. Check the repo/branch name and that git is installed."
|
|
306
|
-
end
|
|
307
|
-
branch || `git -C #{Shellwords.escape(dest)} rev-parse --abbrev-ref HEAD`.strip
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
private
|
|
313
|
-
|
|
314
|
-
DATE_FILTER_FORMAT = /\A\d{4}(-\d{2}(-\d{2})?)?\z/
|
|
315
|
-
|
|
316
|
-
def parse_date_option(value, name)
|
|
317
|
-
return unless value
|
|
318
|
-
|
|
319
|
-
unless value.match?(DATE_FILTER_FORMAT)
|
|
320
|
-
raise ArgumentError,
|
|
321
|
-
"Invalid #{name}: #{value.inspect}. Expected YYYY, YYYY-MM, or YYYY-MM-DD."
|
|
322
|
-
end
|
|
323
|
-
parts = value.split("-").map(&:to_i)
|
|
324
|
-
Date.new(*parts.concat([1] * (3 - parts.size)))
|
|
325
|
-
rescue Date::Error
|
|
326
|
-
raise ArgumentError,
|
|
327
|
-
"Invalid #{name}: #{value.inspect}. Date components are out of range."
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
def validate_date_range(date_after, date_before)
|
|
331
|
-
return unless date_after && date_before
|
|
332
|
-
return if date_after < date_before
|
|
333
|
-
|
|
334
|
-
raise ArgumentError,
|
|
335
|
-
"Invalid date range: --publication-date-after (#{date_after}) must be before --publication-date-before (#{date_before})."
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
# @param code [String]
|
|
339
|
-
# @param options [Hash]
|
|
340
|
-
# @option options [String] :type
|
|
341
|
-
# @option options [String, NilClass] :format
|
|
342
|
-
# @option options [Integer, NilClass] :year
|
|
343
|
-
# @return [String, nil]
|
|
344
|
-
def fetch_document(code, options) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/AbcSize,Metrics/MethodLength
|
|
345
|
-
year = options[:year]&.to_s
|
|
346
|
-
dup_opts = options.dup.transform_keys { |k| k.to_s.gsub("-", "_").to_sym }
|
|
347
|
-
%i[publication_date_before publication_date_after].each do |key|
|
|
348
|
-
dup_opts[key] = parse_date_option(dup_opts[key], key.to_s.tr("_", "-").prepend("--")) if dup_opts[key]
|
|
349
|
-
end
|
|
350
|
-
validate_date_range dup_opts[:publication_date_after], dup_opts[:publication_date_before]
|
|
351
|
-
if (processor = Relaton::Db::Registry.instance.by_type options[:type]&.upcase)
|
|
352
|
-
doc = Relaton.db.fetch_std code, year, processor.short, **dup_opts
|
|
353
|
-
elsif options[:type] then return
|
|
354
|
-
else doc = Relaton.db.fetch(code, year, **dup_opts)
|
|
355
|
-
end
|
|
356
|
-
return "No matching bibliographic entry found" unless doc
|
|
357
|
-
|
|
358
|
-
serialize doc, options[:format]
|
|
359
|
-
rescue Relaton::UnknownReferenceError => e
|
|
360
|
-
# No flavor recognizes the reference: log it, return no document (the
|
|
361
|
-
# command then prints the supported types).
|
|
362
|
-
Util.warn e.message
|
|
363
|
-
nil
|
|
364
|
-
rescue Pubid::Errors::Error
|
|
365
|
-
%("#{code}" is not a recognized standards identifier)
|
|
366
|
-
rescue Relaton::RequestError => e
|
|
367
|
-
e.message
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
# @param doc [Relaton::Bib::ItemData]
|
|
371
|
-
# @param format [String]
|
|
372
|
-
# @return [String]
|
|
373
|
-
def serialize(doc, format)
|
|
374
|
-
case format
|
|
375
|
-
when "yaml", "yml" then doc.to_yaml
|
|
376
|
-
when "bibtex" then doc.to_bibtex
|
|
377
|
-
else doc.to_xml bibdata: true
|
|
378
|
-
end
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
def supported_type_message
|
|
382
|
-
["Recognised types:", registered_types.sort.join(", ")].join(" ")
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
def registered_types
|
|
386
|
-
@registered_types ||=
|
|
387
|
-
Relaton::Db::Registry.instance.processors.each.map { |_n, pr| pr.prefix }
|
|
388
|
-
end
|
|
389
|
-
end
|
|
390
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
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
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
module Relaton
|
|
2
|
-
module Cli
|
|
3
|
-
# Resolves the compiled frontend bundle (`app.iife.js` + `style.css`) that is
|
|
4
|
-
# built from `gems/relaton-cli/frontend/` at gem-build time and shipped inside
|
|
5
|
-
# the gem under `frontend/dist/`. Both the installed gem and a git checkout
|
|
6
|
-
# place `frontend/dist` at the gem root, so one relative path covers both.
|
|
7
|
-
#
|
|
8
|
-
# If the bundle is missing (a fresh checkout that hasn't run the frontend
|
|
9
|
-
# build), raise a clear, actionable error rather than emitting a broken page.
|
|
10
|
-
module FrontendAssets
|
|
11
|
-
class BuildMissingError < StandardError; end
|
|
12
|
-
|
|
13
|
-
# gem root = up three from lib/relaton/cli
|
|
14
|
-
DEFAULT_DIST = File.expand_path("../../../frontend/dist", __dir__).freeze
|
|
15
|
-
|
|
16
|
-
class << self
|
|
17
|
-
# Overridable so specs can point at a fixture dist without a Node build.
|
|
18
|
-
attr_writer :dist_dir
|
|
19
|
-
|
|
20
|
-
def dist_dir
|
|
21
|
-
@dist_dir || DEFAULT_DIST
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Run a block with a temporary dist dir, restoring the previous one.
|
|
25
|
-
def with_dist_dir(dir)
|
|
26
|
-
previous = @dist_dir
|
|
27
|
-
@dist_dir = dir
|
|
28
|
-
yield
|
|
29
|
-
ensure
|
|
30
|
-
@dist_dir = previous
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def iife
|
|
34
|
-
read("app.iife.js")
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def stylesheet
|
|
38
|
-
read("style.css")
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def built?
|
|
42
|
-
File.exist?(File.join(dist_dir, "app.iife.js"))
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def read(name)
|
|
46
|
-
path = File.join(dist_dir, name)
|
|
47
|
-
unless File.exist?(path)
|
|
48
|
-
raise BuildMissingError,
|
|
49
|
-
"Frontend build missing (#{name} not found in #{dist_dir}). " \
|
|
50
|
-
"Run `bundle exec rake build_frontend` in gems/relaton-cli " \
|
|
51
|
-
"(or `cd frontend && npm install && npm run build`) first."
|
|
52
|
-
end
|
|
53
|
-
File.read(path, encoding: "utf-8")
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|