pleasant_path 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -9
- data/README.md +11 -11
- data/lib/pleasant_path.rb +1 -1
- data/lib/pleasant_path/enumerable.rb +43 -0
- data/lib/pleasant_path/file.rb +28 -25
- data/lib/pleasant_path/io.rb +22 -21
- data/lib/pleasant_path/json/object.rb +12 -12
- data/lib/pleasant_path/json/pathname.rb +24 -27
- data/lib/pleasant_path/pathname.rb +745 -221
- data/lib/pleasant_path/string.rb +31 -19
- data/lib/pleasant_path/version.rb +1 -1
- data/lib/pleasant_path/yaml/object.rb +9 -5
- data/lib/pleasant_path/yaml/pathname.rb +18 -11
- metadata +4 -5
- data/lib/pleasant_path/array.rb +0 -37
@@ -1,22 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Pathname
|
2
4
|
|
3
|
-
# {https://ruby-
|
4
|
-
# a Pathname. On POSIX systems, this should be equivalent to
|
5
|
+
# {https://docs.ruby-lang.org/en/trunk/File/File/Constants.html#NULL +File::NULL+}
|
6
|
+
# as a Pathname. On POSIX systems, this should be equivalent to
|
5
7
|
# +Pathname.new("/dev/null")+.
|
6
8
|
NULL = Pathname.new(File::NULL)
|
7
9
|
|
8
10
|
# Returns the Pathname unmodified. Exists for parity with
|
9
11
|
# {String#to_pathname}.
|
10
12
|
#
|
11
|
-
# @return [
|
13
|
+
# @return [self]
|
12
14
|
def to_pathname
|
13
15
|
self
|
14
16
|
end
|
15
17
|
|
16
|
-
# Joins the
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# Joins the Pathname +dirname+ with the given +sibling+.
|
19
|
+
#
|
20
|
+
# The mnemonic for this operator is that the result is formed by going
|
21
|
+
# up one directory level from the original path, then going back down
|
22
|
+
# to +sibling+.
|
20
23
|
#
|
21
24
|
# @example
|
22
25
|
# Pathname.new("path/to/file1") ^ "file2" # == Pathname.new("path/to/file2")
|
@@ -27,7 +30,7 @@ class Pathname
|
|
27
30
|
self.dirname / sibling
|
28
31
|
end
|
29
32
|
|
30
|
-
# Returns the +basename+ of the
|
33
|
+
# Returns the +basename+ of the parent directory (+dirname+).
|
31
34
|
#
|
32
35
|
# @example
|
33
36
|
# Pathname.new("path/to/file").parentname # == Pathname.new("to")
|
@@ -48,13 +51,15 @@ class Pathname
|
|
48
51
|
#
|
49
52
|
# Pathname.new("dir1/file2").existence # == nil
|
50
53
|
#
|
51
|
-
# @return [
|
54
|
+
# @return [self, nil]
|
52
55
|
def existence
|
53
56
|
self if self.exist?
|
54
57
|
end
|
55
58
|
|
56
|
-
#
|
57
|
-
# common.
|
59
|
+
# Returns the longest path that the Pathname and +other+ have in
|
60
|
+
# common.
|
61
|
+
#
|
62
|
+
# @see File.common_path
|
58
63
|
#
|
59
64
|
# @example
|
60
65
|
# f1 = Pathname.new("dir1/file1")
|
@@ -79,28 +84,21 @@ class Pathname
|
|
79
84
|
# @return [Boolean]
|
80
85
|
alias :dir? :directory?
|
81
86
|
|
82
|
-
#
|
83
|
-
# directories or files.
|
84
|
-
#
|
85
|
-
# @example
|
86
|
-
# FileUtils.mkdir("parent")
|
87
|
-
# FileUtils.mkdir("parent/dir1")
|
87
|
+
# @deprecated Use +Pathname#empty?+.
|
88
88
|
#
|
89
|
-
#
|
90
|
-
# Pathname.new("parent/dir1").dir_empty? # == true
|
89
|
+
# Alias of +Pathname#empty?+.
|
91
90
|
#
|
92
91
|
# @return [Boolean]
|
93
|
-
|
94
|
-
self.children(false).empty?
|
95
|
-
end
|
92
|
+
alias :dir_empty? :empty?
|
96
93
|
|
97
|
-
# Returns the immediate
|
98
|
-
#
|
99
|
-
#
|
94
|
+
# Returns the immediate child directories of the directory indicated
|
95
|
+
# by the Pathname. Returned Pathnames are prefixed by the original
|
96
|
+
# Pathname.
|
100
97
|
#
|
101
98
|
# @example
|
102
99
|
# FileUtils.mkdir("parent")
|
103
100
|
# FileUtils.mkdir("parent/dir1")
|
101
|
+
# FileUtils.mkdir("parent/dir1/dir1")
|
104
102
|
# FileUtils.mkdir("parent/dir2")
|
105
103
|
# FileUtils.touch("parent/file1")
|
106
104
|
#
|
@@ -111,13 +109,15 @@ class Pathname
|
|
111
109
|
# # ]
|
112
110
|
#
|
113
111
|
# @return [Array<Pathname>]
|
112
|
+
# @raise [SystemCallError]
|
113
|
+
# if the Pathname does not point to an existing directory
|
114
114
|
def dirs
|
115
115
|
self.children.tap{|c| c.select!(&:dir?) }
|
116
116
|
end
|
117
117
|
|
118
|
-
# Returns
|
119
|
-
#
|
120
|
-
#
|
118
|
+
# Returns all (recursive) descendent directories of the directory
|
119
|
+
# indicated by the Pathname. Returned Pathnames are prefixed by the
|
120
|
+
# original Pathname, and are in depth-first order.
|
121
121
|
#
|
122
122
|
# @example
|
123
123
|
# FileUtils.mkdir("parent")
|
@@ -135,18 +135,48 @@ class Pathname
|
|
135
135
|
#
|
136
136
|
# @return [Array<Pathname>]
|
137
137
|
def dirs_r
|
138
|
-
self.
|
138
|
+
self.find_dirs.to_a
|
139
139
|
end
|
140
140
|
|
141
|
-
#
|
142
|
-
# indicated by the Pathname.
|
143
|
-
# original Pathname.
|
141
|
+
# Iterates over all (recursive) descendent directories of the
|
142
|
+
# directory indicated by the Pathname. Iterated Pathnames are
|
143
|
+
# prefixed by the original Pathname, and are in depth-first order.
|
144
|
+
#
|
145
|
+
# If no block is given, this method returns an Enumerator. Otherwise,
|
146
|
+
# the block is called with each descendent Pathname, and this method
|
147
|
+
# returns the original Pathname.
|
148
|
+
#
|
149
|
+
# @see https://docs.ruby-lang.org/en/trunk/Pathname.html#method-i-find Pathname#find
|
150
|
+
#
|
151
|
+
# @overload find_dirs()
|
152
|
+
# @return [Enumerator<Pathname>]
|
153
|
+
#
|
154
|
+
# @overload find_dirs(&block)
|
155
|
+
# @yieldparam descendent [Pathname]
|
156
|
+
# @return [Pathname]
|
157
|
+
def find_dirs
|
158
|
+
return to_enum(__method__) unless block_given?
|
159
|
+
|
160
|
+
self.find do |path|
|
161
|
+
if path.file?
|
162
|
+
Find.prune
|
163
|
+
elsif path != self
|
164
|
+
yield path
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns the immediate child files of the directory indicated by the
|
172
|
+
# Pathname. Returned Pathnames are prefixed by the original Pathname.
|
144
173
|
#
|
145
174
|
# @example
|
146
175
|
# FileUtils.mkdir("parent")
|
147
176
|
# FileUtils.touch("parent/file1")
|
148
|
-
# FileUtils.touch("parent/file2")
|
149
177
|
# FileUtils.mkdir("parent/dir1")
|
178
|
+
# FileUtils.touch("parent/dir1/file1")
|
179
|
+
# FileUtils.touch("parent/file2")
|
150
180
|
#
|
151
181
|
# Pathname.new("parent").files
|
152
182
|
# # == [
|
@@ -155,43 +185,68 @@ class Pathname
|
|
155
185
|
# # ]
|
156
186
|
#
|
157
187
|
# @return [Array<Pathname>]
|
188
|
+
# @raise [SystemCallError]
|
189
|
+
# if the Pathname does not point to an existing directory
|
158
190
|
def files
|
159
191
|
self.children.tap{|c| c.select!(&:file?) }
|
160
192
|
end
|
161
193
|
|
162
|
-
# Returns
|
163
|
-
#
|
164
|
-
#
|
194
|
+
# Returns all (recursive) descendent files of the directory indicated
|
195
|
+
# by the Pathname. Returned Pathnames are prefixed by the original
|
196
|
+
# Pathname, and are in depth-first order.
|
165
197
|
#
|
166
198
|
# @example
|
167
199
|
# FileUtils.mkdir("parent")
|
200
|
+
# FileUtils.touch("parent/file1")
|
168
201
|
# FileUtils.mkdir("parent/dir1")
|
169
202
|
# FileUtils.touch("parent/dir1/file1")
|
170
|
-
# FileUtils.touch("parent/
|
203
|
+
# FileUtils.touch("parent/file2")
|
171
204
|
#
|
172
205
|
# Pathname.new("parent").files_r
|
173
206
|
# # == [
|
174
207
|
# # Pathname.new("parent/dir1/file1"),
|
175
208
|
# # Pathname.new("parent/file1")
|
209
|
+
# # Pathname.new("parent/file2")
|
176
210
|
# # ]
|
177
211
|
#
|
178
212
|
# @return [Array<Pathname>]
|
179
213
|
def files_r
|
180
|
-
self.
|
214
|
+
self.find_files.to_a
|
181
215
|
end
|
182
216
|
|
183
|
-
#
|
184
|
-
# the Pathname.
|
185
|
-
#
|
186
|
-
#
|
217
|
+
# Iterates over all (recursive) descendent files of the directory
|
218
|
+
# indicated by the Pathname. Iterated Pathnames are prefixed by the
|
219
|
+
# original Pathname, and are in depth-first order.
|
220
|
+
#
|
221
|
+
# If no block is given, this method returns an Enumerator. Otherwise,
|
222
|
+
# the block is called with each descendent Pathname, and this method
|
223
|
+
# returns the original Pathname.
|
187
224
|
#
|
188
|
-
#
|
189
|
-
# returns the Pathname.
|
225
|
+
# @see https://docs.ruby-lang.org/en/trunk/Pathname.html#method-i-find Pathname#find
|
190
226
|
#
|
191
|
-
#
|
192
|
-
#
|
227
|
+
# @overload find_files()
|
228
|
+
# @return [Enumerator<Pathname>]
|
229
|
+
#
|
230
|
+
# @overload find_files(&block)
|
231
|
+
# @yieldparam descendent [Pathname]
|
232
|
+
# @return [Pathname]
|
233
|
+
def find_files
|
234
|
+
return to_enum(__method__) unless block_given?
|
235
|
+
|
236
|
+
self.find do |path|
|
237
|
+
yield path if path.file?
|
238
|
+
end
|
239
|
+
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
243
|
+
# Changes the current working directory to the Pathname. If no block
|
244
|
+
# is given, this method returns the Pathname. Otherwise, the block is
|
245
|
+
# called with the Pathname, the original working directory is restored
|
246
|
+
# after the block exits, this method returns the return value of the
|
247
|
+
# block.
|
193
248
|
#
|
194
|
-
#
|
249
|
+
# @see https://docs.ruby-lang.org/en/trunk/Dir.html#method-c-chdir Dir.chdir
|
195
250
|
#
|
196
251
|
# @example
|
197
252
|
# FileUtils.mkdir("dir1")
|
@@ -205,12 +260,14 @@ class Pathname
|
|
205
260
|
#
|
206
261
|
# @overload chdir()
|
207
262
|
# @return [Pathname]
|
208
|
-
#
|
209
|
-
#
|
210
|
-
# @
|
211
|
-
# @
|
263
|
+
#
|
264
|
+
# @overload chdir(&block)
|
265
|
+
# @yieldparam working_dir [Pathname]
|
266
|
+
# @yieldreturn [Object] retval
|
267
|
+
# @return [retval]
|
268
|
+
#
|
212
269
|
# @raise [SystemCallError]
|
213
|
-
# if the
|
270
|
+
# if the Pathname does not point to an existing directory
|
214
271
|
def chdir
|
215
272
|
if block_given?
|
216
273
|
Dir.chdir(self) do |dir|
|
@@ -222,7 +279,10 @@ class Pathname
|
|
222
279
|
end
|
223
280
|
end
|
224
281
|
|
225
|
-
#
|
282
|
+
# Creates the directory indicated by the Pathname, including any
|
283
|
+
# necessary parent directories. Returns the Pathname.
|
284
|
+
#
|
285
|
+
# @see https://docs.ruby-lang.org/en/trunk/Pathname.html#method-i-mkpath Pathname#mkpath
|
226
286
|
#
|
227
287
|
# @example
|
228
288
|
# Dir.exist?("path") # == false
|
@@ -233,14 +293,16 @@ class Pathname
|
|
233
293
|
# Dir.exist?("path") # == true
|
234
294
|
# Dir.exist?("path/to") # == true
|
235
295
|
#
|
236
|
-
# @return [
|
296
|
+
# @return [self]
|
297
|
+
# @raise [SystemCallError]
|
298
|
+
# if the Pathname points to an existing file (non-directory)
|
237
299
|
def make_dir
|
238
300
|
self.mkpath
|
239
301
|
self
|
240
302
|
end
|
241
303
|
|
242
|
-
# Creates the
|
243
|
-
#
|
304
|
+
# Creates the directory indicated by the Pathname +dirname+, including
|
305
|
+
# any necessary parent directories. Returns the Pathname.
|
244
306
|
#
|
245
307
|
# @example
|
246
308
|
# Dir.exist?("path") # == false
|
@@ -252,16 +314,44 @@ class Pathname
|
|
252
314
|
# Dir.exist?("path/to") # == true
|
253
315
|
# Dir.exist?("path/to/file") # == false
|
254
316
|
#
|
255
|
-
# @return [
|
317
|
+
# @return [self]
|
318
|
+
# @raise [SystemCallError]
|
319
|
+
# if any element of the +dirname+ points to an existing file
|
320
|
+
# (non-directory)
|
256
321
|
def make_dirname
|
257
322
|
self.dirname.make_dir
|
258
323
|
self
|
259
324
|
end
|
260
325
|
|
261
|
-
#
|
262
|
-
#
|
263
|
-
#
|
264
|
-
#
|
326
|
+
# Creates the file indicated by the Pathname, including any necessary
|
327
|
+
# parent directories. Returns the Pathname.
|
328
|
+
#
|
329
|
+
# @example
|
330
|
+
# Dir.exist?("path") # == false
|
331
|
+
# Dir.exist?("path/to") # == false
|
332
|
+
#
|
333
|
+
# Pathname.new("path/to/file").make_file # == Pathname.new("path/to/file")
|
334
|
+
#
|
335
|
+
# Dir.exist?("path") # == true
|
336
|
+
# Dir.exist?("path/to") # == true
|
337
|
+
# File.exist?("path/to/file") # == true
|
338
|
+
#
|
339
|
+
# @return [Pathname]
|
340
|
+
# @raise [SystemCallError]
|
341
|
+
# if the Pathname points to an existent directory
|
342
|
+
def make_file
|
343
|
+
self.make_dirname.open("a"){}
|
344
|
+
self
|
345
|
+
end
|
346
|
+
|
347
|
+
# @deprecated Use {Pathname#make_file}.
|
348
|
+
#
|
349
|
+
# Creates the file indicated by the Pathname, including any necessary
|
350
|
+
# parent directories. If the file already exists, its modification
|
351
|
+
# time (mtime) and access time (atime) are updated. Returns the
|
352
|
+
# Pathname.
|
353
|
+
#
|
354
|
+
# @see https://docs.ruby-lang.org/en/trunk/FileUtils.html#method-c-touch FileUtils.touch
|
265
355
|
#
|
266
356
|
# @example
|
267
357
|
# Dir.exist?("path") # == false
|
@@ -273,16 +363,16 @@ class Pathname
|
|
273
363
|
# Dir.exist?("path/to") # == true
|
274
364
|
# File.exist?("path/to/file") # == true
|
275
365
|
#
|
276
|
-
# @return [
|
366
|
+
# @return [self]
|
277
367
|
def touch_file
|
278
368
|
self.make_dirname
|
279
369
|
FileUtils.touch(self)
|
280
370
|
self
|
281
371
|
end
|
282
372
|
|
283
|
-
# Recursively deletes the directory or file indicated by the Pathname
|
284
|
-
#
|
285
|
-
#
|
373
|
+
# Recursively deletes the directory or file indicated by the Pathname.
|
374
|
+
# Similar to +Pathname#rmtree+, but does not raise an exception if the
|
375
|
+
# file does not exist. Returns the Pathname.
|
286
376
|
#
|
287
377
|
# @example
|
288
378
|
# File.exist?("path/to/file") # == true
|
@@ -293,30 +383,92 @@ class Pathname
|
|
293
383
|
# Dir.exist?("path/to") # == false
|
294
384
|
# File.exist?("path/to/file") # == false
|
295
385
|
#
|
296
|
-
# @return [
|
386
|
+
# @return [self]
|
297
387
|
def delete!
|
298
388
|
self.rmtree if self.exist?
|
299
389
|
self
|
300
390
|
end
|
301
391
|
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
392
|
+
# Finds an available name based on the Pathname. If the Pathname does
|
393
|
+
# not point to an existing file or directory, returns the Pathname.
|
394
|
+
# Otherwise, iteratively generates and tests names until one is found
|
395
|
+
# that does not point to an existing file or directory.
|
396
|
+
#
|
397
|
+
# Names are generated using a Hash-style format string with three
|
398
|
+
# populated values:
|
399
|
+
#
|
400
|
+
# * +%{name}+: original Pathname basename *without* extname
|
401
|
+
# * +%{ext}+: original Pathname extname, including leading dot
|
402
|
+
# * +%{i}+: iteration counter; can be initialized via +:i+ kwarg
|
403
|
+
#
|
404
|
+
# @example Incremental
|
405
|
+
# Pathname.new("dir/file.txt").available_name # == Pathname.new("dir/file.txt")
|
406
|
+
#
|
407
|
+
# FileUtils.mkdir("dir")
|
408
|
+
# FileUtils.touch("dir/file.txt")
|
409
|
+
#
|
410
|
+
# Pathname.new("dir/file.txt").available_name # == Pathname.new("dir/file_1.txt")
|
411
|
+
#
|
412
|
+
# FileUtils.touch("dir/file_1.txt")
|
413
|
+
# FileUtils.touch("dir/file_2.txt")
|
414
|
+
#
|
415
|
+
# Pathname.new("dir/file.txt").available_name # == Pathname.new("dir/file_3.txt")
|
416
|
+
#
|
417
|
+
# @example Specifying format
|
418
|
+
# FileUtils.touch("file.txt")
|
419
|
+
#
|
420
|
+
# Pathname.new("file.txt").available_name("%{name} (%{i})%{ext}")
|
421
|
+
# # == Pathname.new("file (1).txt")
|
422
|
+
#
|
423
|
+
# @example Specifying initial counter
|
424
|
+
# FileUtils.touch("file.txt")
|
425
|
+
#
|
426
|
+
# Pathname.new("file.txt").available_name(i: 0)
|
427
|
+
# # == Pathname.new("file_0.txt")
|
428
|
+
#
|
429
|
+
# @param format [String]
|
430
|
+
# @param i [Integer]
|
431
|
+
# @return [Pathname]
|
432
|
+
def available_name(format = "%{name}_%{i}%{ext}", i: 1)
|
433
|
+
return self unless self.exist?
|
434
|
+
|
435
|
+
dirname = File.dirname(self)
|
436
|
+
format = "%{dirname}/" + format unless dirname == "."
|
437
|
+
|
438
|
+
values = {
|
439
|
+
dirname: dirname,
|
440
|
+
name: File.basename(self, ".*"),
|
441
|
+
ext: self.extname,
|
442
|
+
i: i,
|
443
|
+
}
|
444
|
+
|
445
|
+
while (path = format % values) && File.exist?(path)
|
446
|
+
values[:i] += 1
|
447
|
+
end
|
448
|
+
|
449
|
+
path.to_pathname
|
450
|
+
end
|
451
|
+
|
452
|
+
# Moves the file or directory indicated by the Pathname to
|
453
|
+
# +destination+, in the same manner as +FileUtils.mv+. Creates any
|
454
|
+
# necessary parent directories of the destination. Returns
|
455
|
+
# +destination+ as a Pathname.
|
456
|
+
#
|
457
|
+
# @see https://docs.ruby-lang.org/en/trunk/FileUtils.html#method-c-mv FileUtils.mv
|
306
458
|
#
|
307
459
|
# @example
|
308
|
-
# File.exist?("path/to/file")
|
309
|
-
# Dir.exist?("
|
310
|
-
# Dir.exist?("
|
311
|
-
# File.exist?("
|
460
|
+
# File.exist?("path/to/file") # == true
|
461
|
+
# Dir.exist?("other") # == false
|
462
|
+
# Dir.exist?("other/dir") # == false
|
463
|
+
# File.exist?("other/dir/same_file") # == false
|
312
464
|
#
|
313
|
-
# Pathname.new("path/to/file").move("
|
314
|
-
# # == Pathname.new("
|
465
|
+
# Pathname.new("path/to/file").move("other/dir/same_file")
|
466
|
+
# # == Pathname.new("other/dir/same_file")
|
315
467
|
#
|
316
|
-
# File.exist?("path/to/file")
|
317
|
-
# Dir.exist?("
|
318
|
-
# Dir.exist?("
|
319
|
-
# File.exist?("
|
468
|
+
# File.exist?("path/to/file") # == false
|
469
|
+
# Dir.exist?("other") # == true
|
470
|
+
# Dir.exist?("other/dir") # == true
|
471
|
+
# File.exist?("other/dir/same_file") # == true
|
320
472
|
#
|
321
473
|
# @param destination [Pathname, String]
|
322
474
|
# @return [Pathname]
|
@@ -327,48 +479,189 @@ class Pathname
|
|
327
479
|
destination
|
328
480
|
end
|
329
481
|
|
330
|
-
# Moves the file or directory indicated by the Pathname
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
482
|
+
# Moves the file or directory indicated by the Pathname to a
|
483
|
+
# destination, replacing any existing file or directory.
|
484
|
+
#
|
485
|
+
# If a block is given and a file or directory does exist at the
|
486
|
+
# destination, the block is called with the source and destination
|
487
|
+
# Pathnames, and the return value of the block is used as the new
|
488
|
+
# destination. If the block returns the source Pathname or +nil+, the
|
489
|
+
# move is aborted.
|
490
|
+
#
|
491
|
+
# Creates any necessary parent directories of the destination.
|
492
|
+
# Returns the destination as a Pathname (or the source Pathname in the
|
493
|
+
# case that the move is aborted).
|
494
|
+
#
|
495
|
+
# *WARNING:* Due to system API limitations, the move is performed in
|
496
|
+
# two steps, non-atomically. First, any file or directory existing
|
497
|
+
# at the destination is deleted. Next, the source is moved to the
|
498
|
+
# destination. The second step can fail independently of the first,
|
499
|
+
# e.g. due to insufficient disk space, leaving the file or directory
|
500
|
+
# previously at the destination deleted without replacement.
|
501
|
+
#
|
502
|
+
# @example Without a block
|
503
|
+
# FileUtils.touch("file")
|
504
|
+
#
|
505
|
+
# Pathname.new("file").move_as("dir/file")
|
506
|
+
# # == Pathname.new("dir/file")
|
507
|
+
#
|
508
|
+
# File.exist?("file") # == false
|
509
|
+
# File.exist?("dir/file") # == true
|
510
|
+
#
|
511
|
+
# @example Error on older source
|
512
|
+
# FileUtils.touch("file")
|
513
|
+
# sleep 1
|
514
|
+
# FileUtils.touch("file.new")
|
515
|
+
#
|
516
|
+
# Pathname.new("file.new").move_as("file") do |source, destination|
|
517
|
+
# if source.mtime < destination.mtime
|
518
|
+
# raise "cannot replace newer file #{destination} with #{source}"
|
519
|
+
# end
|
520
|
+
# destination
|
521
|
+
# end # == Pathname.new("file")
|
522
|
+
#
|
523
|
+
# File.exist?("file.new") # == false
|
524
|
+
# File.exist?("file") # == true
|
525
|
+
#
|
526
|
+
# @example Abort on conflict
|
527
|
+
# FileUtils.touch("file1")
|
528
|
+
# FileUtils.touch("file2")
|
529
|
+
#
|
530
|
+
# Pathname.new("file1").move_as("file2") do |source, destination|
|
531
|
+
# puts "#{source} not moved to #{destination} due to conflict"
|
532
|
+
# nil
|
533
|
+
# end # == Pathname.new("file1")
|
534
|
+
#
|
535
|
+
# File.exist?("file1") # == true
|
536
|
+
# File.exist?("file2") # == true
|
537
|
+
#
|
538
|
+
# @example New destination on conflict
|
539
|
+
# FileUtils.touch("file1")
|
540
|
+
# FileUtils.touch("file2")
|
541
|
+
#
|
542
|
+
# Pathname.new("file1").move_as("file2") do |source, destination|
|
543
|
+
# destination.available_name
|
544
|
+
# end # == Pathname.new("file2_1")
|
545
|
+
#
|
546
|
+
# File.exist?("file1") # == false
|
547
|
+
# File.exist?("file2") # == true
|
548
|
+
# File.exist?("file2_1") # == true
|
549
|
+
#
|
550
|
+
# @overload move_as(destination)
|
551
|
+
# @param destination [Pathname, String]
|
552
|
+
# @return [Pathname]
|
342
553
|
#
|
343
|
-
#
|
344
|
-
#
|
345
|
-
#
|
346
|
-
#
|
554
|
+
# @overload move_as(destination, &block)
|
555
|
+
# @param destination [Pathname, String]
|
556
|
+
# @yieldparam source [Pathname]
|
557
|
+
# @yieldparam destination [Pathname]
|
558
|
+
# @yieldreturn [Pathname, nil]
|
559
|
+
# @return [Pathname]
|
560
|
+
def move_as(destination)
|
561
|
+
destination = destination.to_pathname
|
562
|
+
|
563
|
+
if block_given? && destination.exist? && self.exist? && !File.identical?(self, destination)
|
564
|
+
destination = (yield self, destination) || self
|
565
|
+
end
|
566
|
+
|
567
|
+
if destination != self
|
568
|
+
if File.identical?(self, destination)
|
569
|
+
# FileUtils.mv raises an ArgumentError when both paths refer to
|
570
|
+
# the same file. On case-insensitive file systems, this occurs
|
571
|
+
# even when both paths have different casing. We want to
|
572
|
+
# disregard the ArgumentError at all times, and change the
|
573
|
+
# filename casing when applicable.
|
574
|
+
File.rename(self, destination)
|
575
|
+
else
|
576
|
+
destination.delete!
|
577
|
+
self.move(destination)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
destination
|
582
|
+
end
|
583
|
+
|
584
|
+
# Moves the file or directory indicated by the Pathname into
|
585
|
+
# +directory+, replacing any existing file or directory of the same
|
586
|
+
# basename.
|
587
|
+
#
|
588
|
+
# If a block is given and a file or directory does exist at the
|
589
|
+
# resultant destination, the block is called with the source and
|
590
|
+
# destination Pathnames, and the return value of the block is used as
|
591
|
+
# the new destination. If the block returns the source Pathname or
|
592
|
+
# +nil+, the move is aborted.
|
593
|
+
#
|
594
|
+
# Creates any necessary parent directories of the destination.
|
595
|
+
# Returns the destination as a Pathname (or the source Pathname in the
|
596
|
+
# case that the move is aborted).
|
597
|
+
#
|
598
|
+
# *WARNING:* Due to system API limitations, the move is performed in
|
599
|
+
# two steps, non-atomically. First, any file or directory existing
|
600
|
+
# at the destination is deleted. Next, the source is moved to the
|
601
|
+
# destination. The second step can fail independently of the first,
|
602
|
+
# e.g. due to insufficient disk space, leaving the file or directory
|
603
|
+
# previously at the destination deleted without replacement.
|
604
|
+
#
|
605
|
+
# @see Pathname#move_as
|
606
|
+
#
|
607
|
+
# @example Without a block
|
608
|
+
# FileUtils.touch("file")
|
609
|
+
#
|
610
|
+
# Pathname.new("file").move_into("dir")
|
611
|
+
# # == Pathname.new("dir/file")
|
612
|
+
#
|
613
|
+
# File.exist?("file") # == false
|
614
|
+
# File.exist?("dir/file") # == true
|
615
|
+
#
|
616
|
+
# @example With a block
|
617
|
+
# FileUtils.mkpath("files")
|
618
|
+
# FileUtils.touch("files/file1")
|
619
|
+
# FileUtils.mkpath("dir/files")
|
620
|
+
# FileUtils.touch("dir/files/file2")
|
621
|
+
#
|
622
|
+
# Pathname.new("files").move_into("dir") do |source, destination|
|
623
|
+
# source # == Pathname.new("files")
|
624
|
+
# destination # == Pathname.new("dir/files")
|
625
|
+
# end # == Pathname.new("dir/files")
|
626
|
+
#
|
627
|
+
# Dir.exist?("files") # == false
|
628
|
+
# File.exist?("dir/files/file1") # == true
|
629
|
+
# File.exist?("dir/files/file2") # == false
|
630
|
+
#
|
631
|
+
# @overload move_into(directory)
|
632
|
+
# @param directory [Pathname, String]
|
633
|
+
# @return [Pathname]
|
347
634
|
#
|
348
|
-
# @
|
349
|
-
#
|
350
|
-
|
351
|
-
|
635
|
+
# @overload move_into(directory, &block)
|
636
|
+
# @param directory [Pathname, String]
|
637
|
+
# @yieldparam source [Pathname]
|
638
|
+
# @yieldparam destination [Pathname]
|
639
|
+
# @yieldreturn [Pathname, nil]
|
640
|
+
# @return [Pathname]
|
641
|
+
def move_into(directory, &block)
|
642
|
+
self.move_as(directory / self.basename, &block)
|
352
643
|
end
|
353
644
|
|
354
|
-
# Copies the file or directory indicated by the Pathname to
|
355
|
-
# destination
|
356
|
-
#
|
357
|
-
# +
|
645
|
+
# Copies the file or directory indicated by the Pathname to
|
646
|
+
# +destination+, in the same manner as +FileUtils.cp_r+. Creates any
|
647
|
+
# necessary parent directories of the destination. Returns
|
648
|
+
# +destination+ as a Pathname.
|
649
|
+
#
|
650
|
+
# @see https://docs.ruby-lang.org/en/trunk/FileUtils.html#method-c-cp_r FileUtils.cp_r
|
358
651
|
#
|
359
652
|
# @example
|
360
|
-
# File.exist?("path/to/file")
|
361
|
-
# Dir.exist?("
|
362
|
-
# Dir.exist?("
|
363
|
-
# File.exist?("
|
653
|
+
# File.exist?("path/to/file") # == true
|
654
|
+
# Dir.exist?("other") # == false
|
655
|
+
# Dir.exist?("other/dir") # == false
|
656
|
+
# File.exist?("other/dir/same_file") # == false
|
364
657
|
#
|
365
|
-
# Pathname.new("path/to/file").copy("
|
366
|
-
# # == Pathname.new("
|
658
|
+
# Pathname.new("path/to/file").copy("other/dir/same_file")
|
659
|
+
# # == Pathname.new("other/dir/same_file")
|
367
660
|
#
|
368
|
-
# File.exist?("path/to/file")
|
369
|
-
# Dir.exist?("
|
370
|
-
# Dir.exist?("
|
371
|
-
# File.exist?("
|
661
|
+
# File.exist?("path/to/file") # == true
|
662
|
+
# Dir.exist?("other") # == true
|
663
|
+
# Dir.exist?("other/dir") # == true
|
664
|
+
# File.exist?("other/dir/same_file") # == true
|
372
665
|
#
|
373
666
|
# @param destination [Pathname, String]
|
374
667
|
# @return [Pathname]
|
@@ -379,86 +672,311 @@ class Pathname
|
|
379
672
|
destination
|
380
673
|
end
|
381
674
|
|
382
|
-
# Copies the file or directory indicated by the Pathname
|
383
|
-
#
|
384
|
-
#
|
385
|
-
#
|
386
|
-
#
|
387
|
-
#
|
388
|
-
#
|
389
|
-
#
|
390
|
-
#
|
675
|
+
# Copies the file or directory indicated by the Pathname to a
|
676
|
+
# destination, replacing any existing file or directory.
|
677
|
+
#
|
678
|
+
# If a block is given and a file or directory does exist at the
|
679
|
+
# destination, the block is called with the source and destination
|
680
|
+
# Pathnames, and the return value of the block is used as the new
|
681
|
+
# destination. If the block returns the source Pathname or +nil+, the
|
682
|
+
# copy is aborted.
|
683
|
+
#
|
684
|
+
# Creates any necessary parent directories of the destination.
|
685
|
+
# Returns the destination as a Pathname (or the source Pathname in the
|
686
|
+
# case that the copy is aborted).
|
687
|
+
#
|
688
|
+
# *WARNING:* Due to system API limitations, the copy is performed in
|
689
|
+
# two steps, non-atomically. First, any file or directory existing
|
690
|
+
# at the destination is deleted. Next, the source is copied to the
|
691
|
+
# destination. The second step can fail independently of the first,
|
692
|
+
# e.g. due to insufficient disk space, leaving the file or directory
|
693
|
+
# previously at the destination deleted without replacement.
|
694
|
+
#
|
695
|
+
# @example Without a block
|
696
|
+
# FileUtils.touch("file")
|
697
|
+
#
|
698
|
+
# Pathname.new("file").copy_as("dir/file")
|
699
|
+
# # == Pathname.new("dir/file")
|
700
|
+
#
|
701
|
+
# File.exist?("file") # == true
|
702
|
+
# File.exist?("dir/file") # == true
|
703
|
+
#
|
704
|
+
# @example Error on older source
|
705
|
+
# File.write("file", "A")
|
706
|
+
# sleep 1
|
707
|
+
# File.write("file.new", "B")
|
708
|
+
#
|
709
|
+
# Pathname.new("file.new").copy_as("file") do |source, destination|
|
710
|
+
# if source.mtime < destination.mtime
|
711
|
+
# raise "cannot replace newer file #{destination} with #{source}"
|
712
|
+
# end
|
713
|
+
# destination
|
714
|
+
# end # == Pathname.new("file")
|
715
|
+
#
|
716
|
+
# File.read("file.new") # == "B"
|
717
|
+
# File.read("file") # == "B"
|
718
|
+
#
|
719
|
+
# @example Abort on conflict
|
720
|
+
# File.write("file1", "A")
|
721
|
+
# File.write("file2", "B")
|
722
|
+
#
|
723
|
+
# Pathname.new("file1").copy_as("file2") do |source, destination|
|
724
|
+
# puts "#{source} not copied to #{destination} due to conflict"
|
725
|
+
# nil
|
726
|
+
# end # == Pathname.new("file1")
|
727
|
+
#
|
728
|
+
# File.read("file1") # == "A"
|
729
|
+
# File.read("file2") # == "B"
|
730
|
+
#
|
731
|
+
# @example New destination on conflict
|
732
|
+
# File.write("file1", "A")
|
733
|
+
# File.write("file2", "B")
|
734
|
+
#
|
735
|
+
# Pathname.new("file1").copy_as("file2") do |source, destination|
|
736
|
+
# destination.available_name
|
737
|
+
# end # == Pathname.new("file2_1")
|
738
|
+
#
|
739
|
+
# File.read("file1") # == "A"
|
740
|
+
# File.read("file2") # == "B"
|
741
|
+
# File.read("file2_1") # == "A"
|
742
|
+
#
|
743
|
+
# @overload copy_as(destination)
|
744
|
+
# @param destination [Pathname, String]
|
745
|
+
# @return [Pathname]
|
391
746
|
#
|
392
|
-
#
|
393
|
-
#
|
747
|
+
# @overload copy_as(destination, &block)
|
748
|
+
# @param destination [Pathname, String]
|
749
|
+
# @yieldparam source [Pathname]
|
750
|
+
# @yieldparam destination [Pathname]
|
751
|
+
# @yieldreturn [Pathname, nil]
|
752
|
+
# @return [Pathname]
|
753
|
+
def copy_as(destination)
|
754
|
+
destination = destination.to_pathname
|
755
|
+
|
756
|
+
if block_given? && destination.exist? && self.exist? && !File.identical?(self, destination)
|
757
|
+
destination = yield self, destination
|
758
|
+
destination = nil if destination == self
|
759
|
+
end
|
760
|
+
|
761
|
+
if destination
|
762
|
+
destination.delete! unless File.identical?(self, destination)
|
763
|
+
self.copy(destination)
|
764
|
+
end
|
765
|
+
|
766
|
+
destination || self
|
767
|
+
end
|
768
|
+
|
769
|
+
|
770
|
+
# Copies the file or directory indicated by the Pathname into
|
771
|
+
# +directory+, replacing any existing file or directory of the same
|
772
|
+
# basename.
|
773
|
+
#
|
774
|
+
# If a block is given and a file or directory does exist at the
|
775
|
+
# resultant destination, the block is called with the source and
|
776
|
+
# destination Pathnames, and the return value of the block is used as
|
777
|
+
# the new destination. If the block returns the source Pathname or
|
778
|
+
# +nil+, the copy is aborted.
|
779
|
+
#
|
780
|
+
# Creates any necessary parent directories of the destination.
|
781
|
+
# Returns the destination as a Pathname (or the source Pathname in the
|
782
|
+
# case that the copy is aborted).
|
783
|
+
#
|
784
|
+
# *WARNING:* Due to system API limitations, the copy is performed in
|
785
|
+
# two steps, non-atomically. First, any file or directory existing
|
786
|
+
# at the destination is deleted. Next, the source is copied to the
|
787
|
+
# destination. The second step can fail independently of the first,
|
788
|
+
# e.g. due to insufficient disk space, leaving the file or directory
|
789
|
+
# previously at the destination deleted without replacement.
|
790
|
+
#
|
791
|
+
# @see Pathname#copy_as
|
792
|
+
#
|
793
|
+
# @example Without a block
|
794
|
+
# FileUtils.touch("file")
|
795
|
+
#
|
796
|
+
# Pathname.new("file").copy_into("dir")
|
797
|
+
# # == Pathname.new("dir/file")
|
798
|
+
#
|
799
|
+
# File.exist?("file") # == true
|
800
|
+
# File.exist?("dir/file") # == true
|
801
|
+
#
|
802
|
+
# @example With a block
|
803
|
+
# FileUtils.mkpath("files")
|
804
|
+
# FileUtils.touch("files/file1")
|
805
|
+
# FileUtils.mkpath("dir/files")
|
806
|
+
# FileUtils.touch("dir/files/file2")
|
807
|
+
#
|
808
|
+
# Pathname.new("files").copy_into("dir") do |source, destination|
|
809
|
+
# source # == Pathname.new("files")
|
810
|
+
# destination # == Pathname.new("dir/files")
|
811
|
+
# end # == Pathname.new("dir/files")
|
812
|
+
#
|
813
|
+
# File.exist?("files/file1") # == true
|
814
|
+
# File.exist?("dir/files/file1") # == true
|
815
|
+
# File.exist?("dir/files/file2") # == false
|
816
|
+
#
|
817
|
+
# @overload copy_into(directory)
|
818
|
+
# @param directory [Pathname, String]
|
819
|
+
# @return [Pathname]
|
394
820
|
#
|
395
|
-
#
|
396
|
-
#
|
397
|
-
#
|
398
|
-
#
|
821
|
+
# @overload copy_into(directory, &block)
|
822
|
+
# @param directory [Pathname, String]
|
823
|
+
# @yieldparam source [Pathname]
|
824
|
+
# @yieldparam destination [Pathname]
|
825
|
+
# @yieldreturn [Pathname, nil]
|
826
|
+
# @return [Pathname]
|
827
|
+
def copy_into(directory, &block)
|
828
|
+
self.copy_as(directory / self.basename, &block)
|
829
|
+
end
|
830
|
+
|
831
|
+
# Alias of +Pathname#move_as+.
|
399
832
|
#
|
400
|
-
# @param directory [Pathname, String]
|
401
833
|
# @return [Pathname]
|
402
|
-
|
403
|
-
self.copy(directory / self.basename)
|
404
|
-
end
|
834
|
+
alias :rename_as :move_as
|
405
835
|
|
406
|
-
# Renames the file or directory indicated by the Pathname
|
407
|
-
#
|
408
|
-
#
|
836
|
+
# Renames the file or directory indicated by the Pathname relative to
|
837
|
+
# its +dirname+, replacing any existing file or directory of the same
|
838
|
+
# basename.
|
409
839
|
#
|
410
|
-
#
|
411
|
-
#
|
840
|
+
# If a block is given and a file or directory does exist at the
|
841
|
+
# resultant destination, the block is called with the source and
|
842
|
+
# destination Pathnames, and the return value of the block is used as
|
843
|
+
# the new destination. If the block returns the source Pathname or
|
844
|
+
# +nil+, the rename is aborted.
|
412
845
|
#
|
413
|
-
#
|
414
|
-
#
|
846
|
+
# Returns the destination as a Pathname (or the source Pathname in the
|
847
|
+
# case that the rename is aborted).
|
415
848
|
#
|
416
|
-
#
|
417
|
-
#
|
849
|
+
# *WARNING:* Due to system API limitations, the rename is performed in
|
850
|
+
# two steps, non-atomically. First, any file or directory existing
|
851
|
+
# at the destination is deleted. Next, the source is moved to the
|
852
|
+
# destination. The second step can fail independently of the first,
|
853
|
+
# e.g. due to insufficient disk space, leaving the file or directory
|
854
|
+
# previously at the destination deleted without replacement.
|
418
855
|
#
|
419
|
-
# @
|
420
|
-
#
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
856
|
+
# @see Pathname#move_as
|
857
|
+
#
|
858
|
+
# @example Without a block
|
859
|
+
# FileUtils.mkpath("dir")
|
860
|
+
# FileUtils.touch("dir/file")
|
861
|
+
#
|
862
|
+
# Pathname.new("dir/file").rename_basename("same_file")
|
863
|
+
# # == Pathname.new("dir/same_file")
|
864
|
+
#
|
865
|
+
# File.exist?("dir/file") # == false
|
866
|
+
# File.exist?("dir/same_file") # == true
|
867
|
+
#
|
868
|
+
# @example With a block
|
869
|
+
# FileUtils.mkpath("dir")
|
870
|
+
# FileUtils.touch("dir/file1")
|
871
|
+
# FileUtils.touch("dir/file2")
|
872
|
+
#
|
873
|
+
# Pathname.new("dir/file1").rename_basename("file2") do |source, destination|
|
874
|
+
# source # == Pathname.new("dir/file1")
|
875
|
+
# destination # == Pathname.new("dir/file2")
|
876
|
+
# end # == Pathname.new("dir/file2")
|
877
|
+
#
|
878
|
+
# File.exist?("dir/file1") # == false
|
879
|
+
# File.exist?("dir/file2") # == true
|
880
|
+
#
|
881
|
+
# @overload rename_basename(new_basename)
|
882
|
+
# @param new_basename [Pathname, String]
|
883
|
+
# @return [Pathname]
|
884
|
+
#
|
885
|
+
# @overload rename_basename(new_basename, &block)
|
886
|
+
# @param new_basename [Pathname, String]
|
887
|
+
# @yieldparam source [Pathname]
|
888
|
+
# @yieldparam destination [Pathname]
|
889
|
+
# @yieldreturn [Pathname, nil]
|
890
|
+
# @return [Pathname]
|
891
|
+
def rename_basename(new_basename, &block)
|
892
|
+
self.move_as(self.dirname / new_basename, &block)
|
425
893
|
end
|
426
894
|
|
427
|
-
#
|
428
|
-
#
|
895
|
+
# Changes the file extension (+extname+) of the file indicated by the
|
896
|
+
# Pathname, replacing any existing file or directory of the same
|
897
|
+
# resultant basename.
|
429
898
|
#
|
430
|
-
#
|
431
|
-
#
|
899
|
+
# If a block is given and a file or directory does exist at the
|
900
|
+
# resultant destination, the block is called with the source and
|
901
|
+
# destination Pathnames, and the return value of the block is used as
|
902
|
+
# the new destination. If the block returns the source Pathname or
|
903
|
+
# +nil+, the rename is aborted.
|
432
904
|
#
|
433
|
-
#
|
434
|
-
#
|
905
|
+
# Returns the destination as a Pathname (or the source Pathname in the
|
906
|
+
# case that the rename is aborted).
|
435
907
|
#
|
436
|
-
#
|
437
|
-
#
|
908
|
+
# *WARNING:* Due to system API limitations, the rename is performed in
|
909
|
+
# two steps, non-atomically. First, any file or directory existing
|
910
|
+
# at the destination is deleted. Next, the source is moved to the
|
911
|
+
# destination. The second step can fail independently of the first,
|
912
|
+
# e.g. due to insufficient disk space, leaving the file or directory
|
913
|
+
# previously at the destination deleted without replacement.
|
438
914
|
#
|
439
|
-
# @
|
440
|
-
# File.exist?("path/to/file.abc") # == true
|
915
|
+
# @see Pathname#move_as
|
441
916
|
#
|
442
|
-
#
|
443
|
-
#
|
917
|
+
# @example Replace extension
|
918
|
+
# FileUtils.mkpath("dir")
|
919
|
+
# FileUtils.touch("dir/file.abc")
|
444
920
|
#
|
445
|
-
#
|
446
|
-
#
|
921
|
+
# Pathname.new("dir/file.abc").rename_extname(".xyz")
|
922
|
+
# # == Pathname.new("dir/file.xyz")
|
447
923
|
#
|
448
|
-
#
|
449
|
-
#
|
450
|
-
|
924
|
+
# File.exist?("dir/file.abc") # == false
|
925
|
+
# File.exist?("dir/file.xyz") # == true
|
926
|
+
#
|
927
|
+
# @example Add extension
|
928
|
+
# FileUtils.mkpath("dir")
|
929
|
+
# FileUtils.touch("dir/file")
|
930
|
+
#
|
931
|
+
# Pathname.new("dir/file").rename_extname(".abc")
|
932
|
+
# # == Pathname.new("dir/file.abc")
|
933
|
+
#
|
934
|
+
# File.exist?("dir/file") # == false
|
935
|
+
# File.exist?("dir/file.abc") # == true
|
936
|
+
#
|
937
|
+
# @example Remove extension
|
938
|
+
# FileUtils.mkpath("dir")
|
939
|
+
# FileUtils.touch("dir/file.abc")
|
940
|
+
#
|
941
|
+
# Pathname.new("dir/file.abc").rename_extname("")
|
942
|
+
# # == Pathname.new("dir/file")
|
943
|
+
#
|
944
|
+
# File.exist?("dir/file.abc") # == false
|
945
|
+
# File.exist?("dir/file") # == true
|
946
|
+
#
|
947
|
+
# @example With a block
|
948
|
+
# FileUtils.mkpath("dir")
|
949
|
+
# FileUtils.touch("dir/file.abc")
|
950
|
+
# FileUtils.touch("dir/file.xyz")
|
951
|
+
#
|
952
|
+
# Pathname.new("dir/file.abc").rename_extname(".xyz") do |source, destination|
|
953
|
+
# source # == Pathname.new("dir/file.abc")
|
954
|
+
# destination # == Pathname.new("dir/file.xyz")
|
955
|
+
# end # == Pathname.new("dir/file.xyz")
|
956
|
+
#
|
957
|
+
# File.exist?("dir/file.abc") # == false
|
958
|
+
# File.exist?("dir/file.xyz") # == true
|
959
|
+
#
|
960
|
+
# @overload rename_extname(new_extname)
|
961
|
+
# @param new_extname [String]
|
962
|
+
# @return [Pathname]
|
963
|
+
#
|
964
|
+
# @overload rename_extname(new_extname, &block)
|
965
|
+
# @param new_extname [String]
|
966
|
+
# @yieldparam source [Pathname]
|
967
|
+
# @yieldparam destination [Pathname]
|
968
|
+
# @yieldreturn [Pathname, nil]
|
969
|
+
# @return [Pathname]
|
970
|
+
def rename_extname(new_extname, &block)
|
451
971
|
unless new_extname.start_with?(".") || new_extname.empty?
|
452
972
|
new_extname = ".#{new_extname}"
|
453
973
|
end
|
454
|
-
|
455
|
-
self.rename(new_path)
|
456
|
-
new_path
|
974
|
+
self.move_as(self.sub_ext(new_extname), &block)
|
457
975
|
end
|
458
976
|
|
459
|
-
# Writes
|
460
|
-
#
|
461
|
-
# necessary parent directories
|
977
|
+
# Writes +text+ to the file indicated by the Pathname, overwriting the
|
978
|
+
# file if it exists. Creates the file if it does not exist, including
|
979
|
+
# any necessary parent directories. Returns the Pathname.
|
462
980
|
#
|
463
981
|
# @example
|
464
982
|
# Dir.exist?("path") # == false
|
@@ -471,15 +989,15 @@ class Pathname
|
|
471
989
|
# File.read("path/to/file") # == "hello world"
|
472
990
|
#
|
473
991
|
# @param text [String]
|
474
|
-
# @return [
|
992
|
+
# @return [self]
|
475
993
|
def write_text(text)
|
476
994
|
self.make_dirname.open("w"){|f| f.write(text) }
|
477
995
|
self
|
478
996
|
end
|
479
997
|
|
480
|
-
# Appends
|
481
|
-
#
|
482
|
-
#
|
998
|
+
# Appends +text+ to the file indicated by the Pathname. Creates the
|
999
|
+
# file if it does not exist, including any necessary parent
|
1000
|
+
# directories. Returns the Pathname.
|
483
1001
|
#
|
484
1002
|
# @example
|
485
1003
|
# Dir.exist?("path") # == false
|
@@ -492,16 +1010,16 @@ class Pathname
|
|
492
1010
|
# File.read("path/to/file") # == "hello world"
|
493
1011
|
#
|
494
1012
|
# @param text [String]
|
495
|
-
# @return [
|
1013
|
+
# @return [self]
|
496
1014
|
def append_text(text)
|
497
1015
|
self.make_dirname.open("a"){|f| f.write(text) }
|
498
1016
|
self
|
499
1017
|
end
|
500
1018
|
|
501
|
-
# Writes each object as a string plus
|
502
|
-
#
|
503
|
-
#
|
504
|
-
# necessary parent directories
|
1019
|
+
# Writes each object in +lines+ as a string plus end-of-line (EOL)
|
1020
|
+
# characters to the file indicated by the Pathname, overwriting the
|
1021
|
+
# file if it exists. Creates the file if it does not exist, including
|
1022
|
+
# any necessary parent directories. Returns the Pathname.
|
505
1023
|
#
|
506
1024
|
# @example
|
507
1025
|
# File.exist?("path/to/file") # false
|
@@ -512,16 +1030,17 @@ class Pathname
|
|
512
1030
|
# File.read("path/to/file") # == "one\ntwo\n"
|
513
1031
|
#
|
514
1032
|
# @param lines [Enumerable<#to_s>]
|
515
|
-
# @
|
516
|
-
|
517
|
-
|
1033
|
+
# @param eol [String]
|
1034
|
+
# @return [self]
|
1035
|
+
def write_lines(lines, eol: $/)
|
1036
|
+
self.make_dirname.open("w"){|f| f.write_lines(lines, eol: eol) }
|
518
1037
|
self
|
519
1038
|
end
|
520
1039
|
|
521
|
-
# Appends each object as a string plus
|
522
|
-
#
|
523
|
-
#
|
524
|
-
#
|
1040
|
+
# Appends each object in +lines+ as a string plus end-of-line (EOL)
|
1041
|
+
# characters to the file indicated by the Pathname. Creates the file
|
1042
|
+
# if it does not exist, including any necessary parent directories.
|
1043
|
+
# Returns the Pathname.
|
525
1044
|
#
|
526
1045
|
# @example
|
527
1046
|
# File.exist?("path/to/file") # false
|
@@ -532,9 +1051,10 @@ class Pathname
|
|
532
1051
|
# File.read("path/to/file") # == "one\ntwo\nthree\nfour\n"
|
533
1052
|
#
|
534
1053
|
# @param lines [Enumerable<#to_s>]
|
535
|
-
# @
|
536
|
-
|
537
|
-
|
1054
|
+
# @param eol [String]
|
1055
|
+
# @return [self]
|
1056
|
+
def append_lines(lines, eol: $/)
|
1057
|
+
self.make_dirname.open("a"){|f| f.write_lines(lines, eol: eol) }
|
538
1058
|
self
|
539
1059
|
end
|
540
1060
|
|
@@ -543,31 +1063,33 @@ class Pathname
|
|
543
1063
|
# @return [String]
|
544
1064
|
alias :read_text :read
|
545
1065
|
|
546
|
-
# Reads from the file indicated by the Pathname
|
547
|
-
# them
|
548
|
-
#
|
549
|
-
#
|
1066
|
+
# Reads all lines from the file indicated by the Pathname, and returns
|
1067
|
+
# them with all end-of-line (EOL) characters stripped.
|
1068
|
+
#
|
1069
|
+
# @see IO#read_lines
|
550
1070
|
#
|
551
|
-
#
|
552
|
-
#
|
1071
|
+
# @note Not to be confused with +Pathname#readlines+, which retains
|
1072
|
+
# end-of-line (EOL) characters.
|
553
1073
|
#
|
554
1074
|
# @example
|
555
1075
|
# File.read("path/to/file") # == "one\ntwo\n"
|
556
1076
|
#
|
557
1077
|
# Pathname.new("path/to/file").read_lines # == ["one", "two"]
|
558
1078
|
#
|
1079
|
+
# @param eol [String]
|
559
1080
|
# @return [Array<String>]
|
560
|
-
def read_lines
|
561
|
-
self.open("r"){|f| f.read_lines }
|
1081
|
+
def read_lines(eol: $/)
|
1082
|
+
self.open("r"){|f| f.read_lines(eol: eol) }
|
562
1083
|
end
|
563
1084
|
|
564
|
-
# Reads the contents of the file indicated by the Pathname
|
565
|
-
#
|
1085
|
+
# Reads the entire contents of the file indicated by the Pathname as a
|
1086
|
+
# string, and yields that string to the given block for editing.
|
566
1087
|
# Writes the return value of the block back to the file, overwriting
|
567
|
-
# previous contents. Returns the
|
568
|
-
#
|
1088
|
+
# previous contents. Returns the return value of the block.
|
1089
|
+
#
|
1090
|
+
# @see File.edit_text
|
569
1091
|
#
|
570
|
-
# @example
|
1092
|
+
# @example Update JSON data file
|
571
1093
|
# File.read("data.json") # == '{"nested":{"key":"value"}}'
|
572
1094
|
#
|
573
1095
|
# Pathname.new("data.json").edit_text do |text|
|
@@ -578,23 +1100,24 @@ class Pathname
|
|
578
1100
|
#
|
579
1101
|
# File.read("data.json") # == '{"nested":{"key":"new value"}}'
|
580
1102
|
#
|
581
|
-
# @yield [text]
|
582
|
-
# @yieldparam text [String]
|
583
|
-
# @yieldreturn [String]
|
1103
|
+
# @yield [text]
|
1104
|
+
# @yieldparam text [String]
|
1105
|
+
# @yieldreturn [String]
|
584
1106
|
# @return [String]
|
585
1107
|
def edit_text(&block)
|
586
1108
|
File.edit_text(self, &block)
|
587
1109
|
end
|
588
1110
|
|
589
|
-
# Reads the contents of the file indicated by the Pathname
|
590
|
-
#
|
1111
|
+
# Reads the entire contents of the file indicated by the Pathname as
|
1112
|
+
# an array of lines, and yields that array to the given block for
|
591
1113
|
# editing. Writes the return value of the block back to the file,
|
592
|
-
# overwriting previous contents.
|
593
|
-
#
|
594
|
-
#
|
595
|
-
# contents. See also {File.edit_lines}.
|
1114
|
+
# overwriting previous contents. End-of-line (EOL) characters are
|
1115
|
+
# stripped when reading, and appended after each line when writing.
|
1116
|
+
# Returns the return value of the block.
|
596
1117
|
#
|
597
|
-
# @
|
1118
|
+
# @see File.edit_lines
|
1119
|
+
#
|
1120
|
+
# @example Dedup lines of file
|
598
1121
|
# File.read("entries.txt") # == "AAA\nBBB\nBBB\nCCC\nAAA\n"
|
599
1122
|
#
|
600
1123
|
# Pathname.new("entries.txt").edit_lines(&:uniq)
|
@@ -602,16 +1125,17 @@ class Pathname
|
|
602
1125
|
#
|
603
1126
|
# File.read("entries.txt") # == "AAA\nBBB\nCCC\n"
|
604
1127
|
#
|
605
|
-
# @
|
606
|
-
# @
|
607
|
-
# @
|
1128
|
+
# @param eol [String]
|
1129
|
+
# @yield [lines]
|
1130
|
+
# @yieldparam lines [Array<String>]
|
1131
|
+
# @yieldreturn [Array<String>]
|
608
1132
|
# @return [Array<String>]
|
609
|
-
def edit_lines(&block)
|
610
|
-
File.edit_lines(self, &block)
|
1133
|
+
def edit_lines(eol: $/, &block)
|
1134
|
+
File.edit_lines(self, eol: eol, &block)
|
611
1135
|
end
|
612
1136
|
|
613
|
-
# Appends the contents of
|
614
|
-
# Pathname. Returns the
|
1137
|
+
# Appends the contents of file indicated by +source+ to the file
|
1138
|
+
# indicated by the Pathname. Returns the Pathname.
|
615
1139
|
#
|
616
1140
|
# @example
|
617
1141
|
# File.read("yearly.log") # == "one\ntwo\n"
|
@@ -623,7 +1147,7 @@ class Pathname
|
|
623
1147
|
# File.read("yearly.log") # == "one\ntwo\nthree\nfour\n"
|
624
1148
|
#
|
625
1149
|
# @param source [String, Pathname]
|
626
|
-
# @return [
|
1150
|
+
# @return [self]
|
627
1151
|
def append_file(source)
|
628
1152
|
self.open("a"){|destination| IO::copy_stream(source, destination) }
|
629
1153
|
self
|