rubypath 0.2.1 → 0.3.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 +12 -4
- data/doc/file.README.html +74 -3
- data/lib/rubypath/backend/mock.rb +41 -0
- data/lib/rubypath/backend/sys.rb +20 -0
- data/lib/rubypath/backend.rb +5 -0
- data/lib/rubypath/comparison.rb +4 -7
- data/lib/rubypath/dir_operations.rb +72 -0
- data/lib/rubypath/file_operations.rb +26 -0
- data/lib/rubypath/path_operations.rb +20 -3
- data/lib/rubypath/version.rb +2 -2
- data/spec/rubypath/comparison_spec.rb +32 -1
- data/spec/rubypath/dir_operations_spec.rb +78 -1
- data/spec/rubypath/file_operations_spec.rb +39 -0
- data/spec/rubypath/identity_spec.rb +1 -1
- data/spec/rubypath/path_operations_spec.rb +49 -14
- data/spec/support/with_backend.rb +17 -11
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b6a50c0dd1eea579ab534a5b321a38265bd9c4f6
         | 
| 4 | 
            +
              data.tar.gz: ad371190f1cc868eeba76d652302445f515dddfb
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 97edfdceb6a0bea7103f0f1923a36b9c1fdba7a604672910c7162886c701475b6bdb1975875d3d4bd42f75663e7f46ec34114d9a22208fa07b9528b33f40e82b
         | 
| 7 | 
            +
              data.tar.gz: c4dbb1749cbc28cb73411cf387f39b10bf78a114f1b3d883b32d8ef13514e17878fc66f4a75b32a947443dbfb639e2a576fade52fb41481bc1e0248ec40581c3
         | 
    
        data/README.md
    CHANGED
    
    | @@ -6,7 +6,7 @@ | |
