refinements 7.5.0 → 7.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ccd018fc5d5ae2bfa0b6fc1b9f090bc3638ac4053ea5790d7826e2c291bc378
4
- data.tar.gz: 43cd2a8603f9e8667eab9c18aa24ccf5b1f7860054d88b7bc39e22df1bfad573
3
+ metadata.gz: 2b7b36399c299675bc98b26334acc2466c5c506849e34b36534f21d2eed72e57
4
+ data.tar.gz: 0d00c8e399a0d43c21dafef5985e9d0eb51fe7d544fbe93f4d184eafa75018a8
5
5
  SHA512:
6
- metadata.gz: b5dd7fb29e0de7fe1e51209d427889331d8179c5e92868167e818e25dd792509d4221cdf07448a86bea13820cb13ccf0c680cb4b459c7c57ebb0dc0017255aba
7
- data.tar.gz: 94a069d63f4c565fd0c189d489887d23e7420bb63b8a9ec3cb5defc3626a87158037eb7cefdafdaa1b43e5b66764fda59dcf9007279cdcc3d8d53eadbf078bb7
6
+ metadata.gz: 558191fc552ba160302b9189b8bd2e3ed17ceaee226426cf9453c0c65b8ca0e771b9e85be1e5453c8c1438bc52511a45afc218c72329c17570a6036b65e9f91f
7
+ data.tar.gz: 69436b5353f1541b644f9ca72346da713bd8bb1c0f9595983392881727f06ea46ecacdea1412a929981ee24e0243865c6ac9274e747f04d3584c85216e7e300b
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -9,68 +9,26 @@ image::https://badge.fury.io/rb/refinements.svg[Gem Version]
9
9
  [link=https://circleci.com/gh/bkuhlmann/refinements]
10
10
  image::https://circleci.com/gh/bkuhlmann/refinements.svg?style=svg[Circle CI Status]
11
11
 
12
- A collection of refinements (enhancements) to core Ruby objects.
12
+ A collection of refinements (enhancements) to primitive Ruby objects.
13
13
 
14
14
  toc::[]
15
15
 
16
16
  == Features
17
17
 
18
- * *Arrays*:
19
- ** `#compress` - Removes `nil` and empty values without modifying itself.
20
- ** `#compress!` - Removes `nil` and empty values while modifying itself.
21
- ** `#ring` - Answers a circular array which can enumerate before, current, after elements.
22
- * *BigDecimals*:
23
- ** `#inspect` - Allows one to inspect a big decimal with numeric representation.
24
- * *DateTimes*:
25
- ** `.utc` - Answers new DateTime object for current UTC date/time.
26
- * *Files*:
27
- ** `#rewrite` - When given a file path and a block, it provides the contents of the recently read
28
- file for manipulation and immediate writing back to the same file.
29
- * *Hashes*:
30
- ** `#except` - Answers new hash with given keys removed without modifying itself.
31
- ** `#except!` - Answers new hash with given keys removed while modifying itself.
32
- ** `#symbolize_keys` - Converts keys to symbols without modifying itself.
33
- ** `#symbolize_keys!` - Converts keys to symbols while modifying itself.
34
- ** `#deep_merge` - Merges deeply nested hashes together without modifying itself.
35
- ** `#deep_merge!` - Merges deeply nested hashes together while modifying itself.
36
- ** `#deep_symbolize_keys` - Symbolizes keys of nested hash without modifying itself. Does not handle
37
- nested arrays, though.
38
- ** `#deep_symbolize_keys!` - Symbolizes keys of nested hash while modifying itself. Does not handle
39
- nested arrays, though.
40
- ** `#recurse` - Applies block to nested hash. Does not handle nested arrays, though.
41
- ** `#rekey` - Transforms keys per mapping (size of mapping can vary) without modifying itself.
42
- ** `#rekey!` - Transforms keys per mapping (size of mapping can vary) while modifying itself.
43
- ** `#reverse_merge` - Merges calling hash into passed in hash without modifying itself.
44
- ** `#reverse_merge!` - Merges calling hash into passed in hash while modifying itself.
45
- ** `#use` - Passes each hash value as a block argument for further processing.
46
- * *Pathnames*:
47
- ** `Pathname` - Conversion function (refined from `Kernel`) which can cast `nil` into a pathname.
48
- ** `#name` - Answers file name without extension.
49
- ** `#copy` - Copies file from current location to new location.
50
- ** `#directories` - Answers all or filtered directories for current path.
51
- ** `#extensions` - Answers file extensions as an array.
52
- ** `#files` - Answers all or filtered files for current path.
53
- ** `#relative_parent_from` - Answers relative path from parent directory. This is a complement to
54
- `#relative_path_from`.
55
- ** `#make_ancestors` - Ensures all ancestor directories are created for a path.
56
- ** `#rewrite` - When given a block, it provides the contents of the recently read file for
57
- manipulation and immediate writing back to the same file.
58
- ** `#touch` - Updates access and modification times for path. Defaults to current time.
59
- * *Strings*:
60
- ** `#first` - Answers first character of a string or first set of characters if given a number.
61
- ** `#last` - Answers last character of a string or last set of characters if given a number.
62
- ** `#blank?` - Answers `true`/`false` based on whether string is blank or not
63
- (i.e. `<space>`, `\n`, `\t`, `\r`).
64
- ** `#up` - Answers string with only first letter upcased.
65
- ** `#down` - Answers string with only first letter downcased.
66
- ** `#camelcase` - Answers a camelcased string.
67
- ** `#snakecase` - Answers a snakecased string.
68
- ** `#titleize` - Answers titleized string.
69
- ** `#to_bool` - Answers string as a boolean.
18
+ Enhances the following objects:
19
+
20
+ * Array
21
+ * BigDecimal
22
+ * DateTime
23
+ * File
24
+ * Hash
25
+ * Pathname
26
+ * String
27
+ * StringIO
70
28
 
71
29
  == Requirements
72
30
 
73
- . https://www.ruby-lang.org[Ruby 2.7.x].
31
+ . https://www.ruby-lang.org[Ruby].
74
32
  . A solid understanding of link:https://www.alchemists.io/articles/ruby_refinements[Ruby refinements
75
33
  and lexical scope].
