fast_find 0.1.1 → 0.2.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 +5 -5
- data/.github/workflows/ci.yml +17 -0
- data/README.md +55 -45
- data/bin/benchmark +37 -16
- data/fast_find.gemspec +10 -8
- data/lib/fast_find.rb +106 -152
- data/lib/fast_find/version.rb +3 -1
- metadata +30 -17
- data/.travis.yml +0 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 84526cadc7a5aaedce296eafbef1df65c837fba29f0eca88a8510eb89d92a105
         | 
| 4 | 
            +
              data.tar.gz: 74d015c6eea35d56091f2ad5a0c34bf68d59cb893e0eee2272228d83bcd2ce6b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: '0985c016e8df0f5cd0d1c1a4a7f8e0601c8f573345bbf5c10b1cf649affdc24b20a2b8ea045e4417f5e94b42c45245847b35b93e77b4d85bc15711d626c51931'
         | 
| 7 | 
            +
              data.tar.gz: 5c866bc2cd95b4ad053a1c17069d67fe48e1895a8141724f56e1787e9ea632075150e43db26e4ed5c74ec56e172e596bb6cbcaa569e29816a8a4c2bf04b408a4
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            name: CI
         | 
| 2 | 
            +
            on: [push, pull_request]
         | 
| 3 | 
            +
            jobs:
         | 
| 4 | 
            +
              test:
         | 
| 5 | 
            +
                strategy:
         | 
| 6 | 
            +
                  fail-fast: false
         | 
| 7 | 
            +
                  matrix:
         | 
| 8 | 
            +
                    os: [ubuntu-latest]
         | 
| 9 | 
            +
                    ruby: ['2.5', '2.6', '3.0', head, jruby, jruby-head, truffleruby]
         | 
| 10 | 
            +
                runs-on: ${{ matrix.os }}
         | 
| 11 | 
            +
                steps:
         | 
| 12 | 
            +
                - uses: actions/checkout@v2
         | 
| 13 | 
            +
                - uses: ruby/setup-ruby@v1
         | 
| 14 | 
            +
                  with:
         | 
| 15 | 
            +
                    ruby-version: ${{ matrix.ruby }}
         | 
| 16 | 
            +
                    bundler-cache: true # runs 'bundle install' and caches installed gems automatically
         | 
| 17 | 
            +
                - run: bundle exec rake test
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,80 +1,90 @@ | |
| 1 1 | 
             
            # FastFind
         | 
| 2 2 |  | 
| 3 | 
            -
            FastFind is a  | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 3 | 
            +
            `FastFind` is a multi-threaded alternative to the standard `Find` module,
         | 
| 4 | 
            +
            offering increased performance on Rubies which can run `Dir#entries` and
         | 
| 5 | 
            +
            `File#lstat` calls concurrently (i.e. JRuby).
         | 
| 6 6 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            still prove a win.
         | 
| 11 | 
            -
             | 
| 12 | 
            -
            This code is considered experimental.  Beware of dog.
         | 
| 7 | 
            +
            While it can operate as a drop-in replacement for `Find`, it's best used with
         | 
| 8 | 
            +
            a two-argument block which also yields the `File::Stat` object associated with
         | 
| 9 | 
            +
            each yielded path.
         | 
| 13 10 |  | 
| 14 11 | 
             
            ## Installation
         | 
| 15 12 |  | 
| 16 13 | 
             
            Add this line to your application's Gemfile:
         | 
| 17 14 |  | 
| 18 | 
            -
             | 
| 15 | 
            +
            ```shell
         | 
| 16 | 
            +
            gem 'fast_find'
         | 
| 17 | 
            +
            ```
         | 
| 19 18 |  | 
| 20 19 | 
             
            And then execute:
         | 
| 21 20 |  | 
| 22 | 
            -
             | 
| 21 | 
            +
            ```shell
         | 
| 22 | 
            +
            $ bundle
         | 
| 23 | 
            +
            ```
         | 
| 23 24 |  | 
| 24 25 | 
             
            ## Usage
         | 
| 25 26 |  | 
| 26 | 
            -
            Traditional Find-style:
         | 
| 27 | 
            +
            Traditional Find-style (not recommended):
         | 
