rails-brotli-cache 0.3.13 → 0.4.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 +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
         |