prj 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -3,7 +3,7 @@ GEM
3
3
  specs:
4
4
  coderay (1.0.7)
5
5
  diff-lcs (1.1.3)
6
- fakefs (0.4.1)
6
+ fakefs (0.4.2)
7
7
  method_source (0.8)
8
8
  multi_json (1.3.7)
9
9
  pry (0.9.10)
data/README.md CHANGED
@@ -1,26 +1,48 @@
1
1
  Prj
2
2
  ==========
3
3
 
4
+ [![Build Status](https://secure.travis-ci.org/v-yarotsky/prj.png)](http://travis-ci.org/v-yarotsky/prj)
5
+ [![Code Climate](https://codeclimate.com/github/v-yarotsky/prj.png)](https://codeclimate.com/github/v-yarotsky/prj)
6
+ [![Gem Version](https://badge.fury.io/rb/prj.png)](http://badge.fury.io/rb/prj)
7
+
4
8
  Cd to your project the right way!
5
9
 
6
- Prj chooses a project directory based on fuzzy matching
10
+ Prj chooses a project directory based on fuzzy matching.
7
11
 
8
- Usage:
9
- ------
12
+ It finds a project directory (a directory with .git/ or other vcs directory inside) such that it's name
13
+ contains supplied letters in given order. The search is scoped by projects root
14
+ directory, which is specified in ~/.prj.yml config file (Default: ~/Projects).
15
+ See Installation & Configuration section.
10
16
 
11
- Put this into your .zshrc (.bashrc)
17
+ Installation & Configuration:
18
+ -----------------------------
19
+ 1. Install the gem:
12
20
 
13
- ```
14
- function p() {
15
- builtin cd "$(prj $1)"
16
- }
17
- ```
21
+ ```gem install prj```
18
22
 
19
- Put a project root directory into ~/.prj, i.e:
23
+ ([RVM](http://rvm.io) users) check out [this blog post](http://blog.yarotsky.me/2012-12-15-faster-ruby-scripts-startup)
24
+ 2. ([oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh) users) Put [scripts/zsh/prj.plugin.zsh](https://raw.github.com/v-yarotsky/prj/master/scripts/zsh/prj.plugin.zsh) into ``~/.oh-my-zsh/custom/plugins/prj/prj.plugin.zsh``.
25
+ Don't forget to enable the plugin in ~/.zshrc
20
26
 
27
+ 3. Put a project root directory name into ~/.prj.yml, i.e:
28
+ ```
29
+ projects_root: ~/Projects
30
+ vcs_directories:
31
+ - .git
32
+ - .svn
33
+ - .hg
34
+ ```
35
+
36
+ Usage:
37
+ ------
38
+
39
+ With the following directory structure
21
40
  ```
22
- ~/Projects
41
+ ~/
42
+ Projects/
43
+ my_super_project/
44
+ rails/
45
+ love_hate_unicorns/
23
46
  ```
24
-
25
- and run ```p letters_from_your_project_folder_name```
47
+ You can reach ``~/Projects/my_super_project`` with ```p msp```
26
48
 
data/Rakefile CHANGED
@@ -17,10 +17,10 @@ RSpec::Core::RakeTask.new do |t|
17
17
  t.pattern = 'spec/lib/**/*_spec.rb'
18
18
  end
19
19
 
20
- RSpec::Core::RakeTask.new do |t|
20
+ RSpec::Core::RakeTask.new(:acceptance_spec) do |t|
21
21
  t.pattern = 'spec/acceptance/**/*_spec.rb'
22
- t.name = :acceptance_spec
23
22
  end
24
23
 
25
- task :default => [:spec]
24
+ task :all_specs => [:spec, :acceptance_spec]
25
+ task :default => [:all_specs]
26
26
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/bin/prj CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  require 'rubygems'
6
6
  require 'prj'
7
- require 'prj/app'
8
7
 
9
8
  include Prj
10
9
 
data/lib/prj/app.rb CHANGED
@@ -1,48 +1,48 @@
1
- require 'prj/finder'
2
- require 'prj/filter'
1
+ require 'yaml'
3
2
 
4
3
  module Prj
4
+
5
5
  class App
6
6
  class << self
7
- attr_accessor :default_projects_root, :ignore_config
7
+ attr_accessor :config_path
8
8
  end
9
- @default_projects_root = "~/Projects".freeze
10
- @ignore_config = false
9
+ @config_path = File.expand_path("~/.prj.yml").freeze
11
10
 
12
- def initialize(sink, args = [])
11
+ def initialize(output, args = [])
13
12
  @letters = String(args.first).each_char.to_a
14
- @sink = sink
13
+ @output = output
15
14
  end
16
15
 
17
16
  def run
18
17
  if @letters.empty?
19
- @sink.puts projects_root
20
- else
21
- finder = Finder.new(projects_root)
22
- filter = Filter.new(@letters)
23
-
24
- directories = finder.find_project_directories
25
- filtered_directories = filter.filter(directories)
26
-
27
- target_directory = File.join(projects_root, filtered_directories.first.to_s)
28
-
29
- @sink.puts target_directory
18
+ @output.puts config.fetch("projects_root")
19
+ return 0
30
20
  end
31
-
32
- return 0
21
+ finder = Finder.new(config.fetch("projects_root"), config.fetch("vcs_directories"))
22
+ filter = Filter.new(@letters)
23
+ directories = finder.find_project_directories
24
+ filtered_directories = filter.filter(directories)
25
+ target_directory = File.join(config.fetch("projects_root"), filtered_directories.first.to_s)
26
+ @output.puts target_directory
27
+ 0
33
28
  end
34
29
 
35
- def projects_root
36
- @projects_root ||= begin
37
- path = begin
38
- raise "default config" if self.class.ignore_config
39
- File.read(File.expand_path("~/.prj")).chomp
40
- rescue
41
- self.class.default_projects_root
42
- end
43
- File.expand_path(path).freeze
30
+ def config
31
+ @config ||= begin
32
+ config = File.exists?(self.class.config_path) ? YAML.load(File.read(self.class.config_path)) : {}
33
+ default_config.merge(config)
44
34
  end
45
35
  end
36
+
37
+ private
38
+
39
+ def default_config
40
+ default_config = {
41
+ "projects_root" => File.expand_path("~/Projects"),
42
+ "vcs_directories" => [".git"]
43
+ }
44
+ end
46
45
  end
46
+
47
47
  end
48
48
 
@@ -0,0 +1,34 @@
1
+ require 'forwardable'
2
+
3
+ module Prj
4
+
5
+ class DirWithScore
6
+ include Comparable
7
+ extend Forwardable
8
+
9
+ attr_accessor :dir, :score
10
+
11
+ def_delegators :dir, :length, :to_s
12
+
13
+ def initialize(dir, score)
14
+ @dir, @score = dir, score
15
+ end
16
+
17
+ def <=>(other)
18
+ if score < other.score
19
+ -1
20
+ elsif score > other.score
21
+ 1
22
+ else
23
+ if length < other.length
24
+ -1
25
+ elsif length > other.length
26
+ 1
27
+ else
28
+ 0
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ end
data/lib/prj/filter.rb CHANGED
@@ -1,56 +1,30 @@
1
+ require 'prj/dir_with_score'
2
+ require 'strscan'
3
+
1
4
  module Prj
5
+
2
6
  class Filter
3
7
  def initialize(letters)
4
8
  @letters = letters.to_a
5
9
  end
6
10
 
7
11
  def filter(directories)
8
- sort(directories.select { |d| filter_dir(d, @letters) })
12
+ wrapped_with_score(directories).sort.map(&:to_s)
9
13
  end
10
14
 
11
- def dispersion(dir)
12
- return 0 if dir.empty? || @letters.empty?
13
- indices = []
14
- d = dir.dup
15
+ def distance(dir)
16
+ scanner = StringScanner.new(dir)
15
17
  @letters.each do |letter|
16
- idx = d.index(letter)
17
- indices << idx
18
- d = d[(idx + 1)..-1]
19
- end
20
- indices.inject(0, &:+) - indices.first
21
- end
22
-
23
- private
24
-
25
- def filter_dir(dir, letters)
26
- if letters.empty?
27
- true
28
- else
29
- letter = letters.first
30
- i = dir.index(letter)
31
- i && filter_dir(dir[(i + 1)..-1], letters[1..-1])
18
+ scanner.scan(/.*?[#{letter}]/) or return :no_score
32
19
  end
20
+ scanner.pos - @letters.length
33
21
  end
34
22
 
35
- def sort(directories)
36
- directories.sort do |d1, d2|
37
- disp1 = dispersion(d1)
38
- disp2 = dispersion(d2)
39
- if disp1 < disp2
40
- -1
41
- elsif disp1 > disp2
42
- 1
43
- else
44
- if d1.length < d2.length
45
- -1
46
- elsif d1.length > d2.length
47
- 1
48
- else
49
- 0
50
- end
51
- end
52
- end
23
+ def wrapped_with_score(directories)
24
+ directories.map { |d| DirWithScore.new(d, distance(d)) }.reject { |d| d.score == :no_score }
53
25
  end
26
+ private :wrapped_with_score
54
27
  end
28
+
55
29
  end
56
30
 
data/lib/prj/finder.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require 'find'
2
2
 
3
3
  module Prj
4
+
4
5
  class Finder
5
- def initialize(root)
6
+ def initialize(root, vcs_directories)
6
7
  @root = File.expand_path(root)
8
+ @vcs_directories = Array(vcs_directories)
7
9
  end
8
10
 
9
11
  ##
@@ -12,9 +14,12 @@ module Prj
12
14
  def find_project_directories
13
15
  subdirectories = []
14
16
  Find.find(@root) do |d|
15
- subdirectories << d && Find.prune if File.exists?(File.join(d, ".git/"))
17
+ if @vcs_directories.any? { |vcs_dir| Dir.exists?(File.join(d, vcs_dir)) }
18
+ subdirectories << d && Find.prune
19
+ end
16
20
  end
17
21
  subdirectories.map { |r| r.gsub(@root, "") }
18
22
  end
19
23
  end
24
+
20
25
  end
data/lib/prj.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module Prj
2
2
  version_file = File.expand_path('../VERSION', File.dirname(__FILE__))
3
3
  VERSION = File.read(version_file).freeze
4
- end
5
4
 
6
- require 'prj/filter'
7
- require 'prj/finder'
5
+ autoload :DirWithScore, 'prj/dir_with_score'
6
+ autoload :Filter, 'prj/filter'
7
+ autoload :Finder, 'prj/finder'
8
+ autoload :App, 'prj/app'
9
+ end
8
10
 
@@ -1,58 +1,62 @@
1
1
  require 'spec_helper'
2
- require 'prj/app'
3
- require 'stringio'
4
- require 'tmpdir'
2
+ require 'fakefs/spec_helpers'
5
3
  require 'fileutils'
4
+ require 'prj'
5
+ require 'stringio'
6
+ require 'yaml'
6
7
 
7
8
  describe "Prj::App" do
8
- around(:each) do |example|
9
- Dir.mktmpdir do |root|
10
- @root = root
11
- @subdirectories = [
12
- "foo/.git/",
13
- "bar/",
14
- "baz/qux/crisp/.git/",
15
- "baz/craps/poops/.git/",
16
- "any/thing/here/"
17
- ].map { |d| File.join(@root, d) }
18
- @subdirectories.each { |d| FileUtils.mkdir_p(d) }
19
- @sink = StringIO.new
20
- example.run
21
- end
9
+ include FakeFS::SpecHelpers
10
+
11
+ let(:output) { StringIO.new }
12
+ let(:root) { "/Projects" }
13
+ let(:subdirectories) do
14
+ [
15
+ "foo/.git/",
16
+ "bar/",
17
+ "baz/qux/crisp/.git/",
18
+ "baz/craps/poops/.git/",
19
+ "any/thing/here/"
20
+ ].map { |d| File.join(root, d) }
22
21
  end
23
22
 
24
- context "with default projcts root" do
25
- around(:each) do |example|
26
- with_projects_root(@root) do
27
- example.run
28
- end
29
- end
23
+ before(:each) do
24
+ FileUtils.mkdir_p(root)
25
+ subdirectories.each { |d| FileUtils.mkdir_p(d) }
26
+ end
30
27
 
28
+ context "with default projcts root" do
31
29
  it "prints matching directory and returns 0" do
32
- Prj::App.new(@sink, ["ap"]).run.should == 0
33
- @sink.string.chomp.should == File.join(@root, "baz/craps/poops")
30
+ with_config("projects_root" => root) do
31
+ Prj::App.new(output, ["ap"]).run.should == 0
32
+ output.string.chomp.should == File.join(root, "baz/craps/poops")
33
+ end
34
34
  end
35
35
 
36
36
  it "prints projects root and returns 0 if directory not found" do
37
- Prj::App.new(@sink, ["nothingtofind"]).run.should == 0
38
- @sink.string.chomp.should == @root + "/"
37
+ with_config("projects_root" => root) do
38
+ Prj::App.new(output, ["nothingtofind"]).run.should == 0
39
+ output.string.chomp.should == root + "/"
40
+ end
39
41
  end
42
+ end
40
43
 
44
+ it "uses ~/.prj.yml as config file" do
45
+ Prj::App.config_path.should == File.expand_path("~/.prj.yml")
41
46
  end
42
47
 
43
- it "Uses project root from config if available" do
44
- File.should_receive(:read).with(File.expand_path("~/.prj"))
45
- Prj::App.new(@sink, ["something"]).run.should == 0
48
+ it "defaults to ~/Projects as default projects root" do
49
+ Prj::App.new(output, ["asdf"]).config.fetch("projects_root").should == File.expand_path("~/Projects")
46
50
  end
47
51
 
48
- def with_projects_root(root)
49
- tmp = Prj::App.default_projects_root
50
- Prj::App.default_projects_root = root
51
- Prj::App.ignore_config = true
52
+ def with_config(config = {})
53
+ tmp = Prj::App.config_path
54
+ config_path = ".prj.yml"
55
+ File.open(config_path, "w") { |f| f.write YAML.dump(config) }
56
+ Prj::App.config_path = config_path
52
57
  yield
53
- Prj::App.default_projects_root = tmp
54
- Prj::App.ignore_config = false
58
+ ensure
59
+ Prj::App.config_path = tmp
55
60
  end
56
61
  end
57
62
 
58
-
@@ -6,10 +6,10 @@ describe "Prj::Filter" do
6
6
  Prj::Filter.new(letters)
7
7
  end
8
8
 
9
- describe "#dispersion" do
10
- it "should calculate dispersion as difference between character positions except first character position" do
11
- filter(%w(f o)).dispersion("foo bar").should == 0
12
- filter(%w(f b)).dispersion("foo bar").should == 3
9
+ describe "#distance" do
10
+ it "should calculate distance as difference between character positions except first character position" do
11
+ filter(%w(f o)).distance("foo bar").should == 0
12
+ filter(%w(f b)).distance("foo bar").should == 3
13
13
  end
14
14
  end
15
15
 
@@ -19,6 +19,11 @@ describe "Prj::Filter" do
19
19
  filter(%w(f o)).filter(dirs).should == ["foos"]
20
20
  end
21
21
 
22
+ it "filters out non-satisfying entries" do
23
+ dirs = ["foos", "bars", "quxs"]
24
+ filter(%w(f o o q x b d u)).filter(dirs).should == []
25
+ end
26
+
22
27
  it "counts same chars correctly" do
23
28
  dirs = ["solar studio", "mars maraphone", "way too complex", "totla madness"]
24
29
  filter(%w(s s)).filter(dirs).should =~ ["solar studio", "totla madness"]
@@ -29,12 +34,12 @@ describe "Prj::Filter" do
29
34
  filter(%w(m a d n e)).filter(dirs).should =~ ["totla madness"]
30
35
  end
31
36
 
32
- it "sorts by dispersion" do
37
+ it "sorts by distance" do
33
38
  dirs = ["/koans/", "/omniauth/", "/ruby-download/"]
34
39
  filter(%w(o a)).filter(dirs).should == ["/koans/", "/omniauth/", "/ruby-download/"]
35
40
  end
36
41
 
37
- it "sorts by length for lines when dispersion is same" do
42
+ it "sorts by length for lines when distance is same" do
38
43
  dirs = ["/franchise/", "/granny/"]
39
44
  filter(%w(a n)).filter(dirs).should == ["/granny/", "/franchise/"]
40
45
 
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'fakefs/spec_helpers'
3
+ require 'fileutils'
3
4
  require 'prj/finder'
4
5
 
5
6
  describe "Prj::Finder" do
@@ -9,18 +10,25 @@ describe "Prj::Finder" do
9
10
 
10
11
  it "finds directories containing .git/ scoped to given root and returns their relative paths" do
11
12
  make_directories(root, "/foo/.git", "/bar/qux", "/baz/.git")
12
- Prj::Finder.new(root).find_project_directories.should =~ ["/foo", "/baz"]
13
+ finder(".git").find_project_directories.should =~ ["/foo", "/baz"]
13
14
  end
14
15
 
15
16
  it "does not find directories nested under directory containing .git/" do
16
17
  make_directories(root, "/foo/.git", "/foo/bar", "/foo/baz/.git")
17
- Prj::Finder.new(root).find_project_directories.should =~ ["/foo"]
18
+ finder(".git").find_project_directories.should =~ ["/foo"]
19
+ end
20
+
21
+ it "does support other vcs repos" do
22
+ make_directories(root, "/foo/.git", "/bar/.svn", "/foo/baz/.unknown")
23
+ finder(".git", ".svn").find_project_directories.should =~ ["/foo", "/bar"]
18
24
  end
19
25
 
20
26
  def make_directories(root, *directories)
21
- directories.each do |d|
22
- FakeFS::FileSystem.add(File.join(root, d))
23
- end
27
+ directories.each { |d| FileUtils.mkdir_p(File.join(root, d)) }
28
+ end
29
+
30
+ def finder(*vcs_directories)
31
+ Prj::Finder.new(root, vcs_directories)
24
32
  end
25
33
  end
26
34
 
data/spec/spec_helper.rb CHANGED
@@ -17,5 +17,3 @@ require 'rspec/autorun'
17
17
 
18
18
  $:.unshift File.expand_path('../../lib', __FILE__)
19
19
 
20
- Dir[File.dirname(__FILE__) + "/support/matchers/*.rb"].each {|f| require f}
21
-
metadata CHANGED
@@ -1,18 +1,24 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prj
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
4
  prerelease:
5
+ version: 0.0.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Vladimir Yarotsky
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-22 00:00:00.000000000 Z
12
+ date: 2013-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.11'
16
22
  requirement: !ruby/object:Gem::Requirement
17
23
  none: false
18
24
  requirements:
@@ -21,14 +27,14 @@ dependencies:
21
27
  version: '2.11'
22
28
  type: :development
23
29
  prerelease: false
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
24
32
  version_requirements: !ruby/object:Gem::Requirement
25
33
  none: false
26
34
  requirements:
27
35
  - - ~>
28
36
  - !ruby/object:Gem::Version
29
- version: '2.11'
30
- - !ruby/object:Gem::Dependency
31
- name: rake
37
+ version: '0.9'
32
38
  requirement: !ruby/object:Gem::Requirement
33
39
  none: false
34
40
  requirements:
@@ -37,14 +43,14 @@ dependencies:
37
43
  version: '0.9'
38
44
  type: :development
39
45
  prerelease: false
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
40
48
  version_requirements: !ruby/object:Gem::Requirement
41
49
  none: false
42
50
  requirements:
43
51
  - - ~>
44
52
  - !ruby/object:Gem::Version
45
- version: '0.9'
46
- - !ruby/object:Gem::Dependency
47
- name: simplecov
53
+ version: '0.6'
48
54
  requirement: !ruby/object:Gem::Requirement
49
55
  none: false
50
56
  requirements:
@@ -53,14 +59,14 @@ dependencies:
53
59
  version: '0.6'
54
60
  type: :development
55
61
  prerelease: false
62
+ - !ruby/object:Gem::Dependency
63
+ name: fakefs
56
64
  version_requirements: !ruby/object:Gem::Requirement
57
65
  none: false
58
66
  requirements:
59
67
  - - ~>
60
68
  - !ruby/object:Gem::Version
61
- version: '0.6'
62
- - !ruby/object:Gem::Dependency
63
- name: fakefs
69
+ version: '0.4'
64
70
  requirement: !ruby/object:Gem::Requirement
65
71
  none: false
66
72
  requirements:
@@ -69,12 +75,6 @@ dependencies:
69
75
  version: '0.4'
70
76
  type: :development
71
77
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: '0.4'
78
78
  description: ! ' Prj is an utility to quickly go to project directory using fuzzy
79
79
  matching
80
80
 
@@ -87,6 +87,7 @@ extra_rdoc_files: []
87
87
  files:
88
88
  - bin/prj
89
89
  - lib/prj/app.rb
90
+ - lib/prj/dir_with_score.rb
90
91
  - lib/prj/filter.rb
91
92
  - lib/prj/finder.rb
92
93
  - lib/prj.rb
@@ -121,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  version: 1.3.6
122
123
  requirements: []
123
124
  rubyforge_project:
124
- rubygems_version: 1.8.24
125
+ rubygems_version: 1.8.25
125
126
  signing_key:
126
127
  specification_version: 3
127
128
  summary: Fuzzy-matching project finder