strongspace 0.1.1 → 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.
@@ -0,0 +1,19 @@
1
+ #!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
2
+ #
3
+ # This file was generated by RubyGems.
4
+ #
5
+ # The application 'strongspace' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'rubygems'
10
+
11
+ version = ">= 0"
12
+
13
+ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
14
+ version = $1
15
+ ARGV.shift
16
+ end
17
+
18
+ gem 'strongspace', version
19
+ load 'strongspace'
@@ -0,0 +1,20 @@
1
+ #!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
2
+ #
3
+ # This file was generated by RubyGems.
4
+ #
5
+ # The application 'strongspace' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'rubygems'
10
+
11
+ version = ">= 0"
12
+
13
+ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
14
+ version = $1
15
+ ARGV.shift
16
+ end
17
+
18
+ gem 'strongspace', version
19
+ load 'strongspace'
20
+
data/lib/strongspace.rb CHANGED
@@ -1,3 +1,24 @@
1
1
  module Strongspace; end
2
2
 
3
- require 'strongspace/client'
3
+ require 'rest_client'
4
+ require 'uri'
5
+ require 'json/pure' unless {}.respond_to?(:to_json)
6
+ require 'fileutils'
7
+
8
+ STRONGSPACE_LIB_PATH = File.dirname(__FILE__) + "/strongspace/"
9
+
10
+ [
11
+ "version",
12
+ "exceptions",
13
+ "client",
14
+ "helpers",
15
+ "plugin_interface",
16
+ "plugin",
17
+ "command",
18
+ "commands/base"
19
+ ].each do |library|
20
+ require STRONGSPACE_LIB_PATH + library
21
+ end
22
+
23
+
24
+ Dir["#{STRONGSPACE_LIB_PATH}/commands/*.rb"].each { |c| require c }
@@ -1,8 +1,3 @@
1
- require 'rest_client'
2
- require 'uri'
3
- require 'strongspace/version'
4
- require 'json/pure' unless {}.respond_to?(:to_json)
5
-
6
1
  # A Ruby class to call the Strongspace REST API. You might use this if you want to
7
2
  # manage your Strongspace apps from within a Ruby program, such as Capistrano.
8
3
  #
@@ -23,14 +18,26 @@ class Strongspace::Client
23
18
  attr_accessor :host, :user, :password
24
19
 
25
20
  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
21
+ begin
22
+ client = new(user, password, host)
23
+ return JSON.parse client.get('/api/v1/api_token', :username => user, :password => password).to_s
24
+ rescue RestClient::Request::Unauthorized => e
25
+ raise Strongspace::Exceptions::InvalidCredentials
26
+ rescue SocketError => e
27
+ raise Strongspace::Exceptions::NoConnection
28
+ end
28
29
  end
29
30
 
30
31
  def username
32
+ return nil if !user
33
+
31
34
  self.user.split("/")[0]
32
35
  end
33
36
 
37
+ def login_token
38
+ doc = JSON.parse get('/api/v1/login_token')
39
+ end
40
+
34
41
  def initialize(user, password, host='https://www.strongspace.com')
35
42
  @user = user
36
43
  @password = password
@@ -73,6 +80,7 @@ class Strongspace::Client
73
80
 
74
81
  def snapshots(space_name)
75
82
  doc = JSON.parse get("/api/v1/spaces/#{escape(space_name)}/snapshots").to_s
83
+ doc["snapshots"]
76
84
  end
77
85
 
78
86
  def delete_snapshot(space_name, snapshot_name)
@@ -87,6 +95,7 @@ class Strongspace::Client
87
95
  # Get the list of ssh public keys for the current user.
88
96
  def keys
89
97
  doc = JSON.parse get('/api/v1/ssh_keys')
98
+ doc["ssh_keys"]
90
99
  end
91
100
 
92
101
  # Add an ssh public key to the current user.
@@ -1,9 +1,3 @@
1
- require 'strongspace/helpers'
2
- require 'strongspace/plugin'
3
- require 'strongspace/commands/base'
4
-
5
- Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
6
-
7
1
  module Strongspace
8
2
  module Command
9
3
  class InvalidCommand < RuntimeError; end
