fast_ignore 0.10.2 → 0.11.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: 2dd945c76bf226766ac86086faf839eac44c4c0cce452e2a3a44e7c47964b7f5
4
- data.tar.gz: a72de31878713db71c94aa8067c87b807f19833eb008a1a6d6e673fcdeff5a7a
3
+ metadata.gz: f467113b0ee03faf028758ac176c59b3e51465684f8ffe144d432ba75133c10f
4
+ data.tar.gz: 2f6f0ce0babec00977a55595baa7ec806d7a9a3f1fec86df46f29475b41238cc
5
5
  SHA512:
6
- metadata.gz: cff75a6418ecc958f2a839b9617554e3857763611248063aef27ec244dd0d1b433d61b81bfdb85a74dd8497fea8599f146ae723fd76023aef72285f53a620ecd
7
- data.tar.gz: 39aeceb2cd078c8b873019b9763eccb01450e408a24c3fb04005f9451879e1a125bcd73f26a1c4e67e6c999e7d6d0f78b4a1b41abbc0a2650ec36bb1ac7adde6
6
+ metadata.gz: 3a7698e54ab0897b6b50c8638d41f3243bd3ba4ced559a5c7e01f6d365b6fb875b906a1924e96641572815748479eb793dfabc91da6f3bec62115f98ff39a392
7
+ data.tar.gz: 7e8b85ff838d04d1c16cd3eca5a480d6948342a07667760782840b0e36a706c02db2bb4e09e3370b3ccc1cdc2f38f9e893707c9f2128652a8a4aae4cdb8dd3d0
@@ -10,8 +10,11 @@ enoent
10
10
  enumerables
11
11
  env
12
12
  errno
13
+ esque
13
14
  extensionless
14
15
  fancyignore
16
+ filesystem
17
+ fnmatch
15
18
  frotz
16
19
  gemfile
17
20
  github
@@ -34,6 +37,7 @@ rspec
34
37
  rubo
35
38
  rubocop
36
39
  rubygems
40
+ rubyish
37
41
  rulesets
38
42
  rvm
39
43
  sherson
@@ -2,6 +2,7 @@
2
2
  sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
+ before_install: gem install bundler
5
6
  rvm:
6
7
  - 2.4
7
8
  - 2.5
@@ -1,3 +1,7 @@
1
+ # v0.11.0
2
+ - major performance improvement (use regexp rather than fnmatch)
3
+ - optionally pass directory: and content: into allowed? if these are already loaded.
4
+
1
5
  # v0.10.2
2
6
  - add FastIgnore#=== as an alias for FastIgnore#allowed? so that FastIgnore objects can be used for case statements.
3
7
  - Fix shebangs in non-pwd-root situations
data/README.md CHANGED
@@ -11,6 +11,16 @@ Filter a directory tree using a .gitignore file. Recognises all of the [gitignor
11
11
  FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
12
12
  ```
13
13
 
14
+ ## Features
15
+
16
+ - Fast (faster than using `` `git ls-files`.split("\n") `` for small repos (because it avoids the overhead of ``` `` ```))
17
+ - Supports ruby 2.4 - 2.7
18
+ - supports all gitignore rule patterns
19
+ - doesn't require git to be installed
20
+ - supports a gitignore-esque "include" patterns. (`include_rules:`/`include_files:`)
21
+ - supports an expansion of include patterns, matching expanded paths (`argv_rules:`)
22
+ - supports matching by shebang rather than filename for extensionless files: `#!:`
23
+
14
24
  ## Installation
15
25
 
16
26
  Add this line to your application's Gemfile:
@@ -62,7 +72,8 @@ FastIgnore.new.allowed?('~/home/path')
62
72
  This is aliased as `===` so you can use the FastIgnore object in case statements.
63
73
  ```ruby
64
74
  case my_path
65
- when FastIgnore.new then puts my_path
75
+ when FastIgnore.new
76
+ puts(my_path)
66
77
  end
67
78
  ```
68
79
 
@@ -214,6 +225,15 @@ FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
214
225
 
215
226
  This does unfortunately lose the file path as the root for `/` and `/**` rules.
216
227
 
228
+ ### optimising #allowed?
229
+
230
+ To avoid unnecessary calls to the filesystem, if your code already knows whether or not it's a directory, or if you're checking shebangs and you have already read the content of the file: use
231
+ ```ruby
232
+ FastIgnore.new.allowed?('relative/path', directory: false, content: "#!/usr/bin/ruby\n\nputs 'ok'\n")
233
+ ```
234
+ This is not required, and if FastIgnore does have to go to the filesystem for this information it's well optimised to only read what is necessary.
235
+
236
+
217
237
  ## Known issues
218
238
  - Doesn't take into account project excludes in `.git/info/exclude`
219
239
  - Doesn't take into account globally ignored files in `git config core.excludesFile`.
@@ -224,9 +244,13 @@ This does unfortunately lose the file path as the root for `/` and `/**` rules.
224
244
 
225
245
  ## Development
226
246
 
227
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
247
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests and linters.
248
+
249
+ You can run `bin/console` for an interactive prompt that will allow you to experiment.
250
+ `bin/ls [argv_rules]` will return something equivalent to `git ls-files` and `bin/time [argv_rules]` will give you the average time for 30 runs.
251
+ This repo is too small to stress bin/time more than 0.01s, switch to a large repo and find the average time before and after changes.
228
252
 
229
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
253
+ To install this gem onto your local machine, run `bundle exec rake install`.
230
254
 
231
255
  ## Contributing
232
256
 
data/bin/time CHANGED
@@ -5,17 +5,17 @@
5
5
  # ensure nothing else is watching that dir in the filesystem e.g. webpack
6
6
 
7
7
  require 'open3'
8
+ require 'shellwords'
8
9
  RUNS = 30
9
- SCRIPT = "time #{__dir__}/ls"
10
- Dir.chdir ARGV[0] do
11
- times = Array.new(RUNS).map do
12
- run_times = Open3.capture3(SCRIPT)[1]
13
- puts run_times.lstrip
14
- run_times.scan(/(?:\d+(?:.\d+)?)/)
15
- end
10
+ SCRIPT = "time #{__dir__}/ls #{Shellwords.join(ARGV)}"
16
11
 
17
- puts format(
18
- "\e[1mAverage:\n\e[32m%0.2f real %0.2f user %0.2f sys\e[0m", # rubocop:disable Style/FormatStringToken
19
- *times.transpose.map { |n| (n.map(&:to_f).sum / RUNS) }
20
- )
12
+ times = Array.new(RUNS).map do
13
+ run_times = Open3.capture3(SCRIPT)[1]
14
+ puts run_times.lstrip
15
+ run_times.scan(/(?:\d+(?:.\d+)?)/)
21
16
  end
17
+
18
+ puts format(
19
+ "\e[1mAverage:\n\e[32m%0.2f real %0.2f user %0.2f sys\e[0m", # rubocop:disable Style/FormatStringToken
20
+ *times.transpose.map { |n| (n.map(&:to_f).sum / RUNS) }
21
+ )
@@ -7,6 +7,7 @@ require_relative './fast_ignore/rule_builder'
7
7
  require_relative './fast_ignore/rule_set'
8
8
  require_relative './fast_ignore/rule'
9
9
  require_relative './fast_ignore/shebang_rule'
10
+ require_relative './fast_ignore/fn_match_to_re'
10
11
 
11
12
  class FastIgnore
12
13
  class Error < StandardError; end
@@ -14,13 +15,8 @@ class FastIgnore
14
15
  include ::Enumerable
15
16
 
16
17
  # :nocov:
17
- if ::FastIgnore::Backports.ruby_version_less_than?(2, 5)
18
- require_relative 'fast_ignore/backports/delete_prefix_suffix'
19
- using ::FastIgnore::Backports::DeletePrefixSuffix
20
-
21
- require_relative 'fast_ignore/backports/dir_each_child'
22
- using ::FastIgnore::Backports::DirEachChild
23
- end
18
+ using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
19
+ using ::FastIgnore::Backports::DirEachChild if defined?(::FastIgnore::Backports::DirEachChild)
24
20
  # :nocov:
25
21
 
26
22
  def initialize(relative: false, root: nil, follow_symlinks: false, **rule_set_builder_args)
@@ -41,23 +37,15 @@ class FastIgnore
41
37
  each_recursive(root_from_pwd, '', &block)
42
38
  end
43
39
 
44
- def directory?(path)
45
- if @follow_symlinks
46
- ::File.stat(path).directory?
47
- else
48
- ::File.lstat(path).directory?
49
- end
50
- end
51
-
52
- def allowed?(path)
40
+ def allowed?(path, directory: nil, content: nil)
53
41
  full_path = ::File.expand_path(path, @root)
54
42
  return false unless full_path.start_with?(@root)
55
- return false if directory?(full_path)
43
+ return false if directory.nil? ? directory?(full_path) : directory
56
44
 
57
45
  relative_path = full_path.delete_prefix(@root)
58
46
  filename = ::File.basename(relative_path)
59
47
 
60
- @rule_sets.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename) }
48
+ @rule_sets.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename, content) }
61
49
  rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
