refinements 7.5.0 → 7.10.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: 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