restify 1.7.0 → 1.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2f418e5e5915c208da950884e753c70e0c6da648978dde9eb75f067ba6feedc
4
- data.tar.gz: 98d1d14d29a06ac14ecaf564b0788d7054a7685ddc3126b4c24e80771ab95e86
3
+ metadata.gz: 6209f25a9b9d70898ee86458568d669ee588dd7d01a0918855ca39c6995332dd
4
+ data.tar.gz: 32e35cd463f1ce87d76cf0ded3bcf3f8266f305a5d10aafbc62fb47ff3145315
5
5
  SHA512:
6
- metadata.gz: eb15fbef6af84a77badcfea901c83676952359a509e3d7a91a8b46db4d2808fd7f4c552e151d9a2b7ede68b0f46aaca5c33c77f78bd4aaa614d10a04c31862f6
7
- data.tar.gz: 858169275d7a141d641cf1ef492692c6546057aaa6c865b1c5277038d417448b12288ac57cdc7a3e26a260b90275c6fe448deb135154ebe48dca95787ebdbf84
6
+ metadata.gz: eb70a7e9edb83c76074566b8607c9439b585b8c95925806a84b343dee96872f2d497fb96edf2120109c8cbcc337a141c8c6447ad35ed88b83c51dddcdb2cb0c0
7
+ data.tar.gz: 38cc84e8835134df693ef1161cf1696d15823e28077d5c5c4214c939298db79a01127c3147fe1f9ecdbce08de5a782a592da2bc7d9bfc79afdb7e70d194f0324
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.8.0
4
+
5
+ * Add HEAD request method (#16)
6
+
3
7
  ## 1.7.0
4
8
 
5
9
  * Introduce promise dependency timeouts (#15)
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Restify
2
2
 
3
- Restify is an experimental hypermedia REST client that does parallel, concurrent and keep-alive requests by default.
3
+ [![Build Status](https://travis-ci.org/jgraichen/restify.svg?branch=master)](https://travis-ci.org/jgraichen/restify)
4
+
5
+ Restify is an hypermedia REST client that does parallel, concurrent and keep-alive requests by default.
4
6
 
5
7
  Restify scans Link headers and returned resource for links and relations to other resources, represented as RFC6570 URI Templates, and exposes those to the developer.
6
8
 
@@ -121,7 +123,7 @@ See commented example in main spec [`spec/restify_spec.rb`](https://github.com/j
121
123
 
122
124
  ## License
123
125
 
124
- Copyright (C) 2014-2015 Jan Graichen
126
+ Copyright (C) 2014-2018 Jan Graichen
125
127
 
126
128
  This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
127
129
 
@@ -37,6 +37,7 @@ module Restify
37
37
  requests << [request, writer, retried]
38
38
  end
39
39
  end
40
+ # rubocop:enable all
40
41
 
41
42
  def connection
42
43
  @connection ||= EventMachine::HttpRequest.new(origin)
@@ -101,12 +102,13 @@ module Restify
101
102
  else
102
103
  begin
103
104
  raise "(#{req.response_header.status}) #{req.error}"
104
- rescue => e
105
+ rescue StandardError => e
105
106
  writer.reject e
106
107
  end
107
108
  end
108
109
  end
109
110
  end
111
+ # rubocop:enable all
110
112
  end
111
113
 
112
114
  def call_native(request, writer)
@@ -128,7 +130,7 @@ module Restify
128
130
  Thread.new do
129
131
  begin
130
132
  EventMachine.run {}
131
- rescue => e
133
+ rescue StandardError => e
132
134
  puts "#{self.class} -> #{e}\n#{e.backtrace.join("\n")}"
133
135
  raise e
134
136
  end
@@ -263,7 +263,7 @@ module Restify
263
263
  Thread.new do
264
264
  begin
265
265
  EventMachine.run {}
266
- rescue => e
266
+ rescue StandardError => e
267
267
  logger.error(e)
268
268
  raise e
269
269
  end
@@ -27,6 +27,8 @@ module Restify
27
27
  @sync
28
28
  end
29
29
 
30
+ # rubocop:disable AbcSize
31
+ # rubocop:disable MethodLength
30
32
  def call_native(request, writer)
31
33
  req = convert(request, writer)
32
34
 
@@ -34,20 +36,28 @@ module Restify
34
36
  req.run
35
37
  else
36
38
  @mutex.synchronize do
37
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] [#{request.object_id}] request:add method=#{request.method.upcase} url=#{request.uri}" }
39
+ debug 'request:add',
40
+ tag: request.object_id,
41
+ method: request.method.upcase,
42
+ url: request.uri
43
+
38
44
  @hydra.queue(req)
39
45
  @hydra.dequeue_many
40
46
 
41
47
  thread.run unless thread.status
42
48
  end
43
49
 
44
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] [#{request.object_id}] request:signal" }
50
+ debug 'request:signal', tag: request.object_id
51
+
45
52
  @signal.signal
46
53
  end
47
54
  end
55
+ # rubocop:enable all
48
56
 
49
57
  private
50
58
 
59
+ # rubocop:disable AbcSize
60
+ # rubocop:disable MethodLength
51
61
  def convert(request, writer)
52
62
  ::Typhoeus::Request.new(
53
63
  request.uri,
@@ -59,18 +69,22 @@ module Restify
59
69
  connecttimeout: request.timeout
60
70
  ).tap do |req|
61
71
  req.on_complete do |response|
62
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] [#{request.object_id}] request:complete status=#{response.code}" }
72
+ debug 'request:complete',
73
+ tag: request.object_id,
74
+ status: response.code
63
75
 
64
76
  if response.timed_out?
65
77
  writer.reject Restify::Timeout.new request
66
- elsif response.code == 0
67
- writer.reject Restify::NetworkError.new request, response.return_message
78
+ elsif response.code.zero?
79
+ writer.reject \
80
+ Restify::NetworkError.new(request, response.return_message)
68
81
  else
69
82
  writer.fulfill convert_back(response, request)
70
83
  end
71
84
  end
72
85
  end
73
86
  end
87
+ # rubocop:enable all
74
88
 
75
89
  def convert_back(response, request)
76
90
  uri = request.uri
@@ -92,7 +106,8 @@ module Restify
92
106
  def thread
93
107
  if @thread.nil? || !@thread.status
94
108
  # Recreate thread if nil or dead
95
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] hydra:spawn" }
109
+ debug 'hydra:spawn'
110
+
96
111
  @thread = Thread.new { _loop }
97
112
  end
98
113
 
@@ -108,21 +123,27 @@ module Restify
108
123
  @hydra.queued_requests.any? || @hydra.multi.easy_handles.any?
109
124
  end
110
125
 
126
+ # rubocop:disable MethodLength
111
127
  def _run
112
- logger.debug { "[#{self.object_id}/#{Process.pid}] hydra:run" }
128
+ debug 'hydra:run'
113
129
  @hydra.run while _ongoing?
114
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] hydra:completed" }
130
+ debug 'hydra:completed'
115
131
 
116
132
  @mutex.synchronize do
117
133
  return if _ongoing?
118
134
 
119
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] hydra:pause" }
135
+ debug 'hydra:pause'
120
136
  @signal.wait(@mutex, 60)
121
- logger.debug { "[#{self.object_id}/#{Thread.current.object_id}] hydra:resumed" }
137
+ debug 'hydra:resumed'
122
138
  end
123
139
  rescue StandardError => e
124
140
  logger.error(e)
125
141
  end
142
+ # rubocop:enable all
143
+
144
+ def _log_prefix
145
+ "[#{object_id}/#{Thread.current.object_id}]"
146
+ end
126
147
  end
127
148
  end
128
149
  end
@@ -35,7 +35,7 @@ module Restify
35
35
  end
36
36
 
37
37
  def inherit(uri, **kwargs)
38
- uri = self.uri unless uri
38
+ uri ||= self.uri
39
39
  Context.new uri, kwargs.merge(options)
40
40
  end
41
41
 
@@ -66,6 +66,7 @@ module Restify
66
66
  end
67
67
  end
68
68
  end
69
+ # rubocop:enable all
69
70
 
70
71
  def encode_with(coder)
71
72
  coder.map = marshal_dump
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restify
4
- #
5
-
6
4
  # A {NetworkError} is raised on unusual network exceptions such as
7
5
  # unresolvable hosts or disconnects.
8
6
  #
@@ -33,9 +33,7 @@ module Restify
33
33
  ::Logging.logger[Restify]
34
34
  end
35
35
 
36
- def logger=(logger)
37
-
38
- end
36
+ def logger=(logger); end
39
37
 
40
38
  private
41
39
 
@@ -7,5 +7,24 @@ module Restify
7
7
  def logger
8
8
  @logger ||= ::Logging.logger[self]
9
9
  end
10
+
11
+ def debug(message = nil, tag: nil, **kwargs)
12
+ logger.debug do
13
+ [
14
+ _log_prefix,
15
+ *Array(tag),
16
+ message,
17
+ _fmt(**kwargs)
18
+ ].map(&:to_s).reject(&:empty?).join(' ')
19
+ end
20
+ end
21
+
22
+ def _log_prefix
23
+ nil
24
+ end
25
+
26
+ def _fmt(**kwargs)
27
+ kwargs.each.map {|k, v| "#{k}=#{v}" }.join(' ')
28
+ end
10
29
  end
11
30
  end
@@ -1,16 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restify
4
- #
5
4
  module Processors
6
- #
7
5
  class Base
8
6
  extend Forwardable
9
7
 
10
- #
11
8
  attr_reader :context
12
9
 
13
- #
14
10
  attr_reader :response
15
11
 
16
12
  def initialize(context, response)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restify
4
- #
5
4
  class Promise < Concurrent::IVar
6
5
  def initialize(*dependencies, &task)
7
6
  @task = task
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restify
4
- #
5
4
  class Relation
6
5
  # Relation context
7
6
  #
@@ -28,6 +27,10 @@ module Restify
28
27
  request :get, nil, params, opts
29
28
  end
30
29
 
30
+ def head(params = {}, opts = {})
31
+ request :head, nil, params, opts
32
+ end
33
+
31
34
  def delete(params = {}, opts = {})
32
35
  request :delete, nil, params, opts
33
36
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restify
4
- #
5
4
  class Request
6
5
  #
7
6
  # HTTP method.
@@ -3,7 +3,6 @@
3
3
  require 'delegate'
4
4
 
5
5
  module Restify
6
- #
7
6
  class Resource < SimpleDelegator
8
7
  # @api private
9
8
  #
@@ -81,6 +80,7 @@ module Restify
81
80
  raise 'Nothing to follow'
82
81
  end
83
82
  end
83
+ # rubocop:enable all
84
84
 
85
85
  # @api private
86
86
  def _restify_relations
@@ -104,6 +104,7 @@ module Restify
104
104
  end
105
105
  end
106
106
  end
107
+ # rubocop:enable all
107
108
 
108
109
  # Return content type header from response headers.
109
110
  #
@@ -3,7 +3,7 @@
3
3
  module Restify
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 7
6
+ MINOR = 8
7
7
  PATCH = 0
8
8
  STAGE = nil
9
9
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.').freeze
@@ -40,7 +40,7 @@ describe Restify::Context do
40
40
  subject { super().options[:headers] }
41
41
 
42
42
  it 'all headers are serialized' do
43
- expect(subject).to eq({'Accept' => 'application/json'})
43
+ expect(subject).to eq('Accept' => 'application/json')
44
44
  end
45
45
  end
46
46
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify do
6
+ let!(:request_stub) do
7
+ stub_request(:head, 'http://localhost/base')
8
+ .with(query: hash_including({}))
9
+ .to_return do
10
+ <<-RESPONSE.gsub(/^ {8}/, '')
11
+ HTTP/1.1 200 OK
12
+ Content-Length: 333
13
+ Transfer-Encoding: chunked
14
+ Link: <http://localhost/other>; rel="neat"
15
+ RESPONSE
16
+ end
17
+ end
18
+
19
+ describe 'HEAD requests' do
20
+ subject { Restify.new('http://localhost/base').head(params).value! }
21
+ let(:params) { {} }
22
+
23
+ it 'returns a resource with access to headers' do
24
+ expect(subject.response.headers).to include('CONTENT_LENGTH' => '333')
25
+ end
26
+
27
+ it 'parses Link headers into relations' do
28
+ expect(subject).to have_relation :neat
29
+ end
30
+
31
+ context 'with params' do
32
+ let(:params) { {foo: 'bar'} }
33
+
34
+ it 'adds them to the query string' do
35
+ subject
36
+ expect(
37
+ request_stub.with(query: {foo: 'bar'})
38
+ ).to have_been_requested
39
+ end
40
+ end
41
+ end
42
+ end
@@ -5,14 +5,14 @@ require 'spec_helper'
5
5
  describe Restify do
6
6
  let!(:request_stub) do
7
7
  stub_request(:get, 'http://localhost/base').to_return do
8
- <<-EOF.gsub(/^ {8}/, '')
8
+ <<-RESPONSE.gsub(/^ {8}/, '')
9
9
  HTTP/1.1 200 OK
10
10
  Content-Type: application/json
11
11
  Transfer-Encoding: chunked
12
12
  Link: <http://localhost/base>; rel="self"
13
13
 
14
14
  { "response": "success" }
15
- EOF
15
+ RESPONSE
16
16
  end
17
17
  end
18
18
 
@@ -22,7 +22,7 @@ describe Restify do
22
22
  it 'sends the headers only for that request' do
23
23
  root = context.get(
24
24
  {},
25
- headers: {'Accept' => 'application/msgpack, application/json'}
25
+ {headers: {'Accept' => 'application/msgpack, application/json'}}
26
26
  ).value!
27
27
 
28
28
  root.rel(:self).get.value!
@@ -55,7 +55,7 @@ describe Restify do
55
55
  it 'can overwrite headers for single requests' do
56
56
  root = context.get(
57
57
  {},
58
- headers: {'Accept' => 'application/xml'}
58
+ {headers: {'Accept' => 'application/xml'}}
59
59
  ).value!
60
60
 
61
61
  root.rel(:self).get.value!
@@ -71,7 +71,7 @@ describe Restify do
71
71
  it 'can add additional headers for single requests' do
72
72
  root = context.get(
73
73
  {},
74
- headers: {'X-Custom' => 'foobar'}
74
+ {headers: {'X-Custom' => 'foobar'}}
75
75
  ).value!
76
76
 
77
77
  root.rel(:self).get.value!
@@ -23,32 +23,32 @@ describe Restify::Timeout do
23
23
 
24
24
  describe '#wait_on!' do
25
25
  it 'calls block on IVar timeout' do
26
- expect {
26
+ expect do
27
27
  timer.wait_on!(Concurrent::IVar.new)
28
- }.to raise_error ::Restify::Timeout::Error
28
+ end.to raise_error ::Restify::Timeout::Error
29
29
  end
30
30
 
31
31
  it 'calls block on Promise timeout' do
32
- expect {
32
+ expect do
33
33
  timer.wait_on!(Restify::Promise.new)
34
- }.to raise_error ::Restify::Timeout::Error
34
+ end.to raise_error ::Restify::Timeout::Error
35
35
  end
36
36
 
37
37
  it 'does nothing on successful IVar' do
38
- expect {
38
+ expect do
39
39
  Concurrent::IVar.new.tap do |ivar|
40
40
  Thread.new { ivar.set :success }
41
41
  expect(timer.wait_on!(ivar)).to eq :success
42
42
  end
43
- }.to_not raise_error
43
+ end.to_not raise_error
44
44
  end
45
45
 
46
46
  it 'does nothing on successful Promise' do
47
- expect {
47
+ expect do
48
48
  Restify::Promise.fulfilled(:success).tap do |p|
49
49
  expect(timer.wait_on!(p)).to eq :success
50
50
  end
51
- }.to_not raise_error
51
+ end.to_not raise_error
52
52
  end
53
53
  end
54
54
  end
@@ -6,7 +6,7 @@ describe Restify do
6
6
  context 'as a dynamic HATEOAS client' do
7
7
  before do
8
8
  stub_request(:get, 'http://localhost/base').to_return do
9
- <<-EOF.gsub(/^ {10}/, '')
9
+ <<-RESPONSE.gsub(/^ {10}/, '')
10
10
  HTTP/1.1 200 OK
11
11
  Content-Type: application/json
12
12
  Transfer-Encoding: chunked
@@ -18,11 +18,11 @@ describe Restify do
18
18
  "search_url": "http://localhost/base/search?q={query}",
19
19
  "mirror_url": null
20
20
  }
21
- EOF
21
+ RESPONSE
22
22
  end
23
23
 
24
24
  stub_request(:get, 'http://localhost/base/users').to_return do
25
- <<-EOF.gsub(/^ {10}/, '')
25
+ <<-RESPONSE.gsub(/^ {10}/, '')
26
26
  HTTP/1.1 200 OK
27
27
  Content-Type: application/json
28
28
  Transfer-Encoding: chunked
@@ -37,25 +37,25 @@ describe Restify do
37
37
  "name": "Jane Smith",
38
38
  "self_url": "http://localhost/base/user/jane.smith"
39
39
  }]
40
- EOF
40
+ RESPONSE
41
41
  end
42
42
 
43
43
  stub_request(:post, 'http://localhost/base/users')
44
44
  .with(body: {})
45
45
  .to_return do
46
- <<-EOF.gsub(/^ {12}/, '')
46
+ <<-RESPONSE.gsub(/^ {12}/, '')
47
47
  HTTP/1.1 422 Unprocessable Entity
48
48
  Content-Type: application/json
49
49
  Transfer-Encoding: chunked
50
50
 
51
51
  {"errors":{"name":["can't be blank"]}}
52
- EOF
52
+ RESPONSE
53
53
  end
54
54
 
55
55
  stub_request(:post, 'http://localhost/base/users')
56
56
  .with(body: {name: 'John Smith'})
57
57
  .to_return do
58
- <<-EOF.gsub(/^ {12}/, '')
58
+ <<-RESPONSE.gsub(/^ {12}/, '')
59
59
  HTTP/1.1 201 Created
60
60
  Content-Type: application/json
61
61
  Location: http://localhost/base/users/john.smith
@@ -67,12 +67,12 @@ describe Restify do
67
67
  "blurb_url": "http://localhost/base/users/john.smith/blurb",
68
68
  "languages": ["de", "en"]
69
69
  }
70
- EOF
70
+ RESPONSE
71
71
  end
72
72
 
73
73
  stub_request(:get, 'http://localhost/base/users/john.smith')
74
74
  .to_return do
75
- <<-EOF.gsub(/^ {10}/, '')
75
+ <<-RESPONSE.gsub(/^ {10}/, '')
76
76
  HTTP/1.1 200 OK
77
77
  Content-Type: application/json
78
78
  Link: <http://localhost/base/users/john.smith>; rel="self"
@@ -82,12 +82,12 @@ describe Restify do
82
82
  "name": "John Smith",
83
83
  "url": "http://localhost/base/users/john.smith"
84
84
  }
85
- EOF
85
+ RESPONSE
86
86
  end
87
87
 
88
88
  stub_request(:get, 'http://localhost/base/users/john.smith/blurb')
89
89
  .to_return do
90
- <<-EOF.gsub(/^ {10}/, '')
90
+ <<-RESPONSE.gsub(/^ {10}/, '')
91
91
  HTTP/1.1 200 OK
92
92
  Content-Type: application/json
93
93
  Link: <http://localhost/base/users/john.smith>; rel="user"
@@ -97,7 +97,7 @@ describe Restify do
97
97
  "title": "Prof. Dr. John Smith",
98
98
  "image": "http://example.org/avatar.png"
99
99
  }
100
- EOF
100
+ RESPONSE
101
101
  end
