Skip to content

Commit

Permalink
Merge dd52004 into 7e179e0
Browse files Browse the repository at this point in the history
  • Loading branch information
0exp committed Sep 24, 2018
2 parents 7e179e0 + dd52004 commit 9622723
Show file tree
Hide file tree
Showing 21 changed files with 780 additions and 148 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.

## [Unreleased]
### Added
- multi-operations: `#read_multi`, `#write_multi`, `#fetch_multi`, `#delete_matched`;
- logging:
- configuration: `AnyCache.configure { |conf| conf.logger = your_logger_object }`;
- disable logging: `AnyCache.configure { |conf| conf.logger = nil }`;
Expand Down
38 changes: 20 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# AnyCache · [![Gem Version](https://badge.fury.io/rb/any_cache.svg)](https://badge.fury.io/rb/any_cache) [![Build Status](https://travis-ci.org/0exp/any_cache.svg?branch=master)](https://travis-ci.org/0exp/any_cache) [![Coverage Status](https://coveralls.io/repos/github/0exp/any_cache/badge.svg?branch=master)](https://coveralls.io/github/0exp/any_cache?branch=master)

AnyCache - a simplest cache wrapper that provides a minimalistic generic interface for all well-known cache storages and includes a minimal set of necessary operations:
`fetch`, `read`, `write`, `delete`, `expire`, `persist`, `exist?`, `clear`, `increment`, `decrement`.
`fetch`, `read`, `write`, `delete`, `fetch_multi`, `read_multi`, `write_multi`, `delete_matched`, `expire`, `persist`, `exist?`, `clear`, `increment`, `decrement`.

Supported clients:

