fast_ignore 0.15.2 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9992ae13ee95ee05f55cceda56e5bd1c4210d5817be17d9116193787ac658d38
4
- data.tar.gz: '093e221a737476e92c0c6cf1b6632abb4da103ea01ee3d0f66acf067011f092c'
3
+ metadata.gz: 78cb85cea0888d67c91de09bd54ad644510167c993dc355d187593003d341393
4
+ data.tar.gz: fb1fda9c1675ee28f4a6ff23191f95c2dd6e497ca2a70c4791dc5dfb85baecd1
5
5
  SHA512:
6
- metadata.gz: 78db62b2bfa57b7b509b6f326f3b4f3f7371336bf30caa46c2d1b3ccc4086e2bbdb7abf3cc843700d4a93ff82f368a829e0c05da0ceae52f0e8ab2af4eaa32bd
7
- data.tar.gz: c0df1d280a71bb7140076d39774a96728f2a1c8e29b756216211467a05eb93f7a3001c4472dc47155d0b57ec40d4d65f83c0f4f813014c01f55f7b9e03ffac67
6
+ metadata.gz: 10531a1d819b74fcc442ce7591be124430d3c55c55afbccb79ba295dd89c0e85e4a1a0a75b11996557057fa2f5dbad71e9284789143395d229e3abbbbd42ba3c
7
+ data.tar.gz: d0b07a2757c3b962a59973581234ec9e346717d24cdbd5443073c0fd14f840fb287419d63eeb31d4aa52d3fff2e1c0842cc119ed52e836cd55ed73bcd29a3fc7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ # v0.17.0
2
+ - allow overriding `exists:` in `allowed?`
3
+ - allow setting `include_directories: true` in `allowed?`
4
+ - subsequent calls to `allowed?` with the same path but different `directory:` or `content:` arguments won't potentially mess up the cache
5
+ - slight performance improvements of the shebang rule matcher loading the first line
6
+ - drop support for ruby 2.4
7
+ - add ruby 3.1 to the test matrix
8
+
9
+ # v0.16.1
10
+ - respect GIT_CONFIG_SYSTEM, GIT_CONFIG_NOSYSTEM and GIT_CONFIG_GLOBAL env vars the same way git does
11
+ - make the tests more resilient to whatever global config is going on.
12
+
13
+ # v0.16.0
14
+ - Entirely rewrite the way that git config files are read. previously it was just a regexp. now we actually parse git config files according to the same rules as git.
15
+ - Add ruby 3 to the test matrix
16
+
1
17
  # v0.15.2
2
18
  - Updated methods with multiple `_` arguments to have different names to make sorbet happy
3
19
 
data/README.md CHANGED
@@ -15,7 +15,7 @@ FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
15
15
  ## Features
16
16
 
17
17
  - Fast (faster than using `` `git ls-files`.split("\n") `` for small repos (because it avoids the overhead of ``` `` ```))
18
- - Supports ruby 2.4-3.0.0.preview1 & jruby
18
+ - Supports ruby 2.5-3.1.x & jruby
19
19
  - supports all [gitignore rule patterns](https://git-scm.com/docs/gitignore#_pattern_format)
20
20
  - doesn't require git to be installed
21
21
  - supports a gitignore-esque "include" patterns. ([`include_rules:`](#include_rules)/[`include_files:`](#include_files))
@@ -81,20 +81,79 @@ Relative paths will be considered relative to the [`root:`](#root) directory, no
81
81
 
82
82
  This is aliased as `===` so you can use a FastIgnore instance in case statements.
83
83
  ```ruby
84
+ @path_matcher ||= FastIgnore.new
85
+
84
86
  case my_path
85
- when FastIgnore.new
87
+ when @path_matcher
86
88
  puts(my_path)
87
89
  end
88
90
  ```
89
91
 
90
92
  It's recommended to save the FastIgnore instance to a variable to avoid having to read and parse the gitignore file and gitconfig files repeatedly.
91
93
 
92
- See [Optimising allowed](#optimising_allowed) for ways to make this even faster
94
+ #### directory: true/false/nil
95
+
96
+ If your code already knows the path to test is/not a directory or wants to lie about whether it is/is not a directory, you can pass `directory: true` or `directory: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `directory: nil` or nothing)
97
+
98
+ ```
99
+ FastIgnore.new.allowed?('relative/path', directory: false) # matches `path` as a file
100
+ FastIgnore.new.allowed?('relative/path', directory: true) # matches `path` as a directory
101
+ FastIgnore.new.allowed?('relative/path', directory: nil) # matches path as whatever it is on the filesystem
102
+ FastIgnore.new.allowed?('relative/path) # or as a file if it doesn't exist on the file system
103
+ ```
104
+
105
+ #### content: true/false/nil
106
+
107
+ default: `nil`
108
+
109
+ If your code already knows the path to test is has a particular text content or wants to lie about the content, you can pass `directory: true` or `directory: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `directory: nil` or nothing)
110
+
111
+ ```
112
+ FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env ruby\n\nputs 'hello'") # matches ruby shebang
113
+ FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env bash\n\necho 'hello'") # matches bash shebang
114
+ FastIgnore.new.allowed?('relative/path', content: nil) # matches path as whatever content is on the filesystem
115
+ FastIgnore.new.allowed?('relative/path) # or as an empty file if it doesn't actually exist
116
+ ```
117
+
118
+ #### content: true/false/nil
119
+
120
+ default: `nil`
121
+
122
+ If your code already knows the path to test is has a particular text content or wants to lie about the content, you can pass `directory: true` or `directory: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `directory: nil` or nothing)
123
+
124
+ ```
125
+ FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env ruby\n\nputs 'hello'") # matches ruby shebang
126
+ FastIgnore.new.allowed?('relative/path', content: "#!/usr/bin/env bash\n\necho 'hello'") # matches bash shebang
127
+ FastIgnore.new.allowed?('relative/path', content: nil) # matches path as whatever content is on the filesystem
128
+ FastIgnore.new.allowed?('relative/path) # or as an empty file if it doesn't actually exist
129
+ ```
93
130
 
94
- **Note: A file must exist at that path and not be a directory for it to be considered allowed.**
95
- Essentially it can be thought of as `` `git ls-files`.include?(path) `` but much faster.
96
- This excludes all directories and all possible path names that don't exist.
131
+ #### exist: true/false/nil
97
132
 
133
+ default: `nil`
134
+
135
+ If your code already knows the path to test exists or wants to lie about its existence, you can pass `exists: true` or `exists: false` as an argument to `allowed?` (to have FastIgnore ask the file system, you can pass `exists: nil` or nothing)
136
+
137
+ ```
138
+ FastIgnore.new.allowed?('relative/path', exists: true) # will check the path regardless of whether it actually truly exists
139
+ FastIgnore.new.allowed?('relative/path', exists: false) # will always return false
140
+ FastIgnore.new.allowed?('relative/path', exists: nil) # asks the filesystem
141
+ FastIgnore.new.allowed?('relative/path) # asks the filesystem
142
+ ```
143
+
144
+ #### include_directories: true/false
145
+
146
+ default: `false`
147
+
148
+ By default a file must not be a directory for it to be considered allowed. This is intended to match the behaviour of `git ls-files` which only lists files.
149
+
150
+ To match directories you can pass `include_directories: true` to `allowed?`
151
+
152
+ ```
153
+ FastIgnore.new.allowed?('relative/path', include_directories: true) # will test the path even if it's a directory
154
+ FastIgnore.new.allowed?('relative/path', include_directories: false) # will always return false if the path is a directory
155
+ FastIgnore.new.allowed?('relative/path) # will always return false if the path is a directory
156
+ ```
98
157
 
99
158
  ### `relative: true`
100
159
 
@@ -293,15 +352,6 @@ FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
293
352
  ```
294
353
 
295
354
  This does unfortunately lose the file path as the root `/` and there is no workaround except setting the [`root:`](#root) for the whole FastIgnore instance.
296
-
297
- ### optimising #allowed?
298
-
299
- 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
300
- ```ruby
301
- FastIgnore.new.allowed?('relative/path', directory: false, content: "#!/usr/bin/ruby\n\nputs 'ok'\n")
302
- ```
303
- 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.
304
-
305
355
  ## Limitations
306
356
  - Doesn't know what to do if you change the current working directory inside the [`FastIgnore#each`](#each_map_etc) block.
307
357
  So don't do that.
@@ -309,6 +359,7 @@ This is not required, and if FastIgnore does have to go to the filesystem for th
309
359
  (It does handle changing the current working directory between [`FastIgnore#allowed?`](#allowed) calls)
310
360
  - FastIgnore always matches patterns case-insensitively. (git varies by filesystem).
311
361
  - FastIgnore always outputs paths as literal UTF-8 characters. (git depends on your core.quotepath setting but by default outputs non ascii paths with octal escapes surrounded by quotes).
362
+ - git has a system-wide config file installed at `$(prefix)/etc/gitconfig`, where `prefix` is defined for git at install time. FastIgnore assumes that it will always be `/usr/local/etc/gitconfig`. if it's important your system config file is looked at, as that's where you have the core.excludesfile defined, use git's built-in way to override this by adding `export GIT_CONFIG_SYSTEM='/the/actual/location'` to your shell profile.
312
363
  - Because git looks at its own index objects and FastIgnore looks at the file system there may be some differences between FastIgnore and `git ls-files`. To avoid these differences you may want to use the [`git_ls`](https://github.com/robotdana/git_ls) gem instead
313
364
  - Tracked files that were committed before the matching ignore rule was committed will be returned by `git ls-files`, but not by FastIgnore.
314
365
  - Untracked files will be returned by FastIgnore, but not by `git ls-files`
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+ class FastIgnore
5
+ class GitconfigParseError < FastIgnore::Error; end
6
+
7
+ class GitconfigParser # rubocop:disable Metrics/ClassLength
8
+ def self.parse(file, root: Dir.pwd, nesting: 1)
9
+ new(file, root: root, nesting: nesting).parse
10
+ end
11
+
12
+ def initialize(path, root: Dir.pwd, nesting: 1)
13
+ @path = path
14
+ @root = root
15
+ @nesting = nesting
16
+ end
17
+
18
+ def parse
19
+ raise ::FastIgnore::GitconfigParseError if nesting >= 10
20
+
21
+ read_file(path)
22
+ return unless value
23
+
24
+ value
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :nesting
30
+ attr_reader :path
31
+ attr_reader :root
32
+ attr_accessor :value
33
+ attr_accessor :within_quotes
34
+ attr_accessor :section
35
+
36
+ def read_file(path) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
37
+ return unless ::File.readable?(path)
38
+
39
+ file = StringScanner.new(::File.read(path))
40
+
41
+ until file.eos?
42
+ if file.skip(/(\s+|[#;].*\n)/)
43
+ # skip
44
+ elsif file.skip(/\[core\]/i)
45
+ self.section = :core
46
+ elsif file.skip(/\[include\]/i)
47
+ self.section = :include
48
+ elsif file.skip(/\[(?i:includeif) +"/)
49
+ self.section = include_if(file) ? :include : :not_include
50
+ elsif file.skip(/\[[\w.]+( "([^\0\\"]|\\(\\{2})*"|\\{2}*)+")?\]/)
51
+ self.section = :other
52
+ elsif section == :core && file.skip(/excludesfile\s*=(\s|\\\n)*/i)
53
+ self.value = scan_value(file)
54
+ elsif section == :include && file.skip(/path\s*=(\s|\\\n)*/)
55
+ include_path = scan_value(file)
56
+
57
+ value = ::FastIgnore::GitconfigParser.parse(
58
+ ::File.expand_path(include_path, ::File.dirname(path)),
59
+ root: root,
60
+ nesting: nesting + 1
61
+ )
62
+ self.value = value if value
63
+ self.section = :include
64
+ elsif file.skip(/[a-zA-Z0-9]\w*\s*([#;].*)?\n/)
65
+ nil
66
+ elsif file.skip(/[a-zA-Z0-9]\w*\s*=(\s|\\\n)*/)
67
+ skip_value(file)
68
+ else
69
+ raise ::FastIgnore::GitconfigParseError
70
+ end
71
+ end
72
+ end
73
+
74
+ def scan_condition_value(file)
75
+ if file.scan(/([^\0\\\n"]|\\(\\{2})*"|\\{2}*)+(?="\])/)
76
+ value = file.matched
77
+ file.skip(/"\]/)
78
+ value
79
+ else
80
+ raise ::FastIgnore::GitconfigParseError
81
+ end
82
+ end
83
+
84
+ def skip_condition_value(file)
85
+ raise ::FastIgnore::GitconfigParseError unless file.skip(/([^\0\\\n"]|\\(\\{2})*"|\\{2}*)+"\]/)
86
+ end
87
+
88
+ def include_if(file)
89
+ if file.skip(/onbranch:/)
90
+ on_branch?(scan_condition_value(file))
91
+ elsif file.skip(/gitdir:/)
92
+ gitdir?(scan_condition_value(file), path: path)
93
+ elsif file.skip(%r{gitdir/i:})
94
+ gitdir?(scan_condition_value(file), case_insensitive: true, path: path)
95
+ else
96
+ skip_condition_value(file)
97
+ false
98
+ end
99
+ end
100
+
101
+ def on_branch?(branch_pattern)
102
+ branch_pattern += '**' if branch_pattern.end_with?('/')
103
+ current_branch = ::File.readable?("#{root}/.git/HEAD") && ::File.read("#{root}/.git/HEAD").sub!(
104
+ %r{\Aref: refs/heads/}, ''
105
+ )
106
+ return unless current_branch
107
+
108
+ # goddamit git what does 'a pattern with standard globbing wildcards' mean
109
+ ::File.fnmatch(branch_pattern, current_branch, ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH)
110
+ end
111
+
112
+ def gitdir?(gitdir, path:, case_insensitive: false)
113
+ gitdir += '**' if gitdir.end_with?('/')
114
+ gitdir.sub!(%r{\A~/}, ENV['HOME'] + '/')
115
+ gitdir.sub!(/\A\./, path + '/')
116
+ gitdir = "**/#{gitdir}" unless gitdir.start_with?('/')
117
+ options = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH
118
+ options |= ::File::FNM_CASEFOLD if case_insensitive
119
+ ::File.fnmatch(gitdir, ::File.join(root, '.git'), options)
120
+ end
121
+
122
+ def scan_value(file) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
123
+ value = +''
124
+ until file.eos?
125
+ if file.skip(/\\\n/)
126
+ # continue
127
+ elsif file.skip(/\\\\/)
128
+ value << '\\'
129
+ elsif file.skip(/\\n/)
130
+ value << "\n"
131
+ elsif file.skip(/\\t/)
132
+ value << "\t"
133
+ elsif file.skip(/\\b/)
134
+ value.chop!
135
+ elsif file.skip(/\\"/)
136
+ value << '"'
137
+ elsif file.skip(/\\/)
138
+ raise ::FastIgnore::GitconfigParseError
139
+ elsif within_quotes
140
+ if file.skip(/"/)
141
+ self.within_quotes = false
142
+ elsif file.scan(/[^"\\\n]+/)
143
+ value << file.matched
144
+ elsif file.skip(/\n/)
145
+ raise ::FastIgnore::GitconfigParseError
146
+ # :nocov:
147
+ else
148
+ raise "Unmatched #{file.rest}"
149
+ # :nocov:
150
+ end
151
+ elsif file.skip(/"/)
152
+ self.within_quotes = true
153
+ elsif file.scan(/[^;#"\s\\]+/)
154
+ value << file.matched
155
+ elsif file.skip(/\s*[;#\n]/)
156
+ break
157
+ elsif file.scan(/\s+/) # rubocop:disable Lint/DuplicateBranch
158
+ value << file.matched
159
+ # :nocov:
160
+ else
161
+ raise "Unmatched #{file.rest}"
162
+ # :nocov:
163
+ end
164
+ end
165
+
166
+ raise ::FastIgnore::GitconfigParseError if within_quotes
167
+
168
+ value
169
+ end
170
+
171
+ def skip_value(file) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
172
+ until file.eos?
173
+ if file.skip(/\\(?:\n|\\|n|t|b|")/)
174
+ nil
175
+ elsif file.skip(/\\/)
176
+ raise ::FastIgnore::GitconfigParseError
177
+ elsif within_quotes
178
+ if file.skip(/"/)
179
+ self.within_quotes = false
180
+ elsif file.skip(/[^"\\\n]+/)
181
+ nil
182
+ elsif file.scan(/\n/)
183
+ raise ::FastIgnore::GitconfigParseError
184
+ # :nocov:
185
+ else
186
+ raise "Unmatched #{file.rest}"
187
+ # :nocov:
188
+ end
189
+ elsif file.skip(/"/)
190
+ self.within_quotes = true
191
+ elsif file.skip(/[^;#"\s\\]+/) # rubocop:disable Lint/DuplicateBranch
192
+ nil
193
+ elsif file.skip(/\s*[;#\n]/)
194
+ break
195
+ elsif file.skip(/\s+/) # rubocop:disable Lint/DuplicateBranch
196
+ nil
197
+ # :nocov:
198
+ else
199
+ raise "Unmatched #{file.rest}"
200
+ # :nocov:
201
+ end
202
+ end
203
+
204
+ raise ::FastIgnore::GitconfigParseError if within_quotes
205
+ end
206
+ end
207
+ end
@@ -4,46 +4,84 @@ class FastIgnore
4
4
  module GlobalGitignore
5
5
  class << self
6
6
  def path(root:)
7
- gitconfig_gitignore_path(::File.expand_path('.git/config', root)) ||
8
- gitconfig_gitignore_path(::File.expand_path('~/.gitconfig')) ||
9
- gitconfig_gitignore_path(xdg_config_path) ||
10
- gitconfig_gitignore_path('/etc/gitconfig') ||
7
+ ignore_path = gitconfigs_gitignore_path(root) ||
11
8
  default_global_gitignore_path
9
+
10
+ ignore_path unless ignore_path.empty?
12
11
  end
13
12
 
14
13
  private
15
14
 
15
+ def gitconfigs_gitignore_path(root)
16
+ gitconfig_gitignore_path(repo_config_path(root)) ||
17
+ gitconfig_gitignore_path(global_config_path) ||
18
+ gitconfig_gitignore_path(default_user_config_path) ||
19
+ gitconfig_gitignore_path(system_config_path)
20
+ rescue ::FastIgnore::GitconfigParseError
21
+ ''
22
+ end
23
+
16
24
  def gitconfig_gitignore_path(config_path)
17
25
  return unless config_path
18
- return unless ::File.exist?(config_path)
26
+ return unless ::File.readable?(config_path)
19
27
 
20
- ignore_path = ::File.readlines(config_path).find { |l| l.sub!(/\A\s*excludesfile\s*=/, '') }
28
+ ignore_path = ::FastIgnore::GitconfigParser.parse(config_path)
21
29
  return unless ignore_path
22
30
 
23
31
  ignore_path.strip!
24
- return ignore_path if ignore_path.empty? # don't expand path in this case
32
+ return '' if ignore_path.empty? # don't expand path in this case
25
33
 
26
34
  ::File.expand_path(ignore_path)
27
35
  end
28
36
 
29
- def xdg_config_path
30
- xdg_config_home? && ::File.expand_path('git/config', xdg_config_home)
37
+ def default_user_config_path
38
+ return if env('GIT_CONFIG_GLOBAL')
39
+
40
+ ::File.expand_path('git/config', default_config_home)
31
41
  end
32
42
 
33
43
  def default_global_gitignore_path
34
- if xdg_config_home?
35
- ::File.expand_path('git/ignore', xdg_config_home)
44
+ ::File.expand_path('git/ignore', default_config_home)
45
+ end
46
+
47
+ def repo_config_path(root)
48
+ ::File.expand_path('.git/config', root)
49
+ end
50
+
51
+ def global_config_path
52
+ ::File.expand_path(env('GIT_CONFIG_GLOBAL', '~/.gitconfig'))
53
+ end
54
+
55
+ def system_config_path
56
+ return if env?('GIT_CONFIG_NOSYSTEM')
57
+
58
+ ::File.expand_path(env('GIT_CONFIG_SYSTEM', '/usr/local/etc/gitconfig'))
59
+ end
60
+
61
+ def default_config_home
62
+ env('XDG_CONFIG_HOME', '~/.config')
63
+ end
64
+
65
+ def env(env_var, default = nil)
66
+ value = ::ENV[env_var]
67
+
68
+ if value && (not value.empty?)
69
+ value
36
70
  else
37
- ::File.expand_path('~/.config/git/ignore')
71
+ default
38
72
  end
39
73
  end
40
74
 
41
- def xdg_config_home
42
- ::ENV['XDG_CONFIG_HOME']
43
- end
75
+ def env?(env_var)
76
+ value = ::ENV[env_var]
44
77
 
45
- def xdg_config_home?
46
- xdg_config_home && (not xdg_config_home.empty?)
78
+ if value&.match?(/\A(yes|on|true|1)\z/i)
79
+ true
80
+ elsif !value || value.match?(/\A(no|off|false|0|)\z/i)
81
+ false
82
+ else
83
+ raise ::FastIgnore::GitconfigParseError
84
+ end
47
85
  end
48
86
  end
49
87
  end
@@ -11,7 +11,7 @@ class FastIgnore
11
11
  @file_rules = (squash ? squash_rules(rules.reject(&:dir_only?)) : rules.reject(&:dir_only?)).freeze
12
12
  @has_shebang_rules = rules.any?(&:shebang?)
13
13
 
14
- @allowed_recursive = { '.' => true }
14
+ @allowed_recursive = { ['.', true, nil] => true }
15
15
  @allow = allow
16
16
  @gitignore = gitignore
17
17
 
@@ -27,8 +27,8 @@ class FastIgnore
27
27
  end
28
28
 
29
29
  def allowed_recursive?(relative_path, dir, full_path, filename, content = nil)
30
- @allowed_recursive.fetch(relative_path) do
31
- @allowed_recursive[relative_path] =
30
+ @allowed_recursive.fetch([relative_path, dir, content]) do |key|
31
+ @allowed_recursive[key] =
32
32
  allowed_recursive?(::File.dirname(relative_path), true, nil, nil, nil) &&
33
33
  allowed_unrecursive?(relative_path, dir, full_path, filename, content)
34
34
  end
@@ -27,8 +27,8 @@ class FastIgnore
27
27
  @array.freeze if @gitignore_rule_set
28
28
  end
29
29
 
30
- def allowed_recursive?(relative_path, full_path, filename, content)
31
- @array.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename, content) }
30
+ def allowed_recursive?(relative_path, dir, full_path, filename, content)
31
+ @array.all? { |r| r.allowed_recursive?(relative_path, dir, full_path, filename, content) }
32
32
  end
33
33
 
34
34
  def allowed_unrecursive?(relative_path, dir, full_path, filename)
@@ -73,7 +73,7 @@ class FastIgnore
73
73
  end
74
74
 
75
75
  def build_from_root_gitignore_file(path)
76
- return unless ::File.exist?(path)
76
+ return unless path && ::File.exist?(path)
77
77
 
78
78
  build_rule_set(::File.readlines(path), false, gitignore: true)
79
79
  end
@@ -61,12 +61,9 @@ class FastIgnore
61
61
 
62
62
  def first_line(path) # rubocop:disable Metrics/MethodLength
63
63
  file = ::File.new(path)
64
- first_line = new_fragment = file.sysread(64)
64
+ first_line = file.sysread(64)
65
65
  if first_line.start_with?('#!')
66
- until new_fragment.include?("\n")
67
- new_fragment = file.sysread(64)
68
- first_line += new_fragment
69
- end
66
+ first_line += file.readline unless first_line.match?(/\n/)
70
67
  else
71
68
  file.close
72
69
  return
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.15.2'
4
+ VERSION = '0.17.0'
5
5
  end
data/lib/fast_ignore.rb CHANGED
@@ -4,22 +4,24 @@ require_relative './fast_ignore/backports'
4
4
 
5
5
  require 'set'
6
6
  require 'strscan'
7
- require_relative './fast_ignore/rule_sets'
8
- require_relative './fast_ignore/rule_set'
9
- require_relative './fast_ignore/global_gitignore'
10
- require_relative './fast_ignore/rule_builder'
11
- require_relative './fast_ignore/gitignore_rule_builder'
12
- require_relative './fast_ignore/gitignore_include_rule_builder'
13
- require_relative './fast_ignore/gitignore_rule_regexp_builder'
14
- require_relative './fast_ignore/gitignore_rule_scanner'
15
- require_relative './fast_ignore/file_root'
16
- require_relative './fast_ignore/rule'
17
- require_relative './fast_ignore/unmatchable_rule'
18
- require_relative './fast_ignore/shebang_rule'
19
7
 
20
8
  class FastIgnore
21
9
  class Error < StandardError; end
22
10
 
11
+ require_relative './fast_ignore/rule_sets'
12
+ require_relative './fast_ignore/rule_set'
13
+ require_relative './fast_ignore/global_gitignore'
14
+ require_relative './fast_ignore/rule_builder'
15
+ require_relative './fast_ignore/gitignore_rule_builder'
16
+ require_relative './fast_ignore/gitignore_include_rule_builder'
17
+ require_relative './fast_ignore/gitignore_rule_regexp_builder'
18
+ require_relative './fast_ignore/gitignore_rule_scanner'
19
+ require_relative './fast_ignore/file_root'
20
+ require_relative './fast_ignore/rule'
21
+ require_relative './fast_ignore/unmatchable_rule'
22
+ require_relative './fast_ignore/shebang_rule'
23
+ require_relative './fast_ignore/gitconfig_parser'
24
+
23
25
  include ::Enumerable
24
26
 
25
27
  # :nocov:
@@ -47,19 +49,30 @@ class FastIgnore
47
49
  each_recursive(root_from_pwd, '', &block)
48
50
  end
49
51
 
50
- def allowed?(path, directory: nil, content: nil)
52
+ def allowed?(path, directory: nil, content: nil, exists: nil, include_directories: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
51
53
  full_path = ::File.expand_path(path, @root)
52
54
  return false unless full_path.start_with?(@root)
53
- return false if directory.nil? ? @follow_symlinks_method.call(full_path).directory? : directory
55
+
56
+ begin
57
+ directory = directory.nil? ? @follow_symlinks_method.call(full_path).directory? : directory
58
+ rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
59
+ exists = false if exists.nil?
60
+ directory = false
61
+ end
62
+
63
+ return false if !include_directories && directory
64
+
65
+ exists = exists.nil? ? ::File.exist?(full_path) : exists
66
+
67
+ return false unless exists
54
68
 
55
69
  relative_path = full_path.delete_prefix(@root)
56
70
  load_gitignore_recursive(relative_path) if @gitignore_enabled
57
71
 
58
72
  filename = ::File.basename(relative_path)
73
+ content = content.slice(/.*/) if content # we only care about the first line
59
74
 
60
- @rule_sets.allowed_recursive?(relative_path, full_path, filename, content)
61
- rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
62
- false
75
+ @rule_sets.allowed_recursive?(relative_path, directory, full_path, filename, content)
63
76
  end
64
77
  alias_method :===, :allowed?
65
78
 
@@ -75,7 +88,7 @@ class FastIgnore
75
88
  paths << path
76
89
  end
77
90
 
78
- paths.reverse_each(&method(:load_gitignore))
91
+ paths.reverse_each { |p| load_gitignore(p) }
79
92
  end
80
93
 
81
94
  def load_gitignore(parent_path, check_exists: true)
@@ -91,21 +104,19 @@ class FastIgnore
91
104
  load_gitignore(parent_relative_path, check_exists: false) if @gitignore_enabled && children.include?('.gitignore')
92
105
 
93
106
  children.each do |filename|
94
- begin
95
- full_path = parent_full_path + filename
96
- relative_path = parent_relative_path + filename
97
- dir = @follow_symlinks_method.call(full_path).directory?
98
-
99
- next unless @rule_sets.allowed_unrecursive?(relative_path, dir, full_path, filename)
100
-
101
- if dir
102
- each_recursive(full_path + '/', relative_path + '/', &block)
103
- else
104
- yield(@relative ? relative_path : @root + relative_path)
105
- end
106
- rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
107
- nil
107
+ full_path = parent_full_path + filename
108
+ relative_path = parent_relative_path + filename
109
+ dir = @follow_symlinks_method.call(full_path).directory?
110
+
111
+ next unless @rule_sets.allowed_unrecursive?(relative_path, dir, full_path, filename)
112
+
113
+ if dir
114
+ each_recursive(full_path + '/', relative_path + '/', &block)
115
+ else
116
+ yield(@relative ? relative_path : @root + relative_path)
108
117
  end
118
+ rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
119
+ nil
109
120
  end
110
121
  end
111
122
  end
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.15.2
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dana Sherson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-18 00:00:00.000000000 Z
11
+ date: 2022-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -87,6 +87,9 @@ dependencies:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: 0.93.1
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '1.12'
90
93
  type: :development
91
94
  prerelease: false
92
95
  version_requirements: !ruby/object:Gem::Requirement
@@ -94,6 +97,23 @@ dependencies:
94
97
  - - ">="
95
98
  - !ruby/object:Gem::Version
96
99
  version: 0.93.1
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.12'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop-rake
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
97
117
  - !ruby/object:Gem::Dependency
98
118
  name: rubocop-rspec
99
119
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +142,20 @@ dependencies:
122
142
  - - "~>"
123
143
  - !ruby/object:Gem::Version
124
144
  version: 0.18.5
145
+ - !ruby/object:Gem::Dependency
146
+ name: simplecov-console
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
125
159
  - !ruby/object:Gem::Dependency
126
160
  name: spellr
127
161
  requirement: !ruby/object:Gem::Requirement
@@ -136,7 +170,7 @@ dependencies:
136
170
  - - ">="
137
171
  - !ruby/object:Gem::Version
138
172
  version: 0.8.3
139
- description:
173
+ description:
140
174
  email:
141
175
  - robot@dana.sh
142
176
  executables: []
@@ -149,6 +183,7 @@ files:
149
183
  - lib/fast_ignore.rb
150
184
  - lib/fast_ignore/backports.rb
151
185
  - lib/fast_ignore/file_root.rb
186
+ - lib/fast_ignore/gitconfig_parser.rb
152
187
  - lib/fast_ignore/gitignore_include_rule_builder.rb
153
188
  - lib/fast_ignore/gitignore_rule_builder.rb
154
189
  - lib/fast_ignore/gitignore_rule_regexp_builder.rb
@@ -168,7 +203,7 @@ metadata:
168
203
  homepage_uri: https://github.com/robotdana/fast_ignore
169
204
  source_code_uri: https://github.com/robotdana/fast_ignore
170
205
  changelog_uri: https://github.com/robotdana/fast_ignore/blob/main/CHANGELOG.md
171
- post_install_message:
206
+ post_install_message:
172
207
  rdoc_options: []
173
208
  require_paths:
174
209
  - lib
@@ -176,15 +211,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
176
211
  requirements:
177
212
  - - ">="
178
213
  - !ruby/object:Gem::Version
179
- version: 2.4.0
214
+ version: 2.5.0
180
215
  required_rubygems_version: !ruby/object:Gem::Requirement
181
216
  requirements:
182
217
  - - ">="
183
218
  - !ruby/object:Gem::Version
184
219
  version: '0'
185
220
  requirements: []
186
- rubygems_version: 3.1.2
187
- signing_key:
221
+ rubygems_version: 3.2.15
222
+ signing_key:
188
223
  specification_version: 4
189
224
  summary: Parse gitignore files, quickly
190
225
  test_files: []