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 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 JRUBY (not that you must run
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.0.0
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.0.0"
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-10-14"
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
- request(:get, path)
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
- request(:delete, path)
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 = nil)
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 headers
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
- it 'sends POST requests to the server and returns the response body' do
17
- client.should_receive(:post).with(url_for('/foo'), 'bar', {}) \
18
- .and_return(response)
19
- api.post('/foo', 'bar').should == 'foo'
20
- end
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
- it 'sends GET requests to the server and returns the response body' do
29
- client.should_receive(:get).with(url_for('/foo'), {}) \
30
- .and_return(response)
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
- it 'sends DELETE requests to the server and returns the response body' do
35
- client.should_receive(:delete).with(url_for('/foo'), {}) \
36
- .and_return(response)
37
- api.delete('/foo').should == 'foo'
38
- end
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
- describe 'any type of HTTP request' do
41
- before(:each) do
42
- client.stub!(:http_verb => response)
43
- end
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
- it 'returns the response body' do
46
- api.request(:http_verb, '/foo', 'bar').should == 'foo'
47
- end
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
- it 'raises an UnexpectedResponse if the request is not successful' do
50
- response.stub!(code: 500)
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
- context 'when custom headers are specified' do
57
- let(:api) {
58
- klass = Class.new(Kookaburra::APIDriver) do
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
- it "sets headers on requests" do
66
- client.should_receive(:http_verb).with(url_for('/foo'), {}, 'Header-Foo' => 'Baz', 'Header-Bar' => 'Bam')
67
- api.request(:http_verb, '/foo', {})
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
- context 'when a custom encoder is specified' do
72
- let(:api) {
73
- klass = Class.new(Kookaburra::APIDriver) do
74
- encode_with { |data| :some_encoded_data }
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
- it "encodes input to requests" do
80
- client.should_receive(:http_verb) do |_, data, _|
81
- data.should == :some_encoded_data
82
- response
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
- api.request(:http_verb, '/foo', :ruby_data)
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
- context 'when a custom decoder is specified' do
90
- let(:api) {
91
- klass = Class.new(Kookaburra::APIDriver) do
92
- decode_with { |data| :some_decoded_data }
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
- klass.new(configuration, client)
95
- }
141
+ end
142
+ end
143
+ end
96
144
 
97
- it "decodes response bodies from requests" do
98
- api.request(:http_verb, '/foo').should == :some_decoded_data
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.0.0
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-10-14 00:00:00.000000000 Z
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: -1361909781639378810
257
+ hash: -3075821559668997121
256
258
  required_rubygems_version: !ruby/object:Gem::Requirement
257
259
  none: false
258
260
  requirements: