hyperclient 0.0.8 → 0.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.
@@ -0,0 +1,39 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/resource'
3
+
4
+ module Hyperclient
5
+ describe Collection do
6
+ let(:representation) do
7
+ JSON.parse( File.read('test/fixtures/element.json'))
8
+ end
9
+
10
+ let(:collection) do
11
+ Collection.new(representation)
12
+ end
13
+
14
+ it 'exposes the collection as methods' do
15
+ collection.title.must_equal 'Real World ASP.NET MVC3'
16
+ collection.description.must_match(/production/)
17
+ collection.permitted.must_equal true
18
+ end
19
+
20
+ it 'exposes collection as a hash' do
21
+ collection['title'].must_equal 'Real World ASP.NET MVC3'
22
+ collection['description'].must_match(/production/)
23
+ collection['permitted'].must_equal true
24
+ end
25
+
26
+ it 'correctly responds to methods' do
27
+ collection.must_respond_to :title
28
+ end
29
+
30
+ it 'acts as enumerable' do
31
+ names = collection.map do |name, value|
32
+ name
33
+ end
34
+
35
+ names.must_equal ['_links', 'title', 'description', 'permitted', '_embedded']
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,51 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/entry_point'
3
+
4
+ module Hyperclient
5
+ describe EntryPoint do
6
+ let(:api) do
7
+ EntryPoint.new 'http://my.api.org'
8
+ end
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'
18
+ end
19
+
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'
26
+ end
27
+
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'
32
+ end
33
+ end
34
+
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)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -3,33 +3,47 @@ require 'hyperclient/http'
3
3
 
4
4
  module Hyperclient
5
5
  describe HTTP do
6
- let (:resource) do
7
- resource = MiniTest::Mock.new
8
- resource.expect(:url, 'http://api.example.org/productions/1')
6
+ let(:url) do
7
+ '/productions/1'
9
8
  end
10
9
 
11
- let (:http) do
10
+ let(:config) { {base_uri: 'http://api.example.org'} }
11
+
12
+ let(:http) do
12
13
  HTTP.instance_variable_set("@default_options", {})
13
- HTTP.new(resource)
14
+ HTTP.new(url, config)
15
+ end
16
+
17
+ describe 'url' do
18
+ it 'merges the resource url with the base uri' do
19
+ http.url.to_s.must_equal 'http://api.example.org/productions/1'
20
+ end
21
+
22
+ it 'returns the given url if it cannot merge it' do
23
+ config = {base_uri: nil}
24
+ http = HTTP.new(url, config)
25
+ http.url.to_s.must_equal '/productions/1'
26
+ end
14
27
  end
15
28
 
16
29
  describe 'authentication' do
17
30
  it 'sets the authentication options' do
18
- stub_request(:get, 'user:pass@api.example.org/productions/1').
19
- to_return(body: 'This is the resource')
31
+ stub_request(:get, 'http://user:pass@api.example.org/productions/1').
32
+ to_return(body: '{"resource": "This is the resource"}')
33
+
34
+ config.update({auth: {type: :basic, user: 'user', password: 'pass'}})
20
35
 
21
- http = HTTP.new(resource, {auth: {type: :basic, credentials: ['user','pass']}})
22
- http.get.must_equal 'This is the resource'
36
+ http.get.must_equal({'resource' => 'This is the resource'})
23
37
  end
24
38
  end
25
39
 
26
40
  describe 'headers' do
27
41
  it 'sets headers from the given option' do
28
- http = HTTP.new(resource, {headers: {'accept-encoding' => 'deflate, gzip'}})
42
+ config.update({headers: {'accept-encoding' => 'deflate, gzip'}})
29
43
 
30
- stub_request(:get, 'api.example.org/productions/1').
44
+ stub_request(:get, 'http://api.example.org/productions/1').
31
45
  with(headers: {'Accept-Encoding' => 'deflate, gzip'}).
32
- to_return(body: 'This is the resource')
46
+ to_return(body: '{"resource": "This is the resource"}')
33
47
 
