globby 0.0.1
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.
- data/LICENSE.txt +20 -0
- data/README.md +34 -0
- data/Rakefile +9 -0
- data/lib/globby.rb +54 -0
- data/spec/globby_spec.rb +157 -0
- metadata +74 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Jon Jensen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# globby
|
2
|
+
|
3
|
+
globby is a [.gitignore](http://www.kernel.org/pub/software/scm/git/docs/gitignore.html)-compatible file globber for ruby
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Globby.new(rules).matches
|
8
|
+
|
9
|
+
### An example:
|
10
|
+
|
11
|
+
> rules = File.read('.gitignore').split(/\n/)
|
12
|
+
> pp Globby.new(rules).matches
|
13
|
+
["Gemfile.lock",
|
14
|
+
"doc/Foreigner.html",
|
15
|
+
"doc/Foreigner/Adapter.html",
|
16
|
+
"doc/Gemfile.html",
|
17
|
+
"doc/Immigrant.html",
|
18
|
+
...
|
19
|
+
"immigrant-0.1.3.gem",
|
20
|
+
"immigrant-0.1.4.gem"]
|
21
|
+
=> nil
|
22
|
+
|
23
|
+
## Why on earth would I ever use this?
|
24
|
+
|
25
|
+
* You're curious what is getting `.gitignore`'d and/or you want to do something
|
26
|
+
with those files.
|
27
|
+
* You're writing a library/tool that will have its own list of ignored/tracked
|
28
|
+
files. My use case is for an I18n library that extracts strings from ruby
|
29
|
+
files... I need to provide users a nice configurable way to whitelist given
|
30
|
+
files/directories/patterns.
|
31
|
+
|
32
|
+
## License
|
33
|
+
|
34
|
+
Copyright (c) 2012 Jon Jensen, released under the MIT license
|
data/Rakefile
ADDED
data/lib/globby.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
class Globby
|
4
|
+
def initialize(patterns = [])
|
5
|
+
@patterns = patterns
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches
|
9
|
+
result = Set.new
|
10
|
+
@patterns.each do |pattern|
|
11
|
+
if pattern[0, 1] == '!'
|
12
|
+
result.subtract matches_for(pattern[1..-1])
|
13
|
+
else
|
14
|
+
result.merge matches_for(pattern)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
result.to_a.sort
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches_for(pattern)
|
21
|
+
return [] unless pattern = normalize(pattern)
|
22
|
+
expects_dir = pattern.sub!(/\/\z/, '')
|
23
|
+
|
24
|
+
files = Dir.glob(pattern, File::FNM_PATHNAME)
|
25
|
+
result = []
|
26
|
+
files.each do |file|
|
27
|
+
next if ['.', '..'].include?(File.basename(file))
|
28
|
+
if directory?(file)
|
29
|
+
result.concat matches_for("/" + file + "/**/{*,.*}")
|
30
|
+
elsif !expects_dir
|
31
|
+
result << file
|
32
|
+
end
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def normalize(pattern)
|
38
|
+
pattern = pattern.strip
|
39
|
+
first = pattern[0, 1]
|
40
|
+
if pattern.empty? || first == '#'
|
41
|
+
nil
|
42
|
+
elsif first == '/'
|
43
|
+
pattern[1..-1]
|
44
|
+
else
|
45
|
+
"**/" + pattern # could be anywhere
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def directory?(pattern)
|
52
|
+
File.directory?(pattern) && !File.symlink?(pattern)
|
53
|
+
end
|
54
|
+
end
|
data/spec/globby_spec.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'globby'
|
2
|
+
RSpec.configure { |config| config.mock_framework = :mocha }
|
3
|
+
|
4
|
+
# A blank line matches no files, so it can serve as a separator for
|
5
|
+
# readability.
|
6
|
+
#
|
7
|
+
# A line starting with # serves as a comment.
|
8
|
+
#
|
9
|
+
# An optional prefix ! which negates the pattern; any matching file excluded by
|
10
|
+
# a previous pattern will become included again. If a negated pattern matches,
|
11
|
+
# this will override lower precedence patterns sources.
|
12
|
+
#
|
13
|
+
# If the pattern ends with a slash, it is removed for the purpose of the
|
14
|
+
# following description, but it would only find a match with a directory. In
|
15
|
+
# other words, foo/ will match a directory foo and paths underneath it, but
|
16
|
+
# will not match a regular file or a symbolic link foo (this is consistent with
|
17
|
+
# the way how pathspec works in general in git).
|
18
|
+
#
|
19
|
+
# If the pattern does not contain a slash /, git treats it as a shell glob
|
20
|
+
# pattern and checks for a match against the pathname relative to the location
|
21
|
+
# of the .gitignore file (relative to the toplevel of the work tree if not from
|
22
|
+
# a .gitignore file).
|
23
|
+
#
|
24
|
+
# Otherwise, git treats the pattern as a shell glob suitable for consumption by
|
25
|
+
# fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not
|
26
|
+
# match a / in the pathname. For example, "Documentation/*.html" matches
|
27
|
+
# "Documentation/git.html" but not "Documentation/ppc/ppc.html" or
|
28
|
+
# "tools/perf/Documentation/perf.html".
|
29
|
+
#
|
30
|
+
# A leading slash matches the beginning of the pathname. For example, "/*.c"
|
31
|
+
# matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
32
|
+
|
33
|
+
describe Globby do
|
34
|
+
describe "#matches_for" do
|
35
|
+
|
36
|
+
let(:globby) { Globby.new }
|
37
|
+
|
38
|
+
context "a blank line" do
|
39
|
+
it "should return nothing" do
|
40
|
+
Dir.expects(:glob).never
|
41
|
+
globby.matches_for("").should == []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "a comment" do
|
46
|
+
it "should return nothing" do
|
47
|
+
Dir.expects(:glob).never
|
48
|
+
globby.matches_for("#comment").should == []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "a pattern ending in a slash" do
|
53
|
+
it "should return a matching directory's contents" do
|
54
|
+
globby.stubs(:directory?).returns true, false
|
55
|
+
Dir.expects(:glob).twice.returns ["foo"], ['foo/bar']
|
56
|
+
globby.matches_for("foo/").should == ['foo/bar']
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should ignore symlinks and regular files" do
|
60
|
+
globby.stubs(:directory?).returns false
|
61
|
+
Dir.expects(:glob).once.returns ["foo"]
|
62
|
+
globby.matches_for("foo/").should == []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "a pattern without a slash" do
|
67
|
+
it "should return all glob matches" do
|
68
|
+
Dir.expects(:glob).with{ |*args| args.first == "**/*rb"}.returns []
|
69
|
+
globby.matches_for("*rb")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "a pattern with a slash" do
|
74
|
+
it "should return all glob matches" do
|
75
|
+
Dir.expects(:glob).with{ |*args| args.first == "**/foo/bar"}.returns []
|
76
|
+
globby.matches_for("foo/bar")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "a pattern starting in a slash" do
|
81
|
+
it "should return all root glob matches" do
|
82
|
+
Dir.expects(:glob).with{ |*args| args.first == "foo/bar"}.returns []
|
83
|
+
globby.matches_for("/foo/bar")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#matches" do
|
89
|
+
it "should match gitignore perfectly" do
|
90
|
+
require 'tmpdir'
|
91
|
+
require 'fileutils'
|
92
|
+
|
93
|
+
files = <<-FILES.strip.split(/\s+/)
|
94
|
+
.gitignore
|
95
|
+
foo.rb
|
96
|
+
foo.html
|
97
|
+
bar
|
98
|
+
baz/lol.txt
|
99
|
+
foo/.hidden
|
100
|
+
foo/bar.rb
|
101
|
+
foo/bar.html
|
102
|
+
foo/bar/baz.pdf
|
103
|
+
foobar/.hidden
|
104
|
+
foobar/baz.txt
|
105
|
+
foobar/baz.rb
|
106
|
+
foobar/baz/lol.wut
|
107
|
+
FILES
|
108
|
+
|
109
|
+
ignore = <<-IGNORE.gsub(/^ +/, '')
|
110
|
+
# here we go...
|
111
|
+
|
112
|
+
# some dotfiles
|
113
|
+
.hidden
|
114
|
+
|
115
|
+
# html, but just in the root
|
116
|
+
/*.html
|
117
|
+
|
118
|
+
# all rb files anywhere
|
119
|
+
*.rb
|
120
|
+
|
121
|
+
# except rb files immediately under foobar
|
122
|
+
!foobar/*.rb
|
123
|
+
|
124
|
+
# this will match foo/bar but not bar
|
125
|
+
bar/
|
126
|
+
|
127
|
+
# this will match nothing
|
128
|
+
foo*bar/baz.pdf
|
129
|
+
|
130
|
+
# this will match baz/ and foobar/baz/
|
131
|
+
baz
|
132
|
+
IGNORE
|
133
|
+
|
134
|
+
Dir.mktmpdir do |dir|
|
135
|
+
Dir.chdir(dir) do
|
136
|
+
File.open('.gitignore', 'w'){ |f| f.write ignore }
|
137
|
+
files.each do |file|
|
138
|
+
FileUtils.mkdir_p File.dirname(file)
|
139
|
+
FileUtils.touch file
|
140
|
+
end
|
141
|
+
|
142
|
+
`git init .`
|
143
|
+
untracked = `git status -uall`.gsub(/.*#\n|#\s+|^nothing.*/m, '').split(/\n/)
|
144
|
+
|
145
|
+
globby = Globby.new(ignore.split(/\n/))
|
146
|
+
ignored = globby.matches
|
147
|
+
|
148
|
+
all_files = Dir.glob('**/*', File::FNM_DOTMATCH | File::FNM_PATHNAME).
|
149
|
+
reject{ |f| f =~ /^\.git\// }.
|
150
|
+
select{ |f| File.symlink?(f) || File.file?(f) }
|
151
|
+
|
152
|
+
all_files.sort.should == (untracked + ignored).sort
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: globby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jon Jensen
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-01-16 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: find files using .gitignore-style globs
|
22
|
+
email: jenseng@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- LICENSE.txt
|
31
|
+
- Rakefile
|
32
|
+
- README.md
|
33
|
+
- lib/globby.rb
|
34
|
+
- spec/globby_spec.rb
|
35
|
+
homepage: http://github.com/jenseng/globby
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 57
|
49
|
+
segments:
|
50
|
+
- 1
|
51
|
+
- 8
|
52
|
+
- 7
|
53
|
+
version: 1.8.7
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 17
|
60
|
+
segments:
|
61
|
+
- 1
|
62
|
+
- 3
|
63
|
+
- 5
|
64
|
+
version: 1.3.5
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.24
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: .gitignore-compatible file globber
|
72
|
+
test_files: []
|
73
|
+
|
74
|
+
has_rdoc:
|