prj 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,11 +1,10 @@
1
- source :rubygems
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 "fakefs", "~> 0.4", :require => "fakefs/safe"
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: http://rubygems.org/
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
- vcs_directories:
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
@@ -24,3 +24,9 @@ end
24
24
  task :all_specs => [:spec, :acceptance_spec]
25
25
  task :default => [:all_specs]
26
26
 
27
+ require 'rake/extensiontask'
28
+ Rake::ExtensionTask.new do |t|
29
+ t.name = 'finder'
30
+ t.ext_dir = 'ext/prj'
31
+ t.lib_dir = 'lib/prj'
32
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 1.0.0
data/bin/prj CHANGED
@@ -2,7 +2,7 @@
2
2
  # -*- mode: ruby -*-
3
3
  # vi: set ft=ruby :
4
4
 
5
- require 'rubygems'
5
+ $:.unshift File.expand_path("../../lib", __FILE__)
6
6
  require 'prj'
7
7
 
8
8
  include Prj
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('finder')
4
+ create_makefile('finder')
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.fetch("vcs_directories"))
22
- filter = Filter.new(@letters, config.fetch("case_sensitive") { true })
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" => File.expand_path("~/Projects"),
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
@@ -1,18 +1,16 @@
1
1
  require 'spec_helper'
2
- require 'fakefs/spec_helpers'
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
- before(:each) do
24
- FileUtils.mkdir_p(root)
25
- subdirectories.each { |d| FileUtils.mkdir_p(d) }
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
- Prj::App.new(output, ["asdf"]).config.fetch("projects_root").should == File.expand_path("~/Projects")
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
 
@@ -1,34 +1,46 @@
1
1
  require 'spec_helper'
2
- require 'fakefs/spec_helpers'
2
+ require 'tmpdir'
3
3
  require 'fileutils'
4
4
  require 'prj/finder'
5
5
 
6
6
  describe "Prj::Finder" do
7
- include FakeFS::SpecHelpers
8
-
9
- let(:root) { "~/projects" }
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 directories nested under directory containing .git/" do
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(*vcs_directories)
31
- Prj::Finder.new(root, vcs_directories)
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: 0.1.0
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-09 00:00:00.000000000 Z
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.rb
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