rubypath 1.0.0 → 1.0.1

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.
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Path
4
+ # @!group File Extensions
5
+
6
+ # Return list of all file extensions.
7
+ #
8
+ # @example
9
+ # Path.new('/path/to/template.de.html.erb').extensions
10
+ # #=> ['de', 'html', 'erb']
11
+ #
12
+ # @return [Array<String>] List of file extensions.
13
+ #
14
+ def extensions
15
+ if dotfile?
16
+ name.split('.')[2..-1]
17
+ else
18
+ name.split('.')[1..-1]
19
+ end
20
+ end
21
+ alias exts extensions
22
+
23
+ # Return last file extension.
24
+ #
25
+ # @example
26
+ # Path.new('/path/to/template.de.html.erb').extension
27
+ # #=> 'erb'
28
+ #
29
+ # @return [String] Last file extensions.
30
+ #
31
+ def extension
32
+ extensions.last
33
+ end
34
+ alias ext extension
35
+
36
+ # Return last file extension include dot character.
37
+ #
38
+ # @return [String] Ext name.
39
+ # @see ::File.extname
40
+ #
41
+ def extname
42
+ ::File.extname name
43
+ end
44
+
45
+ # Return the file name without any extensions.
46
+ #
47
+ # @example
48
+ # Path("template.de.html.slim").pure_name
49
+ # #=> "template"
50
+ #
51
+ # @example
52
+ # Path("~/.gitconfig").pure_name
53
+ # #=> ".gitconfig"
54
+ #
55
+ # @return [String] File name without extensions.
56
+ #
57
+ def pure_name
58
+ if dotfile?
59
+ name.split('.', 3)[0..1].join('.')
60
+ else
61
+ name.split('.', 2)[0]
62
+ end
63
+ end
64
+
65
+ # Replace file extensions with given new ones or by a given
66
+ # translation map.
67
+ #
68
+ # @overload replace_extensions(exts)
69
+ # Replace all extensions with given new ones. Number of given extensions
70
+ # does not need to match number of existing extensions.
71
+ #
72
+ # @example
73
+ # Path('file.de.txt').replace_extensions(%w(en html))
74
+ # #=> <Path "file.en.html">
75
+ #
76
+ # @example
77
+ # Path('file.de.mobile.html.haml').replace_extensions(%w(int txt))
78
+ # #=> <Path "file.int.txt">
79
+ #
80
+ # @param exts [Array<String>] New extensions.
81
+ #
82
+ # @overload replace_extensions(ext, [ext, [..]])
83
+ # Replace all extensions with given new ones. Number of given extensions
84
+ # does not need to match number of existing extensions.
85
+ #
86
+ # @example
87
+ # Path('file.de.txt').replace_extensions('en', 'html')
88
+ # #=> <Path "file.en.html">
89
+ #
90
+ # @example
91
+ # Path('file.de.mobile.html.haml').replace_extensions('en', 'html')
92
+ # #=> <Path "file.en.html">
93
+ #
94
+ # @example
95
+ # Path('file.de.txt').replace_extensions('html')
96
+ # #=> <Path "file.html">
97
+ #
98
+ # @param ext [String] New extensions.
99
+ #
100
+ # @overload replace_extensions(map)
101
+ # Replace all matching extensions.
102
+ #
103
+ # @example
104
+ # Path('file.de.html.haml').replace_extensions('de'=>'en', 'haml'=>'slim')
105
+ # #=> <Path "file.en.html.slim">
106
+ #
107
+ # @param map [Hash<String, String>] Translation map as hash.
108
+ #
109
+ # @return [Path] Path to new filename.
110
+ #
111
+ # rubocop:disable AbcSize
112
+ # rubocop:disable CyclomaticComplexity
113
+ # rubocop:disable MethodLength
114
+ # rubocop:disable PerceivedComplexity
115
+ #
116
+ def replace_extensions(*args)
117
+ args.flatten!
118
+ extensions = self.extensions
119
+
120
+ if (replace = (args.last.is_a?(Hash) ? args.pop : nil))
121
+ if args.empty?
122
+ extensions.map! do |ext|
123
+ replace[ext] ? replace[ext].to_s : ext
124
+ end
125
+ else
126
+ raise ArgumentError.new 'Cannot replace extensions with array ' \
127
+ 'and hash at the same time.'
128
+ end
129
+ else
130
+ extensions = args.map(&:to_s)
131
+ end
132
+
133
+ if extensions == self.extensions
134
+ self
135
+ elsif only_filename?
136
+ Path "#{pure_name}.#{extensions.join('.')}"
137
+ else
138
+ dirname.join "#{pure_name}.#{extensions.join('.')}"
139
+ end
140
+ end
141
+ # rubocop:enable all
142
+
143
+ # Replace last extension with one or multiple new extensions.
144
+ #
145
+ # @example
146
+ # Path('file.de.txt').replace_extension('html')
147
+ # #=> <Path "file.de.html">
148
+ #
149
+ # @example
150
+ # Path('file.de.txt').replace_extension('html', 'erb')
151
+ # #=> <Path "file.de.html.erb">
152
+ #
153
+ # @return [Path] Path to new filename.
154
+ #
155
+ def replace_extension(*args)
156
+ extensions = self.extensions
157
+ extensions.pop
158
+ extensions += args.flatten
159
+
160
+ replace_extensions extensions
161
+ end
162
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Path
4
+ # @!group File Operations
5
+
6
+ # Return base name without path.
7
+ #
8
+ # @return [String] Base name.
9
+ #
10
+ def name
11
+ ::File.basename internal_path
12
+ end
13
+ alias basename name
14
+
15
+ # Create new file at pointed location or update modification time if file
16
+ # exists.
17
+ #
18
+ # @example
19
+ # Path('/path/to/file.txt').touch
20
+ # #=> <Path:"/path/to/file.txt">
21
+ #
22
+ # @example
23
+ # Path('/path/to').touch('file.txt')
24
+ # #=> <Path:"/path/to/file.txt">
25
+ #
26
+ # @return [Path] Path to touched file.
27
+ #
28
+ def touch(*args)
29
+ with_path(*args) do |path|
30
+ invoke_backend :touch, path
31
+ Path path
32
+ end
33
+ end
34
+
35
+ # Removes file at current path.
36
+ #
37
+ # Raise an error if file does not exists or is a directory.
38
+ #
39
+ # @example
40
+ # Path('/file.txt').touch.unlink
41
+ # #=> <Path /file.txt>
42
+ #
43
+ # @example
44
+ # Path('/file.txt').touch
45
+ # Path('/').unlink('file.txt')
46
+ # #=> <Path /file.txt>
47
+ #
48
+ # @return [Path] Unlinked path.
49
+ #
50
+ def unlink(*args)
51
+ with_path(*args) do |path|
52
+ invoke_backend :unlink, path
53
+ Path path
54
+ end
55
+ end
56
+
57
+ # Create a file at pointed location and all missing parent directories.
58
+ #
59
+ # Given arguments will be joined with current path before directories and
60
+ # file is created.
61
+ #
62
+ # If file already exists nothing will be done.
63
+ #
64
+ # @example
65
+ # Path('/path/to/file.txt').mkfile
66
+ # #=> <Path:"/path/to/file.txt">
67
+ #
68
+ # @example
69
+ # Path('/').mkfile('path', 'to', 'file.txt')
70
+ # #=> <Path:"/path/to/file.txt">
71
+ #
72
+ # @return [Path] Path to created or existent file.
73
+ #
74
+ def mkfile(*args) # rubocop:disable AbcSize
75
+ with_path(*args) do |path|
76
+ path.parent.mkpath if !path.exists? && path.parent && !path.parent.exists?
77
+
78
+ if path.exists?
79
+ raise Errno::ENOENT.new path.to_s unless path.file?
80
+ else
81
+ path.touch
82
+ end
83
+ end
84
+ end
85
+
86
+ # Search for a file in current directory or parent directories.
87
+ #
88
+ # Given search pattern can either be a regular expression or a shell glob
89
+ # expression.
90
+ #
91
+ # @example
92
+ # Path.cwd.lookup('project.{yml,yaml}')
93
+ # #=> <Path:"/path/config.yml">
94
+ #
95
+ # @example
96
+ # Path.cwd.lookup(/config(_\d+).ya?ml/)
97
+ # #=> <Path:"/path/config_354.yaml">
98
+ #
99
+ # @example
100
+ # Path('~').lookup('*config', ::File::FNM_DOTMATCH)
101
+ # #=> <Path:"/gome/user/.gitconfig">
102
+ #
103
+ # @param pattern [String|RegExp] Expression file name must match.
104
+ # @param flags [Integer] Additional flags. See {::File.fnmatch}.
105
+ # Defaults to `File::FNM_EXTGLOB`.
106
+ # @return [Path] Path to found file or nil.
107
+ #
108
+ def lookup(pattern, flags = nil) # rubocop:disable MethodLength
109
+ flags = self.class.default_glob_flags(flags)
110
+
111
+ expand.ascend do |path|
112
+ case pattern
113
+ when String
114
+ path.entries.each do |c|
115
+ return path.join(c) if ::File.fnmatch?(pattern, c.name, flags)
116
+ end
117
+ when Regexp
118
+ path.entries.each do |c|
119
+ # rubocop:disable RegexpMatch
120
+ return path.join(c) if pattern =~ c.name
121
+ end
122
+ end
123
+ end
124
+
125
+ nil
126
+ end
127
+ # rubocop:enable all
128
+
129
+ # Return file modification time.
130
+ #
131
+ # @return [Time] Time of last modification.
132
+ #
133
+ def mtime
134
+ invoke_backend :mtime
135
+ end
136
+
137
+ # Set last modification time.
138
+ #
139
+ # @param [Time] Time of last modification.
140
+ #
141
+ def mtime=(time)
142
+ invoke_backend :mtime=, internal_path, time
143
+ end
144
+
145
+ # Return file access time.
146
+ #
147
+ # @return [Time] Time of last access.
148
+ #
149
+ def atime
150
+ invoke_backend :atime
151
+ end
152
+
153
+ # Set last access time.
154
+ #
155
+ # @param [Time] Time of last access.
156
+ #
157
+ def atime=(time)
158
+ invoke_backend :atime=, internal_path, time
159
+ end
160
+
161
+ def mode
162
+ invoke_backend :mode
163
+ end
164
+
165
+ def chmod(mode)
166
+ invoke_backend :chmod, internal_path, mode
167
+ end
168
+
169
+ class << self
170
+ # Read or set process umask.
171
+ #
172
+ # @overload umask
173
+ # Read process umask.
174
+ #
175
+ # @return [Integer] Process umask.
176
+ #
177
+ # @overload umask(mask)
178
+ # Set process umask.
179
+ #
180
+ # @param mask [Integer] New process umask.
181
+ #
182
+ # @see File.umask
183
+ #
184
+ def umask(mask = nil)
185
+ if mask
186
+ invoke_backend :umask=, mask
187
+ else
188
+ invoke_backend :umask
189
+ end
190
+ end
191
+ alias umask= umask
192
+ end
193
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Path
4
+ # @!group File Predicates
5
+
6
+ # Check if path points to file.
7
+ #
8
+ # @return [Boolean] True if path is a file.
9
+ # @see ::File.file?
10
+ #
11
+ def file?
12
+ invoke_backend :file?
13
+ end
14
+
15
+ # Check if path points to an existing location.
16
+ #
17
+ # @return [Boolean] True if path exists.
18
+ # @see ::File.exists?
19
+ #
20
+ def exists?
21
+ invoke_backend :exists?
22
+ end
23
+ alias exist? exists?
24
+ alias existent? exists?
25
+
26
+ # Check if path points to a directory.
27
+ #
28
+ # @return [Boolean] True if path is a directory.
29
+ # @see ::File.directory?
30
+ #
31
+ def directory?
32
+ invoke_backend :directory?
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Path
4
+ # @!group Identity
5
+
6
+ # Return path as string. String will be duped before it gets returned and
7
+ # cannot be used to modify the path object.
8
+ #
9
+ # @return [String] Path as string.
10
+ def path
11
+ internal_path.dup
12
+ end
13
+ alias to_path path
14
+ alias to_str path
15
+ alias to_s path
16
+
17
+ # Return a useful object string representation.
18
+ #
19
+ # @return [String] Useful object representation
20
+ def inspect
21
+ "<#{self.class.name}:#{internal_path}>"
22
+ end
23
+
24
+ protected
25
+
26
+ # Return internal path object without duping.
27
+ #
28
+ # Must not be modified to not change internal state.
29
+ #
30
+ # @return [String] Internal path.
31
+ # @see #path
32
+ #
33
+ def internal_path
34
+ @path
35
+ end
36
+
37
+ # If arguments are provided the current path will be joined with given
38
+ # arguments to the result will be yielded. If no arguments are given the
39
+ # current path will be yielded.
40
+ #
41
+ # Internal helper method.
42
+ #
43
+ # @example
44
+ # def handle_both(*args)
45
+ # with_path(*args) do |path|
46
+ # # do something
47
+ # end
48
+ # end
49
+ #
50
+ # Returns whatever the block returns.
51
+ #
52
+ def with_path(*args)
53
+ if args.any?
54
+ yield join(*args)
55
+ else
56
+ yield self
57
+ end
58
+ end
59
+ end