| 27 28 |  | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 29 | 
            +
            ```ruby
         | 
| 30 | 
            +
            FastFind.find(dir) { |entry| frob(entry) }
         | 
| 31 | 
            +
            FastFind.find(dir, ignore_errors: false) { .. } # => explodes in your face
         | 
| 32 | 
            +
            FastFind.find(dir) # => Enumerator
         | 
| 33 | 
            +
            ```
         | 
| 31 34 |  | 
| 32 35 | 
             
            Extended style using the second argument to get a `File::Stat`, or `Exception`
         | 
| 33 | 
            -
            object (if `ignore_errors` is false, this will be raised  | 
| 34 | 
            -
             | 
| 35 | 
            -
                FastFind.find(dir) {|entry, stat| frob(entry, stat) }
         | 
| 36 | 
            +
            object (if `ignore_errors` is false, this will be raised after the block).
         | 
| 36 37 |  | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 38 | 
            +
            ```ruby
         | 
| 39 | 
            +
            FastFind.find(dir) { |entry, stat| frob(entry, stat) }
         | 
| 40 | 
            +
            ```
         | 
| 40 41 |  | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 42 | 
            +
            `FastFind` uses a concurrent-ruby executor to run, which can be customised
         | 
| 43 | 
            +
            by passing it as a named argument:
         | 
| 43 44 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 45 | 
            +
            ```ruby
         | 
| 46 | 
            +
            executor = Concurrent::FixedThreadPool.new(16, idletime: 90)
         | 
| 47 | 
            +
            FastFind.find(dir, executor: executor)
         | 
| 48 | 
            +
            ```
         | 
| 47 49 |  | 
| 48 | 
            -
             | 
| 50 | 
            +
            Or while no concurrent `find` operations are in progress, to the module itself:
         | 
| 49 51 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            +
            ```ruby
         | 
| 53 | 
            +
            FastFind.default_executor = Concurrent::FixedThreadPool.new(16, idletime: 90)
         | 
| 54 | 
            +
            ```
         | 
| 52 55 |  | 
| 53 | 
            -
             | 
| 56 | 
            +
            Due to the use of a bounded result queue it is *not* recommended to use an
         | 
| 57 | 
            +
            executor with a bounded queue or which runs in the same thread as this may
         | 
| 58 | 
            +
            result in deadlocks or dropped results.
         | 
| 54 59 |  | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
            `FastFind#prune` works.  So does `Find#prune`.
         | 
| 60 | 
            +
            As with `Find`, `FastFind#prune` will avoid recursing into a directory.
         | 
| 58 61 |  | 
| 59 62 | 
             
            ## Performance
         | 
| 60 63 |  | 
| 61 | 
            -
            Scanning a cached copy of the NetBSD CVS repository:
         | 
| 64 | 
            +
            Scanning a cached copy of the NetBSD CVS repository wtih default settings:
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7-1 on 15.0.2+7-1 +jit:
         | 
| 62 67 |  | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 68 | 
            +
            ```
         | 
| 69 | 
            +
                                   user     system      total        real
         | 
| 70 | 
            +
            FastFind          22.296875  40.851562  63.148438 (  8.014612)
         | 
| 71 | 
            +
            Find              15.179688  28.031250  43.210938 ( 43.036654)
         | 
| 72 | 
            +
            FastFind as Find  37.679688  44.375000  82.054688 ( 29.502235)
         | 
| 73 | 
            +
            ```
         | 
| 65 74 |  | 
| 66 | 
            -
             | 
| 67 | 
            -
                Find      32.890625  27.742188  60.632813 ( 47.518944)
         | 
| 68 | 
            -
                FastFind  35.273438  41.742188  77.015625 (  8.140893)
         | 
| 75 | 
            +
            These results highlight the importance of the two-argument version.
         | 
| 69 76 |  | 
| 70 | 
            -
            ruby  | 
| 77 | 
            +
            ruby 3.0.0p0 (2020-12-25 revision 95aff21468) \[x86_64-freebsd12.2]:
         | 
| 71 78 |  | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 79 | 
            +
            ```
         | 
| 80 | 
            +
                                   user     system      total        real
         | 
| 81 | 
            +
            FastFind          30.436830  29.265892  59.702722 ( 41.432262)
         | 
| 82 | 
            +
            Find              10.346662  20.317705  30.664367 ( 30.666812)
         | 
| 83 | 
            +
            FastFind as Find  28.300448  35.654871  63.955319 ( 39.212032)
         | 
| 84 | 
            +
            ```
         | 
| 75 85 |  | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 86 | 
            +
            Sadly the current implementation is a significant pessimisation on MRI, likely
         | 
| 87 | 
            +
            due to thread overhead.
         | 
| 78 88 |  | 
| 79 89 | 
             
            ## Development
         | 
| 80 90 |  | 
    
        data/bin/benchmark
    CHANGED
    
    | @@ -1,28 +1,49 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 2 3 |  | 
| 3 4 | 
             
            require 'find'
         | 
| 4 5 | 
             
            require 'benchmark'
         | 
| 5 6 |  | 
| 6 | 
            -
            require  | 
| 7 | 
            +
            require 'bundler/setup'
         | 
| 7 8 | 
             
            require 'fast_find'
         | 
| 8 9 |  | 
| 9 | 
            -
            FastFinder = FastFind::Finder.new
         | 
| 10 | 
            -
             | 
| 11 10 | 
             
            test_dirs = ARGV
         | 
| 12 | 
            -
            abort("Usage: #{$ | 
| 11 | 
            +
            abort("Usage: #{$PROGRAM_NAME} [dir1 [dir2[ ..]]]") if test_dirs.empty?
         | 
| 13 12 |  | 
| 14 13 | 
             
            Benchmark.bmbm do |b|
         | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 14 | 
            +
              b.report('FastFind') do
         | 
| 15 | 
            +
                files = directories = 0
         | 
| 16 | 
            +
                FastFind.find(*test_dirs) do |f, stat|
         | 
| 17 | 
            +
                  if stat.directory?
         | 
| 18 | 
            +
                    directories += 1
         | 
| 19 | 
            +
                  else
         | 
| 20 | 
            +
                    files += 1
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  # files << [f, stat]
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              b.report('Find') do
         | 
| 27 | 
            +
                files = directories = 0
         | 
| 28 | 
            +
                Find.find(*test_dirs) do |f|
         | 
| 29 | 
            +
                  if File.directory?(f)
         | 
| 30 | 
            +
                    directories += 1
         | 
| 31 | 
            +
                  else
         | 
| 32 | 
            +
                    files += 1
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                  # files << [f, File.lstat(f)]
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 21 37 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 38 | 
            +
              b.report('FastFind as Find') do
         | 
| 39 | 
            +
                files = directories = 0
         | 
| 40 | 
            +
                FastFind.find(*test_dirs) do |f|
         | 
| 41 | 
            +
                  if File.directory?(f)
         | 
| 42 | 
            +
                    directories += 1
         | 
| 43 | 
            +
                  else
         | 
| 44 | 
            +
                    files += 1
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                  # files << [f, File.lstat(f)]
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 28 49 | 
             
            end
         | 
    
        data/fast_find.gemspec
    CHANGED
    
    | @@ -9,10 +9,10 @@ Gem::Specification.new do |spec| | |
| 9 9 | 
             
              spec.authors       = ["Thomas Hurst"]
         | 
| 10 10 | 
             
              spec.email         = ["tom@hur.st"]
         | 
| 11 11 |  | 
| 12 | 
            -
              spec.summary       = %q{ | 
| 13 | 
            -
              spec.description   = %q{FastFind is a  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 12 | 
            +
              spec.summary       = %q{Multi-threaded Find alternative.}
         | 
| 13 | 
            +
              spec.description   = %q{FastFind is a high-performance Find alternative for
         | 
| 14 | 
            +
                                      Ruby implementations with effective thread
         | 
| 15 | 
            +
                                      concurrency, particularly JRuby.}
         | 
| 16 16 | 
             
              spec.homepage      = "https://github.com/Freaky/fast_find"
         | 
| 17 17 | 
             
              spec.license       = "MIT"
         | 
| 18 18 |  | 
| @@ -24,14 +24,16 @@ Gem::Specification.new do |spec| | |
| 24 24 | 
             
                raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
         | 
| 25 25 | 
             
              end
         | 
| 26 26 |  | 
| 27 | 
            -
              spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
         | 
| 27 | 
            +
              spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|\.git)/}) }
         | 
