hyperclient 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,3 @@
1
- require 'hyperclient/http'
2
1
  require 'hyperclient/resource'
3
2
  require 'uri_template'
4
3
 
@@ -6,11 +5,6 @@ module Hyperclient
6
5
  # Internal: The Link is used to let a Resource interact with the API.
7
6
  #
8
7
  class Link
9
- extend Forwardable
10
- # Public: Delegate all HTTP methods (get, post, put, delete, options and
11
- # head) to the http connection.
12
- def_delegators :http, :get, :post, :put, :delete, :options, :head
13
-
14
8
  # Public: Initializes a new Link.
15
9
  #
16
10
  # link - The String with the URI of the link.
@@ -23,15 +17,10 @@ module Hyperclient
23
17
  @uri_variables = uri_variables
24
18
  end
25
19
 
26
- # Public: Returns the Resource which the Link is pointing to.
27
- def resource
28
- @resource ||=Resource.new(http.get, @entry_point)
29
- end
30
-
31
20
  # Public: Indicates if the link is an URITemplate or a regular URI.
32
21
  #
33
22
  # Returns true if it is templated.
34
- # Returns false if it nos templated.
23
+ # Returns false if it not templated.
35
24
  def templated?
36
25
  !!@link['templated']
37
26
  end
@@ -56,12 +45,48 @@ module Hyperclient
56
45
  @url ||= URITemplate.new(@link['href']).expand(@uri_variables)
57
46
  end
58
47
 
59
- private
60
- # Internal: Returns the HTTP client used to interact with the API.
61
- def http
62
- @http ||= HTTP.new(url, @entry_point.config)
48
+ # Public: Returns the Resource which the Link is pointing to.
49
+ def resource
50
+ @resource ||=Resource.new(get.body, @entry_point)
51
+ end
52
+
53
+ def connection
54
+ @entry_point.connection
55
+ end
56
+
57
+ def get
58
+ connection.get(url)
59
+ end
60
+
61
+ def options
62
+ connection.run_request(:options, url, nil, nil)
63
63
  end
64
64
 
65
+ def head
66
+ connection.head(url)
67
+ end
68
+
69
+ def delete
70
+ connection.delete(url)
71
+ end
72
+
73
+ def post(params)
74
+ connection.post(url, params)
75
+ end
76
+
77
+ def put(params)
78
+ connection.put(url, params)
79
+ end
80
+
81
+ def patch(params)
82
+ connection.patch(url, params)
83
+ end
84
+
85
+ def inspect
86
+ "#<#{self.class.name} #{@link}>"
87
+ end
88
+
89
+ private
65
90
  # Internal: Delegate the method to the API if it exists.
66
91
  #
67
92
  # This allows `api.links.posts.embedded` instead of
@@ -16,6 +16,7 @@ module Hyperclient
16
16
  # entry_point - The EntryPoint object to inject the cofnigutation.
17
17
  #
18
18
  def initialize(collection, entry_point)
19
+ raise "Invalid response for LinkCollection. The response was: #{collection.inspect}" if collection && !collection.respond_to?(:inject)
19
20
  @collection = (collection || {}).inject({}) do |hash, (name, link)|
20
21
  hash.update(name => Link.new(link, entry_point))
21
22
  end
@@ -19,12 +19,13 @@ module Hyperclient
19
19
  attr_reader :embedded
20
20
 
21
21
  # Public: Delegate all HTTP methods (get, post, put, delete, options and
22
- # head) to its Link.
22
+ # head) to its self link.
23
23
  def_delegators :self_link, :get, :post, :put, :delete, :options, :head
24
24
 
25
25
  # Public: Initializes a Resource.
26
+ #
26
27
  # representation - The hash with the HAL representation of the Resource.
27
- # entry_point - The EntryPoint object to inject the cofnigutation.
28
+ # entry_point - The EntryPoint object to inject the configutation.
28
29
  def initialize(representation, entry_point)
29
30
  @links = LinkCollection.new(representation['_links'], entry_point)
