lhc 6.1.3 → 6.2.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
  SHA1:
3
- metadata.gz: 20a9521feba97cd8bd75c3af4e7b44ed138a3de0
4
- data.tar.gz: d33be7d617ca75e711ee53c725a36cdf4f2efb59
3
+ metadata.gz: 7caf5dc6e24346cc42e287a8bac832cd81011c5a
4
+ data.tar.gz: 17bd6df84e9fce4212921c8348b2053031f62da1
5
5
  SHA512:
6
- metadata.gz: d18302eeeae9e5b4be7cc01b9566cb9e448bf6aa0ee6ca7697e3a2fe4f0dfc68a8e37f21de495ddcd3a84feba6ecedc6bf8f4b5923f81d92d6149c81c8b025a5
7
- data.tar.gz: af340762cdca2439f2b5b3e776c95e6a950043f1f159371b5bc2c084f7c02d74ba654e45316e21468a9ae1a4e63b043d11ca96b565ce46308bc104fb67c8f51e
6
+ metadata.gz: cb849054f246290fe96388ed66abaf3ded5dc8d2a00b7eb7afd857efbfd3d9e820bf0014780c296a37f865c2665cee1b399f1a6d9bc549d48092177cb4885908
7
+ data.tar.gz: 2f3a0b17c338a1c36dca14e1fa2b3cfc3d1897263cc373a9912a4da9717c5a909677cc4c0fd987ef7e010c964ad2529ae4df816db31384560be826bc28d2256e
@@ -31,23 +31,25 @@ You can also enable caching when configuring an endpoint in LHS.
31
31
  Only GET requests are cached by default. If you want to cache any other request method, just configure it:
32
32
 
33
33
  ```ruby
34
- LHC.get('http://local.ch', cache: true, cached_methods: [:post, :head])
34
+ LHC.get('http://local.ch', cache: { methods: [:get] })
35
35
  ```
36
36
 
37
37
  ## Options
38
38
 
39
39
  ```ruby
40
- LHC.get('http://local.ch', cache: true, cache_expires_in: 1.day, cache_race_condition_ttl: 15.seconds)
40
+ LHC.get('http://local.ch', cache: { key: 'key' expires_in: 1.day, race_condition_ttl: 15.seconds, use: ActiveSupport::Cache::MemoryStore.new })
41
41
  ```
42
42
 
43
- `cache_expires_in` - lets the cache expires every X seconds.
43
+ `expires_in` - lets the cache expires every X seconds.
44
44
 
45
- `cache_key` - Set the key that is used for caching by using the option. Every key is prefixed with `LHC_CACHE(v1): `.
45
+ `key` - Set the key that is used for caching by using the option. Every key is prefixed with `LHC_CACHE(v1): `.
46
46
 
47
- `cache_race_condition_ttl` - very useful in situations where a cache entry is used very frequently and is under heavy load.
47
+ `race_condition_ttl` - very useful in situations where a cache entry is used very frequently and is under heavy load.
48
48
  If a cache expires and due to heavy load several different processes will try to read data natively and then they all will try to write to cache.
49
49
  To avoid that case the first process to find an expired cache entry will bump the cache expiration time by the value set in `cache_race_condition_ttl`.
50
50
 
51
+ `use` - Set an explicit cache to be used for this request. If this option is missing `LHC::Caching.cache` is used.
52
+
51
53
  ## Testing
52
54
 
53
55
  Add to your spec_helper.rb:
@@ -30,6 +30,6 @@ Gem::Specification.new do |s|
30
30
  s.add_development_dependency 'pry'
31
31
  s.add_development_dependency 'rubocop', '~> 0.36.0'
32
32
  s.add_development_dependency 'rubocop-rspec'
33
-
33
+
34
34
  s.license = 'GPL-3'
35
35
  end
@@ -6,31 +6,64 @@ class LHC::Caching < LHC::Interceptor
6
6
  CACHE_VERSION = '1'
7
7
 
8
8
  # Options forwarded to the cache
9
- FORWARDED_OPTIONS = {
10
- cache_expires_in: :expires_in,
11
- cache_race_condition_ttl: :race_condition_ttl
12
- }
9
+ FORWARDED_OPTIONS = [:expires_in, :race_condition_ttl]
13
10
 
14
11
  def before_request(request)