76
34
 
@@ -132,11 +90,13 @@ require "refinements/files"
132
90
  require "refinements/hashes"
133
91
  require "refinements/pathnames"
134
92
  require "refinements/strings"
93
+ require "refinements/string_ios"
135
94
  ----
136
95
 
137
96
  === Using
138
97
 
139
- Much like including/extending a module, you’ll need modify your object(s) to use the refinement(s):
98
+ Much like including/extending a module, you’ll need to modify your object(s) to use the
99
+ refinement(s):
140
100
 
141
101
  [source,ruby]
142
102
  ----
@@ -148,6 +108,7 @@ class Example
148
108
  using Refinements::Hashes
149
109
  using Refinements::Pathnames
150
110
  using Refinements::Strings
111
+ using Refinements::StringIOs
151
112
  end
152
113
  ----
153
114
 
@@ -157,19 +118,58 @@ The following sections demonstrate how each refinement enriches your objects wit
157
118
 
158
119
  ==== Array
159
120
 
121
+ ===== #compress
122
+
123
+ Removes `nil` and empty values without mutating itself.
124
+
160
125
  [source,ruby]
161
126
  ----
162
127
  example = ["An", nil, "", "Example"]
163
- example.compress # => ["An", "Example"]
164
- example # => ["An", nil, "", "Example"]
128
+ example.compress # => ["An", "Example"]
129
+ example # => ["An", nil, "", "Example"]
130
+ ----
165
131
 
132
+ ===== #compress!
133
+
134
+ Removes `nil` and empty values while mutating itself.
135
+
136
+ [source,ruby]
137
+ ----
166
138
  example = ["An", nil, "", "Example"]
167
- example.compress! # => ["An", "Example"]
168
- example # => ["An", "Example"]
139
+ example.compress! # => ["An", "Example"]
140
+ example # => ["An", "Example"]
141
+ ----
142
+
143
+ ===== #include
144
+
145
+ Adds given array or elements without mutating itself.
146
+
147
+ [source,ruby]
148
+ ----
149
+ [1, 2, 3].include [4, 5] # => [1, 2, 3, 4, 5]
150
+ [1, 2, 3].include 4, 5 # => [1, 2, 3, 4, 5]
151
+ ----
152
+
153
+ ===== #exclude
169
154
 
155
+ Removes given array or elements without mutating itself.
156
+
157
+ [source,ruby]
158
+ ----
159
+ [1, 2, 3, 4, 5].exclude [4, 5] # => [1, 2, 3]
160
+ [1, 2, 3, 4, 5].exclude 4, 5 # => [1, 2, 3]
161
+ ----
162
+
163
+ ===== #ring
164
+
165
+ Answers a circular array which can enumerate before, current, after elements.
166
+
167
+ [source,ruby]
168
+ ----
170
169
  example = [1, 2, 3]
171
170
  example.ring # => #<Enumerator: ...>
172
171
  example.ring { |(before, current, after)| puts "#{before} #{current} #{after}" }
172
+
173
173
  # [3 1 2]
174
174
  # [1 2 3]
175
175
  # [2 3 1]
@@ -177,6 +177,10 @@ example.ring { |(before, current, after)| puts "#{before} #{current} #{after}" }
177
177
 
178
178
  ==== Big Decimal
179
179
 
180
+ ===== #inspect
181
+
182
+ Allows one to inspect a big decimal with numeric representation.
183
+
180
184
  [source,ruby]
181
185
  ----
182
186
  BigDecimal.new("5.0E-10").inspect # => "#<BigDecimal:3fd3d458fe84 0.0000000005>"
@@ -184,6 +188,10 @@ BigDecimal.new("5.0E-10").inspect # => "#<BigDecimal:3fd3d458fe84 0.0000000005>"
184
188
 
185
189
  ==== DateTime
186
190
 
191
+ ===== .utc
192
+
193
+ Answers new DateTime object for current UTC date/time.
194
+
187
195
  [source,ruby]
188
196
  ----
189
197
  DateTime.utc # => #<DateTime: 2019-12-31T18:17:00+00:00 ((2458849j,65820s,181867000n),+0s,2299161j)>
@@ -191,6 +199,11 @@ DateTime.utc # => #<DateTime: 2019-12-31T18:17:00+00:00 ((2458849j,65820s,181867
191
199
 
192
200
  ==== File
193
201
 
202
+ ===== .rewrite
203
+
204
+ When given a file path and a block, it provides the contents of the recently read file for
205
+ manipulation and immediate writing back to the same file.
206
+
194
207
  [source,ruby]
195
208
  ----
196
209
  File.rewrite("/test.txt") { |content| content.gsub "[placeholder]", "example" }
@@ -198,129 +211,493 @@ File.rewrite("/test.txt") { |content| content.gsub "[placeholder]", "example" }
198
211
 
199
212
  ==== Hash
200
213
 
214
+ ===== .infinite
215
+
216
+ Answers new hash where missing keys, even deeply nested, answer an empty hash.
217
+
218
+ [source,ruby]
219
+ ----
220
+ example = Hash.infinite
221
+ example[:a] # => {}
222
+ example[:a][:b][:c] # => {}
223
+ ----
224
+
225
+ ===== .with_default
226
+
227
+ Answers new hash where every top-level missing key has the same default value.
228
+
229
+ [source,ruby]
230
+ ----
231
+ example = Hash.with_default ""
232
+ example[:a] # => ""
233
+
234
+ example = Hash.with_default []
235
+ example[:b] # => []
236
+ ----
237
+
238
+ ===== #except
239
+
240
+ Answers new hash with given keys removed without mutating itself.
241
+
201
242
  [source,ruby]
202
243
  ----
203
244
  example = {a: 1, b: 2, c: 3}
204
- example.except :a, :b # => {c: 3}
205
- example # => {a: 1, b: 2, c: 3}
245
+ example.except :a, :b # => {c: 3}
246
+ example # => {a: 1, b: 2, c: 3}
247
+ ----
248
+
249
+ ===== #except!
206
250
 
251
+ Answers new hash with given keys removed while mutating itself.
252
+
253
+ [source,ruby]
254
+ ----
207
255
  example = {a: 1, b: 2, c: 3}
208
- example.except! :a, :b # => {c: 3}
209
- example # => {c: 3}
256
+ example.except! :a, :b # => {c: 3}
257
+ example # => {c: 3}
258
+ ----
259
+
260
+ ===== #flatten_keys
261
+
262
+ Flattens nested keys as top-level keys without mutating itself. Does not handle nested arrays,
263
+ though.
264
+
265
+ [source,ruby]
266
+ ----
267
+ {a: {b: 1}}.flatten_keys prefix: :test # => {test_a_b: 1}
268
+ {a: {b: 1}}.flatten_keys delimiter: :| # => {:"a|b" => 1}
269
+
270
+ {a: {b: 1}}.flatten_keys cast: :to_s # => {"a_b" => 1}
271
+ {"a" => {"b" => 1}}.flatten_keys cast: :to_sym # => {a_b: 1}
272
+
273
+ example = {a: {b: 1}}
274
+ example.flatten_keys # => {a_b: 1}
275
+ example # => {a: {b: 1}}
276
+ ----
210
277
 
278
+ ===== #flatten_keys!
279
+
280
+ Flattens nested keys as top-level keys while mutating itself. Does not handle nested arrays,
281
+ though.
282
+
283
+ [source,ruby]
284
+ ----
285
+ example = {a: {b: 1}}
286
+ example.flatten_keys! # => {a_b: 1}
287
+ example # => {a_b: 1}
288
+ ----
289
+
290
+ ===== #stringify_keys
291
+
292
+ Converts keys to strings without mutating itself.
293
+
294
+ [source,ruby]
295
+ ----
296
+ example = {a: 1, b: 2}
297
+ example.stringify_keys # => {"a" => 1, "b" => 2}
298
+ example # => {a: 1, b: 2}
299
+ ----
300
+
301
+ ===== #stringify_keys!
302
+
303
+ Converts keys to strings while mutating itself.
304
+
305
+ [source,ruby]
306
+ ----
307
+ example = {a: 1, b: 2}
308
+ example.stringify_keys! # => {"a" => 1, "b" => 2}
309
+ example # => {"a" => 1, "b" => 2}
310
+ ----
311
+
312
+ ===== #symbolize_keys
313
+
314
+ Converts keys to symbols without mutating itself.
315
+
316
+ [source,ruby]
317
+ ----
211
318
  example = {"a" => 1, "b" => 2}
212
- example.symbolize_keys # => {a: 1, b: 2}
213
- example # => {"a" => 1, "b" => 2}
319
+ example.symbolize_keys # => {a: 1, b: 2}
320
+ example # => {"a" => 1, "b" => 2}
321
+ ----
214
322
 
323
+ ===== #symbolize_keys!
324
+
325
+ Converts keys to symbols while mutating itself.
326
+
327
+ [source,ruby]
328
+ ----
215
329
  example = {"a" => 1, "b" => 2}
216
- example.symbolize_keys! # => {a: 1, b: 2}
217
- example # => {a: 1, b: 2}
330
+ example.symbolize_keys! # => {a: 1, b: 2}
331
+ example # => {a: 1, b: 2}
332
+ ----
218
333
 
