relaton 1.7.3 → 1.7.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +46 -0
  3. data/docs/README.adoc +151 -3
  4. data/lib/relaton.rb +8 -7
  5. data/lib/relaton/config.rb +5 -2
  6. data/lib/relaton/db.rb +283 -92
  7. data/lib/relaton/db_cache.rb +25 -3
  8. data/lib/relaton/processor.rb +11 -0
  9. data/lib/relaton/registry.rb +1 -1
  10. data/lib/relaton/util.rb +1 -1
  11. data/lib/relaton/version.rb +1 -1
  12. data/lib/relaton/workers_pool.rb +21 -0
  13. data/relaton.gemspec +6 -4
  14. data/spec/relaton/config_spec.rb +1 -1
  15. data/spec/relaton/db_spec.rb +154 -0
  16. data/spec/relaton/processor_spec.rb +4 -0
  17. data/spec/relaton/regirtry_spec.rb +51 -45
  18. data/spec/relaton_spec.rb +26 -17
  19. data/spec/vcr_cassetes/19133_2005.yml +22 -22
  20. data/spec/vcr_cassetes/async_fetch.yml +4423 -0
  21. data/spec/vcr_cassetes/bsi_bs_en_iso_8848.yml +159 -0
  22. data/spec/vcr_cassetes/cc_dir_10005_2019.yml +14 -14
  23. data/spec/vcr_cassetes/cie_001_1980.yml +120 -0
  24. data/spec/vcr_cassetes/ecma_6.yml +134 -67
  25. data/spec/vcr_cassetes/fisp_140.yml +4 -4
  26. data/spec/vcr_cassetes/gb_t_20223_2006.yml +11 -11
  27. data/spec/vcr_cassetes/iec_60050_102_2007.yml +30 -26
  28. data/spec/vcr_cassetes/iec_combined_included.yml +71 -73
  29. data/spec/vcr_cassetes/ieee_528_2019.yml +41 -39
  30. data/spec/vcr_cassetes/iho_b_11.yml +11 -11
  31. data/spec/vcr_cassetes/iso_111111119115_1.yml +4 -4
  32. data/spec/vcr_cassetes/iso_19115.yml +23 -23
  33. data/spec/vcr_cassetes/iso_19115_1.yml +21 -21
  34. data/spec/vcr_cassetes/iso_19115_1_2.yml +43 -43
  35. data/spec/vcr_cassetes/iso_awi_24229.yml +20 -20
  36. data/spec/vcr_cassetes/iso_combined_applied.yml +41 -41
  37. data/spec/vcr_cassetes/iso_combined_included.yml +42 -42
  38. data/spec/vcr_cassetes/itu_combined_included.yml +219 -159
  39. data/spec/vcr_cassetes/ogc_19_025r1.yml +1520 -1124
  40. data/spec/vcr_cassetes/ogm_ami4ccm_1_0.yml +11 -23
  41. data/spec/vcr_cassetes/rfc_8341.yml +47 -15
  42. data/spec/vcr_cassetes/rfc_unsuccess.yml +70 -0
  43. data/spec/vcr_cassetes/sp_800_38b.yml +4 -4
  44. data/spec/vcr_cassetes/un_rtade_cefact_2004_32.yml +34 -36
  45. data/spec/vcr_cassetes/w3c_json_ld11.yml +12 -12
  46. metadata +40 -39
  47. data/.github/workflows/macos.yml +0 -34
  48. data/.github/workflows/ubuntu.yml +0 -32
  49. data/.github/workflows/windows.yml +0 -35
  50. data/localcache/iec/iec_60050_102_2007.xml +0 -58
  51. data/localcache/iec/version +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f957d5a962fa3529fb21f4289ff4dc648029497ec3bf209e084b85c49a336531
4
- data.tar.gz: 6e6ad879c1552c8396c8f365c4bdc9c088197424f695b68b0dbeec9cc272f1a4
3
+ metadata.gz: d0ff67d3ebeb9332a6aa18ad88e8b585478c29adb27ccda935ab9b8af4b34e2b
4
+ data.tar.gz: 9aefca4601eb5bd7c4491dafc41fcee87cbb7e5d7508ee05086cfa39122bfbef
5
5
  SHA512:
