shelly 0.0.16 → 0.0.17

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,17 @@
1
+ class Hash
2
+ def deep_stringify_keys
3
+ new_hash = {}
4
+ self.each do |key, value|
5
+ new_hash.merge!(key.to_s => (value.is_a?(Hash) ? value.deep_stringify_keys : value))
6
+ end
7
+ new_hash
8
+ end
9
+
10
+ def deep_symbolize_keys
11
+ new_hash = {}
12
+ self.each do |key, value|
13
+ new_hash.merge!(key.to_sym => (value.is_a?(Hash) ? value.deep_symbolize_keys : value))
14
+ end
15
+ new_hash
16
+ end
17
+ end
data/lib/shelly.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  require "rubygems"
2
2
  require "thor"
3
3
  require "core_ext/object"
4
+ require "core_ext/hash"
4
5
  require "shelly/helpers"
5
6
  require "shelly/base"
7
+ require "thor/options"
6
8
 
7
9
  module Shelly
8
10
  autoload :App, "shelly/app"
11
+ autoload :Cloudfile, "shelly/cloudfile"
9
12
  autoload :Client, "shelly/client"
10
13
  autoload :User, "shelly/user"
11
14
  autoload :VERSION, "shelly/version"
12
15
  end
16
+
data/lib/shelly/app.rb CHANGED
@@ -4,7 +4,7 @@ require 'launchy'
4
4
  module Shelly
5
5
  class App < Base
6
6
  DATABASE_KINDS = %w(postgresql mongodb redis none)
7
- attr_accessor :purpose, :code_name, :databases, :ruby_version, :environment, :git_url, :domains
7
+ attr_accessor :code_name, :databases, :ruby_version, :environment, :git_url, :domains
8
8
 
9
9
  def initialize
10
10
  @ruby_version = "MRI-1.9.2"
@@ -12,14 +12,14 @@ module Shelly
12
12
  end
13
13
 
14
14
  def add_git_remote
15
- system("git remote rm #{purpose} &> /dev/null")
16
- system("git remote add #{purpose} #{git_url}")
15
+ system("git remote rm production &> /dev/null")
16
+ system("git remote add production #{git_url}")
17
17
  end
18
18
 
19
19
  def generate_cloudfile
20
20
  @email = current_user.email
21
21
  @databases = databases
22
- @domains = domains.nil? ? ["#{code_name}.winniecloud.com"] : domains
22
+ @domains = domains
23
23
  template = File.read(cloudfile_template_path)
24
24
  cloudfile = ERB.new(template, 0, "%<>-")
25
25
  cloudfile.result(binding)
@@ -35,10 +35,11 @@ module Shelly
35
35
  :code_name => code_name,
36
36
  :environment => environment,
37
37
  :ruby_version => ruby_version,
38
- :domain_name => "#{code_name}.shellycloud.com"
38
+ :domain_name => domains
39
39
  }
40
40
  response = shelly.create_app(attributes)
41
41
  self.git_url = response["git_url"]
42
+ self.domains = response["domain_name"].split if domains.nil?
42
43
  end
43
44
 
44
45
  def create_cloudfile
@@ -68,4 +69,3 @@ module Shelly
68
69
  end
69
70
  end
70
71
  end
71
-
@@ -8,6 +8,7 @@ module Shelly
8
8
  include Thor::Actions
9
9
  include Helpers
10
10
  register(Users, "users", "users <command>", "Manages users using this app")
11
+ check_unknown_options!
11
12
 
12
13
  map %w(-v --version) => :version
13
14
  desc "version", "Displays shelly version"
@@ -30,15 +31,16 @@ module Shelly
30
31
  say "Check you mailbox for email address confirmation"
31
32
  rescue Client::APIError => e
32
33
  if e.validation?
33
- e.errors.each do |error|
34
- say_error "#{error.first} #{error.last}", :with_exit => false
35
- end
34
+ e.each_error { |error| say_error "#{error}", :with_exit => false }
36
35
  exit 1
37
36
  end
38
- rescue RestClient::Conflict
39
- say_error "User with your ssh key already exists.", :with_exit => false
40
- say_error "You can login using: shelly login [EMAIL]", :with_exit => false
41
- exit 1
37
+ rescue RestClient::Conflict
38
+ say_error "User with your ssh key already exists.", :with_exit => false
39
+ say_error "You can login using: shelly login [EMAIL]", :with_exit => false
40
+ exit 1
41
+ rescue Errno::ENOENT => e
42
+ say_error e, :with_exit => false
43
+ say_error "Use ssh-keygen to generate ssh key pair"
42
44
  end
43
45
 
44
46
  desc "login [EMAIL]", "Logins user to Shelly Cloud"
@@ -52,40 +54,38 @@ module Shelly
52
54
  user.apps.each do |app|
53
55
  say " #{app["code_name"]}"
54
56
  end
55
- rescue RestClient::Unauthorized
56
- say_error "Wrong email or password", :with_exit => false
57
- say_error "You can reset password by using link:", :with_exit => false
58
- say_error "https://admin.winniecloud.com/users/password/new", :with_exit => false
59
- exit 1
60
57
  rescue Client::APIError => e
61
58
  if e.validation?
62
- e.errors.each { |error| say_error "#{error.first} #{error.last}", :with_exit => false }
59
+ e.each_error { |error| say_error "#{error}", :with_exit => false }
60
+ exit 1
61
+ end
62
+ if e.unauthorized?
63
+ say_error "Wrong email or password", :with_exit => false
64
+ say_error "You can reset password by using link:", :with_exit => false
65
+ say_error "#{e.url}", :with_exit => false
63
66
  exit 1
64
67
  end
65
68
  end
66
69
 
67
- method_option :code_name, :type => :string, :aliases => "-c",
70
+ method_option "code-name", :type => :string, :aliases => "-c",
68
71
  :desc => "Unique code_name of your application"
69
- method_option :environment, :type => :string, :aliases => "-e",
70
- :desc => "Environment that your application will be running"
71
72
  method_option :databases, :type => :array, :aliases => "-d",
72
73
  :banner => "#{Shelly::App::DATABASE_KINDS.join(' ')}",
