pleasant_path 1.0.0 → 1.1.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/README.md +97 -47
- data/lib/pleasant_path/array.rb +10 -0
- data/lib/pleasant_path/file.rb +43 -0
- data/lib/pleasant_path/io.rb +21 -5
- data/lib/pleasant_path/json/object.rb +27 -0
- data/lib/pleasant_path/json/pathname.rb +58 -0
- data/lib/pleasant_path/json.rb +4 -0
- data/lib/pleasant_path/pathname.rb +341 -32
- data/lib/pleasant_path/string.rb +20 -4
- data/lib/pleasant_path/version.rb +1 -1
- data/lib/pleasant_path/yaml/object.rb +22 -0
- data/lib/pleasant_path/yaml/pathname.rb +36 -0
- data/lib/pleasant_path/yaml.rb +4 -0
- data/lib/pleasant_path.rb +7 -7
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddfbea700d9c72193add267fe9a09e88bc5d99ff
|
4
|
+
data.tar.gz: 7b5bce70e3faad80ac3296767a6cbd8b398a454e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdf0eb05a7e27313ec2f0a9016319952c65a0931371b575fa4b72dd8959efc124db7272d3fd4c2d9bc35bde095839594110ce52be48de2ec467a6439f1b0a9bc
|
7
|
+
data.tar.gz: e67312af4aa40ed25954d05979983fe45a38f727c9810184e2a63eb45e7b6fb0c340ea5d765c10848143ad46cdccdd01ebefa199d06cbc576cc35fd0ae3df195
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# pleasant_path
|
2
2
|
|
3
3
|
A [fluent API] for pleasant file IO, written as extensions to core Ruby
|
4
|
-
objects. See
|
4
|
+
objects. See API listing below, or browse the [full documentation].
|
5
5
|
|
6
6
|
[fluent API]: https://en.wikipedia.org/wiki/Fluent_interface
|
7
7
|
[full documentation]: http://www.rubydoc.info/gems/pleasant_path/
|
@@ -10,65 +10,115 @@ objects. See method listing below or browse the [full documentation].
|
|
10
10
|
## Examples
|
11
11
|
|
12
12
|
```ruby
|
13
|
-
# Dedup lines in a file
|
14
|
-
"line_items.txt".path.edit_lines(&:uniq)
|
15
|
-
|
16
13
|
# Filter lines across multiple files
|
17
14
|
"logs/*.txt".glob.each do |log|
|
18
|
-
log.read_lines.grep(/
|
15
|
+
log.read_lines.grep(/^ERROR /).append_to_file("errors.txt")
|
19
16
|
end
|
17
|
+
|
18
|
+
# Dedup lines in a file
|
19
|
+
"names.txt".path.edit_lines(&:uniq)
|
20
20
|
```
|
21
21
|
|
22
22
|
|
23
|
-
## API
|
23
|
+
## Core API
|
24
|
+
|
25
|
+
The following methods are available:
|
26
|
+
|
27
|
+
- [Pathname](http://www.rubydoc.info/gems/pleasant_path/Pathname)
|
28
|
+
- [::NULL](http://www.rubydoc.info/gems/pleasant_path/Pathname#NULL-constant)
|
29
|
+
- [#^](http://www.rubydoc.info/gems/pleasant_path/Pathname:%5E)
|
30
|
+
- [#append_file](http://www.rubydoc.info/gems/pleasant_path/Pathname:append_file)
|
31
|
+
- [#append_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname:append_lines)
|
32
|
+
- [#append_text](http://www.rubydoc.info/gems/pleasant_path/Pathname:append_text)
|
33
|
+
- [#common_path](http://www.rubydoc.info/gems/pleasant_path/Pathname:common_path)
|
34
|
+
- [#copy](http://www.rubydoc.info/gems/pleasant_path/Pathname:copy)
|
35
|
+
- [#copy_into](http://www.rubydoc.info/gems/pleasant_path/Pathname:copy_into)
|
36
|
+
- [#delete!](http://www.rubydoc.info/gems/pleasant_path/Pathname:delete%21)
|
37
|
+
- [#dir?](http://www.rubydoc.info/gems/pleasant_path/Pathname:dir%3F)
|
38
|
+
- [#dir_empty?](http://www.rubydoc.info/gems/pleasant_path/Pathname:dir_empty%3F)
|
39
|
+
- [#dirs](http://www.rubydoc.info/gems/pleasant_path/Pathname:dirs)
|
40
|
+
- [#dirs_r](http://www.rubydoc.info/gems/pleasant_path/Pathname:dirs_r)
|
41
|
+
- [#edit_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname:edit_lines)
|
42
|
+
- [#edit_text](http://www.rubydoc.info/gems/pleasant_path/Pathname:edit_text)
|
43
|
+
- [#files](http://www.rubydoc.info/gems/pleasant_path/Pathname:files)
|
44
|
+
- [#files_r](http://www.rubydoc.info/gems/pleasant_path/Pathname:files_r)
|
45
|
+
- [#make_dir](http://www.rubydoc.info/gems/pleasant_path/Pathname:make_dir)
|
46
|
+
- [#make_dirname](http://www.rubydoc.info/gems/pleasant_path/Pathname:make_dirname)
|
47
|
+
- [#move](http://www.rubydoc.info/gems/pleasant_path/Pathname:move)
|
48
|
+
- [#move_into](http://www.rubydoc.info/gems/pleasant_path/Pathname:move_into)
|
49
|
+
- [#parentname](http://www.rubydoc.info/gems/pleasant_path/Pathname:parentname)
|
50
|
+
- [#read_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname:read_lines)
|
51
|
+
- [#rename_basename](http://www.rubydoc.info/gems/pleasant_path/Pathname:rename_basename)
|
52
|
+
- [#rename_extname](http://www.rubydoc.info/gems/pleasant_path/Pathname:rename_extname)
|
53
|
+
- [#to_pathname](http://www.rubydoc.info/gems/pleasant_path/Pathname:to_pathname)
|
54
|
+
- [#touch_file](http://www.rubydoc.info/gems/pleasant_path/Pathname:touch_file)
|
55
|
+
- [#write_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname:write_lines)
|
56
|
+
- [#write_text](http://www.rubydoc.info/gems/pleasant_path/Pathname:write_text)
|
57
|
+
- [String](http://www.rubydoc.info/gems/pleasant_path/String)
|
58
|
+
- [#/](http://www.rubydoc.info/gems/pleasant_path/String:%2F)
|
59
|
+
- [#^](http://www.rubydoc.info/gems/pleasant_path/String:%5E)
|
60
|
+
- [#append_to_file](http://www.rubydoc.info/gems/pleasant_path/String:append_to_file)
|
61
|
+
- [#glob](http://www.rubydoc.info/gems/pleasant_path/String:glob)
|
62
|
+
- [#path](http://www.rubydoc.info/gems/pleasant_path/String:path)
|
63
|
+
- [#to_pathname](http://www.rubydoc.info/gems/pleasant_path/String:to_pathname)
|
64
|
+
- [#write_to_file](http://www.rubydoc.info/gems/pleasant_path/String:write_to_file)
|
65
|
+
- [Array](http://www.rubydoc.info/gems/pleasant_path/Array)
|
66
|
+
- [#append_to_file](http://www.rubydoc.info/gems/pleasant_path/Array:append_to_file)
|
67
|
+
- [#write_to_file](http://www.rubydoc.info/gems/pleasant_path/Array:write_to_file)
|
68
|
+
- [File](http://www.rubydoc.info/gems/pleasant_path/File)
|
69
|
+
- [.common_path](http://www.rubydoc.info/gems/pleasant_path/File.common_path)
|
70
|
+
- [.edit_lines](http://www.rubydoc.info/gems/pleasant_path/File.edit_lines)
|
71
|
+
- [.edit_text](http://www.rubydoc.info/gems/pleasant_path/File.edit_text)
|
72
|
+
- [IO](http://www.rubydoc.info/gems/pleasant_path/IO)
|
73
|
+
- [#read_lines](http://www.rubydoc.info/gems/pleasant_path/IO:read_lines)
|
74
|
+
- [#write_lines](http://www.rubydoc.info/gems/pleasant_path/IO:write_lines)
|
75
|
+
|
76
|
+
|
77
|
+
## JSON-related and YAML-related API
|
78
|
+
|
79
|
+
*pleasant_path* also includes methods for interacting with JSON and YAML
|
80
|
+
files, using the [JSON module] and [YAML module] that are part of Ruby's
|
81
|
+
standard library. Because Ruby does not load these modules by default,
|
82
|
+
*pleasant_path* does not load its JSON-related and YAML-related API by
|
83
|
+
default either. To load these *pleasant_path* APIs **and** the relevant
|
84
|
+
standard library modules, use:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
require "pleasant_path/json"
|
88
|
+
require "pleasant_path/yaml"
|
89
|
+
```
|
90
|
+
|
91
|
+
[JSON module]: https://ruby-doc.org/stdlib/libdoc/json/rdoc/JSON.html
|
92
|
+
[YAML module]: https://ruby-doc.org/stdlib/libdoc/yaml/rdoc/YAML.html
|
24
93
|
|
94
|
+
The following methods are available:
|
95
|
+
|
96
|
+
- Object
|
97
|
+
- [write_to_json](http://www.rubydoc.info/gems/pleasant_path/Object:write_to_json)
|
98
|
+
- [write_to_yaml](http://www.rubydoc.info/gems/pleasant_path/Object:write_to_yaml)
|
25
99
|
- Pathname
|
26
|
-
- [
|
27
|
-
- [
|
28
|
-
- [
|
29
|
-
- [
|
30
|
-
- [delete!](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Adelete%21)
|
31
|
-
- [dir_empty?](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Adir_empty%3F)
|
32
|
-
- [dirs](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Adirs)
|
33
|
-
- [dirs_r](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Adirs_r)
|
34
|
-
- [edit_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Aedit_lines)
|
35
|
-
- [edit_text](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Aedit_text)
|
36
|
-
- [files](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Afiles)
|
37
|
-
- [files_r](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Afiles_r)
|
38
|
-
- [make_dir](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Amake_dir)
|
39
|
-
- [make_dirname](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Amake_dirname)
|
40
|
-
- [move](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Amove)
|
41
|
-
- [move_into](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Amove_into)
|
42
|
-
- [parentname](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Aparentname)
|
43
|
-
- [read_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Aread_lines)
|
44
|
-
- [to_pathname](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Ato_pathname)
|
45
|
-
- [touch_file](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Atouch_file)
|
46
|
-
- [write_lines](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Awrite_lines)
|
47
|
-
- [write_text](http://www.rubydoc.info/gems/pleasant_path/Pathname%3Awrite_text)
|
48
|
-
- String
|
49
|
-
- [/](http://www.rubydoc.info/gems/pleasant_path/String%3A%2F)
|
50
|
-
- [^](http://www.rubydoc.info/gems/pleasant_path/String%3A%5E)
|
51
|
-
- [append_to_file](http://www.rubydoc.info/gems/pleasant_path/String%3Aappend_to_file)
|
52
|
-
- [glob](http://www.rubydoc.info/gems/pleasant_path/String%3Aglob)
|
53
|
-
- [to_pathname](http://www.rubydoc.info/gems/pleasant_path/String%3Ato_pathname)
|
54
|
-
- [write_to_file](http://www.rubydoc.info/gems/pleasant_path/String%3Awrite_to_file)
|
55
|
-
- Array
|
56
|
-
- [append_to_file](http://www.rubydoc.info/gems/pleasant_path/Array%3Aappend_to_file)
|
57
|
-
- [write_to_file](http://www.rubydoc.info/gems/pleasant_path/Array%3Awrite_to_file)
|
58
|
-
- File
|
59
|
-
- [edit_lines](http://www.rubydoc.info/gems/pleasant_path/File.edit_lines)
|
60
|
-
- [edit_text](http://www.rubydoc.info/gems/pleasant_path/File.edit_text)
|
61
|
-
- IO
|
62
|
-
- [read_lines](http://www.rubydoc.info/gems/pleasant_path/IO%3Aread_lines)
|
63
|
-
- [write_lines](http://www.rubydoc.info/gems/pleasant_path/IO%3Awrite_lines)
|
100
|
+
- [load_json](http://www.rubydoc.info/gems/pleasant_path/Pathname:load_json)
|
101
|
+
- [load_yaml](http://www.rubydoc.info/gems/pleasant_path/Pathname:load_yaml)
|
102
|
+
- [read_json](http://www.rubydoc.info/gems/pleasant_path/Pathname:read_json)
|
103
|
+
- [read_yaml](http://www.rubydoc.info/gems/pleasant_path/Pathname:read_yaml)
|
64
104
|
|
65
105
|
|
66
106
|
## Installation
|
67
107
|
|
68
|
-
|
108
|
+
Install from [Ruby Gems](https://rubygems.org/gems/pleasant_path):
|
109
|
+
|
110
|
+
```bash
|
111
|
+
$ gem install pleasant_path
|
112
|
+
```
|
113
|
+
|
114
|
+
Then require in your Ruby script:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
require "pleasant_path"
|
118
|
+
```
|
69
119
|
|
70
120
|
|
71
|
-
##
|
121
|
+
## Contributing
|
72
122
|
|
73
123
|
Run `rake test` to run the tests. You can also run `rake irb` for an
|
74
124
|
interactive prompt that pre-loads the project code.
|
@@ -76,4 +126,4 @@ interactive prompt that pre-loads the project code.
|
|
76
126
|
|
77
127
|
## License
|
78
128
|
|
79
|
-
[MIT License](
|
129
|
+
[MIT License](https://opensource.org/licenses/MIT)
|
data/lib/pleasant_path/array.rb
CHANGED
@@ -5,6 +5,10 @@ class Array
|
|
5
5
|
# The file is overwritten if it already exists. Any necessary parent
|
6
6
|
# directories are created if they do not exist.
|
7
7
|
#
|
8
|
+
# @example
|
9
|
+
# [:one, :two].write_to_file("out.txt") # == [:one, :two]
|
10
|
+
# File.read("out.txt") # == "one\ntwo\n"
|
11
|
+
#
|
8
12
|
# @param file [String, Pathname]
|
9
13
|
# @return [Array]
|
10
14
|
def write_to_file(file)
|
@@ -17,6 +21,12 @@ class Array
|
|
17
21
|
# The file is created if it does not exist. Any necessary parent
|
18
22
|
# directories are created if they do not exist.
|
19
23
|
#
|
24
|
+
# @example
|
25
|
+
# [:one, :two].append_to_file("out.txt") # == [:one, :two]
|
26
|
+
# File.read("out.txt") # == "one\ntwo\n"
|
27
|
+
# [:three, :four].append_to_file("out.txt") # == [:three, :four]
|
28
|
+
# File.read("out.txt") # == "one\ntwo\nthree\nfour\n"
|
29
|
+
#
|
20
30
|
# @param file [String, Pathname]
|
21
31
|
# @return [Array]
|
22
32
|
def append_to_file(file)
|
data/lib/pleasant_path/file.rb
CHANGED
@@ -1,10 +1,42 @@
|
|
1
1
|
class File
|
2
2
|
|
3
|
+
# Computes the longest path that every path in a list has in common.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# File.common_path(["a/b/x", "a/b/y", "a/b/z"]) # == "a/b/"
|
7
|
+
# File.common_path(["a/b/x", "a/b/y", "a/z"]) # == "a/"
|
8
|
+
# File.common_path(["a/b/x", "a/b/y", "a"]) # == "a"
|
9
|
+
#
|
10
|
+
# @param paths [Enumerable<String>]
|
11
|
+
# @return [String]
|
12
|
+
def self.common_path(paths)
|
13
|
+
return paths.first if paths.length <= 1
|
14
|
+
short, long = paths.minmax
|
15
|
+
i = 0
|
16
|
+
last = -1
|
17
|
+
while i < short.length && short[i] == long[i]
|
18
|
+
last = i if short[i] == "/".freeze
|
19
|
+
i += 1
|
20
|
+
end
|
21
|
+
short[0, i == short.length ? i : (last + 1)]
|
22
|
+
end
|
23
|
+
|
3
24
|
# Reads from the specified file its contents as a string, and yields
|
4
25
|
# the string to the given block for editing. Writes the return value
|
5
26
|
# of the block back to the file, overwriting previous contents.
|
6
27
|
# Returns the file's new contents.
|
7
28
|
#
|
29
|
+
# @example update JSON data file
|
30
|
+
# File.read("data.json") # == '{"nested":{"key":"value"}}'
|
31
|
+
#
|
32
|
+
# File.edit_text("data.json") do |text|
|
33
|
+
# data = JSON.parse(text)
|
34
|
+
# data["nested"]["key"] = "new value"
|
35
|
+
# data.to_json
|
36
|
+
# end # == '{"nested":{"key":"new value"}}'
|
37
|
+
#
|
38
|
+
# File.read("data.json") # == '{"nested":{"key":"new value"}}'
|
39
|
+
#
|
8
40
|
# @param filename [String, Pathname]
|
9
41
|
# @yield [text] edits current file contents
|
10
42
|
# @yieldparam text [String] current contents
|
@@ -15,6 +47,7 @@ class File
|
|
15
47
|
text = yield f.read
|
16
48
|
f.seek(0, IO::SEEK_SET)
|
17
49
|
f.write(text)
|
50
|
+
f.truncate(f.pos)
|
18
51
|
text
|
19
52
|
end
|
20
53
|
end
|
@@ -26,6 +59,14 @@ class File
|
|
26
59
|
# characters to use for both reading and writing. Returns the array
|
27
60
|
# of lines that comprises the file's new contents.
|
28
61
|
#
|
62
|
+
# @example dedup lines of file
|
63
|
+
# File.read("entries.txt") # == "AAA\nBBB\nBBB\nCCC\nAAA\n"
|
64
|
+
#
|
65
|
+
# File.edit_lines("entries.txt", &:uniq)
|
66
|
+
# # == ["AAA", "BBB", "CCC"]
|
67
|
+
#
|
68
|
+
# File.read("entries.txt") # == "AAA\nBBB\nCCC\n"
|
69
|
+
#
|
29
70
|
# @param filename [String, Pathname]
|
30
71
|
# @yield [lines] edits current file contents
|
31
72
|
# @yieldparam lines [Array<String>] current contents
|
@@ -36,6 +77,8 @@ class File
|
|
36
77
|
lines = yield f.read_lines
|
37
78
|
f.seek(0, IO::SEEK_SET)
|
38
79
|
f.write_lines(lines)
|
80
|
+
f.truncate(f.pos)
|
81
|
+
lines
|
39
82
|
end
|
40
83
|
end
|
41
84
|
|
data/lib/pleasant_path/io.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
class IO
|
2
2
|
|
3
|
-
# Writes each string plus a succeeding new line character
|
4
|
-
# (<code>$/</code>) to the IO. Returns the
|
3
|
+
# Writes each object as a string plus a succeeding new line character
|
4
|
+
# (<code>$/</code>) to the IO. Returns the objects unmodified.
|
5
5
|
#
|
6
|
-
# @
|
7
|
-
#
|
6
|
+
# @example
|
7
|
+
# # NOTE File inherits from IO
|
8
|
+
# File.open("out.txt") do |file|
|
9
|
+
# file.write_lines([:one, :two]) # == [:one, :two]
|
10
|
+
# end # == [:one, :two]
|
11
|
+
#
|
12
|
+
# File.read("out.txt") # == "one\ntwo\n"
|
13
|
+
#
|
14
|
+
# @param lines [Enumerable<#to_s>]
|
15
|
+
# @return [Enumerable<#to_s>]
|
8
16
|
def write_lines(lines)
|
9
17
|
lines.each do |line|
|
10
18
|
self.write(line)
|
@@ -16,11 +24,19 @@ class IO
|
|
16
24
|
|
17
25
|
# Reads from the IO all lines, and returns them as an array,
|
18
26
|
# end-of-line characters excluded. The <code>$/</code> global string
|
19
|
-
# specifies what end-of-line characters to
|
27
|
+
# specifies what end-of-line characters to exclude.
|
20
28
|
#
|
21
29
|
# (Not to be confused with +IO#readlines+ which retains end-of-line
|
22
30
|
# characters in every string it returns.)
|
23
31
|
#
|
32
|
+
# @example
|
33
|
+
# # NOTE File inherits from IO
|
34
|
+
# File.read("in.txt") # == "one\ntwo\n"
|
35
|
+
#
|
36
|
+
# File.open("in.txt") do |file|
|
37
|
+
# file.read_lines # == ["one", "two"]
|
38
|
+
# end # == ["one", "two"]
|
39
|
+
#
|
24
40
|
# @return [Array<String>]
|
25
41
|
def read_lines
|
26
42
|
self.readlines.each(&:chomp!)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
# Dumps the Object as JSON, and writes the JSON to the specified file.
|
4
|
+
# Returns the Object unmodified.
|
5
|
+
#
|
6
|
+
# For information about available options see
|
7
|
+
# {http://ruby-doc.org/stdlib/libdoc/json/rdoc/JSON.html#method-i-generate
|
8
|
+
# +JSON.generate+}.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# { "key" => "value" }.write_to_json("out.json") # == { "key" => "value" }
|
12
|
+
# File.read("out.json") # == '{"key":"value"}'
|
13
|
+
#
|
14
|
+
# @param file [String, Pathname]
|
15
|
+
# @param options [Hash]
|
16
|
+
# @return [self]
|
17
|
+
def write_to_json(file, options = {})
|
18
|
+
options = {
|
19
|
+
quirks_mode: true,
|
20
|
+
allow_nan: true,
|
21
|
+
}.merge(options)
|
22
|
+
|
23
|
+
file.to_pathname.write_text(JSON.generate(self, options))
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Pathname
|
2
|
+
|
3
|
+
# Reads the contents of the file indicated by the Pathname, and parses
|
4
|
+
# it as JSON. The returned result will be a basic Ruby data
|
5
|
+
# structure, namely, one of: +nil+, +true+, +false+, a +Numeric+, a
|
6
|
+
# +String+, an +Array+, or a +Hash+.
|
7
|
+
#
|
8
|
+
# For information about available options, see
|
9
|
+
# {http://ruby-doc.org/stdlib/libdoc/json/rdoc/JSON.html#method-i-parse
|
10
|
+
# +JSON.parse+}.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# File.write("in.json", '{"key": "value"}')
|
14
|
+
#
|
15
|
+
# Pathname.new("in.json").read_json # == { "key" => "value" }
|
16
|
+
#
|
17
|
+
# @param options [Hash]
|
18
|
+
# @return [nil, true, false, Numeric, String, Symbol, Array, Hash]
|
19
|
+
def read_json(options = {})
|
20
|
+
options = {
|
21
|
+
quirks_mode: true,
|
22
|
+
allow_nan: true,
|
23
|
+
max_nesting: false,
|
24
|
+
create_additions: false,
|
25
|
+
}.merge(options)
|
26
|
+
|
27
|
+
JSON.parse(self.read_text, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Reads the contents of the file indicated by the Pathname, and parses
|
31
|
+
# it as JSON. The parser will use type information embedded in the
|
32
|
+
# JSON to deserialize custom types. This is *UNSAFE* for JSON from
|
33
|
+
# an untrusted source. To consume untrusted JSON, use
|
34
|
+
# {Pathname#read_json} instead.
|
35
|
+
#
|
36
|
+
# For information about available options, see
|
37
|
+
# {http://ruby-doc.org/stdlib/libdoc/json/rdoc/JSON.html#method-i-parse
|
38
|
+
# +JSON.parse+}.
|
39
|
+
#
|
40
|
+
# For information about serializing custom types to JSON, see the
|
41
|
+
# {https://github.com/flori/json/blob/master/README.md#more-examples
|
42
|
+
# JSON readme}.
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# require "json/add/core" # provides Struct#to_json
|
46
|
+
# Point = Struct.new(:x, :y)
|
47
|
+
# point = Point.new(10, 20)
|
48
|
+
# File.write("in.json", point.to_json)
|
49
|
+
#
|
50
|
+
# Pathname.new("in.json").load_json # == Point.new(10, 20)
|
51
|
+
#
|
52
|
+
# @param options [Hash]
|
53
|
+
# @return deserialized object
|
54
|
+
def load_json(options = {})
|
55
|
+
self.open('r'){|f| JSON.load(f, nil, options) }
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
class Pathname
|
2
2
|
|
3
|
+
# {https://ruby-doc.org/core/File/Constants.html#NULL +File::NULL+} as
|
4
|
+
# a Pathname. On POSIX systems, this should be equivalent to
|
5
|
+
# +Pathname.new("/dev/null")+.
|
6
|
+
NULL = Pathname.new(File::NULL)
|
7
|
+
|
3
8
|
# Returns the Pathname unmodified. Exists for parity with
|
4
|
-
#
|
9
|
+
# {String#to_pathname}.
|
5
10
|
#
|
6
11
|
# @return [Pathname]
|
7
12
|
def to_pathname
|
@@ -14,7 +19,7 @@ class Pathname
|
|
14
19
|
# specified by the argument.
|
15
20
|
#
|
16
21
|
# @example
|
17
|
-
#
|
22
|
+
# Pathname.new("path/to/file1") ^ "file2" # == Pathname.new("path/to/file2")
|
18
23
|
#
|
19
24
|
# @param sibling [Pathname, String]
|
20
25
|
# @return [Pathname]
|
@@ -25,13 +30,34 @@ class Pathname
|
|
25
30
|
# Returns the +basename+ of the Pathname's parent directory.
|
26
31
|
#
|
27
32
|
# @example
|
28
|
-
# Pathname.new("path/to/file").parentname
|
33
|
+
# Pathname.new("path/to/file").parentname # == Pathname.new("to")
|
29
34
|
#
|
30
35
|
# @return [Pathname]
|
31
36
|
def parentname
|
32
37
|
self.dirname.basename
|
33
38
|
end
|
34
39
|
|
40
|
+
# Computes the longest path that the Pathname and +other+ have in
|
41
|
+
# common. See also {File.common_path}.
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# f1 = Pathname.new("dir1/file1")
|
45
|
+
# f2 = Pathname.new("dir1/subdir1/file2")
|
46
|
+
# f3 = Pathname.new("dir1/subdir1/file3")
|
47
|
+
# f4 = Pathname.new("dir2/file4")
|
48
|
+
#
|
49
|
+
# f1.common_path(f2) # == Pathname.new("dir1/")
|
50
|
+
# f2.common_path(f3) # == Pathname.new("dir1/subdir1/")
|
51
|
+
# f3.common_path(f4) # == Pathname.new("")
|
52
|
+
#
|
53
|
+
# [f1, f2, f3].reduce(&:common_path) # == Pathname.new("dir1/")
|
54
|
+
#
|
55
|
+
# @param other [Pathname]
|
56
|
+
# @return [Pathname]
|
57
|
+
def common_path(other)
|
58
|
+
File.common_path([self.to_s, other.to_s]).to_pathname
|
59
|
+
end
|
60
|
+
|
35
61
|
# Alias of +Pathname#directory?+.
|
36
62
|
#
|
37
63
|
# @return [Boolean]
|
@@ -40,6 +66,13 @@ class Pathname
|
|
40
66
|
# True if the directory indicated by the Pathname contains no other
|
41
67
|
# directories or files.
|
42
68
|
#
|
69
|
+
# @example
|
70
|
+
# FileUtils.mkdir("parent")
|
71
|
+
# FileUtils.mkdir("parent/dir1")
|
72
|
+
#
|
73
|
+
# Pathname.new("parent").dir_empty? # == false
|
74
|
+
# Pathname.new("parent/dir1").dir_empty? # == true
|
75
|
+
#
|
43
76
|
# @return [Boolean]
|
44
77
|
def dir_empty?
|
45
78
|
self.children(false).empty?
|
@@ -49,6 +82,18 @@ class Pathname
|
|
49
82
|
# directory indicated by the Pathname. Returned Pathnames are
|
50
83
|
# prefixed by the original Pathname.
|
51
84
|
#
|
85
|
+
# @example
|
86
|
+
# FileUtils.mkdir("parent")
|
87
|
+
# FileUtils.mkdir("parent/dir1")
|
88
|
+
# FileUtils.mkdir("parent/dir2")
|
89
|
+
# FileUtils.touch("parent/file1")
|
90
|
+
#
|
91
|
+
# Pathname.new("parent").dirs
|
92
|
+
# # == [
|
93
|
+
# # Pathname.new("parent/dir1"),
|
94
|
+
# # Pathname.new("parent/dir2")
|
95
|
+
# # ]
|
96
|
+
#
|
52
97
|
# @return [Array<Pathname>]
|
53
98
|
def dirs
|
54
99
|
self.children.tap{|c| c.select!(&:dir?) }
|
@@ -58,6 +103,20 @@ class Pathname
|
|
58
103
|
# directory indicated by the Pathname. Returned Pathnames are
|
59
104
|
# prefixed by the original Pathname.
|
60
105
|
#
|
106
|
+
# @example
|
107
|
+
# FileUtils.mkdir("parent")
|
108
|
+
# FileUtils.mkdir("parent/dir1")
|
109
|
+
# FileUtils.mkdir("parent/dir1/dir1")
|
110
|
+
# FileUtils.mkdir("parent/dir2")
|
111
|
+
# FileUtils.touch("parent/dir2/file1")
|
112
|
+
#
|
113
|
+
# Pathname.new("parent").dirs_r
|
114
|
+
# # == [
|
115
|
+
# # Pathname.new("parent/dir1"),
|
116
|
+
# # Pathname.new("parent/dir1/dir1"),
|
117
|
+
# # Pathname.new("parent/dir2")
|
118
|
+
# # ]
|
119
|
+
#
|
61
120
|
# @return [Array<Pathname>]
|
62
121
|
def dirs_r
|
63
122
|
self.find.select(&:dir?).tap(&:shift)
|
@@ -67,6 +126,18 @@ class Pathname
|
|
67
126
|
# indicated by the Pathname. Returned Pathnames are prefixed by the
|
68
127
|
# original Pathname.
|
69
128
|
#
|
129
|
+
# @example
|
130
|
+
# FileUtils.mkdir("parent")
|
131
|
+
# FileUtils.touch("parent/file1")
|
132
|
+
# FileUtils.touch("parent/file2")
|
133
|
+
# FileUtils.mkdir("parent/dir1")
|
134
|
+
#
|
135
|
+
# Pathname.new("parent").files
|
136
|
+
# # == [
|
137
|
+
# # Pathname.new("parent/file1"),
|
138
|
+
# # Pathname.new("parent/file2")
|
139
|
+
# # ]
|
140
|
+
#
|
70
141
|
# @return [Array<Pathname>]
|
71
142
|
def files
|
72
143
|
self.children.tap{|c| c.select!(&:file?) }
|
@@ -76,6 +147,18 @@ class Pathname
|
|
76
147
|
# indicated by the Pathname. Returned Pathnames are prefixed by the
|
77
148
|
# original Pathname.
|
78
149
|
#
|
150
|
+
# @example
|
151
|
+
# FileUtils.mkdir("parent")
|
152
|
+
# FileUtils.mkdir("parent/dir1")
|
153
|
+
# FileUtils.touch("parent/dir1/file1")
|
154
|
+
# FileUtils.touch("parent/file1")
|
155
|
+
#
|
156
|
+
# Pathname.new("parent").files_r
|
157
|
+
# # == [
|
158
|
+
# # Pathname.new("parent/dir1/file1"),
|
159
|
+
# # Pathname.new("parent/file1")
|
160
|
+
# # ]
|
161
|
+
#
|
79
162
|
# @return [Array<Pathname>]
|
80
163
|
def files_r
|
81
164
|
self.find.select(&:file?)
|
@@ -83,6 +166,15 @@ class Pathname
|
|
83
166
|
|
84
167
|
# Alias of +Pathname#mkpath+, but this method returns the Pathname.
|
85
168
|
#
|
169
|
+
# @example
|
170
|
+
# Dir.exist?("path") # == false
|
171
|
+
# Dir.exist?("path/to") # == false
|
172
|
+
#
|
173
|
+
# Pathname.new("path/to").make_dir # == Pathname.new("path/to")
|
174
|
+
#
|
175
|
+
# Dir.exist?("path") # == true
|
176
|
+
# Dir.exist?("path/to") # == true
|
177
|
+
#
|
86
178
|
# @return [Pathname]
|
87
179
|
def make_dir
|
88
180
|
self.mkpath
|
@@ -92,6 +184,16 @@ class Pathname
|
|
92
184
|
# Creates the parent (+dirname+) directories of the Pathname if they
|
93
185
|
# do not exist, and returns the Pathname.
|
94
186
|
#
|
187
|
+
# @example
|
188
|
+
# Dir.exist?("path") # == false
|
189
|
+
# Dir.exist?("path/to") # == false
|
190
|
+
#
|
191
|
+
# Pathname.new("path/to/file").make_dirname # == Pathname.new("path/to/file")
|
192
|
+
#
|
193
|
+
# Dir.exist?("path") # == true
|
194
|
+
# Dir.exist?("path/to") # == true
|
195
|
+
# Dir.exist?("path/to/file") # == false
|
196
|
+
#
|
95
197
|
# @return [Pathname]
|
96
198
|
def make_dirname
|
97
199
|
self.dirname.make_dir
|
@@ -103,6 +205,16 @@ class Pathname
|
|
103
205
|
# the file and any necessary parent directories if they do not exist.
|
104
206
|
# See also +FileUtils.touch+.
|
105
207
|
#
|
208
|
+
# @example
|
209
|
+
# Dir.exist?("path") # == false
|
210
|
+
# Dir.exist?("path/to") # == false
|
211
|
+
#
|
212
|
+
# Pathname.new("path/to/file").touch_file # == Pathname.new("path/to/file")
|
213
|
+
#
|
214
|
+
# Dir.exist?("path") # == true
|
215
|
+
# Dir.exist?("path/to") # == true
|
216
|
+
# File.exist?("path/to/file") # == true
|
217
|
+
#
|
106
218
|
# @return [Pathname]
|
107
219
|
def touch_file
|
108
220
|
self.make_dirname
|
@@ -114,28 +226,66 @@ class Pathname
|
|
114
226
|
# and returns the Pathname. Similar to +Pathname#rmtree+, but does
|
115
227
|
# not raise an exception if the file does not exist.
|
116
228
|
#
|
229
|
+
# @example
|
230
|
+
# File.exist?("path/to/file") # == true
|
231
|
+
#
|
232
|
+
# Pathname.new("path").delete! # == Pathname.new("path")
|
233
|
+
#
|
234
|
+
# Dir.exist?("path") # == false
|
235
|
+
# Dir.exist?("path/to") # == false
|
236
|
+
# File.exist?("path/to/file") # == false
|
237
|
+
#
|
117
238
|
# @return [Pathname]
|
118
239
|
def delete!
|
119
240
|
self.rmtree if self.exist?
|
120
241
|
self
|
121
242
|
end
|
122
243
|
|
123
|
-
# Moves the file indicated by Pathname to the given
|
124
|
-
# returns that destination as a Pathname. Creates
|
125
|
-
# parent directories if they do not exist.
|
244
|
+
# Moves the file or directory indicated by the Pathname to the given
|
245
|
+
# destination, and returns that destination as a Pathname. Creates
|
246
|
+
# any necessary parent directories if they do not exist. See also
|
247
|
+
# +FileUtils.mv+.
|
248
|
+
#
|
249
|
+
# @example
|
250
|
+
# File.exist?("path/to/file") # == true
|
251
|
+
# Dir.exist?("some") # == false
|
252
|
+
# Dir.exist?("some/other") # == false
|
253
|
+
# File.exist?("some/other/thing") # == false
|
254
|
+
#
|
255
|
+
# Pathname.new("path/to/file").move("some/other/thing")
|
256
|
+
# # == Pathname.new("some/other/thing")
|
257
|
+
#
|
258
|
+
# File.exist?("path/to/file") # == false
|
259
|
+
# Dir.exist?("some") # == true
|
260
|
+
# Dir.exist?("some/other") # == true
|
261
|
+
# File.exist?("some/other/thing") # == true
|
126
262
|
#
|
127
263
|
# @param destination [Pathname, String]
|
128
264
|
# @return [Pathname]
|
129
265
|
def move(destination)
|
130
266
|
destination = destination.to_pathname
|
131
267
|
destination.make_dirname
|
132
|
-
|
268
|
+
FileUtils.mv(self, destination)
|
133
269
|
destination
|
134
270
|
end
|
135
271
|
|
136
|
-
# Moves the file indicated by Pathname into the given
|
137
|
-
# returns the resultant path
|
138
|
-
# necessary parent directories if they do not exist.
|
272
|
+
# Moves the file or directory indicated by the Pathname into the given
|
273
|
+
# directory, and returns the resultant path as a Pathname. Creates
|
274
|
+
# any necessary parent directories if they do not exist.
|
275
|
+
#
|
276
|
+
# @example
|
277
|
+
# File.exist?("path/to/file") # == true
|
278
|
+
# Dir.exist?("other") # == false
|
279
|
+
# Dir.exist?("other/path") # == false
|
280
|
+
# File.exist?("other/path/file") # == false
|
281
|
+
#
|
282
|
+
# Pathname.new("path/to/file").move_into("other/path")
|
283
|
+
# # == Pathname.new("other/path/file")
|
284
|
+
#
|
285
|
+
# File.exist?("path/to/file") # == false
|
286
|
+
# Dir.exist?("other") # == true
|
287
|
+
# Dir.exist?("other/path") # == true
|
288
|
+
# File.exist?("other/path/file") # == true
|
139
289
|
#
|
140
290
|
# @param directory [Pathname, String]
|
141
291
|
# @return [Pathname]
|
@@ -143,10 +293,121 @@ class Pathname
|
|
143
293
|
self.move(directory / self.basename)
|
144
294
|
end
|
145
295
|
|
296
|
+
# Copies the file or directory indicated by the Pathname to the given
|
297
|
+
# destination, and returns that destination as a Pathname. Creates
|
298
|
+
# any necessary parent directories if they do not exist. See also
|
299
|
+
# +FileUtils.cp_r+.
|
300
|
+
#
|
301
|
+
# @example
|
302
|
+
# File.exist?("path/to/file") # == true
|
303
|
+
# Dir.exist?("some") # == false
|
304
|
+
# Dir.exist?("some/other") # == false
|
305
|
+
# File.exist?("some/other/thing") # == false
|
306
|
+
#
|
307
|
+
# Pathname.new("path/to/file").copy("some/other/thing")
|
308
|
+
# # == Pathname.new("some/other/thing")
|
309
|
+
#
|
310
|
+
# File.exist?("path/to/file") # == true
|
311
|
+
# Dir.exist?("some") # == true
|
312
|
+
# Dir.exist?("some/other") # == true
|
313
|
+
# File.exist?("some/other/thing") # == true
|
314
|
+
#
|
315
|
+
# @param destination [Pathname, String]
|
316
|
+
# @return [Pathname]
|
317
|
+
def copy(destination)
|
318
|
+
destination = destination.to_pathname
|
319
|
+
destination.make_dirname
|
320
|
+
FileUtils.cp_r(self, destination)
|
321
|
+
destination
|
322
|
+
end
|
323
|
+
|
324
|
+
# Copies the file or directory indicated by the Pathname into the
|
325
|
+
# given directory, and returns the resultant path as a Pathname.
|
326
|
+
# Creates any necessary parent directories if they do not exist.
|
327
|
+
#
|
328
|
+
# @example
|
329
|
+
# File.exist?("path/to/file") # == true
|
330
|
+
# Dir.exist?("other") # == false
|
331
|
+
# Dir.exist?("other/path") # == false
|
332
|
+
# File.exist?("other/path/file") # == false
|
333
|
+
#
|
334
|
+
# Pathname.new("path/to/file").copy_into("other/path")
|
335
|
+
# # == Pathname.new("other/path/file")
|
336
|
+
#
|
337
|
+
# File.exist?("path/to/file") # == true
|
338
|
+
# Dir.exist?("other") # == true
|
339
|
+
# Dir.exist?("other/path") # == true
|
340
|
+
# File.exist?("other/path/file") # == true
|
341
|
+
#
|
342
|
+
# @param directory [Pathname, String]
|
343
|
+
# @return [Pathname]
|
344
|
+
def copy_into(directory)
|
345
|
+
self.copy(directory / self.basename)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Renames the file or directory indicated by the Pathname, but
|
349
|
+
# preserves its location as indicated by +dirname+. Returns the
|
350
|
+
# resultant path as a Pathname.
|
351
|
+
#
|
352
|
+
# @example
|
353
|
+
# File.exist?("path/to/file") # == true
|
354
|
+
#
|
355
|
+
# Pathname.new("path/to/file").rename_basename("other")
|
356
|
+
# # == Pathname.new("path/to/other")
|
357
|
+
#
|
358
|
+
# File.exist?("path/to/file") # == false
|
359
|
+
# File.exist?("path/to/other") # == true
|
360
|
+
#
|
361
|
+
# @param new_basename [String]
|
362
|
+
# @return [Pathname]
|
363
|
+
def rename_basename(new_basename)
|
364
|
+
self.move(self.dirname / new_basename)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Renames the file extension of the file indicated by the Pathname.
|
368
|
+
# If the file has no extension, the new extension is appended.
|
369
|
+
#
|
370
|
+
# @example replace extension
|
371
|
+
# File.exist?("path/to/file.abc") # == true
|
372
|
+
#
|
373
|
+
# Pathname.new("path/to/file.abc").rename_extname(".xyz")
|
374
|
+
# # == Pathname.new("path/to/file.xyz")
|
375
|
+
#
|
376
|
+
# File.exist?("path/to/file.abc") # == false
|
377
|
+
# File.exist?("path/to/file.xyz") # == true
|
378
|
+
#
|
379
|
+
# @example remove extension
|
380
|
+
# File.exist?("path/to/file.abc") # == true
|
381
|
+
#
|
382
|
+
# Pathname.new("path/to/file.abc").rename_extname("")
|
383
|
+
# # == Pathname.new("path/to/file")
|
384
|
+
#
|
385
|
+
# File.exist?("path/to/file.abc") # == false
|
386
|
+
# File.exist?("path/to/file") # == true
|
387
|
+
#
|
388
|
+
# @param new_extname [String]
|
389
|
+
# @return [Pathname]
|
390
|
+
def rename_extname(new_extname)
|
391
|
+
unless new_extname.start_with?(".") || new_extname.empty?
|
392
|
+
new_extname = ".#{new_extname}"
|
393
|
+
end
|
394
|
+
self.move(self.sub_ext(new_extname))
|
395
|
+
end
|
396
|
+
|
146
397
|
# Writes given text to the file indicated by the Pathname, and returns
|
147
398
|
# the Pathname. The file is overwritten if it already exists. Any
|
148
399
|
# necessary parent directories are created if they do not exist.
|
149
400
|
#
|
401
|
+
# @example
|
402
|
+
# Dir.exist?("path") # == false
|
403
|
+
# Dir.exist?("path/to") # == false
|
404
|
+
# File.exist?("path/to/file") # == false
|
405
|
+
#
|
406
|
+
# Pathname.new("path/to/file").write_text("hello world")
|
407
|
+
# # == Pathname.new("path/to/file")
|
408
|
+
#
|
409
|
+
# File.read("path/to/file") # == "hello world"
|
410
|
+
#
|
150
411
|
# @param text [String]
|
151
412
|
# @return [Pathname]
|
152
413
|
def write_text(text)
|
@@ -158,6 +419,16 @@ class Pathname
|
|
158
419
|
# returns the Pathname. The file is created if it does not exist.
|
159
420
|
# Any necessary parent directories are created if they do not exist.
|
160
421
|
#
|
422
|
+
# @example
|
423
|
+
# Dir.exist?("path") # == false
|
424
|
+
# Dir.exist?("path/to") # == false
|
425
|
+
# File.exist?("path/to/file") # == false
|
426
|
+
#
|
427
|
+
# Pathname.new("path/to/file").append_text("hello").append_text(" world")
|
428
|
+
# # == Pathname.new("path/to/file")
|
429
|
+
#
|
430
|
+
# File.read("path/to/file") # == "hello world"
|
431
|
+
#
|
161
432
|
# @param text [String]
|
162
433
|
# @return [Pathname]
|
163
434
|
def append_text(text)
|
@@ -165,25 +436,40 @@ class Pathname
|
|
165
436
|
self
|
166
437
|
end
|
167
438
|
|
168
|
-
# Writes
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
# exist.
|
439
|
+
# Writes each object as a string plus a succeeding new line character
|
440
|
+
# (<code>$/</code>) to the file indicated by the Pathname. Returns
|
441
|
+
# the Pathname. The file is overwritten if it already exists. Any
|
442
|
+
# necessary parent directories are created if they do not exist.
|
173
443
|
#
|
174
|
-
# @
|
444
|
+
# @example
|
445
|
+
# File.exist?("path/to/file") # false
|
446
|
+
#
|
447
|
+
# Pathname.new("path/to/file").write_lines([:one, :two])
|
448
|
+
# # == Pathname.new("path/to/file")
|
449
|
+
#
|
450
|
+
# File.read("path/to/file") # == "one\ntwo\n"
|
451
|
+
#
|
452
|
+
# @param lines [Enumerable<#to_s>]
|
175
453
|
# @return [Pathname]
|
176
454
|
def write_lines(lines)
|
177
455
|
self.make_dirname.open('w'){|f| f.write_lines(lines) }
|
178
456
|
self
|
179
457
|
end
|
180
458
|
|
181
|
-
# Appends
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
459
|
+
# Appends each object as a string plus a succeeding new line character
|
460
|
+
# (<code>$/</code>) to the file indicated by the Pathname. Returns
|
461
|
+
# the Pathname. The file is created if it does not exist. Any
|
462
|
+
# necessary parent directories are created if they do not exist.
|
185
463
|
#
|
186
|
-
# @
|
464
|
+
# @example
|
465
|
+
# File.exist?("path/to/file") # false
|
466
|
+
#
|
467
|
+
# Pathname.new("path/to/file").append_lines([:one, :two]).append_lines([:three, :four])
|
468
|
+
# # == Pathname.new("path/to/file")
|
469
|
+
#
|
470
|
+
# File.read("path/to/file") # == "one\ntwo\nthree\nfour\n"
|
471
|
+
#
|
472
|
+
# @param lines [Enumerable<#to_s>]
|
187
473
|
# @return [Pathname]
|
188
474
|
def append_lines(lines)
|
189
475
|
self.make_dirname.open('a'){|f| f.write_lines(lines) }
|
@@ -198,11 +484,16 @@ class Pathname
|
|
198
484
|
# Reads from the file indicated by the Pathname all lines, and returns
|
199
485
|
# them as an array, end-of-line characters excluded. The
|
200
486
|
# <code>$/</code> global string specifies what end-of-line characters
|
201
|
-
# to look for. See also
|
487
|
+
# to look for. See also {IO#read_lines}.
|
202
488
|
#
|
203
489
|
# (Not to be confused with +Pathname#readlines+ which retains
|
204
490
|
# end-of-line characters in every string it returns.)
|
205
491
|
#
|
492
|
+
# @example
|
493
|
+
# File.read("path/to/file") # == "one\ntwo\n"
|
494
|
+
#
|
495
|
+
# Pathname.new("path/to/file").read_lines # == ["one", "two"]
|
496
|
+
#
|
206
497
|
# @return [Array<String>]
|
207
498
|
def read_lines
|
208
499
|
self.open('r'){|f| f.read_lines }
|
@@ -212,14 +503,18 @@ class Pathname
|
|
212
503
|
# as a string, and yields the string to the given block for editing.
|
213
504
|
# Writes the return value of the block back to the file, overwriting
|
214
505
|
# previous contents. Returns the file's new contents. See also
|
215
|
-
#
|
506
|
+
# {File.edit_text}.
|
507
|
+
#
|
508
|
+
# @example update JSON data file
|
509
|
+
# File.read("data.json") # == '{"nested":{"key":"value"}}'
|
216
510
|
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
# data =
|
220
|
-
# data
|
221
|
-
#
|
222
|
-
#
|
511
|
+
# Pathname.new("data.json").edit_text do |text|
|
512
|
+
# data = JSON.parse(text)
|
513
|
+
# data["nested"]["key"] = "new value"
|
514
|
+
# data.to_json
|
515
|
+
# end # == '{"nested":{"key":"new value"}}'
|
516
|
+
#
|
517
|
+
# File.read("data.json") # == '{"nested":{"key":"new value"}}'
|
223
518
|
#
|
224
519
|
# @yield [text] edits current file contents
|
225
520
|
# @yieldparam text [String] current contents
|
@@ -235,10 +530,15 @@ class Pathname
|
|
235
530
|
# overwriting previous contents. The <code>$/</code> global string
|
236
531
|
# specifies what end-of-line characters to use for both reading and
|
237
532
|
# writing. Returns the array of lines that comprises the file's new
|
238
|
-
# contents. See also
|
533
|
+
# contents. See also {File.edit_lines}.
|
534
|
+
#
|
535
|
+
# @example dedup lines of file
|
536
|
+
# File.read("entries.txt") # == "AAA\nBBB\nBBB\nCCC\nAAA\n"
|
239
537
|
#
|
240
|
-
#
|
241
|
-
#
|
538
|
+
# Pathname.new("entries.txt").edit_lines(&:uniq)
|
539
|
+
# # == ["AAA", "BBB", "CCC"]
|
540
|
+
#
|
541
|
+
# File.read("entries.txt") # == "AAA\nBBB\nCCC\n"
|
242
542
|
#
|
243
543
|
# @yield [lines] edits current file contents
|
244
544
|
# @yieldparam lines [Array<String>] current contents
|
@@ -251,6 +551,15 @@ class Pathname
|
|
251
551
|
# Appends the contents of another file to the destination indicated by
|
252
552
|
# Pathname. Returns the destination Pathname.
|
253
553
|
#
|
554
|
+
# @example
|
555
|
+
# File.read("yearly.log") # == "one\ntwo\n"
|
556
|
+
# File.read("daily.log") # == "three\nfour\n"
|
557
|
+
#
|
558
|
+
# Pathname.new("yearly.log").append_file("daily.log")
|
559
|
+
# # == Pathname.new("yearly.log")
|
560
|
+
#
|
561
|
+
# File.read("yearly.log") # == "one\ntwo\nthree\nfour\n"
|
562
|
+
#
|
254
563
|
# @param source [String, Pathname]
|
255
564
|
# @return [Pathname]
|
256
565
|
def append_file(source)
|
data/lib/pleasant_path/string.rb
CHANGED
@@ -2,12 +2,15 @@ class String
|
|
2
2
|
|
3
3
|
# Converts the string to a +Pathname+ object.
|
4
4
|
#
|
5
|
+
# @example
|
6
|
+
# "path/to/file".to_pathname # == Pathname.new("path/to/file")
|
7
|
+
#
|
5
8
|
# @return [Pathname]
|
6
9
|
def to_pathname
|
7
10
|
Pathname.new(self)
|
8
11
|
end
|
9
12
|
|
10
|
-
# Alias of
|
13
|
+
# Alias of {String#to_pathname}.
|
11
14
|
#
|
12
15
|
# @return [Pathname]
|
13
16
|
alias :path :to_pathname
|
@@ -16,7 +19,7 @@ class String
|
|
16
19
|
# +File.join+) and returns the result as a +Pathname+ object.
|
17
20
|
#
|
18
21
|
# @example
|
19
|
-
#
|
22
|
+
# "path/to" / "file" # == Pathname.new("path/to/file")
|
20
23
|
#
|
21
24
|
# @param child [String]
|
22
25
|
# @return [Pathname]
|
@@ -28,10 +31,10 @@ class String
|
|
28
31
|
# path with the argument, and returns the result as a +Pathname+
|
29
32
|
# object. The mnenomic for this operator is that the resultant path
|
30
33
|
# goes up one directory level from the original, then goes down to the
|
31
|
-
# directory specified by the argument. See also
|
34
|
+
# directory specified by the argument. See also {Pathname#^}.
|
32
35
|
#
|
33
36
|
# @example
|
34
|
-
#
|
37
|
+
# "path/to/file1" ^ "file2" # == Pathname.new("path/to/file2")
|
35
38
|
#
|
36
39
|
# @param sibling [Pathname, String]
|
37
40
|
# @return [Pathname]
|
@@ -43,6 +46,9 @@ class String
|
|
43
46
|
# into matching paths as +Pathname+ objects. See also +Dir.glob+ and
|
44
47
|
# +Pathname.glob+.
|
45
48
|
#
|
49
|
+
# @example
|
50
|
+
# "*.txt".glob # == Pathname.glob("*.txt")
|
51
|
+
#
|
46
52
|
# @return [Array<Pathname>]
|
47
53
|
def glob
|
48
54
|
Pathname.glob(self)
|
@@ -52,6 +58,10 @@ class String
|
|
52
58
|
# file is overwritten if it already exists. Any necessary parent
|
53
59
|
# directories are created if they do not exist.
|
54
60
|
#
|
61
|
+
# @example
|
62
|
+
# "hello world".write_to_file("out.txt") # == "hello world"
|
63
|
+
# File.read("out.txt") # == "hello world"
|
64
|
+
#
|
55
65
|
# @param file [String, Pathname]
|
56
66
|
# @return [String]
|
57
67
|
def write_to_file(file)
|
@@ -63,6 +73,12 @@ class String
|
|
63
73
|
# file is created if it does not exist. Any necessary parent
|
64
74
|
# directories are created if they do not exist.
|
65
75
|
#
|
76
|
+
# @example
|
77
|
+
# "hello".append_to_file("out.txt") # == "hello"
|
78
|
+
# File.read("out.txt") # == "hello"
|
79
|
+
# " world".append_to_file("out.txt") # == " world"
|
80
|
+
# File.read("out.txt") # == "hello world"
|
81
|
+
#
|
66
82
|
# @param file [String, Pathname]
|
67
83
|
# @return [String]
|
68
84
|
def append_to_file(file)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
# Dumps the Object as YAML, and writes the YAML to the specified file.
|
4
|
+
# Returns the Object unmodified.
|
5
|
+
#
|
6
|
+
# For information about available options see
|
7
|
+
# {https://ruby-doc.org/stdlib/libdoc/psych/rdoc/Psych.html#method-c-dump
|
8
|
+
# +YAML.dump+}.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# { "key" => "value" }.write_to_yaml("out.yaml") # == { "key" => "value" }
|
12
|
+
# File.read("out.yaml") # == "---\nkey: value\n"
|
13
|
+
#
|
14
|
+
# @param file [String, Pathname]
|
15
|
+
# @param options [Hash]
|
16
|
+
# @return [self]
|
17
|
+
def write_to_yaml(file, options = {})
|
18
|
+
File.open(file, 'w'){|f| YAML.dump(self, f, options) }
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Pathname
|
2
|
+
|
3
|
+
# Reads the contents of the file indicated by the Pathname, and parses
|
4
|
+
# it as YAML. The returned result will be a basic Ruby data
|
5
|
+
# structure, namely, one of: +nil+, +true+, +false+, a +Numeric+, a
|
6
|
+
# +String+, an +Array+, or a +Hash+.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# File.write("in.yaml", "key: value")
|
10
|
+
#
|
11
|
+
# Pathname.new("in.yaml").read_yaml # == { "key" => "value" }
|
12
|
+
#
|
13
|
+
# @return [nil, true, false, Numeric, String, Array, Hash]
|
14
|
+
def read_yaml
|
15
|
+
self.open('r'){|f| YAML.safe_load(f, [], [], false, self) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Reads the contents of the file indicated by the Pathname, and parses
|
19
|
+
# it as YAML. The parser will use type information embedded in the
|
20
|
+
# YAML to deserialize custom types. This is *UNSAFE* for YAML from
|
21
|
+
# an untrusted source. To consume untrusted YAML, use
|
22
|
+
# {Pathname#read_yaml} instead.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# Point = Struct.new(:x, :y)
|
26
|
+
# point = Point.new(10, 20)
|
27
|
+
# File.write("in.yaml", point.to_yaml)
|
28
|
+
#
|
29
|
+
# Pathname.new("in.yaml").load_yaml # == Point.new(10, 20)
|
30
|
+
#
|
31
|
+
# @return deserialized object
|
32
|
+
def load_yaml
|
33
|
+
YAML.load_file(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/pleasant_path.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
require "pathname"
|
2
|
+
require_relative "pleasant_path/version"
|
3
|
+
require_relative "pleasant_path/array"
|
4
|
+
require_relative "pleasant_path/file"
|
5
|
+
require_relative "pleasant_path/io"
|
6
|
+
require_relative "pleasant_path/pathname"
|
7
|
+
require_relative "pleasant_path/string"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pleasant_path
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Hefner
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -83,9 +83,15 @@ files:
|
|
83
83
|
- lib/pleasant_path/array.rb
|
84
84
|
- lib/pleasant_path/file.rb
|
85
85
|
- lib/pleasant_path/io.rb
|
86
|
+
- lib/pleasant_path/json.rb
|
87
|
+
- lib/pleasant_path/json/object.rb
|
88
|
+
- lib/pleasant_path/json/pathname.rb
|
86
89
|
- lib/pleasant_path/pathname.rb
|
87
90
|
- lib/pleasant_path/string.rb
|
88
91
|
- lib/pleasant_path/version.rb
|
92
|
+
- lib/pleasant_path/yaml.rb
|
93
|
+
- lib/pleasant_path/yaml/object.rb
|
94
|
+
- lib/pleasant_path/yaml/pathname.rb
|
89
95
|
- pleasant_path.gemspec
|
90
96
|
homepage: https://github.com/jonathanhefner/pleasant_path
|
91
97
|
licenses:
|
@@ -107,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
113
|
version: '0'
|
108
114
|
requirements: []
|
109
115
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.
|
116
|
+
rubygems_version: 2.6.13
|
111
117
|
signing_key:
|
112
118
|
specification_version: 4
|
113
119
|
summary: A fluent API for pleasant file IO.
|