relaton 1.7.9 → 1.8.pre2
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/.github/workflows/rake.yml +1 -1
- data/.rubocop.yml +1 -1
- data/lib/relaton.rb +3 -0
- data/lib/relaton/config.rb +4 -2
- data/lib/relaton/db.rb +106 -52
- data/lib/relaton/db_cache.rb +38 -125
- data/lib/relaton/registry.rb +1 -0
- data/lib/relaton/storage.rb +186 -0
- data/lib/relaton/version.rb +1 -1
- data/relaton.gemspec +23 -19
- data/spec/relaton/db_cache_spec.rb +2 -2
- data/spec/relaton/db_spec.rb +17 -0
- data/spec/relaton/regirtry_spec.rb +9 -1
- data/spec/relaton/storage_spec.rb +130 -0
- data/spec/relaton_spec.rb +48 -7
- data/spec/spec_helper.rb +4 -0
- data/spec/vcr_cassetes/19133_2005.yml +17 -17
- data/spec/vcr_cassetes/api_relaton_org.yml +33 -0
- data/spec/vcr_cassetes/api_relaton_org_unavailable.yml +182 -0
- data/spec/vcr_cassetes/async_fetch.yml +2037 -1606
- data/spec/vcr_cassetes/bsi_bs_en_iso_8848.yml +17 -17
- data/spec/vcr_cassetes/cc_dir_10005_2019.yml +10 -10
- data/spec/vcr_cassetes/cen_en_10160_1999.yml +249 -0
- data/spec/vcr_cassetes/cie_001_1980.yml +7 -7
- data/spec/vcr_cassetes/ecma_6.yml +7 -7
- data/spec/vcr_cassetes/fisp_140.yml +4 -4
- data/spec/vcr_cassetes/gb_t_20223_2006.yml +9 -9
- data/spec/vcr_cassetes/iec_60050_102_2007.yml +23 -23
- data/spec/vcr_cassetes/iec_combined_included.yml +81 -79
- data/spec/vcr_cassetes/ieee_528_2019.yml +38 -38
- data/spec/vcr_cassetes/iho_b_11.yml +7 -7
- data/spec/vcr_cassetes/iso_111111119115_1.yml +3 -3
- data/spec/vcr_cassetes/iso_19115.yml +19 -19
- data/spec/vcr_cassetes/iso_19115_1.yml +18 -18
- data/spec/vcr_cassetes/iso_19115_1_2.yml +35 -35
- data/spec/vcr_cassetes/iso_awi_14093.yml +182 -0
- data/spec/vcr_cassetes/iso_combined_applied.yml +38 -38
- data/spec/vcr_cassetes/iso_combined_included.yml +34 -34
- data/spec/vcr_cassetes/itu_combined_included.yml +456 -459
- data/spec/vcr_cassetes/ogc_19_025r1.yml +1222 -1163
- data/spec/vcr_cassetes/omg_ami4ccm_1_0.yml +43 -0
- data/spec/vcr_cassetes/rfc_8341.yml +7 -7
- data/spec/vcr_cassetes/sp_800_38b.yml +4 -4
- data/spec/vcr_cassetes/un_rtade_cefact_2004_32.yml +29 -29
- data/spec/vcr_cassetes/w3c_json_ld11.yml +17 -17
- metadata +107 -46
- data/spec/vcr_cassetes/iso_awi_24229.yml +0 -182
- data/spec/vcr_cassetes/ogm_ami4ccm_1_0.yml +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7613bdf8be768f76e3e24d8727210075cd8ad8d4e24924ec5d19d28473cd933f
|
|
4
|
+
data.tar.gz: 1332d1463afd07385d3aacb917a2f621b572e802b25ec64d5d5f87781b6c6ef6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd636d5da683476ff6abf8a0c686bd9b7112cbbbda7c19f96249b75a6ecb8a4cb4c5764eeead68e949003b8f460574b50fabbcbe2f838ff9f104cb52adda45ba
|
|
7
|
+
data.tar.gz: 75ec0ed77c187b48baef584702b10cd5add0938325a19a40eac6e0217c86b7f769f30ad2561c612ab4fd2c0905c03e8c94797c92ce28a034215cf88bfc8d5f09
|
data/.github/workflows/rake.yml
CHANGED
data/.rubocop.yml
CHANGED
data/lib/relaton.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "relaton/util"
|
|
2
2
|
require "relaton/config"
|
|
3
|
+
require "yaml"
|
|
3
4
|
|
|
4
5
|
require "relaton/workers_pool"
|
|
5
6
|
require "relaton/db"
|
|
@@ -7,3 +8,5 @@ require "relaton/db_cache"
|
|
|
7
8
|
require "relaton/version"
|
|
8
9
|
require "relaton/registry"
|
|
9
10
|
require "relaton/processor"
|
|
11
|
+
require "relaton/registry"
|
|
12
|
+
require "relaton/db_cache"
|
data/lib/relaton/config.rb
CHANGED
|
@@ -12,13 +12,15 @@ module Relaton
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
class Configuration
|
|
15
|
-
attr_accessor :logs, :use_api
|
|
15
|
+
attr_accessor :logs, :use_api, :api_host, :api_mode
|
|
16
16
|
|
|
17
17
|
def initialize
|
|
18
18
|
@logs = %i(info error) # allowed values: :info, :warning, :error, :debug
|
|
19
19
|
|
|
20
20
|
# @TODO change to true when we start using api.relaton.org
|
|
21
|
-
@use_api =
|
|
21
|
+
@use_api = true
|
|
22
|
+
@api_mode = false
|
|
23
|
+
@api_host = nil # "http://0.0.0.0:9292"
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
|
data/lib/relaton/db.rb
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
require "yaml"
|
|
2
|
-
require_relative "registry"
|
|
3
|
-
require_relative "db_cache"
|
|
4
|
-
|
|
5
1
|
module Relaton
|
|
6
2
|
class RelatonError < StandardError; end
|
|
7
3
|
|
|
@@ -10,12 +6,16 @@ module Relaton
|
|
|
10
6
|
# @param local_cache [String] directory of local DB
|
|
11
7
|
def initialize(global_cache, local_cache)
|
|
12
8
|
@registry = Relaton::Registry.instance
|
|
13
|
-
|
|
9
|
+
if Relaton.configuration.api_mode
|
|
10
|
+
gpath = global_cache
|
|
11
|
+
lpath = local_cache
|
|
12
|
+
else
|
|
13
|
+
gpath = global_cache && File.expand_path(global_cache)
|
|
14
|
+
lpath = local_cache && File.expand_path(local_cache)
|
|
15
|
+
end
|
|
14
16
|
@db = open_cache_biblio(gpath, type: :global)
|
|
15
|
-
lpath = local_cache && File.expand_path(local_cache)
|
|
16
17
|
@local_db = open_cache_biblio(lpath, type: :local)
|
|
17
|
-
@static_db = open_cache_biblio File.expand_path
|
|
18
|
-
__dir__)
|
|
18
|
+
@static_db = open_cache_biblio File.expand_path "../relaton/static_cache", __dir__
|
|
19
19
|
@queues = {}
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -25,10 +25,8 @@ module Relaton
|
|
|
25
25
|
# @return [String, nil]
|
|
26
26
|
def mv(new_dir, type: :global)
|
|
27
27
|
case type
|
|
28
|
-
when :global
|
|
29
|
-
|
|
30
|
-
when :local
|
|
31
|
-
@local_db&.mv new_dir
|
|
28
|
+
when :global then @db&.mv new_dir
|
|
29
|
+
when :local then @local_db&.mv new_dir
|
|
32
30
|
end
|
|
33
31
|
end
|
|
34
32
|
|
|
@@ -89,22 +87,18 @@ module Relaton
|
|
|
89
87
|
result = @static_db.all do |file, yml|
|
|
90
88
|
search_yml file, yml, text, edition, year
|
|
91
89
|
end.compact
|
|
92
|
-
db = @db || @local_db
|
|
93
|
-
|
|
94
|
-
result += db.all do |file, xml|
|
|
95
|
-
search_xml file, xml, text, edition, year
|
|
96
|
-
end.compact
|
|
90
|
+
if (db = @db || @local_db)
|
|
91
|
+
result += db.all { |f, x| search_xml f, x, text, edition, year }.compact
|
|
97
92
|
end
|
|
98
93
|
result
|
|
99
94
|
end
|
|
100
95
|
|
|
101
96
|
# Fetch asynchronously
|
|
102
97
|
def fetch_async(code, year = nil, opts = {}, &_block) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
103
|
-
stdclass = standard_class code
|
|
104
|
-
if stdclass
|
|
98
|
+
if (stdclass = standard_class code)
|
|
105
99
|
unless @queues[stdclass]
|
|
106
100
|
processor = @registry.processors[stdclass]
|
|
107
|
-
wp = WorkersPool.new(processor.threads) { |args| yield fetch
|
|
101
|
+
wp = WorkersPool.new(processor.threads) { |args| yield fetch(*args) }
|
|
108
102
|
@queues[stdclass] = { queue: Queue.new, workers_pool: wp }
|
|
109
103
|
Thread.new { process_queue @queues[stdclass] }
|
|
110
104
|
end
|
|
@@ -173,14 +167,31 @@ module Relaton
|
|
|
173
167
|
def to_xml
|
|
174
168
|
db = @local_db || @db || return
|
|
175
169
|
Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
|
176
|
-
xml.documents
|
|
177
|
-
xml.parent.add_child db.all.join(" ")
|
|
178
|
-
end
|
|
170
|
+
xml.documents { xml.parent.add_child db.all.join(" ") }
|
|
179
171
|
end.to_xml
|
|
180
172
|
end
|
|
181
173
|
|
|
182
174
|
private
|
|
183
175
|
|
|
176
|
+
#
|
|
177
|
+
# @param code [String]
|
|
178
|
+
# @param year [String]
|
|
179
|
+
#
|
|
180
|
+
# @param opts [Hash]
|
|
181
|
+
# @option opts [Boolean] :all_parts If all-parts reference is required
|
|
182
|
+
# @option opts [Boolean] :keep_year If undated reference should return
|
|
183
|
+
# actual reference with year
|
|
184
|
+
#
|
|
185
|
+
# @param stdclass [Symbol]
|
|
186
|
+
def fetch_api(code, year, opts, stdclass)
|
|
187
|
+
return unless Relaton.configuration.use_api
|
|
188
|
+
|
|
189
|
+
params = opts.merge(code: code, year: year).map { |k, v| "#{k}=#{v}" }.join "&"
|
|
190
|
+
url = "#{Relaton.configuration.api_host}/api/v1/fetch?#{params}"
|
|
191
|
+
rsp = Net::HTTP.get_response URI(url)
|
|
192
|
+
@registry.processors[stdclass].from_xml rsp.body if rsp.code == "200"
|
|
193
|
+
end
|
|
194
|
+
|
|
184
195
|
# @param file [String] file path
|
|
185
196
|
# @param yml [String] content in YAML format
|
|
186
197
|
# @param text [String, nil] text to serach
|
|
@@ -207,7 +218,7 @@ module Relaton
|
|
|
207
218
|
end
|
|
208
219
|
|
|
209
220
|
# @param file [String] file path
|
|
210
|
-
# @param content [String] content in XML or
|
|
221
|
+
# @param content [String] content in XML or YAML format
|
|
211
222
|
# @param edition [String, nil] edition to filter
|
|
212
223
|
# @param year [Integer, nil] year to filter
|
|
213
224
|
# @return [BibliographicItem, nil]
|
|
@@ -220,9 +231,14 @@ module Relaton
|
|
|
220
231
|
item.date.detect { |d| d.type == "published" && d.on(:year).to_s == year.to_s })
|
|
221
232
|
end
|
|
222
233
|
|
|
234
|
+
#
|
|
235
|
+
# Look up text in the XML elements attributes and content
|
|
236
|
+
#
|
|
223
237
|
# @param xml [String] content in XML format
|
|
224
238
|
# @param text [String, nil] text to serach
|
|
239
|
+
#
|
|
225
240
|
# @return [Boolean]
|
|
241
|
+
#
|
|
226
242
|
def match_xml_text(xml, text)
|
|
227
243
|
%r{((?<attr>=((?<apstr>')|"))|>).*?#{text}.*?(?(<attr>)(?(<apstr>)'|")|<)}mi.match?(xml)
|
|
228
244
|
end
|
|
@@ -256,19 +272,15 @@ module Relaton
|
|
|
256
272
|
end
|
|
257
273
|
|
|
258
274
|
doc = @registry.processors[stdclass].hash_to_bib docid: { id: code }
|
|
259
|
-
|
|
260
|
-
updates = check_bibliocache(ref, year, opts, stdclass)
|
|
275
|
+
updates = check_bibliocache(refs[0], year, opts, stdclass)
|
|
261
276
|
if updates
|
|
262
|
-
doc.relation << RelatonBib::DocumentRelation.new(bibitem: updates,
|
|
263
|
-
type: "updates")
|
|
277
|
+
doc.relation << RelatonBib::DocumentRelation.new(bibitem: updates, type: "updates")
|
|
264
278
|
end
|
|
265
279
|
divider = stdclass == :relaton_itu ? " " : "/"
|
|
266
280
|
refs[1..-1].each_with_object(doc) do |c, d|
|
|
267
|
-
bib = check_bibliocache(
|
|
281
|
+
bib = check_bibliocache(refs[0] + divider + c, year, opts, stdclass)
|
|
268
282
|
if bib
|
|
269
|
-
d.relation << RelatonBib::DocumentRelation.new(
|
|
270
|
-
type: reltype, description: reldesc, bibitem: bib
|
|
271
|
-
)
|
|
283
|
+
d.relation << RelatonBib::DocumentRelation.new(type: reltype, description: reldesc, bibitem: bib)
|
|
272
284
|
end
|
|
273
285
|
end
|
|
274
286
|
end
|
|
@@ -280,9 +292,7 @@ module Relaton
|
|
|
280
292
|
return name if /^(urn:)?#{processor.prefix}/i.match?(code) ||
|
|
281
293
|
processor.defaultprefix.match(code)
|
|
282
294
|
end
|
|
283
|
-
allowed = @registry.processors.reduce([])
|
|
284
|
-
m << v.prefix
|
|
285
|
-
end
|
|
295
|
+
allowed = @registry.processors.reduce([]) { |m, (_k, v)| m << v.prefix }
|
|
286
296
|
Util.log <<~WARN, :info
|
|
287
297
|
[relaton] #{code} does not have a recognised prefix: #{allowed.join(', ')}.
|
|
288
298
|
See https://github.com/relaton/relaton/ for instructions on prefixing and wrapping document identifiers to disambiguate them.
|
|
@@ -331,8 +341,8 @@ module Relaton
|
|
|
331
341
|
# RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
|
|
332
342
|
# RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
|
|
333
343
|
def bib_retval(entry, stdclass)
|
|
334
|
-
|
|
335
|
-
|
|
344
|
+
unless entry.nil? || entry.match?(/^not_found/)
|
|
345
|
+
@registry.processors[stdclass].from_xml(entry)
|
|
336
346
|
end
|
|
337
347
|
end
|
|
338
348
|
|
|
@@ -356,8 +366,7 @@ module Relaton
|
|
|
356
366
|
# RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
|
|
357
367
|
def check_bibliocache(code, year, opts, stdclass) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
358
368
|
id, searchcode = std_id(code, year, opts, stdclass)
|
|
359
|
-
yaml = @static_db[id]
|
|
360
|
-
if yaml
|
|
369
|
+
if (yaml = @static_db[id])
|
|
361
370
|
return @registry.processors[stdclass].hash_to_bib YAML.safe_load(yaml)
|
|
362
371
|
end
|
|
363
372
|
|
|
@@ -366,8 +375,7 @@ module Relaton
|
|
|
366
375
|
if db.nil?
|
|
367
376
|
return if opts[:fetch_db]
|
|
368
377
|
|
|
369
|
-
bibentry = new_bib_entry(searchcode, year, opts, stdclass, db: db,
|
|
370
|
-
id: id)
|
|
378
|
+
bibentry = new_bib_entry(searchcode, year, opts, stdclass, db: db, id: id)
|
|
371
379
|
return bib_retval(bibentry, stdclass)
|
|
372
380
|
end
|
|
373
381
|
|
|
@@ -376,18 +384,17 @@ module Relaton
|
|
|
376
384
|
return bib_retval(altdb[id], stdclass) if opts[:fetch_db]
|
|
377
385
|
|
|
378
386
|
db.clone_entry id, altdb if altdb.valid_entry? id, year
|
|
379
|
-
db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db,
|
|
380
|
-
id: id)
|
|
387
|
+
db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db, id: id)
|
|
381
388
|
altdb.clone_entry(id, db) if !altdb.valid_entry?(id, year)
|
|
382
389
|
else
|
|
383
390
|
return bib_retval(db[id], stdclass) if opts[:fetch_db]
|
|
384
391
|
|
|
385
|
-
db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db,
|
|
386
|
-
id: id)
|
|
392
|
+
db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db, id: id)
|
|
387
393
|
end
|
|
388
394
|
bib_retval(db[id], stdclass)
|
|
389
395
|
end
|
|
390
396
|
|
|
397
|
+
#
|
|
391
398
|
# @param code [String]
|
|
392
399
|
# @param year [String]
|
|
393
400
|
#
|
|
@@ -400,7 +407,9 @@ module Relaton
|
|
|
400
407
|
# @param stdclass [Symbol]
|
|
401
408
|
# @param db [Relaton::DbCache,`NilClass]
|
|
402
409
|
# @param id [String] docid
|
|
410
|
+
#
|
|
403
411
|
# @return [String]
|
|
412
|
+
#
|
|
404
413
|
def new_bib_entry(code, year, opts, stdclass, **args) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
405
414
|
bib = net_retry(code, year, opts, stdclass, opts.fetch(:retries, 1))
|
|
406
415
|
bib_id = bib&.docidentifier&.first&.id
|
|
@@ -415,8 +424,27 @@ module Relaton
|
|
|
415
424
|
end
|
|
416
425
|
end
|
|
417
426
|
|
|
427
|
+
#
|
|
428
|
+
# @param code [String]
|
|
429
|
+
# @param year [String]
|
|
430
|
+
#
|
|
431
|
+
# @param opts [Hash]
|
|
432
|
+
# @option opts [Boolean] :all_parts If all-parts reference is required
|
|
433
|
+
# @option opts [Boolean] :keep_year If undated reference should return
|
|
434
|
+
# actual reference with year
|
|
435
|
+
#
|
|
436
|
+
# @param stdclass [Symbol]
|
|
437
|
+
# @param retries [Integer] remain Number of network retries
|
|
438
|
+
#
|
|
418
439
|
# @raise [RelatonBib::RequestError]
|
|
440
|
+
# @return [RelatonBib::BibliographicItem]
|
|
441
|
+
#
|
|
419
442
|
def net_retry(code, year, opts, stdclass, retries)
|
|
443
|
+
doc = fetch_api code, year, opts, stdclass
|
|
444
|
+
return doc if doc
|
|
445
|
+
|
|
446
|
+
@registry.processors[stdclass].get(code, year, opts)
|
|
447
|
+
rescue Errno::ECONNREFUSED
|
|
420
448
|
@registry.processors[stdclass].get(code, year, opts)
|
|
421
449
|
rescue RelatonBib::RequestError => e
|
|
422
450
|
raise e unless retries > 1
|
|
@@ -434,11 +462,7 @@ module Relaton
|
|
|
434
462
|
# RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
|
|
435
463
|
# @return [String] XML or "not_found mm-dd-yyyy"
|
|
436
464
|
def bib_entry(bib)
|
|
437
|
-
|
|
438
|
-
bib.to_xml(bibdata: true)
|
|
439
|
-
else
|
|
440
|
-
"not_found #{Date.today}"
|
|
441
|
-
end
|
|
465
|
+
bib.respond_to?(:to_xml) ? bib.to_xml(bibdata: true) : "not_found #{Date.today}"
|
|
442
466
|
end
|
|
443
467
|
|
|
444
468
|
# @param dir [String, nil] DB directory
|
|
@@ -456,8 +480,7 @@ module Relaton
|
|
|
456
480
|
FileUtils.rm_rf(fdir, secure: true)
|
|
457
481
|
Util.log(
|
|
458
482
|
"[relaton] WARNING: cache #{fdir}: version is obsolete and cache is "\
|
|
459
|
-
"cleared.",
|
|
460
|
-
:warning
|
|
483
|
+
"cleared.", :warning
|
|
461
484
|
)
|
|
462
485
|
end
|
|
463
486
|
db
|
|
@@ -469,5 +492,36 @@ module Relaton
|
|
|
469
492
|
def process_queue(qwp)
|
|
470
493
|
while args = qwp[:queue].pop; qwp[:workers_pool] << args end
|
|
471
494
|
end
|
|
495
|
+
|
|
496
|
+
class << self
|
|
497
|
+
# Initialse and return relaton instance, with local and global cache names
|
|
498
|
+
# local_cache: local cache name; none created if nil; "relaton" created
|
|
499
|
+
# if empty global_cache: boolean to create global_cache
|
|
500
|
+
# flush_caches: flush caches
|
|
501
|
+
def init_bib_caches(**opts) # rubocop:disable Metrics/CyclomaticComplexity
|
|
502
|
+
globalname = global_bibliocache_name if opts[:global_cache]
|
|
503
|
+
localname = local_bibliocache_name(opts[:local_cache])
|
|
504
|
+
flush_caches globalname, localname if opts[:flush_caches]
|
|
505
|
+
Relaton::Db.new(globalname, localname)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
private
|
|
509
|
+
|
|
510
|
+
def flush_caches(gcache, lcache)
|
|
511
|
+
FileUtils.rm_rf gcache unless gcache.nil?
|
|
512
|
+
FileUtils.rm_rf lcache unless lcache.nil?
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def global_bibliocache_name
|
|
516
|
+
Relaton.configuration.api_mode ? "cache" : "#{Dir.home}/.relaton/cache"
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def local_bibliocache_name(cachename)
|
|
520
|
+
return nil if Relaton.configuration.api_mode || cachename.nil?
|
|
521
|
+
|
|
522
|
+
cachename = "relaton" if cachename.empty?
|
|
523
|
+
"#{cachename}/cache"
|
|
524
|
+
end
|
|
525
|
+
end
|
|
472
526
|
end
|
|
473
527
|
end
|
data/lib/relaton/db_cache.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "fileutils"
|
|
2
2
|
require "timeout"
|
|
3
|
+
require "relaton/storage"
|
|
3
4
|
|
|
4
5
|
module Relaton
|
|
5
6
|
class DbCache
|
|
@@ -10,16 +11,15 @@ module Relaton
|
|
|
10
11
|
def initialize(dir, ext = "xml")
|
|
11
12
|
@dir = dir
|
|
12
13
|
@ext = ext
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# set_version # unless File.exist? file_version
|
|
14
|
+
@storage = Storage.instance
|
|
15
|
+
FileUtils::mkdir_p @dir unless Relaton.configuration.api_mode
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Move caches to anothe dir
|
|
19
19
|
# @param new_dir [String, nil]
|
|
20
20
|
# @return [String, nil]
|
|
21
21
|
def mv(new_dir)
|
|
22
|
-
return unless new_dir && @ext == "xml"
|
|
22
|
+
return unless new_dir && @ext == "xml" && !Relaton.configuration.api_mode
|
|
23
23
|
|
|
24
24
|
if File.exist? new_dir
|
|
25
25
|
warn "[relaton] WARNING: target directory exists \"#{new_dir}\""
|
|
@@ -32,7 +32,9 @@ module Relaton
|
|
|
32
32
|
|
|
33
33
|
# Clear database
|
|
34
34
|
def clear
|
|
35
|
-
|
|
35
|
+
return if Relaton.configuration.api_mode
|
|
36
|
+
|
|
37
|
+
FileUtils.rm_rf Dir.glob "#{dir}/*" if @ext == "xml" # if it's static DB
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
# Save item
|
|
@@ -43,21 +45,10 @@ module Relaton
|
|
|
43
45
|
delete key
|
|
44
46
|
return
|
|
45
47
|
end
|
|
46
|
-
|
|
47
|
-
prefix_dir = "#{@dir}/#{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
file_safe_write "#{filename(key)}.#{ext(value)}", value
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# @param value [String]
|
|
54
|
-
# @return [String]
|
|
55
|
-
def ext(value)
|
|
56
|
-
case value
|
|
57
|
-
when /^not_found/ then "notfound"
|
|
58
|
-
when /^redirection/ then "redirect"
|
|
59
|
-
else @ext
|
|
60
|
-
end
|
|
48
|
+
/^(?<pref>[^(]+)(?=\()/ =~ key.downcase
|
|
49
|
+
prefix_dir = "#{@dir}/#{pref}"
|
|
50
|
+
file = "#{filename(key)}.#{ext(value)}"
|
|
51
|
+
@storage.save prefix_dir, file, value
|
|
61
52
|
end
|
|
62
53
|
|
|
63
54
|
# Read item
|
|
@@ -86,7 +77,7 @@ module Relaton
|
|
|
86
77
|
value = self[key]
|
|
87
78
|
return unless value
|
|
88
79
|
|
|
89
|
-
if value.match?
|
|
80
|
+
if value.match?(/^not_found/)
|
|
90
81
|
value.match(/\d{4}-\d{2}-\d{2}/).to_s
|
|
91
82
|
else
|
|
92
83
|
doc = Nokogiri::XML value
|
|
@@ -96,41 +87,21 @@ module Relaton
|
|
|
96
87
|
|
|
97
88
|
# Returns all items
|
|
98
89
|
# @return [Array<String>]
|
|
99
|
-
def all
|
|
100
|
-
|
|
101
|
-
content = File.read(f, encoding: "utf-8")
|
|
102
|
-
block_given? ? yield(f, content) : content
|
|
103
|
-
end
|
|
90
|
+
def all(&block)
|
|
91
|
+
@storage.all(@dir, &block)
|
|
104
92
|
end
|
|
105
93
|
|
|
106
94
|
# Delete item
|
|
107
95
|
# @param key [String]
|
|
108
96
|
def delete(key)
|
|
109
|
-
|
|
110
|
-
f = search_ext(file)
|
|
111
|
-
File.delete f if f
|
|
97
|
+
@storage.delete filename(key)
|
|
112
98
|
end
|
|
113
99
|
|
|
114
100
|
# Check if version of the DB match to the gem grammar hash.
|
|
115
101
|
# @param fdir [String] dir pathe to flover cache
|
|
116
|
-
# @return [
|
|
102
|
+
# @return [Boolean]
|
|
117
103
|
def check_version?(fdir)
|
|
118
|
-
|
|
119
|
-
return false unless File.exist? version_dir
|
|
120
|
-
|
|
121
|
-
v = File.read version_dir, encoding: "utf-8"
|
|
122
|
-
v.strip == grammar_hash(fdir)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Set version of the DB to the gem grammar hash.
|
|
126
|
-
# @param fdir [String] dir pathe to flover cache
|
|
127
|
-
# @return [Relaton::DbCache]
|
|
128
|
-
def set_version(fdir)
|
|
129
|
-
file_version = "#{fdir}/version"
|
|
130
|
-
unless File.exist? file_version
|
|
131
|
-
file_safe_write file_version, grammar_hash(fdir)
|
|
132
|
-
end
|
|
133
|
-
self
|
|
104
|
+
@storage.check_version? fdir
|
|
134
105
|
end
|
|
135
106
|
|
|
136
107
|
# if cached reference is undated, expire it after 60 days
|
|
@@ -144,112 +115,54 @@ module Relaton
|
|
|
144
115
|
year || Date.today - date < 60
|
|
145
116
|
end
|
|
146
117
|
|
|
147
|
-
protected
|
|
148
|
-
|
|
149
|
-
# @param fdir [String] dir pathe to flover cache
|
|
150
|
-
# @return [String]
|
|
151
|
-
def grammar_hash(fdir)
|
|
152
|
-
type = fdir.split("/").last
|
|
153
|
-
Relaton::Registry.instance.by_type(type)&.grammar_hash
|
|
154
|
-
end
|
|
155
|
-
|
|
156
118
|
# Reads file by a key
|
|
157
119
|
#
|
|
158
120
|
# @param key [String]
|
|
159
121
|
# @return [String, NilClass]
|
|
160
122
|
def get(key)
|
|
161
|
-
|
|
162
|
-
return unless (f = search_ext(file))
|
|
163
|
-
|
|
164
|
-
File.read(f, encoding: "utf-8")
|
|
123
|
+
@storage.get filename(key), static: @ext == "yml"
|
|
165
124
|
end
|
|
166
125
|
|
|
167
126
|
private
|
|
168
127
|
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
128
|
+
# @param value [String]
|
|
129
|
+
# @return [String]
|
|
130
|
+
def ext(value)
|
|
131
|
+
case value
|
|
132
|
+
when /^not_found/ then "notfound"
|
|
133
|
+
when /^redirection/ then "redirect"
|
|
134
|
+
else @ext
|
|
135
|
+
end
|
|
176
136
|
end
|
|
177
137
|
|
|
178
138
|
# Return item's file name
|
|
179
139
|
# @param key [String]
|
|
180
140
|
# @return [String]
|
|
181
141
|
def filename(key)
|
|
182
|
-
prefcode = key.downcase.match
|
|
142
|
+
prefcode = key.downcase.match(/^(?<prefix>[^(]+)\((?<code>[^)]+)/)
|
|
183
143
|
fn = if prefcode
|
|
184
|
-
"#{prefcode[:prefix]}/#{prefcode[:code].gsub(/[-:\s
|
|
144
|
+
"#{prefcode[:prefix]}/#{prefcode[:code].gsub(/[-:\s\/()]/, '_')
|
|
145
|
+
.squeeze('_')}"
|
|
185
146
|
else
|
|
186
147
|
key.gsub(/[-:\s]/, "_")
|
|
187
148
|
end
|
|
188
149
|
"#{@dir}/#{fn.sub(/(,|_$)/, '')}"
|
|
189
150
|
end
|
|
190
151
|
|
|
152
|
+
# Check if a file content is redirection
|
|
191
153
|
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def search_ext(file)
|
|
198
|
-
if File.exist?("#{file}.#{@ext}")
|
|
199
|
-
"#{file}.#{@ext}"
|
|
200
|
-
elsif File.exist? "#{file}.notfound"
|
|
201
|
-
"#{file}.notfound"
|
|
202
|
-
elsif File.exist? "#{file}.redirect"
|
|
203
|
-
"#{file}.redirect"
|
|
204
|
-
end
|
|
154
|
+
# @prarm value [String] file content
|
|
155
|
+
# @return [String, NilClass] redirection code or nil
|
|
156
|
+
def redirect?(value)
|
|
157
|
+
%r{redirection\s(?<code>.*)} =~ value
|
|
158
|
+
code
|
|
205
159
|
end
|
|
206
160
|
|
|
207
161
|
# Return item's subdir
|
|
208
162
|
# @param key [String]
|
|
209
163
|
# @return [String]
|
|
210
|
-
def prefix(key)
|
|
211
|
-
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# @param file [String]
|
|
215
|
-
# @content [String]
|
|
216
|
-
def file_safe_write(file, content)
|
|
217
|
-
File.open file, File::RDWR | File::CREAT, encoding: "UTF-8" do |f|
|
|
218
|
-
Timeout.timeout(10) { f.flock File::LOCK_EX }
|
|
219
|
-
f.write content
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
class << self
|
|
224
|
-
private
|
|
225
|
-
|
|
226
|
-
def global_bibliocache_name
|
|
227
|
-
"#{Dir.home}/.relaton/cache"
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def local_bibliocache_name(cachename)
|
|
231
|
-
return nil if cachename.nil?
|
|
232
|
-
|
|
233
|
-
cachename = "relaton" if cachename.empty?
|
|
234
|
-
"#{cachename}/cache"
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
public
|
|
238
|
-
|
|
239
|
-
# Initialse and return relaton instance, with local and global cache names
|
|
240
|
-
# local_cache: local cache name; none created if nil; "relaton" created
|
|
241
|
-
# if empty global_cache: boolean to create global_cache
|
|
242
|
-
# flush_caches: flush caches
|
|
243
|
-
def init_bib_caches(opts) # rubocop:disable Metrics/CyclomaticComplexity
|
|
244
|
-
globalname = global_bibliocache_name if opts[:global_cache]
|
|
245
|
-
localname = local_bibliocache_name(opts[:local_cache])
|
|
246
|
-
localname = "relaton" if localname&.empty?
|
|
247
|
-
if opts[:flush_caches]
|
|
248
|
-
FileUtils.rm_rf globalname unless globalname.nil?
|
|
249
|
-
FileUtils.rm_rf localname unless localname.nil?
|
|
250
|
-
end
|
|
251
|
-
Relaton::Db.new(globalname, localname)
|
|
252
|
-
end
|
|
253
|
-
end
|
|
164
|
+
# def prefix(key)
|
|
165
|
+
# key.downcase.match(/^[^(]+(?=\()/).to_s
|
|
166
|
+
# end
|
|
254
167
|
end
|
|
255
168
|
end
|