rubypath 1.0.0 → 1.0.1

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