fast_ignore 0.7.0 → 0.8.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: dc58ddc21668f583d8d2866415dd2654e20b6656c0efd959748be97c69e11018
4
- data.tar.gz: 75b23ef5a9143a398ad68350c0a1700a2566e4cebd4311099d991e0b202a4af9
3
+ metadata.gz: 58d10e2bb7f504f6ff41d4ad3fbea902df56aa785d45a78e160a40ea5795a1a0
4
+ data.tar.gz: 16a77bf6dbdbb76f8e8f08793dd5a81d21147dcf9d77e72c4403de377025d4f3
5
5
  SHA512:
6
- metadata.gz: 963cb997dcdb9f97dd9afdf7cea6eef408a0c225279bb80932e26cb3f495517e28bdd883cf1d45b66806efd39223775d719bd887a39fd6617575535d7ee528de
7
- data.tar.gz: cef7f0d29b7715793d53519df4000fbf1dca04580e1df49ec8a996e96c092f110ac14d9729161e7ad5ba6a2dcc60108a642b30a9888e2271e1cbb8e011f2ac91
6
+ metadata.gz: 1de15ac6a8ce01bd18077bc402026cddbfeae82e33bbbd1e0e587d1ff3d898ac0fa43abc211bcf007cf2cec7c1fe00c2b60375760a767818330efc4164b2bf67
7
+ data.tar.gz: 7daed034a210e0f56be064400e7264c182f3abaeda13cd857b9d81f5fc6dc8c397195d087dc539a03f21787f65f24e39e87c4a5d182c419c80b00e5848148756
data/.simplecov ADDED
@@ -0,0 +1,6 @@
1
+ SimpleCov.start do
2
+ add_filter '/backports'
3
+ add_filter '/spec/'
4
+ enable_coverage(:branch)
5
+ minimum_coverage 100
6
+ end
data/CHANGELOG.md CHANGED
@@ -1,8 +1,11 @@
1
- # v0.7.0
1
+ # v0.8.0
2
2
  - drop support for ruby 2.3. My plan is to only support supported ruby versions
3
3
  - add coverage to the pipeline. removed some methods, added some tests, and now we have 100% test coverage
4
4
  - deprecate using `gitignore: '/path/to/gitignore'`. please use `gitignore: false, ignore_files: '/path/to/gitignore'` instead.
5
5
 
6
+ # v0.7.0
7
+ - add `include_shebang:` which filters by shebangs
8
+
6
9
  # v0.6.0
7
10
  - nicer argv handling
8
11
  - add `argv_rules:` option, which resolves paths and considers everything that doesn't start with a `*` to start with a `/`
data/README.md CHANGED
@@ -71,7 +71,12 @@ Note that the location of the .gitignore file will affect rules beginning with `
71
71
 
72
72
  To raise an `Errno::ENOENT` error if the .gitignore file is not found use:
73
73
  ```ruby
