hyperclient 0.0.1 → 0.0.2
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/.gitignore +17 -0
- data/.rvmrc +1 -1
- data/.travis.yml +2 -0
- data/.yardopts +8 -0
- data/Gemfile +9 -1
- data/Guardfile +6 -0
- data/LICENSE +1 -1
- data/MIT-LICENSE +20 -0
- data/Rakefile +27 -1
- data/Readme.md +145 -0
- data/examples/hal_shop.rb +59 -0
- data/hyperclient.gemspec +11 -6
- data/lib/hyperclient.rb +72 -3
- data/lib/hyperclient/discoverer.rb +63 -0
- data/lib/hyperclient/http.rb +90 -0
- data/lib/hyperclient/resource.rb +82 -0
- data/lib/hyperclient/response.rb +42 -0
- data/lib/hyperclient/version.rb +1 -1
- data/test/fixtures/collection.json +34 -0
- data/test/fixtures/element.json +65 -0
- data/test/fixtures/root.json +6 -0
- data/test/hyperclient/discoverer_test.rb +76 -0
- data/test/hyperclient/http_test.rb +96 -0
- data/test/hyperclient/resource_test.rb +80 -0
- data/test/hyperclient/response_test.rb +50 -0
- data/test/hyperclient_test.rb +63 -0
- data/test/test_helper.rb +7 -0
- metadata +110 -12
- data/README.md +0 -71
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'hyperclient/resource'
|
3
|
+
|
4
|
+
module Hyperclient
|
5
|
+
describe Resource do
|
6
|
+
let(:response) do
|
7
|
+
File.read('test/fixtures/element.json')
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:parsed_response) do
|
11
|
+
JSON.parse(response)
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
Resource.entry_point = 'http://api.example.org'
|
16
|
+
end
|
17
|
+
|
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'
|
22
|
+
end
|
23
|
+
|
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
|
29
|
+
|
30
|
+
describe 'initialize' do
|
31
|
+
before do
|
32
|
+
stub_request(:get, 'http://api.example.org')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'initializes the response when one is given' do
|
36
|
+
resource = Resource.new('/', {response: JSON.parse(response)})
|
37
|
+
|
38
|
+
assert_not_requested(:get, 'http://api.example.org/')
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'updates the resource URL if the response has one' do
|
42
|
+
resource = Resource.new('/', {response: JSON.parse(response)})
|
43
|
+
|
44
|
+
resource.url.must_include '/productions/1'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does no update the resource URL if the response does not have one' do
|
48
|
+
resource = Resource.new('/', {})
|
49
|
+
|
50
|
+
resource.url.wont_include '/productions/1'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'sets the resource name' do
|
54
|
+
resource = Resource.new('/', {name: 'posts'})
|
55
|
+
|
56
|
+
resource.name.must_equal 'posts'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'reload' do
|
61
|
+
before do
|
62
|
+
stub_request(:get, "http://api.example.org/productions/1").
|
63
|
+
to_return(:status => 200, :body => response, headers: {content_type: 'application/json'})
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'retrives itself from the API' do
|
67
|
+
resource = Resource.new('/productions/1')
|
68
|
+
resource.reload
|
69
|
+
|
70
|
+
assert_requested(:get, 'http://api.example.org/productions/1', times: 1)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns itself' do
|
74
|
+
resource = Resource.new('/productions/1')
|
75
|
+
|
76
|
+
resource.reload.must_equal resource
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'hyperclient/response'
|
3
|
+
|
4
|
+
module Hyperclient
|
5
|
+
describe Response do
|
6
|
+
let (:response) do
|
7
|
+
Response.new JSON.parse(File.read('test/fixtures/element.json'))
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'attributes' do
|
11
|
+
it 'returns the resource attributes' do
|
12
|
+
response.attributes['title'].must_equal 'Real World ASP.NET MVC3'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does not include _links as attributes' do
|
16
|
+
response.attributes.wont_include '_links'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not include _embedded as attributes' do
|
20
|
+
response.attributes.wont_include '_embedded'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'links' do
|
25
|
+
it 'returns resources included in the _links section' do
|
26
|
+
response.links.filter.must_be_kind_of Resource
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'resources' do
|
31
|
+
it 'returns resources included in the _embedded section' do
|
32
|
+
response.resources.author.must_be_kind_of Resource
|
33
|
+
response.resources.episodes.first.must_be_kind_of Resource
|
34
|
+
response.resources.episodes.last.must_be_kind_of Resource
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'url' do
|
39
|
+
it 'returns the url of the resource grabbed from the response' do
|
40
|
+
response.url.must_equal '/productions/1'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns nil when the response does not include the resource url' do
|
44
|
+
response = Response.new({_links: {media: {href: '/media/1'}}})
|
45
|
+
|
46
|
+
response.url.must_equal nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'hyperclient'
|
3
|
+
|
4
|
+
describe Hyperclient do
|
5
|
+
let(:api) do
|
6
|
+
Class.new do
|
7
|
+
include Hyperclient
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'entry point' do
|
12
|
+
it 'sets the entry point for Hyperclient::Resource' do
|
13
|
+
api.entry_point 'http://my.api.org'
|
14
|
+
|
15
|
+
Hyperclient::Resource.new('/').url.must_include 'http://my.api.org'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'entry' do
|
20
|
+
before do
|
21
|
+
api.entry_point 'http://my.api.org'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'initializes a Resource at the entry point' do
|
25
|
+
api.new.entry.url.must_equal 'http://my.api.org/'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'sets the Resource name' do
|
29
|
+
api.new.name.must_equal 'Entry point'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'auth' do
|
34
|
+
it 'sets authentication type' do
|
35
|
+
api.auth(:digest, nil, nil)
|
36
|
+
|
37
|
+
api.http_options[:http][:auth][:type].must_equal :digest
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'sets the authentication credentials' do
|
41
|
+
api.auth(:digest, 'user', 'secret')
|
42
|
+
|
43
|
+
api.http_options[:http][:auth][:credentials].must_include 'user'
|
44
|
+
api.http_options[:http][:auth][:credentials].must_include 'secret'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'method missing' do
|
49
|
+
class Hyperclient::Resource
|
50
|
+
def foo
|
51
|
+
'foo'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'delegates undefined methods to the API when they exist' do
|
56
|
+
api.new.foo.must_equal 'foo'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises an error when the method does not exist in the API' do
|
60
|
+
lambda { api.new.this_method_does_not_exist }.must_raise(NoMethodError)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,32 +1,115 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hyperclient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
8
|
+
- Oriol Gual
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
-
dependencies:
|
14
|
-
|
12
|
+
date: 2012-05-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: turn
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: webmock
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: HyperClient is a Ruby Hypermedia API client.
|
15
79
|
email:
|
16
|
-
-
|
80
|
+
- oriol.gual@gmail.com
|
17
81
|
executables: []
|
18
82
|
extensions: []
|
19
83
|
extra_rdoc_files: []
|
20
84
|
files:
|
85
|
+
- .gitignore
|
21
86
|
- .rvmrc
|
87
|
+
- .travis.yml
|
88
|
+
- .yardopts
|
22
89
|
- Gemfile
|
90
|
+
- Guardfile
|
23
91
|
- LICENSE
|
24
|
-
-
|
92
|
+
- MIT-LICENSE
|
25
93
|
- Rakefile
|
94
|
+
- Readme.md
|
95
|
+
- examples/hal_shop.rb
|
26
96
|
- hyperclient.gemspec
|
27
97
|
- lib/hyperclient.rb
|
98
|
+
- lib/hyperclient/discoverer.rb
|
99
|
+
- lib/hyperclient/http.rb
|
100
|
+
- lib/hyperclient/resource.rb
|
101
|
+
- lib/hyperclient/response.rb
|
28
102
|
- lib/hyperclient/version.rb
|
29
|
-
|
103
|
+
- test/fixtures/collection.json
|
104
|
+
- test/fixtures/element.json
|
105
|
+
- test/fixtures/root.json
|
106
|
+
- test/hyperclient/discoverer_test.rb
|
107
|
+
- test/hyperclient/http_test.rb
|
108
|
+
- test/hyperclient/resource_test.rb
|
109
|
+
- test/hyperclient/response_test.rb
|
110
|
+
- test/hyperclient_test.rb
|
111
|
+
- test/test_helper.rb
|
112
|
+
homepage: http://codegram.github.com/hyperclient/
|
30
113
|
licenses: []
|
31
114
|
post_install_message:
|
32
115
|
rdoc_options: []
|
@@ -38,17 +121,32 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
121
|
- - ! '>='
|
39
122
|
- !ruby/object:Gem::Version
|
40
123
|
version: '0'
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
hash: -2437981689318086472
|
41
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
128
|
none: false
|
43
129
|
requirements:
|
44
130
|
- - ! '>='
|
45
131
|
- !ruby/object:Gem::Version
|
46
132
|
version: '0'
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
hash: -2437981689318086472
|
47
136
|
requirements: []
|
48
137
|
rubyforge_project:
|
49
|
-
rubygems_version: 1.8.
|
138
|
+
rubygems_version: 1.8.23
|
50
139
|
signing_key:
|
51
140
|
specification_version: 3
|
52
|
-
summary:
|
53
|
-
|
54
|
-
|
141
|
+
summary: ''
|
142
|
+
test_files:
|
143
|
+
- test/fixtures/collection.json
|
144
|
+
- test/fixtures/element.json
|
145
|
+
- test/fixtures/root.json
|
146
|
+
- test/hyperclient/discoverer_test.rb
|
147
|
+
- test/hyperclient/http_test.rb
|
148
|
+
- test/hyperclient/resource_test.rb
|
149
|
+
- test/hyperclient/response_test.rb
|
150
|
+
- test/hyperclient_test.rb
|
151
|
+
- test/test_helper.rb
|
152
|
+
has_rdoc:
|
data/README.md
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# Hyperclient
|
2
|
-
|
3
|
-
This gem aims to explore and demonstrate how to write a client for [hypermedia APIs](http://blog.steveklabnik.com/posts/2012-02-23-rest-is-over), formerly
|
4
|
-
known as REST interfaces that respect the [HATEOAS constraint](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven).
|
5
|
-
|
6
|
-
Many people have demonstrated how the server should respond. I'm investigating how the client should behave when interacting with a true hypermedia server.
|
7
|
-
|
8
|
-
The gem is being developed against the [Hypertext Application Language](http://stateless.co/hal_specification.html) and [Atompub](http://bitworking.org/projects/atom/rfc5023.html) specifications.
|
9
|
-
|
10
|
-
Contributors welcome!
|
11
|
-
|
12
|
-
## Installation
|
13
|
-
|
14
|
-
Add this line to your application's Gemfile:
|
15
|
-
|
16
|
-
gem 'hyperclient'
|
17
|
-
|
18
|
-
And then execute:
|
19
|
-
|
20
|
-
$ bundle
|
21
|
-
|
22
|
-
Or install it yourself as:
|
23
|
-
|
24
|
-
$ gem install hyperclient
|
25
|
-
|
26
|
-
## Usage
|
27
|
-
|
28
|
-
```ruby
|
29
|
-
require 'hyperclient'
|
30
|
-
|
31
|
-
# NOTE this is the only URI you ever need to specify yourself. The server will provide all future links.
|
32
|
-
resource = Hyperclient::Resource.new("http://api.example.com")
|
33
|
-
|
34
|
-
resource.links.each { |l| puts(l.relation => l.uri) }
|
35
|
-
# {:self => "https://api.example.com"}
|
36
|
-
# {:orders => "https://api.example.com/orders"}
|
37
|
-
# {:customers => "https://api.example.com/customers"}
|
38
|
-
|
39
|
-
# since we're at the root level of this API we haven't drilled into any objects yet
|
40
|
-
pp resource.objects
|
41
|
-
# []
|
42
|
-
|
43
|
-
orders_resource = resource[:orders].get
|
44
|
-
pp orders_resource.links
|
45
|
-
# {:self => "https://api.example.com/orders",
|
46
|
-
# :next => "https://api.example.com/orders/page/2"}
|
47
|
-
|
48
|
-
# some servers may return embedded objects
|
49
|
-
orders_resource.objects.each { |l| puts l.class => l.attributes }
|
50
|
-
# [Hyperclient::Resource => {:id => 50, :item_name => "R2 Motivator", :created_at => "2012-02-03 12:15:02 -0400"},
|
51
|
-
# Hyperclient::Resource => {:id => 51, :item_name => "Hydrospanner", :created_at => "2012-02-04 13:18:12 -0500"}]
|
52
|
-
|
53
|
-
order_resource = orders_resource.post({ item_name: "Droid Coolant" })
|
54
|
-
pp order_resource.location
|
55
|
-
# "https://api.example.com/orders/52"
|
56
|
-
|
57
|
-
pp order_resource.attributes
|
58
|
-
# {:id => 52, :item_name => "Droid Coolant", :created_at => "2012-03-05 21:31:04 -0500"}
|
59
|
-
```
|
60
|
-
|
61
|
-
## Contributing
|
62
|
-
|
63
|
-
1. Fork it
|
64
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
65
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
66
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
67
|
-
5. Create new Pull Request
|
68
|
-
|
69
|
-
## Questions?
|
70
|
-
|
71
|
-
Contact me on [Twitter](https://twitter/subelsky) or [email](mike@subelsky.com).
|