62
50
  false
63
51
  end
@@ -65,6 +53,14 @@ class FastIgnore
65
53
 
66
54
  private
67
55
 
56
+ def directory?(path)
57
+ if @follow_symlinks
58
+ ::File.stat(path).directory?
59
+ else
60
+ ::File.lstat(path).directory?
61
+ end
62
+ end
63
+
68
64
  def each_recursive(parent_full_path, parent_relative_path, &block) # rubocop:disable Metrics/MethodLength
69
65
  ::Dir.each_child(parent_full_path) do |filename|
70
66
  begin
@@ -72,7 +68,7 @@ class FastIgnore
72
68
  relative_path = parent_relative_path + filename
73
69
  dir = directory?(full_path)
74
70
 
75
- next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename) }
71
+ next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename, nil) }
76
72
 
77
73
  if dir
78
74
  each_recursive(full_path + '/', relative_path + '/', &block)
@@ -2,15 +2,69 @@
2
2
 
3
3
  class FastIgnore
4
4
  module Backports
5
- module_function
5
+ ruby_major, ruby_minor = RUBY_VERSION.split('.', 2)
6
+ unless ruby_major.to_i > 2 || ruby_major.to_i == 2 && ruby_minor.to_i > 5
7
+ module DirEachChild
8
+ refine ::Dir.singleton_class do
9
+ def each_child(path, &block)
10
+ Dir.entries(path).each do |entry|
11
+ next if entry == '.' || entry == '..'
6
12
 
7
- def ruby_version_less_than?(major, minor)
8
- ruby_major, ruby_minor = RUBY_VERSION.split('.', 2)
13
+ block.call entry
14
+ end
15
+ end
16
+ end
17
+ end
9
18
 
10
- return true if major > ruby_major.to_i
11
- return true if minor > ruby_minor.to_i
19
+ module DeletePrefixSuffix
20
+ refine ::String do
21
+ # delete_prefix!(prefix) -> self or nil
22
+ # Deletes leading prefix from str, returning nil if no change was made.
23
+ #
24
+ # "hello".delete_prefix!("hel") #=> "lo"
25
+ # "hello".delete_prefix!("llo") #=> nil
26
+ def delete_prefix!(str)
27
+ return unless start_with?(str)
12
28
 