219
- example = {a: 1, b: 2, c: 3}
220
- example.slice :a, :c # => {a: 1, c: 3}
221
- example # => {a: 1, b: 2, c: 3}
334
+ ===== #deep_merge
222
335
 
223
- example = {a: 1, b: 2, c: 3}
224
- example.slice! :a, :c # => {a: 1, c: 3}
225
- example # => {a: 1, c: 3}
336
+ Merges deeply nested hashes together without mutating itself.
226
337
 
338
+ [source,ruby]
339
+ ----
227
340
  example = {a: "A", b: {one: "One", two: "Two"}}
228
- example.deep_merge b: {one: 1} # => {a: "A", b: {one: 1, two: "Two"}}
229
- example # => {a: "A", b: {one: "One", two: "Two"}}
341
+ example.deep_merge b: {one: 1} # => {a: "A", b: {one: 1, two: "Two"}}
342
+ example # => {a: "A", b: {one: "One", two: "Two"}}
343
+ ----
344
+
345
+ ===== #deep_merge!
346
+
347
+ Merges deeply nested hashes together while mutating itself.
230
348
 
349
+ [source,ruby]
350
+ ----
231
351
  example = {a: "A", b: {one: "One", two: "Two"}}
232
- example.deep_merge! b: {one: 1} # => {a: "A", b: {one: 1, two: "Two"}}
233
- example # => {a: "A", b: {one: 1, two: "Two"}}
352
+ example.deep_merge! b: {one: 1} # => {a: "A", b: {one: 1, two: "Two"}}
353
+ example # => {a: "A", b: {one: 1, two: "Two"}}
354
+ ----
355
+
356
+ ===== #deep_stringify_keys
357
+
358
+ Stringifies keys of nested hash without mutating itself. Does not handle nested arrays, though.
359
+
360
+ [source,ruby]
361
+ ----
362
+ example = {a: {b: 2}}
363
+ example.deep_stringify_keys # => {"a" => {"b" => 1}}
364
+ example # => {a: {b: 2}}
365
+ ----
366
+
367
+ ===== #deep_stringify_keys!
368
+
369
+ Stringifies keys of nested hash while mutating itself. Does not handle nested arrays, though.
370
+
371
+ [source,ruby]
372
+ ----
373
+ example = {a: {b: 2}}
374
+ example.deep_stringify_keys! # => {"a" => {"b" => 1}}
375
+ example # => {"a" => {"b" => 1}}
376
+ ----
377
+
378
+ ===== #deep_symbolize_keys
379
+
380
+ Symbolizes keys of nested hash without mutating itself. Does not handle nested arrays, though.
234
381
 
382
+ [source,ruby]
383
+ ----
235
384
  example = {"a" => {"b" => 2}}
236
- example.deep_symbolize_keys # => {a: {b: 1}}
237
- example # => {"a" => {"b" => 2}}
385
+ example.deep_symbolize_keys # => {a: {b: 1}}
386
+ example # => {"a" => {"b" => 2}}
387
+ ----
388
+
389
+ ===== #deep_symbolize_keys!
238
390
 
391
+ Symbolizes keys of nested hash while mutating itself. Does not handle nested arrays, though.
392
+
393
+ [source,ruby]
394
+ ----
239
395
  example = {"a" => {"b" => 2}}
240
- example.deep_symbolize_keys! # => {a: {b: 1}}
241
- example # => {a: {b: 1}}
396
+ example.deep_symbolize_keys! # => {a: {b: 1}}
397
+ example # => {a: {b: 1}}
398
+ ----
242
399
 
400
+ ===== #recurse
401
+
402
+ Recursively iterates over the hash and any hash value by applying the given block to it. Does not
403
+ handle nested arrays, though.
404
+
405
+ [source,ruby]
406
+ ----
243
407
  example = {"a" => {"b" => 1}}
244
- example.recurse(&:symbolize_keys) # => {a: {b: 1}}
245
- example.recurse(&:invert) # => {{"b" => 1} => "a"}
408
+ example.recurse(&:symbolize_keys) # => {a: {b: 1}}
409
+ example.recurse(&:invert) # => {{"b" => 1} => "a"}
410
+ ----
411
+
412
+ ===== #rekey
246
413
 
414
+ Transforms keys per mapping (size of mapping can vary) without mutating itself.
415
+
416
+ [source,ruby]
417
+ ----
247
418
  example = {a: 1, b: 2, c: 3}
248
- example.rekey a: :amber, b: :blue # => {amber: 1, blue: 2, c: 3}
249
- example # => {a: 1, b: 2, c: 3}
419
+ example.rekey a: :amber, b: :blue # => {amber: 1, blue: 2, c: 3}
420
+ example # => {a: 1, b: 2, c: 3}
421
+ ----
422
+
423
+ ===== #rekey!
250
424
 
425
+ Transforms keys per mapping (size of mapping can vary) while mutating itself.
426
+
427
+ [source,ruby]
428
+ ----
251
429
  example = {a: 1, b: 2, c: 3}
