fast_ignore 0.3.3 → 0.4.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: 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