fbe 0.24.4 → 0.25.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: 92baa5e54a0df9b2e57f2d6619cfad4d4ee1b4a4150b1e30b82780062dd9bd34
4
- data.tar.gz: ac08a108536716ff130271018211616e02b58a55dcef303bed0c0d01808387eb
3
+ metadata.gz: c7615e3d4257252fcc987acca6caeb717c328a79edfd563eb67e704be741cef2
4
+ data.tar.gz: 3a256df746d860b44a2d6f3d44cad34690dce87ca9743227b1e114edfcdff5bd
5
5
  SHA512:
6
- metadata.gz: 4b87c1fefa9477f19588b01eeb7a7d593ca38f1eed068fe7d27a84a66b4cc35214f83b8f6128a169a007df962063f4ae5373442a17c61d12f1c7e876c1099ef6
7
- data.tar.gz: ae01c566bca05de64432b34b6f27bbf82497d5d72817c5bbb0ef9f80e0f62f742681f1ae820d30bc081d891b31f926369ee1f0efe42e4b778a0223b4be26959e
6
+ metadata.gz: 3bf3c8e87ea0f0bc52a51d7f89c60bc1dc5e67b78eccac3ce9de0ac80f73406b4b8fec508d7c83c10b42a07fbe594505d3a5cddc80fd8a35e00c292a80eaf911
7
+ data.tar.gz: 2b4b12a737a6f810d304727f649faa7dceaf224c317c7d79d01705af5442a9510ede60d9f63f9afc433b6b3ce3a4bd49a7c30934d2ee9b6c90d26aebbb1a9d84
data/Gemfile CHANGED
@@ -17,7 +17,7 @@ gem 'rubocop-minitest', '~>0.38', require: false
17
17
  gem 'rubocop-performance', '~>1.25', require: false
18
18
  gem 'rubocop-rake', '~>0.7', require: false
19
19
  gem 'simplecov', '~>0.22', require: false
20
- gem 'simplecov-cobertura', '~>2.1', require: false
20
+ gem 'simplecov-cobertura', '~>3.0', require: false
21
21
  gem 'veils', '~>0.4', require: false
22
22
  gem 'webmock', '~>3.25', require: false
23
23
  gem 'yard', '~>0.9', require: false
data/Gemfile.lock CHANGED
@@ -223,7 +223,7 @@ GEM
223
223
  docile (~> 1.1)
224
224
  simplecov-html (~> 0.11)
225
225
  simplecov_json_formatter (~> 0.1)
226
- simplecov-cobertura (2.1.0)
226
+ simplecov-cobertura (3.0.0)
227
227
  rexml
228
228
  simplecov (~> 0.19)
229
229
  simplecov-html (0.13.1)
@@ -277,7 +277,7 @@ DEPENDENCIES
277
277
  rubocop-performance (~> 1.25)
278
278
  rubocop-rake (~> 0.7)
279
279
  simplecov (~> 0.22)
280
- simplecov-cobertura (~> 2.1)
280
+ simplecov-cobertura (~> 3.0)
281
281
  veils (~> 0.4)
282
282
  webmock (~> 3.25)
283
283
  yard (~> 0.9)
@@ -54,9 +54,10 @@ class Fbe::Middleware::SqliteStore
54
54
  # @param maxsize [Integer] Maximum database size in bytes (optional, defaults to 10MB)
55
55
  # @param maxvsize [Integer] Maximum size in bytes of a single value (optional, defaults to 10Kb)
56
56
  # @param ttl [Integer, nil] lifetime of keys in hours
57
+ # @param cache_min_age [Integer, nil] age which will could be overwritten in cache-control header
57
58
  # @raise [ArgumentError] If path is nil/empty, directory doesn't exist, version is nil/empty,
58
59
  # or ttl is not nil or not Integer or not positive
59
- def initialize(path, version, loog: Loog::NULL, maxsize: '10Mb', maxvsize: '10Kb', ttl: nil)
60
+ def initialize(path, version, loog: Loog::NULL, maxsize: '10Mb', maxvsize: '10Kb', ttl: nil, cache_min_age: nil)
60
61
  raise ArgumentError, 'Database path cannot be nil or empty' if path.nil? || path.empty?
61
62
  dir = File.dirname(path)
62
63
  raise ArgumentError, "Directory #{dir} does not exist" unless File.directory?(dir)
@@ -68,6 +69,10 @@ class Fbe::Middleware::SqliteStore
68
69
  @maxvsize = Filesize.from(maxvsize.to_s).to_i
69
70
  raise ArgumentError, 'TTL can be nil or Integer > 0' if !ttl.nil? && !(ttl.is_a?(Integer) && ttl.positive?)
70
71
  @ttl = ttl
72
+ if !cache_min_age.nil? && !(cache_min_age.is_a?(Integer) && cache_min_age.positive?)
73
+ raise ArgumentError, 'Cache min age can be nil or Integer > 0'
74
+ end
75
+ @cache_min_age = cache_min_age
71
76
  end
