pathspec 0.0.2 → 0.1.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
  SHA1:
3
- metadata.gz: a0eb87d15ea86cb98dce7e2ca41f6939418c3b57
4
- data.tar.gz: 084dc186f9fbf2f8c4a7ed8268cfa1c78e296728
3
+ metadata.gz: 3241bfa1371172a86936a49ce7f977ab8c8d9edb
4
+ data.tar.gz: 9c1e2d50895f2c65943e2158b63e818b80c8d2e3
5
5
  SHA512:
6
- metadata.gz: 17ccc33fba7022b67fe0da1f9c02fbb6bd4e2d6d8e40fcb0d8ed0420f9733ace1692de35e69151e948b788cf007d7ea49f8c1760809ab02b672e61af16aac9e0
7
- data.tar.gz: a1d2fa238ea0fbc47083c25a4372a6d55848e200b1b850701e63ef12a191c06141b00f0a1871c14b46fe3e8366dcf1dfcf3b588a99f81b2d48117fbefe6eadd5
6
+ metadata.gz: 66082a9cf1cd342c1e636c5243e424c6649418193ce34fe65b1e1b8e62c9f241c289baf3c5a0c48cc3b870bbb7650540e833fd0400454537f8ec1c9f76ba5f8d
7
+ data.tar.gz: 6fbca7071256e9024293d6e0ed69eed42bb7118cc95e80d8f359a81e32e3d6cbf7e520270ad7ca94127936ec7c925efddfcba0ce3b3224a7c2f32bbeb459d0f9
@@ -1 +1,15 @@
1
- 0.0.1: Initial version.
1
+ # pathspec-ruby CHANGELOG
2
+
3
+ ## 0.1.0
4
+ - Port new edgecase handling from [python-path-specification](https://github.com/cpburnz/python-path-specification/pull/8). Many thanks to @jdpace! :)
5
+ - Removed EOL Ruby support
6
+ - Added current Ruby stable to Travis testing
7
+
8
+ ## 0.0.2
9
+ - Fixed issues with Ruby 1.8.7/2.1.1
10
+ - Added more testing scripts
11
+ - Fixed Windows path related issues
12
+ - Cleanup unnecessary things in gem
13
+
14
+ ## 0.0.1
15
+ - Initial version.
data/README.md CHANGED
@@ -1,14 +1,19 @@
1
1
  pathspec-ruby
2
2
  =============
3
3
 
4
+ [![Build Status](https://travis-ci.org/highb/pathspec-ruby.svg?branch=master)](https://travis-ci.org/highb/pathspec-ruby) [![codecov](https://codecov.io/gh/highb/pathspec-ruby/branch/master/graph/badge.svg)](https://codecov.io/gh/highb/pathspec-ruby)
5
+
6
+ Supported Rubies:
7
+ - 2.2.7 (Maintenance)
8
+ - 2.3.4 (Stable)
9
+ - 2.4.1 (Stable)
10
+
4
11
  Match Path Specifications, such as .gitignore, in Ruby!
5
12
 
6
13
  Follows .gitignore syntax defined on [gitscm](http://git-scm.com/docs/gitignore)
7
14
 
8
15
  .gitignore functionality ported from [Python pathspec](https://pypi.python.org/pypi/pathspec/0.2.2) by [@cpburnz](https://github.com/cpburnz/python-path-specification)
9
16
 
10
- [Travis Status](https://travis-ci.org/highb/pathspec-ruby) ![Travis CI Status](https://travis-ci.org/highb/pathspec-ruby.svg?branch=master)
11
-
12
17
  ## Build/Install from Rubygems
13
18
  ```shell
14
19
  gem install pathspec
@@ -27,6 +27,12 @@ class GitIgnoreSpec < RegexSpec
27
27
  @regex = nil
28
28
  @inclusive = nil
29
29
 
30
+ # EDGE CASE: According to git check-ignore (v2.4.1)), a single '/'
31
+ # does not match any file
32
+ elsif pattern == '/'
33
+ @regex = nil
34
+ @inclusive = nil
35
+
30
36
  # We have a valid pattern!
31
37
  else
32
38
  # A pattern starting with an exclamation mark ('!') negates the
@@ -58,12 +64,15 @@ class GitIgnoreSpec < RegexSpec
58
64
  # to root.
59
65
  if pattern_segs[0].empty?
60
66
  pattern_segs.shift
61
- else
67
+ elsif pattern_segs.length == 1 ||
68
+ pattern_segs.length == 2 && pattern_segs[-1].empty?
62
69
  # A pattern without a beginning slash ('/') will match any
63
70
  # descendant path. This is equivilent to "**/{pattern}". So,
64
71
  # prepend with double-asterisks to make pattern relative to
65
72
  # root.
66
- if pattern_segs.length == 1 && pattern_segs[0] != '**'
73
+ # EDGE CASE: This also holds for a single pattern with a
74
+ # trailing slash (e.g. dir/).
75
+ if pattern_segs[0] != '**'
67
76
  pattern_segs.insert(0, '**')
68
77
  end
69
78
  end
@@ -72,7 +81,7 @@ class GitIgnoreSpec < RegexSpec
72
81
  # paths of if it is a directory but not if it is a regular file.
73
82
  # This is equivilent to "{pattern}/**". So, set last segment to
74
83
  # double asterisks to include all descendants.
75
- if pattern_segs[-1].empty?
84
+ if pattern_segs[-1].empty? && pattern_segs.length > 1
76
85
  pattern_segs[-1] = '**'
77
86
  end
78
87
 
@@ -131,6 +140,16 @@ class GitIgnoreSpec < RegexSpec
131
140
  end
132
141
 
133
142
  regex.concat(translate_segment_glob(seg))
143
+
144
+ if i == regex_end && @inclusive
145
+ # A pattern ending without a slash ('/') will match a file
146
+ # or a directory (with paths underneath it).
147
+ # e.g. foo matches: foo, foo/bar, foo/bar/baz, etc.
148
+ # EDGE CASE: However, this does not hold for exclusion cases
149
+ # according to `git check-ignore` (v2.4.1).
150
+ regex.concat("(?:#{path_sep}.*)?")
151
+ end
152
+
134
153
  need_slash = true
135
154
  end
136
155
  end
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+
5
+ require 'codecov'
6
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
7
+ rescue
8
+ puts 'SimpleCov failed to start, most likely this due to running Ruby 1.8.7'
9
+ end
@@ -0,0 +1,305 @@
1
+ require 'spec_helper'
2
+ require 'pathspec/gitignorespec'
3
+
4
+ describe 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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
+
304
+
305
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'pathspec/spec'
3
+
4
+ describe Spec do
5
+ subject { Spec.new }
6
+
7
+ it "does not allow matching" do
8
+ expect { subject.match "anything" }.to raise_error
9
+ end
10
+
11
+ it { is_expected.to be_inclusive }
12
+ end
@@ -0,0 +1,322 @@
1
+ require 'spec_helper'
2
+ require 'fakefs/safe'
3
+ require 'pathspec'
4
+ require 'fakefs/spec_helpers'
5
+
6
+ describe PathSpec do
7
+ shared_examples "standard gitignore negation" do
8
+ it { is_expected.to_not match('important.txt') }
9
+ it { is_expected.to_not match('foo/important.txt') }
10
+ it { is_expected.to match('foo/bar/') }
11
+ end
12
+
13
+ context "initialization" do
14
+ context "from multilines" do
15
+ context "#new" do
16
+ subject { PathSpec.new <<-IGNORELINES
17
+ !important.txt
18
+ foo/**
19
+ /bar/baz
20
+ IGNORELINES
21
+ }
22
+
23
+ it_behaves_like "standard gitignore negation"
24
+ end
25
+ end
26
+
27
+ context "from a string with no newlines" do
28
+ let(:str) { "foo/**" }
29
+
30
+ context "#new" do
31
+ subject { PathSpec.new str }
32
+
33
+ it { is_expected.to match('foo/important.txt') }
34
+ it { is_expected.to match('foo/bar/') }
35
+ end
36
+ end
37
+
38
+ context "from a non-string/non-enumerable" do
39
+ it "throws an exception" do
40
+ expect { PathSpec.new Object.new }.to raise_error
41
+ end
42
+ end
43
+
44
+ context "from array of gitignore strings" do
45
+ let(:arr) { ["!important.txt", "foo/**", "/bar/baz"] }
46
+
47
+ context "#new" do
48
+ subject { PathSpec.new arr }
49
+
50
+ it_behaves_like "standard gitignore negation"
51
+ end
52
+
53
+ context "#from_lines" do
54
+ subject {
55
+ PathSpec.from_lines(arr)
56
+ }
57
+
58
+ it_behaves_like "standard gitignore negation"
59
+ end
60
+
61
+ context "#add array" do
62
+ subject {
63
+ ps = PathSpec.new []
64
+ ps.add arr
65
+ }
66
+
67
+ it_behaves_like "standard gitignore negation"
68
+ end
69
+ end
70
+
71
+ context "from linedelimited gitignore string" do
72
+ let(:line) { "!important.txt\nfoo/**\n/bar/baz\n" }
73
+
74
+ context "#new" do
75
+ subject { PathSpec.new line }
76
+
77
+ it_behaves_like "standard gitignore negation"
78
+ end
79
+
80
+ context "#from_lines" do
81
+ subject {
82
+ PathSpec.from_lines(line)
83
+ }
84
+
85
+ it_behaves_like "standard gitignore negation"
86
+ end
87
+
88
+ context "#add" do
89
+ subject {
90
+ ps = PathSpec.new
91
+ ps.add line
92
+ }
93
+
94
+ it_behaves_like "standard gitignore negation"
95
+ end
96
+ end
97
+
98
+ context "from a gitignore file" do
99
+ include FakeFS::SpecHelpers
100
+
101
+ let(:filename) { '.gitignore' }
102
+ before(:each) do
103
+ file = File.open(filename, 'w') { |f|
104
+ f << "!important.txt\n"
105
+ f << "foo/**\n"
106
+ f << "/bar/baz\n"
107
+ }
108
+ end
109
+
110
+ context "#new" do
111
+ subject {
112
+ PathSpec.new File.open(filename, 'r')
113
+ }
114
+
115
+ it_behaves_like "standard gitignore negation"
116
+ end
117
+
118
+ context "#from_filename" do
119
+ subject {
120
+ PathSpec.from_filename(filename)
121
+ }
122
+
123
+ it_behaves_like "standard gitignore negation"
124
+ end
125
+ end
126
+
127
+ context "from multiple gitignore files" do
128
+ include FakeFS::SpecHelpers
129
+
130
+ let(:filenames) { ['.gitignore', '.otherignore'] }
131
+ before(:each) do
132
+ file = File.open('.gitignore', 'w') { |f|
133
+ f << "!important.txt\n"
134
+ f << "foo/**\n"
135
+ f << "/bar/baz\n"
136
+ }
137
+
138
+ file = File.open('.otherignore', 'w') { |f|
139
+ f << "ban*na\n"
140
+ f << "!banana\n"
141
+ }
142
+ end
143
+
144
+ context "#new" do
145
+ subject {
146
+ arr = filenames.collect { |f| File.open(f, 'r') }
147
+ PathSpec.new arr
148
+ }
149
+
150
+ it_behaves_like "standard gitignore negation"
151
+
152
+ it { is_expected.to_not match('banana') }
153
+ it { is_expected.to match('banananananana') }
154
+ end
155
+
156
+ context "#add" do
157
+ subject {
158
+ arr = filenames.collect { |f| File.open(f, 'r') }
159
+ ps = PathSpec.new
160
+ ps.add arr
161
+ }
162
+
163
+ it_behaves_like "standard gitignore negation"
164
+
165
+ it { is_expected.to_not match('banana') }
166
+ it { is_expected.to match('banananananana') }
167
+ end
168
+ end
169
+ end
170
+
171
+ context "#match_tree" do
172
+ include FakeFS::SpecHelpers
173
+
174
+ context "unix" do
175
+ let(:root) {'/tmp/project'}
176
+ let(:gitignore) { <<-GITIGNORE
177
+ !**/important.txt
178
+ abc/**
179
+ GITIGNORE
180
+ }
181
+
182
+ before(:each) {
183
+ FileUtils.mkdir_p root
184
+ FileUtils.mkdir_p "#{root}/abc"
185
+ FileUtils.touch "#{root}/abc/1"
186
+ FileUtils.touch "#{root}/abc/2"
187
+ FileUtils.touch "#{root}/abc/important.txt"
188
+ }
189
+
190
+ subject {
191
+ PathSpec.new(gitignore).match_tree(root)
192
+ }
193
+
194
+ it { is_expected.to include "#{root}/abc".to_s }
195
+ it { is_expected.to include "#{root}/abc/1".to_s }
196
+ it { is_expected.not_to include "#{root}/abc/important.txt".to_s }
197
+ it { is_expected.not_to include "#{root}".to_s }
198
+ end
199
+
200
+ context "windows" do
201
+ let(:root) {'C:/project'}
202
+ let(:gitignore) { <<-GITIGNORE
203
+ !**/important.txt
204
+ abc/**
205
+ GITIGNORE
206
+ }
207
+
208
+ before(:each) {
209
+ FileUtils.mkdir_p root
210
+ FileUtils.mkdir_p "#{root}/abc"
211
+ FileUtils.touch "#{root}/abc/1"
212
+ FileUtils.touch "#{root}/abc/2"
213
+ FileUtils.touch "#{root}/abc/important.txt"
214
+ }
215
+
216
+ subject {
217
+ PathSpec.new(gitignore).match_tree(root)
218
+ }
219
+
220
+ it { is_expected.to include "#{root}/abc".to_s }
221
+ it { is_expected.to include "#{root}/abc/1".to_s }
222
+ it { is_expected.not_to include "#{root}/abc/important.txt".to_s }
223
+ it { is_expected.not_to include "#{root}".to_s }
224
+ end
225
+ end
226
+
227
+ context "#match_paths" do
228
+ let(:gitignore) { <<-GITIGNORE
229
+ !**/important.txt
230
+ /abc/**
231
+ GITIGNORE
232
+ }
233
+
234
+ context "with no root arg" do
235
+ subject { PathSpec.new(gitignore).match_paths(['/abc/important.txt', '/abc/', '/abc/1']) }
236
+
237
+ it { is_expected.to include "/abc/" }
238
+ it { is_expected.to include "/abc/1" }
239
+ it { is_expected.not_to include "/abc/important.txt" }
240
+ end
241
+
242
+ context 'relative to non-root dir' do
243
+ subject { PathSpec.new(gitignore).match_paths([
244
+ '/def/abc/important.txt',
245
+ '/def/abc/',
246
+ '/def/abc/1'], '/def') }
247
+
248
+ it { is_expected.to include "/def/abc/" }
249
+ it { is_expected.to include "/def/abc/1" }
250
+ it { is_expected.not_to include "/def/abc/important.txt" }
251
+ end
252
+
253
+ context 'relative to windows drive letter' do
254
+ subject { PathSpec.new(gitignore).match_paths([
255
+ 'C:/def/abc/important.txt',
256
+ 'C:/def/abc/',
257
+ 'C:/def/abc/1'], 'C:/def/') }
258
+
259
+ it { is_expected.to include "C:/def/abc/" }
260
+ it { is_expected.to include "C:/def/abc/1" }
261
+ it { is_expected.not_to include "C:/def/abc/important.txt" }
262
+ end
263
+ end
264
+
265
+ # Example to exclude everything except a specific directory foo/bar (note
266
+ # the /* - without the slash, the wildcard would also exclude everything
267
+ # within foo/bar): (from git-scm.com)
268
+ context "very specific gitignore" do
269
+ let(:gitignore) { <<-GITIGNORE
270
+ # exclude everything except directory foo/bar
271
+ /*
272
+ !/foo
273
+ /foo/*
274
+ !/foo/bar
275
+ GITIGNORE
276
+ }
277
+
278
+ subject { PathSpec.new(gitignore) }
279
+
280
+ it { is_expected.not_to match("foo/bar") }
281
+ it { is_expected.to match("anything") }
282
+ it { is_expected.to match("foo/otherthing") }
283
+ end
284
+
285
+ context "#empty" do
286
+ let(:gitignore) { <<-GITIGNORE
287
+ # A comment
288
+ GITIGNORE
289
+ }
290
+
291
+ subject { PathSpec.new gitignore }
292
+
293
+ it 'is empty when there are no valid lines' do
294
+ expect(subject.empty?).to be true
295
+ end
296
+ end
297
+
298
+ context "regex file" do
299
+ let(:regexfile) { <<-REGEX
300
+ ab*a
301
+ REGEX
302
+ }
303
+
304
+ subject { PathSpec.new regexfile, :regex}
305
+
306
+ it "matches the regex" do
307
+ expect(subject.match('anna')).to be false
308
+ expect(subject.match('abba')).to be true
309
+ end
310
+ end
311
+
312
+ context "unsuppored spec type" do
313
+ let(:file) { <<-REGEX
314
+ This is some kind of nonsense.
315
+ REGEX
316
+ }
317
+
318
+ it "does not allow an unknown spec type" do
319
+ expect { PathSpec.new file, :foo}.to raise_error
320
+ end
321
+ end
322
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pathspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon High
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-11 00:00:00.000000000 Z
11
+ date: 2017-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -51,7 +51,11 @@ files:
51
51
  - lib/pathspec/gitignorespec.rb
52
52
  - lib/pathspec/regexspec.rb
53
53
  - lib/pathspec/spec.rb
54
- homepage: http://rubygems.org/gems/pathspec
54
+ - spec/spec_helper.rb
55
+ - spec/unit/pathspec/gitignorespec_spec.rb
56
+ - spec/unit/pathspec/spec_spec.rb
57
+ - spec/unit/pathspec_spec.rb
58
+ homepage: https://github.com/highb/pathspec-ruby
55
59
  licenses:
56
60
  - Apache
57
61
  metadata: {}
@@ -75,4 +79,8 @@ rubygems_version: 2.2.2
75
79
  signing_key:
76
80
  specification_version: 4
77
81
  summary: 'PathSpec: for matching path patterns'
78
- test_files: []
82
+ test_files:
83
+ - spec/spec_helper.rb
84
+ - spec/unit/pathspec/gitignorespec_spec.rb
85
+ - spec/unit/pathspec/spec_spec.rb
86
+ - spec/unit/pathspec_spec.rb