fast_ignore 0.5.2 → 0.6.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: 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