252
- example.rekey! a: :amber, b: :blue # => {amber: 1, blue: 2, c: 3}
253
- example # => {amber: 1, blue: 2, c: 3}
430
+ example.rekey! a: :amber, b: :blue # => {amber: 1, blue: 2, c: 3}
431
+ example # => {amber: 1, blue: 2, c: 3}
432
+ ----
433
+
434
+ ===== #reverse_merge
435
+
436
+ Merges calling hash into passed in hash without mutating itself.
254
437
 
438
+ [source,ruby]
439
+ ----
255
440
  example = {a: 1, b: 2}
256
- example.reverse_merge a: 0, c: 3 # => {a: 1, b: 2, c: 3}
257
- example # => {a: 1, b: 2}
441
+ example.reverse_merge a: 0, c: 3 # => {a: 1, b: 2, c: 3}
442
+ example # => {a: 1, b: 2}
443
+ ----
444
+
445
+ ===== #reverse_merge!
258
446
 
447
+ Merges calling hash into passed in hash while mutating itself.
448
+
449
+ [source,ruby]
450
+ ----
259
451
  example = {a: 1, b: 2}
260
- example.reverse_merge! a: 0, c: 3 # => {a: 1, b: 2, c: 3}
261
- example # => {a: 1, b: 2, c: 3}
452
+ example.reverse_merge! a: 0, c: 3 # => {a: 1, b: 2, c: 3}
453
+ example # => {a: 1, b: 2, c: 3}
454
+ ----
262
455
 
456
+ ===== #use
457
+
458
+ Passes each hash value as a block argument for further processing.
459
+
460
+ [source,ruby]
461
+ ----
263
462
  example = {unit: "221B", street: "Baker Street", city: "London", country: "UK"}
264
463
  example.use { |unit, street| "#{unit} #{street}" } # => "221B Baker Street"
265
464
  ----
266
465
 
267
466
  ==== Pathname
268
467
 
468
+ ===== Pathname
469
+
470
+ Enhances the conversion function -- refined from `Kernel` -- which casts `nil` into a pathname in
471
+ order to avoid: `TypeError (no implicit conversion of nil into String)`. The pathname is still
472
+ invalid but at least you have an instance of `Pathname`, which behaves like a _Null Object_, that
473
+ can still be used to construct a valid path.
474
+
269
475
  [source,ruby]
270
476
  ----
271
477
  Pathname(nil) # => Pathname("")
478
+ ----
479
+
480
+ ===== #name
272
481
 
482
+ Answers file name without extension.
483
+
484
+ [source,ruby]
485
+ ----
273
486
  Pathname("example.txt").name # => Pathname("example")
487
+ ----
274
488
 
489
+ ===== #copy
490
+
491
+ Copies file from current location to new location.
492
+
493
+ [source,ruby]
494
+ ----
275
495
  Pathname("input.txt").copy Pathname("output.txt")
496
+ ----
497
+
498
+ ===== #directories
276
499
 
277
- Pathname("/example").directories # => [Pathname("a"), Pathname("b")]
278
- Pathname("/example").directories "a*" # => [Pathname("a")]
500
+ Answers all or filtered directories for current path.
279
501
 
502
+ [source,ruby]
503
+ ----
504
+ Pathname("/example").directories # => [Pathname("a"), Pathname("b")]
505
+ Pathname("/example").directories "a*" # => [Pathname("a")]
506
+ Pathname("/example").directories flag: File::FNM_DOTMATCH # => [Pathname(".."), Pathname(".")]
507
+ ----
508
+
509
+ ===== #extensions
510
+
511
+ Answers file extensions as an array.
512
+
513
+ [source,ruby]
514
+ ----
280
515
  Pathname("example.txt.erb").extensions # => [".txt", ".erb"]
516
+ ----
517
+
518
+ ===== #files
519
+
520
+ Answers all or filtered files for current path.
521
+
522
+ [source,ruby]
523
+ ----
524
+ Pathname("/example").files # => [Pathname("a.txt"), Pathname("a.png")]
525
+ Pathname("/example").files "*.png" # => [Pathname("a.png")]
526
+ Pathname("/example").files flag: File::FNM_DOTMATCH # => [Pathname(".ruby-version")]
527
+ ----
281
528
 
282
- Pathname("/example").files # => [Pathname("a.txt"), Pathname("a.png")]
283
- Pathname("/example").files "*.png" # => [Pathname("a.png")]
529
+ ===== #gsub
284
530
 
285
- Pathname("/one/two/three").relative_parent_from("/one") # => Pathname "two"
531
+ Same behavior as `String#gsub` but answers a path with patterns replaced with desired substitutes.
286
532
 
533
+ [source,ruby]
534
+ ----
535
+ Pathname("/a/path/some/path").gsub("path", "test")
536
+ # => Pathname("/a/test/some/test")
537
+
538
+ Pathname("/%placeholder%/some/%placeholder%").gsub("%placeholder%", "test")
539
+ # => Pathname("/test/some/test")
540
+ ----
541
+
542
+ ===== #relative_parent
543
+
544
+ Answers relative path from parent directory. This is a complement to `#relative_path_from`.
545
+
546
+ [source,ruby]
547
+ ----
548
+ Pathname("/one/two/three").relative_parent("/one") # => Pathname "two"
549
+ ----
550
+
551
+ ===== #make_ancestors
552
+
553
+ Ensures all ancestor directories are created for a path.
554
+
555
+ [source,ruby]
556
+ ----
287
557
  Pathname("/one/two").make_ancestors
