beatrichartz/exchange

View on GitHub
lib/exchange/cache/memory.rb

Summary

Maintainability
A
1 hr
Test Coverage
# -*- encoding : utf-8 -*-
module Exchange
  module Cache
    # @author Beat Richartz
    # A class that uses instance variables on the cache singleton class to store values in memory
    # 
    # @version 0.1
    # @since 0.1
    # @example Activate caching via memory by setting the cache in the configuration to :memory
    #   Exchange::Configuration.define do |c| 
    #     c.cache = :memory
    #   end
    #
    class Memory < Base
        
      # returns either cached data from an instance variable or calls the block and caches it in an instance variable.
      # This method has to be the same in all the cache classes in order for the configuration binding to work
      # @param [Exchange::ExternalAPI::Subclass] api The API class the data has to be stored for
      # @param [Hash] opts the options to cache with
      # @option opts [Time] :at the historic time of the exchange rates to be cached
      # @yield [] This method takes a mandatory block with an arity of 0 for caching
      # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
      #
      def cached api, opts={}, &block
        ivar_name = instance_variable_name(api, opts)

        result = instance_variable_get(ivar_name) 
        
        unless result && !result.to_s.empty?
          result = super

          if result && !result.to_s.empty?
            instance_variable_set(ivar_name, result)
          end 

          clean!
        end
        
        opts[:plain] ? result.cachify : result
      end
      
      private
      
      # Generate an instance variable name for the memory cache to get and set
      # @param [Exchange::ExternalAPI::Subclass] api The API to store the data for
      # @param [Hash] opts The options for caching
      # @return [String] A string that can be used as instance variable name
      #
      def instance_variable_name api, opts
        conversion_time          = helper.assure_time(opts[:at], :default => :now)
        time                     = Time.now
        expire_hourly            = config.expire == :hourly || nil
        
        [
          '@' + api.to_s.downcase.gsub(/::/, '_'),
          conversion_time.year.to_s,
          conversion_time.yday.to_s,
          expire_hourly && conversion_time.hour.to_s,
          time.year.to_s, 
          time.yday.to_s, 
          expire_hourly && time.hour.to_s,
          opts[:key_for] && opts[:key_for].join('_')
        ].compact.join('_')
      end
      
      # Clean the memory from expired exchange instance variables. This removes instance variables matching
      # only the specific key pattern
      # @return [Array] The still persisting instance variables
      #
      def clean!
        time = Time.now
        
        instance_variables.select do |i|
          condition = false
          match = i.to_s.match(/\A@exchange[^\d]+\d{4}_\d{1,3}_?\d{0,2}_(\d{4})_(\d{1,3})_?(\d{1,2})?/)
          
          if match
            condition = match[1].to_i != time.year || match[2].to_i != time.yday
            condition = match[3].to_i != time.hour if match[3]
          end
          
          condition
        end.each do |i|
          remove_instance_variable i
        end
      end
      
    end
  end
end