riak-client 0.8.3 → 0.9.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,7 +22,7 @@ module Riak
22
22
  # @return [String] the relationship tag (or "rel") of the other resource to this one
23
23
  attr_accessor :tag
24
24
  alias_method :rel, :tag
25
- alias_method :rel=, :tag=
25
+ alias_method :'rel=', :'tag='
26
26
 
27
27
  # @return [String] the bucket of the related resource
28
28
  attr_accessor :bucket
@@ -30,6 +30,10 @@ module Riak
30
30
  # @return [String] the key of the related resource
31
31
  attr_accessor :key
32
32
 
33
+ %w{bucket key}.each do |m|
34
+ class_eval %{ def #{m}=(value); @url = nil; @#{m} = value; end }
35
+ end
36
+
33
37
  # @param [String] header_string the string value of the Link: HTTP header from a Riak response
34
38
  # @return [Array<Link>] an array of Riak::Link structs parsed from the header
35
39
  def self.parse(header_string)
@@ -56,12 +60,13 @@ module Riak
56
60
 
57
61
  # @return [String] the URL (relative or absolute) of the related resource
58
62
  def url
59
- "/riak/#{escape(bucket)}" + (key.blank? ? "" : "/#{escape(key)}")
63
+ @url ||= "/riak/#{escape(bucket)}" + (key.blank? ? "" : "/#{escape(key)}")
60
64
  end
61
65
 
62
66
  def url=(value)
63
- @bucket = CGI.unescape($1) if value =~ %r{^/[^/]+/([^/]+)/?}
64
- @key = CGI.unescape($1) if value =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
67
+ @url = value
68
+ @bucket = unescape($1) if value =~ %r{^/[^/]+/([^/]+)/?}
69
+ @key = unescape($1) if value =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
65
70
  end
66
71
 
67
72
  def inspect; to_s; end
@@ -18,6 +18,8 @@ en:
18
18
  content_type_undefined: "content_type is not defined!"
19
19
  empty_map_reduce_query: "Specify one or more query phases to your MapReduce."
20
20
  failed_request: "Expected %{expected} from Riak but received %{code}. %{body}"
21
+ filter_needs_block: "Filter %{filter} expects a block."
22
+ filter_arity_mismatch: "Filter %{filter} expects %{expected} arguments but %{received} were given."
21
23
  hash_type: "invalid argument %{hash} is not a Hash"
22
24
  http_configuration: "The %{backend} HTTP backend cannot be used. Please check its requirements."
23
25
  hostname_invalid: "host must be a valid hostname"
@@ -19,7 +19,12 @@ module Riak
19
19
  include Util::Translation
20
20
  include Util::Escape
21
21
 
22
- # @return [Array<[bucket,key]>,String] The bucket/keys for input to the job, or the bucket (all keys).
22
+ autoload :Phase, "riak/map_reduce/phase"
23
+ autoload :FilterBuilder, "riak/map_reduce/filter_builder"
24
+
25
+ # @return [Array<[bucket,key]>,String,Hash<:bucket,:filters>] The
26
+ # bucket/keys for input to the job, or the bucket (all
27
+ # keys), or a hash containing the bucket and key-filters.
23
28
  # @see #add
24
29
  attr_accessor :inputs
25
30
 
@@ -52,9 +57,17 @@ module Riak
52
57
  # @param [String,Bucket] bucket the bucket of the object
53
58
  # @param [String] key the key of the object
54
59
  # @param [String] keydata extra data to pass along with the object to the job
60
+ # @overload add(bucket, filters)
61
+ # Run the job across all keys in the bucket, with the given
62
+ # key-filters. This will replace any other inputs previously
63
+ # added. (Requires Riak 0.14)
64
+ # @param [String,Bucket] bucket the bucket to filter keys from
65
+ # @param [Array<Array>] filters a list of key-filters to apply
66
+ # to the key list
55
67
  # @return [MapReduce] self
56
68
  def add(*params)
57
- params = params.dup.flatten
69
+ params = params.dup
70
+ params = params.first if Array === params.first
58
71
  case params.size
59
72
  when 1
60
73
  p = params.first
@@ -69,14 +82,28 @@ module Riak
69
82
  when 2..3
70
83
  bucket = params.shift
71
84
  bucket = bucket.name if Bucket === bucket
72
- key = params.shift
73
- @inputs << params.unshift(escape(key)).unshift(escape(bucket))
85
+ if Array === params.first
86
+ @inputs = {:bucket => escape(bucket), :filters => params.first }
87
+ else
88
+ key = params.shift
89
+ @inputs << params.unshift(escape(key)).unshift(escape(bucket))
90
+ end
74
91
  end
75
92
  self
76
93
  end
77
94
  alias :<< :add
78
95
  alias :include :add
79
96
 
97
+ # Adds a bucket and key-filters built by the given
98
+ # block. Equivalent to #add with a list of filters.
99
+ # @param [String] bucket the bucket to apply key-filters to
100
+ # @yield [] builder block - instance_eval'ed into a FilterBuilder
101
+ # @return [MapReduce] self
102
+ # @see MapReduce#add
103
+ def filter(bucket, &block)
104
+ add(bucket, FilterBuilder.new(&block).to_a)
105
+ end
106
+
80
107
  # Add a map phase to the job.
81
108
  # @overload map(function)
82
109
  # @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module,function] pair
@@ -144,16 +171,13 @@ module Riak
144
171
  end
145
172
 
146
173
  # Executes this map-reduce job.
147
- # @return [Array<Array>] similar to link-walking, each element is an array of results from a phase where "keep" is true. If there is only one "keep" phase, only the results from that phase will be returned.
148
- def run
174
+ # @return [Array<Array>] similar to link-walking, each element is
175
+ # an array of results from a phase where "keep" is true. If there
176
+ # is only one "keep" phase, only the results from that phase will
177
+ # be returned.
178
+ def run(&block)
149
179
  raise MapReduceError.new(t("empty_map_reduce_query")) if @query.empty?
150
- response = @client.http.post(200, @client.mapred, to_json, {"Content-Type" => "application/json", "Accept" => "application/json"})
151
- begin
152
- raise unless response[:headers]['content-type'].include?('application/json')
153
- JSON.parse(response[:body])
154
- rescue
155
- response
156
- end
180
+ @client.backend.mapred(self, &block)
157
181
  rescue FailedRequest => fr
158
182
  if fr.code == 500 && fr.headers['content-type'].include?("application/json")
159
183
  raise MapReduceError.new(fr.body)
@@ -161,95 +185,5 @@ module Riak
161
185
  raise fr
162
186
  end
163
187
  end
164
-
165
- # Represents an individual phase in a map-reduce pipeline. Generally you'll want to call
166
- # methods of MapReduce instead of using this directly.
167
- class Phase
168
- include Util::Translation
169
- # @return [Symbol] the type of phase - :map, :reduce, or :link
170
- attr_accessor :type
171
-
172
- # @return [String, Array<String, String>, Hash, WalkSpec] For :map and :reduce types, the Javascript function to run (as a string or hash with bucket/key), or the module + function in Erlang to run. For a :link type, a {Riak::WalkSpec} or an equivalent hash.
173
- attr_accessor :function
174
-
175
- # @return [String] the language of the phase's function - "javascript" or "erlang". Meaningless for :link type phases.
176
- attr_accessor :language
177
-
178
- # @return [Boolean] whether results of this phase will be returned
179
- attr_accessor :keep
180
-
181
- # @return [Array] any extra static arguments to pass to the phase
182
- attr_accessor :arg
183
-
184
- # Creates a phase in the map-reduce pipeline
185
- # @param [Hash] options options for the phase
186
- # @option options [Symbol] :type one of :map, :reduce, :link
187
- # @option options [String] :language ("javascript") "erlang" or "javascript"
188
- # @option options [String, Array, Hash] :function In the case of Javascript, a literal function in a string, or a hash with :bucket and :key. In the case of Erlang, an Array of [module, function]. For a :link phase, a hash including any of :bucket, :tag or a WalkSpec.
189
- # @option options [Boolean] :keep (false) whether to return the results of this phase
190
- # @option options [Array] :arg (nil) any extra static arguments to pass to the phase
191
- def initialize(options={})
192
- self.type = options[:type]
193
- self.language = options[:language] || "javascript"
194
- self.function = options[:function]
195
- self.keep = options[:keep] || false
196
- self.arg = options[:arg]
197
- end
198
-
199
- def type=(value)
200
- raise ArgumentError, t("invalid_phase_type") unless value.to_s =~ /^(map|reduce|link)$/i
201
- @type = value.to_s.downcase.to_sym
202
- end
203
-
204
- def function=(value)
205
- case value
206
- when Array
207
- raise ArgumentError, t("module_function_pair_required") unless value.size == 2
208
- @language = "erlang"
209
- when Hash
210
- raise ArgumentError, t("stored_function_invalid") unless type == :link || value.has_key?(:bucket) && value.has_key?(:key)
211
- @language = "javascript"
212
- when String
213
- @language = "javascript"
214
- when WalkSpec
215
- raise ArgumentError, t("walk_spec_invalid_unless_link") unless type == :link
216
- else
217
- raise ArgumentError, t("invalid_function_value", :value => value.inspect)
218
- end
219
- @function = value
220
- end
221
-
222
- # Converts the phase to JSON for use while invoking a job.
223
- # @return [String] a JSON representation of the phase
224
- def to_json(*a)
225
- as_json.to_json(*a)
226
- end
227
-
228
- # Converts the phase to its JSON-compatible representation for job invocation.
229
- # @return [Hash] a Hash-equivalent of the phase
230
- def as_json(options=nil)
231
- obj = case type
232
- when :map, :reduce
233
- defaults = {"language" => language, "keep" => keep}
234
- case function
235
- when Hash
236
- defaults.merge(function)
237
- when String
238
- if function =~ /\s*function/
239
- defaults.merge("source" => function)
240
- else
241
- defaults.merge("name" => function)
242
- end
243
- when Array
244
- defaults.merge("module" => function[0], "function" => function[1])
245
- end
246
- when :link
247
- spec = WalkSpec.normalize(function).first
248
- {"bucket" => spec.bucket, "tag" => spec.tag, "keep" => spec.keep || keep}
249
- end
250
- obj["arg"] = arg if arg
251
- { type => obj }
252
- end
253
- end
254
188
  end
255
189
  end
@@ -0,0 +1,106 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'riak'
15
+
16
+ module Riak
17
+ class MapReduce
18
+ # Builds key-filter lists for MapReduce inputs in a DSL-like fashion.
19
+ class FilterBuilder
20
+ include Util::Translation
21
+
22
+ # Known filters available in riak_kv_mapred_filters, mapped to
23
+ # their arities. These are turned into instance methods.
24
+ # Example:
25
+ #
26
+ # FilterBuilder.new do
27
+ # string_to_int
28
+ # less_than 50
29
+ # end
30
+ FILTERS = {
31
+ :int_to_string => 0,
32
+ :string_to_int => 0,
33
+ :float_to_string => 0,
34
+ :string_to_float => 0,
35
+ :to_upper => 0,
36
+ :to_lower => 0,
37
+ :tokenize => 2,
38
+ :urldecode => 0,
39
+ :greater_than => 1,
40
+ :less_than => 1,
41
+ :greater_than_eq => 1,
42
+ :less_than_eq => 1,
43
+ :between => [2,3],
44
+ :matches => 1,
45
+ :neq => 1,
46
+ :eq => 1,
47
+ :set_member => 1,
48
+ :similar_to => 2,
49
+ :starts_with => 1,
50
+ :ends_with => 1
51
+ }
52
+
53
+ # Available logical operations for joining filter chains. These
54
+ # are turned into instance methods with leading underscores,
55
+ # with aliases to uppercase versions.
56
+ # Example:
57
+ #
58
+ # FilterBuilder.new do
59
+ # string_to_int
60
+ # AND do
61
+ # greater_than_eq 50
62
+ # neq 100
63
+ # end
64
+ # end
65
+ LOGICAL_OPERATIONS = %w{and or not}
66
+
67
+ FILTERS.each do |f,arity|
68
+ class_eval <<-CODE
69
+ def #{f}(*args)
70
+ raise ArgumentError.new(t("filter_arity_mismatch", :filter => :#{f}, :expected => #{arity.inspect}, :received => args.size)) unless Array(#{arity.inspect}).include?(args.size)
71
+ @filters << ([:#{f}] + args)
72
+ end
73
+ CODE
74
+ end
75
+
76
+ LOGICAL_OPERATIONS.each do |op|
77
+ class_eval <<-CODE
78
+ def _#{op}(&block)
79
+ raise ArgumentError.new(t('filter_needs_block', :filter => '#{op}')) unless block_given?
80
+ @filters << [:#{op}, self.class.new(&block).to_a]
81
+ end
82
+ alias :#{op.to_s.upcase} :_#{op}
83
+ CODE
84
+ end
85
+
86
+ # Creates a new FilterBuilder. Pass a block that will be
87
+ # instance_eval'ed to construct the sequence of filters.
88
+ def initialize(&block)
89
+ @filters = []
90
+ instance_eval(&block) if block_given?
91
+ end
92
+
93
+ # Wraps multi-step filters for use inside logical
94
+ # operations. Does not correspond to an actual filter.
95
+ def sequence(&block)
96
+ @filters << self.class.new(&block).to_a
97
+ end
98
+ alias :seq :sequence
99
+
100
+ # @return A list of filters for handing to the MapReduce inputs.
101
+ def to_a
102
+ @filters
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,108 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'riak'
15
+
16
+ module Riak
17
+ class MapReduce
18
+ # Represents an individual phase in a map-reduce pipeline. Generally you'll want to call
19
+ # methods of MapReduce instead of using this directly.
20
+ class Phase
21
+ include Util::Translation
22
+ # @return [Symbol] the type of phase - :map, :reduce, or :link
23
+ attr_accessor :type
24
+
25
+ # @return [String, Array<String, String>, Hash, WalkSpec] For :map and :reduce types, the Javascript function to run (as a string or hash with bucket/key), or the module + function in Erlang to run. For a :link type, a {Riak::WalkSpec} or an equivalent hash.
26
+ attr_accessor :function
27
+
28
+ # @return [String] the language of the phase's function - "javascript" or "erlang". Meaningless for :link type phases.
29
+ attr_accessor :language
30
+
31
+ # @return [Boolean] whether results of this phase will be returned
32
+ attr_accessor :keep
33
+
34
+ # @return [Array] any extra static arguments to pass to the phase
35
+ attr_accessor :arg
36
+
37
+ # Creates a phase in the map-reduce pipeline
38
+ # @param [Hash] options options for the phase
39
+ # @option options [Symbol] :type one of :map, :reduce, :link
40
+ # @option options [String] :language ("javascript") "erlang" or "javascript"
41
+ # @option options [String, Array, Hash] :function In the case of Javascript, a literal function in a string, or a hash with :bucket and :key. In the case of Erlang, an Array of [module, function]. For a :link phase, a hash including any of :bucket, :tag or a WalkSpec.
42
+ # @option options [Boolean] :keep (false) whether to return the results of this phase
43
+ # @option options [Array] :arg (nil) any extra static arguments to pass to the phase
44
+ def initialize(options={})
45
+ self.type = options[:type]
46
+ self.language = options[:language] || "javascript"
47
+ self.function = options[:function]
48
+ self.keep = options[:keep] || false
49
+ self.arg = options[:arg]
50
+ end
51
+
52
+ def type=(value)
53
+ raise ArgumentError, t("invalid_phase_type") unless value.to_s =~ /^(map|reduce|link)$/i
54
+ @type = value.to_s.downcase.to_sym
55
+ end
56
+
57
+ def function=(value)
58
+ case value
59
+ when Array
60
+ raise ArgumentError, t("module_function_pair_required") unless value.size == 2
61
+ @language = "erlang"
62
+ when Hash
63
+ raise ArgumentError, t("stored_function_invalid") unless type == :link || value.has_key?(:bucket) && value.has_key?(:key)
64
+ @language = "javascript"
65
+ when String
66
+ @language = "javascript"
67
+ when WalkSpec
68
+ raise ArgumentError, t("walk_spec_invalid_unless_link") unless type == :link
69
+ else
70
+ raise ArgumentError, t("invalid_function_value", :value => value.inspect)
71
+ end
72
+ @function = value
73
+ end
74
+
75
+ # Converts the phase to JSON for use while invoking a job.
76
+ # @return [String] a JSON representation of the phase
77
+ def to_json(*a)
78
+ as_json.to_json(*a)
79
+ end
80
+
81
+ # Converts the phase to its JSON-compatible representation for job invocation.
82
+ # @return [Hash] a Hash-equivalent of the phase
83
+ def as_json(options=nil)
84
+ obj = case type
85
+ when :map, :reduce
86
+ defaults = {"language" => language, "keep" => keep}
87
+ case function
88
+ when Hash
89
+ defaults.merge(function)
90
+ when String
91
+ if function =~ /\s*function/
92
+ defaults.merge("source" => function)
93
+ else
94
+ defaults.merge("name" => function)
95
+ end
96
+ when Array
97
+ defaults.merge("module" => function[0], "function" => function[1])
98
+ end
99
+ when :link
100
+ spec = WalkSpec.normalize(function).first
101
+ {"bucket" => spec.bucket, "tag" => spec.tag, "keep" => spec.keep || keep}
102
+ end
103
+ obj["arg"] = arg if arg
104
+ { type => obj }
105
+ end
106
+ end
107
+ end
108
+ end
@@ -13,14 +13,15 @@
13
13
  # limitations under the License.
14
14
  require 'riak'
15
15
  require 'set'
16
+ require 'time'
16
17
 
17
18
  module Riak
18
19
  # Represents the data and metadata stored in a bucket/key pair in
19
- # the Riak database, the base unit of data manipulation.
20
+ # the Riak database, the base unit of data manipulation.
20
21
  class RObject
21
- include Util
22
22
  include Util::Translation
23
23
  include Util::Escape
24
+ extend Util::Escape
24
25
 
25
26
  # @return [Bucket] the bucket in which this object is contained
26
27
  attr_accessor :bucket
@@ -57,7 +58,7 @@ module Riak
57
58
  # @return [Array<RObject>] An array of RObject instances
58
59
  def self.load_from_mapreduce(client, response)
59
60
  response.map do |item|
60
- RObject.new(client[CGI.unescape(item['bucket'])], CGI.unescape(item['key'])).load_from_mapreduce(item)
61
+ RObject.new(client[unescape(item['bucket'])], unescape(item['key'])).load_from_mapreduce(item)
61
62
  end
62
63
  end
63
64
 
@@ -72,27 +73,6 @@ module Riak
72
73
  yield self if block_given?
73
74
  end
74
75
 
75
- # Load object data from an HTTP response
76
- # @param [Hash] response a response from {Riak::Client::HTTPBackend}
77
- def load(response)
78
- extract_header(response, "location", :key) {|v| URI.unescape(v.split("/").last) }
79
- extract_header(response, "content-type", :content_type)
80
- extract_header(response, "x-riak-vclock", :vclock)
81
- extract_header(response, "link", :links) {|v| Set.new(Link.parse(v)) }
82
- extract_header(response, "etag", :etag)
83
- extract_header(response, "last-modified", :last_modified) {|v| Time.httpdate(v) }
84
- @meta = response[:headers].inject({}) do |h,(k,v)|
85
- if k =~ /x-riak-meta-(.*)/
86
- h[$1] = v
87
- end
88
- h
89
- end
90
- @conflict = (response[:code].to_i == 300 && content_type =~ /multipart\/mixed/) rescue false
91
- @siblings = nil
92
- self.raw_data = response[:body] if response[:body].present?
93
- self
94
- end
95
-
96
76
  # Load object data from a map/reduce response item.
97
77
  # This method is used by RObject::load_from_mapreduce to instantiate the necessary
98
78
  # objects.
@@ -115,7 +95,7 @@ module Riak
115
95
  self
116
96
  end
117
97
 
118
- # @return [Object] the unmarshaled form of {#raw_data} stored in riak at this object's key
98
+ # @return [Object] the unmarshaled form of {#raw_data} stored in riak at this object's key
119
99
  def data
120
100
  if @raw_data && !@data
121
101
  @data = deserialize(@raw_data)
@@ -147,37 +127,6 @@ module Riak
147
127
  @raw_data = new_raw_data
148
128
  end
149
129
 
150
- # HTTP header hash that will be sent along when storing the object
151
- # @return [Hash] hash of HTTP Headers
152
- def store_headers
153
- {}.tap do |hash|
154
- hash["Content-Type"] = @content_type
155
- hash["X-Riak-Vclock"] = @vclock if @vclock
156
- if @prevent_stale_writes && @etag.present?
157
- hash["If-Match"] = @etag
158
- elsif @prevent_stale_writes
159
- hash["If-None-Match"] = "*"
160
- end
161
- unless @links.blank?
162
- hash["Link"] = @links.reject {|l| l.rel == "up" }.map(&:to_s).join(", ")
163
- end
164
- unless @meta.blank?
165
- @meta.each do |k,v|
166
- hash["X-Riak-Meta-#{k}"] = v.to_s
167
- end
168
- end
169
- end
170
- end
171
-
172
- # HTTP header hash that will be sent along when reloading the object
173
- # @return [Hash] hash of HTTP headers
174
- def reload_headers
175
- {}.tap do |h|
176
- h['If-None-Match'] = @etag if @etag.present?
177
- h['If-Modified-Since'] = @last_modified.httpdate if @last_modified.present?
178
- end
179
- end
180
-
181
130
  # Store the object in Riak
182
131
  # @param [Hash] options query parameters
183
132
  # @option options [Fixnum] :r the "r" parameter (Read quorum for the implicit read performed when validating the store operation)
@@ -189,23 +138,22 @@ module Riak
189
138
  def store(options={})
190
139
  raise ArgumentError, t("content_type_undefined") unless @content_type.present?
191
140
  params = {:returnbody => true}.merge(options)
192
- method, codes, path = @key.present? ? [:put, [200,204,300], "#{escape(@bucket.name)}/#{escape(@key)}"] : [:post, 201, escape(@bucket.name)]
193
- response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, raw_data, store_headers)
194
- load(response)
141
+ @bucket.client.backend.store_object(self, params[:returnbody], params[:w], params[:dw])
142
+ self
195
143
  end
196
144
 
197
145
  # Reload the object from Riak. Will use conditional GETs when possible.
198
146
  # @param [Hash] options query parameters
199
147
  # @option options [Fixnum] :r the "r" parameter (Read quorum)
200
- # @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)
148
+ # @option options [Boolean] :force will force a reload request if
149
+ # the vclock is not present, useful for reloading the object after
150
+ # a store (not passed in the query params)
201
151
  # @return [Riak::RObject] self
202
152
  def reload(options={})
203
153
  force = options.delete(:force)
204
154
  return self unless @key && (@vclock || force)
205
- codes = @bucket.allow_mult ? [200,300,304] : [200,304]
206
- response = @bucket.client.http.get(codes, @bucket.client.prefix, escape(@bucket.name), escape(@key), options, reload_headers)
207
- load(response) unless response[:code] == 304
208
- self
155
+ self.etag = self.last_modified = nil if force
156
+ bucket.client.backend.reload_object(self, options[:r])
209
157
  end
210
158
 
211
159
  alias :fetch :reload
@@ -218,17 +166,14 @@ module Riak
218
166
  freeze
219
167
  end
220
168
 
169
+ attr_writer :siblings, :conflict
170
+
221
171
  # Returns sibling objects when in conflict.
222
172
  # @return [Array<RObject>] an array of conflicting sibling objects for this key
223
173
  # @return [self] this object when not in conflict
224
174
  def siblings
225
175
  return self unless conflict?
226
- @siblings ||= Multipart.parse(data, Multipart.extract_boundary(content_type)).map do |part|
227
- RObject.new(self.bucket, self.key) do |sibling|
228
- sibling.load(part)
229
- sibling.vclock = vclock
230
- end
231
- end
176
+ @siblings
232
177
  end
233
178
 
234
179
  # @return [true,false] Whether this object has conflicting sibling objects (divergent vclocks)
@@ -286,21 +231,14 @@ module Riak
286
231
  else
287
232
  @raw_data && "(#{@raw_data.size} bytes)"
288
233
  end
289
- "#<#{self.class.name} #{url} [#{@content_type}]:#{body}>"
234
+ "#<#{self.class.name} {#{bucket.name}#{"," + @key if @key}} [#{@content_type}]:#{body}>"
290
235
  end
291
236
 
292
237
  # Walks links from this object to other objects in Riak.
293
238
  # @param [Array<Hash,WalkSpec>] link specifications for the query
294
239
  def walk(*params)
295
240
  specs = WalkSpec.normalize(*params)
296
- response = @bucket.client.http.get(200, @bucket.client.prefix, escape(@bucket.name), escape(@key), specs.join("/"))
297
- if boundary = Multipart.extract_boundary(response[:headers]['content-type'].first)
298
- Multipart.parse(response[:body], boundary).map do |group|
299
- map_walk_group(group)
300
- end
301
- else
302
- []
303
- end
241
+ @bucket.client.http.link_walk(self, specs)
304
242
  end
305
243
 
306
244
  # Converts the object to a link suitable for linking other objects
@@ -322,14 +260,14 @@ module Riak
322
260
  alias :vector_clock :vclock
323
261
  alias :vector_clock= :vclock=
324
262
 
325
- protected
263
+ protected
326
264
  def load_map_reduce_value(hash)
327
265
  metadata = hash['metadata']
328
266
  extract_if_present(metadata, 'X-Riak-VTag', :etag)
329
267
  extract_if_present(metadata, 'content-type', :content_type)
330
268
  extract_if_present(metadata, 'X-Riak-Last-Modified', :last_modified) { |v| Time.httpdate( v ) }
331
269
  extract_if_present(metadata, 'Links', :links) do |links|
332
- Set.new( links.map { |l| Link.new("#{@bucket.client.prefix}#{l[0]}/#{l[1]}", l[2]) } )
270
+ Set.new( links.map { |l| Link.new(*l) } )
333
271
  end
334
272
  extract_if_present(metadata, 'X-Riak-Meta', :meta) do |meta|
335
273
  Hash[
@@ -349,20 +287,5 @@ module Riak
349
287
  send("#{attribute}=", value)
350
288
  end
351
289
  end
352
-
353
- def extract_header(response, name, attribute=nil, &block)
354
- extract_if_present(response[:headers], name, attribute) do |value|
355
- block ? block.call(value[0]) : value[0]
356
- end
357
- end
358
-
359
- def map_walk_group(group)
360
- group.map do |obj|
361
- if obj[:headers] && obj[:body] && obj[:headers]['location']
362
- bucket, key = $1, $2 if obj[:headers]['location'].first =~ %r{/.*/(.*)/(.*)$}
363
- RObject.new(@bucket.client.bucket(bucket, :keys => false), key).load(obj)
364
- end
365
- end
366
- end
367
290
  end
368
291
  end