strongspace 0.0.3

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/bin/strongspace ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'strongspace'
7
+ require 'strongspace/command'
8
+
9
+ args = ARGV.dup
10
+ ARGV.clear
11
+ command = args.shift.strip rescue 'help'
12
+
13
+ Strongspace::Command.run(command, args)
14
+
@@ -0,0 +1,136 @@
1
+ require 'rest_client'
2
+ require 'uri'
3
+ require 'strongspace/version'
4
+ require 'json/pure' unless {}.respond_to?(:to_json)
5
+
6
+ # A Ruby class to call the Strongspace REST API. You might use this if you want to
7
+ # manage your Strongspace apps from within a Ruby program, such as Capistrano.
8
+ #
9
+ # Example:
10
+ #
11
+ # require 'strongspace'
12
+ # strongspace = Strongspace::Client.new('me@example.com', 'mypass')
13
+ #
14
+ class Strongspace::Client
15
+ def self.version
16
+ Strongspace::VERSION
17
+ end
18
+
19
+ def self.gem_version_string
20
+ "strongspace-gem/#{version}"
21
+ end
22
+
23
+ attr_accessor :host, :user, :password
24
+
25
+ def self.auth(user, password, host='https://www.strongspace.com')
26
+ client = new(user, password, host)
27
+ JSON.parse client.get('/api/v1/api_token', :username => user, :password => password).to_s
28
+ end
29
+
30
+ def username
31
+ self.user.split("/")[0]
32
+ end
33
+
34
+ def initialize(user, password, host='https://www.strongspace.com')
35
+ @user = user
36
+ @password = password
37
+ @host = host
38
+ end
39
+
40
+ def spaces
41
+ doc = JSON.parse get('/api/v1/spaces')
42
+ end
43
+
44
+ def destroy_space(space_name)
45
+ doc = JSON.parse delete("/api/v1/spaces/#{escape(space_name)}").to_s
46
+ end
47
+
48
+ def create_space(name, type='normal')
49
+ doc = JSON.parse post("/api/v1/spaces", :name => name, :type => type)
50
+ end
51
+
52
+ def get_space(space_name)
53
+ doc = JSON.parse get("/api/v1/spaces/#{escape(space_name)}")
54
+ end
55
+
56
+ def snapshots(space_name)
57
+ doc = JSON.parse get("/api/v1/spaces/#{escape(space_name)}/snapshots").to_s
58
+ end
59
+
60
+ def destroy_snapshot(space_name, snapshot_name)
61
+ doc = JSON.parse delete("/api/v1/spaces/#{escape(space_name)}/snapshots/#{escape(snapshot_name)}").to_s
62
+ end
63
+
64
+ def create_snapshot(space_name, snapshot_name)
65
+ doc = JSON.parse post("/api/v1/spaces/#{escape(space_name)}/snapshots", :name => snapshot_name)
66
+ end
67
+
68
+
69
+ # Get the list of ssh public keys for the current user.
70
+ def keys
71
+ doc = JSON.parse get('/api/v1/ssh_keys')
72
+ end
73
+
74
+ # Add an ssh public key to the current user.
75
+ def add_key(key)
76
+ post("/api/v1/ssh_keys", :key => key).to_s
77
+ end
78
+
79
+ # Remove an existing ssh public key from the current user.
80
+ def remove_key(key_id)
81
+ delete("/api/v1/ssh_keys/#{key_id}").to_s
82
+ end
83
+
84
+ # Clear all keys on the current user.
85
+ def remove_all_keys
86
+ delete("/api/v1/ssh_keys").to_s
87
+ end
88
+
89
+ ##################
90
+
91
+ def resource(uri)
92
+ RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
93
+ if uri =~ /^https?/
94
+ RestClient::Resource.new(uri, user, password)
95
+ elsif host =~ /^https?/
96
+ RestClient::Resource.new(host, user, password)[uri]
97
+ end
98
+ end
99
+
100
+ def get(uri, extra_headers={}) # :nodoc:
101
+ process(:get, uri, extra_headers)
102
+ end
103
+
104
+ def post(uri, payload="", extra_headers={}) # :nodoc:
105
+ process(:post, uri, extra_headers, payload)
106
+ end
107
+
108
+ def put(uri, payload, extra_headers={}) # :nodoc:
109
+ process(:put, uri, extra_headers, payload)
110
+ end
111
+
112
+ def delete(uri, extra_headers={}) # :nodoc:
113
+ process(:delete, uri, extra_headers)
114
+ end
115
+
116
+ def process(method, uri, extra_headers={}, payload=nil)
117
+ headers = strongspace_headers.merge(extra_headers)
118
+ args = [method, payload, headers].compact
119
+ response = resource(uri).send(*args)
120
+ end
121
+
122
+ def escape(value) # :nodoc:
123
+ escaped = URI.escape(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
124
+ escaped.gsub('.', '%2E') # not covered by the previous URI.escape
125
+ end
126
+
127
+ def strongspace_headers # :nodoc:
128
+ {
129
+ 'X-Strongspace-API-Version' => '1',
130
+ 'User-Agent' => self.class.gem_version_string,
131
+ 'X-Ruby-Version' => RUBY_VERSION,
132
+ 'X-Ruby-Platform' => RUBY_PLATFORM
133
+ }
134
+ end
135
+
136
+ end
@@ -0,0 +1,85 @@
1
+ require 'strongspace/helpers'
2
+ require 'strongspace/commands/base'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
5
+
6
+ module Strongspace
7
+ module Command
8
+ class InvalidCommand < RuntimeError; end
9
+ class CommandFailed < RuntimeError; end
10
+
11
+ extend Strongspace::Helpers
12
+
13
+ class << self
14
+
15
+ def run(command, args, retries=0)
16
+ begin
17
+ run_internal 'auth:reauthorize', args.dup if retries > 0
18
+ run_internal(command, args.dup)
19
+ rescue InvalidCommand
20
+ error "Unknown command. Run 'strongspace help' for usage information."
21
+ rescue RestClient::Unauthorized
22
+ if retries < 3
23
+ STDERR.puts "Authentication failure"
24
+ run(command, args, retries+1)
25
+ else
26
+ error "Authentication failure"
27
+ end
28
+ rescue RestClient::ResourceNotFound => e
29
+ error extract_not_found(e.http_body)
30
+ rescue RestClient::RequestFailed => e
31
+ error extract_error(e.http_body) unless e.http_code == 402
32
+ rescue RestClient::RequestTimeout
33
+ error "API request timed out. Please try again, or contact support@strongspace.com if this issue persists."
34
+ rescue CommandFailed => e
35
+ error e.message
36
+ rescue Interrupt => e
37
+ error "\n[canceled]"
38
+ end
39
+ end
40
+
41
+ def run_internal(command, args, strongspace=nil)
42
+ klass, method = parse(command)
43
+ runner = klass.new(args, strongspace)
44
+ raise InvalidCommand unless runner.respond_to?(method)
45
+ runner.send(method)
46
+ end
47
+
48
+ def parse(command)
49
+ parts = command.split(':')
50
+ case parts.size
51
+ when 1
52
+ begin
53
+ return eval("Strongspace::Command::#{command.capitalize}"), :index
54
+ rescue NameError, NoMethodError
55
+ return Strongspace::Command::App, command.to_sym
56
+ end
57
+ else
58
+ begin
59
+ const = Strongspace::Command
60
+ command = parts.pop
61
+ parts.each { |part| const = const.const_get(part.capitalize) }
62
+ return const, command.to_sym
63
+ rescue NameError
64
+ raise InvalidCommand
65
+ end
66
+ end
67
+ end
68
+
69
+ def extract_not_found(body)
70
+ body =~ /^[\w\s]+ not found$/ ? body : "Resource not found"
71
+ end
72
+
73
+ def extract_error(body)
74
+ msg = parse_error_json(body) || 'Internal server error'
75
+ msg.split("\n").map { |line| ' ! ' + line }.join("\n")
76
+ end
77
+
78
+ def parse_error_json(body)
79
+ json = JSON.parse(body.to_s)
80
+ json['status']
81
+ rescue JSON::ParserError
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,143 @@
1
+ module Strongspace::Command
2
+ class Auth < Base
3
+ attr_accessor :credentials
4
+
5
+ def client
6
+ @client ||= init_strongspace
7
+ end
8
+
9
+ def init_strongspace
10
+ client = Strongspace::Client.new(user, password, host)
11
+ client
12
+ end
13
+
14
+ # just a stub; will raise if not authenticated
15
+ def check
16
+ client.spaces
17
+ end
18
+
19
+ def host
20
+ ENV['STRONGSPACE_HOST'] || 'https://www.strongspace.com'
21
+ end
22
+
23
+ def reauthorize
24
+ @credentials = ask_for_credentials
25
+ write_credentials
26
+ end
27
+
28
+ def user # :nodoc:
29
+ get_credentials
30
+ @credentials[0]
31
+ end
32
+
33
+ def password # :nodoc:
34
+ get_credentials
35
+ @credentials[1]
36
+ end
37
+
38
+ def credentials_file
39
+ "#{home_directory}/.strongspace/credentials"
40
+ end
41
+
42
+ def get_credentials # :nodoc:
43
+ return if @credentials
44
+ unless @credentials = read_credentials
45
+ @credentials = ask_for_credentials
46
+ save_credentials
47
+ end
48
+ @credentials
49
+ end
50
+
51
+ def read_credentials
52
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
53
+ end
54
+
55
+ def echo_off
56
+ system "stty -echo"
57
+ end
58
+
59
+ def echo_on
60
+ system "stty echo"
61
+ end
62
+
63
+ def ask_for_credentials
64
+ puts "Enter your Strongspace credentials."
65
+
66
+ print "Username or Email: "
67
+ user = ask
68
+
69
+ print "Password: "
70
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
71
+
72
+ ["#{user}/token", Strongspace::Client.auth(user, password, host)['api_token']]
73
+ end
74
+
75
+ def ask_for_password_on_windows
76
+ require "Win32API"
77
+ char = nil
78
+ password = ''
79
+
80
+ while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
81
+ break if char == 10 || char == 13 # received carriage return or newline
82
+ if char == 127 || char == 8 # backspace and delete
83
+ password.slice!(-1, 1)
84
+ else
85
+ # windows might throw a -1 at us so make sure to handle RangeError
86
+ (password << char.chr) rescue RangeError
87
+ end
88
+ end
89
+ puts
90
+ return password
91
+ end
92
+
93
+ def ask_for_password
94
+ echo_off
95
+ password = ask
96
+ puts
97
+ echo_on
98
+ return password
99
+ end
100
+
101
+ def save_credentials
102
+ begin
103
+ write_credentials
104
+ command = 'auth:check'
105
+ Strongspace::Command.run_internal(command, args)
106
+ rescue RestClient::Unauthorized => e
107
+ delete_credentials
108
+ raise e unless retry_login?
109
+
110
+ display "\nAuthentication failed"
111
+ @credentials = ask_for_credentials
112
+ @client = init_strongspace
113
+ retry
114
+ rescue Exception => e
115
+ delete_credentials
116
+ raise e
117
+ end
118
+ end
119
+
120
+ def retry_login?
121
+ @login_attempts ||= 0
122
+ @login_attempts += 1
123
+ @login_attempts < 3
124
+ end
125
+
126
+ def write_credentials
127
+ FileUtils.mkdir_p(File.dirname(credentials_file))
128
+ File.open(credentials_file, 'w') do |f|
129
+ f.puts self.credentials
130
+ end
131
+ set_credentials_permissions
132
+ end
133
+
134
+ def set_credentials_permissions
135
+ FileUtils.chmod 0700, File.dirname(credentials_file)
136
+ FileUtils.chmod 0600, credentials_file
137
+ end
138
+
139
+ def delete_credentials
140
+ FileUtils.rm_f(credentials_file)
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,16 @@
1
+ module Strongspace::Command
2
+ class Base
3
+ include Strongspace::Helpers
4
+ attr_accessor :args
5
+ def initialize(args, strongspace=nil)
6
+ @args = args
7
+ @strongspace = strongspace
8
+ end
9
+
10
+ def strongspace
11
+ @strongspace ||= Strongspace::Command.run_internal('auth:client', args)
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,84 @@
1
+ module Strongspace::Command
2
+ class Help < Base
3
+ class HelpGroup < Array
4
+ attr_reader :title
5
+
6
+ def initialize(title)
7
+ @title = title
8
+ end
9
+
10
+ def command(name, description)
11
+ self << [name, description]
12
+ end
13
+
14
+ def space
15
+ self << ['', '']
16
+ end
17
+ end
18
+
19
+ def self.groups
20
+ @groups ||= []
21
+ end
22
+
23
+ def self.group(title, &block)
24
+ groups << begin
25
+ group = HelpGroup.new(title)
26
+ yield group
27
+ group
28
+ end
29
+ end
30
+
31
+ def self.create_default_groups!
32
+ group 'General Commands' do |group|
33
+ group.command 'help', 'show this usage'
34
+ group.command 'version', 'show the gem version'
35
+ group.space
36
+ group.space
37
+ group.command 'keys', 'show your user\'s public keys'
38
+ group.command 'keys:add [<path to keyfile>]', 'add a public key'
39
+ group.command 'keys:remove <id> ', 'remove a key by id'
40
+ group.command 'keys:clear', 'remove all keys'
41
+ group.space
42
+ group.space
43
+ group.command 'spaces', 'show your user\'s spaces'
44
+ group.command 'spaces:create <space_name> [type]', 'add a new space. type => (normal,public,backup)'
45
+ group.command 'spaces:destroy <space_name> [type]', 'remove a space by and destroy its data'
46
+ group.command 'spaces:snapshots <space_name>', 'show a space\'s snapshots'
47
+ group.command 'spaces:create_snapshot <space_name@snapshot_name>', 'take a space of a space.'
48
+ group.command 'spaces:destroy_snapshot <space_name@snapshot_name>', 'remove a snapshot from a space'
49
+ end
50
+ end
51
+
52
+ def index
53
+ display usage
54
+ end
55
+
56
+ def version
57
+ display Strongspace::Client.version
58
+ end
59
+
60
+ def usage
61
+ longest_command_length = self.class.groups.map do |group|
62
+ group.map { |g| g.first.length }
63
+ end.flatten.max
64
+
65
+ self.class.groups.inject(StringIO.new) do |output, group|
66
+ output.puts "=== %s" % group.title
67
+ output.puts
68
+
69
+ group.each do |command, description|
70
+ if command.empty?
71
+ output.puts
72
+ else
73
+ output.puts "%-*s # %s" % [longest_command_length, command, description]
74
+ end
75
+ end
76
+
77
+ output.puts
78
+ output
79
+ end.string
80
+ end
81
+ end
82
+ end
83
+
84
+ Strongspace::Command::Help.create_default_groups!
@@ -0,0 +1,50 @@
1
+ module Strongspace::Command
2
+ class Keys < Base
3
+ def list
4
+ long = args.any? { |a| a == '--long' }
5
+ keys = strongspace.keys["ssh_keys"]
6
+ if keys.empty?
7
+ display "No keys for #{strongspace.username}"
8
+ else
9
+ display "=== #{keys.size} key#{'s' if keys.size > 1} for #{strongspace.username}"
10
+ keys.each do |key|
11
+ display long ? key["key"].strip : format_key_for_display(key["key"]) + " key-id: #{key["id"]}"
12
+ end
13
+ end
14
+ end
15
+ alias :index :list
16
+
17
+ def add
18
+ keyfile = args.first || find_key
19
+ key = File.read(keyfile)
20
+
21
+ display "Uploading ssh public key #{keyfile}"
22
+
23
+ strongspace.add_key(key)
24
+ end
25
+
26
+ def remove
27
+ strongspace.remove_key(args.first)
28
+ display "Key #{args.first} removed."
29
+ end
30
+
31
+ def clear
32
+ strongspace.remove_all_keys
33
+ display "All keys removed."
34
+ end
35
+
36
+ protected
37
+ def find_key
38
+ %w(rsa dsa).each do |key_type|
39
+ keyfile = "#{home_directory}/.ssh/id_#{key_type}.pub"
40
+ return keyfile if File.exists? keyfile
41
+ end
42
+ raise CommandFailed, "No ssh public key found in #{home_directory}/.ssh/id_[rd]sa.pub. You may want to specify the full path to the keyfile."
43
+ end
44
+
45
+ def format_key_for_display(key)
46
+ type, hex, local = key.strip.split(/\s/)
47
+ [type, hex[0,10] + '...' + hex[-10,10], local].join(' ')
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,65 @@
1
+ module Strongspace::Command
2
+ class Spaces < Base
3
+ def list
4
+ long = args.any? { |a| a == '--long' }
5
+ spaces = strongspace.spaces["spaces"]
6
+
7
+ if spaces.empty?
8
+ display "#{strongspace.username} has no spaces"
9
+ else
10
+ display "=== #{strongspace.username} has #{spaces.size} space#{'s' if spaces.size > 1}"
11
+ spaces.each do |space|
12
+ space = space["space"]
13
+ display "#{space['name']} [type: #{space['type']}, snapshots: #{space['snapshots']}]"
14
+ end
15
+ end
16
+ end
17
+ alias :index :list
18
+
19
+ def create
20
+ name = args[0]
21
+ type = args[1]
22
+
23
+ strongspace.create_space(name, type)
24
+ display "Create space #{name}"
25
+ end
26
+
27
+ def destroy
28
+ strongspace.destroy_space(args.first)
29
+ display "Space #{args.first} removed."
30
+ end
31
+
32
+ def snapshots
33
+ if args.length == 0
34
+ display "No space specified."
35
+ return
36
+ end
37
+ snapshots = strongspace.snapshots(args.first)["snapshots"]
38
+
39
+ if snapshots.empty?
40
+ display "Space #{args.first} has no snapshots"
41
+ else
42
+ display "=== Space #{args.first} has #{snapshots.size} snapshot#{'s' if snapshots.size > 1}"
43
+ snapshots.each do |snapshot|
44
+ snapshot = snapshot["snapshot"]
45
+ display "#{args.first}@#{snapshot['name']} [created: #{snapshot['created_at']}]"
46
+ end
47
+ end
48
+ end
49
+
50
+ def create_snapshot
51
+ space_name, snapshot_name = args[0].split("@")
52
+
53
+ strongspace.create_snapshot(space_name, snapshot_name)
54
+ display "Created snapshot '#{args[0]}'"
55
+ end
56
+
57
+ def destroy_snapshot
58
+ space_name, snapshot_name = args[0].split("@")
59
+
60
+ strongspace.destroy_snapshot(space_name, snapshot_name)
61
+ display "Destroyed snapshot '#{args.first}'"
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,7 @@
1
+ module Strongspace::Command
2
+ class Version < Base
3
+ def index
4
+ display Strongspace::Client.gem_version_string
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,86 @@
1
+ module Strongspace
2
+ module Helpers
3
+ def home_directory
4
+ running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
5
+ end
6
+
7
+ def running_on_windows?
8
+ RUBY_PLATFORM =~ /mswin32|mingw32/
9
+ end
10
+
11
+ def running_on_a_mac?
12
+ RUBY_PLATFORM =~ /-darwin\d/
13
+ end
14
+
15
+ def display(msg, newline=true)
16
+ if newline
17
+ puts(msg)
18
+ else
19
+ print(msg)
20
+ STDOUT.flush
21
+ end
22
+ end
23
+
24
+ def redisplay(line, line_break = false)
25
+ display("\r\e[0K#{line}", line_break)
26
+ end
27
+
28
+ def error(msg)
29
+ STDERR.puts(msg)
30
+ exit 1
31
+ end
32
+
33
+ def confirm(message="Are you sure you wish to continue? (y/n)?")
34
+ display("#{message} ", false)
35
+ ask.downcase == 'y'
36
+ end
37
+
38
+ def confirm_command(app = app)
39
+ if extract_option('--force')
40
+ display("Warning: The --force switch is deprecated, and will be removed in a future release. Use --confirm #{app} instead.")
41
+ return true
42
+ end
43
+
44
+ raise(Strongspace::Command::CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") unless app
45
+
46
+ confirmed_app = extract_option('--confirm', false)
47
+ if confirmed_app
48
+ unless confirmed_app == app
49
+ raise(Strongspace::Command::CommandFailed, "Confirmed app #{confirmed_app} did not match the selected app #{app}.")
50
+ end
51
+ return true
52
+ else
53
+ display "\n ! Potentially Destructive Action"
54
+ display " ! To proceed, type \"#{app}\" or re-run this command with --confirm #{@app}"
55
+ display "> ", false
56
+ if ask.downcase != app
57
+ display " ! Input did not match #{app}. Aborted."
58
+ false
59
+ else
60
+ true
61
+ end
62
+ end
63
+ end
64
+
65
+ def format_date(date)
66
+ date = Time.parse(date) if date.is_a?(String)
67
+ date.strftime("%Y-%m-%d %H:%M %Z")
68
+ end
69
+
70
+ def ask
71
+ gets.strip
72
+ end
73
+
74
+ def shell(cmd)
75
+ FileUtils.cd(Dir.pwd) {|d| return `#{cmd}`}
76
+ end
77
+ end
78
+ end
79
+
80
+ unless String.method_defined?(:shellescape)
81
+ class String
82
+ def shellescape
83
+ empty? ? "''" : gsub(/([^A-Za-z0-9_\-.,:\/@\n])/n, '\\\\\\1').gsub(/\n/, "'\n'")
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+ module Strongspace
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,3 @@
1
+ module Strongspace; end
2
+
3
+ require 'strongspace/client'
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: strongspace
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Strongspace
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-06 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 27
44
+ segments:
45
+ - 1
46
+ - 3
47
+ - 0
48
+ version: 1.3.0
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: taps
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 5
60
+ segments:
61
+ - 0
62
+ - 3
63
+ - 11
64
+ version: 0.3.11
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: webmock
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 1
78
+ - 5
79
+ - 0
80
+ version: 1.5.0
81
+ type: :development
82
+ version_requirements: *id004
83
+ - !ruby/object:Gem::Dependency
84
+ name: rest-client
85
+ prerelease: false
86
+ requirement: &id005 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - <
90
+ - !ruby/object:Gem::Version
91
+ hash: 11
92
+ segments:
93
+ - 1
94
+ - 7
95
+ - 0
96
+ version: 1.7.0
97
+ type: :runtime
98
+ version_requirements: *id005
99
+ - !ruby/object:Gem::Dependency
100
+ name: json_pure
101
+ prerelease: false
102
+ requirement: &id006 !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - <
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 1
110
+ - 5
111
+ - 0
112
+ version: 1.5.0
113
+ type: :runtime
114
+ version_requirements: *id006
115
+ description: Client library and command line tool for Strongspace.
116
+ email: support@strongspace.com
117
+ executables:
118
+ - strongspace
119
+ extensions: []
120
+
121
+ extra_rdoc_files: []
122
+
123
+ files:
124
+ - bin/strongspace
125
+ - lib/strongspace/client.rb
126
+ - lib/strongspace/command.rb
127
+ - lib/strongspace/commands/auth.rb
128
+ - lib/strongspace/commands/base.rb
129
+ - lib/strongspace/commands/help.rb
130
+ - lib/strongspace/commands/keys.rb
131
+ - lib/strongspace/commands/spaces.rb
132
+ - lib/strongspace/commands/version.rb
133
+ - lib/strongspace/helpers.rb
134
+ - lib/strongspace/version.rb
135
+ - lib/strongspace.rb
136
+ has_rdoc: true
137
+ homepage: http://github.com/expandrive/strongspace
138
+ licenses: []
139
+
140
+ post_install_message:
141
+ rdoc_options: []
142
+
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ hash: 3
151
+ segments:
152
+ - 0
153
+ version: "0"
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ hash: 3
160
+ segments:
161
+ - 0
162
+ version: "0"
163
+ requirements: []
164
+
165
+ rubyforge_project:
166
+ rubygems_version: 1.3.7
167
+ signing_key:
168
+ specification_version: 3
169
+ summary: Client library and CLI for Strongspace.
170
+ test_files: []
171
+