riakrest 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,14 @@
1
+ === 0.1.2 2009-11-29
2
+ - Add wide open schemas.
3
+ - Add wild card schema arrays.
4
+
5
+ === 0.1.1 2009-11-28
6
+ - Fixed several 1.8.7 issues.
7
+ - Added helper for examples and put server URI setting in helpers.
8
+ - Removed POV terminology. Riak doesn't support per-request masks (yet?).
9
+ - Added some gem dependency declarations.
10
+ - Minor refactoring.
11
+
1
12
  === 0.1.0 2009-11-16
2
13
  - Renamed data/JiakDataHash to core/JiakDataFields and reworked.
3
14
  - Move JiakDataHash functionality into JiakData as default implementation
data/README.rdoc CHANGED
@@ -62,6 +62,6 @@ sudo gem install riakrest
62
62
 
63
63
  == LICENSE:
64
64
 
65
- Copyright (c) 2009 Paul Rogers, DingoSky. See LICENCE for details.
65
+ Copyright (c) 2009 Paul Rogers, DingoSky. See LICENSE for details.
66
66
 
67
67
  ===Go forth and Riak!
data/Rakefile CHANGED
@@ -4,25 +4,29 @@ require 'rake'
4
4
  begin
5
5
  require 'jeweler'
6
6
  rescue LoadError
7
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
7
+ puts "Jeweler (or a dependency) not available."
8
+ puts " Install with: sudo gem install jeweler"
8
9
  end
9
10
 
10
11
  Jeweler::Tasks.new do |gem|
11
12
  gem.name = "riakrest"
12
13
  gem.summary = %Q{RiakRest provides structured, RESTful interaction with a Riak document store.}
13
- gem.description = <<-EOH
14
+ gem.description = <<-EOS
14
15
  RiakRest provides structured, RESTful interaction with