34
48
  http.get
35
49
  end
@@ -37,14 +51,14 @@ module Hyperclient
37
51
 
38
52
  describe 'debug' do
39
53
  it 'enables debugging' do
40
- http = HTTP.new(resource, {debug: true})
54
+ config.update({debug: true})
41
55
 
42
56
  http.class.instance_variable_get(:@default_options)[:debug_output].must_equal $stderr
43
57
  end
44
58
 
45
59
  it 'uses a custom stream' do
46
60
  stream = StringIO.new
47
- http = HTTP.new(resource, {debug: stream})
61
+ config.update({debug: stream})
48
62
 
49
63
  http.class.instance_variable_get(:@default_options)[:debug_output].must_equal stream
50
64
  end
@@ -52,14 +66,14 @@ module Hyperclient
52
66
 
53
67
  describe 'get' do
54
68
  it 'sends a GET request and returns the response body' do
55
- stub_request(:get, 'api.example.org/productions/1').
56
- to_return(body: 'This is the resource')
69
+ stub_request(:get, 'http://api.example.org/productions/1').
70
+ to_return(body: '{"resource": "This is the resource"}')
57
71
 
58
- http.get.must_equal 'This is the resource'
72
+ http.get.must_equal({'resource' => 'This is the resource'})
59
73
  end
60
74
 
61
75
  it 'returns the parsed response' do
62
- stub_request(:get, 'api.example.org/productions/1').
76
+ stub_request(:get, 'http://api.example.org/productions/1').
63
77
  to_return(body: '{"some_json": 12345 }', headers: {content_type: 'application/json'})
64
78
 
65
79
  http.get.must_equal({'some_json' => 12345})
@@ -68,7 +82,7 @@ module Hyperclient
68
82
 
69
83
  describe 'post' do
70
84
  it 'sends a POST request' do
71
- stub_request(:post, 'api.example.org/productions/1').
85
+ stub_request(:post, 'http://api.example.org/productions/1').
72
86
  to_return(body: 'Posting like a big boy huh?', status: 201)
73
87
 
74
88
  response = http.post({data: 'foo'})
@@ -80,7 +94,7 @@ module Hyperclient
80
94
 
81
95
  describe 'put' do
82
96
  it 'sends a PUT request' do
83
- stub_request(:put, 'api.example.org/productions/1').
97
+ stub_request(:put, 'http://api.example.org/productions/1').
84
98
  to_return(body: 'No changes were made', status: 204)
85
99
 
86
100
  response = http.put({attribute: 'changed'})
@@ -92,7 +106,7 @@ module Hyperclient
92
106
 
93
107
  describe 'options' do
94
108
  it 'sends a OPTIONS request' do
95
- stub_request(:options, 'api.example.org/productions/1').
109
+ stub_request(:options, 'http://api.example.org/productions/1').
96
110
  to_return(status: 200, headers: {allow: 'GET, POST'})
97
111
 
98
112
  response = http.options
@@ -102,7 +116,7 @@ module Hyperclient
102
116
 
103
117
  describe 'head' do
104
118
  it 'sends a HEAD request' do
105
- stub_request(:head, 'api.example.org/productions/1').
119
+ stub_request(:head, 'http://api.example.org/productions/1').
106
120
  to_return(status: 200, headers: {content_type: 'application/json'})
107
121
 
108
122
  response = http.head
@@ -112,7 +126,7 @@ module Hyperclient
112
126
 
113
127
  describe 'delete' do
114
128
  it 'sends a DELETE request' do
115
- stub_request(:delete, 'api.example.org/productions/1').
129
+ stub_request(:delete, 'http://api.example.org/productions/1').
116
130
  to_return(body: 'Resource deleted', status: 200)
117
131
 
118
132
  response = http.delete
