fast_ignore 0.10.2 → 0.11.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/.spellr_wordlists/english.txt +4 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +4 -0
- data/README.md +27 -3
- data/bin/time +11 -11
- data/lib/fast_ignore.rb +15 -19
- data/lib/fast_ignore/backports.rb +60 -6
- data/lib/fast_ignore/fn_match_to_re.rb +96 -0
- data/lib/fast_ignore/rule.rb +9 -11
- data/lib/fast_ignore/rule_builder.rb +10 -10
- data/lib/fast_ignore/rule_set.rb +18 -14
- data/lib/fast_ignore/rule_set_builder.rb +1 -4
- data/lib/fast_ignore/shebang_rule.rb +12 -6
- data/lib/fast_ignore/version.rb +1 -1
- metadata +3 -4
- data/lib/fast_ignore/backports/delete_prefix_suffix.rb +0 -56
- data/lib/fast_ignore/backports/dir_each_child.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f467113b0ee03faf028758ac176c59b3e51465684f8ffe144d432ba75133c10f
|
4
|
+
data.tar.gz: 2f6f0ce0babec00977a55595baa7ec806d7a9a3f1fec86df46f29475b41238cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a7698e54ab0897b6b50c8638d41f3243bd3ba4ced559a5c7e01f6d365b6fb875b906a1924e96641572815748479eb793dfabc91da6f3bec62115f98ff39a392
|
7
|
+
data.tar.gz: 7e8b85ff838d04d1c16cd3eca5a480d6948342a07667760782840b0e36a706c02db2bb4e09e3370b3ccc1cdc2f38f9e893707c9f2128652a8a4aae4cdb8dd3d0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# v0.11.0
|
2
|
+
- major performance improvement (use regexp rather than fnmatch)
|
3
|
+
- optionally pass directory: and content: into allowed? if these are already loaded.
|
4
|
+
|
1
5
|
# v0.10.2
|
2
6
|
- add FastIgnore#=== as an alias for FastIgnore#allowed? so that FastIgnore objects can be used for case statements.
|
3
7
|
- Fix shebangs in non-pwd-root situations
|
data/README.md
CHANGED
@@ -11,6 +11,16 @@ Filter a directory tree using a .gitignore file. Recognises all of the [gitignor
|
|
11
11
|
FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
|
12
12
|
```
|
13
13
|
|
14
|
+
## Features
|
15
|
+
|
16
|
+
- Fast (faster than using `` `git ls-files`.split("\n") `` for small repos (because it avoids the overhead of ``` `` ```))
|
17
|
+
- Supports ruby 2.4 - 2.7
|
18
|
+
- supports all gitignore rule patterns
|
19
|
+
- doesn't require git to be installed
|
20
|
+
- supports a gitignore-esque "include" patterns. (`include_rules:`/`include_files:`)
|
21
|
+
- supports an expansion of include patterns, matching expanded paths (`argv_rules:`)
|
22
|
+
- supports matching by shebang rather than filename for extensionless files: `#!:`
|
23
|
+
|
14
24
|
## Installation
|
15
25
|
|
16
26
|
Add this line to your application's Gemfile:
|
@@ -62,7 +72,8 @@ FastIgnore.new.allowed?('~/home/path')
|
|
62
72
|
This is aliased as `===` so you can use the FastIgnore object in case statements.
|
63
73
|
```ruby
|
64
74
|
case my_path
|
65
|
-
when FastIgnore.new
|
75
|
+
when FastIgnore.new
|
76
|
+
puts(my_path)
|
66
77
|
end
|
67
78
|
```
|
68
79
|
|
@@ -214,6 +225,15 @@ FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
|
|
214
225
|
|
215
226
|
This does unfortunately lose the file path as the root for `/` and `/**` rules.
|
216
227
|
|
228
|
+
### optimising #allowed?
|
229
|
+
|
230
|
+
To avoid unnecessary calls to the filesystem, if your code already knows whether or not it's a directory, or if you're checking shebangs and you have already read the content of the file: use
|
231
|
+
```ruby
|
232
|
+
FastIgnore.new.allowed?('relative/path', directory: false, content: "#!/usr/bin/ruby\n\nputs 'ok'\n")
|
233
|
+
```
|
234
|
+
This is not required, and if FastIgnore does have to go to the filesystem for this information it's well optimised to only read what is necessary.
|
235
|
+
|
236
|
+
|
217
237
|
## Known issues
|
218
238
|
- Doesn't take into account project excludes in `.git/info/exclude`
|
219
239
|
- Doesn't take into account globally ignored files in `git config core.excludesFile`.
|
@@ -224,9 +244,13 @@ This does unfortunately lose the file path as the root for `/` and `/**` rules.
|
|
224
244
|
|
225
245
|
## Development
|
226
246
|
|
227
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
247
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests and linters.
|
248
|
+
|
249
|
+
You can run `bin/console` for an interactive prompt that will allow you to experiment.
|
250
|
+
`bin/ls [argv_rules]` will return something equivalent to `git ls-files` and `bin/time [argv_rules]` will give you the average time for 30 runs.
|
251
|
+
This repo is too small to stress bin/time more than 0.01s, switch to a large repo and find the average time before and after changes.
|
228
252
|
|
229
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
253
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
230
254
|
|
231
255
|
## Contributing
|
232
256
|
|
data/bin/time
CHANGED
@@ -5,17 +5,17 @@
|
|
5
5
|
# ensure nothing else is watching that dir in the filesystem e.g. webpack
|
6
6
|
|
7
7
|
require 'open3'
|
8
|
+
require 'shellwords'
|
8
9
|
RUNS = 30
|
9
|
-
SCRIPT = "time #{__dir__}/ls"
|
10
|
-
Dir.chdir ARGV[0] do
|
11
|
-
times = Array.new(RUNS).map do
|
12
|
-
run_times = Open3.capture3(SCRIPT)[1]
|
13
|
-
puts run_times.lstrip
|
14
|
-
run_times.scan(/(?:\d+(?:.\d+)?)/)
|
15
|
-
end
|
10
|
+
SCRIPT = "time #{__dir__}/ls #{Shellwords.join(ARGV)}"
|
16
11
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
)
|
12
|
+
times = Array.new(RUNS).map do
|
13
|
+
run_times = Open3.capture3(SCRIPT)[1]
|
14
|
+
puts run_times.lstrip
|
15
|
+
run_times.scan(/(?:\d+(?:.\d+)?)/)
|
21
16
|
end
|
17
|
+
|
18
|
+
puts format(
|
19
|
+
"\e[1mAverage:\n\e[32m%0.2f real %0.2f user %0.2f sys\e[0m", # rubocop:disable Style/FormatStringToken
|
20
|
+
*times.transpose.map { |n| (n.map(&:to_f).sum / RUNS) }
|
21
|
+
)
|
data/lib/fast_ignore.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative './fast_ignore/rule_builder'
|
|
7
7
|
require_relative './fast_ignore/rule_set'
|
8
8
|
require_relative './fast_ignore/rule'
|
9
9
|
require_relative './fast_ignore/shebang_rule'
|
10
|
+
require_relative './fast_ignore/fn_match_to_re'
|
10
11
|
|
11
12
|
class FastIgnore
|
12
13
|
class Error < StandardError; end
|
@@ -14,13 +15,8 @@ class FastIgnore
|
|
14
15
|
include ::Enumerable
|
15
16
|
|
16
17
|
# :nocov:
|
17
|
-
|
18
|
-
|
19
|
-
using ::FastIgnore::Backports::DeletePrefixSuffix
|
20
|
-
|
21
|
-
require_relative 'fast_ignore/backports/dir_each_child'
|
22
|
-
using ::FastIgnore::Backports::DirEachChild
|
23
|
-
end
|
18
|
+
using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
|
19
|
+
using ::FastIgnore::Backports::DirEachChild if defined?(::FastIgnore::Backports::DirEachChild)
|
24
20
|
# :nocov:
|
25
21
|
|
26
22
|
def initialize(relative: false, root: nil, follow_symlinks: false, **rule_set_builder_args)
|
@@ -41,23 +37,15 @@ class FastIgnore
|
|
41
37
|
each_recursive(root_from_pwd, '', &block)
|
42
38
|
end
|
43
39
|
|
44
|
-
def
|
45
|
-
if @follow_symlinks
|
46
|
-
::File.stat(path).directory?
|
47
|
-
else
|
48
|
-
::File.lstat(path).directory?
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def allowed?(path)
|
40
|
+
def allowed?(path, directory: nil, content: nil)
|
53
41
|
full_path = ::File.expand_path(path, @root)
|
54
42
|
return false unless full_path.start_with?(@root)
|
55
|
-
return false if directory?(full_path)
|
43
|
+
return false if directory.nil? ? directory?(full_path) : directory
|
56
44
|
|
57
45
|
relative_path = full_path.delete_prefix(@root)
|
58
46
|
filename = ::File.basename(relative_path)
|
59
47
|
|
60
|
-
@rule_sets.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename) }
|
48
|
+
@rule_sets.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename, content) }
|
61
49
|
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
62
50
|
false
|
63
51
|
end
|
@@ -65,6 +53,14 @@ class FastIgnore
|
|
65
53
|
|
66
54
|
private
|
67
55
|
|
56
|
+
def directory?(path)
|
57
|
+
if @follow_symlinks
|
58
|
+
::File.stat(path).directory?
|
59
|
+
else
|
60
|
+
::File.lstat(path).directory?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
68
64
|
def each_recursive(parent_full_path, parent_relative_path, &block) # rubocop:disable Metrics/MethodLength
|
69
65
|
::Dir.each_child(parent_full_path) do |filename|
|
70
66
|
begin
|
@@ -72,7 +68,7 @@ class FastIgnore
|
|
72
68
|
relative_path = parent_relative_path + filename
|
73
69
|
dir = directory?(full_path)
|
74
70
|
|
75
|
-
next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename) }
|
71
|
+
next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename, nil) }
|
76
72
|
|
77
73
|
if dir
|
78
74
|
each_recursive(full_path + '/', relative_path + '/', &block)
|
@@ -2,15 +2,69 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
module Backports
|
5
|
-
|
5
|
+
ruby_major, ruby_minor = RUBY_VERSION.split('.', 2)
|
6
|
+
unless ruby_major.to_i > 2 || ruby_major.to_i == 2 && ruby_minor.to_i > 5
|
7
|
+
module DirEachChild
|
8
|
+
refine ::Dir.singleton_class do
|
9
|
+
def each_child(path, &block)
|
10
|
+
Dir.entries(path).each do |entry|
|
11
|
+
next if entry == '.' || entry == '..'
|
6
12
|
|
7
|
-
|
8
|
-
|
13
|
+
block.call entry
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
9
18
|
|
10
|
-
|
11
|
-
|
19
|
+
module DeletePrefixSuffix
|
20
|
+
refine ::String do
|
21
|
+
# delete_prefix!(prefix) -> self or nil
|
22
|
+
# Deletes leading prefix from str, returning nil if no change was made.
|
23
|
+
#
|
24
|
+
# "hello".delete_prefix!("hel") #=> "lo"
|
25
|
+
# "hello".delete_prefix!("llo") #=> nil
|
26
|
+
def delete_prefix!(str)
|
27
|
+
return unless start_with?(str)
|
12
28
|
|
13
|
-
|
29
|
+
slice!(0..(str.length - 1))
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# delete_suffix!(suffix) -> self or nil
|
34
|
+
# Deletes trailing suffix from str, returning nil if no change was made.
|
35
|
+
#
|
36
|
+
# "hello".delete_suffix!("llo") #=> "he"
|
37
|
+
# "hello".delete_suffix!("hel") #=> nil
|
38
|
+
def delete_suffix!(str)
|
39
|
+
return unless end_with?(str)
|
40
|
+
|
41
|
+
slice!(-str.length..-1)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# delete_prefix(prefix) -> new_str click to toggle source
|
46
|
+
# Returns a copy of str with leading prefix deleted.
|
47
|
+
#
|
48
|
+
# "hello".delete_prefix("hel") #=> "lo"
|
49
|
+
# "hello".delete_prefix("llo") #=> "hello"
|
50
|
+
def delete_prefix(str)
|
51
|
+
s = dup
|
52
|
+
s.delete_prefix!(str)
|
53
|
+
s
|
54
|
+
end
|
55
|
+
|
56
|
+
# delete_suffix(suffix) -> new_str
|
57
|
+
# Returns a copy of str with trailing suffix deleted.
|
58
|
+
#
|
59
|
+
# "hello".delete_suffix("llo") #=> "he"
|
60
|
+
# "hello".delete_suffix("hel") #=> "hello"
|
61
|
+
def delete_suffix(str) # leftovers:allowed
|
62
|
+
s = dup
|
63
|
+
s.delete_suffix!(str)
|
64
|
+
s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
14
68
|
end
|
15
69
|
end
|
16
70
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module FNMatchToRegex
|
5
|
+
# This doesn't look rubyish because i ported it from rust (the only rust i ever wrote that worked)
|
6
|
+
class << self
|
7
|
+
def call(pattern) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
8
|
+
re = '\\A'.dup
|
9
|
+
|
10
|
+
in_character_group = false
|
11
|
+
has_characters_in_group = false
|
12
|
+
escape_next_character = false
|
13
|
+
last_char_opened_character_group = false
|
14
|
+
negated_character_group = false
|
15
|
+
stars = 0
|
16
|
+
|
17
|
+
pattern.each_char do |char| # rubocop:disable Metrics/BlockLength
|
18
|
+
if escape_next_character
|
19
|
+
re << Regexp.escape(char)
|
20
|
+
escape_next_character = false
|
21
|
+
elsif char == '\\' # single char, just needs to be escaped
|
22
|
+
escape_next_character = true
|
23
|
+
elsif in_character_group
|
24
|
+
if char == '/'
|
25
|
+
if negated_character_group
|
26
|
+
has_characters_in_group = true
|
27
|
+
re << char
|
28
|
+
end
|
29
|
+
elsif char == '^'
|
30
|
+
if last_char_opened_character_group
|
31
|
+
re << char
|
32
|
+
negated_character_group = true
|
33
|
+
else
|
34
|
+
re << '\\^'
|
35
|
+
has_characters_in_group = true
|
36
|
+
end
|
37
|
+
# not characters in group
|
38
|
+
elsif char == ']'
|
39
|
+
break unless has_characters_in_group
|
40
|
+
|
41
|
+
re << ']'
|
42
|
+
in_character_group = false
|
43
|
+
has_characters_in_group = false
|
44
|
+
negated_character_group = false
|
45
|
+
last_char_opened_character_group = false
|
46
|
+
elsif char == '-'
|
47
|
+
has_characters_in_group = true
|
48
|
+
re << char
|
49
|
+
else
|
50
|
+
has_characters_in_group = true
|
51
|
+
re << Regexp.escape(char)
|
52
|
+
end
|
53
|
+
last_char_opened_character_group = false
|
54
|
+
elsif char == '*'
|
55
|
+
stars += 1
|
56
|
+
elsif char == '/'
|
57
|
+
re << if stars >= 2
|
58
|
+
'(?:.*/)?'
|
59
|
+
elsif stars.positive?
|
60
|
+
'[^/]*/'
|
61
|
+
else
|
62
|
+
char
|
63
|
+
end
|
64
|
+
stars = 0
|
65
|
+
else
|
66
|
+
if stars.positive?
|
67
|
+
re << '[^/]*'
|
68
|
+
stars = 0
|
69
|
+
end
|
70
|
+
if char == '?'
|
71
|
+
re << '[^/]'
|
72
|
+
elsif char == '['
|
73
|
+
re << '['
|
74
|
+
in_character_group = true
|
75
|
+
last_char_opened_character_group = true
|
76
|
+
else
|
77
|
+
re << Regexp.escape(char)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if in_character_group
|
83
|
+
return /(?!)/ # impossible to match anything
|
84
|
+
end
|
85
|
+
|
86
|
+
if stars >= 2
|
87
|
+
re << '.*'
|
88
|
+
elsif stars.positive?
|
89
|
+
re << '[^/]*'
|
90
|
+
end
|
91
|
+
re << '\\z'
|
92
|
+
Regexp.new(re, Regexp::IGNORECASE)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/fast_ignore/rule.rb
CHANGED
@@ -2,12 +2,6 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
class Rule
|
5
|
-
# FNMATCH_OPTIONS = (
|
6
|
-
# ::File::FNM_DOTMATCH |
|
7
|
-
# ::File::FNM_PATHNAME |
|
8
|
-
# ::File::FNM_CASEFOLD
|
9
|
-
# ).freeze # = 14
|
10
|
-
|
11
5
|
attr_reader :negation
|
12
6
|
alias_method :negation?, :negation
|
13
7
|
undef :negation
|
@@ -20,12 +14,16 @@ class FastIgnore
|
|
20
14
|
alias_method :unanchored?, :unanchored
|
21
15
|
undef :unanchored
|
22
16
|
|
23
|
-
|
24
|
-
|
17
|
+
attr_reader :type
|
18
|
+
attr_reader :rule
|
19
|
+
|
20
|
+
def initialize(rule, negation, unanchored = nil, dir_only = nil)
|
21
|
+
@rule = rule.is_a?(Regexp) ? rule : ::FastIgnore::FNMatchToRegex.call(rule)
|
25
22
|
@unanchored = unanchored
|
26
23
|
@dir_only = dir_only
|
27
24
|
@negation = negation
|
28
|
-
|
25
|
+
|
26
|
+
@type = negation ? 1 : 0
|
29
27
|
|
30
28
|
freeze
|
31
29
|
end
|
@@ -44,8 +42,8 @@ class FastIgnore
|
|
44
42
|
end
|
45
43
|
# :nocov:
|
46
44
|
|
47
|
-
def match?(relative_path, _, _)
|
48
|
-
|
45
|
+
def match?(relative_path, _, _, _)
|
46
|
+
@rule.match?(relative_path)
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
@@ -4,10 +4,7 @@ class FastIgnore
|
|
4
4
|
module RuleBuilder
|
5
5
|
class << self
|
6
6
|
# :nocov:
|
7
|
-
|
8
|
-
require_relative 'backports/delete_prefix_suffix'
|
9
|
-
using ::FastIgnore::Backports::DeletePrefixSuffix
|
10
|
-
end
|
7
|
+
using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
|
11
8
|
# :nocov:
|
12
9
|
|
13
10
|
def build(rule, allow, expand_path, file_root)
|
@@ -50,8 +47,11 @@ class FastIgnore
|
|
50
47
|
dir_only = extract_dir_only(rule)
|
51
48
|
negation = extract_negation(rule, allow)
|
52
49
|
|
53
|
-
|
54
|
-
|
50
|
+
unanchored = if expand_path
|
51
|
+
expand_rule_path(rule, expand_path)
|
52
|
+
else
|
53
|
+
unanchored?(rule)
|
54
|
+
end
|
55
55
|
rule.delete_prefix!('/')
|
56
56
|
|
57
57
|
rule.prepend("#{file_root}#{'**/' if unanchored}") if file_root || unanchored
|
@@ -73,7 +73,7 @@ class FastIgnore
|
|
73
73
|
def expand_rule_path(rule, root)
|
74
74
|
rule.replace(::File.expand_path(rule)) if rule.match?(EXPAND_PATH_RE)
|
75
75
|
rule.delete_prefix!(root)
|
76
|
-
rule.
|
76
|
+
rule.start_with?('*')
|
77
77
|
end
|
78
78
|
|
79
79
|
def unanchored?(rule)
|
@@ -81,10 +81,10 @@ class FastIgnore
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def build_gitignore_rules(rule, unanchored, allow, dir_only, negation)
|
84
|
-
rules = [::FastIgnore::Rule.new(rule
|
84
|
+
rules = [::FastIgnore::Rule.new(rule, negation, unanchored, dir_only)]
|
85
85
|
return rules unless allow
|
86
86
|
|
87
|
-
rules << ::FastIgnore::Rule.new("#{rule}/**/*", unanchored, false
|
87
|
+
rules << ::FastIgnore::Rule.new("#{rule}/**/*", negation, unanchored, false)
|
88
88
|
rules + ancestor_rules(rule, unanchored)
|
89
89
|
end
|
90
90
|
|
@@ -93,7 +93,7 @@ class FastIgnore
|
|
93
93
|
|
94
94
|
while (parent = ::File.dirname(parent)) != '.'
|
95
95
|
rule = ::File.basename(parent) == '**' ? "#{parent}/*" : parent.freeze
|
96
|
-
ancestor_rules << ::FastIgnore::Rule.new(rule,
|
96
|
+
ancestor_rules << ::FastIgnore::Rule.new(rule, true, unanchored, true)
|
97
97
|
end
|
98
98
|
|
99
99
|
ancestor_rules
|
data/lib/fast_ignore/rule_set.rb
CHANGED
@@ -3,39 +3,43 @@
|
|
3
3
|
class FastIgnore
|
4
4
|
class RuleSet
|
5
5
|
def initialize(rules, allow)
|
6
|
-
@dir_rules = rules.reject(&:file_only?).freeze
|
7
|
-
@file_rules = rules.reject(&:dir_only?).freeze
|
6
|
+
@dir_rules = squash_rules(rules.reject(&:file_only?)).freeze
|
7
|
+
@file_rules = squash_rules(rules.reject(&:dir_only?)).freeze
|
8
8
|
@any_not_anchored = rules.any?(&:unanchored?)
|
9
9
|
@has_shebang_rules = rules.any?(&:shebang)
|
10
|
+
|
10
11
|
@allowed_recursive = { '.' => true }
|
11
12
|
@allow = allow
|
12
13
|
|
13
14
|
freeze
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
@dir_rules.freeze
|
18
|
-
@file_rules.freeze
|
19
|
-
|
20
|
-
super
|
21
|
-
end
|
22
|
-
|
23
|
-
def allowed_recursive?(relative_path, dir, full_path, filename)
|
17
|
+
def allowed_recursive?(relative_path, dir, full_path, filename, content = nil)
|
24
18
|
@allowed_recursive.fetch(relative_path) do
|
25
19
|
@allowed_recursive[relative_path] =
|
26
|
-
allowed_recursive?(::File.dirname(relative_path), true, nil, nil) &&
|
27
|
-
allowed_unrecursive?(relative_path, dir, full_path, filename)
|
20
|
+
allowed_recursive?(::File.dirname(relative_path), true, nil, nil, nil) &&
|
21
|
+
allowed_unrecursive?(relative_path, dir, full_path, filename, content)
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
def allowed_unrecursive?(relative_path, dir, full_path, filename)
|
25
|
+
def allowed_unrecursive?(relative_path, dir, full_path, filename, content)
|
32
26
|
(dir ? @dir_rules : @file_rules).reverse_each do |rule|
|
33
|
-
return rule.negation? if rule.match?(relative_path, full_path, filename)
|
27
|
+
return rule.negation? if rule.match?(relative_path, full_path, filename, content)
|
34
28
|
end
|
35
29
|
|
36
30
|
(not @allow) || (dir && @any_not_anchored)
|
37
31
|
end
|
38
32
|
|
33
|
+
def squash_rules(rules)
|
34
|
+
out = rules.chunk_while { |a, b| a.type == b.type }.map do |chunk|
|
35
|
+
next chunk.first if chunk.length == 1
|
36
|
+
|
37
|
+
chunk.first.class.new(Regexp.union(chunk.map(&:rule)), chunk.first.negation?)
|
38
|
+
end
|
39
|
+
|
40
|
+
out
|
41
|
+
end
|
42
|
+
|
39
43
|
def weight
|
40
44
|
@dir_rules.length + (@has_shebang_rules ? 10 : 0)
|
41
45
|
end
|
@@ -4,10 +4,7 @@ class FastIgnore
|
|
4
4
|
module RuleSetBuilder
|
5
5
|
class << self
|
6
6
|
# :nocov:
|
7
|
-
|
8
|
-
require_relative 'backports/delete_prefix_suffix'
|
9
|
-
using ::FastIgnore::Backports::DeletePrefixSuffix
|
10
|
-
end
|
7
|
+
using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
|
11
8
|
# :nocov:
|
12
9
|
|
13
10
|
def build( # rubocop:disable Metrics/ParameterLists
|
@@ -6,12 +6,18 @@ class FastIgnore
|
|
6
6
|
alias_method :negation?, :negation
|
7
7
|
undef :negation
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :rule
|
10
|
+
alias_method :shebang, :rule
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
attr_reader :type
|
13
|
+
|
14
|
+
def initialize(rule, negation)
|
15
|
+
@rule = rule
|
13
16
|
@negation = negation
|
14
17
|
|
18
|
+
@type = 2
|
19
|
+
@type += 1 if negation
|
20
|
+
|
15
21
|
freeze
|
16
22
|
end
|
17
23
|
|
@@ -29,14 +35,14 @@ class FastIgnore
|
|
29
35
|
|
30
36
|
# :nocov:
|
31
37
|
def inspect
|
32
|
-
"#<ShebangRule #{'allow ' if @negation}#!:#{@
|
38
|
+
"#<ShebangRule #{'allow ' if @negation}#!:#{@rule.to_s[15..-4]}>"
|
33
39
|
end
|
34
40
|
# :nocov:
|
35
41
|
|
36
|
-
def match?(_, full_path, filename)
|
42
|
+
def match?(_, full_path, filename, content)
|
37
43
|
return false if filename.include?('.')
|
38
44
|
|
39
|
-
first_line(full_path)&.match?(@
|
45
|
+
(content || first_line(full_path))&.match?(@rule)
|
40
46
|
end
|
41
47
|
|
42
48
|
private
|
data/lib/fast_ignore/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_ignore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dana Sherson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -165,8 +165,7 @@ files:
|
|
165
165
|
- fast_ignore.gemspec
|
166
166
|
- lib/fast_ignore.rb
|
167
167
|
- lib/fast_ignore/backports.rb
|
168
|
-
- lib/fast_ignore/
|
169
|
-
- lib/fast_ignore/backports/dir_each_child.rb
|
168
|
+
- lib/fast_ignore/fn_match_to_re.rb
|
170
169
|
- lib/fast_ignore/rule.rb
|
171
170
|
- lib/fast_ignore/rule_builder.rb
|
172
171
|
- lib/fast_ignore/rule_set.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# This is a backport of ruby 2.5's delete_prefix/delete_suffix methods
|
4
|
-
class FastIgnore
|
5
|
-
module Backports
|
6
|
-
module DeletePrefixSuffix
|
7
|
-
refine ::String do
|
8
|
-
# delete_prefix!(prefix) -> self or nil
|
9
|
-
# Deletes leading prefix from str, returning nil if no change was made.
|
10
|
-
#
|
11
|
-
# "hello".delete_prefix!("hel") #=> "lo"
|
12
|
-
# "hello".delete_prefix!("llo") #=> nil
|
13
|
-
def delete_prefix!(str)
|
14
|
-
return unless start_with?(str)
|
15
|
-
|
16
|
-
slice!(0..(str.length - 1))
|
17
|
-
self
|
18
|
-
end
|
19
|
-
|
20
|
-
# delete_suffix!(suffix) -> self or nil
|
21
|
-
# Deletes trailing suffix from str, returning nil if no change was made.
|
22
|
-
#
|
23
|
-
# "hello".delete_suffix!("llo") #=> "he"
|
24
|
-
# "hello".delete_suffix!("hel") #=> nil
|
25
|
-
def delete_suffix!(str)
|
26
|
-
return unless end_with?(str)
|
27
|
-
|
28
|
-
slice!(-str.length..-1)
|
29
|
-
self
|
30
|
-
end
|
31
|
-
|
32
|
-
# delete_prefix(prefix) -> new_str click to toggle source
|
33
|
-
# Returns a copy of str with leading prefix deleted.
|
34
|
-
#
|
35
|
-
# "hello".delete_prefix("hel") #=> "lo"
|
36
|
-
# "hello".delete_prefix("llo") #=> "hello"
|
37
|
-
def delete_prefix(str)
|
38
|
-
s = dup
|
39
|
-
s.delete_prefix!(str)
|
40
|
-
s
|
41
|
-
end
|
42
|
-
|
43
|
-
# delete_suffix(suffix) -> new_str
|
44
|
-
# Returns a copy of str with trailing suffix deleted.
|
45
|
-
#
|
46
|
-
# "hello".delete_suffix("llo") #=> "he"
|
47
|
-
# "hello".delete_suffix("hel") #=> "hello"
|
48
|
-
def delete_suffix(str) # leftovers:allowed
|
49
|
-
s = dup
|
50
|
-
s.delete_suffix!(str)
|
51
|
-
s
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# This is a backport of ruby 2.5's each_child method
|
4
|
-
class FastIgnore
|
5
|
-
module Backports
|
6
|
-
module DirEachChild
|
7
|
-
refine ::Dir.singleton_class do
|
8
|
-
def each_child(path, &block)
|
9
|
-
Dir.entries(path).each do |entry|
|
10
|
-
next if entry == '.' || entry == '..'
|
11
|
-
|
12
|
-
block.call entry
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|