fast_ignore 0.10.2 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,38 +2,53 @@
2
2
 
3
3
  class FastIgnore
4
4
  class RuleSet
5
- def initialize(rules, allow)
6
- @dir_rules = rules.reject(&:file_only?).freeze
7
- @file_rules = rules.reject(&:dir_only?).freeze
8
- @any_not_anchored = rules.any?(&:unanchored?)
9
- @has_shebang_rules = rules.any?(&:shebang)
5
+ attr_reader :gitignore
6
+ alias_method :gitignore?, :gitignore
7
+ undef :gitignore
8
+
9
+ def initialize(rules, allow, gitignore)
10
+ @dir_rules = squash_rules(rules.reject(&:file_only?)).freeze
11
+ @file_rules = squash_rules(rules.reject(&:dir_only?)).freeze
12
+ @has_shebang_rules = rules.any?(&:shebang?)
13
+
10
14
  @allowed_recursive = { '.' => true }
11
15
  @allow = allow
16
+ @gitignore = gitignore
12
17
 
13
- freeze
18
+ freeze unless gitignore?
14
19
  end
15
20
 
16
- def freeze
17
- @dir_rules.freeze
18
- @file_rules.freeze
21
+ def <<(other)
22
+ return unless other
19
23
 
20
- super
24
+ @has_shebang_rules ||= other.has_shebang_rules
25
+ @dir_rules = squash_rules(@dir_rules + other.dir_rules)
26
+ @file_rules = squash_rules(@file_rules + other.file_rules)
21
27
  end
22
28
 
23
- def allowed_recursive?(relative_path, dir, full_path, filename)
29
+ def allowed_recursive?(relative_path, dir, full_path, filename, content = nil)
24
30
  @allowed_recursive.fetch(relative_path) do
25
31
  @allowed_recursive[relative_path] =
26
- allowed_recursive?(::File.dirname(relative_path), true, nil, nil) &&
27
- allowed_unrecursive?(relative_path, dir, full_path, filename)
32
+ allowed_recursive?(::File.dirname(relative_path), true, nil, nil, nil) &&
33
+ allowed_unrecursive?(relative_path, dir, full_path, filename, content)
28
34
  end
29
35
  end
30
36
 
31
- def allowed_unrecursive?(relative_path, dir, full_path, filename)
37
+ def allowed_unrecursive?(relative_path, dir, full_path, filename, content)
32
38
  (dir ? @dir_rules : @file_rules).reverse_each do |rule|
33
- return rule.negation? if rule.match?(relative_path, full_path, filename)
39
+ return rule.negation? if rule.match?(relative_path, full_path, filename, content)
34
40
  end
35
41
 
36
- (not @allow) || (dir && @any_not_anchored)
42
+ not @allow
43
+ end
44
+
45
+ def squash_rules(rules)
46
+ rules.chunk_while { |a, b| a.squashable_type == b.squashable_type }.map do |chunk|
47
+ first = chunk.first
48
+ next first if chunk.length == 1
49
+
50
+ first.squash(chunk)
51
+ end
37
52
  end
38
53
 
39
54
  def weight
@@ -43,5 +58,9 @@ class FastIgnore
43
58
  def empty?
44
59
  @dir_rules.empty? && @file_rules.empty?
45
60
  end
61
+
62
+ protected
63
+
64
+ attr_reader :dir_rules, :file_rules, :has_shebang_rules
46
65
  end
47
66
  end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class RuleSets
