<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: caching code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sat, 26 Jul 2008 23:08:07 GMT</pubDate>
    <description>DZone Snippets: caching code</description>
    <item>
      <title>Object.memoize</title>
      <link>http://snippets.dzone.com/posts/show/5300</link>
      <description>The following is inspired by the article "&lt;a href="http://blog.grayproductions.net/articles/caching_and_memoization"&gt;Caching and Memoization&lt;/a&gt;" by James Edward Gray II. &lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;# = Memoization for objects&lt;br /&gt;# &lt;br /&gt;# This module will extend +Object+ with the +memoize+ method.  This method&lt;br /&gt;# provides memoization for instance methods, which means return values will&lt;br /&gt;# be cached and subsequent calls will return the cached value of the first&lt;br /&gt;# call.&lt;br /&gt;# &lt;br /&gt;# Caching is done based on instance, method and arguments to the method.  All&lt;br /&gt;# data is kept in a single +Hash+ store which allows flushing all cached&lt;br /&gt;# results at ones using the +flush_memos+ method.&lt;br /&gt;# &lt;br /&gt;# == Example&lt;br /&gt;#   class Person &lt; Struct.new(:email)&lt;br /&gt;#     def finger&lt;br /&gt;#       `finger #{email}`&lt;br /&gt;#     end&lt;br /&gt;#     memoize :finger&lt;br /&gt;#   end&lt;br /&gt;#   &lt;br /&gt;#   bob = Person.new('bob@test.net')&lt;br /&gt;#   bob.finger                        # finger command executed&lt;br /&gt;#   bob.finger                        # cached value returned&lt;br /&gt;#   Memoizable.flush_memos&lt;br /&gt;#   bob.finger                        # finger command executed&lt;br /&gt;#&lt;br /&gt;# == See also&lt;br /&gt;# http://blog.grayproductions.net/articles/caching_and_memoization&lt;br /&gt;#&lt;br /&gt;# == Author&lt;br /&gt;# R.W. van 't Veer, 2008-04-01, Amsterdam&lt;br /&gt;module Memoizable&lt;br /&gt;  # Store for cached values.&lt;br /&gt;  CACHE = Hash.new{|h,k| h[k] = Hash.new{|h,k| h[k] = {}}} # 3 level hash; CACHE[:foo][:bar][:yelp]&lt;br /&gt;  &lt;br /&gt;  # Memoize the given method(s).&lt;br /&gt;  def memoize(*names)&lt;br /&gt;    names.each do |name|&lt;br /&gt;      unmemoized = "__unmemoized_#{name}"&lt;br /&gt;      &lt;br /&gt;      class_eval %Q{&lt;br /&gt;        alias   :#{unmemoized} :#{name}&lt;br /&gt;        private :#{unmemoized}&lt;br /&gt;        def #{name}(*args)&lt;br /&gt;          cache = CACHE[self][#{name.inspect}]&lt;br /&gt;          cache.has_key?(args) ? cache[args] : (cache[args] = send(:#{unmemoized}, *args))&lt;br /&gt;        end&lt;br /&gt;      }&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  # Flush cached return values.&lt;br /&gt;  def flush_memos&lt;br /&gt;    CACHE.clear&lt;br /&gt;  end&lt;br /&gt;  module_function :flush_memos&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Object # :nodoc:&lt;br /&gt;  extend Memoizable&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;if $0 == __FILE__&lt;br /&gt;  require 'test/unit'&lt;br /&gt;  &lt;br /&gt;  class MemoizeTest &lt; Test::Unit::TestCase # :nodoc:&lt;br /&gt;    def setup&lt;br /&gt;      @obj = TestObject.new&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def teardown&lt;br /&gt;      Memoizable.flush_memos&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_memoize_value_should_stick_until_cache_flushed&lt;br /&gt;      @obj.value = 'a'&lt;br /&gt;      assert_equal 'a', @obj.value&lt;br /&gt;      &lt;br /&gt;      @obj.value = 'b'&lt;br /&gt;      assert_equal 'a', @obj.value&lt;br /&gt;      &lt;br /&gt;      Memoizable.flush_memos&lt;br /&gt;      assert_equal 'b', @obj.value&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_flush_should_clear_all_cached_objects&lt;br /&gt;      @obj.value = 'yelp'&lt;br /&gt;      @obj.value&lt;br /&gt;      &lt;br /&gt;      assert_not_equal 0, Memoizable::CACHE.size&lt;br /&gt;      Memoizable.flush_memos&lt;br /&gt;      assert_equal 0, Memoizable::CACHE.size&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_memoize_should_keep_separate_cache_per_instance&lt;br /&gt;      other = TestObject.new&lt;br /&gt;      @obj.value, other.value = 'a', 'b'&lt;br /&gt;      &lt;br /&gt;      assert_equal 'a', @obj.value&lt;br /&gt;      assert_equal 'b', other.value&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_memoize_should_keep_separate_cache_per_method&lt;br /&gt;      @obj.value, @obj.other = 'a', 'b'&lt;br /&gt;      &lt;br /&gt;      assert_equal 'a', @obj.value&lt;br /&gt;      assert_equal 'b', @obj.other&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_memoize_should_include_arguments_in_cache_key&lt;br /&gt;      @obj.with_arguments = 'a'&lt;br /&gt;      assert_equal 'a', @obj.with_arguments(:this)&lt;br /&gt;      &lt;br /&gt;      @obj.with_arguments = 'b'&lt;br /&gt;      assert_equal 'a', @obj.with_arguments(:this)&lt;br /&gt;      assert_equal 'b', @obj.with_arguments(:that)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    class TestObject # :nodoc:&lt;br /&gt;      attr_accessor :value, :other&lt;br /&gt;      memoize :value, :other&lt;br /&gt;      &lt;br /&gt;      attr_writer :with_arguments&lt;br /&gt;      def with_arguments(*args); @with_arguments; end&lt;br /&gt;      memoize :with_arguments&lt;br /&gt;      &lt;br /&gt;      attr_writer :question&lt;br /&gt;      def question?; @question; end&lt;br /&gt;      memoize :question?&lt;br /&gt;      &lt;br /&gt;      attr_writer :exclamation&lt;br /&gt;      def exclamation!; @exclamation; end&lt;br /&gt;      memoize :exclamation!&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Tue, 01 Apr 2008 14:06:28 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/5300</guid>
      <author>remvee (Remco van 't Veer)</author>
    </item>
    <item>
      <title>Simple cacher module for Rails</title>
      <link>http://snippets.dzone.com/posts/show/5297</link>
      <description>Illustration of a simple cacher module for Rails.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;# lib/cacher.rb&lt;br /&gt;module Cacher&lt;br /&gt;  STORE = {}&lt;br /&gt;  &lt;br /&gt;  def cache(*args)&lt;br /&gt;    STORE[args.inspect] ||= yield&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def flush!&lt;br /&gt;    STORE.clear&lt;br /&gt;  end&lt;br /&gt;  module_function :flush!&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# app/models/person.rb&lt;br /&gt;class Person &lt; AR::Base&lt;br /&gt;  include Cacher&lt;br /&gt;&lt;br /&gt;  def finger&lt;br /&gt;    cache(:finger, email) do&lt;br /&gt;      `finger #{email}`&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# app/controller/application.rb&lt;br /&gt;class ApplicationController &lt; AC::Base&lt;br /&gt;  after_filter { Cacher.flush! }&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Tue, 01 Apr 2008 09:12:56 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/5297</guid>
      <author>remvee (Remco van 't Veer)</author>
    </item>
    <item>
      <title>response caching in camping</title>
      <link>http://snippets.dzone.com/posts/show/4988</link>
      <description>A basic implementation of response caching in camping.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Camping.goes :MyCampingApp&lt;br /&gt;&lt;br /&gt;module MyCampingApp&lt;br /&gt;  module Controller&lt;br /&gt;    class View &lt; R '/'&lt;br /&gt;      def get&lt;br /&gt;        cache('root') do&lt;br /&gt;          'Expensive operation!'&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def flush(id)&lt;br /&gt;    f = File.dirname(__FILE__) + "/cache/#{id}"&lt;br /&gt;    File.delete(f) if File.exists?(f)&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def cache(id, timeout = 1.hour)&lt;br /&gt;    f = File.dirname(__FILE__) + "/cache/#{id}"&lt;br /&gt;    &lt;br /&gt;    if File.exists?(f) &amp;&amp; (Time.now - File.stat(f).mtime) &lt; timeout&lt;br /&gt;      File.read(f)&lt;br /&gt;    else&lt;br /&gt;      r = yield&lt;br /&gt;      open(f, 'w'){|wr| wr.write(r)}&lt;br /&gt;      r&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def self.create&lt;br /&gt;    cache_dir = File.dirname(__FILE__) + "/cache"&lt;br /&gt;    Dir.mkdir(cache_dir) unless File.directory?(cache_dir)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 16 Jan 2008 13:27:23 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4988</guid>
      <author>remvee (Remco van 't Veer)</author>
    </item>
  </channel>
</rss>