@@ -0,0 +1,29 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/link_collection'
3
+
4
+ module Hyperclient
5
+ describe LinkCollection do
6
+ let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+
8
+ let(:representation) do
9
+ JSON.parse( File.read('test/fixtures/element.json'))
10
+ end
11
+
12
+ let(:links) do
13
+ LinkCollection.new(representation['_links'], entry_point)
14
+ end
15
+
16
+ it 'is a collection' do
17
+ LinkCollection.ancestors.must_include Collection
18
+ end
19
+
20
+ it 'initializes the collection with links' do
21
+ links.must_respond_to :filter
22
+ end
23
+
24
+ it 'returns link objects for each link' do
25
+ links.filter.must_be_kind_of Link
26
+ links['self'].must_be_kind_of Link
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,93 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/link'
3
+
4
+ module Hyperclient
5
+ describe Link do
6
+ let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+
8
+ describe 'templated?' do
9
+ it 'returns true if the link is templated' do
10
+ link = Link.new({'templated' => true}, entry_point)
11
+
12
+ link.templated?.must_equal true
13
+ end
14
+
15
+ it 'returns false if the link is not templated' do
16
+ link = Link.new({}, entry_point)
17
+
18
+ link.templated?.must_equal false
19
+ end
20
+ end
21
+
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
+ describe 'expand' do
34
+ it 'buils a Link with the templated URI representation' do
35
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
36
+
37
+ Link.expects(:new).with(anything, entry_point, {id: '1'})
38
+ link.expand(id: '1')
39
+ end
40
+
41
+ it 'raises if no uri variables are given' do
42
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
43
+
44
+ proc { link.resource }.must_raise MissingURITemplateVariablesException
45
+ end
46
+ end
47
+
48
+ describe 'url' do
49
+ it 'raises when missing required uri_variables' do
50
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
51
+
52
+ lambda { link.url }.must_raise MissingURITemplateVariablesException
53
+ end
54
+
55
+ it 'expands an uri template with variables' do
56
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point, {id: 1})
57
+
58
+ link.url.must_equal '/orders?id=1'
59
+ end
60
+
61
+ it 'returns the link when no uri template' do
62
+ link = Link.new({'href' => '/orders'}, entry_point)
63
+ link.url.must_equal '/orders'
64
+ end
65
+ end
66
+
67
+ describe 'method_missing' do
68
+ before do
69
+ stub_request(:get, "http://myapi.org/orders").
70
+ to_return(body: '{"resource": "This is the resource"}')
71
+ Resource.expects(:new).returns(resource).at_least_once
72
+ end
73
+
74
+ let(:link) { Link.new({'href' => 'http://myapi.org/orders'}, entry_point) }
75
+ let(:resource) { mock('Resource') }
76
+
77
+ it 'delegates unkown methods to the resource' do
78
+ resource.expects(:embedded)
79
+
80
+ link.embedded
81
+ end
82
+
83
+ it 'raises an error when the method does not exist in the resource' do
84
+ lambda { link.this_method_does_not_exist }.must_raise(NoMethodError)
85
+ end
86
+
87
+ it 'responds to missing methods' do
88
+ resource.expects(:respond_to?).with('embedded').returns(true)
89
+ link.respond_to?(:embedded).must_equal true
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/resource_collection'
3
+
4
+ module Hyperclient
5
+ describe ResourceCollection do
6
+ let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+
8
+ let(:representation) do
9
+ JSON.parse( File.read('test/fixtures/element.json'))
10
+ end
11
+
12
+ let(:resources) do
13
+ ResourceCollection.new(representation['_embedded'], entry_point)
14
+ end
15
+
16
+ it 'is a collection' do
17
+ ResourceCollection.ancestors.must_include Collection
18
+ end
19
+
20
+ it 'initializes the collection with resources' do
21
+ resources.must_respond_to :author
22
+ resources.must_respond_to :episodes
23
+ end
24
+
25
+ it 'returns resource objects for each resource' do
26
+ resources.author.must_be_kind_of Resource
27
+ end
28
+
29
+ it 'also builds arras of resource' do
30
+ resources.episodes.must_be_kind_of Array
31
+ resources.episodes.first.must_be_kind_of Resource
32
+ end
33
+ end
34
+ end
@@ -3,66 +3,60 @@ require 'hyperclient/resource'
3
3
 