@@ -21,7 +15,7 @@ module Strongspace
21
15
  raise InvalidCommand
22
16
  end
23
17
 
24
- run_internal 'auth:reauthorize', args.dup if retries > 0
18
+ run_internal 'auth:reauthorize_interactve', args.dup if retries > 0
25
19
  run_internal(command, args.dup)
26
20
  rescue InvalidCommand
27
21
  error "Unknown command. Run 'strongspace help' for usage information."
@@ -30,7 +24,7 @@ module Strongspace
30
24
  STDERR.puts "Authentication failure"
31
25
  run(command, args, retries+1)
32
26
  else
33
- error "Authentication failure"
27
+ error "! Authentication failure"
34
28
  end
35
29
  rescue RestClient::ResourceNotFound => e
36
30
  error extract_not_found(e.http_body)
@@ -46,6 +40,9 @@ module Strongspace
46
40
  end
47
41
 
48
42
  def run_internal(command, args, strongspace=nil)
43
+ if command == "web:start"
44
+ require 'strongspace-web'
45
+ end
49
46
  klass, method = parse(command)
50
47
  runner = klass.new(args, strongspace)
51
48
  raise InvalidCommand unless runner.respond_to?(method)
@@ -90,3 +87,4 @@ module Strongspace
90
87
  end
91
88
  end
92
89
  end
90
+
@@ -20,11 +20,34 @@ module Strongspace::Command
20
20
  ENV['STRONGSPACE_HOST'] || 'https://www.strongspace.com'
21
21
  end
22
22
 
23
- def reauthorize
23
+ def reauthorize_interactve
24
24
  @credentials = ask_for_credentials
25
25
  write_credentials
26
26
  end
27
27
 
28
+ def authorize!
29
+ @credentials = [args.first, args[1]]
30
+ r = Strongspace::Client.auth(@credentials[0], @credentials[1])
31
+ if r
32
+ @credentials[0] = "#{@credentials[0]}/token"
33
+ @credentials[1] = r['api_token']
34
+ write_credentials
35
+ return true
36
+ end
37
+
38
+ return false
39
+ end
40
+
41
+ def authenticated_login
42
+ if args.blank?
43
+ url = "#{host}/login/#{client.login_token['login_token']}"
44
+ else
45
+ to = URI.escape(args[0][0..1]) + URI.escape(URI.escape(args[0][2..-1])).gsub('&', '%26')
46
+ url = "#{host}/login/#{client.login_token['login_token']}?to=#{to}"
47
+ end
48
+ `open "#{url}"`
49
+ end
50
+
28
51
  def user # :nodoc:
29
52
  get_credentials
30
53
  @credentials[0]
@@ -36,7 +59,7 @@ module Strongspace::Command
36
59
  end
37
60
 
38
61
  def credentials_file
39
- "#{home_directory}/.strongspace/credentials"
62
+ "#{credentials_folder}/credentials"
40
63
  end
41
64
 
42
65
  def get_credentials # :nodoc:
@@ -72,6 +95,15 @@ module Strongspace::Command
72
95
  ["#{user}/token", Strongspace::Client.auth(user, password, host)['api_token']]
73
96
  end
74
97
 
98
+ def valid_saved_credentials?
99
+ if File.exists?(credentials_file)
100
+ credentials = read_credentials
101
+ r = Strongspace::Client.auth(credentials[0], credentials[1])
102
+ return !r.blank?
103
+ end
104
+ return false
105
+ end
106
+
75
107
  def ask_for_password_on_windows
76
108
  require "Win32API"
77
109
  char = nil
@@ -124,7 +156,12 @@ module Strongspace::Command
124
156
  end
125
157
 
126
158
  def write_credentials
127
- FileUtils.mkdir_p(File.dirname(credentials_file))
159
+ begin
160
+ FileUtils.mkdir_p(credentials_folder)
161
+ rescue Errno::EEXIST => e
162
+
163
+ end
164
+
128
165
  File.open(credentials_file, 'w') do |f|
129
166
  f.puts self.credentials
130
167
  end
@@ -1,7 +1,3 @@
1
- require 'fileutils'
2
- require 'strongspace/plugin_interface'
3
-
4
-
5
1
  module Strongspace::Command
