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.
- data/Gemfile +2 -1
- data/Readme.md +15 -21
- data/examples/cyberscore.rb +28 -16
- data/examples/hal_shop.rb +15 -22
- data/hyperclient.gemspec +2 -0
- data/lib/hyperclient.rb +2 -84
- data/lib/hyperclient/attributes.rb +20 -0
- data/lib/hyperclient/collection.rb +53 -0
- data/lib/hyperclient/entry_point.rb +49 -0
- data/lib/hyperclient/http.rb +38 -37
- data/lib/hyperclient/link.rb +93 -0
- data/lib/hyperclient/link_collection.rb +24 -0
- data/lib/hyperclient/resource.rb +26 -65
- data/lib/hyperclient/resource_collection.rb +33 -0
- data/lib/hyperclient/version.rb +1 -1
- data/test/fixtures/element.json +4 -15
- data/test/hyperclient/attributes_test.rb +26 -0
- data/test/hyperclient/collection_test.rb +39 -0
- data/test/hyperclient/entry_point_test.rb +51 -0
- data/test/hyperclient/http_test.rb +37 -23
- data/test/hyperclient/link_collection_test.rb +29 -0
- data/test/hyperclient/link_test.rb +93 -0
- data/test/hyperclient/resource_collection_test.rb +34 -0
- data/test/hyperclient/resource_test.rb +36 -42
- data/test/test_helper.rb +10 -1
- metadata +55 -16
- data/lib/hyperclient/discoverer.rb +0 -84
- data/lib/hyperclient/representation.rb +0 -43
- data/lib/hyperclient/resource_factory.rb +0 -53
- data/test/hyperclient/discoverer_test.rb +0 -101
- data/test/hyperclient/representation_test.rb +0 -52
- data/test/hyperclient/resource_factory_test.rb +0 -32
- data/test/hyperclient_test.rb +0 -68
data/test/test_helper.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
+
if ENV['COVERAGE']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter '/test/'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
1
8
|
gem 'minitest'
|
2
9
|
|
3
10
|
require 'minitest/spec'
|
4
11
|
require 'minitest/autorun'
|
12
|
+
require 'mocha'
|
5
13
|
require 'turn'
|
6
14
|
require 'webmock/minitest'
|
7
|
-
require 'json'
|
15
|
+
require 'json'
|
16
|
+
require 'pry'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hyperclient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: uri_template
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: minitest
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,6 +91,22 @@ dependencies:
|
|
75
91
|
- - ! '>='
|
76
92
|
- !ruby/object:Gem::Version
|
77
93
|
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
78
110
|
description: HyperClient is a Ruby Hypermedia API client.
|
79
111
|
email:
|
80
112
|
- oriol.gual@gmail.com
|
@@ -96,21 +128,26 @@ files:
|
|
96
128
|
- examples/hal_shop.rb
|
97
129
|
- hyperclient.gemspec
|
98
130
|
- lib/hyperclient.rb
|
99
|
-
- lib/hyperclient/
|
131
|
+
- lib/hyperclient/attributes.rb
|
132
|
+
- lib/hyperclient/collection.rb
|
133
|
+
- lib/hyperclient/entry_point.rb
|
100
134
|
- lib/hyperclient/http.rb
|
101
|
-
- lib/hyperclient/
|
135
|
+
- lib/hyperclient/link.rb
|
136
|
+
- lib/hyperclient/link_collection.rb
|
102
137
|
- lib/hyperclient/resource.rb
|
103
|
-
- lib/hyperclient/
|
138
|
+
- lib/hyperclient/resource_collection.rb
|
104
139
|
- lib/hyperclient/version.rb
|
105
140
|
- test/fixtures/collection.json
|
106
141
|
- test/fixtures/element.json
|
107
142
|
- test/fixtures/root.json
|
108
|
-
- test/hyperclient/
|
143
|
+
- test/hyperclient/attributes_test.rb
|
144
|
+
- test/hyperclient/collection_test.rb
|
145
|
+
- test/hyperclient/entry_point_test.rb
|
109
146
|
- test/hyperclient/http_test.rb
|
110
|
-
- test/hyperclient/
|
111
|
-
- test/hyperclient/
|
147
|
+
- test/hyperclient/link_collection_test.rb
|
148
|
+
- test/hyperclient/link_test.rb
|
149
|
+
- test/hyperclient/resource_collection_test.rb
|
112
150
|
- test/hyperclient/resource_test.rb
|
113
|
-
- test/hyperclient_test.rb
|
114
151
|
- test/test_helper.rb
|
115
152
|
homepage: http://codegram.github.com/hyperclient/
|
116
153
|
licenses: []
|
@@ -126,7 +163,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
163
|
version: '0'
|
127
164
|
segments:
|
128
165
|
- 0
|
129
|
-
hash:
|
166
|
+
hash: -1184667501328950554
|
130
167
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
168
|
none: false
|
132
169
|
requirements:
|
@@ -135,10 +172,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
172
|
version: '0'
|
136
173
|
segments:
|
137
174
|
- 0
|
138
|
-
hash:
|
175
|
+
hash: -1184667501328950554
|
139
176
|
requirements: []
|
140
177
|
rubyforge_project:
|
141
|
-
rubygems_version: 1.8.
|
178
|
+
rubygems_version: 1.8.23
|
142
179
|
signing_key:
|
143
180
|
specification_version: 3
|
144
181
|
summary: ''
|
@@ -146,11 +183,13 @@ test_files:
|
|
146
183
|
- test/fixtures/collection.json
|
147
184
|
- test/fixtures/element.json
|
148
185
|
- test/fixtures/root.json
|
149
|
-
- test/hyperclient/
|
186
|
+
- test/hyperclient/attributes_test.rb
|
187
|
+
- test/hyperclient/collection_test.rb
|
188
|
+
- test/hyperclient/entry_point_test.rb
|
150
189
|
- test/hyperclient/http_test.rb
|
151
|
-
- test/hyperclient/
|
152
|
-
- test/hyperclient/
|
190
|
+
- test/hyperclient/link_collection_test.rb
|
191
|
+
- test/hyperclient/link_test.rb
|
192
|
+
- test/hyperclient/resource_collection_test.rb
|
153
193
|
- test/hyperclient/resource_test.rb
|
154
|
-
- test/hyperclient_test.rb
|
155
194
|
- test/test_helper.rb
|
156
195
|
has_rdoc:
|
@@ -1,84 +0,0 @@
|
|
1
|
-
module Hyperclient
|
2
|
-
# Public: Discovers resources from a Representation.
|
3
|
-
class Discoverer
|
4
|
-
# Include goodness of Enumerable.
|
5
|
-
include Enumerable
|
6
|
-
|
7
|
-
# Public: Initializes a Discoverer.
|
8
|
-
#
|
9
|
-
# representation - A Hash representing some resources.
|
10
|
-
def initialize(representation)
|
11
|
-
@representation = representation
|
12
|
-
end
|
13
|
-
|
14
|
-
# Public: Fetch a Resource with the given name. It is useful when
|
15
|
-
# resources don't have a friendly name and you can't call a method on the
|
16
|
-
# Discoverer.
|
17
|
-
#
|
18
|
-
# name - A String representing the resource name.
|
19
|
-
#
|
20
|
-
# Returns a Resource
|
21
|
-
def [](name)
|
22
|
-
resources[name.to_s]
|
23
|
-
end
|
24
|
-
|
25
|
-
# Public: Iterates over the discovered resources so one can navigate easily
|
26
|
-
# between them.
|
27
|
-
#
|
28
|
-
# block - A block to pass to each.
|
29
|
-
#
|
30
|
-
# Returns an Enumerable.
|
31
|
-
def each(&block)
|
32
|
-
resources.values.each(&block)
|
33
|
-
end
|
34
|
-
|
35
|
-
# Public: Returns a Resource with the name of the method when exists.
|
36
|
-
def method_missing(method, *args, &block)
|
37
|
-
resources.fetch(method.to_s) { super }
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
# Internal: Returns a Hash with the resources of the representation.
|
42
|
-
def resources
|
43
|
-
return {} unless @representation.respond_to?(:inject)
|
44
|
-
|
45
|
-
@resources ||= @representation.inject({}) do |memo, (name, representation)|
|
46
|
-
next memo if name == 'self'
|
47
|
-
memo.update(name => build_resource(representation, name))
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Internal: Returns a Resource (or a collection of Resources).
|
52
|
-
#
|
53
|
-
# representation - A Hash representing the resource representation.
|
54
|
-
# name - An optional String with the name of the resource.
|
55
|
-
def build_resource(representation, name = nil)
|
56
|
-
return representation.map(&method(:build_resource)) if representation.is_a?(Array)
|
57
|
-
|
58
|
-
url = URLExtractor.new(representation).url
|
59
|
-
ResourceFactory.resource(url, {representation: representation, name: name})
|
60
|
-
end
|
61
|
-
|
62
|
-
# Internal: Extract the url from a HAL representation.
|
63
|
-
class URLExtractor
|
64
|
-
# Public: Initializes a URLExtractor.
|
65
|
-
#
|
66
|
-
# representation - A Hash with the representation of a Resource.
|
67
|
-
def initialize(representation)
|
68
|
-
@representation = representation
|
69
|
-
end
|
70
|
-
|
71
|
-
# Public: Returns a String with the resource URL
|
72
|
-
def url
|
73
|
-
return @representation.delete('href') if @representation.include?('href')
|
74
|
-
|
75
|
-
if @representation && @representation['_links'] && @representation['_links']['self'] &&
|
76
|
-
(url = @representation['_links']['self']['href'])
|
77
|
-
return url
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
require 'hyperclient/resource_factory'
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'hyperclient/discoverer'
|
2
|
-
|
3
|
-
module Hyperclient
|
4
|
-
# Public: This class is responsible for parsing a representation from the API
|
5
|
-
# and exposing some methods to access its values.
|
6
|
-
#
|
7
|
-
# It is mainly used by Hyperclient::Resource.
|
8
|
-
class Representation
|
9
|
-
# Public: Initializes a Representation.
|
10
|
-
#
|
11
|
-
# representation - A Hash containing a representation from the API. If the
|
12
|
-
# representation is not a Hash it will try to parse it as JSON.
|
13
|
-
def initialize(representation)
|
14
|
-
begin
|
15
|
-
representation = JSON.parse(representation) unless representation.is_a? Hash
|
16
|
-
rescue JSON::ParserError
|
17
|
-
warn 'WARNING Hyperclient::Representation: JSON representation was not valid:'
|
18
|
-
puts representation
|
19
|
-
representation = {}
|
20
|
-
end
|
21
|
-
@representation = representation
|
22
|
-
end
|
23
|
-
|
24
|
-
# Public: Returns a Discoverer for the _links section of the representation. It
|
25
|
-
# can be used later to use the resources from this section.
|
26
|
-
def links
|
27
|
-
@links ||= Discoverer.new(@representation['_links'])
|
28
|
-
end
|
29
|
-
|
30
|
-
# Public: Returns a Discoverer for the _embedded section of the representation.
|
31
|
-
# It can be used later to use the resources from this section.
|
32
|
-
def embedded
|
33
|
-
@embedded ||= Discoverer.new(@representation['_embedded'])
|
34
|
-
end
|
35
|
-
|
36
|
-
# Public: Returns a Hash with the attributes of the resource.
|
37
|
-
def attributes
|
38
|
-
@attributes ||= @representation.dup.delete_if {|key, value| key =~ /^_/}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
require 'json'
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module Hyperclient
|
2
|
-
# Public: This class acts as an interface to build Resources. It has a simple
|
3
|
-
# identity map so a user can save HTTP calls when interacting with the same
|
4
|
-
# resource.
|
5
|
-
#
|
6
|
-
# Examples
|
7
|
-
#
|
8
|
-
# ResourceFactory.resource('http://myapi.org/resource/1')
|
9
|
-
# => #<Hyperclient::Resource url: 'http://myapi.org/resource/1'>
|
10
|
-
#
|
11
|
-
class ResourceFactory
|
12
|
-
# Public: A factory method to build Resources. It will try to find a
|
13
|
-
# Resource in the identity map or build a new one if does not exist.
|
14
|
-
#
|
15
|
-
# url - A String to identify the Resource
|
16
|
-
# args - An Array to pass other arguments to the Resource initialization.
|
17
|
-
#
|
18
|
-
# Raises MissingURLException if no url given.
|
19
|
-
# Returns a Resource.
|
20
|
-
def self.resource(url, *args)
|
21
|
-
raise MissingURLException.new(args) unless url
|
22
|
-
|
23
|
-
identity_map.fetch(url) do |url|
|
24
|
-
resource = Resource.new(url, *args)
|
25
|
-
identity_map.update(url => resource)
|
26
|
-
resource
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
# Internal: Returns a Hash that acts as identity map.
|
32
|
-
def self.identity_map
|
33
|
-
@identity_map ||= {}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Public: Exception that is raised when building a Resource without a URL.
|
38
|
-
class MissingURLException < StandardError
|
39
|
-
# Public: Initializes a MissingURLException
|
40
|
-
#
|
41
|
-
# args - An Array of the args the were to be used to build the Resource.
|
42
|
-
def initialize(args)
|
43
|
-
@args = args
|
44
|
-
end
|
45
|
-
|
46
|
-
# Public: Returns a String with the exception message.
|
47
|
-
def message
|
48
|
-
"Cannot build Resource without a URL, given args were: #{@args.inspect}"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
require 'hyperclient/resource'
|
@@ -1,101 +0,0 @@
|
|
1
|
-
require_relative '../test_helper'
|
2
|
-
require 'hyperclient/representation'
|
3
|
-
|
4
|
-
module Hyperclient
|
5
|
-
describe Discoverer do
|
6
|
-
before do
|
7
|
-
Resource.entry_point = 'http://api.myexample.org/'
|
8
|
-
end
|
9
|
-
|
10
|
-
let (:representation) do
|
11
|
-
JSON.parse(File.read('test/fixtures/element.json'))
|
12
|
-
end
|
13
|
-
|
14
|
-
describe 'each' do
|
15
|
-
it 'iterates between resources' do
|
16
|
-
discoverer = Discoverer.new(representation['_links'])
|
17
|
-
|
18
|
-
discoverer.each do |resource|
|
19
|
-
resource.must_be_kind_of Resource
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '[]' do
|
25
|
-
it 'fetches a resource' do
|
26
|
-
discoverer = Discoverer.new(representation['_links'])
|
27
|
-
|
28
|
-
discoverer['filter'].must_be_kind_of Resource
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe 'resources' do
|
33
|
-
it 'does not include self as a resource' do
|
34
|
-
discoverer = Discoverer.new(representation['_links'])
|
35
|
-
|
36
|
-
lambda { discoverer.self }.must_raise NoMethodError
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'builds single resources' do
|
40
|
-
discoverer = Discoverer.new(representation['_links'])
|
41
|
-
|
42
|
-
discoverer.filter.must_be_kind_of Resource
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'builds collection resources' do
|
46
|
-
discoverer = Discoverer.new(representation['_embedded'])
|
47
|
-
|
48
|
-
discoverer.episodes.must_be_kind_of Array
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'also builds elements in collection resources' do
|
52
|
-
discoverer = Discoverer.new(representation['_embedded'])
|
53
|
-
|
54
|
-
discoverer.episodes.first.must_be_kind_of Resource
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'initializes resources with its URL' do
|
58
|
-
discoverer = Discoverer.new(representation['_links'])
|
59
|
-
|
60
|
-
discoverer.filter.url.wont_be_empty
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'initializes resources with the representation' do
|
64
|
-
discoverer = Discoverer.new(representation['_embedded'])
|
65
|
-
|
66
|
-
discoverer.author.attributes.wont_be_empty
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'initializes resources with its name' do
|
70
|
-
discoverer = Discoverer.new(representation['_links'])
|
71
|
-
|
72
|
-
discoverer.filter.name.wont_be_empty
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
describe Discoverer::URLExtractor do
|
77
|
-
describe 'url' do
|
78
|
-
it 'extracts the url from embedded resources' do
|
79
|
-
hal = {'_links' => {'self' => {'href' => '/path/to/resource'}}}
|
80
|
-
extractor = Discoverer::URLExtractor.new(hal)
|
81
|
-
|
82
|
-
extractor.url.must_equal '/path/to/resource'
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'extracts the url from linked resources' do
|
86
|
-
hal = {'href' => '/path/to/resource'}
|
87
|
-
extractor = Discoverer::URLExtractor.new(hal)
|
88
|
-
|
89
|
-
extractor.url.must_equal '/path/to/resource'
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'deletes the url from linked resources to prevent empty representations' do
|
93
|
-
hal = {'href' => '/path/to/resource'}
|
94
|
-
Discoverer::URLExtractor.new(hal).url
|
95
|
-
|
96
|
-
hal.include?('href').must_equal false
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require_relative '../test_helper'
|
2
|
-
require 'hyperclient/representation'
|
3
|
-
|
4
|
-
module Hyperclient
|
5
|
-
describe Representation do
|
6
|
-
let (:representation) do
|
7
|
-
Representation.new JSON.parse(File.read('test/fixtures/element.json'))
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'intialize' do
|
11
|
-
it 'handles non-hash representations' do
|
12
|
-
representation = Representation.new '{"title": "Hello world"}'
|
13
|
-
|
14
|
-
representation.attributes['title'].must_equal 'Hello world'
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'does not raise when non-JSON response is given' do
|
18
|
-
representation = Representation.new 'This is not JSON'
|
19
|
-
|
20
|
-
representation.attributes.must_equal({})
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe 'attributes' do
|
25
|
-
it 'returns the resource attributes' do
|
26
|
-
representation.attributes['title'].must_equal 'Real World ASP.NET MVC3'
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'does not include _links as attributes' do
|
30
|
-
representation.attributes.wont_include '_links'
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'does not include _embedded as attributes' do
|
34
|
-
representation.attributes.wont_include '_embedded'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe 'links' do
|
39
|
-
it 'returns resources included in the _links section' do
|
40
|
-
representation.links.filter.must_be_kind_of Resource
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe 'embedded' do
|
45
|
-
it 'returns resources included in the _embedded section' do
|
46
|
-
representation.embedded.author.must_be_kind_of Resource
|
47
|
-
representation.embedded.episodes.first.must_be_kind_of Resource
|
48
|
-
representation.embedded.episodes.last.must_be_kind_of Resource
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|