refinements 7.4.0 → 7.9.0

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