Skip to content

Commit 21552df

Browse files
committed
Merge pull request #87 from dblock/eager-navigation-fix
Fix: eager delegation causes link skipping.
2 parents b4026dc + c25e5d3 commit 21552df

File tree

9 files changed

+66
-13
lines changed

9 files changed

+66
-13
lines changed

.rubocop_todo.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This configuration was generated by `rubocop --auto-gen-config`
2-
# on 2014-10-17 09:13:36 -0400 using RuboCop version 0.26.1.
2+
# on 2015-06-05 09:21:59 -0400 using RuboCop version 0.27.1.
33
# The point is for the user to remove these configuration records
44
# one by one as the offenses are removed from the code base.
55
# Note that changes in the inspected code, or installation of new
@@ -8,12 +8,12 @@
88
# Offense count: 1
99
# Configuration parameters: CountComments.
1010
Metrics/ClassLength:
11-
Max: 129
11+
Max: 103
1212

13-
# Offense count: 72
13+
# Offense count: 78
1414
# Configuration parameters: AllowURI, URISchemes.
1515
Metrics/LineLength:
16-
Max: 140
16+
Max: 142
1717

1818
# Offense count: 3
1919
# Configuration parameters: CountComments.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### 0.7.1 (Next)
22

3+
* [#87](https://github.com/codegram/hyperclient/pull/87): Fix: eager delegation causes link skipping - [@dblock](https://github.com/dblock).
34
* Your contribution here.
45

56
### 0.7.0 (February 23, 2015)

features/api_navigation.feature

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ Feature: API navigation
2121
Given I connect to the API
2222
When I load a single post
2323
Then I should also be able to access it's embedded comments
24+
25+
Scenario: Navigation links
26+
When I connect to the API
27+
Then I should be able to navigate to next page
28+
Then I should be able to navigate to next page without links

features/steps/api_navigation.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,12 @@ class Spinach::Features::ApiNavigation < Spinach::FeatureSteps
3030
comment = @post._embedded.comments.first
3131
comment._attributes.title.wont_equal nil
3232
end
33+
34+
step 'I should be able to navigate to next page' do
35+
assert_equal '/posts_of_page2', api._links.next._links.posts._url
36+
end
37+
38+
step 'I should be able to navigate to next page without links' do
39+
assert_equal '/posts_of_page2', api.next.posts._url
40+
end
3341
end

features/support/api.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ module API
88
stub_request(:any, %r{api.example.org*}).to_return(body: root_response, headers: { 'Content-Type' => 'application/hal+json' })
99
stub_request(:get, 'api.example.org/posts').to_return(body: posts_response, headers: { 'Content-Type' => 'application/hal+json' })
1010
stub_request(:get, 'api.example.org/posts/1').to_return(body: post_response, headers: { 'Content-Type' => 'application/hal+json' })
11+
stub_request(:get, 'api.example.org/page2').to_return(body: page2_response, headers: { 'Content-Type' => 'application/hal+json' })
12+
stub_request(:get, 'api.example.org/page3').to_return(body: page3_response, headers: { 'Content-Type' => 'application/hal+json' })
1113
end
1214

1315
def api

features/support/fixtures.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ def root_response
88
"self": { "href": "/" },
99
"posts": { "href": "/posts" },
1010
"search": { "href": "/search{?q}", "templated": true },
11-
"api:authors": { "href": "/authors" }
11+
"api:authors": { "href": "/authors" },
12+
"next": { "href": "/page2" }
1213
}
1314
}'
1415
end
@@ -39,5 +40,25 @@ def post_response
3940
}
4041
}'
4142
end
43+
44+
def page2_response
45+
'{
46+
"_links": {
47+
"self": { "href": "/page2" },
48+
"posts": { "href": "/posts_of_page2" },
49+
"next": { "href": "/page3" }
50+
}
51+
}'
52+
end
53+
54+
def page3_response
55+
'{
56+
"_links": {
57+
"self": { "href": "/page3" },
58+
"posts": { "href": "/posts_of_page3" },
59+
"api:authors": { "href": "/authors" }
60+
}
61+
}'
62+
end
4263
end
4364
end

lib/hyperclient/entry_point.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def message
2929
#
3030
class EntryPoint < Link
3131
extend Forwardable
32+
3233
# Public: Delegates common methods to be used with the Faraday connection.
3334
def_delegators :connection, :basic_auth, :digest_auth, :token_auth, :headers, :headers=, :params, :params=
3435

lib/hyperclient/link.rb

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,20 +123,25 @@ def to_s
123123

124124
private
125125

126-
# Internal: Delegate the method to the API if it exists.
127-
#
128-
# This allows `api.posts` instead of `api.links.posts.embedded`
126+
# Internal: Delegate the method further down the API if the resource cannot serve it.
129127
def method_missing(method, *args, &block)
130-
if @key && _resource.respond_to?(@key) && (delegate = _resource.send(@key)) && delegate.respond_to?(method.to_s)
131-
# named.named becomes named
132-
delegate.send(method, *args, &block)
133-
elsif _resource.respond_to?(method.to_s)
134-
_resource.send(method, *args, &block)
128+
if _resource.respond_to?(method.to_s)
129+
_resource.send(method, *args, &block) || delegate_method(method, *args, &block)
135130
else
136131
super
137132
end
138133
end
139134

135+
# Internal: Delegate the method to the API if the resource cannot serve it.
136+
#
137+
# This allows `api.posts` instead of `api._links.posts.embedded.posts`
138+
def delegate_method(method, *args, &block)
139+
return unless @key && _resource.respond_to?(@key)
140+
@delegate ||= _resource.send(@key)
141+
return unless @delegate && @delegate.respond_to?(method.to_s)
142+
@delegate.send(method, *args, &block)
143+
end
144+
140145
# Internal: Accessory method to allow the link respond to the
141146
# methods that will hit method_missing.
142147
def respond_to_missing?(method, _include_private = false)

test/hyperclient/link_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,16 @@ module Hyperclient
272272
resource.foos._embedded.orders.first.id.must_equal 1
273273
resource.foos.first.must_equal nil
274274
end
275+
276+
it 'backtracks when navigating links' do
277+
resource = Resource.new({ '_links' => { 'next' => { 'href' => '/page2' } } }, entry_point)
278+
279+
stub_request(entry_point.connection) do |stub|
280+
stub.get('http://api.example.org/page2') { [200, {}, { '_links' => { 'next' => { 'href' => 'http://api.example.org/page3' } } }] }
281+
end
282+
283+
resource.next._links.next._url.must_equal 'http://api.example.org/page3'
284+
end
275285
end
276286

277287
describe 'resource' do

0 commit comments

Comments
 (0)