30
31
  @embedded = ResourceCollection.new(representation['_embedded'], entry_point)
@@ -32,9 +33,13 @@ module Hyperclient
32
33
  @entry_point = entry_point
33
34
  end
34
35
 
36
+ def inspect
37
+ "#<#{self.class.name} self_link:#{self_link.inspect} attributes:#{@attributes.inspect}>"
38
+ end
39
+
35
40
  private
36
41
  # Internal: Returns the self Link of the Resource. Used to handle the HTTP
37
- # connections.
42
+ # methods.
38
43
  def self_link
39
44
  @links['self']
40
45
  end
@@ -1,3 +1,3 @@
1
1
  module Hyperclient
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/hyperclient.rb CHANGED
@@ -1,7 +1,15 @@
1
+ require 'hyperclient/entry_point'
2
+ require "hyperclient/version"
3
+
1
4
  # Public: Hyperclient namespace.
2
5
  #
3
6
  module Hyperclient
7
+ # Public: Convenience method to create new EntryPoints.
8
+ #
9
+ # url - A String with the url of the API.
10
+ #
11
+ # Returns a Hyperclient::EntryPoint
12
+ def self.new(url)
13
+ Hyperclient::EntryPoint.new(url)
14
+ end
4
15
  end
5
-
6
- require 'hyperclient/entry_point'
7
- require "hyperclient/version"
@@ -0,0 +1,29 @@
1
+ require_relative '../test_helper'
2
+ require_relative '../../lib/faraday/connection'
3
+
4
+ module Faraday
5
+ describe Connection do
6
+ describe 'digest_auth' do
7
+ let(:connection) do
8
+ Faraday.new('http://api.example.org/') do |builder|
9
+ builder.request :url_encoded
10
+ builder.adapter :net_http
11
+ end
12
+ end
13
+
14
+ it 'inserts the DigestAuth middleware at the top' do
15
+ connection.digest_auth('user', 'password')
16
+
17
+ connection.builder.handlers.first.klass.must_equal Faraday::Request::DigestAuth
18
+ end
19
+
20
+ it 'passes the user and password to the middleware' do
21
+ connection.digest_auth('user', 'password')
22
+
23
+ Faraday::Request::DigestAuth.expects(:new).with(anything, 'user', 'password').returns(stub_everything)
24
+
25
+ connection.get('/')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../test_helper'
2
+ require 'faraday/request/digest_authentication'
3
+
4
+ module Faraday
5
+ describe Request::DigestAuth do
6
+ let(:connection) do
7
+ Faraday.new('http://api.example.org/') do |builder|
8
+ builder.request :digest, 'USER', 'PASS'
9
+ builder.adapter :net_http
10
+ end
11
+ end
12
+
13
+ describe 'when the server does not return a 401' do
14
+ it 'does nothing' do
15
+ stub_request(:get, 'http://api.example.org/productions/1').
16
+ to_return(status: 500, body: 'Foo body')
17
+
18
+ response = connection.get('/productions/1')
19
+ response.body.must_equal 'Foo body'
20
+ end
21
+ end
22
+
23
+ describe 'when the server returns a 401' do
24
+ let(:first_call_headers) { 'Digest realm="MyApp", algorithm=MD5' }
25
+ let(:second_call_headers) { 'Digest username="USER", realm="MyApp", uri="/", algorithm="MD5"' }
26
+ it 'authenticates using digest' do
27
+ stub_request(:get, 'http://api.example.org/productions/1').
28
+ with(body: nil).
29
+ to_return(status: 401, headers: {'www-authenticate' => first_call_headers})
30
+
31
+ stub_request(:get, 'http://api.example.org/productions/1').
32
+ with(body: "{\"foo\":1}",
33
+ headers: {'Authorization' => %r{second_call_headers}}).
34
+ to_return(body: '{"resource": "This is the resource"}',
35
+ headers: {content_type: 'application/json'})
36
+
37
+ connection.get('/productions/1')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -3,48 +3,31 @@ require 'hyperclient/entry_point'
3
3
 
