fast_ignore 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b87f43721c846fb9a6580451cb7fe607f40de8dd57bfab60c3e48454404c3456
4
- data.tar.gz: d1b79c925d24ff57b3e0a6d7d74923d241807d6015f1570ba17c58bb9458493e
3
+ metadata.gz: 7129d0d60e64bc8b51ab8facc44ddba880dcdf826827a740e813505ac6ce44f1
4
+ data.tar.gz: 38006fd4177cbed4e0d8680a151b1ade36baf50e3ab3d3048059537a5c714bba
5
5
  SHA512:
6
- metadata.gz: ff71be320a1218b786037efa9132d2167e80e899012afb843a59f03b7df414448892c2a71efa9e6504c2b558724a43fb1c17cd688802e0e3bbdf494ff9e7a714
7
- data.tar.gz: 0a4f3bd777b3f8ca63086cbe63a61f759ca4915e4e6f80db74f69310d3ad59e997706fca8e6252326b2d217e2a79e6f3c4020eddb4eb646d6870fdc6cec44847
6
+ metadata.gz: 9ccace7734a566cbaee998a5fc44e646aa166fdd62929f79073d11b4062ec2d2cebebbfd6bfa59872e3e88f8c06c46bbb6a02aaa4a6b74411618875c4f2ccd7e
7
+ data.tar.gz: 90d37e05acfe7d6c0dedf4e28b76b631c121a190b8499daa8b620538b7e682637dc1b55b29a7efb8e98af687077fc4e766b5096455109cc4008dd1feff024059
@@ -1,3 +1,8 @@
1
+ # v0.4.0
2
+ - include_rules support
3
+ - to make room for this, `:rules` and `:files` keyword arguments are deprecated.
4
+ Please use `:ignore_rules` and `:ignore_files` instead.
5
+
1
6
  # v0.3.3
2
7
  - some performance improvements. maybe
