Created
August 18, 2014 21:03
-
-
Save wrightling/59761c0eb2c7f544eba6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CreateKeyVals < ActiveRecord::Migration | |
def change | |
create_table :key_vals do |t| | |
t.string :key | |
t.text :value | |
t.string :value_type | |
t.timestamps | |
end | |
add_index :key_vals, :key, unique: true | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class KeyVal < ActiveRecord::Base | |
def self.[] key | |
Rails.cache.fetch(key, expires_in: 5.minutes) do | |
KeyVal.find_by!(key: key).convert | |
end | |
end | |
def self.[]= key, value | |
keyval = KeyVal.find_or_initialize_by key: key | |
keyval.update value: value, value_type: discern_type(value) | |
Rails.cache.clear key if Rails.cache.exist? key | |
end | |
def convert | |
case value_type | |
when 'integer' | |
value.to_i | |
when 'float' | |
value.to_f | |
when 'boolean' | |
value == 'true' | |
else | |
value | |
end | |
end | |
private | |
def self.discern_type value | |
case value | |
when Integer | |
'integer' | |
when Float | |
'float' | |
when TrueClass, FalseClass | |
'boolean' | |
else | |
'string' | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rails_helper' | |
describe KeyVal, focus: true do | |
before :each do | |
Rails.cache.clear | |
end | |
context "storing values" do | |
it "is stored with value_type=string for strings" do | |
KeyVal[:key] = 'some string' | |
expect(KeyVal.first.value_type).to eql 'string' | |
end | |
it "is stored with value_type=integer for integers" do | |
KeyVal[:key] = 42 | |
expect(KeyVal.first.value_type).to eql 'integer' | |
end | |
it "is stored with value_type=float for floats" do | |
KeyVal[:key] = 4.2 | |
expect(KeyVal.first.value_type).to eql 'float' | |
end | |
it "is stored with value_type=boolean for true or false" do | |
KeyVal[:key] = true | |
expect(KeyVal.first.value_type).to eql 'boolean' | |
end | |
it "overwrites the value if a key is used twice" do | |
KeyVal[:key] = 42 | |
KeyVal[:key] = 'forty-two' | |
expect(KeyVal.first.value).to eql 'forty-two' | |
end | |
end | |
describe "retrieving stored values" do | |
it "comes back as an instance of class String for strings" do | |
KeyVal[:key] = 'some string' | |
expect(KeyVal[:key]).to be_a(String) | |
end | |
it "comes back as an instance of class Integer for integers" do | |
KeyVal[:key] = 42 | |
expect(KeyVal[:key]).to be_a(Integer) | |
end | |
it "comes back as an instance of class Float for floats" do | |
KeyVal[:key] = 4.2 | |
expect(KeyVal[:key]).to be_a(Float) | |
end | |
it "comes back as an instance of class FalseClass for a false boolean" do | |
KeyVal[:key] = false | |
expect(KeyVal[:key]).to be_a(false.class) | |
end | |
it "raises an error if the key is not found" do | |
expect { KeyVal[:key] }.to raise_error(ActiveRecord::RecordNotFound) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A few thoughts:
I spent an hour or so putting this together. I wanted a very simple key-value store that would work with any relational database. The values are stored as text fields to avoid size limitations.
Intended Use:
As you can see, some rudimentary type checking (something we almost always want to avoid in a dynamic, duck-typing-prone language like Ruby) lets us determine whether incoming values are integers, floats, booleans, or default them to string, and lets us return values having already been cast to their appropriate type.
The caching is super basic low-level rails caching.