73
74
  :desc => "Array of databases of your choice"
74
75
  method_option :domains, :type => :array,
75
- :banner => "CODE_NAME.shellycloud.com YOUR_DOMAIN.com",
76
+ :banner => "CODE-NAME.shellyapp.com YOUR-DOMAIN.com",
76
77
  :desc => "Array of your domains"
77
78
  desc "add", "Adds new application to Shelly Cloud"
78
79
  def add
79
80
  say_error "Must be run inside your project git repository" unless App.inside_git_repository?
80
81
  check_options(options)
81
82
  @app = Shelly::App.new
82
- @app.purpose = options["environment"] || ask_for_purpose
83
- @app.code_name = options["code_name"] || ask_for_code_name
83
+ @app.code_name = options["code-name"] || ask_for_code_name
84
84
  @app.databases = options["databases"] || ask_for_databases
85
85
  @app.domains = options["domains"]
86
86
  @app.create
87
87
 
88
- say "Adding remote #{@app.purpose} #{@app.git_url}", :green
88
+ say "Adding remote production #{@app.git_url}", :green
89
89
  @app.add_git_remote
90
90
 
91
91
  say "Creating Cloudfile", :green
@@ -96,22 +96,28 @@ module Shelly
96
96
 
97
97
  info_adding_cloudfile_to_repository
98
98
  info_deploying_to_shellycloud
99
+
99
100
  rescue Client::APIError => e
100
101
  if e.validation?
101
- e.errors.each { |error| say_error "#{error.first} #{error.last}", :with_exit => false }
102
- exit 1
102
+ e.each_error { |error| say_error "#{error}", :with_exit => false }
103
+ say_new_line
104
+ say_error "Fix erros in the below command and type it again to create your application" , :with_exit => false
105
+ say_error "shelly add --code-name=#{@app.code_name} --databases=#{@app.databases.join} --domains=#{@app.code_name}.shellyapp.com"
103
106
  end
104
107
  end
105
108
 
106
109
  # FIXME: move to helpers
107
110
  no_tasks do
108
111
  def check_options(options)
109
- unless ["environment", "code_name", "databases", "domains"].all? do |option|
110
- options.include?(option.to_s) && options[option.to_s] != option.to_s
111
- end && valid_databases?(options["databases"])
112
- say "Wrong parameters. See 'shelly help add' for further information"
113
- exit 1
114
- end unless options.empty?
112
+ unless options.empty?
113
+ options["domains"].map! {|domain| domain.gsub(",", "") } if options["domains"]
114
+ options["databases"].map! {|kind| kind.gsub(",", "") } if options["databases"]
115
+ unless ["code-name", "databases", "domains"].all? do |option|
116
+ options.include?(option.to_s) && options[option.to_s] != option.to_s
117
+ end && valid_databases?(options["databases"])
118
+ say_error "Try 'shelly help add' for more information"
119
+ end
120
+ end
115
121
  end
116
122
 
117
123
  def valid_databases?(databases)
@@ -119,14 +125,6 @@ module Shelly
119
125
  databases.all? { |kind| kinds.include?(kind) }
120
126
  end
121
127
 
122
- def ask_for_email
123
- email_question = User.guess_email.blank? ? "Email:" : "Email (#{User.guess_email} - default):"
124
- email = ask(email_question)
125
- email = email.blank? ? User.guess_email : email
126
- return email if email.present?
127
- say_error "Email can't be blank, please try again"
128
- end
129
-
130
128
  def ask_for_password(options = {})
131
129
  options = {:with_confirmation => true}.merge(options)
132
130
  loop do
@@ -146,13 +144,8 @@ module Shelly
146
144
  end
147
145
  end
148
146
 
149
- def ask_for_purpose
150
- purpose = ask("How will you use this system (production - default,staging):")
151
- purpose.blank? ? "production" : purpose
152
- end
153
-
154
147
  def ask_for_code_name
155
- default_code_name = "#{Shelly::App.guess_code_name}-#{@app.purpose}"
148
+ default_code_name = "#{Shelly::App.guess_code_name}-production"
156
149
  code_name = ask("Application code name (#{default_code_name} - default):")
157
150
  code_name.blank? ? default_code_name : code_name
158
151
  end
@@ -184,8 +177,8 @@ module Shelly
184
177
  say ' git commit -m "Application added to Shelly Cloud"'
185
178
  say " git push"
186
179
  say_new_line
187
- say "Deploy to #{@app.purpose} using:", :green
188
- say " git push #{@app.purpose} master"
180
+ say "Deploy to production using:", :green
181
+ say " git push production master"
189
182
  say_new_line
190
183
  end
191
184
  end
@@ -1,5 +1,6 @@
1
1
  require "yaml"
2
2
  require "shelly/user"
3
+ require "shelly/cloudfile"
3
4
 
4
5
  module Shelly
5
6
  module CLI
@@ -10,20 +11,29 @@ module Shelly
10
11
  desc "list", "List users who have access to current application"
11
12
  def list
12
13
  say_error "Must be run inside your project git repository" unless App.inside_git_repository?
13
- code_names = YAML.load(File.open(File.join(Dir.pwd, "Cloudfile"))).keys
14
- @app = Shelly::App.new
15
- response = @app.users(code_names.sort)
16
- response.each do |app|
17
- say "Cloud #{app['code_name']}:"
18
- app['users'].each do |user|
19
- say " #{user['email']} (#{user['name']})"
20
- end
14
+ @cloudfile = Shelly::Cloudfile.new
15
+ @cloudfile.fetch_users.each do |app, users|
16
+ say "Cloud #{app}:"
17
+ users.each { |user| say " #{user}" }
21
18
  end
22
19
  rescue Client::APIError => e
23
20
  say e.message
24
21
  exit 1
25
22
  end
26
23
 