13
- false
29
+ slice!(0..(str.length - 1))
30
+ self
31
+ end
32
+
33
+ # delete_suffix!(suffix) -> self or nil
34
+ # Deletes trailing suffix from str, returning nil if no change was made.
35
+ #
36
+ # "hello".delete_suffix!("llo") #=> "he"
37
+ # "hello".delete_suffix!("hel") #=> nil
38
+ def delete_suffix!(str)
39
+ return unless end_with?(str)
40
+
41
+ slice!(-str.length..-1)
42
+ self
43
+ end
44
+
45
+ # delete_prefix(prefix) -> new_str click to toggle source
46
+ # Returns a copy of str with leading prefix deleted.
47
+ #
48
+ # "hello".delete_prefix("hel") #=> "lo"
49
+ # "hello".delete_prefix("llo") #=> "hello"
50
+ def delete_prefix(str)
51
+ s = dup
52
+ s.delete_prefix!(str)
53
+ s
54
+ end
55
+
56
+ # delete_suffix(suffix) -> new_str
57
+ # Returns a copy of str with trailing suffix deleted.
58
+ #
59
+ # "hello".delete_suffix("llo") #=> "he"
60
+ # "hello".delete_suffix("hel") #=> "hello"
61
+ def delete_suffix(str) # leftovers:allowed
62
+ s = dup
63
+ s.delete_suffix!(str)
64
+ s
65
+ end
66
+ end
67
+ end
14
68
  end
15
69
  end
16
70
  end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ module FNMatchToRegex
5
+ # This doesn't look rubyish because i ported it from rust (the only rust i ever wrote that worked)
6
+ class << self
7
+ def call(pattern) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
8
+ re = '\\A'.dup
9
+
10
+ in_character_group = false
11
+ has_characters_in_group = false
12
+ escape_next_character = false
13
+ last_char_opened_character_group = false
14
+ negated_character_group = false
15
+ stars = 0
16
+
17
+ pattern.each_char do |char| # rubocop:disable Metrics/BlockLength
18
+ if escape_next_character
19
+ re << Regexp.escape(char)
20
+ escape_next_character = false
21
+ elsif char == '\\' # single char, just needs to be escaped
22
+ escape_next_character = true
23
+ elsif in_character_group
24
+ if char == '/'
25
+ if negated_character_group
26
+ has_characters_in_group = true
27
+ re << char
28
+ end
29
+ elsif char == '^'
30
+ if last_char_opened_character_group
31
+ re << char
32
+ negated_character_group = true
33
+ else
34
+ re << '\\^'
35
+ has_characters_in_group = true
36
+ end
37
+ # not characters in group
38
+ elsif char == ']'
39
+ break unless has_characters_in_group
40
+
41
+ re << ']'
42
+ in_character_group = false
43
+ has_characters_in_group = false
44
+ negated_character_group = false
45
+ last_char_opened_character_group = false
46
+ elsif char == '-'
47
+ has_characters_in_group = true
48
+ re << char
49
+ else
50
+ has_characters_in_group = true
51
+ re << Regexp.escape(char)
52
+ end
53
+ last_char_opened_character_group = false
54
+ elsif char == '*'
55
+ stars += 1
56
+ elsif char == '/'
57
+ re << if stars >= 2
58
+ '(?:.*/)?'
59
+ elsif stars.positive?
60
+ '[^/]*/'
61
+ else
62
+ char
63
+ end
64
+ stars = 0
65
+ else
66
+ if stars.positive?
67
+ re << '[^/]*'
68
+ stars = 0
69
+ end
70
+ if char == '?'
71
+ re << '[^/]'
72
+ elsif char == '['
73
+ re << '['
74
+ in_character_group = true
75
+ last_char_opened_character_group = true
76
+ else
77
+ re << Regexp.escape(char)
78
+ end
79
+ end
80
+ end
81
+
82
+ if in_character_group
83
+ return /(?!)/ # impossible to match anything
84
+ end
85
+
86
+ if stars >= 2
87
+ re << '.*'
88
+ elsif stars.positive?
89
+ re << '[^/]*'
90
+ end
91
+ re << '\\z'
92
+ Regexp.new(re, Regexp::IGNORECASE)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -2,12 +2,6 @@
2
2
 
3
3
  class FastIgnore
4
4
  class Rule
5
- # FNMATCH_OPTIONS = (
6
- # ::File::FNM_DOTMATCH |
7
- # ::File::FNM_PATHNAME |
8
- # ::File::FNM_CASEFOLD
9
- # ).freeze # = 14
10
-
11
5
  attr_reader :negation
12
6
  alias_method :negation?, :negation
13
7
  undef :negation
@@ -20,12 +14,16 @@ class FastIgnore
20
14
  alias_method :unanchored?, :unanchored
21
15
  undef :unanchored
22
16
 
23
- def initialize(rule, unanchored, dir_only, negation)
24
- @rule = rule
17
+ attr_reader :type
18
+ attr_reader :rule
19
+
20
+ def initialize(rule, negation, unanchored = nil, dir_only = nil)
21
+ @rule = rule.is_a?(Regexp) ? rule : ::FastIgnore::FNMatchToRegex.call(rule)
25
22
  @unanchored = unanchored
26
23
  @dir_only = dir_only
27
24
  @negation = negation
28
- @shebang = shebang
25
+
26
+ @type = negation ? 1 : 0
29
27
 
30
28
  freeze
31
29
  end
@@ -44,8 +42,8 @@ class FastIgnore
44
42
  end
45
43
  # :nocov:
46
44
 
47
- def match?(relative_path, _, _)
48
- ::File.fnmatch?(@rule, relative_path, 14)
45
+ def match?(relative_path, _, _, _)
46
+ @rule.match?(relative_path)
49
47
  end
50
48
  end
51
49
  end
@@ -4,10 +4,7 @@ class FastIgnore
4
4
  module RuleBuilder
5
5
  class << self
6
6
  # :nocov:
7
- if ::FastIgnore::Backports.ruby_version_less_than?(2, 5)
8
- require_relative 'backports/delete_prefix_suffix'
9
- using ::FastIgnore::Backports::DeletePrefixSuffix
10
- end
7
+ using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
11
8
  # :nocov:
12
9
 
13
10
  def build(rule, allow, expand_path, file_root)
@@ -50,8 +47,11 @@ class FastIgnore
50
47
  dir_only = extract_dir_only(rule)
51
48
  negation = extract_negation(rule, allow)
52
49
 
53
- expand_rule_path(rule, expand_path) if expand_path
54
- unanchored = unanchored?(rule)
50
+ unanchored = if expand_path
51
+ expand_rule_path(rule, expand_path)
52
+ else
53
+ unanchored?(rule)
54
+ end
55
55
  rule.delete_prefix!('/')
56
56
 
57
57
  rule.prepend("#{file_root}#{'**/' if unanchored}") if file_root || unanchored
@@ -73,7 +73,7 @@ class FastIgnore
73
73
  def expand_rule_path(rule, root)
74
74
  rule.replace(::File.expand_path(rule)) if rule.match?(EXPAND_PATH_RE)
75
75
  rule.delete_prefix!(root)
76
- rule.prepend('/') unless rule.start_with?('*') || rule.start_with?('/')
76
+ rule.start_with?('*')
77
77
  end
78
78
 
79
79
  def unanchored?(rule)
@@ -81,10 +81,10 @@ class FastIgnore
81
81
  end
82
82
 
83
83
  def build_gitignore_rules(rule, unanchored, allow, dir_only, negation)
84
- rules = [::FastIgnore::Rule.new(rule.freeze, unanchored, dir_only, negation)]
84
+ rules = [::FastIgnore::Rule.new(rule, negation, unanchored, dir_only)]
85
85
  return rules unless allow
86
86
 
87
- rules << ::FastIgnore::Rule.new("#{rule}/**/*", unanchored, false, negation)
87
+ rules << ::FastIgnore::Rule.new("#{rule}/**/*", negation, unanchored, false)
88
88
  rules + ancestor_rules(rule, unanchored)
89
89
  end
90
90
 
@@ -93,7 +93,7 @@ class FastIgnore
93
93
 
94
94
  while (parent = ::File.dirname(parent)) != '.'
95
95
  rule = ::File.basename(parent) == '**' ? "#{parent}/*" : parent.freeze
96
- ancestor_rules << ::FastIgnore::Rule.new(rule, unanchored, true, true)
96
+ ancestor_rules << ::FastIgnore::Rule.new(rule, true, unanchored, true)
97
97
  end
98
98
 
99
99
  ancestor_rules
@@ -3,39 +3,43 @@
3
3
  class FastIgnore
4
4
  class RuleSet
5
5
  def initialize(rules, allow)
6
- @dir_rules = rules.reject(&:file_only?).freeze
7
- @file_rules = rules.reject(&:dir_only?).freeze
6
+ @dir_rules = squash_rules(rules.reject(&:file_only?)).freeze
7
+ @file_rules = squash_rules(rules.reject(&:dir_only?)).freeze
8
8
  @any_not_anchored = rules.any?(&:unanchored?)
9
9
  @has_shebang_rules = rules.any?(&:shebang)
10
+
10
11
  @allowed_recursive = { '.' => true }
11
12
  @allow = allow
12
13
 
13
14
  freeze
14
15
  end
15
16
 
16
- def freeze
17
- @dir_rules.freeze
18
- @file_rules.freeze
19
-
20
- super
21
- end
22
-
23
- def allowed_recursive?(relative_path, dir, full_path, filename)
17
+ def allowed_recursive?(relative_path, dir, full_path, filename, content = nil)
24
18
  @allowed_recursive.fetch(relative_path) do
25
19
  @allowed_recursive[relative_path] =
26
- allowed_recursive?(::File.dirname(relative_path), true, nil, nil) &&
27
- allowed_unrecursive?(relative_path, dir, full_path, filename)
20
+ allowed_recursive?(::File.dirname(relative_path), true, nil, nil, nil) &&
21
+ allowed_unrecursive?(relative_path, dir, full_path, filename, content)
28
22
  end
29
23
  end
30
24
 
31
- def allowed_unrecursive?(relative_path, dir, full_path, filename)
25
+ def allowed_unrecursive?(relative_path, dir, full_path, filename, content)
32
26
  (dir ? @dir_rules : @file_rules).reverse_each do |rule|
33
- return rule.negation? if rule.match?(relative_path, full_path, filename)
27
+ return rule.negation? if rule.match?(relative_path, full_path, filename, content)
34
28
  end
35
29
 
36
30
  (not @allow) || (dir && @any_not_anchored)
37
31
  end
38
32
 
33
+ def squash_rules(rules)
34
+ out = rules.chunk_while { |a, b| a.type == b.type }.map do |chunk|
35
+ next chunk.first if chunk.length == 1
36
+
37
+ chunk.first.class.new(Regexp.union(chunk.map(&:rule)), chunk.first.negation?)
38
+ end
39
+
40
+ out
41
+ end
42
+
39
43
  def weight
40
44
  @dir_rules.length + (@has_shebang_rules ? 10 : 0)
41
45
  end
@@ -4,10 +4,7 @@ class FastIgnore
4
4
  module RuleSetBuilder
5
5
  class << self
6
6
  # :nocov:
7
- if ::FastIgnore::Backports.ruby_version_less_than?(2, 5)
8
- require_relative 'backports/delete_prefix_suffix'
9
- using ::FastIgnore::Backports::DeletePrefixSuffix
10
- end
7
+ using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
11
8
  # :nocov:
12
9
 
