diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a8c90ca..f77748a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,5 +1,5 @@ # This configuration was generated by `rubocop --auto-gen-config` -# on 2014-10-17 09:13:36 -0400 using RuboCop version 0.26.1. +# on 2015-06-05 09:21:59 -0400 using RuboCop version 0.27.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -8,12 +8,12 @@ # Offense count: 1 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 129 + Max: 103 -# Offense count: 72 +# Offense count: 78 # Configuration parameters: AllowURI, URISchemes. Metrics/LineLength: - Max: 140 + Max: 142 # Offense count: 3 # Configuration parameters: CountComments. diff --git a/CHANGELOG.md b/CHANGELOG.md index be3434c..e207546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 0.7.1 (Next) +* [#87](https://github.com/codegram/hyperclient/pull/87): Fix: eager delegation causes link skipping - [@dblock](https://github.com/dblock). * Your contribution here. ### 0.7.0 (February 23, 2015) diff --git a/features/api_navigation.feature b/features/api_navigation.feature index e8c8623..6aa4266 100644 --- a/features/api_navigation.feature +++ b/features/api_navigation.feature @@ -21,3 +21,8 @@ Feature: API navigation Given I connect to the API When I load a single post Then I should also be able to access it's embedded comments + + Scenario: Navigation links + When I connect to the API + Then I should be able to navigate to next page + Then I should be able to navigate to next page without links diff --git a/features/steps/api_navigation.rb b/features/steps/api_navigation.rb index a77bd4b..234b67a 100644 --- a/features/steps/api_navigation.rb +++ b/features/steps/api_navigation.rb @@ -30,4 +30,12 @@ class Spinach::Features::ApiNavigation < Spinach::FeatureSteps comment = @post._embedded.comments.first comment._attributes.title.wont_equal nil end + + step 'I should be able to navigate to next page' do + assert_equal '/posts_of_page2', api._links.next._links.posts._url + end + + step 'I should be able to navigate to next page without links' do + assert_equal '/posts_of_page2', api.next.posts._url + end end diff --git a/features/support/api.rb b/features/support/api.rb index 4b7f748..81560ee 100644 --- a/features/support/api.rb +++ b/features/support/api.rb @@ -8,6 +8,8 @@ module API stub_request(:any, %r{api.example.org*}).to_return(body: root_response, headers: { 'Content-Type' => 'application/hal+json' }) stub_request(:get, 'api.example.org/posts').to_return(body: posts_response, headers: { 'Content-Type' => 'application/hal+json' }) stub_request(:get, 'api.example.org/posts/1').to_return(body: post_response, headers: { 'Content-Type' => 'application/hal+json' }) + stub_request(:get, 'api.example.org/page2').to_return(body: page2_response, headers: { 'Content-Type' => 'application/hal+json' }) + stub_request(:get, 'api.example.org/page3').to_return(body: page3_response, headers: { 'Content-Type' => 'application/hal+json' }) end def api diff --git a/features/support/fixtures.rb b/features/support/fixtures.rb index 20235a9..3f2587c 100644 --- a/features/support/fixtures.rb +++ b/features/support/fixtures.rb @@ -8,7 +8,8 @@ def root_response "self": { "href": "/" }, "posts": { "href": "/posts" }, "search": { "href": "/search{?q}", "templated": true }, - "api:authors": { "href": "/authors" } + "api:authors": { "href": "/authors" }, + "next": { "href": "/page2" } } }' end @@ -39,5 +40,25 @@ def post_response } }' end + + def page2_response + '{ + "_links": { + "self": { "href": "/page2" }, + "posts": { "href": "/posts_of_page2" }, + "next": { "href": "/page3" } + } + }' + end + + def page3_response + '{ + "_links": { + "self": { "href": "/page3" }, + "posts": { "href": "/posts_of_page3" }, + "api:authors": { "href": "/authors" } + } + }' + end end end diff --git a/lib/hyperclient/entry_point.rb b/lib/hyperclient/entry_point.rb index b83f83d..108879e 100644 --- a/lib/hyperclient/entry_point.rb +++ b/lib/hyperclient/entry_point.rb @@ -29,6 +29,7 @@ def message # class EntryPoint < Link extend Forwardable + # Public: Delegates common methods to be used with the Faraday connection. def_delegators :connection, :basic_auth, :digest_auth, :token_auth, :headers, :headers=, :params, :params= diff --git a/lib/hyperclient/link.rb b/lib/hyperclient/link.rb index 075b269..c660ed5 100644 --- a/lib/hyperclient/link.rb +++ b/lib/hyperclient/link.rb @@ -123,20 +123,25 @@ def to_s private - # Internal: Delegate the method to the API if it exists. - # - # This allows `api.posts` instead of `api.links.posts.embedded` + # Internal: Delegate the method further down the API if the resource cannot serve it. def method_missing(method, *args, &block) - if @key && _resource.respond_to?(@key) && (delegate = _resource.send(@key)) && delegate.respond_to?(method.to_s) - # named.named becomes named - delegate.send(method, *args, &block) - elsif _resource.respond_to?(method.to_s) - _resource.send(method, *args, &block) + if _resource.respond_to?(method.to_s) + _resource.send(method, *args, &block) || delegate_method(method, *args, &block) else super end end + # Internal: Delegate the method to the API if the resource cannot serve it. + # + # This allows `api.posts` instead of `api._links.posts.embedded.posts` + def delegate_method(method, *args, &block) + return unless @key && _resource.respond_to?(@key) + @delegate ||= _resource.send(@key) + return unless @delegate && @delegate.respond_to?(method.to_s) + @delegate.send(method, *args, &block) + end + # Internal: Accessory method to allow the link respond to the # methods that will hit method_missing. def respond_to_missing?(method, _include_private = false) diff --git a/test/hyperclient/link_test.rb b/test/hyperclient/link_test.rb index cd28f42..3dcff29 100644 --- a/test/hyperclient/link_test.rb +++ b/test/hyperclient/link_test.rb @@ -272,6 +272,16 @@ module Hyperclient resource.foos._embedded.orders.first.id.must_equal 1 resource.foos.first.must_equal nil end + + it 'backtracks when navigating links' do + resource = Resource.new({ '_links' => { 'next' => { 'href' => '/page2' } } }, entry_point) + + stub_request(entry_point.connection) do |stub| + stub.get('http://api.example.org/page2') { [200, {}, { '_links' => { 'next' => { 'href' => 'http://api.example.org/page3' } } }] } + end + + resource.next._links.next._url.must_equal 'http://api.example.org/page3' + end end describe 'resource' do