| 28 28 | 
             
              spec.bindir        = "exe"
         | 
| 29 29 | 
             
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 30 30 | 
             
              spec.require_paths = ["lib"]
         | 
| 31 31 |  | 
| 32 | 
            -
              spec.required_ruby_version = ">= 2. | 
| 32 | 
            +
              spec.required_ruby_version = ">= 2.5"
         | 
| 33 33 |  | 
| 34 | 
            -
              spec. | 
| 35 | 
            -
             | 
| 34 | 
            +
              spec.add_dependency "concurrent-ruby", "~> 1.1"
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              spec.add_development_dependency "bundler", "~> 2.0"
         | 
| 37 | 
            +
              spec.add_development_dependency "rake", "~> 13.0"
         | 
| 36 38 | 
             
              spec.add_development_dependency "minitest"
         | 
| 37 39 | 
             
            end
         | 
    
        data/lib/fast_find.rb
    CHANGED
    
    | @@ -1,158 +1,112 @@ | |
| 1 | 
            -
            #
         | 
| 2 | 
            -
            # fast_find.rb: A Find workalike optimized for performance.
         | 
| 3 | 
            -
            #
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 5 3 | 
             
            require 'set'
         | 
| 6 | 
            -
             | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'concurrent'
         | 
| 6 | 
            +
             | 
