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 +11 -0
- data/README.rdoc +1 -1
- data/Rakefile +8 -4
- data/VERSION +1 -1
- data/examples/auto_update_data.rb +2 -3
- data/examples/auto_update_links.rb +2 -3
- data/examples/basic_client.rb +2 -3
- data/examples/basic_resource.rb +2 -3
- data/examples/example_helper.rb +7 -0
- data/examples/linked_resources.rb +2 -3
- data/lib/riakrest/core/jiak_bucket.rb +2 -2
- data/lib/riakrest/core/jiak_client.rb +108 -45
- data/lib/riakrest/core/jiak_data.rb +10 -11
- data/lib/riakrest/core/jiak_object.rb +2 -1
- data/lib/riakrest/core/jiak_schema.rb +82 -38
- data/lib/riakrest/resource/jiak_resource.rb +84 -73
- data/lib/riakrest.rb +16 -9
- data/spec/core/jiak_bucket_spec.rb +3 -0
- data/spec/core/jiak_client_spec.rb +85 -15
- data/spec/core/jiak_schema_spec.rb +107 -5
- data/spec/resource/jiak_resource_spec.rb +37 -19
- data/spec/spec_helper.rb +3 -0
- metadata +35 -11
- data/examples/bucket_schemas.rb +0 -38
- data/examples/links_only_pov.rb +0 -38
- data/examples/multiple_pov.rb +0 -46
- data/examples/rest_interaction.rb +0 -51
- data/examples/ruby_json_data.rb +0 -19
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
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.
|
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 = <<-
|
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
|
-
|
21
|
+
EOS
|
21
22
|
gem.authors = ["Paul Rogers"]
|
22
|
-
gem.email = "
|
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.
|
1
|
+
0.1.2
|
data/examples/basic_client.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
require '
|
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(
|
8
|
+
client = JiakClient.new(SERVER_URI)
|
10
9
|
bucket = JiakBucket.new('people',PeopleData)
|
11
10
|
client.set_schema(bucket)
|
12
11
|
|
data/examples/basic_resource.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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>::
|
68
|
-
# <code>:writes</code>::
|
69
|
-
# <code>:durable_writes</code>::
|
70
|
-
# <code>:
|
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
|
-
#
|
74
|
-
# and <code>
|
75
|
-
# parameters
|
76
|
-
#
|
77
|
-
#
|
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.
|
157
|
-
#
|
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
|
-
|
163
|
-
|
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(
|
166
|
-
|
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.
|
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
|
-
|
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
|
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 = {
|
320
|
+
req_params = {
|
321
|
+
READS => opts[:reads] || @params[:reads]
|
322
|
+
}
|
285
323
|
|
286
324
|
begin
|
287
|
-
uri = jiak_uri(bucket,key
|
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>:
|
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,[:
|
348
|
+
check_opts(opts,[:deletes],JiakClientException)
|
311
349
|
begin
|
312
|
-
req_params = {
|
313
|
-
uri = jiak_uri(bucket,key
|
314
|
-
RestClient.delete(uri
|
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
|
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
|
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=""
|
380
|
-
|
381
|
-
uri
|
382
|
-
|
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,""
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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 :
|
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
|
#
|