ruby-resty 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/README.md +26 -8
  2. data/Rakefile +7 -0
  3. data/lib/resty/cli.rb +21 -13
  4. data/lib/resty/commands/method_command.rb +27 -12
  5. data/lib/resty/{cli_options.rb → options.rb} +20 -12
  6. data/lib/resty/override_pry_help.rb +17 -0
  7. data/lib/resty/pretty_printer.rb +59 -0
  8. data/lib/resty/repl.rb +6 -4
  9. data/lib/resty/request.rb +18 -10
  10. data/lib/resty.rb +6 -3
  11. data/lib/version.rb +1 -1
  12. data/ruby-resty.gemspec +5 -1
  13. data/spec/lib/resty/commands/method_command_spec.rb +22 -14
  14. data/spec/lib/resty/options_spec.rb +111 -0
  15. data/spec/lib/resty/pretty_printer_spec.rb +110 -0
  16. data/spec/lib/resty/request_spec.rb +69 -45
  17. data/spec/requests/commands/method_command_request_spec.rb +150 -0
  18. data/spec/server.rb +34 -0
  19. data/spec/spec_helper.rb +48 -0
  20. data/spec/vcr/method_command/delete_returns_200.yml +79 -0
  21. data/spec/vcr/method_command/delete_sends_headers.yml +46 -0
  22. data/spec/vcr/method_command/get_non_json_response_returns_200.yml +89 -0
  23. data/spec/vcr/method_command/get_non_json_response_returns_response.yml +89 -0
  24. data/spec/vcr/method_command/get_non_json_response_sends_headers.yml +89 -0
  25. data/spec/vcr/method_command/get_returns_200.yml +79 -0
  26. data/spec/vcr/method_command/get_returns_response.yml +79 -0
  27. data/spec/vcr/method_command/get_sends_headers.yml +44 -0
  28. data/spec/vcr/method_command/get_with_per_request_headers_returns_200.yml +46 -0
  29. data/spec/vcr/method_command/get_with_per_request_headers_returns_response.yml +46 -0
  30. data/spec/vcr/method_command/get_with_per_request_headers_sends_headers.yml +46 -0
  31. data/spec/vcr/method_command/get_without_per_request_headers_returns_200.yml +46 -0
  32. data/spec/vcr/method_command/get_without_per_request_headers_returns_response.yml +46 -0
  33. data/spec/vcr/method_command/get_without_per_request_headers_sends_headers.yml +46 -0
  34. data/spec/vcr/method_command/head_returns_200.yml +79 -0
  35. data/spec/vcr/method_command/head_sends_headers.yml +46 -0
  36. data/spec/vcr/method_command/options_returns_200.yml +79 -0
  37. data/spec/vcr/method_command/options_sends_headers.yml +46 -0
  38. data/spec/vcr/method_command/patch_returns_204.yml +87 -0
  39. data/spec/vcr/method_command/patch_sends_headers.yml +50 -0
  40. data/spec/vcr/method_command/post_returns_200.yml +87 -0
  41. data/spec/vcr/method_command/post_returns_created_object.yml +87 -0
  42. data/spec/vcr/method_command/post_sends_headers.yml +50 -0
  43. data/spec/vcr/method_command/put_returns_204.yml +79 -0
  44. data/spec/vcr/method_command/put_sends_headers.yml +42 -0
  45. metadata +135 -16
  46. data/lib/resty/commands/method_output.rb +0 -43
  47. data/spec/lib/resty/cli_options_spec.rb +0 -103
  48. data/spec/lib/resty/commands/method_output_spec.rb +0 -58
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Resty::Options do
4
+ context "command line options" do
5
+ let(:options) { Resty::Options.new(host: "foo.com", headers: ["key:star", "type:ninja"],
6
+ username: "leeroy", password: "jenkins", alias: "nyan") }
7
+
8
+ it "returns host" do
9
+ expect(options.host).to eq("foo.com")
10
+ end
11
+
12
+ it "returns headers" do
13
+ expect(options.headers).to eq(key: "star", type: "ninja")
14
+ end
15
+
16
+ it "doesn't read config file" do
17
+ options.should have_received(:load_config_file).never
18
+ end
19
+
20
+ it "returns username" do
21
+ expect(options.username).to eq("leeroy")
22
+ end
23
+
24
+ it "returns password" do
25
+ expect(options.password).to eq("jenkins")
26
+ end
27
+
28
+ context "empty headers" do
29
+ let(:options) { Resty::Options.new({}) }
30
+
31
+ it "returns empty hash" do
32
+ expect(options.headers).to eq({})
33
+ end
34
+ end
35
+ end
36
+
37
+ context "config file" do
38
+ context "all values exist" do
39
+ before(:each) do
40
+ YAML.stubs(:load_file).returns({"nyan" => { "host" => "nyan.cat", "username" => "leeroy",
41
+ "headers" => {"header" => "value"},
42
+ "password" => "jenkins"} } )
43
+ File.stubs(:exist?).returns(true)
44
+ @options = Resty::Options.new(alias: "nyan")
45
+ end
46
+
47
+ it "returns host" do
48
+ expect(@options.host).to eq("nyan.cat")
49
+ end
50
+
51
+ it "returns headers" do
52
+ expect(@options.headers).to eq("header" => "value")
53
+ end
54
+
55
+ it "returns username" do
56
+ expect(@options.username).to eq("leeroy")
57
+ end
58
+
59
+ it "returns password" do
60
+ expect(@options.password).to eq("jenkins")
61
+ end
62
+
63
+ it "loads YAML file" do
64
+ YAML.should have_received(:load_file).with("#{Dir.home}/.ruby_resty.yml")
65
+ end
66
+ end
67
+
68
+ context "headers don't exist" do
69
+ before(:each) do
70
+ YAML.stubs(:load_file).returns({"nyan" => { "host" => "nyan.cat" } } )
71
+ File.stubs(:exist?).returns(true)
72
+ @options = Resty::Options.new(alias: "nyan")
73
+ end
74
+
75
+ it "returns host" do
76
+ expect(@options.host).to eq("nyan.cat")
77
+ end
78
+
79
+ it "returns headers" do
80
+ expect(@options.headers).to eq({})
81
+ end
82
+
83
+ it "loads YAML file" do
84
+ YAML.should have_received(:load_file).with("#{Dir.home}/.ruby_resty.yml")
85
+ end
86
+ end
87
+
88
+ context "config file doesn't exist" do
89
+ it "raises ConfigFileError" do
90
+ File.stubs(:exist?).returns(false)
91
+ expect { Resty::Options.new(alias: "nyan") }.to raise_error(Resty::ConfigFileError)
92
+ end
93
+ end
94
+
95
+ context "alias doesn't exist" do
96
+ it "raises ConfigFileError" do
97
+ YAML.stubs(:load_file).returns({"ice_cream" => {} } )
98
+ File.stubs(:exist?).returns(true)
99
+ expect { Resty::Options.new(alias: "nyan") }.to raise_error(Resty::ConfigFileError)
100
+ end
101
+ end
102
+
103
+ context "host doesn't exist" do
104
+ it "raises error" do
105
+ YAML.stubs(:load_file).returns({"nyan" => {}})
106
+ File.stubs(:exist?).returns(true)
107
+ expect { Resty::Options.new(alias: "nyan") }.to raise_error(Resty::ConfigFileError)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe Resty::PrettyPrinter do
5
+ let(:request) { stub }
6
+
7
+ context "#generate" do
8
+ context "JSON response" do
9
+ let(:response) { JSON.dump({foo: "bar"}) }
10
+
11
+ before(:each) { response.stubs(:code).returns(200) }
12
+
13
+ context "non-verbose" do
14
+ let(:printer) { Resty::PrettyPrinter.new(stub(verbose?: false),
15
+ Hashie::Mash.new(response: response, request: request)) }
16
+
17
+ it "returns output" do
18
+ output = <<-eos.unindent
19
+ > Response Code: 200
20
+ {
21
+ "foo": "bar"
22
+ }
23
+ eos
24
+
25
+ # multi-line text adds an extra \n, so let's ignore it
26
+ printer.generate.should eq(output[0..-2])
27
+ end
28
+ end
29
+
30
+ context "verbose" do
31
+ let(:printer) { Resty::PrettyPrinter.new(stub(verbose?: true),
32
+ Hashie::Mash.new(response: response, request: request)) }
33
+
34
+ before(:each) do
35
+ request.stubs(:method).returns("get")
36
+ request.stubs(:url).returns("foo.com")
37
+ request.stubs(:processed_headers).returns(header: "value", header2: "value2")
38
+ response.stubs(:headers).returns(response_header: "value", response_header2: "value2")
39
+ end
40
+
41
+ it "returns verbose output" do
42
+ output = <<-eos.unindent
43
+ > GET foo.com
44
+ > header: value
45
+ > header2: value2
46
+
47
+ > Response Code: 200
48
+ > response_header: value
49
+ > response_header2: value2
50
+ {
51
+ "foo": "bar"
52
+ }
53
+ eos
54
+
55
+ # multi-line text adds an extra \n, so let's ignore it
56
+ printer.generate.should eq(output[0..-2])
57
+ end
58
+ end
59
+ end
60
+
61
+ context "non-json response" do
62
+ let(:response) { "Ender Wiggin" }
63
+
64
+ before(:each) { response.stubs(:code).returns(200) }
65
+
66
+ context "non-verbose" do
67
+ let(:printer) { Resty::PrettyPrinter.new(stub(verbose?: false),
68
+ Hashie::Mash.new(response: response, request: request)) }
69
+
70
+ it "returns output" do
71
+ output = <<-eos.unindent
72
+ > Response Code: 200
73
+ Ender Wiggin
74
+ eos
75
+
76
+ # multi-line text adds an extra \n, so let's ignore it
77
+ printer.generate.should eq(output[0..-2])
78
+ end
79
+ end
80
+
81
+ context "verbose" do
82
+ let(:printer) { Resty::PrettyPrinter.new(stub(verbose?: true),
83
+ Hashie::Mash.new(response: response, request: request)) }
84
+
85
+ before(:each) do
86
+ request.stubs(:method).returns("get")
87
+ request.stubs(:url).returns("foo.com")
88
+ request.stubs(:processed_headers).returns(header: "value", header2: "value2")
89
+ response.stubs(:headers).returns(response_header: "value", response_header2: "value2")
90
+ end
91
+
92
+ it "returns verbose output" do
93
+ output = <<-eos.unindent
94
+ > GET foo.com
95
+ > header: value
96
+ > header2: value2
97
+
98
+ > Response Code: 200
99
+ > response_header: value
100
+ > response_header2: value2
101
+ Ender Wiggin
102
+ eos
103
+
104
+ # multi-line text adds an extra \n, so let's ignore it
105
+ printer.generate.should eq(output[0..-2])
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,88 +1,112 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Resty::Request do
4
- let(:cli_options) { stub(host: "foo.com", verbose?: false, headers: { header: "value" }) }
4
+ let(:options) { Resty::Options.new(host: "foo.com", headers: ["header:value"]) }
5
5
 