| 6 6 | 
             
            [](https://gemnasium.com/jgraichen/rubypath)
         | 
| 7 7 | 
             
            [](http://rubydoc.info/github/jgraichen/rubypath/master/frames)
         | 
| 8 8 |  | 
| 9 | 
            -
            *Ruby Path* introduces a global `Path` class unifying most `File`, `Dir`, `FileUtils`, `Pathname` and `IO` operations with a flexible and powerful Object-Interface and still adding new useful methods and functions like mocking a  | 
| 9 | 
            +
            *Ruby Path* introduces a global `Path` class unifying most `File`, `Dir`, `FileUtils`, `Pathname` and `IO` operations with a flexible and powerful Object-Interface and still adding new useful methods and functions like mocking a whole file system for fast and reliable testing.
         | 
| 10 10 |  | 
| 11 11 | 
             
            ## Installation
         | 
| 12 12 |  | 
| @@ -79,9 +79,17 @@ See full API documentation here: http://rubydoc.info/gems/rubypath/Path | |
| 79 79 |  | 
| 80 80 | 
             
            1. Fork it
         | 
| 81 81 | 
             
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 82 | 
            -
            3.  | 
| 83 | 
            -
            4.  | 
| 84 | 
            -
            5.  | 
| 82 | 
            +
            3. Add specs testing SYS *and* MOCK file system
         | 
| 83 | 
            +
            4. Commit your specs (`git commit -am 'Add specs for feature'`)
         | 
| 84 | 
            +
            5. Add our changes for SYS *and* MOCK file system
         | 
| 85 | 
            +
            6. Commit your changes (`git commit -am 'Add some feature'`)
         | 
| 86 | 
            +
            7. Push to the branch (`git push origin my-new-feature`)
         | 
| 87 | 
            +
            8. Create new Pull Request
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            ### ToDos
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            * Add missing methods
         | 
| 92 | 
            +
            * Improve MOCK FS implementation
         | 
| 85 93 |  | 
| 86 94 | 
             
            ## License
         | 
| 87 95 |  | 
    
        data/doc/file.README.html
    CHANGED
    
    | @@ -63,7 +63,13 @@ | |
| 63 63 |  | 
| 64 64 | 
             
                <div id="content"><div id='filecontents'><h1>Ruby Path</h1>
         | 
| 65 65 |  | 
| 66 | 
            -
            <p>< | 
| 66 | 
            +
            <p><a href="http://badge.fury.io/rb/rubypath"><img src="https://badge.fury.io/rb/rubypath.svg" alt="Gem Version"></a>
         | 
| 67 | 
            +
            <a href="https://travis-ci.org/jgraichen/rubypath"><img src="http://img.shields.io/travis/jgraichen/rubypath/master.svg" alt="Build Status"></a>
         | 
| 68 | 
            +
            <a href="https://coveralls.io/r/jgraichen/rubypath"><img src="http://img.shields.io/coveralls/jgraichen/rubypath/master.svg" alt="Coverage Status"></a>
         | 
| 69 | 
            +
            <a href="https://gemnasium.com/jgraichen/rubypath"><img src="http://img.shields.io/gemnasium/jgraichen/rubypath.svg" alt="Dependency Status"></a>
         | 
| 70 | 
            +
            <a href="http://rubydoc.info/github/jgraichen/rubypath/master/frames"><img src="http://img.shields.io/badge/rubydoc-here-blue.svg" alt="RubyDoc Documentation"></a></p>
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            <p><em>Ruby Path</em> introduces a global <code>Path</code> class unifying most <code>File</code>, <code>Dir</code>, <code>FileUtils</code>, <code>Pathname</code> and <code>IO</code> operations with a flexible and powerful Object-Interface and still adding new useful methods and functions like mocking a whole file system for fast and reliable testing.</p>
         | 
| 67 73 |  | 
| 68 74 | 
             
            <h2>Installation</h2>
         | 
| 69 75 |  | 
| @@ -71,18 +77,83 @@ | |
| 71 77 |  | 
| 72 78 | 
             
            <h2>Usage</h2>
         | 
| 73 79 |  | 
| 74 | 
            -
            <p> | 
| 80 | 
            +
            <p>Using <code>Path</code> with file and directory methods:</p>
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_base'>base</span> <span class='op'>=</span> <span class='const'>Path</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/path/to/base</span><span class='tstring_end'>'</span></span>
         | 
| 83 | 
            +
            <span class='id identifier rubyid_src'>src</span>  <span class='op'>=</span> <span class='id identifier rubyid_base'>base</span><span class='period'>.</span><span class='id identifier rubyid_mkpath'>mkpath</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>project/src</span><span class='tstring_end'>'</span></span>
         | 
| 84 | 
            +
            <span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_touch'>touch</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Rakefile</span><span class='tstring_end'>'</span></span>
         | 
| 85 | 
            +
            <span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_mkdir'>mkdir</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>lib</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_mkdir'>mkdir</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>mylib</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_touch'>touch</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>version.rb</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
         | 
| 86 | 
            +
            <span class='comment'>#=> <Path '/path/to/base/project/src/lib/mylib/version.rb'
         | 
| 87 | 
            +
            </span></code></pre>
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            <p>Using IO:</p>
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_write'>write</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>module Mylib\n  VERSION = '0.1.0'\nend</span><span class='tstring_end'>"</span></span>
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            <span class='id identifier rubyid_src'>src</span><span class='period'>.</span><span class='id identifier rubyid_lookup'>lookup</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>project.yml</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_read'>read</span>
         | 
| 94 | 
            +
            <span class='comment'>#=> "..."
         | 
| 95 | 
            +
            </span></code></pre>
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            <h3>Mock FS in tests</h3>
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            <p>Wrap specific or just all specs in a virtual filesystem:</p>
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            <pre class="code ruby"><code class="ruby"><span class='comment'># spec_helper.rb
         | 
| 102 | 
            +
            </span>
         | 
| 103 | 
            +
            <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_around'>around</span><span class='lparen'>(</span><span class='symbol'>:each</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_example'>example</span><span class='op'>|</span>
         | 
| 104 | 
            +
              <span class='const'>Path</span><span class='op'>::</span><span class='const'>Backend</span><span class='period'>.</span><span class='id identifier rubyid_mock'>mock</span> <span class='label'>root:</span> <span class='symbol'>:tmp</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_example'>example</span>
         | 
| 105 | 
            +
            <span class='kw'>end</span>
         | 
| 106 | 
            +
            </code></pre>
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            <p>Supported options for <code>:root</code> are <code>:tmp</code> using the real filesystem but scoping all actions into a temporary directory similar to chroot or a custom defined path to use as "chroot" directory. This mode does not allow to stub users, home directories and some attributes.</p>
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            <p>If not <code>:root</code> is specified a completely virtual in-memory filesystem will be used. This backend allows to even specify available users and home directories, the current user etc.</p>
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            <p>You can then define a specific scenario in your specs:</p>
         | 
| 113 | 
            +
             | 
| 114 | 
            +
            <pre class="code ruby"><code class="ruby">    <span class='id identifier rubyid_before'>before</span> <span class='kw'>do</span>
         | 
| 115 | 
            +
                  <span class='const'>Path</span><span class='period'>.</span><span class='id identifier rubyid_mock'>mock</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_root'>root</span><span class='comma'>,</span> <span class='id identifier rubyid_backend'>backend</span><span class='op'>|</span>
         | 
| 116 | 
            +
                    <span class='id identifier rubyid_backend'>backend</span><span class='period'>.</span><span class='id identifier rubyid_cwd'>cwd</span>          <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/root</span><span class='tstring_end'>'</span></span>
         | 
| 117 | 
            +
                    <span class='id identifier rubyid_backend'>backend</span><span class='period'>.</span><span class='id identifier rubyid_current_user'>current_user</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>test</span><span class='tstring_end'>'</span></span>
         | 
| 118 | 
            +
                    <span class='id identifier rubyid_backend'>backend</span><span class='period'>.</span><span class='id identifier rubyid_homes'>homes</span>        <span class='op'>=</span> <span class='lbrace'>{</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>test</span><span class='tstring_end'>'</span></span> <span class='op'>=></span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/home/test</span><span class='tstring_end'>'</span></span><span class='rbrace'>}</span>
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    <span class='id identifier rubyid_home'>home</span> <span class='op'>=</span> <span class='id identifier rubyid_root'>root</span><span class='period'>.</span><span class='id identifier rubyid_mkpath'>mkpath</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/home/test</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
         | 
| 121 | 
            +
                    <span class='id identifier rubyid_home'>home</span><span class='period'>.</span><span class='id identifier rubyid_mkfile'>mkfile</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>src/test.txt</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_write'>write</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>CONTENT</span><span class='tstring_end'>'</span></span>
         | 
| 122 | 
            +
                    <span class='id identifier rubyid_home'>home</span><span class='period'>.</span><span class='id identifier rubyid_mkfile'>mkfile</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>src/test.html</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_write'>write</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'><html><head><title></title>...</span><span class='tstring_end'>'</span></span>
         | 
| 123 | 
            +
                  <span class='kw'>end</span>
         | 
| 124 | 
            +
                <span class='kw'>end</span>
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                <span class='id identifier rubyid_it'>it</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>should mock all FS</span><span class='tstring_end'>'</span></span> <span class='kw'>do</span>
         | 
| 127 | 
            +
                  <span class='id identifier rubyid_base'>base</span> <span class='op'>=</span> <span class='const'>Path</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>~test</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_expand'>expand</span>
         | 
| 128 | 
            +
                  <span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_base'>base</span><span class='period'>.</span><span class='id identifier rubyid_join'>join</span><span class='lparen'>(</span><span class='qwords_beg'>%w(</span><span class='tstring_content'>src</span><span class='words_sep'> </span><span class='tstring_content'>test.txt</span><span class='words_sep'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_read'>read</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_to'>to</span> <span class='id identifier rubyid_eq'>eq</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>CONTENT</span><span class='tstring_end'>'</span></span>
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  <span class='id identifier rubyid_files'>files</span> <span class='op'>=</span> <span class='id identifier rubyid_base'>base</span><span class='period'>.</span><span class='id identifier rubyid_glob'>glob</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>**/*</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_select'>select</span><span class='lbrace'>{</span><span class='op'>|</span><span class='id identifier rubyid_p'>p</span><span class='op'>|</span> <span class='id identifier rubyid_p'>p</span><span class='period'>.</span><span class='id identifier rubyid_file?'>file?</span> <span class='rbrace'>}</span>
         | 
| 131 | 
            +
                  <span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_files'>files</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_to'>to</span> <span class='id identifier rubyid_eq'>eq</span> <span class='int'>2</span>
         | 
| 132 | 
            +
                <span class='kw'>end</span>
         | 
| 133 | 
            +
            </code></pre>
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            <p>See full API documentation here: <a href="http://rubydoc.info/gems/rubypath/Path">http://rubydoc.info/gems/rubypath/Path</a></p>
         | 
| 75 136 |  | 
| 76 137 | 
             
            <h2>Contributing</h2>
         | 
| 77 138 |  | 
| 78 139 | 
             
            <ol>
         | 
| 79 140 | 
             
            <li>Fork it</li>
         | 
| 80 141 | 
             
            <li>Create your feature branch (<code>git checkout -b my-new-feature</code>)</li>
         | 
| 142 | 
            +
            <li>Add specs testing SYS <em>and</em> MOCK file system</li>
         | 
| 143 | 
            +
            <li>Commit your specs (<code>git commit -am 'Add specs for feature'</code>)</li>
         | 
| 144 | 
            +
            <li>Add our changes for SYS <em>and</em> MOCK file system</li>
         | 
| 81 145 | 
             
            <li>Commit your changes (<code>git commit -am 'Add some feature'</code>)</li>
         | 
| 82 146 | 
             
            <li>Push to the branch (<code>git push origin my-new-feature</code>)</li>
         | 
| 83 147 | 
             
            <li>Create new Pull Request</li>
         | 
| 84 148 | 
             
            </ol>
         | 
| 85 149 |  | 
| 150 | 
            +
            <h3>ToDos</h3>
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            <ul>
         | 
| 153 | 
            +
            <li>Add missing methods</li>
         | 
| 154 | 
            +
            <li>Improve MOCK FS implementation</li>
         | 
| 155 | 
            +
            </ul>
         | 
| 156 | 
            +
             | 
| 86 157 | 
             
            <h2>License</h2>
         | 
| 87 158 |  | 
| 88 159 | 
             
            <p>Copyright (C) 2014 Jan Graichen</p>
         | 
| @@ -95,7 +166,7 @@ | |
| 95 166 | 
             
            </div></div>
         | 
| 96 167 |  | 
| 97 168 | 
             
                <div id="footer">
         | 
| 98 | 
            -
              Generated on  | 
| 169 | 
            +
              Generated on Sun Apr 27 13:05:43 2014 by
         | 
| 99 170 | 
             
              <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
         | 
| 100 171 | 
             
              0.8.7.3 (ruby-2.1.1).
         | 
| 101 172 | 
             
            </div>
         | 
| @@ -164,6 +164,47 @@ class Path::Backend | |
| 164 164 | 
             
                  lookup!(path).mode
         | 
| 165 165 | 
             
                end
         | 
| 166 166 |  | 
| 167 | 
            +
                def unlink(path)
         | 
| 168 | 
            +
                  node = lookup_parent!(path)
         | 
| 169 | 
            +
                  file = node.lookup ::File.basename path
         | 
| 170 | 
            +
                  case file
         | 
| 171 | 
            +
                    when Dir
         | 
| 172 | 
            +
                      raise Errno::EISDIR.new path
         | 
| 173 | 
            +
                    when File
         | 
| 174 | 
            +
                      node.children.delete(file)
         | 
| 175 | 
            +
                    when nil
         | 
| 176 | 
            +
                      raise Errno::ENOENT.new path
         | 
| 177 | 
            +
                    else
         | 
| 178 | 
            +
                      raise ArgumentError.new "Unknown node #{node.inspect} for #unlink."
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
                end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                def rmtree(path)
         | 
| 183 | 
            +
                  node = lookup path
         | 
| 184 | 
            +
                  case node
         | 
| 185 | 
            +
                    when Dir, File
         | 
| 186 | 
            +
                      lookup_parent!(path).children.delete(node)
         | 
| 187 | 
            +
                    when nil
         | 
| 188 | 
            +
                      nil
         | 
| 189 | 
            +
                    else
         | 
| 190 | 
            +
                      raise ArgumentError.new "Unknown node #{node.inspect} for #rmtree."
         | 
| 191 | 
            +
                  end
         | 
| 192 | 
            +
                end
         | 
| 193 | 
            +
                alias_method :safe_rmtree, :rmtree
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                def rmtree!(path)
         | 
| 196 | 
            +
                  node = lookup path
         | 
| 197 | 
            +
                  case node
         | 
| 198 | 
            +
                    when Dir, File
         | 
| 199 | 
            +
                      lookup_parent!(path).children.delete(node)
         | 
| 200 | 
            +
                    when nil
         | 
| 201 | 
            +
                      raise Errno::ENOENT.new path
         | 
| 202 | 
            +
                    else
         | 
| 203 | 
            +
                      raise ArgumentError.new "Unknown node #{node.inspect} for #rmtree."
         | 
| 204 | 
            +
                  end
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
                alias_method :safe_rmtree!, :rmtree!
         | 
| 207 | 
            +
             | 
| 167 208 | 
             
                # @!group Internal Virtual File System
         | 
| 168 209 |  | 
| 169 210 | 
             
                # Return root node.
         | 
    
        data/lib/rubypath/backend/sys.rb
    CHANGED
    
    | @@ -137,5 +137,25 @@ class Path::Backend | |
| 137 137 | 
             
                def chmod(path, mode)
         | 
| 138 138 | 
             
                  fs path, ::File, :chmod, mode, r(path)
         | 
| 139 139 | 
             
                end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                def unlink(path)
         | 
| 142 | 
            +
                  fs path, ::File, :unlink, r(path)
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                def rmtree(path)
         | 
| 146 | 
            +
                  fs path, ::FileUtils, :rm_r, r(path), force: true
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                def rmtree!(path)
         | 
| 150 | 
            +
                  fs path, ::FileUtils, :rm_r, r(path)
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                def safe_rmtree(path)
         | 
| 154 | 
            +
                  fs path, ::FileUtils, :rm_r, r(path), force: true, secure: true
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                def safe_rmtree!(path)
         | 
| 158 | 
            +
                  fs path, ::FileUtils, :rm_r, r(path), secure: true
         | 
| 159 | 
            +
                end
         | 
| 140 160 | 
             
              end
         | 
| 141 161 | 
             
            end
         | 
    
        data/lib/rubypath/backend.rb
    CHANGED
    
    
    
        data/lib/rubypath/comparison.rb
    CHANGED
    
    | @@ -9,13 +9,10 @@ class Path | |
| 9 9 | 
             
              # @return [Boolean] True if object represents same path.
         | 
| 10 10 | 
             
              #
         | 
| 11 11 | 
             
              def eql?(other)
         | 
| 12 | 
            -
                 | 
| 13 | 
            -
                   | 
| 14 | 
            -
             | 
| 15 | 
            -
                   | 
| 16 | 
            -
                    internal_path.eql? other.path
         | 
| 17 | 
            -
                  else
         | 
| 18 | 
            -
                    Path.new(other).eql?(self) if Path.like? other
         | 
| 12 | 
            +
                if other.is_a?(Path)
         | 
| 13 | 
            +
                  cleanpath.internal_path == other.cleanpath.internal_path
         | 
| 14 | 
            +
                else
         | 
| 15 | 
            +
                  Path.new(other).eql?(self) if Path.like?(other)
         | 
| 19 16 | 
             
                end
         | 
| 20 17 | 
             
              end
         | 
| 21 18 | 
             
              alias_method :==, :eql?
         | 
| @@ -31,6 +31,8 @@ class Path | |
| 31 31 | 
             
                end
         | 
| 32 32 | 
             
              end
         | 
| 33 33 |  | 
| 34 | 
            +
              # @!group Directory Operations
         | 
| 35 | 
            +
             | 
| 34 36 | 
             
              # Create directory.
         | 
| 35 37 | 
             
              #
         | 
| 36 38 | 
             
              # Given arguments will be joined with current path before directory is
         | 
| @@ -80,6 +82,76 @@ class Path | |
| 80 82 | 
             
                Path.glob(::File.join(escaped_glob_path, pattern), flags, &block)
         | 
| 81 83 | 
             
              end
         | 
| 82 84 |  | 
| 85 | 
            +
              # Removes file or directory. If it's a directory it will be removed
         | 
| 86 | 
            +
              # recursively.
         | 
| 87 | 
            +
              #
         | 
| 88 | 
            +
              # WARNING: This method causes local vulnerability if one of parent
         | 
| 89 | 
            +
              # directories or removing directory tree are world writable (including
         | 
| 90 | 
            +
              # `/tmp`, whose permission is 1777), and the current process has strong
         | 
| 91 | 
            +
              # privilege such as Unix super user (root), and the system has symbolic link.
         | 
| 92 | 
            +
              # For secure removing see {#safe_rmtree}.
         | 
| 93 | 
            +
              #
         | 
| 94 | 
            +
              # @return [Path] Path to removed file or directory.
         | 
| 95 | 
            +
              #
         | 
| 96 | 
            +
              def rmtree(*args)
         | 
| 97 | 
            +
                with_path(*args) do |path|
         | 
| 98 | 
            +
                  invoke_backend :rmtree, internal_path
         | 
| 99 | 
            +
                  Path path
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
              alias_method :rm_rf, :rmtree
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              # Removes file or directory. If it's a directory it will be removed
         | 
| 105 | 
            +
              # recursively.
         | 
| 106 | 
            +
              #
         | 
| 107 | 
            +
              # This method uses #{FileUtils#remove_entry_secure} to avoid TOCTTOU
         | 
| 108 | 
            +
              # (time-of-check-to-time-of-use) local security vulnerability of {#rmtree}.
         | 
| 109 | 
            +
              # {#rmtree} causes security hole when:
         | 
| 110 | 
            +
              #
         | 
| 111 | 
            +
              # * Parent directory is world writable (including `/tmp`).
         | 
| 112 | 
            +
              # * Removing directory tree includes world writable directory.
         | 
| 113 | 
            +
              # * The system has symbolic link.
         | 
| 114 | 
            +
              #
         | 
| 115 | 
            +
              # @return [Path] Path to removed file or directory.
         | 
| 116 | 
            +
              #
         | 
| 117 | 
            +
              def safe_rmtree(*args)
         | 
| 118 | 
            +
                with_path(*args) do |path|
         | 
| 119 | 
            +
                  invoke_backend :safe_rmtree, internal_path
         | 
| 120 | 
            +
                  Path path
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              # Removes file or directory. If it's a directory it will be removed
         | 
| 125 | 
            +
              # recursively.
         | 
| 126 | 
            +
              #
         | 
| 127 | 
            +
              # This method behaves exactly like {#rmtree} but will raise exceptions
         | 
| 128 | 
            +
              # e.g. when file does not exist.
         | 
| 129 | 
            +
              #
         | 
| 130 | 
            +
              # @return [Path] Path to removed file or directory.
         | 
| 131 | 
            +
              #
         | 
| 132 | 
            +
              def rmtree!(*args)
         | 
| 133 | 
            +
                with_path(*args) do |path|
         | 
| 134 | 
            +
                  invoke_backend :rmtree!, internal_path
         | 
| 135 | 
            +
                  Path path
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
              end
         | 
| 138 | 
            +
              alias_method :rm_r, :rmtree!
         | 
| 139 | 
            +
             | 
| 140 | 
            +
              # Removes file or directory. If it's a directory it will be removed
         | 
| 141 | 
            +
              # recursively.
         | 
| 142 | 
            +
              #
         | 
| 143 | 
            +
              # This method behaves exactly like {#safe_rmtree} but will raise exceptions
         | 
| 144 | 
            +
              # e.g. when file does not exist.
         | 
| 145 | 
            +
              #
         | 
| 146 | 
            +
              # @return [Path] Path to removed file or directory.
         | 
| 147 | 
            +
              #
         | 
| 148 | 
            +
              def safe_rmtree!(*args)
         | 
| 149 | 
            +
                with_path(*args) do |path|
         | 
| 150 | 
            +
                  invoke_backend :safe_rmtree!, internal_path
         | 
| 151 | 
            +
                  Path path
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
             | 
| 83 155 | 
             
              private
         | 
| 84 156 |  | 
| 85 157 | 
             
              def escaped_glob_path
         | 
| @@ -17,6 +17,10 @@ class Path | |
| 17 17 | 
             
              #   Path('/path/to/file.txt').touch
         | 
| 18 18 | 
             
              #   #=> <Path:"/path/to/file.txt">
         | 
| 19 19 | 
             
              #
         | 
| 20 | 
            +
              # @example
         | 
| 21 | 
            +
              #   Path('/path/to').touch('file.txt')
         | 
| 22 | 
            +
              #   #=> <Path:"/path/to/file.txt">
         | 
| 23 | 
            +
              #
         | 
| 20 24 | 
             
              # @return [Path] Path to touched file.
         | 
| 21 25 | 
             
              #
         | 
| 22 26 | 
             
              def touch(*args)
         | 
| @@ -26,6 +30,28 @@ class Path | |
| 26 30 | 
             
                end
         | 
| 27 31 | 
             
              end
         | 
| 28 32 |  | 
| 33 | 
            +
              # Removes file at current path.
         | 
| 34 | 
            +
              #
         | 
| 35 | 
            +
              # Raise an error if file does not exists or is a directory.
         | 
| 36 | 
            +
              #
         | 
| 37 | 
            +
              # @example
         | 
| 38 | 
            +
              #   Path('/file.txt').touch.unlink
         | 
| 39 | 
            +
              #   #=> <Path /file.txt>
         | 
| 40 | 
            +
              #
         | 
| 41 | 
            +
              # @example
         | 
| 42 | 
            +
              #   Path('/file.txt').touch
         | 
| 43 | 
            +
              #   Path('/').unlink('file.txt')
         | 
| 44 | 
            +
              #   #=> <Path /file.txt>
         | 
| 45 | 
            +
              #
         | 
| 46 | 
            +
              # @return [Path] Unlinked path.
         | 
| 47 | 
            +
              #
         | 
| 48 | 
            +
              def unlink(*args)
         | 
| 49 | 
            +
                with_path(*args) do |path|
         | 
| 50 | 
            +
                  invoke_backend :unlink, path
         | 
| 51 | 
            +
                  Path path
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 29 55 | 
             
              # Create a file at pointed location and all missing parent directories.
         | 
| 30 56 | 
             
              #
         | 
| 31 57 | 
             
              # Given arguments will be joined with current path before directories and
         | 
| @@ -280,9 +280,26 @@ class Path | |
| 280 280 | 
             
              end
         | 
| 281 281 | 
             
              alias_method :relative_path_from, :relative_from
         | 
| 282 282 |  | 
| 283 | 
            -
               | 
| 284 | 
            -
             | 
| 283 | 
            +
              # Return cleaned path with all dot components removed.
         | 
| 284 | 
            +
              #
         | 
| 285 | 
            +
              # No file system will accessed and not symlinks will be resolved.
         | 
| 286 | 
            +
              #
         | 
| 287 | 
            +
              # @example
         | 
| 288 | 
            +
              #   Path('./file.txt').cleanpath
         | 
| 289 | 
            +
              #   #=> <Path file.txt>
         | 
| 290 | 
            +
              #
         | 
| 291 | 
            +
              # @example
         | 
| 292 | 
            +
              #   Path('path/to/another/../file/../../txt').cleanpath
         | 
| 293 | 
            +
              #   #=> <Path path/txt>
         | 
| 294 | 
            +
              #
         | 
| 295 | 
            +
              # @return [Path] Cleaned path.
         | 
| 296 | 
            +
              #
         | 
| 285 297 | 
             
              def cleanpath
         | 
| 286 | 
            -
                 | 
| 298 | 
            +
                path = Pathname.new(self).cleanpath
         | 
| 299 | 
            +
                if path == internal_path
         | 
| 300 | 
            +
                  self
         | 
| 301 | 
            +
                else
         | 
| 302 | 
            +
                  Path path
         | 
| 303 | 
            +
                end
         | 
| 287 304 | 
             
              end
         | 
| 288 305 | 
             
            end
         | 
    
        data/lib/rubypath/version.rb
    CHANGED
    
    
| @@ -11,10 +11,20 @@ describe Path do | |
| 11 11 | 
             
                      expect(res).to be true
         | 
| 12 12 | 
             
                    end
         | 
| 13 13 |  | 
| 14 | 
            -
                    it 'should compare paths ( | 
| 14 | 
            +
                    it 'should compare paths (2)' do
         | 
| 15 15 | 
             
                      res = path.send described_method, Path('/path/to/another/file')
         | 
| 16 16 | 
             
                      expect(res).to be false
         | 
| 17 17 | 
             
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    it 'should compare clean paths (1)' do
         | 
| 20 | 
            +
                      res = path.send described_method, Path('/path/to/./file')
         | 
| 21 | 
            +
                      expect(res).to be true
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    it 'should compare clean paths (2)' do
         | 
| 25 | 
            +
                      res = path.send described_method, Path('/path/to/another/../file')
         | 
| 26 | 
            +
                      expect(res).to be true
         | 
| 27 | 
            +
                    end
         | 
| 18 28 | 
             
                  end
         | 
| 19 29 |  | 
| 20 30 | 
             
                  context 'with String object' do
         | 
| @@ -27,6 +37,16 @@ describe Path do | |
| 27 37 | 
             
                      res = path.send described_method, '/path/to/another/file'
         | 
| 28 38 | 
             
                      expect(res).to be false
         | 
| 29 39 | 
             
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    it 'should compare clean paths (1)' do
         | 
| 42 | 
            +
                      res = path.send described_method, '/path/to/./file'
         | 
| 43 | 
            +
                      expect(res).to be true
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    it 'should compare clean paths (2)' do
         | 
| 47 | 
            +
                      res = path.send described_method, '/path/to/another/../file'
         | 
| 48 | 
            +
                      expect(res).to be true
         | 
| 49 | 
            +
                    end
         | 
| 30 50 | 
             
                  end
         | 
| 31 51 |  | 
| 32 52 | 
             
                  context 'with Pathname object' do
         | 
| @@ -40,6 +60,17 @@ describe Path do | |
| 40 60 | 
             
                                      Pathname.new('/path/to/another/file')
         | 
| 41 61 | 
             
                      expect(res).to be false
         | 
| 42 62 | 
             
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    it 'should compare clean paths (1)' do
         | 
| 65 | 
            +
                      res = path.send described_method, Pathname.new('/path/to/./file')
         | 
| 66 | 
            +
                      expect(res).to be true
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    it 'should compare clean paths (2)' do
         | 
| 70 | 
            +
                      res = path.send described_method,
         | 
| 71 | 
            +
                                      Pathname.new('/path/to/another/../file')
         | 
| 72 | 
            +
                      expect(res).to be true
         | 
| 73 | 
            +
                    end
         | 
| 43 74 | 
             
                  end
         | 
| 44 75 | 
             
                end
         | 
| 45 76 | 
             
              end
         | 
| @@ -39,6 +39,83 @@ describe Path do | |
| 39 39 | 
             
                    end
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 | 
            +
                  shared_examples '#remove_recursive' do
         | 
| 43 | 
            +
                    context 'on existent file' do
         | 
| 44 | 
            +
                      before { path.mkfile }
         | 
| 45 | 
            +
                      it{ expect{ subject }.to change(path, :exist?).from(true).to(false) }
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    context 'on existent directory' do
         | 
| 49 | 
            +
                      before { path.mkpath }
         | 
| 50 | 
            +
                      it{ expect{ subject }.to change(path, :exist?).from(true).to(false) }
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    context 'on existent directory with children' do
         | 
| 54 | 
            +
                      before { path.mkfile('subdir/file') }
         | 
| 55 | 
            +
                      it{ expect{ subject }.to change(path, :exist?).from(true).to(false) }
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  describe_method :rmtree, aliases: [:rm_rf] do
         | 
| 60 | 
            +
                    let(:path) { Path '/path' }
         | 
| 61 | 
            +
                    subject { path.send(described_method) }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    context 'on non-existent file' do
         | 
| 64 | 
            +
                      it { expect{ subject }.to_not raise_error }
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    it_behaves_like '#remove_recursive'
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  describe_method :safe_rmtree do
         | 
| 71 | 
            +
                    let(:path) { Path '/path' }
         | 
| 72 | 
            +
                    subject { path.send(described_method) }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    it 'should use #remove_entry_secure' do
         | 
| 75 | 
            +
                      if backend_type == :sys
         | 
| 76 | 
            +
                        path.mkfile
         | 
| 77 | 
            +
                        expect(FileUtils).to receive(:remove_entry_secure).and_call_original
         | 
| 78 | 
            +
                        subject
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    context 'on non-existent file' do
         | 
| 83 | 
            +
                      it { expect{ subject }.to_not raise_error }
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    it_behaves_like '#remove_recursive'
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  describe_method :rmtree!, aliases: [:rm_r] do
         | 
| 90 | 
            +
                    let(:path) { Path '/path' }
         | 
| 91 | 
            +
                    subject { path.send(described_method) }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    context 'on non-existent file' do
         | 
| 94 | 
            +
                      it { expect{ subject }.to raise_error Errno::ENOENT }
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    it_behaves_like '#remove_recursive'
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  describe_method :safe_rmtree! do
         | 
| 101 | 
            +
                    let(:path) { Path '/path' }
         | 
| 102 | 
            +
                    subject { path.send(described_method) }
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                    it 'should use #remove_entry_secure' do
         | 
| 105 | 
            +
                      if backend_type == :sys
         | 
| 106 | 
            +
                        path.mkfile
         | 
| 107 | 
            +
                        expect(FileUtils).to receive(:remove_entry_secure).and_call_original
         | 
| 108 | 
            +
                        subject
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    context 'on non-existent file' do
         | 
| 113 | 
            +
                      it { expect{ subject }.to raise_error Errno::ENOENT }
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    it_behaves_like '#remove_recursive'
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
             | 
| 42 119 | 
             
                  describe '#glob' do
         | 
| 43 120 | 
             
                    it 'should delegate to class#glob' do
         | 
| 44 121 | 
             
                      expect(Path).to receive(:glob)
         | 
| @@ -130,7 +207,7 @@ describe Path do | |
| 130 207 | 
             
                      end
         | 
| 131 208 |  | 
| 132 209 | 
             
                      it 'should return list of Path objects' do
         | 
| 133 | 
            -
                        subject.each{|e| expect(e).to be_a Path }
         | 
| 210 | 
            +
                        subject.each{ |e| expect(e).to be_a Path }
         | 
| 134 211 | 
             
                      end
         | 
| 135 212 | 
             
                    end
         | 
| 136 213 |  | 
| @@ -15,6 +15,45 @@ describe Path do | |
| 15 15 | 
             
                    end
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 | 
            +
                  describe_method :unlink do
         | 
| 19 | 
            +
                    subject { path.send described_method }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    context 'on non-existent file' do
         | 
| 22 | 
            +
                      it { expect{ subject }.to raise_error Errno::ENOENT }
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    context 'on existent file' do
         | 
| 26 | 
            +
                      before { path.mkfile }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      it 'should unlink file' do
         | 
| 29 | 
            +
                        expect{ subject }.to change(path, :exist?).from(true).to(false)
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    context 'on existent directory' do
         | 
| 34 | 
            +
                      before { path.mkpath }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      it { expect{ subject }.to raise_error Errno::EISDIR }
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    context 'with args' do
         | 
| 40 | 
            +
                      subject { path.send(described_method, 'file') }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      context 'on non-existent file' do
         | 
| 43 | 
            +
                        it { expect{ subject }.to raise_error Errno::ENOENT }
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                      context 'on existent file' do
         | 
| 47 | 
            +
                        before { path.mkfile('file') }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                        it 'should unlink file' do
         | 
| 50 | 
            +
                          expect{ subject }
         | 
| 51 | 
            +
                            .to change(path.join('file'), :exist?).from(true).to(false)
         | 
| 52 | 
            +
                        end
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 18 57 | 
             
                  describe_method :touch do
         | 
| 19 58 | 
             
                    let(:path) { Path '/rubypath' }
         | 
| 20 59 | 
             
                    let(:args) { Array.new }
         | 
| @@ -4,7 +4,7 @@ describe Path do | |
| 4 4 | 
             
              describe 'Identity' do
         | 
| 5 5 | 
             
                let(:str)  { '/path/to/file' }
         | 
| 6 6 | 
             
                let(:args) { [str] }
         | 
| 7 | 
            -
                let(:path) { described_class.new | 
| 7 | 
            +
                let(:path) { described_class.new(*args) }
         | 
| 8 8 | 
             
                subject { path }
         | 
| 9 9 |  | 
| 10 10 | 
             
                describe_method :path, aliases: [:to_path, :to_str, :to_s] do
         | 
| @@ -8,7 +8,7 @@ describe Path do | |
| 8 8 | 
             
                subject { path }
         | 
| 9 9 |  | 
| 10 10 | 
             
                describe_method :join do
         | 
| 11 | 
            -
                  subject { path.send | 
| 11 | 
            +
                  subject { path.send(described_method, *join_args) }
         | 
| 12 12 |  | 
| 13 13 | 
             
                  context 'with single string' do
         | 
| 14 14 | 
             
                    let(:join_args) { ['to/file.txt'] }
         | 
| @@ -36,6 +36,35 @@ describe Path do | |
| 36 36 | 
             
                  end
         | 
| 37 37 | 
             
                end
         | 
| 38 38 |  | 
| 39 | 
            +
                describe_method :cleanpath do
         | 
| 40 | 
            +
                  subject { path.send(described_method) }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  context 'with out dot components' do
         | 
| 43 | 
            +
                    let(:path) { Path 'path/to/file.txt' }
         | 
| 44 | 
            +
                    it { should eq 'path/to/file.txt' }
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  context 'with leading dot' do
         | 
| 48 | 
            +
                    let(:path) { Path './file.txt' }
         | 
| 49 | 
            +
                    it { should eq 'file.txt' }
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  context 'with including dot' do
         | 
| 53 | 
            +
                    let(:path) { Path 'path/to/./file.txt' }
         | 
| 54 | 
            +
                    it { should eq 'path/to/file.txt' }
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  context 'with double dot' do
         | 
| 58 | 
            +
                    let(:path) { Path 'path/to/../file.txt' }
         | 
| 59 | 
            +
                    it { should eq 'path/file.txt' }
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  context 'with multiple dots' do
         | 
| 63 | 
            +
                    let(:path) { Path 'path/to/../../opath/to/./../file.txt' }
         | 
| 64 | 
            +
                    it { should eq 'opath/file.txt' }
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 39 68 | 
             
                describe_method :each_component do
         | 
| 40 69 | 
             
                  let(:block) { nil }
         | 
| 41 70 | 
             
                  let(:opts) { Hash.new }
         | 
| @@ -52,17 +81,17 @@ describe Path do | |
| 52 81 | 
             
                    let(:opts) { {empty: true} }
         | 
| 53 82 |  | 
| 54 83 | 
             
                    it 'should also return empty path components' do
         | 
| 55 | 
            -
                      expect(subject.to_a).to eq | 
| 84 | 
            +
                      expect(subject.to_a).to eq([''] + %w(path to templates dir) + [''])
         | 
| 56 85 | 
             
                    end
         | 
| 57 86 | 
             
                  end
         | 
| 58 87 |  | 
| 59 88 | 
             
                  context 'with block' do
         | 
| 60 | 
            -
                    let(:block) { proc{|fn| fn} }
         | 
| 89 | 
            +
                    let(:block) { proc{|fn| fn } }
         | 
| 61 90 |  | 
| 62 91 | 
             
                    it 'should yield components' do
         | 
| 63 | 
            -
                      expect  | 
| 92 | 
            +
                      expect do |b|
         | 
| 64 93 | 
             
                        path.send described_method, &b
         | 
| 65 | 
            -
                       | 
| 94 | 
            +
                      end.to yield_successive_args(*%w(path to templates dir))
         | 
| 66 95 | 
             
                    end
         | 
| 67 96 |  | 
| 68 97 | 
             
                    it { should eq path }
         | 
| @@ -80,7 +109,8 @@ describe Path do | |
| 80 109 | 
             
                describe_method :dirname, aliases: [:parent] do
         | 
| 81 110 | 
             
                  shared_examples 'dirname' do
         | 
| 82 111 | 
             
                    it 'should return parent directory' do
         | 
| 83 | 
            -
                      expect(Path(base, 'path/to/file').send(described_method)) | 
| 112 | 
            +
                      expect(Path(base, 'path/to/file').send(described_method))
         | 
| 113 | 
            +
                        .to eq "#{base}path/to"
         | 
| 84 114 | 
             
                    end
         | 
| 85 115 |  | 
| 86 116 | 
             
                    context 'when hitting root' do
         | 
| @@ -108,7 +138,8 @@ describe Path do | |
| 108 138 | 
             
                end
         | 
| 109 139 |  | 
| 110 140 | 
             
                with_backends :mock, :sys do
         | 
| 111 | 
            -
                  describe_method :expand, aliases: [:expand_path, | 
| 141 | 
            +
                  describe_method :expand, aliases: [:expand_path,
         | 
| 142 | 
            +
                                                     :absolute, :absolute_path] do
         | 
| 112 143 | 
             
                    let(:cwd) { '/working/dir' }
         | 
| 113 144 | 
             
                    let(:base) { cwd }
         | 
| 114 145 | 
             
                    let(:args) { Array.new }
         | 
| @@ -120,7 +151,7 @@ describe Path do | |
| 120 151 | 
             
                      end
         | 
| 121 152 | 
             
                    end
         | 
| 122 153 |  | 
| 123 | 
            -
                    around{|example| Path::Backend.mock | 
| 154 | 
            +
                    around{|example| Path::Backend.mock(&example) }
         | 
| 124 155 |  | 
| 125 156 | 
             
                    shared_examples '#expand' do
         | 
| 126 157 | 
             
                      subject { Path(path).send(described_method, *args) }
         | 
| @@ -311,11 +342,13 @@ describe Path do | |
| 311 342 | 
             
                      it { should eq path }
         | 
| 312 343 |  | 
| 313 344 | 
             
                      it 'should yield part paths' do
         | 
| 314 | 
            -
                        expect{|b| path.send | 
| 345 | 
            +
                        expect{|b| path.send(described_method, &b) }
         | 
| 346 | 
            +
                          .to yield_successive_args(*expected_paths)
         | 
| 315 347 | 
             
                      end
         | 
| 316 348 |  | 
| 317 349 | 
             
                      it 'should yield Path objects' do
         | 
| 318 | 
            -
                        expect{|b| path.send | 
| 350 | 
            +
                        expect{|b| path.send(described_method, &b) }
         | 
| 351 | 
            +
                          .to yield_successive_args(*expected_paths.map{ Path })
         | 
| 319 352 | 
             
                      end
         | 
| 320 353 | 
             
                    end
         | 
| 321 354 |  | 
| @@ -325,24 +358,26 @@ describe Path do | |
| 325 358 | 
             
                      it { should be_a Enumerator }
         | 
| 326 359 |  | 
| 327 360 | 
             
                      it 'should yield part paths' do
         | 
| 328 | 
            -
                        expect{|b| subject.each | 
| 361 | 
            +
                        expect{|b| subject.each(&b) }
         | 
| 362 | 
            +
                          .to yield_successive_args(*expected_paths)
         | 
| 329 363 | 
             
                      end
         | 
| 330 364 |  | 
| 331 365 | 
             
                      it 'should yield path objects' do
         | 
| 332 | 
            -
                        expect{|b| subject.each | 
| 366 | 
            +
                        expect{|b| subject.each(&b) }
         | 
| 367 | 
            +
                          .to yield_successive_args(*expected_paths.map{ Path })
         | 
| 333 368 | 
             
                      end
         | 
| 334 369 | 
             
                    end
         | 
| 335 370 | 
             
                  end
         | 
| 336 371 |  | 
| 337 372 | 
             
                  context 'with absolute path' do
         | 
| 338 373 | 
             
                    let(:path) { Path '/path/to/file.txt' }
         | 
| 339 | 
            -
                    let(:expected_paths) { %w(/path/to/file.txt /path/to /path /)}
         | 
| 374 | 
            +
                    let(:expected_paths) { %w(/path/to/file.txt /path/to /path /) }
         | 
| 340 375 | 
             
                    it_behaves_like 'ascend'
         | 
| 341 376 | 
             
                  end
         | 
| 342 377 |  | 
| 343 378 | 
             
                  context 'with relative path' do
         | 
| 344 379 | 
             
                    let(:path) { Path 'path/to/file.txt' }
         | 
| 345 | 
            -
                    let(:expected_paths) { %w(path/to/file.txt path/to path .)}
         | 
| 380 | 
            +
                    let(:expected_paths) { %w(path/to/file.txt path/to path .) }
         | 
| 346 381 | 
             
                    it_behaves_like 'ascend'
         | 
| 347 382 | 
             
                  end
         | 
| 348 383 | 
             
                end
         | 
| @@ -1,15 +1,17 @@ | |
| 1 | 
            +
            #
         | 
| 1 2 | 
             
            module WithBackend
         | 
| 2 | 
            -
             | 
| 3 3 | 
             
              def with_backends(*args, &block)
         | 
| 4 4 | 
             
                args.each do |backend|
         | 
| 5 5 | 
             
                  be = case backend
         | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 6 | 
            +
                         when :mock
         | 
| 7 | 
            +
                           ->(ex){ Path::Backend.mock(&ex) }
         | 
| 8 | 
            +
                         when :sys
         | 
| 9 | 
            +
                           ->(ex){ Path::Backend.mock(root: :tmp, &ex) }
         | 
| 10 | 
            +
                         else
         | 
| 11 | 
            +
                           raise ArgumentError.new 'Unknown backend.'
         | 
| 12 | 
            +
                       end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  next if ENV["FS_#{backend.upcase}"] == '0'
         | 
| 13 15 |  | 
| 14 16 | 
             
                  describe "with #{backend.upcase} FS" do
         | 
| 15 17 | 
             
                    let(:backend_type) { backend }
         | 
| @@ -17,15 +19,19 @@ module WithBackend | |
| 17 19 | 
             
                      be.call(example)
         | 
| 18 20 | 
             
                    end
         | 
| 19 21 |  | 
| 20 | 
            -
                    module_eval | 
| 22 | 
            +
                    module_eval(&block)
         | 
| 21 23 | 
             
                  end
         | 
| 22 24 | 
             
                end
         | 
| 23 25 | 
             
              end
         | 
| 24 26 | 
             
              alias_method :with_backend, :with_backends
         | 
| 25 27 |  | 
| 26 28 | 
             
              def pending_backend(*args)
         | 
| 27 | 
            -
                before  | 
| 29 | 
            +
                before do
         | 
| 30 | 
            +
                  if args.include? backend_type
         | 
| 31 | 
            +
                    pending "Pending on #{backend_type} backend."
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 28 34 | 
             
              end
         | 
| 29 35 |  | 
| 30 | 
            -
              RSpec.configure{|c| c.extend WithBackend }
         | 
| 36 | 
            +
              RSpec.configure{ |c| c.extend WithBackend }
         | 
| 31 37 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rubypath
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.3.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Jan Graichen
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2014-04- | 
| 11 | 
            +
            date: 2014-04-27 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         |