shelly 0.0.40 → 0.0.41.pre

Sign up to get free protection for your applications and to get access to all the features.
data/lib/shelly/app.rb CHANGED
@@ -1,14 +1,13 @@
1
1
  require 'erb'
2
2
  require 'launchy'
3
+ require "shelly/backup"
3
4
 
4
5
  module Shelly
5
6
  class App < Model
6
7
  DATABASE_KINDS = %w(postgresql mongodb redis none)
7
8
 
8
9
  attr_accessor :code_name, :databases, :ruby_version, :environment,
9
- :git_url, :domains
10
-
11
- autoload :Backup, "shelly/backup"
10
+ :git_url, :domains, :web_server_ip, :mail_server_ip
12
11
 
13
12
  def initialize(code_name = nil)
14
13
  self.code_name = code_name
@@ -99,10 +98,6 @@ module Shelly
99
98
  File.basename(Dir.pwd)
100
99
  end
101
100
 
102
- def ips
103
- shelly.app_ips(code_name)
104
- end
105
-
106
101
  def users
107
102
  shelly.app_users(code_name)
108
103
  end
@@ -135,6 +130,18 @@ module Shelly
135
130
  shelly.app_delete_config(code_name, path)
136
131
  end
137
132
 
133
+ def attributes
134
+ @attributes ||= shelly.app(code_name)
135
+ end
136
+
137
+ def web_server_ip
138
+ attributes["web_server_ip"]
139
+ end
140
+
141
+ def mail_server_ip
142
+ attributes["mail_server_ip"]
143
+ end
144
+
138
145
  def open_billing_page
139
146
  url = "#{shelly.shellyapp_url}/login?api_key=#{current_user.token}&return_to=/apps/#{code_name}/edit_billing"
140
147
  Launchy.open(url)
@@ -143,5 +150,9 @@ module Shelly
143
150
  def self.inside_git_repository?
144
151
  system("git status > /dev/null 2>&1")
145
152
  end
153
+
154
+ def to_s
155
+ code_name
156
+ end
146
157
  end
147
158
  end
@@ -30,10 +30,10 @@ module Shelly
30
30
  say "No database backups available"
31
31
  end
32
32
  rescue Client::APIError => e
33
- if e.unauthorized?
33
+ if e.not_found?
34
34
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
35
35
  else
36
- say_error e.message
36
+ raise e
37
37
  end
38
38
  end
39
39
 
@@ -49,11 +49,13 @@ module Shelly
49
49
  say_new_line
50
50
  say "Backup file saved to #{backup.filename}", :green
51
51
  rescue Client::APIError => e
52
- if e.not_found?
52
+ case e.resource_not_found
53
+ when :cloud
54
+ say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
55
+ when :backup
53
56
  say_error "Backup not found", :with_exit => false
54
57
  say "You can list available backups with 'shelly backup list' command"
55
- else
56
- raise e
58
+ else; raise e
57
59
  end
58
60
  end
59
61
 
@@ -66,7 +68,7 @@ module Shelly
66
68
  say "Backup requested. It can take up to several minutes for" +
67
69
  "the backup process to finish and the backup to show up in backups list.", :green
68
70
  rescue Client::APIError => e
69
- if e.unauthorized?
71
+ if e.not_found?
70
72
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
71
73
  else
72
74
  say_error e.message
@@ -34,10 +34,10 @@ module Shelly
34
34
  say "Cloud #{cloud} has no configuration files"
35
35
  end
36
36
  rescue Client::APIError => e
37
- if e.unauthorized?
38
- say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
37
+ if e.resource_not_found == :cloud
38
+ say_error "You have no access to '#{@app}' cloud defined in Cloudfile"
39
39
  else
40
- say_error e.message
40
+ raise e
41
41
  end
42
42
  end
43
43
  end
@@ -53,8 +53,13 @@ module Shelly
53
53
  say "Content of #{config["path"]}:", :green
54
54
  say config["content"]
55
55
  rescue Client::APIError => e
56
- if e.unauthorized?
56
+ case e.resource_not_found
57
+ when :cloud
57
58
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
59
+ when :config
60
+ say_error "Config '#{path}' not found", :with_exit => false
61
+ say_error "You can list available config files with `shelly config list --cloud #{@app}`"
62
+ else; raise e
58
63
  end
59
64
  end
60
65
 
@@ -69,7 +74,7 @@ module Shelly
69
74
  @app.create_config(path, output)
70
75
  say "File '#{path}' created, it will be used after next code deploy", :green
71
76
  rescue Client::APIError => e
72
- if e.unauthorized?
77
+ if e.resource_not_found == :cloud
73
78
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
74
79
  elsif e.validation?
75
80
  e.each_error { |error| say_error error, :with_exit => false }
@@ -91,8 +96,11 @@ module Shelly
91
96
  @app.update_config(path, content)
92
97
  say "File '#{config["path"]}' updated, it will be used after next code deploy", :green
93
98
  rescue Client::APIError => e
94
- if e.unauthorized?
99
+ if e.resource_not_found == :cloud
95
100
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
101
+ elsif e.resource_not_found == :config
102
+ say_error "Config '#{path}' not found", :with_exit => false
103
+ say_error "You can list available config files with `shelly config list --cloud #{@app}`"
96
104
  elsif e.validation?
97
105
  e.each_error { |error| say_error error, :with_exit => false }
98
106
  exit 1
@@ -115,8 +123,11 @@ module Shelly
115
123
  say "File not deleted"
116
124
  end
117
125
  rescue Client::APIError => e
118
- if e.unauthorized?
126
+ if e.resource_not_found == :cloud
119
127
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
128
+ elsif e.resource_not_found == :config
129
+ say_error "Config '#{path}' not found", :with_exit => false
130
+ say_error "You can list available config files with `shelly config list --cloud #{@app}`"
120
131
  elsif e.validation?
121
132
  e.each_error { |error| say_error error, :with_exit => false }
122
133
  exit 1
@@ -25,10 +25,10 @@ module Shelly
25
25
  say "No deploy logs available"
26
26
  end
27
27
  rescue Client::APIError => e
28
- if e.unauthorized?
28
+ if e.not_found?
29
29
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
30
30
  else
31
- say_error e.message
31
+ raise e
32
32
  end
33
33
  end
34
34
 
@@ -56,12 +56,15 @@ module Shelly
56
56
  say("Starting thin", :green); say(content["thin_restart"])
