Skip to content

Instantly share code, notes, and snippets.

@jimeh
Created January 17, 2010 14:20
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jimeh/279392 to your computer and use it in GitHub Desktop.
Save jimeh/279392 to your computer and use it in GitHub Desktop.
A simple key/value store model for Rails using the database and memcache

Rails Setting model

This is a semi-quick key/value store I put together for a quick way to store temporary data related to what's in my database, in a reliable way. For example, I'm using it to keep track of where a hourly and a daily crontask that processes statistics left off, so it can resume only processing and summarizing new data on the next run.

Code quality most likely is not great, but it works. And I will update this gist as I update my project.

How It Works

The settings table has two columns, a key and a value column. The value column is serialized, so you can technically store almost anything in there. Memcache is also used as a caching layer to minimize database calls.

Example usage

# fetching a value
>> Setting.hello
=> nil

# setting a value, writes it to memcache, and saves or updates a database
# record is the form of {:key => "hello", :value => serialize(["world"])}
>> Setting.hello = ["world"]
=> ["hello"]

# fetching the value again, which will only read from memcache
>> Setting.hello
=> ["hello"]

# force fetch from database and update memcache
>> Setting.hello!
=> ["hello"]

# remove the hello key from memcache and the database
>> Setting.hello = nil
=> nil

# default memcache expire_in option is 6.hours, you can change it...
>> Setting.__cache_expires_in = 1.hour
=> 3600 seconds
class CreateSettings < ActiveRecord::Migration
def self.up
create_table :settings, :options => "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string :key
t.text :value
end
add_index :settings, :key, :unique => true
end
def self.down
drop_table :settings
end
end
class Setting < ActiveRecord::Base
serialize :value
class << self
attr_accessor :__cache
attr_accessor :__cache_expires_in
end
def self.method_missing(method, *args)
# init memcache if needed
@__cache = ActiveSupport::Cache::MemCacheStore.new if @__cache.nil?
@__cache_expires_in = 6.hours if @__cache_expires_in.nil?
method = method.to_s
# set mode
if method[-1,1] == "="
if args.size > 0
# key/value setup
method = method.chop
method = method.chop if method[-1,1] == "!"
value = {:value => args[0]}
@__cache.write(method, value, :expires_in => @__cache_expires_in)
setting = self.find(:first, :conditions => {:key => method})
if value[:value].nil?
setting.destroy if setting
@__cache.write(method, nil, :expires_in => 0.seconds)
else
setting = self.new if !setting
setting.key = method.to_s if setting.key.blank?
setting.value = value
if setting.save
return value[:value]
end
end
end
# get mode
else
# skip memcache?
if method[-1,1] == "!"
method = method.chop
skip_cache = true
end
if skip_cache.nil? && (result = @__cache.read(method.to_s))
return result[:value]
else
result = self.find(:first, :conditions => {:key => method})
if result
@__cache.write(method, result.value, :expires_in => @__cache_expires_in)
return result.value[:value]
end
end
end
return nil
end
end
@Sovietaced
Copy link

Very nice, thank you for sharing.

@johannesE
Copy link

Awesome, thank you.

@johannesE
Copy link

johannesE commented Apr 26, 2016

My version (a little cleaner and without the cache):
` def self.method_missing(method, *args)

method = method.to_s

# set mode
if method[-1, 1] == "=" && args.size > 0

  method    = method.chop
  value     = args[0]
  system_parameter = self.find_by key: method

  if value.empty?
    return system_parameter.destroy if system_parameter
  else
    system_parameter       = self.new unless system_parameter
    system_parameter.key   = method.to_s if system_parameter.key.blank?
    system_parameter.value = value

    return self if system_parameter.save
  end

  # get mode
else
  result = self.find_by key: method
  if result
    return result.value
  end
end

nil

end`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment