require 'riak' require 'set' module Riak # Parent class of all object types supported by ripple. {Riak::RObject} represents # the data and metadata stored in a bucket/key pair in the Riak database. class RiakContent include Util::Translation include Util::MessageCode # @return [Key] the key in which this RiakContent is stored. attr_accessor :key # @return [String] the data stored in Riak at this object's key. Varies in format by content-type. attr_accessor :value alias_attribute :data, :value # @return [String] the MIME content type of the object attr_accessor :content_type # @return [String] the charset of the object attr_accessor :charset # @return [String] the content encoding of the object attr_accessor :content_encoding # @return [String] the vtag of the object attr_accessor :vtag # @return [Set] an Set of {Riak::Link} objects for relationships between this object and other resources attr_accessor :links # @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading attr_accessor :last_mod alias_attribute :last_modified, :last_mod # @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading attr_accessor :last_mod_usecs alias_attribute :last_modified_usecs, :last_mod_usecs # @return [Hash] a hash of any user-supplied metadata, consisting of a key/value pair attr_accessor :usermeta alias_attribute :meta, :usermeta # Create a new riak_content object manually # @param [Riak::Key] key Key instance that owns this RiakContent (really, you should use the Key to get this) # @param [Hash] contents Any contents to initialize this instance with # @see Key#content # @see RiakContent#load def initialize(key, contents={}) @key = key unless key.nil? @links = Hash.new{|k,v| k[v] = []} @_links = [] @usermeta = {} load(contents) unless contents.empty? yield self if block_given? end # Load information for the content from the response object, Riak::RpbContent. # # @param [RpbContent/Hash] contents an RpbContent object or a Hash. # @return [RiakContent] self def load(contents) if contents.is_a?(Riak::RpbContent) or contents.is_a?(Hash) @content_type = contents[:content_type] unless contents[:content_type].blank? @charset = contents[:charset] unless contents[:charset].blank? @content_encoding = contents[:content_encoding] unless contents[:content_encoding].blank? @vtag = contents[:vtag] unless contents[:vtag].blank? self.links = contents[:links] unless contents[:links].blank? @last_mod = contents[:last_mod] @last_mod_usecs = contents[:last_mod_usecs] self.usermeta = contents[:usermeta] unless contents[:usermeta].blank? unless contents[:value].blank? case @content_type when /json/ @value = ActiveSupport::JSON.decode(contents[:value]) when /octet/ @value = Marshal.load(contents[:value]) else @value = contents[:value] end end return(self) end raise ArgumentError, t("riak_content_type") end # Save the RiakContent instance in riak. # @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response # @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response # @option options [true/false] return_body whether or not to have riak return the key, once saved. default = true def save(options={}) begin save!(options) rescue FailedRequest return(false) end return(true) end # Save the RiakContent instance in riak. Raise/do not rescue on failure. # @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response # @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response # @option options [true/false] return_body whether or not to have riak return the key, once saved. default = true def save!(options={}) options[:content] = self return(true) if @key.save(options) return(false) # Create and raise Error message for this? Extend "Failed Request"? end # Internalizes a link to a Key, which will be saved on next call to... uh, save # @param [Hash] tags name of the tag, pointing to a Key instance, or an array ["bucket", "key"] # @return [Hash] links that this RiakContent points to def link_key(tags) raise TypeError.new t('invalid_tag') unless tag.is_a?(Hash) tags.each do |tag, link| case link when Array bucket ||= link[0] key ||= link[1] raise TypeError.new t('invalid_tag') if bucket.nil? or key.nil? get_link ||= @key.get_linked(bucket, key, {:safely => true}) raise RuntimeError.new t('invalid_key') if get_link.nil? @links[tag.to_s] << get_link when Riak::Key @links[tag.to_s] << link else raise TypeError.new t('invalid_tag') end end # tags.each do |tag, link| end alias :link :link_key # Set the links to other Key in riak. # @param [RpbGetResp, RpbPutResp] contains the tag/bucket/key of a given link # @return [Set] links that this RiakContent points to def links=(pb_links) @links.clear pb_links.each do |pb_link| @links[pb_link.tag] << @key.get_linked(pb_link.bucket, pb_link.key, {:safely => true}) end return(@links) end # @return [Riak::RpbContent] An instance of a RpbContent, suitable for protobuf exchange def to_pb raise TypeError.new t('value_empty') if @value.nil? rpb_content = Riak::RpbContent.new rpb_links = [] @links.each do |tag, links| links.each do |link| pb_link = link.to_pb_link pb_link.tag = tag rpb_links << pb_link end end usermeta = [] @usermeta.each do |key,value| pb_pair = Riak::RpbPair.new pb_pair[:key] = key pb_pair[:value] = value usermeta << pb_pair end catch(:redo) do case @content_type when /octet/ rpb_content.value = Marshal.dump(@value) when /json/ rpb_content.value = ActiveSupport::JSON.encode(@value) when "", nil @content_type = "application/json" redo else rpb_content.value = @value.to_s end end rpb_content.content_type = @content_type unless @content_type.nil? rpb_content.charset = @charset unless @charset.nil? # || @charset.empty? rpb_content.content_encoding = @content_encoding unless @content_encoding.nil? # || @content_encoding.empty? rpb_content.vtag = @vtag unless @vtag.nil? rpb_content.links = rpb_links unless rpb_links.nil? rpb_content.usermeta = usermeta unless usermeta.nil? return(rpb_content) end # @return [String] A representation suitable for IRB and debugging output def inspect "#<#Riak::RiakContent " + [ (@value.nil?) ? nil : "value=#{@value.inspect}", (@content_type.nil?) ? nil : "content_type=#{@content_type.inspect}", (@charset.nil?) ? nil : "charset=#{@charset.inspect}", (@content_encoding.nil?) ? nil : "content_encoding=#{@content_encoding.inspect}", (@vtag.nil?) ? nil : "vtag=#{@vtag.inspect}", (@links.nil?) ? nil : "links=#{@_links.inspect}", (@last_mod.nil?) ? nil : "last_mod=#{last_mod.inspect}", (@last_mod_usecs.nil?) ? nil : "last_mod_usecs=#{last_mod_usecs.inspect}", (@usermeta.nil?) ? nil : "usermeta=#{@usermeta.inspect}" ].compact.join(", ") + ">" end private end # class RiakContent end # module Riak