restify 1.7.0 → 1.8.0

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: 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