| 7 7 | 
             
            require 'fast_find/version'
         | 
| 8 8 |  | 
| 9 | 
            +
            # A Find workalike optimized for multithreaded operation on supporting Rubies
         | 
| 9 10 | 
             
            module FastFind
         | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
            	class Walker
         | 
| 113 | 
            -
            		def spawn(queue)
         | 
| 114 | 
            -
            			Thread.new do
         | 
| 115 | 
            -
            				@encoding = Encoding.find("filesystem")
         | 
| 116 | 
            -
            				while job = queue.deq
         | 
| 117 | 
            -
            					walk(job[0], job[1])
         | 
| 118 | 
            -
            				end
         | 
| 119 | 
            -
            			end
         | 
| 120 | 
            -
            		end
         | 
| 121 | 
            -
             | 
| 122 | 
            -
            		def walk(path, results)
         | 
| 123 | 
            -
            			enc = path.encoding == Encoding::US_ASCII ? @encoding : path.encoding
         | 
| 124 | 
            -
             | 
| 125 | 
            -
            			Dir.entries(path, encoding: enc).each do |entry|
         | 
| 126 | 
            -
            				next if entry == '.' or entry == '..'
         | 
| 127 | 
            -
             | 
| 128 | 
            -
            				stat(File.join(path, entry), results)
         | 
| 129 | 
            -
            			end
         | 
| 130 | 
            -
            		rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP,
         | 
| 131 | 
            -
            		       Errno::ENAMETOOLONG => e
         | 
| 132 | 
            -
            			error(e, results)
         | 
| 133 | 
            -
            		ensure
         | 
| 134 | 
            -
            			finish(path, results)
         | 
| 135 | 
            -
            		end
         | 
| 136 | 
            -
             | 
| 137 | 
            -
            		def stat(entry, results)
         | 
| 138 | 
            -
            			results << [entry, Util.safe_stat(entry)]
         | 
| 139 | 
            -
            		end
         | 
| 140 | 
            -
             | 
| 141 | 
            -
            		def finish(path, results)
         | 
| 142 | 
            -
            			results << [path, :finished]
         | 
| 143 | 
            -
            		end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
            		def error(e, results)
         | 
| 146 | 
            -
            			results << [:exception, e]
         | 
| 147 | 
            -
            		end
         | 
| 148 | 
            -
            	end
         | 
| 149 | 
            -
             | 
| 150 | 
            -
            	module Util
         | 
| 151 | 
            -
            		def self.safe_stat(path)
         | 
| 152 | 
            -
            			File.lstat(path)
         | 
| 153 | 
            -
            		rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP,
         | 
| 154 | 
            -
            		       Errno::ENAMETOOLONG => e
         | 
| 155 | 
            -
            			e
         | 
| 156 | 
            -
            		end
         | 