102
102
  end
103
103
 
@@ -40,6 +40,10 @@ Dir[File.expand_path('spec/support/**/*.rb')].each {|f| require f }
40
40
  RSpec.configure do |config|
41
41
  config.order = 'random'
42
42
 
43
+ config.before(:suite) do
44
+ ::Restify::Timeout.default_timeout = 2
45
+ end
46
+
43
47
  config.before(:each) do
44
48
  Ethon.logger = ::Logging.logger[Ethon] if defined?(Ethon)
45
49
 
@@ -47,9 +51,7 @@ RSpec.configure do |config|
47
51
  ::Logging.logger.root.add_appenders ::Logging.appenders.stdout
48
52
  end
49
53
 
50
- config.after(:all) do
51
- if defined?(EventMachine) && EventMachine.reactor_running?
52
- EventMachine.stop
53
- end
54
+ config.after(:suite) do
55
+ EventMachine.stop if defined?(EventMachine) && EventMachine.reactor_running?
54
56
  end
55
57
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restify
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Graichen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-15 00:00:00.000000000 Z
11
+ date: 2018-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -186,6 +186,7 @@ files:
186
186
  - lib/restify/version.rb
187
187
  - spec/restify/cache_spec.rb
188
188
  - spec/restify/context_spec.rb
189
+ - spec/restify/features/head_requests_spec.rb
189
190
  - spec/restify/features/request_headers_spec.rb
190
191
  - spec/restify/global_spec.rb
191
192
  - spec/restify/link_spec.rb
@@ -226,6 +227,7 @@ summary: An experimental hypermedia REST client.
226
227
  test_files:
227
228
  - spec/restify/cache_spec.rb
228
229
  - spec/restify/context_spec.rb
230
+ - spec/restify/features/head_requests_spec.rb
229
231
  - spec/restify/features/request_headers_spec.rb
230
232
  - spec/restify/global_spec.rb
231
233
  - spec/restify/link_spec.rb