lib/riak/robject.rb in ripple-0.5.1 vs lib/riak/robject.rb in ripple-0.6.0

- old
+ new

@@ -10,10 +10,11 @@ # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. 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 RObject @@ -34,11 +35,11 @@ alias_attribute :vector_clock, :vclock # @return [Object] the data stored in Riak at this object's key. Varies in format by content-type, defaulting to String from the response body. attr_accessor :data - # @return [Array<Link>] an array of {Riak::Link} objects for relationships between this object and other resources + # @return [Set<Link>] an Set of {Riak::Link} objects for relationships between this object and other resources attr_accessor :links # @return [String] the ETag header from the most recent HTTP response, useful for caching and reloading attr_accessor :etag @@ -52,28 +53,31 @@ # @param [Bucket] bucket the bucket in which the object exists # @param [String] key the key at which the object resides. If nil, a key will be assigned when the object is saved. # @see Bucket#get def initialize(bucket, key=nil) @bucket, @key = bucket, key - @links, @meta = [], {} + @links, @meta = Set.new, {} + yield self if block_given? end # Load object data from an HTTP response # @param [Hash] response a response from {Riak::Client::HTTPBackend} def load(response) extract_header(response, "location", :key) {|v| URI.unescape(v.split("/").last) } extract_header(response, "content-type", :content_type) extract_header(response, "x-riak-vclock", :vclock) - extract_header(response, "link", :links) {|v| Link.parse(v) } + extract_header(response, "link", :links) {|v| Set.new(Link.parse(v)) } extract_header(response, "etag", :etag) extract_header(response, "last-modified", :last_modified) {|v| Time.httpdate(v) } @meta = response[:headers].inject({}) do |h,(k,v)| if k =~ /x-riak-meta-(.*)/ h[$1] = v end h end + @conflict = response[:code].try(:to_i) == 300 && content_type =~ /multipart\/mixed/ + @siblings = nil @data = deserialize(response[:body]) if response[:body].present? self end # HTTP header hash that will be sent along when storing the object @@ -124,12 +128,13 @@ # @option options [Boolean] :force will force a reload request if the vclock is not present, useful for reloading the object after a store (not passed in the query params) # @return [Riak::RObject] self def reload(options={}) force = options.delete(:force) return self unless @key && (@vclock || force) - response = @bucket.client.http.get([200, 304], @bucket.client.prefix, @bucket.name, @key, options, reload_headers) - load(response) if response[:code] == 200 + codes = @bucket.allow_mult ? [200,300,304] : [200,304] + response = @bucket.client.http.get(codes, @bucket.client.prefix, @bucket.name, @key, options, reload_headers) + load(response) unless response[:code] == 304 self end alias :fetch :reload @@ -139,10 +144,28 @@ return if key.blank? @bucket.client.http.delete([204,404], @bucket.client.prefix, @bucket.name, key) freeze end + # Returns sibling objects when in conflict. + # @return [Array<RObject>] an array of conflicting sibling objects for this key + # @return [self] this object when not in conflict + def siblings + return self unless conflict? + @siblings ||= Multipart.parse(data, Multipart.extract_boundary(content_type)).map do |part| + RObject.new(self.bucket, self.key) do |sibling| + sibling.load(part) + sibling.vclock = vclock + end + end + end + + # @return [true,false] Whether this object has conflicting sibling objects (divergent vclocks) + def conflict? + @conflict.present? + end + # Serializes the internal object data for sending to Riak. Differs based on the content-type. # This method is called internally when storing the object. # Automatically serialized formats: # * JSON (application/json) # * YAML (text/yaml) @@ -205,15 +228,15 @@ end else [] end end - + # Converts the object to a link suitable for linking other objects to it def to_link(tag=nil) Link.new(@bucket.client.http.path(@bucket.client.prefix, @bucket.name, @key).path, tag) end - + private def extract_header(response, name, attribute=nil) if response[:headers][name].present? value = response[:headers][name].try(:first) value = yield value if block_given?