unbundler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in unbundler.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # Unbundler
2
+
3
+ Remove gems installed by bundler
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'unbundler'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install unbundler
18
+
19
+ ## Usage
20
+
21
+ `unbundle` - uninstall all gems installed by current bundle except gems in global keep list
22
+ `unbundle --keep gem1` - uninstall all gems except gem1 and gems in global keep list
23
+ `unbundle show` or `unbundle list` - show list of gems to be unbundled
24
+ `unbundle edit_keep_list` - open global keep list in default editor
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/unbundle ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # Exit cleanly from an early interrupt
3
+ Signal.trap("INT") { exit 1 }
4
+
5
+ require 'unbundler'
6
+ Unbundler::CLI.start
data/bin/unbundler ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'unbundler'
4
+ bin = "#{File.dirname(__FILE__)}/unbundle #{ARGV.join(" ")}"
5
+ exec bin
@@ -0,0 +1,87 @@
1
+ require 'trollop'
2
+ require 'unbundler/gem_list'
3
+ require 'pp'
4
+
5
+ module Unbundler
6
+ class CLI
7
+ def self.start
8
+ cli = CLI.new
9
+ cli.run(cli.parse_args)
10
+ end
11
+
12
+ def run(opts)
13
+ @interactive = opts[:interactive]
14
+ @plain = opts[:plain]
15
+ @quiet = opts[:quiet]
16
+ @gems = GemList.new(opts[:keep])
17
+ self.send(opts[:action])
18
+ end
19
+
20
+ def parse_args
21
+ opts = { :action => :unbundle }
22
+ case ARGV.first
23
+ when "show", "list"
24
+ opts[:action] = :show
25
+ when "edit_keep_list"
26
+ opts[:action] = :edit_keep_list
27
+ end
28
+ opts.merge( Trollop::options do
29
+ version "unbundler v#{Unbundler::VERSION}"
30
+ opt :keep, "List of gems to keep", :type => :strings
31
+ opt :plain, "Plain output for unbundle list"
32
+ opt :interactive, "Interactive unbundle(ask about each gem)"
33
+ opt :quiet, "Produce no output"
34
+ end)
35
+ end
36
+
37
+ private
38
+
39
+ def unbundle
40
+ @gems.each do |gem|
41
+ uninstall_gem(gem) unless keep(gem)
42
+ end
43
+ end
44
+
45
+ def show
46
+ puts "Gems to be unbundled:" unless @plain
47
+ @gems.each do |gem|
48
+ puts @plain ? gem : " * #{gem}"
49
+ end
50
+ end
51
+
52
+ def edit_keep_list
53
+ exec "${FCEDIT:-${VISUAL:-${EDITOR:-vi}}} ~/.unbundler_keep"
54
+ end
55
+
56
+ # http://stackoverflow.com/a/14527475/162459
57
+ def get_char
58
+ state = `stty -g`
59
+ `stty raw -echo -icanon isig`
60
+ STDIN.getc.chr
61
+ ensure
62
+ `stty #{state}`
63
+ end
64
+
65
+ def keep(gem)
66
+ return false unless @interactive
67
+ while true do
68
+ puts "Delete #{gem} (y/n/a)"
69
+ case get_char
70
+ when 'y', 'Y'
71
+ return false
72
+ when 'n', 'N'
73
+ return true
74
+ when 'a', 'A'
75
+ @interactive = false
76
+ return false
77
+ else
78
+ puts "y/n/a expected"
79
+ end
80
+ end
81
+ end
82
+
83
+ def uninstall_gem(gem)
84
+ `gem -x uninstall #{gem}`
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,52 @@
1
+ require 'unbundler/keep_list'
2
+ require 'bundler'
3
+
4
+ module Unbundler
5
+ class GemList
6
+ def initialize(keep_list)
7
+ @keep_list = KeepList.new(keep_list || [])
8
+ @global_keep_list = KeepList.new(global_keep_list)
9
+ end
10
+
11
+ def each
12
+ gems_to_unbundle.each do |gem|
13
+ yield gem.name
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def unbundler_runtime_dependencies
20
+ Gem::Dependency.new("unbundler").to_spec.runtime_dependencies
21
+ end
22
+
23
+ def create_default_keep_list
24
+ keep_list_file = File.expand_path("~/.unbundler_keep")
25
+ File.open(keep_list_file, "w") do |keep_list|
26
+ keep_list.puts "unbundler"
27
+ unbundler_runtime_dependencies.each do |gem|
28
+ keep_list.puts "#{gem.name} '#{gem.requirement.to_s}'"
29
+ end
30
+ end
31
+ end
32
+
33
+ def global_keep_list
34
+ keep_list_file = File.expand_path("~/.unbundler_keep")
35
+ create_default_keep_list unless File.exist?(keep_list_file)
36
+ File.open(keep_list_file).readlines.map(&:chomp).map(&:strip)
37
+ end
38
+
39
+ def in_keep_list?(gem)
40
+ @keep_list.include?(gem) || @global_keep_list.include?(gem)
41
+ end
42
+
43
+ def bundled_gems
44
+ @bundled_gems ||= Bundler.load.specs
45
+ end
46
+
47
+
48
+ def gems_to_unbundle
49
+ @gems_to_unbundle ||= bundled_gems.reject { |gem| in_keep_list?(gem) }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+
3
+ module Unbundler
4
+ class KeepList
5
+ def initialize(gems_list)
6
+ name_version_pairs = gems_list.map do |gem|
7
+ gem = gem.tr("\"'", "")
8
+ pair = gem.split(" ", 2)
9
+ [pair.shift, Gem::Dependency.new('unbundler', pair.shift)]
10
+ end
11
+ @keep_list = Hash[name_version_pairs]
12
+ end
13
+
14
+ def include?(gem)
15
+ return false unless @keep_list.keys.include?(gem.name)
16
+ return false unless @keep_list[gem.name].match?('unbundler', gem.version)
17
+ true
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Unbundler
2
+ VERSION = "0.0.1"
3
+ end
data/lib/unbundler.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "unbundler/version"
2
+ require "unbundler/cli"
3
+
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'unbundler'
2
+ require 'spec_helper'
3
+
4
+ describe Unbundler::CLI do
5
+ def replace_argv(new_argv)
6
+ ::Object.send(:remove_const, :ARGV) # suppress const redefinition warning
7
+ ::Object.send(:const_set, :ARGV, Array(new_argv))
8
+ end
9
+
10
+ def run_cli(args=[])
11
+ replace_argv(args)
12
+ subject.run(subject.parse_args)
13
+ return 0
14
+ rescue SystemExit => e
15
+ return e.status
16
+ end
17
+
18
+ shared_examples_for 'command line argument' do
19
+ it 'short form' do
20
+ replace_argv([short_name] | Array(value))
21
+ subject.parse_args[key].should == expected_value
22
+ end
23
+ it 'long form' do
24
+ replace_argv([long_name] | Array(value))
25
+ subject.parse_args[key] == value
26
+ end
27
+ it 'short and long form should match' do
28
+ replace_argv([short_name] | Array(value))
29
+ opts = subject.parse_args
30
+ replace_argv([long_name] | Array(value))
31
+ opts[key].should == subject.parse_args[key]
32
+ end
33
+ end
34
+
35
+ context 'keep option' do
36
+ let(:short_name) {'-k'}
37
+ let(:long_name) {'--keep'}
38
+ let(:key) {:keep}
39
+ let(:value) {%w(gem1 gem2 gem3)}
40
+ let(:expected_value){%w(gem1 gem2 gem3)}
41
+ it_should_behave_like 'command line argument'
42
+ end
43
+
44
+ context 'plain option' do
45
+ let(:short_name) {'-p'}
46
+ let(:long_name) {'--plain'}
47
+ let(:key) {:plain}
48
+ let(:value) {''}
49
+ let(:expected_value){true}
50
+ it_should_behave_like 'command line argument'
51
+ end
52
+
53
+ context 'interactive option' do
54
+ let(:short_name) {'-i'}
55
+ let(:long_name) {'--interactive'}
56
+ let(:key) {:interactive}
57
+ let(:value) {''}
58
+ let(:expected_value){true}
59
+ it_should_behave_like 'command line argument'
60
+ end
61
+
62
+ context 'quiet option' do
63
+ let(:short_name) {'-q'}
64
+ let(:long_name) {'--quiet'}
65
+ let(:key) {:quiet}
66
+ let(:value) {''}
67
+ let(:expected_value){true}
68
+ it_should_behave_like 'command line argument'
69
+ end
70
+
71
+ it 'should run action specified in command line' do
72
+ ['show', 'edit_keep_list'].each do |action|
73
+ subject.should_receive(action.to_sym).and_return(true)
74
+ run_cli(action)
75
+ end
76
+ end
77
+
78
+ it 'should run unbundle action by default(i.e. no action is specified)' do
79
+ subject.should_receive(:unbundle).and_return(true)
80
+ run_cli
81
+ end
82
+
83
+ end
@@ -0,0 +1,42 @@
1
+ require 'unbundler'
2
+ require 'unbundler/gem_list'
3
+ require 'spec_helper'
4
+
5
+ describe Unbundler::GemList do
6
+ subject { lambda { |keep_list=nil| Unbundler::GemList.new(keep_list || []) } }
7
+ let(:specs) { [Gem::Specification.new("gem1"),
8
+ Gem::Specification.new("gem2"),
9
+ Gem::Specification.new("gem3"),
10
+ Gem::Specification.new("gem4"),
11
+ Gem::Specification.new("gem5"),
12
+ Gem::Specification.new("gem6")] }
13
+ before(:each) do
14
+ bundler_load = double("Bundler.load")
15
+ bundler_load.should_receive(:specs).and_return(specs)
16
+ Bundler.should_receive(:load).and_return(bundler_load)
17
+ end
18
+
19
+ it 'should allow to enumarate gems to unbundle' do
20
+ gems = []
21
+ subject.call.each { |gem| gems << gem }
22
+ gems.should =~ specs.map { |spec| spec.name }
23
+ end
24
+
25
+ it 'should exclude gems from global keep list' do
26
+ file = double(file)
27
+ keep_list_file = File.expand_path("~/.unbundler_keep")
28
+ File.should_receive(:exist?).with(keep_list_file).and_return(true)
29
+ File.should_receive(:open).with(keep_list_file).and_return(file)
30
+ file.should_receive(:readlines).and_return(["gem3"])
31
+ gems = []
32
+ subject.call.each { |gem| gems << gem }
33
+ gems.should_not include "gem3"
34
+ end
35
+
36
+ it 'should exclude gems from provided keep list' do
37
+ gems = []
38
+ keep_list = %w(gem2 gem4 gem6)
39
+ subject.call(keep_list).each { |gem| gems << gem }
40
+ expect(gems).not_to match_array keep_list
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ require 'unbundler'
2
+ require 'unbundler/keep_list'
3
+ require 'spec_helper'
4
+
5
+ describe Unbundler::KeepList do
6
+ subject { lambda { |gem_list| Unbundler::KeepList.new(gem_list) } }
7
+
8
+ it 'should check gem presence by gem name' do
9
+ gem = Gem::Specification.new("gem")
10
+ expect {subject.call(%w(gem gem2 gem3)).include?(gem)}.to be_true
11
+ end
12
+
13
+ it 'should check gem presence by gem name and version' do
14
+ gem = Gem::Specification.new("gem", "1.0.0")
15
+ gems = %(gem1 gem3 gem3)
16
+ gems << "gem '1.0.0'"
17
+ expect {subject.call(gems).include?(gem)}.to be_true
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ RSpec.configure do |config|
2
+ # Use color in STDOUT
3
+ config.color_enabled = true
4
+
5
+ # Use color not only in STDOUT but also in pagers and files
6
+ config.tty = true
7
+
8
+ # Use the specified formatter
9
+ config.formatter = :documentation # :progress, :html, :textmate
10
+ end
data/unbundler.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'unbundler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "unbundler"
8
+ spec.version = Unbundler::VERSION
9
+ spec.authors = ["Stas Turlo"]
10
+ spec.email = ["stanislav.turlo@rightscale.com"]
11
+ spec.description = %q{Remove gems installed by bundler}
12
+ spec.summary = %q{Remove gems installed by bundler}
13
+ spec.homepage = ""
14
+ spec.license = "WTFPL"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec", ">= 2.0.0"
23
+
24
+ spec.add_dependency "bundler"
25
+ spec.add_dependency "trollop"
26
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unbundler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stas Turlo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 2.0.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.0.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: trollop
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Remove gems installed by bundler
79
+ email:
80
+ - stanislav.turlo@rightscale.com
81
+ executables:
82
+ - unbundle
83
+ - unbundler
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - Gemfile
89
+ - LICENSE.txt
90
+ - README.md
91
+ - Rakefile
92
+ - bin/unbundle
93
+ - bin/unbundler
94
+ - lib/unbundler.rb
95
+ - lib/unbundler/cli.rb
96
+ - lib/unbundler/gem_list.rb
97
+ - lib/unbundler/keep_list.rb
98
+ - lib/unbundler/version.rb
99
+ - spec/cli_spec.rb
100
+ - spec/gem_list_spec.rb
101
+ - spec/keep_list_spec.rb
102
+ - spec/spec_helper.rb
103
+ - unbundler.gemspec
104
+ homepage: ''
105
+ licenses:
106
+ - WTFPL
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ segments:
118
+ - 0
119
+ hash: -3134477564524655065
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ segments:
127
+ - 0
128
+ hash: -3134477564524655065
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 1.8.23
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: Remove gems installed by bundler
135
+ test_files:
136
+ - spec/cli_spec.rb
137
+ - spec/gem_list_spec.rb
138
+ - spec/keep_list_spec.rb
139
+ - spec/spec_helper.rb