hubless 0.1.3 → 0.2.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/README.markdown +1 -1
- data/VERSION +1 -1
- data/hubless.gemspec +1 -1
- data/lib/application.rb +31 -6
- data/lib/gem_description.rb +107 -107
- data/lib/hubless.rb +14 -10
- data/test/test_application.rb +24 -0
- data/test/test_hubless.rb +29 -94
- metadata +1 -1
data/README.markdown
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/hubless.gemspec
CHANGED
data/lib/application.rb
CHANGED
@@ -4,14 +4,39 @@ class Hubless
|
|
4
4
|
class Application
|
5
5
|
|
6
6
|
def self.run(*args)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
if help_option?(args)
|
8
|
+
display_help
|
9
|
+
else
|
10
|
+
hubless = Hubless.new
|
11
|
+
hubless.gem_breakdown
|
12
|
+
hubless.github_repos
|
13
|
+
hubless.gemcutter_gems
|
14
|
+
hubless.uninstall_instructions
|
15
|
+
if install_option?(args)
|
16
|
+
hubless.install_gems
|
17
|
+
else
|
18
|
+
hubless.install_instructions
|
19
|
+
end
|
20
|
+
end
|
13
21
|
return 0
|
14
22
|
end
|
23
|
+
|
24
|
+
protected
|
15
25
|
|
26
|
+
def self.display_help
|
27
|
+
$stdout.puts "Search your local gem repository for gems installed from GitHub that have since moved to Gemcutter"
|
28
|
+
$stdout.puts "Usage: hubless [-i|-h] "
|
29
|
+
$stdout.puts "\t-i, --install\tinstall GitHub gems that are on Gemcutter (consider running with sudo)"
|
30
|
+
$stdout.puts "\t-h, --help\tdisplay this help and exit"
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.install_option?(args)
|
34
|
+
args.include?('-i') || args.include?('--install')
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.help_option?(args)
|
38
|
+
args.include?('-h') || args.include?('--help')
|
39
|
+
end
|
40
|
+
|
16
41
|
end
|
17
42
|
end
|
data/lib/gem_description.rb
CHANGED
@@ -2,141 +2,141 @@ require 'rubygems'
|
|
2
2
|
require 'net/http'
|
3
3
|
|
4
4
|
class Hubless
|
5
|
-
ServiceError
|
5
|
+
class ServiceError < StandardError; end
|
6
6
|
|
7
|
-
class GemDescription
|
7
|
+
class GemDescription
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
attr_reader :name
|
10
|
+
attr_accessor :version
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
# GemDescriptions of all gems installed locally
|
13
|
+
def self.local_gems
|
14
|
+
@@local_gems ||= Gem.cache.map {|g| new(g.first) }.sort!{|x,y| y.name <=> x.name }
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
17
|
+
# New GemDescription from a one-liner or options Hash
|
18
|
+
# Hubless::GemDescription.new('my-awesome_gem-3.4.5')
|
19
|
+
# Hubless::GemDescription.new(:name => 'my-awesome_gem', :version => '3.4.5')
|
20
|
+
def initialize(*args)
|
21
|
+
case args.first
|
22
|
+
when String
|
23
|
+
self.attributes_from_one_liner(args.first)
|
24
|
+
when Hash
|
25
|
+
self.name = args.first[:name]
|
26
|
+
self.version = args.first[:version]
|
27
|
+
end
|
27
28
|
end
|
28
|
-
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
# Assign gem description name and clear any cached values
|
31
|
+
def name=(str)
|
32
|
+
self.clear
|
33
|
+
@name = str
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
# Does a repo exist on GitHub that matches this gem
|
37
|
+
def github?
|
38
|
+
if @is_github.nil?
|
39
|
+
@is_github = (self.github_like? && self.github_repo_exist?)
|
40
|
+
else
|
41
|
+
@is_github
|
42
|
+
end
|
42
43
|
end
|
43
|
-
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
# Full name of this gem including GitHub username
|
46
|
+
def github_name
|
47
|
+
self.name if self.github_user_name
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
def github_like?
|
51
|
+
self.github_user_name && self.github_repo_name
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
# Does a gem exist on Gemcutter that matches this gem
|
55
|
+
def gemcutter?
|
56
|
+
if @is_gemcutter.nil?
|
57
|
+
@is_gemcutter = self.gemcutter_gem_exist?
|
58
|
+
else
|
59
|
+
@is_gemcutter
|
60
|
+
end
|
60
61
|
end
|
61
|
-
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
# Likely name of this gem on Gemcutter (without the GitHub username)
|
64
|
+
def gemcutter_name
|
65
|
+
self.github? ? self.github_repo_name : self.name
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
68
|
+
# Command to install gem from Gemcutter
|
69
|
+
def install_cmd
|
70
|
+
cmd = ["gem install"]
|
71
|
+
cmd << self.gemcutter_name
|
72
|
+
cmd << "-v #{self.version}" if self.version
|
73
|
+
cmd.join(' ')
|
74
|
+
end
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
76
|
+
# Command to uninstall gem
|
77
|
+
def uninstall_cmd
|
78
|
+
cmd = ["gem uninstall"]
|
79
|
+
cmd << self.name
|
80
|
+
cmd << "-v #{self.version}" if self.version
|
81
|
+
cmd.join(' ')
|
82
|
+
end
|
83
83
|
|
84
|
-
protected
|
84
|
+
protected
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
def clear
|
87
|
+
@is_gemcutter = @is_github = nil
|
88
|
+
end
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
def github_user_name
|
91
|
+
if self.name =~ /^([^-]*)-.*$/
|
92
|
+
$1
|
93
|
+
end
|
93
94
|
end
|
94
|
-
end
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
def github_repo_name
|
97
|
+
if self.name =~ /^[^-]*-(.*)$/
|
98
|
+
$1
|
99
|
+
end
|
99
100
|
end
|
100
|
-
end
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
102
|
+
def github_uri
|
103
|
+
URI.parse("http://github.com/api/v2/yaml/repos/show/#{self.github_user_name}/#{self.github_repo_name}")
|
104
|
+
end
|
105
105
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
106
|
+
def github_repo_exist?
|
107
|
+
response = Net::HTTP.get(self.github_uri)
|
108
|
+
case
|
109
|
+
when response =~ /error: repository not found/
|
110
|
+
false
|
111
|
+
when response =~ /error: too many requests/
|
112
|
+
raise ServiceError
|
113
|
+
else
|
114
|
+
true
|
115
|
+
end
|
115
116
|
end
|
116
|
-
end
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
118
|
+
def gemcutter_uri
|
119
|
+
URI.parse("http://gemcutter.org/api/v1/gems/#{self.name}.json")
|
120
|
+
end
|
121
121
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
122
|
+
def gemcutter_gem_exist?
|
123
|
+
response = Net::HTTP.get(self.gemcutter_uri)
|
124
|
+
case
|
125
|
+
when response =~ /This rubygem could not be found./
|
126
|
+
false
|
127
|
+
when response =~ /error: too many requests/
|
128
|
+
raise ServiceError
|
129
|
+
else
|
130
|
+
true
|
131
|
+
end
|
131
132
|
end
|
132
|
-
end
|
133
133
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
134
|
+
def attributes_from_one_liner(one_liner)
|
135
|
+
if one_liner =~ /^(.*)-([\d\.]+)$/
|
136
|
+
self.name = $1
|
137
|
+
self.version = $2
|
138
|
+
end
|
138
139
|
end
|
139
|
-
end
|
140
140
|
|
141
|
-
end
|
141
|
+
end
|
142
142
|
end
|
data/lib/hubless.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/gem_description'
|
2
2
|
|
3
|
+
|
4
|
+
|
3
5
|
class Hubless
|
6
|
+
class GemInstallError < StandardError; end
|
7
|
+
|
4
8
|
@@io = $stdout
|
5
9
|
@@timeout = 1
|
6
10
|
|
@@ -55,14 +59,16 @@ class Hubless
|
|
55
59
|
self.gems.each {|g| @@io.puts(g.install_cmd) if g.github? && g.gemcutter? }
|
56
60
|
end
|
57
61
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
def install_gems
|
63
|
+
@@io.puts("\nInstalling gems:")
|
64
|
+
self.gems.each do |g|
|
65
|
+
if g.github? && g.gemcutter?
|
66
|
+
cmd = g.install_cmd
|
67
|
+
@@io.puts cmd
|
68
|
+
raise GemInstallError unless Kernel.system(cmd)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
66
72
|
|
67
73
|
protected
|
68
74
|
|
@@ -71,5 +77,3 @@ protected
|
|
71
77
|
end
|
72
78
|
|
73
79
|
end
|
74
|
-
|
75
|
-
# Hubless.local_gems_on_github.each{|g| puts g.inspect }
|
data/test/test_application.rb
CHANGED
@@ -14,4 +14,28 @@ class TestApplication < Test::Unit::TestCase
|
|
14
14
|
Hubless::Application.run
|
15
15
|
end
|
16
16
|
|
17
|
+
def test_run_with_i
|
18
|
+
hubless = mock(
|
19
|
+
:gem_breakdown => true,
|
20
|
+
:github_repos => true,
|
21
|
+
:gemcutter_gems => true,
|
22
|
+
:uninstall_instructions => true,
|
23
|
+
:install_gems => true
|
24
|
+
)
|
25
|
+
Hubless.expects(:new).returns(hubless)
|
26
|
+
Hubless::Application.run('-i')
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_run_with_h
|
30
|
+
original_stdout = $stdout
|
31
|
+
fake_stdout = mock
|
32
|
+
fake_stdout.stubs(:write)
|
33
|
+
$stdout = fake_stdout
|
34
|
+
fake_stdout.expects(:puts).at_least_once
|
35
|
+
Hubless.expects(:new).never
|
36
|
+
Hubless::Application.run('-h')
|
37
|
+
ensure
|
38
|
+
$stdout = original_stdout
|
39
|
+
end
|
40
|
+
|
17
41
|
end
|
data/test/test_hubless.rb
CHANGED
@@ -98,99 +98,34 @@ class TestHubless < Test::Unit::TestCase
|
|
98
98
|
hubless.install_instructions
|
99
99
|
end
|
100
100
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# assert ! local_github_like_gems.detect{|gem_description| gem_description.name == 'my_non_github_gem' }
|
131
|
-
# end
|
132
|
-
#
|
133
|
-
# def test_local_gems_on_github
|
134
|
-
# Net::HTTP.expects(:get).times(3).
|
135
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
136
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
137
|
-
# returns("--- \nerror: \n- error: repository not found\n")
|
138
|
-
# local_gems_on_github = Hubless.local_gems_on_github
|
139
|
-
# assert local_gems_on_github.detect{|gem_description| gem_description.name == 'my-github_and_gemcutter_gem' }
|
140
|
-
# assert local_gems_on_github.detect{|gem_description| gem_description.name == 'my-github_only_gem' }
|
141
|
-
# assert ! local_gems_on_github.detect{|gem_description| gem_description.name == 'my-github_look_a_like_on_gemcutter_gem' }
|
142
|
-
# assert ! local_gems_on_github.detect{|gem_description| gem_description.name == 'my_non_github_gem' }
|
143
|
-
# end
|
144
|
-
#
|
145
|
-
# def test_local_gems_on_github_with_github_error
|
146
|
-
# Net::HTTP.expects(:get).times(2).
|
147
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
148
|
-
# returns("--- \nerror: \n- error: too many requests\n")
|
149
|
-
# assert_raise(ServiceError) { Hubless.local_gems_on_github }
|
150
|
-
# end
|
151
|
-
#
|
152
|
-
# def test_local_gems_on_github_and_gemcutter
|
153
|
-
# Net::HTTP.expects(:get).times(5).
|
154
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
155
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
156
|
-
# returns("--- \nerror: \n- error: repository not found\n").
|
157
|
-
# returns(%Q{{"version_downloads":137674,"info":" Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick\n on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.\n","project_uri":"http://gemcutter.org/gems/rails","name":"rails","version":"2.3.5","gem_uri":"http://gemcutter.org/gems/rails-2.3.5.gem","downloads":218523,"authors":"David Heinemeier Hansson"}}).
|
158
|
-
# returns("This rubygem could not be found.")
|
159
|
-
# local_gems_on_github_and_gemcutter = Hubless.local_gems_on_github_and_gemcutter
|
160
|
-
# assert local_gems_on_github_and_gemcutter.detect{|gem_description| gem_description.name == 'my-github_and_gemcutter_gem' }
|
161
|
-
# assert ! local_gems_on_github_and_gemcutter.detect{|gem_description| gem_description.name == 'my-github_only_gem' }
|
162
|
-
# assert ! local_gems_on_github_and_gemcutter.detect{|gem_description| gem_description.name == 'my-github_look_a_like_on_gemcutter_gem' }
|
163
|
-
# assert ! local_gems_on_github_and_gemcutter.detect{|gem_description| gem_description.name == 'my_non_github_gem' }
|
164
|
-
# end
|
165
|
-
#
|
166
|
-
# def test_clear
|
167
|
-
# Net::HTTP.expects(:get).times(5).
|
168
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
169
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
170
|
-
# returns("--- \nerror: \n- error: repository not found\n").
|
171
|
-
# returns(%Q{{"version_downloads":137674,"info":" Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick\n on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.\n","project_uri":"http://gemcutter.org/gems/rails","name":"rails","version":"2.3.5","gem_uri":"http://gemcutter.org/gems/rails-2.3.5.gem","downloads":218523,"authors":"David Heinemeier Hansson"}}).
|
172
|
-
# returns("This rubygem could not be found.")
|
173
|
-
#
|
174
|
-
# Hubless.local_gems_on_github_and_gemcutter
|
175
|
-
#
|
176
|
-
# Net::HTTP.expects(:get).times(0)
|
177
|
-
#
|
178
|
-
# Hubless.local_gems_on_github_and_gemcutter
|
179
|
-
#
|
180
|
-
# Hubless.clear
|
181
|
-
#
|
182
|
-
# Net::HTTP.expects(:get).times(5).
|
183
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
184
|
-
# returns("--- \nrepository: \n :description: Description here\n :forks: 0\n :url: http://github.com/some_user/some_repo\n :fork: false\n :open_issues: 0\n :watchers: 1\n :private: false\n :name: some_repo\n :owner: some_user\n").
|
185
|
-
# returns("--- \nerror: \n- error: repository not found\n").
|
186
|
-
# returns(%Q{{"version_downloads":137674,"info":" Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick\n on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.\n","project_uri":"http://gemcutter.org/gems/rails","name":"rails","version":"2.3.5","gem_uri":"http://gemcutter.org/gems/rails-2.3.5.gem","downloads":218523,"authors":"David Heinemeier Hansson"}}).
|
187
|
-
# returns("This rubygem could not be found.")
|
188
|
-
#
|
189
|
-
# Hubless.local_gems_on_github_and_gemcutter
|
190
|
-
# end
|
191
|
-
def test_nothin
|
192
|
-
assert true
|
193
|
-
end
|
194
|
-
|
101
|
+
def test_install_gems
|
102
|
+
local_gems = [
|
103
|
+
mock(:install_cmd => 'foo', :github? => true, :gemcutter? => true),
|
104
|
+
mock(:install_cmd => 'bar', :github? => true, :gemcutter? => true),
|
105
|
+
mock(:install_cmd => 'abc', :github? => true, :gemcutter? => true),
|
106
|
+
mock(:github? => true, :gemcutter? => false),
|
107
|
+
mock(:github? => false)
|
108
|
+
]
|
109
|
+
Hubless::GemDescription.expects(:local_gems).once.returns(local_gems)
|
110
|
+
@io.expects(:puts).with{|s| s =~ /Installing gems:/ }
|
111
|
+
@io.expects(:puts).once.with{|s| s =~ /foo/ }
|
112
|
+
@io.expects(:puts).once.with{|s| s =~ /bar/ }
|
113
|
+
@io.expects(:puts).once.with{|s| s =~ /abc/ }
|
114
|
+
Kernel.expects(:system).once.with{|s| s =~ /foo/ }.returns(true)
|
115
|
+
Kernel.expects(:system).once.with{|s| s =~ /bar/ }.returns(true)
|
116
|
+
Kernel.expects(:system).once.with{|s| s =~ /abc/ }.returns(true)
|
117
|
+
hubless = Hubless.new
|
118
|
+
hubless.install_gems
|
119
|
+
|
120
|
+
local_gems = [
|
121
|
+
mock(:install_cmd => 'foo', :github? => true, :gemcutter? => true)
|
122
|
+
]
|
123
|
+
Hubless::GemDescription.expects(:local_gems).once.returns(local_gems)
|
124
|
+
@io.expects(:puts).with{|s| s =~ /Installing gems:/ }
|
125
|
+
@io.expects(:puts).once.with{|s| s =~ /foo/ }
|
126
|
+
Kernel.expects(:system).once.with{|s| s =~ /foo/ }.returns(false)
|
127
|
+
hubless = Hubless.new
|
128
|
+
assert_raise(Hubless::GemInstallError) { hubless.install_gems }
|
129
|
+
end
|
195
130
|
|
196
131
|
end
|