hyperclient 0.2.0 → 0.3.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.
@@ -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'