13
10
  def build( # rubocop:disable Metrics/ParameterLists
@@ -6,12 +6,18 @@ class FastIgnore
6
6
  alias_method :negation?, :negation
7
7
  undef :negation
8
8
 
9
- attr_reader :shebang
9
+ attr_reader :rule
10
+ alias_method :shebang, :rule
10
11
 
11
- def initialize(shebang, negation)
12
- @shebang = shebang
12
+ attr_reader :type
13
+
14
+ def initialize(rule, negation)
15
+ @rule = rule
13
16
  @negation = negation
14
17
 
18
+ @type = 2
19
+ @type += 1 if negation
20
+
15
21
  freeze
16
22
  end
17
23
 
@@ -29,14 +35,14 @@ class FastIgnore
29
35
 
30
36
  # :nocov:
31
37
  def inspect
32
- "#<ShebangRule #{'allow ' if @negation}#!:#{@shebang.to_s[15..-4]}>"
38
+ "#<ShebangRule #{'allow ' if @negation}#!:#{@rule.to_s[15..-4]}>"
33
39
  end
34
40
  # :nocov:
35
41
 
36
- def match?(_, full_path, filename)
42
+ def match?(_, full_path, filename, content)
37
43
  return false if filename.include?('.')
38
44
 
39
- first_line(full_path)&.match?(@shebang)
45
+ (content || first_line(full_path))&.match?(@rule)
40
46
  end
41
47
 
42
48
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.10.2'
4
+ VERSION = '0.11.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.10.2
4
+ version: 0.11.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-04-26 00:00:00.000000000 Z
11
+ date: 2020-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -165,8 +165,7 @@ files:
165
165
  - fast_ignore.gemspec
166
166
  - lib/fast_ignore.rb
167
167
  - lib/fast_ignore/backports.rb
168
- - lib/fast_ignore/backports/delete_prefix_suffix.rb
169
- - lib/fast_ignore/backports/dir_each_child.rb
168
+ - lib/fast_ignore/fn_match_to_re.rb
170
169
  - lib/fast_ignore/rule.rb
171
170
  - lib/fast_ignore/rule_builder.rb
172
171
  - lib/fast_ignore/rule_set.rb
@@ -1,56 +0,0 @@
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
- # delete_prefix!(prefix) -> self or nil
9
- # Deletes leading prefix from str, returning nil if no change was made.
10
- #
11
- # "hello".delete_prefix!("hel") #=> "lo"
12
- # "hello".delete_prefix!("llo") #=> nil
13
- def delete_prefix!(str)
14
- return unless start_with?(str)
15
-
16
- slice!(0..(str.length - 1))
17
- self
18
- end
19
-
20
- # delete_suffix!(suffix) -> self or nil
21
- # Deletes trailing suffix from str, returning nil if no change was made.
22
- #
23
- # "hello".delete_suffix!("llo") #=> "he"
24
- # "hello".delete_suffix!("hel") #=> nil
25
- def delete_suffix!(str)
26
- return unless end_with?(str)
27
-
28
- slice!(-str.length..-1)
29
- self
30
- end
31
-
32
- # delete_prefix(prefix) -> new_str click to toggle source
33
- # Returns a copy of str with leading prefix deleted.
34
- #
35
- # "hello".delete_prefix("hel") #=> "lo"
36
- # "hello".delete_prefix("llo") #=> "hello"
37
- def delete_prefix(str)
38
- s = dup
39
- s.delete_prefix!(str)
40
- s
41
- end
42
-
43
- # delete_suffix(suffix) -> new_str
44
- # Returns a copy of str with trailing suffix deleted.
45
- #
46
- # "hello".delete_suffix("llo") #=> "he"
47
- # "hello".delete_suffix("hel") #=> "hello"
48
- def delete_suffix(str) # leftovers:allowed
49
- s = dup
50
- s.delete_suffix!(str)
51
- s
52
- end
53
- end
54
- end
55
- end
56
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This is a backport of ruby 2.5's each_child method
4
- class FastIgnore
5
- module Backports
6
- module DirEachChild
7
- refine ::Dir.singleton_class do
8
- def each_child(path, &block)
9
- Dir.entries(path).each do |entry|
10
- next if entry == '.' || entry == '..'
11
-
12
- block.call entry
13
- end
14
- end
15
- end
16
- end
17
- end
18
- end