# frozen_string_literal: true require_relative '../modules/dictionary_cache_keys' require_relative '../modules/dictionary_cache_validatable' require_relative '../modules/dictionary_keyable' require_relative '../modules/dictionary_sourceable' module LittleWeasel module Services # This class provides methods and attributes that can be used to manage the # dictionary cache. The "dictionary cache" is a simple Hash that provides # access to informaiton related to dictionaries through a dictionary "key". # A dictionary "key" is a unique key comprised of a locale and # optional "tag" (see Modules::Taggable and DictionaryKey for more # information). The dictionary cache also provides a way for dictionary # objects to share dictionary information, in particular, the dictionary # file and dictionary metadata. class DictionaryCacheService include Modules::DictionaryCacheKeys include Modules::DictionaryCacheValidatable include Modules::DictionaryKeyable include Modules::DictionarySourceable attr_reader :dictionary_cache # This class produces the following (example) Hash that represents the # dictionary cache structure: # # @example This is an example: # # { # 'dictionary_cache' => # { # 'dictionary_references' => # { # 'en' => # { # 'dictionary_id' => 19ec7845 # }, # 'en-US' => # { # 'dictionary_id' => 0987a3f2 # }, # 'en-US-temp' => # { # 'dictionary_id' => 9273eac6 # } # }, # 'dictionaries' => # { # 19ec7845 => # { # 'source' => '/en.txt', # 'dictionary_object' => {} # }, # 0987a3f2 => # { # 'source' => '/en-US.txt', # 'dictionary_object' => {} # }, # 9273eac6 => # { # 'source' => '*736ed423', # 'dictionary_object' => {} # } # } # } # } def initialize(dictionary_key:, dictionary_cache:) validate_dictionary_key dictionary_key: dictionary_key self.dictionary_key = dictionary_key validate_dictionary_cache dictionary_cache: dictionary_cache self.dictionary_cache = dictionary_cache self.class.init(dictionary_cache: dictionary_cache) unless dictionary_cache[DICTIONARY_CACHE] end class << self # This method resets dictionary_cache to its initialized state. # This class method is different from the #init instance method # in that ALL dictionary references and ALL dictionaries are # initialized. def init(dictionary_cache:) Modules::DictionaryCacheKeys.initialize_dictionary_cache dictionary_cache: dictionary_cache end # Returns true if the dictionary cache is initialized; that # is, if the given dictionary_cache is in the same state the # dictionary cache would be in after .init were called. def init?(dictionary_cache:) initialized_dictionary_cache = init(dictionary_cache: {}) dictionary_cache.eql?(initialized_dictionary_cache) end # Returns the number of dictionaries currently in the cache. def count(dictionary_cache:) dictionary_cache.dig(self::DICTIONARY_CACHE, self::DICTIONARIES)&.keys&.count || 0 end end # This method resets the dictionary cache for the given key. This method # is different from the .init class method in that ONLY the dictionary # reference and dictionary specific to the given key is initialized. def init # TODO: Do not delete the dictionary if it is being pointed to by # another dictionary reference. dictionary_cache_hash = dictionary_cache[DICTIONARY_CACHE] dictionary_cache_hash[DICTIONARIES]&.delete(dictionary_id) dictionary_cache_hash[DICTIONARY_REFERENCES]&.delete(key) self end # Returns true if the dictionary reference exists for the given key; false # otherwise. This method is only concerned with the dictionary reference # and has nothing to do with whether or not the associated dictionary # is actually loaded into the dictionary cache. def dictionary_reference? dictionary_reference&.present? || false end # Returns true if a dictionaries Hash key exists for the given dictionary_id # in the dictionary cache. This method is only concerned with the existance of # the key and has nothing to do with whether or not file/memory sources are # present or the presence of a dictionary object. def dictionary? dictionary_cache[DICTIONARY_CACHE][DICTIONARIES].key? dictionary_id end # Returns the dictionary id if there is a dictionary id in the dictionary # cache associated with the given key; nil otherwise. def dictionary_id dictionary_cache.dig(DICTIONARY_CACHE, DICTIONARY_REFERENCES, key, DICTIONARY_ID) end # Returns the dictionary id if there is a dictionary id in the dictionary # cache associated with the given key. This method raises an error if the # dictionary id cannot be found. def dictionary_id! return dictionary_id if dictionary_id? raise ArgumentError, "A dictionary id could not be found for key '#{key}'." end # This method returns true if the dictionary associated with the # given dictionary key is loaded/cached. If this is the case, # a dictionary object is available in the dictionary cache. def dictionary_object? dictionary_object.present? end alias dictionary_exist? dictionary_object? # DEPRECATED: Use dictionary_exist? instead. def dictionary_exists? warn "[DEPRECATION] 'dictionary_exists?' is deprecated. Please use 'dictionary_exist?' instead." dictionary_object? end # Returns the dictionary object from the dictionary cache for the given # key. This method raises an error if the dictionary is not in the cache; # that is, if the dictionary was not previously loaded from disk or memory. def dictionary_object! unless dictionary_object? raise ArgumentError, "The dictionary object associated with argument key '#{key}' is not in the cache." end dictionary_object end def dictionary_object dictionary_cache.dig(DICTIONARY_CACHE, DICTIONARIES, dictionary_id, DICTIONARY_OBJECT) end def dictionary_object=(object) raise ArgumentError, 'Argument object is not a Dictionary object' unless object.is_a? Dictionary unless dictionary_reference? raise ArgumentError, "The dictionary reference associated with key '#{key}' could not be found." end return if object.equal? dictionary_object if dictionary_exist? raise ArgumentError, "The dictionary is already loaded/cached for key '#{key}'; use #unload or #kill first." end dictionary_cache[DICTIONARY_CACHE][DICTIONARIES][dictionary_id!][DICTIONARY_OBJECT] = object end private attr_writer :dictionary_cache def dictionary_reference dictionary_cache.dig(DICTIONARY_CACHE, DICTIONARY_REFERENCES, key) end def set_dictionary_reference(dictionary_id:) dictionary_cache[DICTIONARY_CACHE][DICTIONARY_REFERENCES][key] = { DICTIONARY_ID => dictionary_id } end def dictionary_id? dictionary_id.present? end end end end