57
57
  end
58
58
  rescue Client::APIError => e
59
- if e.unauthorized?
60
- say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
61
- elsif e.message == "Log not found"
62
- say_error "Log not found, list all deploy logs using:", :with_exit => false
63
- say " shelly deploys list #{cloud}"
64
- exit 1
59
+ if e.not_found?
60
+ case e.resource_not_found
61
+ when :cloud
62
+ say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
63
+ when :log
64
+ say_error "Log not found, list all deploy logs using `shelly deploys list --cloud=#{@app.code_name}`"
65
+ end
66
+ else
67
+ raise e
65
68
  end
66
69
  end
67
70
 
@@ -143,16 +143,16 @@ module Shelly
143
143
 
144
144
  desc "ip", "Lists clouds IP's"
145
145
  def ip
146
+ say_error "No Cloudfile found" unless Cloudfile.present?
146
147
  @cloudfile = Cloudfile.new
147
148
  @cloudfile.clouds.each do |cloud|
148
149
  begin
149
150
  @app = App.new(cloud)
150
151
  say "Cloud #{cloud}:", :green
151
- ips = @app.ips
152
- print_wrapped "Web server IP: #{ips['web_server_ip']}", :ident => 2
153
- print_wrapped "Mail server IP: #{ips['mail_server_ip']}", :ident => 2
152
+ print_wrapped "Web server IP: #{@app.web_server_ip}", :ident => 2
153
+ print_wrapped "Mail server IP: #{@app.mail_server_ip}", :ident => 2
154
154
  rescue Client::APIError => e
155
- if e.unauthorized?
155
+ if e.not_found?
156
156
  say_error "You have no access to '#{cloud}' cloud defined in Cloudfile", :with_exit => false
157
157
  else
158
158
  say_error e.message, :with_exit => false
@@ -190,7 +190,7 @@ module Shelly
190
190
  end
191
191
  exit 1
192
192
  rescue Client::APIError => e
193
- if e.unauthorized?
193
+ if e.not_found?
194
194
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
195
195
  end
196
196
  end
@@ -203,7 +203,7 @@ module Shelly
203
203
  @app.stop
204
204
  say "Cloud '#{@app.code_name}' stopped"
205
205
  rescue Client::APIError => e
206
- if e.unauthorized?
206
+ if e.not_found?
207
207
  say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
208
208
  end
209
209
  end
@@ -230,7 +230,9 @@ module Shelly
230
230
  say "Missing git remote"
231
231
  end
232
232
  rescue Client::APIError => e
233
- say_error e.message
233
+ if e.not_found?
234
+ say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
235
+ end
234
236
  end
235
237
 
236
238
  desc "logs", "Show latest application logs from each instance"
@@ -247,7 +249,7 @@ module Shelly
247
249
  say log
248
250
  end
249
251
  rescue Client::APIError => e
250
- if e.unauthorized?
252
+ if e.not_found?
251
253
  say_error "You have no access to cloud '#{cloud || @app.code_name}'"
252
254
  end
253
255
  end
@@ -19,7 +19,7 @@ module Shelly
19
19
  say "Cloud #{cloud}:"
20
20
  @app.users.each { |user| say " #{user["email"]}" }
21
21
  rescue Client::APIError => e
22
- if e.unauthorized?
22
+ if e.not_found?
23
23
  say_error "You have no access to '#{cloud}' cloud defined in Cloudfile", :with_exit => false
24
24
  else
25
25
  say_error e.message, :with_exit => false
@@ -39,7 +39,7 @@ module Shelly
39
39
  rescue Client::APIError => e
40
40
  if e.validation? && e.errors.include?(["email", "#{email} has already been taken"])
41
41
  say_error "User #{email} is already in the cloud #{cloud}", :with_exit => false
42
- elsif e.unauthorized?
42
+ elsif e.not_found?
43
43
  say_error "You have no access to '#{cloud}' cloud defined in Cloudfile", :with_exit => false
44
44
  elsif e.validation?
45
45
  e.each_error { |error| say_error error, :with_exit => false }
@@ -51,12 +51,7 @@ module Shelly
51
51
  say "Sending invitation to #{user_email} to work on #{cloud}", :green
52
52
  end
53
53
  end
54
- rescue Client::APIError => e
55
- if e.unauthorized?
56
- e.errors.each { |error| say_error error, :with_exit => false}
57
- exit 1
58
- end
59
54
  end
60
55
  end
61
56
  end
62
- end
57
+ end
data/lib/shelly/client.rb CHANGED
@@ -5,42 +5,45 @@ require "cgi"
5
5
  module Shelly
6
6
  class Client
7
7
  class APIError < Exception
8
- attr_reader :status_code
8
+ attr_reader :status_code, :body
9
9
 
10
- def initialize(response_body, status_code)
11
- @response = JSON.parse(response_body)
10
+ def initialize(status_code, body = {})
12
11
  @status_code = status_code
12
+ @body = body
13
13
  end
14
14
 
15
15
  def message
16
- @response["message"]
16
+ body["message"]
17
17
  end
18
18
 
19
19
  def errors
20
- @response["errors"]
20
+ body["errors"]
21
21
  end
22
22
 
23
23
  def url
24
- @response["url"]
24
+ body["url"]
25
25
  end
26
26
 
27
27
  def validation?
28
28
  message == "Validation Failed"
29
29
  end
30
-
30
+
31
31
  def not_found?
32
32
  status_code == 404
33
33
  end
34
34
 
35
+ def resource_not_found
36
+ return unless not_found?
37
+ message =~ /Couldn't find (.*) with/ && $1.downcase.to_sym
38
+ end
39
+
35
40
  def unauthorized?
36
- # FIXME: Return unauthorized if user has no access to cloud, checked by 401 status code
37
- # Return 404 if child of app doesn't exist, should be fixed in API
38
- message == "Unauthorized" || message =~ /Cloud .+ not found/
41
+ status_code == 401
39
42
  end
40
43
 
41
44
  def each_error
42
- @response["errors"].each do |index,message|
43
- yield index.gsub('_',' ').capitalize + " " + message
45
+ errors.each do |field, message|
46
+ yield [field.gsub('_',' ').capitalize, message].join(" ")
44
47
  end
45
48
  end
46
49
  end
@@ -115,6 +118,10 @@ module Shelly
115
118
  def apps
116
119
  get("/apps")
117
120
  end
121
+
122
+ def app(code_name)
123
+ get("/apps/#{code_name}")
124
+ end
118
125
 
119
126
  def deploy_logs(cloud)
120
127
  get("/apps/#{cloud}/deploys")
@@ -148,10 +155,6 @@ module Shelly
148
155
  get("/apps/#{cloud}/users")
149
156
  end
150
157
 
151
- def app_ips(cloud)
152
- get("/apps/#{cloud}/ips")
153
- end
154
-
155
158
  def post(path, params = {})
156
159
  request(path, :post, params)
157
160
  end
@@ -211,12 +214,11 @@ module Shelly
211
214
  end
212
215
 
213
216
  def process_response(response)
214
- if [401, 404, 422, 500].include?(response.code)
215
- raise APIError.new(response.body, response.code)
216
- end
217
-
217
+ body = JSON.parse(response.body) rescue JSON::ParserError && {}
218
+ code = response.code
219
+ raise APIError.new(code, body) if (400..599).include?(code)
218
220
  response.return!
219
- JSON.parse(response.body)
221
+ body
220
222
  end
221
223
  end
222
224
  end
@@ -1,3 +1,3 @@
1
1
  module Shelly
2
- VERSION = "0.0.40"
2
+ VERSION = "0.0.41.pre"
3
3
  end
@@ -24,13 +24,6 @@ describe Shelly::App do
24
24
  end
25
25
  end
26
26
 
27
- describe "#ips" do
28
- it "should get app's ips" do
29
- @client.should_receive(:app_ips).with("foo-staging")
30
- @app.ips
31
- end
32
- end
33
-
34
27
  describe "#add_git_remote" do
35
28
  before do
36
29
  @app.stub(:git_url).and_return("git@git.shellycloud.com:foo-staging.git")
@@ -50,18 +43,67 @@ describe Shelly::App do
50
43
 
51
44
  describe "#configs" do
52
45
  it "should get configs from client" do
53
- @client.should_receive(:app_configs).with("foo-staging")
54
- @app.configs
46
+ @client.should_receive(:app_configs).with("foo-staging").and_return(config_response)
47
+ @app.configs.should == config_response
55
48
  end
56
49
 
57
50
  it "should return only user config files" do
58
- @client.should_receive(:app_configs).with("foo-staging").and_return([])
59
- @app.user_configs
51
+ @client.should_receive(:app_configs).with("foo-staging").and_return(config_response)
52
+ @app.user_configs.should == [{"path" => "user_created", "created_by_user" => true}]
60
53
  end
61
54
 
62
55
  it "should return only shelly genereted config files" do
63
- @client.should_receive(:app_configs).with("foo-staging").and_return([])
64
- @app.shelly_generated_configs
56
+ @client.should_receive(:app_configs).with("foo-staging").and_return(config_response)
57
+ @app.shelly_generated_configs.should == [{"path" => "shelly_created", "created_by_user" => false}]
58
+ end
59
+
60
+ def config_response
61
+ [{"path" => "user_created", "created_by_user" => true},
62
+ {"path" => "shelly_created", "created_by_user" => false}]
63
+ end
64
+
65
+ it "should get config from client" do
66
+ @client.should_receive(:app_config).with("foo-staging", "path")
67
+ @app.config("path")
68
+ end
69
+
70
+ it "should create config using client" do
71
+ @client.should_receive(:app_create_config).with("foo-staging", "path", "content")
72
+ @app.create_config("path", "content")
73
+ end
74
+
75
+ it "should update config using client" do
76
+ @client.should_receive(:app_update_config).with("foo-staging", "path", "content")
77
+ @app.update_config("path", "content")
78
+ end
79
+
80
+ it "should delete config using client" do
81
+ @client.should_receive(:app_delete_config).with("foo-staging", "path")
82
+ @app.delete_config("path")
83
+ end
84
+ end
85
+
86
+ describe "#attributes" do
87
+ before do
88
+ @response = {"web_server_ip" => "192.0.2.1", "mail_server_ip" => "192.0.2.3"}
89
+ @client.stub(:app).and_return(@response)
90
+ end
91
+
92
+ it "should fetch app attributes from API and cache them" do
93
+ @client.should_receive(:app).with("foo-staging").exactly(:once).and_return(@response)
94
+ 2.times { @app.attributes }
95
+ end
96
+
97
+ describe "#web_server_ip" do
98
+ it "should return web server ip address" do
99
+ @app.web_server_ip.should == "192.0.2.1"
100
+ end
101
+ end
102
+
103
+ describe "#mail_server_ip" do
104
+ it "should return mail server ip address" do
105
+ @app.mail_server_ip.should == "192.0.2.3"
106
+ end
65
107
  end
66
108
  end
67
109
 
@@ -238,5 +280,11 @@ config
238
280
  end
239
281
  end
240
282
  end
283
+
284
+ describe "#to_s" do
285
+ it "should return code_name" do
286
+ @app.to_s.should == "foo-staging"
287
+ end
288
+ end
241
289
  end
242
290
 
@@ -30,8 +30,7 @@ describe Shelly::CLI::Backup do
30
30
  end
31
31
 
32
32
  it "should exit if user doesn't have access to cloud in Cloudfile" do
33
- response = {"message" => "Cloud foo-staging not found"}
34
- exception = Shelly::Client::APIError.new(response.to_json, 401)
33
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Cloud with"})
35
34
  @client.stub(:database_backups).and_raise(exception)
36
35
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
37
36
  lambda { invoke(@backup, :list) }.should raise_error(SystemExit)
@@ -104,7 +103,7 @@ describe Shelly::CLI::Backup do
104
103
 
105
104
  context "on backup not found" do
106
105
  it "it should display error message" do
107
- exception = Shelly::Client::APIError.new({}.to_json, 404)
106
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Backup with"})
108
107
  @client.stub(:database_backup).and_raise(exception)
109
108
  $stdout.should_receive(:puts).with(red "Backup not found")
110
109
  $stdout.should_receive(:puts).with("You can list available backups with 'shelly backup list' command")
@@ -114,7 +113,7 @@ describe Shelly::CLI::Backup do
114
113
 
115
114
  context "on unsupported exception" do
116
115
  it "should re-raise it" do
117
- exception = Shelly::Client::APIError.new({}.to_json, 500)
116
+ exception = Shelly::Client::APIError.new(500)
118
117
  @client.stub(:database_backup).and_raise(exception)
119
118
  $stdout.should_not_receive(:puts).with(red "Backup not found")
120
119
  $stdout.should_not_receive(:puts).with("You can list available backups with 'shelly backup list' command")
@@ -134,8 +133,7 @@ describe Shelly::CLI::Backup do
134
133
  end
135
134
 
136
135
  it "should exit if user doesn't have access to cloud in Cloudfile" do
137
- response = {"message" => "Cloud foo-staging not found"}
138
- exception = Shelly::Client::APIError.new(response.to_json, 404)
136
+ exception = Shelly::Client::APIError.new(404, {"message" => "Cloud foo-staging not found"})
139
137
  @client.stub(:request_backup).and_raise(exception)
140
138
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
141
139
  lambda { invoke(@backup, :create) }.should raise_error(SystemExit)
@@ -143,7 +141,7 @@ describe Shelly::CLI::Backup do
143
141
 
144
142
  it "should display errors and exit 1 when kind is not valid" do
145
143
  response = {"message" => "Wrong KIND argument. User one of following: postgresql, mongodb, redis"}
146
- exception = Shelly::Client::APIError.new(response.to_json, 422)
144
+ exception = Shelly::Client::APIError.new(422, response)
147
145
  @client.should_receive(:request_backup).and_raise(exception)
148
146
  $stdout.should_receive(:puts).with(red response["message"])
149
147
  lambda { invoke(@backup, :create) }.should raise_error(SystemExit)
@@ -34,8 +34,8 @@ describe Shelly::CLI::Config do
34
34
  end
35
35
 
36
36
  it "should exit if user doesn't have access to cloud in Cloudfile" do
37
- response = {"message" => "Cloud foo-staging not found"}
38
- exception = Shelly::Client::APIError.new(response.to_json, 404)
37
+ response = {"message" => "Couldn't find Cloud with code_name = foo-staging"}
38
+ exception = Shelly::Client::APIError.new(404, response)
39
39
  @client.stub(:app_configs).and_raise(exception)
40
40
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
41
41
  lambda { invoke(@config, :list) }.should raise_error(SystemExit)
@@ -76,6 +76,31 @@ describe Shelly::CLI::Config do
76
76
  invoke(@config, :show, "path")
77
77
  end
78
78
 
79
+ describe "on failure" do
80
+ context "when config doesn't exist" do
81
+ it "should display error message and exit with 1" do
82
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Config with"})
83
+ @client.should_receive(:app_config).and_raise(exception)
84
+ $stdout.should_receive(:puts).with(red "Config 'config/app.yml' not found")
85
+ $stdout.should_receive(:puts).with(red "You can list available config files with `shelly config list --cloud foo-staging`")
86
+ lambda {
87
+ invoke(@config, :show, "config/app.yml")
88
+ }.should raise_error(SystemExit)
89
+ end
90
+ end
91
+
92
+ context "when user doesn't have access to cloud" do
93
+ it "should display error message and exit with 1" do
94
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Cloud with"})
95
+ @client.should_receive(:app_config).and_raise(exception)
96
+ $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
97
+ lambda {
98
+ invoke(@config, :show, "config/app.yml")
99
+ }.should raise_error(SystemExit)
100
+ end
101
+ end
102
+ end
103
+
79
104
  context "multiple clouds" do
80
105
  before do
81
106
  File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
@@ -167,6 +192,31 @@ describe Shelly::CLI::Config do
167
192
  invoke(@config, :edit, "path")
168
193
  end
169
194
 
195
+ describe "on failure" do
196
+ context "when config doesn't exist" do
197
+ it "should display error message and exit with 1" do
198
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Config with"})
199
+ @client.should_receive(:app_config).and_raise(exception)
200
+ $stdout.should_receive(:puts).with(red "Config 'config/app.yml' not found")
201
+ $stdout.should_receive(:puts).with(red "You can list available config files with `shelly config list --cloud foo-staging`")
202
+ lambda {
203
+ invoke(@config, :edit, "config/app.yml")
204
+ }.should raise_error(SystemExit)
205
+ end
206
+ end
207
+
208
+ context "when user doesn't have access to cloud" do
209
+ it "should display error message and exit with 1" do
210
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Cloud with"})
211
+ @client.should_receive(:app_config).and_raise(exception)
212
+ $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
213
+ lambda {
214
+ invoke(@config, :edit, "config/app.yml")
215
+ }.should raise_error(SystemExit)
216
+ end
217
+ end
218
+ end
219
+
170
220
  context "multiple clouds" do
171
221
  before do
172
222
  File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
@@ -235,5 +285,35 @@ describe Shelly::CLI::Config do
235
285
  end
236
286
  end
237
287
  end
288
+
289
+ describe "on failure" do
290
+ context "when config doesn't exist" do
291
+ it "should display error message and exit with 1" do
292
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Config with"})
293
+ @client.should_receive(:app_delete_config).and_raise(exception)
294
+ $stdout.should_receive(:puts).with(red "Config 'config/app.yml' not found")
295
+ $stdout.should_receive(:puts).with(red "You can list available config files with `shelly config list --cloud foo-staging`")
296
+ fake_stdin(["y"]) do
297
+ lambda {
298
+ invoke(@config, :delete, "config/app.yml")
299
+ }.should raise_error(SystemExit)
300
+ end
301
+ end
302
+ end
303
+
304
+ context "when user doesn't have access to cloud" do
305
+ it "should display error message and exit with 1" do
306
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Cloud with"})
307
+ @client.should_receive(:app_delete_config).and_raise(exception)
308
+ $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
309
+ fake_stdin(["y"]) do
310
+ lambda {
311
+ invoke(@config, :delete, "config/app.yml")
312
+ }.should raise_error(SystemExit)
313
+ end
314
+ end
315
+ end
316
+ end
317
+
238
318
  end
239
319
  end
@@ -29,9 +29,7 @@ describe Shelly::CLI::Deploys do
29
29
  end
30
30
 
31
31
  it "should exit if user doesn't have access to cloud in Cloudfile" do
32
- response = {"message" => "Cloud foo-staging not found"}
33
- exception = Shelly::Client::APIError.new(response.to_json, 404)
34
- @client.stub(:deploy_logs).and_raise(exception)
32
+ @client.stub(:deploy_logs).and_raise(Shelly::Client::APIError.new(404))
35
33
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
36
34
  lambda { invoke(@deploys, :list) }.should raise_error(SystemExit)
