songkick-transport 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +24 -8
- data/lib/songkick/transport.rb +16 -1
- data/lib/songkick/transport/base.rb +1 -1
- data/lib/songkick/transport/curb.rb +1 -1
- data/lib/songkick/transport/headers.rb +1 -0
- data/lib/songkick/transport/response.rb +11 -7
- data/spec/songkick/transport/curb_spec.rb +2 -2
- data/spec/songkick/transport/response_spec.rb +4 -4
- data/spec/songkick/transport_spec.rb +7 -0
- data/spec/spec_helper.rb +5 -0
- metadata +2 -18
data/README.rdoc
CHANGED
@@ -10,7 +10,8 @@ and serialization details. It transparently deals with parameter serialization,
|
|
10
10
|
including the following:
|
11
11
|
|
12
12
|
* Correctly CGI-escaping any data you pass in
|
13
|
-
* Nested parameters, e.g. <tt>'foo' => {'
|
13
|
+
* Nested parameters, e.g. <tt>'foo' => {'a' => 'b', 'c' => 'd'}</tt> becomes
|
14
|
+
<tt>foo[a]=b&foo[c]=d</tt>
|
14
15
|
* File uploads and multipart requests
|
15
16
|
* Entity body for POST/PUT, query string for everything else
|
16
17
|
|
@@ -20,7 +21,9 @@ We currently support three backends:
|
|
20
21
|
* Talking HTTP with {HTTParty}[http://httparty.rubyforge.org/]
|
21
22
|
* Talking directly to a {Rack}[http://rack.rubyforge.org/] app with Rack::Test
|
22
23
|
|
23
|
-
|
24
|
+
If the service you're talking to returns <tt>Content-Type: application/json</tt>,
|
25
|
+
we automatically parse the response for you. You can register parsers for other
|
26
|
+
content types as described below.
|
24
27
|
|
25
28
|
|
26
29
|
== Using the transports
|
@@ -31,6 +34,7 @@ exposes some JSON:
|
|
31
34
|
require 'sinatra'
|
32
35
|
|
33
36
|
get '/ohai' do
|
37
|
+
headers 'Content-Type' => 'application/json'
|
34
38
|
'{"hello":"world"}'
|
35
39
|
end
|
36
40
|
|
@@ -63,8 +67,9 @@ takes a reference to a Rack application, for example:
|
|
63
67
|
|
64
68
|
All transports expose exactly the same instance methods.
|
65
69
|
|
66
|
-
The client supports the +get+, +
|
67
|
-
which all take a path and an optional +Hash+ of parameters,
|
70
|
+
The client supports the +delete+, +get+, +head+, +patch+, +post+, +put+ and
|
71
|
+
+options+ methods, which all take a path and an optional +Hash+ of parameters,
|
72
|
+
for example:
|
68
73
|
|
69
74
|
client.post('/users', :username => 'bob', :password => 'foo')
|
70
75
|
|
@@ -94,10 +99,10 @@ This library was primarily developed to talk to Songkick's backend services, and
|
|
94
99
|
as such adopts some conventions that put it at a higher level of abstraction
|
95
100
|
than a vanilla HTTP client.
|
96
101
|
|
97
|
-
|
98
|
-
has the following properties:
|
102
|
+
A response object has the following properties:
|
99
103
|
|
100
|
-
* +
|
104
|
+
* +body+ -- the raw response body
|
105
|
+
* +data+ -- the result of parsing the body according to its content-type
|
101
106
|
* +headers+ -- a read-only hash-like object containing response headers
|
102
107
|
* +status+ -- the response's status code
|
103
108
|
|
@@ -127,6 +132,17 @@ If the request raises an exception, it will be of one of the following types:
|
|
127
132
|
non-successful status code, e.g. 404 or 500
|
128
133
|
|
129
134
|
|
135
|
+
=== Registering response parsers
|
136
|
+
|
137
|
+
<tt>Songkick::Transport</tt> automatically sets <tt>response.data</tt> if the
|
138
|
+
content-type of the response is <tt>application/json</tt>. You can register
|
139
|
+
parsers for other content-types like so:
|
140
|
+
|
141
|
+
Songkick::Transport.register_parser('application/yaml', YAML)
|
142
|
+
|
143
|
+
The parser object you register must respond to <tt>parse(string)</tt>.
|
144
|
+
|
145
|
+
|
130
146
|
=== Nested parameters
|
131
147
|
|
132
148
|
All transports support serialization of nested parameters, for example you can
|
@@ -297,7 +313,7 @@ the total time spent calling backend services during the block.
|
|
297
313
|
|
298
314
|
The MIT License
|
299
315
|
|
300
|
-
Copyright (c) 2012 Songkick
|
316
|
+
Copyright (c) 2012-2013 Songkick
|
301
317
|
|
302
318
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
303
319
|
this software and associated documentation files (the "Software"), to deal in
|
data/lib/songkick/transport.rb
CHANGED
@@ -9,7 +9,7 @@ module Songkick
|
|
9
9
|
DEFAULT_TIMEOUT = 5
|
10
10
|
DEFAULT_FORMAT = :json
|
11
11
|
|
12
|
-
HTTP_VERBS = %w[get post put delete
|
12
|
+
HTTP_VERBS = %w[options head get patch post put delete]
|
13
13
|
USE_BODY = %w[post put]
|
14
14
|
FORM_ENCODING = 'application/x-www-form-urlencoded'
|
15
15
|
|
@@ -33,6 +33,21 @@ module Songkick
|
|
33
33
|
autoload :ConnectionFailedError,ROOT + '/transport/upstream_error'
|
34
34
|
autoload :InvalidJSONError, ROOT + '/transport/upstream_error'
|
35
35
|
autoload :HttpError, ROOT + '/transport/http_error'
|
36
|
+
|
37
|
+
def self.regsiter_parser(content_type, parser)
|
38
|
+
@parsers ||= {}
|
39
|
+
@parsers[content_type] = parser
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.parser_for(content_type)
|
43
|
+
parser = @parsers && @parsers[content_type]
|
44
|
+
unless parser
|
45
|
+
raise TypeError, "Could not find a parser for content-type: #{content_type}"
|
46
|
+
end
|
47
|
+
parser
|
48
|
+
end
|
49
|
+
|
50
|
+
regsiter_parser 'application/json', Yajl::Parser
|
36
51
|
|
37
52
|
IO = UploadIO
|
38
53
|
|
@@ -53,7 +53,7 @@ module Songkick
|
|
53
53
|
if req.use_body?
|
54
54
|
connection.__send__("http_#{req.verb}", req.body)
|
55
55
|
else
|
56
|
-
connection.
|
56
|
+
connection.http(req.verb.upcase)
|
57
57
|
end
|
58
58
|
|
59
59
|
process(req, connection.response_code, response_headers, connection.body_str)
|
@@ -16,16 +16,20 @@ module Songkick
|
|
16
16
|
Transport.logger.warn "Request returned invalid JSON: #{request}"
|
17
17
|
raise Transport::InvalidJSONError, request
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.parse(body, content_type)
|
21
|
+
return body unless body.is_a?(String)
|
22
|
+
return nil if body.strip == ''
|
23
|
+
|
24
|
+
content_type = (content_type || '').split(/\s*;\s*/).first
|
25
|
+
Transport.parser_for(content_type).parse(body)
|
26
|
+
end
|
19
27
|
|
20
|
-
attr_reader :data, :headers, :status
|
28
|
+
attr_reader :body, :data, :headers, :status
|
21
29
|
|
22
30
|
def initialize(status, headers, body)
|
23
|
-
@
|
24
|
-
|
25
|
-
else
|
26
|
-
body
|
27
|
-
end
|
28
|
-
|
31
|
+
@body = body
|
32
|
+
@data = Response.parse(body, headers['Content-Type'])
|
29
33
|
@headers = Headers.new(headers)
|
30
34
|
@status = status.to_i
|
31
35
|
end
|
@@ -6,7 +6,7 @@ describe Songkick::Transport::Response do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe "200 with a body" do
|
9
|
-
let(:response) { process("", 200, {}, '{"hello":"world"}') }
|
9
|
+
let(:response) { process("", 200, {"Content-Type" => "application/json; charset=utf-8"}, '{"hello":"world"}') }
|
10
10
|
|
11
11
|
it "is an OK" do
|
12
12
|
response.should be_a(Songkick::Transport::Response::OK)
|
@@ -18,7 +18,7 @@ describe Songkick::Transport::Response do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
describe "200 with a body with an empty line in it" do
|
21
|
-
let(:response) { process("", 200, {}, "{\"hello\":\"world\"\n\n}") }
|
21
|
+
let(:response) { process("", 200, {"Content-Type" => "application/json"}, "{\"hello\":\"world\"\n\n}") }
|
22
22
|
|
23
23
|
it "is an OK" do
|
24
24
|
response.should be_a(Songkick::Transport::Response::OK)
|
@@ -30,7 +30,7 @@ describe Songkick::Transport::Response do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
describe "200 with a parsed body" do
|
33
|
-
let(:response) { process("", 200, {}, {"hello" => "world"}) }
|
33
|
+
let(:response) { process("", 200, {"Content-Type" => "application/json"}, {"hello" => "world"}) }
|
34
34
|
|
35
35
|
it "exposes its data" do
|
36
36
|
response.data.should == {"hello" => "world"}
|
@@ -62,7 +62,7 @@ describe Songkick::Transport::Response do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
describe "409 with a body" do
|
65
|
-
let(:response) { process("", 409, {}, '{"errors":[]}') }
|
65
|
+
let(:response) { process("", 409, {"Content-Type" => "application/json"}, '{"errors":[]}') }
|
66
66
|
|
67
67
|
it "is a UserError" do
|
68
68
|
response.should be_a(Songkick::Transport::Response::UserError)
|
@@ -66,6 +66,13 @@ describe Songkick::Transport do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
describe :options do
|
70
|
+
it "sends an OPTIONS request" do
|
71
|
+
response = transport.options("/.well-known/host-meta")
|
72
|
+
response.headers["Access-Control-Allow-Methods"].should == "GET, PUT, DELETE"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
69
76
|
describe :post do
|
70
77
|
it "sends data using POST" do
|
71
78
|
data = transport.post("/artists", :name => "Amon Tobin").data
|
data/spec/spec_helper.rb
CHANGED
@@ -29,6 +29,11 @@ class TestApp < Sinatra::Base
|
|
29
29
|
get '/artists/:id' do
|
30
30
|
Yajl::Encoder.encode('id' => params[:id].to_i)
|
31
31
|
end
|
32
|
+
|
33
|
+
options '/.well-known/host-meta' do
|
34
|
+
headers 'Access-Control-Allow-Methods' => 'GET, PUT, DELETE'
|
35
|
+
''
|
36
|
+
end
|
32
37
|
|
33
38
|
post '/artists' do
|
34
39
|
Yajl::Encoder.encode('id' => 'new', 'name' => params[:name].upcase)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: songkick-transport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multipart-post
|
@@ -91,22 +91,6 @@ dependencies:
|
|
91
91
|
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: 0.4.0
|
94
|
-
- !ruby/object:Gem::Dependency
|
95
|
-
name: guard-rspec
|
96
|
-
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
|
-
requirements:
|
99
|
-
- - ! '>='
|
100
|
-
- !ruby/object:Gem::Version
|
101
|
-
version: '0'
|
102
|
-
type: :development
|
103
|
-
prerelease: false
|
104
|
-
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
|
-
requirements:
|
107
|
-
- - ! '>='
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
110
94
|
- !ruby/object:Gem::Dependency
|
111
95
|
name: rspec
|
112
96
|
requirement: !ruby/object:Gem::Requirement
|