ActiveRecord helper for serialized hashes

Posted on September 19, 2012

ActiveRecord’s serialize functionality is quite useful when you want some schema-less data within a relational database. I have found this particularly useful for two cases:

  • Design elements on a page
  • Authentication data for various (unknown) external services

However, the one thing that is missing from the built in functionality is the nice getter/setters that ActiveRecord provides. Let’s face it, hash syntax can be pretty annoying.

Here’s a helper I created to automagically generate attribute methods for keys in a serialized hash. There’s also a bit of functionality to interpret the data as markdown and return HTML. Enjoy!

module HasSerializedHash
extend ActiveSupport::Concern
def render_html(text)
@@markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML,
autolink: true)
text ||= "" #needs to be string
@@markdown.render(text).html_safe
end
module ClassMethods
@@serialized_hashes ||= {}
def serialized_hash_keys(attr)
@@serialized_hashes[attr]
end
def has_serialized_hash(attr_name, *keys)
serialize attr_name, Hash
@@serialized_hashes[attr_name] = keys
attr_accessible *keys
keys.each do |k|
#getter
define_method "#{k}" do
h = self.send(attr_name)
h[k]
end
#getter html
define_method "#{k}_html" do
h = self.send(attr_name)[k]
self.render_html(h)
end
#setter
define_method "#{k}=" do |value|
h = self.send(attr_name)
h[k] = value
end
#exists
define_method "#{k}?" do
h = self.send(attr_name)
h[k].present?
end
end
define_method "all_#{attr_name}_exist?" do
h = self.send(attr_name)
keys.all? {|s| h.key? s}
end
end
end
end

require 'spec_helper'
class TestHasSerializedHash
include ActiveRecord::AttributeMethods::Serialization
include ActiveModel::MassAssignmentSecurity
include HasSerializedHash
attr_accessor :my_hash
has_serialized_hash :my_hash, :key1, :key2
def initialize
@my_hash = {}
@hash1 = {}
end
end
describe TestHasSerializedHash do
let(:subject) { TestHasSerializedHash.new }
let(:markdown) { "This is *awesome*. www.howaboutwe.com" }
def should_render_markdown_to_html(rendered)
rendered.should include("<em>awesome</em>")
rendered.should include("http://www.howaboutwe.com")
end
it "should have a getter method for each key" do
subject.should be_respond_to(:key1)
subject.should be_respond_to(:key2)
end
it "should translate text to markdown via #render_html" do
rendered = subject.render_html(markdown)
should_render_markdown_to_html(rendered)
end
context "html getter" do
it "should have a getter html method for each key" do
subject.should be_respond_to(:key1_html)
subject.should be_respond_to(:key2_html)
end
it "should translate markdown to html" do
subject.key1 = markdown
rendered = subject.key1_html
should_render_markdown_to_html(rendered)
end
end
it "should have a setter method for each key" do
subject.should be_respond_to(:key1=)
subject.should be_respond_to(:key2=)
end
it "should have an exists method for each key" do
subject.should be_respond_to(:key1?)
subject.should be_respond_to(:key2?)
end
it "should be able to set a value via the setter" do
subject.key1 = "hello"
subject.my_hash[:key1].should == "hello"
end
it "should be able to get the value via the getter" do
subject.my_hash[:key2] = "hiii"
subject.key2.should == "hiii"
end
it "should return the correct values from exists methods" do
subject.key1 = "exist"
subject.should be_key1
subject.should_not be_key2
end
it "should have all_my_hash_exist? method" do
subject.should be_respond_to(:all_my_hash_exist?)
end
it "should return correct value for all_my_hash_exist? methods" do
subject.all_my_hash_exist?.should be_false
subject.key1 = "hello"
subject.all_my_hash_exist?.should be_false
subject.key2 = "hii"
subject.all_my_hash_exist?.should be_true
end
it ".serialized_hash_keys" do
TestHasSerializedHash.serialized_hash_keys(:my_hash).should include(:key1, :key2)
end
it "can mass assign attributes" do
TestHasSerializedHash.should_receive(:attr_accessible).with(:key1, :key2)
TestHasSerializedHash.has_serialized_hash :my_hash, :key1, :key2
end
end

Visit the Gist here.


No Replies to "ActiveRecord helper for serialized hashes"


    Got something to say?

    Some html is OK

    This site uses Akismet to reduce spam. Learn how your comment data is processed.