6
2
  class Base
7
3
  include Strongspace::Helpers
@@ -58,8 +58,8 @@ module Strongspace::Command
58
58
  group.command 'spaces:create <name> [type]', 'add a new space. type => (normal,public,backup)'
59
59
  group.command 'spaces:delete <name> [type]', 'remove a space by and destroy its data'
60
60
  group.command 'spaces:snapshots <name>', 'show a space\'s snapshots'
61
- group.command 'spaces:create_snapshot <name@snapshot_name>', 'take a space of a space.'
62
- group.command 'spaces:delete_snapshot <name@snapshot_name>', 'remove a snapshot from a space'
61
+ group.command 'spaces:create_snapshot <name> [snapshot_name]', 'take a space of a space - snapshot_name defaults to current date/time.'
62
+ group.command 'spaces:delete_snapshot <name> <snapshot_name>', 'remove a snapshot from a space'
63
63
  end
64
64
 
65
65
  group 'Plugins' do |group|
@@ -2,7 +2,7 @@ module Strongspace::Command
2
2
  class Keys < Base
3
3
  def list
4
4
  long = args.any? { |a| a == '--long' }
5
- keys = strongspace.keys["ssh_keys"]
5
+ keys = strongspace.keys
6
6
  if keys.empty?
7
7
  display "No keys for #{strongspace.username}"
8
8
  else
@@ -11,6 +11,7 @@ module Strongspace::Command
11
11
  display long ? key["key"].strip : format_key_for_display(key["key"]) + " key-id: #{key["id"]}"
12
12
  end
13
13
  end
14
+ keys
14
15
  end
15
16
  alias :index :list
16
17
 
@@ -19,6 +20,25 @@ module Strongspace::Command
19
20
  return ($? == 0)
20
21
  end
21
22
 
23
+ def generate_for_gui
24
+ return unless running_on_a_mac?
25
+ FileUtils.mkdir "#{credentials_folder}" unless File.exist? "#{credentials_folder}"
26
+
27
+
28
+ File.open("#{credentials_folder}/known_hosts", "w") do |f|
29
+ f.write "*.strongspace.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXBYAoHZWVzLfHNMlgteAbq20AaCVcE1qALqVjYZerIpa3rBjNlv2i/2O8ul3OmSfcQwQGPTnABLqz9cozAbxF01eDfqUiSABUDT6m1/lY1a0V7RGS46Y/KJMVbOb4mVpxDZOVwBQh/DYTu7R55vFc93lXpE+tZboqnuq+LvJIZDqzoGTHIUprRs3sNY8Xegnz+m68P+tV6iLkXMRk8Gh8/IIavN4mXYhWPVbCv6Gqo2XhiYVMrCqLZFKLG0W6uwWY/xOhUjWxKDZMlqhyU/YUsMB5BZc9/x0t+Sc82OL+Eh3IB5EUmmCWnhm/LKxjMIn2UNe48BQqwaU/gozVtVPQ==\n"
30
+ end
31
+
32
+
33
+ if !File.exist? "#{credentials_folder}/#{hostname}.rsa"
34
+ `/usr/bin/ssh-keygen -f #{credentials_folder}/#{hostname}.rsa -b 2048 -C \" Strongspace App - #{hostname}\" -q -N ""` unless File.exist? "#{credentials_folder}/#{hostname}.rsa"
35
+ args[0] = "#{credentials_folder}/#{hostname}.rsa.pub"
36
+ begin
37
+ add
38
+ rescue RestClient::Conflict => e # Swallow errors if the key already exists on Strongspace
39
+ end
40
+ end
41
+ end
22
42
 
23
43
  def add
24
44
  keyfile = args.first || find_key
@@ -39,6 +59,19 @@ module Strongspace::Command
39
59
  display "All keys removed."
40
60
  end
41
61
 
62
+ def valid_key_gui?
63
+ return unless running_on_a_mac? and File.exist? "#{support_directory}/ssh"
64
+
65
+ ret = `ssh -o PreferredAuthentications=publickey -i "#{support_directory}/ssh/#{hostname}.rsa" #{strongspace.username}@#{strongspace.username}.strongspace.com 2>&1`
66
+
67
+ if ret.include? "Strongspace"
68
+ display "Valid key installed"
69
+ return true
70
+ end
71
+ display "No valid key installed"
72
+ return false
73
+ end
74
+
42
75
  protected
43
76
  def find_key
44
77
  %w(rsa dsa).each do |key_type|
@@ -2,14 +2,13 @@ module Strongspace::Command
2
2
  class Spaces < Base
3
3
  def list
4
4
  long = args.any? { |a| a == '--long' }
5
- spaces = strongspace.spaces["spaces"]
5
+ spaces = strongspace.spaces
6
6
 
7
7
  if spaces.empty?
8
8
  display "#{strongspace.username} has no spaces"
9
9
  else
10
10
  display "=== #{strongspace.username} has #{spaces.size} space#{'s' if spaces.size > 1}"
11
11
  spaces.each do |space|
12
- space = space["space"]
13
12
  display "#{space['name']} [type: #{space['type']}, snapshots: #{space['snapshots']}]"
14
13
  end
15
14
  end
@@ -34,31 +33,171 @@ module Strongspace::Command
34
33
  display "No space specified."
35
34
  return
36
35
  end
37
- snapshots = strongspace.snapshots(args.first)["snapshots"]
36
+ snapshots = strongspace.snapshots(args.first)
38
37
 
39
38
  if snapshots.empty?
40
39
  display "Space #{args.first} has no snapshots"
41
40
  else
42
41
  display "=== Space #{args.first} has #{snapshots.size} snapshot#{'s' if snapshots.size > 1}"
43
42
  snapshots.each do |snapshot|
44
- snapshot = snapshot["snapshot"]
45
43
  display "#{args.first}@#{snapshot['name']} [created: #{snapshot['created_at']}]"
46
44
  end
47
45
  end
48
46
  end
49
47
 
50
48
  def create_snapshot
51
- space_name, snapshot_name = args[0].split("@")
49
+ space_name, snapshot_name = args[0..1]
50
+
51
+ if snapshot_name.blank?
52
+ snapshot_name = Time.now.strftime("%Y-%m-%d-%H%M%S")
53
+ end
52
54
 
53
55
  strongspace.create_snapshot(space_name, snapshot_name)
54
- display "Created snapshot '#{args[0]}'"
56
+ display "Created snapshot '#{space_name}@#{snapshot_name}'"
57
+ end
58
+
59
+ def create_snapshot_and_thin
60
+
61
+ retries = 0
62
+ success = false
63
+ while (!success and (retries < 5)) do
64
+ begin
65
+ create_snapshot
66
+ thin_snapshots
67
+ rescue SocketError => e
68
+ sleep(10)
69
+ retries = retries + 1
70
+ next
71
+ end
72
+ success = true
73
+ end
74
+
55
75
  end
56
76
 
57
77
  def delete_snapshot
58
- space_name, snapshot_name = args[0].split("@")
78
+ space_name, snapshot_name = args[0..1]
59
79
 
60
80
  strongspace.delete_snapshot(space_name, snapshot_name)
