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?