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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9c5cd7c541e439cb4bd46acd6c28838e7867a369
4
- data.tar.gz: 827db02601353683fd17828a779f71d5a282f80c
2
+ SHA256:
3
+ metadata.gz: 84526cadc7a5aaedce296eafbef1df65c837fba29f0eca88a8510eb89d92a105
4
+ data.tar.gz: 74d015c6eea35d56091f2ad5a0c34bf68d59cb893e0eee2272228d83bcd2ce6b
5
5
  SHA512:
6
- metadata.gz: dd2cde72e12d19e762e9a6830f73c12024bab29cecdb7f439501627f0a4c306aafc7130bd2e3ac6344ea7e0b20b4c57c22510f2859cdfd5a2c37dc7935d93913
7
- data.tar.gz: af278abe2c42f33cb905e227c389a4a8c0d73aa5c4b30eb7f08b479159a21049e59ba1353776888c72ac1a7dbef1135931b7439060adc07b804f4fd5c8fbadec
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 performance-oriented multi-threaded alternative to the standard
4
- `Find` module that ships with Ruby. It should generally be a drop-in
5
- replacement.
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
- FastFind is expected to be marginally slower on MRI/YARV, since multithreaded
8
- `File#lstat` calls there appear to serialize. However, using the FastFind-
9
- specific second argument to pass in the `File::Stat` object for each file may
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
- gem 'fast_find', git: 'https://github.com/Freaky/fast_find.git'
15
+ ```shell
16
+ gem 'fast_find'
17
+ ```
19
18
 
20
19
  And then execute:
21
20
 
22
- $ bundle
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
- FastFind.find(dir) {|entry| frob(entry) }
29
- FastFind.find(dir, ignore_errors: false) { .. } # => explodes in your face
30
- FastFind.find(dir) # => Enumerator
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 instead).
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
- For increased performance and better scaling behaviour, it is recommended to use
38
- a single shared FastFind object. Multiple concurrent calls to
39
- `FastFind::Finder#find` are safe and will share a persistant work pool.
38
+ ```ruby
39
+ FastFind.find(dir) { |entry, stat| frob(entry, stat) }
40
+ ```
40
41
 
41
- Finder = FastFind::Finder.new
42
- Finder.find(dir) { .. }
42
+ `FastFind` uses a concurrent-ruby executor to run, which can be customised
43
+ by passing it as a named argument:
43
44
 
44
- You can call `Finder#shutdown` to close the work pool if you're done with the
45
- instance for the time being. Ensure no other calls to its `#find` are in flight
46
- beforehand. The pool is restarted the next time `#find` is called.
45
+ ```ruby
46
+ executor = Concurrent::FixedThreadPool.new(16, idletime: 90)
47
+ FastFind.find(dir, executor: executor)
48
+ ```
47
49
 
48
- Use the `concurrency` named argument to change the number of worker threads:
50
+ Or while no concurrent `find` operations are in progress, to the module itself:
49
51
 
50
- FastFind.find(dir, concurrency: 4)
51
- FastFind::Finder.new(concurrency: 4)
52
+ ```ruby
53
+ FastFind.default_executor = Concurrent::FixedThreadPool.new(16, idletime: 90)
54
+ ```
52
55
 
53
- Defaults are `8` for Rubinius and JRuby, `1` for anything else.
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
- Note the yielded blocks are all executed in the parent thread, *not* in workers.
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
- jruby 9.0.1.0-SNAPSHOT (2.2.2) 2015-07-23 e88911e OpenJDK 64-Bit Server VM
64
- 25.51-b03 on 1.8.0_51-b16 +jit [FreeBSD-amd64]:
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
- user system total real
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 2.2.2p95 (2015-04-13 revision 50295) [x86_64-freebsd10.1]:
77
+ ruby 3.0.0p0 (2020-12-25 revision 95aff21468) \[x86_64-freebsd12.2]:
71
78
 
72
- user system total real
73
- Find 10.187500 22.351562 32.539062 ( 32.545201)
74
- FastFind 9.039062 14.226562 23.265625 ( 23.277589)
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
- On MRI `Find` here is penalised because both `Find` and the benchmark code is
77
- performing a `File#lstat`.
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 "bundler/setup"
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: #{$0} [dir1 [dir2[ ..]]]") if test_dirs.empty?
11
+ abort("Usage: #{$PROGRAM_NAME} [dir1 [dir2[ ..]]]") if test_dirs.empty?
13
12
 