288
- Pathname("/one").exist? # => true
289
- Pathname("/one/two").exist? # => false
558
+ Pathname("/one").exist? # => true
559
+ Pathname("/one/two").exist? # => false
560
+ ----
290
561
 
562
+ ===== #rewrite
563
+
564
+ When given a block, it provides the contents of the recently read file for manipulation and
565
+ immediate writing back to the same file.
566
+
567
+ [source,ruby]
568
+ ----
291
569
  Pathname("/test.txt").rewrite { |content| content.sub "[placeholder]", "example" }
570
+ ----
292
571
 
572
+ ===== #touch
573
+
574
+ Updates access and modification times for path. Defaults to current time.
575
+
576
+ [source,ruby]
577
+ ----
293
578
  Pathname("example.txt").touch
294
579
  Pathname("example.txt").touch at: Time.now - 1
295
580
  ----
296
581
 
297
582
  ==== String
298
583
 
584
+ ===== #first
585
+
586
+ Answers first character of a string or first set of characters if given a number.
587
+
299
588
  [source,ruby]
300
589
  ----
301
- "example".first # => "e"
302
- "example".first 4 # => "exam"
590
+ "example".first # => "e"
591
+ "example".first 4 # => "exam"
592
+ ----
593
+
594
+ ===== #last
303
595
 
304
- "instant".last # => "t"
305
- "instant".last 3 # => "ant"
596
+ Answers last character of a string or last set of characters if given a number.
597
+
598
+ [source,ruby]
599
+ ----
600
+ "instant".last # => "t"
601
+ "instant".last 3 # => "ant"
602
+ ----
306
603
 
604
+ ===== #blank?
605
+
606
+ Answers `true`/`false` based on whether string is blank, `<space>`, `\n`, `\t`, and/or `\r`.
607
+
608
+ [source,ruby]
609
+ ----
307
610
  " \n\t\r".blank? # => true
611
+ ----
612
+
613
+ ===== #up
308
614
 
615
+ Answers string with only first letter upcased.
616
+
617
+ [source,ruby]
618
+ ----
309
619
  "example".up # => "Example"
620
+ ----
621
+
622
+ ===== #down
623
+
624
+ Answers string with only first letter downcased.
310
625
 
626
+ [source,ruby]
627
+ ----
311
628
  "EXAMPLE".down # => "eXAMPLE"
629
+ ----
630
+
631
+ ===== #indent
312
632
 
633
+ Answers string indented by two spaces by default.
634
+
635
+ [source,ruby]
636
+ ----
637
+ "example".indent # => " example"
638
+ "example".indent 0 # => "example"
639
+ "example".indent -1 # => "example"
640
+ "example".indent 2 # => " example"
641
+ "example".indent 3, padding: " " # => " example"
642
+ ----
643
+
644
+ ===== #camelcase
645
+
646
+ Answers a camelcased string.
647
+
648
+ [source,ruby]
649
+ ----
313
650
  "this_is_an_example".camelcase # => "ThisIsAnExample"
651
+ ----
652
+
653
+ ===== #snakecase
314
654
 
655
+ Answers a snakecased string.
656
+
657
+ [source,ruby]
658
+ ----
315
659
  "ThisIsAnExample".snakecase # => "this_is_an_example"
660
+ ----
661
+
662
+ ===== #titleize
663
+
664
+ Answers titleized string.
316
665
 
666
+ [source,ruby]
667
+ ----
317
668
  "ThisIsAnExample".titleize # => "This Is An Example"
669
+ ----
670
+
671
+ ===== #to_bool
672
+
673
+ Answers string as a boolean.
674
+
675
+ [source,ruby]
676
+ ----
677
+ "true".to_bool # => true
678
+ "yes".to_bool # => true
679
+ "1".to_bool # => true
680
+ "".to_bool # => false
681
+ "example".to_bool # => false
682
+ ----
683
+
684
+ ==== String IO
685
+
686
+ ===== #reread
687
+
688
+ Answers full string by rewinding to beginning of string and reading all content.
689
+
690
+ [source,ruby]
691
+ ----
692
+ io = StringIO.new
693
+ io.write "This is a test."
694
+
695
+ io.reread # => "This is a test."
696
+ io.reread 4 # => "This"
318
697
 
319
- "true".to_bool # => true
320
- "yes".to_bool # => true
321
- "1".to_bool # => true
322
- "".to_bool # => false
323
- "example".to_bool # => false
698
+ buffer = "".dup
699
+ io.reread(buffer: buffer)
700
+ buffer # => "This is a test."
324
701
  ----
325
702
 
326
703
  == Tests
@@ -8,3 +8,4 @@ require "refinements/files"
8
8
  require "refinements/hashes"
9
9
  require "refinements/pathnames"
10
10
  require "refinements/strings"
11
+ require "refinements/string_ios"
@@ -11,6 +11,14 @@ module Refinements
11
11
  replace compress
12
12
  end
13
13
 