5
+ # :nocov:
6
+ using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
7
+ # :nocov:
8
+
9
+ def initialize( # rubocop:disable Metrics/ParameterLists
10
+ root:,
11
+ ignore_rules: nil,
12
+ ignore_files: nil,
13
+ gitignore: true,
14
+ include_rules: nil,
15
+ include_files: nil,
16
+ argv_rules: nil
17
+ )
18
+ @array = []
19
+ @project_root = root
20
+ append_root_gitignore(gitignore)
21
+ append_set_from_array(ignore_rules)
22
+ append_set_from_array(include_rules, allow: true)
23
+ append_set_from_array(argv_rules, allow: true, expand_path_with: @project_root)
24
+ append_sets_from_files(ignore_files)
25
+ append_sets_from_files(include_files, allow: true)
26
+ @array.sort_by!(&:weight)
27
+ @array.freeze if @gitignore_rule_set
28
+ end
29
+
30
+ def allowed_recursive?(relative_path, full_path, filename, content)
31
+ @array.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename, content) }
32
+ end
33
+
34
+ def allowed_unrecursive?(relative_path, dir, full_path, filename)
35
+ @array.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename, nil) }
36
+ end
37
+
38
+ def append_subdir_gitignore(relative_path:, check_exists: true)
39
+ new_gitignore = build_set_from_file(relative_path, gitignore: true, check_exists: check_exists)
40
+ return if !new_gitignore || new_gitignore.empty?
41
+
42
+ if @gitignore_rule_set
43
+ @gitignore_rule_set << new_gitignore
44
+ else
45
+ @array << new_gitignore
46
+ @gitignore_rule_set = new_gitignore
47
+ @array.sort_by!(&:weight) && @array.freeze
48
+ end
49
+ new_gitignore
50
+ end
51
+
52
+ private
53
+
54
+ def append_and_return_if_present(value)
55
+ return unless value && !value.empty?
56
+
57
+ @array << value
58
+ value
59
+ end
60
+
61
+ def append_root_gitignore(gitignore)
62
+ return @gitignore_rule_set = nil unless gitignore
63
+
64
+ append_set_from_array('.git')
65
+ gi = ::FastIgnore::RuleSet.new([], false, true)
66
+ gi << build_from_root_gitignore_file(::FastIgnore::GlobalGitignore.path(root: @project_root))
67
+ gi << build_from_root_gitignore_file("#{@project_root}.git/info/exclude")
68
+ gi << build_from_root_gitignore_file("#{@project_root}.gitignore")
69
+ @gitignore_rule_set = append_and_return_if_present(gi)
70
+ end
71
+
72
+ def build_from_root_gitignore_file(path)
73
+ return unless ::File.exist?(path)
74
+
75
+ build_rule_set(::File.readlines(path), false, gitignore: true)
76
+ end
77
+
78
+ def build_rule_set(rules, allow, expand_path_with: nil, file_root: nil, gitignore: false)
79
+ rules = rules.flat_map do |rule|
80
+ ::FastIgnore::RuleBuilder.build(rule, allow, expand_path_with, file_root)
81
+ end
82
+
83
+ ::FastIgnore::RuleSet.new(rules, allow, gitignore)
84
+ end
85
+
86
+ def build_set_from_file(filename, allow: false, file_root: nil, gitignore: false, check_exists: false)
87
+ filename = ::File.expand_path(filename, @project_root)
88
+ return if check_exists && !::File.exist?(filename)
89
+ unless file_root || filename.start_with?(@project_root)
90
+ raise ::FastIgnore::Error, "#{filename} is not within #{@project_root}"
91
+ end
92
+
93
+ file_root ||= "#{::File.dirname(filename)}/".delete_prefix(@project_root)
94
+ build_rule_set(::File.readlines(filename), allow, file_root: file_root, gitignore: gitignore)
95
+ end
96
+
97
+ def append_sets_from_files(files, allow: false)
98
+ Array(files).each do |file|
99
+ append_and_return_if_present(build_set_from_file(file, allow: allow))
100
+ end
101
+ end
102
+
103
+ def append_set_from_array(rules, allow: false, expand_path_with: nil)
104
+ return unless rules
105
+
106
+ rules = Array(rules).flat_map { |string| string.to_s.lines }
107
+ return if rules.empty?
108
+
109
+ append_and_return_if_present(build_rule_set(rules, allow, expand_path_with: expand_path_with))
110
+ end
111
+ end
112
+ end
@@ -6,12 +6,20 @@ class FastIgnore
6
6
  alias_method :negation?, :negation
