hyperclient 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,6 +3,7 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  gem 'rake'
6
+ gem 'growl'
6
7
  gem 'guard'
7
8
  gem 'guard-minitest'
8
9
  gem 'pry'
data/examples/cs.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'hyperclient'
2
+
3
+ class HalShop
4
+ include Hyperclient
5
+
6
+ entry_point 'http://cs-api.heroku.com/api/'
7
+ end
8
+
9
+ def print_resources(resources)
10
+ resources.each do |resource|
11
+ if resource.is_a?(Array)
12
+ print_resources(resource)
13
+ else
14
+ puts %{Found "#{resource.name}" at "#{resource.url}" }
15
+ end
16
+ end
17
+ end
18
+
19
+ def print_games(games)
20
+ games.each do |game|
21
+ puts %{Found "#{game.attributes['name']}" at "#{game.url}" }
22
+ end
23
+ end
24
+
25
+
26
+ api = HalShop.new
27
+
28
+ puts "Let's inspect the API:"
29
+ puts "\n"
30
+
31
+ puts 'Links from the entry point:'
32
+ print_resources(api.links)
33
+ puts "\n"
34
+
35
+ puts 'How is the server feeling today?'
36
+ puts api.attributes['motd']
37
+ puts "\n"
38
+
39
+ puts "Let's read the feeds:"
40
+ print_resources(api.links.feeds.links)
41
+ puts "\n"
42
+
43
+ puts "I like games!"
44
+ print_games(api.links.feeds.links.games.resources.games)
data/lib/hyperclient.rb CHANGED
@@ -17,7 +17,7 @@ module Hyperclient
17
17
 
18
18
  # Public: Initializes the API with the entry point.
19
19
  def entry
20
- @entry ||= Resource.new('/', resource_options)
20
+ @entry ||= Resource.new('', resource_options)
21
21
  end
22
22
 
23
23
  # Internal: Delegate the method to the API if it exists.
@@ -1,14 +1,14 @@
1
1
  module Hyperclient
2
- # Public: Discovers resources from an HTTP response.
2
+ # Public: Discovers resources from a Representation.
3
3
  class Discoverer
4
4
  # Include goodness of Enumerable.
5
5
  include Enumerable
6
6
 
7
7
  # Public: Initializes a Discoverer.
8
8
  #
9
- # response - A Hash representing some resources.
10
- def initialize(response)
11
- @response = response
9
+ # representation - A Hash representing some resources.
10
+ def initialize(representation)
11
+ @representation = representation
12
12
  end
13
13
 
14
14
  # Public: Fetch a Resource with the given name. It is useful when
@@ -38,24 +38,24 @@ module Hyperclient
38
38
  end
39
39
 
40
40
  private
41
- # Internal: Returns a Hash with the resources of the response.
41
+ # Internal: Returns a Hash with the resources of the representation.
42
42
  def resources
43
- return {} unless @response.respond_to?(:inject)
43
+ return {} unless @representation.respond_to?(:inject)
44
44
 
45
- @resources ||= @response.inject({}) do |memo, (name, response)|
45
+ @resources ||= @representation.inject({}) do |memo, (name, representation)|
46
46
  next memo if name == 'self'
47
- memo.update(name => build_resource(response, name))
47
+ memo.update(name => build_resource(representation, name))
48
48
  end
49
49
  end
50
50
 
51
51
  # Internal: Returns a Resource (or a collection of Resources).
52
52
  #
53
- # response - A Hash representing the resource response.
53
+ # representation - A Hash representing the resource representation.
54
54
  # name - An optional String with the name of the resource.
55
- def build_resource(response, name = nil)
56
- return response.map(&method(:build_resource)) if response.is_a?(Array)
55
+ def build_resource(representation, name = nil)
56
+ return representation.map(&method(:build_resource)) if representation.is_a?(Array)
57
57
 
58
- ResourceFactory.resource(response.delete('href'), {response: response, name: name})
58
+ ResourceFactory.resource(representation.delete('href'), {representation: representation, name: name})
59
59
  end
60
60
  end
61
61
  end
@@ -29,7 +29,7 @@ module Hyperclient
29
29
 
30
30
  # Public: Sends a GET request the the resource url.
31
31
  #
32
- # Returns: The response parsed response.
32
+ # Returns: The parsed response.
33
33
  def get
34
34
  self.class.get(url).parsed_response
35
35
  end
@@ -0,0 +1,52 @@
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 resources
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
+
41
+ # Public: Returns a String with the resource URL or nil of it does not have
42
+ # one.
43
+ def url
44
+ if @representation && @representation['_links'] && @representation['_links']['self'] &&
45
+ (url = @representation['_links']['self']['href'])
46
+ return url
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ require 'json'
@@ -1,4 +1,4 @@
1
- require 'hyperclient/response'
1
+ require 'hyperclient/representation'
2
2
  require 'hyperclient/http'
