prj 0.0.1 → 0.0.2

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