# Provides ActiveRecord 3.1x-friendly serialization for descendants of # ApplicationRecord. Backwards compatible with older YAML methods and # will fall back to string decoding in the worst case # # @example Using default default of {} # serialize :foo, MetasploitDataModels::Base64Serializer.new # # @example Overriding default to [] # serialize :bar, MetasploitDataModels::Base64Serializer.new(:default => []) # class MetasploitDataModels::Base64Serializer # # CONSTANTS # # The default for {#default} DEFAULT = {} # Deserializers for {#load} # 1. Base64 decoding and then unmarshalling the value. # 2. Parsing the value as YAML. # 3. The raw value. LOADERS = [ lambda { |serialized| marshaled = serialized.unpack('m').first # Load the unpacked Marshal object first Marshal.load(marshaled) }, lambda { |serialized| # Support legacy YAML encoding for existing data YAML.load(serialized) }, lambda { |serialized| # Fall back to string decoding serialized } ] # # Methods # # Creates a duplicate of default value # # @return def default @default.dup end attr_writer :default # Serializes the value by marshalling the value and then base64 encodes the marshaled value. # # @param value [Object] value to serialize # @return [String] def dump(value) # Always store data back in the Marshal format marshalled = Marshal.dump(value) base64_encoded = [ marshalled ].pack('m') base64_encoded end # @param attributes [Hash] attributes # @option attributes [Object] :default ({}) Value to use for {#default}. def initialize(attributes={}) attributes.assert_valid_keys(:default) @default = attributes.fetch(:default, DEFAULT) end # Deserializes the value by either # 1. Base64 decoding and then unmarshalling the value. # 2. Parsing the value as YAML. # 3. Returns the raw value. # # @param value [String] serialized value # @return [Object] # # @see #default def load(value) loaded = nil if value.blank? loaded = default else LOADERS.each do |loader| begin loaded = loader.call(value) rescue next else break end end end loaded end end