fbe 0.19.2 → 0.19.3
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 +4 -4
- data/lib/fbe/middleware/sqlite_store.rb +14 -27
- data/lib/fbe/octo.rb +15 -7
- data/lib/fbe.rb +1 -1
- data/test/fbe/middleware/test_sqlite_store.rb +98 -14
- data/test/fbe/test_octo.rb +5 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df9755e6d4d7bf5ae3a716031216786929ddfcdf328a3f026b308e9bbfc93f92
|
4
|
+
data.tar.gz: ed1cfe9f9877a3e3e1b77f30f4b9c8ca525885306d100058d5397019ca1005e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bfc015d2f0c47035fae903627404da6fafcd823c14abdcdc8a37694fea73bbb4edd1f1467497379fff51e62388b741a205b28bcd0ee8a4b5c28aceaf0f6c22c
|
7
|
+
data.tar.gz: dc92c946e0d1bd14deb3d29a73b36f9317d7e06597ac2ec4fc05030ed9afea415830a71f101eb54ddd2793a02a3c6b480d2a2b95482f3deb0d62903230b1432e
|
@@ -21,9 +21,6 @@ class Fbe::Middleware::SqliteStore
|
|
21
21
|
dir = File.dirname(path)
|
22
22
|
raise ArgumentError, "Directory #{dir} does not exist" unless File.directory?(dir)
|
23
23
|
@path = File.absolute_path(path)
|
24
|
-
open
|
25
|
-
prepare
|
26
|
-
at_exit { close }
|
27
24
|
end
|
28
25
|
|
29
26
|
def read(key)
|
@@ -37,9 +34,13 @@ class Fbe::Middleware::SqliteStore
|
|
37
34
|
end
|
38
35
|
|
39
36
|
def write(key, value)
|
37
|
+
return if value.is_a?(Array) && value.any? do |vv|
|
38
|
+
req = JSON.parse(vv[0])
|
39
|
+
req['url'].include?('?') || req['method'] != 'get'
|
40
|
+
end
|
40
41
|
value = JSON.dump(value)
|
41
|
-
perform do |
|
42
|
-
|
42
|
+
perform do |t|
|
43
|
+
t.execute(<<~SQL, [key, value])
|
43
44
|
INSERT INTO cache(key, value) VALUES(?1, ?2)
|
44
45
|
ON CONFLICT(key) DO UPDATE SET value = ?2
|
45
46
|
SQL
|
@@ -47,32 +48,10 @@ class Fbe::Middleware::SqliteStore
|
|
47
48
|
nil
|
48
49
|
end
|
49
50
|
|
50
|
-
def open
|
51
|
-
return if @db
|
52
|
-
@db = SQLite3::Database.new(@path)
|
53
|
-
end
|
54
|
-
|
55
|
-
def prepare
|
56
|
-
perform do |tdb|
|
57
|
-
tdb.execute 'CREATE TABLE IF NOT EXISTS cache(key TEXT UNIQUE NOT NULL, value TEXT);'
|
58
|
-
tdb.execute 'CREATE INDEX IF NOT EXISTS key_idx ON cache(key);'
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def close
|
63
|
-
return if !@db || @db.closed?
|
64
|
-
@db.close
|
65
|
-
@db = nil
|
66
|
-
end
|
67
|
-
|
68
51
|
def clear
|
69
52
|
perform { _1.execute 'DELETE FROM cache;' }
|
70
53
|
end
|
71
54
|
|
72
|
-
def drop
|
73
|
-
perform { _1.execute 'DROP TABLE IF EXISTS cache;' }
|
74
|
-
end
|
75
|
-
|
76
55
|
def all
|
77
56
|
perform { _1.execute('SELECT key, value FROM cache') }
|
78
57
|
end
|
@@ -80,6 +59,14 @@ class Fbe::Middleware::SqliteStore
|
|
80
59
|
private
|
81
60
|
|
82
61
|
def perform(&)
|
62
|
+
@db ||=
|
63
|
+
SQLite3::Database.new(@path).tap do |d|
|
64
|
+
d.transaction do |t|
|
65
|
+
t.execute 'CREATE TABLE IF NOT EXISTS cache(key TEXT UNIQUE NOT NULL, value TEXT);'
|
66
|
+
t.execute 'CREATE INDEX IF NOT EXISTS key_idx ON cache(key);'
|
67
|
+
end
|
68
|
+
at_exit { @db&.close }
|
69
|
+
end
|
83
70
|
@db.transaction(&)
|
84
71
|
end
|
85
72
|
end
|
data/lib/fbe/octo.rb
CHANGED
@@ -76,16 +76,24 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
76
76
|
methods: [:get],
|
77
77
|
backoff_factor: 2
|
78
78
|
)
|
79
|
-
serializer = Marshal
|
80
79
|
if options.sqlite_cache
|
81
80
|
store = Fbe::Middleware::SqliteStore.new(options.sqlite_cache)
|
82
|
-
|
83
|
-
|
81
|
+
loog.info(
|
82
|
+
"Using HTTP cache in SQLite file: #{store.path} (" \
|
83
|
+
"#{File.exist?(store.path) ? "#{File.size(store.path)} bytes" : 'file is absent'}" \
|
84
|
+
')'
|
85
|
+
)
|
86
|
+
builder.use(
|
87
|
+
Faraday::HttpCache,
|
88
|
+
store:, serializer: JSON, shared_cache: false, logger: Loog::NULL
|
89
|
+
)
|
90
|
+
else
|
91
|
+
loog.info("No HTTP cache in SQLite file, because 'sqlite_cache' option is not provided")
|
92
|
+
builder.use(
|
93
|
+
Faraday::HttpCache,
|
94
|
+
serializer: Marshal, shared_cache: false, logger: Loog::NULL
|
95
|
+
)
|
84
96
|
end
|
85
|
-
builder.use(
|
86
|
-
Faraday::HttpCache,
|
87
|
-
store: store, serializer: serializer, shared_cache: false, logger: Loog::NULL
|
88
|
-
)
|
89
97
|
builder.use(Octokit::Response::RaiseError)
|
90
98
|
builder.use(Faraday::Response::Logger, loog, formatter: Fbe::Middleware::Formatter)
|
91
99
|
builder.use(Fbe::Middleware::Trace, trace)
|
data/lib/fbe.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
+
require 'qbash'
|
6
7
|
require_relative '../../test__helper'
|
7
8
|
require_relative '../../../lib/fbe/middleware'
|
8
9
|
require_relative '../../../lib/fbe/middleware/sqlite_store'
|
@@ -12,24 +13,107 @@ require_relative '../../../lib/fbe/middleware/sqlite_store'
|
|
12
13
|
# Copyright:: Copyright (c) 2024-2025 Zerocracy
|
13
14
|
# License:: MIT
|
14
15
|
class SqliteStoreTest < Fbe::Test
|
15
|
-
def
|
16
|
-
|
17
|
-
store = Fbe::Middleware::SqliteStore.new(
|
18
|
-
|
19
|
-
assert_nil(store.
|
20
|
-
assert_nil(store.
|
21
|
-
|
22
|
-
assert_nil(store.write(
|
23
|
-
assert_equal(
|
24
|
-
|
25
|
-
assert_nil(store.
|
16
|
+
def test_simple_caching_algorithm
|
17
|
+
with_tmpfile('x.db') do |f|
|
18
|
+
store = Fbe::Middleware::SqliteStore.new(f)
|
19
|
+
k = 'some-key'
|
20
|
+
assert_nil(store.read(k))
|
21
|
+
assert_nil(store.delete(k))
|
22
|
+
v1 = 'first value to save'
|
23
|
+
assert_nil(store.write(k, v1))
|
24
|
+
assert_equal(v1, store.read(k))
|
25
|
+
v2 = 'another value to save'
|
26
|
+
assert_nil(store.write(k, v2))
|
27
|
+
assert_equal(v2, store.read(k))
|
28
|
+
assert_nil(store.delete(k))
|
29
|
+
assert_nil(store.read(k))
|
30
|
+
assert_path_exists(f)
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
29
|
-
def
|
30
|
-
|
31
|
-
store = Fbe::Middleware::SqliteStore.new(
|
34
|
+
def test_returns_empty_list
|
35
|
+
with_tmpfile('b.db') do |f|
|
36
|
+
store = Fbe::Middleware::SqliteStore.new(f)
|
37
|
+
assert_empty(store.all)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_clear_all_keys
|
42
|
+
with_tmpfile('a.db') do |f|
|
43
|
+
store = Fbe::Middleware::SqliteStore.new(f)
|
44
|
+
k = 'a key'
|
45
|
+
store.write(k, 'some value')
|
46
|
+
store.clear
|
47
|
+
assert_empty(store.all)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_empty_all_if_not_written
|
52
|
+
with_tmpfile do |f|
|
53
|
+
store = Fbe::Middleware::SqliteStore.new(f)
|
32
54
|
assert_empty(store.all)
|
33
55
|
end
|
34
56
|
end
|
57
|
+
|
58
|
+
def test_wrong_db_path
|
59
|
+
assert_raises(ArgumentError) do
|
60
|
+
Fbe::Middleware::SqliteStore.new(nil).read('my_key')
|
61
|
+
end
|
62
|
+
assert_raises(ArgumentError) do
|
63
|
+
Fbe::Middleware::SqliteStore.new('').read('my_key')
|
64
|
+
end
|
65
|
+
assert_raises(ArgumentError) do
|
66
|
+
Fbe::Middleware::SqliteStore.new('/fakepath/fakefolder/test.db').read('my_key')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_not_db_file
|
71
|
+
with_tmpfile do |f|
|
72
|
+
File.binwrite(f, Array.new(20) { rand(0..255) }.pack('C*'))
|
73
|
+
ex =
|
74
|
+
assert_raises(SQLite3::NotADatabaseException) do
|
75
|
+
Fbe::Middleware::SqliteStore.new(f).read('my_key')
|
76
|
+
end
|
77
|
+
assert_match('file is not a database', ex.message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_defer_db_close_callback
|
82
|
+
txt = <<~RUBY
|
83
|
+
require 'tempfile'
|
84
|
+
require 'sqlite3'
|
85
|
+
require 'fbe/middleware/sqlite_store'
|
86
|
+
|
87
|
+
SQLite3::Database.class_eval do
|
88
|
+
prepend(Module.new do
|
89
|
+
def close
|
90
|
+
super
|
91
|
+
puts 'closed sqlite after process exit'
|
92
|
+
end
|
93
|
+
end)
|
94
|
+
end
|
95
|
+
|
96
|
+
Tempfile.open('test.db') do |f|
|
97
|
+
Fbe::Middleware::SqliteStore.new(f.path).then do |s|
|
98
|
+
s.write('my_key', 'my_value')
|
99
|
+
s.read('my_key')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
RUBY
|
103
|
+
out =
|
104
|
+
qbash(
|
105
|
+
'bundle exec ruby ' \
|
106
|
+
"-I#{Shellwords.escape(File.expand_path('../../../lib', __dir__))} " \
|
107
|
+
"-e #{Shellwords.escape(txt)} 2>&1"
|
108
|
+
)
|
109
|
+
assert_match('closed sqlite after process exit', out)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def with_tmpfile(name = 'test.db', &)
|
115
|
+
Dir.mktmpdir do |dir|
|
116
|
+
yield File.expand_path(name, dir)
|
117
|
+
end
|
118
|
+
end
|
35
119
|
end
|
data/test/fbe/test_octo.rb
CHANGED
@@ -256,7 +256,10 @@ class TestOcto < Fbe::Test
|
|
256
256
|
end
|
257
257
|
|
258
258
|
def test_reads_quota
|
259
|
-
WebMock.
|
259
|
+
WebMock.disable_net_connect!
|
260
|
+
stub_request(:get, 'https://api.github.com/rate_limit').to_return(
|
261
|
+
{ body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
|
262
|
+
)
|
260
263
|
o = Fbe.octo(loog: Loog::VERBOSE, global: {}, options: Judges::Options.new({ 'github_api_pause' => 0.01 }))
|
261
264
|
refute_nil(o.off_quota?)
|
262
265
|
end
|
@@ -365,7 +368,7 @@ class TestOcto < Fbe::Test
|
|
365
368
|
assert_includes second_output, 'GitHub API trace is empty'
|
366
369
|
end
|
367
370
|
|
368
|
-
def
|
371
|
+
def test_works_via_sqlite_store
|
369
372
|
WebMock.disable_net_connect!
|
370
373
|
Dir.mktmpdir do |dir|
|
371
374
|
global = {}
|