6
6
  context "#send_request" do
7
+ let(:request) { stub(:execute) }
8
+
7
9
  before(:each) do
8
- RestClient.stubs(:send)
10
+ RestClient::Request.stubs(:new).returns(request)
9
11
  end
10
12
 
11
- context "GET" do
13
+ context "HTTP method without body" do
12
14
  let(:params) { { method: "get", path: "/api/merchants" } }
13
15
 
14
16
  before(:each) do
15
- Resty::Request.new(cli_options, params).send_request
17
+ Resty::Request.new(options, params).send_request
16
18
  end
17
19
 
18
- it "sends request" do
19
- RestClient.should have_received(:send).with("get", "foo.com/api/merchants",
20
- {header: "value"})
21
- end
22
- end
23
-
24
- context "DELETE" do
25
- let(:params) { { method: "delete", path: "/api/merchants" } }
26
-
27
- before(:each) do
28
- Resty::Request.new(cli_options, params).send_request
20
+ it "creates request" do
21
+ RestClient::Request.should have_received(:new).with(url: "foo.com/api/merchants",
22
+ method: "get",
23
+ headers: {header: "value"})
29
24
  end
30
25
 
31
26
  it "sends request" do
32
- RestClient.should have_received(:send).with("delete", "foo.com/api/merchants",
33
- {header: "value"})
27
+ request.should have_received(:execute)
34
28
  end
