fast_ignore 0.5.2 → 0.6.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: 35a905f9dd8c28df7b46d6c0c61ad1fcaf44a9566c19b704a88584668f49ba20
4
- data.tar.gz: 620917002cd9696bb5787819675034d6b5269d70812f0e5247523b10091d37b9
3
+ metadata.gz: 85adcabd5164bc3c1cf44af50ff5565b8ce00d6c69f8145eab0634fbaef551fb
4
+ data.tar.gz: de0192432d8423e5fee408aa7d3a052bc008d96b5cacadf7d2d6b66da3527030
5
5
  SHA512:
6
- metadata.gz: 6406dd9e6998fb9a3345981239e7fa0046f1356852a599f2c347d4b7bec4654069ac5704351c3079ce79c0965925a601dbcf56990ac6850aefef5f3a109793fa
7
- data.tar.gz: 83a7aacfd25b3350a4100d6247c683f6b4f1754c6311d9b6064631e5033890ea2fcc0c8dd5b5757f8f0e9a39af55cfc3f7560918813e69ab6e9139734abc4fa1
6
+ metadata.gz: 8507d8f07c51e0cb2b00a0ea619683bc41ca85b0a89d7e4787f5b0aa9157d255ec261a4ffab130982c89f1c85b4a0dd5f7030bb18de20186362eaf894b816b65
7
+ data.tar.gz: 661628a1913544cc67d1833ddb84218db6b85de165ba11fb39dd1f62c014638b9fd72d8f3efe0bc8ce2f1379acf43a63a2bedf016e9ff5927d400e8c4932a57c
data/.rubocop.yml CHANGED
@@ -169,6 +169,10 @@ Style/IfInsideElse:
169
169
  Style/MultipleComparison:
170
170
  Enabled: false
171
171
 
172
+ # it's microscopically faster
173
+ Style/Not:
174
+ Enabled: false
175
+
172
176
  # we use %w{} pretty frequently
173
177
  Style/PercentLiteralDelimiters:
174
178
  PreferredDelimiters:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # v0.6.0
2
+ - nicer argv handling
3
+ - add `argv_rules:` option, which resolves paths and considers everything that doesn't start with a `*` to start with a `/`
4
+ - combine the different includes methods and files using AND
5
+ - slightly more realistic version comparison just in case someone releases ruby 2.10
6
+ - can be run with --disable-gems
7
+ - `.allowed?` now more exactly matches `.each`, it returns false for directories and unreadable files.
8
+
1
9
  # v0.5.2
2
10
  - performance improvements
3
11
 