37
35
  end
@@ -88,12 +86,22 @@ describe Shelly::CLI::Deploys do
88
86
  }.should raise_error(SystemExit)
89
87
  end
90
88
 
91
- it "should exit if user doesn't have access to cloud in Cloudfile" do
92
- response = {"message" => "Cloud foo-staging not found"}
93
- exception = Shelly::Client::APIError.new(response.to_json, 404)
94
- @client.stub(:deploy_log).and_raise(exception)
95
- $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
96
- lambda { invoke(@deploys, :show, "last") }.should raise_error(SystemExit)
89
+ context "user doesn't have access to cloud" do
90
+ it "should exit 1 with message" do
91
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Cloud with"})
92
+ @client.stub(:deploy_log).and_raise(exception)
93
+ $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
94
+ lambda { @deploys.show("last") }.should raise_error(SystemExit)
95
+ end
96
+ end
97
+
98
+ context "log not found" do
99
+ it "should exit 1 with message" do
100
+ exception = Shelly::Client::APIError.new(404, {"message" => "Couldn't find Log with"})
101
+ @client.stub(:deploy_log).and_raise(exception)
102
+ $stdout.should_receive(:puts).with(red "Log not found, list all deploy logs using `shelly deploys list --cloud=foo-staging`")
103
+ lambda { @deploys.show("last") }.should raise_error(SystemExit)
104
+ end
97
105
  end
98
106
 
99
107
  context "multiple clouds" do
@@ -173,7 +173,7 @@ OUT
173
173
  context "on unsuccessful registration" do
174
174
  it "should display errors and exit with 1" do
175
175
  response = {"message" => "Validation Failed", "errors" => [["email", "has been already taken"]]}
176
- exception = Shelly::Client::APIError.new(response.to_json, 422)
176
+ exception = Shelly::Client::APIError.new(422, response)
177
177
  @client.stub(:register_user).and_raise(exception)
178
178
  $stdout.should_receive(:puts).with("\e[31mEmail has been already taken\e[0m")