| 157 | 
            -
            	end
         | 
| 11 | 
            +
              class << self
         | 
| 12 | 
            +
                attr_accessor :default_executor
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def prune
         | 
| 15 | 
            +
                  throw :prune
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def find(*paths, ignore_error: true, executor: default_executor, &block)
         | 
| 19 | 
            +
                  block or return enum_for(__method__, *paths, ignore_error: ignore_error, executor: executor)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  results = SizedQueue.new(1024)
         | 
| 22 | 
            +
                  pending = Set.new
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  paths.map!(&:dup).each do |path|
         | 
| 25 | 
            +
                    path = path.to_path if path.respond_to? :to_path
         | 
| 26 | 
            +
                    results << [path, safe_stat(path)]
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                  results << %i[initial finished]
         | 
| 29 | 
            +
                  pending << path_signature(:initial)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  while (result = results.deq)
         | 
| 32 | 
            +
                    path, stat = result
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    if stat == :finished
         | 
| 35 | 
            +
                      break if pending.delete(path_signature(path)).empty?
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      next
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    catch(:prune) do
         | 
| 41 | 
            +
                      yield_entry(result, block) if path.is_a? String
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      if stat.is_a?(File::Stat) && stat.directory? && pending.add?(path_signature(path))
         | 
| 44 | 
            +
                        executor.post(path, results) do |path_, results_|
         | 
| 45 | 
            +
                          walk(path_, results_)
         | 
| 46 | 
            +
                        end
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    raise stat if stat.is_a?(Exception) && !ignore_error
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                private
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                FS_ENCODING = Encoding.find('filesystem')
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def walk(path, results)
         | 
| 59 | 
            +
                  enc = path.encoding == Encoding::US_ASCII ? FS_ENCODING : path.encoding
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  # This benchmarks as about 10% faster than Dirs.foreach
         | 
| 62 | 
            +
                  Dir.entries(path, encoding: enc).each do |entry|
         | 
| 63 | 
            +
                    next if (entry == '.') || (entry == '..')
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    results << stat(File.join(path, entry))
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP,
         | 
| 68 | 
            +
                       Errno::ENAMETOOLONG => e
         | 
| 69 | 
            +
                  results << error(e)
         | 
| 70 | 
            +
                ensure
         | 
| 71 | 
            +
                  results << finish(path)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                def stat(entry)
         | 
| 75 | 
            +
                  [entry, safe_stat(entry)]
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                def finish(path)
         | 
| 79 | 
            +
                  [path, :finished]
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def error(e)
         | 
| 83 | 
            +
                  [:exception, e]
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def path_signature(path)
         | 
| 87 | 
            +
                  [path, path.encoding]
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def yield_entry(entry, block)
         | 
| 91 | 
            +
                  if block.arity == 2
         | 
| 92 | 
            +
                    block.call(entry[0].dup, entry[1])
         | 
| 93 | 
            +
                  else
         | 
| 94 | 
            +
                    block.call entry[0].dup
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                def safe_stat(path)
         | 
| 99 | 
            +
                  File.lstat(path)
         | 
| 100 | 
            +
                rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP,
         | 
| 101 | 
            +
                       Errno::ENAMETOOLONG => e
         | 
| 102 | 
            +
                  e
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              self.default_executor = case RUBY_ENGINE
         | 
| 107 | 
            +
                                      when 'jruby', 'rbx'
         | 
| 108 | 
            +
                                        Concurrent::FixedThreadPool.new(8, idletime: 60)
         | 
| 109 | 
            +
                                      else
         | 
| 110 | 
            +
                                        Concurrent::FixedThreadPool.new(1, idletime: 60)
         | 
| 111 | 
            +
                                      end
         | 
| 158 112 | 
             
            end
         | 
    
        data/lib/fast_find/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,43 +1,57 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: fast_find
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Thomas Hurst
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2021-02-02 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 15 15 | 
             
                requirements:
         | 
| 16 16 | 
             
                - - "~>"
         | 
| 17 17 | 
             
                  - !ruby/object:Gem::Version
         | 
| 18 | 
            -
                    version: '1. | 
| 19 | 
            -
              name:  | 
| 18 | 
            +
                    version: '1.1'
         | 