15
16
  the HTTP/JSON interface of a Riak[http://riak.basho.com] document data
16
17
  store. RiakRest provides two levels of interaction: Core Client and
17
18
  Resource. Core Client works at the Jiak level and exposes Jiak
18
19
  internals. JiakResource is an abstraction built on top of the Core Client
19
20
  that gives a true RESTful feel.
20
- EOH
21
+ EOS
21
22
  gem.authors = ["Paul Rogers"]
22
- gem.email = "paul@dingosky.com"
23
+ gem.email = "riak@dingosky.com"
23
24
  gem.homepage = "http://github.com/wcpr/riakrest"
24
25
  gem.add_dependency('rest-client', '>= 1.0.0')
26
+ gem.add_dependency('json', '>= 1.1.9')
25
27
  gem.add_development_dependency "rest-client", ">= 1.0.0"
28
+ gem.add_development_dependency "json", ">= 1.1.9"
29
+ gem.add_development_dependency "jeweler", ">= 1.4.0"
26
30
  gem.add_development_dependency "rspec", ">= 1.2.9"
27
31
  end
28
32
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.2
@@ -1,9 +1,8 @@
1
- require 'riakrest'
2
- include RiakRest
1
+ require File.dirname(__FILE__) + '/example_helper.rb'
3
2
 
4
3
  class People
5
4
  include JiakResource
6
- server 'http://localhost:8002/jiak'
5
+ server SERVER_URI
7
6
  jattr_accessor :name, :age
8
7
  keygen { name }
9
8
  auto_manage
@@ -1,9 +1,8 @@
1
- require 'riakrest'
2
- include RiakRest
1
+ require File.dirname(__FILE__) + '/example_helper.rb'
3
2
 
4
3
  class People
5
4
  include JiakResource
6
- server 'http://localhost:8002/jiak'
5
+ server SERVER_URI
7
6
  jattr_accessor :name, :age
8
7
  keygen { name.downcase }
9
8
  auto_post
@@ -1,12 +1,11 @@
1
- require 'riakrest'
2
- include RiakRest
1
+ require File.dirname(__FILE__) + '/example_helper.rb'
3
2
 
4
3
  class PeopleData
5
4
  include JiakData
6
5
  jattr_accessor :name, :age
7
6
  end
8
7
 
9
- client = JiakClient.new("http://localhost:8002/jiak")
8
+ client = JiakClient.new(SERVER_URI)
10
9
  bucket = JiakBucket.new('people',PeopleData)
11
10
  client.set_schema(bucket)
12
11
 
@@ -1,9 +1,8 @@
1
- require 'riakrest'
2
- include RiakRest
1
+ require File.dirname(__FILE__) + '/example_helper.rb'
3
2
 
4
3
  class People
5
4
  include JiakResource
6
- server 'http://localhost:8002/jiak'
5
+ server SERVER_URI
7
6
  jattr_accessor :name, :age
8
7
  keygen {name.downcase}
9
8
  auto_manage
@@ -0,0 +1,7 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'riakrest'
5
+ include RiakRest
6
+
7
+ SERVER_URI = 'http://localhost:8002/jiak'
@@ -1,9 +1,8 @@
1
- require 'riakrest'
2
- include RiakRest
1
+ require File.dirname(__FILE__) + '/example_helper.rb'
3
2
 
4
3
  class Parents
5
4
  include JiakResource
6
- server 'http://localhost:8002/jiak'
5
+ server SERVER_URI
7
6
  jattr_accessor :name
8
7
  keygen { name }
9
8
  end
@@ -102,8 +102,8 @@ module RiakRest
102
102
  private :transform_name
103
103
 
104
104
  def check_data_class(data_class)
105
- unless data_class.include?(JiakData)
106
- raise JiakBucketException, "Data class must be type of JiakData."
105
+ unless !data_class.nil? && data_class.include?(JiakData)
106
+ raise JiakBucketException, "Data class must include JiakData."
107
107
  end
108
108
  data_class
109
109
  end
@@ -46,15 +46,28 @@ module RiakRest
46
46
 
47
47
  # :stopdoc:
48
48
  APP_JSON = 'application/json'
49
+ APP_JSON.freeze
50
+
49
51
  RETURN_BODY = 'returnbody'
52
+ RETURN_BODY.freeze
50
53
  READS = 'r'
54
+ READS.freeze
51
55
  WRITES = 'w'
56
+ WRITES.freeze
52
57
  DURABLE_WRITES = 'dw'
53
- RESPONSE_WAITS = 'rw'
58
+ DURABLE_WRITES.freeze
59
+ DELETES = 'rw'
60
+ DELETES.freeze
61
+
62
+ VALID_PARAMS = [:reads,:writes,:durable_writes,:deletes]
63
+ VALID_PARAMS.freeze
64
+ VALID_OPTS = Array.new(VALID_PARAMS) << :proxy
65
+ VALID_OPTS.freeze
66
+
54
67
  KEYS = 'keys'
68
+ KEYS.freeze
55
69
  SCHEMA = 'schema'
56
- VALID_PARAMS = [:reads,:writes,:durable_writes,:waits]
57
- VALID_OPTS = VALID_PARAMS << :proxy
70
+ SCHEMA.freeze
58
71
  # :startdoc:
59
72
 
60
73
  # :call-seq:
@@ -64,17 +77,22 @@ module RiakRest
64
77
  # the specified URI. Go through a proxy if proxy option specified.
65
78
  #
66
79
  # =====Valid options
67
- # <code>:reads</code>:: Respond after this many Riak nodes reply to a read request.
68
- # <code>:writes</code>:: Respond after this many Riak nodes reply to a write request.
69
- # <code>:durable_writes</code>:: Ensure this many Riak nodes perform a durable write.
70
- # <code>:waits</code>:: Respond after this many Riak nodes reply to a delete request.
80
+ # <code>:reads</code>:: Minimum number of responding nodes for successful reads.
81
+ # <code>:writes</code>:: Minimum number of responding nodes for successful writes. Writes can be buffered on the server nodes for performance.
82
+ # <code>:durable_writes</code>:: Minimum number of resonding nodes that must perform a durable write to the persistence layer.
83
+ # <code>:deletes</code>:: Minimum number of responding nodes for successful delete.
71
84
  # <code>:proxy</code>:: Proxy server URI
72
85
  #
73
- # Any of the request parameters <code>writes, durable_writes, reads,</code>
74
- # and <code>waits</code> can be set on a per request basis. If the
75
- # parameters are not specified, the value set for JiakClient is used. If
76
- # neither of those scopes include the parameter, the value set on the Riak
77
- # cluster is used.
86
+ # The configuration of a Riak cluster includes server setting for the
87
+ # <code>writes, durable_writes, reads,</code> and <code>deletes</code>
88
+ # parameters. None of these request parameter are required by RiakRest, and
89
+ # their use within RiakRest is to override the Riak cluster settings,
90
+ # either at the JiakClient level or the individual request level.
91
+ #
92
+ # Settings passed to individual <code>get, store,</code> and
93
+ # <code>delete</code> requests take precendence over the setting maintained
94
+ # by a JiakClient. Any request parameter not set in JiakClient or on an
95
+ # individual request will default to the values set in the Riak cluster.
78
96
  #
79
97
  def initialize(uri, opts={})
80
98
  check_opts(opts,VALID_OPTS,JiakClientException)
@@ -145,25 +163,38 @@ module RiakRest
145
163
  # :call-seq:
146
164
  # params -> hash
147
165
  #
148
- # Copy of the current parameters hash.
166
+ # Copy of the current request parameters hash.
149
167
  def params
150
168
  @params.dup
151
169
  end
152
170
 
153
171
  # :call-seq:
154
- # set_schema(bucket) -> nil
172
+ # set_schema(bucket,schema=nil) -> nil
155
173
  #
156
- # Set the Jiak server schema for a bucket. The schema is determined by the
157
- # JiakData associated with the JiakBucket.
174
+ # Set the Jiak server schema for a bucket. If a JaikSchema is given, bucket
175
+ # must be a string; otherwise, if the schema is not given bucket must be a
176
+ # JiakBucket and the associated JiakData schema is used.
158
177
  #
159
178
  # Raise JiakClientException if the bucket is not a JiakBucket.
160
- #
161
- def set_schema(bucket)
162
- unless bucket.is_a?(JiakBucket)
163
- raise JiakClientException, "Bucket must be a JiakBucket."
179
+ # Raise JiakClientException if the schema is not a JiakSchema.
180
+ def set_schema(bucket,schema=nil)
181
+ if(schema.nil?)
182
+ unless bucket.is_a?(JiakBucket)
183
+ raise JiakClientException, "Bucket must be a JiakBucket."
184
+ end
185
+ bucket_name = bucket.name
186
+ schema = bucket.schema
187
+ else
188
+ unless(bucket.is_a?(String))
189
+ raise JiakClientException, "Bucket must be a string name."
190
+ end
191
+ unless(schema.is_a?(JiakSchema))
192
+ raise JiakClientException, "Schema must be a JiakSchema."
193
+ end
194
+ bucket_name = bucket
164
195
  end
165
- resp = RestClient.put(jiak_uri(bucket),
166
- bucket.schema.to_jiak.to_json,
196
+ resp = RestClient.put(jiak_uri(bucket_name),
197
+ schema.to_jiak.to_json,
167
198
  :content_type => APP_JSON,
168
199
  :accept => APP_JSON)
169
200
  end
@@ -171,9 +202,7 @@ module RiakRest
171
202
  # :call-seq:
172
203
  # schema(bucket) -> JiakSchema
173
204
  #
174
- # Get the data schema for a bucket on a Jiak server. This involves a call
175
- # to the Jiak server. See JiakBucket#schema for a way to get this
176
- # information without server access.
205
+ # Get the data schema for a bucket on a Jiak server.
177
206
  def schema(bucket)
178
207
  JiakSchema.jiak_create(bucket_info(bucket,SCHEMA))
179
208
  end
@@ -204,6 +233,11 @@ module RiakRest
204
233
  # <code>:durable_writes</code><br/>
205
234
  # <code>:reads</code><br/>
206
235
  #
236
+ # See JiakClient#new for description of <code>writes,
237
+ # durable_writes,</code> and <code>reades</code> parameters. The
238
+ # <code>reads</code> parameter only takes effect if the JiakObject is being
239
+ # returned (which involves reading the writes).
240
+ #
207
241
  # Raise JiakClientException if object not a JiakObject or illegal options
208
242
  # are passed.<br/>
209
243
  # Raise JiakResourceException on RESTful HTTP errors.
@@ -214,12 +248,14 @@ module RiakRest
214
248
  req_params = {
215
249
  WRITES => opts[:writes] || @params[:writes],
216
250
  DURABLE_WRITES => opts[:durable_writes] || @params[:durable_writes],
217
- READS => opts[:reads] || @params[:reads]
218
251
  }
219
- req_params[RETURN_BODY] = (opts[:return] == :object)
252
+ if(opts[:return] == :object)
253
+ req_params[RETURN_BODY] = true
254
+ req_params[READS] = opts[:reads] || @params[:reads]
255
+ end
220
256
 
221
257
  begin
222
- uri = jiak_uri(jobj.bucket,jobj.key,req_params)
258
+ uri = jiak_uri(jobj.bucket,jobj.key) << jiak_qstring(req_params)
223
259
  payload = jobj.to_jiak.to_json
224
260
  headers = {
225
261
  :content_type => APP_JSON,
@@ -270,7 +306,7 @@ module RiakRest
270
306
  # String.
271
307
  #
272
308
  # =====Valid options
273
- # <code>:reads</code>
309
+ # <code>:reads</code> --- See JiakClient#new
274
310
  #
275
311
  # Raise JiakClientException if bucket not a JiakBucket.<br/>
276
312
  # Raise JiakResourceNotFound if resource not found on Jiak server.<br/>
@@ -281,10 +317,12 @@ module RiakRest
281
317
  raise JiakClientException, "Bucket must be a JiakBucket."
282
318
  end
283
319
  check_opts(opts,[:reads],JiakClientException)
284
- req_params = {READS => opts[:reads] || @params[:reads]}
320
+ req_params = {
321
+ READS => opts[:reads] || @params[:reads]
322
+ }
285
323
 
286
324
  begin
287
- uri = jiak_uri(bucket,key,req_params)
325
+ uri = jiak_uri(bucket,key) << jiak_qstring(req_params)
288
326
  resp = RestClient.get(uri, :accept => APP_JSON)
289
327
  JiakObject.jiak_create(JSON.parse(resp),bucket.data_class)
290
328
  rescue RestClient::ResourceNotFound => err
@@ -302,16 +340,16 @@ module RiakRest
302
340
  # Delete the JiakObject stored at the bucket/key.
303
341
  #
304
342
  # =====Valid options
305
- # <code>:waits</code>
343
+ # <code>:deletes</code> --- See JiakClient#new
306
344
  #
307
345
  # Raise JiakResourceException on RESTful HTTP errors.
308
346
  #
309
347
  def delete(bucket,key,opts={})
310
- check_opts(opts,[:waits],JiakClientException)
348
+ check_opts(opts,[:deletes],JiakClientException)
311
349
  begin
312
- req_params = {RESPONSE_WAITS => opts[:waits] || @params[:waits]}
313
- uri = jiak_uri(bucket,key,req_params)
314
- RestClient.delete(uri, :accept => APP_JSON)
350
+ req_params = {DELETES => opts[:deletes] || @params[:deletes]}
351
+ uri = jiak_uri(bucket,key) << jiak_qstring(req_params)
352
+ RestClient.delete(uri,:accept => APP_JSON)
315
353
  true
316
354
  rescue RestClient::ExceptionWithResponse => err
317
355
  fail_with_response("delete", err)
@@ -320,6 +358,22 @@ module RiakRest
320
358
  end
321
359
  end
322
360
 
361
+ # :call-seq:
362
+ # exist?(bucket,key) -> true or false
363
+ #
364
+ # Return true if a resource exists at bucket/key
365
+ def exist?(bucket,key)
366
+ begin
367
+ uri = jiak_uri(bucket,key)
368
+ RestClient.head(uri,:accept => APP_JSON)
369
+ true
370
+ rescue RestClient::ResourceNotFound
371
+ false
372
+ rescue RestClient::Exception => err
373
+ fail_with_message("delete", err)
374
+ end
375
+ end
376
+
323
377
  # :call-seq:
324
378
  # walk(bucket,key,query,data_class) -> array
325
379
  #
@@ -334,7 +388,7 @@ module RiakRest
334
388
  start = jiak_uri(bucket,key)
335
389
  case query
336
390
  when QueryLink
337
- uri = start+'/'+query.for_uri
391
+ uri = "#{start}/#{query.for_uri}"
338
392
  when Array
339
393
  uri = query.inject(start) {|build,link| build+'/'+link.for_uri}
340
394
  else
@@ -342,7 +396,6 @@ module RiakRest
342
396
  'a QueryLink or an Array of QueryLink objects'
343
397
  end
344
398
  resp = RestClient.get(uri, :accept => APP_JSON)
345
- # JSON.parse(resp)['results'][0]
346
399
  JSON.parse(resp)['results'][0].map do |jiak|
347
400
  JiakObject.jiak_create(jiak,data_class)
348
401
  end
@@ -356,7 +409,7 @@ module RiakRest
356
409
  # :call-seq:
357
410
  # client == other -> true or false
358
411
  #
359
- # Equality -- JiakClients are equal if they have the same URI
412
+ # Equality --- JiakClients are equal if they have the same URI
360
413
  def ==(other)
361
414
  (@server == other.server) rescue false
362
415
  end
@@ -376,20 +429,30 @@ module RiakRest
376
429
  end
377
430
 
378
431
  # Build the URI for accessing the Jiak server.
379
- def jiak_uri(bucket,key="",params={})
380
- uri = "#{@server}#{URI.encode(bucket.name)}"
381
- uri += "/#{URI.encode(key)}" unless key.empty?
382
- qstring = params.reject {|k,v| v.nil?}.map{|k,v| "#{k}=#{v}"}.join('&')
383
- uri += "?#{URI.encode(qstring)}" unless qstring.empty?
432
+ def jiak_uri(bucket,key="")
433
+ bucket_name = bucket.is_a?(JiakBucket) ? bucket.name : bucket
434
+ uri = "#{@server}#{URI.encode(bucket_name)}"
435
+ uri << "/#{URI.encode(key)}" unless key.empty?
384
436
  uri
385
437
  end
386
438
  private :jiak_uri
439
+
440
+ # Build query string. Strip keys with nil values.
441
+ def jiak_qstring(params={})
442
+ qstring = ""
443
+ params.delete_if {|k,v| v.nil?}
444
+ unless(params.empty?)
445
+ qstring << "?" << params.map{|k,v| "#{k}=#{v}"}.join('&')
446
+ end
447
+ qstring
448
+ end
449
+ private :jiak_qstring
387
450
 
388
451
  # Get either the schema or keys for the bucket.
389
452
  def bucket_info(bucket,info)
390
453
  ignore = (info == SCHEMA) ? KEYS : SCHEMA
391
454
  begin
392
- uri = jiak_uri(bucket,"",{ignore => false})
455
+ uri = jiak_uri(bucket,"") << jiak_qstring({ignore => false})
393
456
  JSON.parse(RestClient.get(uri, :accept => APP_JSON))[info]
394
457
  rescue RestClient::ExceptionWithResponse => err
395
458
  fail_with_response("get", err)
@@ -91,7 +91,7 @@ module RiakRest
91
91
  #
92
92
  # Raise JiakDataException if the fields include <code>jiak</code>.
93
93
  def allow(*fields)
94
- delegate_schema("allow",*fields)
94
+ expand_schema("allow",*fields)
95
95
  end
96
96
 
97
97
  # :call-seq:
@@ -102,7 +102,7 @@ module RiakRest
102
102
  #
103
103
  # Returns an array of added fields.
104
104
  def require(*fields)
105
- delegate_schema("require",*fields)
105
+ expand_schema("require",*fields)
106
106
  end
107
107
 
108
108
  # :call-seq:
@@ -113,7 +113,7 @@ module RiakRest
113
113
  #
114
114
  # Returns an array of added fields.
115
115
  def readable(*fields)
116
- delegate_schema("readable",*fields)
116
+ expand_schema("readable",*fields)
117
117
  end
118
118
 
119
119
  # :call-seq:
@@ -124,7 +124,7 @@ module RiakRest
124
124
  #
125
125
  # Returns an array of added fields.
126
126
  def writable(*fields)
127
- delegate_schema("writable",*fields)
127
+ expand_schema("writable",*fields)
128
128
  end
129
129
 
130
130
  # :call-seq:
@@ -142,7 +142,7 @@ module RiakRest
142
142
 
143
143
  # Delegates adding fields to the schema, then creates attr accessors for
144
144
  # each field added.
145
- def delegate_schema(method,*fields)
145
+ def expand_schema(method,*fields)
146
146
  @schema ||= JiakSchema.new
147
147
  prev_allowed = @schema.allowed_fields
148
148
  added_fields = @schema.send(method,*fields)
@@ -150,7 +150,7 @@ module RiakRest
150
150
  added_allowed.each {|field| attr_accessor field}
151
151
  added_fields
152
152
  end
153
- private :delegate_schema
153
+ private :expand_schema
154
154
 
155
155
  # :call-seq:
156
156
  # JiakData.schema -> JiakSchema
@@ -158,7 +158,6 @@ module RiakRest
158
158
  # Get a JiakSchema representation this data.
159
159
  def schema
160
160
  @schema ||= JiakSchema.new
161
- @schema.dup
162
161
  end
163
162
 
164
163
  # :call-seq:
@@ -200,10 +199,6 @@ module RiakRest
200
199
  def self.included(including_class) # :nodoc:
201
200
  including_class.extend(ClassMethods)
202
201
 
203
- define_method(:initialize) do |hash={}|
204
- hash.each {|k,v| instance_variable_set("@#{k}", v)}
205
- end
206
-
207
202
  define_method(:to_jiak) do
208
203
  self.class.schema.write_mask.inject({}) do |build,field|
209
204
  build[field] = send("#{field}")
@@ -235,6 +230,10 @@ module RiakRest
235
230
  # Instance methods
236
231
  # ----------------------------------------------------------------------
237
232
 
233
+ def initialize(hash={})
234
+ hash.each {|k,v| instance_variable_set("@#{k}", v)}
235
+ end
236
+
238
237
  # :call-seq:
239
238
  # to_jiak -> hash
240
239
  #
@@ -40,7 +40,8 @@ module RiakRest
40
40
 
41
41
  # The Riak context for the object if provided.
42
42
  if opts[:vclock]
43
- @riak = {}.merge(opts)
43
+ @riak = {}
44
+ [:vclock,:vtag,:lastmod].each {|k| @riak[k] = opts[k]}
44
45
  end
45
46
  end
46
47