berkeley_library-util 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +18 -0
- data/.gitignore +240 -0
- data/.idea/.gitignore +8 -0
- data/.idea/inspectionProfiles/Project_Default.xml +21 -0
- data/.idea/misc.xml +6 -0
- data/.idea/modules.xml +8 -0
- data/.idea/util.iml +123 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +334 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/.yardopts +1 -0
- data/CHANGES.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE.md +21 -0
- data/README.md +21 -0
- data/Rakefile +20 -0
- data/berkeley_library-util.gemspec +42 -0
- data/lib/berkeley_library/util/arrays.rb +178 -0
- data/lib/berkeley_library/util/module_info.rb +16 -0
- data/lib/berkeley_library/util/paths.rb +111 -0
- data/lib/berkeley_library/util/stringios.rb +30 -0
- data/lib/berkeley_library/util/strings.rb +42 -0
- data/lib/berkeley_library/util/sys_exits.rb +15 -0
- data/lib/berkeley_library/util/times.rb +22 -0
- data/lib/berkeley_library/util/uris/appender.rb +162 -0
- data/lib/berkeley_library/util/uris/requester.rb +62 -0
- data/lib/berkeley_library/util/uris/validator.rb +32 -0
- data/lib/berkeley_library/util/uris.rb +44 -0
- data/lib/berkeley_library/util.rb +1 -0
- data/rakelib/bundle.rake +8 -0
- data/rakelib/coverage.rake +11 -0
- data/rakelib/gem.rake +54 -0
- data/rakelib/rubocop.rake +18 -0
- data/rakelib/spec.rake +2 -0
- data/spec/.rubocop.yml +40 -0
- data/spec/berkeley_library/util/arrays_spec.rb +340 -0
- data/spec/berkeley_library/util/paths_spec.rb +90 -0
- data/spec/berkeley_library/util/stringios_spec.rb +34 -0
- data/spec/berkeley_library/util/strings_spec.rb +59 -0
- data/spec/berkeley_library/util/times_spec.rb +39 -0
- data/spec/berkeley_library/util/uris/requester_spec.rb +75 -0
- data/spec/berkeley_library/util/uris/validator_spec.rb +51 -0
- data/spec/berkeley_library/util/uris_spec.rb +133 -0
- data/spec/spec_helper.rb +30 -0
- metadata +321 -0
data/.rubocop.yml
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
inherit_mode:
|
2
|
+
merge:
|
3
|
+
- Exclude
|
4
|
+
- Include
|
5
|
+
|
6
|
+
# Allow one line around block body (Layout/EmptyLines will still disallow two or more)
|
7
|
+
Layout/EmptyLinesAroundBlockBody:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
# Allow one line around class body (Layout/EmptyLines will still disallow two or more)
|
11
|
+
Layout/EmptyLinesAroundClassBody:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
# Allow one line around module body (Layout/EmptyLines will still disallow two or more)
|
15
|
+
Layout/EmptyLinesAroundModuleBody:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
# Reasonable line-length check; it's too easy for the cure to be worse than the disease
|
19
|
+
Layout/LineLength:
|
20
|
+
Max: 150
|
21
|
+
|
22
|
+
# Make indents consistent regardless of the lengths of variables and method names and whatnot
|
23
|
+
Layout/MultilineMethodCallIndentation:
|
24
|
+
EnforcedStyle: indented
|
25
|
+
|
26
|
+
# Produces monsters
|
27
|
+
Layout/MultilineOperationIndentation:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
# We meant to do that
|
31
|
+
Naming/MemoizedInstanceVariableName:
|
32
|
+
Enabled: False
|
33
|
+
|
34
|
+
# It works in context, trust us
|
35
|
+
Naming/MethodParameterName:
|
36
|
+
Enabled: False
|
37
|
+
|
38
|
+
# Confusing and weird
|
39
|
+
Naming/VariableNumber:
|
40
|
+
Enabled: False
|
41
|
+
|
42
|
+
# Do what's readable in the context you're in
|
43
|
+
Style/AccessModifierDeclarations:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
# 👎 to cultural imperialism
|
47
|
+
Style/AsciiComments:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
# Seriously?
|
51
|
+
Style/CommentedKeyword:
|
52
|
+
Enabled: False
|
53
|
+
|
54
|
+
# Disable problematic module documentation check (see https://github.com/bbatsov/rubocop/issues/947)
|
55
|
+
Style/Documentation:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
# Adding more line noise to format strings will not improve them
|
59
|
+
Style/FormatStringToken:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
# Putting '# frozen_string_literal: true' everywhere does not make the world a better place
|
63
|
+
Style/FrozenStringLiteralComment:
|
64
|
+
Enabled: false
|
65
|
+
|
66
|
+
# Requiring the lambda() method just makes wordy calls wordier
|
67
|
+
Style/Lambda:
|
68
|
+
EnforcedStyle: literal
|
69
|
+
|
70
|
+
# `foo.positive?` is cute, but it's not actually more readable than `foo > 0`
|
71
|
+
Style/NumericPredicate:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
# Unclear why it's a good idea to give parameters semantically meaningless names
|
75
|
+
Style/SingleLineBlockParams:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
# The semantics of `foo&.bar` are a lot less interchangeable with `foo && foo.bar` than RuboCop thinks
|
79
|
+
Style/SafeNavigation:
|
80
|
+
Enabled: false
|
81
|
+
|
82
|
+
# Short arrays are often more readable as plain arrays
|
83
|
+
Style/WordArray:
|
84
|
+
MinSize: 3
|
85
|
+
|
86
|
+
############################################################
|
87
|
+
# Added in RuboCop 0.80
|
88
|
+
|
89
|
+
Style/HashEachMethods:
|
90
|
+
Enabled: true
|
91
|
+
|
92
|
+
Style/HashTransformKeys:
|
93
|
+
Enabled: true
|
94
|
+
|
95
|
+
Style/HashTransformValues:
|
96
|
+
Enabled: true
|
97
|
+
|
98
|
+
############################################################
|
99
|
+
# Added in RuboCop 0.81
|
100
|
+
|
101
|
+
Lint/StructNewOverride:
|
102
|
+
Enabled: true
|
103
|
+
|
104
|
+
Lint/RaiseException:
|
105
|
+
Enabled: true
|
106
|
+
|
107
|
+
############################################################
|
108
|
+
# Added in RuboCop 0.82
|
109
|
+
|
110
|
+
Layout/SpaceAroundMethodCallOperator:
|
111
|
+
Enabled: true
|
112
|
+
|
113
|
+
Style/ExponentialNotation:
|
114
|
+
Enabled: false
|
115
|
+
|
116
|
+
############################################################
|
117
|
+
# Added in RuboCop 0.83
|
118
|
+
|
119
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
120
|
+
Enabled: true
|
121
|
+
|
122
|
+
Style/SlicingWithRange:
|
123
|
+
Enabled: true
|
124
|
+
|
125
|
+
############################################################
|
126
|
+
# Added in RuboCop 0.84
|
127
|
+
|
128
|
+
Lint/DeprecatedOpenSSLConstant:
|
129
|
+
Enabled: true
|
130
|
+
|
131
|
+
############################################################
|
132
|
+
# Added in RuboCop 0.85
|
133
|
+
|
134
|
+
Lint/MixedRegexpCaptureTypes:
|
135
|
+
Enabled: true
|
136
|
+
|
137
|
+
Style/RedundantRegexpEscape:
|
138
|
+
Enabled: true
|
139
|
+
|
140
|
+
Style/RedundantRegexpCharacterClass:
|
141
|
+
Enabled: true
|
142
|
+
|
143
|
+
############################################################
|
144
|
+
# Added in Rubocop 0.86
|
145
|
+
|
146
|
+
Style/RedundantFetchBlock:
|
147
|
+
Enabled: true
|
148
|
+
|
149
|
+
############################################################
|
150
|
+
# Added in Rubocop 0.87
|
151
|
+
|
152
|
+
# Sometimes we separate things for a reason
|
153
|
+
Style/AccessorGrouping:
|
154
|
+
Enabled: false
|
155
|
+
|
156
|
+
Style/BisectedAttrAccessor:
|
157
|
+
Enabled: true
|
158
|
+
|
159
|
+
Style/RedundantAssignment:
|
160
|
+
Enabled: true
|
161
|
+
|
162
|
+
############################################################
|
163
|
+
# Added in Rubocop 0.88
|
164
|
+
|
165
|
+
Lint/DuplicateElsifCondition:
|
166
|
+
Enabled: true
|
167
|
+
|
168
|
+
Style/ArrayCoercion:
|
169
|
+
Enabled: true
|
170
|
+
|
171
|
+
Style/CaseLikeIf:
|
172
|
+
Enabled: true
|
173
|
+
|
174
|
+
Style/HashAsLastArrayItem:
|
175
|
+
Enabled: true
|
176
|
+
|
177
|
+
Style/HashLikeCase:
|
178
|
+
Enabled: true
|
179
|
+
|
180
|
+
Style/RedundantFileExtensionInRequire:
|
181
|
+
Enabled: true
|
182
|
+
|
183
|
+
############################################################
|
184
|
+
# Added in Rubocop 0.89
|
185
|
+
|
186
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
187
|
+
Enabled: true
|
188
|
+
|
189
|
+
Lint/DuplicateRescueException:
|
190
|
+
Enabled: true
|
191
|
+
|
192
|
+
Lint/EmptyConditionalBody:
|
193
|
+
Enabled: true
|
194
|
+
|
195
|
+
Lint/FloatComparison:
|
196
|
+
Enabled: true
|
197
|
+
|
198
|
+
Lint/MissingSuper:
|
199
|
+
Enabled: true
|
200
|
+
|
201
|
+
Lint/OutOfRangeRegexpRef:
|
202
|
+
Enabled: true
|
203
|
+
|
204
|
+
Lint/SelfAssignment:
|
205
|
+
Enabled: true
|
206
|
+
|
207
|
+
Lint/TopLevelReturnWithArgument:
|
208
|
+
Enabled: true
|
209
|
+
|
210
|
+
Lint/UnreachableLoop:
|
211
|
+
Enabled: true
|
212
|
+
|
213
|
+
Style/ExplicitBlockArgument:
|
214
|
+
Enabled: true
|
215
|
+
|
216
|
+
Style/GlobalStdStream:
|
217
|
+
Enabled: true
|
218
|
+
|
219
|
+
Style/OptionalBooleanParameter:
|
220
|
+
Enabled: true
|
221
|
+
|
222
|
+
Style/SingleArgumentDig:
|
223
|
+
Enabled: true
|
224
|
+
|
225
|
+
Style/SoleNestedConditional:
|
226
|
+
Enabled: true
|
227
|
+
|
228
|
+
Style/StringConcatenation:
|
229
|
+
Enabled: true
|
230
|
+
|
231
|
+
############################################################
|
232
|
+
# Added in Rubocop 0.90
|
233
|
+
|
234
|
+
Lint/DuplicateRequire:
|
235
|
+
Enabled: true
|
236
|
+
|
237
|
+
Lint/EmptyFile:
|
238
|
+
Enabled: true
|
239
|
+
|
240
|
+
Lint/TrailingCommaInAttributeDeclaration:
|
241
|
+
Enabled: true
|
242
|
+
|
243
|
+
Lint/UselessMethodDefinition:
|
244
|
+
Enabled: true
|
245
|
+
|
246
|
+
Style/CombinableLoops:
|
247
|
+
Enabled: true
|
248
|
+
|
249
|
+
Style/KeywordParametersOrder:
|
250
|
+
Enabled: true
|
251
|
+
|
252
|
+
Style/RedundantSelfAssignment:
|
253
|
+
Enabled: true
|
254
|
+
|
255
|
+
############################################################
|
256
|
+
# Added in Rubocop 0.91
|
257
|
+
|
258
|
+
Layout/BeginEndAlignment:
|
259
|
+
Enabled: true
|
260
|
+
|
261
|
+
Lint/ConstantDefinitionInBlock:
|
262
|
+
Enabled: true
|
263
|
+
|
264
|
+
Lint/IdentityComparison:
|
265
|
+
Enabled: true
|
266
|
+
|
267
|
+
Lint/UselessTimes:
|
268
|
+
Enabled: true
|
269
|
+
|
270
|
+
############################################################
|
271
|
+
# Added in Rubocop 1.1–1.9
|
272
|
+
|
273
|
+
Layout/SpaceBeforeBrackets: # (new in 1.7)
|
274
|
+
Enabled: true
|
275
|
+
Lint/AmbiguousAssignment: # (new in 1.7)
|
276
|
+
Enabled: true
|
277
|
+
Lint/DeprecatedConstants: # (new in 1.8)
|
278
|
+
Enabled: true
|
279
|
+
Lint/DuplicateBranch: # (new in 1.3)
|
280
|
+
Enabled: true
|
281
|
+
Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
|
282
|
+
Enabled: true
|
283
|
+
Lint/EmptyBlock: # (new in 1.1)
|
284
|
+
Enabled: true
|
285
|
+
Lint/EmptyClass: # (new in 1.3)
|
286
|
+
Enabled: true
|
287
|
+
Lint/LambdaWithoutLiteralBlock: # (new in 1.8)
|
288
|
+
Enabled: true
|
289
|
+
Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
|
290
|
+
Enabled: true
|
291
|
+
Lint/NumberedParameterAssignment: # (new in 1.9)
|
292
|
+
Enabled: true
|
293
|
+
Lint/OrAssignmentToConstant: # (new in 1.9)
|
294
|
+
Enabled: true
|
295
|
+
Lint/RedundantDirGlobSort: # (new in 1.8)
|
296
|
+
Enabled: true
|
297
|
+
Lint/SymbolConversion: # (new in 1.9)
|
298
|
+
Enabled: true
|
299
|
+
Lint/ToEnumArguments: # (new in 1.1)
|
300
|
+
Enabled: true
|
301
|
+
Lint/TripleQuotes: # (new in 1.9)
|
302
|
+
Enabled: true
|
303
|
+
Lint/UnexpectedBlockArity: # (new in 1.5)
|
304
|
+
Enabled: true
|
305
|
+
Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
|
306
|
+
Enabled: true
|
307
|
+
Style/ArgumentsForwarding: # (new in 1.1)
|
308
|
+
Enabled: true
|
309
|
+
Style/CollectionCompact: # (new in 1.2)
|
310
|
+
Enabled: true
|
311
|
+
Style/DocumentDynamicEvalDefinition: # (new in 1.1)
|
312
|
+
Enabled: true
|
313
|
+
Style/EndlessMethod: # (new in 1.8)
|
314
|
+
Enabled: true
|
315
|
+
Style/HashExcept: # (new in 1.7)
|
316
|
+
Enabled: true
|
317
|
+
Style/IfWithBooleanLiteralBranches: # (new in 1.9)
|
318
|
+
Enabled: true
|
319
|
+
Style/NegatedIfElseCondition: # (new in 1.2)
|
320
|
+
Enabled: true
|
321
|
+
Style/NilLambda: # (new in 1.3)
|
322
|
+
Enabled: true
|
323
|
+
Style/RedundantArgument: # (new in 1.4)
|
324
|
+
Enabled: true
|
325
|
+
Style/SwapValues: # (new in 1.1)
|
326
|
+
Enabled: true
|
327
|
+
|
328
|
+
############################################################
|
329
|
+
# Added in RuboCop 1.10
|
330
|
+
|
331
|
+
Gemspec/DateAssignment: # (new in 1.10)
|
332
|
+
Enabled: true
|
333
|
+
Style/HashConversion: # (new in 1.10)
|
334
|
+
Enabled: true
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.4
|
data/.simplecov
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --protected lib/**/*.rb -m markdown
|
data/CHANGES.md
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright © 2021 The Regents of the University of California
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
6
|
+
copy of this software and associated documentation files (the “Software”),
|
7
|
+
to deal in the Software without restriction, including without limitation
|
8
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
9
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
10
|
+
Software is furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
21
|
+
DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# BerkeleyLibrary::Util
|
2
|
+
|
3
|
+
[](https://github.com/BerkeleyLibrary/util/actions/workflows/build.yml)
|
4
|
+
[](https://github.com/BerkeleyLibrary/util/releases)
|
5
|
+
|
6
|
+
Miscellaneous utility code for the UC Berkeley Library.
|
7
|
+
|
8
|
+
### Notable modules
|
9
|
+
|
10
|
+
## [BerkeleyLibrary::Util::Arrays](lib/berkeley_library/util/arrays.rb)]
|
11
|
+
|
12
|
+
Routines for comparing, merging, and examining the contents of arrays.
|
13
|
+
|
14
|
+
## [BerkeleyLibrary::Util::Paths](lib/berkeley_library/util/paths.rb)]
|
15
|
+
|
16
|
+
Routines for modifying paths separated by forward slashes,
|
17
|
+
(such as URL paths), modeled on the [Go `path` package](https://golang.org/pkg/path/).
|
18
|
+
|
19
|
+
## [BerkeleyLibrary::Util::URIs](lib/berkeley_library/util/uris.rb)]
|
20
|
+
|
21
|
+
Routines for constructing, validating, and retrieving the content from URIs.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('Gemfile', __dir__)
|
2
|
+
require 'bundler/setup' # Set up gems listed in the Gemfile.
|
3
|
+
|
4
|
+
# ------------------------------------------------------------
|
5
|
+
# Application code
|
6
|
+
|
7
|
+
File.expand_path('lib', __dir__).tap do |lib|
|
8
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
9
|
+
end
|
10
|
+
|
11
|
+
# ------------------------------------------------------------
|
12
|
+
# CI
|
13
|
+
|
14
|
+
ENV['RAILS_ENV'] = 'test' if ENV['CI']
|
15
|
+
|
16
|
+
# ------------------------------------------------------------
|
17
|
+
# Custom tasks
|
18
|
+
|
19
|
+
desc 'Run tests, check test coverage, check code style, check for vulnerabilities, build gem'
|
20
|
+
task default: %i[coverage rubocop bundle:audit gem]
|
@@ -0,0 +1,42 @@
|
|
1
|
+
File.expand_path('lib', __dir__).tap do |lib|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
end
|
4
|
+
|
5
|
+
ruby_version = '>= 2.7'
|
6
|
+
|
7
|
+
require 'berkeley_library/util/module_info'
|
8
|
+
|
9
|
+
Gem::Specification.new do |spec|
|
10
|
+
spec.name = BerkeleyLibrary::Util::ModuleInfo::NAME
|
11
|
+
spec.author = BerkeleyLibrary::Util::ModuleInfo::AUTHOR
|
12
|
+
spec.email = BerkeleyLibrary::Util::ModuleInfo::AUTHOR_EMAIL
|
13
|
+
spec.summary = BerkeleyLibrary::Util::ModuleInfo::SUMMARY
|
14
|
+
spec.description = BerkeleyLibrary::Util::ModuleInfo::DESCRIPTION
|
15
|
+
spec.license = BerkeleyLibrary::Util::ModuleInfo::LICENSE
|
16
|
+
spec.version = BerkeleyLibrary::Util::ModuleInfo::VERSION
|
17
|
+
spec.homepage = BerkeleyLibrary::Util::ModuleInfo::HOMEPAGE
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.required_ruby_version = ruby_version
|
24
|
+
|
25
|
+
spec.add_dependency 'berkeley_library-logging', '~> 0.2'
|
26
|
+
spec.add_dependency 'rest-client', '~> 2.1'
|
27
|
+
spec.add_dependency 'typesafe_enum', '~> 0.3'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundle-audit', '~> 0.1'
|
30
|
+
spec.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
|
31
|
+
spec.add_development_dependency 'colorize', '~> 0.8'
|
32
|
+
spec.add_development_dependency 'dotenv', '~> 2.7'
|
33
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
35
|
+
spec.add_development_dependency 'rubocop', '= 1.11'
|
36
|
+
spec.add_development_dependency 'rubocop-rake', '~> 0.5'
|
37
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
|
38
|
+
spec.add_development_dependency 'ruby-prof', '~> 0.17.0'
|
39
|
+
spec.add_development_dependency 'simplecov', '~> 0.21'
|
40
|
+
spec.add_development_dependency 'simplecov-rcov', '~> 0.2'
|
41
|
+
spec.add_development_dependency 'webmock', '~> 3.12'
|
42
|
+
end
|
@@ -0,0 +1,178 @@
|
|
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
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module BerkeleyLibrary
|
2
|
+
module Util
|
3
|
+
class ModuleInfo
|
4
|
+
NAME = 'berkeley_library-util'.freeze
|
5
|
+
AUTHOR = 'David Moles'.freeze
|
6
|
+
AUTHOR_EMAIL = 'dmoles@berkeley.edu'.freeze
|
7
|
+
SUMMARY = 'Miscellaneous Ruby utilities for the UC Berkeley Library'.freeze
|
8
|
+
DESCRIPTION = <<~DESC.gsub(/\s+/, ' ').strip
|
9
|
+
A collection of miscellaneous Ruby routines for the UC Berkeley Library.
|
10
|
+
DESC
|
11
|
+
LICENSE = 'MIT'.freeze
|
12
|
+
VERSION = '0.1.0'.freeze
|
13
|
+
HOMEPAGE = 'https://github.com/BerkeleyLibrary/util'.freeze
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|