heroics 0.0.14 → 0.0.15

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