61
- display "Destroyed snapshot '#{args.first}'"
81
+ display "Destroyed snapshot '#{space_name}@#{snapshot_name}'"
82
+ end
83
+
84
+ def thin_snapshots
85
+ snapshots = strongspace.snapshots(args.first)
86
+
87
+ keeplist = []
88
+
89
+ if snapshots.count < 24
90
+ return
91
+ end
92
+
93
+ snapshots.each do |s|
94
+
95
+ if Time.parse(s['created_at']) > (Time.now - 3600*24)
96
+ keeplist << s
97
+ next
98
+ end
99
+
100
+ end
101
+
102
+ (snapshots - keeplist).each do |k|
103
+ puts "Drop: " + k['name']
104
+ strongspace.delete_snapshot(args.first, k['name'])
105
+ end
106
+
107
+ end
108
+
109
+
110
+ def schedule_snapshots
111
+ space_name = args[0]
112
+
113
+ if running_on_a_mac?
114
+ plist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
115
+ <!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
116
+ http://www.apple.com/DTDs/PropertyList-1.0.dtd >
117
+ <plist version=\"1.0\">
118
+ <dict>
119
+ <key>Label</key>
120
+ <string>com.strongspace.Snapshots.#{space_name}</string>
121
+ <key>Program</key>
122
+ <string>#{support_directory}/gems/bin/strongspace</string>
123
+ <key>ProgramArguments</key>
124
+ <array>
125
+ <string>strongspace</string>
126
+ <string>spaces:create_snapshot_and_thin</string>
127
+ <string>#{space_name}</string>
128
+ </array>
129
+ <key>KeepAlive</key>
130
+ <false/>
131
+ <key>StartCalendarInterval</key>
132
+ <dict>
133
+ <key>Minute</key>
134
+ <integer>0</integer>
135
+ </dict>
136
+ <key>RunAtLoad</key>
137
+ <true/>
138
+ <key>StandardOutPath</key>
139
+ <string>#{log_file}</string>
140
+ <key>StandardErrorPath</key>
141
+ <string>#{log_file}</string>
142
+ <key>EnvironmentVariables</key>
143
+ <dict>
144
+ <key>GEM_PATH</key>
145
+ <string>#{support_directory}/gems</string>
146
+ <key>GEM_HOME</key>
147
+ <string>#{support_directory}/gems</string>
148
+ <key>RACK_ENV</key>
149
+ <string>production</string>
150
+ </dict>
151
+
152
+ </dict>
153
+ </plist>"
154
+
155
+ file = File.new(launchd_plist_file(space_name), "w+")
156
+ file.puts plist
157
+ file.close
158
+
159
+ r = `launchctl load -S aqua '#{launchd_plist_file(space_name)}'`
160
+ if r.strip.ends_with?("Already loaded")
161
+ error "This task is aready scheduled, unload before scheduling again"
162
+ return
163
+ end
164
+ display "Scheduled Snapshots of #{space_name}"
165
+ end
166
+ end
167
+
168
+ def unschedule_snapshots
169
+ space_name = args[0]
170
+
171
+ if space_name.blank?
172
+ display "Please supply the name of a space"
173
+ return false
174
+ end
175
+
176
+ if running_on_windows?
177
+ error "Scheduling currently isn't supported on Windows"
178
+ return
179
+ end
180
+
181
+ if running_on_a_mac?
182
+ if File.exist? launchd_plist_file(space_name)
183
+ `launchctl unload '#{launchd_plist_file(space_name)}'`
184
+ FileUtils.rm(launchd_plist_file(space_name))
185
+ end
186
+ else # Assume we're running on linux/unix
187
+ CronEdit::Crontab.Remove "strongspace-snapshots-#{space_name}"
188
+ end
189
+
190
+ display "Unscheduled snapshotting of #{space_name}"
191
+ end
192
+
193
+
194
+ private
195
+ def launchd_plist_file(space_name)
196
+ "#{launchd_agents_folder}/com.strongspace.Snapshots.#{space_name}.plist"
197
+ end
198
+
199
+ def log_file
200
+ "#{logs_folder}/Strongspace.log"
62
201
  end
63
202
 
64
203
  end
@@ -0,0 +1,17 @@
1
+ module Strongspace
2
+ module Exceptions
3
+ class StrongspaceError < StandardError; end
4
+
5
+ class InvalidCredentials < StrongspaceError
6
+ def message
7
+ "Invalid Strongspace Credentials"
8
+ end
9
+ end
10
+
11
+ class NoConnection < StrongspaceError
12
+ def message
13
+ "Could not connect to Strongspace"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -5,32 +5,77 @@ module Strongspace
5
5
  self.class.name.split("::").last
6
6
  end
7
7
 
8
- def home_directory
8
+ def self.home_directory
9
9
  running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
10
10
  end
11
11
 
12
+ def home_directory
13
+ return Strongspace::Helpers.home_directory
14
+ end
15
+
16
+ def self.support_directory
17
+ running_on_windows? ? "#{home_directory}/Strongspace" : "#{home_directory}/Library/Strongspace"
18
+ end
19
+
20
+ def support_directory
21
+ return Strongspace::Helpers.support_directory
22
+ end
23
+
24
+ def self.running_on_windows?
25
+ RUBY_PLATFORM =~ /mswin32|mingw32/
26
+ end
27
+
12
28
  def running_on_windows?
13
29
  RUBY_PLATFORM =~ /mswin32|mingw32/
14
30
  end
15
31
 
32
+ def self.running_on_a_mac?
33
+ RUBY_PLATFORM =~ /-darwin\d/
34
+ end
35
+
16
36
  def running_on_a_mac?
17
37
  RUBY_PLATFORM =~ /-darwin\d/
18
38
  end
19
39
 
40
+ def gui_ssh_key
41
+ "#{credentials_folder}/#{hostname}.rsa"
42
+ end
43
+
44
+ def hostname
45
+ @hostname ||= `hostname`.strip
46
+
47
+ if @hostname.include?(".local")
48
+ @hostname = @hostname.split(".")[0]
49
+ end
50
+ return @hostname
51
+ end
52
+
53
+ def credentials_folder
54
+ "#{support_directory}/credentials"
55
+ end
56
+
20
57
  def pids_folder
21
- "#{home_directory}/.strongspace/pids"
58
+ "#{support_directory}/pids"
22
59
  end
23
60
 
24
61
  def plugins_folder
25
62
  Strongspace::Plugin.directory
26
63
  end
27
64
 
65
+ def logs_folder
66
+ if running_on_a_mac?
67
+ "#{home_directory}/Library/Logs/Strongspace"
68
+ else
69
+ "#{support_directory}/logs"
70
+ end
71
+ end
72
+
28
73
  def bin_folder
29
- "#{home_directory}/.strongspace/bin"
74
+ "#{support_directory}/bin"
30
75
  end
31
76
 
32
77
  def launchd_agents_folder
33
- "#{home_directory}/Library/LaunchAgents"
78
+ "#{support_directory}/LaunchAgents"
34
79
  end
35
80
 
36
81
  def pid_file_path(name)
@@ -58,7 +103,8 @@ module Strongspace
58
103
  end
59
104
 
60
105
  begin
61
- # This process is running
106
+ # This process is running, Kill 0 is a no-op that only works
107
+ # if the process exists
62
108
  Process.kill(0, existing_pid)
63
109
  return true
64
110
  rescue Errno::EPERM
@@ -165,24 +211,17 @@ module Strongspace
165
211
  end
166
212
 
167
213
  def space_exist?(name)
168
- strongspace.spaces["spaces"].each do |space|
214
+ strongspace.spaces.each do |space|
169
215
  # TODO: clean up the json returned by the strongspace API requests to simplify this iteration
170
- space = space["space"]
171
216
  return true if space["name"] == name
172
217
  end
173
218
  return false
174
219
  end
175
220
 
176
- def valid_space_name?(name)
177
- # For now, just make sure the space name is all "word characters," i.e. [0-9A-Za-z_]
178
- return false if name =~ /\W/
179
- return true
180
- end
181
221
 
182
222
  def backup_space?(name)
183
223
  space = nil
184
- strongspace.spaces["spaces"].each do |s|
185
- s = s["space"]
224
+ strongspace.spaces.each do |s|
186
225
  if s["name"] == name then
187
226
  space = s
188
227
  break
@@ -1,5 +1,3 @@
1
- # based on the Rails Plugin
2
-
3
1
  module Strongspace
4
2
  class Plugin
5
3
  class << self
@@ -9,7 +7,7 @@ module Strongspace
9
7
  attr_reader :name, :uri
10
8
 
11
9
  def self.directory
12
- File.expand_path("#{home_directory}/.strongspace/plugins")
10
+ File.expand_path("#{support_directory}/plugins")
13
11
  end
14
12
 
15
13
  def self.list
@@ -19,6 +17,7 @@ module Strongspace
19
17
  end
20
18
 
21
19
  def self.load!
20
+ self.update_support_directory!
22
21
  list.each do |plugin|
23
22
  begin
24
23
  load_plugin(plugin)
@@ -26,6 +25,14 @@ module Strongspace
26
25
  display "Unable to load plugin: #{plugin}: #{e.message}"
27
26
  end
28
27
  end
28
+ self.load_default_gem_plugins
29
+ end
30
+
31
+ def self.load_default_gem_plugins
32
+ begin
33
+ require 'strongspace-rsync'
34
+ rescue Exception => e
35
+ end
29
36
  end
