shelly 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -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