path 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011-2012 Benoit Daloze
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,183 @@
1
+ # Path - a Path manipulation library
2
+
3
+ [Path](http://rubydoc.info/github/eregon/path/master/Path) is a library to manage paths.
4
+ It is similar to Pathname, but has some extra goodness.
5
+ The method names are intended to be short and explicit, and avoid too much duplication like having 'name' or 'path' in the method name.
6
+
7
+ I believe the object-oriented approach to manipulate paths is very elegant and useful.
8
+ Paths are naturally the subject of their methods and even if they are simple Strings behind, they carry way much more information and deserve a first-class status.
9
+
10
+ Also, using a path library like this avoid to remember in which class the functionality is implemented, everything is in one place (if not, please open an issue!).
11
+
12
+ ## Installation
13
+
14
+ gem install path
15
+
16
+ ## Links
17
+
18
+ * [GitHub](https://github.com/eregon/path)
19
+ * [YARD Documentation](http://rubydoc.info/github/eregon/path/master/file/README.md)
20
+ * [Changelog](https://github.com/eregon/path/blob/master/Changelog.md)
21
+
22
+ ## API
23
+
24
+ See the [Path](http://rubydoc.info/github/eregon/path/master/Path) class documentation for details.
25
+
26
+ All the useful methods of `File` (and so `IO`) and `Dir` should be included.
27
+ Most methods of `FileUtils` should be there too.
28
+
29
+ ### creation
30
+
31
+ ``` ruby
32
+ Path.new('/usr/bin')
33
+ Path['/usr/bin']
34
+ Path('/usr/bin') # unless NO_PATH_GLOBAL_FUNCTION is defined
35
+
36
+ Path.new('~myuser/path') # expanded if it begins with ~
37
+
38
+ # Separators are replaced by / on systems having File::ALT_SEPARATOR
39
+ Path.new('win\sepa\rator') # => #<Path win/sepa/rator>
40
+
41
+ Path.new('/usr', 'bin')
42
+ %w[foo bar].map(&Path) # => [Path('foo'), Path('bar')]
43
+ ```
44
+
45
+ ``` ruby
46
+ Path.file # == Path(__FILE__).expand
47
+ Path.dir # == Path(File.dirname(__FILE__)).expand
48
+ Path.relative(path) # == Path(File.expand_path("../#{path}", __FILE__))
49
+ Path.home or Path.~ # == Path(File.expand_path('~'))
50
+ Path.~(user) # == Path(File.expand_path("~#{user}"))
51
+ ```
52
+
53
+ ### temporary
54
+
55
+ ``` ruby
56
+ Path.tmpfile
57
+ Path.tmpdir
58
+ ```
59
+
60
+ ### aliases
61
+
62
+ * expand => expand\_path
63
+ * relative\_to => relative\_path\_from
64
+
65
+ ### parts
66
+
67
+ Path can split a path in two ways:
68
+
69
+ The first way is the one done by File methods (dirname, basename, extname).
70
+
71
+ The second is Path's own way in which the base is given without the extension and the extension is given without the leading dot.
72
+ The rationale behind this is to have a true three-components path, splitting on the / and the . (See [this issue](https://github.com/eregon/path/pull/8#issuecomment-3499030) for details)
73
+
74
+ dirname basename
75
+ ____________ ______
76
+ / \ / \
77
+ /some/path/dir/file.ext
78
+ \____________/ \__/ \_/
79
+ dir base ext
80
+
81
+ path = dirname / basename
82
+ path = dirname / basename(extname) extname
83
+ path = dir / base [. ext]
84
+
85
+ * dirname: "/some/path/dir"
86
+ * basename: "file.ext"
87
+ * extname: ".ext"
88
+
89
+ <!-- -->
90
+
91
+ * dir: alias of dirname: "/some/paths/dir"
92
+ * base: basename(extname), the basename without the extension: "file"
93
+ * ext: extname without the leading dot: "ext"
94
+
95
+ <!-- -->
96
+
97
+ ### join
98
+
99
+ * join(*parts)
100
+ * /: join paths (as Pathname#+)
101
+
102
+ ```ruby
103
+ Path('/usr')/'bin'
104
+ ```
105
+
106
+ ### extensions
107
+
108
+ * add\_ext / add\_extension
109
+ * rm\_ext / without\_extension
110
+ * sub\_ext(new\_ext) / replace\_extension(new\_ext)
111
+
112
+ ### glob
113
+
114
+ * children: files under self, without . and ..
115
+ * glob: relative glob to self, yield absolute paths
116
+
117
+ ### structure
118
+
119
+ * parent: parent directory (don't use #dirname more than once, use #parent instead)
120
+ * ascend, ancestors: self and all the parent directories
121
+ * descend: in the reverse order
122
+ * backfind: ascends the parents until it finds the given path
123
+
124
+ ``` ruby
125
+ # Path.backfind is Path.here.backfind
126
+ Path.backfind('lib') # => Path's lib folder
127
+
128
+ # It accepts XPath-like context
129
+ Path.backfind('.[.git]') # => the root of this repository
130
+ ```
131
+
132
+ ### IO
133
+
134
+ * read
135
+ * write(contents)
136
+ * append(contents)
137
+
138
+ ### management
139
+
140
+ * mkdir
141
+ * mkdir\_p
142
+ * rm\_rf
143
+
144
+ ### require
145
+
146
+ * Path.require\_tree: require all .rb files recursively (in alphabetic order)
147
+
148
+ ### relocate
149
+
150
+ ``` ruby
151
+ from = Path('pictures')
152
+ to = Path('output/public/thumbnails')
153
+ earth = Path('pictures/nature/earth.jpg')
154
+
155
+ earth.relocate(from, to, '.png') { |rel| "#{rel}-200" }
156
+ # => #<Path output/public/thumbnails/nature/earth-200.png>
157
+ ```
158
+
159
+ ## Transition from String/Pathname
160
+
161
+ One aim of Path is to help the user make the transition coming from
162
+ String (not using a path library), Pathname, or another library.
163
+
164
+ To this intend, [`Path + config`](http://rubydoc.info/github/eregon/path/master/Path#%2B-class_method) allows to configure the behavior of `Path#+`.
165
+
166
+ Coming from String, one should use `Path + :string`, and run ruby with the verbose option (`-w`),
167
+ which will show where `+` is used as String concatenation.
168
+
169
+ Coming from a path library using `+` as #join, one should just use the default (`Path + :warning`),
170
+ which will show where `+` is used.
171
+
172
+ ## Status
173
+
174
+ This is still in the early development stage, you should expect many additions and some changes.
175
+
176
+ ## Author
177
+
178
+ Benoit Daloze - eregon
179
+
180
+ ## Contributors
181
+
182
+ Bernard Lambeau - blambeau
183
+ Ravil Bayramgalin - brainopia
@@ -0,0 +1,2 @@
1
+ # For compatibility with old gem name
2
+ require File.expand_path('../path.rb', __FILE__)
@@ -0,0 +1,159 @@
1
+ # Path - a Path manipulation library
2
+
3
+ Dir.glob(File.expand_path('../path/*.rb',__FILE__)) { |file| require file }
4
+
5
+ require 'tempfile'
6
+
7
+ class Path
8
+ class << self
9
+ # {Path} to the current file +Path(__FILE__)+.
10
+ def file(from = nil)
11
+ from ||= caller # this can not be moved as a default argument, JRuby optimizes it
12
+ # v This : is there to define a group without capturing
13
+ new(from.first.rpartition(/:\d+(?:$|:in )/).first).expand
14
+ end
15
+ alias :here :file
16
+
17
+ # {Path} to the directory of this file: +Path(__FILE__).dir+.
18
+ def dir(from = nil)
19
+ from ||= caller # this can not be moved as a default argument, JRuby optimizes it
20
+ file(from).dir
21
+ end
22
+
23
+ # {Path} relative to the directory of this file.
24
+ def relative(path, from = nil)
25
+ from ||= caller # this can not be moved as a default argument, JRuby optimizes it
26
+ new(path).expand dir(from)
27
+ end
28
+
29
+ # A {Path} to the home directory of +user+ (defaults to the current user).
30
+ # The form with an argument (+user+) is not supported on Windows.
31
+ def ~(user = '')
32
+ new("~#{user}")
33
+ end
34
+ alias :home :~
35
+
36
+ # Same as +Path.file.backfind(path)+. See {#backfind}.
37
+ def backfind(path)
38
+ file(caller).backfind(path)
39
+ end
40
+
41
+ # @yieldparam [Path] tmpfile
42
+ def tmpfile(basename = '', tmpdir = nil, options = nil)
43
+ tempfile = Tempfile.new(basename, *[tmpdir, options].compact)
44
+ file = new tempfile
45
+ if block_given?
46
+ begin
47
+ yield file
48
+ ensure
49
+ tempfile.close!
50
+ end
51
+ end
52
+ file
53
+ end
54
+ alias :tempfile :tmpfile
55
+
56
+ # @yieldparam [Path] tmpdir
57
+ def tmpdir(prefix_suffix = nil, *rest)
58
+ require 'tmpdir'
59
+ dir = new Dir.mktmpdir(prefix_suffix, *rest)
60
+ if block_given?
61
+ begin
62
+ yield dir
63
+ ensure
64
+ FileUtils.remove_entry_secure(dir) rescue nil
65
+ end
66
+ end
67
+ dir
68
+ end
69
+
70
+ # @yieldparam [Path] tmpdir
71
+ def tmpchdir(prefix_suffix = nil, *rest)
72
+ tmpdir do |dir|
73
+ dir.chdir do
74
+ yield dir
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # Whether +self+ is inside +ancestor+, such that +ancestor+ is an ancestor of +self+.
81
+ # This is pure String manipulation. Paths should be absolute.
82
+ # +self+ is considered to be inside itself.
83
+ def inside? ancestor
84
+ @path == ancestor.to_s or @path.start_with?("#{ancestor}/")
85
+ end
86
+
87
+ # The opposite of {#inside?}.
88
+ def outside? ancestor
89
+ !inside?(ancestor)
90
+ end
91
+
92
+ # Ascends the parents until it finds the given +path+.
93
+ #
94
+ # Path.backfind('lib') # => the lib folder
95
+ #
96
+ # It accepts an XPath-like context:
97
+ #
98
+ # Path.backfind('.[.git]') # => the root of the repository
99
+ def backfind(path)
100
+ condition = path[/\[(.*)\]$/, 1] || ''
101
+ path = $` unless condition.empty?
102
+
103
+ result = ancestors.find { |ancestor| (ancestor/path/condition).exist? }
104
+ result/path if result
105
+ end
106
+
107
+ # Relocates this path somewhere else.
108
+ #
109
+ # Without a block, this method is a simple shorcut for a longer
110
+ # expression that proves difficult to remember in practice:
111
+ #
112
+ # to / (self.sub_ext(new_ext) % from)
113
+ #
114
+ # That is, it relocates the original path to a target folder +to+
115
+ # appended with the relative path from a source folder +from+. An
116
+ # optional new extension can also be specified, as it is a common
117
+ # use case.
118
+ #
119
+ # With a block, the relative path is passed to the block for user
120
+ # update, without the last extension. +new_ext+ is added after (or
121
+ # the original extension if not provided).
122
+ #
123
+ # from = Path('pictures')
124
+ # to = Path('output/public/thumbnails')
125
+ # earth = from / 'nature/earth.jpg'
126
+ #
127
+ # earth.relocate(from, to)
128
+ # # => #<Path output/public/thumbnails/nature/earth.jpg>
129
+ #
130
+ # earth.relocate(from, to, '.png') { |rel|
131
+ # "#{rel}-200"
132
+ # }
133
+ # # => #<Path output/public/thumbnails/nature/earth-200.png>
134
+ def relocate(from, to, new_ext = ext, &updater)
135
+ updater ||= lambda { |path| path }
136
+ renamer = lambda { |rel|
137
+ Path(updater.call(rel.rm_ext)).add_ext(new_ext)
138
+ }
139
+ to / renamer.call(self % from)
140
+ end
141
+
142
+ # Setup
143
+ register_loader 'yml', 'yaml' do |path|
144
+ require 'yaml'
145
+ YAML.load_file(path)
146
+ end
147
+
148
+ register_loader 'json' do |path|
149
+ require 'json'
150
+ JSON.load(path.read)
151
+ end
152
+
153
+ register_loader 'gemspec' do |path|
154
+ eval path.read
155
+ end
156
+ end
157
+
158
+ # to meet everyone's expectations and for compatibility with old gem name
159
+ EPath = Path
@@ -0,0 +1,60 @@
1
+ class Path
2
+ private
3
+
4
+ if File.respond_to?(:realpath) and File.respond_to?(:realdirpath)
5
+ def real_path_internal(strict = false, basedir = nil)
6
+ strict ? File.realpath(@path, basedir) : File.realdirpath(@path, basedir)
7
+ end
8
+ else
9
+ def realpath_rec(prefix, unresolved, h, strict, last = true)
10
+ resolved = []
11
+ until unresolved.empty?
12
+ n = unresolved.shift
13
+ if n == '..'
14
+ resolved.pop
15
+ else
16
+ path = prepend_prefix(prefix, resolved + [n])
17
+ if h.include? path
18
+ if h[path] == :resolving
19
+ raise Errno::ELOOP.new(path)
20
+ else
21
+ prefix, *resolved = h[path]
22
+ end
23
+ else
24
+ begin
25
+ s = File.lstat(path)
26
+ rescue Errno::ENOENT => e
27
+ raise e if strict || !last || !unresolved.empty?
28
+ resolved << n
29
+ break
30
+ end
31
+ if s.symlink?
32
+ h[path] = :resolving
33
+ link_prefix, link_names = split_names(File.readlink(path))
34
+ if link_prefix == '' # if link is relative
35
+ link_prefix, link_names = prefix, resolved.concat(link_names)
36
+ end
37
+ prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h, strict, unresolved.empty?)
38
+ else
39
+ resolved << n
40
+ h[path] = [prefix, *resolved]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ return prefix, *resolved
46
+ end
47
+
48
+ def real_path_internal(strict = false, basedir = nil)
49
+ path = @path
50
+ path = File.join(basedir, path) if basedir and relative?
51
+ prefix, names = split_names(path)
52
+ if prefix == ''
53
+ prefix, names2 = split_names(Dir.pwd)
54
+ names = names2.concat(names)
55
+ end
56
+ prefix, *names = realpath_rec(prefix, names, {}, strict)
57
+ prepend_prefix(prefix, names)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,147 @@
1
+ class Path
2
+ class << self
3
+ # @!group Directory
4
+
5
+ # Returns or yields Path objects. See +Dir.glob+.
6
+ # @yieldparam [Path] path
7
+ def glob(pattern, flags = 0)
8
+ if block_given?
9
+ Dir.glob(pattern, flags) { |f| yield new(f) }
10
+ else
11
+ Dir.glob(pattern, flags).map(&Path)
12
+ end
13
+ end
14
+
15
+ # Returns the current working directory as a Path. See +Dir.getwd+.
16
+ def Path.getwd
17
+ new Dir.getwd
18
+ end
19
+ alias :pwd :getwd
20
+ end
21
+
22
+ # @!group Directory
23
+
24
+ # Iterates over the entries (files and subdirectories) in the directory.
25
+ #
26
+ # Path("/usr/local").each_entry { |entry| p entry } # =>
27
+ # #<Path .>
28
+ # #<Path ..>
29
+ # #<Path lib>
30
+ # #<Path share>
31
+ # # ...
32
+ #
33
+ # @deprecated Use {#each_child} instead.
34
+ # This method is deprecated since it is too low level and likely useless in Ruby.
35
+ # But it is there for the sake of compatibility with Dir.foreach and Pathname#each_entry.
36
+ # @yieldparam [Path] entry
37
+ def each_entry(&block)
38
+ Dir.foreach(@path) { |f| yield Path.new(f) }
39
+ end
40
+
41
+ # Create the referenced directory and returns self. See +Dir.mkdir+.
42
+ def mkdir(*args)
43
+ Dir.mkdir(@path, *args)
44
+ self
45
+ end
46
+
47
+ # Remove the referenced directory. See +Dir.rmdir+.
48
+ def rmdir
49
+ Dir.rmdir(@path)
50
+ end
51
+
52
+ # See +Dir.open+.
53
+ # @yieldparam [Dir] dir
54
+ def opendir(&block)
55
+ Dir.open(@path, &block)
56
+ end
57
+
58
+ # Returns or yields Path objects. See +Dir.glob+.
59
+ # @yieldparam [Path] path
60
+ def glob(pattern, flags = 0)
61
+ if block_given?
62
+ Dir.glob(join(pattern), flags) { |f| yield Path.new(f) }
63
+ else
64
+ Dir.glob(join(pattern), flags).map(&Path)
65
+ end
66
+ end
67
+
68
+ # Return the entries (files and subdirectories) in the directory.
69
+ # Each Path only contains the filename.
70
+ # The result may contain the current directory #<Path .> and the parent directory #<Path ..>.
71
+ #
72
+ # Path('/usr/local').entries
73
+ # # => [#<Path share>, #<Path lib>, #<Path .>, #<Path ..>, <Path bin>, ...]
74
+ #
75
+ # @deprecated Use {#children} instead.
76
+ # This method is deprecated since it is too low level and likely useless in Ruby.
77
+ # But it is there for the sake of compatibility with Dir.entries (and Pathname#entries).
78
+ def entries
79
+ Dir.entries(@path).map(&Path)
80
+ end
81
+
82
+ # Changes the current working directory of the process to self. See Dir.chdir.
83
+ # The recommended way to use it is to use the block form, or not use it all!
84
+ def chdir(&block)
85
+ Dir.chdir(@path, &block)
86
+ end
87
+
88
+ # Returns the children of the directory (files and subdirectories, not
89
+ # recursive) as an array of Path objects. By default, the returned
90
+ # paths will have enough information to access the files. If you set
91
+ # +with_directory+ to +false+, then the returned paths will contain the
92
+ # filename only.
93
+ #
94
+ # For example:
95
+ # pn = Path("/usr/lib/ruby/1.8")
96
+ # pn.children
97
+ # # -> [ #<Path /usr/lib/ruby/1.8/English.rb>,
98
+ # #<Path /usr/lib/ruby/1.8/Env.rb>,
99
+ # #<Path /usr/lib/ruby/1.8/abbrev.rb>, ... ]
100
+ # pn.children(false)
101
+ # # -> [ #<Path English.rb>, #<Path Env.rb>, #<Path abbrev.rb>, ... ]
102
+ #
103
+ # Note that the results never contain the entries +.+ and +..+ in
104
+ # the directory because they are not children.
105
+ def children(with_directory=true)
106
+ with_directory = false if @path == '.'
107
+ result = []
108
+ Dir.foreach(@path) { |e|
109
+ next if e == '.' || e == '..'
110
+ if with_directory
111
+ result << Path.new(@path, e)
112
+ else
113
+ result << Path.new(e)
114
+ end
115
+ }
116
+ result
117
+ end
118
+
119
+ # Iterates over the children of the directory (files and subdirectories, not recursive).
120
+ # By default, the yielded paths will have enough information to access the files.
121
+ # If you set +with_directory+ to +false+, then the returned paths will contain the filename only.
122
+ #
123
+ # Path("/usr/local").each_child { |f| p f } # =>
124
+ # #<Path /usr/local/share>
125
+ # #<Path /usr/local/bin>
126
+ # #<Path /usr/local/games>
127
+ # #<Path /usr/local/lib>
128
+ # #<Path /usr/local/include>
129
+ # #<Path /usr/local/sbin>
130
+ # #<Path /usr/local/src>
131
+ # #<Path /usr/local/man>
132
+ #
133
+ # Path("/usr/local").each_child(false) { |f| p f } # =>
134
+ # #<Path share>
135
+ # #<Path bin>
136
+ # #<Path games>
137
+ # #<Path lib>
138
+ # #<Path include>
139
+ # #<Path sbin>
140
+ # #<Path src>
141
+ # #<Path man>
142
+ #
143
+ # @yieldparam [Path] child
144
+ def each_child(with_directory=true, &b)
145
+ children(with_directory).each(&b)
146
+ end
147
+ end