7
7
  undef :negation
8
8
 
9
- attr_reader :shebang
9
+ attr_reader :rule
10
10
 
11
- def initialize(shebang, negation)
12
- @shebang = shebang
11
+ attr_reader :squashable_type
12
+
13
+ def squash(rules)
14
+ ::FastIgnore::ShebangRule.new(::Regexp.union(rules.map(&:rule)).freeze, negation?)
15
+ end
16
+
17
+ def initialize(rule, negation)
18
+ @rule = rule
13
19
  @negation = negation
14
20
 
21
+ @squashable_type = negation ? 3 : 2
22
+
15
23
  freeze
16
24
  end
17
25
 
@@ -23,28 +31,36 @@ class FastIgnore
23
31
  false
24
32
  end
25
33
 
26
- def unanchored?
27
- true
28
- end
29
-
30
34
  # :nocov:
31
35
  def inspect
32
- "#<ShebangRule #{'allow ' if @negation}#!:#{@shebang.to_s[15..-4]}>"
36
+ "#<ShebangRule #{'allow ' if @negation}#!:#{@rule.to_s[15..-4]}>"
33
37
  end
34
38
  # :nocov:
35
39
 
36
- def match?(_, full_path, filename)
40
+ def match?(_, full_path, filename, content)
37
41
  return false if filename.include?('.')
38
42
 
39
- first_line(full_path)&.match?(@shebang)
43
+ (content || first_line(full_path))&.match?(@rule)
44
+ end
45
+
46
+ def shebang?
47
+ true
40
48
  end
41
49
 
42
50
  private
43
51
 
44
- def first_line(path)
52
+ def first_line(path) # rubocop:disable Metrics/MethodLength
45
53
  file = ::File.new(path)
46
- first_line = file.sysread(25)
47
- first_line += file.sysread(50) until first_line.include?("\n")
54
+ first_line = new_fragment = file.sysread(64)
55
+ if first_line.start_with?('#!')
56
+ until new_fragment.include?("\n")
57
+ new_fragment = file.sysread(64)
58
+ first_line += new_fragment
59
+ end
60
+ else
61
+ file.close
62
+ return
63
+ end
48
64
  file.close
49
65
  first_line
50
66
  rescue ::EOFError, ::SystemCallError
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class UnmatchableRule
5
+ class << self
6
+ def squash(_)
7
+ self
8
+ end
9
+
10
+ def squashable_type
11
+ 5
12
+ end
13
+
14
+ def dir_only?
15
+ false
16
+ end
17
+
18
+ def file_only?
19
+ false
20
+ end
21
+
22
+ def shebang?
23
+ false
24
+ end
25
+
26
+ # :nocov:
27
+ def inspect
28
+ '#<UnmatchableRule>'
29
+ end
30
+ # :nocov:
31
+
32
+ def match?(_, _, _, _)
33
+ false
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FastIgnore
4
- VERSION = '0.10.2'
4
+ VERSION = '0.14.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.14.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-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -143,35 +143,19 @@ executables: []
143
143
  extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
- - ".gitignore"
147
- - ".leftovers.yml"
148
- - ".rspec"
149
- - ".rubocop.yml"
150
- - ".simplecov"
151
- - ".spellr.yml"
152
- - ".spellr_wordlists/english.txt"
153
- - ".spellr_wordlists/ruby.txt"
154
- - ".spellr_wordlists/shell.txt"
155
- - ".travis.yml"
156
146
  - CHANGELOG.md
157
- - Gemfile
158
147
  - LICENSE.txt
159
148
  - README.md
160
- - Rakefile
161
- - bin/console
162
- - bin/ls
163
- - bin/setup
164
- - bin/time
165
- - fast_ignore.gemspec
166
149
  - lib/fast_ignore.rb
167
150
  - lib/fast_ignore/backports.rb
168
- - lib/fast_ignore/backports/delete_prefix_suffix.rb
169
- - lib/fast_ignore/backports/dir_each_child.rb
151
+ - lib/fast_ignore/gitignore_rule_builder.rb
152
+ - lib/fast_ignore/global_gitignore.rb
170
153
  - lib/fast_ignore/rule.rb
171
154
  - lib/fast_ignore/rule_builder.rb
172
155
  - lib/fast_ignore/rule_set.rb
173
- - lib/fast_ignore/rule_set_builder.rb
156
+ - lib/fast_ignore/rule_sets.rb
174
157
  - lib/fast_ignore/shebang_rule.rb
158
+ - lib/fast_ignore/unmatchable_rule.rb
175
159
  - lib/fast_ignore/version.rb
176
160
  homepage: https://github.com/robotdana/fast_ignore
177
161
  licenses:
data/.gitignore DELETED
@@ -1,6 +0,0 @@
1
- /.bundle/
2
- /coverage/
3
- /pkg/
4
- .rspec_status
5
- *.gem
6
- Gemfile.lock
@@ -1,5 +0,0 @@
1
- exclude_paths:
2
- - vendor
3
- rules:
4
- - names: allowed? # public api
5
- skip: true
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
@@ -1,216 +0,0 @@
1
- require: rubocop-rspec
2
-
3
- # Reference:
4
- # https://rubocop.readthedocs.io/en/latest/
5
-
6
- # Keep this in alphabetical order.
7
- # Each override should have a comment (even if it's just "default is bad")
8
-
9
- AllCops:
10
- NewCops: enable
11
- Exclude:
12
- - db/schema*
13
- - .bundle/**/*
14
- - tmp/**/*
15
- - vendor/**/*
16
- DisplayCopNames: true
17
- DisplayStyleGuide: true
18
- TargetRubyVersion: 2.4
19
-
20
- # all of our layout customisations are because we prefer indentation to be
21
- # always consistently 2 spaces, for blocks, scopes, multiline expressions, etc
22
- # e.g.
23
- # class Klass
24
- # def method(arg1,
25
- # arg2)
26
- # value = if arg1 == 'value' && arg2 == 'value'
27
- # method2
28
- # .method(arg_a, arg_b,
29
- # arg_c, arg_d, keyword1: true,
30
- # keyword2: true) do
31
- # @last = [
32
- # arg_a, arg_b,
33
- # arg_c, arg_d
34
- # ]
35
- # end
36
- # end
37
- # value
38
- # end
39
- # end
40
-
41
- # to match our preference for consistent indentation
42
- Layout/HashAlignment:
43
- EnforcedLastArgumentHashStyle: always_ignore
44
-
45
- # to match our preference for consistent indentation
46
- Layout/ParameterAlignment:
47
- EnforcedStyle: with_fixed_indentation
48
-
49
- # to match our preference for consistent indentation
50
- Layout/BlockAlignment:
51
- EnforcedStyleAlignWith: start_of_block
52
-
53
- # to match our preference for consistent indentation
54
- Layout/CaseIndentation:
55
- EnforcedStyle: end
56
-
57
- # to match our preference for consistent indentation
58
- Layout/EndAlignment:
59
- EnforcedStyleAlignWith: start_of_line
60
-
61
- # Aligning Assignments, etc makes diffs noisy
62
- Layout/ExtraSpacing:
63
- AllowForAlignment: false
64
-
65
- # to match our preference for consistent indentation
66
- Layout/FirstArrayElementLineBreak:
67
- Enabled: true
68
-
69
- # to match our preference for consistent indentation
70
- Layout/FirstHashElementLineBreak:
71
- Enabled: true
72
-
73
- # to match our preference for consistent indentation
74
- Layout/FirstArgumentIndentation:
75
- EnforcedStyle: consistent
76
-
77
- # to match our preference for consistent indentation
78
- Layout/FirstArrayElementIndentation:
79
- EnforcedStyle: consistent
80
-
81
- # to match our preference for consistent indentation
82
- Layout/FirstHashElementIndentation:
83
- EnforcedStyle: consistent
84
-
85
- Layout/LineLength:
86
- Max: 120
87
-
88
- # to match our preference for consistent indentation
89
- # and hanging assignment looks lost
90
- Layout/MultilineAssignmentLayout:
91
- EnforcedStyle: same_line
92
-
93
- # this changes our preferred:
94
- # value = if thing1 &&
95
- # thing2
96
- # to:
97
- # value = if thing1 &&
98
- # thing2
99
- # even though the IndentationWidth is 2
100
- # but it's right most of the time so I put up with it
101
- Layout/MultilineOperationIndentation:
102
- EnforcedStyle: indented
103
-
104
- Layout/MultilineMethodCallIndentation:
105
- EnforcedStyle: indented
106
-
107
- # Temporarily disable this spec as a recent change has broken it for us:
108
- # https://github.com/rubocop-hq/rubocop/issues/6254
109
- Layout/RescueEnsureAlignment:
110
- Enabled: false
111
-
112
- Metrics:
113
- CountComments: false
114
-
115
- Metrics/BlockLength:
116
- ExcludedMethods:
117
- - configure
118
- - describe
119
- - context
120
- - shared_examples
121
-
122
- Metrics/CyclomaticComplexity:
123
- Enabled: false
124
-
125
- Metrics/PerceivedComplexity:
126
- Enabled: false
127
-
128
- RSpec:
129
- Enabled: true
130
- Include:
131
- - 'spec/**/*.rb'
132
-
133
- RSpec/DescribeClass:
134
- Enabled: false
135
-
136
- # I misuse matchers often
137
- RSpec/ExpectActual:
138
- Enabled: false
139
-
140
- RSpec/FilePath:
141
- Enabled: false
142
-
143
- # Multiple expectations are useful
144
- # checking you've partially achieved something on the way to completely achieving it is useful for debugging failures
145
- RSpec/MultipleExpectations:
146
- Enabled: false
147
-
148
- # It should be obvious from context. Chill out rubocop
149
- RSpec/NamedSubject:
150
- Enabled: false
151
-
152
- RSpec/NestedGroups:
153
- Max: 7
154
-
155
- # This matches the style we've been using all along (ever so slightly more frequently)
156
- Style/Alias:
157
- EnforcedStyle: prefer_alias_method
158
-
159
- Style/CollectionMethods:
160
- Enabled: true
161
-
162
- # we don't rdoc
163
- Style/Documentation:
164
- Enabled: false
165
-
166
- # this can mess with the balance of symmetric cases
167
- Style/IfInsideElse:
168
- Enabled: false
169
-
170
- # [a, b].include?(x) is more unclear than a == x || b == x
171
- Style/MultipleComparison:
172
- Enabled: false
173
-
174
- # it's microscopically faster
175
- Style/Not:
176
- Enabled: false
177
-
178
- # we use %w{} pretty frequently
179
- Style/PercentLiteralDelimiters:
180
- PreferredDelimiters:
181
- default: '{}'
182
- '%w': '{}'
183
- '%W': '{}'
184
- '%i': '{}'
185
- '%I': '{}'
186
- '%r': '{}'
187
-
188
- # We want this to warn to force consistency within the codebase.
189
- Style/SafeNavigation:
190
- Enabled: true
191
-
192
- # different methods calls that do exactly the same thing are a smell, regardless of semantics
193
- Style/SignalException:
194
- EnforcedStyle: only_raise
195
-
196
- # this wants less descriptive names
197
- Style/SingleLineBlockParams:
198
- Enabled: false
199
-
200
- Style/SymbolArray:
201
- Enabled: false
202
-
203
- Style/WordArray:
204
- Enabled: false
205
-
206
- Style/HashEachMethods:
207
- Enabled: true
208
-
209
- Style/HashTransformKeys:
210
- Enabled: true
211
-
212
- Style/HashTransformValues:
213
- Enabled: true
214
-
215
- Style/CommentedKeyword:
216
- Enabled: false