24
+ desc "add [EMAIL]", "Add new developer to applications defined in Cloudfile"
25
+ def add(email = nil)
26
+ say_error "Must be run inside your project git repository" unless App.inside_git_repository?
27
+ user_email = email || ask_for_email({:guess_email => false})
28
+ @cloudfile = Shelly::Cloudfile.new
29
+ @user = Shelly::User.new
30
+ @user.send_invitation(@cloudfile.clouds, user_email)
31
+ say "Sending invitation to #{user_email}"
32
+ rescue Client::APIError => e
33
+ say e.message
34
+ exit 1
35
+ end
36
+
27
37
  end
28
38
  end
29
39
  end
data/lib/shelly/client.rb CHANGED
@@ -16,9 +16,23 @@ module Shelly
16
16
  @response["errors"]
17
17
  end
18
18
 
19
+ def url
20
+ @response["url"]
21
+ end
22
+
19
23
  def validation?
20
24
  message == "Validation Failed"
21
25
  end
26
+
27
+ def unauthorized?
28
+ message == "Unauthorized"
29
+ end
30
+
31
+ def each_error
32
+ @response["errors"].each do |index,message|
33
+ yield index.gsub('_',' ').capitalize + " " + message
34
+ end
35
+ end
22
36
  end
23
37
 
24
38
  def initialize(email = nil, password = nil)
@@ -42,6 +56,12 @@ module Shelly
42
56
  get("/token")
43
57
  end
44
58
 
59
+ def send_invitation(apps, email)
60
+ apps.map do |app|
61
+ post("/apps/#{app}/collaborations", :email => email)
62
+ end
63
+ end
64
+
45
65
  def create_app(attributes)
46
66
  post("/apps", :app => attributes)
47
67
  end
@@ -102,7 +122,7 @@ module Shelly
102
122
  end
103
123
 
104
124
  def process_response(response)
105
- if [404, 422, 500].include?(response.code)
125
+ if [401, 404, 422, 500].include?(response.code)
106
126
  raise APIError.new(response.body)
107
127
  end
108
128
 
@@ -0,0 +1,45 @@
1
+ module Shelly
2
+ class Cloudfile < Base
3
+ attr_accessor :content
4
+
5
+ def initialize
6
+ open if File.exists?(path)
7
+ end
8
+
9
+ def path
10
+ File.join(Dir.pwd, "Cloudfile")
11
+ end
12
+
13
+ def open
14
+ @content = YAML.load(File.open(path))
15
+ end
16
+
17
+ def write(hash)
18
+ @content = hash
19
+ File.open(path, "w") do |f|
20
+ f.write(yaml(hash))
21
+ end
22
+ end
23
+
24
+ def clouds
25
+ @content.keys.sort
26
+ end
27
+
28
+ def yaml(hash)
29
+ string = hash.deep_stringify_keys.to_yaml
30
+ # FIXME: check if it possible to remove sub("---", "") by passing options to_yaml
31
+ string.sub("---","").split("\n").map(&:rstrip).join("\n").strip
32
+ end
33
+
34
+ def fetch_users
35
+ response = shelly.app_users(clouds)
36
+ response.inject({}) do |result, app|
37
+ result[app['code_name']] = app['users'].map do |user|
38
+ "#{user['email']} (#{user['name']})"
39
+ end
40
+ result
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -16,5 +16,16 @@ module Shelly
16
16
  say message, :red
17
17
  exit 1 if options[:with_exit]
18
18
  end
19
+
20
+ def ask_for_email(options = {})
21
+ options = {:guess_email => true}.merge(options)
22
+ email_question = options[:guess_email] && !User.guess_email.blank? ? "Email (#{User.guess_email} - default):" : "Email:"
23
+ email = ask(email_question)
24
+ email = email.blank? ? User.guess_email : email
25
+ return email if email.present?
26
+ say_error "Email can't be blank, please try again"
27
+ end
28
+
19
29
  end
20
30
  end
31
+
data/lib/shelly/user.rb CHANGED
@@ -29,6 +29,10 @@ module Shelly
29
29
  shelly.token["token"]
30
30
  end
31
31
 
32
+ def send_invitation(apps, email)
33
+ shelly.send_invitation(apps, email)
34
+ end
35
+
32
36
  def load_credentials
33
37
  return unless credentials_exists?
34
38
  @email, @password = File.read(credentials_path).split("\n")
@@ -47,7 +51,7 @@ module Shelly
47
51
  def ssh_key_path
48
52
  File.expand_path("~/.ssh/id_rsa.pub")
49
53
  end
50
-
54
+
51
55
  def ssh_key_registered?
52
56
  ssh_key = File.read(ssh_key_path).strip
53
57
  shelly.ssh_key_available?(ssh_key)
@@ -81,3 +85,4 @@ module Shelly
81
85
  end
82
86
  end
83
87
  end
88
+
@@ -1,3 +1,3 @@
1
1
  module Shelly
2
- VERSION = "0.0.16"
2
+ VERSION = "0.0.17"
3
3
  end
@@ -0,0 +1,12 @@
1
+ class Thor
2
+ class Options < Arguments
3
+
4
+ def check_unknown!
5
+ raise UnknownArgumentError, "shelly: unrecognized option '#{@unknown.join(', ')}'\n" +
6
+ "Usage: shelly [COMMAND]... [OPTIONS]\n" +
7
+ "Try 'shelly --help' for more information" unless @unknown.empty?
8
+ end
9
+
10
+ end
11
+ end
12
+
@@ -8,7 +8,6 @@ describe Shelly::App do
8
8
  @client = mock(:api_url => "https://api.example.com", :shellyapp_url => "http://shellyapp.example.com")
9
9
  Shelly::Client.stub(:new).and_return(@client)
10
10
  @app = Shelly::App.new
11
- @app.purpose = "staging"
12
11
  @app.code_name = "foo-staging"
13
12
  end
14
13
 
@@ -42,12 +41,12 @@ describe Shelly::App do
42
41
  end
43
42
 
44
43
  it "should try to remove existing git remote" do
45
- @app.should_receive(:system).with("git remote rm staging &> /dev/null")
44
+ @app.should_receive(:system).with("git remote rm production &> /dev/null")
46
45
  @app.add_git_remote
47
46
  end
48
47
 
49
48
  it "should add git remote with proper name and git repository" do
50
- @app.should_receive(:system).with("git remote add staging git@git.shellycloud.com:foo-staging.git")
49
+ @app.should_receive(:system).with("git remote add production git@git.shellycloud.com:foo-staging.git")
51
50
  @app.add_git_remote
52
51
  end
53
52
  end
@@ -122,25 +121,52 @@ config
122
121
  end
123
122
 
124
123
  describe "#create" do
125
- it "should create the app on shelly cloud via API client" do
126
- @app.purpose = "dev"
127
- @app.code_name = "fooo"
128
- attributes = {
129
- :code_name => "fooo",
130
- :name => "fooo",
131
- :environment => "production",
132
- :ruby_version => "MRI-1.9.2",
133
- :domain_name => "fooo.shellycloud.com"
134
- }
135
- @client.should_receive(:create_app).with(attributes).and_return("git_url" => "git@git.shellycloud.com:fooo.git")
136
- @app.create
124
+ context "without providing domain" do
125
+ it "should create the app on shelly cloud via API client" do
126
+ @app.code_name = "fooo"
127
+ attributes = {
128
+ :code_name => "fooo",
129
+ :name => "fooo",
130
+ :environment => "production",
131
+ :ruby_version => "MRI-1.9.2",
132
+ :domain_name => nil
133
+ }
134
+ @client.should_receive(:create_app).with(attributes).and_return("git_url" => "git@git.shellycloud.com:fooo.git",
135
+ "domain_name" => "fooo.shellyapp.com")
136
+ @app.create
137
+ end
138
+
139
+ it "should assign returned git_url and domain" do
140
+ @client.stub(:create_app).and_return("git_url" => "git@git.example.com:fooo.git",
141
+ "domain_name" => "fooo.shellyapp.com")
142
+ @app.create
143
+ @app.git_url.should == "git@git.example.com:fooo.git"
144
+ @app.domains.should == ["fooo.shellyapp.com"]
145
+ end
137
146
  end
138
147
 
139
- it "should assign returned git_url" do
140
- @client.stub(:create_app).and_return("git_url" => "git@git.example.com:fooo.git")
141
- @app.create
142
- @app.git_url.should == "git@git.example.com:fooo.git"
148
+ context "with providing domain" do
149
+ it "should create the app on shelly cloud via API client" do
150
+ @app.code_name = "boo"
151
+ @app.domains = ["boo.shellyapp.com", "boo.example.com"]
152
+ attributes = {
153
+ :code_name => "boo",
154
+ :name => "boo",
155
+ :environment => "production",
156
+ :ruby_version => "MRI-1.9.2",
157
+ :domain_name => ["boo.shellyapp.com", "boo.example.com"]
158
+ }
159
+ @client.should_receive(:create_app).with(attributes).and_return("git_url" => "git@git.shellycloud.com:fooo.git",
160
+ "domain_name" => "boo.shellyapp.com boo.example.com")
161
+ @app.create
162
+ end
163
+
164
+ it "should assign returned git_url and domain" do
165
+ @client.stub(:create_app).and_return("git_url" => "git@git.example.com:fooo.git",
166
+ "domain_name" => "boo.shellyapp.com boo.example.com")
167
+ @app.create
168
+ @app.domains.should == ["boo.shellyapp.com", "boo.example.com"]
169
+ end
143
170
  end
144
171
  end
145
172
  end
146
-
@@ -3,7 +3,6 @@ require "shelly/cli/main"
3
3
 
4
4
  describe Shelly::CLI::Main do
5
5
  before do
6
- ENV['SHELLY_GIT_HOST'] = nil
7
6
  FileUtils.stub(:chmod)
8
7
  @main = Shelly::CLI::Main.new
9
8
  @client = mock
@@ -45,6 +44,16 @@ OUT
45
44
  Shelly::User.stub(:new).and_return(@user)
46
45
  end
47
46
 
47
+ it "should return false if ssh key don't exist on local hard drive" do
48
+ @user.stub(:ssh_key_registered?).and_raise(Errno::ENOENT)
49
+ File.exists?(File.expand_path("~/.ssh/id_rsa.pub")).should be_false
50
+ $stdout.should_receive(:puts).with("\e[31mNo such file or directory\e[0m")
51
+ $stdout.should_receive(:puts).with("\e[31mUse ssh-keygen to generate ssh key pair\e[0m")
52
+ lambda {
53
+ @main.register
54
+ }.should raise_error(SystemExit)
55
+ end
56
+
48
57
  it "should check ssh key in database" do
49
58
  @user.stub(:ssh_key_registered?).and_raise(RestClient::Conflict)
50
59
  $stdout.should_receive(:puts).with("\e[31mUser with your ssh key already exists.\e[0m")
@@ -134,7 +143,7 @@ OUT
134
143
  response = {"message" => "Validation Failed", "errors" => [["email", "has been already taken"]]}
135
144
  exception = Shelly::Client::APIError.new(response.to_json)
136
145
  @client.stub(:register_user).and_raise(exception)
