fast_ignore 0.12.1 → 0.13.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: e68a04cc6c34d7f72a6049beb86c9808c43bae4e712a1e9e8462e8e18f331bf0
4
- data.tar.gz: 3c015e68f03e9d387064a68d42fe6f367e60894e1a20f02945af2c6669ddcebb
3
+ metadata.gz: ee7e9eb231ff6441ca703c935e9c270e4cb6c2f8f3216d993d4fc891807e77f6
4
+ data.tar.gz: 68f1ace7ab6420ce140a43a747a969f29401eba718dd9d1e94b9538774b0ac6d
5
5
  SHA512:
6
- metadata.gz: 047ec0c2f7fe90a3bf9a23b01ec3aa054bd7770e2f3b9d38dae8e77704ef8dd7d8d1449633faa3c7aae9ecc7b4d236f8b3442e5b3dcf489cf825da85efce19e5
7
- data.tar.gz: d9fa32ba54a4268ce530556767972fd60dbf04a24ec8a890110fd084675611721244cbdf1f1722bfc70fe41794458a7fe6a0ecbde388149308ff710e76610a43
6
+ metadata.gz: 83609a93cf79d3d4063e69d213450d85f81c4b9bb7c1200468e46fa7fce01602c751c4afd6dbaef652d1761c0f180fa5806ff29d0913ca184a5e41850875a8d0
7
+ data.tar.gz: dbf3e341e671fa1a51dce4c885c04e1eadf0717d78ffd94f1746c63b1024f78bd9d4b6915e7b892ca3af21d27ef2566df980be41101573c3dd586384b6f6d7c6
@@ -1,3 +1,8 @@
1
+ # v0.13.0
2
+ - Attempt to improve documentation structure
3
+ - Remove `gitignore: true` raising `Errno::ENOENT` if root:/.gitignore didn't exist. I can't think of a use. Now `gitignore: true` is just the default behaviour.
4
+ - Don't ignore `.git` if `gitignore: false`.
5
+
1
6
  # v0.12.1
2
7
  - Reads all relevant git config files when finding a global .gitignore
3
8
 
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # FastIgnore
2
2
 