Expand Down Expand Up @@ -49,12 +49,11 @@ require 'any_cache'
- [Custom cache clients](#custom-cache-clients)
- [Logging](#logging)
- [Operations](#operations)
- [Fetch](#fetch)
- [Read](#read)
- [Write](#write)
- [Delete](#delete)
- [Increment](#increment)
- [Decrement](#decrement)
- [Fetch](#fetch) / [Fetch Multi](#fetch-multi)
- [Read](#read) / [Read Multi](#read-multi)
- [Write](#write) / [Write Multi](#write-multi)
- [Delete](#delete) / [Delete Matched](#delete-matched)
- [Increment](#increment) / [Decrement](#decrement)
- [Expire](#expire)
- [Persist](#persist)
- [Existence](#existence)
Expand Down Expand Up @@ -251,10 +250,14 @@ dalli_cache = DalliCache.build

If you want to use your own cache client implementation, you should provide an object that responds to:

- `#fetch(*key, [**options])` ([doc](#fetch))
- `#fetch(key, [**options])` ([doc](#fetch))
- `#fetch_multi(*keys, [**options])` ([doc](#fetch-multi))
- `#read(key, [**options])` ([doc](#read))
- `#read_multi(*keys, [**options])` ([doc](#read-multi))
- `#write(key, value, [**options])` ([doc](#write))
- `#write_multi(entries, [**options])` ([doc](#write-multi))
- `#delete(key, [**options])` ([doc](#delete))
- `#delete_matched(pattern, [**options])` ([doc](#delete-matched))
- `#increment(key, amount, [**options])` ([doc](#increment))
- `#decrement(key, amount, [**options])` ([doc](#decrement))
- `#expire(key, [**options])` ([doc](#expire))
Expand Down Expand Up @@ -332,12 +335,11 @@ any_cache.clear

`AnyCache` provides a following operation set:

- [fetch](#fetch)
- [read](#read)
- [write](#write)
- [delete](#delete)
- [increment](#increment)
- [decrement](#decrement)
- [fetch](#fetch) / [fetch_multi](#fetch-multi)
- [read](#read) / [read_multi](#read-multi)
- [write](#write) / [write_multi](#write-multi)
- [delete](#delete) / [delete_matched](#delete-matched)
- [increment](#increment) / [decrement](#decrement)
- [expire](#expire)
- [persist](#persist)
- [clear](#clear)
Expand All @@ -351,7 +353,7 @@ any_cache.clear
- works in `ActiveSupport::Cache::Store#fetch`-manner;
- fetches data from the cache using the given key;
- if a block has been passed and data with the given key does not exist - that block
will be called and the return value will be written to the cache;
will be called with the given key and the return value will be written to the cache;

```ruby
# --- entry exists ---
Expand All @@ -360,19 +362,19 @@ cache_store.fetch("data") { "new_data" } # => "some_data"

# --- entry does not exist ---
cache_store.fetch("data") # => nil
cache_store.fetch("data") { "new_data" } # => "new_data"
cache_store.fetch("data") { |key| "new_data" } # => "new_data"
cache_store.fetch("data") # => "new_data"

# --- new entry with expiration time ---
cache_store.fetch("data") # => nil
cache_store.fetch("data", expires_in: 8) { "new_data" } # => "new_data"
cache_store.fetch("data", expires_in: 8) { |key| "new_data" } # => "new_data"
cache_store.fetch("data") # => "new_data"
# ...sleep 8 seconds...
cache_store.fetch("data") # => nil

# --- force update/rewrite ---
cache_store.fetch("data") # => "some_data"
cache_store.fetch("data", expires_in: 8, force: true) { "new_data" } # => "new_data"
cache_store.fetch("data", expires_in: 8, force: true) { |key| "new_data" } # => "new_data"
cache_store.fetch("data") # => "new_data"
# ...sleep 8 seconds...
cache_store.fetch("data") # => nil
Expand Down
8 changes: 6 additions & 2 deletions lib/any_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,19 @@ def build(driver = Drivers.build(config))
# @since 0.3.0
def_loggable_delegators :adapter,
:read,
:read_multi,
:write,
:write_multi,
:fetch,
:fetch_multi,
:delete,
:delete_matched,
:increment,
:decrement,
:expire,
:persist,
:clear,
:exist?,
:fetch
:exist?

# @return [AnyCache::Adapters::Basic]
#
Expand Down
87 changes: 71 additions & 16 deletions lib/any_cache/adapters/active_support_mem_cache_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ def supported_driver?(driver)
end
end

# @return [Array]
#
# @api private
# @since 0.3.0
READ_MULTI_EMPTY_KEYS_SET = [].freeze

# @return [NilClass]
#
# @api private
Expand Down Expand Up @@ -44,7 +50,7 @@ def supported_driver?(driver)
# @since 0.2.0
def_delegators :driver, :delete, :clear

# @param key
# @param key [String]
# @param options [Hash]
# @return [Object]
#
Expand All @@ -56,6 +62,20 @@ def read(key, **options)
driver.read(key, raw: raw)
end

# @param keys [Array<String>]
# @param options [Hash]
# @return [Hash]
#
# @api private
# @since 0.3.0
def read_multi(*keys, **options)
raw = options.fetch(:raw, true)

driver.read_multi(*keys, raw: raw).tap do |res|
res.merge!(Hash[(keys - res.keys).zip(READ_MULTI_EMPTY_KEYS_SET)])
end
end

# @param key [String]
# @param value [Object]
# @option expires_in [NilClass, Integer] Time in seconds
Expand All @@ -70,6 +90,56 @@ def write(key, value, **options)
driver.write(key, value, expires_in: expires_in, raw: raw)
end

# @param entries [Hash]
# @param options [Hash]
# @return [void]
#
# @api private
# @since 0.3.0
def write_multi(entries, **options)
raw = options.fetch(:raw, true)

driver.write_multi(entries, expires_in: NO_EXPIRATION_TTL, raw: raw)
end

# @param key [String]
# @option expires_in [Integer]
# @option force [Boolean, Proc]
# @return [Object]
#
# @api private
# @since 0.2.0
def fetch(key, **options, &fallback)
force_rewrite = options.fetch(:force, false)
force_rewrite = force_rewrite.call(key) if force_rewrite.respond_to?(:call)
expires_in = options.fetch(:expires_in, NO_EXPIRATION_TTL)

driver.fetch(key, force: force_rewrite, expires_in: expires_in, &fallback)
end

# @param keys [Array<String>]
# @param options [Hash]
# @param fallback [Proc]
# @return [Hash]
#
# @api private
# @since 0.3.0
def fetch_multi(*keys, **options, &fallback)
keys.each_with_object({}) do |key, dataset|
dataset[key] = fetch(key, **options, &fallback)
end
end

# @param pattern [???]
# @param options [???]
# @return [void]
#
# @api private
# @since 0.3.0
def delete_matched(pattern, **options)
# TODO: implement
end

# @param key [String]
# @param amount [Integer]
# @options expires_in [Integer]
Expand Down Expand Up @@ -144,20 +214,5 @@ def persist(key, **options)
def exist?(key, **options)
driver.exist?(key)
end

# @param key [String]
# @option expires_in [Integer]
# @option force [Boolean]
# @return [Object]
#
# @api private
# @since 0.2.0
def fetch(key, **options, &block)
force_rewrite = options.fetch(:force, false)
force_rewrite = force_rewrite.call if force_rewrite.respond_to?(:call)
expires_in = options.fetch(:expires_in, NO_EXPIRATION_TTL)

driver.fetch(key, force: force_rewrite, expires_in: expires_in, &block)
end
end
end
96 changes: 79 additions & 17 deletions lib/any_cache/adapters/active_support_naive_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ def read(key, **options)
lock.with_read_lock { super }
end

# @param keys [Array<String>]
# @param options [Hash]
# @return [Hash]
#
# @api private
# @since 0.3.0
def read_multi(*keys, **options)
lock.with_read_lock do
super.tap do |res|
res.merge!(Hash[(keys - res.keys).zip(Operation::READ_MULTI_EMPTY_KEYS_SET)])
end
end
end

# @param key [String]
# @param options [Hash]
# @return [void]
Expand All @@ -44,6 +58,16 @@ def delete(key, **options)
lock.with_write_lock { super }
end

# @param pattern [Object]
# @param options [Hash]
# @return [void]
#
# @api private
# @since 0.3.0
def delete_matched(pattern, **options)
lock.with_write_lock { super }
end

# @param options [Hash]
# @return [void]
#
Expand All @@ -68,6 +92,61 @@ def write(key, value, **options)
end
end

# @param entries [Hash]
# @param options [Hash]
# @return [void]
#
# @api private
# @since 0.3.0
def write_multi(entries, **options)
lock.with_write_lock do
expires_in = self.class::Operation::NO_EXPIRATION_TTL

super(entries, expires_in: expires_in)
end
end

# @param key [String]
# @param fallback [Proc]
# @option expires_in [Integer]
# @option force [Boolean, Proc]
# @return [Object]
#
# @api private
# @since 0.2.0
def fetch(key, **options, &fallback)
lock.with_write_lock do
force_rewrite = options.fetch(:force, false)
force_rewrite = force_rewrite.call(key) if force_rewrite.respond_to?(:call)
expires_in = options.fetch(:expires_in, self.class::Operation::NO_EXPIRATION_TTL)

super(key, force: force_rewrite, expires_in: expires_in, &fallback)
end
end

# @param keys [Array<String>]
# @param fallback [Proc]
# @option force [Boolean, Proc]
# @option expires_in [Integer]
# @return [Hash]
#
# @api private
# @since 0.3.0
def fetch_multi(*keys, **options, &fallback)
lock.with_write_lock do
force_rewrite = options.fetch(:force, false)
expires_in = options.fetch(:expires_in, self.class::Operation::NO_EXPIRATION_TTL)

# NOTE:
# use own #fetch_multi implementation cuz original #fetch_multi
# does not support :force option
keys.each_with_object({}) do |key, dataset|
force = force_rewrite.respond_to?(:call) ? force_rewrite.call(key) : force_rewrite
dataset[key] = driver.fetch(key, force: force, expires_in: expires_in, &fallback)
end
end
end

# @param key [String]
# @param amount [Integer, Float]
# @option expires_in [NilClass, Integer]
Expand Down Expand Up @@ -128,23 +207,6 @@ def exist?(key, **options)
lock.with_read_lock { super }
end

# @param key [String]
# @option expires_in [Integer]
# @option force [Boolean]
# @return [Object]
#
# @api private
# @since 0.2.0
def fetch(key, **options, &block)
lock.with_write_lock do
force_rewrite = options.fetch(:force, false)
force_rewrite = force_rewrite.call if force_rewrite.respond_to?(:call)
expires_in = options.fetch(:expires_in, self.class::Operation::NO_EXPIRATION_TTL)

super(key, force: force_rewrite, expires_in: expires_in, &block)
end
end

private

# @return [Concurrent::ReentrantReadWriteLock]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class Operation
# @since 0.1.0
extend Forwardable

# @return [Array]
#
# @api private
# @since 0.3.0
READ_MULTI_EMPTY_KEYS_SET = [].freeze

# @return [NilClass]
#
# @api private
Expand Down
Loading

0 comments on commit 9622723

Please sign in to comment.