rails-brotli-cache 0.3.13 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rails-brotli-cache/store.rb +27 -30
- data/lib/rails-brotli-cache/version.rb +1 -1
- data/spec/rails-brotli-cache/redis_spec.rb +0 -16
- data/spec/rails-brotli-cache/store_spec.rb +93 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f95297d5e14bdcdef5195541bdcde94c2c0cbbf91cf4cb546f77edce9e67c5b
|
4
|
+
data.tar.gz: 7c0d650a47162ecc053fa3dca2c7824f3b9de173289321212c57d600afa87897
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5fcbc3d4605329e227ca69fe94d320e16ea3f825babb6fa547e384df51b15879553800960615e1db9f24b3123747a07a9411837b6a5d9ff03591564f09a7840
|
7
|
+
data.tar.gz: 8e9200b30e53bdabee4f4f803384c84247aea22fb4e42d838e7eedd09fb8843b06b336f0ed4de401687a61a9dcd5ec37f51ade8daf3af8c322be1aace241e53d
|
@@ -9,6 +9,22 @@ module RailsBrotliCache
|
|
9
9
|
BR_COMPRESS_QUALITY = ENV.fetch("BR_CACHE_COMPRESS_QUALITY", 5).to_i
|
10
10
|
MARK_BR_COMPRESSED = "\x02".b
|
11
11
|
|
12
|
+
class BrotliCompressor
|
13
|
+
def self.deflate(payload)
|
14
|
+
::Brotli.deflate(payload, quality: BR_COMPRESS_QUALITY)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.inflate(payload)
|
18
|
+
::Brotli.inflate(payload)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
DEFAULT_OPTIONS = {
|
23
|
+
compress_threshold: COMPRESS_THRESHOLD,
|
24
|
+
compress: true,
|
25
|
+
compressor_class: BrotliCompressor
|
26
|
+
}
|
27
|
+
|
12
28
|
attr_reader :core_store
|
13
29
|
|
14
30
|
def initialize(core_store, options = {})
|
@@ -19,11 +35,11 @@ module RailsBrotliCache
|
|
19
35
|
"br-"
|
20
36
|
end
|
21
37
|
|
22
|
-
@
|
38
|
+
@init_options = options.reverse_merge(DEFAULT_OPTIONS)
|
23
39
|
end
|
24
40
|
|
25
41
|
def fetch(name, options = nil, &block)
|
26
|
-
options
|
42
|
+
options = (options || {}).reverse_merge(@init_options)
|
27
43
|
|
28
44
|
if !block_given? && options[:force]
|
29
45
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
@@ -42,7 +58,7 @@ module RailsBrotliCache
|
|
42
58
|
end
|
43
59
|
|
44
60
|
def write(name, value, options = nil)
|
45
|
-
options = (options || {}).reverse_merge(
|
61
|
+
options = (options || {}).reverse_merge(@init_options)
|
46
62
|
payload = compressed(value, options)
|
47
63
|
|
48
64
|
@core_store.write(
|
@@ -53,6 +69,8 @@ module RailsBrotliCache
|
|
53
69
|
end
|
54
70
|
|
55
71
|
def read(name, options = nil)
|
72
|
+
options = (options || {}).reverse_merge(@init_options)
|
73
|
+
|
56
74
|
payload = @core_store.read(
|
57
75
|
cache_key(name),
|
58
76
|
options
|
@@ -62,7 +80,7 @@ module RailsBrotliCache
|
|
62
80
|
end
|
63
81
|
|
64
82
|
def write_multi(hash, options = nil)
|
65
|
-
options
|
83
|
+
options = (options || {}).reverse_merge(@init_options)
|
66
84
|
new_hash = hash.map do |key, val|
|
67
85
|
[
|
68
86
|
cache_key(key),
|
@@ -79,6 +97,7 @@ module RailsBrotliCache
|
|
79
97
|
def read_multi(*names)
|
80
98
|
options = names.extract_options!
|
81
99
|
names = names.map { |name| cache_key(name) }
|
100
|
+
options = options.reverse_merge(@init_options)
|
82
101
|
|
83
102
|
Hash[core_store.read_multi(*names, options).map do |key, val|
|
84
103
|
[source_cache_key(key), uncompressed(val, options)]
|
@@ -88,6 +107,7 @@ module RailsBrotliCache
|
|
88
107
|
def fetch_multi(*names)
|
89
108
|
options = names.extract_options!
|
90
109
|
names = names.map { |name| cache_key(name) }
|
110
|
+
options = options.reverse_merge(@init_options)
|
91
111
|
|
92
112
|
@core_store.fetch_multi(
|
93
113
|
*names, options.merge(compress: false)
|
@@ -125,13 +145,11 @@ module RailsBrotliCache
|
|
125
145
|
private
|
126
146
|
|
127
147
|
def compressed(value, options)
|
128
|
-
options ||= {}
|
129
|
-
|
130
148
|
return value if value.is_a?(Integer)
|
131
149
|
serialized = Marshal.dump(value)
|
132
150
|
|
133
|
-
if serialized.bytesize >=
|
134
|
-
compressor =
|
151
|
+
if serialized.bytesize >= options.fetch(:compress_threshold) && !options.fetch(:compress) == false
|
152
|
+
compressor = options.fetch(:compressor_class)
|
135
153
|
compressed_payload = compressor.deflate(serialized)
|
136
154
|
if compressed_payload.bytesize < serialized.bytesize
|
137
155
|
MARK_BR_COMPRESSED + compressed_payload
|
@@ -144,14 +162,12 @@ module RailsBrotliCache
|
|
144
162
|
end
|
145
163
|
|
146
164
|
def uncompressed(payload, options)
|
147
|
-
options ||= {}
|
148
|
-
|
149
165
|
return nil unless payload.present?
|
150
166
|
|
151
167
|
return payload if payload.is_a?(Integer)
|
152
168
|
|
153
169
|
serialized = if payload.start_with?(MARK_BR_COMPRESSED)
|
154
|
-
compressor =
|
170
|
+
compressor = options.fetch(:compressor_class)
|
155
171
|
compressor.inflate(payload.byteslice(1..-1))
|
156
172
|
else
|
157
173
|
payload
|
@@ -160,15 +176,6 @@ module RailsBrotliCache
|
|
160
176
|
Marshal.load(serialized)
|
161
177
|
end
|
162
178
|
|
163
|
-
def compressor_class(options, default:)
|
164
|
-
options = options || {}
|
165
|
-
if (klass = options[:compressor_class])
|
166
|
-
klass
|
167
|
-
else
|
168
|
-
default
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
179
|
def cache_key(name)
|
173
180
|
"#{@prefix}#{name}"
|
174
181
|
end
|
@@ -176,15 +183,5 @@ module RailsBrotliCache
|
|
176
183
|
def source_cache_key(name)
|
177
184
|
name.delete_prefix(@prefix.to_s)
|
178
185
|
end
|
179
|
-
|
180
|
-
class BrotliCompressor
|
181
|
-
def self.deflate(payload)
|
182
|
-
::Brotli.deflate(payload, quality: BR_COMPRESS_QUALITY)
|
183
|
-
end
|
184
|
-
|
185
|
-
def self.inflate(payload)
|
186
|
-
::Brotli.inflate(payload)
|
187
|
-
end
|
188
|
-
end
|
189
186
|
end
|
190
187
|
end
|
@@ -4,16 +4,6 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
return unless ENV['RAILS_CACHE_STORE'] == 'redis_cache_store'
|
6
6
|
|
7
|
-
class DummyCompressor
|
8
|
-
def self.deflate(payload)
|
9
|
-
payload
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.inflate(payload)
|
13
|
-
payload
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
7
|
describe RailsBrotliCache do
|
18
8
|
let(:options) do
|
19
9
|
{}
|
@@ -49,12 +39,6 @@ describe RailsBrotliCache do
|
|
49
39
|
expect($redis.get("gz-test-key").size < $redis.get("br-test-key").size).to eq true
|
50
40
|
end
|
51
41
|
|
52
|
-
it "allows specyfing custom compressor class" do
|
53
|
-
Rails.cache.write("gz-test-key", json)
|
54
|
-
cache_store.write("test-key", json, compressor_class: DummyCompressor)
|
55
|
-
expect($redis.get("gz-test-key").size < $redis.get("br-test-key").size).to eq true
|
56
|
-
end
|
57
|
-
|
58
42
|
describe "disable_prefix" do
|
59
43
|
context "default prefix" do
|
60
44
|
it "appends 'br-' prefix" do
|
@@ -4,7 +4,28 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe RailsBrotliCache do
|
6
6
|
subject(:cache_store) do
|
7
|
-
RailsBrotliCache::Store.new(
|
7
|
+
RailsBrotliCache::Store.new(
|
8
|
+
ActiveSupport::Cache::MemoryStore.new,
|
9
|
+
options
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:big_enough_to_compress_value) do
|
14
|
+
SecureRandom.hex(2048)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:options) do
|
18
|
+
{}
|
19
|
+
end
|
20
|
+
|
21
|
+
class DummyCompressor
|
22
|
+
def self.deflate(payload)
|
23
|
+
Zlib::Deflate.deflate(payload)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.inflate(payload)
|
27
|
+
Zlib::Inflate.inflate(payload)
|
28
|
+
end
|
8
29
|
end
|
9
30
|
|
10
31
|
describe "#fetch" do
|
@@ -38,6 +59,30 @@ describe RailsBrotliCache do
|
|
38
59
|
expect(cache_store.read("forced-key")).to eq 2
|
39
60
|
end
|
40
61
|
end
|
62
|
+
|
63
|
+
it "stores value in the configured Rails.cache when options passed" do
|
64
|
+
cache_store.fetch("test-key", expires_in: 5.seconds) { big_enough_to_compress_value }
|
65
|
+
expect(cache_store.read("test-key")).to eq big_enough_to_compress_value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#write_multi and #read_multi" do
|
70
|
+
it "works" do
|
71
|
+
values = {
|
72
|
+
"key_1" => big_enough_to_compress_value,
|
73
|
+
"key_2" => 123
|
74
|
+
}
|
75
|
+
|
76
|
+
cache_store.write_multi(values, expires_in: 5.seconds)
|
77
|
+
expect(cache_store.read_multi("key_1", "key_2")).to eq values
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "fetch_multi" do
|
82
|
+
it "works" do
|
83
|
+
cache_store.fetch_multi("key_1", "key_2", expires_in: 5.seconds) { big_enough_to_compress_value }
|
84
|
+
expect(cache_store.read("key_1")).to eq big_enough_to_compress_value
|
85
|
+
end
|
41
86
|
end
|
42
87
|
|
43
88
|
describe "#increment and #decrement" do
|
@@ -72,20 +117,60 @@ describe RailsBrotliCache do
|
|
72
117
|
end
|
73
118
|
|
74
119
|
describe "#read and #write" do
|
120
|
+
let(:one_kb_value) do
|
121
|
+
SecureRandom.hex(512)
|
122
|
+
end
|
123
|
+
|
75
124
|
it "reads values stored in Rails cache with a prefix" do
|
76
125
|
expect(cache_store.read("test-key")).to eq nil
|
77
|
-
expect(cache_store.write("test-key",
|
78
|
-
expect(cache_store.read("test-key")).to eq
|
126
|
+
expect(cache_store.write("test-key", big_enough_to_compress_value))
|
127
|
+
expect(cache_store.read("test-key")).to eq big_enough_to_compress_value
|
79
128
|
end
|
80
129
|
|
81
|
-
|
82
|
-
|
83
|
-
|
130
|
+
describe ":compressor_class option" do
|
131
|
+
context "as an init config" do
|
132
|
+
let(:options) do
|
133
|
+
{ compressor_class: DummyCompressor }
|
134
|
+
end
|
135
|
+
|
136
|
+
it "calls the custom compressor_class" do
|
137
|
+
expect(DummyCompressor).to receive(:deflate).and_call_original
|
138
|
+
cache_store.write("test-key", one_kb_value)
|
139
|
+
expect(DummyCompressor).to receive(:inflate).and_call_original
|
140
|
+
cache_store.read("test-key")
|
141
|
+
end
|
84
142
|
end
|
85
143
|
|
86
|
-
|
144
|
+
context "as an method call" do
|
145
|
+
it "calls the custom compressor_class" do
|
146
|
+
expect(DummyCompressor).to receive(:deflate).and_call_original
|
147
|
+
cache_store.write("test-key", one_kb_value, compressor_class: DummyCompressor)
|
148
|
+
expect(DummyCompressor).to receive(:inflate).and_call_original
|
149
|
+
cache_store.read("test-key", compressor_class: DummyCompressor)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe ":compress_threshold option" do
|
155
|
+
it "applies compression for larger objects" do
|
156
|
+
expect(Brotli).to receive(:deflate).and_call_original
|
157
|
+
cache_store.write("test-key", one_kb_value)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "does not apply compression for smaller objects" do
|
161
|
+
expect(Brotli).not_to receive(:deflate)
|
87
162
|
cache_store.write("test-key", 123)
|
88
|
-
|
163
|
+
end
|
164
|
+
|
165
|
+
context "custom :compress_threshold value" do
|
166
|
+
let(:options) do
|
167
|
+
{ compress_threshold: 2.kilobyte }
|
168
|
+
end
|
169
|
+
|
170
|
+
it "does not apply compression for objects smaller then custom threshold" do
|
171
|
+
expect(Brotli).not_to receive(:deflate)
|
172
|
+
cache_store.write("test-key", one_kb_value)
|
173
|
+
end
|
89
174
|
end
|
90
175
|
end
|
91
176
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-brotli-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pawurb
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|