179
179
  lambda {
@@ -260,7 +260,7 @@ OUT
260
260
  context "on unauthorized user" do
261
261
  it "should exit with 1 and display error message" do
262
262
  response = {"message" => "Unauthorized", "url" => "https://admin.winniecloud.com/users/password/new"}
263
- exception = Shelly::Client::APIError.new(response.to_json, 401)
263
+ exception = Shelly::Client::APIError.new(401, response)
264
264
  @client.stub(:token).and_raise(exception)
265
265
  $stdout.should_receive(:puts).with("\e[31mWrong email or password\e[0m")
266
266
  $stdout.should_receive(:puts).with("\e[31mYou can reset password by using link:\e[0m")
@@ -380,7 +380,7 @@ OUT
380
380
 
381
381
  it "should display validation errors if they are any" do
382
382
  response = {"message" => "Validation Failed", "errors" => [["code_name", "has been already taken"]]}
383
- exception = Shelly::Client::APIError.new(response.to_json, 422)
383
+ exception = Shelly::Client::APIError.new(422, response)
384
384
  @app.should_receive(:create).and_raise(exception)
385
385
  $stdout.should_receive(:puts).with("\e[31mCode name has been already taken\e[0m")
386
386
  $stdout.should_receive(:puts).with("\e[31mFix erros in the below command and type it again to create your cloud\e[0m")
@@ -468,8 +468,7 @@ OUT
468
468
 
469
469
  context "on failure" do
470
470
  it "should display info that user is not logged in" do
471
- body = {"message" => "Unauthorized"}
472
- error = Shelly::Client::APIError.new(body.to_json, 401)
471
+ error = Shelly::Client::APIError.new(401)
473
472
  @client.stub(:token).and_raise(error)
474
473
  $stdout.should_receive(:puts).with(red "You are not logged in. To log in use:")
475
474
  $stdout.should_receive(:puts).with(" shelly login")
@@ -502,16 +501,15 @@ OUT
502
501
  end
503
502
 
504
503
  it "should exit if user doesn't have access to clouds in Cloudfile" do
505
- response = {"message" => "Cloud foo-production not found"}
506
- exception = Shelly::Client::APIError.new(response.to_json, 404)
504
+ response = {"message" => "Couldn't find Cloud with"}
505
+ exception = Shelly::Client::APIError.new(404, response)
507
506
  @client.stub(:start_cloud).and_raise(exception)
508
507
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
509
508
  lambda { invoke(@main, :start) }.should raise_error(SystemExit)
510
509
  end
511
510
 
512
511
  it "should exit if user is not logged in" do
513
- response = {"message" => "Unauthorized"}
514
- exception = Shelly::Client::APIError.new(response.to_json, 401)
512
+ exception = Shelly::Client::APIError.new(401)
515
513
  @client.stub(:token).and_raise(exception)
516
514
  $stdout.should_receive(:puts).with(red "You are not logged in. To log in use:")
517
515
  $stdout.should_receive(:puts).with(" shelly login")
@@ -620,17 +618,13 @@ OUT
620
618
  end
621
619
 
622
620
  it "should exit if user doesn't have access to clouds in Cloudfile" do
623
- response = {"message" => "Cloud foo-production not found"}
624
- exception = Shelly::Client::APIError.new(response.to_json, 404)
625
- @client.stub(:stop_cloud).and_raise(exception)
621
+ @client.stub(:stop_cloud).and_raise(Shelly::Client::APIError.new(404))
626
622
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
627
623
  lambda { invoke(@main, :stop) }.should raise_error(SystemExit)
628
624
  end
629
625
 
630
626
  it "should exit if user is not logged in" do
631
- response = {"message" => "Unauthorized"}
632
- exception = Shelly::Client::APIError.new(response.to_json, 401)
633
- @client.stub(:token).and_raise(exception)
627
+ @client.stub(:token).and_raise(Shelly::Client::APIError.new(401))
634
628
  $stdout.should_receive(:puts).with(red "You are not logged in. To log in use:")
635
629
  $stdout.should_receive(:puts).with(" shelly login")
636
630
  lambda { invoke(@main, :stop) }.should raise_error(SystemExit)
@@ -673,14 +667,6 @@ OUT
673
667
  Shelly::App.stub(:inside_git_repository?).and_return(true)
674
668
  end
675
669
 
676
- it "should exit with message if command run outside git repository" do
677
- Shelly::App.stub(:inside_git_repository?).and_return(false)
678
- $stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
679
- lambda {
680
- invoke(@main, :ip)
681
- }.should raise_error(SystemExit)
682
- end
683
-
684
670
  it "should exit with message if there is no Cloudfile" do
685
671
  File.delete("Cloudfile")
686
672
  $stdout.should_receive(:puts).with("\e[31mNo Cloudfile found\e[0m")
@@ -691,7 +677,7 @@ OUT
691
677
 
692
678
  context "on success" do
693
679
  it "should display mail and web server ip's" do
694
- @client.stub(:app_ips).and_return(response)
680
+ @client.stub(:app).and_return(response)
695
681
  $stdout.should_receive(:puts).with("\e[32mCloud foo-production:\e[0m")
696
682
  $stdout.should_receive(:puts).with(" Web server IP: 22.22.22.22")
697
683
  $stdout.should_receive(:puts).with(" Mail server IP: 11.11.11.11")
@@ -704,16 +690,9 @@ OUT
704
690
  end
705
691
 
706
692
  context "on failure" do
707
- it "should raise an error if user is not in git repository" do
708
- Shelly::App.stub(:inside_git_repository?).and_return(false)
709
- $stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
710
- lambda { invoke(@main, :ip) }.should raise_error(SystemExit)
711
- end
712
-
713
693
  it "should raise an error if user does not have access to cloud" do
714
- response = {"message" => "Cloud foo-staging not found"}
715
- exception = Shelly::Client::APIError.new(response.to_json, 404)
716
- @client.stub(:app_ips).and_raise(exception)
694
+ exception = Shelly::Client::APIError.new(404, {"message" => "Cloud foo-staging not found"})
695
+ @client.stub(:app).and_raise(exception)
717
696
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
718
697
  invoke(@main, :ip)
719
698
  end
@@ -758,7 +737,8 @@ OUT
758
737
  @app.should_not_receive(:delete)
759
738
  lambda{
760
739
  fake_stdin(["yes", "yes", "no"]) do
761
- invoke(@main, :delete, "--cloud", "foo-staging")
740
+ @main.options = {:cloud => "foo-staging"}
741
+ invoke(@main, :delete)
762
742
  end
763
743
  }.should raise_error(SystemExit)
764
744
  end
@@ -774,7 +754,8 @@ OUT
774
754
  Shelly::App.stub(:inside_git_repository?).and_return(false)
775
755
  $stdout.should_receive(:puts).with("Missing git remote")
776
756
  fake_stdin(["yes", "yes", "yes"]) do
777
- invoke(@main, :delete, "--cloud", "foo-staging")
757
+ @main.options = {:cloud => "foo-staging"}
758
+ invoke(@main, :delete)
778
759
  end
779
760
  end
780
761
  end
@@ -786,13 +767,13 @@ OUT
786
767
  end
787
768
 
788
769
  it "should raise Client::APIError" do
789
- response = {:message => "Application not found"}
790
- exception = Shelly::Client::APIError.new(response.to_json, 404)
770
+ exception = Shelly::Client::APIError.new(404)
791
771
  @app.stub(:delete).and_raise(exception)
792
- $stdout.should_receive(:puts).with("\e[31mApplication not found\e[0m")
772
+ $stdout.should_receive(:puts).with(red "You have no access to 'foo-bar' cloud defined in Cloudfile")
793
773
  lambda{
794
774
  fake_stdin(["yes", "yes", "yes"]) do
795
- invoke(@main, :delete, "--cloud", "foo-bar")
775
+ @main.options = {:cloud => "foo-bar"}
776
+ invoke(@main, :delete)
796
777
  end
797
778
  }.should raise_error(SystemExit)
798
779
  end
@@ -846,7 +827,7 @@ OUT
846
827
 
847
828
  it "should exit if user doesn't have access to clouds in Cloudfile" do
848
829
  response = {"message" => "Cloud foo-production not found"}
849
- exception = Shelly::Client::APIError.new(response.to_json, 404)
830
+ exception = Shelly::Client::APIError.new(404, response)
850
831
  @client.stub(:application_logs).and_raise(exception)
851
832
  $stdout.should_receive(:puts).
852
833
  with(red "You have no access to cloud 'foo-production'")
@@ -854,8 +835,7 @@ OUT
854
835
  end
855
836
 
856
837
  it "should exit if user is not logged in" do
857
- response = {"message" => "Unauthorized"}
858
- exception = Shelly::Client::APIError.new(response.to_json, 401)
838
+ exception = Shelly::Client::APIError.new(401)
859
839
  @client.stub(:token).and_raise(exception)
860
840
  $stdout.should_receive(:puts).
861
841
  with(red "You are not logged in. To log in use:")
@@ -51,7 +51,7 @@ describe Shelly::CLI::User do
51
51
 
52
52
  it "should exit if user is not logged in" do
53
53
  response = {"message" => "Unauthorized", "url" => "https://admin.winniecloud.com/users/password/new"}
54
- exception = Shelly::Client::APIError.new(response.to_json, 404)
54
+ exception = Shelly::Client::APIError.new(401, response)
55
55
  @client.stub(:token).and_raise(exception)
56
56
  $stdout.should_receive(:puts).with(red "You are not logged in. To log in use:")
57
57
  $stdout.should_receive(:puts).with(" shelly login")
@@ -81,8 +81,8 @@ describe Shelly::CLI::User do
81
81
 
82
82
  context "on failure" do
83
83
  it "should raise an error if user does not have access to cloud" do
84
- response = {"message" => "Cloud foo-staging not found"}
85
- exception = Shelly::Client::APIError.new(response.to_json, 404)
84
+ response = {"message" => "Couldn't find Cloud with code_name = foo-staging"}
85
+ exception = Shelly::Client::APIError.new(404, response)
86
86
  @client.stub(:app_users).and_raise(exception)
87
87
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
88
88
  invoke(@cli_user, :list)
@@ -143,7 +143,7 @@ describe Shelly::CLI::User do
143
143
  context "on failure" do
144
144
  it "should raise error if user doesnt have access to cloud" do
145
145
  response = {"message" => "Cloud foo-staging not found"}
146
- exception = Shelly::Client::APIError.new(response.to_json, 404)
146
+ exception = Shelly::Client::APIError.new(404, response)
147
147
  @client.stub(:send_invitation).and_raise(exception)
148
148
  $stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
149
149
  invoke(@cli_user, :add, "megan@example.com")
@@ -2,12 +2,13 @@ require "spec_helper"
2
2
 
3
3
  describe Shelly::Client::APIError do
4
4
  before do
5
- body = {"message" => "something went wrong", "errors" => [["first", "foo"]], "url" => "https://foo.bar"}
6
- @error = Shelly::Client::APIError.new(body.to_json, 404)
5
+ body = {"message" => "Couldn't find Cloud with code_name = fooo",
6
+ "errors" => [["first", "foo"]], "url" => "https://foo.bar"}
7
+ @error = Shelly::Client::APIError.new(404, body)
7
8
  end
8
9
 
9
10
  it "should return error message" do
10
- @error.message.should == "something went wrong"
11
+ @error.message.should == "Couldn't find Cloud with code_name = fooo"
11
12
  end
12
13
 
13
14
  it "should return array of errors" do
@@ -19,16 +20,31 @@ describe Shelly::Client::APIError do
19
20
  end
20
21
 
21
22
  it "should return user friendly string" do
22
- @error.each_error{|error| error.should == "First foo"}
23
+ @error.each_error { |error| error.should == "First foo" }
23
24
  end
24
-
25
+
26
+ describe "#resource_not_found?" do
27
+ context "on 404 response" do
28
+ it "should return which resource was not found" do
29
+ @error.resource_not_found.should == :cloud
30
+ end
31
+ end
32
+
33
+ context "on non 404 response" do
34
+ it "should return nil" do
35
+ error = Shelly::Client::APIError.new(401)
36
+ error.resource_not_found.should be_nil
37
+ end
38
+ end
39
+ end
40
+
25
41
  describe "#not_found?" do
26
42
  it "should return true if response status code is 404" do
27
43
  @error.should be_not_found
28
44
  end
29
-
45
+
30
46
  it "should return false if response status code is not 404" do
31
- error = Shelly::Client::APIError.new({}.to_json, 500)
47
+ error = Shelly::Client::APIError.new(500)
32
48
  error.should_not be_not_found
33
49
  end
34
50
  end
@@ -37,7 +53,7 @@ describe Shelly::Client::APIError do
37
53
  context "when error is caused by validation errors" do
38
54
  it "should return true" do
39
55
  body = {"message" => "Validation Failed"}
40
- error = Shelly::Client::APIError.new(body.to_json, 422)
56
+ error = Shelly::Client::APIError.new(422, body)
41
57
  error.should be_validation
42
58
  end
43
59
  end
@@ -52,8 +68,7 @@ describe Shelly::Client::APIError do
52
68
  describe "#unauthorized?" do
53
69
  context "when error is caused by unauthorized error" do
54
70
  it "should return true" do
55
- body = {"message" => "Unauthorized"}
56
- error = Shelly::Client::APIError.new(body.to_json, 401)
71
+ error = Shelly::Client::APIError.new(401)
57
72
  error.should be_unauthorized
58
73
  end
59
74
  end
@@ -71,7 +86,7 @@ describe Shelly::Client do
71
86
  ENV['SHELLY_URL'] = nil
72
87
  @client = Shelly::Client.new("bob@example.com", "secret")
73
88
  end
74
-
89
+
75
90
  def api_url(resource = "")
76
91
  auth = "#{CGI.escape(@client.email)}:#{@client.password}@"
77
92
  "https://#{auth}admin.winniecloud.com/apiv2/#{resource}"
@@ -142,6 +157,38 @@ describe Shelly::Client do
142
157
  end
143
158
  end
144
159
 
160
+ describe "#app_config" do
161
+ it "should send get request" do
162
+ FakeWeb.register_uri(:get, api_url("apps/staging-foo/configs/path"), :body => [{:content => "content", :path => "path"}].to_json)
163
+ response = @client.app_config("staging-foo", "path")
164
+ response.should == [{"content" => "content", "path" => "path"}]
165
+ end
166
+ end
167
+
168
+ describe "#app_create_config" do
169
+ it "should send post request" do
170
+ FakeWeb.register_uri(:post, api_url("apps/staging-foo/configs"), :body => {}.to_json, :status => 201)
171
+ response = @client.app_create_config("staging-foo", "path", "content")
172
+ response.should == {}
173
+ end
174
+ end
175
+
176
+ describe "#app_update_config" do
177
+ it "should send put request" do
178
+ FakeWeb.register_uri(:put, api_url("apps/staging-foo/configs/path"), :body => {}.to_json)
179
+ response = @client.app_update_config("staging-foo", "path", "content")
180
+ response.should == {}
181
+ end
182
+ end
183
+
184
+ describe "#app_delete_config" do
185
+ it "should send delete request" do
186
+ FakeWeb.register_uri(:delete, api_url("apps/staging-foo/configs/path"), :body => {}.to_json)
187
+ response = @client.app_delete_config("staging-foo", "path")
188
+ response.should == {}
189
+ end
190
+ end
191
+
145
192
  describe "#application_logs" do
146
193
  it "should send get request" do
147
194
  time = Time.now
@@ -167,12 +214,13 @@ describe Shelly::Client do
167
214
  response.should == [{"email" => "test@example.com"}, {"email" => "test2@example.com"}]
168
215
  end
169
216
  end
170
-
171
- describe "#app_ips" do
172
- it "should send get request with app code_name" do
173
- FakeWeb.register_uri(:get, api_url("apps/staging-foo/ips"), :body => {:mail_server_ip => "10.0.1.1", :web_server_ip => "88.198.21.187"}.to_json)
174
- response = @client.app_ips("staging-foo")
175
- response.should == {"mail_server_ip" => "10.0.1.1", "web_server_ip" => "88.198.21.187"}
217
+
218
+ describe "#app" do
219
+ it "should fetch app from API" do
220
+ FakeWeb.register_uri(:get, api_url("apps/staging-foo"),
221
+ :body => {:web_server_ip => "192.0.2.1", :mail_server_ip => "192.0.2.3"}.to_json)
222
+ response = @client.app("staging-foo")
223
+ response.should == {"web_server_ip" => "192.0.2.1", "mail_server_ip" => "192.0.2.3"}
176
224
  end
177
225
  end
178
226
 
@@ -221,7 +269,7 @@ describe Shelly::Client do
221
269
  @client.ssh_key_available?("ssh-key Abb")
222
270
  end
223
271
  end
224
-
272
+
225
273
  describe "#database_backup" do
226
274
  it "should fetch backup description from API" do
227
275
  expected = {
@@ -232,11 +280,11 @@ describe Shelly::Client do
232
280
  filename = "2011.11.26.04.00.10.foo.postgres.tar.gz"
233
281
  url = api_url("apps/foo/database_backups/#{filename}")
234
282
  FakeWeb.register_uri(:get, url, :body => expected.to_json)
235
-
283
+
236
284
  @client.database_backup("foo", filename).should == expected
237
285
  end
238
286
  end
239
-
287
+
240
288
  describe "#download_backup" do
241
289
  before do
242
290
  @filename = "2011.11.26.04.00.10.foo.postgres.tar.gz"
@@ -246,18 +294,18 @@ describe Shelly::Client do
246
294
  response.stub(:read_body).and_yield("aaa").and_yield("bbbbb").and_yield("dddf")
247
295
  FakeWeb.register_uri(:get, url, :response => response)
248
296
  end
249
-
297
+
250
298
  it "should write streamed database backup to file" do
251
299
  @client.download_backup("foo", @filename)
252
300
  File.read(@filename).should == %w(aaa bbbbb dddf).join
253
301
  end
254
-
302
+
255
303
  it "should execute progress_callback with size of every chunk" do
256
304
  progress = mock(:update => true)
257
305
  progress.should_receive(:update).with(3)
258
306
  progress.should_receive(:update).with(5)
259
307
  progress.should_receive(:update).with(4)
260
-
308
+
261
309
  callback = lambda { |size| progress.update(size) }
262
310
 
263
311
  @client.download_backup("foo", @filename, callback)
@@ -331,6 +379,13 @@ describe Shelly::Client do
331
379
  end
332
380
  end
333
381
  end
382
+
383
+ it "should return empty hash if response is not a valid JSON" do
384
+ JSON.should_receive(:parse).with("").and_raise(JSON::ParserError)
385
+ @response.stub(:code).and_return("204")
386
+ @response.stub(:body).and_return("")
387
+ @client.post("/api/apps/flower").should == {}
388
+ end
334
389
  end
335
390
 
336
391
  describe "#headers" do
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shelly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.40
5
- prerelease:
4
+ version: 0.0.41.pre
5
+ prerelease: 7
6
6
  platform: ruby
7
7
  authors:
8
8
  - Shelly Cloud team
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-30 00:00:00.000000000 Z
12
+ date: 2012-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70136906680620 !ruby/object:Gem::Requirement
16
+ requirement: &70129852876100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70136906680620
24
+ version_requirements: *70129852876100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70136906696580 !ruby/object:Gem::Requirement
27
+ requirement: &70129852875680 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70136906696580
35
+ version_requirements: *70129852875680
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: guard
38
- requirement: &70136906696160 !ruby/object:Gem::Requirement
38
+ requirement: &70129852875260 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70136906696160
46
+ version_requirements: *70129852875260
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: guard-rspec
49
- requirement: &70136906695740 !ruby/object:Gem::Requirement
49
+ requirement: &70129852874840 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70136906695740
57
+ version_requirements: *70129852874840
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: growl_notify
60
- requirement: &70136906695280 !ruby/object:Gem::Requirement
60
+ requirement: &70129852890740 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70136906695280
68
+ version_requirements: *70129852890740
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rb-fsevent
71
- requirement: &70136906694860 !ruby/object:Gem::Requirement
71
+ requirement: &70129852890320 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70136906694860
79
+ version_requirements: *70129852890320
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: fakefs
82
- requirement: &70136906694440 !ruby/object:Gem::Requirement
82
+ requirement: &70129852889900 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70136906694440
90
+ version_requirements: *70129852889900
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: fakeweb
93
- requirement: &70136906694020 !ruby/object:Gem::Requirement
93
+ requirement: &70129852889480 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70136906694020
101
+ version_requirements: *70129852889480
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: wijet-thor
104
- requirement: &70136906693520 !ruby/object:Gem::Requirement
104
+ requirement: &70129852888980 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: 0.14.7
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *70136906693520
112
+ version_requirements: *70129852888980
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: rest-client
115
- requirement: &70136906693100 !ruby/object:Gem::Requirement
115
+ requirement: &70129852888560 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: '0'
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *70136906693100
123
+ version_requirements: *70129852888560
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: json
126
- requirement: &70136906692640 !ruby/object:Gem::Requirement
126
+ requirement: &70129852888100 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ! '>='
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '0'
132
132
  type: :runtime
133
133
  prerelease: false
134
- version_requirements: *70136906692640
134
+ version_requirements: *70129852888100
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: wijet-launchy
137
- requirement: &70136906692220 !ruby/object:Gem::Requirement
137
+ requirement: &70129852887680 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :runtime
144
144
  prerelease: false
145
- version_requirements: *70136906692220
145
+ version_requirements: *70129852887680
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: progressbar
148
- requirement: &70136906691800 !ruby/object:Gem::Requirement
148
+ requirement: &70129852887260 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,7 +153,7 @@ dependencies:
153
153
  version: '0'
154
154
  type: :runtime
155
155
  prerelease: false
156
- version_requirements: *70136906691800
156
+ version_requirements: *70129852887260
157
157
  description: Tool for managing applications and clouds at shellycloud.com
158
158
  email:
159
159
  - support@shellycloud.com
@@ -223,21 +223,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
223
223
  - - ! '>='
224
224
  - !ruby/object:Gem::Version
225
225
  version: '0'
226
- segments:
227
- - 0
228
- hash: 2595737964624093032
229
226
  required_rubygems_version: !ruby/object:Gem::Requirement
230
227
  none: false
231
228
  requirements:
232
- - - ! '>='
229
+ - - ! '>'
233
230
  - !ruby/object:Gem::Version
234
- version: '0'
235
- segments:
236
- - 0
237
- hash: 2595737964624093032
231
+ version: 1.3.1
238
232
  requirements: []
239
233
  rubyforge_project: shelly
240
- rubygems_version: 1.8.11
234
+ rubygems_version: 1.8.10
241
235
  signing_key:
242
236
  specification_version: 3
243
237
  summary: Shelly Cloud command line tool