gemical 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/README.md +28 -0
- data/bin/gemical +6 -0
- data/lib/gemical/auth.rb +113 -0
- data/lib/gemical/commands/base.rb +44 -0
- data/lib/gemical/commands/bundle.rb +49 -0
- data/lib/gemical/commands/gems.rb +46 -0
- data/lib/gemical/commands/vaults.rb +11 -0
- data/lib/gemical/commands.rb +6 -0
- data/lib/gemical/configuration.rb +55 -0
- data/lib/gemical/connection.rb +78 -0
- data/lib/gemical/format/command_help.erb +25 -0
- data/lib/gemical/format/help.erb +28 -0
- data/lib/gemical/format.rb +8 -0
- data/lib/gemical/import.rb +51 -0
- data/lib/gemical/singleton.rb +22 -0
- data/lib/gemical/vault.rb +10 -0
- data/lib/gemical/version.rb +10 -0
- data/lib/gemical.rb +31 -0
- metadata +129 -0
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Gemical command-line client
|
2
|
+
|
3
|
+
Manage your private Gems in through the command line.
|
4
|
+
|
5
|
+
## License
|
6
|
+
|
7
|
+
(The MIT License)
|
8
|
+
|
9
|
+
Copyright (c) 2011 Black Square Media Ltd
|
10
|
+
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
12
|
+
a copy of this software and associated documentation files (the
|
13
|
+
"Software"), to deal in the Software without restriction, including
|
14
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
15
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
16
|
+
permit persons to whom the Software is furnished to do so, subject to
|
17
|
+
the following conditions:
|
18
|
+
|
19
|
+
The above copyright notice and this permission notice shall be
|
20
|
+
included in all copies or substantial portions of the Software.
|
21
|
+
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
23
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
24
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
25
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
26
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
27
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
28
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/gemical
ADDED
data/lib/gemical/auth.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
class Gemical::Auth
|
2
|
+
include Gemical::Singleton
|
3
|
+
|
4
|
+
def configuration
|
5
|
+
Gemical.configuration
|
6
|
+
end
|
7
|
+
|
8
|
+
def verify_account!
|
9
|
+
@verified_account ||= if credentials
|
10
|
+
get_account
|
11
|
+
else
|
12
|
+
say_ok "\nWelcome to Gemical! You are new, right?"
|
13
|
+
say_ok "We need to authenticate your account:"
|
14
|
+
collect_credentials
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def verify_vault!
|
19
|
+
verify_account!
|
20
|
+
|
21
|
+
@verified_vault ||= if primary_vault
|
22
|
+
get_vault(primary_vault) || collect_vault("Ooops, you don't seem to have access to '#{primary_vault}' anymore.")
|
23
|
+
else
|
24
|
+
collect_vault("You haven't specified your primary vault yet.")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def vaults
|
29
|
+
verify_account!
|
30
|
+
@vaults ||= Gemical.connection.get("/vaults").map do |hash|
|
31
|
+
Gemical::Vault.new(hash)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def credentials
|
36
|
+
return nil unless configuration.credentials?
|
37
|
+
pair = configuration.credentials.read.split(/\s+/)[0..1]
|
38
|
+
pair if pair.size == 2 && pair.all? {|v| string_value?(v) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def basic_auth
|
42
|
+
[credentials.last, 'x'] if credentials
|
43
|
+
end
|
44
|
+
|
45
|
+
def primary_vault
|
46
|
+
return nil unless configuration.primary_vault?
|
47
|
+
value = configuration.primary_vault.read.split(/\s+/).first
|
48
|
+
value if string_value?(value)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def get_account
|
54
|
+
Gemical.connection.get("/account")
|
55
|
+
rescue Gemical::Connection::HTTPUnauthorized
|
56
|
+
renew_credentials?
|
57
|
+
end
|
58
|
+
|
59
|
+
def collect_credentials(attempt = 1)
|
60
|
+
email = ask('Email: ')
|
61
|
+
pass = password
|
62
|
+
account = Gemical.connection.get "/account", :basic_auth => [email, pass]
|
63
|
+
configuration.write! :credentials, email, account["authentication_token"]
|
64
|
+
say_ok "Authentication successful. Thanks!\n"
|
65
|
+
account
|
66
|
+
rescue Gemical::Connection::HTTPUnauthorized
|
67
|
+
if attempt < 3
|
68
|
+
say_warning "\nInvalid email and/or password."
|
69
|
+
say_warning "Try again:"
|
70
|
+
attempt += 1
|
71
|
+
retry
|
72
|
+
else
|
73
|
+
say_error "\nAre your sure you have the right credentials?"
|
74
|
+
say_error "Visit http://gemical.com/password/new for instructions."
|
75
|
+
exit(1)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def renew_credentials?
|
80
|
+
say_warning "\nYour stored credentials seem to be outdated."
|
81
|
+
if agree("Do you want to re-authenticate your account? ")
|
82
|
+
collect_credentials
|
83
|
+
else
|
84
|
+
say_warning "You can check your stored credentials in #{configuration.credentials}."
|
85
|
+
exit(1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_vault(name)
|
90
|
+
ensure_vaults!
|
91
|
+
vaults.find {|v| v == name }
|
92
|
+
end
|
93
|
+
|
94
|
+
def collect_vault(message, save = true)
|
95
|
+
ensure_vaults!
|
96
|
+
say_warning "\n#{message}"
|
97
|
+
name = ask "Which one should it be: ", vaults
|
98
|
+
configuration.write! :primary_vault, name
|
99
|
+
get_vault(name)
|
100
|
+
end
|
101
|
+
|
102
|
+
def ensure_vaults!
|
103
|
+
return unless vaults.empty?
|
104
|
+
say_error "You have no vaults associated with your account."
|
105
|
+
say_error "Please sign in to your account and create one: http://gemical.com/sign_in."
|
106
|
+
exit(1)
|
107
|
+
end
|
108
|
+
|
109
|
+
def string_value?(value)
|
110
|
+
value.is_a?(String) && !value.strip.empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Gemical::Commands::Base
|
2
|
+
|
3
|
+
protected
|
4
|
+
|
5
|
+
def current_path
|
6
|
+
@current_path ||= Pathname.new(File.expand_path('.'))
|
7
|
+
end
|
8
|
+
|
9
|
+
def terminate(message)
|
10
|
+
say_error message
|
11
|
+
exit(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
def success(message)
|
15
|
+
say_ok message
|
16
|
+
exit(0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_errors(errors, mappings = {})
|
20
|
+
errors.map do |attr, messages|
|
21
|
+
name = mappings[attr] || attr
|
22
|
+
messages.map do |message|
|
23
|
+
"#{name} #{message}"
|
24
|
+
end.join(', ')
|
25
|
+
end.join(', ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def authenticate!
|
29
|
+
Gemical.auth.verify_account!
|
30
|
+
end
|
31
|
+
|
32
|
+
def conn
|
33
|
+
Gemical.connection
|
34
|
+
end
|
35
|
+
|
36
|
+
def current_vault(options)
|
37
|
+
options.vault || primary_vault
|
38
|
+
end
|
39
|
+
|
40
|
+
def primary_vault
|
41
|
+
Gemical.auth.verify_vault!
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Gemical::Commands::Bundle < Gemical::Commands::Base
|
2
|
+
|
3
|
+
class Evaluator
|
4
|
+
attr_reader :sources
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
@sources = []
|
8
|
+
instance_eval file.read
|
9
|
+
end
|
10
|
+
|
11
|
+
def source(path, *)
|
12
|
+
@sources << path
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(sym, *a)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(args, options)
|
20
|
+
gemfile = current_path.join('Gemfile')
|
21
|
+
terminate "Unable to find a Gemfile in #{current_path}" unless gemfile.file?
|
22
|
+
terminate "Gemfile in #{current_path} is not writable" unless gemfile.writable?
|
23
|
+
|
24
|
+
evaluation = Evaluator.new(gemfile)
|
25
|
+
terminate "Gemfile doesn't seem to be valid" if evaluation.sources.empty?
|
26
|
+
|
27
|
+
authenticate!
|
28
|
+
vault = current_vault(options)
|
29
|
+
|
30
|
+
if evaluation.sources.any? {|s| s.to_s.include?(vault.token) }
|
31
|
+
terminate "Vault '#{vault}' is already sourced in your Gemfile."
|
32
|
+
end
|
33
|
+
|
34
|
+
source = gemfile.open('r')
|
35
|
+
output = Tempfile.new "Gemfile"
|
36
|
+
|
37
|
+
while string = source.gets
|
38
|
+
output << string
|
39
|
+
if string =~ /^(\s*)source\b/
|
40
|
+
output << "#{$1}source 'http://#{vault.token}@bundle.gemical.com'\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
output.close
|
44
|
+
|
45
|
+
FileUtils.cp output.path, source.path
|
46
|
+
success "Vault '#{vault}' was added as a source to your Gemfile."
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Gemical::Commands::Gems < Gemical::Commands::Base
|
2
|
+
|
3
|
+
def index(args, options)
|
4
|
+
authenticate!
|
5
|
+
|
6
|
+
vault = current_vault(options)
|
7
|
+
conn.get("/vaults/#{vault}/gems").each do |rubygem|
|
8
|
+
versions = rubygem["versions"].map {|v| v["name"] }.sort.reverse
|
9
|
+
say "#{rubygem["name"]} (#{versions.join(', ')})"
|
10
|
+
end
|
11
|
+
rescue Gemical::Connection::HTTPNotFound
|
12
|
+
terminate "Sorry, no such vault '#{vault}'."
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(args, options)
|
16
|
+
terminate "Please provide a GEM file." unless args.first.is_a?(String)
|
17
|
+
|
18
|
+
file = current_path.join(args.first)
|
19
|
+
terminate "Hmm, no file in #{file}" unless file.file?
|
20
|
+
|
21
|
+
authenticate!
|
22
|
+
vault = current_vault(options)
|
23
|
+
response = conn.post("/vaults/#{vault}/gems", :params => { "rubygem[file]" => file.open('rb') })
|
24
|
+
success "#{response['original_name']} was successfully added to vault '#{vault}'."
|
25
|
+
rescue Gemical::Connection::HTTPNotFound
|
26
|
+
terminate "Sorry, no such vault '#{vault}'."
|
27
|
+
rescue Gemical::Connection::HTTPUnprocessable => e
|
28
|
+
terminate "Sorry, #{full_errors(e.response, 'name' => 'gem')}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def destroy(args, options)
|
32
|
+
name, version = args.first(2)
|
33
|
+
terminate "Please provide a valid GEM name." unless name.to_s =~ /\A[\w\-]+\z/
|
34
|
+
terminate "Please provide a valid VERSION." unless version.to_s =~ /\A[\w\-\.]+\z/
|
35
|
+
|
36
|
+
authenticate!
|
37
|
+
vault = current_vault(options)
|
38
|
+
response = conn.delete("/vaults/#{vault}/gems/#{name}/#{version}")
|
39
|
+
success "#{name} (#{version}) was successfully removed from #{vault}."
|
40
|
+
rescue Gemical::Connection::HTTPNotFound
|
41
|
+
terminate "Sorry, unable to find '#{name} (#{version})' in vault '#{vault}'."
|
42
|
+
rescue Gemical::Connection::HTTPUnprocessable => e
|
43
|
+
"Sorry, #{full_errors(e.response)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class Gemical::Configuration
|
4
|
+
include Gemical::Singleton
|
5
|
+
|
6
|
+
attr_writer :home
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@home = ENV['HOME']
|
10
|
+
end
|
11
|
+
|
12
|
+
def root
|
13
|
+
@root ||= Pathname.new File.join(@home, ".gemical")
|
14
|
+
end
|
15
|
+
|
16
|
+
def credentials
|
17
|
+
root.join('credentials')
|
18
|
+
end
|
19
|
+
|
20
|
+
def primary_vault
|
21
|
+
root.join('primary_vault')
|
22
|
+
end
|
23
|
+
|
24
|
+
def root?
|
25
|
+
root.directory? && root.readable?
|
26
|
+
end
|
27
|
+
|
28
|
+
def credentials?
|
29
|
+
readable_file? credentials
|
30
|
+
end
|
31
|
+
|
32
|
+
def primary_vault?
|
33
|
+
readable_file? primary_vault
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_root!
|
37
|
+
return if root?
|
38
|
+
FileUtils.mkdir_p root
|
39
|
+
FileUtils.chmod_R 0700, root
|
40
|
+
end
|
41
|
+
|
42
|
+
def write!(symbol, *lines)
|
43
|
+
create_root!
|
44
|
+
send(symbol).open "w", 0600 do |file|
|
45
|
+
file << (lines + ['']).join("\n")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def readable_file?(path)
|
52
|
+
root? && path.file? && path.readable?
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
class Gemical::Connection
|
5
|
+
include Gemical::Singleton
|
6
|
+
|
7
|
+
HTTPError = Class.new(StandardError)
|
8
|
+
HTTPUnauthorized = Class.new(HTTPError)
|
9
|
+
HTTPServerError = Class.new(HTTPError)
|
10
|
+
HTTPNotFound = Class.new(HTTPError)
|
11
|
+
|
12
|
+
class HTTPUnprocessable < HTTPError
|
13
|
+
|
14
|
+
attr_reader :response
|
15
|
+
|
16
|
+
def initialize(response)
|
17
|
+
@response = response
|
18
|
+
super "Unprocessable Entity (422)"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :base_url
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@base_url = ENV['GEMICAL_URL'] || "http://api.gemical.com"
|
27
|
+
self.proxy = ENV['http_proxy']
|
28
|
+
end
|
29
|
+
|
30
|
+
def proxy
|
31
|
+
RestClient.proxy
|
32
|
+
end
|
33
|
+
|
34
|
+
def proxy=(url)
|
35
|
+
RestClient.proxy = url if url
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(path, options = {})
|
39
|
+
request :get, path, build_headers(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def post(path, options = {})
|
43
|
+
request :post, path, options.delete(:params), build_headers(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(path, options = {})
|
47
|
+
request :delete, path, build_headers(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def request(method, path, *args)
|
53
|
+
url = [base_url, path].join
|
54
|
+
MultiJson.decode RestClient.send(method, url, *args)
|
55
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
|
56
|
+
say_error "Ooops. Can't establish a connection to #{base_url}. Are you on-line?"
|
57
|
+
exit(1)
|
58
|
+
rescue RestClient::UnprocessableEntity => e
|
59
|
+
raise HTTPUnprocessable.new MultiJson.decode(e.response.body)
|
60
|
+
rescue RestClient::Unauthorized, RestClient::Forbidden => e
|
61
|
+
raise HTTPUnauthorized, "Unauthorized request (#{e.http_code})"
|
62
|
+
rescue RestClient::ResourceNotFound
|
63
|
+
raise HTTPNotFound, "Resource not found (404)"
|
64
|
+
rescue RestClient::Exception => e
|
65
|
+
raise HTTPServerError, "Service responded with #{e.to_s}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_headers(options)
|
69
|
+
{ :accept => :json }.merge(authorization(options)).merge(options.delete(:headers) || {})
|
70
|
+
end
|
71
|
+
|
72
|
+
def authorization(options)
|
73
|
+
pair = options.delete(:basic_auth) || Gemical.auth.basic_auth
|
74
|
+
token = [pair.join(':')].pack("m").delete("\r\n")
|
75
|
+
{ :authorization => "Basic #{token}" }
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
Usage:
|
3
|
+
<%= @syntax || "gemical #{@name} [options]" %>
|
4
|
+
<% if @description || @summary -%>
|
5
|
+
|
6
|
+
Summary:
|
7
|
+
<%= @description || @summary %>
|
8
|
+
<% end -%>
|
9
|
+
<% unless @options.empty? -%>
|
10
|
+
|
11
|
+
Options:
|
12
|
+
<% for option in @options -%>
|
13
|
+
<%= "%-20s %s" % [option[:switches].join(', '), option[:description]] %>
|
14
|
+
<% end -%>
|
15
|
+
<% end -%>
|
16
|
+
<% unless @examples.empty? -%>
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
<% for description, command in @examples -%>
|
20
|
+
|
21
|
+
# <%= description %>
|
22
|
+
<%= command %>
|
23
|
+
<% end -%>
|
24
|
+
<% end -%>
|
25
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
<%= program :name %>
|
3
|
+
|
4
|
+
Commands:
|
5
|
+
<% for name, command in @commands.sort -%>
|
6
|
+
<% unless alias? name -%>
|
7
|
+
<%= "%-20s %s" % [command.name, command.summary || command.description] %>
|
8
|
+
<% end -%>
|
9
|
+
<% end -%>
|
10
|
+
<% unless @aliases.empty? %>
|
11
|
+
Aliases:
|
12
|
+
<% for alias_name, args in @aliases.sort -%>
|
13
|
+
<%= "%-20s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] %>
|
14
|
+
<% end -%>
|
15
|
+
<% end %>
|
16
|
+
<% unless @options.empty? -%>
|
17
|
+
Global Options:
|
18
|
+
<% for option in @options -%>
|
19
|
+
<%= "%-20s %s" % [option[:switches].join(', '), option[:description]] %>
|
20
|
+
<% end -%>
|
21
|
+
<% end -%>
|
22
|
+
<% if program :help -%>
|
23
|
+
<% for title, body in program(:help) %>
|
24
|
+
<%= title %>:
|
25
|
+
<%= body %>
|
26
|
+
<% end %>
|
27
|
+
<% end -%>
|
28
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'gemical'
|
2
|
+
require 'commander/import'
|
3
|
+
|
4
|
+
program :version, Gemical::VERSION::STRING
|
5
|
+
program :name, 'Gemical command-line client ~ http://gemical.com'
|
6
|
+
program :description, '-'
|
7
|
+
program :help_formatter, Gemical::Format
|
8
|
+
|
9
|
+
command :bundle do |c|
|
10
|
+
c.summary = 'Bundle a vault with your project'
|
11
|
+
c.description = 'This command will add one of your vaults as a source to the Gemfile'
|
12
|
+
c.example 'Bundle your primary vault', 'cd my-project; gemical bundle'
|
13
|
+
c.example 'Bundle a specific vault', 'cd my-project; gemical bundle --vault my-vault'
|
14
|
+
c.option '--vault VAULT', 'Specify a vault'
|
15
|
+
c.when_called Gemical::Commands::Bundle, :create
|
16
|
+
end
|
17
|
+
|
18
|
+
command :vaults do |c|
|
19
|
+
c.syntax = 'gemical vaults'
|
20
|
+
c.summary = 'List all vaults'
|
21
|
+
c.description = 'This command will list all vaults you have access to'
|
22
|
+
c.description = 'Display all my vaults'
|
23
|
+
c.when_called Gemical::Commands::Vaults, :index
|
24
|
+
end
|
25
|
+
|
26
|
+
command :gems do |c|
|
27
|
+
c.summary = 'List all gems within a vault'
|
28
|
+
c.description = 'This command will list all Gems within a vault (primary, unless specified)'
|
29
|
+
c.option '--vault [VAULT]', 'Specify a vault'
|
30
|
+
c.when_called Gemical::Commands::Gems, :index
|
31
|
+
end
|
32
|
+
|
33
|
+
command :"gems:add" do |c|
|
34
|
+
c.syntax = 'gemical gems:add GEM [options]'
|
35
|
+
c.summary = 'Add a gem to a vault'
|
36
|
+
c.description = 'This command will upload the specified Gem to one of your vaults (primary, unless specified)'
|
37
|
+
c.example 'Push a Gem to your primary vault', 'gemical gems:add my-gem-1.0.0.gem'
|
38
|
+
c.example 'Push a Gem to a specific vault', 'gemical gems:add my-gem-1.0.0.gem --vault my-vault'
|
39
|
+
c.option '--vault VAULT', 'Specify a vault'
|
40
|
+
c.when_called Gemical::Commands::Gems, :create
|
41
|
+
end
|
42
|
+
|
43
|
+
command :"gems:remove" do |c|
|
44
|
+
c.syntax = 'gemical gems:remove GEM VERSION [options]'
|
45
|
+
c.summary = 'Remove gem from a vault'
|
46
|
+
c.description = 'This command will remove the specified Gem version from one of your vaults (primary, unless specified)'
|
47
|
+
c.example 'Remove a Gem from your primary vault', 'gemical gems:remove my-gem 1.0.0'
|
48
|
+
c.example 'Remove a Gem from a specific vault', 'gemical gems:remove my-gem 1.0.0 --vault my-vault'
|
49
|
+
c.option '--vault VAULT', 'Specify a vault'
|
50
|
+
c.when_called Gemical::Commands::Gems, :destroy
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Gemical::Singleton
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
class << base
|
5
|
+
private :new
|
6
|
+
end
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def instance
|
13
|
+
@instance ||= new
|
14
|
+
end
|
15
|
+
|
16
|
+
def reload!
|
17
|
+
@instance = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/gemical.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'pathname'
|
3
|
+
require 'erb'
|
4
|
+
require 'commander'
|
5
|
+
|
6
|
+
include Commander::UI
|
7
|
+
include Commander::UI::AskForClass
|
8
|
+
|
9
|
+
module Gemical
|
10
|
+
autoload :Auth, 'gemical/auth'
|
11
|
+
autoload :Format, 'gemical/format'
|
12
|
+
autoload :Commands, 'gemical/commands'
|
13
|
+
autoload :Configuration, 'gemical/configuration'
|
14
|
+
autoload :Connection, 'gemical/connection'
|
15
|
+
autoload :Singleton, 'gemical/singleton'
|
16
|
+
autoload :Vault, 'gemical/vault'
|
17
|
+
autoload :VERSION, 'gemical/version'
|
18
|
+
|
19
|
+
def self.auth
|
20
|
+
Gemical::Auth.instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configuration
|
24
|
+
Gemical::Configuration.instance
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.connection
|
28
|
+
Gemical::Connection.instance
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gemical
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Black Square Media Ltd
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: commander
|
16
|
+
requirement: &14809440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *14809440
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rest-client
|
27
|
+
requirement: &14808980 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *14808980
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: multi_json
|
38
|
+
requirement: &14808520 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *14808520
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &14808080 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *14808080
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rspec
|
60
|
+
requirement: &14807560 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *14807560
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: &14807060 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *14807060
|
80
|
+
description: Manage your private Gems through the command line.
|
81
|
+
email: info@blacksquaremedia.com
|
82
|
+
executables:
|
83
|
+
- gemical
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- README.md
|
88
|
+
- lib/gemical.rb
|
89
|
+
- lib/gemical/connection.rb
|
90
|
+
- lib/gemical/singleton.rb
|
91
|
+
- lib/gemical/format/help.erb
|
92
|
+
- lib/gemical/format/command_help.erb
|
93
|
+
- lib/gemical/auth.rb
|
94
|
+
- lib/gemical/configuration.rb
|
95
|
+
- lib/gemical/commands.rb
|
96
|
+
- lib/gemical/import.rb
|
97
|
+
- lib/gemical/vault.rb
|
98
|
+
- lib/gemical/version.rb
|
99
|
+
- lib/gemical/format.rb
|
100
|
+
- lib/gemical/commands/base.rb
|
101
|
+
- lib/gemical/commands/bundle.rb
|
102
|
+
- lib/gemical/commands/vaults.rb
|
103
|
+
- lib/gemical/commands/gems.rb
|
104
|
+
- bin/gemical
|
105
|
+
homepage: http://github.com/bsm/gemical
|
106
|
+
licenses: []
|
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: 1.8.7
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 1.3.6
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.8.10
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Gemical command-line client.
|
129
|
+
test_files: []
|