4
4
  module Hyperclient
5
5
  describe Resource do
6
- let(:representation) do
7
- File.read('test/fixtures/element.json')
8
- end
9
-
10
- let(:parsed_representation) do
11
- JSON.parse(representation)
12
- end
6
+ let(:entry_point) { mock('Entry point') }
13
7
 
14
- before do
15
- Resource.entry_point = 'http://api.example.org'
16
- end
8
+ describe 'initialize' do
9
+ it 'initializes its links' do
10
+ LinkCollection.expects(:new).with({"self" => { "href" => "/orders/523" }}, entry_point)
17
11
 
18
- describe 'url' do
19
- it 'merges the resource url with the entry point' do
20
- resource = Resource.new('/path/to/resource')
21
- resource.url.to_s.must_equal 'http://api.example.org/path/to/resource'
12
+ Resource.new({'_links' => {"self" => { "href" => "/orders/523" } }}, entry_point)
22
13
  end
23
14
 
24
- it 'returns the given url if it cannot merge it' do
25
- resource = Resource.new('/search={terms}')
26
- resource.url.to_s.must_equal '/search={terms}'
27
- end
28
- end
15
+ it 'initializes its attributes' do
16
+ Attributes.expects(:new).with({foo: :bar})
29
17
 
30
- describe 'initialize' do
31
- before do
32
- stub_request(:get, 'http://api.example.org')
18
+ Resource.new({foo: :bar}, entry_point)
33
19
  end
34
20
 
35
- it 'initializes the representation when one is given' do
36
- resource = Resource.new('/', {representation: JSON.parse(representation)})
21
+ it 'initializes links' do
22
+ ResourceCollection.expects(:new).with({"orders" => []}, entry_point)
37
23
 
38
- assert_not_requested(:get, 'http://api.example.org/')
24
+ Resource.new({'_embedded' => {"orders" => [] }}, entry_point)
39
25
  end
26
+ end
40
27
 
41
- it 'sets the resource name' do
42
- resource = Resource.new('/', {name: 'posts'})
43
-
44
- resource.name.must_equal 'posts'
28
+ describe 'accessors' do
29
+ let(:resource) do
30
+ Resource.new({}, entry_point)
45
31
  end
46
- end
47
32
 
48
- describe 'reload' do
49
- before do
50
- stub_request(:get, "http://api.example.org/productions/1").
51
- to_return(:status => 200, :body => representation, headers: {content_type: 'application/json'})
33
+ describe 'links' do
34
+ it 'returns a LinkCollection' do
35
+ resource.links.must_be_kind_of LinkCollection
36
+ end
52
37
  end
53
38
 
54
- it 'retrives itself from the API' do
55
- resource = Resource.new('/productions/1')
56
- resource.reload
39
+ describe 'attributes' do
40
+ it 'returns a Attributes' do
41
+ resource.attributes.must_be_kind_of Attributes
42
+ end
43
+ end
57
44
 
58
- assert_requested(:get, 'http://api.example.org/productions/1', times: 1)
45
+ describe 'embedded' do
46
+ it 'returns a ResourceCollection' do
47
+ resource.embedded.must_be_kind_of ResourceCollection
48
+ end
59
49
  end
50
+ end
60
51
 
61
- it 'returns itself' do
62
- resource = Resource.new('/productions/1')
52
+ it 'uses its self Link to handle HTTP connections' do
53
+ self_link = mock('Self Link')
54
+ self_link.expects(:get)
63
55
 
64
- resource.reload.must_equal resource
65
- end
56
+ LinkCollection.expects(:new).returns({'self' => self_link})
57
+ resource = Resource.new({}, entry_point)
58
+
59
+ resource.get
66
60
  end
67
61
  end
68
62
  end