35
29
  end
36
30
 
37
- context "HEAD" do
38
- let(:params) { { method: "head", path: "/api/merchants" } }
31
+ context "HTTP method with body" do
32
+ let(:params) { { method: "post", path: "/api/merchants", data: {"foo" => "bar"} } }
39
33
 
40
34
  before(:each) do
41
- Resty::Request.new(cli_options, params).send_request
35
+ Resty::Request.new(options, params).send_request
36
+ end
37
+
38
+ it "creates resource" do
39
+ RestClient::Request.should have_received(:new).with(url: "foo.com/api/merchants",
40
+ method: "post",
41
+ payload: {"foo" => "bar"},
42
+ headers: {header: "value"})
42
43
  end
43
44
 
44
45
  it "sends request" do
45
- RestClient.should have_received(:send).with("head", "foo.com/api/merchants",
46
- {header: "value"})
46
+ request.should have_received(:execute)
47
47
  end
48
48
  end
49
49
 
50
- context "OPTIONS" do
51
- let(:params) { { method: "options", path: "/api/merchants" } }
50
+ context "with basic authentication" do
51
+ let(:options) { Resty::Options.new(host: "foo.com", headers: ["header:value"],
52
+ username: "leeroy", password: "jenkins") }
53
+ let(:params) { { method: "get", path: "/api/merchants" } }
52
54
 
