Created
July 20, 2012 17:08
-
-
Save bf4/3151933 to your computer and use it in GitHub Desktop.
Rails attribute serializer macro
This file contains 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
module Macros | |
module Serializer | |
# usage | |
# include Macros::Serializer | |
# e.g. a field named :metadata must be a Hash | |
# serializer :metadata, Hash | |
def self.included(host_class) | |
# field must be a symbol | |
# klass must be a constant data structure initialized with new | |
# you can also add other extensions to the data by passing in a block | |
# such used in the serialized_hash macro | |
def host_class.serializer(field,klass) | |
serialize field, klass | |
# ensure we're dealing with a klass, e.g. Hash or Set | |
# by returning the klass (and writing the attribute) when the field is nil | |
# when asking for the value of this attribute | |
define_method(field) do | |
# get current field value | |
data = read_attribute(field) | |
# nil, i.e. not set, initialize it with the desired klass | |
if data.nil? | |
data = klass.new | |
write_attribute(field, data) | |
end | |
# in all cases, return the value of the attribute | |
data | |
end | |
end | |
# usage is special case for a serialized Hash, | |
# which allows us to (optionally) limit the keys to a defined list | |
# and fail validation if extra keys are added | |
# example: | |
# serialized_hash :metadata, [:key1, :key2] | |
def host_class.serialized_hash(field,valid_keys_array=[]) | |
serializer(field, Hash) | |
# needs to be instance_eval to get access to e.g. metadata, errors | |
instance_eval do | |
define_method("valid_serialized_keys_for_#{field}") do | |
extra_keys = method(field).call.keys - valid_keys_array | |
if extra_keys.size != 0 | |
errors.add(field, "contains invalid key(s): #{extra_keys.inspect}. Valid keys are #{valid_keys_array.inspect}") | |
end | |
end | |
end | |
validate "valid_serialized_keys_for_#{field}".to_sym | |
end | |
end | |
end | |
end |
This file contains 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
# not sure how to test this in general | |
# activesupport/lib/active_support/core_ext/hash/keys.rb | |
require 'rails' | |
require 'spec_helper' | |
# require 'macros/serializer' | |
class TestSerializerClass < SomeActiveRecordBaseObject | |
include Macros::Serializer | |
serializer :field1, Set | |
serialized_hash :field2, [:key1, :key2] | |
def self.columns_hash | |
{ | |
:field1, | |
:field2 | |
} | |
end | |
end | |
describe Macros::Serializer do | |
let(:record) { TestSerializerClass.new } | |
it "default a set to Set.new" do | |
record.field1.should eql(Set.new) | |
end | |
it "can set a list of serialized attributes for a Set" do | |
record.field1 << 'foo' << 'bar' | |
record.field1.map.should =~ ['foo','bar'] | |
end | |
it "defaults a Hash to Hash.new" do | |
record.field2.should eql(Hash.new) | |
end | |
it "can set a list of serialized attributes for a Hash" do | |
record.field2[:key1] = 'v1' | |
record.field2[:key2] = 'v2' | |
record.field2.should eql({:key1 => 'v1', :key2 => 'v2'}) | |
end | |
it "invalidates the record when the serialized Hash is set with an invalid key" do | |
record.field2[:invalid] = 'nope' | |
record.should be_invalid | |
record.errors.keys.should include(:field2) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment