fast_ignore 0.7.0 → 0.8.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: 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