module CachedAttr::ClassMethods

Public Instance Methods

cached_attr(name, options = {}, &block) click to toggle source

Defines an attribute on the including class.

@param name [Symbol] the name of the attribute @param options [Hash] options @option options :writer if set to true, also define a writer method for the property @option options :ttl the time to live for a value. Ignored if nil or false @option options :method the method to call if no block is given

# File lib/cached_attr.rb, line 33
def cached_attr(name, options = {}, &block)
  default_opts = {
      :writer => false,
      :method => false,
      :ttl => false
  }
  opts = default_opts.merge(options)

  class_eval do
    internal_retrieve_method = "__internal_retrieve_#{name}".to_sym
    define_method(internal_retrieve_method) do
      if block.nil?
        val = self.send(opts[:method])
      else
        val = self.instance_exec(&block)
      end
      cached_attributes_store[name][:invoke_count] += 1
      val
    end
    internal_init_method = "__internal_init_#{name}".to_sym
    define_method(internal_init_method) do |val = nil|
      unless cached_attributes_store.has_key?(name)
        cached_attributes_store[name] = {:call_count => 0, :invoke_count => 0}
        if val.nil?
          cached_attributes_store[name][:value] = self.send(internal_retrieve_method)
          cached_attributes_store[name][:expires] = Time.now + opts[:ttl] if opts[:ttl]

        else
          cached_attributes_store[name][:value] = val
        end
      end
      cached_attributes_store[name]
    end


    define_method("_#{name}_reset!".to_sym) do
      cached_attributes_store.delete(name)
    end

    define_method("_#{name}_expires".to_sym) do
      self.send(internal_init_method)[:expires]
    end

    define_method("_#{name}_call_count".to_sym) do
      self.send(internal_init_method)[:call_count]
    end

    define_method("_#{name}_invoke_count".to_sym) do
      self.send(internal_init_method)[:invoke_count]
    end

    define_method(name) do

      cache_val = self.send(internal_init_method)

      if opts[:ttl] && cache_val[:expires] && Time.now > cache_val[:expires]
        cache_val[:value] = self.send(internal_retrieve_method)
        cache_val[:expires] = Time.now + opts[:ttl]
      end

      cache_val[:call_count] = cached_attributes_store[name][:call_count] + 1
      cache_val[:value]
    end

    if opts[:writer]
      define_method("#{name}=") do |val|
        self.send(internal_init_method, val)
      end
    end
  end

end