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 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