30
37
 
31
38
  def self.load_plugin(plugin)
@@ -38,6 +45,15 @@ module Strongspace
38
45
  FileUtils.rm_rf("#{self.directory}/#{plugin}")
39
46
  end
40
47
 
48
+ def self.update_support_directory!
49
+ if running_on_a_mac?
50
+ # if File.exist?("#{home_directory}/.strongspace") and !File.exist?("#{support_directory}")
51
+ # FileUtils.mv("#{home_directory}/.strongspace", "#{support_directory}")
52
+ # end
53
+
54
+ FileUtils.mkdir_p(launchd_agents_folder) unless File.exist? launchd_agents_folder
55
+ end
56
+ end
41
57
 
42
58
  def initialize(uri)
43
59
  @uri = uri
@@ -1,3 +1,3 @@
1
1
  module Strongspace
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/spec/auth_spec.rb ADDED
@@ -0,0 +1,19 @@
1
+ require File.expand_path("./base", File.dirname(__FILE__))
2
+
3
+ require "strongspace"
4
+
5
+ def prepare_command(klass)
6
+ command = klass.new(['--app', 'myapp'])
7
+ command.stub!(:args).and_return([])
8
+ command.stub!(:display)
9
+ command
10
+ end
11
+
12
+
13
+
14
+ describe Strongspace::Command::Auth do
15
+
16
+
17
+
18
+
19
+ end
data/spec/base.rb CHANGED
@@ -8,9 +8,7 @@ require 'fileutils'
8
8
  require 'tmpdir'
9
9
  require 'webmock/rspec'
10
10
 
11
- require 'strongspace/command'
12
- require 'strongspace/commands/base'
13
- Dir["#{File.dirname(__FILE__)}/../lib/strongspace/commands/*"].each { |c| require c }
11
+ require 'strongspace'
14
12
 
15
13
  include WebMock::API
16
14
 
data/spec/client_spec.rb CHANGED
@@ -1,23 +1,42 @@
1
1
  require File.expand_path("./base", File.dirname(__FILE__))
2
2
  require "cgi"
3
- require "strongspace/client"
3
+ require "strongspace"
4
4
 
5
5
  describe Strongspace::Client do
6
6
  before do
7
7
  @client = Strongspace::Client.new(nil, nil)
8
8
  end
9
9
 
10
- it "Client.auth -> get user details" do
10
+ it "should return the current version" do
11
+ Strongspace::Client.version.should == Strongspace::VERSION
12
+ end
13
+
14
+ it "should return a gem version string" do
15
+ Strongspace::Client.gem_version_string.should == "strongspace-gem/#{Strongspace::VERSION}"
16
+ end
17
+
18
+ it "should return an API key hash for auth" do
11
19
  api_token = { "api_key" => "abc" }
12
20
  stub_request(:get, "https://foo:bar@www.strongspace.com/api/v1/api_token").to_return(:body => api_token.to_json)
13
21
  Strongspace::Client.auth("foo", "bar").should == api_token
14
22
  end
15
23
 
16
- it "list -> get a list of this user's apps" do
24
+ it "should fail auth gracefully with a bad password" do
25
+ api_token = { "api_key" => "abc" }
26
+ stub_request(:get, "https://foo:bar@www.strongspace.com/api/v1/api_token").to_return(:body => api_token.to_json)
27
+ lambda {Strongspace::Client.auth("foo", "ba3r")}.should raise_error(WebMock::NetConnectNotAllowedError)
28
+ end
29
+
30
+ it "should return nil for username and password" do
31
+ @client.username.should == nil
32
+ @client.password.should == nil
33
+ end
34
+
35
+ it "should return an array of spaces" do
17
36
  stub_api_request(:get, "/spaces").to_return(:body => <<-EOJSON)
18
- {"spaces":[{"space":{"name":"a space", "snapshots":0, "type":"normal"}}, {"space":{"name":"diskimages", "snapshots":0, "type":"normal"}}]}
37
+ {"spaces" : [{"space":{"name":"a space", "snapshots":0, "type":"normal"}}, {"space":{"name":"diskimages", "snapshots":0, "type":"normal"}}]}
19
38
  EOJSON
