epath 0.0.1 → 0.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.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Path - Enhanced Pathname
1
+ # Path - a Path manipulation library
2
2
 
3
3
  Path is a library to manage paths.
4
4
  It is similar to Pathname, but has some extra goodness.
@@ -9,6 +9,11 @@ Paths are naturally the subject of their methods and even if they are simple Str
9
9
 
10
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
11
 
12
+ ## Links
13
+
14
+ * [GitHub](https://github.com/eregon/epath)
15
+ * [YARD Documentation](http://rubydoc.info/github/eregon/epath/master/file/README.md)
16
+
12
17
  ## API
13
18
 
14
19
  All the useful methods of `File` (and so `IO`) and `Dir` should be included.
@@ -26,10 +31,10 @@ Path.new('/usr', 'bin')
26
31
  ```
27
32
 
28
33
  ``` ruby
29
- Path.here # == Path(__FILE__).expand
30
- Path.dir # == Path(File.dirname(__FILE__)).expand
34
+ Path.file # == Path(__FILE__).expand
35
+ Path.dir # == Path(File.dirname(__FILE__)).expand
31
36
  Path.relative(path) # == Path(File.expand_path("../#{path}", __FILE__))
32
- Path.home # == Path(File.expand_path('~'))
37
+ Path.home # == Path(File.expand_path('~'))
33
38
  ```
34
39
 
35
40
  ### temporary
@@ -41,24 +46,55 @@ Path.tmpdir
41
46
 
42
47
  ### aliases
43
48
 
44
- * expand => expand_path
45
- * relative_to => relative_path_from
49
+ * expand => expand\_path
50
+ * relative\_to => relative\_path\_from
46
51
 
47
52
  ### parts
48
53
 
49
- * base: basename(extname)
50
- * dir: alias of dirname
51
- * ext: extname without the leading dot
52
- * /: join paths
54
+ Path can split a path in two ways:
55
+
56
+ The first way is the one done by File methods (dirname, basename, extname).
57
+
58
+ 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.
59
+ The rationale behind this is to have a true three-components path, splitting on the / and the . (See [this issue](https://github.com/eregon/epath/pull/8#issuecomment-3499030) for details)
60
+
61
+ dirname basename
62
+ ____________ ______
63
+ / \ / \
64
+ /some/path/dir/file.ext
65
+ \____________/ \__/ \_/
66
+ dir base ext
67
+
68
+ path = dirname / basename
69
+ path = dirname / basename(extname) extname
70
+ path = dir / base [. ext]
71
+
72
+ * dirname: "/some/path/dir"
73
+ * basename: "file.ext"
74
+ * extname: ".ext"
75
+
76
+ <!-- -->
77
+
78
+ * dir: alias of dirname: "/some/paths/dir"
79
+ * base: basename(extname), the basename without the extension: "file"
80
+ * ext: extname without the leading dot: "ext"
81
+
82
+ <!-- -->
83
+
84
+ ### join
85
+
86
+ * join(*parts)
87
+ * /: join paths (as Pathname#+)
53
88
 
54
89
  ```ruby
55
90
  Path('/usr')/'bin'
56
91
  ```
57
92
 
58
- These method names are too long, any idea to make them shorter and clear?
93
+ ### extensions
59
94
 
60
- * without_extension
61
- * replace_extension(new_ext)
95
+ * add\_ext / add\_extension
96
+ * rm\_ext / without\_extension
97
+ * sub\_ext(new\_ext) / replace\_extension(new\_ext)
62
98
 
63
99
  ### glob
64
100
 
@@ -67,13 +103,15 @@ These method names are too long, any idea to make them shorter and clear?
67
103
 
68
104
  ### structure
69
105
 
70
- * ancestors: self and all the parent directories
106
+ * ascend, ancestors: self and all the parent directories
107
+ * descend: in the reverse order
71
108
  * backfind: ascends the parents until it finds the given path
72
109
 
73
110
  ``` ruby
74
111
  # Path.backfind is Path.here.backfind
75
112
  Path.backfind('lib') # => Path's lib folder
76
- # it accepts XPath-like context
113
+
114
+ # It accepts XPath-like context
77
115
  Path.backfind('.[.git]') # => the root of this repository
78
116
  ```
79
117
 
@@ -86,12 +124,12 @@ Path.backfind('.[.git]') # => the root of this repository
86
124
  ### management
87
125
 
88
126
  * mkdir
89
- * mkdir_p
90
- * rm_rf
127
+ * mkdir\_p
128
+ * rm\_rf
91
129
 
92
- ### Incompatibilities with Pathname
130
+ ### require
93
131
 
94
- * #entries never returns . and ..
132
+ * Path.require\_tree: require all .rb files recursively (in alphabetic order)
95
133
 
96
134
  ## Status
97
135
 
@@ -101,6 +139,7 @@ This is still in the early development stage, you should expect many additions a
101
139
 
102
140
  Benoit Daloze - eregon
103
141
 
104
- ### Contributors
142
+ ## Contributors
105
143
 
106
- Bernard Lambeau - blambeau
144
+ Bernard Lambeau - blambeau
145
+ Ravil Bayramgalin - brainopia
@@ -5,5 +5,7 @@ Gem::Specification.new do |s|
5
5
  s.email = 'eregontp@gmail.com'
6
6
  s.homepage = 'https://github.com/eregon/epath'
7
7
  s.files = Dir['lib/**/*.rb'] + %w[README.md LICENSE epath.gemspec]
8
- s.version = '0.0.1'
8
+ s.version = '0.1.0'
9
+
10
+ s.add_development_dependency 'rspec'
9
11
  end
@@ -1,34 +1,20 @@
1
- # Enchanced Pathname
2
- # Use the composite pattern with a Pathname
1
+ # Path - a Path manipulation library
2
+
3
+ Dir.glob(File.expand_path('../epath/*.rb',__FILE__)) { |file| require file }
3
4
 
4
- require File.expand_path('../epath/implementation', __FILE__)
5
5
  require 'tempfile'
6
6
 
7
7
  class Path
8
- DOTS = %w[. ..]
9
-
10
8
  class << self
11
- def new(*args)
12
- if args.size == 1 and Path === args[0]
13
- args[0]
14
- else
15
- super(*args)
16
- end
17
- end
18
- alias_method :[], :new
19
-
20
- def to_proc
21
- lambda { |path| new(path) }
22
- end
23
-
24
- def here(from = nil)
25
- from ||= caller
26
- new(from.first.split(/:\d+(?:$|:in)/).first).expand
9
+ def file(from = nil)
10
+ from ||= caller # this can not be moved as a default argument, JRuby optimizes it
11
+ # v This : is there to define a group without capturing
12
+ new(from.first.rpartition(/:\d+(?:$|:in )/).first).expand
27
13
  end
28
- alias_method :file, :here
14
+ alias :here :file
29
15
 
30
16
  def dir(from = nil)
31
- from ||= caller
17
+ from ||= caller # this can not be moved as a default argument, JRuby optimizes it
32
18
  file(from).dir
33
19
  end
34
20
 
@@ -41,7 +27,7 @@ class Path
41
27
  end
42
28
 
43
29
  def backfind(path)
44
- here(caller).backfind(path)
30
+ file(caller).backfind(path)
45
31
  end
46
32
 
47
33
  def tmpfile(basename = '', tmpdir = nil, options = nil)
@@ -57,7 +43,7 @@ class Path
57
43
  end
58
44
  file
59
45
  end
60
- alias_method :tempfile, :tmpfile
46
+ alias :tempfile :tmpfile
61
47
 
62
48
  def tmpdir(prefix_suffix = nil, *rest)
63
49
  require 'tmpdir'
@@ -71,108 +57,27 @@ class Path
71
57
  end
72
58
  dir
73
59
  end
74
- end
75
-
76
- def initialize(*parts)
77
- path = parts.size > 1 ? parts.join(File::SEPARATOR) : parts.first
78
- if Tempfile === path
79
- @_tmpfile = path # We would not want it to be GC'd
80
- @path = path.path
81
- elsif String === path
82
- @path = path.dup
83
- else
84
- @path = path.to_s
85
- end
86
- taint if @path.tainted?
87
- end
88
-
89
- def / part
90
- join part.to_s
91
- end
92
-
93
- def base # basename(extname)
94
- basename(extname)
95
- end
96
-
97
- def ext # extname without leading .
98
- ext = extname
99
- ext.empty? ? ext : ext[1..-1]
100
- end
101
-
102
- def without_extension # rm_ext
103
- dir / base
104
- end
105
-
106
- # NOTE: Pathname has a similar feature named sub_ext
107
- # It might be a better method name
108
- def replace_extension(ext)
109
- ext = ".#{ext}" unless ext.start_with? '.'
110
- Path.new(without_extension.to_s + ext)
111
- end
112
60
 
113
- def entries
114
- (Dir.entries(@path) - DOTS).map { |entry| Path.new(@path, entry).cleanpath }
115
- end
116
-
117
- def glob(pattern, flags = 0)
118
- Dir.glob(join(pattern), flags).map(&Path)
119
- end
120
-
121
- def rm
122
- FileUtils.rm(@path)
123
- self
124
- end
125
-
126
- def rm_f
127
- FileUtils.rm_f(@path)
128
- self
129
- end
130
-
131
- def rm_rf
132
- FileUtils.rm_rf(@path)
133
- self
134
- end
135
-
136
- def mkdir_p
137
- FileUtils.mkdir_p(@path)
138
- self
139
- end
140
-
141
- def write(contents, open_args = nil)
142
- if IO.respond_to? :write
143
- IO.write(@path, contents, *[open_args].compact)
144
- else
145
- open('w', *[open_args].compact) { |f| f.write(contents) }
61
+ def tmpchdir(prefix_suffix = nil, *rest)
62
+ tmpdir do |dir|
63
+ dir.chdir do
64
+ yield dir
65
+ end
66
+ end
146
67
  end
147
68
  end
148
69
 
149
- def append(contents, open_args = nil)
150
- if IO.respond_to? :write
151
- open_args = (Array(open_args) << {:mode => 'a'})
152
- IO.write(@path, contents, *open_args.compact)
153
- else
154
- open('a', *[open_args].compact) { |f| f.write(contents) }
155
- end
156
- end
70
+ alias :/ :+
157
71
 
158
- def to_sym
159
- to_s.to_sym
160
- end
72
+ alias :relative_to :relative_path_from
73
+ alias :% :relative_path_from
161
74
 
162
- def relative_to other
163
- relative_path_from Path.new other
75
+ def inside? ancestor
76
+ @path == ancestor.to_s or @path.start_with?("#{ancestor}/")
164
77
  end
165
- alias_method :%, :relative_to
166
78
 
167
- def ancestors
168
- ancestors = lambda do |y|
169
- y << path = expand
170
- until (path = path.parent).root?
171
- y << path
172
- end
173
- y << path
174
- end
175
- RUBY_VERSION > '1.9' ? Enumerator.new(&ancestors) : ancestors.call([])
79
+ def outside? ancestor
80
+ !inside?(ancestor)
176
81
  end
177
82
 
178
83
  def backfind(path)
@@ -183,14 +88,20 @@ class Path
183
88
  result/path if result
184
89
  end
185
90
 
186
- alias_method :expand, :expand_path
187
- alias_method :dir, :dirname
188
- end
91
+ # Setup
92
+ register_loader 'yml', 'yaml' do |path|
93
+ require 'yaml'
94
+ YAML.load_file(path)
95
+ end
189
96
 
190
- EPath = Path # to meet everyone's expectations
97
+ register_loader 'json' do |path|
98
+ require 'json'
99
+ JSON.load(path.read)
100
+ end
191
101
 
192
- unless defined? NO_EPATH_GLOBAL_FUNCTION
193
- def Path(*args)
194
- Path.new(*args)
102
+ register_loader 'gemspec' do |path|
103
+ eval path.read
195
104
  end
196
105
  end
106
+
107
+ EPath = Path # to meet everyone's expectations
@@ -0,0 +1,123 @@
1
+ class Path
2
+ class << self
3
+ # Returns or yields Path objects. See +Dir.glob+.
4
+ def glob(*args) # :yield: path
5
+ if block_given?
6
+ Dir.glob(*args) { |f| yield new(f) }
7
+ else
8
+ Dir.glob(*args).map(&Path)
9
+ end
10
+ end
11
+
12
+ # Returns the current working directory as a Path. See +Dir.getwd+.
13
+ def Path.getwd
14
+ new Dir.getwd
15
+ end
16
+ alias :pwd :getwd
17
+ end
18
+
19
+ # Iterates over the entries (files and subdirectories) in the directory.
20
+ # It yields a Path object for each entry.
21
+ def each_entry(&block) # :yield: path
22
+ Dir.foreach(@path) { |f| yield Path.new(f) }
23
+ end
24
+
25
+ # Create the referenced directory and returns self. See +Dir.mkdir+.
26
+ def mkdir(*args)
27
+ Dir.mkdir(@path, *args)
28
+ self
29
+ end
30
+
31
+ # Remove the referenced directory. See +Dir.rmdir+.
32
+ def rmdir
33
+ Dir.rmdir(@path)
34
+ end
35
+
36
+ # See +Dir.open+.
37
+ def opendir(&block) # :yield: dir
38
+ Dir.open(@path, &block)
39
+ end
40
+
41
+ def glob(pattern, flags = 0)
42
+ Dir.glob(join(pattern), flags).map(&Path)
43
+ end
44
+
45
+ # [DEPRECATED] Return the entries (files and subdirectories) in the directory.
46
+ # Each Path only contains the filename.
47
+ # The result may contain the current directory #<Path .> and the parent directory #<Path ..>.
48
+ #
49
+ # Path('/usr/local').entries
50
+ # # => [#<Path share>, #<Path lib>, #<Path .>, #<Path ..>, <Path bin>, ...]
51
+ #
52
+ # This method is deprecated, since it is too low level and likely useless in Ruby.
53
+ # But it is there for the sake of compatibility with Dir.entries (and Pathname#entries)
54
+ #
55
+ # Use #children instead.
56
+ def entries
57
+ Dir.entries(@path).map(&Path)
58
+ end
59
+
60
+ def chdir(&block)
61
+ Dir.chdir(@path, &block)
62
+ end
63
+
64
+ # Returns the children of the directory (files and subdirectories, not
65
+ # recursive) as an array of Path objects. By default, the returned
66
+ # paths will have enough information to access the files. If you set
67
+ # +with_directory+ to +false+, then the returned paths will contain the
68
+ # filename only.
69
+ #
70
+ # For example:
71
+ # pn = Path("/usr/lib/ruby/1.8")
72
+ # pn.children
73
+ # # -> [ #<Path /usr/lib/ruby/1.8/English.rb>,
74
+ # #<Path /usr/lib/ruby/1.8/Env.rb>,
75
+ # #<Path /usr/lib/ruby/1.8/abbrev.rb>, ... ]
76
+ # pn.children(false)
77
+ # # -> [ #<Path English.rb>, #<Path Env.rb>, #<Path abbrev.rb>, ... ]
78
+ #
79
+ # Note that the results never contain the entries +.+ and +..+ in
80
+ # the directory because they are not children.
81
+ def children(with_directory=true)
82
+ with_directory = false if @path == '.'
83
+ result = []
84
+ Dir.foreach(@path) { |e|
85
+ next if e == '.' || e == '..'
86
+ if with_directory
87
+ result << Path.new(@path, e)
88
+ else
89
+ result << Path.new(e)
90
+ end
91
+ }
92
+ result
93
+ end
94
+
95
+ # Iterates over the children of the directory
96
+ # (files and subdirectories, not recursive).
97
+ # It yields Path object for each child.
98
+ # By default, the yielded paths will have enough information to access the files.
99
+ # If you set +with_directory+ to +false+, then the returned paths will contain the filename only.
100
+ #
101
+ # Path("/usr/local").each_child { |f| p f } # =>
102
+ # #<Path /usr/local/share>
103
+ # #<Path /usr/local/bin>
104
+ # #<Path /usr/local/games>
105
+ # #<Path /usr/local/lib>
106
+ # #<Path /usr/local/include>
107
+ # #<Path /usr/local/sbin>
108
+ # #<Path /usr/local/src>
109
+ # #<Path /usr/local/man>
110
+ #
111
+ # Path("/usr/local").each_child(false) { |f| p f } # =>
112
+ # #<Path share>
113
+ # #<Path bin>
114
+ # #<Path games>
115
+ # #<Path lib>
116
+ # #<Path include>
117
+ # #<Path sbin>
118
+ # #<Path src>
119
+ # #<Path man>
120
+ def each_child(with_directory=true, &b)
121
+ children(with_directory).each(&b)
122
+ end
123
+ end