kookaburra 1.0.0 → 1.1.0
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/README.markdown +1 -1
- data/VERSION +1 -1
- data/kookaburra.gemspec +4 -2
- data/lib/core_ext/object/to_param.rb +81 -0
- data/lib/core_ext/object/to_query.rb +53 -0
- data/lib/kookaburra/api_driver.rb +19 -10
- data/spec/kookaburra/api_driver_spec.rb +127 -64
- metadata +5 -3
data/README.markdown
CHANGED
@@ -5,7 +5,7 @@ order to keep acceptance tests maintainable.
|
|
5
5
|
|
6
6
|
## Requirements ##
|
7
7
|
|
8
|
-
Requires Ruby 1.9. Tested with both MRI and
|
8
|
+
Requires Ruby 1.9. Tested with both MRI and JRuby (note that you must run
|
9
9
|
JRuby in 1.9 compatability mode.)
|
10
10
|
|
11
11
|
## Installation ##
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.1.0
|
data/kookaburra.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "kookaburra"
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["John Wilger", "Sam Livingston-Gray", "Ravi Gadad"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-12-14"
|
13
13
|
s.description = "Cucumber + Capybara = Kookaburra? It made sense at the time."
|
14
14
|
s.email = "johnwilger@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -28,6 +28,8 @@ Gem::Specification.new do |s|
|
|
28
28
|
"Rakefile",
|
29
29
|
"VERSION",
|
30
30
|
"kookaburra.gemspec",
|
31
|
+
"lib/core_ext/object/to_param.rb",
|
32
|
+
"lib/core_ext/object/to_query.rb",
|
31
33
|
"lib/kookaburra.rb",
|
32
34
|
"lib/kookaburra/api_driver.rb",
|
33
35
|
"lib/kookaburra/assertion.rb",
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# If ActiveSupport is available, use the #to_param method it provides,
|
2
|
+
# otherwise, use our own. The code is taken from ActiveSupport 3.2.8.
|
3
|
+
begin
|
4
|
+
require 'active_support/core_ext/object/to_param'
|
5
|
+
rescue LoadError
|
6
|
+
# Copyright (c) 2005-2012 David Heinemeier Hansson
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
# a copy of this software and associated documentation files (the
|
10
|
+
# "Software"), to deal in the Software without restriction, including
|
11
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
13
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
14
|
+
# the following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be
|
17
|
+
# included in all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
22
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
23
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
24
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
25
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
class Object
|
27
|
+
# Alias of <tt>to_s</tt>.
|
28
|
+
def to_param
|
29
|
+
to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class NilClass
|
34
|
+
def to_param
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TrueClass
|
40
|
+
def to_param
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class FalseClass
|
46
|
+
def to_param
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Array
|
52
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
53
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
54
|
+
def to_param
|
55
|
+
collect { |e| e.to_param }.join '/'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Hash
|
60
|
+
# Returns a string representation of the receiver suitable for use as a URL
|
61
|
+
# query string:
|
62
|
+
#
|
63
|
+
# {:name => 'David', :nationality => 'Danish'}.to_param
|
64
|
+
# # => "name=David&nationality=Danish"
|
65
|
+
#
|
66
|
+
# An optional namespace can be passed to enclose the param names:
|
67
|
+
#
|
68
|
+
# {:name => 'David', :nationality => 'Danish'}.to_param('user')
|
69
|
+
# # => "user[name]=David&user[nationality]=Danish"
|
70
|
+
#
|
71
|
+
# The string pairs "key=value" that conform the query string
|
72
|
+
# are sorted lexicographically in ascending order.
|
73
|
+
#
|
74
|
+
# This method is also aliased as +to_query+.
|
75
|
+
def to_param(namespace = nil)
|
76
|
+
collect do |key, value|
|
77
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
78
|
+
end.sort * '&'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# If ActiveSupport is available, use the #to_query method it provides,
|
2
|
+
# otherwise, use our own. The code is taken from ActiveSupport 3.2.8.
|
3
|
+
begin
|
4
|
+
require 'active_support/core_ext/object/to_query'
|
5
|
+
rescue LoadError
|
6
|
+
# Copyright (c) 2005-2012 David Heinemeier Hansson
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
# a copy of this software and associated documentation files (the
|
10
|
+
# "Software"), to deal in the Software without restriction, including
|
11
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
13
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
14
|
+
# the following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be
|
17
|
+
# included in all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
22
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
23
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
24
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
25
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
require 'core_ext/object/to_param'
|
27
|
+
|
28
|
+
class Object
|
29
|
+
# Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
|
30
|
+
# param name.
|
31
|
+
#
|
32
|
+
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
|
33
|
+
def to_query(key)
|
34
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
35
|
+
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Array
|
40
|
+
# Converts an array into a string suitable for use as a URL query string,
|
41
|
+
# using the given +key+ as the param name.
|
42
|
+
#
|
43
|
+
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
44
|
+
def to_query(key)
|
45
|
+
prefix = "#{key}[]"
|
46
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Hash
|
51
|
+
alias_method :to_query, :to_param
|
52
|
+
end
|
53
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'restclient'
|
2
|
+
require 'core_ext/object/to_query'
|
2
3
|
require 'kookaburra/exceptions'
|
3
4
|
|
4
5
|
class Kookaburra
|
@@ -99,29 +100,31 @@ class Kookaburra
|
|
99
100
|
# Convenience method to make a POST request
|
100
101
|
#
|
101
102
|
# @see APIDriver#request
|
102
|
-
def post(path, data)
|
103
|
-
request(:post, path, data)
|
103
|
+
def post(path, data = nil, headers = {})
|
104
|
+
request(:post, path, data, headers)
|
104
105
|
end
|
105
106
|
|
106
107
|
# Convenience method to make a PUT request
|
107
108
|
#
|
108
109
|
# @see APIDriver#request
|
109
|
-
def put(path, data)
|
110
|
-
request(:put, path, data)
|
110
|
+
def put(path, data = nil, headers = {})
|
111
|
+
request(:put, path, data, headers)
|
111
112
|
end
|
112
113
|
|
113
114
|
# Convenience method to make a GET request
|
114
115
|
#
|
115
116
|
# @see APIDriver#request
|
116
|
-
def get(path)
|
117
|
-
|
117
|
+
def get(path, data = nil, headers = {})
|
118
|
+
path = add_querystring_to_path(path, data)
|
119
|
+
request(:get, path, nil, headers)
|
118
120
|
end
|
119
121
|
|
120
122
|
# Convenience method to make a DELETE request
|
121
123
|
#
|
122
124
|
# @see APIDriver#request
|
123
|
-
def delete(path)
|
124
|
-
|
125
|
+
def delete(path, data = nil, headers = {})
|
126
|
+
path = add_querystring_to_path(path, data)
|
127
|
+
request(:delete, path, nil, headers)
|
125
128
|
end
|
126
129
|
|
127
130
|
# Make an HTTP request
|
@@ -161,8 +164,9 @@ class Kookaburra
|
|
161
164
|
#
|
162
165
|
# @raise [Kookaburra::UnexpectedResponse] Raised if the HTTP
|
163
166
|
# response received is not in the 2XX-3XX range.
|
164
|
-
def request(method, path, data
|
167
|
+
def request(method, path, data, headers)
|
165
168
|
data = encode(data)
|
169
|
+
headers = global_headers.merge(headers)
|
166
170
|
response = @http_client.send(method, url_for(path), *[data, headers].compact)
|
167
171
|
decode(response.body)
|
168
172
|
rescue RestClient::Exception => e
|
@@ -171,7 +175,12 @@ class Kookaburra
|
|
171
175
|
|
172
176
|
private
|
173
177
|
|
174
|
-
def
|
178
|
+
def add_querystring_to_path(path, data)
|
179
|
+
return path if data.nil? || data == {}
|
180
|
+
"#{path}?#{data.to_query}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def global_headers
|
175
184
|
self.class.headers
|
176
185
|
end
|
177
186
|
|
@@ -13,90 +13,153 @@ describe Kookaburra::APIDriver do
|
|
13
13
|
|
14
14
|
let(:client) { stub('RestClient') }
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
it 'sends PUT requests to the server and returns the response body' do
|
23
|
-
client.should_receive(:put).with(url_for('/foo'), 'bar', {}) \
|
24
|
-
.and_return(response)
|
25
|
-
api.put('/foo', 'bar').should == 'foo'
|
26
|
-
end
|
16
|
+
shared_examples_for 'any type of HTTP request' do |http_verb|
|
17
|
+
context "(#{http_verb})" do
|
18
|
+
before(:each) do
|
19
|
+
client.stub!(http_verb => response)
|
20
|
+
end
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
api.get('/foo').should == 'foo'
|
32
|
-
end
|
22
|
+
it 'returns the response body' do
|
23
|
+
api.send(http_verb, '/foo').should == 'foo'
|
24
|
+
end
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
26
|
+
it 'raises an UnexpectedResponse if the request is not successful' do
|
27
|
+
response.stub!(code: 500)
|
28
|
+
client.stub!(http_verb).and_raise(RestClient::Exception.new(response))
|
29
|
+
lambda { api.send(http_verb, '/foo') } \
|
30
|
+
.should raise_error(Kookaburra::UnexpectedResponse)
|
31
|
+
end
|
39
32
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
let(:expect_client_to_receive_headers) { ->(expected_headers) {
|
34
|
+
# Some HTTP verb methods pass data, some don't, and their arity
|
35
|
+
# is different
|
36
|
+
client.should_receive(http_verb) do |path, data_or_headers, headers|
|
37
|
+
headers ||= data_or_headers
|
38
|
+
expect(headers).to eq(expected_headers)
|
39
|
+
response
|
40
|
+
end
|
41
|
+
}}
|
42
|
+
|
43
|
+
context 'when custom global headers are specified' do
|
44
|
+
let(:api) {
|
45
|
+
klass = Class.new(Kookaburra::APIDriver) do
|
46
|
+
header 'Header-Foo', 'Baz'
|
47
|
+
header 'Header-Bar', 'Bam'
|
48
|
+
end
|
49
|
+
klass.new(configuration, client)
|
50
|
+
}
|
51
|
+
|
52
|
+
it "sets global headers on requests" do
|
53
|
+
expect_client_to_receive_headers.call('Header-Foo' => 'Baz', 'Header-Bar' => 'Bam')
|
54
|
+
api.send(http_verb, '/foo')
|
55
|
+
end
|
44
56
|
|
45
|
-
|
46
|
-
|
47
|
-
|
57
|
+
context "and additional headers are specified on a single call" do
|
58
|
+
it 'sets both the global and additional headers on the request' do
|
59
|
+
expect_client_to_receive_headers.call('Header-Foo' => 'Baz', 'Header-Bar' => 'Bam', 'Yak' => 'Shaved')
|
60
|
+
api.send(http_verb, '/foo', nil, 'Yak' => 'Shaved')
|
61
|
+
end
|
48
62
|
|
49
|
-
|
50
|
-
|
51
|
-
client.stub!(:http_verb).and_raise(RestClient::Exception.new(response))
|
52
|
-
lambda { api.request(:http_verb, '/foo') } \
|
53
|
-
.should raise_error(Kookaburra::UnexpectedResponse)
|
54
|
-
end
|
63
|
+
it 'only sets the global headers on subsequent requests' do
|
64
|
+
api.send(http_verb, '/foo', nil, 'Yak' => 'Shaved')
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
header 'Header-Foo', 'Baz'
|
60
|
-
header 'Header-Bar', 'Bam'
|
66
|
+
expect_client_to_receive_headers.call('Header-Foo' => 'Baz', 'Header-Bar' => 'Bam')
|
67
|
+
api.send(http_verb, '/foo')
|
68
|
+
end
|
61
69
|
end
|
62
|
-
klass.new(configuration, client)
|
63
|
-
}
|
64
70
|
|
65
|
-
|
66
|
-
|
67
|
-
|
71
|
+
context 'and global header values are overriden by a single call' do
|
72
|
+
it 'uses the override value for the the request' do
|
73
|
+
expect_client_to_receive_headers.call('Header-Foo' => 'Baz', 'Header-Bar' => 'Yak')
|
74
|
+
api.send(http_verb, '/foo', nil, 'Header-Bar' => 'Yak')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'uses the global value for subsequent requests' do
|
78
|
+
api.send(http_verb, '/foo', nil, 'Header-Bar' => 'Yak')
|
79
|
+
|
80
|
+
expect_client_to_receive_headers.call('Header-Foo' => 'Baz', 'Header-Bar' => 'Bam')
|
81
|
+
api.send(http_verb, '/foo')
|
82
|
+
end
|
83
|
+
end
|
68
84
|
end
|
69
|
-
end
|
70
85
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
86
|
+
context 'when headers are specified' do
|
87
|
+
it 'sets the headers on the request' do
|
88
|
+
expected_headers = {'Foo' => 'Bar', 'Baz' => 'Bam'}
|
89
|
+
expect_client_to_receive_headers.call(expected_headers)
|
90
|
+
api.send(http_verb, '/foo', nil, expected_headers)
|
75
91
|
end
|
76
|
-
klass.new(configuration, client)
|
77
|
-
}
|
78
92
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
93
|
+
it 'does not set the headers on subsequent requests' do
|
94
|
+
api.send(http_verb, '/foo', nil, :foo => :bar)
|
95
|
+
|
96
|
+
expect_client_to_receive_headers.call({})
|
97
|
+
api.send(http_verb, '/foo')
|
83
98
|
end
|
99
|
+
end
|
84
100
|
|
85
|
-
|
101
|
+
context 'when a custom decoder is specified' do
|
102
|
+
let(:api) {
|
103
|
+
klass = Class.new(Kookaburra::APIDriver) do
|
104
|
+
decode_with { |data| :some_decoded_data }
|
105
|
+
end
|
106
|
+
klass.new(configuration, client)
|
107
|
+
}
|
108
|
+
|
109
|
+
it "decodes response bodies from requests" do
|
110
|
+
api.send(http_verb, '/foo').should == :some_decoded_data
|
111
|
+
end
|
86
112
|
end
|
87
113
|
end
|
114
|
+
end
|
88
115
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
116
|
+
shared_examples_for 'it encodes request data' do |http_verb|
|
117
|
+
context "(#{http_verb})" do
|
118
|
+
before(:each) do
|
119
|
+
client.stub!(http_verb => response)
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when a custom encoder is specified' do
|
123
|
+
let(:api) {
|
124
|
+
klass = Class.new(Kookaburra::APIDriver) do
|
125
|
+
encode_with { |data|
|
126
|
+
data.should == :some_ruby_data
|
127
|
+
:some_encoded_data
|
128
|
+
}
|
129
|
+
end
|
130
|
+
klass.new(configuration, client)
|
131
|
+
}
|
132
|
+
|
133
|
+
it "encodes input to requests" do
|
134
|
+
client.should_receive(http_verb) do |_, data, _|
|
135
|
+
data.should == :some_encoded_data
|
136
|
+
response
|
137
|
+
end
|
138
|
+
|
139
|
+
api.send(http_verb, '/foo', :some_ruby_data)
|
93
140
|
end
|
94
|
-
|
95
|
-
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
96
144
|
|
97
|
-
|
98
|
-
|
145
|
+
shared_examples_for 'it encodes data as a querystring' do |http_verb|
|
146
|
+
context "(#{http_verb})" do
|
147
|
+
it 'adds data as querystirng params' do
|
148
|
+
client.should_receive(http_verb).with(url_for('/foo?bar=baz&yak=shaved'), {}) \
|
149
|
+
.and_return(response)
|
150
|
+
api.send(http_verb, '/foo', bar: 'baz', yak: 'shaved')
|
99
151
|
end
|
100
152
|
end
|
101
153
|
end
|
154
|
+
|
155
|
+
it_behaves_like 'any type of HTTP request', :get
|
156
|
+
it_behaves_like 'any type of HTTP request', :post
|
157
|
+
it_behaves_like 'any type of HTTP request', :put
|
158
|
+
it_behaves_like 'any type of HTTP request', :delete
|
159
|
+
|
160
|
+
it_behaves_like 'it encodes data as a querystring', :get
|
161
|
+
it_behaves_like 'it encodes data as a querystring', :delete
|
162
|
+
|
163
|
+
it_behaves_like 'it encodes request data', :post
|
164
|
+
it_behaves_like 'it encodes request data', :put
|
102
165
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kookaburra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-12-14 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rest-client
|
@@ -208,6 +208,8 @@ files:
|
|
208
208
|
- Rakefile
|
209
209
|
- VERSION
|
210
210
|
- kookaburra.gemspec
|
211
|
+
- lib/core_ext/object/to_param.rb
|
212
|
+
- lib/core_ext/object/to_query.rb
|
211
213
|
- lib/kookaburra.rb
|
212
214
|
- lib/kookaburra/api_driver.rb
|
213
215
|
- lib/kookaburra/assertion.rb
|
@@ -252,7 +254,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
252
254
|
version: '0'
|
253
255
|
segments:
|
254
256
|
- 0
|
255
|
-
hash: -
|
257
|
+
hash: -3075821559668997121
|
256
258
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
257
259
|
none: false
|
258
260
|
requirements:
|