72
77
 
73
78
  # Read a value from the cache.
@@ -106,6 +111,26 @@ class Fbe::Middleware::SqliteStore
106
111
  req = JSON.parse(vv[0])
107
112
  req['method'] != 'get'
108
113
  end
114
+ if @cache_min_age && value.is_a?(Array) && value[0].is_a?(Array) && value[0].size > 1
115
+ begin
116
+ resp = JSON.parse(value[0][1])
117
+ rescue TypeError, JSON::ParserError => e
118
+ @loog.info("Failed to parse response to rewrite the cache age: #{e.message}")
119
+ resp = nil
120
+ end
121
+ cache_control = resp.dig('response_headers', 'cache-control') if resp.is_a?(Hash)
122
+ if cache_control && !cache_control.empty?
123
+ %w[max-age s-maxage].each do |key|
124
+ age = cache_control.scan(/#{key}=(\d+)/i).first&.first&.to_i
125
+ if age
126
+ age = [age, @cache_min_age].max
127
+ cache_control = cache_control.sub(/#{key}=(\d+)/, "#{key}=#{age}")
128
+ end
129
+ end
130
+ resp['response_headers']['cache-control'] = cache_control
131
+ value[0][1] = JSON.dump(resp)
132
+ end
133
+ end
109
134
  value = Zlib::Deflate.deflate(JSON.dump(value))
110
135
  return if value.bytesize > @maxvsize
111
136
  perform do |t|
data/lib/fbe/octo.rb CHANGED
@@ -93,8 +93,9 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
93
93
  if options.sqlite_cache
94
94
  maxsize = Filesize.from(options.sqlite_cache_maxsize || '100M').to_i
95
95
  maxvsize = Filesize.from(options.sqlite_cache_maxvsize || '100K').to_i
96
+ cache_min_age = options.sqlite_cache_min_age&.to_i
96
97
  store = Fbe::Middleware::SqliteStore.new(
97
- options.sqlite_cache, Fbe::VERSION, loog:, maxsize:, maxvsize:, ttl: 24
98
+ options.sqlite_cache, Fbe::VERSION, loog:, maxsize:, maxvsize:, ttl: 24, cache_min_age:
98
99
  )
99
100
  loog.info(
100
101
  "Using HTTP cache in SQLite file: #{store.path} (" \
data/lib/fbe.rb CHANGED
@@ -10,5 +10,5 @@
10
10
  # License:: MIT
11
11
  module Fbe
12
12
  # Current version of the gem (changed by +.rultor.yml+ on every release)
13
- VERSION = '0.24.4' unless const_defined?(:VERSION)
13
+ VERSION = '0.25.0' unless const_defined?(:VERSION)
14
14
  end
@@ -330,6 +330,94 @@ class SqliteStoreTest < Fbe::Test
330
330
  end
331
331
  end
332
332
 
333
+ def test_set_correct_cache_min_age
334
+ with_tmpfile('c.db') do |f|
335
+ s = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: nil)
336
+ refute_nil(s)
337
+ s = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 600)
338
+ refute_nil(s)
339
+ end
340
+ end
341
+
342
+ def test_set_incorrect_cache_min_age
343
+ with_tmpfile('c.db') do |f|
344
+ msg = 'Cache min age can be nil or Integer > 0'
345
+ [0, -50, 120.0, '120', Object.new].each do |cache_min_age|
346
+ ex =
347
+ assert_raises(ArgumentError) do
348
+ Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age:)
349
+ end
350
+ assert_equal(msg, ex.message)
351
+ end
352
+ end
353
+ end
354
+
355
+ def test_not_overwrite_cache_control
356
+ with_tmpfile('t.db') do |f|
357
+ Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 30).then do |store|
358
+ store.write(
359
+ 'test1',
360
+ faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=60, s-maxage=60' } })
361
+ )
362
+ store.write(
363
+ 'test2',
364
+ faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=30, s-maxage=30' } })
365
+ )
366
+ store.write(
367
+ 'test3',
368
+ faraday_value(resp: { 'response_headers' => { 'content-type' => 'application/json; charset=utf-8' } })
369
+ )
370
+ store.write('test4', faraday_value(resp: { 'status' => 200, 'body' => '{"some":"value"}' }))
371
+ store.write('test5', faraday_value(resp: {}))
372
+ store.write('test6', [[JSON.dump({ 'method' => 'get' }), 1]])
373
+ store.write('test7', faraday_value(resp: 'some string'))
374
+ store.write('test8', faraday_value(resp: nil))
375
+ end
376
+ Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog).then do |store|
377
+ assert_equal(
378
+ 'public, max-age=60, s-maxage=60',
379
+ JSON.parse(store.read('test1')[0][1]).dig('response_headers', 'cache-control')
380
+ )
381
+ assert_equal(
382
+ 'public, max-age=30, s-maxage=30',
383
+ JSON.parse(store.read('test2')[0][1]).dig('response_headers', 'cache-control')
384
+ )
385
+ assert_nil(JSON.parse(store.read('test3')[0][1]).dig('response_headers', 'cache-control'))
386
+ assert_nil(JSON.parse(store.read('test4')[0][1]).dig('response_headers', 'cache-control'))
387
+ assert_nil(JSON.parse(store.read('test5')[0][1]).dig('response_headers', 'cache-control'))
388
+ assert_equal(1, store.read('test6')[0][1])
389
+ assert_equal(JSON.dump('some string'), store.read('test7')[0][1])
390
+ assert_nil(store.read('test8')[0][1])
391
+ end
392
+ end
393
+ end
394
+
395
+ def test_overwrite_cache_control
396
+ with_tmpfile('t.db') do |f|
397
+ Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 300).then do |store|
398
+ store.write(
399
+ 'test1',
400
+ faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=60, s-maxage=60' } })
401
+ )
402
+ end
403
+ Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 1555).then do |store|
404
+ store.write(
405
+ 'test2',
406
+ faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=60, s-maxage=60' } })
407
+ )
408
+ end
409
+ store = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog)
410
+ assert_equal(
411
+ 'public, max-age=300, s-maxage=300',
412
+ JSON.parse(store.read('test1')[0][1]).dig('response_headers', 'cache-control')
413
+ )
414
+ assert_equal(
415
+ 'public, max-age=1555, s-maxage=1555',
416
+ JSON.parse(store.read('test2')[0][1]).dig('response_headers', 'cache-control')
417
+ )
418
+ end
419
+ end
420
+
333
421
  private
