Build Status

db_memoize

library to cache (memoize) method return values in database

Note: when updating from version 0.1 to 0.2 you need to run a migration on the existing table, see below.

Usage in ActiveRecord models:

Example Class:

class Letter < ActiveRecord::Base
  include DbMemoize::Model

  def hello(name = 'John')
    "Hello #{name}!"
  end
  db_memoize :hello

  def bye
    'Best regards'
  end
  db_memoize :bye
end

Get Values

record = Letter.first
record.hello
=> 'Hello John!'
record.hello
=> 'Hello John!'

will call the original method only once. Consecutive calls will return a cached value.

If the method takes arguments..

record.hello('Maria')
record.hello('John')

a cached value will be created for every set of arguments.

Clear Values

To clear cached values for a single method

record.unmemoize(:hello)

To clear all cached values of one record

record.unmemoize

To clear cached values of given records for a single method

Letter.unmemoize([letter1, letter2], :hello)

To clear all cached values of given records

Letter.unmemoize([letter1, letter2])

Instead of ActiveRecord instances it's sufficient to pass in the ids of the records, too

Letter.unmemoize([23,24])

Gotchas

As the cached values themselves are writtten to the database, are ActiveRecord records (of type DbMemoize::Value) and are reqistered as an association you can access all of the cached values of an object like this:

record.memoized_values

This means you can also very easily perform eager loading on them:

Letter.includes(:memoized_values).all

DbMemoize by default will write log output to STDOUT. You can change this by setting another logger like so:

DbMemoize.logger = your_logger

Rake Tasks

To warmup your cache you can pre-generate cached values via a rake task like this (only works for methods not depending on arguments)

bundle exec rake db_memoize:warmup class=Letter methods=hello,bye

Similarly you can wipe all cached values for a given class

bundle exec rake db_memoize:clear class=Letter

Setup

To create the required DB tables add a migration like this:

class CreateMemoizedValues < ActiveRecord::Migration
  def up
    require 'db_memoize/migrations'
    DbMemoize::Migrations.create_tables(self)
  end

  def down
    drop_table :memoized_values
  end
end

Testing

Note that db_memoize needs Postgres. To set up the database needed to run tests, this is what you can do:

# sudo su postgresql

   # createuser >>yourusername<< # createdb -O >>yourusername<< db_memoize_test

Updating from 0.1 -> 0.2

You need to run a migration, like the following:

class FixDbMemoizeTable < ActiveRecord::Migration
  def up
    # After updating db_memoize to 0.2 we need to update some structures, depending
    # on whether or not db_memoize 0.1 or 0.2 was creating the database tables.
    execute "    ALTER TABLE memoized_values\n      ALTER COLUMN entity_table_name SET NOT NULL,\n      ALTER COLUMN entity_id SET NOT NULL,\n      ALTER COLUMN method_name SET NOT NULL,\n      ALTER COLUMN created_at SET NOT NULL;\n    DROP INDEX IF EXISTS index_memoized_values_on_entity_table_name_and_entity_id;\n    CREATE INDEX IF NOT EXISTS index_memoized_values_on_entity_id_and_entity_table_name \n      ON memoized_values(entity_id, entity_table_name);\n    CREATE INDEX IF NOT EXISTS memoized_attributes_idx \n      ON memoized_values (((arguments_hash IS NULL)))\n    SQL\n  end\n\n  def down\n    execute <<-SQL\n      DROP INDEX memoized_attributes_idx;\n      DROP INDEX index_memoized_values_on_entity_id_and_entity_table_name;\n    SQL\n  end\nend\n"

Have fun!