berkeley_library-tind 0.4.0 → 0.5.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/.github/workflows/build.yml +1 -1
- data/.idea/inspectionProfiles/Project_Default.xml +18 -0
- data/.idea/tind.iml +91 -91
- data/.ruby-version +1 -1
- data/CHANGES.md +33 -1
- data/README.md +15 -1
- data/berkeley_library-tind.gemspec +3 -2
- data/lib/berkeley_library/tind/api/api.rb +17 -11
- data/lib/berkeley_library/tind/api/collection.rb +1 -1
- data/lib/berkeley_library/tind/api/search.rb +2 -2
- data/lib/berkeley_library/tind/export/exporter.rb +1 -1
- data/lib/berkeley_library/tind/export/table.rb +1 -1
- data/lib/berkeley_library/tind/export/table_metrics.rb +1 -1
- data/lib/berkeley_library/tind/marc/xml_builder.rb +62 -0
- data/lib/berkeley_library/tind/marc/xml_reader.rb +32 -19
- data/lib/berkeley_library/tind/marc/xml_writer.rb +152 -0
- data/lib/berkeley_library/tind/module_info.rb +1 -1
- data/lib/berkeley_library/util/files.rb +39 -0
- data/lib/berkeley_library/util/ods/spreadsheet.rb +1 -1
- data/lib/berkeley_library/util/ods/xml/element_node.rb +1 -1
- data/spec/berkeley_library/tind/export/export_spec.rb +3 -1
- data/spec/berkeley_library/tind/export/table_spec.rb +2 -0
- data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +42 -1
- data/spec/berkeley_library/tind/marc/xml_writer_spec.rb +156 -0
- data/spec/data/new-records.xml +46 -0
- metadata +36 -39
- data/Jenkinsfile +0 -18
- data/lib/berkeley_library/util/arrays.rb +0 -178
- data/lib/berkeley_library/util/logging.rb +0 -1
- data/lib/berkeley_library/util/paths.rb +0 -111
- data/lib/berkeley_library/util/stringios.rb +0 -30
- data/lib/berkeley_library/util/strings.rb +0 -42
- data/lib/berkeley_library/util/sys_exits.rb +0 -15
- data/lib/berkeley_library/util/times.rb +0 -22
- data/lib/berkeley_library/util/uris/appender.rb +0 -162
- data/lib/berkeley_library/util/uris/requester.rb +0 -62
- data/lib/berkeley_library/util/uris/validator.rb +0 -32
- data/lib/berkeley_library/util/uris.rb +0 -44
- data/spec/berkeley_library/util/arrays_spec.rb +0 -340
- data/spec/berkeley_library/util/paths_spec.rb +0 -90
- data/spec/berkeley_library/util/stringios_spec.rb +0 -34
- data/spec/berkeley_library/util/strings_spec.rb +0 -27
- data/spec/berkeley_library/util/times_spec.rb +0 -39
- data/spec/berkeley_library/util/uris_spec.rb +0 -118
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: berkeley_library-tind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Moles
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: berkeley_library-logging
|
@@ -30,14 +30,34 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.3.0
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 0.3.1
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
41
|
- - "~>"
|
39
42
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
43
|
+
version: 0.3.0
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.3.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: berkeley_library-util
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.1'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.1'
|
41
61
|
- !ruby/object:Gem::Dependency
|
42
62
|
name: ice_nine
|
43
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,25 +199,19 @@ dependencies:
|
|
179
199
|
- !ruby/object:Gem::Version
|
180
200
|
version: '2.7'
|
181
201
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
202
|
+
name: equivalent-xml
|
183
203
|
requirement: !ruby/object:Gem::Requirement
|
184
204
|
requirements:
|
185
|
-
- - "
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: 3.0.5
|
188
|
-
- - "<"
|
205
|
+
- - "~>"
|
189
206
|
- !ruby/object:Gem::Version
|
190
|
-
version: '
|
207
|
+
version: '0.6'
|
191
208
|
type: :development
|
192
209
|
prerelease: false
|
193
210
|
version_requirements: !ruby/object:Gem::Requirement
|
194
211
|
requirements:
|
195
|
-
- - "
|
196
|
-
- !ruby/object:Gem::Version
|
197
|
-
version: 3.0.5
|
198
|
-
- - "<"
|
212
|
+
- - "~>"
|
199
213
|
- !ruby/object:Gem::Version
|
200
|
-
version: '
|
214
|
+
version: '0.6'
|
201
215
|
- !ruby/object:Gem::Dependency
|
202
216
|
name: rake
|
203
217
|
requirement: !ruby/object:Gem::Requirement
|
@@ -360,7 +374,6 @@ files:
|
|
360
374
|
- CHANGES.md
|
361
375
|
- Dockerfile
|
362
376
|
- Gemfile
|
363
|
-
- Jenkinsfile
|
364
377
|
- LICENSE.md
|
365
378
|
- README.md
|
366
379
|
- Rakefile
|
@@ -396,10 +409,11 @@ files:
|
|
396
409
|
- lib/berkeley_library/tind/export/table.rb
|
397
410
|
- lib/berkeley_library/tind/export/table_metrics.rb
|
398
411
|
- lib/berkeley_library/tind/marc.rb
|
412
|
+
- lib/berkeley_library/tind/marc/xml_builder.rb
|
399
413
|
- lib/berkeley_library/tind/marc/xml_reader.rb
|
414
|
+
- lib/berkeley_library/tind/marc/xml_writer.rb
|
400
415
|
- lib/berkeley_library/tind/module_info.rb
|
401
|
-
- lib/berkeley_library/util/
|
402
|
-
- lib/berkeley_library/util/logging.rb
|
416
|
+
- lib/berkeley_library/util/files.rb
|
403
417
|
- lib/berkeley_library/util/ods/spreadsheet.rb
|
404
418
|
- lib/berkeley_library/util/ods/xml/content_doc.rb
|
405
419
|
- lib/berkeley_library/util/ods/xml/document_node.rb
|
@@ -439,15 +453,6 @@ files:
|
|
439
453
|
- lib/berkeley_library/util/ods/xml/table/table_column.rb
|
440
454
|
- lib/berkeley_library/util/ods/xml/table/table_row.rb
|
441
455
|
- lib/berkeley_library/util/ods/xml/text/p.rb
|
442
|
-
- lib/berkeley_library/util/paths.rb
|
443
|
-
- lib/berkeley_library/util/stringios.rb
|
444
|
-
- lib/berkeley_library/util/strings.rb
|
445
|
-
- lib/berkeley_library/util/sys_exits.rb
|
446
|
-
- lib/berkeley_library/util/times.rb
|
447
|
-
- lib/berkeley_library/util/uris.rb
|
448
|
-
- lib/berkeley_library/util/uris/appender.rb
|
449
|
-
- lib/berkeley_library/util/uris/requester.rb
|
450
|
-
- lib/berkeley_library/util/uris/validator.rb
|
451
456
|
- rakelib/bundle.rake
|
452
457
|
- rakelib/coverage.rake
|
453
458
|
- rakelib/gem.rake
|
@@ -472,7 +477,7 @@ files:
|
|
472
477
|
- spec/berkeley_library/tind/export/row_spec.rb
|
473
478
|
- spec/berkeley_library/tind/export/table_spec.rb
|
474
479
|
- spec/berkeley_library/tind/marc/xml_reader_spec.rb
|
475
|
-
- spec/berkeley_library/
|
480
|
+
- spec/berkeley_library/tind/marc/xml_writer_spec.rb
|
476
481
|
- spec/berkeley_library/util/ods/spreadsheet_spec.rb
|
477
482
|
- spec/berkeley_library/util/ods/xml/content_doc_spec.rb
|
478
483
|
- spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb
|
@@ -482,14 +487,10 @@ files:
|
|
482
487
|
- spec/berkeley_library/util/ods/xml/style/family_spec.rb
|
483
488
|
- spec/berkeley_library/util/ods/xml/table/table_row_spec.rb
|
484
489
|
- spec/berkeley_library/util/ods/xml/table/table_spec.rb
|
485
|
-
- spec/berkeley_library/util/paths_spec.rb
|
486
|
-
- spec/berkeley_library/util/stringios_spec.rb
|
487
|
-
- spec/berkeley_library/util/strings_spec.rb
|
488
|
-
- spec/berkeley_library/util/times_spec.rb
|
489
|
-
- spec/berkeley_library/util/uris_spec.rb
|
490
490
|
- spec/data/collection-names.txt
|
491
491
|
- spec/data/collections.json
|
492
492
|
- spec/data/disjoint-records.xml
|
493
|
+
- spec/data/new-records.xml
|
493
494
|
- spec/data/record-184453.xml
|
494
495
|
- spec/data/record-184458.xml
|
495
496
|
- spec/data/record-187888.xml
|
@@ -549,7 +550,7 @@ test_files:
|
|
549
550
|
- spec/berkeley_library/tind/export/row_spec.rb
|
550
551
|
- spec/berkeley_library/tind/export/table_spec.rb
|
551
552
|
- spec/berkeley_library/tind/marc/xml_reader_spec.rb
|
552
|
-
- spec/berkeley_library/
|
553
|
+
- spec/berkeley_library/tind/marc/xml_writer_spec.rb
|
553
554
|
- spec/berkeley_library/util/ods/spreadsheet_spec.rb
|
554
555
|
- spec/berkeley_library/util/ods/xml/content_doc_spec.rb
|
555
556
|
- spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb
|
@@ -559,14 +560,10 @@ test_files:
|
|
559
560
|
- spec/berkeley_library/util/ods/xml/style/family_spec.rb
|
560
561
|
- spec/berkeley_library/util/ods/xml/table/table_row_spec.rb
|
561
562
|
- spec/berkeley_library/util/ods/xml/table/table_spec.rb
|
562
|
-
- spec/berkeley_library/util/paths_spec.rb
|
563
|
-
- spec/berkeley_library/util/stringios_spec.rb
|
564
|
-
- spec/berkeley_library/util/strings_spec.rb
|
565
|
-
- spec/berkeley_library/util/times_spec.rb
|
566
|
-
- spec/berkeley_library/util/uris_spec.rb
|
567
563
|
- spec/data/collection-names.txt
|
568
564
|
- spec/data/collections.json
|
569
565
|
- spec/data/disjoint-records.xml
|
566
|
+
- spec/data/new-records.xml
|
570
567
|
- spec/data/record-184453.xml
|
571
568
|
- spec/data/record-184458.xml
|
572
569
|
- spec/data/record-187888.xml
|
data/Jenkinsfile
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env groovy
|
2
|
-
|
3
|
-
dockerComposePipeline(
|
4
|
-
commands: [
|
5
|
-
'bundle exec rake coverage',
|
6
|
-
'bundle exec rake rubocop',
|
7
|
-
'bundle exec rake bundle:audit',
|
8
|
-
'bundle exec rake gem'
|
9
|
-
],
|
10
|
-
artifacts: [
|
11
|
-
junit: 'artifacts/rspec/**/*.xml',
|
12
|
-
html : [
|
13
|
-
'Code Coverage': 'artifacts/rcov',
|
14
|
-
'RuboCop' : 'artifacts/rubocop'
|
15
|
-
],
|
16
|
-
raw : ['artifacts/**/*.gem']
|
17
|
-
]
|
18
|
-
)
|
@@ -1,178 +0,0 @@
|
|
1
|
-
module BerkeleyLibrary
|
2
|
-
module Util
|
3
|
-
module Arrays
|
4
|
-
class << self
|
5
|
-
# Clients can chose to call class methods directly, or include the module
|
6
|
-
include Arrays
|
7
|
-
end
|
8
|
-
|
9
|
-
# Recursively checks whether the specified list contains, in the
|
10
|
-
# same order, all values in the other specified list (additional codes
|
11
|
-
# in between are fine)
|
12
|
-
#
|
13
|
-
# @param subset [Array] the values to look for
|
14
|
-
# @param superset [Array] the list of values to look in
|
15
|
-
# @return boolean True if all values were found, false otherwise
|
16
|
-
def ordered_superset?(superset:, subset:)
|
17
|
-
!find_indices(in_array: superset, for_array: subset).nil?
|
18
|
-
end
|
19
|
-
|
20
|
-
# Counts how many contiguous elements from the start of an
|
21
|
-
# sequence of values satisfy the given block.
|
22
|
-
#
|
23
|
-
# @overload count_while(arr:)
|
24
|
-
# Returns an enumerator.
|
25
|
-
# @param values [Enumerable] the values
|
26
|
-
# @return [Enumerator] the enumerator.
|
27
|
-
# @overload count_while(arr:, &block)
|
28
|
-
# Passes elements to the block until the block returns nil or false,
|
29
|
-
# then stops iterating and returns the count of matching elements.
|
30
|
-
# @param values [Enumerable] the values
|
31
|
-
# @return [Integer] the count
|
32
|
-
def count_while(values:)
|
33
|
-
return to_enum(:count_while, values: values) unless block_given?
|
34
|
-
|
35
|
-
values.inject(0) do |count, x|
|
36
|
-
matched = yield x
|
37
|
-
break count unless matched
|
38
|
-
|
39
|
-
count + 1
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Given two lists, one of which is a superset of the other, with elements
|
44
|
-
# in the same order (but possibly with additional elements in the superset),
|
45
|
-
# returns an array the length of the subset, containing for each element in
|
46
|
-
# the subset the index of the corresponding element in the superset.
|
47
|
-
#
|
48
|
-
# @overload find_matching_indices(for_array:, in_array:)
|
49
|
-
# For each value in `for_array`, finds the index of the first equal value
|
50
|
-
# in `in_array` after the previously matched value.
|
51
|
-
# @param in_array [Array] the list of values to look in
|
52
|
-
# @param for_array [Array] the values to look for
|
53
|
-
# @return [Array<Integer>, nil] the indices in `in_array` of each value in `for_array`,
|
54
|
-
# or `nil` if not all values could be found
|
55
|
-
#
|
56
|
-
# @overload find_matching_indices(for_array:, in_array:)
|
57
|
-
# For each value in `for_array`, finds the index of the first value
|
58
|
-
# in `in_array` after the previously matched value that matches
|
59
|
-
# the specified match function.
|
60
|
-
# @param in_array [Array] the list of values to look in
|
61
|
-
# @param for_array [Array] the values to look for
|
62
|
-
# @yieldparam source [Object] the value to compare
|
63
|
-
# @yieldparam target [Object] the value to compare against
|
64
|
-
# @return [Array<Integer>, nil] the indices in `in_array` of each value in `for_array`,
|
65
|
-
# or `nil` if not all values could be found
|
66
|
-
def find_indices(for_array:, in_array:, &block)
|
67
|
-
return find_indices_matching(for_array, in_array, &block) if block_given?
|
68
|
-
|
69
|
-
find_all_indices(for_array, in_array)
|
70
|
-
end
|
71
|
-
|
72
|
-
# Given a block or a value, finds the index of the first matching value
|
73
|
-
# at or after the specified start index.
|
74
|
-
#
|
75
|
-
# @overload find_index(value, in_array:, start_index:)
|
76
|
-
# Finds the first index of the specified value.
|
77
|
-
# @param value [Object] the value to find
|
78
|
-
# @param in_array [Array] the array to search
|
79
|
-
# @param start_index [Integer] the index to start with
|
80
|
-
# @return [Integer, nil] the index, or `nil` if no value matches
|
81
|
-
# @overload find_index(&block)
|
82
|
-
# Finds the index of the first value matching
|
83
|
-
# the specified block.
|
84
|
-
# @param in_array [Array] the array to search
|
85
|
-
# @param start_index [Integer] the index to start with
|
86
|
-
# @yieldreturn [Boolean] whether the element matches
|
87
|
-
# @return [Integer, nil] the index, or `nil` if no value matches
|
88
|
-
# @overload find_index
|
89
|
-
# @param in_array [Array] the array to search
|
90
|
-
# @param start_index [Integer] the index to start with
|
91
|
-
# @return [Enumerator] a new enumerator
|
92
|
-
def find_index(*args, in_array:, start_index: 0, &block)
|
93
|
-
raise ArgumentError, "wrong number of arguments (given #{value.length}, expected 0..1" if args.size > 1
|
94
|
-
return Enumerator.new { |y| find_index(in_array: in_array, start_index: start_index, &y) } if args.empty? && !block_given?
|
95
|
-
return unless (relative_index = in_array[start_index..].find_index(*args, &block))
|
96
|
-
|
97
|
-
relative_index + start_index
|
98
|
-
end
|
99
|
-
|
100
|
-
# Given an array of unique integers _a<sub>1</sub>_, returns a new array
|
101
|
-
# _a<sub>2</sub>_ in which the value at each index _i<sub>2</sub>_ is the
|
102
|
-
# index _i<sub>1</sub>_ at which that value was found in _a<sub>1</sub>_.
|
103
|
-
# E.g., given `[0, 2, 3]`, returns `[0, nil, 1, 2]`. The indices need
|
104
|
-
# not be in order but must be unique.
|
105
|
-
#
|
106
|
-
# @param arr [Array<Integer>, nil] the array to invert.
|
107
|
-
# @return [Array<Integer, nil>, nil] the inverted array, or nil if the input array is nil
|
108
|
-
# @raise TypeError if `arr` is not an array of integers
|
109
|
-
# @raise ArgumentError if `arr` contains duplicate values
|
110
|
-
def invert(arr)
|
111
|
-
return unless arr
|
112
|
-
|
113
|
-
# noinspection RubyNilAnalysis
|
114
|
-
Array.new(arr.size).tap do |inv|
|
115
|
-
arr.each_with_index do |v, i|
|
116
|
-
next inv[v] = i unless (prev_index = inv[v])
|
117
|
-
|
118
|
-
raise ArgumentError, "Duplicate value #{v} at index #{i} already found at #{prev_index}"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
# Merges two arrays in an order-preserving manner.
|
124
|
-
# @param a1 [Array] the first array
|
125
|
-
# @param a2 [Array] the second array
|
126
|
-
# @return [Array] a merged array that is an ordered superset of both `a1` and `a2`
|
127
|
-
# @see Arrays#ordered_superset?
|
128
|
-
def merge(a1, a2)
|
129
|
-
return a1 if a2.empty?
|
130
|
-
return a2 if a1.empty?
|
131
|
-
|
132
|
-
shorter, longer = a1.size > a2.size ? [a2, a1] : [a1, a2]
|
133
|
-
do_merge(shorter, longer)
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
def do_merge(shorter, longer)
|
139
|
-
shorter.each_with_index do |v, ix_s|
|
140
|
-
next unless (ix_l = longer.find_index(v))
|
141
|
-
|
142
|
-
shorter_unmatched = shorter[0...ix_s]
|
143
|
-
longer_unmatched = longer[0...ix_l]
|
144
|
-
all_unmatched = sort_by_first_and_flatten(shorter_unmatched, longer_unmatched)
|
145
|
-
return (all_unmatched << v) + merge(shorter[ix_s + 1..], longer[ix_l + 1..])
|
146
|
-
end
|
147
|
-
|
148
|
-
sort_by_first_and_flatten(longer, shorter)
|
149
|
-
end
|
150
|
-
|
151
|
-
def sort_by_first_and_flatten(a1, a2)
|
152
|
-
return a1 if a2.empty?
|
153
|
-
return a2 if a1.empty?
|
154
|
-
return a2 + a1 if a1.first.respond_to?(:>) && a1.first > a2.first
|
155
|
-
|
156
|
-
a1 + a2
|
157
|
-
end
|
158
|
-
|
159
|
-
def find_all_indices(source, target)
|
160
|
-
source.each_with_object([]) do |src, target_indices|
|
161
|
-
target_offset = (target_indices.last&.+ 1) || 0
|
162
|
-
return nil unless (target_index = find_index(src, in_array: target, start_index: target_offset))
|
163
|
-
|
164
|
-
target_indices << target_index
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def find_indices_matching(source, target)
|
169
|
-
source.each_with_object([]) do |src, target_indices|
|
170
|
-
target_offset = (target_indices.last&.+ 1) || 0
|
171
|
-
return nil unless (target_index = find_index(in_array: target, start_index: target_offset) { |tgt| yield src, tgt })
|
172
|
-
|
173
|
-
target_indices << target_index
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
require 'berkeley_library/logging'
|
@@ -1,111 +0,0 @@
|
|
1
|
-
require 'berkeley_library/util/stringios'
|
2
|
-
|
3
|
-
module BerkeleyLibrary
|
4
|
-
module Util
|
5
|
-
# This module, modeled on the {https://golang.org/pkg/path/ Go `path` package},
|
6
|
-
# provides utility routines for modifying paths separated by forward slashes,
|
7
|
-
# such as URL paths. For system-dependent file paths, use
|
8
|
-
# {https://ruby-doc.org/stdlib-2.7.0/libdoc/pathname/rdoc/Pathname.html `Pathname`}
|
9
|
-
# instead.
|
10
|
-
module Paths
|
11
|
-
include BerkeleyLibrary::Util::StringIOs
|
12
|
-
|
13
|
-
class << self
|
14
|
-
include Paths
|
15
|
-
end
|
16
|
-
|
17
|
-
# Returns the shortest path name equivalent to `path` by purely lexical
|
18
|
-
# processing by:
|
19
|
-
#
|
20
|
-
# 1. replacing runs of multiple `/` with a single `/`
|
21
|
-
# 2. eliminating all `.` (current directory) elements
|
22
|
-
# 3. eliminating all `<child>/..` in favor of directly
|
23
|
-
# referencing the parent directory
|
24
|
-
# 4. replaing all `/..` at the beginning of the path
|
25
|
-
# with a single leading `/`
|
26
|
-
#
|
27
|
-
# The returned path ends in a slash only if it is the root `/`.
|
28
|
-
# @see https://9p.io/sys/doc/lexnames.html Rob Pike, "Lexical File Names in Plan 9 or Getting Dot-Dot Right"
|
29
|
-
#
|
30
|
-
# @param path [String, nil] the path to clean
|
31
|
-
# @return [String, nil] the cleaned path, or `nil` for a nil path.
|
32
|
-
def clean(path)
|
33
|
-
return unless path
|
34
|
-
return '.' if ['', '.'].include?(path)
|
35
|
-
|
36
|
-
StringIO.new.tap do |out|
|
37
|
-
out << '/' if path[0] == '/'
|
38
|
-
dotdot = (r = out.size)
|
39
|
-
r, dotdot = process_next(r, dotdot, path, out) while r < path.size
|
40
|
-
out << '.' if out.pos == 0
|
41
|
-
end.string
|
42
|
-
end
|
43
|
-
|
44
|
-
# Joins any number of path elements into a single path, separating
|
45
|
-
# them with slashes, ignoring empty elements and passing the result
|
46
|
-
# to {Paths#clean}.
|
47
|
-
#
|
48
|
-
# @param elements [Array<String>] the elements to join
|
49
|
-
# @return [String] the joined path
|
50
|
-
def join(*elements)
|
51
|
-
elements = elements.reject { |e| [nil, ''].include?(e) }
|
52
|
-
joined_raw = elements.join('/')
|
53
|
-
return '' if joined_raw == ''
|
54
|
-
|
55
|
-
clean(joined_raw)
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def process_next(r, dotdot, path, out)
|
61
|
-
# empty path element, or .
|
62
|
-
return r + 1, dotdot if empty_or_dot?(r, path)
|
63
|
-
# .. element: remove to last /
|
64
|
-
return handle_dotdot(r, dotdot, path, out) if dotdot?(r, path)
|
65
|
-
|
66
|
-
# real path element
|
67
|
-
[append_from(r, path, out), dotdot]
|
68
|
-
end
|
69
|
-
|
70
|
-
def handle_dotdot(r, dotdot, path, out)
|
71
|
-
if out.pos > dotdot
|
72
|
-
backtrack_to_dotdot(out, dotdot)
|
73
|
-
elsif path[0] != '/'
|
74
|
-
dotdot = append_dotdot(out)
|
75
|
-
end
|
76
|
-
|
77
|
-
[r + 2, dotdot]
|
78
|
-
end
|
79
|
-
|
80
|
-
def dotdot?(r, path)
|
81
|
-
path[r] == '.' && (r + 2 == path.size || path[r + 2] == '/')
|
82
|
-
end
|
83
|
-
|
84
|
-
def empty_or_dot?(r, path)
|
85
|
-
path[r] == '/' || (path[r] == '.' && (r + 1 == path.size || path[r + 1] == '/'))
|
86
|
-
end
|
87
|
-
|
88
|
-
def append_from(r, path, out)
|
89
|
-
out << '/' if (path[0] == '/' && out.pos != 1) || (path[0] != '/' && out.pos != 0)
|
90
|
-
while r < path.size && path[r] != '/'
|
91
|
-
out << path[r]
|
92
|
-
r += 1
|
93
|
-
end
|
94
|
-
r
|
95
|
-
end
|
96
|
-
|
97
|
-
def append_dotdot(out)
|
98
|
-
out << '/' if out.pos > 1
|
99
|
-
out << '..'
|
100
|
-
out.pos
|
101
|
-
end
|
102
|
-
|
103
|
-
def backtrack_to_dotdot(out, dotdot)
|
104
|
-
out.seek(-1, IO::SEEK_CUR)
|
105
|
-
out.seek(-1, IO::SEEK_CUR) while out.pos > dotdot && getbyte(out, out.pos) != 47 # '/' is ASCII 37
|
106
|
-
out.truncate(out.pos)
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
|
3
|
-
module BerkeleyLibrary
|
4
|
-
module Util
|
5
|
-
module StringIOs
|
6
|
-
class << self
|
7
|
-
include StringIOs
|
8
|
-
end
|
9
|
-
|
10
|
-
# Returns the byte (**not** character) at the specified byte index
|
11
|
-
# in the specified `StringIO`.
|
12
|
-
#
|
13
|
-
# @param s [StringIO] the StringIO to search in
|
14
|
-
# @param i [Integer] the byte index
|
15
|
-
# @return [Integer, nil] the byte, or nil if the byte index is invalid.
|
16
|
-
def getbyte(s, i)
|
17
|
-
return if i >= s.size
|
18
|
-
return if s.size + i < 0
|
19
|
-
|
20
|
-
pos_orig = s.pos
|
21
|
-
begin
|
22
|
-
s.seek(i >= 0 ? i : s.size + i)
|
23
|
-
s.getbyte
|
24
|
-
ensure
|
25
|
-
s.seek(pos_orig)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module BerkeleyLibrary
|
2
|
-
module Util
|
3
|
-
module Strings
|
4
|
-
|
5
|
-
ASCII_0 = '0'.ord
|
6
|
-
ASCII_9 = '9'.ord
|
7
|
-
|
8
|
-
def ascii_numeric?(s)
|
9
|
-
s.chars.all? do |c|
|
10
|
-
ord = c.ord
|
11
|
-
ord >= ASCII_0 && ord <= ASCII_9
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
# Locates the point at which two strings differ
|
16
|
-
#
|
17
|
-
# @return [Integer, nil] the index of the first character in either string
|
18
|
-
# that differs from the other, or `nil` if the strings are identical,
|
19
|
-
# or are not strings
|
20
|
-
def diff_index(s1, s2)
|
21
|
-
return unless string_like?(s1, s2)
|
22
|
-
|
23
|
-
shorter, longer = s1.size > s2.size ? [s2, s1] : [s1, s2]
|
24
|
-
shorter.chars.each_with_index do |c, i|
|
25
|
-
return i if c != longer[i]
|
26
|
-
end
|
27
|
-
shorter.length if shorter.length < longer.length # otherwise they're equal
|
28
|
-
end
|
29
|
-
|
30
|
-
class << self
|
31
|
-
include Strings
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def string_like?(*strs)
|
37
|
-
strs.all? { |s| s.respond_to?(:chars) && s.respond_to?(:size) }
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module BerkeleyLibrary
|
2
|
-
module Util
|
3
|
-
# cf. BSD sysexits.h https://cgit.freebsd.org/src/tree/include/sysexits.h?h=releng/2.0
|
4
|
-
module SysExits
|
5
|
-
# successful termination
|
6
|
-
EX_OK = 0
|
7
|
-
|
8
|
-
# command line usage error
|
9
|
-
EX_USAGE = 64
|
10
|
-
|
11
|
-
# internal software error
|
12
|
-
EX_SOFTWARE = 70 # command line usage error
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'time'
|
2
|
-
|
3
|
-
module BerkeleyLibrary
|
4
|
-
module Util
|
5
|
-
module Times
|
6
|
-
class << self
|
7
|
-
include Times
|
8
|
-
end
|
9
|
-
|
10
|
-
# @param time [Time, Date] the time
|
11
|
-
# @return the UTC time corresponding to `time`
|
12
|
-
def ensure_utc(time)
|
13
|
-
return unless time
|
14
|
-
return time if time.respond_to?(:utc?) && time.utc?
|
15
|
-
return time.getutc if time.respond_to?(:getutc)
|
16
|
-
return time.to_time.getutc if time.respond_to?(:to_time)
|
17
|
-
|
18
|
-
raise ArgumentError, "Not a date or time: #{time.inspect}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|