53
55
  before(:each) do
54
- Resty::Request.new(cli_options, params).send_request
56
+ Resty::Request.new(options, params).send_request(foo: "bar")
57
+ end
58
+
59
+ it "creates request" do
60
+ RestClient::Request.should have_received(:new).with(url: "foo.com/api/merchants",
61
+ method: "get",
62
+ user: "leeroy",
63
+ password: "jenkins",
64
+ headers: {header: "value"})
55
65
  end
56
66
 
57
67
  it "sends request" do
58
- RestClient.should have_received(:send).with("options", "foo.com/api/merchants",
59
- {header: "value"})
68
+ request.should have_received(:execute)
60
69
  end
61
70
  end
62
71
 
63
- context "PUT" do
64
- let(:params) { { method: "put", path: "/api/merchants", data: {"foo" => "bar"} } }
65
-
66
- before(:each) do
67
- Resty::Request.new(cli_options, params).send_request
72
+ context "with request options" do
73
+ context "with global headers" do
74
+ let(:options) { Resty::Options.new(host: "foo.com", headers: ["header:value"]) }
75
+ let(:params) { { method: "get", path: "/api/merchants" } }
76
+
77
+ before(:each) do
78
+ Resty::Request.new(options, params).send_request(headers: { name: "cat", age: 42})
79
+ end
80
+
81
+ it "creates request" do
82
+ RestClient::Request.should have_received(:new).with(url: "foo.com/api/merchants",
83
+ method: "get",
84
+ headers: { header: "value",
85
+ name: "cat", age: 42})
86
+ end
87
+
88
+ it "sends request" do
89
+ request.should have_received(:execute)
90
+ end
68
91
  end
69
92
 
70
- it "sends request" do
71
- RestClient.should have_received(:send).with("put", "foo.com/api/merchants",
72
- {"foo" => "bar"}, {header: "value"})
73
- end
74
- end
93
+ context "without global headers" do
94
+ let(:options) { Resty::Options.new(host: "foo.com") }
95
+ let(:params) { { method: "get", path: "/api/merchants" } }
75
96
 
76
- context "POST" do
77
- let(:params) { { method: "post", path: "/api/merchants", data: {"foo" => "bar"} } }
97
+ before(:each) do
98
+ Resty::Request.new(options, params).send_request(headers: { name: "cat", age: 42})
99
+ end
78
100
 
79
- before(:each) do
80
- Resty::Request.new(cli_options, params).send_request
81
- end
101
+ it "creates request" do
102
+ RestClient::Request.should have_received(:new).with(url: "foo.com/api/merchants",
103
+ method: "get",
104
+ headers: { name: "cat", age: 42 })
105
+ end
82
106
 
83
- it "sends request" do
84
- RestClient.should have_received(:send).with("post", "foo.com/api/merchants",
85
- {"foo" => "bar"}, {header: "value"})
107
+ it "sends request" do
108
+ request.should have_received(:execute)
109
+ end
86
110
  end
87
111
  end
88
112
  end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe "method_command", :vcr do
4
+ let(:options) { Resty::Options.new(host: "localhost:4567", username: "nyan", password: "cat",
5
+ headers: ["name:nyaaa", "color:green"]) }
6
+
7
+ context "GET" do
8
+ context "with per request headers" do
9
+ before(:each) do
10
+ @result = pry_eval(options, "get /api/nyan -H age:42 -H address:space")
11
+ end
12
+
13
+ it "sends headers" do
14
+ @result.request.headers.should eq(name: "nyaaa", color: "green", age: "42", address: "space")
15
+ end
16
+
17
+ it "returns 200" do
18
+ @result.response.code.should eq(200)
19
+ end
20
+
21
+ it "returns response" do
22
+ JSON.parse(@result.response).should eq("nyan" => "cat")
23
+ end
24
+ end
25
+
26
+ context "without per request headers" do
27
+ before(:each) do
28
+ @result = pry_eval(options, "get /api/nyan")
29
+ end
30
+
31
+ it "sends headers" do
32
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
33
+ end
34
+
35
+ it "returns 200" do
36
+ @result.response.code.should eq(200)
37
+ end
38
+
39
+ it "returns response" do
40
+ JSON.parse(@result.response).should eq("nyan" => "cat")
41
+ end
42
+ end
43
+
44
+ context "non-json response" do
45
+ before(:each) do
46
+ @result = pry_eval(options, "get /api/nyan?format=xml")
47
+ end
48
+
49
+ it "sends headers" do
50
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
51
+ end
52
+
53
+ it "returns 200" do
54
+ @result.response.code.should eq(200)
55
+ end
56
+
57
+ it "returns response" do
58
+ @result.response.should eq("<nyan>cat</nyan>")
59
+ end
60
+ end
61
+ end
62
+
63
+ context "DELETE" do
64
+ before(:each) do
65
+ @result = pry_eval(options, "delete /api/nyan")
66
+ end
67
+
68
+ it "sends headers" do
69
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
70
+ end
71
+
72
+ it "returns 200" do
73
+ @result.response.code.should eq(200)
74
+ end
75
+ end
76
+
77
+ context "HEAD" do
78
+ before(:each) do
79
+ @result = pry_eval(options, "head /api/nyan")
80
+ end
81
+
82
+ it "sends headers" do
83
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
84
+ end
85
+
86
+ it "returns 200" do
87
+ @result.response.code.should eq(200)
88
+ end
89
+ end
90
+
91
+ context "OPTIONS" do
92
+ before(:each) do
93
+ @result = pry_eval(options, "options /api/nyan")
94
+ end
95
+
96
+ it "sends headers" do
97
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
98
+ end
99
+
100
+ it "returns 200" do
101
+ @result.response.code.should eq(200)
102
+ end
103
+ end
104
+
105
+ context "POST" do
106
+ before(:each) do
107
+ @result = pry_eval(options, "post /api/nyan {nyan: 'cat'}")
108
+ end
109
+
110
+ it "sends headers" do
111
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
112
+ end
113
+
114
+ it "returns 200" do
115
+ @result.response.code.should eq(200)
116
+ end
117
+
118
+ it "returns created object" do
119
+ JSON.parse(@result.response.body).should eq("nyan" => "cat")
120
+ end
121
+ end
122
+
123
+ context "PUT" do
124
+ before(:each) do
125
+ @result = pry_eval(options, "put /api/nyan {nyan: 'cat'}")
126
+ end
127
+
128
+ it "sends headers" do
129
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
130
+ end
131
+
132
+ it "returns 204" do
133
+ @result.response.code.should eq(204)
134
+ end
135
+ end
136
+
137
+ context "PATCH" do
138
+ before(:each) do
139
+ @result = pry_eval(options, "patch /api/nyan {nyan: 'cat'}")
140
+ end
141
+
142
+ it "sends headers" do
143
+ @result.request.headers.should eq(name: "nyaaa", color: "green")
144
+ end
145
+
146
+ it "returns 204" do
147
+ @result.response.code.should eq(200)
148
+ end
149
+ end
150
+ end
data/spec/server.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'sinatra'
2
+ require 'json'
3
+
4
+ use Rack::Auth::Basic do |username, password|
5
+ username == 'nyan' && password == 'cat'
6
+ end
7
+
8
+ get '/api/nyan' do
9
+ if params[:format] == "xml"
10
+ "<nyan>cat</nyan>"
11
+ else
12
+ JSON.dump({ nyan: "cat" })
13
+ end
14
+ end
15
+
16
+ delete '/api/nyan' do
17
+ end
18
+
19
+ head '/api/nyan' do
20
+ end
21
+
22
+ options '/api/nyan' do
23
+ end
24
+
25
+ put '/api/nyan' do
26
+ [204]
27
+ end
28
+
29
+ post '/api/nyan' do
30
+ JSON.dump(params)
31
+ end
32
+
33
+ patch '/api/nyan' do
34
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,15 +2,63 @@ require_relative "../lib/resty"
2
2
 