6
- metadata.gz: f87f3d11aae3e805fa2fe3d9875692c2004a7b7a51fc1235181f8a4a9b314cf0403e50a4134fb093edc0da2cdb42339a7fe4ee7024e25de92d7923bf5029ab35
7
- data.tar.gz: 2684a909260936e26f9a2b987c667e61d24e96c01f326bc008deaf9358f1dc349f216c239932ee5780325057bfb192426173d1e226c031880743ff4ce0ad4d12
6
+ metadata.gz: b69bf1fbbbc4258bd22304dda3d54b23bc8a8e2361aeff2f26487518236e6b46690d0a793dd076489749ae0dcd93a6c3da7bc1969d3e877149ad9722b9ed0e9e
7
+ data.tar.gz: '0682111008bcb648773cd0e90bb4f5a789007b5d5deca6aa2c5aa245b1f677a136eff936c6256b8e6bf76bcf4cb65f8371ba0cb5fe0c9ffe908e7d4640031342'
@@ -0,0 +1,46 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ name: rake
4
+
5
+ on:
6
+ push:
7
+ branches: [ master, main ]
8
+ tags: [ v* ]
9
+ pull_request:
10
+
11
+ jobs:
12
+ rake:
13
+ name: Test on Ruby ${{ matrix.ruby }} ${{ matrix.os }}
14
+ runs-on: ${{ matrix.os }}
15
+ continue-on-error: ${{ matrix.experimental }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ ruby: [ '2.7', '2.6', '2.5', '2.4' ]
20
+ os: [ ubuntu-latest, windows-latest, macos-latest ]
21
+ experimental: [ false ]
22
+ include:
23
+ - ruby: '3.0'
24
+ os: 'ubuntu-latest'
25
+ experimental: true
26
+ - ruby: '3.0'
27
+ os: 'windows-latest'
28
+ experimental: true
29
+ - ruby: '3.0'
30
+ os: 'macos-latest'
31
+ experimental: true
32
+ steps:
33
+ - uses: actions/checkout@v2
34
+ with:
35
+ submodules: true
36
+
37
+ # https://github.com/ruby-debug/debase/issues/89#issuecomment-686827382
38
+ - if: matrix.os == 'macos-latest' && matrix.ruby == '2.5'
39
+ run: echo BUNDLE_BUILD__DEBASE="--with-cflags=\"-Wno-error=implicit-function-declaration\"" >> $GITHUB_ENV
40
+
41
+ - uses: ruby/setup-ruby@v1
42
+ with:
43
+ ruby-version: ${{ matrix.ruby }}
44
+ bundler-cache: true
45
+
46
+ - run: bundle exec rake
data/docs/README.adoc CHANGED
@@ -64,6 +64,11 @@ e.g. `get("ISO 19115-1", "2014", all_parts: true)` is transformed into a referen
64
64
 
65
65
  === Create DB
66
66
 
67
+ `Relaton::Db#new(globalcache, localcache)` creates new DB. Returns Relaton::Db instance.
68
+
69
+ * `globalcache` - (String or nil) path to globalcache directory
70
+ * `localcache` - (String or nil) path to localcache directory
71
+
67
72
  [source,ruby]
68
73
  ----
69
74
  require "relaton"
@@ -97,9 +102,46 @@ db = Relaton::Db.new("globalcache", "localcache")
97
102
  ...
98
103
  ----
99
104
 
105
+ === Modify DB
106
+
107
+ ==== Move DB
108
+
109
+ `Relaton::Db#mv(new_dir, type: :global)` moves DB directory to new location. Returns path to new directory if successful, or `nil` if target directiory exists.
110
+
111
+ * `new_dir` - (String) new cache location
112
+ * `type` - (Symbol) type of cache DB. Allowed values are: `:global`, `:local`. Default is `:global`.
113
+
114
+ [source,ruby]
115
+ ----
116
+ db.mv("new_global_dir")
117
+ => "new_global_dir"
118
+
119
+ db.mv("new_local_dir", type: :local)
120
+ => "new_local_dir"
121
+ ----
122
+
123
+ ==== Clear DB
124
+
125
+ `Relaton::Db#clear` removes all entries form DB
126
+
100
127
  === Fetch documens
101
128
 
102
- ==== Fetch by references
129
+ ==== Fetch document by references
130
+
131
+ There are 3 fetching methods:
132
+
133
+ * `Relaton::Db#fetch(reference, year, options)` - fetches document from local cache or remote source.
134
+ * `Relaton::Db#fetch_db(reference, year, options)` - fetches document from local cache
135
+ * `Relaton::Db#fetch_async(reference, year, options, &block)` - fetches document asynchronously
136
+
137
+ Arguments:
138
+
139
+ * `reference` - (String) reference to fethc document
140
+ * `year` - (String or nil) year to filter relult (optional)
141
+ * `options` - (Hash) hash of options. Alloved options:
142
+ - `:all_parts` - (Boolean) should be `true` if all-parts reference is required
143
+ - `:keep_yer` - (Boolean) should be `true` if undated reference should return actual reference with year
144
+ - `:retries` - (Number) number of network retries. Default 1
103
145
 
104
146
  [source,ruby]
105
147
  ----
@@ -114,7 +156,7 @@ x = db.fetch("ISO 19011")
114
156
  => #<RelatonIsoBib::IsoBibliographicItem:0x007fb1d0ab2f00
115
157
  ...
116
158
 
117
- x = db.fetch("ISO 19011", "2011")
159
+ x = db.fetch("ISO 19011", "2011", retries: 3)
118
160
  [relaton-iso] ("ISO 19011") fetching...
119
161
  [relaton-iso] ("ISO 19011") found ISO 19011:2011
120
162
  => #<RelatonIsoBib::IsoBibliographicItem:0x007fb1d2593068
@@ -125,6 +167,36 @@ x = db.fetch("ISO 19115", nil, all_parts: true)
125
167
  [relaton-iso] ("ISO 19115") found ISO 19115 (all parts)
126
168
  => #<RelatonIsoBib::IsoBibliographicItem:0x007fb1d0ae8bf0
127
169
  ...
170
+
171
+ # Fetchig from local cache
172
+
173
+ x = db.fetch("ISO 19011")
174
+ => #<RelatonIsoBib::IsoBibliographicItem:0x007fde5f48a9f0
175
+ ...
176
+
177
+ x = db.fetch_db("ISO 5749")
178
+ => nil
179
+
180
+ # Fetching asynchronously
181
+
182
+ # prepare queue for results
183
+ results = Queue.new
184
+
185
+ # references ot fetch
186
+ refs = ["ISO 19011", "ISO 19115"]
187
+
188
+ # fetch documents
189
+ refs.each do |ref|
190
+ db.fetch_async(ref) do |doc|
191
+ results << [ref, doc]
192
+ end
193
+ end
194
+
195
+ # wait until documets fetching
196
+ refs.size.times do
197
+ ref, doc = results.pop
198
+ # do thatever you need with result x
199
+ end
128
200
  ----
129
201
 
130
202
  ==== Fetch by URN
@@ -192,6 +264,82 @@ bib.relation[1].bibitem.docidentifier[0].id
192
264
  [source,ruby]
193
265
  ----
194
266
  bib = db.fetch "ISO 19115-1, Amd 1"
267
+ => ["Chinese Standard", "GB/T 1.1"]
268
+ [relaton-iso] ("ISO 19115-1") fetching...
269
+ [relaton-iso] ("ISO 19115-1") found ISO 19115-1:2014
270
+ [relaton-iso] ("ISO 19115-1/Amd 1") fetching...
271
+ [relaton-iso] ("ISO 19115-1/Amd 1") found ISO 19115-1:2014/Amd 1:2018
272
+ => #<RelatonIsoBib::IsoBibliographicItem:0x007fb09b36d1b8
273
+ ...
274
+ ----
275
+
276
+ ==== Fetch all documents from cache
277
+
278
+ `Relaton::Db#fetch_all(text = nil, edition: nil, year: nil)` - fetches all document from local cache
279
+
280
+ * `text` - (String) filter entries by a text (optional)
281
+ * `edition` - (String) filter entries by an edition (optional)
282
+ * `year` - (Integer) filter entries by a year (optional)
283
+
284
+ [source,ruby]
285
+ ----
286
+ # query for all entries in a cahche
287
+
288
+ items = db.fetch_all
289
+ => [#<RelatonIec::IecBibliographicItem:0x007facda8fdc28
290
+ ...
291
+
292
+ items.size
293
+ => 6
294
+
295
+ # query for all entries in a cahche for a certain string
296
+
297
+ items = db.fetch_all("mathematical terminology")
298
+ => [#<RelatonIec::IecBibliographicItem:0x007ffeae5bd240
299
+ ...
300
+
301
+ items.size
302
+ => 1
303
+
304
+ items[0].docidentifier[0].id
305
+ => "IEC 60050-102:2007"
306
+
307
+ # query for all entries in a cahche for a certain string and edition
308
+
309
+ items = db.fetch_all("system", edition: "2")
310
+ => [#<RelatonIsoBib::IsoBibliographicItem:0x007ffebe2d1be8
311
+ ...
312
+
313
+ items.size
314
+ => 1
315
+
316
+ items[0].docidentifier[0].id
317
+ => "ISO 19011:2011"
318
+
319
+ # query for all entries in a cahche for a certain string and year
320
+
321
+ items = db.fetch_all("system", year: 2018)
322
+ => [#<RelatonIsoBib::IsoBibliographicItem:0x007ffeae645fa0
323
+ ...
324
+
325
+ items.size
326
+ => 1
327
+
328
+ items[0].docidentifier[0].id
329
+ => "ISO 19011 (all parts)"
330
+ ----
331
+
332
+ === Static DB
333
+
334
+ This gem has a static DB which is distributed with it. Now the static contains documents:
335
+ ----
336
+ ISO/IEC DIR 1 IEC SUP
337
+ ISO/IEC DIR 1 ISO SUP
338
+ ISO/IEC DIR 1
339
+ ISO/IEC DIR 2 IEC
340
+ ISO/IEC DIR 2 ISO
341
+ ISO/IEC DIR IEC SUP
342
+ ISO/IEC DIR 1 JTC SUP
195
343
  ----
196
344
 
197
345
  === Get document type
@@ -229,7 +377,7 @@ x.to_xml bibdata: true
229
377
  db.load_entry("ISO(ISO 19011)")
230
378
  => "<bibdata type="standard">
231
379
  ...
232
- <?bibdata>"
380
+ </bibdata>"
233
381
  ----
234
382
 
235
383
  === Entry manipulation
data/lib/relaton.rb CHANGED
@@ -1,8 +1,9 @@
1
- require_relative "relaton/util"
2
- require_relative "relaton/config"
1
+ require "relaton/util"
2
+ require "relaton/config"
3
3
 
4
- require_relative "relaton/db"
5
- require_relative "relaton/db_cache"
6
- require_relative "relaton/version"
7
- require_relative "relaton/registry"
8
- require_relative "relaton/processor"
4
+ require "relaton/workers_pool"
5
+ require "relaton/db"
6
+ require "relaton/db_cache"
7
+ require "relaton/version"
8
+ require "relaton/registry"
9
+ require "relaton/processor"
@@ -12,10 +12,13 @@ module Relaton
12
12
  end
13
13
 
14
14
  class Configuration
15
- attr_accessor :logs
15
+ attr_accessor :logs, :use_api
16
16
 
17
17
  def initialize
18
- @logs = %i(warning error)
18
+ @logs = %i(info error)
19
+
20
+ # @TODO change to true when we start using api.relaton.org
21
+ @use_api = false
19
22
  end
20
23
  end
21
24
 
data/lib/relaton/db.rb CHANGED
@@ -10,83 +10,127 @@ module Relaton
10
10
  # @param local_cache [String] directory of local DB
11
11
  def initialize(global_cache, local_cache)
12
12
  @registry = Relaton::Registry.instance
13
- @db = open_cache_biblio(global_cache, type: :global)
14
- @local_db = open_cache_biblio(local_cache, type: :local)
15
- @db_name = global_cache
16
- @local_db_name = local_cache
17
- static_db_name = File.expand_path "../relaton/static_cache", __dir__
18
- @static_db = open_cache_biblio static_db_name
13
+ gpath = global_cache && File.expand_path(global_cache)
14
+ @db = open_cache_biblio(gpath, type: :global)
15
+ lpath = local_cache && File.expand_path(local_cache)
16
+ @local_db = open_cache_biblio(lpath, type: :local)
17
+ @static_db = open_cache_biblio File.expand_path("../relaton/static_cache",
18
+ __dir__)
19
+ @queues = {}
19
20
  end
20
21
 
22
+ # Move global or local caches to anothe dirs
23
+ # @param new_dir [String, nil]
24
+ # @param type: [Symbol]
25
+ # @return [String, nil]
26
+ def mv(new_dir, type: :global)
27
+ case type
28
+ when :global
29
+ @db&.mv new_dir
30
+ when :local
31
+ @local_db&.mv new_dir
32
+ end
33
+ end
34
+
35
+ # Clear global and local databases
36
+ def clear
37
+ @db&.clear
38
+ @local_db&.clear
39
+ end
40
+
41
+ ##
21
42
  # The class of reference requested is determined by the prefix of the code:
22
- # GB Standard for gbbib, IETF for ietfbib, ISO for isobib, IEC or IEV for iecbib,
43
+ # GB Standard for gbbib, IETF for ietfbib, ISO for isobib, IEC or IEV for
44
+ # iecbib,
45
+ #
23
46
  # @param code [String] the ISO standard Code to look up (e.g. "ISO 9000")
24
47
  # @param year [String] the year the standard was published (optional)
25
- # @param opts [Hash] options; restricted to :all_parts if all-parts reference is required
26
- # @return [nil, RelatonBib::BibliographicItem, RelatonIsoBib::IsoBibliographicItem,
27
- # RelatonItu::ItuBibliographicItem, RelatonIetf::IetfBibliographicItem,
28
- # RelatonIec::IecBibliographicItem, RelatonIeee::IeeeBibliographicItem,
29
- # RelatonNist::NistBibliongraphicItem, RelatonGb::GbbibliographicItem,
30
- # RelatonOgc::OgcBibliographicItem, RelatonCalconnect::CcBibliographicItem]
48
+ #
49
+ # @param opts [Hash] options
50
+ # @option opts [Boolean] :all_parts If all-parts reference is required
51
+ # @option opts [Boolean] :keep_year If undated reference should return
52
+ # actual reference with year
53
+ # @option opts [Integer] :retries (1) Number of network retries
54
+ #
55
+ # @return [nil, RelatonBib::BibliographicItem,
56
+ # RelatonIsoBib::IsoBibliographicItem, RelatonItu::ItuBibliographicItem,
57
+ # RelatonIetf::IetfBibliographicItem, RelatonIec::IecBibliographicItem,
58
+ # RelatonIeee::IeeeBibliographicItem, RelatonNist::NistBibliongraphicItem,
59
+ # RelatonGb::GbbibliographicItem, RelatonOgc::OgcBibliographicItem,
60
+ # RelatonCalconnect::CcBibliographicItem, RelatinUn::UnBibliographicItem,
31
61
  # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
32
- # RelatonOmg::OmgBibliographicItem RelatinUn::UnBibliographicItem,
33
- # RelatonW3c::W3cBibliographicItem
62
+ # RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
63
+ ##
34
64
  def fetch(code, year = nil, opts = {})
35
65
  stdclass = standard_class(code) || return
36
66
  processor = @registry.processors[stdclass]
37
- ref = processor.respond_to?(:urn_to_code) ? processor.urn_to_code(code)&.first : code
67
+ ref = if processor.respond_to?(:urn_to_code)
68
+ processor.urn_to_code(code)&.first
69
+ else code
70
+ end
38
71
  ref ||= code
39
- cd = combine_doc ref, year, opts, stdclass
40
- return cd if cd
72
+ result = combine_doc ref, year, opts, stdclass
73
+ result ||= check_bibliocache(ref, year, opts, stdclass)
74
+ result
75
+ end
41
76
 
42
- check_bibliocache(ref, year, opts, stdclass)
77
+ # @see Relaton::Db#fetch
78
+ def fetch_db(code, year = nil, opts = {})
79
+ opts[:fetch_db] = true
80
+ fetch code, year, opts
43
81
  end
44
82
 
45
- # @param code [String]
46
- # @param year [String, nil]
47
- # @param stdslass [String]
48
- # @return [nil, RelatonBib::BibliographicItem, RelatonIsoBib::IsoBibliographicItem,
49
- # RelatonItu::ItuBibliographicItem, RelatonIetf::IetfBibliographicItem,
50
- # RelatonIec::IecBibliographicItem, RelatonIeee::IeeeBibliographicItem,
51
- # RelatonNist::NistBibliongraphicItem, RelatonGb::GbbibliographicItem,
52
- # RelatonOgc::OgcBibliographicItem, RelatonCalconnect::CcBibliographicItem]
53
- # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
54
- # RelatonOmg::OmgBibliographicItem RelatinUn::UnBibliographicItem,
55
- # RelatonW3c::W3cBibliographicItem
56
- def combine_doc(code, year, opts, stdclass) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
57
- if (refs = code.split " + ").size > 1
58
- reltype = "derivedFrom"
59
- reldesc = nil
60
- elsif (refs = code.split ", ").size > 1
61
- reltype = "complements"
62
- reldesc = RelatonBib::FormattedString.new content: "amendment"
63
- else return
83
+ # fetch all standards from DB
84
+ # @param test [String, nil]
85
+ # @param edition [String], nil
86
+ # @param year [Integer, nil]
87
+ # @return [Array]
88
+ def fetch_all(text = nil, edition: nil, year: nil)
89
+ result = @static_db.all do |file, yml|
90
+ search_yml file, yml, text, edition, year
91
+ end.compact
92
+ db = @db || @local_db
93
+ if db
94
+ result += db.all do |file, xml|
95
+ search_xml file, xml, text, edition, year
96
+ end.compact
64
97
  end
98
+ result
99
+ end
65
100
 
66
- doc = @registry.processors[stdclass].hash_to_bib docid: { id: code }
67
- ref = refs[0]
68
- updates = check_bibliocache(ref, year, opts, stdclass)
69
- doc.relation << RelatonBib::DocumentRelation.new(bibitem: updates, type: "updates") if updates
70
- refs[1..-1].each_with_object(doc) do |c, d|
71
- bib = check_bibliocache("#{ref}/#{c}", year, opts, stdclass)
72
- if bib
73
- d.relation << RelatonBib::DocumentRelation.new(type: reltype, description: reldesc, bibitem: bib)
101
+ # Fetch asynchronously
102
+ def fetch_async(code, year = nil, opts = {}, &_block) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
103
+ stdclass = standard_class code
104
+ if stdclass
105
+ unless @queues[stdclass]
106
+ processor = @registry.processors[stdclass]
107
+ wp = WorkersPool.new(processor.threads) { |args| yield fetch *args }
108
+ @queues[stdclass] = { queue: Queue.new, workers_pool: wp }
109
+ Thread.new { process_queue @queues[stdclass] }
74
110
  end
111
+ @queues[stdclass][:queue] << [code, year, opts]
112
+ else yield nil
75
113
  end
76
114
  end
77
115
 
78
116
  # @param code [String]
79
117
  # @param year [String, NilClass]
80
118
  # @param stdclass [Symbol, NilClass]
119
+ #
81
120
  # @param opts [Hash]
82
- # @return [nil, RelatonBib::BibliographicItem, RelatonIsoBib::IsoBibliographicItem,
83
- # RelatonItu::ItuBibliographicItem, RelatonIetf::IetfBibliographicItem,
84
- # RelatonIec::IecBibliographicItem, RelatonIeee::IeeeBibliographicItem,
85
- # RelatonNist::NistBibliongraphicItem, RelatonGb::GbbibliographicItem,
86
- # RelatonOgc::OgcBibliographicItem, RelatonCalconnect::CcBibliographicItem]
121
+ # @option opts [Boolean] :all_parts If all-parts reference is required
122
+ # @option opts [Boolean] :keep_year If undated reference should return
123
+ # actual reference with year
124
+ # @option opts [Integer] :retries (1) Number of network retries
125
+ #
126
+ # @return [nil, RelatonBib::BibliographicItem,
127
+ # RelatonIsoBib::IsoBibliographicItem, RelatonItu::ItuBibliographicItem,
128
+ # RelatonIetf::IetfBibliographicItem, RelatonIec::IecBibliographicItem,
129
+ # RelatonIeee::IeeeBibliographicItem, RelatonNist::NistBibliongraphicItem,
130
+ # RelatonGb::GbbibliographicItem, RelatonOgc::OgcBibliographicItem,
131
+ # RelatonCalconnect::CcBibliographicItem, RelatinUn::UnBibliographicItem,
87
132
  # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
88
- # RelatonOmg::OmgBibliographicItem RelatinUn::UnBibliographicItem,
89
- # RelatonW3c::W3cBibliographicItem
133
+ # RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
90
134
  def fetch_std(code, year = nil, stdclass = nil, opts = {})
91
135
  std = nil
92
136
  @registry.processors.each do |name, processor|
@@ -137,6 +181,98 @@ module Relaton
137
181
 
138
182
  private
139
183
 
184
+ # @param file [String] file path
185
+ # @param yml [String] content in YAML format
186
+ # @param text [String, nil] text to serach
187
+ # @param edition [String, nil] edition to filter
188
+ # @param year [Integer, nil] year to filter
189
+ # @return [BibliographicItem, nil]
190
+ def search_yml(file, yml, text, edition, year)
191
+ item = search_edition_year(file, yml, edition, year)
192
+ return unless item
193
+
194
+ item if match_xml_text(item.to_xml(bibdata: true), text)
195
+ end
196
+
197
+ # @param file [String] file path
198
+ # @param xml [String] content in XML format
199
+ # @param text [String, nil] text to serach
200
+ # @param edition [String, nil] edition to filter
201
+ # @param year [Integer, nil] year to filter
202
+ # @return [BibliographicItem, nil]
203
+ def search_xml(file, xml, text, edition, year)
204
+ return unless text.nil? || match_xml_text(xml, text)
205
+
206
+ search_edition_year(file, xml, edition, year)
207
+ end
208
+
209
+ # @param file [String] file path
210
+ # @param content [String] content in XML or YAmL format
211
+ # @param edition [String, nil] edition to filter
212
+ # @param year [Integer, nil] year to filter
213
+ # @return [BibliographicItem, nil]
214
+ def search_edition_year(file, content, edition, year) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
215
+ processor = @registry.processors[standard_class(file.split("/")[-2])]
216
+ item = if file.match?(/xml$/) then processor.from_xml(content)
217
+ else processor.hash_to_bib(YAML.safe_load(content))
218
+ end
219
+ item if (edition.nil? || item.edition == edition) && (year.nil? ||
220
+ item.date.detect { |d| d.type == "published" && d.on(:year).to_s == year.to_s })
221
+ end
222
+
223
+ # @param xml [String] content in XML format
224
+ # @param text [String, nil] text to serach
225
+ # @return [Boolean]
226
+ def match_xml_text(xml, text)
227
+ %r{((?<attr>=((?<apstr>')|"))|>).*?#{text}.*?(?(<attr>)(?(<apstr>)'|")|<)}mi.match?(xml)
228
+ end
229
+
230
+ # @param code [String]
231
+ # @param year [String, nil]
232
+ # @param stdslass [String]
233
+ #
234
+ # @param opts [Hash] options
235
+ # @option opts [Boolean] :all_parts If all-parts reference is required
236
+ # @option opts [Boolean] :keep_year If undated reference should return
237
+ # actual reference with year
238
+ # @option opts [Integer] :retries (1) Number of network retries
239
+ #
240
+ # @return [nil, RelatonBib::BibliographicItem,
241
+ # RelatonIsoBib::IsoBibliographicItem, RelatonItu::ItuBibliographicItem,
242
+ # RelatonIetf::IetfBibliographicItem, RelatonIec::IecBibliographicItem,
243
+ # RelatonIeee::IeeeBibliographicItem, RelatonNist::NistBibliongraphicItem,
244
+ # RelatonGb::GbbibliographicItem, RelatonOgc::OgcBibliographicItem,
245
+ # RelatonCalconnect::CcBibliographicItem, RelatinUn::UnBibliographicItem,
246
+ # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
247
+ # RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
248
+ def combine_doc(code, year, opts, stdclass) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
249
+ if (refs = code.split " + ").size > 1
250
+ reltype = "derivedFrom"
251
+ reldesc = nil
252
+ elsif (refs = code.split ", ").size > 1
253
+ reltype = "complements"
254
+ reldesc = RelatonBib::FormattedString.new content: "amendment"
255
+ else return
256
+ end
257
+
258
+ doc = @registry.processors[stdclass].hash_to_bib docid: { id: code }
259
+ ref = refs[0]
260
+ updates = check_bibliocache(ref, year, opts, stdclass)
261
+ if updates
262
+ doc.relation << RelatonBib::DocumentRelation.new(bibitem: updates,
263
+ type: "updates")
264
+ end
265
+ divider = stdclass == :relaton_itu ? " " : "/"
266
+ refs[1..-1].each_with_object(doc) do |c, d|
267
+ bib = check_bibliocache(ref + divider + c, year, opts, stdclass)
268
+ if bib
269
+ d.relation << RelatonBib::DocumentRelation.new(
270
+ type: reltype, description: reldesc, bibitem: bib
271
+ )
272
+ end
273
+ end
274
+ end
275
+
140
276
  # @param code [String] code of standard
141
277
  # @return [Symbol] standard class name
142
278
  def standard_class(code)
@@ -147,8 +283,8 @@ module Relaton
147
283
  allowed = @registry.processors.reduce([]) do |m, (_k, v)|
148
284
  m << v.prefix
149
285
  end
150
- warn <<~WARN
151
- #{code} does not have a recognised prefix: #{allowed.join(', ')}.
286
+ Util.log <<~WARN, :info
287
+ [relaton] #{code} does not have a recognised prefix: #{allowed.join(', ')}.
152
288
  See https://github.com/relaton/relaton/ for instructions on prefixing and wrapping document identifiers to disambiguate them.
153
289
  WARN
154
290
  end
@@ -157,7 +293,13 @@ module Relaton
157
293
  # Fofmat ID
158
294
  # @param code [String]
159
295
  # @param year [String]
296
+ #
160
297
  # @param opts [Hash]
298
+ # @option opts [Boolean] :all_parts If all-parts reference is required
299
+ # @option opts [Boolean] :keep_year If undated reference should return
300
+ # actual reference with year
301
+ # @option opts [Integer] :retries (1) Number of network retries
302
+ #
161
303
  # @param stdClass [Symbol]
162
304
  # @return [Array<String>] docid and code
163
305
  def std_id(code, year, opts, stdclass)
@@ -180,79 +322,116 @@ module Relaton
180
322
 
181
323
  # @param entry [String] XML string
182
324
  # @param stdclass [Symbol]
183
- # @param id [String] docid
184
- # @return [nil, RelatonBib::BibliographicItem, RelatonIsoBib::IsoBibliographicItem,
185
- # RelatonItu::ItuBibliographicItem, RelatonIetf::IetfBibliographicItem,
186
- # RelatonIec::IecBibliographicItem, RelatonIeee::IeeeBibliographicItem,
187
- # RelatonNist::NistBibliongraphicItem, RelatonGb::GbbibliographicItem,
188
- # RelatonOgc::OgcBibliographicItem, RelatonCalconnect::CcBibliographicItem]
325
+ # @return [nil, RelatonBib::BibliographicItem,
326
+ # RelatonIsoBib::IsoBibliographicItem, RelatonItu::ItuBibliographicItem,
327
+ # RelatonIetf::IetfBibliographicItem, RelatonIec::IecBibliographicItem,
328
+ # RelatonIeee::IeeeBibliographicItem, RelatonNist::NistBibliongraphicItem,
329
+ # RelatonGb::GbbibliographicItem, RelatonOgc::OgcBibliographicItem,
330
+ # RelatonCalconnect::CcBibliographicItem, RelatinUn::UnBibliographicItem,
189
331
  # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
190
- # RelatonOmg::OmgBibliographicItem RelatinUn::UnBibliographicItem,
191
- # RelatonW3c::W3cBibliographicItem
192
- def bib_retval(entry, stdclass, _id)
193
- entry.match?(/^not_found/) ? nil : @registry.processors[stdclass].from_xml(entry)
332
+ # RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
333
+ def bib_retval(entry, stdclass)
334
+ if entry.nil? || entry.match?(/^not_found/) then nil
335
+ else @registry.processors[stdclass].from_xml(entry)
336
+ end
194
337
  end
195
338
 
196
339
  # @param code [String]
197
340
  # @param year [String]
341
+ #
198
342
  # @param opts [Hash]
343
+ # @option opts [Boolean] :all_parts If all-parts reference is required
344
+ # @option opts [Boolean] :keep_year If undated reference should return
345
+ # actual reference with year
346
+ # @option opts [Integer] :retries (1) Number of network retries
347
+ #
199
348
  # @param stdclass [Symbol]
200
- # @return [nil, RelatonBib::BibliographicItem, RelatonIsoBib::IsoBibliographicItem,
201
- # RelatonItu::ItuBibliographicItem, RelatonIetf::IetfBibliographicItem,
202
- # RelatonIec::IecBibliographicItem, RelatonIeee::IeeeBibliographicItem,
203
- # RelatonNist::NistBibliongraphicItem, RelatonGb::GbbibliographicItem,
204
- # RelatonOgc::OgcBibliographicItem, RelatonCalconnect::CcBibliographicItem]
349
+ # @return [nil, RelatonBib::BibliographicItem,
350
+ # RelatonIsoBib::IsoBibliographicItem, RelatonItu::ItuBibliographicItem,
351
+ # RelatonIetf::IetfBibliographicItem, RelatonIec::IecBibliographicItem,
352
+ # RelatonIeee::IeeeBibliographicItem, RelatonNist::NistBibliongraphicItem,
353
+ # RelatonGb::GbbibliographicItem, RelatonOgc::OgcBibliographicItem,
354
+ # RelatonCalconnect::CcBibliographicItem, RelatinUn::UnBibliographicItem,
205
355
  # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
206
- # RelatonOmg::OmgBibliographicItem RelatinUn::UnBibliographicItem,
207
- # RelatonW3c::W3cBibliographicItem
356
+ # RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
208
357
  def check_bibliocache(code, year, opts, stdclass) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
209
358
  id, searchcode = std_id(code, year, opts, stdclass)
210
359
  yaml = @static_db[id]
211
- return @registry.processors[stdclass].hash_to_bib YAML.safe_load(yaml) if yaml
360
+ if yaml
361
+ return @registry.processors[stdclass].hash_to_bib YAML.safe_load(yaml)
362
+ end
212
363
 
213
364
  db = @local_db || @db
214
365
  altdb = @local_db && @db ? @db : nil
215
366
  if db.nil?
216
- bibentry = new_bib_entry(searchcode, year, opts, stdclass, db: db, id: id)
217
- return bib_retval(bibentry, stdclass, id)
367
+ return if opts[:fetch_db]
368
+
369
+ bibentry = new_bib_entry(searchcode, year, opts, stdclass, db: db,
370
+ id: id)
371
+ return bib_retval(bibentry, stdclass)
218
372
  end
219
373
 
220
374
  db.delete(id) unless db.valid_entry?(id, year)
221
375
  if altdb
222
- # db[id] ||= altdb[id]
223
- db.clone_entry id, altdb
224
- db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db, id: id)
376
+ return bib_retval(altdb[id], stdclass) if opts[:fetch_db]
377
+
378
+ 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)
225
381
  altdb.clone_entry(id, db) if !altdb.valid_entry?(id, year)
226
382
  else
227
- db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db, id: id)
383
+ return bib_retval(db[id], stdclass) if opts[:fetch_db]
384
+
385
+ db[id] ||= new_bib_entry(searchcode, year, opts, stdclass, db: db,
386
+ id: id)
228
387
  end
229
- bib_retval(db[id], stdclass, id)
388
+ bib_retval(db[id], stdclass)
230
389
  end
231
390
 
232
391
  # @param code [String]
233
392
  # @param year [String]
393
+ #
234
394
  # @param opts [Hash]
395
+ # @option opts [Boolean] :all_parts If all-parts reference is required
396
+ # @option opts [Boolean] :keep_year If undated reference should return
397
+ # actual reference with year
398
+ # @option opts [Integer] :retries (1) Number of network retries
399
+ #
235
400
  # @param stdclass [Symbol]
236
401
  # @param db [Relaton::DbCache,`NilClass]
237
402
  # @param id [String] docid
238
403
  # @return [String]
239
404
  def new_bib_entry(code, year, opts, stdclass, **args) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
240
- bib = @registry.processors[stdclass].get(code, year, opts)
405
+ bib = net_retry(code, year, opts, stdclass, opts.fetch(:retries, 1))
241
406
  bib_id = bib&.docidentifier&.first&.id
242
407
 
243
408
  # when docid doesn't match bib's id then return a reference to bib's id
244
- if args[:db] && args[:id] && bib_id && args[:id] !~ %r{#{Regexp.quote("(#{bib_id})")}}
409
+ if args[:db] && args[:id] &&
410
+ bib_id && args[:id] !~ %r{#{Regexp.quote("(#{bib_id})")}}
245
411
  bid = std_id(bib.docidentifier.first.id, nil, {}, stdclass).first
246
412
  args[:db][bid] ||= bib_entry bib
247
413
  "redirection #{bid}"
248
- else
249
- bib_entry bib
414
+ else bib_entry bib
250
415
  end
251
416
  end
252
417
 
253
- # @param bib [RelatonGb::GbBibliongraphicItem, RelatonIsoBib::IsoBibliographicItem,
254
- # RelatonIetf::IetfBibliographicItem, RelatonItu::ItuBibliographicItem,
255
- # RelatonNist::NistBibliongraphicItem, RelatonOgc::OgcBibliographicItem]
418
+ # @raise [RelatonBib::RequestError]
419
+ def net_retry(code, year, opts, stdclass, retries)
420
+ @registry.processors[stdclass].get(code, year, opts)
421
+ rescue RelatonBib::RequestError => e
422
+ raise e unless retries > 1
423
+
424
+ net_retry(code, year, opts, stdclass, retries - 1)
425
+ end
426
+
427
+ # @param bib [RelatonBib::BibliographicItem,
428
+ # RelatonIsoBib::IsoBibliographicItem, RelatonItu::ItuBibliographicItem,
429
+ # RelatonIetf::IetfBibliographicItem, RelatonIec::IecBibliographicItem,
430
+ # RelatonIeee::IeeeBibliographicItem, RelatonNist::NistBibliongraphicItem,
431
+ # RelatonGb::GbbibliographicItem, RelatonOgc::OgcBibliographicItem,
432
+ # RelatonCalconnect::CcBibliographicItem, RelatinUn::UnBibliographicItem,
433
+ # RelatonBipm::BipmBibliographicItem, RelatonIho::IhoBibliographicItem,
434
+ # RelatonOmg::OmgBibliographicItem, RelatonW3c::W3cBibliographicItem]
256
435
  # @return [String] XML or "not_found mm-dd-yyyy"
257
436
  def bib_entry(bib)
258
437
  if bib.respond_to? :to_xml
@@ -262,21 +441,33 @@ module Relaton
262
441
  end
263
442
  end
264
443
 
265
- # @param dir [String] DB directory
444
+ # @param dir [String, nil] DB directory
266
445
  # @param type [Symbol]
267
446
  # @return [Relaton::DbCache, NilClass]
268
- def open_cache_biblio(dir, type: :static)
447
+ def open_cache_biblio(dir, type: :static) # rubocop:disable Metrics/MethodLength
269
448
  return nil if dir.nil?
270
449
 
271
450
  db = DbCache.new dir, type == :static ? "yml" : "xml"
451
+ return db if type == :static
272
452
 
273
453
  Dir["#{dir}/*/"].each do |fdir|
274
- next if type == :static || db.check_version?(fdir)
454
+ next if db.check_version?(fdir)
275
455
 
276
- FileUtils.rm_rf(Dir.glob(fdir + "/*"), secure: true)
277
- warn "[relaton] cache #{fdir}: version is obsolete and cache is cleared."
456
+ FileUtils.rm_rf(fdir, secure: true)
457
+ Util.log(
458
+ "[relaton] WARNING: cache #{fdir}: version is obsolete and cache is "\
459
+ "cleared.",
460
+ :warning
461
+ )
278
462
  end
279
463
  db
280
464
  end
465
+
466
+ # @param qwp [Hash]
467
+ # @option qwp [Queue] :queue The queue of references to fetch
468
+ # @option qwp [Relaton::WorkersPool] :workers_pool The pool of workers
469
+ def process_queue(qwp)
470
+ while args = qwp[:queue].pop; qwp[:workers_pool] << args end
471
+ end
281
472
  end
282
473
  end