15
- return unless cache
16
- return unless request.options[:cache]
17
- return unless cached_method?(request.method, request.options[:cache_methods])
18
- cached_response_data = cache.fetch(key(request))
19
- return unless cached_response_data
20
- logger.info "Served from cache: #{key(request)}" if logger
21
- from_cache(request, cached_response_data)
12
+ return unless cache?(request)
13
+ deprecation_warning(request.options)
14
+ options = options(request.options)
15
+ key = key(request, options[:key])
16
+ response_data = cache_for(options).fetch(key)
17
+ return unless response_data
18
+ logger.info "Served from cache: #{key}" if logger
19
+ from_cache(request, response_data)
22
20
  end
23
21
 
24
22
  def after_response(response)
25
- return unless cache
23
+ return unless response.success?
26
24
  request = response.request
27
- return unless cached_method?(request.method, request.options[:cache_methods])
28
- return if !request.options[:cache] || !response.success?
29
- cache.write(key(request), to_cache(response), options(request.options))
25
+ return unless cache?(request)
26
+ options = options(request.options)
27
+ cache_for(options).write(
28
+ key(request, options[:key]),
29
+ to_cache(response),
30
+ cache_options(options)
31
+ )
30
32
  end
31
33
 
32
34
  private
33
35
 
36
+ # return the cache for the given options
37
+ def cache_for(options)
38
+ options.fetch(:use, cache)
39
+ end
40
+
41
+ # do we even need to bother with this interceptor?
42
+ # based on the options, this method will
43
+ # return false if this interceptor cannot work
44
+ def cache?(request)
45
+ return false unless request.options[:cache]
46
+ options = options(request.options)
47
+ cache_for(options) &&
48
+ cached_method?(request.method, options[:methods])
49
+ end
50
+
51
+ # returns the request_options
52
+ # will map deprecated options to the new format
53
+ def options(request_options)
54
+ options = request_options[:cache] == true ? {} : request_options[:cache].dup
55
+ map_deprecated_options!(request_options, options)
56
+ options
57
+ end
58
+
59
+ # maps `cache_key` -> `key`, `cache_expires_in` -> `expires_in` and so on
60
+ def map_deprecated_options!(request_options, options)
61
+ deprecated_keys(request_options).each do |deprecated_key|
62
+ new_key = deprecated_key.to_s.gsub(/^cache_/, '').to_sym
63
+ options[new_key] = request_options[deprecated_key]
64
+ end
65
+ end
66
+
34
67
  # converts json we read from the cache to an LHC::Response object
35
68
  def from_cache(request, data)
36
69
  raw = Typhoeus::Response.new(data)
@@ -53,8 +86,7 @@ class LHC::Caching < LHC::Interceptor
53
86
  data
54
87
  end
55
88
 
56
- def key(request)
57
- key = request.options[:cache_key]
89
+ def key(request, key)
58
90
  unless key
59
91
  key = "#{request.method.upcase} #{request.url}"
60
92
  key += "?#{request.params.to_query}" unless request.params.blank?
@@ -68,11 +100,26 @@ class LHC::Caching < LHC::Interceptor
68
100
  (cached_methods || [:get]).include?(method)
69
101
  end
70
102
 
71
- def options(input = {})
72
- options = {}
73
- FORWARDED_OPTIONS.each do |k, v|
74
- options[v] = input[k] if input.key?(k)
103
+ # extracts the options that should be forwarded to
104
+ # the cache
105
+ def cache_options(input = {})
106
+ input.each_with_object({}) do |(key, value), result|
107
+ result[key] = value if key.in? FORWARDED_OPTIONS
108
+ result
109
+ end
110
+ end
111
+
112
+ # grabs the deprecated keys from the request options
113
+ def deprecated_keys(request_options)
114
+ request_options.keys.select { |k| k =~ /^cache_.*/ }.sort
115
+ end
116
+
117
+ # emits a deprecation warning if necessary
118
+ def deprecation_warning(request_options)
119
+ unless deprecated_keys(request_options).empty?
120
+ ActiveSupport::Deprecation.warn(
121
+ "Cache options have changed! #{deprecated_keys(request_options).join(', ')} are deprecated and will be removed in future versions."
122
+ )
75
123
  end
76
- options
77
124
  end
78
125
  end
@@ -1,3 +1,3 @@
1
1
  module LHC
2
- VERSION ||= "6.1.3"
2
+ VERSION ||= "6.2.0"
3
3
  end
@@ -11,7 +11,7 @@ describe LHC::Caching do
11
11
 
12
12
  it 'serves a response from cache' do
13
13
  stub
14
- LHC.config.endpoint(:local, 'http://local.ch', cache: true, cache_expires_in: 5.minutes)
14
+ LHC.config.endpoint(:local, 'http://local.ch', cache: { expires_in: 5.minutes })
15
15
  expect(Rails.cache).to receive(:write)