4
4
  module Hyperclient
5
5
  describe EntryPoint do
6
- let(:api) do
6
+ let(:entry_point) do
7
7
  EntryPoint.new 'http://my.api.org'
8
8
  end
9
9
 
10
- before do
11
- stub_request(:get, "http://my.api.org/").
12
- to_return(body: '{"_links": {"self": {"href": "http://my.api.org"}}}', headers: {content_type: 'application/json'})
13
- end
14
-
15
- describe 'initialize' do
16
- it 'initializes a Resource at the entry point' do
17
- api.links['self'].url.must_equal 'http://my.api.org'
10
+ describe 'connection' do
11
+ it 'creates a Faraday connection with the entry point url' do
12
+ entry_point.connection.url_prefix.to_s.must_equal 'http://my.api.org/'
18
13
  end
19
14
 
20
- it 'setups the HTTP config' do
21
- options = {:headers => {'accept-encoding' => 'deflate, gzip'}}
22
-
23
- api = EntryPoint.new('http://my.api.org', options)
24
-
25
- api.config[:headers].must_include 'accept-encoding'
15
+ it 'creates a Faraday connection with the default headers' do
16
+ entry_point.headers['Content-Type'].must_equal 'application/json'
17
+ entry_point.headers['Accept'].must_equal 'application/json'
26
18
  end
27
19
 
28
- it 'sets the base_uri for HTTP' do
29
- api = EntryPoint.new('http://my.api.org')
30
-
31
- api.config[:base_uri].must_equal 'http://my.api.org'
20
+ it 'creates a Faraday connection with the default block' do
21
+ handlers = entry_point.connection.builder.handlers
22
+ handlers.must_include FaradayMiddleware::EncodeJson
23
+ handlers.must_include FaradayMiddleware::ParseJson
24
+ handlers.must_include Faraday::Adapter::NetHttp
32
25
  end
33
26
  end
34
27
 
35
- describe 'method missing' do
36
- it 'delegates undefined methods to the API when they exist' do
37
- Resource.any_instance.expects(:foo).returns 'foo'
38
- api.foo.must_equal 'foo'
39
- end
40
-
41
- it 'responds to missing methods' do
42
- Resource.any_instance.expects(:respond_to?).with('foo').returns(true)
43
- api.respond_to?(:foo).must_equal true
44
- end
45
-
46
- it 'raises an error when the method does not exist in the API' do
47
- lambda { api.this_method_does_not_exist }.must_raise(NoMethodError)
28
+ describe 'initialize' do
29
+ it 'sets a Link with the entry point url' do
30
+ entry_point.url.must_equal 'http://my.api.org'
48
31
  end
49
32
  end
50
33
  end
@@ -1,9 +1,12 @@
1
1
  require_relative '../test_helper'
2
2
  require 'hyperclient/link'
3
+ require 'hyperclient/entry_point'
3
4
 
4
5
  module Hyperclient
5
6
  describe Link do
6
- let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+ let(:entry_point) do
8
+ EntryPoint.new('http://api.example.org/')
9
+ end
7
10
 
8
11
  describe 'templated?' do
9
12
  it 'returns true if the link is templated' do
@@ -19,17 +22,6 @@ module Hyperclient
19
22
  end
20
23
  end
21
24
 
22
- describe 'resource' do
23
- let(:http) { mock('HTTP', get: {}) }
24
-
25
- it 'builds a resource with the hyperlink representation' do
26
- HTTP.expects(:new).returns(http, {})
27
- Resource.expects(:new).with({}, entry_point)
28
-
29
- Link.new({}, entry_point).resource
30
- end
31
- end
32
-
33
25
  describe 'expand' do
34
26
  it 'buils a Link with the templated URI representation' do
35
27
  link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
@@ -64,6 +56,95 @@ module Hyperclient
64
56
  end
65
57
  end
66
58
 
