Skip to content

Commit

Permalink
Merge 34505dd into 2578932
Browse files Browse the repository at this point in the history
  • Loading branch information
0exp committed Oct 7, 2018
2 parents 2578932 + 34505dd commit c92d3c9
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 35 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.

## [0.3.1] - 2018-10-08
### Added
- patch interface `AnyCache.enable_patch!(:patch_series_name)`:
- `ActiveSupport::Cache::DalliStore` patch: now the `#fetch` method provides
a cache key attribute to the fallback proc (can be enabled via `.enable(:dalli_store)`);

## [0.3.0] - 2018-10-06
### Added
- support for `ActiveSupport::Cache::DalliStore` client;
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ require 'any_cache'
- [Persist](#persist)
- [Existence](#existence)
- [Clear](#clear)
- [Roadmap](#roadmap)

---

Expand Down Expand Up @@ -195,6 +196,8 @@ require 'dalli'
require 'active_support'
require 'any_cache'

AnyCache.enable_patch!(:dalli_store)

AnyCache.configure do |conf|
conf.driver = :as_dalli_store
conf.as_dalli_store.servers = ... # string or array of strings
Expand Down
4 changes: 3 additions & 1 deletion lib/any_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class AnyCache
require_relative 'any_cache/adapters'
require_relative 'any_cache/logging'
require_relative 'any_cache/delegation'
require_relative 'any_cache/patches'

# @since 0.2.0
include Qonfig::Configurable

# @since 0.3.0
include Delegation
# @since 0.3.1
extend Patches::InterfaceAccessMixin

# @since 0.2.0
# rubocop:disable Metrics/BlockLength
Expand Down
3 changes: 3 additions & 0 deletions lib/any_cache/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ class AnyCache

# @since 0.1.0
UnsupportedDriverError = Class.new(Error)

# @since 0.3.1
NonexistentPatchError = Class.new(ArgumentError)
end
41 changes: 41 additions & 0 deletions lib/any_cache/patches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

# @api private
# @since 0.3.1
module AnyCache::Patches
# @since 0.3.1
require_relative 'patches/dalli_store'

class << self
# @param patch_series [Symbol, String]
# @return [void]
#
# @raise [AnyCache::NonexistentPatchError]
#
# @api private
# @since 0.3.1
def enable!(patch_series)
case patch_series
when :dalli_store, 'dalli_store'
AnyCache::Patches::DalliStore.enable!
else
raise AnyCache::NonexistentPatchError, "Can't enable nonexistnet patch! (#{patch_series})"
end
end
end

# @api private
# @since 0.3.1
module InterfaceAccessMixin
# @param patch_series [Symbol, String]
# @return [void]
#
# @see AnyCache::Patches#enable!
#
# @api private
# @since 0.3.1
def enable_patch!(patch_series)
AnyCache::Patches.enable!(patch_series)
end
end
end
61 changes: 61 additions & 0 deletions lib/any_cache/patches/dalli_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

# @api private
# @since 0.3.1
module AnyCache::Patches::DalliStore
class << self
# @return [void]
#
# @api private
# @since 0.3.1
def enable!
defined?(::Dalli) &&
defined?(::ActiveSupport::Cache::DalliStore) &&
Gem::Version.new(::Dalli::VERSION) <= Gem::Version.new('2.7.8') &&
::ActiveSupport::Cache::DalliStore.prepend(ActiveSupportFetchWithKey)
end
end

# @api private
# @since 0.3.1
module ActiveSupportFetchWithKey
# NOTE: original fetch implementation with my own little fix (see #43 line of code below)
# rubocop:disable all
def fetch(name, options=nil)
options ||= {}
options[:cache_nils] = true if @options[:cache_nils]
namespaced_name = namespaced_key(name, options)
not_found = options[:cache_nils] ? Dalli::Server::NOT_FOUND : nil
if block_given?
entry = not_found
unless options[:force]
entry = instrument_with_log(:read, namespaced_name, options) do |payload|
read_entry(namespaced_name, options).tap do |result|
if payload
payload[:super_operation] = :fetch
payload[:hit] = not_found != result
end
end
end
end

if not_found == entry
result = instrument_with_log(:generate, namespaced_name, options) do |payload|
# FIX: https://github.com/petergoldstein/dalli/pull/701
yield(name)
# FIX: https://github.com/petergoldstein/dalli/pull/701
end
write(name, result, options)
result
else
instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
entry
end
else
read(name, options)
end
end
# rubocop:enable all
end
end

3 changes: 3 additions & 0 deletions lib/any_cache/version.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

class AnyCache
# @return [String]
#
# @api public
# @since 0.1.0
VERSION = '0.3.0'
end
46 changes: 12 additions & 34 deletions spec/features/fetch_multi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,12 @@
"#{key}-#{data_stub}"
end

unless test_as_dalli_store_cache? # TODO: remove this condition in future
# NOTE: entry_2 has especial dynamically calculated value
expect(data).to match(
entry_1[:key] => entry_1[:value],
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end

if test_as_dalli_store_cache? # TODO: remove this block of code in future
# NOTE: entry_2 has especial dynamically calculated value
expect(data).to match(
entry_1[:key] => entry_1[:value],
entry_2[:key] => "-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end
# NOTE: entry_2 has especial dynamically calculated value
expect(data).to match(
entry_1[:key] => entry_1[:value],
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)

# NOTE: force rewrite
data_stub = SecureRandom.hex(4)
Expand All @@ -73,23 +62,12 @@
"#{key}-#{data_stub}"
end

unless test_as_dalli_store_cache? # TODO: remove this condition in future
# NOTE: entries with new values (and expiration time)
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => "#{entry_1[:key]}-#{data_stub}",
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end

if test_as_dalli_store_cache? # TODO: remove this block of code in future
# NOTE: entries with new values (and expiration time)
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => "-#{data_stub}",
entry_2[:key] => "-#{data_stub}",
entry_3[:key] => entry_3[:value]
)
end
# NOTE: entries with new values (and expiration time)
expect(cache_store.fetch_multi(entry_1[:key], entry_2[:key], entry_3[:key])).to match(
entry_1[:key] => "#{entry_1[:key]}-#{data_stub}",
entry_2[:key] => "#{entry_2[:key]}-#{data_stub}",
entry_3[:key] => entry_3[:value]
)

sleep(expires_in + 1)

Expand Down
13 changes: 13 additions & 0 deletions spec/features/patches_interface_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

describe 'Patches interface' do
describe 'patch activation' do
it 'fails when the required patch does not exist' do
random_nonexistent_patch_name = SecureRandom.hex(rand(1..4)).to_sym

expect do
AnyCache.enable_patch!(random_nonexistent_patch_name)
end.to raise_error(AnyCache::NonexistentPatchError)
end
end
end
5 changes: 5 additions & 0 deletions spec/support/spec_support/cache/active_support_dalli_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class CacheStore < AnyCache
class << self
def build
load_dependencies!
enable_patches!
build_cache_store
end

Expand All @@ -24,6 +25,10 @@ def load_dependencies!
require 'active_support/cache/dalli_store'
end

def enable_patches!
AnyCache.enable_patch!(:dalli_store)
end

def build_cache_store
CacheStore.build
end
Expand Down

0 comments on commit c92d3c9

Please sign in to comment.