winnie 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +11 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/bin/winnie +8 -0
- data/lib/winnie/client.rb +86 -0
- data/lib/winnie/command.rb +38 -0
- data/lib/winnie/commands/app.rb +28 -0
- data/lib/winnie/commands/auth.rb +49 -0
- data/lib/winnie/commands/base.rb +39 -0
- data/lib/winnie/commands/help.rb +12 -0
- data/lib/winnie/helpers.rb +36 -0
- data/lib/winnie/winnie.rb +11 -0
- data/spec/client_spec.rb +142 -0
- data/spec/command_spec.rb +68 -0
- data/spec/commands/app_spec.rb +56 -0
- data/spec/commands/auth_spec.rb +92 -0
- data/spec/commands/base_spec.rb +16 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/winnie_spec.rb +1 -0
- data/winnie.gemspec +84 -0
- metadata +154 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Ragnarson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "winnie"
|
8
|
+
gem.summary = %Q{Winnie command line tool}
|
9
|
+
gem.description = %Q{Command line tool which allows interacting with winnie's API}
|
10
|
+
gem.email = "winnie-devs@ragnarson.com"
|
11
|
+
gem.homepage = "http://winniecloud.net"
|
12
|
+
gem.authors = ["winnie"]
|
13
|
+
gem.add_dependency "json"
|
14
|
+
gem.add_dependency "rest-client"
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_development_dependency "fakefs"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
25
|
+
spec.libs << 'lib' << 'spec'
|
26
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
32
|
+
spec.rcov = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :spec => :check_dependencies
|
36
|
+
|
37
|
+
task :default => :spec
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "winnie #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/winnie
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Winnie
|
2
|
+
class Client
|
3
|
+
def initialize(api_key)
|
4
|
+
@api_key = api_key
|
5
|
+
end
|
6
|
+
|
7
|
+
def host
|
8
|
+
ENV['WINNIE_HOST'] || 'admin.winniecloud.net'
|
9
|
+
end
|
10
|
+
|
11
|
+
def winnie_url
|
12
|
+
"http://#{host}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def account
|
16
|
+
get('/account')
|
17
|
+
end
|
18
|
+
|
19
|
+
def apps
|
20
|
+
get('/apps')
|
21
|
+
end
|
22
|
+
|
23
|
+
def command(command, code_name)
|
24
|
+
post("/apps/#{code_name}/command", :body => command)
|
25
|
+
end
|
26
|
+
|
27
|
+
def post(path, params = {})
|
28
|
+
request(path, :post, params)
|
29
|
+
end
|
30
|
+
|
31
|
+
def get(path)
|
32
|
+
request(path, :get)
|
33
|
+
end
|
34
|
+
|
35
|
+
def request(path, method, params = {})
|
36
|
+
headers = {:accept => 'application/json'}.merge(Client.winnie_headers)
|
37
|
+
params.merge!(:api_key => @api_key)
|
38
|
+
|
39
|
+
RestClient::Request.execute(
|
40
|
+
:method => method,
|
41
|
+
:url => "#{winnie_url}#{path}",
|
42
|
+
:headers => headers,
|
43
|
+
:payload => params
|
44
|
+
) { |response, request| process_response(response) }
|
45
|
+
end
|
46
|
+
|
47
|
+
class UnauthorizedException < Exception; end
|
48
|
+
class ResourceNotFoundException < Exception; end
|
49
|
+
class CommandFailedException < Exception; end
|
50
|
+
|
51
|
+
def self.version
|
52
|
+
version_file = File.join(File.dirname(__FILE__), '..', '..', 'VERSION')
|
53
|
+
@@version ||= File.read(version_file).strip
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def self.winnie_headers
|
59
|
+
{'User-Agent' => "winnie-gem-#{version}",
|
60
|
+
'X-Ruby-Version' => RUBY_VERSION}
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_response(response)
|
64
|
+
# TODO: this fragment could look better
|
65
|
+
if [404, 500, 401].include?(response.code)
|
66
|
+
|
67
|
+
exception = case response.code
|
68
|
+
when 404; ResourceNotFoundException
|
69
|
+
when 500; CommandFailedException
|
70
|
+
when 401; raise UnauthorizedException.new
|
71
|
+
end
|
72
|
+
|
73
|
+
error = JSON.parse(response.body)['error']
|
74
|
+
raise exception.new(error)
|
75
|
+
end
|
76
|
+
|
77
|
+
response.return!
|
78
|
+
|
79
|
+
if response.code == 302 and response.args[:url] =~ /user_session/
|
80
|
+
raise UnauthorizedException.new
|
81
|
+
end
|
82
|
+
|
83
|
+
JSON.parse(response.body)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Winnie
|
2
|
+
class Command
|
3
|
+
extend Helpers
|
4
|
+
|
5
|
+
def self.run(name, *args)
|
6
|
+
begin
|
7
|
+
class_name, command_name = name.split(':')
|
8
|
+
command_name ||= 'run'
|
9
|
+
|
10
|
+
begin
|
11
|
+
command_class = eval("Winnie::Commands::#{class_name.capitalize}")
|
12
|
+
rescue NameError
|
13
|
+
command_class = Winnie::Commands::App
|
14
|
+
command_name = class_name
|
15
|
+
end
|
16
|
+
|
17
|
+
command = command_class.new(args)
|
18
|
+
|
19
|
+
if command.respond_to?(command_name)
|
20
|
+
command.send(command_name)
|
21
|
+
else
|
22
|
+
raise UnknownCommandException.new
|
23
|
+
end
|
24
|
+
|
25
|
+
rescue UnknownCommandException
|
26
|
+
error 'Unknown command'
|
27
|
+
rescue Winnie::Client::UnauthorizedException
|
28
|
+
error "Your API key is not correct\nUse 'winnie auth' command to re-enter your winnie API key"
|
29
|
+
rescue Winnie::Commands::ApplicationNotSpecyfiedException
|
30
|
+
error "Application not specified\nUse --app <code name> to specify the application"
|
31
|
+
rescue Winnie::Client::ResourceNotFoundException, Winnie::Client::CommandFailedException => e
|
32
|
+
error e.message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class UnknownCommandException < Exception; end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Winnie
|
2
|
+
module Commands
|
3
|
+
class App < Base
|
4
|
+
def run
|
5
|
+
list
|
6
|
+
end
|
7
|
+
|
8
|
+
def command
|
9
|
+
response = winnie.command(args.first, code_name)
|
10
|
+
display response['result']
|
11
|
+
end
|
12
|
+
|
13
|
+
def list
|
14
|
+
apps = winnie.apps
|
15
|
+
|
16
|
+
unless apps.empty?
|
17
|
+
display_columns "Name", "Code name"
|
18
|
+
line
|
19
|
+
apps.each do |app|
|
20
|
+
display_columns app['app']['name'], app['app']['code_name']
|
21
|
+
end
|
22
|
+
else
|
23
|
+
display "You don't have any apps yet"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Winnie
|
4
|
+
module Commands
|
5
|
+
class Auth < Base
|
6
|
+
attr_accessor :api_key
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super(args)
|
10
|
+
load_api_key if api_key_exists?
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
confirm('Do you want to change your API key?') if api_key_exists?
|
15
|
+
ask_for_api_key
|
16
|
+
save_api_key
|
17
|
+
validate_api_key
|
18
|
+
end
|
19
|
+
|
20
|
+
def ask_for_api_key
|
21
|
+
display "Type your Winnie API key"
|
22
|
+
display "Key: ", false
|
23
|
+
self.api_key = ask
|
24
|
+
end
|
25
|
+
|
26
|
+
def save_api_key
|
27
|
+
FileUtils.mkdir_p(config_path)
|
28
|
+
File.open(api_key_path, 'w') { |file| file << api_key }
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_api_key
|
32
|
+
self.api_key = File.read(api_key_path)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_api_key
|
36
|
+
winnie.account # If it's not correct, Winnie::Client::UnauthorizedException will be raised
|
37
|
+
display 'Your winnie API key is OK!'
|
38
|
+
end
|
39
|
+
|
40
|
+
def api_key_exists?
|
41
|
+
File.exists?(api_key_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def api_key_path
|
45
|
+
File.join(config_path, 'api_key')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Winnie
|
2
|
+
module Commands
|
3
|
+
class Base
|
4
|
+
attr_reader :winnie, :args
|
5
|
+
|
6
|
+
include Winnie::Helpers
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
@args = args.flatten
|
10
|
+
extract_options(@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def extract_options(args)
|
14
|
+
return if args.empty?
|
15
|
+
|
16
|
+
OptionParser.new do |opts|
|
17
|
+
opts.on("-a", "--app [CODE_NAME]", :text, "Run for given application") do |a|
|
18
|
+
@code_name = a
|
19
|
+
end
|
20
|
+
end.parse!(@args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def code_name
|
24
|
+
raise ApplicationNotSpecyfiedException.new unless @code_name
|
25
|
+
@code_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def winnie
|
29
|
+
auth = Winnie::Commands::Auth.new
|
30
|
+
@winnie ||= Winnie::Client.new(auth.api_key)
|
31
|
+
end
|
32
|
+
|
33
|
+
def config_path
|
34
|
+
File.expand_path('~/.winnie')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
class ApplicationNotSpecyfiedException < Exception; end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Winnie
|
2
|
+
module Commands
|
3
|
+
class Help < Base
|
4
|
+
def run
|
5
|
+
display_columns 'auth', 'Configure your winnie username and password'
|
6
|
+
display_columns 'command', 'Run single ruby command'
|
7
|
+
display_columns 'list', 'List your applications'
|
8
|
+
display_columns 'help', 'Show this information'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Winnie
|
2
|
+
module Helpers
|
3
|
+
def display(message, new_line = true)
|
4
|
+
STDOUT << message
|
5
|
+
STDOUT << "\n" if new_line
|
6
|
+
end
|
7
|
+
|
8
|
+
def display_columns(*columns)
|
9
|
+
columns.each { |field| display(field.to_s.ljust(20), false) }
|
10
|
+
display ''
|
11
|
+
end
|
12
|
+
|
13
|
+
def line
|
14
|
+
display '-' * 45
|
15
|
+
end
|
16
|
+
|
17
|
+
def error(message)
|
18
|
+
STDERR << message << "\n"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def ask
|
23
|
+
gets.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def confirm(message)
|
27
|
+
loop do
|
28
|
+
STDERR << message << " (Y/N): "
|
29
|
+
case ask.upcase
|
30
|
+
when 'Y'; break
|
31
|
+
when 'N'; exit 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# TODO: loop here, and load all commands
|
2
|
+
module Winnie; end
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'json'
|
6
|
+
require 'optparse'
|
7
|
+
require 'winnie/helpers'
|
8
|
+
require 'winnie/client'
|
9
|
+
require 'winnie/command'
|
10
|
+
require 'winnie/commands/base'
|
11
|
+
Dir["#{File.dirname(__FILE__)}/commands/*"].each { |c| require c }
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Winnie::Client do
|
4
|
+
before do
|
5
|
+
Winnie::Client.stub(:winnie_headers).and_return({})
|
6
|
+
@client = Winnie::Client.new('secret_one')
|
7
|
+
RestClient::Request.stub!(:execute)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "request" do
|
11
|
+
it "should make a request to give URL" do
|
12
|
+
RestClient::Request.should_receive(:execute).with(
|
13
|
+
request_parameters('/account', :get)
|
14
|
+
)
|
15
|
+
@client.request('/account', :get)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should include provided parameters in the request" do
|
19
|
+
RestClient::Request.should_receive(:execute).with(
|
20
|
+
request_parameters('/account', :post,
|
21
|
+
:payload => {:api_key => 'secret_one', :name => 'test'}
|
22
|
+
)
|
23
|
+
)
|
24
|
+
@client.request('/account', :post, :name => 'test')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should include api_key in the request parameters" do
|
28
|
+
@client = Winnie::Client.new('AABBCC')
|
29
|
+
RestClient::Request.should_receive(:execute).with(
|
30
|
+
request_parameters('/account', :get,
|
31
|
+
:payload => {:api_key => 'AABBCC'}
|
32
|
+
)
|
33
|
+
)
|
34
|
+
@client.request('/account', :get)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should accept json responses only" do
|
38
|
+
RestClient::Request.should_receive(:execute).with(
|
39
|
+
request_parameters('/account', :get,
|
40
|
+
:headers => {:accept => 'application/json'}
|
41
|
+
)
|
42
|
+
)
|
43
|
+
@client.request('/account', :get)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should pass response to process_response method" do
|
47
|
+
response = mock(RestClient::Response)
|
48
|
+
request = mock(RestClient::Request)
|
49
|
+
@client.should_receive(:process_response).with(response)
|
50
|
+
RestClient::Request.should_receive(:execute).with(request_parameters('/account', :get)).and_yield(response, request)
|
51
|
+
|
52
|
+
@client.request('/account', :get)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def request_parameters(path, method, params = {})
|
57
|
+
{:payload => {:api_key => 'secret_one'},
|
58
|
+
:method => method,
|
59
|
+
:headers => {:accept => 'application/json'},
|
60
|
+
:url => "http://admin.winniecloud.net#{path}"
|
61
|
+
}.merge(params)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "process_response" do
|
66
|
+
before do
|
67
|
+
@response = mock(RestClient::Response, :code => 200, :body => '{}', :return! => nil)
|
68
|
+
@request = mock(RestClient::Request)
|
69
|
+
RestClient::Request.stub(:execute).and_yield(@response, @request)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not follow redirections" do
|
73
|
+
@response.should_receive(:return!)
|
74
|
+
@client.get('/account')
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should raise UnauthorizedException when response has 401 code" do
|
78
|
+
@response.stub(:code).and_return(401)
|
79
|
+
lambda {
|
80
|
+
@client.get('/account')
|
81
|
+
}.should raise_error(Winnie::Client::UnauthorizedException)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should raise UnauthorizedException when it gets redirected to login page" do
|
85
|
+
@response.stub(:args).and_return({:url => 'user_session'})
|
86
|
+
@response.stub(:code).and_return(302)
|
87
|
+
|
88
|
+
lambda {
|
89
|
+
@client.get('/account')
|
90
|
+
}.should raise_error(Winnie::Client::UnauthorizedException)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise ResourceNotFoundException when response has 404 code" do
|
94
|
+
@response.stub(:code).and_return(404)
|
95
|
+
@response.stub(:body).and_return({'error' => 'App not found'}.to_json)
|
96
|
+
|
97
|
+
lambda {
|
98
|
+
@client.post('/apps/flower/command', :body => 'puts User.count')
|
99
|
+
}.should raise_error(Winnie::Client::ResourceNotFoundException, 'App not found')
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should raise CommandFailedException when response has 500 code" do
|
103
|
+
@response.stub(:code).and_return(500)
|
104
|
+
@response.stub(:body).and_return({'error' => 'random error happened'}.to_json)
|
105
|
+
|
106
|
+
lambda {
|
107
|
+
@client.post('/apps/flower/command', :body => 'puts User.count')
|
108
|
+
}.should raise_error(Winnie::Client::CommandFailedException, 'random error happened')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should make GET request to given path" do
|
113
|
+
@client.should_receive(:request).with('/account', :get)
|
114
|
+
@client.get('/account')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should make POST request to given path with parameters" do
|
118
|
+
@client.should_receive(:request).with('/account', :post, :name => 'pink-one')
|
119
|
+
@client.post('/account', :name => 'pink-one')
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "API methods" do
|
123
|
+
it "should get the list of user applications" do
|
124
|
+
@client.should_receive(:get).with('/apps')
|
125
|
+
@client.apps
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should get account info" do
|
129
|
+
@client.should_receive(:get).with('/account')
|
130
|
+
@client.account
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should send command to winnie" do
|
134
|
+
@client.should_receive(:post).with('/apps/flower-16/command', :body => 'User[:bob].destroy')
|
135
|
+
@client.command('User[:bob].destroy', 'flower-16')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should return winnie-app URL" do
|
140
|
+
@client.winnie_url.should == "http://#{@client.host}"
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Winnie::Command do
|
4
|
+
describe "run" do
|
5
|
+
before do
|
6
|
+
@command = mock(Winnie::Commands::Help.new, :run => '')
|
7
|
+
@command.stub!(:respond_to?).and_return(true)
|
8
|
+
Winnie::Commands::Help.stub!(:new).and_return(@command)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should create command class object" do
|
12
|
+
Winnie::Command.should_receive(:eval).with('Winnie::Commands::Help').and_return(Winnie::Commands::Help)
|
13
|
+
Winnie::Command.run('help')
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "when command class exists" do
|
17
|
+
describe "and command name is provided" do
|
18
|
+
it "should parse command name and invoke it" do
|
19
|
+
@command.should_receive(:connection).and_return(true)
|
20
|
+
Winnie::Command.run('help:connection')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "and command name is not provided" do
|
25
|
+
it "should invoke run method" do
|
26
|
+
@command.should_receive(:run).and_return(true)
|
27
|
+
Winnie::Command.run('help').should be_true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "when command class doesn't exist" do
|
33
|
+
before do
|
34
|
+
@app = mock('Winnie::Commands::App', :list => [])
|
35
|
+
Winnie::Commands::App.stub(:new).and_return(@app)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should use App command class as default" do
|
39
|
+
Winnie::Commands::App.should_receive(:new).and_return(@app)
|
40
|
+
Winnie::Command.run('list')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should use command class name as command name" do
|
44
|
+
@app.should_receive(:list)
|
45
|
+
Winnie::Command.run('list')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should notify user when resource doesn't exist" do
|
50
|
+
app = mock(Winnie::Commands::App)
|
51
|
+
app.should_receive(:command).and_raise(Winnie::Client::ResourceNotFoundException.new('App not found'))
|
52
|
+
Winnie::Commands::App.stub(:new).and_return(app)
|
53
|
+
|
54
|
+
Winnie::Command.should_receive(:error).with('App not found')
|
55
|
+
|
56
|
+
Winnie::Command.run('command', ["'puts User.count'", '--app', 'flower'])
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should notify user when command doesn't exist" do
|
60
|
+
Winnie::Command.should_receive(:error).with('Unknown command')
|
61
|
+
Winnie::Command.run('unknown')
|
62
|
+
|
63
|
+
@command.should_receive(:respond_to?).with("foooo").and_return(false)
|
64
|
+
Winnie::Command.should_receive(:error).with('Unknown command')
|
65
|
+
Winnie::Command.run('help:foooo')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Winnie::Commands::App do
|
4
|
+
before do
|
5
|
+
@app = Winnie::Commands::App.new('--app', 'flower')
|
6
|
+
@app.stub(:display_columns)
|
7
|
+
@app.stub(:line)
|
8
|
+
@client = mock(Winnie::Client.new('secret-api-key'))
|
9
|
+
@app.stub(:winnie).and_return(@client)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "when user has some apps" do
|
13
|
+
it "should list apps" do
|
14
|
+
@client.should_receive(:apps).and_return(apps)
|
15
|
+
@app.should_receive(:display_columns).exactly(3)
|
16
|
+
@app.should_receive(:line)
|
17
|
+
@app.list
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "when user doesn't have any app" do
|
22
|
+
it "should inform the user that he doesn't have any app" do
|
23
|
+
@client.should_receive(:apps).and_return([])
|
24
|
+
@app.should_receive(:display).with("You don't have any apps yet")
|
25
|
+
@app.list
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "running commands" do
|
30
|
+
before do
|
31
|
+
@app = Winnie::Commands::App.new('--app', 'shop-16', "Product.cleanup_cache!")
|
32
|
+
@app.stub(:display_columns)
|
33
|
+
@app.stub(:display)
|
34
|
+
@app.stub(:line)
|
35
|
+
@app.stub(:winnie).and_return(@client)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should send command to winnie given as a command line parameter" do
|
39
|
+
@client.should_receive(:command).with('Product.cleanup_cache!', 'shop-16').and_return({})
|
40
|
+
@app.command
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should display the result" do
|
44
|
+
@client.should_receive(:command).with('Product.cleanup_cache!', 'shop-16').and_return('result' => 'foo')
|
45
|
+
@app.should_receive(:display).with('foo')
|
46
|
+
@app.command
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def apps
|
52
|
+
[
|
53
|
+
{"app" => {"name" => "Green One", "code_name" => "green-one"}},
|
54
|
+
{"app" => {"name" => "Pink", "code_name" => "pink"}}
|
55
|
+
]
|
56
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Winnie::Commands::Auth do
|
4
|
+
include FakeFS::SpecHelpers
|
5
|
+
|
6
|
+
before do
|
7
|
+
@auth = Winnie::Commands::Auth.new
|
8
|
+
@auth.stub(:display)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "initialize" do
|
12
|
+
describe "when API key doesn't exist" do
|
13
|
+
it "should not load API key" do
|
14
|
+
@auth.api_key.should be_nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "when API key exists" do
|
19
|
+
before { create_api_key_file('NobodyKnowsIt') }
|
20
|
+
|
21
|
+
it "should load API key" do
|
22
|
+
auth = Winnie::Commands::Auth.new
|
23
|
+
auth.api_key.should == 'NobodyKnowsIt'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "run" do
|
29
|
+
before do
|
30
|
+
@auth.stub(:ask_for_api_key)
|
31
|
+
@auth.stub(:save_api_key)
|
32
|
+
@auth.stub(:validate_api_key)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when API key doesn't exist" do
|
36
|
+
it "should ask for API key" do
|
37
|
+
@auth.should_receive(:ask_for_api_key)
|
38
|
+
@auth.run
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should save API key" do
|
42
|
+
@auth.should_receive(:save_api_key)
|
43
|
+
@auth.run
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should validate API key" do
|
47
|
+
@auth.should_receive(:validate_api_key)
|
48
|
+
@auth.run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "when API key exists" do
|
53
|
+
it "should ask if overwrite API key" do
|
54
|
+
@auth.should_receive(:confirm).with('Do you want to change your API key?')
|
55
|
+
@auth.stub!(:api_key_exists?).and_return(true)
|
56
|
+
@auth.run
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "save_api_key" do
|
62
|
+
before { @auth.api_key = 'secret_api_key' }
|
63
|
+
|
64
|
+
it "should create config directory" do
|
65
|
+
FileUtils.should_receive(:mkdir_p).with(@auth.config_path)
|
66
|
+
@auth.save_api_key
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should save API key to file" do
|
70
|
+
@auth.save_api_key
|
71
|
+
key = File.read(@auth.api_key_path)
|
72
|
+
key.should == 'secret_api_key'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "load_api_key" do
|
77
|
+
before { create_api_key_file('VerySecretOne') }
|
78
|
+
|
79
|
+
it "should load api key from a file" do
|
80
|
+
@auth.load_api_key
|
81
|
+
@auth.api_key.should == 'VerySecretOne'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should return api_key_path" do
|
86
|
+
@auth.api_key_path.should == File.join(@auth.config_path, 'api_key')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def create_api_key_file(key)
|
91
|
+
File.open(@auth.api_key_path, 'w') { |file| file << key }
|
92
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Winnie::Commands::Base do
|
4
|
+
describe "config_path" do
|
5
|
+
it "should return default config path" do
|
6
|
+
Winnie::Commands::Base.new.config_path.should == File.expand_path('~/.winnie')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "extract_options" do
|
11
|
+
it "should extract app code name if provided" do
|
12
|
+
base = Winnie::Commands::Base.new('--app', 'crazy-one')
|
13
|
+
base.code_name.should == 'crazy-one'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'winnie/winnie'
|
5
|
+
require 'spec'
|
6
|
+
require 'spec/autorun'
|
7
|
+
require 'fakefs'
|
8
|
+
require 'fakefs/spec_helpers'
|
9
|
+
|
10
|
+
Spec::Runner.configure do |config|
|
11
|
+
|
12
|
+
end
|
data/spec/winnie_spec.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
data/winnie.gemspec
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{winnie}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["winnie"]
|
12
|
+
s.date = %q{2010-08-27}
|
13
|
+
s.default_executable = %q{winnie}
|
14
|
+
s.description = %q{Command line tool which allows interacting with winnie's API}
|
15
|
+
s.email = %q{winnie-devs@ragnarson.com}
|
16
|
+
s.executables = ["winnie"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"LICENSE",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"bin/winnie",
|
29
|
+
"lib/winnie/client.rb",
|
30
|
+
"lib/winnie/command.rb",
|
31
|
+
"lib/winnie/commands/app.rb",
|
32
|
+
"lib/winnie/commands/auth.rb",
|
33
|
+
"lib/winnie/commands/base.rb",
|
34
|
+
"lib/winnie/commands/help.rb",
|
35
|
+
"lib/winnie/helpers.rb",
|
36
|
+
"lib/winnie/winnie.rb",
|
37
|
+
"spec/client_spec.rb",
|
38
|
+
"spec/command_spec.rb",
|
39
|
+
"spec/commands/app_spec.rb",
|
40
|
+
"spec/commands/auth_spec.rb",
|
41
|
+
"spec/commands/base_spec.rb",
|
42
|
+
"spec/spec.opts",
|
43
|
+
"spec/spec_helper.rb",
|
44
|
+
"spec/winnie_spec.rb",
|
45
|
+
"winnie.gemspec"
|
46
|
+
]
|
47
|
+
s.homepage = %q{http://winniecloud.net}
|
48
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = %q{1.3.7}
|
51
|
+
s.summary = %q{Winnie command line tool}
|
52
|
+
s.test_files = [
|
53
|
+
"spec/client_spec.rb",
|
54
|
+
"spec/command_spec.rb",
|
55
|
+
"spec/commands/app_spec.rb",
|
56
|
+
"spec/commands/auth_spec.rb",
|
57
|
+
"spec/commands/base_spec.rb",
|
58
|
+
"spec/spec_helper.rb",
|
59
|
+
"spec/winnie_spec.rb"
|
60
|
+
]
|
61
|
+
|
62
|
+
if s.respond_to? :specification_version then
|
63
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
64
|
+
s.specification_version = 3
|
65
|
+
|
66
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
67
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
68
|
+
s.add_runtime_dependency(%q<rest-client>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
70
|
+
s.add_development_dependency(%q<fakefs>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<json>, [">= 0"])
|
73
|
+
s.add_dependency(%q<rest-client>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
75
|
+
s.add_dependency(%q<fakefs>, [">= 0"])
|
76
|
+
end
|
77
|
+
else
|
78
|
+
s.add_dependency(%q<json>, [">= 0"])
|
79
|
+
s.add_dependency(%q<rest-client>, [">= 0"])
|
80
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
81
|
+
s.add_dependency(%q<fakefs>, [">= 0"])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: winnie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- winnie
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-27 00:00:00 +02:00
|
19
|
+
default_executable: winnie
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: json
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rest-client
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 13
|
58
|
+
segments:
|
59
|
+
- 1
|
60
|
+
- 2
|
61
|
+
- 9
|
62
|
+
version: 1.2.9
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: fakefs
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
type: :development
|
78
|
+
version_requirements: *id004
|
79
|
+
description: Command line tool which allows interacting with winnie's API
|
80
|
+
email: winnie-devs@ragnarson.com
|
81
|
+
executables:
|
82
|
+
- winnie
|
83
|
+
extensions: []
|
84
|
+
|
85
|
+
extra_rdoc_files:
|
86
|
+
- LICENSE
|
87
|
+
- README.rdoc
|
88
|
+
files:
|
89
|
+
- .document
|
90
|
+
- .gitignore
|
91
|
+
- LICENSE
|
92
|
+
- README.rdoc
|
93
|
+
- Rakefile
|
94
|
+
- VERSION
|
95
|
+
- bin/winnie
|
96
|
+
- lib/winnie/client.rb
|
97
|
+
- lib/winnie/command.rb
|
98
|
+
- lib/winnie/commands/app.rb
|
99
|
+
- lib/winnie/commands/auth.rb
|
100
|
+
- lib/winnie/commands/base.rb
|
101
|
+
- lib/winnie/commands/help.rb
|
102
|
+
- lib/winnie/helpers.rb
|
103
|
+
- lib/winnie/winnie.rb
|
104
|
+
- spec/client_spec.rb
|
105
|
+
- spec/command_spec.rb
|
106
|
+
- spec/commands/app_spec.rb
|
107
|
+
- spec/commands/auth_spec.rb
|
108
|
+
- spec/commands/base_spec.rb
|
109
|
+
- spec/spec.opts
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
- spec/winnie_spec.rb
|
112
|
+
- winnie.gemspec
|
113
|
+
has_rdoc: true
|
114
|
+
homepage: http://winniecloud.net
|
115
|
+
licenses: []
|
116
|
+
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options:
|
119
|
+
- --charset=UTF-8
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
hash: 3
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
version: "0"
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
hash: 3
|
137
|
+
segments:
|
138
|
+
- 0
|
139
|
+
version: "0"
|
140
|
+
requirements: []
|
141
|
+
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 1.3.7
|
144
|
+
signing_key:
|
145
|
+
specification_version: 3
|
146
|
+
summary: Winnie command line tool
|
147
|
+
test_files:
|
148
|
+
- spec/client_spec.rb
|
149
|
+
- spec/command_spec.rb
|
150
|
+
- spec/commands/app_spec.rb
|
151
|
+
- spec/commands/auth_spec.rb
|
152
|
+
- spec/commands/base_spec.rb
|
153
|
+
- spec/spec_helper.rb
|
154
|
+
- spec/winnie_spec.rb
|