74
- FastIgnore.new(gitignore: true)
74
+ FastIgnore.new(gitignore: true).to_a
75
+ ```
76
+
77
+ To filter by extensionless files shebang/hashbang/etc:
78
+ ```ruby
79
+ FastIgnore.new(include_rules: '*.rb', include_shebangs: 'ruby').to_a
75
80
  ```
76
81
 
77
82
  To check if a single file is allowed, use
data/bin/ls-shebangs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/fast_ignore'
5
+
6
+ puts FastIgnore.new(relative: true, include_shebangs: ARGV).to_a
data/fast_ignore.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_development_dependency 'bundler', '>= 1.17'
33
33
  spec.add_development_dependency 'pry', '> 0'
34
- spec.add_development_dependency 'rake', '~> 10.0'
34
+ spec.add_development_dependency 'rake', '>= 12.3.3'
35
35
  spec.add_development_dependency 'rspec', '~> 3.0'
36
36
  spec.add_development_dependency 'rubocop', '>= 0.74.0'
37
37
  spec.add_development_dependency 'rubocop-rspec', '~> 1'
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'rule'
4
- require_relative 'backports'
5
-
6
3
  class FastIgnore
7
4
  class RuleParser
8
5
  # :nocov:
@@ -1,31 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'rule_parser'
4
-
5
3
  class FastIgnore
6
4
  class RuleSet
7
5
  attr_reader :rules
6
+ attr_reader :allow
7
+ alias_method :allow?, :allow
8
8
 
9
- def initialize(project_root: Dir.pwd, allow: false)
10
- @rules = []
11
- @non_dir_only_rules = []
9
+ def initialize(project_root: Dir.pwd, allow: false, and_no_ext: false)
10
+ @dir_rules = []
11
+ @file_rules = []
12
12
  @project_root = project_root
13
+ @and_no_ext = and_no_ext
13
14
 
14
15
  @any_not_anchored = false
15
16
  @allow = allow
16
17
  @default = true unless allow
17
18
  end
18
19
 
19
- def allowed_recursive?(path, dir)
20
+ def allowed_recursive?(path, dir, basename)
20
21
  @allowed_recursive ||= { @project_root => true }
21
22
  @allowed_recursive.fetch(path) do
22
23
  @allowed_recursive[path] =
23
- allowed_recursive?(::File.dirname(path), true) && allowed_unrecursive?(path, dir)
24
+ allowed_recursive?(::File.dirname(path), true, nil) && allowed_unrecursive?(path, dir, basename)
24
25
  end
25
26
  end
26
27
 
27
- def allowed_unrecursive?(path, dir)
28
- (dir ? @rules : @non_dir_only_rules).reverse_each do |rule|
28
+ def allowed_unrecursive?(path, dir, basename)
29
+ if @and_no_ext
30
+ return true if dir
31
+ return true unless basename&.include?('.')
32
+ end
33
+
34
+ (dir ? @dir_rules : @file_rules).reverse_each do |rule|
29
35
  # 14 = Rule::FNMATCH_OPTIONS
30
36
  return rule.negation? if ::File.fnmatch?(rule.rule, path, 14)
31
37
  end
@@ -35,18 +41,18 @@ class FastIgnore
35
41
 
36
42
  def append_rules(anchored, rules)
37
43
  rules.each do |rule|
38
- @rules << rule
39
- @non_dir_only_rules << rule unless rule.dir_only?
44
+ @dir_rules << rule
45
+ @file_rules << rule unless rule.dir_only?
40
46
  @any_not_anchored ||= !anchored
41
47
  end
42
48
  end
43
49
 
44
50
  def length
45
- @rules.length
51
+ @dir_rules.length
46
52
  end
47
53
 
48
54
  def empty?
49
- @rules.empty?
55
+ @dir_rules.empty?
50
56
  end
51
57
  end
52
58
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'rule_set'
4
- require_relative 'rule_parser'
5
-
6
3
  class FastIgnore
7
4
  class RuleSetBuilder
8
5
  def self.from_args( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
@@ -12,28 +9,32 @@ class FastIgnore
12
9
  gitignore: :auto,
13
10
  include_rules: nil,
14
11
  include_files: nil,
15
- argv_rules: nil
12
+ argv_rules: nil,
13
+ and_no_ext: false
16
14
  )
17
15
  rule_sets = [
18
16
  from_array(ignore_rules, root: root),
19
17
  *from_files(ignore_files, project_root: root),
20
18
  from_array('.git', root: root),
21
19
  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)
20
+ from_array(include_rules, root: root, allow: true, and_no_ext: and_no_ext),
21
+ *from_files(include_files, allow: true, project_root: root, and_no_ext: and_no_ext),
22
+ from_array(argv_rules, root: root, allow: true, expand_path: true, and_no_ext: and_no_ext)
25
23
  ]
26
24
 
27
25
  rule_sets.compact!
28
26
  rule_sets.reject!(&:empty?)
27
+ if and_no_ext && rule_sets.none?(&:allow?)
28
+ rule_sets << ::FastIgnore::RuleSet.new(project_root: root, allow: true, and_no_ext: and_no_ext)
29
+ end
29
30
  rule_sets.sort_by!(&:length)
30
31
  rule_sets
31
32
  end
32
33
 
33
- def self.from_file(filename, allow: false, project_root: Dir.pwd)
34
+ def self.from_file(filename, allow: false, project_root: Dir.pwd, and_no_ext: false)
34
35
  filename = ::File.expand_path(filename)
35
36
  root = ::File.dirname(filename)
36
- rule_set = ::FastIgnore::RuleSet.new(project_root: project_root, allow: allow)
37
+ rule_set = ::FastIgnore::RuleSet.new(project_root: project_root, allow: allow, and_no_ext: and_no_ext)
37
38
 
38
39
  ::IO.foreach(filename) do |rule_string|
39
40
  parse_rules(rule_string, allow: allow, rule_set: rule_set, root: root)
@@ -42,9 +43,9 @@ class FastIgnore
42
43
  rule_set
43
44
  end
44
45
 
45
- def self.from_files(files, allow: false, project_root: Dir.pwd)
46
+ def self.from_files(files, allow: false, project_root: Dir.pwd, and_no_ext: false)
46
47
  Array(files).map do |file|
47
- from_file(file, allow: allow, project_root: project_root)
48
+ from_file(file, allow: allow, project_root: project_root, and_no_ext: and_no_ext)
48
49
  end
49
50
  end
50
51
 
@@ -64,11 +65,11 @@ class FastIgnore
64
65
  end
65
66
  end
66
67
 
67
- def self.from_array(rules, allow: false, expand_path: false, root: Dir.pwd)
68
+ def self.from_array(rules, allow: false, expand_path: false, root: Dir.pwd, and_no_ext: false)
68
69
  rules = Array(rules)
69
70
  return if rules.empty?
70
71
 
71
- rule_set = ::FastIgnore::RuleSet.new(project_root: root, allow: allow)
72
+ rule_set = ::FastIgnore::RuleSet.new(project_root: root, allow: allow, and_no_ext: and_no_ext)
72
73
 
73
74
  rules.each_with_object(rule_set) do |rule_string, set|
74
75
  rule_string.each_line do |rule_line|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.7.0'
4
+ VERSION = '0.8.0'
5
5
  end
data/lib/fast_ignore.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './fast_ignore/rule_set_builder'
4
3
  require_relative './fast_ignore/backports'
5
4
 
5
+ require_relative './fast_ignore/rule_parser'
6
+ require_relative './fast_ignore/rule_set_builder'
7
+ require_relative './fast_ignore/rule_set'
8
+ require_relative './fast_ignore/rule'
9
+
6
10
  class FastIgnore
7
11
  include ::Enumerable
8
12
 
@@ -24,10 +28,12 @@ class FastIgnore
24
28
  gitignore: :auto,
25
29
  include_rules: nil,
26
30
  include_files: nil,
31
+ include_shebangs: nil,
27
32
  argv_rules: nil
28
33
  )
29
34
  @root = root.delete_suffix('/')
30
35
  @root_trailing_slash = "#{@root}/"
36
+ @shebang_pattern = prepare_shebang_pattern(include_shebangs)
31
37
 
32
38
  @rule_sets = ::FastIgnore::RuleSetBuilder.from_args(
33
39
  root: @root,
@@ -36,7 +42,8 @@ class FastIgnore
36
42
  gitignore: gitignore,
37
43
  include_rules: include_rules,
38
44
  include_files: include_files,
39
- argv_rules: argv_rules
45
+ argv_rules: argv_rules,
46
+ and_no_ext: @shebang_pattern
40
47
  )
41
48
 
42
49
  @relative = relative
@@ -56,7 +63,9 @@ class FastIgnore
56
63
  dir = stat.directory?
57
64
  return false if dir
58
65
 
59
- @rule_sets.all? { |r| r.allowed_recursive?(path, dir) }
66
+ basename = ::File.basename(path)
67
+
68
+ @rule_sets.all? { |r| r.allowed_recursive?(path, dir, basename) } && match_shebang?(path, basename)
60
69
  rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
61
70
  false
62
71
  end
@@ -68,17 +77,22 @@ class FastIgnore
68
77
  end
69
78
 
70
79
  def each_allowed(path = @root_trailing_slash, &block) # rubocop:disable Metrics/MethodLength
71
- Dir.each_child(path) do |child|
80
+ Dir.each_child(path) do |basename|
72
81
  begin
73
- child = path + child
82
+ child = path + basename
74
83
  stat = ::File.stat(child)
75
84
 
76
85
  dir = stat.directory?
77
- next unless @rule_sets.all? { |r| r.allowed_unrecursive?(child, dir) }
78
86
 
79
87
  if dir
88
+ next unless @rule_sets.all? { |r| r.allowed_unrecursive?(child, dir, nil) }
89
+
80
90
  each_allowed("#{child}/", &block)
81
91
  else
92
+ unless @rule_sets.all? { |r| r.allowed_unrecursive?(child, dir, basename) } && match_shebang?(child, basename)
93
+ next
94
+ end
95
+
82
96
  yield prepare_path(child)
83
97
  end
84
98
  rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
@@ -86,4 +100,26 @@ class FastIgnore
86
100
  end
87
101
  end
88
102
  end
103
+
104
+ def match_shebang?(path, basename)
105
+ return true unless @shebang_pattern
106
+ return true if basename.include?('.')
107
+
108
+ begin
109
+ f = ::File.new(path)
110
+ # i can't imagine a shebang being longer than 20 characters, lets multiply that by 10 just in case.
111
+ fragment = f.sysread(256)
112
+ f.close
113
+ rescue SystemCallError, EOFError
114
+ return
115
+ end
116
+
117
+ @shebang_pattern.match?(fragment)
118
+ end
119
+
120
+ def prepare_shebang_pattern(rules)
121
+ return if !rules || rules.empty?
122
+
123
+ /\A#!.*\b(?:#{rules.map { |s| Regexp.escape(s.to_s) }.join('|')})\b/.freeze
124
+ end
89
125
  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.7.0
4
+ version: 0.8.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-28 00:00:00.000000000 Z
11
+ date: 2020-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: 12.3.3
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: 12.3.3
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -118,6 +118,7 @@ files:
118
118
  - ".gitignore"
119
119
  - ".rspec"
120
120
  - ".rubocop.yml"
121
+ - ".simplecov"
121
122
  - ".travis.yml"
122
123
  - CHANGELOG.md
123
124
  - Gemfile
@@ -127,6 +128,7 @@ files:
127
128
  - bin/console
128
129
  - bin/ls
129
130
  - bin/ls-disable-gems
131
+ - bin/ls-shebangs
130
132
  - bin/setup
131
133
  - fast_ignore.gemspec
132
134
  - lib/fast_ignore.rb