3
3
 
4
4
  module Hyperclient
@@ -7,8 +7,8 @@ module Hyperclient
7
7
  # resource's attributes, links and embedded resources.
8
8
  class Resource
9
9
  extend Forwardable
10
- # Public: Delegate attributes and resources to the response.
11
- def_delegators :response, :attributes, :resources, :links
10
+ # Public: Delegate attributes and resources to the representation.
11
+ def_delegators :representation, :attributes, :resources, :links
12
12
 
13
13
  # Public: Delegate all HTTP methods (get, post, put, delete, options and
14
14
  # head) to Hyperclient::HTTP.
@@ -24,14 +24,14 @@ module Hyperclient
24
24
  #
25
25
  # options - An options Hash to initialize different values:
26
26
  # :name - The String name of the resource.
27
- # :response - An optional Hash representation of the resource's
28
- # HTTP response.
27
+ # :representation - An optional Hash representation of the resource's
28
+ # HTTP representation.
29
29
  # :http - An optional Hash to pass to the HTTP class.
30
30
  def initialize(url, options = {})
31
31
  @url = url
32
32
  @name = options[:name]
33
33
  @http = HTTP.new(self, options[:http])
34
- initialize_response(options[:response])
34
+ initialize_representation(options[:representation])
35
35
  end
36
36
 
37
37
  # Public: Sets the entry point for all the resources in your API client.
@@ -52,31 +52,31 @@ module Hyperclient
52
52
  end
53
53
  end
54
54
 
55
- # Public: Gets a fresh response from the resource representation.
55
+ # Public: Gets a fresh representation from the resource representation.
56
56
  #
57
57
  # Returns itself (this way you can chain method calls).
58
58
  def reload
59
- initialize_response(get)
59
+ initialize_representation(get)
60
60
  self
61
61
  end
62
62
 
63
63
  private
64
- # Internal: Initializes a Response
64
+ # Internal: Initializes a Representation
65
65
  #
66
- # raw_response - A Hash representing the HTTP response for the resource.
66
+ # raw_representation - A Hash representing the HTTP representation for the resource.
67
67
  #
68
68
  # Return nothing.
69
- def initialize_response(raw_response)
70
- if raw_response && raw_response.is_a?(Hash) && !raw_response.empty?
71
- @response = Response.new(raw_response)
72
- @url = @response.url if @response.url
69
+ def initialize_representation(raw_representation)
70
+ if raw_representation && !raw_representation.empty?
71
+ @representation = Representation.new(raw_representation)
72
+ @url = @representation.url if @representation.url
73
73
  end
74
74
  end
75
75
 
76
- # Internal: Returns the resource response.
77
- def response
78
- reload unless @response
79
- @response
76
+ # Internal: Returns the resource representation.
77
+ def representation
78
+ reload unless @representation
79
+ @representation
80
80
  end
81
81
  end
82
82
  end
@@ -1,3 +1,3 @@
1
1
  module Hyperclient
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  require_relative '../test_helper'
2
- require 'hyperclient/response'
2
+ require 'hyperclient/representation'
3
3
 
4
4
  module Hyperclient
5
5
  describe Discoverer do
@@ -7,13 +7,13 @@ module Hyperclient
7
7
  Resource.entry_point = 'http://api.myexample.org/'
8
8
  end
9
9
 
10
- let (:response) do
10
+ let (:representation) do
11
11
  JSON.parse(File.read('test/fixtures/element.json'))
12
12
  end
13
13
 
14
14
  describe 'each' do
15
15
  it 'iterates between resources' do
16
- discoverer = Discoverer.new(response['_links'])
16
+ discoverer = Discoverer.new(representation['_links'])
17
17
 
18
18
  discoverer.each do |resource|
19
19
  resource.must_be_kind_of Resource
@@ -23,7 +23,7 @@ module Hyperclient
23
23
 
24
24
  describe '[]' do
25
25
  it 'fetches a resource' do
26
- discoverer = Discoverer.new(response['_links'])
26
+ discoverer = Discoverer.new(representation['_links'])
27
27
 
28
28
  discoverer['filter'].must_be_kind_of Resource
29
29
  end
@@ -31,43 +31,43 @@ module Hyperclient
31
31
 
32
32
  describe 'resources' do
33
33
  it 'does not include self as a resource' do
34
- discoverer = Discoverer.new(response['_links'])
34
+ discoverer = Discoverer.new(representation['_links'])
35
35
 
36
36
  lambda { discoverer.self }.must_raise NoMethodError
