fast_ignore 0.17.2 → 0.17.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +0 -13
  4. data/lib/fast_ignore/builders/gitignore.rb +15 -0
  5. data/lib/fast_ignore/builders/shebang.rb +17 -0
  6. data/lib/fast_ignore/builders/shebang_or_gitignore.rb +15 -0
  7. data/lib/fast_ignore/candidate.rb +85 -0
  8. data/lib/fast_ignore/gitconfig_parser.rb +1 -1
  9. data/lib/fast_ignore/gitignore_include_rule_builder.rb +40 -63
  10. data/lib/fast_ignore/gitignore_rule_builder.rb +49 -26
  11. data/lib/fast_ignore/gitignore_rule_group.rb +31 -0
  12. data/lib/fast_ignore/gitignore_rule_scanner.rb +12 -0
  13. data/lib/fast_ignore/matchers/allow_any_dir.rb +39 -0
  14. data/lib/fast_ignore/matchers/allow_path_regexp.rb +45 -0
  15. data/lib/fast_ignore/matchers/ignore_path_regexp.rb +45 -0
  16. data/lib/fast_ignore/matchers/shebang_regexp.rb +46 -0
  17. data/lib/fast_ignore/matchers/unmatchable.rb +31 -0
  18. data/lib/fast_ignore/matchers/within_dir.rb +50 -0
  19. data/lib/fast_ignore/path_expander.rb +11 -0
  20. data/lib/fast_ignore/path_regexp_builder.rb +130 -0
  21. data/lib/fast_ignore/patterns.rb +33 -0
  22. data/lib/fast_ignore/relative_candidate.rb +20 -0
  23. data/lib/fast_ignore/rule_group.rb +42 -0
  24. data/lib/fast_ignore/rule_groups.rb +55 -0
  25. data/lib/fast_ignore/version.rb +1 -1
  26. data/lib/fast_ignore/walkers/base.rb +26 -0
  27. data/lib/fast_ignore/walkers/file_system.rb +46 -0
  28. data/lib/fast_ignore/walkers/gitignore_collecting_file_system.rb +48 -0
  29. data/lib/fast_ignore.rb +54 -84
  30. metadata +23 -11
  31. data/lib/fast_ignore/file_root.rb +0 -35
  32. data/lib/fast_ignore/gitignore_rule_regexp_builder.rb +0 -76
  33. data/lib/fast_ignore/rule.rb +0 -65
  34. data/lib/fast_ignore/rule_builder.rb +0 -37
  35. data/lib/fast_ignore/rule_set.rb +0 -76
  36. data/lib/fast_ignore/rule_sets.rb +0 -109
  37. data/lib/fast_ignore/shebang_rule.rb +0 -80
  38. data/lib/fast_ignore/unmatchable_rule.rb +0 -41
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ module Matchers
5
+ class ShebangRegexp
6
+ attr_reader :squash_id
7
+ attr_reader :rule
8
+
9
+ def initialize(rule, negation)
10
+ @rule = rule
11
+ @return_value = negation ? :allow : :ignore
12
+ @squash_id = negation ? :allow_shebang : :ignore_shebang
13
+
14
+ freeze
15
+ end
16
+
17
+ def squash(list)
18
+ self.class.new(::Regexp.union(list.map(&:rule)), @return_value == :allow)
19
+ end
20
+
21
+ def file_only?
22
+ true
23
+ end
24
+
25
+ def dir_only?
26
+ false
27
+ end
28
+
29
+ # :nocov:
30
+ def inspect
31
+ "#<ShebangRegexp #{@return_value} /#{@rule.to_s[26..-4]}/>"
32
+ end
33
+ # :nocov:
34
+
35
+ def match?(candidate)
36
+ return false if candidate.filename.include?('.')
37
+
38
+ @return_value if candidate.first_line.match?(@rule)
39
+ end
40
+
41
+ def weight
42
+ 2
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ module Matchers
5
+ module Unmatchable
6
+ class << self
7
+ def dir_only?
8
+ false
9
+ end
10
+
11
+ def file_only?
12
+ false
13
+ end
14
+
15
+ def weight
16
+ 0
17
+ end
18
+
19
+ # :nocov:
20
+ def inspect
21
+ '#<Unmatchable>'
22
+ end
23
+ # :nocov:
24
+
25
+ def match?(_)
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ module Matchers
5
+ class WithinDir
6
+ attr_reader :weight
7
+
8
+ def initialize(matchers, root)
9
+ @dir_matchers = squash_matchers(matchers.reject(&:file_only?))
10
+ @file_matchers = squash_matchers(matchers.reject(&:dir_only?))
11
+
12
+ @weight = @dir_matchers.sum(&:weight) + @file_matchers.sum(&:weight)
13
+ @root = root
14
+
15
+ freeze
16
+ end
17
+
18
+ def match?(candidate)
19
+ relative_candidate = candidate.relative_to(@root)
20
+ return false unless relative_candidate
21
+
22
+ (candidate.directory? ? @dir_matchers : @file_matchers).reverse_each do |rule|
23
+ val = rule.match?(relative_candidate)
24
+ return val if val
25
+ end
26
+
27
+ false
28
+ end
29
+
30
+ def empty?
31
+ @dir_matchers.empty? && @file_matchers.empty?
32
+ end
33
+
34
+ private
35
+
36
+ def squash_matchers(matchers)
37
+ return matchers if matchers.empty?
38
+
39
+ matchers -= [::FastIgnore::Matchers::Unmatchable]
40
+ return [::FastIgnore::Matchers::Unmatchable] if matchers.empty?
41
+
42
+ matchers.chunk_while { |a, b| a.squash_id == b.squash_id }.map do |chunk|
43
+ next chunk.first if chunk.length == 1
44
+
45
+ chunk.first.squash(chunk)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ module PathExpander
5
+ def self.expand_path(path, dir)
6
+ ::File.expand_path(path, dir)
7
+ rescue ::ArgumentError
8
+ ::File.expand_path("./#{path}", dir)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class PathRegexpBuilder
5
+ def initialize
6
+ @string = +''
7
+ end
8
+
9
+ def to_regexp
10
+ # Regexp::IGNORECASE = 1
11
+ ::Regexp.new(@string, 1)
12
+ end
13
+
14
+ # String methods
15
+
16
+ def dup
17
+ out = super
18
+
19
+ @string = @string.dup
20
+
21
+ out
22
+ end
23
+
24
+ def to_str
25
+ @string
26
+ end
27
+ alias_method :to_s, :to_str
28
+
29
+ def empty?
30
+ @string.empty?
31
+ end
32
+
33
+ def end_with?(str)
34
+ @string.end_with?(str)
35
+ end
36
+
37
+ def append(value)
38
+ @string.<<(value)
39
+
40
+ self
41
+ rescue FrozenError
42
+ # :nocov:
43
+ # the string seems to become inadvertently frozen in 2.5 and 2.6 with specific inputs
44
+ # and i don't understand why
45
+ # it seems like it's happening during the Regexp.new
46
+ # for some reason that i don't understand
47
+ @string = @string.dup
48
+ append(value)
49
+ # :nocov:
50
+ end
51
+ alias_method :<<, :append
52
+
53
+ def prepend(value)
54
+ @string.prepend(value)
55
+
56
+ self
57
+ rescue FrozenError
58
+ # :nocov:
59
+ # the string seems to become inadvertently frozen in 2.5 and 2.6 with specific inputs
60
+ # and i don't understand why
61
+ # it seems like it's happening during the Regexp.new
62
+ # for some reason that i don't understand
63
+ @string = @string.dup
64
+ prepend(value)
65
+ # :nocov:
66
+ end
67
+
68
+ # builder methods
69
+
70
+ def append_escaped(value)
71
+ return unless value
72
+
73
+ append(::Regexp.escape(value))
74
+ end
75
+
76
+ def append_dir
77
+ append('/')
78
+ end
79
+
80
+ def append_any_dir
81
+ append('(?:.*/)?')
82
+ end
83
+
84
+ def append_one_non_dir
85
+ append('[^/]')
86
+ end
87
+
88
+ def append_any_non_dir
89
+ append_one_non_dir
90
+ append('*')
91
+ end
92
+
93
+ def append_many_non_dir
94
+ append_one_non_dir
95
+ append('+')
96
+ end
97
+
98
+ def append_end_anchor
99
+ append('\\z')
100
+ end
101
+
102
+ def append_start_anchor
103
+ append('\\A')
104
+ end
105
+
106
+ def append_dir_or_start_anchor
107
+ append('(?:\\A|/)')
108
+ end
109
+
110
+ def append_dir_or_end_anchor
111
+ append('(?:/|\\z)')
112
+ end
113
+
114
+ def append_character_class_open
115
+ append('(?!/)[')
116
+ end
117
+
118
+ def append_character_class_negation
119
+ append('^')
120
+ end
121
+
122
+ def append_character_class_dash
123
+ append('-')
124
+ end
125
+
126
+ def append_character_class_close
127
+ append(']')
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,33 @@
1
+ # frozen-string-literal: true
2
+
3
+ class FastIgnore
4
+ class Patterns
5
+ def initialize(*patterns, from_file: nil, format: :gitignore, root: nil)
6
+ if from_file
7
+ @root = root || ::File.dirname(from_file)
8
+ @patterns = ::File.exist?(from_file) ? ::File.readlines(from_file) : []
9
+ else
10
+ @root = root || ::Dir.pwd
11
+ @patterns = patterns.flatten.flat_map { |string| string.to_s.lines }
12
+ end
13
+ @root += '/' unless @root.end_with?('/')
14
+ @expand_path_with = (@root if format == :expand_path)
15
+ end
16
+
17
+ def build_matchers(allow: false) # rubocop:disable Metrics/MethodLength
18
+ matchers = @patterns.flat_map do |p|
19
+ ::FastIgnore::Builders::ShebangOrGitignore.build(p, allow, expand_path_with: @expand_path_with)
20
+ end
21
+
22
+ return if matchers.empty?
23
+ return [::FastIgnore::Matchers::WithinDir.new(matchers, @root)] unless allow
24
+
25
+ [
26
+ ::FastIgnore::Matchers::WithinDir.new(matchers, @root),
27
+ ::FastIgnore::Matchers::WithinDir.new(
28
+ ::FastIgnore::GitignoreIncludeRuleBuilder.new(@root).build_as_parent, '/'
29
+ )
30
+ ]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen-string-literal: true
2
+
3
+ class FastIgnore
4
+ class RelativeCandidate
5
+ attr_reader :relative_path
6
+
7
+ def initialize(relative_path, root_candidate)
8
+ @relative_path = relative_path
9
+ @root_candidate = root_candidate
10
+ end
11
+
12
+ def filename
13
+ @root_candidate.filename
14
+ end
15
+
16
+ def first_line
17
+ @root_candidate.first_line
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class RuleGroup
5
+ def initialize(patterns, allow)
6
+ @matchers = Array(patterns).flat_map { |x| x.build_matchers(allow: allow) }.compact
7
+ @allow = allow
8
+ @allowed_recursive = { ::FastIgnore::Candidate.root.key => true }.compare_by_identity
9
+ end
10
+
11
+ def empty?
12
+ @matchers.empty? || @matchers.all?(&:empty?)
13
+ end
14
+
15
+ def weight
16
+ @matchers.sum(&:weight)
17
+ end
18
+
19
+ def freeze
20
+ @matchers.freeze
21
+
22
+ super
23
+ end
24
+
25
+ def allowed_recursive?(candidate)
26
+ @allowed_recursive.fetch(candidate.key) do
27
+ @allowed_recursive[candidate.key] =
28
+ allowed_recursive?(candidate.parent) &&
29
+ allowed_unrecursive?(candidate)
30
+ end
31
+ end
32
+
33
+ def allowed_unrecursive?(candidate)
34
+ @matchers.reverse_each do |matcher|
35
+ val = matcher.match?(candidate)
36
+ return val == :allow if val
37
+ end
38
+
39
+ not @allow
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class RuleGroups
5
+ def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/AbcSize, Metrics/MethodLength
6
+ root:,
7
+ ignore_rules: nil,
8
+ ignore_files: nil,
9
+ gitignore: true,
10
+ include_rules: nil,
11
+ include_files: nil,
12
+ argv_rules: nil
13
+ )
14
+ @array = []
15
+ if gitignore
16
+ @gitignore_rule_group = ::FastIgnore::GitignoreRuleGroup.new(root)
17
+ @array << @gitignore_rule_group
18
+ end
19
+ @array << ::FastIgnore::RuleGroup.new(::FastIgnore::Patterns.new(ignore_rules, root: root), false).freeze
20
+ @array << ::FastIgnore::RuleGroup.new(::FastIgnore::Patterns.new(include_rules, root: root), true).freeze
21
+ @array << ::FastIgnore::RuleGroup.new(
22
+ ::FastIgnore::Patterns.new(argv_rules, root: root, format: :expand_path),
23
+ true
24
+ ).freeze
25
+
26
+ Array(ignore_files).each do |f|
27
+ path = PathExpander.expand_path(f, root)
28
+ @array << ::FastIgnore::RuleGroup.new(::FastIgnore::Patterns.new(from_file: path), false).freeze
29
+ end
30
+ Array(include_files).each do |f|
31
+ path = PathExpander.expand_path(f, root)
32
+ @array << ::FastIgnore::RuleGroup.new(::FastIgnore::Patterns.new(from_file: path), true).freeze
33
+ end
34
+ @array.reject!(&:empty?)
35
+ @array.sort_by!(&:weight)
36
+ @array.freeze
37
+ end
38
+
39
+ def allowed_recursive?(candidate)
40
+ @array.all? { |r| r.allowed_recursive?(candidate) }
41
+ end
42
+
43
+ def allowed_unrecursive?(candidate)
44
+ @array.all? { |r| r.allowed_unrecursive?(candidate) }
45
+ end
46
+
47
+ def add_gitignore(dir)
48
+ @gitignore_rule_group.add_gitignore(dir)
49
+ end
50
+
51
+ def add_gitignore_to_root(path)
52
+ @gitignore_rule_group.add_gitignore_to_root(path)
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.17.2'
4
+ VERSION = '0.17.4'
5
5
  end
@@ -0,0 +1,26 @@
1
+ # frozen-string-literal: true
2
+
3
+ class FastIgnore
4
+ module Walkers
5
+ class Base
6
+ def initialize(rule_groups, follow_symlinks:)
7
+ warn 'FastIgnore deprecation: follow_symlinks argument is deprecated' if follow_symlinks
8
+
9
+ @follow_symlinks = follow_symlinks
10
+ @rule_groups = rule_groups
11
+ end
12
+
13
+ private
14
+
15
+ def directory?(full_path, directory)
16
+ if !directory.nil?
17
+ directory
18
+ elsif @follow_symlinks
19
+ ::File.stat(full_path).directory?
20
+ else
21
+ ::File.lstat(full_path).directory?
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+ # frozen-string-literal: true
2
+
3
+ class FastIgnore
4
+ module Walkers
5
+ class FileSystem < Base
6
+ def allowed?(path, root: Dir.pwd, directory: nil, content: nil, exists: nil, include_directories: false) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
7
+ full_path = PathExpander.expand_path(path, root)
8
+ return false unless full_path.start_with?(root)
9
+
10
+ begin
11
+ dir = directory?(full_path, directory)
12
+ rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
13
+ nil
14
+ end
15
+
16
+ return false if !include_directories && dir
17
+
18
+ candidate = ::FastIgnore::Candidate.new(full_path, nil, dir, exists, content)
19
+
20
+ return false unless candidate.exists?
21
+
22
+ @rule_groups.allowed_recursive?(candidate)
23
+ end
24
+
25
+ def each(parent_full_path, parent_relative_path, &block) # rubocop:disable Metrics/MethodLength
26
+ ::Dir.children(parent_full_path).each do |filename|
27
+ full_path = parent_full_path + filename
28
+ dir = directory?(full_path, nil)
29
+ candidate = ::FastIgnore::Candidate.new(full_path, filename, dir, true, nil)
30
+
31
+ next unless @rule_groups.allowed_unrecursive?(candidate)
32
+
33
+ relative_path = parent_relative_path + filename
34
+
35
+ if dir
36
+ each(full_path + '/', relative_path + '/', &block)
37
+ else
38
+ yield(relative_path)
39
+ end
40
+ rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
41
+ nil
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ # frozen-string-literal: true
2
+
3
+ class FastIgnore
4
+ module Walkers
5
+ class GitignoreCollectingFileSystem < Base
6
+ def allowed?(path, root: Dir.pwd, directory: nil, content: nil, exists: nil, include_directories: false) # rubocop:disable Metrics/ParameterLists
7
+ full_path = PathExpander.expand_path(path, root)
8
+ return false unless full_path.start_with?(root)
9
+
10
+ candidate = ::FastIgnore::Candidate.new(full_path, nil, directory, exists, content)
11
+
12
+ begin
13
+ return false if !include_directories && directory?(full_path, directory)
14
+ rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
15
+ # nil
16
+ end
17
+
18
+ return false unless candidate.exists?
19
+
20
+ @rule_groups.add_gitignore_to_root(full_path)
21
+ @rule_groups.allowed_recursive?(candidate)
22
+ end
23
+
24
+ def each(parent_full_path, parent_relative_path, &block) # rubocop:disable Metrics/MethodLength
25
+ children = ::Dir.children(parent_full_path)
26
+ @rule_groups.add_gitignore(parent_full_path) if children.include?('.gitignore')
27
+
28
+ children.each do |filename|
29
+ full_path = parent_full_path + filename
30
+ dir = directory?(full_path, nil)
31
+ candidate = ::FastIgnore::Candidate.new(full_path, filename, dir, true, nil)
32
+
33
+ next unless @rule_groups.allowed_unrecursive?(candidate)
34
+
35
+ relative_path = parent_relative_path + filename
36
+
37
+ if dir
38
+ each(full_path + '/', relative_path + '/', &block)
39
+ else
40
+ yield(relative_path)
41
+ end
42
+ rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
43
+ nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end