pathspec 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,21 @@
1
1
  require 'pathspec/spec'
2
2
 
3
- class RegexSpec < Spec
4
- def initialize(regex)
5
- @regex = Regexp.compile regex
3
+ class PathSpec
4
+ # Simple regex-based spec
5
+ class RegexSpec < Spec
6
+ def initialize(pattern)
7
+ @pattern = pattern.dup
8
+ @regex = Regexp.compile pattern
6
9
 
7
- super
8
- end
10
+ super
11
+ end
9
12
 
10
- def inclusive?
11
- true
12
- end
13
+ def inclusive?
14
+ true
15
+ end
13
16
 
14
- def match(path)
15
- @regex.match(path) if @regex
17
+ def match(path)
18
+ @regex&.match(path)
19
+ end
16
20
  end
17
21
  end
@@ -1,14 +1,20 @@
1
- class Spec
2
- attr_reader :regex
1
+ class PathSpec
2
+ # Abstract spec
3
+ class Spec
4
+ attr_reader :regex, :pattern
3
5
 
4
- def initialize(*_)
5
- end
6
+ def initialize(*_); end
6
7
 
7
- def match(files)
8
- raise "Unimplemented"
9
- end
8
+ def match(files)
9
+ raise 'Unimplemented'
10
+ end
11
+
12
+ def inclusive?
13
+ true
14
+ end
10
15
 
11
- def inclusive?
12
- true
16
+ def to_s
17
+ @pattern
18
+ end
13
19
  end
14
20
  end
@@ -0,0 +1,2 @@
1
+ !**/important.txt
2
+ abc/**
@@ -0,0 +1,51 @@
1
+ # Source: https://github.com/github/gitignore/blob/master/Ruby.gitignore
2
+ *.gem
3
+ *.rbc
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ ## Specific to RubyMotion:
18
+ .dat*
19
+ .repl_history
20
+ build/
21
+ *.bridgesupport
22
+ build-iPhoneOS/
23
+ build-iPhoneSimulator/
24
+
25
+ ## Specific to RubyMotion (use of CocoaPods):
26
+ #
27
+ # We recommend against adding the Pods directory to your .gitignore. However
28
+ # you should judge for yourself, the pros and cons are mentioned at:
29
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
30
+ #
31
+ # vendor/Pods/
32
+
33
+ ## Documentation cache and generated files:
34
+ /.yardoc/
35
+ /_yardoc/
36
+ /doc/
37
+ /rdoc/
38
+
39
+ ## Environment normalization:
40
+ /.bundle/
41
+ /vendor/bundle
42
+ /lib/bundler/man/
43
+
44
+ # for a library or gem, you might want to ignore these files since the code is
45
+ # intended to run in multiple environments; otherwise, check them in:
46
+ # Gemfile.lock
47
+ # .ruby-version
48
+ # .ruby-gemset
49
+
50
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
51
+ .rvmrc
@@ -0,0 +1 @@
1
+ *.md
@@ -0,0 +1 @@
1
+ .*\.md
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ rescue StandardError
5
+ puts 'SimpleCov failed to start, most likely this due to running Ruby 1.8.7'
6
+ end
@@ -0,0 +1,303 @@
1
+ require 'spec_helper'
2
+ require 'pathspec/gitignorespec'
3
+
4
+ describe PathSpec::GitIgnoreSpec do
5
+ # Original specification by http://git-scm.com/docs/gitignore
6
+
7
+ # A blank line matches no files, so it can serve as a separator for
8
+ # readability.
9
+ describe 'does nothing for newlines' do
10
+ subject { PathSpec::GitIgnoreSpec.new "\n" }
11
+ it { is_expected.to_not match('foo.tmp') }
12
+ it { is_expected.to_not match(' ') }
13
+ it { is_expected.to_not be_inclusive }
14
+ end
15
+
16
+ describe 'does nothing for blank strings' do
17
+ subject { PathSpec::GitIgnoreSpec.new '' }
18
+ it { is_expected.to_not match 'foo.tmp' }
19
+ it { is_expected.to_not match ' ' }
20
+ it { is_expected.to_not be_inclusive }
21
+ end
22
+
23
+ # A line starting with # serves as a comment. Put a backslash ("\") in front
24
+ # of the first hash for patterns that begin with a hash.
25
+ describe 'does nothing for comments' do
26
+ subject { PathSpec::GitIgnoreSpec.new '# this is a gitignore style comment' }
27
+ it { is_expected.to_not match('foo.tmp') }
28
+ it { is_expected.to_not match(' ') }
29
+ it { is_expected.to_not be_inclusive }
30
+ end
31
+
32
+ describe 'ignores comment char with a slash' do
33
+ subject { PathSpec::GitIgnoreSpec.new '\#averystrangefile' }
34
+ it { is_expected.to match('#averystrangefile') }
35
+ it { is_expected.to_not match('foobar') }
36
+ it { is_expected.to be_inclusive }
37
+ end
38
+
39
+ describe 'escapes characters with slashes' do
40
+ subject { PathSpec::GitIgnoreSpec.new 'twinkletwinkle\*' }
41
+ it { is_expected.to match('twinkletwinkle*') }
42
+ it { is_expected.to_not match('twinkletwinkletwinkle') }
43
+ it { is_expected.to be_inclusive }
44
+ end
45
+
46
+ # Trailing spaces are ignored unless they are quoted with backlash ("\").
47
+ describe 'ignores trailing spaces' do
48
+ subject { PathSpec::GitIgnoreSpec.new 'foo ' }
49
+ it { is_expected.to match('foo') }
50
+ it { is_expected.to_not match('foo ') }
51
+ it { is_expected.to be_inclusive }
52
+ end
53
+
54
+ # This is not handled properly yet
55
+ describe 'does not ignore escaped trailing spaces'
56
+
57
+ # An optional prefix "!" which negates the pattern; any matching file excluded
58
+ # by a previous pattern will become included again. It is not possible to
59
+ # re-include a file if a parent directory of that file is excluded. Git
60
+ # doesn't list excluded directories for performance reasons, so any patterns
61
+ # on contained files have no effect, no matter where they are defined. Put a
62
+ # backslash ("\") in front of the first "!" for patterns that begin with a
63
+ # literal "!", for example, "\!important!.txt".
64
+ describe 'is exclusive of !' do
65
+ subject { PathSpec::GitIgnoreSpec.new '!important.txt' }
66
+ it { is_expected.to match('important.txt') }
67
+ it { is_expected.to_not be_inclusive }
68
+ it { is_expected.to_not match('!important.txt') }
69
+ end
70
+
71
+ # If the pattern ends with a slash, it is removed for the purpose of the
72
+ # following description, but it would only find a match with a directory. In
73
+ # other words, foo/ will match a directory foo and paths underneath it, but
74
+ # will not match a regular file or a symbolic link foo (this is consistent
75
+ # with the way how pathspec works in general in Git).
76
+ describe 'trailing slashes match directories and their contents but not regular files or symlinks' do
77
+ subject { PathSpec::GitIgnoreSpec.new 'foo/' }
78
+ it { is_expected.to match('foo/') }
79
+ it { is_expected.to match('foo/bar') }
80
+ it { is_expected.to match('baz/foo/bar') }
81
+ it { is_expected.to_not match('foo') }
82
+ it { is_expected.to be_inclusive }
83
+ end
84
+
85
+ # If the pattern does not contain a slash '/', Git treats it as a shell glob
86
+ # pattern and checks for a match against the pathname relative to the location
87
+ # of the .gitignore file (relative to the toplevel of the work tree if not
88
+ # from a .gitignore file).
89
+ describe 'handles basic globbing' do
90
+ subject { PathSpec::GitIgnoreSpec.new '*.tmp' }
91
+ it { is_expected.to match('foo.tmp') }
92
+ it { is_expected.to match('foo/bar.tmp') }
93
+ it { is_expected.to match('foo/bar.tmp/baz') }
94
+ it { is_expected.to_not match('foo.rb') }
95
+ it { is_expected.to be_inclusive }
96
+ end
97
+
98
+ describe 'handles inner globs' do
99
+ subject { PathSpec::GitIgnoreSpec.new 'foo-*-bar' }
100
+ it { is_expected.to match('foo--bar') }
101
+ it { is_expected.to match('foo-hello-bar') }
102
+ it { is_expected.to match('a/foo-hello-bar') }
103
+ it { is_expected.to match('foo-hello-bar/b') }
104
+ it { is_expected.to match('a/foo-hello-bar/b') }
105
+ it { is_expected.to_not match('foo.tmp') }
106
+ end
107
+
108
+ describe 'handles postfix globs' do
109
+ subject { PathSpec::GitIgnoreSpec.new '~temp-*' }
110
+ it { is_expected.to match('~temp-') }
111
+ it { is_expected.to match('~temp-foo') }
112
+ it { is_expected.to match('foo/~temp-bar') }
113
+ it { is_expected.to match('foo/~temp-bar/baz') }
114
+ it { is_expected.to_not match('~temp') }
115
+ end
116
+
117
+ describe 'handles multiple globs' do
118
+ subject { PathSpec::GitIgnoreSpec.new '*.middle.*' }
119
+ it { is_expected.to match('hello.middle.rb') }
120
+ it { is_expected.to_not match('foo.rb') }
121
+ it { is_expected.to be_inclusive }
122
+ end
123
+
124
+ describe 'handles dir globs' do
125
+ subject { PathSpec::GitIgnoreSpec.new 'dir/*' }
126
+ it { is_expected.to match('dir/foo') }
127
+ it { is_expected.to_not match('foo/') }
128
+ it { is_expected.to be_inclusive }
129
+ end
130
+
131
+ # Otherwise, Git treats the pattern as a shell glob suitable for consumption
132
+ # by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not
133
+ # match a / in the pathname. For example, "Documentation/*.html" matches
134
+ # "Documentation/git.html" but not "Documentation/ppc/ppc.html" or
135
+ # "tools/perf/Documentation/perf.html".
136
+ describe 'handles dir globs' do
137
+ subject { PathSpec::GitIgnoreSpec.new 'dir/*' }
138
+ it { is_expected.to match('dir/foo') }
139
+ it { is_expected.to_not match('foo/') }
140
+ it { is_expected.to be_inclusive }
141
+ end
142
+
143
+ describe 'handles globs inside of dirs' do
144
+ subject { PathSpec::GitIgnoreSpec.new 'Documentation/*.html' }
145
+ it { is_expected.to match('Documentation/git.html') }
146
+ it { is_expected.to_not match('Documentation/ppc/ppc.html') }
147
+ it { is_expected.to_not match('tools/perf/Documentation/perf.html') } # TODO: Or is it? Git 2 weirdness?
148
+ it { is_expected.to be_inclusive }
149
+ end
150
+
151
+ describe 'handles wildcards' do
152
+ subject { PathSpec::GitIgnoreSpec.new 'jokeris????' }
153
+ it { is_expected.to match('jokeriswild') }
154
+ it { is_expected.to_not match('jokerisfat') }
155
+ it { is_expected.to be_inclusive }
156
+ end
157
+
158
+ describe 'handles brackets' do
159
+ subject { PathSpec::GitIgnoreSpec.new '*[eu][xl]*' }
160
+ it { is_expected.to match('youknowregex') }
161
+ it { is_expected.to match('youknowregularexpressions') }
162
+ it { is_expected.to_not match('youknownothing') }
163
+ it { is_expected.to be_inclusive }
164
+ end
165
+
166
+ describe 'handles unmatched brackets' do
167
+ subject { PathSpec::GitIgnoreSpec.new '*[*[*' }
168
+ it { is_expected.to match('bracket[oh[wow') }
169
+ it { is_expected.to be_inclusive }
170
+ end
171
+
172
+ describe 'handles brackets with carats' do
173
+ subject { PathSpec::GitIgnoreSpec.new '*[^]' }
174
+ it { is_expected.to match('myfavorite^') }
175
+ it { is_expected.to be_inclusive }
176
+ end
177
+
178
+ describe 'handles brackets for brackets' do
179
+ subject { PathSpec::GitIgnoreSpec.new '*[]]' }
180
+ it { is_expected.to match('yodawg[]]') }
181
+ it { is_expected.to be_inclusive }
182
+ end
183
+
184
+ describe 'handles brackets with escaped characters' do
185
+ # subject { GitIgnoreSpec.new 'back[\\]slash' }
186
+ # it { is_expected.to match('back\\slash') }
187
+ # it { is_expected.to_not match('back\\\\slash') }
188
+ # it { is_expected.to be_inclusive }
189
+ end
190
+
191
+ describe 'handles negated brackets' do
192
+ subject { PathSpec::GitIgnoreSpec.new 'ab[!cd]ef' }
193
+ it { is_expected.to_not match('abcef') }
194
+ it { is_expected.to match('abzef') }
195
+ it { is_expected.to be_inclusive }
196
+ end
197
+
198
+ # A leading slash matches the beginning of the pathname. For example, "/*.c"
199
+ # matches "cat-file.c" but not "mozilla-sha1/sha1.c".
200
+ describe 'handles leading / as relative to base directory' do
201
+ subject { PathSpec::GitIgnoreSpec.new '/*.c' }
202
+ it { is_expected.to match('cat-file.c') }
203
+ it { is_expected.to_not match('mozilla-sha1/sha1.c') }
204
+ it { is_expected.to be_inclusive }
205
+ end
206
+
207
+ describe 'handles simple single paths' do
208
+ subject { PathSpec::GitIgnoreSpec.new 'spam' }
209
+ it { is_expected.to match('spam') }
210
+ it { is_expected.to match('spam/') }
211
+ it { is_expected.to match('foo/spam') }
212
+ it { is_expected.to match('spam/foo') }
213
+ it { is_expected.to match('foo/spam/bar') }
214
+ it { is_expected.to_not match('foo') }
215
+ end
216
+
217
+ # Two consecutive asterisks ("**") in patterns matched against full pathname
218
+ # may have special meaning:
219
+
220
+ # A leading "**" followed by a slash means match in all directories. For
221
+ # example, "**/foo" matches file or directory "foo" anywhere, the same as
222
+ # pattern "foo". "**/foo/bar" matches file or directory "bar" anywhere that is
223
+ # directly under directory "foo".
224
+ describe 'handles prefixed ** as searching any location' do
225
+ subject { PathSpec::GitIgnoreSpec.new '**/foo' }
226
+ it { is_expected.to match('foo') }
227
+ it { is_expected.to match('bar/foo') }
228
+ it { is_expected.to match('baz/bar/foo') }
229
+ it { is_expected.to_not match('baz/bar/foo.rb') }
230
+ it { is_expected.to be_inclusive }
231
+ end
232
+
233
+ describe 'handles prefixed ** with a directory as searching a file under a directory in any location' do
234
+ subject { PathSpec::GitIgnoreSpec.new '**/foo/bar' }
235
+ it { is_expected.to_not match('foo') }
236
+ it { is_expected.to match('foo/bar') }
237
+ it { is_expected.to match('baz/foo/bar') }
238
+ it { is_expected.to match('baz/foo/bar/sub') }
239
+ it { is_expected.to_not match('baz/foo/bar.rb') }
240
+ it { is_expected.to_not match('baz/bananafoo/bar') }
241
+ it { is_expected.to be_inclusive }
242
+ end
243
+
244
+ # A trailing "/**" matches everything inside. For example, "abc/**" matches
245
+ # all files inside directory "abc", relative to the location of the .gitignore
246
+ # file, with infinite depth.
247
+ describe 'handles leading /** as all files inside a directory' do
248
+ subject { PathSpec::GitIgnoreSpec.new 'abc/**' }
249
+ it { is_expected.to match('abc/') }
250
+ it { is_expected.to match('abc/def') }
251
+ it { is_expected.to_not match('123/abc/def') }
252
+ it { is_expected.to_not match('123/456/abc/') }
253
+ it { is_expected.to be_inclusive }
254
+ end
255
+
256
+ # A slash followed by two consecutive asterisks then a slash matches zero or
257
+ # more directories. For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b"
258
+ # and so on.
259
+ describe 'handles /** in the middle of a path' do
260
+ subject { PathSpec::GitIgnoreSpec.new 'a/**/b' }
261
+ it { is_expected.to match('a/b') }
262
+ it { is_expected.to match('a/x/b') }
263
+ it { is_expected.to match('a/x/y/b') }
264
+ it { is_expected.to_not match('123/a/b') }
265
+ it { is_expected.to_not match('123/a/x/b') }
266
+ it { is_expected.to be_inclusive }
267
+ end
268
+
269
+ describe 'matches all paths when given **' do
270
+ subject { PathSpec::GitIgnoreSpec.new '**' }
271
+
272
+ it { is_expected.to match('a/b') }
273
+ it { is_expected.to match('a/x/b') }
274
+ it { is_expected.to match('a/x/y/b') }
275
+ it { is_expected.to match('123/a/b') }
276
+ it { is_expected.to match('123/a/x/b') }
277
+ end
278
+
279
+ # Other consecutive asterisks are considered invalid.
280
+ describe 'considers other consecutive asterisks invalid' do
281
+ subject { PathSpec::GitIgnoreSpec.new 'a/***/b' }
282
+ it { is_expected.to_not match('a/b') }
283
+ it { is_expected.to_not match('a/x/b') }
284
+ it { is_expected.to_not match('a/x/y/b') }
285
+ it { is_expected.to_not match('123/a/b') }
286
+ it { is_expected.to_not match('123/a/x/b') }
287
+ it { is_expected.to_not be_inclusive }
288
+ end
289
+
290
+ describe 'does not match single absolute paths' do
291
+ subject { PathSpec::GitIgnoreSpec.new '/' }
292
+ it { is_expected.to_not match('foo.tmp') }
293
+ it { is_expected.to_not match(' ') }
294
+ it { is_expected.to_not match('a/b') }
295
+ end
296
+
297
+ describe 'nested paths are relative to the file' do
298
+ subject { PathSpec::GitIgnoreSpec.new 'foo/spam' }
299
+ it { is_expected.to match('foo/spam') }
300
+ it { is_expected.to match('foo/spam/bar') }
301
+ it { is_expected.to_not match('bar/foo/spam') }
302
+ end
303
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'pathspec/spec'
3
+
4
+ describe PathSpec::Spec do
5
+ subject { PathSpec::Spec.new }
6
+
7
+ it 'does not allow matching' do
8
+ expect { subject.match 'anything' }.to raise_error(/Unimplemented/)
9
+ end
10
+
11
+ it { is_expected.to be_inclusive }
12
+ end