Skip to content

Commit

Permalink
Merge pull request #83 from marian13/CS-43_array_based_cache
Browse files Browse the repository at this point in the history
CS-43: Split Cache into Array-based and Hash-based
  • Loading branch information
marian13 committed Apr 11, 2023
2 parents 10822ff + 1ea17c9 commit 60a0ce8
Show file tree
Hide file tree
Showing 30 changed files with 2,067 additions and 317 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Concern
# @return [ConvenientService::Support::Cache]
#
def cache
@cache ||= Support::Cache.new
@cache ||= Support::Cache.create(backend: :hash)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def result_value
end

##
# @return [ConvenientService::Support::Cache::Key]
# @return [ConvenientService::Support::Cache::Entities::Key]
#
def key
@key ||= service_class.stubbed_results.keygen(*arguments[:args], **arguments[:kwargs], &arguments[:block])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ def stubbed_results
#
cache =
if Support::Gems::RSpec.current_example
Utils::Object.instance_variable_fetch(Support::Gems::RSpec.current_example, :@__convenient_service_stubbed_results__) { Support::Cache.new }
Utils::Object.instance_variable_fetch(Support::Gems::RSpec.current_example, :@__convenient_service_stubbed_results__) { Support::Cache.create(backend: :thread_safe_array) }
else
Support::Cache.new
Support::Cache.create(backend: :thread_safe_array)
end

cache.scope(self)
Expand Down
159 changes: 16 additions & 143 deletions lib/convenient_service/support/cache.rb
Original file line number Diff line number Diff line change
@@ -1,157 +1,30 @@
# frozen_string_literal: true

require_relative "cache/key"
require_relative "cache/constants"
require_relative "cache/entities"
require_relative "cache/errors"

module ConvenientService
module Support
class Cache
##
# @return [void]
#
def initialize(hash = {})
@hash = hash
end

class << self
##
# @return [ConvenientService::Support::Cache::Key]
# @param backend [Symbol]
# @return [ConvenientService::Support::Cache::Entities::Caches::Base]
#
def keygen(...)
Cache::Key.new(...)
def create(backend: Constants::Backends::HASH)
case backend
when Constants::Backends::ARRAY
Entities::Caches::Array.new
when Constants::Backends::HASH
Entities::Caches::Hash.new
when Constants::Backends::THREAD_SAFE_ARRAY
Entities::Caches::ThreadSafeArray.new
else
raise Errors::NotSupportedBackend.new(backend: backend)
end
end
end

##
# @return [Boolean]
#
# @internal
# https://ruby-doc.org/core-2.7.0/Hash.html#method-i-empty-3F
#
def empty?
hash.empty?
end

##
# @return [Boolean]
#
# @internal
# https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-exist-3F
#
def exist?(key)
hash.has_key?(key)
end

##
# @return [Object] Can be any type.
#
# @internal
# https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-read
#
def read(key)
hash[key]
end

##
# @return [Object] Can be any type.
#
# @internal
# NOTE: `alias_method` is NOT used in order to have an ability to use `allow(cache).to receive(:read).with(key).and_call_original` for both `cache[key]` and `cache.read(key)` in RSpec.
#
def [](key)
read(key)
end

##
# @param key [Object] Can be any type.
# @param value [Object] Can be any type.
# @return [Object] Can be any type.
#
# @internal
# https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-write
#
def write(key, value)
hash[key] = value
end

##
# @param key [Object] Can be any type.
# @param value [Object] Can be any type.
# @return [Object] Can be any type.
#
# @internal
# NOTE: `alias_method` is NOT used in order to have an ability to use `allow(cache).to receive(:write).with(key, value).and_call_original` for both `cache[key] = value` and `cache.write(key, value)` in RSpec.
#
def []=(key, value)
write(key, value)
end

##
# @param key [Object] Can be any type.
# @return [Object] Can be any type.
#
# @internal
# https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-delete
#
def delete(key)
hash.delete(key)
end

##
# @param key [Object] Can be any type.
# @param block [Proc, nil]
# @return [Object] Can be any type.
#
# @internal
# https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-fetch
#
def fetch(key, &block)
return read(key) unless block

exist?(key) ? read(key) : write(key, block.call)
end

##
# @return [ConvenientService::Support::Cache]
#
def clear
hash.clear

self
end

##
# @return [ConvenientService::Support::Cache]
#
def scope(key)
fetch(key) { Support::Cache.new }
end

##
# @return [ConvenientService::Support::Cache::Key]
#
def keygen(...)
Cache.keygen(...)
end

##
# @param other [Object] Can be any type.
# @return [Boolean]
#
def ==(other)
return unless other.instance_of?(self.class)

return false if hash != other.hash

true
end

protected

##
# @!attribute [r] hash
# @return [Hash]
#
attr_reader :hash
end
end
end
36 changes: 36 additions & 0 deletions lib/convenient_service/support/cache/constants.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module ConvenientService
module Support
class Cache
class Constants
module Backends
##
# @return [Symbol]
#
ARRAY = :array

##
# @return [Symbol]
#
HASH = :hash

##
# @return [Symbol]
#
THREAD_SAFE_ARRAY = :thread_safe_array

##
# @return [Array<Symbol>]
#
ALL = [ARRAY, HASH, THREAD_SAFE_ARRAY]

##
# @return [Symbol]
#
DEFAULT = HASH
end
end
end
end
end
4 changes: 4 additions & 0 deletions lib/convenient_service/support/cache/entities.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

require_relative "entities/caches"
require_relative "entities/key"
8 changes: 8 additions & 0 deletions lib/convenient_service/support/cache/entities/caches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

require_relative "caches/base"

require_relative "caches/array"
require_relative "caches/hash"

require_relative "caches/thread_safe_array"
135 changes: 135 additions & 0 deletions lib/convenient_service/support/cache/entities/caches/array.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# frozen_string_literal: true

require_relative "array/entities"

module ConvenientService
module Support
class Cache
module Entities
module Caches
class Array < Caches::Base
##
# @return [void]
#
def initialize(array = [])
@array = array
end

##
# @return [Array<ConvenientService::Support::Cache::Entities::Caches::Array::Entities::Pair>]
#
def store
array
end

##
# @return [Boolean]
#
def empty?
array.empty?
end

##
# @param key [Object] Can be any type.
# @return [Boolean]
#
def exist?(key)
index = index(key)

index ? true : false
end

##
# @param key [Object] Can be any type.
# @return [Object] Can be any type.
#
def read(key)
index = index(key)

array[index].value if index
end

##
# @param key [Object] Can be any type.
# @param value [Object] Can be any type.
# @return [Object] Can be any type.
#
def write(key, value)
index = index(key) || array.size

array[index] = pair(key, value)

value
end

##
# @param key [Object] Can be any type.
# @param block [Proc, nil]
# @return [Object] Can be any type.
#
# @internal
# https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-fetch
#
def fetch(key, &block)
index = index(key)

return array[index].value if index

return unless block

value = block.call

array << pair(key, value)

value
end

##
# @param key [Object] Can be any type.
# @return [Object] Can be any type.
#
def delete(key)
index = index(key)

array.delete_at(index).value if index
end

##
# @return [ConvenientService::Support::Cache::Entities::Caches::Array]
#
def clear
array.clear

self
end

private

##
# @!attribute [r] array
# @return [Array]
#
attr_reader :array

##
# @param key [Object] Can be any type.
# @return [Integer, nil]
#
def index(key)
array.find_index { |pair| pair.key == key }
end

##
# @param key [Object] Can be any type.
# @param value [Object] Can be any type.
# @return [ConvenientService::Support::Cache::Entities::Caches::Array::Pair]
#
def pair(key, value)
Entities::Pair.new(key: key, value: value)
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative "entities/pair"
Loading

0 comments on commit 60a0ce8

Please sign in to comment.