137
- $stdout.should_receive(:puts).with("\e[31memail has been already taken\e[0m")
146
+ $stdout.should_receive(:puts).with("\e[31mEmail has been already taken\e[0m")
138
147
  lambda {
139
148
  fake_stdin(["kate@example.com", "pass", "pass"]) do
140
149
  @main.register
@@ -187,7 +196,8 @@ OUT
187
196
 
188
197
  context "on unauthorized user" do
189
198
  it "should exit with 1 and display error message" do
190
- exception = RestClient::Unauthorized.new
199
+ response = {"message" => "Unauthorized", "url" => "https://admin.winniecloud.com/users/password/new"}
200
+ exception = Shelly::Client::APIError.new(response.to_json)
191
201
  @client.stub(:token).and_raise(exception)
192
202
  $stdout.should_receive(:puts).with("\e[31mWrong email or password\e[0m")
193
203
  $stdout.should_receive(:puts).with("\e[31mYou can reset password by using link:\e[0m")
@@ -219,68 +229,67 @@ OUT
219
229
  Shelly::App.stub(:inside_git_repository?).and_return(false)
220
230
  $stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
221
231
  lambda {
222
- fake_stdin(["staging", "", ""]) do
232
+ fake_stdin(["", ""]) do
223
233
  @main.add
224
234
  end
225
235
  }.should raise_error(SystemExit)
226
236
  end
227
237
 
228
- it "should ask user how he will use application" do
229
- $stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
230
- @app.should_receive(:purpose=).with("staging")
231
- fake_stdin(["staging", "", ""]) do
232
- @main.add
233
- end
234
- end
235
-
236
238
  context "command line options" do
237
239
  context "invalid params" do
238
240
  it "should show help and exit if not all options are passed" do
239
- $stdout.should_receive(:puts).with("Wrong parameters. See 'shelly help add' for further information")
240
- @main.options = {"code_name" => "foo"}
241
+ $stdout.should_receive(:puts).with("\e[31mTry 'shelly help add' for more information\e[0m")
242
+ @main.options = {"code-name" => "foo"}
241
243
  lambda { @main.add }.should raise_error(SystemExit)
242
244
  end
243
245
 
244
246
  it "should exit if databases are not valid" do
245
- $stdout.should_receive(:puts).with("Wrong parameters. See 'shelly help add' for further information")
246
- @main.options = {"code_name" => "foo", "environment" => "production", "databases" => ["not existing"], "domains" => ["foo.example.com"]}
247
+ $stdout.should_receive(:puts).with("\e[31mTry 'shelly help add' for more information\e[0m")
248
+ @main.options = {"code-name" => "foo", "databases" => ["not existing"], "domains" => ["foo.example.com"]}
247
249
  lambda { @main.add }.should raise_error(SystemExit)
248
250
  end
249
- end
250
251
 
251
- context "valid params" do
252
- it "should create app on shelly cloud" do
253
- @app.should_receive(:create)
254
- @main.options = {"code_name" => "foo", "environment" => "production", "databases" => ["postgresql"], "domains" => ["foo.example.com"]}
252
+ it "should accept databases separated by comma" do
253
+ @main.options = {"code-name" => "foo", "databases" => ["postgresql,", "mongodb"], "domains" => ["foo.example.com"]}
254
+ @app.should_receive(:databases=).with(["postgresql", "mongodb"])
255
255
  @main.add
256
256
  end
257
+
258
+ it "should display which parameter was wrong" do
259
+ expected = "shelly: unrecognized option '--unknown=param'\n" +
260
+ "Usage: shelly [COMMAND]... [OPTIONS]\n" +
261
+ "Try 'shelly --help' for more information"
262
+
263
+ Open3.popen3("bin/shelly add --unknown=param") do |stdin, stdout, stderr, wait_thr|
264
+ out = stderr.read.strip
265
+ out.should == expected
266
+ end
267
+ end
268
+
257
269
  end
258
- end
259
270
 
260
- context "when user provided empty purpose" do
261
- it "should use 'production' as default" do
262
- $stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
263
- @app.should_receive(:purpose=).with("production")
264
- fake_stdin(["", "", ""]) do
271
+ context "valid params" do
272
+ it "should create app on shelly cloud" do
273
+ @app.should_receive(:create)
274
+ @main.options = {"code-name" => "foo", "databases" => ["postgresql"], "domains" => ["foo.example.com"]}
265
275
  @main.add
266
276
  end
267
277
  end
268
278
  end
269
279
 
270
280
  it "should use code name provided by user" do
271
- $stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
272
- $stdout.should_receive(:print).with("Application code name (foo-staging - default): ")
281
+ $stdout.should_receive(:print).with("Application code name (foo-production - default): ")
273
282
  @app.should_receive(:code_name=).with("mycodename")
274
- fake_stdin(["staging", "mycodename", ""]) do
283
+ fake_stdin(["mycodename", ""]) do
275
284
  @main.add
276
285
  end
277
286
  end
278
287
 
279
288
  context "when user provided empty code name" do
280
289
  it "should use 'current_dirname-purpose' as default" do
281
- $stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
282
- $stdout.should_receive(:print).with("Application code name (foo-staging - default): ")
283
- fake_stdin(["staging", "", ""]) do
290
+ $stdout.should_receive(:print).with("Application code name (foo-production - default): ")
291
+ @app.should_receive(:code_name=).with("foo-production")
292
+ fake_stdin(["", ""]) do
284
293
  @main.add
285
294
  end
286
295
  end
@@ -289,7 +298,7 @@ OUT
289
298
  it "should use database provided by user (separated by comma or space)" do
290
299
  $stdout.should_receive(:print).with("Which database do you want to use postgresql, mongodb, redis, none (postgresql - default): ")
291
300
  @app.should_receive(:databases=).with(["postgresql", "mongodb", "redis"])
292
- fake_stdin(["staging", "", "postgresql ,mongodb redis"]) do
301
+ fake_stdin(["", "postgresql ,mongodb redis"]) do
293
302
  @main.add
294
303
  end
295
304
  end
@@ -297,7 +306,7 @@ OUT
297
306
  it "should ask again for databases if unsupported kind typed" do
298
307
  $stdout.should_receive(:print).with("Which database do you want to use postgresql, mongodb, redis, none (postgresql - default): ")
299
308
  $stdout.should_receive(:print).with("Unknown database kind. Supported are: postgresql, mongodb, redis, none: ")
300
- fake_stdin(["staging", "", "postgresql,doesnt-exist", "none"]) do
309
+ fake_stdin(["", "postgresql,doesnt-exist", "none"]) do
301
310
  @main.add
302
311
  end
303
312
  end
@@ -305,7 +314,7 @@ OUT
305
314
  context "when user provided empty database" do
306
315
  it "should use 'postgresql' database as default" do
307
316
  @app.should_receive(:databases=).with(["postgresql"])
308
- fake_stdin(["staging", "", ""]) do
317
+ fake_stdin(["", ""]) do
309
318
  @main.add
310
319
  end
311
320
  end
@@ -313,7 +322,7 @@ OUT
313
322
 
314
323
  it "should create the app on shelly cloud" do
315
324
  @app.should_receive(:create)
316
- fake_stdin(["", "", ""]) do
325
+ fake_stdin(["", ""]) do
317
326
  @main.add
318
327
  end
319
328
  end
@@ -322,25 +331,27 @@ OUT
322
331
  response = {"message" => "Validation Failed", "errors" => [["code_name", "has been already taken"]]}
323
332
  exception = Shelly::Client::APIError.new(response.to_json)
324
333
  @app.should_receive(:create).and_raise(exception)
325
- $stdout.should_receive(:puts).with("\e[31mcode_name has been already taken\e[0m")
334
+ $stdout.should_receive(:puts).with("\e[31mCode name has been already taken\e[0m")
335
+ $stdout.should_receive(:puts).with("\e[31mFix erros in the below command and type it again to create your application\e[0m")
336
+ $stdout.should_receive(:puts).with("\e[31mshelly add --code-name=foo-production --databases=postgresql --domains=foo-production.shellyapp.com\e[0m")
326
337
  lambda {
327
- fake_stdin(["", "", ""]) do
338
+ fake_stdin(["", ""]) do
328
339
  @main.add
329
340
  end
330
341
  }.should raise_error(SystemExit)
331
342
  end
332
343
 
333
344
  it "should add git remote" do
334
- $stdout.should_receive(:puts).with("\e[32mAdding remote staging git@git.shellycloud.com:foooo.git\e[0m")
345
+ $stdout.should_receive(:puts).with("\e[32mAdding remote production git@git.shellycloud.com:foooo.git\e[0m")
335
346
  @app.should_receive(:add_git_remote)
336
- fake_stdin(["staging", "foooo", ""]) do
347
+ fake_stdin(["foooo", ""]) do
337
348
  @main.add
338
349
  end
339
350
  end
340
351
 
341
352
  it "should create Cloudfile" do
342
353
  File.exists?("/projects/foo/Cloudfile").should be_false
343
- fake_stdin(["staging", "foooo", ""]) do
354
+ fake_stdin(["foooo", ""]) do
344
355
  @main.add
345
356
  end
346
357
  File.read("/projects/foo/Cloudfile").should == "Example Cloudfile"
@@ -349,7 +360,7 @@ OUT
349
360
  it "should browser window with link to edit billing information" do
350
361
  $stdout.should_receive(:puts).with("\e[32mProvide billing details. Opening browser...\e[0m")
351
362
  @app.should_receive(:open_billing_page)
352
- fake_stdin(["staging", "foooo", ""]) do
363
+ fake_stdin(["foooo", ""]) do
353
364
  @main.add
354
365
  end
355
366
  end
@@ -358,7 +369,7 @@ OUT
358
369
  $stdout.should_receive(:puts).with("\e[32mProject is now configured for use with Shell Cloud:\e[0m")
359
370
  $stdout.should_receive(:puts).with("\e[32mYou can review changes using\e[0m")
360
371
  $stdout.should_receive(:puts).with(" git status")
361
- fake_stdin(["staging", "foooo", "none"]) do
372
+ fake_stdin(["foooo", "none"]) do
362
373
  @main.add
363
374
  end
364
375
  end
@@ -368,9 +379,9 @@ OUT
368
379
  $stdout.should_receive(:puts).with(" git add .")
369
380
  $stdout.should_receive(:puts).with(' git commit -m "Application added to Shelly Cloud"')
370
381
  $stdout.should_receive(:puts).with(" git push")
371
- $stdout.should_receive(:puts).with("\e[32mDeploy to staging using:\e[0m")
372
- $stdout.should_receive(:puts).with(" git push staging master")
373
- fake_stdin(["staging", "foooo", "none"]) do
382
+ $stdout.should_receive(:puts).with("\e[32mDeploy to production using:\e[0m")
383
+ $stdout.should_receive(:puts).with(" git push production master")
384
+ fake_stdin(["foooo", "none"]) do
374
385
  @main.add
375
386
  end
376
387
  end
@@ -12,17 +12,25 @@ describe Shelly::CLI::Users do
12
12
  $stdout.stub(:print)
13
13
  end
14
14
 
15
+ describe "#help" do
16
+ it "should show help" do
17
+ $stdout.should_receive(:puts).with("Tasks:")
18
+ $stdout.should_receive(:puts).with(/add \[EMAIL\]\s+# Add new developer to applications defined in Cloudfile/)
19
+ $stdout.should_receive(:puts).with(/list\s+# List users who have access to current application/)
20
+ @users.help
21
+ end
22
+ end
23
+
15
24
  describe "#list" do
16
25
  before do
17
26
  FileUtils.mkdir_p("/projects/foo")
18
27
  Dir.chdir("/projects/foo")
19
28
  File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
20
- @app = mock
21
- Shelly::App.stub(:new).and_return(@app)
22
29
  Shelly::App.stub(:inside_git_repository?).and_return(true)
23
30
  end
24
31
 
25
32
  it "should exit with message if command run outside git repository" do
33
+ @client.stub(:app_users).and_return(response)
26
34
  Shelly::App.stub(:inside_git_repository?).and_return(false)
27
35
  $stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
28
36
  lambda {
@@ -32,35 +40,79 @@ describe Shelly::CLI::Users do
32
40
 
33
41
  context "on success" do
34
42
  it "should receive clouds from the Cloudfile" do
35
- @app.should_receive(:users).with(["foo-production","foo-staging"]).
43
+ @client.should_receive(:app_users).with(["foo-production", "foo-staging"]).
36
44
  and_return(response)
37
45
  @users.list
38
46
  end
39
47
 
40
48
  it "should display clouds and users" do
41
- @app.stub(:users).and_return(response)
49
+ @client.stub(:app_users).and_return(response)
42
50
  $stdout.should_receive(:puts).with("Cloud foo-staging:")
43
51
  $stdout.should_receive(:puts).with(" user@example.com (username)")
44
52
  $stdout.should_receive(:puts).with("Cloud foo-production:")
45
53
  $stdout.should_receive(:puts).with(" user2@example.com (username2)")
46
54
  @users.list
47
55
  end
56
+ end
48
57
 
49
- def response
50
- [{'code_name' => 'foo-staging','users' => [{'name' => 'username','email' => 'user@example.com'}]},
51
- {'code_name' => 'foo-production','users' => [{'name' => 'username2','email' => 'user2@example.com'}]}]
52
- end
58
+ def response
59
+ [{'code_name' => 'foo-staging','users' => [{'name' => 'username','email' => 'user@example.com'}]},
60
+ {'code_name' => 'foo-production','users' => [{'name' => 'username2','email' => 'user2@example.com'}]}]
53
61
  end
54
62
 
55
63
  context "on failure" do
56
- it "should raise an error if user does not have to any app" do
64
+ it "should raise an error if user does not have access to any app" do
57
65
  response = {"message" => "You do not have access to this app"}
58
66
  exception = Shelly::Client::APIError.new(response.to_json)
59
- @app.stub(:users).and_raise(exception)
67
+ @client.stub(:app_users).and_raise(exception)
60
68
  $stdout.should_receive(:puts).with("You do not have access to this app")
61
69
  lambda { @users.list }.should raise_error(SystemExit)
62
70
  end
63
71
  end
64
72
  end
73
+
74
+ describe "#add" do
75
+ before do
76
+ FileUtils.mkdir_p("/projects/foo")
77
+ Dir.chdir("/projects/foo")
78
+ File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
79
+ Shelly::App.stub(:inside_git_repository?).and_return(true)
80
+ @client.stub(:token).and_return("abc")
81
+ @user = Shelly::User.new
82
+ @client.stub(:apps).and_return([{"code_name" => "abc"}, {"code_name" => "fooo"}])
83
+ Shelly::User.stub(:new).and_return(@user)
84
+ end
85
+
86
+ it "should exit with message if command run outside git repository" do
87
+ Shelly::App.stub(:inside_git_repository?).and_return(false)
88
+ $stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
89
+ lambda {
90
+ @users.add
91
+ }.should raise_error(SystemExit)
92
+ end
93
+
94
+ context "on success" do
95
+ before do
96
+ @client.should_receive(:send_invitation).with(["foo-production", "foo-staging"], "megan@example.com")
97
+ end
98
+
99
+ it "should ask about email" do
100
+ fake_stdin(["megan@example.com"]) do
101
+ @users.add
102
+ end
103
+ end
104
+
105
+ it "should receive clouds from the Cloudfile" do
106
+ @users.add("megan@example.com")
107
+ end
108
+
109
+ it "should receive clouds from the Cloudfile" do
110
+ $stdout.should_receive(:puts).with("Sending invitation to megan@example.com")
111
+ @users.add("megan@example.com")
112
+ end
113
+ end
114
+
115
+ end
116
+
65
117
  end
66
118
 
@@ -2,7 +2,7 @@ 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"}]}
5
+ body = {"message" => "something went wrong", "errors" => [["first", "foo"]], "url" => "https://foo.bar"}
6
6
  @error = Shelly::Client::APIError.new(body.to_json)
7
7
  end
8
8
 
@@ -10,8 +10,16 @@ describe Shelly::Client::APIError do
10
10
  @error.message.should == "something went wrong"
11
11
  end
12
12
 
13
- it "should return list of errors" do
14
- @error.errors.should == [{"first" => "foo"}]
13
+ it "should return array of errors" do
14
+ @error.errors.should == [["first", "foo"]]
15
+ end
16
+
17
+ it "should return url" do
18
+ @error.url.should == "https://foo.bar"
19
+ end
20
+
21
+ it "should return user friendly string" do
22
+ @error.each_error{|error| error.should == "First foo"}
15
23
  end
16
24
 
17
25
  describe "#validation?" do
@@ -29,6 +37,22 @@ describe Shelly::Client::APIError do
29
37
  end
30
38
  end
31
39
  end
40
+
41
+ describe "#unauthorized?" do
42
+ context "when error is caused by unauthorized error" do
43
+ it "should return true" do
44
+ body = {"message" => "Unauthorized"}
45
+ error = Shelly::Client::APIError.new(body.to_json)
46
+ error.should be_unauthorized
47
+ end
48
+ end
49
+
50
+ context "when error is not caused by unauthorized" do
51
+ it "should return false" do
52
+ @error.should_not be_unauthorized
53
+ end
54
+ end
55
+ end
32
56
  end
33
57
 
34
58
  describe Shelly::Client do
@@ -93,6 +117,15 @@ describe Shelly::Client do
93
117
  end
94
118
  end
95
119
 
120
+ describe "#send_invitation" do
121
+ it "should send post with developer's email" do
122
+ FakeWeb.register_uri(:post, @url + "/apps/staging-foo/collaborations", :body => {}.to_json)
123
+ FakeWeb.register_uri(:post, @url + "/apps/production-foo/collaborations", :body => {}.to_json)
124
+ response = @client.send_invitation(["staging-foo", "production-foo"], "megan@example.com")
125
+ response.should == [{}, {}]
126
+ end
127
+ end
128
+
96
129
  describe "#update_ssh_key" do
97
130
  it "should send put with give SSH key" do
98
131
  @client.should_receive(:put).with("/ssh_key", {:ssh_key => "abc"})
@@ -169,7 +202,7 @@ describe Shelly::Client do
169
202
  @client.get('/account')
170
203
  end
171
204
 
172
- %w(404 422 500).each do |code|
205
+ %w(401, 404 422 500).each do |code|
173
206
  context "on #{code} response code" do
174
207
  it "should raise APIError" do
175
208
  @response.stub(:code).and_return(code.to_i)
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+ require "shelly/cloudfile"
3
+
4
+ describe Shelly::Cloudfile do
5
+ before do
6
+ FileUtils.mkdir_p("/projects/foo")
7
+ Dir.chdir("/projects/foo")
8
+ @hash = {:code_name => {:code => "test"}}
9
+ @client = mock
10
+ Shelly::Client.stub(:new).and_return(@client)
11
+ @cloudfile = Shelly::Cloudfile.new
12
+ end
13
+
14
+ describe "#hash converting" do
15
+ it "should convert hash to proper string" do
16
+ @cloudfile.yaml(@hash).should == "code_name:\n code: test"
17
+ end
18
+
19
+ it "should conver a hash to yaml format" do
20
+ @cloudfile.write(@hash)
21
+ @cloudfile.open.should == {"code_name" => {"code" => "test"}}
22
+ end
23
+ end
24
+
25
+ describe "#fetch_users" do
26
+ it "should return array to display with clouds and users" do
27
+ @cloudfile.write(@hash)
28
+ @client.should_receive(:app_users).and_return(response)
29
+ response = @cloudfile.fetch_users
30
+ response.should == {"foo-staging" => ["user@example.com (username)"]}
31
+ end
32
+
33
+ def response
34
+ [{'code_name' => 'foo-staging','users' => [{'name' => 'username','email' => 'user@example.com'}]}]
35
+ end
36
+ end
37
+
38
+ end
@@ -92,6 +92,13 @@ describe Shelly::User do
92
92
  end
93
93
  end
94
94
 
95
+ describe "#send_invitation" do
96
+ it "should send invitation" do
97
+ @client.should_receive(:send_invitation).with(["foo-staging"], "megan@example.com")
98
+ @user.send_invitation(["foo-staging"], "megan@example.com")
99
+ end
100
+ end
101
+
95
102
  describe "#ssh_key_path" do
96
103
  it "should return path to public ssh key file" do
97
104
  @user.ssh_key_path.should == File.expand_path("~/.ssh/id_rsa.pub")
@@ -105,7 +112,7 @@ describe Shelly::User do
105
112
  @user.should_not be_ssh_key_exists
106
113
  end
107
114
  end
108
-
115
+
109
116
  describe "#ssh_key_registered?" do
110
117
  it "should read and check if ssh key exists in database" do
111
118
  @client.should_receive(:ssh_key_available?).with('ssh-key AAbbcc')
@@ -155,3 +162,4 @@ describe Shelly::User do
155
162
  end
156
163
  end
157
164
  end
165
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shelly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.16
4
+ version: 0.0.17
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-25 00:00:00.000000000Z
12
+ date: 2011-11-03 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2153427120 !ruby/object:Gem::Requirement
16
+ requirement: &2156677640 !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: *2153427120
24
+ version_requirements: *2156677640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2153426700 !ruby/object:Gem::Requirement
27
+ requirement: &2156677220 !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: *2153426700
35
+ version_requirements: *2156677220
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: guard
38
- requirement: &2153426280 !ruby/object:Gem::Requirement
38
+ requirement: &2156676800 !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: *2153426280
46
+ version_requirements: *2156676800
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: guard-rspec
49
- requirement: &2153425840 !ruby/object:Gem::Requirement
49
+ requirement: &2156676380 !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: *2153425840
57
+ version_requirements: *2156676380
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: growl_notify
60
- requirement: &2153425360 !ruby/object:Gem::Requirement
60
+ requirement: &2156675920 !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: *2153425360
68
+ version_requirements: *2156675920
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rb-fsevent
71
- requirement: &2153424920 !ruby/object:Gem::Requirement
71
+ requirement: &2156675500 !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: *2153424920
79
+ version_requirements: *2156675500
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: fakefs
82
- requirement: &2153424500 !ruby/object:Gem::Requirement
82
+ requirement: &2156675080 !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: *2153424500
90
+ version_requirements: *2156675080
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: fakeweb
93
- requirement: &2153424080 !ruby/object:Gem::Requirement
93
+ requirement: &2156674660 !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: *2153424080
101
+ version_requirements: *2156674660
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: thor
104
- requirement: &2153423660 !ruby/object:Gem::Requirement
104
+ requirement: &2156674240 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *2153423660
112
+ version_requirements: *2156674240
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: rest-client
115
- requirement: &2153423240 !ruby/object:Gem::Requirement
115
+ requirement: &2156673780 !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: *2153423240
123
+ version_requirements: *2156673780
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: json
126
- requirement: &2153422820 !ruby/object:Gem::Requirement
126
+ requirement: &2156673340 !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: *2153422820
134
+ version_requirements: *2156673340
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: launchy
137
- requirement: &2153422400 !ruby/object:Gem::Requirement
137
+ requirement: &2156672840 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,7 +142,7 @@ dependencies:
142
142
  version: '0'
143
143
  type: :runtime
144
144
  prerelease: false
145
- version_requirements: *2153422400
145
+ version_requirements: *2156672840
146
146
  description: Tool for managing applications and clouds at shellycloud.com
147
147
  email:
148
148
  - support@shellycloud.com
@@ -158,6 +158,7 @@ files:
158
158
  - README.md
159
159
  - Rakefile
160
160
  - bin/shelly
161
+ - lib/core_ext/hash.rb
161
162
  - lib/core_ext/object.rb
162
163
  - lib/shelly.rb
163
164
  - lib/shelly/app.rb
@@ -165,10 +166,12 @@ files:
165
166
  - lib/shelly/cli/main.rb
166
167
  - lib/shelly/cli/users.rb
167
168
  - lib/shelly/client.rb
169
+ - lib/shelly/cloudfile.rb
168
170
  - lib/shelly/helpers.rb
169
171
  - lib/shelly/templates/Cloudfile.erb
170
172
  - lib/shelly/user.rb
171
173
  - lib/shelly/version.rb
174
+ - lib/thor/options.rb
172
175
  - shelly.gemspec
173
176
  - spec/helpers.rb
174
177
  - spec/input_faker.rb
@@ -177,6 +180,7 @@ files:
177
180
  - spec/shelly/cli/main_spec.rb
178
181
  - spec/shelly/cli/users_spec.rb
179
182
  - spec/shelly/client_spec.rb
183
+ - spec/shelly/cloudfile_spec.rb
180
184
  - spec/shelly/user_spec.rb
181
185
  - spec/spec_helper.rb
182
186
  homepage: http://shellycloud.com
@@ -211,5 +215,6 @@ test_files:
211
215
  - spec/shelly/cli/main_spec.rb
212
216
  - spec/shelly/cli/users_spec.rb
213
217
  - spec/shelly/client_spec.rb
218
+ - spec/shelly/cloudfile_spec.rb
214
219
  - spec/shelly/user_spec.rb
215
220
  - spec/spec_helper.rb