334
422
 
335
423
  def with_tmpfile(name = 'test.db', &)
@@ -337,4 +425,22 @@ class SqliteStoreTest < Fbe::Test
337
425
  yield File.expand_path(name, dir)
338
426
  end
339
427
  end
428
+
429
+ def faraday_value(
430
+ req: {
431
+ 'method' => 'get',
432
+ 'url' => 'https://example.com/test',
433
+ 'headers' => { 'Content-Type' => 'application/json' }
434
+ },
435
+ resp: {
436
+ 'status' => 200,
437
+ 'body' => '{"some":"value"}',
438
+ 'response_headers' => { 'content-type' => 'application/json; charset=utf-8' }
439
+ }
440
+ )
441
+ value = []
442
+ value << JSON.dump(req) if req
443
+ value << JSON.dump(resp) if resp
444
+ [value]
445
+ end
340
446
  end
@@ -709,4 +709,53 @@ class TestOcto < Fbe::Test
709
709
  end
710
710
  end
711
711
  end
712
+
713
+ def test_octo_with_set_sqlite_cache_min_age
714
+ WebMock.disable_net_connect!
715
+ now = Time.now
716
+ stub_request(:get, 'https://api.github.com/rate_limit')
717
+ .to_return(
718
+ status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
719
+ body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
720
+ )
721
+ Dir.mktmpdir do |dir|
722
+ sqlite_cache = File.expand_path('t.db', dir)
723
+ options = Judges::Options.new({ 'sqlite_cache' => sqlite_cache, 'sqlite_cache_min_age' => 120 })
724
+ o = Fbe.octo(loog: fake_loog, global: {}, options:)
725
+ stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
726
+ status: 200,
727
+ body: { id: 798_641_472, name: 'factbase' }.to_json,
728
+ headers: {
729
+ 'Date' => now.httpdate,
730
+ 'Content-Type' => 'application/json; charset=utf-8',
731
+ 'Cache-Control' => 'public, max-age=60, s-maxage=60',
732
+ 'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
733
+ 'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
734
+ }
735
+ ).times(1).then.to_raise('no more request to /repositories/798641472')
736
+ Time.stub(:now, now) do
737
+ assert_equal('factbase', o.repo(798_641_472)['name'])
738
+ end
739
+ Time.stub(:now, now + 50) do
740
+ assert_equal('factbase', o.repo(798_641_472)['name'])
741
+ end
742
+ Time.stub(:now, now + 100) do
743
+ assert_equal('factbase', o.repo(798_641_472)['name'])
744
+ end
745
+ stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
746
+ status: 200,
747
+ body: { id: 798_641_472, name: 'factbase_changed' }.to_json,
748
+ headers: {
749
+ 'Date' => (now + 120).httpdate,
750
+ 'Content-Type' => 'application/json; charset=utf-8',
751
+ 'Cache-Control' => 'public, max-age=60, s-maxage=60',
752
+ 'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
753
+ 'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
754
+ }
755
+ )
756
+ Time.stub(:now, now + 120) do
757
+ assert_equal('factbase_changed', o.repo(798_641_472)['name'])
758
+ end
759
+ end
760
+ end
712
761
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fbe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.4
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko