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,290 +0,0 @@
|
|
|
1
|
-
require "nokogiri"
|
|
2
|
-
require "pathname"
|
|
3
|
-
|
|
4
|
-
module Relaton
|
|
5
|
-
module Cli
|
|
6
|
-
class RelatonFile
|
|
7
|
-
def initialize(source, options = {})
|
|
8
|
-
@source = source
|
|
9
|
-
@options = options
|
|
10
|
-
@outdir = options.fetch(:outdir, nil)
|
|
11
|
-
@outfile = options.fetch(:outfile, nil)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def extract
|
|
15
|
-
extract_and_write_to_files
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def concatenate
|
|
19
|
-
concatenate_and_write_to_files
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def split
|
|
23
|
-
split_and_write_to_files
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Extract files
|
|
27
|
-
#
|
|
28
|
-
# This interface expect us to provide a source file / directory,
|
|
29
|
-
# output directory and custom configuration options. Then it wll
|
|
30
|
-
# extract Relaton XML file / files to output directory from the
|
|
31
|
-
# source file / directory. During this process it will use custom
|
|
32
|
-
# options when available.
|
|
33
|
-
#
|
|
34
|
-
# @param source [Dir] The source directory for files
|
|
35
|
-
# @param outdir [Dir] The output directory for files
|
|
36
|
-
# @param options [Hash] Options as hash key value pair
|
|
37
|
-
#
|
|
38
|
-
def self.extract(source, outdir, options = {})
|
|
39
|
-
new(source, options.merge(outdir: outdir)).extract
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Concatenate files
|
|
43
|
-
#
|
|
44
|
-
# This interface expect us to provide a source directory, output
|
|
45
|
-
# file and custom configuration options. Normally, this expect the
|
|
46
|
-
# source directory to contain RXL fles, but it also converts any
|
|
47
|
-
# YAML files to RXL and then finally combines those together.
|
|
48
|
-
#
|
|
49
|
-
# This interface also allow us to provdie options like title and
|
|
50
|
-
# organization and then it usage those details to generate the
|
|
51
|
-
# collection file.
|
|
52
|
-
#
|
|
53
|
-
# @param source [Dir] The source directory for files
|
|
54
|
-
# @param output [String] The collection output file
|
|
55
|
-
# @param options [Hash] Options as hash key value pair
|
|
56
|
-
#
|
|
57
|
-
def self.concatenate(source, outfile, options = {})
|
|
58
|
-
new(source, options.merge(outfile: outfile)).concatenate
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Split collection
|
|
62
|
-
#
|
|
63
|
-
# This interface expects us to provide a Relaton Collection
|
|
64
|
-
# file and also an output directory, then it will split that
|
|
65
|
-
# collection into multiple files.
|
|
66
|
-
#
|
|
67
|
-
# By default it usages `rxl` extension for these new files,
|
|
68
|
-
# but we can also customize that by providing the correct
|
|
69
|
-
# one as `extension` option parameter.
|
|
70
|
-
#
|
|
71
|
-
# @param source [File] The source collection file
|
|
72
|
-
# @param output [Dir] The output directory for files
|
|
73
|
-
# @param options [Hash] Options as hash key value pair
|
|
74
|
-
#
|
|
75
|
-
def self.split(source, outdir = nil, options = {})
|
|
76
|
-
new(source, options.merge(outdir: outdir)).split
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
private
|
|
80
|
-
|
|
81
|
-
attr_reader :source, :options, :outdir, :outfile
|
|
82
|
-
|
|
83
|
-
def bibcollection
|
|
84
|
-
Bibcollection.new(
|
|
85
|
-
title: options[:title],
|
|
86
|
-
items: concatenate_files,
|
|
87
|
-
doctype: options[:doctype],
|
|
88
|
-
author: options[:organization],
|
|
89
|
-
)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Turn an XML string into a Nokogiri XML document.
|
|
93
|
-
#
|
|
94
|
-
# @param document [String] XML
|
|
95
|
-
# @param file [String, nil] path to file
|
|
96
|
-
# @return [Nokogiri::XML::Document]
|
|
97
|
-
def nokogiri_document(document, file = nil)
|
|
98
|
-
document ||= File.read(file, encoding: "utf-8")
|
|
99
|
-
Nokogiri.XML(document)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def select_source_files
|
|
103
|
-
if File.file?(source)
|
|
104
|
-
[source]
|
|
105
|
-
else
|
|
106
|
-
select_files_with("xml")
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def relaton_collection
|
|
111
|
-
@relaton_collection ||= Bibcollection.from_xml(nokogiri_document(nil, source))
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def extract_and_write_to_files # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
115
|
-
select_source_files.each do |file|
|
|
116
|
-
xml = nokogiri_document(nil, file)
|
|
117
|
-
xml.remove_namespaces!
|
|
118
|
-
|
|
119
|
-
if (bib = xml.at("//bibdata"))
|
|
120
|
-
bib = nokogiri_document(bib.to_xml)
|
|
121
|
-
elsif (rfc = xml.at("//rfc"))
|
|
122
|
-
require "relaton/ietf"
|
|
123
|
-
require "relaton/ietf/bibxml_parser"
|
|
124
|
-
ietf = Relaton::Ietf::BibXMLParser.parse_rfc rfc.to_xml
|
|
125
|
-
bib = nokogiri_document(ietf.to_xml(bibdata: true))
|
|
126
|
-
else
|
|
127
|
-
next
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
bib.remove_namespaces!
|
|
131
|
-
|
|
132
|
-
bibdata = Relaton::Bibdata.from_xml(bib.root)
|
|
133
|
-
if bibdata
|
|
134
|
-
build_bibdata_relaton(bibdata, file)
|
|
135
|
-
|
|
136
|
-
write_to_file(bibdata.send(output_type), outdir, build_filename(file))
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Map all relevant files to the corresponding bibdata instances
|
|
142
|
-
#
|
|
143
|
-
# @return [Array<Relaton::Bibdata>]
|
|
144
|
-
def concatenate_files
|
|
145
|
-
xml_files = [convert_rxl_to_xml, convert_yamls_to_xml, convert_xml_to_xml]
|
|
146
|
-
|
|
147
|
-
xml_files.flatten.reduce([]) do |mem, xml|
|
|
148
|
-
doc = nokogiri_document(xml[:content])
|
|
149
|
-
if (rfc = doc.at("/rfc"))
|
|
150
|
-
require "relaton/ietf"
|
|
151
|
-
require "relaton/ietf/bibxml_parser"
|
|
152
|
-
ietf = Relaton::Ietf::BibXMLParser.parse_rfc rfc.to_xml
|
|
153
|
-
d = nokogiri_document ietf.to_xml(bibdata: true)
|
|
154
|
-
mem << bibdata_instance(d, xml[:file])
|
|
155
|
-
elsif %w[bibitem bibdata].include? doc&.root&.name
|
|
156
|
-
mem << bibdata_instance(doc, xml[:file])
|
|
157
|
-
else mem
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def concatenate_and_write_to_files
|
|
163
|
-
write_to_file(bibcollection.send(output_type))
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def split_and_write_to_files
|
|
167
|
-
output_dir = outdir || build_dirname(source)
|
|
168
|
-
|
|
169
|
-
relaton_collection.items.each do |content|
|
|
170
|
-
name = build_filename(nil, content.docidentifier)
|
|
171
|
-
find_available_bibrxl_file(name, output_dir, content)
|
|
172
|
-
write_to_file(content.send(output_type), output_dir, name)
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def find_available_bibrxl_file(name, _ouputdir, content)
|
|
177
|
-
if options[:extension] == "yaml" || options[:extension] == "yml"
|
|
178
|
-
bib_rxl = Pathname.new([outdir, name].join("/")).sub_ext(".rxl")
|
|
179
|
-
content.bib_rxl = bib_rxl.to_s if File.file?(bib_rxl)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def output_type(ext = options[:extension])
|
|
184
|
-
ext ||= File.extname(outfile)[1..-1] if outfile
|
|
185
|
-
case ext
|
|
186
|
-
when "rxl", "xml"
|
|
187
|
-
:to_xml
|
|
188
|
-
when "yml", "yaml"
|
|
189
|
-
:to_yaml
|
|
190
|
-
else
|
|
191
|
-
puts "[relaton-cli] the given extension of '#{ext}' is "\
|
|
192
|
-
"not supported. Use 'rxl'."
|
|
193
|
-
:to_xml
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# Create a bibdata instance from the XML document,
|
|
198
|
-
# while also adding file paths of related artifacts
|
|
199
|
-
# as URI elements to the bibdata instance.
|
|
200
|
-
#
|
|
201
|
-
# @param document [Nokogiri::XML::Document]
|
|
202
|
-
# @param file [String] path to file
|
|
203
|
-
# @return [Relaton::Bibdata]
|
|
204
|
-
def bibdata_instance(document, file)
|
|
205
|
-
document = clean_nokogiri_document(document)
|
|
206
|
-
bibdata = Relaton::Bibdata.from_xml document.root
|
|
207
|
-
build_bibdata_relaton(bibdata, file) if bibdata
|
|
208
|
-
|
|
209
|
-
bibdata
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# For each file type, add to the bibdata:
|
|
213
|
-
# - a URI element that points to the file of that file type
|
|
214
|
-
#
|
|
215
|
-
# The URI element generation is skipped if the file does not exist.
|
|
216
|
-
#
|
|
217
|
-
# @param bibdata [Relaton::Bibdata]
|
|
218
|
-
# @param file [String] path to file
|
|
219
|
-
def build_bibdata_relaton(bibdata, file)
|
|
220
|
-
["xml", "pdf", "doc", "html", "rxl", "txt"].each do |type|
|
|
221
|
-
file = Pathname.new(file).sub_ext(".#{type}")
|
|
222
|
-
bibdata.send("#{type}=", file.to_s) if File.file?(file)
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
# Force a namespace otherwise Nokogiri won't parse.
|
|
227
|
-
# The reason is we use Bibcollection's from_xml, but that one
|
|
228
|
-
# has an xmlns. We don't want to change the code for bibdata
|
|
229
|
-
# hence this hack #bibdata_doc.root['xmlns'] = "xmlns"
|
|
230
|
-
#
|
|
231
|
-
# @param document [Nokogiri::XML::Document]
|
|
232
|
-
# @return [Nokogiri::XML::Document]
|
|
233
|
-
def clean_nokogiri_document(document)
|
|
234
|
-
document.remove_namespaces!
|
|
235
|
-
nokogiri_document(document.to_xml)
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def convert_rxl_to_xml
|
|
239
|
-
select_files_with("{rxl}").map do |file|
|
|
240
|
-
{ file: file, content: File.read(file, encoding: "utf-8") }
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def convert_yamls_to_xml
|
|
245
|
-
select_files_with("yaml").map do |file|
|
|
246
|
-
{ file: file, content: YAMLConvertor.to_xml(file, write: false) }
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def convert_xml_to_xml
|
|
251
|
-
select_files_with("{xml}").map do |file|
|
|
252
|
-
{ file: file, content: File.read(file, encoding: "utf-8") }
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def select_files_with(extension)
|
|
257
|
-
files = File.join(source, "**", "*.#{extension}")
|
|
258
|
-
Dir[files].reject { |file| File.directory?(file) }
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def write_to_file(content, directory = nil, output_file = nil)
|
|
262
|
-
file_with_dir = [directory, output_file || outfile].compact.join("/")
|
|
263
|
-
File.open(file_with_dir, "w:utf-8") { |file| file.write(content) }
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def build_dirname(filename)
|
|
267
|
-
basename = File.basename(filename)&.gsub(/.(xml|rxl)/, "")
|
|
268
|
-
directory_name = sanitize_string(basename)
|
|
269
|
-
FileUtils.mkdir_p(directory_name) # unless File.exists?(directory_name)
|
|
270
|
-
|
|
271
|
-
directory_name
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def build_filename(file, identifier = nil, ext = "rxl")
|
|
275
|
-
identifier ||= Pathname.new(File.basename(file.to_s, ".xml")).to_s
|
|
276
|
-
[sanitize_string(identifier), options[:extension] || ext].join(".")
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def sanitize_string(string)
|
|
280
|
-
clean_string = replace_bad_characters(string.downcase)
|
|
281
|
-
clean_string.gsub(/^\s+/, "").gsub(/\s+$/, "").gsub(/\s+/, "-")
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
def replace_bad_characters(string)
|
|
285
|
-
bad_chars = ["/", "\\", "?", "%", "*", ":", "|", '"', "<", ">", ".", " "]
|
|
286
|
-
bad_chars.reduce(string.downcase) { |res, char| res.gsub(char, "-") }
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
end
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
require "pubid"
|
|
2
|
-
require "relaton/cli/full_text_search"
|
|
3
|
-
|
|
4
|
-
module Relaton
|
|
5
|
-
module Cli
|
|
6
|
-
class SubcommandCollection < Thor
|
|
7
|
-
include Relaton::Cli
|
|
8
|
-
class_option :verbose, aliases: :v, type: :boolean, desc: "Output warnings"
|
|
9
|
-
|
|
10
|
-
desc "create COLLECTION", "Create collection"
|
|
11
|
-
option :dir, aliases: :d, desc: "Directory to store collection. " \
|
|
12
|
-
"Default is $HOME/.relaton/collections."
|
|
13
|
-
option :author, desc: "Author"
|
|
14
|
-
option :title, desc: "Title"
|
|
15
|
-
option :doctype, desc: "Documents type"
|
|
16
|
-
|
|
17
|
-
def create(file)
|
|
18
|
-
dir = directory
|
|
19
|
-
file_path = File.join dir, file
|
|
20
|
-
col = Relaton::Bibcollection.new options
|
|
21
|
-
if File.exist? file_path
|
|
22
|
-
Util.warn "Collection `#{file}` aready exist"
|
|
23
|
-
else
|
|
24
|
-
FileUtils.mkdir_p dir # unless Dir.exist? dir
|
|
25
|
-
File.write file_path, col.to_yaml, encoding: "UTF-8"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
desc "info COLLECTION", "View collection information"
|
|
30
|
-
option :dir, aliases: :d, desc: "Directory to store collection. " \
|
|
31
|
-
"Default is $HOME/.relaton/collections."
|
|
32
|
-
|
|
33
|
-
def info(file) # rubocop:disable Metrics/AbcSize
|
|
34
|
-
path = File.join directory, file
|
|
35
|
-
puts "Collection: #{File.basename path}"
|
|
36
|
-
puts "Last updated: #{File.mtime path}"
|
|
37
|
-
puts "File size: #{File.size path}"
|
|
38
|
-
col = Relaton::Bibcollection.new YAML.load_file(path)["root"]
|
|
39
|
-
puts "Number of items: #{col.items.size}"
|
|
40
|
-
puts "Author: #{col.author}"
|
|
41
|
-
puts "Title: #{col.title}"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
desc "list", "List collections"
|
|
45
|
-
option :dir, aliases: :d, desc: "Directory with collections. Default " \
|
|
46
|
-
"is $HOME/.relaton/collections."
|
|
47
|
-
option :entries, aliases: :e, type: :boolean, desc: "Show entries"
|
|
48
|
-
|
|
49
|
-
def list
|
|
50
|
-
Dir[File.join(directory, "*")].each do |f|
|
|
51
|
-
yml = read_yaml f
|
|
52
|
-
if yml && yml["root"]
|
|
53
|
-
puts File.basename f
|
|
54
|
-
puts_entries yml
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
map ls: :list
|
|
60
|
-
|
|
61
|
-
desc "get CODE", "Fetch document from collection by ID"
|
|
62
|
-
option :collection, aliases: :c, desc: "Collection to fetch document. " \
|
|
63
|
-
"By default fetch the first match across all collections."
|
|
64
|
-
option :dir, aliases: :d, desc: "Directory with collections. Default " \
|
|
65
|
-
"is $HOME/.relaton/collections."
|
|
66
|
-
option :format, aliases: :f, desc: "Output format (xml, abb). " \
|
|
67
|
-
"If not defined the output in a human-readable form."
|
|
68
|
-
option :output, aliases: :o, desc: "Output to the specified file. The " \
|
|
69
|
-
" file's extension (abb, xml) defines output format."
|
|
70
|
-
|
|
71
|
-
def get(docid)
|
|
72
|
-
collections.each do |col|
|
|
73
|
-
col[:collection].items.each do |item|
|
|
74
|
-
if item.docidentifier == docid
|
|
75
|
-
output_item(item)
|
|
76
|
-
return
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
desc "find TEXT", "Full-text search"
|
|
83
|
-
option :collection, aliases: :c, desc: "Collection to search text. " \
|
|
84
|
-
"By default search across all collections."
|
|
85
|
-
option :dir, aliases: :d, desc: "Directory with collections. Default is " \
|
|
86
|
-
"$HOME/.relaton/collections."
|
|
87
|
-
|
|
88
|
-
def find(text)
|
|
89
|
-
collections.each do |col|
|
|
90
|
-
searcher = Relaton::FullTextSeatch.new(col[:collection])
|
|
91
|
-
searcher.search text
|
|
92
|
-
if searcher.any?
|
|
93
|
-
puts "Collection: #{File.basename(col[:file])}"
|
|
94
|
-
searcher.print_results
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
map search: :find
|
|
100
|
-
|
|
101
|
-
desc "fetch CODE", "Fetch a document and store it into a collection"
|
|
102
|
-
option :type, aliases: :t, required: true,
|
|
103
|
-
desc: "Type of standard to get bibliographic entry for"
|
|
104
|
-
option :year, aliases: :y, type: :numeric,
|
|
105
|
-
desc: "Year the standard was published"
|
|
106
|
-
option :"publication-date-before",
|
|
107
|
-
desc: "Fetch only documents published before the specified date " \
|
|
108
|
-
"(e.g. 2008, 2008-02, or 2008-02-02)"
|
|
109
|
-
option :"publication-date-after",
|
|
110
|
-
desc: "Fetch only documents published after the specified date " \
|
|
111
|
-
"(e.g. 2002, 2002-01, or 2002-01-01)"
|
|
112
|
-
option :collection, aliases: :c, required: true,
|
|
113
|
-
desc: "Collection to store a document"
|
|
114
|
-
option :dir, aliases: :d, desc: "Directory with collections. Default is " \
|
|
115
|
-
"$HOME/.relaton/collections."
|
|
116
|
-
|
|
117
|
-
def fetch(code) # rubocop:disable Metrics/AbcSize
|
|
118
|
-
opts = {}
|
|
119
|
-
if options[:"publication-date-before"]
|
|
120
|
-
opts[:publication_date_before] = parse_date_option(options[:"publication-date-before"], "--publication-date-before")
|
|
121
|
-
end
|
|
122
|
-
if options[:"publication-date-after"]
|
|
123
|
-
opts[:publication_date_after] = parse_date_option(options[:"publication-date-after"], "--publication-date-after")
|
|
124
|
-
end
|
|
125
|
-
validate_date_range opts[:publication_date_after], opts[:publication_date_before]
|
|
126
|
-
doc = Relaton.db.fetch(code, options[:year]&.to_s, **opts)
|
|
127
|
-
if doc
|
|
128
|
-
colfile = File.join directory, options[:collection]
|
|
129
|
-
coll = read_collection colfile
|
|
130
|
-
coll << doc
|
|
131
|
-
File.write colfile, coll.to_yaml, encoding: "UTF-8"
|
|
132
|
-
else Util.info "No matching bibliographic entry found"
|
|
133
|
-
end
|
|
134
|
-
rescue Relaton::UnknownReferenceError => e
|
|
135
|
-
Util.warn e.message
|
|
136
|
-
rescue Pubid::Errors::Error
|
|
137
|
-
Util.error %("#{code}" is not a recognized standards identifier)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
desc "import FILE", "Import document or collection from an XML file " \
|
|
141
|
-
"into another collection"
|
|
142
|
-
option :collection, aliases: :c, required: true,
|
|
143
|
-
desc: "Collection to store a document. If " \
|
|
144
|
-
"collection doesn't exist then it'll be created."
|
|
145
|
-
option :dir, aliases: :d, desc: "Directory with collections. Default is " \
|
|
146
|
-
"$HOME/.relaton/collections."
|
|
147
|
-
|
|
148
|
-
def import(file) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
149
|
-
collfile = File.join directory, options[:collection]
|
|
150
|
-
coll = read_collection collfile
|
|
151
|
-
xml = Nokogiri::XML File.read(file, encoding: "UTF-8")
|
|
152
|
-
xml.remove_namespaces!
|
|
153
|
-
if xml.at "relaton-collection"
|
|
154
|
-
imported = import_collection(xml)
|
|
155
|
-
if coll
|
|
156
|
-
coll << imported
|
|
157
|
-
else
|
|
158
|
-
coll = imported
|
|
159
|
-
end
|
|
160
|
-
else
|
|
161
|
-
coll ||= Relaton::Bibcollection.new({})
|
|
162
|
-
coll << Relaton::Bibdata.from_xml(xml)
|
|
163
|
-
end
|
|
164
|
-
File.write collfile, coll.to_yaml, encoding: "UTF-8"
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
desc "export COLLECTION", "Export collection into XML file"
|
|
168
|
-
option :dir, aliases: :d, desc: "Directory with collections. Default is " \
|
|
169
|
-
"$HOME/.relaton/collections."
|
|
170
|
-
|
|
171
|
-
def export(file)
|
|
172
|
-
coll = read_collection File.join(directory, file)
|
|
173
|
-
outfile = "#{file.sub(/\.\w+$/, '')}.xml"
|
|
174
|
-
File.write outfile, coll.to_xml(bibdata: true), encoding: "UTF-8"
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
private
|
|
178
|
-
|
|
179
|
-
# Parse a namespace-free collection XML document.
|
|
180
|
-
# Bibcollection.from_xml expects namespaced docs (via apply_namespace),
|
|
181
|
-
# so we use plain XPath on the namespace-stripped document instead.
|
|
182
|
-
def import_collection(xml)
|
|
183
|
-
title = xml.at("relaton-collection/title")&.text
|
|
184
|
-
author = xml.at_xpath(
|
|
185
|
-
"./relaton-collection/contributor[role/@type='author']"\
|
|
186
|
-
"/organization/name",
|
|
187
|
-
)&.text
|
|
188
|
-
items = xml.xpath("relaton-collection/relation").map do |rel|
|
|
189
|
-
el = rel.at("bibdata") || rel.at("bibitem")
|
|
190
|
-
Relaton::Bibdata.from_xml(el || rel)
|
|
191
|
-
end.compact
|
|
192
|
-
Relaton::Bibcollection.new(title: title, author: author, items: items)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# @return [String]
|
|
196
|
-
def directory
|
|
197
|
-
options.fetch :dir, File.join(Dir.home, ".relaton/collections")
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# @param file [String]
|
|
201
|
-
# @return [Hash, nil]
|
|
202
|
-
def read_yaml(file)
|
|
203
|
-
YAML.load_file file if File.file? file
|
|
204
|
-
rescue Psych::SyntaxError
|
|
205
|
-
Util.error "the file `#{file}` isn't a collection."
|
|
206
|
-
nil
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
# @param file [String]
|
|
210
|
-
# @return [Relaton::Bibcollection, nil]
|
|
211
|
-
def read_collection(file)
|
|
212
|
-
return unless File.file?(file)
|
|
213
|
-
|
|
214
|
-
Relaton::Bibcollection.new YAML.load_file(file)["root"]
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# @return [Array<Hash>]
|
|
218
|
-
def collections
|
|
219
|
-
file = options.fetch :collection, "*"
|
|
220
|
-
Dir[File.join directory, file].reduce([]) do |m, f|
|
|
221
|
-
yml = read_yaml f
|
|
222
|
-
if yml && yml["root"]
|
|
223
|
-
m << { collection: Relaton::Bibcollection.new(yml["root"]),
|
|
224
|
-
file: f }
|
|
225
|
-
end
|
|
226
|
-
m
|
|
227
|
-
end
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Puts document IDs for each item in tthe cokllection
|
|
231
|
-
# @param hash [Hash] Relaton collection
|
|
232
|
-
def puts_entries(hash)
|
|
233
|
-
return unless options[:entries]
|
|
234
|
-
|
|
235
|
-
Relaton::Bibcollection.new(hash["root"]).items.each do |b|
|
|
236
|
-
puts " #{b.docidentifier}"
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# @param item [Relaton::Bibdata]
|
|
241
|
-
def output_item(item)
|
|
242
|
-
case options[:format]
|
|
243
|
-
when "xml" then puts item.to_xml bibdata: true
|
|
244
|
-
when "abb" then puts item.to_asciibib
|
|
245
|
-
else puts_human_readable_item item
|
|
246
|
-
end
|
|
247
|
-
out = case options[:output]
|
|
248
|
-
when /\.abb$/ then item.to_asciibib
|
|
249
|
-
when /\.xml$/ then item.to_xml bibitem: true
|
|
250
|
-
end
|
|
251
|
-
File.write options[:output], out, encoding: "UTF-8" if out
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# @param item [Relaton::Bibdata]
|
|
255
|
-
def puts_human_readable_item(item) # rubocop:disable Metrics/AbcSize
|
|
256
|
-
puts "Document identifier: #{item.docidentifier}"
|
|
257
|
-
puts "Title: #{item.title.first.content}"
|
|
258
|
-
puts "Status: #{item.status.stage}"
|
|
259
|
-
item.date.each { |d| puts "Date #{d.type}: #{d.at || d.from}" }
|
|
260
|
-
end
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
end
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
module Relaton
|
|
2
|
-
module Cli
|
|
3
|
-
class SubcommandDb < Thor
|
|
4
|
-
include Relaton::Cli
|
|
5
|
-
class_option :verbose, aliases: :v, type: :boolean, desc: "Output warnings"
|
|
6
|
-
|
|
7
|
-
desc "create DIR", "Create new cache DB. Default DIR is " \
|
|
8
|
-
"/home/user/.relaon/cache/"
|
|
9
|
-
|
|
10
|
-
def create(dir = nil)
|
|
11
|
-
db = Relaton.db (dir && File.expand_path(dir))
|
|
12
|
-
path = db.instance_variable_get(:@db).dir
|
|
13
|
-
Util.warn "Cache DB is in `#{path}`"
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
desc "mv DIR", "Move cache DB to a new directory"
|
|
17
|
-
|
|
18
|
-
def mv(dir)
|
|
19
|
-
new_path = File.expand_path dir
|
|
20
|
-
path = Relaton.db.mv new_path
|
|
21
|
-
if path
|
|
22
|
-
File.write Cli::RelatonDb::DBCONF, path, encoding: "UTF-8"
|
|
23
|
-
Util.warn "Cache DB is moved to `#{path}`"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
desc "clear", "Clear cache DB"
|
|
28
|
-
|
|
29
|
-
def clear
|
|
30
|
-
db = Relaton.db
|
|
31
|
-
db.clear
|
|
32
|
-
Util.warn "Cache DB is cleared"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
desc "fetch CODE", "Fetch Relaton XML for Standard identifier CODE " \
|
|
36
|
-
"from cache DB"
|
|
37
|
-
option :type, aliases: :t, desc: "Type of standard to " \
|
|
38
|
-
"get bibliographic entry for"
|
|
39
|
-
option :format, aliases: :f, desc: "Output format (xml, yaml, bibtex). " \
|
|
40
|
-
"Default xml."
|
|
41
|
-
option :year, aliases: :y, type: :numeric, desc: "Year the standard " \
|
|
42
|
-
"was published"
|
|
43
|
-
option :"publication-date-before",
|
|
44
|
-
desc: "Fetch only documents published before the specified date " \
|
|
45
|
-
"(e.g. 2008, 2008-02, or 2008-02-02)"
|
|
46
|
-
option :"publication-date-after",
|
|
47
|
-
desc: "Fetch only documents published after the specified date " \
|
|
48
|
-
"(e.g. 2002, 2002-01, or 2002-01-01)"
|
|
49
|
-
|
|
50
|
-
def fetch(code)
|
|
51
|
-
io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
|
|
52
|
-
opts = options.merge(fetch_db: true)
|
|
53
|
-
io.puts(fetch_document(code, opts) || supported_type_message)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
desc "fetch_all TEXT", "Query for all documents in a cache DB for a " \
|
|
57
|
-
"certain string"
|
|
58
|
-
option :edition, aliases: :e, desc: "Filter entries by edition"
|
|
59
|
-
option :year, aliases: :y, desc: "Filter entries by year"
|
|
60
|
-
option :format, aliases: :f, desc: "Output format (xml, yaml, bibtex). " \
|
|
61
|
-
"Default xml."
|
|
62
|
-
|
|
63
|
-
def fetch_all(text = nil) # rubocop:disable Metrics/AbcSize
|
|
64
|
-
io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
|
|
65
|
-
opts = options.each_with_object({}) do |(k, v), o|
|
|
66
|
-
o[k.to_sym] = v unless k == "format"
|
|
67
|
-
end
|
|
68
|
-
Relaton.db.fetch_all(text, **opts).each do |doc|
|
|
69
|
-
io.puts serialize(doc, options[:format])
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
desc "doctype REF", "Detect document type from REF"
|
|
74
|
-
|
|
75
|
-
def doctype(ref)
|
|
76
|
-
io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
|
|
77
|
-
io.puts Relaton.db.docid_type(ref)
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
data/lib/relaton/cli/util.rb
DELETED
data/lib/relaton/cli/version.rb
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# relaton-cli ships in lockstep with the combined `relaton` gem, so its version
|
|
2
|
-
# is that gem's version — the single source of truth in Relaton::VERSION. relaton
|
|
3
|
-
# is a hard runtime dependency, so requiring its lightweight version file always
|
|
4
|
-
# resolves. Don't hardcode a string here; it would silently drift on a bump.
|
|
5
|
-
require "relaton/version"
|
|
6
|
-
|
|
7
|
-
module Relaton
|
|
8
|
-
module Cli
|
|
9
|
-
VERSION = Relaton::VERSION
|
|
10
|
-
end
|
|
11
|
-
end
|