14
13
  Benchmark.bmbm do |b|
15
- b.report("Find") do
16
- files = Set.new
17
- Find.find(*test_dirs) do |f|
18
- files << [f, File.lstat(f)]
19
- end
20
- end
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
- b.report("FastFind") do
23
- files = Set.new
24
- FastFinder.find(*test_dirs) do |f, stat|
25
- files << [f, stat]
26
- end
27
- end
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{High performance 'find' alternative.}
13
- spec.description = %q{FastFind is a find workalike which optionally passes
14
- in the File::Stat to the block, and can use multile
15
- threads to walk directories concurrently.}
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.0"
32
+ spec.required_ruby_version = ">= 2.5"
33
33
 
34
- spec.add_development_dependency "bundler", "~> 1.9"
35
- spec.add_development_dependency "rake", "~> 10.0"
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
- require 'thread'
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
- DEFAULT_CONCURRENCY = %w(jruby rbx).include?(RUBY_ENGINE) ? 8 : 1
11
-
12
- def self.find(*paths, concurrency: DEFAULT_CONCURRENCY, ignore_error: true,
13
- &block)
14
- Finder.new(concurrency: concurrency, one_shot: true)
15
- .find(*paths, ignore_error: ignore_error, &block)
16
- end
17
-
18
- def self.prune
19
- throw :prune
20
- end
21
-
22
- class Finder
23
- def initialize(concurrency: DEFAULT_CONCURRENCY, one_shot: false)
24
- @mutex = Mutex.new
25
- @queue = Queue.new
26
- @one_shot = one_shot
27
- @concurrency = concurrency
28
- @walkers = nil
29
- end
30
-
31
- def startup
32
- @mutex.synchronize do
33
- return if @walkers
34
-
35
- @walkers = @concurrency.times.map { Walker.new.spawn(@queue) }
36
- end
37
- end
38
-
39
- def shutdown
40
- @mutex.synchronize do
41
- return unless @walkers
42
-
43
- @queue.clear
44
- @walkers.each { @queue << nil }
45
- @walkers.each(&:join)
46
-
47
- @walkers = nil
48
- end
49
- end
50
-
51
- def find(*paths, ignore_error: true, &block)
52
- block or return enum_for(__method__, *paths, ignore_error: ignore_error)
53
-
54
- results = Queue.new
55
- pending = Set.new
56
-
57
- startup
58
-
59
- paths.map!(&:dup).each do |path|
60
- path = path.to_path if path.respond_to? :to_path
61
- results << [path, Util.safe_stat(path)]
62
- end
63
- results << [:initial, :finished]
64
- pending << [:initial, :initial.encoding]
65
-
66
- while result = results.deq
67
- path, stat = result
68
-
69
- if stat == :finished
70
- pending.delete([path, path.encoding])
71
-
72
- if pending.empty?
73
- break
74
- else
75
- next
76
- end
77
- end
78
-
79
- catch(:prune) do
80
- yield_entry(result, block) if path.is_a? String
81
-
82
- case stat
83
- when Exception then raise stat unless ignore_error
84
- when File::Stat
85
- if stat.directory? and !pending.include?(pe = [path, path.encoding])
86
- pending << pe
87
- @queue << [path, results]
88
- end
89
- end
90
- end
91
- end
92
- ensure
93
- if one_shot?
94
- @queue.clear
95
- shutdown
96
- end
97
- end
98
-
99
- private
100
-
101
- def one_shot?() !!@one_shot end
102
-
103
- def yield_entry(entry, block)
104
- if block.arity == 2
105
- block.call(entry[0].dup.taint, entry[1])
106
- else
107
- block.call entry[0].dup.taint
108
- end
109
- end
110
- end
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastFind
2
- VERSION = "0.1.1"
4
+ VERSION = '0.2.0'
3
5
  end
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.1.1
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: 2015-08-01 00:00:00.000000000 Z
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.9'
19
- name: bundler
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: '1.9'
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: '10.0'
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: '10.0'
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 find workalike which optionally passes
57
- in the File::Stat to the block, and can use multile
58
- threads to walk directories concurrently.
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.0'
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
- rubyforge_project:
98
- rubygems_version: 2.4.8
111
+ rubygems_version: 3.0.6
99
112
  signing_key:
100
113
  specification_version: 4
101
- summary: High performance 'find' alternative.
114
+ summary: Multi-threaded Find alternative.
102
115
  test_files: []
data/.travis.yml DELETED
@@ -1,3 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.2