20
- @client.spaces.should == {"spaces"=>[{"space"=>{"name"=>"a space", "snapshots"=>0, "type"=>"normal"}}, {"space"=>{"name"=>"diskimages", "snapshots"=>0, "type"=>"normal"}}]}
39
+ @client.spaces.should == [{"space"=>{"name"=>"a space", "snapshots"=>0, "type"=>"normal"}}, {"space"=>{"name"=>"diskimages", "snapshots"=>0, "type"=>"normal"}}]
21
40
  end
22
41
 
23
42
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strongspace
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Strongspace
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-11 00:00:00 -05:00
18
+ date: 2011-01-30 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -91,39 +91,37 @@ dependencies:
91
91
  type: :development
92
92
  version_requirements: *id005
93
93
  - !ruby/object:Gem::Dependency
94
- name: taps
94
+ name: webmock
95
95
  prerelease: false
96
96
  requirement: &id006 !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
99
  - - ~>
100
100
  - !ruby/object:Gem::Version
101
- hash: 5
101
+ hash: 3
102
102
  segments:
103
+ - 1
104
+ - 5
103
105
  - 0
104
- - 3
105
- - 11
106
- version: 0.3.11
106
+ version: 1.5.0
107
107
  type: :development
108
108
  version_requirements: *id006
109
109
  - !ruby/object:Gem::Dependency
110
- name: webmock
110
+ name: ruby-fsevent
111
111
  prerelease: false
112
112
  requirement: &id007 !ruby/object:Gem::Requirement
113
113
  none: false
114
114
  requirements:
115
- - - ~>
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
117
  hash: 3
118
118
  segments:
119
- - 1
120
- - 5
121
119
  - 0
122
- version: 1.5.0
120
+ version: "0"
123
121
  type: :development
124
122
  version_requirements: *id007
125
123
  - !ruby/object:Gem::Dependency
126
- name: open4
124
+ name: sinatra
127
125
  prerelease: false
128
126
  requirement: &id008 !ruby/object:Gem::Requirement
129
127
  none: false
@@ -137,7 +135,7 @@ dependencies:
137
135
  type: :development
138
136
  version_requirements: *id008
139
137
  - !ruby/object:Gem::Dependency
140
- name: ruby-fsevent
138
+ name: sinatra-reloader
141
139
  prerelease: false
142
140
  requirement: &id009 !ruby/object:Gem::Requirement
143
141
  none: false
@@ -184,14 +182,14 @@ dependencies:
184
182
  requirement: &id012 !ruby/object:Gem::Requirement
185
183
  none: false
186
184
  requirements:
187
- - - <
185
+ - - "="
188
186
  - !ruby/object:Gem::Version
189
- hash: 11
187
+ hash: 13
190
188
  segments:
191
189
  - 1
192
- - 7
193
- - 0
194
- version: 1.7.0
190
+ - 6
191
+ - 1
192
+ version: 1.6.1
195
193
  type: :runtime
196
194
  version_requirements: *id012
197
195
  - !ruby/object:Gem::Dependency
@@ -220,6 +218,8 @@ extensions: []
220
218
  extra_rdoc_files: []
221
219
 
222
220
  files:
221
+ - bin/as_installed/ss
222
+ - bin/as_installed/strongspace
223
223
  - bin/ss
224
224
  - bin/strongspace
225
225
  - lib/strongspace/client.rb
@@ -232,12 +232,14 @@ files:
232
232
  - lib/strongspace/commands/plugins.rb
233
233
  - lib/strongspace/commands/spaces.rb
234
234
  - lib/strongspace/commands/version.rb
235
+ - lib/strongspace/exceptions.rb
235
236
  - lib/strongspace/helpers.rb
236
237
  - lib/strongspace/plugin.rb
237
238
  - lib/strongspace/plugin_interface.rb
238
239
  - lib/strongspace/version.rb
239
240
  - lib/strongspace.rb
240
241
  - README.markdown
242
+ - spec/auth_spec.rb
241
243
  - spec/base.rb
242
244
  - spec/client_spec.rb
243
245
  has_rdoc: true