data/README.md CHANGED
@@ -66,6 +66,11 @@ FastIgnore.new(gitignore: '/absolute/path/to/.gitignore').to_a
66
66
  ```
67
67
  Note that the location of the .gitignore file will affect rules beginning with `/` or ending in `/**`
68
68
 
69
+ To raise if the gitignore file is not found use:
70
+ ```ruby
71
+ FastIgnore.new(gitignore: true)
72
+ ```
73
+
69
74
  To check if a single file is allowed, use
70
75
  ```ruby
71
76
  FastIgnore.new.allowed?('relative/path')
@@ -78,7 +83,7 @@ FastIgnore.new.allowed?('~/home/path')
78
83
 
79
84
  Building on the gitignore format, FastIgnore also accepts a list of allowed or included files.
80
85
 
81
- ```
86
+ ```gitignore
82
87
  # a line like this means any files named foo will be included
83
88
  # as well as any files within directories named foo
84
89
  foo
@@ -98,7 +103,32 @@ FastIgnore.new(include_files: '/absolute/path/to/my/include/file', gitignore: fa
98
103
  FastIgnore.new(include_rules: %w{my*rule /and/another !rule}, gitignore: false)
99
104
  ```
100
105
 
101
- 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.
106
+ There is an additional argument meant for dealing with humans and `ARGV` values.
107
+
108
+ ```ruby
109
+ FastIgnore.new(argv_rules: ['./a/pasted/path', '/or/a/path/from/stdin', 'an/argument', '*.txt'])
110
+ ```
111
+
112
+ It resolves absolute paths, and paths beginning with `~`, `../` and `./` (with and without `!`)
113
+ It assumes all rules are anchored unless they begin with `*` or `!*`.
114
+
115
+ Note: it will *not* resolve e.g. `/../` in the middle of a rule that doesn't begin with any of `~`,`../`,`./`,`/`.
116
+
117
+ ## Combinations
118
+
119
+ 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.
120
+
121
+ To combine files using `OR`, that is, a file may be included by either file it doesn't have to be referred to in both:
122
+
123
+ ```ruby
124
+ FastIgnore.new(include_files: StringIO.new([File.read('/my/path'), File.read('/another/path')]).join("\n"))
125
+ ```
126
+
127
+ To use the additional ARGV handling rules mentioned above for files
128
+
129
+ ```ruby
130
+ FastIgnore.new(include_rules: ["my/rule", File.read('/my/path')])
131
+ ```
102
132
 
103
133
  ## Known issues
104
134
  - Doesn't take into account project excludes in `.git/info/exclude`
data/bin/ls CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  require_relative '../lib/fast_ignore'
5
5
 
6
- puts FastIgnore.new(relative: true, include_rules: ARGV).to_a
6
+ puts FastIgnore.new(relative: true, argv_rules: ARGV).to_a
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby --disable-gems
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/fast_ignore'
5
+
6
+ puts FastIgnore.new(relative: true, argv_rules: ARGV).to_a
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ module Backports
5
+ module_function
6
+
7
+ def ruby_version_less_than?(major, minor)
8
+ ruby_major, ruby_minor = RUBY_VERSION.split('.', 2)
9
+
10
+ return true if major > ruby_major.to_i
11
+ return true if minor > ruby_minor.to_i
12
+
13
+ false
14
+ end
15
+ end
16
+ end
@@ -1,17 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'rule'
4
+ require_relative 'backports'
4
5
 
5
6
  class FastIgnore
6
7
  class RuleParser
7
- unless ::RUBY_VERSION >= '2.4'
8
- require_relative 'backports/match'
9
- using ::FastIgnore::Backports::Match
10
- end
11
-
12
- unless ::RUBY_VERSION >= '2.5'
8
+ if ::FastIgnore::Backports.ruby_version_less_than?(2, 5)
13
9
  require_relative 'backports/delete_prefix_suffix'
14
10
  using ::FastIgnore::Backports::DeletePrefixSuffix
11
+
12
+ if ::FastIgnore::Backports.ruby_version_less_than?(2, 4)
13
+ require_relative 'backports/match'
14
+ using ::FastIgnore::Backports::Match
15
+ end
15
16
  end
16
17
 
17
18
  # rule or nil
@@ -19,11 +20,11 @@ class FastIgnore
19
20
  def new_rule(rule, root:, rule_set:, allow: false, expand_path: false) # rubocop:disable Metrics/MethodLength
20
21
  rule = strip(rule)
21
22
  rule, dir_only = extract_dir_only(rule)
22
- rule = expand_path(rule, root) if expand_path
23
23
 
24
24
  return if skip?(rule)
25
25
 
26
26
  rule, negation = extract_negation(rule, allow)
27
+ rule = expand_path(rule, root) if expand_path
27
28
 
28
29
  anchored, prefix = prefix(rule)
29
30
 
@@ -79,23 +80,15 @@ class FastIgnore
79
80
  end
80
81
 
81
82
  def expand_path(rule, root)
82
- if rule.match?(%r{^(?:[~/]|\.{1,2}/)})
83
- ::File.expand_path(rule).delete_prefix(root)
84
- else
85
- rule
86
- end
87
- end
83
+ rule = ::File.expand_path(rule).delete_prefix(root) if rule.match?(%r{^(?:[~/]|\.{1,2}/)})
88
84
 
89
- def skip?(rule)
90
- empty?(rule) || comment?(rule)
91
- end
85
+ rule = "/#{rule}" unless rule.start_with?('*') || rule.start_with?('/')
92
86
 
93
- def empty?(rule)
94
- rule.empty?
87
+ rule
95
88
  end
96
89
 
97
- def comment?(rule)
98
- rule.start_with?('#')
90
+ def skip?(rule)
91
+ rule.empty? || rule.start_with?('#')
99
92
  end
100
93
  end
101
94
  end
@@ -12,8 +12,8 @@ class FastIgnore
12
12
  @project_root = project_root
13
13
 
14
14
  @any_not_anchored = false
15
- @empty = true
16
15
  @allow = allow
16
+ @default = true unless allow
17
17
  end
18
18
 
19
19
  def allowed_recursive?(path, dir)
@@ -30,35 +30,23 @@ class FastIgnore
30
30
  return rule.negation? if ::File.fnmatch?(rule.rule, path, 14)
31
31
  end
32
32
 
33
- default?(dir)
34
- end
35
-
36
- def parse_rules(rule_line, root: @root, expand_path: false)
37
- ::FastIgnore::RuleParser.new_rule(rule_line, rule_set: self, allow: @allow, root: root, expand_path: expand_path)
33
+ (not @allow) || (@any_not_anchored if dir)
38
34
  end
39
35
 
40
36
  def append_rules(anchored, rules)
41
37
  rules.each do |rule|
42
- @empty = false
43
38
  @rules << rule
44
39
  @non_dir_only_rules << rule unless rule.dir_only?
45
40
  @any_not_anchored ||= !anchored
46
41
  end
47
42
  end
48
43
 
49
- def clear_cache
50
- @allowed_recursive = nil
44
+ def length
45
+ @rules.length
51
46
  end
52
47
 
53
- private
54
-
55
- def default?(dir)
56
- return true unless @allow
57
- return true if @empty
58
- return false unless dir
59
- return true if @any_not_anchored
60
-
61
- false
48
+ def empty?
49
+ @rules.empty?
62
50
  end
63
51
  end
64
52
  end
@@ -1,37 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'rule_set'
4
+ require_relative 'rule_parser'
4
5
 
5
6
  class FastIgnore
6
7
  class RuleSetBuilder
7
- attr_reader :rule_set
8
+ def self.from_args( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
9
+ root: Dir.pwd,
10
+ ignore_rules: nil,
11
+ ignore_files: nil,
12
+ gitignore: :auto,
13
+ include_rules: nil,
14
+ include_files: nil,
15
+ argv_rules: nil
16
+ )
17
+ rule_sets = [
18
+ from_array(ignore_rules, root: root),
19
+ *from_files(ignore_files, project_root: root),
20
+ from_array('.git', root: root),
21
+ from_gitignore_arg(gitignore, root: root),
22
+ from_array(include_rules, root: root, allow: true),
23
+ *from_files(include_files, allow: true, project_root: root),
24
+ from_array(argv_rules, root: root, allow: true, expand_path: true)
25
+ ]
8
26
 
9
- def initialize(root:, allow: false)
10
- @rule_set = RuleSet.new(project_root: root, allow: allow)
11
- @root = root
12
- @allow = allow
27
+ rule_sets.compact!
28
+ rule_sets.reject!(&:empty?)
29
+ rule_sets.sort_by!(&:length)
30
+ rule_sets
13
31
  end
14
32
 
15
- def add_rules(rules, expand_path: false)
16
- rules.each do |rule_string|
17
- rule_string.each_line do |rule_line|
18
- @rule_set.parse_rules(rule_line, root: @root, expand_path: expand_path)
19
- end
33
+ def self.from_file(filename, allow: false, project_root: Dir.pwd)
34
+ filename = ::File.expand_path(filename)
35
+ root = ::File.dirname(filename)
36
+ rule_set = ::FastIgnore::RuleSet.new(project_root: project_root, allow: allow)
37
+
38
+ ::IO.foreach(filename) do |rule_string|
39
+ parse_rules(rule_string, allow: allow, rule_set: rule_set, root: root)
20
40
  end
21
41
 
22
- @rule_set.clear_cache
42
+ rule_set
23
43
  end
24
44
 
25
- def add_files(files)
26
- files.each do |filename|
27
- filename = ::File.expand_path(filename)
28
- root = ::File.dirname(filename)
29
- ::IO.foreach(filename) do |rule_string|
30
- @rule_set.parse_rules(rule_string, root: root)
45
+ def self.from_files(files, allow: false, project_root: Dir.pwd)
46
+ Array(files).map do |file|
47
+ from_file(file, allow: allow, project_root: project_root)
48
+ end
49
+ end
50
+
51
+ def self.from_gitignore_arg(gitignore, root: Dir.pwd) # rubocop:disable Metrics/MethodLength
52
+ default_path = ::File.join(root, '.gitignore')
53
+ case gitignore
54
+ when :auto
55
+ from_file(default_path, project_root: root) if ::File.exist?(default_path)
56
+ when true
57
+ from_file(default_path, project_root: root)
58
+ when false
59
+ nil
60
+ else
61
+ from_file(gitignore, project_root: root)
62
+ end
63
+ end
64
+
65
+ def self.from_array(rules, allow: false, expand_path: false, root: Dir.pwd)
66
+ rules = Array(rules)
67
+ return if rules.empty?
68
+
69
+ rule_set = ::FastIgnore::RuleSet.new(project_root: root, allow: allow)
70
+
71
+ rules.each_with_object(rule_set) do |rule_string, set|
72
+ rule_string.each_line do |rule_line|
73
+ parse_rules(rule_line, rule_set: set, allow: allow, root: root, expand_path: expand_path)
31
74
  end
32
75
  end
76
+ end
33
77
 
34
- @rule_set.clear_cache
78
+ def self.parse_rules(rule_line, rule_set:, allow: false, root: Dir.pwd, expand_path: false)
79
+ ::FastIgnore::RuleParser.new_rule(
80
+ rule_line,
81
+ rule_set: rule_set,
82
+ allow: allow,
83
+ root: root,
84
+ expand_path: expand_path
85
+ )
35
86
  end
36
87
  end
37
88
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.5.2'
4
+ VERSION = '0.6.0'
5
5
  end
data/lib/fast_ignore.rb CHANGED
@@ -1,69 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './fast_ignore/rule_set_builder'
4
+ require_relative './fast_ignore/backports'
4
5
 
5
6
  class FastIgnore
6
7
  include ::Enumerable
7
8
 
8
- unless ::RUBY_VERSION >= '2.5'
9
+ if ::FastIgnore::Backports.ruby_version_less_than?(2, 5)
9
10
  require_relative 'fast_ignore/backports/delete_prefix_suffix'
10
11
  using ::FastIgnore::Backports::DeletePrefixSuffix
11
- end
12
12
 
13
- unless ::RUBY_VERSION >= '2.5'
14
13
  require_relative 'fast_ignore/backports/dir_each_child'
15
14
  using ::FastIgnore::Backports::DirEachChild
16
15
  end
17
16
 
18
- def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength, Metrics/AbcSize
17
+ def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
19
18
  relative: false,
20
19
  root: ::Dir.pwd,
21
20
  ignore_rules: nil,
22
21
  ignore_files: nil,
23
- gitignore: ::File.join(root, '.gitignore'),
22
+ gitignore: :auto,
24
23
  include_rules: nil,
25
- include_files: nil
24
+ include_files: nil,
25
+ argv_rules: nil
26
26
  )
27
27
  @root = root.delete_suffix('/')
28
28
  @root_trailing_slash = "#{@root}/"
29
- ignore = ::FastIgnore::RuleSetBuilder.new(root: @root)
30
- only = ::FastIgnore::RuleSetBuilder.new(allow: true, root: @root)
31
- only.add_files(Array(include_files))
32
- only.add_rules(Array(include_rules), expand_path: true)
33
- @only = only.rule_set
34
29
 
35
- ignore.add_rules(['.git'])
36
- ignore.add_files([gitignore]) if gitignore && ::File.exist?(gitignore)
37
- ignore.add_files(Array(ignore_files))
38
- ignore.add_rules(Array(ignore_rules))
39
- @ignore = ignore.rule_set
30
+ @rule_sets = ::FastIgnore::RuleSetBuilder.from_args(
31
+ root: @root,
32
+ ignore_rules: ignore_rules,
33
+ ignore_files: ignore_files,
34
+ gitignore: gitignore,
35
+ include_rules: include_rules,
36
+ include_files: include_files,
37
+ argv_rules: argv_rules
38
+ )
39
+
40
40
  @relative = relative
41
41
  end
42
42
 
43
43
  def each(&block)
44
44
  if block_given?
45
- all_allowed(&block)
45
+ each_allowed(&block)
46
46
  else
47
- enum_for(:all_allowed)
47
+ enum_for(:each_allowed)
48
48
  end
49
49
  end
50
50
 
51
51
  def allowed?(path)
52
52
  path = ::File.expand_path(path)
53
- dir = ::File.directory?(path)
54
- @ignore.allowed_recursive?(path, dir) && @only.allowed_recursive?(path, dir)
55
- end
56
-
57
- def all_allowed
58
- find_children(@root_trailing_slash) do |path, dir|
59
- next false unless @ignore.allowed_unrecursive?(path, dir)
60
- next false unless @only.allowed_unrecursive?(path, dir)
61
- next true if dir
53
+ stat = ::File.stat(path)
54
+ dir = stat.directory?
55
+ return false unless stat.readable?
56
+ return false if dir
62
57
 
63
- yield prepare_path(path)
64
-
65
- false
66
- end
58
+ @rule_sets.all? { |r| r.allowed_recursive?(path, dir) }
59
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
60
+ false
67
61
  end
68
62
 
69
63
  private
@@ -72,15 +66,21 @@ class FastIgnore
72
66
  @relative ? path.delete_prefix(@root_trailing_slash) : path
73
67
  end
74
68
 
75
- def find_children(path, &block) # rubocop:disable Metrics/MethodLength
69
+ def each_allowed(path = @root_trailing_slash, &block) # rubocop:disable Metrics/MethodLength
76
70
  Dir.each_child(path) do |child|
77
71
  begin
78
72
  child = path + child
79
73
  stat = ::File.stat(child)
80
74
  next unless stat.readable?
81
75
 
82
- look_at_children = block.call child, stat.directory?
83
- find_children("#{child}/", &block) if look_at_children
76
+ dir = stat.directory?
77
+ next unless @rule_sets.all? { |r| r.allowed_unrecursive?(child, dir) }
78
+
79
+ if dir
80
+ each_allowed("#{child}/", &block)
81
+ else
82
+ yield prepare_path(child)
83
+ end
84
84
  rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
85
85
  nil
86
86
  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.5.2
4
+ version: 0.6.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-02-20 00:00:00.000000000 Z
11
+ date: 2020-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -112,9 +112,11 @@ files:
112
112
  - Rakefile
113
113
  - bin/console
114
114
  - bin/ls
115
+ - bin/ls-disable-gems
115
116
  - bin/setup
116
117
  - fast_ignore.gemspec
117
118
  - lib/fast_ignore.rb
119
+ - lib/fast_ignore/backports.rb
118
120
  - lib/fast_ignore/backports/delete_prefix_suffix.rb
119
121
  - lib/fast_ignore/backports/dir_each_child.rb
120
122
  - lib/fast_ignore/backports/match.rb