37
37
  end
38
38
 
39
39
  it 'builds single resources' do
40
- discoverer = Discoverer.new(response['_links'])
40
+ discoverer = Discoverer.new(representation['_links'])
41
41
 
42
42
  discoverer.filter.must_be_kind_of Resource
43
43
  end
44
44
 
45
45
  it 'builds collection resources' do
46
- discoverer = Discoverer.new(response['_embedded'])
46
+ discoverer = Discoverer.new(representation['_embedded'])
47
47
 
48
48
  discoverer.episodes.must_be_kind_of Array
49
49
  end
50
50
 
51
51
  it 'also builds elements in collection resources' do
52
- discoverer = Discoverer.new(response['_embedded'])
52
+ discoverer = Discoverer.new(representation['_embedded'])
53
53
 
54
54
  discoverer.episodes.first.must_be_kind_of Resource
55
55
  end
56
56
 
57
57
  it 'initializes resources with its URL' do
58
- discoverer = Discoverer.new(response['_links'])
58
+ discoverer = Discoverer.new(representation['_links'])
59
59
 
60
60
  discoverer.filter.url.wont_be_empty
61
61
  end
62
62
 
63
- it 'initializes resources with the response' do
64
- discoverer = Discoverer.new(response['_embedded'])
63
+ it 'initializes resources with the representation' do
64
+ discoverer = Discoverer.new(representation['_embedded'])
65
65
 
66
66
  discoverer.author.attributes.wont_be_empty
67
67
  end
68
68
 
69
69
  it 'initializes resources with its name' do
70
- discoverer = Discoverer.new(response['_links'])
70
+ discoverer = Discoverer.new(representation['_links'])
71
71
 
72
72
  discoverer.filter.name.wont_be_empty
73
73
  end
@@ -0,0 +1,64 @@
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 '{"_links": {"self": {"href": "/productions/1"}}}'
13
+
14
+ representation.url.must_equal '/productions/1'
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.url.must_equal nil
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 'resources' do
45
+ it 'returns resources included in the _embedded section' do
46
+ representation.resources.author.must_be_kind_of Resource
47
+ representation.resources.episodes.first.must_be_kind_of Resource
48
+ representation.resources.episodes.last.must_be_kind_of Resource
49
+ end
50
+ end
51
+
52
+ describe 'url' do
53
+ it 'returns the url of the resource grabbed from the representation' do
54
+ representation.url.must_equal '/productions/1'
55
+ end
56
+
57
+ it 'returns nil when the representation does not include the resource url' do
58
+ representation = Representation.new({_links: {media: {href: '/media/1'}}})
59
+
60
+ representation.url.must_equal nil
61
+ end
62
+ end
63
+ end
64
+ end
@@ -3,12 +3,12 @@ require 'hyperclient/resource'
3
3
 
4
4
  module Hyperclient
5
5
  describe Resource do
6
- let(:response) do
6
+ let(:representation) do
7
7
  File.read('test/fixtures/element.json')
8
8
  end
9
9
 
10
- let(:parsed_response) do
11
- JSON.parse(response)
10
+ let(:parsed_representation) do
11
+ JSON.parse(representation)
12
12
  end
13
13
 
14
14
  before do
@@ -32,19 +32,19 @@ module Hyperclient
32
32
  stub_request(:get, 'http://api.example.org')
33
33
  end
34
34
 
35
- it 'initializes the response when one is given' do
36
- resource = Resource.new('/', {response: JSON.parse(response)})
35
+ it 'initializes the representation when one is given' do
36
+ resource = Resource.new('/', {representation: JSON.parse(representation)})
37
37
 
38
38
  assert_not_requested(:get, 'http://api.example.org/')
39
39
  end
40
40
 
41
- it 'updates the resource URL if the response has one' do
42
- resource = Resource.new('/', {response: JSON.parse(response)})
41
+ it 'updates the resource URL if the representation has one' do
42
+ resource = Resource.new('/', {representation: JSON.parse(representation)})
43
43
 
44
44
  resource.url.must_include '/productions/1'
45
45
  end
46
46
 
47
- it 'does no update the resource URL if the response does not have one' do
47
+ it 'does no update the resource URL if the representation does not have one' do
48
48
  resource = Resource.new('/', {})
49
49
 
50
50
  resource.url.wont_include '/productions/1'
@@ -60,7 +60,7 @@ module Hyperclient
60
60
  describe 'reload' do
61
61
  before do
62
62
  stub_request(:get, "http://api.example.org/productions/1").
63
- to_return(:status => 200, :body => response, headers: {content_type: 'application/json'})
63
+ to_return(:status => 200, :body => representation, headers: {content_type: 'application/json'})
64
64
  end