14
+ def include *elements
15
+ self + elements.flatten
16
+ end
17
+
18
+ def exclude *elements
19
+ self - elements.flatten
20
+ end
21
+
14
22
  def ring &block
15
23
  [last, *self, first].each_cons 3, &block
16
24
  end
@@ -2,6 +2,16 @@
2
2
 
3
3
  module Refinements
4
4
  module Hashes
5
+ refine Hash.singleton_class do
6
+ def infinite
7
+ new { |new_hash, missing_key| new_hash[missing_key] = new(&new_hash.default_proc) }
8
+ end
9
+
10
+ def with_default value
11
+ new { |new_hash, missing_key| new_hash[missing_key] = value }
12
+ end
13
+ end
14
+
5
15
  refine Hash do
6
16
  def except *keys
7
17
  reject { |key, _value| keys.include? key }
@@ -11,6 +21,33 @@ module Refinements
11
21
  replace except(*keys)
12
22
  end
13
23
 
24
+ # :reek:TooManyStatements
25
+ def flatten_keys prefix: nil, delimiter: "_", cast: :to_sym
26
+ fail StandardError, "Unknown cast: #{cast}." unless %i[to_sym to_s].include? cast
27
+
28
+ reduce({}) do |flat, (key, value)|
29
+ flat_key = prefix ? "#{prefix}#{delimiter}#{key}" : key
30
+
31
+ next flat.merge flat_key.public_send(cast) => value unless value.is_a? self.class
32
+
33
+ flat.merge(
34
+ recurse { value.flatten_keys prefix: flat_key, delimiter: delimiter, cast: cast }
35
+ )
36
+ end
37
+ end
38
+
39
+ def flatten_keys! prefix: nil, delimiter: "_", cast: :to_sym
40
+ replace flatten_keys(prefix: prefix, delimiter: delimiter, cast: cast)
41
+ end
42
+
43
+ def stringify_keys
44
+ reduce({}) { |hash, (key, value)| hash.merge key.to_s => value }
45
+ end
46
+
47
+ def stringify_keys!
48
+ replace stringify_keys
49
+ end
50
+
14
51
  def symbolize_keys
15
52
  reduce({}) { |hash, (key, value)| hash.merge key.to_sym => value }
16
53
  end
@@ -20,13 +57,9 @@ module Refinements
20
57
  end
21
58
 
22
59
  def deep_merge other
23
- dup.deep_merge! other
24
- end
25
-
26
- def deep_merge! other
27
60
  clazz = self.class
28
61
 
29
- merge! other do |_key, this_value, other_value|
62
+ merge other do |_key, this_value, other_value|
30
63
  if this_value.is_a?(clazz) && other_value.is_a?(clazz)
31
64
  this_value.deep_merge other_value
32
65
  else
@@ -35,12 +68,24 @@ module Refinements
35
68
  end
36
69
  end
37
70
 
71
+ def deep_merge! other
72
+ replace deep_merge(other)
73
+ end
74
+
75
+ def deep_stringify_keys
76
+ recurse(&:stringify_keys)
77
+ end
78
+
79
+ def deep_stringify_keys!
80
+ replace deep_stringify_keys
81
+ end
82
+
38
83
  def deep_symbolize_keys
39
84
  recurse(&:symbolize_keys)
40
85
  end
41
86
 
42
87
  def deep_symbolize_keys!
43
- recurse(&:symbolize_keys!)
88
+ replace deep_symbolize_keys
44
89
  end
45
90
 
46
91
  def recurse &block
@@ -65,12 +110,12 @@ module Refinements
65
110
 
66
111
  def reverse_merge other
67
112
  warn "[DEPRECATION]: #reverse_merge is deprecated, use #merge instead."
68
- other.merge self
113
+ merge(other) { |_key, old_value, _new_value| old_value }
69
114
  end
70
115
 
71
116
  def reverse_merge! other
72
117
  warn "[DEPRECATION]: #reverse_merge! is deprecated, use #merge! instead."
73
- merge!(other) { |_key, old_value, _new_value| old_value }
118
+ replace reverse_merge(other)
74
119
  end
75
120
 
76
121
  def use &block
@@ -5,7 +5,7 @@ module Refinements
5
5
  module Identity
6
6
  NAME = "refinements"
7
7
  LABEL = "Refinements"
8
- VERSION = "7.5.0"
8
+ VERSION = "7.10.0"
9
9
  VERSION_LABEL = "#{LABEL} #{VERSION}"
10
10
  end
11
11
  end
@@ -23,20 +23,30 @@ module Refinements
23
23
  self
24
24
  end
25
25
 
26
- def directories pattern = "*"
27
- glob(pattern).select(&:directory?).sort
26
+ def directories pattern = "*", flag: File::FNM_SYSCASE
27
+ glob(pattern, flag).select(&:directory?).sort
28
28
  end
29
29
 
30
30
  def extensions
31
31
  basename.to_s.split(/(?=\.)+/).tap(&:shift)
32
32
  end
33
33
 
34
- def files pattern = "*"
35
- glob(pattern).select(&:file?).sort
34
+ def files pattern = "*", flag: File::FNM_SYSCASE
35
+ glob(pattern, flag).select(&:file?).sort
36
36
  end
37
37
 
38
- def relative_parent_from root
39
- relative_path_from(root).parent
38
+ def gsub pattern, replacement
39
+ self.class.new to_s.gsub(pattern, replacement)
40
+ end
41
+
42
+ def relative_parent root_dir
43
+ relative_path_from(root_dir).parent
44
+ end
45
+
46
+ def relative_parent_from root_dir
47
+ warn "[DEPRECATION]: Pathname#relative_parent_from is deprecated, " \
48
+ "use Pathname#relative_parent instead."
49
+ relative_parent root_dir
40
50
  end
41
51
 
42
52
  def make_ancestors
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module Refinements
6
+ module StringIOs
7
+ refine StringIO do
8
+ def reread length = nil, buffer: nil
9
+ tap(&:rewind).read length, buffer
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,9 +2,12 @@
2
2
 
3
3
  module Refinements
4
4
  module Strings
5
+ DELIMITERS = %r([a-z][A-Z]|\s*-\s*|\s*/\s*|\s*:+\s*|\s*_\s*|\s+).freeze
6
+
5
7
  refine String.singleton_class do
6
8
  def delimiters
7
- %r([a-z][A-Z]|\s*-\s*|\s*/\s*|\s*:+\s*|\s*_\s*|\s+)
9
+ warn "[DEPRECATION]: .delimiters is deprecated, use DELIMITERS instead."
10
+ DELIMITERS
8
11
  end
9
12
  end
10
13
 
@@ -47,8 +50,14 @@ module Refinements
47
50
  first.downcase + self[1, size]
48
51
  end
49
52
 
53
+ def indent multiplier = 1, padding: " "
54
+ return self if multiplier.negative?
55
+
56
+ padding * multiplier + self
57
+ end
58
+
50
59
  def camelcase
51
- return up unless match? self.class.delimiters
60
+ return up unless match? DELIMITERS
52
61
 
53
62
  split(%r(\s*-\s*|\s*/\s*|\s*:+\s*)).then { |parts| combine parts, :up, "::" }
54
63
  .then { |text| text.split(/\s*_\s*|\s+/) }
@@ -56,7 +65,7 @@ module Refinements
56
65
  end
57
66
 
58
67
  def snakecase
59
- return downcase unless match? self.class.delimiters
68
+ return downcase unless match? DELIMITERS
60
69
 
61
70
  split(%r(\s*-\s*|\s*/\s*|\s*:+\s*)).then { |parts| combine parts, :down, "/" }
62
71
  .then { |text| text.split(/(?=[A-Z])|\s*_\s*|\s+/) }
@@ -64,7 +73,7 @@ module Refinements
64
73
  end
65
74
 
66
75
  def titleize
67
- return capitalize unless match? self.class.delimiters
76
+ return capitalize unless match? DELIMITERS
68
77
 
69
78
  split(/(?=[A-Z])|\s*_\s*|\s*-\s*|\s+/).then { |parts| combine parts, :up, " " }
70
79
  .then { |text| text.split %r(\s*/\s*|\s*:+\s*) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: refinements
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.5.0
4
+ version: 7.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -28,7 +28,7 @@ cert_chain:
28
28
  2XV8FRa7/JimI07sPLC13eLY3xd/aYTi85Z782KIA4j0G8XEEWAX0ouBhlXPocZv
29
29
  QWc=
30
30
  -----END CERTIFICATE-----
31
- date: 2020-06-07 00:00:00.000000000 Z
31
+ date: 2020-09-27 00:00:00.000000000 Z
32
32
  dependencies:
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: bundler-audit
@@ -50,28 +50,28 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '14.0'
53
+ version: '14.2'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '14.0'
60
+ version: '14.2'
61
61
  - !ruby/object:Gem::Dependency
62
- name: git-cop
62
+ name: git-lint
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '4.0'
67
+ version: '1.0'
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '4.0'
74
+ version: '1.0'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: guard-rspec
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -162,14 +162,14 @@ dependencies:
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: '0.83'
165
+ version: '0.89'
166
166
  type: :development
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
- version: '0.83'
172
+ version: '0.89'
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: rubocop-performance
175
175
  requirement: !ruby/object:Gem::Requirement
@@ -218,14 +218,14 @@ dependencies:
218
218
  requirements:
219
219
  - - "~>"
220
220
  - !ruby/object:Gem::Version
221
- version: '0.18'
221
+ version: '0.19'
222
222
  type: :development
223
223
  prerelease: false
224
224
  version_requirements: !ruby/object:Gem::Requirement
225
225
  requirements:
226
226
  - - "~>"
227
227
  - !ruby/object:Gem::Version
228
- version: '0.18'
228
+ version: '0.19'
229
229
  description:
230
230
  email:
231
231
  - brooke@alchemists.io
@@ -245,6 +245,7 @@ files:
245
245
  - lib/refinements/hashes.rb
246
246
  - lib/refinements/identity.rb
247
247
  - lib/refinements/pathnames.rb
248
+ - lib/refinements/string_ios.rb
248
249
  - lib/refinements/strings.rb
249
250
  homepage: https://www.alchemists.io/projects/refinements
250
251
  licenses:
metadata.gz.sig CHANGED
Binary file