59
+ describe 'resource' do
60
+ it 'builds a resource with the link href representation' do
61
+ Resource.expects(:new).with({}, entry_point)
62
+
63
+ link = Link.new({'href' => '/'}, entry_point)
64
+ link.expects(:get).returns(mock(body: {}))
65
+
66
+ link.resource
67
+ end
68
+ end
69
+
70
+ describe 'connection' do
71
+ it 'returns the entry point connection' do
72
+ Link.new({}, entry_point).connection.must_equal entry_point.connection
73
+ end
74
+ end
75
+
76
+ describe 'get' do
77
+ it 'sends a GET request with the link url' do
78
+ link = Link.new({'href' => '/productions/1'}, entry_point)
79
+
80
+ entry_point.connection.expects(:get).with('/productions/1')
81
+ link.get
82
+ end
83
+ end
84
+
85
+ describe 'options' do
86
+ it 'sends a OPTIONS request with the link url' do
87
+ link = Link.new({'href' => '/productions/1'}, entry_point)
88
+
89
+ entry_point.connection.expects(:run_request).with(:options, '/productions/1', nil, nil)
90
+ link.options
91
+ end
92
+ end
93
+
94
+ describe 'head' do
95
+ it 'sends a HEAD request with the link url' do
96
+ link = Link.new({'href' => '/productions/1'}, entry_point)
97
+
98
+ entry_point.connection.expects(:head).with('/productions/1')
99
+ link.head
100
+ end
101
+ end
102
+
103
+ describe 'delete' do
104
+ it 'sends a DELETE request with the link url' do
105
+ link = Link.new({'href' => '/productions/1'}, entry_point)
106
+
107
+ entry_point.connection.expects(:delete).with('/productions/1')
108
+ link.delete
109
+ end
110
+ end
111
+
112
+ describe 'post' do
113
+ it 'sends a POST request with the link url and params' do
114
+ link = Link.new({'href' => '/productions/1'}, entry_point)
115
+
116
+ entry_point.connection.expects(:post).with('/productions/1', {'foo' => 'bar'})
117
+ link.post({'foo' => 'bar'})
118
+ end
119
+ end
120
+
121
+ describe 'put' do
122
+ it 'sends a PUT request with the link url and params' do
123
+ link = Link.new({'href' => '/productions/1'}, entry_point)
124
+
125
+ entry_point.connection.expects(:put).with('/productions/1', {'foo' => 'bar'})
126
+ link.put({'foo' => 'bar'})
127
+ end
128
+ end
129
+
130
+ describe 'patch' do
131
+ it 'sends a PATCH request with the link url and params' do
132
+ link = Link.new({'href' => '/productions/1'}, entry_point)
133
+
134
+ entry_point.connection.expects(:patch).with('/productions/1', {'foo' => 'bar'})
135
+ link.patch({'foo' => 'bar'})
136
+ end
137
+ end
138
+
139
+ describe 'inspect' do
140
+ it 'outputs a custom-friendly output' do
141
+ link = Link.new({'href'=>'/productions/1'}, 'foo')
142
+
143
+ link.inspect.must_include 'Link'
144
+ link.inspect.must_include '"href"=>"/productions/1"'
145
+ end
146
+ end
147
+
67
148
  describe 'method_missing' do
68
149
  before do
69
150
  stub_request(:get, "http://myapi.org/orders").
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+ require 'hyperclient'
3
+
4
+ describe Hyperclient do
5
+ describe 'new' do
6
+ it 'creates a new EntryPoint with the url' do
7
+ Hyperclient::EntryPoint.expects(:new).with('http://api.example.org')
8
+
9
+ Hyperclient.new('http://api.example.org')
10
+ end
11
+ end
12
+ end
data/test/test_helper.rb CHANGED
@@ -9,7 +9,7 @@ gem 'minitest'
9
9
 
10
10
  require 'minitest/spec'
11
11
  require 'minitest/autorun'
12
- require 'mocha'
12
+ require 'mocha/setup'
13
13
  require 'turn'
14
14
  require 'webmock/minitest'
15
15
  require 'json'