riakrest 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,28 +10,10 @@ class DogData # :nodoc:
10
10
  allowed :name, :birthdate, :weight, :breed
11
11
  readwrite :name, :birthdate, :weight
12
12
 
13
- def initialize(hsh)
14
- hsh.each {|key,val| send("#{key}=",val)}
15
- end
16
-
17
- def self.create(hsh)
18
- new(hsh)
19
- end
13
+ keygen { name.downcase }
20
14
 
21
15
  def self.jiak_create(jiak)
22
16
  jiak['birthdate'] = Date.parse(jiak['birthdate']) if jiak['birthdate']
23
17
  new(jiak)
24
18
  end
25
-
26
- def for_jiak
27
- self.class.write_mask.inject({}) do |build,field|
28
- val = send("#{field}")
29
- build[field] = val unless val.nil?
30
- build
31
- end
32
- end
33
-
34
- def keygen
35
- @name
36
- end
37
19
  end
@@ -1,46 +1,35 @@
1
1
  module RiakRest
2
2
 
3
- # Data is stored on the Jiak server by key under a bucket. During Jiak
4
- # interaction, the bucket on the server has an associated schema which
5
- # determines permissible data interaction. See JiakSchema for a discussion of
6
- # schemas in Jiak. Since the bucket schema can be changed dynamically,
7
- # schemas can be viewed more as a loose type system rather than an onerous
8
- # restriction.
3
+ # Data in stored in Riak by key in a bucket. Riak introduces structured
4
+ # interaction with the data in a bucket via a concept called a schema. The
5
+ # schema is not a constraint on bucket data, but rather on the interaction
6
+ # with bucket data. See JiakSchema.
9
7
  #
10
8
  # In RiakRest buckets have an associated JiakData class, and each JiakData
11
9
  # class has an associated JiakSchema. These associations facility setting and
12
10
  # maintaining the current schema in use for a Jiak bucket. Dynamically
13
11
  # changing the bucket schema means you can have either homogeneous (simplest)
14
- # or heterogenous data in a single Jiak server bucket. It also means you can
15
- # define multiple JiakData classes that effectively present different "views"
16
- # (via schemas) into the same data stored on the Jiak server. These classes
17
- # act like types that can determine which fields are accessible for reading
18
- # and writing data. The JiakData class associated with a bucket is also used
19
- # to marshal user-defined data going to and from the Jiak server.
20
- #
21
- # JiakResource greatly eases the bookkeeping necessary for heterogenous, as
22
- # well as homogenous, data interaction with the Jiak server.
12
+ # or heterogenous data in a single Jiak bucket. It also means you can define
13
+ # multiple JiakData classes that effectively present different "views" (via
14
+ # schemas) into the same data stored on the Jiak server. These classes act
15
+ # like loose, dynamic types that can determine which fields are accessible
16
+ # for reading and writing data. The JiakData class associated with a bucket
17
+ # is also used to marshal user-defined data to and from the Jiak server.
23
18
  class JiakBucket
24
19
 
25
20
  attr_reader :schema
26
- attr_accessor :name, :data_class, :params
21
+ attr_accessor :name, :data_class
27
22
 
28
23
  # :call-seq:
29
- # JiakBucket.new(name,data_class,params={}) -> JiakBucket
24
+ # JiakBucket.new(name,data_class) -> JiakBucket
30
25
  #
31
26
  # Create a bucket for use in Jiak interaction.
32
27
  #
33
- # Valid optional parameters are <code>params</code> hash are <code>:reads,
34
- # :writes, :durable_writes, :waits</code>. See JiakClient#store,
35
- # JiakClient#get, and JiakClient#delete for discriptions of these
36
- # parameters.
37
- #
38
28
  # Raise JiakBucketException if the bucket name is not a non-empty string or
39
29
  # the data class has not included JiakData.
40
- def initialize(name,data_class,params={})
30
+ def initialize(name,data_class)
41
31
  @name = transform_name(name)
42
32
  @data_class = check_data_class(data_class)
43
- @params = check_params(params)
44
33
  end
45
34
 
46
35
  # :call-seq:
@@ -63,16 +52,6 @@ module RiakRest
63
52
  @data_class = check_data_class(data_class)
64
53
  end
65
54
 
66
- # :call-seq:
67
- # bucket.params = params
68
- #
69
- # Set default params for Jiak client requests. See JiakBucket#new for
70
- # valid parameters.
71
- #
72
- def params=(params)
73
- @params = check_params(params)
74
- end
75
-
76
55
  # :call-seq:
77
56
  # bucket.schema -> JiakSchema
78
57
  #
@@ -93,8 +72,7 @@ module RiakRest
93
72
  # values.
94
73
  def ==(other)
95
74
  (@name == other.name &&
96
- @data_class == other.data_class &&
97
- @params == other.params) rescue false
75
+ @data_class == other.data_class) rescue false
98
76
  end
99
77
 
100
78
  # :call-seq:
@@ -103,13 +81,13 @@ module RiakRest
103
81
  # Returns <code>true</code> if <i>jiak_bucket</i> and <i>other</i> contain
104
82
  # the same attribute values.
105
83
  def eql?(other)
106
- (@name.eql?(other.name) &&
107
- @data_class.eql?(other.data_class) &&
108
- @params.eql?(other.params)) rescue false
84
+ other.is_a?(JiakBucket) &&
85
+ @name.eql?(other.name) &&
86
+ @data_class.eql?(other.data_class)
109
87
  end
110
88
 
111
89
  def hash # :nodoc:
112
- @name.hash + @data_class.hash + @params.hash
90
+ @name.hash + @data_class.hash
113
91
  end
114
92
 
115
93
  def transform_name(name)
@@ -131,16 +109,6 @@ module RiakRest
131
109
  end
132
110
  private :check_data_class
133
111
 
134
- def check_params(params)
135
- valid = [:reads,:writes,:durable_writes,:waits]
136
- err = params.select {|k,v| !valid.include?(k)}
137
- unless err.empty?
138
- raise JiakBucketException, "unrecognized request params: #{err.keys}"
139
- end
140
- params
141
- end
142
- private :check_params
143
-
144
112
  end
145
113
 
146
114
  end
@@ -1,21 +1,60 @@
1
1
  module RiakRest
2
2
 
3
- # Restful client interaction with a Riak document store via a JSON
4
- # interface. See RiakRest for a basic example usage.
3
+ # Client for restful interaction with a Riak document store via the Jiak
4
+ # HTTP/JSON interface. RiakRest Core Client classes expose Jiak server
5
+ # constructs and concepts. JiakResource wraps Jiak interaction at a higher
6
+ # level of abstraction and takes care of much of the Core Client bookkeeping
7
+ # tasks. See JiakResource.
8
+ #
9
+ # ===Example
10
+ # require 'riakrest'
11
+ # include RiakRest
12
+ #
13
+ # class PeopleData
14
+ # include JiakData
15
+ # jattr_accessor :name, :age
16
+ # end
17
+ #
18
+ # client = JiakClient.new("http://localhost:8002/jiak")
19
+ # bucket = JiakBucket.new('people',PeopleData)
20
+ # client.set_schema(bucket)
21
+ #
22
+ # remy = client.store(JiakObject.new(:bucket => bucket,
23
+ # :data => PeopleData.new(:name => "remy",
24
+ # :age => 10)),
25
+ # :return => :object)
26
+ # callie = client.store(JiakObject.new(:bucket => bucket,
27
+ # :data => PeopleData.new(:name => "Callie",
28
+ # :age => 12)),
29
+ # :return => :object)
30
+ #
31
+ # remy.data.name = "Remy"
32
+ # remy << JiakLink.new(bucket,callie.key,'sister')
33
+ # client.store(remy)
34
+ #
35
+ # sisters = client.walk(bucket,remy.key,
36
+ # QueryLink.new(bucket,'sister'),PeopleData)
37
+ # sisters[0].eql?(callie) # => true
38
+ #
39
+ # client.delete(bucket,remy.key)
40
+ # client.delete(bucket,callie.key)
41
+ #
42
+ # See JiakResource for the same example using the RiakRest Resource layer.
5
43
  class JiakClient
6
44
 
7
- # :stopdoc:
8
- APP_JSON = 'application/json'
9
- JSON_DATA = 'json'
45
+ attr_accessor :server, :proxy, :params
10
46
 
11
- RETURN_BODY = 'returnbody'
12
- READS = 'r'
13
- WRITES = 'w'
47
+ # :stopdoc:
48
+ APP_JSON = 'application/json'
49
+ RETURN_BODY = 'returnbody'
50
+ READS = 'r'
51
+ WRITES = 'w'
14
52
  DURABLE_WRITES = 'dw'
15
53
  RESPONSE_WAITS = 'rw'
16
-
17
- KEYS='keys'
18
- SCHEMA='schema'
54
+ KEYS = 'keys'
55
+ SCHEMA = 'schema'
56
+ VALID_PARAMS = [:reads,:writes,:durable_writes,:waits]
57
+ VALID_OPTS = VALID_PARAMS << :proxy
19
58
  # :startdoc:
20
59
 
21
60
  # :call-seq:
@@ -24,44 +63,91 @@ module RiakRest
24
63
  # Create a new client for Riak RESTful (Jiak) interaction with the server at
25
64
  # the specified URI. Go through a proxy if proxy option specified.
26
65
  #
27
- # Valid options:
28
- # <code>:proxy</code> Proxy server URI
29
- #
30
- # Raise JiakClientException if server or proxy (if exists) URI are not strings.
31
- #
32
- def initialize(uri='http://127.0.0.1:8002/jiak/', opts={})
33
- check_opts(opts,[:proxy],JiakClientException)
34
- server(uri)
35
- proxy(opts[:proxy]) if(opts[:proxy])
66
+ # =====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.
71
+ # <code>:proxy</code>:: Proxy server URI
72
+ #
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.
78
+ #
79
+ def initialize(uri, opts={})
80
+ check_opts(opts,VALID_OPTS,JiakClientException)
81
+ self.server = uri
82
+ self.proxy = opts.delete(:proxy)
83
+ self.params = opts
36
84
  end
37
85
 
38
86
  # :call-seq:
39
- # server(uri) -> string
87
+ # server = uri
40
88
  #
41
89
  # Set the Jiak server URI for the client.
42
90
  #
43
91
  # Raise JiakClientException if server URI is not string.
44
- def server(uri)
92
+ def server=(uri)
45
93
  unless uri.is_a?(String)
46
94
  raise JiakClientException, "Jiak server URI should be a string."
47
95
  end
48
- @uri = uri
49
- @uri += '/' unless @uri.end_with?('/')
50
- @uri
96
+ @server = uri
97
+ @server += '/' unless @server.end_with?('/')
98
+ @server
51
99
  end
52
100
 
53
101
  # :call-seq:
54
- # proxy(uri) -> string
102
+ # server -> string
103
+ #
104
+ # Return Jiak server URI
105
+ def server
106
+ @server
107
+ end
108
+
109
+ # :call-seq:
110
+ # proxy = uri
55
111
  #
56
112
  # Set Jiak interaction to go through a proxy.
57
113
  #
58
114
  # Raise JiakClientException if proxy URI is not string.
59
- def proxy(uri)
60
- unless uri.is_a?(String)
61
- raise JiakClientException, "Proxy URI should be a string."
115
+ def proxy=(uri)
116
+ unless(uri.nil?)
117
+ unless uri.is_a?(String)
118
+ raise JiakClientException, "Proxy URI should be a string."
119
+ end
120
+ RestClient.proxy = uri
62
121
  end
63
122
  @proxy = uri
64
- RestClient.proxy = uri
123
+ end
124
+
125
+ # :call-seq:
126
+ # set_params(req_params={}) -> hash
127
+ #
128
+ # Set specified request parameters without changing unspecified ones. This
129
+ # method merges the passed parameters with the current settings.
130
+ def set_params(req_params={})
131
+ check_opts(req_params,VALID_PARAMS,JiakClientException)
132
+ @params.merge!(req_params)
133
+ end
134
+
135
+ # :call-seq:
136
+ # params = req_params
137
+ #
138
+ # Set default params for Jiak client requests.
139
+ #
140
+ def params=(req_params)
141
+ check_opts(req_params,VALID_PARAMS,JiakClientException)
142
+ @params = req_params
143
+ end
144
+
145
+ # :call-seq:
146
+ # params -> hash
147
+ #
148
+ # Copy of the current parameters hash.
149
+ def params
150
+ @params.dup
65
151
  end
66
152
 
67
153
  # :call-seq:
@@ -77,7 +163,7 @@ module RiakRest
77
163
  raise JiakClientException, "Bucket must be a JiakBucket."
78
164
  end
79
165
  resp = RestClient.put(jiak_uri(bucket),
80
- bucket.schema.to_jiak,
166
+ bucket.schema.to_jiak.to_json,
81
167
  :content_type => APP_JSON,
82
168
  :accept => APP_JSON)
83
169
  end
@@ -89,7 +175,7 @@ module RiakRest
89
175
  # to the Jiak server. See JiakBucket#schema for a way to get this
90
176
  # information without server access.
91
177
  def schema(bucket)
92
- JiakSchema.from_jiak(bucket_info(bucket,SCHEMA))
178
+ JiakSchema.jiak_create(bucket_info(bucket,SCHEMA))
93
179
  end
94
180
 
95
181
  # :call-seq:
@@ -105,23 +191,18 @@ module RiakRest
105
191
  # :call-seq:
106
192
  # store(object,opts={}) -> JiakObject or key
107
193
  #
108
- # Stores user-defined data (wrapped in a JiakObject) on the Jiak
109
- # server. JiakData#to_jiak is used to prepare user-defined data for JSON
110
- # transport to Jiak. That call is expected to return a Ruby hash
111
- # representation of the writable JiakData fields that are JSONized for HTTP
112
- # transport. Successful server writes return either the storage key or the
194
+ # Stores user-defined data (wrapped as a JiakObject) in Riak.
195
+ # Successful server writes return either the storage key or the
113
196
  # stored JiakObject depending on the option <code>key</code>. The object
114
- # for storage must be JiakObject. Valid options are:
197
+ # for storage must be JiakObject.
115
198
  #
116
- # <code>:return</code> :: If <code>:key</code>, return the key under which the data was stored. If <code>:object</code> return the stored JiakObject (which includes Riak context). Defaults to <code>:key</code>.
117
- # <code>:writes</code> :: The number of Riak nodes that must successfully store the data.
118
- # <code>:durable_writes</code> :: The number of Riak nodes (<code>< writes</code>) that must successfully store the data in a durable manner.
119
- # <code>:reads</code> :: The number of Riak nodes that must successfully read data if a JiakObject is being returned.
120
- #
121
- # If any of the request parameters <code>:writes, :durable_writes,
122
- # :reads</code> are not set, each first defaults to the value set for the
123
- # JiakBucket in the JiakObject, then to the value set on the Riak
124
- # cluster. In general the values set on the Riak cluster should suffice.
199
+ # =====Valid options
200
+ # <code>:return</code> -- <code>:key</code> (default), returns the key
201
+ # use to store the data, or <code>:object</code> returns the stored
202
+ # JiakObject with included Riak context.<br/>
203
+ # <code>:writes</code><br/>
204
+ # <code>:durable_writes</code><br/>
205
+ # <code>:reads</code><br/>
125
206
  #
126
207
  # Raise JiakClientException if object not a JiakObject or illegal options
127
208
  # are passed.<br/>
@@ -130,17 +211,16 @@ module RiakRest
130
211
  def store(jobj,opts={})
131
212
  check_opts(opts,[:return,:reads,:writes,:durable_writes],
132
213
  JiakClientException)
133
- params = jobj.bucket.params
134
214
  req_params = {
135
- WRITES => opts[:writes] || params[:writes],
136
- DURABLE_WRITES => opts[:durable_writes] || params[:durable_writes],
137
- READS => opts[:reads] || params[:reads]
215
+ WRITES => opts[:writes] || @params[:writes],
216
+ DURABLE_WRITES => opts[:durable_writes] || @params[:durable_writes],
217
+ READS => opts[:reads] || @params[:reads]
138
218
  }
139
219
  req_params[RETURN_BODY] = (opts[:return] == :object)
140
220
 
141
221
  begin
142
222
  uri = jiak_uri(jobj.bucket,jobj.key,req_params)
143
- payload = jobj.to_jiak
223
+ payload = jobj.to_jiak.to_json
144
224
  headers = {
145
225
  :content_type => APP_JSON,
146
226
  :accept => APP_JSON }
@@ -159,7 +239,7 @@ module RiakRest
159
239
  end
160
240
 
161
241
  if(req_params[RETURN_BODY])
162
- JiakObject.from_jiak(JSON.parse(resp),jobj.bucket.data_class)
242
+ JiakObject.jiak_create(JSON.parse(resp),jobj.bucket.data_class)
163
243
  elsif(key_empty)
164
244
  resp.headers[:location].split('/').last
165
245
  else
@@ -187,12 +267,10 @@ module RiakRest
187
267
  # in a bucket this will not be an issue.
188
268
  #
189
269
  # The bucket must be a JiakBucket and the key must be a non-empty
190
- # String. Valid options are:
270
+ # String.
191
271
  #
192
- # <code>:reads</code> --- The number of Riak nodes that must successfully
193
- # reply with the data. If not set, defaults first to the value set for the
194
- # JiakBucket, then to the value set on the Riak cluster. In general the
195
- # values set on the Riak cluster should suffice.
272
+ # =====Valid options
273
+ # <code>:reads</code>
196
274
  #
197
275
  # Raise JiakClientException if bucket not a JiakBucket.<br/>
198
276
  # Raise JiakResourceNotFound if resource not found on Jiak server.<br/>
@@ -203,12 +281,12 @@ module RiakRest
203
281
  raise JiakClientException, "Bucket must be a JiakBucket."
204
282
  end
205
283
  check_opts(opts,[:reads],JiakClientException)
206
- req_params = {READS => opts[:reads] || bucket.params[:reads]}
284
+ req_params = {READS => opts[:reads] || @params[:reads]}
207
285
 
208
286
  begin
209
287
  uri = jiak_uri(bucket,key,req_params)
210
288
  resp = RestClient.get(uri, :accept => APP_JSON)
211
- JiakObject.from_jiak(JSON.parse(resp),bucket.data_class)
289
+ JiakObject.jiak_create(JSON.parse(resp),bucket.data_class)
212
290
  rescue RestClient::ResourceNotFound => err
213
291
  raise JiakResourceNotFound, "failed get: #{err.message}"
214
292
  rescue RestClient::ExceptionWithResponse => err
@@ -221,19 +299,17 @@ module RiakRest
221
299
  # :call-seq:
222
300
  # delete(bucket,key,opts={}) -> true or false
223
301
  #
224
- # Delete the JiakObject stored at the bucket/key. Valid options are:
302
+ # Delete the JiakObject stored at the bucket/key.
225
303
  #
226
- # <code>:waits</code> --- The number of Riak nodes that must reply the
227
- # delete has occurred before success. If not set, defaults first to the
228
- # value set for the JiakBucket, then to the value set on the Riak
229
- # cluster. In general the values set on the Riak cluster should suffice.
304
+ # =====Valid options
305
+ # <code>:waits</code>
230
306
  #
231
307
  # Raise JiakResourceException on RESTful HTTP errors.
232
308
  #
233
309
  def delete(bucket,key,opts={})
234
310
  check_opts(opts,[:waits],JiakClientException)
235
311
  begin
236
- req_params = {RESPONSE_WAITS => opts[:waits] || bucket.params[:waits]}
312
+ req_params = {RESPONSE_WAITS => opts[:waits] || @params[:waits]}
237
313
  uri = jiak_uri(bucket,key,req_params)
238
314
  RestClient.delete(uri, :accept => APP_JSON)
239
315
  true
@@ -268,7 +344,7 @@ module RiakRest
268
344
  resp = RestClient.get(uri, :accept => APP_JSON)
269
345
  # JSON.parse(resp)['results'][0]
270
346
  JSON.parse(resp)['results'][0].map do |jiak|
271
- JiakObject.from_jiak(jiak,data_class)
347
+ JiakObject.jiak_create(jiak,data_class)
272
348
  end
273
349
  rescue RestClient::ExceptionWithResponse => err
274
350
  fail_with_response("put", err)
@@ -277,20 +353,12 @@ module RiakRest
277
353
  end
278
354
  end
279
355
 
280
- # :call-seq:
281
- # client.uri -> string
282
- #
283
- # String representation of the base URI of the Jiak server.
284
- def uri
285
- @uri
286
- end
287
-
288
356
  # :call-seq:
289
357
  # client == other -> true or false
290
358
  #
291
359
  # Equality -- JiakClients are equal if they have the same URI
292
360
  def ==(other)
293
- (@uri == other.uri) rescue false
361
+ (@server == other.server) rescue false
294
362
  end
295
363
 
296
364
  # :call-seq:
@@ -300,16 +368,16 @@ module RiakRest
300
368
  # same URI.
301
369
  def eql?(other)
302
370
  other.is_a?(JiakClient) &&
303
- @uri.eql?(other.uri)
371
+ @server.eql?(other.server)
304
372
  end
305
373
 
306
374
  def hash # :nodoc:
307
- @uri.hash
375
+ @server.hash
308
376
  end
309
377
 
310
378
  # Build the URI for accessing the Jiak server.
311
379
  def jiak_uri(bucket,key="",params={})
312
- uri = "#{@uri}#{URI.encode(bucket.name)}"
380
+ uri = "#{@server}#{URI.encode(bucket.name)}"
313
381
  uri += "/#{URI.encode(key)}" unless key.empty?
314
382
  qstring = params.reject {|k,v| v.nil?}.map{|k,v| "#{k}=#{v}"}.join('&')
315
383
  uri += "?#{URI.encode(qstring)}" unless qstring.empty?