gemical 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'gemical/import'
@@ -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,11 @@
1
+ class Gemical::Commands::Vaults < Gemical::Commands::Base
2
+
3
+ def index(args, options)
4
+ authenticate!
5
+
6
+ Gemical.auth.vaults.each do |vault|
7
+ say vault
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,6 @@
1
+ module Gemical::Commands
2
+ autoload :Base, 'gemical/commands/base'
3
+ autoload :Bundle, 'gemical/commands/bundle'
4
+ autoload :Gems, 'gemical/commands/gems'
5
+ autoload :Vaults, 'gemical/commands/vaults'
6
+ 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,8 @@
1
+ class Gemical::Format < Commander::HelpFormatter::Terminal
2
+
3
+ def template name
4
+ path = File.expand_path("../format/#{name}.erb", __FILE__)
5
+ ERB.new File.read(path), nil, '-'
6
+ end
7
+
8
+ end
@@ -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
@@ -0,0 +1,10 @@
1
+ class Gemical::Vault < String
2
+
3
+ attr_reader :token
4
+
5
+ def initialize(hash)
6
+ @token = hash["token"]
7
+ super hash["name"]
8
+ end
9
+
10
+ end
@@ -0,0 +1,10 @@
1
+ module Gemical
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 2
6
+ PRE = nil
7
+
8
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
+ end
10
+ 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: []