riakrest 0.1.2 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,14 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/example_helper.rb'
2
2
 
3
- class Parents
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
- # invert relationships
20
- child_parents = parent_children.inject({}) do |build, (p,cs)|
21
- cs.each do |c|
22
- build[c] ? build[c] << p : build[c] = [p]
23
- end
24
- build
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
- # store data and relationships
22
+ # Store data and relationships
28
23
  parent_children.each do |pname,cnames|
29
- p = Parents.new(:name => pname).post
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
- # retrieve parents
44
- parents = parent_children.keys.map {|p| Parents.get(p)}
45
- p0,p1,p2,p3 = parents
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
- # retrieve parent children
54
- p0c,p1c,p2c,p3c = parents.map {|p| p.query(Child,'child')}
55
- puts p2c[0].name # => 'c2' (could be 'c3')
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
- # retrieve children parents
58
- c0p,c1p,c2p,c3p = children.map {|c| c.query(Parents,'parent')}
59
- puts c3p[0].name # => 'p2'
60
- puts c3p[1].name # => 'p3'
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
- # retrieve children siblings
63
- c0s,c1s,c2s,c3s = children.map do |c|
64
- c.query(Parents,'parent',Child,'child').delete_if{|s| s.eql?(c)}
65
- end
66
- puts c3s[0].name # => 'c2'
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
- # who is c3's step-sibling's other parent?
69
- c3sp = c3.query(Parents,'parent',Child,'child',Parents,'parent')
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[0].name # => "p1"
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
- # add sibling links
63
+ # Add sibling links using existing links (say, for convenience)
78
64
  children.each do |c|
79
- siblings = c.query(Parents,'parent',Child,'child').delete_if{|s| s.eql?(c)}
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
- # do any odd parents have normal children?
101
- op = parents.inject([]) do |build,parent|
102
- build << parent.query(Child,'normal',Parents,'odd')
103
- build.flatten.uniq
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
- # clean-up by deleting everybody
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
- # jattr_accessor :name, :age
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 = 'application/json'
48
+ APP_JSON = 'application/json'
49
49
  APP_JSON.freeze
50
50
 
51
- RETURN_BODY = 'returnbody'
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
- VALID_PARAMS = [:reads,:writes,:durable_writes,:deletes]
63
- VALID_PARAMS.freeze
64
- VALID_OPTS = Array.new(VALID_PARAMS) << :proxy
65
- VALID_OPTS.freeze
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
- KEYS = 'keys'
68
- KEYS.freeze
69
- SCHEMA = 'schema'
70
- SCHEMA.freeze
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 resonding nodes that must perform a durable write to the persistence layer.
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,VALID_OPTS,JiakClientException)
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 ones. This
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,VALID_PARAMS,JiakClientException)
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,VALID_PARAMS,JiakClientException)
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>reades</code> parameters. The
238
- # <code>reads</code> parameter only takes effect if the JiakObject is being
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,[:return,:reads,:writes,:durable_writes],
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,[:reads],JiakClientException)
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,[:deletes],JiakClientException)
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>jattr_reader,jattr_writer</code>, and
11
- # <code>jattr_accessor</code> are used to declare the Jiak readable and
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
- # jattr_accessor :foo, :bar
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
- # jattr_reader :f1,...,:fn
57
+ # attr_reader :f1,...,:fn
58
58
  #
59
59
  # Add read accessible fields.
60
- def jattr_reader(*fields)
60
+ def attr_reader(*fields)
61
61
  readable *fields
62
62
  nil
63
63
  end
64
- alias :jattr :jattr_reader
64
+ alias :attr :attr_reader
65
65
 
66
66
  # :call-seq:
67
- # jattr_writer :f1,...,:fn
67
+ # attr_writer :f1,...,:fn
68
68
  #
69
69
  # Add write accessible fields.
70
- def jattr_writer(*fields)
70
+ def attr_writer(*fields)
71
71
  writable *fields
72
72
  nil
73
73
  end
74
74
 
75
75
  # :call-seq:
76
- # jattr_accessor :f1,...,:fn
76
+ # attr_accessor :f1,...,:fn
77
77
  #
78
78
  # Add read/write accessible fields.
79
- def jattr_accessor(*fields)
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>jattr_*</code> methods which implicitly override this
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>jattr_*</code> methods which implicitly provide an implicit
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 jattr_* methods to specify attributes
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
- jattr_accessor *args[0]
31
+ attr_accessor *args[0]
32
32
  when JiakSchema
33
- jattr_reader *args[0].read_mask
34
- jattr_writer *args[0].write_mask
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
- jattr_accessor *args
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 in Jiak. Links are
4
- # established using a JiakLink and queried using a QueryLink. The structures
5
- # are very similar but significantly different.
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(['children','odd','_'])
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 (erlang) wildcard character (atom)
23
+ # Jiak wildcard character (erlang atom)
18
24
  ANY = '_'
25
+ ANY.freeze
19
26
 
20
27
  # :call-seq:
21
28
  # QueryLink.new(*args) -> QueryLink