sawyer 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/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/README.md +17 -0
- data/Rakefile +135 -0
- data/SPEC.md +71 -0
- data/example/client.rb +50 -0
- data/example/nigiri.schema.json +47 -0
- data/example/server.rb +114 -0
- data/example/user.schema.json +51 -0
- data/lib/sawyer.rb +14 -0
- data/lib/sawyer/agent.rb +82 -0
- data/lib/sawyer/relation.rb +253 -0
- data/lib/sawyer/resource.rb +69 -0
- data/lib/sawyer/response.rb +42 -0
- data/sawyer.gemspec +78 -0
- data/test/agent_test.rb +60 -0
- data/test/helper.rb +7 -0
- data/test/relation_test.rb +109 -0
- data/test/resource_test.rb +99 -0
- data/test/response_test.rb +54 -0
- metadata +116 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'yajl'
|
2
|
+
|
3
|
+
module Sawyer
|
4
|
+
class Response
|
5
|
+
attr_reader :agent,
|
6
|
+
:status,
|
7
|
+
:headers,
|
8
|
+
:data,
|
9
|
+
:rels
|
10
|
+
|
11
|
+
# Builds a Response after a completed request.
|
12
|
+
#
|
13
|
+
# agent - The Sawyer::Agent that is managing the API connection.
|
14
|
+
# res - A Faraday::Response.
|
15
|
+
def initialize(agent, res)
|
16
|
+
@agent = agent
|
17
|
+
@status = res.status
|
18
|
+
@headers = res.headers
|
19
|
+
@data = process_data(@agent.decode_body(res.body))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Turns parsed contents from an API response into a Resource or
|
23
|
+
# collection of Resources.
|
24
|
+
#
|
25
|
+
# data - Either an Array or Hash parsed from JSON.
|
26
|
+
#
|
27
|
+
# Returns either a Resource or Array of Resources.
|
28
|
+
def process_data(data)
|
29
|
+
case data
|
30
|
+
when Hash then Resource.new(agent, data)
|
31
|
+
when Array then data.map { |hash| process_data(hash) }
|
32
|
+
when nil then nil
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Unable to process #{data.inspect}. Want a Hash or Array"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
%(#<#{self.class}: #{@status} @rels=#{@rels.inspect} @data=#{@data.inspect}>)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/sawyer.gemspec
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
## This is the rakegem gemspec template. Make sure you read and understand
|
2
|
+
## all of the comments. Some sections require modification, and others can
|
3
|
+
## be deleted if you don't need them. Once you understand the contents of
|
4
|
+
## this file, feel free to delete any comments that begin with two hash marks.
|
5
|
+
## You can find comprehensive Gem::Specification documentation, at
|
6
|
+
## http://docs.rubygems.org/read/chapter/20
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.rubygems_version = '1.3.5'
|
11
|
+
|
12
|
+
## Leave these as is they will be modified for you by the rake gemspec task.
|
13
|
+
## If your rubyforge_project name is different, then edit it and comment out
|
14
|
+
## the sub! line in the Rakefile
|
15
|
+
s.name = 'sawyer'
|
16
|
+
s.version = '0.0.1'
|
17
|
+
s.date = '2012-09-25'
|
18
|
+
s.rubyforge_project = 'sawyer'
|
19
|
+
|
20
|
+
## Make sure your summary is short. The description may be as long
|
21
|
+
## as you like.
|
22
|
+
s.summary = "Secret User Agent of HTTP"
|
23
|
+
s.description = "#{s.summary} built on Faraday"
|
24
|
+
|
25
|
+
## List the primary authors. If there are a bunch of authors, it's probably
|
26
|
+
## better to set the email to an email list or something. If you don't have
|
27
|
+
## a custom homepage, consider using your GitHub URL or the like.
|
28
|
+
s.authors = ["Rick Olson"]
|
29
|
+
s.email = 'technoweenie@gmail.com'
|
30
|
+
s.homepage = 'https://github.com/technoweenie/sawyer'
|
31
|
+
|
32
|
+
## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
|
33
|
+
## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
|
34
|
+
s.require_paths = %w[lib]
|
35
|
+
|
36
|
+
## List your runtime dependencies here. Runtime dependencies are those
|
37
|
+
## that are needed for an end user to actually USE your code.
|
38
|
+
s.add_dependency('faraday', ['~> 0.8.4'])
|
39
|
+
s.add_dependency('uri_template', ['~> 0.5.0'])
|
40
|
+
s.add_dependency('yajl-ruby', ['~> 1.1.0'])
|
41
|
+
|
42
|
+
## List your development dependencies here. Development dependencies are
|
43
|
+
## those that are only needed during development
|
44
|
+
#s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"])
|
45
|
+
|
46
|
+
## Leave this section as-is. It will be automatically generated from the
|
47
|
+
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
48
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
49
|
+
# = MANIFEST =
|
50
|
+
s.files = %w[
|
51
|
+
Gemfile
|
52
|
+
LICENSE.md
|
53
|
+
README.md
|
54
|
+
Rakefile
|
55
|
+
SPEC.md
|
56
|
+
example/client.rb
|
57
|
+
example/nigiri.schema.json
|
58
|
+
example/server.rb
|
59
|
+
example/user.schema.json
|
60
|
+
lib/sawyer.rb
|
61
|
+
lib/sawyer/agent.rb
|
62
|
+
lib/sawyer/relation.rb
|
63
|
+
lib/sawyer/resource.rb
|
64
|
+
lib/sawyer/response.rb
|
65
|
+
sawyer.gemspec
|
66
|
+
test/agent_test.rb
|
67
|
+
test/helper.rb
|
68
|
+
test/relation_test.rb
|
69
|
+
test/resource_test.rb
|
70
|
+
test/response_test.rb
|
71
|
+
]
|
72
|
+
# = MANIFEST =
|
73
|
+
|
74
|
+
## Test files will be grabbed from the file list. Make sure the path glob
|
75
|
+
## matches what you actually use.
|
76
|
+
s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
|
77
|
+
end
|
78
|
+
|
data/test/agent_test.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
|
3
|
+
module Sawyer
|
4
|
+
class AgentTest < TestCase
|
5
|
+
def setup
|
6
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
7
|
+
@agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
|
8
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
9
|
+
conn.adapter :test, @stubs
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_starts_a_session
|
14
|
+
@stubs.get '/a/' do |env|
|
15
|
+
assert_equal 'foo.com', env[:url].host
|
16
|
+
|
17
|
+
[200, {}, Yajl.dump(
|
18
|
+
:_links => {
|
19
|
+
:users => {:href => '/users'}})]
|
20
|
+
end
|
21
|
+
|
22
|
+
res = @agent.start
|
23
|
+
|
24
|
+
assert_equal 200, res.status
|
25
|
+
assert_kind_of Sawyer::Resource, resource = res.data
|
26
|
+
|
27
|
+
assert_equal '/users', resource.rels[:users].href
|
28
|
+
assert_equal :get, resource.rels[:users].method
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_requests_with_body_and_options
|
32
|
+
@stubs.post '/a/b/c' do |env|
|
33
|
+
assert_equal '{"a":1}', env[:body]
|
34
|
+
assert_equal 'abc', env[:request_headers]['x-test']
|
35
|
+
assert_equal 'foo=bar', env[:url].query
|
36
|
+
[200, {}, "{}"]
|
37
|
+
end
|
38
|
+
|
39
|
+
res = @agent.call :post, 'b/c' , {:a => 1},
|
40
|
+
:headers => {"X-Test" => "abc"},
|
41
|
+
:query => {:foo => 'bar'}
|
42
|
+
assert_equal 200, res.status
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_requests_with_body_and_options_to_get
|
46
|
+
@stubs.get '/a/b/c' do |env|
|
47
|
+
assert_nil env[:body]
|
48
|
+
assert_equal 'abc', env[:request_headers]['x-test']
|
49
|
+
assert_equal 'foo=bar', env[:url].query
|
50
|
+
[200, {}, "{}"]
|
51
|
+
end
|
52
|
+
|
53
|
+
res = @agent.call :get, 'b/c' , {:a => 1},
|
54
|
+
:headers => {"X-Test" => "abc"},
|
55
|
+
:query => {:foo => 'bar'}
|
56
|
+
assert_equal 200, res.status
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
|
3
|
+
module Sawyer
|
4
|
+
class RelationTest < TestCase
|
5
|
+
def test_builds_relation_from_hash
|
6
|
+
hash = {:href => '/users/1', :method => 'post'}
|
7
|
+
rel = Sawyer::Relation.from_link(nil, :self, hash)
|
8
|
+
|
9
|
+
assert_equal :self, rel.name
|
10
|
+
assert_equal '/users/1', rel.href
|
11
|
+
assert_equal :post, rel.method
|
12
|
+
assert_equal [:post], rel.available_methods.to_a
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_builds_multiple_rels_from_multiple_methods
|
16
|
+
index = {
|
17
|
+
'comments' => {:href => '/comments', :method => 'get,post'}
|
18
|
+
}
|
19
|
+
|
20
|
+
rels = Sawyer::Relation.from_links(nil, index)
|
21
|
+
assert_equal 1, rels.size
|
22
|
+
assert_equal [:comments], rels.keys
|
23
|
+
|
24
|
+
assert rel = rels[:comments]
|
25
|
+
assert_equal '/comments', rel.href
|
26
|
+
assert_equal :get, rel.method
|
27
|
+
assert_equal [:get, :post], rel.available_methods.to_a
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_builds_rels_from_hash_index
|
31
|
+
index = {
|
32
|
+
'self' => {:href => '/users/1'}
|
33
|
+
}
|
34
|
+
|
35
|
+
rels = Sawyer::Relation.from_links(nil, index)
|
36
|
+
|
37
|
+
assert_equal 1, rels.size
|
38
|
+
assert_equal [:self], rels.keys
|
39
|
+
assert rel = rels[:self]
|
40
|
+
assert_equal :self, rel.name
|
41
|
+
assert_equal '/users/1', rel.href
|
42
|
+
assert_equal :get, rel.method
|
43
|
+
assert_equal [:get], rel.available_methods.to_a
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_builds_rels_from_nil
|
47
|
+
rels = Sawyer::Relation.from_links nil, nil
|
48
|
+
assert_equal 0, rels.size
|
49
|
+
assert_equal [], rels.keys
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_relation_api_calls
|
53
|
+
agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
|
54
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
55
|
+
conn.adapter :test do |stubs|
|
56
|
+
stubs.get '/a/1' do
|
57
|
+
[200, {}, '{}']
|
58
|
+
end
|
59
|
+
stubs.delete '/a/1' do
|
60
|
+
[204, {}, '{}']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
rel = Sawyer::Relation.new agent, :self, "/a/1", "get,put,delete"
|
66
|
+
assert_equal :get, rel.method
|
67
|
+
[:get, :put, :delete].each do |m|
|
68
|
+
assert rel.available_methods.include?(m), "#{m.inspect} is not available: #{rel.available_methods.inspect}"
|
69
|
+
end
|
70
|
+
|
71
|
+
assert_equal 200, rel.call.status
|
72
|
+
assert_equal 200, rel.call(:method => :head).status
|
73
|
+
assert_equal 204, rel.call(nil, :method => :delete).status
|
74
|
+
assert_raises ArgumentError do
|
75
|
+
rel.call nil, :method => :post
|
76
|
+
end
|
77
|
+
|
78
|
+
assert_equal 200, rel.head.status
|
79
|
+
assert_equal 200, rel.get.status
|
80
|
+
assert_equal 204, rel.delete.status
|
81
|
+
|
82
|
+
assert_raises ArgumentError do
|
83
|
+
rel.post
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_relation_api_calls_with_uri_tempate
|
88
|
+
agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
|
89
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
90
|
+
conn.adapter :test do |stubs|
|
91
|
+
stubs.get '/octocat/hello' do |env|
|
92
|
+
assert_equal "a=1&b=2", env[:url].query
|
93
|
+
[200, {}, '{}']
|
94
|
+
end
|
95
|
+
|
96
|
+
stubs.get '/a' do
|
97
|
+
[404, {}, '{}']
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
rel = Sawyer::Relation.new agent, :repo, "{/user,repo}{?a,b}"
|
103
|
+
|
104
|
+
assert_equal 404, rel.get.status
|
105
|
+
assert_equal 200, rel.get(:uri => {'user' => 'octocat', 'repo' => 'hello', 'a' => 1, 'b' => 2}).status
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
|
3
|
+
module Sawyer
|
4
|
+
class ResourceTest < TestCase
|
5
|
+
def test_accessible_keys
|
6
|
+
res = Resource.new :agent, :a => 1,
|
7
|
+
:_links => {:self => {:href => '/'}}
|
8
|
+
|
9
|
+
assert_equal 1, res.a
|
10
|
+
assert res.rels[:self]
|
11
|
+
assert_equal :agent, res.agent
|
12
|
+
assert_equal 1, res.fields.size
|
13
|
+
assert res.fields.include?(:a)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_clashing_keys
|
17
|
+
res = Resource.new :agent, :agent => 1, :rels => 2, :fields => 3,
|
18
|
+
:_links => {:self => {:href => '/'}}
|
19
|
+
|
20
|
+
assert_equal 1, res.agent
|
21
|
+
assert_equal 2, res.rels
|
22
|
+
assert_equal 3, res.fields
|
23
|
+
|
24
|
+
assert res._rels[:self]
|
25
|
+
assert_equal :agent, res._agent
|
26
|
+
assert_equal 3, res._fields.size
|
27
|
+
[:agent, :rels, :fields].each do |f|
|
28
|
+
assert res._fields.include?(f)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_nested_object
|
33
|
+
res = Resource.new :agent,
|
34
|
+
:user => {:id => 1, :_links => {:self => {:href => '/users/1'}}},
|
35
|
+
:_links => {:self => {:href => '/'}}
|
36
|
+
|
37
|
+
assert_equal '/', res.rels[:self].href
|
38
|
+
assert_kind_of Resource, res.user
|
39
|
+
assert_equal 1, res.user.id
|
40
|
+
assert_equal '/users/1', res.user.rels[:self].href
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_nested_collection
|
44
|
+
res = Resource.new :agent,
|
45
|
+
:users => [{:id => 1, :_links => {:self => {:href => '/users/1'}}}],
|
46
|
+
:_links => {:self => {:href => '/'}}
|
47
|
+
|
48
|
+
assert_equal '/', res.rels[:self].href
|
49
|
+
assert_kind_of Array, res.users
|
50
|
+
|
51
|
+
assert user = res.users.first
|
52
|
+
assert_kind_of Resource, user
|
53
|
+
assert_equal 1, user.id
|
54
|
+
assert_equal '/users/1', user.rels[:self].href
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_attribute_predicates
|
58
|
+
res = Resource.new :agent, :a => 1, :b => true, :c => nil, :d => false
|
59
|
+
|
60
|
+
assert res.a?
|
61
|
+
assert res.b?
|
62
|
+
assert !res.c?
|
63
|
+
assert !res.d?
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_attribute_setter
|
67
|
+
res = Resource.new :agent, :a => 1
|
68
|
+
assert_equal 1, res.a
|
69
|
+
assert !res.key?(:b)
|
70
|
+
|
71
|
+
res.b = 2
|
72
|
+
assert_equal 2, res.b
|
73
|
+
assert res.key?(:b)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_dynamic_attribute_methods_from_getter
|
77
|
+
res = Resource.new :agent, :a => 1
|
78
|
+
assert res.key?(:a)
|
79
|
+
assert !res.respond_to?(:a)
|
80
|
+
assert !res.respond_to?(:a=)
|
81
|
+
|
82
|
+
assert_equal 1, res.a
|
83
|
+
assert res.respond_to?(:a)
|
84
|
+
assert res.respond_to?(:a=)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_dynamic_attribute_methods_from_setter
|
88
|
+
res = Resource.new :agent, :a => 1
|
89
|
+
assert !res.key?(:b)
|
90
|
+
assert !res.respond_to?(:b)
|
91
|
+
assert !res.respond_to?(:b=)
|
92
|
+
|
93
|
+
res.b = 1
|
94
|
+
assert res.key?(:b)
|
95
|
+
assert res.respond_to?(:b)
|
96
|
+
assert res.respond_to?(:b=)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
|
3
|
+
module Sawyer
|
4
|
+
class ResponseTest < TestCase
|
5
|
+
def setup
|
6
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
7
|
+
@agent = Sawyer::Agent.new "http://foo.com" do |conn|
|
8
|
+
conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
9
|
+
conn.adapter :test, @stubs do |stub|
|
10
|
+
stub.get '/' do
|
11
|
+
[200, {'Content-Type' => 'application/json'}, Yajl.dump(
|
12
|
+
:a => 1,
|
13
|
+
:_links => {
|
14
|
+
:self => {:href => '/a', :method => 'POST'}
|
15
|
+
}
|
16
|
+
)]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
@res = @agent.start
|
22
|
+
assert_kind_of Sawyer::Response, @res
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_gets_status
|
26
|
+
assert_equal 200, @res.status
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_gets_headers
|
30
|
+
assert_equal 'application/json', @res.headers['content-type']
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_gets_body
|
34
|
+
assert_equal 1, @res.data.a
|
35
|
+
assert_equal [:a], @res.data.fields.to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_gets_rels
|
39
|
+
assert_equal '/a', @res.data.rels[:self].href
|
40
|
+
assert_equal :post, @res.data.rels[:self].method
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_makes_request_from_relation
|
44
|
+
@stubs.post '/a' do
|
45
|
+
[201, {}, ""]
|
46
|
+
end
|
47
|
+
|
48
|
+
res = @res.data.rels[:self].call
|
49
|
+
assert_equal 201, res.status
|
50
|
+
assert_nil res.data
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|