hyperclient 0.9.3 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30a6a424eb3f9cd9a70b19d49f3f9d3f3b952921fb6a2b034116296c7eb1e31f
4
- data.tar.gz: b35c0cd0abaa39753b53f59c0f718152acf3a8915b498d0acef4e7b8f2f0d82c
3
+ metadata.gz: 5f55cb82362d8eca25347e15c91387bbf2957f1f1241af27fccff1086b412d1b
4
+ data.tar.gz: f31325a293b0aec888b94910b8c3dc28d2f30a6666fe59063b4112251f46e7c4
5
5
  SHA512:
6
- metadata.gz: 93c51dca3fa731081e1e82540d0c0f2d22f17e65ae336021b4b7199eac4ff1eecd3bd37e840cc02831e2d62b98d20a4a1e696dd5fba8bdd5ff51516f6bc763b6
7
- data.tar.gz: 9a2ad7090c37531384d42105754bc4d492ba711048d758b042863e43a43612317007d8446c02ad0efc88e09f95f6f5cdbb48239319724e1c2f35862f2ad92dcc
6
+ metadata.gz: e9b17e112614d3c600497c6c7cfd56268195427da789629d006742ff96e70d2b6972ba16fbfa075ab62613eeace923d8fdb4f231b9d86a04f82b75fa5d4a4826
7
+ data.tar.gz: 8a8861e266967702c86d5762ec5b786687298c37e797a5c324583c9d3c9d2fe4dc83cf8841c912db3d14a0bf731d1dbee0c1c85b3d43961589c5741b43b3f128
@@ -3,8 +3,8 @@ inherit_from: .rubocop_todo.yml
3
3
  AllCops:
4
4
  TargetRubyVersion: 2.3
5
5
 
6
- Metrics/BlockLength:
7
- ExcludedMethods: [it, describe]
6
+ Metrics:
7
+ Enabled: false
8
8
 
9
9
  Style/FrozenStringLiteralComment:
10
- Enabled: false
10
+ Enabled: false
@@ -1,26 +1,11 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2020-05-10 19:37:12 -0400 using RuboCop version 0.81.0.
3
+ # on 2020-12-03 14:08:14 -0500 using RuboCop version 0.81.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- # Configuration parameters: CountComments.
11
- Metrics/ClassLength:
12
- Max: 103
13
-
14
- # Offense count: 4
15
- # Configuration parameters: CountComments, ExcludedMethods.
16
- Metrics/MethodLength:
17
- Max: 25
18
-
19
- # Offense count: 3
20
- # Configuration parameters: CountComments.
21
- Metrics/ModuleLength:
22
- Max: 265
23
-
24
9
  # Offense count: 2
25
10
  # Cop supports --auto-correct.
26
11
  # Configuration parameters: AutoCorrect, EnforcedStyle.
@@ -59,9 +44,9 @@ Style/MethodMissingSuper:
59
44
  Exclude:
60
45
  - 'lib/hyperclient/collection.rb'
61
46
 
62
- # Offense count: 91
47
+ # Offense count: 101
63
48
  # Cop supports --auto-correct.
64
49
  # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
65
50
  # URISchemes: http, https
66
51
  Layout/LineLength:
67
- Max: 142
52
+ Max: 147
@@ -5,21 +5,20 @@ sudo: false
5
5
  matrix:
6
6
  include:
7
7
  - rvm: 2.6.6
8
- - rvm: 2.5.5
9
- - rvm: 2.4.6
10
- - rvm: 2.4.6
8
+ env: FARADAY_VERSION=0.9.0
9
+ - rvm: 2.6.6
10
+ env: FARADAY_VERSION=0.17.0
11
+ - rvm: 2.6.6
12
+ env: FARADAY_VERSION="~> 1.0"
13
+ - rvm: 2.6.6
11
14
  script:
12
15
  - bundle exec danger
16
+ - rvm: 2.3.8
13
17
  - rvm: jruby-9.2.7.0
14
18
  - rvm: jruby-head
15
- - rvm: 2.3.8
16
19
  - rvm: ruby-head
17
20
  allow_failures:
18
21
  - rvm: ruby-head
19
22
  - rvm: jruby-head
20
23
 
21
- before_install:
22
- - gem update --system
23
- - gem install bundler
24
-
25
24
  bundler_args: --without development
@@ -1,5 +1,15 @@
1
1
  ## Changelog
2
2
 
3
+ ### 1.0.1 (2021/01/02)
4
+
5
+ * [#193](https://github.com/codegram/hyperclient/pull/193): Auto-paginate collections - [@dblock](https://github.com/dblock).
6
+ * [#163](https://github.com/codegram/hyperclient/pull/163): Test against Faraday 0.9, 0.17 and 1.0+ - [@dblock](https://github.com/dblock).
7
+ * [#199](https://github.com/codegram/hyperclient/pull/199): Use digest_auth from faraday-digestauth - [@dblock](https://github.com/dblock).
8
+
9
+ ### 1.0.0 (2021/01/02)
10
+
11
+ * NOTE: **⚠ This version has been yanked ⚠** - [@dblock](https://github.com/dblock).
12
+
3
13
  ### 0.9.3 (2020/05/14)
4
14
 
5
15
  * [#149](https://github.com/codegram/hyperclient/pull/149): Address Faraday warnings - [@yuki24](https://github.com/yuki24).
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ git_source(:github) { |repo| "https://github.com/#{repo['/'] ? repo : "#{repo}/#
3
3
 
4
4
  source 'https://rubygems.org'
5
5
 
6
+ gem 'faraday', ENV['FARADAY_VERSION'] if ENV.key?('FARADAY_VERSION')
7
+
6
8
  gemspec
7
9
 
8
10
  group :development do
data/README.md CHANGED
@@ -85,12 +85,19 @@ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api') do |client|
85
85
  end
86
86
  ```
87
87
 
88
- You can modify headers or specify authentication after a connection has been created. Hyperclient supports Basic, Token or Digest auth as well as many other Faraday extensions.
88
+ You can modify headers or specify authentication after a connection has been created. Hyperclient supports Basic, Token or [Digest auth](https://github.com/bhaberer/faraday-digestauth) as well as many other Faraday extensions.
89
89
 
90
90
  ```ruby
91
- api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api')
92
- api.digest_auth('username', 'password')
93
- api.headers.update('Accept-Encoding' => 'deflate, gzip')
91
+ require 'faraday/digestauth'
92
+
93
+ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api') do |client|
94
+ client.connection(default: false) do |conn|
95
+ conn.request :digest, 'username', 'password'
96
+ conn.request :json
97
+ conn.response :json, content_type: /\bjson$/
98
+ conn.adapter :net_http
99
+ end
100
+ end
94
101
  ```
95
102
 
96
103
  You can access the Faraday connection directly after it has been created and add middleware to it. As an example, you could use the [faraday-http-cache-middleware](https://github.com/plataformatec/faraday-http-cache).
@@ -102,7 +109,7 @@ api.connection.use :http_cache
102
109
 
103
110
  ## Resources and Attributes
104
111
 
105
- Hyperclient will fetch and discover the resources from your API.
112
+ Hyperclient will fetch and discover the resources from your API and automatically paginate when possible.
106
113
 
107
114
  ```ruby
108
115
  api.splines.each do |spline|
@@ -7,6 +7,11 @@ Feature: API navigation
7
7
  When I connect to the API
8
8
  Then I should be able to navigate to posts and authors
9
9
 
10
+ Scenario: Links
11
+ When I connect to the API
12
+ Then I should be able to paginate posts
13
+ Then I should be able to paginate authors
14
+
10
15
  Scenario: Templated links
11
16
  Given I connect to the API
12
17
  When I search for a post with a templated link
@@ -9,6 +9,19 @@ class Spinach::Features::ApiNavigation < Spinach::FeatureSteps
9
9
  assert_requested :get, 'http://api.example.org/authors'
10
10
  end
11
11
 
12
+ step 'I should be able to paginate posts' do
13
+ assert_kind_of Enumerator, api.posts.each
14
+ assert_equal 4, api.posts.to_a.count
15
+ assert_requested :get, 'http://api.example.org/posts'
16
+ assert_requested :get, 'http://api.example.org/posts?page=2'
17
+ assert_requested :get, 'http://api.example.org/posts?page=3'
18
+ end
19
+
20
+ step 'I should be able to paginate authors' do
21
+ assert_equal 1, api._links['api:authors'].to_a.count
22
+ assert_requested :get, 'http://api.example.org/authors'
23
+ end
24
+
12
25
  step 'I search for a post with a templated link' do
13
26
  api._links.search._expand(q: 'something')._resource
14
27
  end
@@ -50,8 +63,8 @@ class Spinach::Features::ApiNavigation < Spinach::FeatureSteps
50
63
  step 'I should be able to count embedded items' do
51
64
  assert_equal 2, api._links.posts._resource._embedded.posts.count
52
65
  assert_equal 2, api.posts._embedded.posts.count
53
- assert_equal 2, api.posts.count
54
- assert_equal 2, api.posts.map.count
66
+ assert_equal 4, api.posts.count
67
+ assert_equal 4, api.posts.map.count
55
68
  end
56
69
 
57
70
  step 'I should be able to iterate over embedded items' do
@@ -59,6 +72,6 @@ class Spinach::Features::ApiNavigation < Spinach::FeatureSteps
59
72
  api.posts.each do |_post|
60
73
  count += 1
61
74
  end
62
- assert_equal 2, count
75
+ assert_equal 4, count
63
76
  end
64
77
  end
@@ -25,7 +25,7 @@ class Spinach::Features::DefaultConfig < Spinach::FeatureSteps
25
25
  end
26
26
 
27
27
  step 'it should have been parsed as JSON' do
28
- @posts._attributes.total_posts.to_i.must_equal 2
29
- @posts._attributes['total_posts'].to_i.must_equal 2
28
+ @posts._attributes.total_posts.to_i.must_equal 4
29
+ @posts._attributes['total_posts'].to_i.must_equal 4
30
30
  end
31
31
  end
@@ -8,8 +8,14 @@ module API
8
8
  WebMock::Config.instance.query_values_notation = :flat_array
9
9
 
10
10
  stub_request(:any, /api.example.org*/).to_return(body: root_response, headers: { 'Content-Type' => 'application/hal+json' })
11
+ stub_request(:get, 'api.example.org').to_return(body: root_response, headers: { 'Content-Type' => 'application/hal+json' })
12
+ stub_request(:get, 'api.example.org/authors').to_return(body: authors_response, headers: { 'Content-Type' => 'application/hal+json' })
11
13
  stub_request(:get, 'api.example.org/posts').to_return(body: posts_response, headers: { 'Content-Type' => 'application/hal+json' })
12
- stub_request(:get, 'api.example.org/posts/1').to_return(body: post_response, headers: { 'Content-Type' => 'application/hal+json' })
14
+ stub_request(:get, 'api.example.org/posts?page=2').to_return(body: posts_page2_response, headers: { 'Content-Type' => 'application/hal+json' })
15
+ stub_request(:get, 'api.example.org/posts?page=3').to_return(body: posts_page3_response, headers: { 'Content-Type' => 'application/hal+json' })
16
+ stub_request(:get, 'api.example.org/posts/1').to_return(body: post1_response, headers: { 'Content-Type' => 'application/hal+json' })
17
+ stub_request(:get, 'api.example.org/posts/2').to_return(body: post2_response, headers: { 'Content-Type' => 'application/hal+json' })
18
+ stub_request(:get, 'api.example.org/posts/3').to_return(body: post3_response, headers: { 'Content-Type' => 'application/hal+json' })
13
19
  stub_request(:get, 'api.example.org/page2').to_return(body: page2_response, headers: { 'Content-Type' => 'application/hal+json' })
14
20
  stub_request(:get, 'api.example.org/page3').to_return(body: page3_response, headers: { 'Content-Type' => 'application/hal+json' })
15
21
  end
@@ -15,13 +15,32 @@ module Spinach
15
15
  }'
16
16
  end
17
17
 
18
+ def authors_response
19
+ '{
20
+ "_links": {
21
+ "self": { "href": "/authors" }
22
+ },
23
+ "_embedded": {
24
+ "api:authors": [
25
+ {
26
+ "name": "Lorem Ipsum",
27
+ "_links": {
28
+ "self": { "href": "/authors/1" }
29
+ }
30
+ }
31
+ ]
32
+ }
33
+ }'
34
+ end
35
+
18
36
  def posts_response
19
37
  '{
20
38
  "_links": {
21
39
  "self": { "href": "/posts" },
40
+ "next": {"href": "/posts?page=2"},
22
41
  "last_post": {"href": "/posts/1"}
23
42
  },
24
- "total_posts": "2",
43
+ "total_posts": "4",
25
44
  "_embedded": {
26
45
  "posts": [
27
46
  {
@@ -43,7 +62,48 @@ module Spinach
43
62
  }'
44
63
  end
45
64
 
46
- def post_response
65
+ def posts_page2_response
66
+ '{
67
+ "_links": {
68
+ "self": { "href": "/posts?page=2" },
69
+ "next": { "href": "/posts?page=3" }
70
+ },
71
+ "total_posts": "4",
72
+ "_embedded": {
73
+ "posts": [
74
+ {
75
+ "title": "My third blog post",
76
+ "body": "Lorem ipsum dolor sit amet",
77
+ "_links": {
78
+ "self": { "href": "/posts/3" }
79
+ }
80
+ }
81
+ ]
82
+ }
83
+ }'
84
+ end
85
+
86
+ def posts_page3_response
87
+ '{
88
+ "_links": {
89
+ "self": { "href": "/posts?page=3" }
90
+ },
91
+ "total_posts": "4",
92
+ "_embedded": {
93
+ "posts": [
94
+ {
95
+ "title": "My third blog post",
96
+ "body": "Lorem ipsum dolor sit amet",
97
+ "_links": {
98
+ "self": { "href": "/posts/4" }
99
+ }
100
+ }
101
+ ]
102
+ }
103
+ }'
104
+ end
105
+
106
+ def post1_response
47
107
  '{
48
108
  "_links": {
49
109
  "self": { "href": "/posts/1" }
@@ -60,6 +120,40 @@ module Spinach
60
120
  }'
61
121
  end
62
122
 
123
+ def post2_response
124
+ '{
125
+ "_links": {
126
+ "self": { "href": "/posts/2" }
127
+ },
128
+ "title": "My first blog post",
129
+ "body": "Lorem ipsum dolor sit amet",
130
+ "_embedded": {
131
+ "comments": [
132
+ {
133
+ "title": "Some comment"
134
+ }
135
+ ]
136
+ }
137
+ }'
138
+ end
139
+
140
+ def post3_response
141
+ '{
142
+ "_links": {
143
+ "self": { "href": "/posts/3" }
144
+ },
145
+ "title": "My first blog post",
146
+ "body": "Lorem ipsum dolor sit amet",
147
+ "_embedded": {
148
+ "comments": [
149
+ {
150
+ "title": "Some comment"
151
+ }
152
+ ]
153
+ }
154
+ }'
155
+ end
156
+
63
157
  def page2_response
64
158
  '{
65
159
  "_links": {
@@ -15,8 +15,6 @@ Gem::Specification.new do |gem|
15
15
 
16
16
  gem.add_dependency 'addressable'
17
17
  gem.add_dependency 'faraday', '>= 0.9.0'
18
- gem.add_dependency 'faraday-digestauth', '>= 0.3.0'
19
18
  gem.add_dependency 'faraday_hal_middleware'
20
19
  gem.add_dependency 'faraday_middleware'
21
- gem.add_dependency 'net-http-digest_auth'
22
20
  end
@@ -1,6 +1,5 @@
1
1
  require 'faraday_middleware'
2
2
  require 'faraday_hal_middleware'
3
- require_relative '../faraday/connection'
4
3
 
5
4
  module Hyperclient
6
5
  # Public: Exception that is raised when trying to modify an
@@ -30,7 +29,7 @@ module Hyperclient
30
29
  extend Forwardable
31
30
 
32
31
  # Public: Delegates common methods to be used with the Faraday connection.
33
- def_delegators :connection, :basic_auth, :digest_auth, :token_auth, :params, :params=
32
+ def_delegators :connection, :params, :params=
34
33
 
35
34
  # Public: Initializes an EntryPoint.
36
35
  #
@@ -4,6 +4,8 @@ module Hyperclient
4
4
  # Internal: The Link is used to let a Resource interact with the API.
5
5
  #
6
6
  class Link
7
+ include Enumerable
8
+
7
9
  # Public: Initializes a new Link.
8
10
  #
9
11
  # key - The key or name of the link.
@@ -19,6 +21,25 @@ module Hyperclient
19
21
  @resource = nil
20
22
  end
21
23
 
24
+ # Public: Each implementation to allow the class to use the Enumerable
25
+ # benefits for paginated, embedded items.
26
+ #
27
+ # Returns an Enumerator.
28
+ def each(&block)
29
+ if block_given?
30
+ current = self
31
+ while current
32
+ coll = current.respond_to?(@key) ? current.send(@key) : _resource
33
+ coll.each(&block)
34
+ break unless current._links[:next]
35
+
36
+ current = current._links.next
37
+ end
38
+ else
39
+ to_enum(:each)
40
+ end
41
+ end
42
+
22
43
  # Public: Indicates if the link is an URITemplate or a regular URI.
23
44
  #
24
45
  # Returns true if it is templated.
@@ -1,3 +1,3 @@
1
1
  module Hyperclient
2
- VERSION = '0.9.3'.freeze
2
+ VERSION = '1.0.1'.freeze
3
3
  end
@@ -12,29 +12,29 @@ module Hyperclient
12
12
  end
13
13
 
14
14
  it 'does not set _links as an attribute' do
15
- attributes.wont_respond_to :_links
15
+ _(attributes).wont_respond_to :_links
16
16
  end
17
17
 
18
18
  it 'does not set _embedded as an attribute' do
19
- attributes.wont_respond_to :_embedded
19
+ _(attributes).wont_respond_to :_embedded
20
20
  end
21
21
 
22
22
  it 'sets normal attributes' do
23
- attributes.must_respond_to :permitted
24
- attributes.permitted.must_equal true
23
+ _(attributes).must_respond_to :permitted
24
+ _(attributes.permitted).must_equal true
25
25
 
26
- attributes.must_respond_to :title
27
- attributes.title.must_equal 'Real World ASP.NET MVC3'
26
+ _(attributes).must_respond_to :title
27
+ _(attributes.title).must_equal 'Real World ASP.NET MVC3'
28
28
  end
29
29
 
30
30
  # Underscores should be allowed per http://tools.ietf.org/html/draft-kelly-json-hal#appendix-B.4
31
31
  it 'sets _hidden_attribute as an attribute' do
32
- attributes.must_respond_to :_hidden_attribute
33
- attributes._hidden_attribute.must_equal 'useful value'
32
+ _(attributes).must_respond_to :_hidden_attribute
33
+ _(attributes._hidden_attribute).must_equal 'useful value'
34
34
  end
35
35
 
36
36
  it 'is a collection' do
37
- Attributes.ancestors.must_include Collection
37
+ _(Attributes.ancestors).must_include Collection
38
38
  end
39
39
  end
40
40
  end