65
65
 
66
66
  it 'retrives itself from the API' do
@@ -22,7 +22,12 @@ describe Hyperclient do
22
22
  end
23
23
 
24
24
  it 'initializes a Resource at the entry point' do
25
- api.new.entry.url.must_equal 'http://my.api.org/'
25
+ api.new.entry.url.must_equal 'http://my.api.org'
26
+ end
27
+
28
+ it 'also works with entry points that are not in the root' do
29
+ api.entry_point 'http://my.api.org/api'
30
+ api.new.entry.url.must_equal 'http://my.api.org/api'
26
31
  end
27
32
 
28
33
  it 'sets the Resource name' do
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.3
4
+ version: 0.0.4
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-05-31 00:00:00.000000000 Z
12
+ date: 2012-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
@@ -92,23 +92,24 @@ files:
92
92
  - MIT-LICENSE
93
93
  - Rakefile
94
94
  - Readme.md
95
+ - examples/cs.rb
95
96
  - examples/hal_shop.rb
96
97
  - hyperclient.gemspec
97
98
  - lib/hyperclient.rb
98
99
  - lib/hyperclient/discoverer.rb
99
100
  - lib/hyperclient/http.rb
101
+ - lib/hyperclient/representation.rb
100
102
  - lib/hyperclient/resource.rb
101
103
  - lib/hyperclient/resource_factory.rb
102
- - lib/hyperclient/response.rb
103
104
  - lib/hyperclient/version.rb
104
105
  - test/fixtures/collection.json
105
106
  - test/fixtures/element.json
106
107
  - test/fixtures/root.json
107
108
  - test/hyperclient/discoverer_test.rb
108
109
  - test/hyperclient/http_test.rb
110
+ - test/hyperclient/representation_test.rb
109
111
  - test/hyperclient/resource_factory_test.rb
110
112
  - test/hyperclient/resource_test.rb
111
- - test/hyperclient/response_test.rb
112
113
  - test/hyperclient_test.rb
113
114
  - test/test_helper.rb
114
115
  homepage: http://codegram.github.com/hyperclient/
@@ -125,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
126
  version: '0'
126
127
  segments:
127
128
  - 0
128
- hash: 2448296263010756108
129
+ hash: 12208377733581260
129
130
  required_rubygems_version: !ruby/object:Gem::Requirement
130
131
  none: false
131
132
  requirements:
@@ -134,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
135
  version: '0'
135
136
  segments:
136
137
  - 0
137
- hash: 2448296263010756108
138
+ hash: 12208377733581260
138
139
  requirements: []
139
140
  rubyforge_project:
140
141
  rubygems_version: 1.8.23
@@ -147,9 +148,9 @@ test_files:
147
148
  - test/fixtures/root.json
148
149
  - test/hyperclient/discoverer_test.rb
149
150
  - test/hyperclient/http_test.rb
151
+ - test/hyperclient/representation_test.rb
150
152
  - test/hyperclient/resource_factory_test.rb
151
153
  - test/hyperclient/resource_test.rb
152
- - test/hyperclient/response_test.rb
153
154
  - test/hyperclient_test.rb
154
155
  - test/test_helper.rb
155
156
  has_rdoc:
@@ -1,42 +0,0 @@
1
- require 'hyperclient/discoverer'
2
-
3
- module Hyperclient
4
- # Public: This class is responsible for parsing a response from the API
5
- # and exposing some methods to access its values.
6
- #
7
- # It is mainly used by Hyperclient::Resource.
8
- class Response
9
- # Public: Initializes a Response.
10
- #
11
- # response - A Hash representing the response from the API.
12
- def initialize(response)
13
- @response = response
14
- end
15
-
16
- # Public: Returns a Discoverer for the _links section of the response. It
17
- # can be used later to use the resources from this section.
18
- def links
19
- @links ||= Discoverer.new(@response['_links'])
20
- end
21
-
22
- # Public: Returns a Discoverer for the _embedded section of the response.
23
- # It can be used later to use the resources from this section.
24
- def resources
25
- @embedded ||= Discoverer.new(@response['_embedded'])
26
- end
27
-
28
- # Public: Returns a Hash with the attributes of the resource.
29
- def attributes
30
- @attributes ||= @response.dup.delete_if {|key, value| key =~ /^_/}
31
- end
32
-
33
- # Public: Returns a String with the resource URL or nil of it does not have
34
- # one.
35
- def url
36
- if @response && @response['_links'] && @response['_links']['self'] &&
37
- (url = @response['_links']['self']['href'])
38
- return url
39
- end
40
- end
41
- end
42
- end
@@ -1,50 +0,0 @@
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