relaton 2.1.0 → 2.2.0.pre.alpha.1
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/CLAUDE.md +8 -0
- data/Gemfile +8 -0
- data/lib/relaton/db/cache.rb +232 -0
- data/lib/relaton/db/config.rb +24 -0
- data/lib/relaton/db/registry.rb +119 -0
- data/lib/relaton/db/util.rb +9 -0
- data/lib/relaton/db/version.rb +5 -0
- data/lib/relaton/db/workers_pool.rb +22 -0
- data/lib/relaton/db.rb +21 -206
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton.rb +2 -10
- data/relaton.gemspec +47 -36
- data/spec/relaton/config_spec.rb +1 -1
- data/spec/relaton/db_cache_spec.rb +4 -4
- data/spec/relaton/db_spec.rb +18 -7
- data/spec/relaton/registry_spec.rb +39 -39
- data/spec/relaton/util_spec.rb +1 -1
- data/spec/relaton_meta_spec.rb +25 -0
- data/spec/relaton_spec.rb +163 -88
- data/spec/spec_helper.rb +2 -2
- data/spec/vcr_cassetes/api_relaton_org.yml +8 -8
- data/spec/vcr_cassetes/api_relaton_org_unavailable.yml +76 -79
- data/spec/vcr_cassetes/cc_dir_10005_2019.yml +80 -77
- data/spec/vcr_cassetes/cipm_meeting_43.yml +1372 -1380
- data/spec/vcr_cassetes/gb_t_20223_2006.yml +500 -504
- data/spec/vcr_cassetes/iso_19115_1.yml +17204 -13047
- data/spec/vcr_cassetes/iso_19115_1_2.yml +193 -240
- data/spec/vcr_cassetes/iso_19115_1_std.yml +17207 -13050
- data/spec/vcr_cassetes/iso_19115_all_parts.yml +116 -160
- data/spec/vcr_cassetes/iso_19133_2005.yml +78 -82
- data/spec/vcr_cassetes/iso_combined_applied.yml +186 -232
- data/spec/vcr_cassetes/iso_combined_included.yml +187 -233
- data/spec/vcr_cassetes/ogc_19_025r1.yml +211 -208
- data/spec/vcr_cassetes/omg_ami4ccm_1_0.yml +246 -246
- data/spec/vcr_cassetes/rfc_8341.yml +1026 -1020
- metadata +133 -78
- data/.rubocop.yml +0 -12
- data/lib/relaton/config.rb +0 -24
- data/lib/relaton/db_cache.rb +0 -230
- data/lib/relaton/registry.rb +0 -118
- data/lib/relaton/util.rb +0 -7
- data/lib/relaton/workers_pool.rb +0 -21
- data/spec/vcr_cassetes/3gpp_tr_00_01u_umts_3_0_0.yml +0 -12720
- data/spec/vcr_cassetes/cie_001_1980.yml +0 -401
- data/spec/vcr_cassetes/doi_10_6028_nist_ir_8245.yml +0 -86
- data/spec/vcr_cassetes/ecma_6.yml +0 -112
- data/spec/vcr_cassetes/en_10160_1999.yml +0 -13624
- data/spec/vcr_cassetes/ieee_528_2019.yml +0 -2786
- data/spec/vcr_cassetes/iso_dis.yml +0 -157
data/lib/relaton/db_cache.rb
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
require "fileutils"
|
|
2
|
-
require "timeout"
|
|
3
|
-
|
|
4
|
-
module Relaton
|
|
5
|
-
class DbCache
|
|
6
|
-
# @return [String]
|
|
7
|
-
attr_reader :dir
|
|
8
|
-
|
|
9
|
-
# @param dir [String] DB directory
|
|
10
|
-
def initialize(dir, ext = "xml")
|
|
11
|
-
@dir = dir
|
|
12
|
-
@ext = ext
|
|
13
|
-
FileUtils::mkdir_p dir
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Move caches to anothe dir
|
|
17
|
-
# @param new_dir [String, nil]
|
|
18
|
-
# @return [String, nil]
|
|
19
|
-
def mv(new_dir)
|
|
20
|
-
return unless new_dir && @ext == "xml"
|
|
21
|
-
|
|
22
|
-
if File.exist? new_dir
|
|
23
|
-
Util.info "target directory exists `#{new_dir}`"
|
|
24
|
-
return
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
FileUtils.mv dir, new_dir
|
|
28
|
-
@dir = new_dir
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Clear database
|
|
32
|
-
def clear
|
|
33
|
-
FileUtils.rm_rf Dir.glob "#{dir}/*"
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Save item
|
|
37
|
-
# @param key [String]
|
|
38
|
-
# @param value [String] Bibitem xml serialization
|
|
39
|
-
def []=(key, value)
|
|
40
|
-
if value.nil?
|
|
41
|
-
delete key
|
|
42
|
-
return
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
prefix_dir = "#{@dir}/#{prefix(key)}"
|
|
46
|
-
FileUtils::mkdir_p prefix_dir
|
|
47
|
-
set_version prefix_dir
|
|
48
|
-
file_safe_write "#{filename(key)}.#{ext(value)}", value
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# @param value [String]
|
|
52
|
-
# @return [String]
|
|
53
|
-
def ext(value)
|
|
54
|
-
case value
|
|
55
|
-
when /^not_found/ then "notfound"
|
|
56
|
-
when /^redirection/ then "redirect"
|
|
57
|
-
else @ext
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Read item
|
|
62
|
-
# @param key [String]
|
|
63
|
-
# @return [String]
|
|
64
|
-
def [](key)
|
|
65
|
-
value = get(key)
|
|
66
|
-
if (code = redirect_code value)
|
|
67
|
-
self[code]
|
|
68
|
-
else
|
|
69
|
-
value
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
#
|
|
74
|
-
# Save entry from cache of `db` to this cache.
|
|
75
|
-
#
|
|
76
|
-
# @param [String] key key of the entry
|
|
77
|
-
# @param [Relaton::Db] db database
|
|
78
|
-
#
|
|
79
|
-
def clone_entry(key, db)
|
|
80
|
-
self[key] ||= db.get(key)
|
|
81
|
-
if (code = redirect_code get(key))
|
|
82
|
-
clone_entry code, db
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Return fetched date
|
|
87
|
-
# @param key [String]
|
|
88
|
-
# @return [String]
|
|
89
|
-
def fetched(key)
|
|
90
|
-
value = self[key]
|
|
91
|
-
return unless value
|
|
92
|
-
|
|
93
|
-
if value.match?(/^not_found/)
|
|
94
|
-
value.match(/\d{4}-\d{2}-\d{2}/).to_s
|
|
95
|
-
else
|
|
96
|
-
doc = Nokogiri::XML value
|
|
97
|
-
doc.at("/bibitem/fetched|bibdata/fetched")&.text
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Returns all items
|
|
102
|
-
# @return [Array<String>]
|
|
103
|
-
def all(&block)
|
|
104
|
-
Dir.glob("#{@dir}/**/*.{xml,yml,yaml}").map do |f|
|
|
105
|
-
content = File.read(f, encoding: "utf-8")
|
|
106
|
-
block ? yield(f, content) : content
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Delete item
|
|
111
|
-
# @param key [String]
|
|
112
|
-
def delete(key)
|
|
113
|
-
file = filename key
|
|
114
|
-
f = search_ext file
|
|
115
|
-
return unless f
|
|
116
|
-
|
|
117
|
-
if File.extname(f) == ".redirect"
|
|
118
|
-
code = redirect_code get(key)
|
|
119
|
-
delete code if code
|
|
120
|
-
end
|
|
121
|
-
File.delete f
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Check if version of the DB match to the gem grammar hash.
|
|
125
|
-
# @param fdir [String] dir pathe to flover cache
|
|
126
|
-
# @return [Boolean]
|
|
127
|
-
def check_version?(fdir)
|
|
128
|
-
version_dir = "#{fdir}/version"
|
|
129
|
-
return false unless File.exist? version_dir
|
|
130
|
-
|
|
131
|
-
v = File.read version_dir, encoding: "utf-8"
|
|
132
|
-
v.strip == self.class.grammar_hash(fdir)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# if cached reference is undated, expire it after 60 days
|
|
136
|
-
# @param key [String]
|
|
137
|
-
# @param year [String]
|
|
138
|
-
def valid_entry?(key, year)
|
|
139
|
-
datestr = fetched key
|
|
140
|
-
return false unless datestr
|
|
141
|
-
|
|
142
|
-
date = Date.parse datestr
|
|
143
|
-
year || Date.today - date < 60
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Reads file by a key
|
|
147
|
-
#
|
|
148
|
-
# @param key [String]
|
|
149
|
-
# @return [String, NilClass]
|
|
150
|
-
def get(key)
|
|
151
|
-
file = filename key
|
|
152
|
-
return unless (f = search_ext(file))
|
|
153
|
-
|
|
154
|
-
File.read(f, encoding: "utf-8")
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
# @param fdir [String] dir pathe to flover cache
|
|
158
|
-
# @return [String]
|
|
159
|
-
def self.grammar_hash(fdir)
|
|
160
|
-
type = fdir.split("/").last
|
|
161
|
-
Relaton::Registry.instance.by_type(type)&.grammar_hash
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
private
|
|
165
|
-
|
|
166
|
-
# @param value [String]
|
|
167
|
-
# @return [String]
|
|
168
|
-
def filename(key)
|
|
169
|
-
prefcode = key.downcase.match(/^(?<prefix>[^(]+)\((?<code>[^)]+)/)
|
|
170
|
-
fn = if prefcode
|
|
171
|
-
"#{prefcode[:prefix]}/#{prefcode[:code].gsub(/[:\s\/()]/,
|
|
172
|
-
'_').squeeze('_')}"
|
|
173
|
-
else
|
|
174
|
-
key.gsub(/[-:\s]/, "_")
|
|
175
|
-
end
|
|
176
|
-
"#{@dir}/#{fn.sub(/(,|_$)/, '')}"
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
#
|
|
180
|
-
# Checks if there is file with xml or txt extension and return filename with
|
|
181
|
-
# the extension.
|
|
182
|
-
#
|
|
183
|
-
# @param file [String]
|
|
184
|
-
# @return [String, NilClass]
|
|
185
|
-
def search_ext(file)
|
|
186
|
-
if File.exist?("#{file}.#{@ext}")
|
|
187
|
-
"#{file}.#{@ext}"
|
|
188
|
-
elsif File.exist? "#{file}.notfound"
|
|
189
|
-
"#{file}.notfound"
|
|
190
|
-
elsif File.exist? "#{file}.redirect"
|
|
191
|
-
"#{file}.redirect"
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Set version of the DB to the gem grammar hash.
|
|
196
|
-
# @param fdir [String] dir pathe to flover cache
|
|
197
|
-
def set_version(fdir)
|
|
198
|
-
file_version = "#{fdir}/version"
|
|
199
|
-
unless File.exist? file_version
|
|
200
|
-
file_safe_write file_version, self.class.grammar_hash(fdir)
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# Return item's file name
|
|
205
|
-
# @param key [String]
|
|
206
|
-
# @return [String]
|
|
207
|
-
def prefix(key)
|
|
208
|
-
key.downcase.match(/^[^(]+(?=\()/).to_s
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Check if a file content is redirection
|
|
212
|
-
#
|
|
213
|
-
# @prarm value [String] file content
|
|
214
|
-
# @return [String, NilClass] redirection code or nil
|
|
215
|
-
def redirect_code(value)
|
|
216
|
-
%r{redirection\s(?<code>.*)} =~ value
|
|
217
|
-
code
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# @param file [String]
|
|
221
|
-
# @content [String]
|
|
222
|
-
def file_safe_write(file, content)
|
|
223
|
-
File.open file, File::RDWR | File::CREAT, encoding: "UTF-8" do |f|
|
|
224
|
-
Timeout.timeout(10) { f.flock File::LOCK_EX }
|
|
225
|
-
f.write content
|
|
226
|
-
f.flock File::LOCK_UN
|
|
227
|
-
end
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
data/lib/relaton/registry.rb
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
require "singleton"
|
|
2
|
-
|
|
3
|
-
# class Error < StandardError
|
|
4
|
-
# end
|
|
5
|
-
|
|
6
|
-
module Relaton
|
|
7
|
-
class Registry
|
|
8
|
-
SUPPORTED_GEMS = %w[
|
|
9
|
-
relaton/gb relaton/iec relaton/ietf relaton/iso
|
|
10
|
-
relaton/itu relaton/nist relaton/ogc relaton/calconnect
|
|
11
|
-
relaton/omg relaton/un relaton/w3c relaton/ieee
|
|
12
|
-
relaton/iho relaton/bipm relaton/ecma relaton/cie
|
|
13
|
-
relaton/bsi relaton/cen relaton/iana relaton/3gpp
|
|
14
|
-
relaton/oasis relaton/doi relaton/jis relaton/xsf
|
|
15
|
-
relaton/ccsds relaton/etsi relaton/isbn relaton/plateau
|
|
16
|
-
].freeze
|
|
17
|
-
|
|
18
|
-
include Singleton
|
|
19
|
-
|
|
20
|
-
attr_reader :processors
|
|
21
|
-
|
|
22
|
-
def initialize
|
|
23
|
-
@processors = {}
|
|
24
|
-
register_gems
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def register_gems
|
|
28
|
-
# Util.info("Info: detecting backends:")
|
|
29
|
-
|
|
30
|
-
SUPPORTED_GEMS.each do |b|
|
|
31
|
-
require "#{b}/processor"
|
|
32
|
-
register Kernel.const_get "#{gem_to_module_path(b)}::Processor"
|
|
33
|
-
rescue LoadError => e
|
|
34
|
-
Util.error "backend #{b} not present\n" \
|
|
35
|
-
"#{e.message}\n#{e.backtrace.join "\n"}"
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def register(processor)
|
|
40
|
-
raise Error unless processor < Core::Processor
|
|
41
|
-
|
|
42
|
-
p = processor.new
|
|
43
|
-
return if processors[p.short]
|
|
44
|
-
|
|
45
|
-
Util.debug("processor \"#{p.short}\" registered")
|
|
46
|
-
processors[p.short] = p
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def find_processor(short)
|
|
50
|
-
processors[short.to_sym]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# @return [Array<Symbol>]
|
|
54
|
-
def supported_processors
|
|
55
|
-
processors.keys
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
# Search a rpocessos by dataset name
|
|
60
|
-
#
|
|
61
|
-
# @param [String] dataset
|
|
62
|
-
#
|
|
63
|
-
# @return [Relaton::Core::Processor, nil]
|
|
64
|
-
#
|
|
65
|
-
def find_processor_by_dataset(dataset)
|
|
66
|
-
processors.values.detect { |p| p.datasets&.include? dataset }
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
#
|
|
70
|
-
# Find processor by type
|
|
71
|
-
#
|
|
72
|
-
# @param type [String]
|
|
73
|
-
# @return [Relaton::Core::Processor]
|
|
74
|
-
def by_type(type)
|
|
75
|
-
processors.values.detect { |v| v.prefix == type&.upcase }
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def [](stdclass)
|
|
79
|
-
processors[stdclass]
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
#
|
|
83
|
-
# Find processor by reference or prefix
|
|
84
|
-
#
|
|
85
|
-
# @param [String] ref reference or prefix
|
|
86
|
-
#
|
|
87
|
-
# @return [Relaton::Core::Processor] processor
|
|
88
|
-
#
|
|
89
|
-
def processor_by_ref(ref)
|
|
90
|
-
processors[class_by_ref(ref)]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
#
|
|
94
|
-
# Find processor by refernce or prefix
|
|
95
|
-
#
|
|
96
|
-
# @param ref [String] reference or prefix
|
|
97
|
-
#
|
|
98
|
-
# @return [Symbol, nil] standard class name
|
|
99
|
-
#
|
|
100
|
-
def class_by_ref(ref)
|
|
101
|
-
ref = Regexp.last_match(1) if ref =~ /^\w+\((.*)\)$/
|
|
102
|
-
@processors.each do |class_name, processor|
|
|
103
|
-
return class_name if /^(urn:)?#{processor.prefix}\b/i.match?(ref) ||
|
|
104
|
-
processor.defaultprefix.match(ref)
|
|
105
|
-
end
|
|
106
|
-
Util.info "`#{ref}` does not have a recognised prefix", key: ref
|
|
107
|
-
nil
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
private
|
|
111
|
-
|
|
112
|
-
def gem_to_module_path(gem_name)
|
|
113
|
-
gem_name.split("/").map do |part|
|
|
114
|
-
part.capitalize.sub("3gpp", "ThreeGpp")
|
|
115
|
-
end.join("::")
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
data/lib/relaton/util.rb
DELETED
data/lib/relaton/workers_pool.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Relaton
|
|
4
|
-
# Workers poll.
|
|
5
|
-
class WorkersPool
|
|
6
|
-
def initialize(workers = 2, &)
|
|
7
|
-
# num_workers = workers < 2 ? 2 : workers
|
|
8
|
-
@queue = SizedQueue.new(workers * 2)
|
|
9
|
-
@threads = Array.new workers do
|
|
10
|
-
Thread.new do
|
|
11
|
-
while item = @queue.pop; yield(item) end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def <<(item)
|
|
17
|
-
@queue << item
|
|
18
|
-
self
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|