| 19 | 
            +
              name: concurrent-ruby
         | 
| 20 | 
            +
              type: :runtime
         | 
| 20 21 | 
             
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1.1'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 29 | 
            +
                requirements:
         | 
| 30 | 
            +
                - - "~>"
         | 
| 31 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 32 | 
            +
                    version: '2.0'
         | 
| 33 | 
            +
              name: bundler
         | 
| 21 34 | 
             
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 22 36 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 37 | 
             
                requirements:
         | 
| 24 38 | 
             
                - - "~>"
         | 
| 25 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: ' | 
| 40 | 
            +
                    version: '2.0'
         | 
| 27 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 42 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 29 43 | 
             
                requirements:
         | 
| 30 44 | 
             
                - - "~>"
         | 
| 31 45 | 
             
                  - !ruby/object:Gem::Version
         | 
| 32 | 
            -
                    version: ' | 
| 46 | 
            +
                    version: '13.0'
         | 
| 33 47 | 
             
              name: rake
         | 
| 34 | 
            -
              prerelease: false
         | 
| 35 48 | 
             
              type: :development
         | 
| 49 | 
            +
              prerelease: false
         | 
| 36 50 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 51 | 
             
                requirements:
         | 
| 38 52 | 
             
                - - "~>"
         | 
| 39 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            -
                    version: ' | 
| 54 | 
            +
                    version: '13.0'
         | 
| 41 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 56 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 43 57 | 
             
                requirements:
         | 
| @@ -45,25 +59,25 @@ dependencies: | |
| 45 59 | 
             
                  - !ruby/object:Gem::Version
         | 
| 46 60 | 
             
                    version: '0'
         | 
| 47 61 | 
             
              name: minitest
         | 
| 48 | 
            -
              prerelease: false
         | 
| 49 62 | 
             
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 50 64 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 65 | 
             
                requirements:
         | 
| 52 66 | 
             
                - - ">="
         | 
| 53 67 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 68 | 
             
                    version: '0'
         | 
| 55 69 | 
             
            description: |-
         | 
| 56 | 
            -
              FastFind is a  | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 70 | 
            +
              FastFind is a high-performance Find alternative for
         | 
| 71 | 
            +
                                        Ruby implementations with effective thread
         | 
| 72 | 
            +
                                        concurrency, particularly JRuby.
         | 
| 59 73 | 
             
            email:
         | 
| 60 74 | 
             
            - tom@hur.st
         | 
| 61 75 | 
             
            executables: []
         | 
| 62 76 | 
             
            extensions: []
         | 
| 63 77 | 
             
            extra_rdoc_files: []
         | 
| 64 78 | 
             
            files:
         | 
| 79 | 
            +
            - ".github/workflows/ci.yml"
         | 
| 65 80 | 
             
            - ".gitignore"
         | 
| 66 | 
            -
            - ".travis.yml"
         | 
| 67 81 | 
             
            - Gemfile
         | 
| 68 82 | 
             
            - LICENSE.txt
         | 
| 69 83 | 
             
            - README.md
         | 
| @@ -87,16 +101,15 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 87 101 | 
             
              requirements:
         | 
| 88 102 | 
             
              - - ">="
         | 
| 89 103 | 
             
                - !ruby/object:Gem::Version
         | 
| 90 | 
            -
                  version: '2. | 
| 104 | 
            +
                  version: '2.5'
         | 
| 91 105 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 92 106 | 
             
              requirements:
         | 
| 93 107 | 
             
              - - ">="
         | 
| 94 108 | 
             
                - !ruby/object:Gem::Version
         | 
| 95 109 | 
             
                  version: '0'
         | 
| 96 110 | 
             
            requirements: []
         | 
| 97 | 
            -
             | 
| 98 | 
            -
            rubygems_version: 2.4.8
         | 
| 111 | 
            +
            rubygems_version: 3.0.6
         | 
| 99 112 | 
             
            signing_key:
         | 
| 100 113 | 
             
            specification_version: 4
         | 
| 101 | 
            -
            summary:  | 
| 114 | 
            +
            summary: Multi-threaded Find alternative.
         | 
| 102 115 | 
             
            test_files: []
         | 
    
        data/.travis.yml
    DELETED