pleasant_path 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|