3
8
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fast_ignore (0.3.3)
4
+ fast_ignore (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -28,7 +28,7 @@ $ gem install fast_ignore
28
28
  ## Usage
29
29
 
30
30
  ```ruby
31
- FastIgnore.new.each { |file| puts "#{file} is not ignored by the .gitignore" }
31
+ FastIgnore.new.each { |file| puts "#{file} is not ignored by the .gitignore file" }
32
32
  ```
33
33
 
34
34
  Like many other enumerables, `FastIgnore#each` can return an enumerator
@@ -45,32 +45,64 @@ FastIgnore.new(relative: true).to_a
45
45
 
46
46
  You can specify other gitignore-style files to ignore as well. These rules will be appended after the gitignore file in order (order matters for negations)
47
47
  ```ruby
48
- FastIgnore.new(files: '/absolute/path/to/my/ignore/file').to_a
49
- FastIgnore.new(files: ['/absolute/path/to/my/ignore/file', '/and/another']).to_a
48
+ FastIgnore.new(ignore_files: '/absolute/path/to/my/ignore/file').to_a
49
+ FastIgnore.new(ignore_files: ['/absolute/path/to/my/ignore/file', '/and/another']).to_a
50
50
  ```
51
- You can also supply an array of rule lines. These rules will be appended after the gitignore and any other files in order (order matters for negations)
51
+ You can also supply an array of rule strings. These rules will be appended after the gitignore and any other files in order (order matters for negations)
52
52
  ```ruby
53
- FastIgnore.new(rules: '.DS_Store').to_a
54
- FastIgnore.new(rules: ['.git', '.gitkeep']).to_a
53
+ FastIgnore.new(ignore_rules: '.DS_Store').to_a
54
+ FastIgnore.new(ignore_rules: ['.git', '.gitkeep']).to_a
55
55
  ```
56
56
 
57
- To only use another ignore file or set of rules, and not try to load a gitignore file:
57
+ To use only another ignore file or an array of rules, and not try to load a gitignore file:
58
58
  ```ruby
59
- FastIgnore.new(files: 'absolute/path/to/my/ignore/file', gitignore: false)
60
- FastIgnore.new(rules: %w{my*rule /and/another !rule}, gitignore: false)
59
+ FastIgnore.new(ignore_files: '/absolute/path/to/my/ignore/file', gitignore: false)
60
+ FastIgnore.new(ignore_rules: %w{my*rule /and/another !rule}, gitignore: false)
61
61
  ```
62
62
 
63
63
  By default, FastIgnore will look in the directory the script is run in (`PWD`) for a gitignore file. If it's somewhere else:
64
64
  ```ruby
65
65
  FastIgnore.new(gitignore: '/absolute/path/to/.gitignore').to_a
66
66
  ```
67
- Note that the location of the .gitignore file will affect things like rules beginning with `/` or ending in `/**`
67
+ Note that the location of the .gitignore file will affect rules beginning with `/` or ending in `/**`
68
68
 
69
69
  To check if a single file is allowed, use
70
70
  ```ruby
71
- FastIgnore.new.allowed?('/absolute/path/to/file')
71
+ FastIgnore.new.allowed?('relative/path')
72
+ FastIgnore.new.allowed?('./relative/path')
73
+ FastIgnore.new.allowed?('/absolute/path')
74
+ FastIgnore.new.allowed?('~/home/path')
72
75
  ```
73
76
 
77
+ ### Using an includes list.
78
+
79
+ Building on the gitignore format, FastIgnore also accepts a list of allowed or included files.
80
+
81
+ ```
82
+ # a line like this means any files named foo will be included
83
+ # as well as any files within directories named foo
84
+ foo
85
+ # a line beginning with a slash will be anything in a directory that is a child of the PWD
86
+ /foo
87
+ # a line ending in a slash will will include any files in any directories named foo
88
+ # but not any files named foo
89
+ foo/
90
+ # negated rules are slightly different from gitignore
91
+ # in that they're evaluated after all the other the matching files rather than
92
+ # in sequence with other rules
93
+ fo*
94
+ !foe
95
+ # otherwise this format deals with *'s and ?'s and etc as you'd expect from gitignore.
96
+ ```
97
+
98
+ These can be passed either as files or as an array or string rules
99
+ ```ruby
100
+ FastIgnore.new(include_files: '/absolute/path/to/my/include/file', gitignore: false)
101
+ FastIgnore.new(include_rules: %w{my*rule /and/another !rule}, gitignore: false)
102
+ ```
103
+
104
+ The string array rules additionally expects to handle all kinds of `ARGV` values, so will correctly resolve absolute paths and paths beginning with `~`, `../` and `./`. Note: it will *not* resolve e.g. `/../` in the middle of a rule that doesn't begin with any of `~`,`../`,`./`,`/`. This is not available for the file form.
105
+
74
106
  ## Known issues
75
107
  - Doesn't take into account project excludes in `.git/info/exclude`
76
108
  - Doesn't take into account globally ignored files in `git config core.excludesFile`.
data/bin/ls CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../lib/fast_ignore'
4
5
 
5
- puts FastIgnore.new(relative: true).to_a.join("\n")
6
+ puts FastIgnore.new(relative: true, include_rules: ARGV).to_a
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './fast_ignore/delete_prefix_suffix'
4
3
  require_relative './fast_ignore/rule'
5
4
  require_relative './fast_ignore/rule_list'
6
5
  require_relative './fast_ignore/file_rule_list'
@@ -8,32 +7,60 @@ require_relative './fast_ignore/gitignore_rule_list'
8
7
 
9
8
  require 'find'
10
9
 
11
- class FastIgnore
12
- include Enumerable
13
- using DeletePrefixSuffix unless RUBY_VERSION >= '2.5'
10
+ class FastIgnore # rubocop:disable Metrics/ClassLength
11
+ include ::Enumerable
12
+
13
+ unless ::RUBY_VERSION >= '2.5'
14
+ require_relative 'fast_ignore/backports/delete_prefix_suffix'
15
+ using ::FastIgnore::Backports::DeletePrefixSuffix
16
+ end
14
17
 
15
- attr_reader :rules
16
18
  attr_reader :relative
17
19
  alias_method :relative?, :relative
18
20
  attr_reader :root
19
21
 
20
- def initialize(
22
+ def initialize( # rubocop:disable Metrics/ParameterLists
23
+ relative: false,
24
+ root: ::Dir.pwd,
21
25
  rules: nil,
26
+ ignore_rules: rules,
22
27
  files: nil,
23
- relative: false,
24
- root: Dir.pwd,
25
- gitignore: File.join(root, '.gitignore')
28
+ ignore_files: files,
29
+ gitignore: ::File.join(root, '.gitignore'),
30
+ include_rules: nil,
31
+ include_files: nil
26
32
  )
27
- @rules = []
28
- @rules += FastIgnore::RuleList.new(*Array(rules)).to_a
29
- Array(files).reverse_each do |file|
30
- @rules += FastIgnore::FileRuleList.new(file).to_a
33
+ if rules || files
34
+ warn <<~WARNING
35
+ \e[33mFastIgnore.new `:rules` and `:files` keyword arguments are deprecated.
36
+ Please use `:ignore_rules` and `:ignore_files` instead.\e[0m
37
+ WARNING
31
38
  end
32
- @rules += FastIgnore::GitignoreRuleList.new(gitignore).to_a if gitignore
39
+ prepare_include_rules(include_rules, include_files)
40
+ prepare_ignore_rules(ignore_rules, ignore_files, gitignore)
33
41
  @relative = relative
34
42
  @root = root
35
43
  end
36
44
 
45
+ def prepare_ignore_rules(ignore_rules, ignore_files, gitignore)
46
+ @ignore_rules += ::FastIgnore::RuleList.new(*Array(ignore_rules)).to_a
47
+ Array(ignore_files).reverse_each do |file|
48
+ @ignore_rules += ::FastIgnore::FileRuleList.new(file).to_a
49
+ end
50
+
51
+ @ignore_rules += ::FastIgnore::GitignoreRuleList.new(gitignore).to_a if gitignore
52
+ end
53
+
54
+ def prepare_include_rules(include_rules, include_files)
55
+ include_rules = ::FastIgnore::RuleList.new(*Array(include_rules), expand_path: true).to_a
56
+ Array(include_files).reverse_each do |file|
57
+ include_rules += ::FastIgnore::FileRuleList.new(file).to_a
58
+ end
59
+
60
+ @include_rules = include_rules.reject(&:negation?)
61
+ @ignore_rules = include_rules.select(&:negation?).each(&:invert)
62
+ end
63
+
37
64
  def each(&block)
38
65
  if block_given?
39
66
  enumerator.each(&block)
@@ -42,33 +69,105 @@ class FastIgnore
42
69
  end
43
70
  end
44
71
 
45
- def allowed?(path, dir: File.directory?(path))
46
- return true if path == root
47
-
48
- allowed?(File.dirname(path), dir: true) && pruned_allowed?(path, dir: dir)
72
+ def allowed?(path)
73
+ allowed_expanded?(::File.expand_path(path))
49
74
  end
50
75
 
51
76
  private
52
77
 
53
- def enumerator # rubocop:disable Metrics/MethodLength
54
- Enumerator.new do |yielder|
55
- Find.find(root) do |path|
56
- dir = File.directory?(path)
78
+ def enumerator
79
+ if !@include_rules.empty? && @include_rules.all?(&:globbable?)
80
+ glob_enumerator
81
+ else
82
+ find_enumerator
83
+ end
84
+ end
85
+
86
+ def allowed_expanded?(path, dir = ::File.directory?(path))
87
+ not_excluded_recursive?(path, dir) && not_ignored_recursive?(path, dir)
88
+ end
89
+
90
+ def glob_enumerator # rubocop:disable Metrics/MethodLength
91
+ seen = {}
92
+ ::Enumerator.new do |yielder|
93
+ ::Dir.glob(@include_rules.flat_map(&:glob_pattern), ::FastIgnore::Rule::FNMATCH_OPTIONS) do |path|
94
+ next if seen[path]
95
+
96
+ seen[path] = true
97
+ next if ::File.directory?(path)
98
+ next unless ::File.readable?(path)
99
+ next unless not_ignored_recursive?(path, false)
100
+
101
+ path = path.delete_prefix("#{root}/") if @relative
102
+
103
+ yielder << path
104
+ end
105
+ end
106
+ end
107
+
108
+ def find_enumerator # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
109
+ ::Enumerator.new do |yielder|
110
+ ::Find.find(root) do |path|
57
111
  next if path == root
58
- next unless File.readable?(path)
59
- next Find.prune unless pruned_allowed?(path, dir: dir)
112
+ next unless ::File.readable?(path)
113
+
114
+ dir = ::File.directory?(path)
115
+ next ::Find.prune unless not_ignored?(path, dir)
116
+ next unless not_excluded_recursive?(path, dir)
60
117
  next if dir
61
118
 
62
- path = path.delete_prefix("#{root}/") if relative?
119
+ path = path.delete_prefix("#{root}/") if @relative
63
120
 
64
121
  yielder << path
65
122
  end
66
123
  end
67
124
  end
68
125
 
69
- def pruned_allowed?(path, dir = File.directory?(path))
70
- rules.each do |rule|
71
- return rule.negation? if rule.match?(path, dir = dir)
126
+ def not_ignored_recursive?(path, dir = ::File.directory?(path))
127
+ @not_ignored ||= {}
128
+ @not_ignored.fetch(path) do
129
+ @not_ignored[path] = if path == root
130
+ true
131
+ else
132
+ not_ignored_recursive?(::File.dirname(path), true) && not_ignored?(path, dir)
133
+ end
134
+ end
135
+ end
136
+
137
+ def not_excluded_recursive?(path, dir = ::File.directory?(path))
138
+ return true if @include_rules.empty?
139
+
140
+ @not_excluded ||= {}
141
+ @not_excluded.fetch(path) do
142
+ @not_excluded[path] = if path == root
143
+ false
144
+ else
145
+ not_excluded_recursive?(::File.dirname(path), true) || not_excluded?(path, dir)
146
+ end
72
147
  end
73
148
  end
149
+
150
+ def non_dir_ignore_rules
151
+ @non_dir_ignore_rules ||= @ignore_rules.reject(&:dir_only?)
152
+ end
153
+
154
+ def non_dir_include_rules
155
+ @non_dir_include_rules ||= @include_rules.reject(&:dir_only?)
156
+ end
157
+
158
+ def not_excluded?(path, dir)
159
+ return true if @include_rules.empty?
160
+
161
+ (dir ? @include_rules : non_dir_include_rules).find do |rule|
162
+ rule.match?(path)
163
+ end
164
+ end
165
+
166
+ def not_ignored?(path, dir)
167
+ (dir ? @ignore_rules : non_dir_ignore_rules).each do |rule|
168
+ return rule.negation? if rule.match?(path)
169
+ end
170
+
171
+ true
172
+ end
74
173
  end
@@ -0,0 +1,28 @@
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
+ def delete_prefix!(str)
9
+ slice!(0..(str.length - 1)) if start_with?(str)
10
+ self
11
+ end
12
+
13
+ def delete_suffix!(str)
14
+ slice!(-str.length..-1) if end_with?(str)
15
+ self
16
+ end
17
+
18
+ def delete_prefix(str)
19
+ dup.delete_prefix!(str)
20
+ end
21
+
22
+ def delete_suffix(str)
23
+ dup.delete_suffix!(str)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This is a backport of ruby 2.4's match? method
4
+ class FastIgnore
5
+ module Backports
6
+ module Match
7
+ refine ::String do
8
+ alias_method :match?, :match
9
+ end
10
+
11
+ refine ::Regexp do
12
+ alias_method :match?, :match
13
+ end
14
+ end
15
+ end
16
+ end
@@ -3,9 +3,9 @@
3
3
  require_relative './rule'
4
4
 
5
5
  class FastIgnore
6
- class FileRuleList < FastIgnore::RuleList
7
- def initialize(file, root: File.dirname(file))
8
- @lines = IO.foreach(file)
6
+ class FileRuleList < ::FastIgnore::RuleList
7
+ def initialize(file, root: ::File.dirname(file))
8
+ @lines = ::IO.foreach(file)
9
9
  @root = root
10
10
  end
11
11
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  class FastIgnore
4
4
  class GitignoreRuleList
5
- include Enumerable
5
+ include ::Enumerable
6
6
 
7
7
  def initialize(file)
8
8
  @file = file
9
9
  end
10
10
 
11
11
  def each(&block)
12
- FastIgnore::RuleList.new('.git').each(&block)
13
- FastIgnore::FileRuleList.new(file).each(&block)
12
+ ::FastIgnore::RuleList.new('.git').each(&block)
13
+ ::FastIgnore::FileRuleList.new(file).each(&block)
14
14
  end
15
15
 
16
16
  private
@@ -2,17 +2,28 @@
2
2
 
3
3
  class FastIgnore
4
4
  class Rule
5
- using DeletePrefixSuffix unless RUBY_VERSION >= '2.5'
5
+ unless ::RUBY_VERSION >= '2.5'
6
+ require_relative 'backports/delete_prefix_suffix'
7
+ using ::FastIgnore::Backports::DeletePrefixSuffix
8
+ end
9
+
10
+ unless ::RUBY_VERSION >= '2.4'
11
+ require_relative 'backports/match'
12
+ using ::FastIgnore::Backports::Match
13
+ end
14
+
15
+ FNMATCH_OPTIONS = (::File::FNM_DOTMATCH | ::File::FNM_PATHNAME | ::File::FNM_CASEFOLD).freeze
6
16
 
7
- FNMATCH_OPTIONS = File::FNM_DOTMATCH | File::FNM_PATHNAME
17
+ attr_reader :rule, :glob_prefix
8
18
 
9
- def initialize(rule, root:)
19
+ def initialize(rule, root:, expand_path: false)
10
20
  @rule = rule
11
21
  strip!
22
+ extract_dir_only
23
+ expand_path(root) if expand_path
12
24
  return if skip?
13
25
 
14
26
  extract_negation
15
- extract_dir_only
16
27
 
17
28
  @rule = "#{root}#{prefix}#{@rule}"
18
29
  end
@@ -21,6 +32,22 @@ class FastIgnore
21
32
  @negation
22
33
  end
23
34
 
35
+ def invert
36
+ @negation = !@negation
37
+ end
38
+
39
+ def glob_pattern
40
+ @glob_pattern ||= if @dir_only
41
+ "#{@rule}/**/*"
42
+ else
43
+ [@rule, "#{@rule}/**/*"]
44
+ end
45
+ end
46
+
47
+ def globbable?
48
+ !@rule.match?(%r{/\*\*/.*[^*/]})
49
+ end
50
+
24
51
  def extract_negation
25
52
  @negation = false
26
53
  return unless @rule.start_with?('!')
@@ -31,18 +58,18 @@ class FastIgnore
31
58
 
32
59
  def extract_dir_only
33
60
  @dir_only = false
34
- @not_dir_only = true
35
61
  return unless @rule.end_with?('/')
36
62
 
37
63
  @rule = @rule[0..-2]
38
64
  @dir_only = true
39
- @not_dir_only = false
40
65
  end
41
66
 
42
- def match?(path, dir = File.directory?(path))
43
- return false unless dir || @not_dir_only
67
+ def dir_only?
68
+ @dir_only
69
+ end
44
70
 
45
- File.fnmatch?(@rule, path, FNMATCH_OPTIONS)
71
+ def match?(path)
72
+ ::File.fnmatch?(@rule, path, ::FastIgnore::Rule::FNMATCH_OPTIONS)
46
73
  end
47
74
 
48
75
  def empty?
@@ -63,7 +90,9 @@ class FastIgnore
63
90
 
64
91
  private
65
92
 
66
- attr_reader :root
93
+ def expand_path(root)
94
+ @rule = ::File.expand_path(@rule).delete_prefix(root) if @rule.match?(%r{^(?:[~/]|..?/)})
95
+ end
67
96
 
68
97
  def prefix
69
98
  @prefix ||= if @rule.start_with?('/')
@@ -2,11 +2,12 @@
2
2
 
3
3
  class FastIgnore
4
4
  class RuleList
5
- include Enumerable
5
+ include ::Enumerable
6
6
 
7
- def initialize(*lines, root: Dir.pwd)
7
+ def initialize(*lines, root: ::Dir.pwd, expand_path: false)
8
8
  @lines = lines
9
9
  @root = root
10
+ @expand_path = expand_path
10
11
  end
11
12
 
12
13
  def each(&block)
@@ -17,12 +18,12 @@ class FastIgnore
17
18
 
18
19
  private
19
20
 
20
- attr_reader :lines, :root
21
+ attr_reader :lines, :root, :expand_path
21
22
 
22
23
  def enumerator
23
- Enumerator.new do |yielder|
24
+ ::Enumerator.new do |yielder|
24
25
  lines.reverse_each do |rule|
25
- rule = FastIgnore::Rule.new(rule, root: root)
26
+ rule = ::FastIgnore::Rule.new(rule, root: root, expand_path: expand_path)
26
27
  yielder << rule unless rule.skip?
27
28
  end
28
29
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.3.3'
4
+ VERSION = '0.4.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.3.3
4
+ version: 0.4.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: 2019-09-27 00:00:00.000000000 Z
11
+ date: 2019-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -116,7 +116,8 @@ files:
116
116
  - bin/setup
117
117
  - fast_ignore.gemspec
118
118
  - lib/fast_ignore.rb
119
- - lib/fast_ignore/delete_prefix_suffix.rb
119
+ - lib/fast_ignore/backports/delete_prefix_suffix.rb
120
+ - lib/fast_ignore/backports/match.rb
120
121
  - lib/fast_ignore/file_rule_list.rb
121
122
  - lib/fast_ignore/gitignore_rule_list.rb
122
123
  - lib/fast_ignore/rule.rb
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This is a backport of ruby 2.5's delete_prefix/delete_suffix methods
4
- module DeletePrefixSuffix
5
- refine String do
6
- def delete_prefix!(str)
7
- slice!(0..(str.length - 1)) if start_with?(str)
8
- self
9
- end
10
-
11
- def delete_suffix!(str)
12
- slice!(-str.length..-1) if end_with?(str)
13
- self
14
- end
15
-
16
- def delete_prefix(str)
17
- dup.delete_prefix!(str)
18
- end
19
-
20
- def delete_suffix(str)
21
- dup.delete_suffix!(str)
22
- end
23
- end
24
- end