riakrest 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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?