def cache_with_timeout(method, timeout = nil, &block)
      raise "no block given" if block.nil?
      raise ArgumentError, "method must be a Symbol" unless method.respond_to?(:to_sym)

      key = "#{name}.#{method}".to_sym

      $cache_with_timeout_lock.synchronize(:EX) do
        $cache_with_timeout[key] = {}
      end

      define_singleton_method(method) do |*args|
        force_reload = args.first
        return $cache_with_timeout_lock.synchronize(:EX) do
          cache = $cache_with_timeout[key]

          old_timeout = cache[:timeout]
          cache.clear if force_reload || (old_timeout && Time.now.utc > old_timeout)
          break cache[:value] if cache.key?(:value)

          new_timeout = timeout || 300 # 5 minutes
          new_timeout = new_timeout.call if new_timeout.kind_of?(Proc)
          new_timeout = Time.now.utc + new_timeout
          new_value   = block.call

          cache[:timeout] = new_timeout
          cache[:value]   = new_value
        end
      end

      define_singleton_method("#{method}_clear_cache") do |*_args|
        $cache_with_timeout_lock.synchronize(:EX) do
          $cache_with_timeout[key].clear
        end
      end

      define_singleton_method("#{method}_cached?") do |*_args|
        $cache_with_timeout_lock.synchronize(:EX) do
          !$cache_with_timeout[key].empty?
        end
      end

      method.to_sym
    end
def cache_with_timeout(method, timeout = nil, &block)
      raise "no block given" if block.nil?
      raise ArgumentError, "method must be a Symbol" unless method.respond_to?(:to_sym)

      key = "#{name}.#{method}".to_sym

      $cache_with_timeout_lock.synchronize(:EX) do
        $cache_with_timeout[key] = {}
      end

      define_singleton_method(method) do |*args|
        force_reload = args.first
        return $cache_with_timeout_lock.synchronize(:EX) do
          cache = $cache_with_timeout[key]

          old_timeout = cache[:timeout]
          cache.clear if force_reload || (old_timeout && Time.now.utc > old_timeout)
          break cache[:value] if cache.key?(:value)

          new_timeout = timeout || 300 # 5 minutes
          new_timeout = new_timeout.call if new_timeout.kind_of?(Proc)
          new_timeout = Time.now.utc + new_timeout
          new_value   = block.call

          cache[:timeout] = new_timeout
          cache[:value]   = new_value
        end
      end

      define_singleton_method("#{method}_clear_cache") do |*_args|
        $cache_with_timeout_lock.synchronize(:EX) do
          $cache_with_timeout[key].clear
        end
      end

      define_singleton_method("#{method}_cached?") do |*_args|
        $cache_with_timeout_lock.synchronize(:EX) do
          !$cache_with_timeout[key].empty?
        end
      end

      method.to_sym
    end
cache_with_timeout creates singleton methods that cache the
   results of the given block, but only for a short amount of time.
   If the method is called again after time has passed, then the
   cache is cleared and the block is reevaluated.  Note that the
   cache is only cleared on the next invocation after the time has
   passed, so if the block retains references to large objects,
   they will never be garbage collected if the method is never
   called again.

   The methods created are
   - `method`
   - `#{method}_clear_cache` - Will clear the cached value on demand
   - `#{method}_cached?` - Says whether or not there is a value cached

 Example:
    class Foo
      cache_with_timeout(:expensive_operation) do
        sleep 100
        rand(100)
      end
    end

    Foo.expensive_operation # => 42
    Foo.expensive_operation # => 42
    # ... wait 5+ minutes ...
    Foo.expensive_operation # => 18

 @param [String|Symbol] method The name of the method to create.
 @param [Integer] timeout The number of seconds after which the cache is
   cleared (defaults to: 300 (5 minutes))
 @returns [Symbol] The name of the method created.