globby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/LICENSE.txt +20 -0
  2. data/README.md +34 -0
  3. data/Rakefile +9 -0
  4. data/lib/globby.rb +54 -0
  5. data/spec/globby_spec.rb +157 -0
  6. metadata +74 -0
@@ -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.
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ task :default => :spec
@@ -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
@@ -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: