prj 0.1.0 → 1.0.0
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/Gemfile +2 -3
- data/Gemfile.lock +4 -11
- data/README.md +5 -2
- data/Rakefile +6 -0
- data/VERSION +1 -1
- data/bin/prj +1 -1
- data/ext/prj/extconf.rb +4 -0
- data/ext/prj/finder.c +127 -0
- data/lib/prj/app.rb +10 -5
- data/lib/prj/finder.bundle +0 -0
- data/spec/acceptance/app_spec.rb +31 -10
- data/spec/lib/prj/finder_spec.rb +22 -10
- metadata +7 -20
- data/lib/prj/finder.rb +0 -25
data/Gemfile
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
group :development do
|
4
4
|
gem "rspec", "~> 2.11"
|
5
5
|
gem "rake", "~> 0.9"
|
6
6
|
gem 'simplecov', "~> 0.6", :require => false
|
7
|
-
gem "
|
8
|
-
gem 'pry'
|
7
|
+
gem "rake-compiler"
|
9
8
|
end
|
10
9
|
|
11
10
|
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,11 @@
|
|
1
1
|
GEM
|
2
|
-
remote:
|
2
|
+
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
coderay (1.0.7)
|
5
4
|
diff-lcs (1.1.3)
|
6
|
-
fakefs (0.4.2)
|
7
|
-
method_source (0.8)
|
8
5
|
multi_json (1.3.7)
|
9
|
-
pry (0.9.10)
|
10
|
-
coderay (~> 1.0.5)
|
11
|
-
method_source (~> 0.8)
|
12
|
-
slop (~> 3.3.1)
|
13
6
|
rake (0.9.5)
|
7
|
+
rake-compiler (0.8.3)
|
8
|
+
rake
|
14
9
|
rspec (2.12.0)
|
15
10
|
rspec-core (~> 2.12.0)
|
16
11
|
rspec-expectations (~> 2.12.0)
|
@@ -23,14 +18,12 @@ GEM
|
|
23
18
|
multi_json (~> 1.0)
|
24
19
|
simplecov-html (~> 0.7.1)
|
25
20
|
simplecov-html (0.7.1)
|
26
|
-
slop (3.3.2)
|
27
21
|
|
28
22
|
PLATFORMS
|
29
23
|
ruby
|
30
24
|
|
31
25
|
DEPENDENCIES
|
32
|
-
fakefs (~> 0.4)
|
33
|
-
pry
|
34
26
|
rake (~> 0.9)
|
27
|
+
rake-compiler
|
35
28
|
rspec (~> 2.11)
|
36
29
|
simplecov (~> 0.6)
|
data/README.md
CHANGED
@@ -14,6 +14,8 @@ contains supplied letters in given order. The search is scoped by projects root
|
|
14
14
|
directory, which is specified in ~/.prj.yml config file (Default: ~/Projects).
|
15
15
|
See Installation & Configuration section.
|
16
16
|
|
17
|
+
*now even faster with C extension*
|
18
|
+
|
17
19
|
Installation & Configuration:
|
18
20
|
-----------------------------
|
19
21
|
1. Install the gem:
|
@@ -27,8 +29,9 @@ Installation & Configuration:
|
|
27
29
|
3. Put a project root directory name into ~/.prj.yml, i.e:
|
28
30
|
```
|
29
31
|
projects_root: ~/Projects
|
30
|
-
case_sensitive: false
|
31
|
-
|
32
|
+
case_sensitive: false # default: true
|
33
|
+
search_nested_repositories: false # default: false (slower if true)
|
34
|
+
vcs_directories: # default: [.git]
|
32
35
|
- .git
|
33
36
|
- .svn
|
34
37
|
- .hg
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
1.0.0
|
data/bin/prj
CHANGED
data/ext/prj/extconf.rb
ADDED
data/ext/prj/finder.c
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
#include <sys/types.h>
|
4
|
+
#include <sys/stat.h>
|
5
|
+
#include <fts.h>
|
6
|
+
|
7
|
+
void Init_finder();
|
8
|
+
|
9
|
+
VALUE prj_finder_initialize_m(VALUE self, VALUE projectsRoot, VALUE options);
|
10
|
+
VALUE expand_path(VALUE path);
|
11
|
+
|
12
|
+
VALUE prj_finder_find_project_directories_m(VALUE self);
|
13
|
+
VALUE prj_finder_traverse_projects_root(VALUE self, char *projectsRoot, int searchNestedRepositories, VALUE (*callback)(VALUE self, const FTS *fs, const FTSENT *parent, const FTSENT *child));
|
14
|
+
VALUE prj_finder_collect_project(VALUE self, const FTS *fs, const FTSENT *parent, const FTSENT *child);
|
15
|
+
char * prj_finder_normalize_path(char *path);
|
16
|
+
VALUE prj_finder_is_vcs_dir(VALUE self, const char *dir);
|
17
|
+
|
18
|
+
VALUE Prj, PrjFinder;
|
19
|
+
|
20
|
+
void Init_finder() {
|
21
|
+
Prj = rb_define_module("Prj");
|
22
|
+
PrjFinder = rb_define_class_under(Prj, "Finder", rb_cObject);
|
23
|
+
rb_define_method(PrjFinder, "initialize", prj_finder_initialize_m, 2);
|
24
|
+
rb_define_method(PrjFinder, "find_project_directories", prj_finder_find_project_directories_m, 0);
|
25
|
+
}
|
26
|
+
|
27
|
+
VALUE prj_finder_initialize_m(VALUE self, VALUE projectsRoot, VALUE options) {
|
28
|
+
VALUE expandedProjectsRoot, vcsDirectories, searchNestedRepositories;
|
29
|
+
expandedProjectsRoot = expand_path(projectsRoot);
|
30
|
+
|
31
|
+
vcsDirectories = rb_hash_aref(options, ID2SYM(rb_intern("vcs_directories")));
|
32
|
+
|
33
|
+
if (!RTEST(vcsDirectories)) {
|
34
|
+
vcsDirectories = rb_ary_new();
|
35
|
+
}
|
36
|
+
|
37
|
+
searchNestedRepositories = rb_hash_aref(options, ID2SYM(rb_intern("search_nested_repositories")));
|
38
|
+
|
39
|
+
rb_iv_set(self, "@root", expandedProjectsRoot);
|
40
|
+
rb_iv_set(self, "@vcs_directories", rb_check_array_type(vcsDirectories));
|
41
|
+
rb_iv_set(self, "@search_nested_repositories", searchNestedRepositories);
|
42
|
+
rb_iv_set(self, "@project_directories", rb_ary_new());
|
43
|
+
return self;
|
44
|
+
}
|
45
|
+
|
46
|
+
VALUE expand_path(VALUE path) {
|
47
|
+
VALUE file;
|
48
|
+
file = rb_const_get(rb_cObject, rb_intern("File"));
|
49
|
+
return rb_funcall(file, rb_intern("expand_path"), 1, rb_check_string_type(path));
|
50
|
+
}
|
51
|
+
|
52
|
+
VALUE prj_finder_find_project_directories_m(VALUE self) {
|
53
|
+
VALUE result, root, searchNestedRepositories;
|
54
|
+
|
55
|
+
result = rb_iv_get(self, "@project_directories");
|
56
|
+
root = rb_iv_get(self, "@root");
|
57
|
+
searchNestedRepositories = rb_iv_get(self, "@search_nested_repositories");
|
58
|
+
|
59
|
+
if (RARRAY_LEN(result) == 0) {
|
60
|
+
prj_finder_traverse_projects_root(self, StringValueCStr(root), RTEST(searchNestedRepositories), &prj_finder_collect_project);
|
61
|
+
}
|
62
|
+
|
63
|
+
return result;
|
64
|
+
}
|
65
|
+
|
66
|
+
VALUE prj_finder_traverse_projects_root(VALUE self, char *projectsRoot, int searchNestedRepositories, VALUE (*callback)(VALUE self, const FTS *fs, const FTSENT *parent, const FTSENT *child)) {
|
67
|
+
FTS *fs = NULL;
|
68
|
+
FTSENT *child = NULL;
|
69
|
+
FTSENT *parent = NULL;
|
70
|
+
|
71
|
+
char *paths[] = { projectsRoot, NULL };
|
72
|
+
fs = fts_open(paths, FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOSTAT, NULL);
|
73
|
+
|
74
|
+
if (fs == NULL) {
|
75
|
+
return Qnil;
|
76
|
+
}
|
77
|
+
|
78
|
+
while ((parent = fts_read(fs)) != NULL) {
|
79
|
+
child = fts_children(fs, 0);
|
80
|
+
while (child != NULL) {
|
81
|
+
if (child->fts_info == FTS_D && RTEST(callback(self, fs, parent, child))) {
|
82
|
+
fts_set(fs, (searchNestedRepositories ? child : parent), FTS_SKIP);
|
83
|
+
}
|
84
|
+
child = child->fts_link;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
fts_close(fs);
|
88
|
+
|
89
|
+
return Qnil;
|
90
|
+
}
|
91
|
+
|
92
|
+
VALUE prj_finder_collect_project(VALUE self, const FTS *fs, const FTSENT *parent, const FTSENT *child) {
|
93
|
+
VALUE root, result;
|
94
|
+
char *prefixed, *unprefixed;
|
95
|
+
|
96
|
+
if (!prj_finder_is_vcs_dir(self, child->fts_name)) {
|
97
|
+
return Qfalse;
|
98
|
+
}
|
99
|
+
|
100
|
+
root = rb_iv_get(self, "@root");
|
101
|
+
result = rb_iv_get(self, "@project_directories");
|
102
|
+
|
103
|
+
prefixed = parent->fts_path;
|
104
|
+
unprefixed = prefixed + RSTRING_LEN(root);
|
105
|
+
|
106
|
+
rb_ary_push(result, rb_str_new2(prj_finder_normalize_path(unprefixed)));
|
107
|
+
return Qtrue;
|
108
|
+
}
|
109
|
+
|
110
|
+
// strips unnecessary trailing / on linux
|
111
|
+
char * prj_finder_normalize_path(char *path) {
|
112
|
+
size_t path_length;
|
113
|
+
path_length = strlen(path);
|
114
|
+
|
115
|
+
if (path[path_length - 1] == '/') {
|
116
|
+
path[path_length - 1] = '\0';
|
117
|
+
}
|
118
|
+
|
119
|
+
return path;
|
120
|
+
}
|
121
|
+
|
122
|
+
VALUE prj_finder_is_vcs_dir(VALUE self, const char *dir) {
|
123
|
+
VALUE vcsDirectories;
|
124
|
+
vcsDirectories = rb_iv_get(self, "@vcs_directories");
|
125
|
+
return rb_funcall(vcsDirectories, rb_intern("include?"), 1, rb_str_new2(dir));
|
126
|
+
}
|
127
|
+
|
data/lib/prj/app.rb
CHANGED
@@ -18,11 +18,11 @@ module Prj
|
|
18
18
|
@output.puts config.fetch("projects_root")
|
19
19
|
return 0
|
20
20
|
end
|
21
|
-
finder = Finder.new(config.fetch("projects_root"), config
|
22
|
-
filter = Filter.new(@letters, config.fetch("case_sensitive")
|
21
|
+
finder = Finder.new(config.fetch("projects_root"), symbolize_keys(config))
|
22
|
+
filter = Filter.new(@letters, config.fetch("case_sensitive"))
|
23
23
|
directories = finder.find_project_directories
|
24
24
|
filtered_directories = filter.filter(directories)
|
25
|
-
target_directory = File.join(config.fetch("projects_root"), filtered_directories.first.to_s)
|
25
|
+
target_directory = File.expand_path(File.join(config.fetch("projects_root"), filtered_directories.first.to_s))
|
26
26
|
@output.puts target_directory
|
27
27
|
0
|
28
28
|
end
|
@@ -38,10 +38,15 @@ module Prj
|
|
38
38
|
|
39
39
|
def default_config
|
40
40
|
default_config = {
|
41
|
-
"projects_root"
|
42
|
-
"vcs_directories" => [".git"]
|
41
|
+
"projects_root" => File.expand_path("~/Projects"),
|
42
|
+
"vcs_directories" => [".git"],
|
43
|
+
"case_sensitive" => true
|
43
44
|
}
|
44
45
|
end
|
46
|
+
|
47
|
+
def symbolize_keys(hash)
|
48
|
+
Hash[hash.map { |k, v| [k.to_sym, v] }]
|
49
|
+
end
|
45
50
|
end
|
46
51
|
|
47
52
|
end
|
Binary file
|
data/spec/acceptance/app_spec.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require '
|
2
|
+
require 'tmpdir'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'prj'
|
5
5
|
require 'stringio'
|
6
6
|
require 'yaml'
|
7
7
|
|
8
8
|
describe "Prj::App" do
|
9
|
-
include FakeFS::SpecHelpers
|
10
|
-
|
11
9
|
let(:output) { StringIO.new }
|
12
|
-
let(:root) { "/Projects" }
|
13
10
|
let(:subdirectories) do
|
14
11
|
[
|
15
12
|
"foo/.git/",
|
13
|
+
"foo/baz/.git/",
|
16
14
|
"bar/",
|
17
15
|
"baz/qux/crisp/.git/",
|
18
16
|
"baz/craps/poops/.git/",
|
@@ -20,9 +18,12 @@ describe "Prj::App" do
|
|
20
18
|
].map { |d| File.join(root, d) }
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
around(:each) do |example|
|
22
|
+
Dir.mktmpdir("Projects") do |root|
|
23
|
+
@root = root
|
24
|
+
subdirectories.each { |d| FileUtils.mkdir_p(d) }
|
25
|
+
example.run
|
26
|
+
end
|
26
27
|
end
|
27
28
|
|
28
29
|
context "within projcts root" do
|
@@ -36,7 +37,7 @@ describe "Prj::App" do
|
|
36
37
|
it "prints projects root and returns 0 if directory not found" do
|
37
38
|
with_config("projects_root" => root) do
|
38
39
|
Prj::App.new(output, ["nothingtofind"]).run.should == 0
|
39
|
-
output.string.chomp.should == root
|
40
|
+
output.string.chomp.should == root
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
@@ -46,6 +47,20 @@ describe "Prj::App" do
|
|
46
47
|
output.string.chomp.should == File.join(root, "foo")
|
47
48
|
end
|
48
49
|
end
|
50
|
+
|
51
|
+
it "does not search nested repos by default" do
|
52
|
+
with_config("projects_root" => root) do
|
53
|
+
Prj::App.new(output, ["fob"]).run.should == 0
|
54
|
+
output.string.chomp.should_not == File.join(root, "foo/baz")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "can search nested repos if 'search_nested_repositories' option is true" do
|
59
|
+
with_config("projects_root" => root, "search_nested_repositories" => true) do
|
60
|
+
Prj::App.new(output, ["fob"]).run.should == 0
|
61
|
+
output.string.chomp.should == File.join(root, "foo/baz")
|
62
|
+
end
|
63
|
+
end
|
49
64
|
end
|
50
65
|
|
51
66
|
it "uses ~/.prj.yml as config file" do
|
@@ -53,17 +68,23 @@ describe "Prj::App" do
|
|
53
68
|
end
|
54
69
|
|
55
70
|
it "defaults to ~/Projects as default projects root" do
|
56
|
-
|
71
|
+
with_config do
|
72
|
+
Prj::App.new(output, ["asdf"]).config.fetch("projects_root").should == File.expand_path("~/Projects")
|
73
|
+
end
|
57
74
|
end
|
58
75
|
|
59
76
|
def with_config(config = {})
|
60
77
|
tmp = Prj::App.config_path
|
61
|
-
config_path = ".prj.yml"
|
78
|
+
config_path = File.join(Dir.tmpdir, ".prj.yml")
|
62
79
|
File.open(config_path, "w") { |f| f.write YAML.dump(config) }
|
63
80
|
Prj::App.config_path = config_path
|
64
81
|
yield
|
65
82
|
ensure
|
66
83
|
Prj::App.config_path = tmp
|
67
84
|
end
|
85
|
+
|
86
|
+
def root
|
87
|
+
@root
|
88
|
+
end
|
68
89
|
end
|
69
90
|
|
data/spec/lib/prj/finder_spec.rb
CHANGED
@@ -1,34 +1,46 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require '
|
2
|
+
require 'tmpdir'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'prj/finder'
|
5
5
|
|
6
6
|
describe "Prj::Finder" do
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
around(:each) do |example|
|
8
|
+
Dir.mktmpdir("projects") do |root|
|
9
|
+
@root = root
|
10
|
+
example.run
|
11
|
+
end
|
12
|
+
end
|
10
13
|
|
11
14
|
it "finds directories containing .git/ scoped to given root and returns their relative paths" do
|
12
15
|
make_directories(root, "/foo/.git", "/bar/qux", "/baz/.git")
|
13
|
-
finder(".git").find_project_directories.should =~ ["/foo", "/baz"]
|
16
|
+
finder(:vcs_directories => [".git"]).find_project_directories.should =~ ["/foo", "/baz"]
|
14
17
|
end
|
15
18
|
|
16
|
-
it "does not find
|
19
|
+
it "does not find nested repos unless :search_nested_repositories" do
|
17
20
|
make_directories(root, "/foo/.git", "/foo/bar", "/foo/baz/.git")
|
18
|
-
finder(".git").find_project_directories.should =~ ["/foo"]
|
21
|
+
finder(:vcs_directories => [".git"]).find_project_directories.should =~ ["/foo"]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "finds nested repos if :search_nested_repositories option is true" do
|
25
|
+
make_directories(root, "/foo/.git", "/foo/bar", "/foo/baz/.git")
|
26
|
+
finder(:vcs_directories => [".git"], :search_nested_repositories => true).find_project_directories.should =~ ["/foo", "/foo/baz"]
|
19
27
|
end
|
20
28
|
|
21
29
|
it "does support other vcs repos" do
|
22
30
|
make_directories(root, "/foo/.git", "/bar/.svn", "/foo/baz/.unknown")
|
23
|
-
finder(".git", ".svn").find_project_directories.should =~ ["/foo", "/bar"]
|
31
|
+
finder(:vcs_directories => [".git", ".svn"]).find_project_directories.should =~ ["/foo", "/bar"]
|
24
32
|
end
|
25
33
|
|
26
34
|
def make_directories(root, *directories)
|
27
35
|
directories.each { |d| FileUtils.mkdir_p(File.join(root, d)) }
|
28
36
|
end
|
29
37
|
|
30
|
-
def finder(
|
31
|
-
Prj::Finder.new(root,
|
38
|
+
def finder(options = {})
|
39
|
+
Prj::Finder.new(root, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def root
|
43
|
+
@root
|
32
44
|
end
|
33
45
|
end
|
34
46
|
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: prj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version:
|
5
|
+
version: 1.0.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Vladimir Yarotsky
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -59,22 +59,6 @@ dependencies:
|
|
59
59
|
version: '0.6'
|
60
60
|
type: :development
|
61
61
|
prerelease: false
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: fakefs
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ~>
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0.4'
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
71
|
-
none: false
|
72
|
-
requirements:
|
73
|
-
- - ~>
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0.4'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
62
|
description: ! ' Prj is an utility to quickly go to project directory using fuzzy
|
79
63
|
matching
|
80
64
|
|
@@ -82,15 +66,18 @@ description: ! ' Prj is an utility to quickly go to project directory using f
|
|
82
66
|
email: vladimir.yarotksy@gmail.com
|
83
67
|
executables:
|
84
68
|
- prj
|
85
|
-
extensions:
|
69
|
+
extensions:
|
70
|
+
- ext/prj/extconf.rb
|
86
71
|
extra_rdoc_files: []
|
87
72
|
files:
|
88
73
|
- bin/prj
|
89
74
|
- lib/prj/app.rb
|
90
75
|
- lib/prj/dir_with_score.rb
|
91
76
|
- lib/prj/filter.rb
|
92
|
-
- lib/prj/finder.
|
77
|
+
- lib/prj/finder.bundle
|
93
78
|
- lib/prj.rb
|
79
|
+
- ext/prj/extconf.rb
|
80
|
+
- ext/prj/finder.c
|
94
81
|
- spec/acceptance/app_spec.rb
|
95
82
|
- spec/lib/prj/filter_spec.rb
|
96
83
|
- spec/lib/prj/finder_spec.rb
|
data/lib/prj/finder.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'find'
|
2
|
-
|
3
|
-
module Prj
|
4
|
-
|
5
|
-
class Finder
|
6
|
-
def initialize(root, vcs_directories)
|
7
|
-
@root = File.expand_path(root)
|
8
|
-
@vcs_directories = Array(vcs_directories)
|
9
|
-
end
|
10
|
-
|
11
|
-
##
|
12
|
-
# Returns directories containing .git/ directory, relative to @root
|
13
|
-
#
|
14
|
-
def find_project_directories
|
15
|
-
subdirectories = []
|
16
|
-
Find.find(@root) do |d|
|
17
|
-
if @vcs_directories.any? { |vcs_dir| Dir.exists?(File.join(d, vcs_dir)) }
|
18
|
-
subdirectories << d && Find.prune
|
19
|
-
end
|
20
|
-
end
|
21
|
-
subdirectories.map { |r| r.gsub(@root, "") }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|