riakrest 0.1.2 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/README.rdoc +35 -25
- data/VERSION +1 -1
- data/examples/auto_manage_data.rb +51 -0
- data/examples/auto_update_links.rb +31 -14
- data/examples/basic_client.rb +33 -10
- data/examples/basic_resource.rb +29 -12
- data/examples/basic_resource_pov.rb +65 -0
- data/examples/buoy.rb +102 -0
- data/examples/buoy_pov.rb +46 -0
- data/examples/example_helper.rb +1 -0
- data/examples/linked_resources.rb +38 -70
- data/lib/riakrest/core/jiak_client.rb +56 -31
- data/lib/riakrest/core/jiak_data.rb +24 -14
- data/lib/riakrest/core/jiak_data_fields.rb +4 -4
- data/lib/riakrest/core/query_link.rb +13 -6
- data/lib/riakrest/resource/jiak_resource.rb +85 -71
- data/lib/riakrest/resource/jiak_resource_pov.rb +279 -0
- data/lib/riakrest.rb +3 -2
- data/spec/core/jiak_client_spec.rb +20 -5
- data/spec/resource/jiak_resource_spec.rb +111 -119
- metadata +11 -4
- data/examples/auto_update_data.rb +0 -38
@@ -1,14 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/example_helper.rb'
|
2
2
|
|
3
|
-
|
4
|
-
include JiakResource
|
5
|
-
server SERVER_URI
|
6
|
-
jattr_accessor :name
|
7
|
-
keygen { name }
|
8
|
-
end
|
9
|
-
(Child = Parents.dup).group 'children'
|
10
|
-
|
11
|
-
# relationships
|
3
|
+
# The parent-child relationships
|
12
4
|
parent_children = {
|
13
5
|
'p0' => ['c0'],
|
14
6
|
'p1' => ['c0','c1','c2'],
|
@@ -16,17 +8,20 @@ parent_children = {
|
|
16
8
|
'p3' => ['c3']
|
17
9
|
}
|
18
10
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
11
|
+
# Simple resource classes. Parent group (Jiak bucket name) defaults to
|
12
|
+
# lowercase of class name, so Child class just like Parent, but grouped by
|
13
|
+
# 'child' rather than 'parent'.
|
14
|
+
class Parent
|
15
|
+
include JiakResource
|
16
|
+
server SERVER_URI
|
17
|
+
attr_accessor :name
|
18
|
+
keygen { name }
|
25
19
|
end
|
20
|
+
(Child = Parent.dup).group 'child'
|
26
21
|
|
27
|
-
#
|
22
|
+
# Store data and relationships
|
28
23
|
parent_children.each do |pname,cnames|
|
29
|
-
p =
|
24
|
+
p = Parent.new(:name => pname).post
|
30
25
|
cnames.each do |cname|
|
31
26
|
begin
|
32
27
|
c = Child.get(cname)
|
@@ -40,70 +35,43 @@ parent_children.each do |pname,cnames|
|
|
40
35
|
p.update
|
41
36
|
end
|
42
37
|
|
43
|
-
#
|
44
|
-
parents
|
45
|
-
|
46
|
-
puts p1.name # => 'p1'
|
47
|
-
|
48
|
-
# retrieve children
|
49
|
-
children = child_parents.keys.map {|c| Child.get(c)}
|
50
|
-
c0,c1,c2,c3 = children
|
51
|
-
puts c1.name # => 'c1'
|
38
|
+
# Retrieve parents and children objects for future reference
|
39
|
+
parents = parent_children.keys.map {|p| Parent.get(p)}
|
40
|
+
children = parent_children.values.flatten.uniq.map {|c| Child.get(c)}
|
52
41
|
|
53
|
-
#
|
54
|
-
|
55
|
-
puts
|
42
|
+
# Retrieve parent-1 children by query
|
43
|
+
p1c = parents[1].query([Child,'child'])
|
44
|
+
puts " 3 children? #{p1c.size == 3}" # => true
|
45
|
+
puts " Including c2? #{p1c.include?(children[2])}" # => true
|
56
46
|
|
57
|
-
#
|
58
|
-
|
59
|
-
puts c3p
|
60
|
-
puts c3p[
|
47
|
+
# Retrieve child-3 parents by query
|
48
|
+
c3p = children[3].query([Parent,'parent'])
|
49
|
+
puts " 2 parents? #{c3p.size == 2}" # => true
|
50
|
+
puts " Including p2? #{c3p.include?(parents[2])}" # => true
|
61
51
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
puts
|
52
|
+
# Retrieve child-2 siblings by multi-step query
|
53
|
+
c2s = children[2].query([Parent,'parent',Child,'child'])
|
54
|
+
c2s.delete_if{|s| s.eql?(children[2])}
|
55
|
+
puts " 3 siblings? #{c2s.size == 3}" # => true
|
56
|
+
puts " Including c0? #{c2s.include?(children[0])}" # => true
|
67
57
|
|
68
|
-
#
|
69
|
-
c3sp =
|
58
|
+
# Who is c3's step-sibling's other parent?
|
59
|
+
c3sp = children[3].query([Parent,'parent',Child,'child',Parent,'parent'])
|
70
60
|
c3p.each {|p| c3sp.delete_if{|sp| p.eql?(sp)}}
|
71
|
-
puts c3sp[
|
72
|
-
|
73
|
-
# turn on auto-update at class level
|
74
|
-
Parents.auto_update true
|
75
|
-
Child.auto_update true
|
61
|
+
puts " Parent p1? #{c3sp.include?(parents[1])}" # => true
|
76
62
|
|
77
|
-
#
|
63
|
+
# Add sibling links using existing links (say, for convenience)
|
78
64
|
children.each do |c|
|
79
|
-
siblings = c.query(
|
65
|
+
siblings = c.query([Parent,'parent',Child,'child']).delete_if{|s| s.eql?(c)}
|
80
66
|
siblings.each {|s| c.link(s,'sibling')}
|
81
67
|
c.update
|
82
68
|
end
|
83
|
-
puts c1.query(Child,'sibling').size # => 2
|
84
|
-
|
85
|
-
# some folks are odd, and others are normal
|
86
|
-
parent_children.keys.each do |p|
|
87
|
-
parent = Parents.get(p)
|
88
|
-
p_children = parent.query(Child,'child')
|
89
|
-
p_children.each do |child|
|
90
|
-
child.link(parent, p[1].to_i.odd? ? 'odd' : 'normal')
|
91
|
-
child.update
|
92
|
-
parent.link(child, child.name[1].to_i.odd? ? 'odd' : 'normal')
|
93
|
-
end
|
94
|
-
parent.update
|
95
|
-
end
|
96
|
-
# refresh parents and children variables
|
97
|
-
parents.each {|p| p.refresh}
|
98
|
-
children.each {|c| c.refresh}
|
99
69
|
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
105
|
-
puts op[0].name # => 'p1'
|
70
|
+
# Retrieve child-2 siblings using new links
|
71
|
+
c2s = children[2].query([Child,'sibling'])
|
72
|
+
puts " 3 siblings? #{c2s.size == 3}" # => true
|
73
|
+
puts " Including c0? #{c2s.include?(children[0])}" # => true
|
106
74
|
|
107
|
-
#
|
75
|
+
# Clean-up by deleting all parents and children
|
108
76
|
parents.each {|p| p.delete}
|
109
77
|
children.each {|c| c.delete}
|
@@ -12,7 +12,7 @@ module RiakRest
|
|
12
12
|
#
|
13
13
|
# class PeopleData
|
14
14
|
# include JiakData
|
15
|
-
#
|
15
|
+
# attr_accessor :name, :age
|
16
16
|
# end
|
17
17
|
#
|
18
18
|
# client = JiakClient.new("http://localhost:8002/jiak")
|
@@ -45,29 +45,41 @@ module RiakRest
|
|
45
45
|
attr_accessor :server, :proxy, :params
|
46
46
|
|
47
47
|
# :stopdoc:
|
48
|
-
APP_JSON
|
48
|
+
APP_JSON = 'application/json'
|
49
49
|
APP_JSON.freeze
|
50
50
|
|
51
|
-
|
51
|
+
KEYS = 'keys'
|
52
|
+
SCHEMA = 'schema'
|
53
|
+
KEYS.freeze
|
54
|
+
SCHEMA.freeze
|
55
|
+
|
56
|
+
RETURN_BODY = 'returnbody'
|
57
|
+
READS = 'r'
|
58
|
+
WRITES = 'w'
|
59
|
+
DURABLE_WRITES = 'dw'
|
60
|
+
DELETES = 'rw'
|
61
|
+
COPY = 'copy'
|
62
|
+
READ = 'read'
|
63
|
+
|
52
64
|
RETURN_BODY.freeze
|
53
|
-
READS = 'r'
|
54
65
|
READS.freeze
|
55
|
-
WRITES = 'w'
|
56
66
|
WRITES.freeze
|
57
|
-
DURABLE_WRITES = 'dw'
|
58
67
|
DURABLE_WRITES.freeze
|
59
|
-
DELETES = 'rw'
|
60
68
|
DELETES.freeze
|
69
|
+
COPY.freeze
|
70
|
+
READ.freeze
|
61
71
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
72
|
+
CLIENT_PARAMS = [:reads,:writes,:durable_writes,:deletes,:proxy]
|
73
|
+
STORE_PARAMS = [:writes,:durable_writes,:return,:reads,:copy,:read]
|
74
|
+
GET_PARAMS = [:reads,:read]
|
75
|
+
DELETE_PARAMS = [:delete]
|
76
|
+
WALK_PARAMS = [:read]
|
66
77
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
78
|
+
CLIENT_PARAMS.freeze
|
79
|
+
STORE_PARAMS.freeze
|
80
|
+
GET_PARAMS.freeze
|
81
|
+
DELETE_PARAMS.freeze
|
82
|
+
WALK_PARAMS.freeze
|
71
83
|
# :startdoc:
|
72
84
|
|
73
85
|
# :call-seq:
|
@@ -79,7 +91,7 @@ module RiakRest
|
|
79
91
|
# =====Valid options
|
80
92
|
# <code>:reads</code>:: Minimum number of responding nodes for successful reads.
|
81
93
|
# <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
|
94
|
+
# <code>:durable_writes</code>:: Minimum number of responding nodes that must perform a durable write to a persistence layer.
|
83
95
|
# <code>:deletes</code>:: Minimum number of responding nodes for successful delete.
|
84
96
|
# <code>:proxy</code>:: Proxy server URI
|
85
97
|
#
|
@@ -95,7 +107,7 @@ module RiakRest
|
|
95
107
|
# individual request will default to the values set in the Riak cluster.
|
96
108
|
#
|
97
109
|
def initialize(uri, opts={})
|
98
|
-
check_opts(opts,
|
110
|
+
check_opts(opts,CLIENT_PARAMS,JiakClientException)
|
99
111
|
self.server = uri
|
100
112
|
self.proxy = opts.delete(:proxy)
|
101
113
|
self.params = opts
|
@@ -143,10 +155,10 @@ module RiakRest
|
|
143
155
|
# :call-seq:
|
144
156
|
# set_params(req_params={}) -> hash
|
145
157
|
#
|
146
|
-
# Set specified request parameters without changing unspecified
|
147
|
-
# method merges the passed parameters with the current settings.
|
158
|
+
# Set specified client request parameters without changing unspecified
|
159
|
+
# ones. This method merges the passed parameters with the current settings.
|
148
160
|
def set_params(req_params={})
|
149
|
-
check_opts(req_params,
|
161
|
+
check_opts(req_params,CLIENT_PARAMS,JiakClientException)
|
150
162
|
@params.merge!(req_params)
|
151
163
|
end
|
152
164
|
|
@@ -156,7 +168,7 @@ module RiakRest
|
|
156
168
|
# Set default params for Jiak client requests.
|
157
169
|
#
|
158
170
|
def params=(req_params)
|
159
|
-
check_opts(req_params,
|
171
|
+
check_opts(req_params,CLIENT_PARAMS,JiakClientException)
|
160
172
|
@params = req_params
|
161
173
|
end
|
162
174
|
|
@@ -232,32 +244,39 @@ module RiakRest
|
|
232
244
|
# <code>:writes</code><br/>
|
233
245
|
# <code>:durable_writes</code><br/>
|
234
246
|
# <code>:reads</code><br/>
|
247
|
+
# <code>:copy</code> -- <code>true</code> to indicate the any data
|
248
|
+
# fields already stored on the server and not explicitly altered by this
|
249
|
+
# write should be copied and left unchanged. Default is
|
250
|
+
# <code>false</code><br/>.
|
251
|
+
# <code>:read</code> -- Comma separated list of fields to be returned on
|
252
|
+
# read.
|
235
253
|
#
|
236
254
|
# See JiakClient#new for description of <code>writes,
|
237
|
-
# durable_writes,</code> and <code>
|
238
|
-
# <code>reads</code> parameter only takes effect if
|
239
|
-
# returned (which involves reading the writes).
|
255
|
+
# durable_writes,</code> and <code>reads</code> parameters. The
|
256
|
+
# <code>reads</code> and <code>read</code> parameter only takes effect if
|
257
|
+
# the JiakObject is being returned (which involves reading the writes).
|
240
258
|
#
|
241
259
|
# Raise JiakClientException if object not a JiakObject or illegal options
|
242
260
|
# are passed.<br/>
|
243
261
|
# Raise JiakResourceException on RESTful HTTP errors.
|
244
262
|
#
|
245
263
|
def store(jobj,opts={})
|
246
|
-
check_opts(opts,
|
247
|
-
JiakClientException)
|
264
|
+
check_opts(opts,STORE_PARAMS,JiakClientException)
|
248
265
|
req_params = {
|
249
266
|
WRITES => opts[:writes] || @params[:writes],
|
250
267
|
DURABLE_WRITES => opts[:durable_writes] || @params[:durable_writes],
|
268
|
+
COPY => opts[:copy]
|
251
269
|
}
|
252
270
|
if(opts[:return] == :object)
|
253
271
|
req_params[RETURN_BODY] = true
|
254
272
|
req_params[READS] = opts[:reads] || @params[:reads]
|
273
|
+
req_params[READ] = opts[:read]
|
255
274
|
end
|
256
275
|
|
257
276
|
begin
|
258
277
|
uri = jiak_uri(jobj.bucket,jobj.key) << jiak_qstring(req_params)
|
259
278
|
payload = jobj.to_jiak.to_json
|
260
|
-
headers = {
|
279
|
+
headers = {
|
261
280
|
:content_type => APP_JSON,
|
262
281
|
:accept => APP_JSON }
|
263
282
|
# Decision tree:
|
@@ -316,9 +335,10 @@ module RiakRest
|
|
316
335
|
unless bucket.is_a?(JiakBucket)
|
317
336
|
raise JiakClientException, "Bucket must be a JiakBucket."
|
318
337
|
end
|
319
|
-
check_opts(opts,
|
338
|
+
check_opts(opts,GET_PARAMS,JiakClientException)
|
320
339
|
req_params = {
|
321
|
-
READS => opts[:reads] || @params[:reads]
|
340
|
+
READS => opts[:reads] || @params[:reads],
|
341
|
+
READ => opts[:read]
|
322
342
|
}
|
323
343
|
|
324
344
|
begin
|
@@ -345,7 +365,7 @@ module RiakRest
|
|
345
365
|
# Raise JiakResourceException on RESTful HTTP errors.
|
346
366
|
#
|
347
367
|
def delete(bucket,key,opts={})
|
348
|
-
check_opts(opts,
|
368
|
+
check_opts(opts,DELETE_PARAMS,JiakClientException)
|
349
369
|
begin
|
350
370
|
req_params = {DELETES => opts[:deletes] || @params[:deletes]}
|
351
371
|
uri = jiak_uri(bucket,key) << jiak_qstring(req_params)
|
@@ -383,7 +403,11 @@ module RiakRest
|
|
383
403
|
# JiakObject array.
|
384
404
|
#
|
385
405
|
# See QueryLink for a description of the <code>query</code> structure.
|
386
|
-
def walk(bucket,key,query,data_class)
|
406
|
+
def walk(bucket,key,query,data_class,opts={})
|
407
|
+
check_opts(opts,WALK_PARAMS,JiakClientException)
|
408
|
+
req_params = {
|
409
|
+
READ => opts[:read]
|
410
|
+
}
|
387
411
|
begin
|
388
412
|
start = jiak_uri(bucket,key)
|
389
413
|
case query
|
@@ -395,6 +419,7 @@ module RiakRest
|
|
395
419
|
raise QueryLinkException, 'failed: query must be '+
|
396
420
|
'a QueryLink or an Array of QueryLink objects'
|
397
421
|
end
|
422
|
+
uri << jiak_qstring(req_params)
|
398
423
|
resp = RestClient.get(uri, :accept => APP_JSON)
|
399
424
|
JSON.parse(resp)['results'][0].map do |jiak|
|
400
425
|
JiakObject.jiak_create(jiak,data_class)
|
@@ -7,8 +7,8 @@ module RiakRest
|
|
7
7
|
# user-data instances, rather it facilitates creating the class used to
|
8
8
|
# create user-data instances.
|
9
9
|
#
|
10
|
-
# The class methods <code>
|
11
|
-
# <code>
|
10
|
+
# The class methods <code>attr_reader,attr_writer</code>, and
|
11
|
+
# <code>attr_accessor</code> are used to declare the Jiak readable and
|
12
12
|
# writable fields for a JiakData. The method <code>keygen</code> is used
|
13
13
|
# to specify a block for generating the key for a data instance. By default,
|
14
14
|
# JiakData generates an empty key which is interpreted by the Jiak server as
|
@@ -16,7 +16,7 @@ module RiakRest
|
|
16
16
|
# ====Example
|
17
17
|
# class FooBar
|
18
18
|
# include JiakData
|
19
|
-
#
|
19
|
+
# attr_accessor :foo, :bar
|
20
20
|
# keygen { foo.downcase }
|
21
21
|
# end
|
22
22
|
#
|
@@ -54,29 +54,29 @@ module RiakRest
|
|
54
54
|
module ClassMethods
|
55
55
|
|
56
56
|
# :call-seq:
|
57
|
-
#
|
57
|
+
# attr_reader :f1,...,:fn
|
58
58
|
#
|
59
59
|
# Add read accessible fields.
|
60
|
-
def
|
60
|
+
def attr_reader(*fields)
|
61
61
|
readable *fields
|
62
62
|
nil
|
63
63
|
end
|
64
|
-
alias :
|
64
|
+
alias :attr :attr_reader
|
65
65
|
|
66
66
|
# :call-seq:
|
67
|
-
#
|
67
|
+
# attr_writer :f1,...,:fn
|
68
68
|
#
|
69
69
|
# Add write accessible fields.
|
70
|
-
def
|
70
|
+
def attr_writer(*fields)
|
71
71
|
writable *fields
|
72
72
|
nil
|
73
73
|
end
|
74
74
|
|
75
75
|
# :call-seq:
|
76
|
-
#
|
76
|
+
# attr_accessor :f1,...,:fn
|
77
77
|
#
|
78
78
|
# Add read/write accessible fields.
|
79
|
-
def
|
79
|
+
def attr_accessor(*fields)
|
80
80
|
readable *fields
|
81
81
|
writable *fields
|
82
82
|
end
|
@@ -147,7 +147,17 @@ module RiakRest
|
|
147
147
|
prev_allowed = @schema.allowed_fields
|
148
148
|
added_fields = @schema.send(method,*fields)
|
149
149
|
added_allowed = @schema.allowed_fields - prev_allowed
|
150
|
-
added_allowed.each {|field| attr_accessor field}
|
150
|
+
# added_allowed.each {|field| attr_accessor field}
|
151
|
+
added_allowed.each do |field|
|
152
|
+
class_eval <<-EOH
|
153
|
+
def #{field}
|
154
|
+
@#{field}
|
155
|
+
end
|
156
|
+
def #{field}=(val)
|
157
|
+
@#{field} = val
|
158
|
+
end
|
159
|
+
EOH
|
160
|
+
end
|
151
161
|
added_fields
|
152
162
|
end
|
153
163
|
private :expand_schema
|
@@ -178,7 +188,7 @@ module RiakRest
|
|
178
188
|
# structured Jiak interaction. See JiakSchema for read mask discussion.
|
179
189
|
#
|
180
190
|
# User-defined data classes must either override this method explicitly
|
181
|
-
# or use the <code>
|
191
|
+
# or use the <code>attr_*</code> methods which implicitly override this
|
182
192
|
# method. The method is automatically called to marshall data from
|
183
193
|
# Jiak. You do not call this method explicitly.
|
184
194
|
#
|
@@ -242,11 +252,11 @@ module RiakRest
|
|
242
252
|
# for shema discussion.
|
243
253
|
#
|
244
254
|
# User-defined data classes must either override this method explicitly or
|
245
|
-
# use the <code>
|
255
|
+
# use the <code>attr_*</code> methods which implicitly provide an implicit
|
246
256
|
# override. The method is automatically called to marshall data to
|
247
257
|
# Jiak. You do not call this method explicitly.
|
248
258
|
|
249
|
-
# Data classes that do not used the
|
259
|
+
# Data classes that do not used the attr_* methods to specify attributes
|
250
260
|
# must override this method.
|
251
261
|
#
|
252
262
|
# ====Example
|
@@ -28,15 +28,15 @@ module RiakRest
|
|
28
28
|
if(args.size == 1)
|
29
29
|
case args[0]
|
30
30
|
when Symbol, Array
|
31
|
-
|
31
|
+
attr_accessor *args[0]
|
32
32
|
when JiakSchema
|
33
|
-
|
34
|
-
|
33
|
+
attr_reader *args[0].read_mask
|
34
|
+
attr_writer *args[0].write_mask
|
35
35
|
allow *args[0].allowed_fields
|
36
36
|
require *args[0].required_fields
|
37
37
|
end
|
38
38
|
else
|
39
|
-
|
39
|
+
attr_accessor *args
|
40
40
|
end
|
41
41
|
|
42
42
|
# :call-seq:
|
@@ -1,21 +1,28 @@
|
|
1
1
|
module RiakRest
|
2
2
|
|
3
|
-
# Represents a link used to query linked objects
|
4
|
-
# established using a JiakLink and queried
|
5
|
-
# are very similar but significantly
|
3
|
+
# Represents a link used to query linked objects using Jiak's link/step
|
4
|
+
# map-reduce facility. Links are established using a JiakLink and queried
|
5
|
+
# using a QueryLink. The structures are very similar but significantly
|
6
|
+
# different. A JiakLink is [bucket,key,tag] and a QueryLink is
|
7
|
+
# [bucket,tag,acc]. The accumulator field of a QueryLink allows for
|
8
|
+
# accumulating intermediate link/step results. Since RiakRest auto-inflates
|
9
|
+
# data returned via Jiak link/step processing, the accumulation of
|
10
|
+
# intermediate results is not supported by RiakRest. The setting of the
|
11
|
+
# <code>acc</code> is implemented for future consideration but should not be
|
12
|
+
# used.
|
6
13
|
#
|
7
14
|
# ===Usage
|
8
15
|
# <code>
|
9
16
|
# link = QueryLink.new('people','parent')
|
10
|
-
# link = QueryLink.new(
|
11
|
-
# link = QueryLink.new('blogs',nil,QueryLink::ANY)
|
17
|
+
# link = QueryLink.new('blogs',nil)
|
12
18
|
# </code>
|
13
19
|
class QueryLink
|
14
20
|
|
15
21
|
attr_accessor :bucket, :tag, :acc
|
16
22
|
|
17
|
-
# Jiak
|
23
|
+
# Jiak wildcard character (erlang atom)
|
18
24
|
ANY = '_'
|
25
|
+
ANY.freeze
|
19
26
|
|
20
27
|
# :call-seq:
|
21
28
|
# QueryLink.new(*args) -> QueryLink
|