# -*- 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