3
3
  require 'mocha/api'
4
4
  require 'bourne'
5
+ require 'vcr'
5
6
 
7
+ require 'active_support/inflector'
6
8
  require 'pry/test/helper'
7
9
 
8
10
  RSpec.configure do |c|
9
11
  c.mock_with :mocha
12
+ c.treat_symbols_as_metadata_keys_with_true_values = true
13
+
14
+ c.around(:each, :vcr) do |example|
15
+ name = example.metadata[:full_description].split(/\s+/, 2).join("/").underscore.gsub(/[^\w\/]+/, "_")
16
+ options = {}
17
+ options[:record] = example.metadata[:record] if example.metadata[:record]
18
+ VCR.use_cassette(name, options) { example.call }
19
+ end
10
20
 
11
21
  include PryTestHelpers
12
22
  end
13
23
 
24
+ VCR.configure do |c|
25
+ c.cassette_library_dir = 'spec/vcr'
26
+ c.hook_into :webmock
27
+
28
+ c.default_cassette_options = {
29
+ :record => :once,
30
+ :decode_compressed_response => true,
31
+ # Because psych is binary encoding response headers marked with ASCII-8BIT
32
+ # https://groups.google.com/forum/?fromgroups#!topic/vcr-ruby/2sKrJa86ktU
33
+ # And syck's output is much easier to read
34
+ :serialize_with => :syck,
35
+ }
36
+
37
+ # Pretty print your json so it's not all on one line
38
+ # From discussion here: https://github.com/myronmarston/vcr/pull/147
39
+ # https://gist.github.com/26edfe7669cc7b85e164
40
+ c.before_record do |i|
41
+ type = Array(i.response.headers['Content-Type']).join(',').split(';').first
42
+ code = i.response.status.code
43
+
44
+ if type =~ /[\/+]json$/ or 'text/javascript' == type
45
+ begin
46
+ data = JSON.parse i.response.body
47
+ rescue
48
+ if code != 404
49
+ puts
50
+ warn "VCR: JSON parse error for Content-type #{type}"
51
+ warn "Your unparseable json is: " + i.response.body.inspect
52
+ puts
53
+ end
54
+ else
55
+ i.response.body = JSON.pretty_generate data
56
+ i.response.update_content_length_header
57
+ end
58
+ end
59
+ end
60
+ end
61
+
14
62
  class String
15
63
  def unindent
16
64
  gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")