├── .rspec ├── .autotest ├── .gitignore ├── features ├── fixtures │ └── script_ids.json ├── .nav ├── support │ ├── hooks.rb │ ├── stubbed_http_requests.rb │ ├── setup.rb │ ├── vimmer_stub.rb │ └── stub-commands.rb ├── step_definitions │ ├── plugin_context_steps.rb │ ├── plugin_assertion_steps.rb │ └── environment_context_steps.rb ├── changelog.md ├── uninstall_plugin.feature ├── README.md ├── setup.feature └── install.feature ├── lib ├── vimmer │ ├── version.rb │ ├── installers.rb │ ├── installers │ │ ├── vim_dot_org.rb │ │ └── github.rb │ ├── cli.rb │ └── settings.rb └── vimmer.rb ├── bin └── vimmer ├── .rvmrc ├── Gemfile ├── cucumber.yml ├── script └── run_until_failure ├── Rakefile ├── spec ├── spec_helper.rb ├── install │ ├── load_installer_spec.rb │ ├── vim_dot_org_spec.rb │ └── github_spec.rb └── settings_spec.rb ├── vimmer.gemspec ├── Gemfile.lock └── README.mdown /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.autotest: -------------------------------------------------------------------------------- 1 | require 'autotest/growl' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | *.gem 3 | .bundle 4 | tmp/ 5 | log 6 | -------------------------------------------------------------------------------- /features/fixtures/script_ids.json: -------------------------------------------------------------------------------- 1 | {"2975":"fugitive.vim"} 2 | -------------------------------------------------------------------------------- /lib/vimmer/version.rb: -------------------------------------------------------------------------------- 1 | module Vimmer 2 | VERSION = "0.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /bin/vimmer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'vimmer/cli' 3 | Vimmer::CLI.start 4 | -------------------------------------------------------------------------------- /features/.nav: -------------------------------------------------------------------------------- 1 | - changelog.md 2 | - setup.feature 3 | - install.feature 4 | - uninstall_plugin.feature 5 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | if [[ -n "$rvm_environments_path" && -s "$rvm_environments_path/ruby-1.9.2-p0@vimmer" ]] ; then 2 | . "$rvm_environments_path/ruby-1.9.2-p0@vimmer" 3 | else 4 | rvm --create "ruby-1.9.2-p0@vimmer" 5 | fi -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in vimmer.gemspec 4 | gemspec 5 | 6 | if RUBY_PLATFORM =~ /darwin/ 7 | gem "autotest-growl" 8 | gem "autotest-fsevent" 9 | end 10 | -------------------------------------------------------------------------------- /features/support/hooks.rb: -------------------------------------------------------------------------------- 1 | Before do 2 | @aruba_timeout_seconds = 20 3 | end 4 | 5 | After do 6 | FileUtils.rm_rf("tmp/aruba/bundle") 7 | end 8 | 9 | at_exit do 10 | FileUtils.rm_rf("tmp/aruba") 11 | end 12 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | default: --format progress features --tags ~@wip 2 | html_report: --format progress --format html --out=features_report.html features 3 | wip: --tags @wip:1 --wip 4 | autotest: --color 5 | autotest-all: --color 6 | -------------------------------------------------------------------------------- /features/step_definitions/plugin_context_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^I have no plugins installed$/ do 2 | @vimmer = VimmerStub.new 3 | Given 'a file named ".vimmer/plugins.yml" with:', {}.to_yaml 4 | Given 'a directory named "bundle"' 5 | end 6 | 7 | -------------------------------------------------------------------------------- /features/support/stubbed_http_requests.rb: -------------------------------------------------------------------------------- 1 | include WebMock::API 2 | 3 | stub_request(:get, "http://www.vim-scripts.org/api/script_ids.json"). 4 | with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). 5 | to_return(:status => 200, :body => File.read(File.dirname(__FILE__) + "/../fixtures/script_ids.json")) 6 | 7 | -------------------------------------------------------------------------------- /script/run_until_failure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | counter=1 4 | 5 | rake > /dev/null 2>&1 6 | 7 | while [[ $? -eq 0 ]]; do 8 | echo "Count: $counter" 9 | let counter=$counter+1 10 | rake > /dev/null 2>&1 11 | done 12 | 13 | message="Ran $counter times before failure" 14 | 15 | if [[ $1 -eq "-s" ]]; then 16 | say $message 17 | fi 18 | 19 | echo $message 20 | echo "$message\n" >> log/failures.log 21 | -------------------------------------------------------------------------------- /features/step_definitions/plugin_assertion_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^a plugin named "([^"]*)" should be installed$/ do |name| 2 | @vimmer.installed_plugins.should include(name) 3 | @vimmer.plugin_store[name].should =~ %r{https://github.com/.*/#{name}\.git} 4 | end 5 | 6 | Then /^I should still not have any plugins installed$/ do 7 | @vimmer.installed_plugins.should be_empty 8 | @vimmer.plugin_store.should == {} 9 | end 10 | 11 | -------------------------------------------------------------------------------- /features/step_definitions/environment_context_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^a bundle path set for my system$/ do 2 | bundle_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "tmp", "aruba", "bundle")) 3 | Given 'a file named ".vimmer/config" with:', "bundle_path: #{bundle_path}" 4 | end 5 | 6 | Given /^no directory named "([^"]*)"$/ do |directory| 7 | in_current_dir do 8 | FileUtils.rm_rf(directory) 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 'rspec/core/rake_task' 3 | require 'cucumber/rake/task' 4 | 5 | Bundler::GemHelper.install_tasks 6 | 7 | Cucumber::Rake::Task.new(:features) do |t| 8 | t.cucumber_opts = "features --format pretty" 9 | end 10 | 11 | RSpec::Core::RakeTask.new(:spec) 12 | 13 | task :default => [:features, :spec] 14 | 15 | namespace :relish do 16 | task :push do 17 | `relish push joefiorini/vimmer` 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /features/support/setup.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | require 'webmock' 3 | require File.dirname(__FILE__) + '/stubbed_http_requests' 4 | 5 | ENV['RUBYOPT'] = "-r#{File.expand_path(File.join(File.dirname(__FILE__), 'stub-commands.rb'))} -rwebmock -r#{File.expand_path(File.join(File.dirname(__FILE__), 'stubbed_http_requests'))} #{ENV['RUBYOPT']}" 6 | ENV['VIMMER_HOME'] = File.expand_path(File.join(File.dirname(__FILE__), 7 | %w(.. .. tmp aruba .vimmer))) 8 | -------------------------------------------------------------------------------- /lib/vimmer/installers.rb: -------------------------------------------------------------------------------- 1 | module Vimmer 2 | module Installers 3 | extend self 4 | 5 | autoload :Github, 'vimmer/installers/github' 6 | autoload :VimDotOrg, 'vimmer/installers/vim_dot_org' 7 | 8 | 9 | def for_url(url) 10 | if Github.match?(url) 11 | Github 12 | elsif VimDotOrg.match?(url) 13 | VimDotOrg.for_url(url) 14 | else 15 | raise Vimmer::InstallerNotFoundError.new(url) 16 | end 17 | end 18 | 19 | 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /features/changelog.md: -------------------------------------------------------------------------------- 1 | ## vimmer release history 2 | 3 | ### 0.2.0 12/21/2010 4 | 5 | [full changelog](https://github.com/densitypop/vimmer/compare/v0.1.0...v0.2.0) 6 | 7 | * Enchancements 8 | * Can use the project's "front-end" Github URL 9 | * vimmer install http://github.com/tpope/vim-fugitive 10 | * Install plugins available on vim.org using project's page URL 11 | * vimmer install "http://www.vim.org/scripts/script.php?script_id=2501" 12 | * README updates 13 | * Bug fixes 14 | * Support Github URLs that end in ".vim" 15 | -------------------------------------------------------------------------------- /features/support/vimmer_stub.rb: -------------------------------------------------------------------------------- 1 | class VimmerStub 2 | 3 | def installed_plugins 4 | bundle_path.entries.map do |entry| 5 | next if entry.to_s =~ /^\.\.?$/ 6 | 7 | entry = bundle_path.join(entry) 8 | if entry.directory? 9 | entry.split.last.to_s 10 | end 11 | end.compact 12 | end 13 | 14 | 15 | def plugin_store 16 | if plugin_store_file.exist? 17 | YAML.load_file(plugin_store_file) 18 | else 19 | {} 20 | end 21 | end 22 | 23 | def plugin_store_file 24 | Pathname.new("tmp/aruba/.vimmer/plugins.yml") 25 | end 26 | 27 | 28 | def bundle_path 29 | Pathname.new("tmp/aruba/bundle") 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | 3 | $:.unshift File.expand_path('..', __FILE__) 4 | $:.unshift File.expand_path('../../lib', __FILE__) 5 | 6 | require 'vimmer' 7 | 8 | require 'fakefs/spec_helpers' 9 | require 'bourne' 10 | 11 | require 'webmock/rspec' 12 | 13 | 14 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} 15 | 16 | RSpec.configure do |config| 17 | 18 | config.include FakeFS::SpecHelpers 19 | config.include WebMock::API 20 | config.mock_with :mocha 21 | 22 | def app_root 23 | Pathname.new(File.join(File.dirname(__FILE__), "..")) 24 | end 25 | 26 | def vimmer_home 27 | app_root.join("tmp", ".vimmer") 28 | end 29 | 30 | 31 | end 32 | -------------------------------------------------------------------------------- /features/uninstall_plugin.feature: -------------------------------------------------------------------------------- 1 | Feature: Uninstall plugin 2 | 3 | Background: 4 | Given a directory named ".vimmer" 5 | And a bundle path set for my system 6 | And I have no plugins installed 7 | 8 | Scenario: Uninstall from Github 9 | When I successfully run "vimmer install 'https://github.com/tpope/vim-awesomemofo.git'" 10 | And I successfully run "vimmer uninstall 'vim-awesomemofo'" 11 | Then I should still not have any plugins installed 12 | 13 | Scenario: Attempt to uninstall a plugin that is not installed 14 | When I run "vimmer uninstall not_installed" 15 | Then it should fail with: 16 | """ 17 | The plugin not_installed is not installed. 18 | """ 19 | 20 | -------------------------------------------------------------------------------- /features/support/stub-commands.rb: -------------------------------------------------------------------------------- 1 | # redefine ` to stub the curl and git commands 2 | module Kernel 3 | alias_method :real_backtick, :` 4 | def `(command) 5 | case command 6 | when /^curl (.+)/ 7 | if $1 =~ /\.git\b|not-found/ 8 | '404' 9 | else 10 | '200' 11 | end 12 | 13 | when /^git (.+)/ 14 | git_arguments = $1 15 | # git clone () 16 | if git_arguments =~ /^clone \S+ (.+)/ 17 | require 'fileutils' 18 | FileUtils.mkdir_p $1 19 | else 20 | puts 'delegating to git' 21 | real_backtick "git #{git_arguments}" 22 | end 23 | 24 | else 25 | real_backtick command 26 | end 27 | end 28 | end 29 | 30 | -------------------------------------------------------------------------------- /features/README.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | 3 | Vimmer is documented using the [Cucumber BDD framework](http://cukes.info). Cucumber gives us executable documentation, which for you means that as long as I keep my tests passing it will never be out of date. 4 | 5 | ## Issues 6 | 7 | The documentation for Vimmer is a work in progress. We'll be adding 8 | Cucumber features over time and clarifying existing ones. If you have 9 | specific features you'd like to see added, find the existing documentation 10 | incomplete or confusing or, better yet, wish to write a missing Cucumber 11 | feature yourself, please [submit an 12 | issue](http://github.com/densitypop/vimmer/issues) or a [pull 13 | request](http://github.com/densitypop/vimmer). 14 | 15 | -------------------------------------------------------------------------------- /features/setup.feature: -------------------------------------------------------------------------------- 1 | Feature: Setup 2 | 3 | Scenario: Run without .vimmer directory 4 | Given no directory named ".vimmer" 5 | When I successfully run "vimmer install 'https://github.com/tpope/vim-awesomemofo.git'" 6 | Then a directory named ".vimmer" should exist 7 | And the file ".vimmer/config" should contain "bundle_path: ~/.vim/bundle" 8 | 9 | Scenario: Run with .vimmer directory (doesn't overwrite) 10 | Given a directory named ".vimmer" 11 | And a file named ".vimmer/config" with: 12 | """ 13 | bundle_path: ~/.vim/custom_bundle 14 | """ 15 | When I successfully run "vimmer install 'https://github.com/tpope/vim-awesomemofo.git'" 16 | Then the file ".vimmer/config" should contain "bundle_path: ~/.vim/custom_bundle" 17 | -------------------------------------------------------------------------------- /spec/install/load_installer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "When loading an installer by URL" do 4 | include Vimmer 5 | 6 | context "for a Github URL" do 7 | 8 | subject { Installers.for_url("https://github.com/foo/bar.git") } 9 | 10 | it { should == Installers::Github } 11 | 12 | end 13 | 14 | 15 | context "for a Vim.org URL" do 16 | 17 | before do 18 | Installers::VimDotOrg.stubs(:for_url).returns(Installers::VimDotOrg) 19 | end 20 | 21 | subject { Installers.for_url("http://vim.org/scripts/script.php?script_id=1234") } 22 | 23 | it { should == Installers::VimDotOrg } 24 | 25 | end 26 | 27 | 28 | context "for an unrecognized URL" do 29 | 30 | subject { Installers.for_url("http://foo.com/bar") } 31 | 32 | it { lambda { subject }.should raise_error(Vimmer::InstallerNotFoundError) } 33 | 34 | end 35 | 36 | 37 | end 38 | -------------------------------------------------------------------------------- /lib/vimmer/installers/vim_dot_org.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | 4 | module Vimmer 5 | module Installers 6 | 7 | VIM_DOT_ORG_URL_PATTERN = %r{https?://(?:www\.)?vim\.org/scripts/script.php\?script_id=(\d{1,5})} 8 | 9 | class VimDotOrg 10 | attr_reader :path, :plugin_name, :script_id 11 | 12 | 13 | def self.match?(url) 14 | !(url =~ VIM_DOT_ORG_URL_PATTERN).nil? 15 | end 16 | 17 | def self.for_url(path) 18 | 19 | m = VIM_DOT_ORG_URL_PATTERN.match(path) 20 | script_id = m[1] 21 | 22 | raise Vimmer::PluginNotFoundError unless repository.key?(script_id) 23 | 24 | script_name = repository[script_id] 25 | github_path_template = "https://github.com/vim-scripts/%s.git" 26 | 27 | github_path = github_path_template % [script_name] 28 | 29 | Github.new(:path => github_path) 30 | end 31 | 32 | private 33 | 34 | def self.repository 35 | @repository ||= JSON.parse(Net::HTTP.get(repository_uri)) 36 | end 37 | 38 | def self.repository_uri 39 | URI.parse("http://vim-scripts.org/api/script_ids.json") 40 | end 41 | 42 | 43 | end 44 | 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/settings_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ".vimmer/" do 4 | 5 | let(:settings) { Vimmer::Settings.new } 6 | 7 | it "can be moved with an environment variable" do 8 | ENV['VIMMER_HOME'] = app_root.join("tmp", ".vimmer").to_s 9 | settings.config_root.relative_path_from(app_root).to_s.should == "tmp/.vimmer" 10 | settings.config_file.relative_path_from(app_root).to_s.should == "tmp/.vimmer/config" 11 | ENV['VIMMER_HOME'] = nil 12 | end 13 | 14 | it "creates the .vimmer directory" do 15 | settings.create_vimmer_home! 16 | settings.config_root.should exist 17 | end 18 | 19 | 20 | it "creates the config file" do 21 | settings.create_default_config_file! 22 | settings.config_file.should exist 23 | File.read(settings.config_file).should =~ /bundle_path: ~\/.vim\/bundle/ 24 | end 25 | 26 | 27 | it "expands the path when calling bundle_path" do 28 | ENV['VIMMER_HOME'] = vimmer_home.to_s 29 | 30 | bundle_path = app_root.join("lib", "..", "tmp", "bundle") 31 | File.open(vimmer_home.join("config").to_s, "w") do |f| 32 | f << "bundle_path: #{bundle_path}" 33 | end 34 | 35 | Vimmer::Settings.new[:bundle_path].should == File.expand_path(bundle_path) 36 | 37 | ENV['VIMMER_HOME'] = nil 38 | end 39 | 40 | 41 | end 42 | -------------------------------------------------------------------------------- /vimmer.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "vimmer/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "vimmer" 7 | s.version = Vimmer::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Joe Fiorini"] 10 | s.email = ["joe@densitypop.com"] 11 | s.homepage = "http://rubygems.org/gems/vimmer" 12 | s.summary = %q{Automated Vim plugin management} 13 | s.description = %q{Install, update and remove Vim plugins without changing directories using this simple command line utility.} 14 | 15 | s.rubyforge_project = "vimmer" 16 | 17 | s.add_dependency "thor" 18 | 19 | s.add_development_dependency "bundler", ">=1.0.0" 20 | s.add_development_dependency "rspec", "~> 2.3.0" 21 | s.add_development_dependency "cucumber", "= 0.10.0" 22 | s.add_development_dependency "autotest" 23 | s.add_development_dependency "fuubar" 24 | s.add_development_dependency "fakefs" 25 | s.add_development_dependency "aruba", "= 0.2.7" 26 | s.add_development_dependency "mocha" 27 | s.add_development_dependency "bourne" 28 | s.add_development_dependency "webmock" 29 | 30 | s.files = `git ls-files`.split("\n") 31 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 32 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 33 | s.require_paths = ["lib"] 34 | end 35 | -------------------------------------------------------------------------------- /lib/vimmer.rb: -------------------------------------------------------------------------------- 1 | module Vimmer 2 | 3 | autoload :Installers, 'vimmer/installers' 4 | autoload :Settings, 'vimmer/settings' 5 | autoload :PluginPath, 'vimmer/plugin_path' 6 | autoload :Plugin, 'vimmer/plugin' 7 | 8 | class PluginNotFoundError < StandardError; end 9 | class InvalidPathError < StandardError 10 | attr_accessor :path 11 | 12 | def initialize(path) 13 | @path = path 14 | end 15 | 16 | end 17 | 18 | class InstallerNotFoundError < StandardError 19 | attr_accessor :path 20 | 21 | def initialize(path) 22 | @path = path 23 | end 24 | 25 | end 26 | 27 | 28 | def bundle_path 29 | settings[:bundle_path] 30 | end 31 | module_function :bundle_path 32 | 33 | 34 | def settings 35 | @settings ||= Settings.new 36 | end 37 | module_function :settings 38 | 39 | 40 | def add_plugin(name, path) 41 | settings.add_plugin(name, path) 42 | end 43 | module_function :add_plugin 44 | 45 | 46 | def remove_plugin(name) 47 | settings.remove_plugin(name) 48 | end 49 | module_function :remove_plugin 50 | 51 | 52 | def plugin?(name) 53 | plugins.key?(name) 54 | end 55 | module_function :plugin? 56 | 57 | def plugins 58 | settings.plugins 59 | end 60 | module_function :plugins 61 | 62 | 63 | def setup 64 | settings.create_vimmer_home! 65 | settings.create_default_config_file! 66 | end 67 | module_function :setup 68 | 69 | 70 | end 71 | -------------------------------------------------------------------------------- /lib/vimmer/cli.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'vimmer' 3 | 4 | module Vimmer 5 | class CLI < Thor 6 | 7 | desc "install PATH", "Installs plugin available at path PATH" 8 | def install(path) 9 | setup 10 | begin 11 | installer = Vimmer::Installers.for_url(path) 12 | # TODO: Make this consistent with VimDotOrg installer 13 | if installer == Vimmer::Installers::Github 14 | installer = installer.new(:path => path) 15 | end 16 | installer.install 17 | rescue Vimmer::InstallerNotFoundError => e 18 | $stderr.puts "The URL #{e.path} is invalid." 19 | exit 1 20 | rescue Vimmer::InvalidPathError => e 21 | $stderr.puts "The URL #{e.path} is invalid." 22 | exit 1 23 | rescue Vimmer::PluginNotFoundError 24 | $stderr.puts "The plugin #{installer.plugin_name} could not be found" 25 | exit 1 26 | end 27 | end 28 | 29 | 30 | desc "uninstall NAME", "Removes plugin named NAME" 31 | def uninstall(name) 32 | unless Vimmer.plugin?(name) 33 | $stderr.puts "The plugin #{name} is not installed." 34 | exit 1 35 | end 36 | installer = Vimmer::Installers::Github.new(:name => name) 37 | installer.uninstall 38 | end 39 | 40 | private 41 | 42 | def setup 43 | Vimmer.setup 44 | end 45 | 46 | 47 | def method_missing(name, *args) 48 | $stderr.puts 'Could not find command "%s".' % name 49 | exit 1 50 | end 51 | 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | vimmer (0.1.0) 5 | thor 6 | 7 | GEM 8 | remote: http://rubygems.org/ 9 | specs: 10 | addressable (2.2.2) 11 | aruba (0.2.7) 12 | background_process 13 | cucumber (~> 0.10.0) 14 | autotest (4.4.5) 15 | autotest-fsevent (0.2.3) 16 | sys-uname 17 | autotest-growl (0.2.6) 18 | background_process (1.2) 19 | bourne (1.0) 20 | mocha (= 0.9.8) 21 | builder (3.0.0) 22 | crack (0.1.8) 23 | cucumber (0.10.0) 24 | builder (>= 2.1.2) 25 | diff-lcs (~> 1.1.2) 26 | gherkin (~> 2.3.2) 27 | json (~> 1.4.6) 28 | term-ansicolor (~> 1.0.5) 29 | diff-lcs (1.1.2) 30 | fakefs (0.2.1) 31 | fuubar (0.0.2) 32 | rspec (~> 2.0) 33 | rspec-instafail (~> 0.1.4) 34 | ruby-progressbar (~> 0.0.9) 35 | gherkin (2.3.2) 36 | json (~> 1.4.6) 37 | term-ansicolor (~> 1.0.5) 38 | json (1.4.6) 39 | mocha (0.9.8) 40 | rake 41 | rake (0.8.7) 42 | rspec (2.3.0) 43 | rspec-core (~> 2.3.0) 44 | rspec-expectations (~> 2.3.0) 45 | rspec-mocks (~> 2.3.0) 46 | rspec-core (2.3.1) 47 | rspec-expectations (2.3.0) 48 | diff-lcs (~> 1.1.2) 49 | rspec-instafail (0.1.4) 50 | rspec-mocks (2.3.0) 51 | ruby-progressbar (0.0.9) 52 | sys-uname (0.8.4) 53 | term-ansicolor (1.0.5) 54 | thor (0.14.6) 55 | webmock (1.6.1) 56 | addressable (>= 2.2.2) 57 | crack (>= 0.1.7) 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | aruba (= 0.2.7) 64 | autotest 65 | autotest-fsevent 66 | autotest-growl 67 | bourne 68 | bundler (>= 1.0.0) 69 | cucumber (= 0.10.0) 70 | fakefs 71 | fuubar 72 | mocha 73 | rspec (~> 2.3.0) 74 | thor 75 | vimmer! 76 | webmock 77 | -------------------------------------------------------------------------------- /spec/install/vim_dot_org_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | include Vimmer::Installers 4 | 5 | describe "When installing from vim.org" do 6 | 7 | VDO_NOT_FOUND_URL = "http://www.vim.org/scripts/script.php?script_id=0000" 8 | VDO_MALFORMED_URL = "https://foo.com/bar" 9 | 10 | RSpec::Matchers.define :be_a_valid_url do |expected| 11 | match do 12 | VimDotOrg.match?(expected) == true 13 | end 14 | end 15 | 16 | context "with a non-existent URL" do 17 | 18 | before do 19 | VimDotOrg.stubs(:repository).returns({}) 20 | end 21 | 22 | it { should be_a_valid_url(VDO_NOT_FOUND_URL) } 23 | 24 | specify "the installer should raise an exception" do 25 | lambda { VimDotOrg.for_url(VDO_NOT_FOUND_URL) }.should raise_error(Vimmer::PluginNotFoundError) 26 | end 27 | 28 | end 29 | 30 | 31 | context "with a malformed URL" do 32 | 33 | it { should_not be_a_valid_url(VDO_MALFORMED_URL) } 34 | 35 | end 36 | 37 | context "with a good URL" do 38 | 39 | before do 40 | VimDotOrg.stubs(:repository).returns({"2975" => "fugitive.vim"}) 41 | end 42 | 43 | let(:installer) { VimDotOrg.for_url("http://www.vim.org/scripts/script.php?script_id=2975") } 44 | 45 | context "using the vim-scripts mirror" do 46 | 47 | it "should return a Github installer" do 48 | installer.should be_a(Github) 49 | end 50 | 51 | it "uses the Github path" do 52 | installer.path.should == "https://github.com/vim-scripts/fugitive.vim.git" 53 | end 54 | 55 | end 56 | 57 | specify "the installer should not raise an exception" do 58 | 59 | class << installer 60 | def git_clone(arg1, arg2) 61 | end 62 | end 63 | 64 | lambda { installer.install }.should_not raise_error 65 | end 66 | 67 | specify "the installer calculates the plugin's name" do 68 | installer.plugin_name.should == "fugitive.vim" 69 | end 70 | 71 | end 72 | 73 | end 74 | -------------------------------------------------------------------------------- /features/install.feature: -------------------------------------------------------------------------------- 1 | Feature: Install plugin 2 | 3 | Background: 4 | Given a directory named ".vimmer" 5 | And a bundle path set for my system 6 | And I have no plugins installed 7 | 8 | Scenario: Install from Github 9 | When I successfully run "vimmer install 'https://github.com/tpope/vim-awesomemofo.git'" 10 | Then a plugin named "vim-awesomemofo" should be installed 11 | And the stdout should contain "vim-awesomemofo has been installed" 12 | 13 | Scenario: Install from Github with bad URL 14 | When I run "vimmer install 'https://github.com/tpope/not-found.git'" 15 | Then I should still not have any plugins installed 16 | And it should fail with: 17 | """ 18 | The plugin not-found could not be found 19 | """ 20 | 21 | Scenario: Install from Github with a non-Github URL 22 | When I run "vimmer install 'http://example.com/bad'" 23 | Then I should still not have any plugins installed 24 | And it should fail with: 25 | """ 26 | The URL http://example.com/bad is invalid. 27 | """ 28 | 29 | Scenario Outline: Install from Github with a front-end Github URL 30 | When I successfully run "vimmer install ''" 31 | Then a plugin named "vim-awesomemofo" should be installed 32 | And the stdout should contain "vim-awesomemofo has been installed" 33 | 34 | Examples: 35 | | URL | 36 | | http://github.com/tpope/vim-awesomemofo | 37 | | https://github.com/tpope/vim-awesomemofo | 38 | 39 | Scenario: Accept URLs with a "." 40 | When I run "vimmer install 'http://github.com/tpope/awesomemofo.vim'" 41 | Then it should pass with: 42 | """ 43 | awesomemofo.vim has been installed 44 | """ 45 | 46 | @wip 47 | Scenario: Install from vim.org 48 | When I successfully run "vimmer install http://www.vim.org/scripts/script.php?script_id=2975" 49 | Then a plugin named "fugitive.vim" should be installed 50 | And the stdout should contain "fugitive.vim has been installed" 51 | -------------------------------------------------------------------------------- /README.mdown: -------------------------------------------------------------------------------- 1 | # Vimmer: Easily manage your Vim plugins # 2 | 3 | Vimmer makes it easy to install, remove and update Vim plugins from the following sources: 4 | 5 | - Github 6 | - Vim.org 7 | 8 | N.B. Vimmer is a work in progress. This is an early alpha release for those who are willing to try it and submit feedback. I am offering it with NO GUARANTEES. If you're still with me, please submit your feedback at [https://github.com/densitypop/Vimmer/issues](https://github.com/densitypop/Vimmer/issues). Thank you! 9 | 10 | ## Installation and Usage ## 11 | 12 | Installing Vimmer is as simple as running: 13 | 14 | gem install vimmer 15 | 16 | ### Dependencies ### 17 | 18 | Vimmer depends on the Pathogen plugin for managing Vim's runtime path. Using Pathogen, Vimmer will install plugins into ~/.vim/bundle. Installing a plugin is as simple as running: 19 | 20 | vimmer install https://github.com/tpope/vim-fugitive.git 21 | 22 | To install a plugin, colorscheme or other script from vim.org use the URL to the script's page. For example: 23 | 24 | vimmer install "http://www.vim.org/scripts/script.php?script_id=2501" 25 | 26 | Restart Vim and you're done! For details on installing Pathogen see [Pathogen on Vim.org](http://www.vim.org/scripts/script.php?script_id=2332). Note: I am planning to automatically setup Pathogen during the Vimmer install process. 27 | 28 | ### Documentation ### 29 | 30 | Vimmer is documented using Cucumber features and hosted on [Relish](http://www.relishapp.com). [View the documentation.](http://relishapp.com/joefiorini/vimmer) 31 | 32 | ## Contributing to Vimmer ## 33 | 34 | 1. Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet 35 | 2. Check out the [issue tracker](http://github.com/densitypop/Vimmer/issues) to make sure someone already hasn't requested it and/or contributed it 36 | 3. Fork the project; make sure you're working in your own branch off the develop branch 37 | 4. Write some tests/Cukes; implement until you're happy and the tests pass. This is important to make sure I don't accidentally break your fix in the future 38 | 5. Send me a pull request 39 | 40 | -------------------------------------------------------------------------------- /lib/vimmer/settings.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'yaml' 3 | 4 | module Vimmer 5 | class Settings 6 | 7 | 8 | def initialize 9 | @config = defaults.merge(load_config(config_file)) 10 | end 11 | 12 | 13 | def [](key) 14 | value = File.expand_path(@config[key.to_s]) 15 | if value && File.exists?(value) 16 | Pathname.new(value) 17 | else 18 | value 19 | end 20 | end 21 | 22 | 23 | def config_file 24 | config_root.join("config") 25 | end 26 | 27 | def plugin_store_file 28 | @plugin_store_file ||= config_root.join("plugins.yml") 29 | end 30 | 31 | def config_root 32 | Pathname.new(ENV['VIMMER_HOME'] || File.join(Gem.user_home, ".vimmer")) 33 | end 34 | 35 | 36 | def create_vimmer_home! 37 | FileUtils.mkdir_p(config_root.to_s) 38 | end 39 | 40 | 41 | def create_default_config_file! 42 | return if File.exist?(config_file.to_s) 43 | write_default_config_file 44 | @config = load_config(config_file) 45 | end 46 | 47 | def write_default_config_file 48 | File.open(config_file.to_s, "w") do |f| 49 | f << "bundle_path: ~/.vim/bundle" 50 | end 51 | end 52 | 53 | 54 | def add_plugin(name, path) 55 | existing_plugins = plugins.dup 56 | existing_plugins.merge!(name => path) 57 | 58 | write_to_manifest(existing_plugins) 59 | end 60 | 61 | def remove_plugin(name) 62 | existing_plugins = plugins.dup 63 | existing_plugins.delete(name) 64 | 65 | write_to_manifest(existing_plugins) 66 | end 67 | 68 | def plugins 69 | @plugins = if !File.exist?(plugin_store_file.to_s) 70 | write_to_manifest({}) 71 | end 72 | 73 | read_from_manifest 74 | end 75 | 76 | 77 | def read_from_manifest 78 | YAML.load_file(plugin_store_file.to_s) 79 | end 80 | 81 | def write_to_manifest(hash) 82 | File.open(plugin_store_file.to_s, "w") do |f| 83 | f << hash.to_yaml 84 | end 85 | end 86 | 87 | def load_config(config_file) 88 | config_file = File.expand_path(config_file) 89 | if config_file && File.exist?(config_file.to_s) 90 | YAML.load_file(config_file) 91 | else 92 | {} 93 | end 94 | end 95 | 96 | 97 | def defaults 98 | { "bundle_path" => "~/.vim/bundle" } 99 | end 100 | 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/vimmer/installers/github.rb: -------------------------------------------------------------------------------- 1 | module Vimmer 2 | module Installers 3 | 4 | GITHUB_GIT_PATH_TEMPLATE = "https://github.com/%s/%s.git" 5 | GITHUB_GIT_URL_PATTERN = %r{^https://github.com/[a-zA-Z0-9\-_\+%]+/([a-zA-Z0-9\-_\+\.]+).git$} 6 | GITHUB_PUBLIC_URL_PATTERN = %r{^https?://github.com/([a-zA-Z0-9\-_\+%]+)/([a-zA-Z0-9\-_\+\.]+[^.git])$} 7 | 8 | class Github 9 | attr_reader :path, :plugin_name 10 | 11 | def initialize(args={}) 12 | if args[:path] 13 | initialize_with_path(args[:path]) 14 | elsif args[:name] 15 | initialize_with_name(args[:name]) 16 | end 17 | end 18 | 19 | 20 | def install 21 | if path_exists? 22 | git_clone(path, File.join(Vimmer.bundle_path, plugin_name)) 23 | Vimmer.add_plugin(plugin_name, path) 24 | puts "#{plugin_name} has been installed" 25 | else 26 | raise Vimmer::PluginNotFoundError 27 | end 28 | end 29 | 30 | 31 | def uninstall 32 | plugin_path = File.join(Vimmer.bundle_path, plugin_name) 33 | if File.directory? plugin_path 34 | FileUtils.rm_rf(plugin_path) 35 | Vimmer.remove_plugin(plugin_name) 36 | puts "#{plugin_name} has been uninstalled" 37 | else 38 | raise Vimmer::PluginNotFoundError 39 | end 40 | end 41 | 42 | 43 | def path_exists? 44 | `curl --head -w %{http_code} -o /dev/null #{remove_extension(path)} 2> /dev/null`.chomp == "200" 45 | end 46 | 47 | 48 | def self.match?(url) 49 | is_github_url?(url) 50 | end 51 | 52 | def self.is_github_url?(url) 53 | !(GITHUB_PUBLIC_URL_PATTERN.match(url) || 54 | GITHUB_GIT_URL_PATTERN.match(url)).nil? 55 | end 56 | 57 | 58 | private 59 | 60 | def git_clone(path, install_to) 61 | output = `git clone #{path} #{install_to}` 62 | end 63 | 64 | 65 | def initialize_with_path(path) 66 | 67 | path = public_path_to_git_path($1, $2) if path =~ GITHUB_PUBLIC_URL_PATTERN 68 | 69 | raise Vimmer::InvalidPathError.new(path) unless path =~ GITHUB_GIT_URL_PATTERN 70 | 71 | @plugin_name = $1 72 | @path = path 73 | end 74 | 75 | 76 | def initialize_with_name(name) 77 | @path = Vimmer.plugins[name] 78 | @plugin_name = name 79 | end 80 | 81 | 82 | def remove_extension(path) 83 | path.gsub(/\.git$/, '') 84 | end 85 | 86 | 87 | def public_path_to_git_path(user, repo) 88 | GITHUB_GIT_PATH_TEMPLATE % [user, repo] 89 | end 90 | 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/install/github_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | include Vimmer::Installers 4 | 5 | describe "When installing from Github" do 6 | 7 | FOUND_URL = "https://github.com/tpope/vim-awesomemofo.git" 8 | NOT_FOUND_URL = "https://github.com/tpope/not-found.git" 9 | MALFORMED_URL = "https://foo.com/bar" 10 | 11 | RSpec::Matchers.define :be_a_valid_github_url do |expected| 12 | match do 13 | Github.match?(expected) == true 14 | end 15 | end 16 | 17 | context "with a non-existant URL" do 18 | 19 | let(:installer) { Github.new(:path => NOT_FOUND_URL) } 20 | 21 | before do 22 | installer.stubs(:path_exists?).returns(false) 23 | installer.stubs(:git_clone) 24 | end 25 | 26 | it { should be_a_valid_github_url(NOT_FOUND_URL) } 27 | 28 | specify "the installer should raise an exception" do 29 | lambda { installer.install }.should raise_error(Vimmer::PluginNotFoundError) 30 | end 31 | 32 | end 33 | 34 | context "with a good URL" do 35 | 36 | let(:installer) { Github.new(:path => FOUND_URL) } 37 | 38 | before do 39 | installer.stubs(:git_clone) 40 | installer.stubs(:path_exists?).returns(true) 41 | end 42 | 43 | it { should be_a_valid_github_url(FOUND_URL) } 44 | 45 | specify "the installer should not raise an exception" do 46 | lambda { installer.install }.should_not raise_error 47 | end 48 | 49 | specify "the installer calculates the plugin's name" do 50 | installer.plugin_name.should == "vim-awesomemofo" 51 | end 52 | 53 | end 54 | 55 | context "with a malformed URL" do 56 | 57 | let(:installer) { Github.new(:path => MALFORMED_URL) } 58 | 59 | 60 | it { should_not be_a_valid_github_url(MALFORMED_URL) } 61 | 62 | specify "the installer should raise an exception" do 63 | lambda { installer }.should raise_error(Vimmer::InvalidPathError) 64 | end 65 | 66 | end 67 | 68 | it "provides access to the path" do 69 | Github.new(:path => FOUND_URL). 70 | path.should == FOUND_URL 71 | end 72 | 73 | it "adds plugin to list of installed plugins" do 74 | 75 | installer = Github.new(:path => FOUND_URL) 76 | 77 | installer.stubs(:path_exists?).returns(true) 78 | installer.stubs(:git_clone) 79 | 80 | installer.install 81 | 82 | Vimmer.plugins["vim-awesomemofo"].should == FOUND_URL 83 | 84 | end 85 | 86 | context "with a public facing Github url" do 87 | 88 | subject { Github.new(:path => "http://github.com/tpope/vim-awesomemofo") } 89 | 90 | its(:path) { should == FOUND_URL } 91 | 92 | end 93 | 94 | it "installs the plugin" do 95 | 96 | installer = Github.new(:path => FOUND_URL) 97 | 98 | stub_for_install! installer 99 | 100 | installer.install 101 | 102 | plugin_path = Vimmer.bundle_path.join("vim-awesomemofo") 103 | 104 | 105 | File.directory?(plugin_path.to_s).should be_true 106 | File.directory?(plugin_path.join("plugins").to_s).should be_true 107 | File.file?(plugin_path.join("plugins", "vim-awesomemofo.vim")).should be_true 108 | 109 | end 110 | 111 | it "uninstalls the plugin" do 112 | 113 | installer = Github.new(:name => "vim-awesomemofo") 114 | 115 | 116 | stub_for_install! installer 117 | 118 | installer.install 119 | 120 | installer.uninstall 121 | 122 | plugin_path = Vimmer.bundle_path.join("vim-awesomemofo") 123 | File.directory?(plugin_path.to_s).should be_false 124 | File.directory?(plugin_path.join("plugins").to_s).should be_false 125 | end 126 | 127 | 128 | private 129 | 130 | def stub_for_install!(installer) 131 | 132 | Vimmer.stubs(:bundle_path).returns(Pathname.new("tmp/bundle")) 133 | 134 | class << installer 135 | def git_clone(arg1, arg2) 136 | FileUtils.mkdir_p(Vimmer.bundle_path.join("vim-awesomemofo", "plugins")) 137 | FileUtils.touch(Vimmer.bundle_path.join("vim-awesomemofo", "plugins", "vim-awesomemofo.vim")) 138 | end 139 | end 140 | 141 | installer.stubs(:path_exists?).returns true 142 | 143 | end 144 | 145 | end 146 | 147 | --------------------------------------------------------------------------------