3
- [![travis](https://travis-ci.org/robotdana/fast_ignore.svg?branch=master)](https://travis-ci.org/robotdana/fast_ignore)
3
+ [![travis](https://travis-ci.com/robotdana/fast_ignore.svg?branch=master)](https://travis-ci.com/robotdana/fast_ignore)
4
+ [![Gem Version](https://badge.fury.io/rb/fast_ignore.svg)](https://rubygems.org/gems/fast_ignore)
4
5
 
5
6
  This started as a way to quickly and natively ruby-ly parse gitignore files and find matching files.
6
7
  It's now gained an equivalent includes file functionality, ARGV awareness, and some shebang matching, while still being extremely fast, to be a one-stop file-list for your linter.
@@ -14,13 +15,15 @@ FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
14
15
  ## Features
15
16
 
16
17
  - 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 & jruby
18
- - supports all gitignore rule patterns
18
+ - Supports ruby 2.4-2.7 & jruby
19
+ - supports all [gitignore rule patterns](https://git-scm.com/docs/gitignore#_pattern_format)
19
20
  - 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
- - in addition to .gitignore, reads .git/info/excludes and the ignore file mentioned in your ~/.gitconfig
21
+ - supports a gitignore-esque "include" patterns. ([`include_rules:`](#include_rules)/[`include_files:`](#include_files))
22
+ - supports an expansion of include patterns, expanding and anchoring paths ([`argv_rules:`](#argv_rules))
23
+ - supports [matching by shebang](#shebang_rules) rather than filename for extensionless files: `#!:`
24
+ - reads .gitignore in all subdirectories
25
+ - reads .git/info/excludes
26
+ - reads the ignore file mentioned in your git config
24
27
 
25
28
  ## Installation
26
29
 
@@ -47,22 +50,26 @@ FastIgnore.new.each { |file| puts "#{file} is not ignored by the .gitignore file
47
50
 
48
51
  ### `#each`, `#map` etc
49
52
 
50
- The FastIgnore object is an enumerable and responds to all Enumerable methods
53
+ This yields paths that are _not_ ignored by the gitignore, i.e. the paths that would be returned by `git ls-files`.
54
+
55
+ A FastIgnore instance is an Enumerable and responds to all Enumerable methods:
51
56
 
52
57
  ```ruby
53
58
  FastIgnore.new.to_a
54
59
  FastIgnore.new.map { |file| file.upcase }
55
60
  ```
56
61
 
57
- Like other enumerables, `FastIgnore#each` can return an enumerator
62
+ Like other enumerables, `FastIgnore#each` can return an enumerator:
58
63
 
59
64
  ```ruby
60
65
  FastIgnore.new.each.with_index { |file, index| puts "#{file}#{index}" }
61
66
  ```
62
67
 
68
+ **Warning: Do not change directory (e.g. `Dir.chdir`) in the block.**
69
+
63
70
  ### `#allowed?`
64
71
 
65
- To check if a single file is allowed, use
72
+ To check if a single path is allowed, use
66
73
  ```ruby
67
74
  FastIgnore.new.allowed?('relative/path')
68
75
  FastIgnore.new.allowed?('./relative/path')
@@ -70,7 +77,9 @@ FastIgnore.new.allowed?('/absolute/path')
70
77
  FastIgnore.new.allowed?('~/home/path')
71
78
  ```
72
79
 
73
- This is aliased as `===` so you can use the FastIgnore object in case statements.
80
+ Relative paths will be considered relative to the [`root:`](#root) directory, not the current directory.
81
+
82
+ This is aliased as `===` so you can use a FastIgnore instance in case statements.
74
83
  ```ruby
75
84
  case my_path
76
85
  when FastIgnore.new
@@ -78,18 +87,34 @@ when FastIgnore.new
78
87
  end
79
88
  ```
80
89
 
81
- It's recommended to memoize the FastIgnore.new object somehow to avoid having to parse the gitignore file repeatedly.
90
+ 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
+
92
+ See [Optimising allowed](#optimising_allowed) for ways to make this even faster
93
+
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.
97
+
82
98
 
83
99
  ### `relative: true`
84
- By default, FastIgnore.each will yield full paths. To yield paths relative to the current working directory, or if supplied, [`root:`](#root), use:
100
+
101
+ **Default: false**
102
+
103
+ When `relative: false`: FastIgnore#each will yield full paths.
104
+ When `relative: true`: FastIgnore#each will yield paths relative to the [`root:`](#root) directory
85
105
 
86
106
  ```ruby
87
107
  FastIgnore.new(relative: true).to_a
88
108
  ```
89
109
 
90
110
  ### `follow_symlinks: true`
91
- By default, FastIgnore will match git's behaviour and not follow symbolic links.
92
- To make it follow symlinks, use:
111
+
112
+ **Default: false**
113
+
114
+ When `follow_symlinks: false`: FastIgnore#each will match git's behaviour and not follow symbolic links.
115
+ When `follow_symlinks: true`: FastIgnore#each will check if a symlink points to a directory, and files in linked directories must also match rules using the symlink path as the directory location, not the real directory location.
116
+
117
+ **This doesn't use the real path for matching or yield or return it.**
93
118
 
94
119
  ```ruby
95
120
  FastIgnore.new(follow_symlinks: true).to_a
@@ -97,14 +122,16 @@ FastIgnore.new(follow_symlinks: true).to_a
97
122
 
98
123
  ### `root:`
99
124
 
100
- By default, root is PWD (the current working directory)
125
+ **Default: Dir.pwd ($PWD, the current working directory)**
126
+
101
127
  This directory is used for:
102
- - the location of .git/core/exclude
103
- - the outermost directory that project .gitignore files are looked for
104
- - the root directory for array rules starting with `/` or ending with `/**`
105
- - and the path that relative is relative to
106
- - the ancestor of which files get checked
107
- - the ancestor of all include_files and ignore_files
128
+ - the location of `.git/core/exclude`
129
+ - the ancestor of all non-global [automatically loaded `.gitignore` files](#gitignore_false)
130
+ - the root directory for array rules ([`ignore_rules:`](#ignore_rules), [`include_rules:`](#include_rules), [`argv_rules:`](#argv_rules)) containing `/`
131
+ - the path that [`relative:`](#relative_true) is relative to
132
+ - the ancestor of all paths yielded by [`#each`](#each_map_etc)
133
+ - the path that [`#allowed?`](#allowed) considers relative paths relative to
134
+ - the ancestor of all [`include_files:`](#include_files) and [`ignore_files:`](#ignore_files)
108
135
 
109
136
  To use a different directory:
110
137
  ```ruby
@@ -112,37 +139,49 @@ FastIgnore.new(root: '/absolute/path/to/root').to_a
112
139
  FastIgnore.new(root: '../relative/path/to/root').to_a
113
140
  ```
114
141
 
142
+ A relative root will be found relative to the current working directory when the FastIgnore instance is initialized, and that will be the last time the current working directory is relevant.
143
+
144
+ **Note: Changes to the current working directory (e.g. with `Dir.chdir`), after initialising a FastIgnore instance, will _not_ affect the FastIgnore instance. `root:` will always be what it was when the instance was initialized.**
145
+
115
146
  ### `gitignore:`
116
147
 
117
- By default, the .gitignore file in the root directory is loaded, plus any .gitignore files in subdirectories, the global ignore file, and .git/info/exclude.
148
+ **Default: true**
149
+
150
+ When `gitignore: true`: the .gitignore file in the [`root:`](#root) directory is loaded, plus any .gitignore files in its subdirectories, the global git ignore file as described in git config, and .git/info/exclude. `.git` directories are also excluded to match the behaviour of `git ls-files`.
151
+ When `gitignore: false`: no ignore files or git config files are automatically read, and `.git` will not be automatically excluded.
118
152
 
119
- To not do this use
120
153
  ```ruby
121
154
  FastIgnore.new(gitignore: false).to_a
122
155
  ```
123
156
 
124
- To raise an `Errno::ENOENT` error if the .gitignore file is not found use:
125
- ```ruby
126
- FastIgnore.new(gitignore: true).to_a
127
- ```
157
+ ### `ignore_files:`
128
158
 
129
- If the gitignore file is somewhere else
130
- ```ruby
131
- FastIgnore.new(ignore_file: '/absolute/path/to/.gitignore').to_a
132
- ```
133
- Note that the location of the .gitignore file will affect rules beginning with `/` or ending in `/**`
159
+ **This is a list of files in the gitignore format to parse and match paths against, not a list of files to ignore** If you want an array of files use [`ignore_rules:`](#ignore_rules)
160
+
161
+ Additional gitignore-style files, either as a path or an array of paths.
134
162
 
135
- ### `ignore_files:`
136
163
  You can specify other gitignore-style files to ignore as well.
137
164
  Missing files will raise an `Errno::ENOENT` error.
138
165
 
166
+ Relative paths are relative to the [`root:`](#root) directory.
167
+ Absolute paths also need to be within the [`root:`](#root) directory.
168
+
169
+
139
170
  ```ruby
140
171
  FastIgnore.new(ignore_files: 'relative/path/to/my/ignore/file').to_a
141
172
  FastIgnore.new(ignore_files: ['/absolute/path/to/my/ignore/file', '/and/another']).to_a
142
173
  ```
143
174
 
175
+ Note: the location of the files will affect rules beginning with or containing `/`.
176
+
177
+ To avoid raising `Errno::ENOENT` when the file doesn't exist:
178
+ ```ruby
179
+ FastIgnore.new(ignore_files: ['/ignore/file'].select { |f| File.exist?(f) }).to_a
180
+ ```
181
+
144
182
  ### `ignore_rules:`
145
- You can also supply an array of rule strings.
183
+
184
+ This can be a string, or an array of strings, and multiline strings can be used with one rule per line.
146
185
 
147
186
  ```ruby
148
187
  FastIgnore.new(ignore_rules: '.DS_Store').to_a
@@ -150,84 +189,110 @@ FastIgnore.new(ignore_rules: ['.git', '.gitkeep']).to_a
150
189
  FastIgnore.new(ignore_rules: ".git\n.gitkeep").to_a
151
190
  ```
152
191
 
153
- ### `include_files:` and `include_rules:`
192
+ These rules use the [`root:`](#root) argument to resolve rules containing `/`.
154
193
 
155
- Building on the gitignore format, FastIgnore also accepts a list of allowed or included files.
194
+ ### `include_files:`
156
195
 
157
- ```gitignore
158
- # a line like this means any files named foo will be included
159
- # as well as any files within directories named foo
160
- foo
161
- # a line beginning with a slash will be anything in a directory that is a child of the $PWD
162
- /foo
163
- # a line ending in a slash will will include any files in any directories named foo
164
- # but not any files named foo
165
- foo/
166
- fo*
167
- !foe
168
- # otherwise this format deals with !'s, *'s and ?'s and etc as you'd expect from gitignore.
196
+ **This is an array of files in the gitignore format to parse and match paths against, not a list of files to include.** If you want an array of files use [`include_rules:`](#include_rules).
197
+
198
+ Building on the gitignore format, FastIgnore also accepts rules to include matching paths (rather than ignoring them).
199
+ A rule matching a directory will include all descendants of that directory.
200
+
201
+ These rules can be provided in files either as absolute or relative paths, or an array of paths.
202
+ Relative paths are relative to the [`root:`](#root) directory.
203
+ Absolute paths also need to be within the [`root:`](#root) directory.
204
+
205
+ ```ruby
206
+ FastIgnore.new(include_files: 'my_include_file').to_a
207
+ FastIgnore.new(include_files: ['/absolute/include/file', './relative/include/file']).to_a
208
+ ```
209
+
210
+ Missing files will raise an `Errno::ENOENT` error.
211
+
212
+ To avoid raising `Errno::ENOENT` when the file doesn't exist:
213
+ ```ruby
214
+ FastIgnore.new(include_files: ['include/file'].select { |f| File.exist?(f) }).to_a
169
215
  ```
170
216
 
171
- These can be passed either as files or as an array or string rules
217
+ **Note: All paths checked must not be excluded by any ignore files AND each included by include file separately AND the [`include_rules:`](#include_rules) AND the [`argv_rules:`](#argv_rules). see [Combinations](#combinations) for solutions to using OR.**
218
+
219
+ ### `include_rules:`
220
+
221
+ Building on the gitignore format, FastIgnore also accepts rules to include matching paths (rather than ignoring them).
222
+ A rule matching a directory will include all descendants of that directory.
223
+
224
+ This can be a string, or an array of strings, and multiline strings can be used with one rule per line.
172
225
  ```ruby
173
- FastIgnore.new(include_files: '/absolute/path/to/my/include/file', gitignore: false).to_a
174
226
  FastIgnore.new(include_rules: %w{my*rule /and/another !rule}, gitignore: false).to_a
175
227
  ```
176
228
 
177
- There is an additional argument meant for dealing with humans and `ARGV` values.
229
+ Rules use the [`root:`](#root) argument to resolve rules containing `/`.
230
+
231
+ **Note: All paths checked must not be excluded by any ignore files AND each included by [include file](#include_files) separately AND the `include_rules:` AND the [`argv_rules:`](#argv_rules). see [Combinations](#combinations) for solutions to using OR.**
232
+
233
+ ### `argv_rules:`
234
+ This is like [`include_rules:`](#include_rules) with additional features meant for dealing with humans and `ARGV` values.
235
+
236
+ It expands rules that are absolute paths, and paths beginning with `~`, `../` and `./` (with and without `!`).
237
+ This means rules beginning with `/` are absolute. Not relative to [`root:`](#root).
238
+
239
+ Additionally it assumes all rules are relative to the [`root:`](#root) directory (after resolving absolute paths) unless they begin with `*` (or `!*`).
240
+
241
+ This can be a string, or an array of strings, and multiline strings can be used with one rule per line.
178
242
 
179
243
  ```ruby
180
244
  FastIgnore.new(argv_rules: ['./a/pasted/path', '/or/a/path/from/stdin', 'an/argument', '*.txt']).to_a
181
245
  ```
182
246
 
183
- It resolves absolute paths, and paths beginning with `~`, `../` and `./` (with and without `!`)
184
- It assumes all rules are anchored unless they begin with `*` or `!*`.
247
+ **Warning: it will *not* expand e.g. `/../` in the middle of a rule that doesn't begin with any of `~`,`../`,`./`,`/`.**
185
248
 
186
- Note: it will *not* resolve e.g. `/../` in the middle of a rule that doesn't begin with any of `~`,`../`,`./`,`/`.
249
+ **Note: All paths checked must not be excluded by any ignore files AND each included by [include file](#include_files) separately AND the [`include_rules:`](#include_rules) AND the `argv_rules:`. see [Combinations](#combinations) for solutions to using OR.**
187
250
 
188
251
  ### shebang rules
189
252
 
190
- Sometimes you need to match files by their shebang rather than their path or filename
191
-
192
- To match extensionless files by shebang/hashbang/etc:
253
+ Sometimes you need to match files by their shebang/hashbang/etc rather than their path or filename
193
254
 
194
- Lines beginning with `#!:` will match whole words in the shebang line of extensionless files.
255
+ Rules beginning with `#!:` will match whole words in the shebang line of extensionless files.
195
256
  e.g.
196
257
  ```gitignore
197
258
  #!:ruby
198
259
  ```
199
260
  will match shebang lines: `#!/usr/bin/env ruby` or `#!/usr/bin/ruby` or `#!/usr/bin/ruby -w`
261
+
200
262
  e.g.
201
263
  ```gitignore
202
264
  #!:bin/ruby
203
265
  ```
204
266
  will match `#!/bin/ruby` or `#!/usr/bin/ruby` or `#!/usr/bin/ruby -w`
205
- Currently only exact substring matches are available, There's no special handling of * or / or etc.
267
+ Only exact substring matches are available, There's no special handling of * or / or etc.
206
268
 
269
+ These rules can be supplied any way regular rules are, whether in a .gitignore file or files mentioned in [`include_files:`](#include_files) or [`ignore_files:`](#ignore_files) or [`include_rules:`](#include_rules) or [`ignore_rules:`](#ignore_rules) or [`argv_rules:`](#argv_rules)
207
270
  ```ruby
208
271
  FastIgnore.new(include_rules: ['*.rb', '#!:ruby']).to_a
209
272
  FastIgnore.new(ignore_rules: ['*.sh', '#!:sh', '#!:bash', '#!:zsh']).to_a
210
273
  ```
211
274
 
275
+ **Note: git considers rules like this as a comment and will ignore them.**
276
+
212
277
  ## Combinations
213
278
 
214
- In the simplest case a file must be allowed by each ignore file, each include file, and each array of rules. That is, they are combined using AND.
279
+ In the simplest case a file must be allowed by each ignore file, each include file, and each array of rules. That is, they are combined using `AND`.
215
280
 
216
281
  To combine files using `OR`, that is, a file may be matched by either file it doesn't have to be referred to in both:
217
- provide the files as strings to `include_rules:` or `ignore_rules:`
282
+ provide the files as strings to [`include_rules:`](#include_rules) or [`ignore_rules:`](#ignore_rules)
218
283
  ```ruby
219
284
  FastIgnore.new(include_rules: [File.read('/my/path'), File.read('/another/path')])).to_a
220
285
  ```
221
- This does unfortunately lose the file path as the root for `/` and `/**` rules.
222
- If that's important, combine the files in the file system and use `include_files:` or `ignore_files:` as normal.
286
+ This does unfortunately lose the file path as the root for rules containing `/`.
287
+ If that's important, combine the files in the file system and use [`include_files:`](#include_files) or [`ignore_files:`](#ignore_files) as normal.
223
288
 
224
- To use the additional ARGV handling rules mentioned above for files, read the file into the array as a string.
289
+ To use the additional `ARGV` handling of [`argv_rules:`](#argv_rules) on a file, read the file into the array.
225
290
 
226
291
  ```ruby
227
292
  FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
228
293
  ```
229
294
 
230
- This does unfortunately lose the file path as the root for `/` and `/**` rules.
295
+ 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.
231
296
 
232
297
  ### optimising #allowed?
233
298
 
@@ -239,10 +304,11 @@ This is not required, and if FastIgnore does have to go to the filesystem for th
239
304
 
240
305
 
241
306
  ## Known issues
242
- - Doesn't know what to do if you change the current working directory inside the `FastIgnore#each` block.
307
+ - Doesn't know what to do if you change the current working directory inside the [`FastIgnore#each`](#each_map_etc) block.
243
308
  So don't do that.
244
309
 
245
- (It does handle changing the current working directory between `FastIgnore#allowed?` calls.)
310
+ (It does handle changing the current working directory between [`FastIgnore#allowed?`](#allowed) calls) (changing directories doesn't affect the [`root:`](#root) directory, that's frozen at FastIgnore.new (this is a design decision, not an issue)).
311
+ - FastIgnore always matches patterns case-insensitively. (git varies by filesystem).
246
312
 
247
313
  ## Development
248
314
 
@@ -254,6 +320,12 @@ This repo is too small to stress bin/time more than 0.01s, switch to a large rep
254
320
 
255
321
  To install this gem onto your local machine, run `bundle exec rake install`.
256
322
 
323
+ ### Goals
324
+
325
+ 1. Match `git ls-files` behaviour quirk for quirk.
326
+ 2. Provide a convenient interface for allowlist/denylist files in ruby.
327
+ 3. Be fast.
328
+
257
329
  ## Contributing
258
330
 
259
331
  Bug reports and pull requests are welcome on GitHub at https://github.com/robotdana/fast_ignore.
@@ -22,9 +22,9 @@ class FastIgnore
22
22
 
23
23
  def initialize(relative: false, root: nil, gitignore: :auto, follow_symlinks: false, **rule_set_builder_args)
24
24
  @relative = relative
25
- @follow_symlinks = follow_symlinks
25
+ @follow_symlinks_method = ::File.method(follow_symlinks ? :stat : :lstat)
26
26
  @gitignore_enabled = gitignore
27
- @loaded_gitignore_files = Set[''] if gitignore
27
+ @loaded_gitignore_files = ::Set[''] if gitignore
28
28
  @root = "#{::File.expand_path(root.to_s, Dir.pwd)}/"
29
29
  @rule_sets = ::FastIgnore::RuleSetBuilder.build(root: @root, gitignore: gitignore, **rule_set_builder_args)
30
30
 
@@ -43,7 +43,7 @@ class FastIgnore
43
43
  def allowed?(path, directory: nil, content: nil)
44
44
  full_path = ::File.expand_path(path, @root)
45
45
  return false unless full_path.start_with?(@root)
46
- return false if directory.nil? ? directory?(full_path) : directory
46
+ return false if directory.nil? ? @follow_symlinks_method.call(full_path).directory? : directory
47
47
 
48
48
  relative_path = full_path.delete_prefix(@root)
49
49
  load_gitignore_recursive(relative_path) if @gitignore_enabled
@@ -58,14 +58,6 @@ class FastIgnore
58
58
 
59
59
  private
60
60
 
61
- def directory?(path)
62
- if @follow_symlinks
63
- ::File.stat(path).directory?
64
- else
65
- ::File.lstat(path).directory?
66
- end
67
- end
68
-
69
61
  def load_gitignore_recursive(path)
70
62
  paths = []
71
63
  while (path = ::File.dirname(path)) != '.'
@@ -93,7 +85,7 @@ class FastIgnore
93
85
  begin
94
86
  full_path = parent_full_path + filename
95
87
  relative_path = parent_relative_path + filename
96
- dir = directory?(full_path)
88
+ dir = @follow_symlinks_method.call(full_path).directory?
97
89
 
98
90
  next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename, nil) }
99
91
 
@@ -32,7 +32,7 @@ class FastIgnore
32
32
  end
33
33
 
34
34
  def shebang_rules(rule, allow)
35
- rules = [::FastIgnore::ShebangRule.new(/\A#!.*\b#{Regexp.escape(rule)}\b/.freeze, allow)]
35
+ rules = [::FastIgnore::ShebangRule.new(/\A#!.*\b#{Regexp.escape(rule)}\b/i.freeze, allow)]
36
36
  return rules unless allow
37
37
 
38
38
  rules << ::FastIgnore::Rule.new('**/*', true, true, true)
@@ -46,9 +46,10 @@ class FastIgnore
46
46
 
47
47
  def squash_rules(rules)
48
48
  out = rules.chunk_while { |a, b| a.type == b.type }.map do |chunk|
49
- next chunk.first if chunk.length == 1
49
+ first = chunk.first
50
+ next first if chunk.length == 1
50
51
 
51
- chunk.first.class.new(Regexp.union(chunk.map(&:rule)), chunk.first.negation?)
52
+ first.class.new(Regexp.union(chunk.map(&:rule)), first.negation?)
52
53
  end
53
54
 
54
55
  out
@@ -11,7 +11,7 @@ class FastIgnore
11
11
  root:,
12
12
  ignore_rules: nil,
13
13
  ignore_files: nil,
14
- gitignore: :auto,
14
+ gitignore: true,
15
15
  include_rules: nil,
16
16
  include_files: nil,
17
17
  argv_rules: nil
@@ -19,7 +19,7 @@ class FastIgnore
19
19
  prepare [
20
20
  from_array(ignore_rules),
21
21
  *from_files(ignore_files, project_root: root),
22
- from_array('.git'),
22
+ (from_array('.git') if gitignore),
23
23
  from_gitignore_arg(gitignore, project_root: root),
24
24
  from_array(include_rules, allow: true),
25
25
  *from_files(include_files, allow: true, project_root: root),
@@ -72,12 +72,12 @@ class FastIgnore
72
72
  gi = ::FastIgnore::RuleSet.new([], false, true)
73
73
  gi << from_root_gitignore_file(global_gitignore_path(root: project_root))
74
74
  gi << from_root_gitignore_file(::File.join(project_root, '.git/info/exclude'))
75
- gi << from_root_gitignore_file(::File.join(project_root, '.gitignore'), soft: gitignore == :auto)
75
+ gi << from_root_gitignore_file(::File.join(project_root, '.gitignore'))
76
76
  gi
77
77
  end
78
78
 
79
- def from_root_gitignore_file(path, soft: true)
80
- return if soft && !::File.exist?(path)
79
+ def from_root_gitignore_file(path)
80
+ return unless ::File.exist?(path)
81
81
 
82
82
  build_rule_set(::File.readlines(path), false, file_root: '', gitignore: true)
83
83
  end
@@ -15,8 +15,7 @@ class FastIgnore
15
15
  @rule = rule
16
16
  @negation = negation
17
17
 
18
- @type = 2
19
- @type += 1 if negation
18
+ @type = negation ? 3 : 2
20
19
 
21
20
  freeze
22
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.12.1'
4
+ VERSION = '0.13.0'
5
5
  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.12.1
4
+ version: 0.13.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-05-24 00:00:00.000000000 Z
11
+ date: 2020-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -143,27 +143,9 @@ executables: []
143
143
  extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
- - ".gitignore"
147
- - ".leftovers.yml"
148
- - ".rspec"
149
- - ".rubocop.yml"
150
- - ".simplecov"
151
- - ".spellr.yml"
152
- - ".spellr_wordlists/english.txt"
153
- - ".spellr_wordlists/ruby.txt"
154
- - ".spellr_wordlists/shell.txt"
155
- - ".travis.yml"
156
146
  - CHANGELOG.md
157
- - Gemfile
158
147
  - LICENSE.txt
159
148
  - README.md
160
- - Rakefile
161
- - bin/console
162
- - bin/ls
163
- - bin/ls_seconds
164
- - bin/setup
165
- - bin/time
166
- - fast_ignore.gemspec
167
149
  - lib/fast_ignore.rb
168
150
  - lib/fast_ignore/backports.rb
169
151
  - lib/fast_ignore/fn_match_to_re.rb
data/.gitignore DELETED
@@ -1,6 +0,0 @@
1
- /.bundle/
2
- /coverage/
3
- /pkg/
4
- .rspec_status
5
- *.gem
6
- Gemfile.lock
@@ -1,5 +0,0 @@
1
- exclude_paths:
2
- - vendor
3
- rules:
4
- - names: allowed? # public api
5
- skip: true
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
@@ -1,216 +0,0 @@
1
- require: rubocop-rspec
2
-
3
- # Reference:
4
- # https://rubocop.readthedocs.io/en/latest/
5
-
6
- # Keep this in alphabetical order.
7
- # Each override should have a comment (even if it's just "default is bad")
8
-
9
- AllCops:
10
- NewCops: enable
11
- Exclude:
12
- - db/schema*
13
- - .bundle/**/*
14
- - tmp/**/*
15
- - vendor/**/*
16
- DisplayCopNames: true
17
- DisplayStyleGuide: true
18
- TargetRubyVersion: 2.4
19
-
20
- # all of our layout customisations are because we prefer indentation to be
21
- # always consistently 2 spaces, for blocks, scopes, multiline expressions, etc
22
- # e.g.
23
- # class Klass
24
- # def method(arg1,
25
- # arg2)
26
- # value = if arg1 == 'value' && arg2 == 'value'
27
- # method2
28
- # .method(arg_a, arg_b,
29
- # arg_c, arg_d, keyword1: true,
30
- # keyword2: true) do
31
- # @last = [
32
- # arg_a, arg_b,
33
- # arg_c, arg_d
34
- # ]
35
- # end
36
- # end
37
- # value
38
- # end
39
- # end
40
-
41
- # to match our preference for consistent indentation
42
- Layout/HashAlignment:
43
- EnforcedLastArgumentHashStyle: always_ignore
44
-
45
- # to match our preference for consistent indentation
46
- Layout/ParameterAlignment:
47
- EnforcedStyle: with_fixed_indentation
48
-
49
- # to match our preference for consistent indentation
50
- Layout/BlockAlignment:
51
- EnforcedStyleAlignWith: start_of_block
52
-
53
- # to match our preference for consistent indentation
54
- Layout/CaseIndentation:
55
- EnforcedStyle: end
56
-
57
- # to match our preference for consistent indentation
58
- Layout/EndAlignment:
59
- EnforcedStyleAlignWith: start_of_line
60
-
61
- # Aligning Assignments, etc makes diffs noisy
62
- Layout/ExtraSpacing:
63
- AllowForAlignment: false
64
-
65
- # to match our preference for consistent indentation
66
- Layout/FirstArrayElementLineBreak:
67
- Enabled: true
68
-
69
- # to match our preference for consistent indentation
70
- Layout/FirstHashElementLineBreak:
71
- Enabled: true
72
-
73
- # to match our preference for consistent indentation
74
- Layout/FirstArgumentIndentation:
75
- EnforcedStyle: consistent
76
-
77
- # to match our preference for consistent indentation
78
- Layout/FirstArrayElementIndentation:
79
- EnforcedStyle: consistent
80
-
81
- # to match our preference for consistent indentation
82
- Layout/FirstHashElementIndentation:
83
- EnforcedStyle: consistent
84
-
85
- Layout/LineLength:
86
- Max: 120
87
-
88
- # to match our preference for consistent indentation
89
- # and hanging assignment looks lost
90
- Layout/MultilineAssignmentLayout:
91
- EnforcedStyle: same_line
92
-
93
- # this changes our preferred:
94
- # value = if thing1 &&
95
- # thing2
96
- # to:
97
- # value = if thing1 &&
98
- # thing2
99
- # even though the IndentationWidth is 2
100
- # but it's right most of the time so I put up with it
101
- Layout/MultilineOperationIndentation:
102
- EnforcedStyle: indented
103
-
104
- Layout/MultilineMethodCallIndentation:
105
- EnforcedStyle: indented
106
-
107
- # Temporarily disable this spec as a recent change has broken it for us:
108
- # https://github.com/rubocop-hq/rubocop/issues/6254
109
- Layout/RescueEnsureAlignment:
110
- Enabled: false
111
-
112
- Metrics:
113
- CountComments: false
114
-
115
- Metrics/BlockLength:
116
- ExcludedMethods:
117
- - configure
118
- - describe
119
- - context
120
- - shared_examples
121
-
122
- Metrics/CyclomaticComplexity:
123
- Enabled: false
124
-
125
- Metrics/PerceivedComplexity:
126
- Enabled: false
127
-
128
- RSpec:
129
- Enabled: true
130
- Include:
131
- - 'spec/**/*.rb'
132
-
133
- RSpec/DescribeClass:
134
- Enabled: false
135
-
136
- # I misuse matchers often
137
- RSpec/ExpectActual:
138
- Enabled: false
139
-
140
- RSpec/FilePath:
141
- Enabled: false
142
-
143
- # Multiple expectations are useful
144
- # checking you've partially achieved something on the way to completely achieving it is useful for debugging failures
145
- RSpec/MultipleExpectations:
146
- Enabled: false
147
-
148
- # It should be obvious from context. Chill out rubocop
149
- RSpec/NamedSubject:
150
- Enabled: false
151
-
152
- RSpec/NestedGroups:
153
- Max: 7
154
-
155
- # This matches the style we've been using all along (ever so slightly more frequently)
156
- Style/Alias:
157
- EnforcedStyle: prefer_alias_method
158
-
159
- Style/CollectionMethods:
160
- Enabled: true
161
-
162
- # we don't rdoc
163
- Style/Documentation:
164
- Enabled: false
165
-
166
- # this can mess with the balance of symmetric cases
167
- Style/IfInsideElse:
168
- Enabled: false
169
-
170
- # [a, b].include?(x) is more unclear than a == x || b == x
171
- Style/MultipleComparison:
172
- Enabled: false
173
-
174
- # it's microscopically faster
175
- Style/Not:
176
- Enabled: false
177
-
178
- # we use %w{} pretty frequently
179
- Style/PercentLiteralDelimiters:
180
- PreferredDelimiters:
181
- default: '{}'
182
- '%w': '{}'
183
- '%W': '{}'
184
- '%i': '{}'
185
- '%I': '{}'
186
- '%r': '{}'
187
-
188
- # We want this to warn to force consistency within the codebase.
189
- Style/SafeNavigation:
190
- Enabled: true
191
-
192
- # different methods calls that do exactly the same thing are a smell, regardless of semantics
193
- Style/SignalException:
194
- EnforcedStyle: only_raise
195
-
196
- # this wants less descriptive names
197
- Style/SingleLineBlockParams:
198
- Enabled: false
199
-
200
- Style/SymbolArray:
201
- Enabled: false
202
-
203
- Style/WordArray:
204
- Enabled: false
205
-
206
- Style/HashEachMethods:
207
- Enabled: true
208
-
209
- Style/HashTransformKeys:
210
- Enabled: true
211
-
212
- Style/HashTransformValues:
213
- Enabled: true
214
-
215
- Style/CommentedKeyword:
216
- Enabled: false
data/.simplecov DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- SimpleCov.start do
4
- add_filter '/backports'
5
- add_filter '/spec/'
6
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5')
7
- enable_coverage(:branch)
8
- minimum_coverage line: 100, branch: 100
9
- else
10
- minimum_coverage 100
11
- end
12
- end
@@ -1,5 +0,0 @@
1
- excludes:
2
- - vendor
3
- languages:
4
- english:
5
- locale: [US, AU]
@@ -1,62 +0,0 @@
1
- argv
2
- backports
3
- baz
4
- changelog
5
- codebase
6
- config
7
- cov
8
- cyclomatic
9
- enoent
10
- enumerables
11
- env
12
- errno
13
- esque
14
- excludesfile
15
- extensionless
16
- fancyignore
17
- filesystem
18
- fnmatch
19
- frotz
20
- gemfile
21
- gitconfig
22
- github
23
- gitignore
24
- gitkeep
25
- hashbang
26
- includefile
27
- janky
28
- jruby
29
- klass
30
- llo
31
- memoize
32
- nocov
33
- noninfringement
34
- params
35
- pathspec
36
- pwd
37
- rdoc
38
- regexp
39
- rspec
40
- rubo
41
- rubocop
42
- rubygems
43
- rubyish
44
- rulesets
45
- rvm
46
- sherson
47
- simplecov
48
- stdin
49
- subdir
50
- substring
51
- sudo
52
- symlinks
53
- tmp
54
- toplevel
55
- txt
56
- unrecursive
57
- upcase
58
- usr
59
- webpack
60
- xconfig
61
- xdg
62
- zsh
@@ -1 +0,0 @@
1
- rspec
@@ -1,3 +0,0 @@
1
- env
2
- euo
3
- usr
@@ -1,11 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- before_install: gem install bundler
6
- rvm:
7
- - 2.4
8
- - 2.5
9
- - 2.6
10
- - 2.7
11
- - jruby
data/Gemfile DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
-
7
- # Specify your gem's dependencies in fast_ignore.gemspec
8
-
9
- gemspec
data/Rakefile DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
- require 'rubocop/rake_task'
6
- require 'spellr/rake_task'
7
- require 'leftovers/rake_task'
8
-
9
- RuboCop::RakeTask.new
10
- RSpec::Core::RakeTask.new(:spec)
11
- Spellr::RakeTask.generate_task
12
- Leftovers::RakeTask.generate_task
13
-
14
- if RUBY_PLATFORM == 'java'
15
- task default: :spec
16
- else
17
- task default: [:spec, :rubocop, :spellr, :leftovers]
18
- end
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'fast_ignore'
6
-
7
- require 'pry'
8
- Pry.start
data/bin/ls DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby --disable-all
2
- # frozen_string_literal: true
3
-
4
- require_relative '../lib/fast_ignore'
5
-
6
- puts FastIgnore.new(relative: true, argv_rules: ARGV).to_a
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env ruby --disable-all
2
- # frozen_string_literal: true
3
-
4
- require_relative '../lib/fast_ignore'
5
-
6
- t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
7
- new_a = FastIgnore.new(relative: true, argv_rules: ARGV).to_a
8
- t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
9
- puts "new #{new_a.count}: #{t2 - t1}"
10
- fi = FastIgnore.new(relative: true, argv_rules: ARGV)
11
- fi.to_a
12
- t3 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
13
- cached_a = fi.to_a
14
- t4 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
15
- puts "cached #{cached_a.count}: #{t4 - t3}"
16
- t5 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
17
- git_a = `git ls-files`.split("\n")
18
- t6 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
19
- puts "git ls-files #{git_a.count}: #{t6 - t5}"
20
-
21
- exit 1 unless new_a.length == cached_a.length && new_a.length == git_a.length
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/bin/time DELETED
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # use mac terminal not vs code terminal
5
- # ensure nothing else is watching that dir in the filesystem e.g. webpack
6
-
7
- require 'open3'
8
- require 'shellwords'
9
- RUNS = 30
10
- SCRIPT = "time #{__dir__}/ls #{Shellwords.join(ARGV)}"
11
-
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+)?)/)
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
- )
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'fast_ignore/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'fast_ignore'
9
- spec.version = FastIgnore::VERSION
10
- spec.authors = ['Dana Sherson']
11
- spec.email = ['robot@dana.sh']
12
-
13
- spec.summary = 'Parse gitignore files, quickly'
14
- spec.homepage = 'https://github.com/robotdana/fast_ignore'
15
- spec.license = 'MIT'
16
-
17
- spec.required_ruby_version = '~> 2.4'
18
-
19
- if spec.respond_to?(:metadata)
20
- spec.metadata['homepage_uri'] = spec.homepage
21
- spec.metadata['source_code_uri'] = 'https://github.com/robotdana/fast_ignore'
22
- spec.metadata['changelog_uri'] = 'https://github.com/robotdana/fast_ignore/blob/master/CHANGELOG.md'
23
- end
24
-
25
- # Specify which files should be added to the gem when it is released.
26
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
- end
30
- spec.require_paths = ['lib']
31
-
32
- spec.add_development_dependency 'bundler', '>= 1.17'
33
- spec.add_development_dependency 'leftovers', '>= 0.2.2'
34
- spec.add_development_dependency 'pry', '> 0'
35
- spec.add_development_dependency 'rake', '>= 12.3.3'
36
- spec.add_development_dependency 'rspec', '~> 3.0'
37
- spec.add_development_dependency 'rubocop', '>= 0.74.0'
38
- spec.add_development_dependency 'rubocop-rspec', '~> 1'
39
- spec.add_development_dependency 'simplecov', '~> 0.18.5'
40
- spec.add_development_dependency 'spellr', '>= 0.8.3'
41
- end