16
16
  .with(
17
17
  "LHC_CACHE(v#{LHC::Caching::CACHE_VERSION}): GET http://local.ch",
@@ -44,7 +44,7 @@ describe LHC::Caching do
44
44
  end
45
45
 
46
46
  it 'lets you configure the cache key that will be used' do
47
- LHC.config.endpoint(:local, 'http://local.ch', cache: true, cache_key: 'STATICKEY')
47
+ LHC.config.endpoint(:local, 'http://local.ch', cache: { key: 'STATICKEY' })
48
48
  expect(Rails.cache).to receive(:fetch).with("LHC_CACHE(v#{LHC::Caching::CACHE_VERSION}): STATICKEY").and_call_original
49
49
  expect(Rails.cache).to receive(:write).with("LHC_CACHE(v#{LHC::Caching::CACHE_VERSION}): STATICKEY", anything, anything).and_call_original
50
50
  stub
@@ -62,7 +62,7 @@ describe LHC::Caching do
62
62
 
63
63
  it 'marks response not from cache as not served from cache and from cache as served from cache' do
64
64
  stub
65
- LHC.config.endpoint(:local, 'http://local.ch', cache: true, cache_expires_in: 5.minutes)
65
+ LHC.config.endpoint(:local, 'http://local.ch', cache: true)
66
66
  original_response = LHC.get(:local)
67
67
  cached_response = LHC.get(:local)
68
68
  expect(original_response.from_cache?).to eq false
@@ -10,7 +10,7 @@ describe LHC::Caching do
10
10
  let!(:stub) { stub_request(:post, 'http://local.ch').to_return(status: 200, body: 'The Website') }
11
11
 
12
12
  before(:each) do
13
- LHC.config.endpoint(:local, 'http://local.ch', cache: true, cache_expires_in: 5.minutes)
13
+ LHC.config.endpoint(:local, 'http://local.ch', cache: { expires_in: 5.minutes })
14
14
  end
15
15
 
16
16
  it 'only caches GET requests by default' do
@@ -32,8 +32,8 @@ describe LHC::Caching do
32
32
  }, { expires_in: 5.minutes }
33
33
  )
34
34
  .and_call_original
35
- original_response = LHC.post(:local, cache_methods: [:post])
36
- cached_response = LHC.post(:local, cache_methods: [:post])
35
+ original_response = LHC.post(:local, cache: { methods: [:post] })
36
+ cached_response = LHC.post(:local, cache: { methods: [:post] })
37
37
  expect(original_response.body).to eq cached_response.body
38
38
  expect(original_response.code).to eq cached_response.code
39
39
  expect(original_response.headers).to eq cached_response.headers
@@ -0,0 +1,88 @@
1
+ require 'rails_helper'
2
+
3
+ # due to the fact that options passed into LHC get dup'ed
4
+ # we need a class where we can setup method expectations
5
+ # with `expect_any_instance`
6
+ class CacheMock
7
+ def fetch(*_)
8
+ end
9
+
10
+ def write(*_)
11
+ end
12
+ end
13
+
14
+ describe LHC::Caching do
15
+ let(:default_cache) { LHC::Caching.cache }
16
+ before(:each) do
17
+ stub_request(:get, 'http://local.ch').to_return(status: 200, body: 'The Website')
18
+ LHC.config.interceptors = [LHC::Caching]
19
+ default_cache.clear
20
+ end
21
+
22
+ it 'maps deprecated cache options' do
23
+ expected_options = { expires_in: 5.minutes, race_condition_ttl: 15.seconds }
24
+ expected_key = "LHC_CACHE(v1): key"
25
+ expect(default_cache).to receive(:write).with(expected_key, anything, expected_options)
26
+ expect(lambda {
27
+ LHC.get('http://local.ch', cache: true, cache_expires_in: 5.minutes, cache_key: 'key', cache_race_condition_ttl: 15.seconds)
28
+ }).to output(
29
+ %r{Cache options have changed! cache_expires_in, cache_key, cache_race_condition_ttl are deprecated and will be removed in future versions.}
30
+ ).to_stderr
31
+ end
32
+
33
+ it 'does cache' do
34
+ expect(default_cache).to receive(:fetch)
35
+ expect(default_cache).to receive(:write)
36
+ LHC.get('http://local.ch', cache: true)
37
+ end
38
+
39
+ it 'does not cache' do
40
+ expect(default_cache).not_to receive(:fetch)
41
+ expect(default_cache).not_to receive(:write)
42
+ LHC.get('http://local.ch')
43
+ end
44
+
45
+ context 'options - directly via LHC.get' do
46
+ it 'uses the default cache' do
47
+ expect(default_cache).to receive(:fetch)
48
+ expect(default_cache).to receive(:write)
49
+ LHC.get('http://local.ch', cache: true)
50
+ end
51
+
52
+ it 'uses the provided cache' do
53
+ expect_any_instance_of(CacheMock).to receive(:fetch)
54
+ expect_any_instance_of(CacheMock).to receive(:write)
55
+ LHC.get('http://local.ch', cache: { use: CacheMock.new })
56
+ end
57
+
58
+ it 'cache options are properly forwarded to the cache' do
59
+ cache_options = { expires_in: 5.minutes, race_condition_ttl: 15.seconds }
60
+ expect(default_cache).to receive(:write).with(anything, anything, cache_options)
61
+ LHC.get('http://local.ch', cache: cache_options)
62
+ end
63
+ end
64
+
65
+ context 'options - via endpoint configuration' do
66
+ it 'uses the default cache' do
67
+ LHC.config.endpoint(:local, 'http://local.ch', cache: true)
68
+ expect(default_cache).to receive(:fetch)
69
+ expect(default_cache).to receive(:write)
70
+ LHC.get(:local)
71
+ end
72
+
73
+ it 'uses the provided cache' do
74
+ options = { cache: { use: CacheMock.new } }
75
+ LHC.config.endpoint(:local, 'http://local.ch', options)
76
+ expect_any_instance_of(CacheMock).to receive(:fetch)
77
+ expect_any_instance_of(CacheMock).to receive(:write)
78
+ LHC.get(:local)
79
+ end
80
+
81
+ it 'cache options are properly forwarded to the cache' do
82
+ cache_options = { expires_in: 5.minutes, race_condition_ttl: 15.seconds }
83
+ LHC.config.endpoint(:local, 'http://local.ch', cache: cache_options)
84
+ expect(default_cache).to receive(:write).with(anything, anything, cache_options)
85
+ LHC.get(:local)
86
+ end
87
+ end
88
+ end
@@ -8,7 +8,7 @@ describe LHC::Caching do
8
8
  end
9
9
 
10
10
  it 'considers parameters when writing/reading from cache' do
11
- LHC.config.endpoint(:local, 'http://local.ch', cache: true, cache_expires_in: 5.minutes)
11
+ LHC.config.endpoint(:local, 'http://local.ch', cache: true)
12
12
  stub_request(:get, 'http://local.ch').to_return(status: 200, body: 'The Website')
13
13
  stub_request(:get, 'http://local.ch?location=zuerich').to_return(status: 200, body: 'The Website for Zuerich')
14
14
  expect(
@@ -3,7 +3,7 @@ require 'rails_helper'
3
3
  describe LHC::Caching do
4
4
  before(:each) do
5
5
  LHC.config.interceptors = [LHC::Caching]
6
- LHC.config.endpoint(:local, 'http://local.ch', cache: true, cache_expires_in: 5.minutes)
6
+ LHC.config.endpoint(:local, 'http://local.ch', cache: true)
7
7
  Rails.cache.clear
8
8
  # leverage the Typhoeus internal mock attribute in order to get Typhoeus evaluate the return_code
9
9
  # lib/typhoeus/response/status.rb:48
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhc
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.3
4
+ version: 6.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhc/contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-10 00:00:00.000000000 Z
11
+ date: 2017-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -264,6 +264,7 @@ files:
264
264
  - spec/interceptors/caching/hydra_spec.rb
265
265
  - spec/interceptors/caching/main_spec.rb
266
266
  - spec/interceptors/caching/methods_spec.rb
267
+ - spec/interceptors/caching/options_spec.rb
267
268
  - spec/interceptors/caching/parameters_spec.rb
268
269
  - spec/interceptors/caching/response_status_spec.rb
269
270
  - spec/interceptors/caching/to_cache_spec.rb
@@ -390,6 +391,7 @@ test_files:
390
391
  - spec/interceptors/caching/hydra_spec.rb
391
392
  - spec/interceptors/caching/main_spec.rb
392
393
  - spec/interceptors/caching/methods_spec.rb
394
+ - spec/interceptors/caching/options_spec.rb
393
395
  - spec/interceptors/caching/parameters_spec.rb
394
396
  - spec/interceptors/caching/response_status_spec.rb
395
397
  - spec/interceptors/caching/to_cache_spec.rb