heroics 0.0.14 → 0.0.15

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
  SHA1:
3
- metadata.gz: aa499bd9600ebaf398364bede676a650e59b9069
4
- data.tar.gz: 1d3cc821c826a4de74c668357bb992e0cb65312f
3
+ metadata.gz: 5fdba196ca9514256d462ec682debf617bf37f16
4
+ data.tar.gz: af76b3f90fd215ee446b0c954b35dbf9071c3b2e
5
5
  SHA512:
6
- metadata.gz: 4e355d82e3f72aa0f753beda66de2571a0428436a7edc3ebe7553657572361bf6e1e6b49956a97c953ef86d163f1ebcc231cf5db2935bfe5212200c09e6a5d68
7
- data.tar.gz: 1fd82c26f754a3a4934a907cfeb03816ef6919a387bdd7df5711ef12031c8dafff03b5ba0faecb9956fd3f1dfb896f04872ed893844faa2df9bb2fdb751ff52a
6
+ metadata.gz: a73cb1eba0b3ae7f3390612af5b99fdbd323a73838f810c569179bd0d6f25a15f85b500d1d934e37f627de84e802fc5b9c1d6674b3b0c3e81f6bf3bd66fd50e6
7
+ data.tar.gz: 313d065db244cb6c99c5ccb02ec781188125f5de45c35c77630966d046c3b6443a6e3217c20637fbbebb7b4dd5a6eb5c0279b0c14e9d4987790dc3db2dc013ee
data/.travis.yml CHANGED
@@ -1,6 +1,8 @@
1
+ sudo: false
1
2
  language: ruby
2
3
  rvm:
3
4
  - 1.9.3
4
- - 2.0.0-p598
5
- - 2.1.5
6
- - 2.2.0
5
+ - 2.0.0-p648
6
+ - 2.1.8
7
+ - 2.2.4
8
+ - 2.3.0
data/heroics.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'rake'
26
26
  spec.add_development_dependency 'turn'
27
27
 
28
- spec.add_dependency 'erubis', '~> 2.7.0'
28
+ spec.add_dependency 'erubis', '~> 2.0'
29
29
  spec.add_dependency 'excon'
30
30
  spec.add_dependency 'moneta'
31
31
  spec.add_dependency 'multi_json', '>= 1.9.2'
data/lib/heroics.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'base64'
2
3
  require 'erubis'
3
4
  require 'excon'
data/lib/heroics/cli.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  class CLI
3
4
  # Instantiate a CLI for an API described by a JSON schema.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # An HTTP client with methods mapped to API resources.
3
4
  class Client
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # Generate a static client that uses Heroics under the hood. This is a good
3
4
  # option if you want to ship a gem or generate API documentation using Yard.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  class Command
3
4
  # Instantiate a command.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # Raised when a schema has an error that prevents it from being parsed
3
4
  # correctly.
data/lib/heroics/link.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # A link invokes requests with an HTTP server.
3
4
  class Link
@@ -50,25 +51,14 @@ module Heroics
50
51
  headers = headers.merge({'Content-Type' => @link_schema.content_type})
51
52
  body = @link_schema.encode(body)
52
53
  end
53
- cache_key = "#{path}:#{headers.hash}"
54
- if @link_schema.method == :get
55
- etag = @cache["etag:#{cache_key}"]
56
- headers = headers.merge({'If-None-Match' => etag}) if etag
57
- end
58
54
 
59
- connection = Excon.new(@root_url)
60
- response = connection.request(method: @link_schema.method, path: path,
55
+ connection = Excon.new(@root_url, thread_safe_sockets: true)
56
+ response = request_with_cache(connection,
57
+ method: @link_schema.method, path: path,
61
58
  headers: headers, body: body,
62
- expects: [200, 201, 202, 204, 206, 304])
59
+ expects: [200, 201, 202, 204, 206])
63
60
  content_type = response.headers['Content-Type']
64
- if response.status == 304
65
- MultiJson.load(@cache["data:#{cache_key}"])
66
- elsif content_type && content_type =~ /application\/.*json/
67
- etag = response.headers['ETag']
68
- if etag
69
- @cache["etag:#{cache_key}"] = etag
70
- @cache["data:#{cache_key}"] = response.body
71
- end
61
+ if content_type && content_type =~ /application\/.*json/
72
62
  body = MultiJson.load(response.body)
73
63
  if response.status == 206
74
64
  next_range = response.headers['Next-Range']
@@ -83,7 +73,8 @@ module Heroics
83
73
  # next range.
84
74
  break unless next_range
85
75
  headers = headers.merge({'Range' => next_range})
86
- response = connection.request(method: @link_schema.method,
76
+ response = request_with_cache(connection,
77
+ method: @link_schema.method,
87
78
  path: path, headers: headers,
88
79
  expects: [200, 201, 206])
89
80
  body = MultiJson.load(response.body)
@@ -100,6 +91,31 @@ module Heroics
100
91
 
101
92
  private
102
93
 
94
+ def request_with_cache(connection, options)
95
+ options[:expects] << 304
96
+ cache_key = "#{options[:path]}:#{options[:headers].hash}"
97
+ if options[:method] == :get
98
+ etag = @cache["etag:#{cache_key}"]
99
+ options[:headers] = options[:headers].merge({'If-None-Match' => etag}) if etag
100
+ end
101
+ response = connection.request(options)
102
+
103
+ if response.status == 304
104
+ body = @cache["data:#{cache_key}"]
105
+ status = @cache["status:#{cache_key}"]
106
+ headers = @cache["headers:#{cache_key}"]
107
+ CachedResponse.new(status, body, headers)
108
+ else
109
+ if etag = response.headers['ETag']
110
+ @cache["etag:#{cache_key}"] = etag
111
+ @cache["data:#{cache_key}"] = response.body
112
+ @cache["status:#{cache_key}"] = response.status
113
+ @cache["headers:#{cache_key}"] = response.headers
114
+ end
115
+ response
116
+ end
117
+ end
118
+
103
119
  # Unpack the URL and split it into a root URL and a path prefix, if one
104
120
  # exists.
105
121
  #
@@ -116,5 +132,15 @@ module Heroics
116
132
  path_prefix = parts[5]
117
133
  return root_url.join(''), path_prefix
118
134
  end
135
+
136
+ class CachedResponse
137
+ attr_reader :status, :body, :headers
138
+ def initialize(status, body, headers)
139
+ @status = status
140
+ @body = body
141
+ @headers = headers
142
+ end
143
+ end
144
+
119
145
  end
120
146
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # Process a name to make it suitable for use as a Ruby method name.
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # A resource with methods mapped to API links.
3
4
  class Resource
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
3
  # A wrapper around a bare JSON schema to make it easier to use.
3
4
  class Schema
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Heroics
2
- VERSION = '0.0.14'
3
+ VERSION = '0.0.15'
3
4
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  #
4
5
  # WARNING: Do not edit by hand, this file was generated by Heroics:
@@ -66,7 +67,7 @@ module <%= @module_name %>
66
67
  final_options[:default_headers].merge!(options[:default_headers])
67
68
  end
68
69
  final_options[:cache] = options[:cache] if options[:cache]
69
- final_options[:url] = options[:url] if options[:url]
70
+ final_options[:url] = options[:url] || <%= @cache %>
70
71
  final_options[:user] = options[:user] if options[:user]
71
72
  final_options
72
73
  end
@@ -74,10 +75,8 @@ module <%= @module_name %>
74
75
  # Get the default options.
75
76
  def self.default_options
76
77
  default_headers = <%= @default_headers %>
77
- cache = <%= @cache %>
78
78
  {
79
79
  default_headers: default_headers,
80
- cache: cache,
81
80
  url: "<%= @url %>"
82
81
  }
83
82
  end
data/test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'netrc'
2
3
 
3
4
  _, token = Netrc.read['api.heroku.com']
data/test/cli_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
  require 'stringio'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
  require 'netrc'
3
4
  require 'stringio'
data/test/client_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
 
3
4
  class ClientTest < MiniTest::Unit::TestCase
data/test/command_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
  require 'stringio'
3
4
 
data/test/helper.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'minitest/autorun'
2
3
  require 'time'
3
4
 
data/test/link_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
 
3
4
  class LinkTest < MiniTest::Unit::TestCase
@@ -339,6 +340,8 @@ class LinkTest < MiniTest::Unit::TestCase
339
340
  cache = Moneta.new(:Memory)
340
341
  cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
341
342
  cache["data:/resource:#{headers.hash}"] = MultiJson.dump(body)
343
+ cache["status:/resource:#{headers.hash}"] = 200
344
+ cache["headers:/resource:#{headers.hash}"] = {'Content-Type' => 'application/json'}
342
345
  schema = Heroics::Schema.new(SAMPLE_SCHEMA)
343
346
  link = Heroics::Link.new('https://example.com',
344
347
  schema.resource('resource').link('list'),
@@ -395,4 +398,47 @@ class LinkTest < MiniTest::Unit::TestCase
395
398
  schema.resource('resource').link('list'))
396
399
  assert_equal([1, 2], link.run.to_a)
397
400
  end
401
+
402
+ # Ensure that caching does not prevent pagination from working correctly.
403
+ # See https://github.com/heroku/platform-api/issues/16
404
+ def test_run_with_range_response_and_cache
405
+ Excon.stub(method: :get) do |request|
406
+ Excon.stubs.shift
407
+ {status: 206, headers: {'Content-Type' => 'application/json',
408
+ 'Content-Range' => 'id 1..2; max=200',
409
+ 'ETag' => 'second-page'},
410
+ body: MultiJson.dump([2])}
411
+ end
412
+
413
+ Excon.stub(method: :get) do |request|
414
+ Excon.stubs.shift
415
+ {status: 206, headers: {'Content-Type' => 'application/json',
416
+ 'Content-Range' => 'id 0..1; max=200',
417
+ 'Next-Range' => '201',
418
+ 'ETag' => 'first-page'},
419
+ body: MultiJson.dump([1])}
420
+ end
421
+
422
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
423
+ link = Heroics::Link.new('https://example.com',
424
+ schema.resource('resource').link('list'),
425
+ cache: Moneta.new(:Memory))
426
+ assert_equal([1, 2], link.run.to_a)
427
+
428
+ Excon.stub(method: :get) do |request|
429
+ assert_equal('second-page', request[:headers]['If-None-Match'])
430
+ assert_equal('201', request[:headers]['Range'])
431
+ Excon.stubs.shift
432
+ {status: 304, headers: {'Content-Type' => 'application/json'}}
433
+ end
434
+
435
+ Excon.stub(method: :get) do |request|
436
+ assert_equal('first-page', request[:headers]['If-None-Match'])
437
+ assert_equal(nil, request[:headers]['Range'])
438
+ Excon.stubs.shift
439
+ {status: 304, headers: {'Content-Type' => 'application/json'}}
440
+ end
441
+
442
+ assert_equal([1, 2], link.run.to_a)
443
+ end
398
444
  end
data/test/naming_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
 
3
4
  class RubyNameTest < MiniTest::Unit::TestCase
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
 
3
4
  class ResourceTest < MiniTest::Unit::TestCase
data/test/schema_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
 
3
4
  class SchemaTest < MiniTest::Unit::TestCase
data/test/version_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'helper'
2
3
 
3
4
  class VersionTest < MiniTest::Unit::TestCase
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - geemus
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-04-16 00:00:00.000000000 Z
12
+ date: 2016-03-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -73,14 +73,14 @@ dependencies:
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: 2.7.0
76
+ version: '2.0'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: 2.7.0
83
+ version: '2.0'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: excon
86
86
  requirement: !ruby/object:Gem::Requirement
@@ -202,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
202
  version: '0'
203
203
  requirements: []
204
204
  rubyforge_project:
205
- rubygems_version: 2.2.2
205
+ rubygems_version: 2.5.1
206
206
  signing_key:
207
207
  specification_version: 4
208
208
  summary: A Ruby client generator for HTTP APIs described with a JSON schema