couchbase 1.1.5 → 1.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +12 -1
  3. data/HISTORY.markdown +112 -1
  4. data/README.markdown +149 -6
  5. data/couchbase.gemspec +5 -1
  6. data/ext/couchbase_ext/.gitignore +4 -0
  7. data/ext/couchbase_ext/arguments.c +973 -0
  8. data/ext/couchbase_ext/arithmetic.c +322 -0
  9. data/ext/couchbase_ext/bucket.c +1092 -0
  10. data/ext/couchbase_ext/couchbase_ext.c +618 -3247
  11. data/ext/couchbase_ext/couchbase_ext.h +519 -0
  12. data/ext/couchbase_ext/delete.c +167 -0
  13. data/ext/couchbase_ext/extconf.rb +24 -5
  14. data/ext/couchbase_ext/get.c +301 -0
  15. data/ext/couchbase_ext/gethrtime.c +124 -0
  16. data/ext/couchbase_ext/http.c +402 -0
  17. data/ext/couchbase_ext/observe.c +174 -0
  18. data/ext/couchbase_ext/result.c +126 -0
  19. data/ext/couchbase_ext/stats.c +169 -0
  20. data/ext/couchbase_ext/store.c +522 -0
  21. data/ext/couchbase_ext/timer.c +192 -0
  22. data/ext/couchbase_ext/touch.c +190 -0
  23. data/ext/couchbase_ext/unlock.c +180 -0
  24. data/ext/couchbase_ext/utils.c +471 -0
  25. data/ext/couchbase_ext/version.c +147 -0
  26. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  27. data/lib/active_support/cache/couchbase_store.rb +356 -0
  28. data/lib/couchbase.rb +24 -3
  29. data/lib/couchbase/bucket.rb +372 -9
  30. data/lib/couchbase/result.rb +26 -0
  31. data/lib/couchbase/utils.rb +59 -0
  32. data/lib/couchbase/version.rb +1 -1
  33. data/lib/couchbase/view.rb +305 -0
  34. data/lib/couchbase/view_row.rb +230 -0
  35. data/lib/ext/multi_json_fix.rb +47 -0
  36. data/lib/rack/session/couchbase.rb +104 -0
  37. data/tasks/compile.rake +5 -14
  38. data/test/setup.rb +6 -2
  39. data/test/test_arithmetic.rb +32 -2
  40. data/test/test_async.rb +18 -4
  41. data/test/test_bucket.rb +11 -1
  42. data/test/test_cas.rb +13 -3
  43. data/test/test_couchbase_rails_cache_store.rb +294 -0
  44. data/test/test_delete.rb +60 -3
  45. data/test/test_format.rb +28 -17
  46. data/test/test_get.rb +91 -14
  47. data/test/test_store.rb +31 -1
  48. data/test/{test_flush.rb → test_timer.rb} +11 -18
  49. data/test/test_touch.rb +33 -5
  50. data/test/test_unlock.rb +120 -0
  51. data/test/test_utils.rb +26 -0
  52. metadata +101 -8
@@ -0,0 +1,522 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ static VALUE
21
+ storage_observe_callback(VALUE args, VALUE cookie)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ VALUE res = rb_ary_shift(args);
25
+
26
+ if (ctx->proc != Qnil) {
27
+ rb_ivar_set(res, id_iv_operation, ctx->operation);
28
+ cb_proc_call(ctx->proc, 1, res);
29
+ }
30
+ if (!RTEST(ctx->observe_options)) {
31
+ ctx->nqueries--;
32
+ if (ctx->nqueries == 0) {
33
+ cb_gc_unprotect(ctx->bucket, ctx->proc);
34
+ }
35
+ }
36
+ return Qnil;
37
+ }
38
+
39
+ void
40
+ storage_callback(lcb_t handle, const void *cookie, lcb_storage_t operation,
41
+ lcb_error_t error, const lcb_store_resp_t *resp)
42
+ {
43
+ struct context_st *ctx = (struct context_st *)cookie;
44
+ struct bucket_st *bucket = ctx->bucket;
45
+ VALUE key, cas, *rv = ctx->rv, exc, res;
46
+
47
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
48
+ strip_key_prefix(bucket, key);
49
+
50
+ cas = resp->v.v0.cas > 0 ? ULL2NUM(resp->v.v0.cas) : Qnil;
51
+ switch(operation) {
52
+ case LCB_ADD:
53
+ ctx->operation = sym_add;
54
+ break;
55
+ case LCB_REPLACE:
56
+ ctx->operation = sym_replace;
57
+ break;
58
+ case LCB_SET:
59
+ ctx->operation = sym_set;
60
+ break;
61
+ case LCB_APPEND:
62
+ ctx->operation = sym_append;
63
+ break;
64
+ case LCB_PREPEND:
65
+ ctx->operation = sym_prepend;
66
+ break;
67
+ default:
68
+ ctx->operation = Qnil;
69
+ }
70
+ exc = cb_check_error(error, "failed to store value", key);
71
+ if (exc != Qnil) {
72
+ rb_ivar_set(exc, id_iv_cas, cas);
73
+ rb_ivar_set(exc, id_iv_operation, ctx->operation);
74
+ if (NIL_P(ctx->exception)) {
75
+ ctx->exception = cb_gc_protect(bucket, exc);
76
+ }
77
+ }
78
+
79
+ if (bucket->async) { /* asynchronous */
80
+ if (RTEST(ctx->observe_options)) {
81
+ VALUE args[2]; /* it's ok to pass pointer to stack struct here */
82
+ args[0] = rb_hash_new();
83
+ rb_hash_aset(args[0], key, cas);
84
+ args[1] = ctx->observe_options;
85
+ rb_block_call(bucket->self, id_observe_and_wait, 2, args,
86
+ storage_observe_callback, (VALUE)ctx);
87
+ cb_gc_unprotect(bucket, ctx->observe_options);
88
+ } else if (ctx->proc != Qnil) {
89
+ res = rb_class_new_instance(0, NULL, cResult);
90
+ rb_ivar_set(res, id_iv_error, exc);
91
+ rb_ivar_set(res, id_iv_key, key);
92
+ rb_ivar_set(res, id_iv_operation, ctx->operation);
93
+ rb_ivar_set(res, id_iv_cas, cas);
94
+ cb_proc_call(ctx->proc, 1, res);
95
+ }
96
+ } else { /* synchronous */
97
+ rb_hash_aset(*rv, key, cas);
98
+ }
99
+
100
+ if (!RTEST(ctx->observe_options)) {
101
+ ctx->nqueries--;
102
+ if (ctx->nqueries == 0) {
103
+ cb_gc_unprotect(bucket, ctx->proc);
104
+ }
105
+ }
106
+ (void)handle;
107
+ }
108
+
109
+ static inline VALUE
110
+ cb_bucket_store(lcb_storage_t cmd, int argc, VALUE *argv, VALUE self)
111
+ {
112
+ struct bucket_st *bucket = DATA_PTR(self);
113
+ struct context_st *ctx;
114
+ VALUE args, rv, proc, exc, obs = Qnil;
115
+ lcb_error_t err;
116
+ struct params_st params;
117
+
118
+ if (bucket->handle == NULL) {
119
+ rb_raise(eConnectError, "closed connection");
120
+ }
121
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
122
+ if (!bucket->async && proc != Qnil) {
123
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
124
+ }
125
+ memset(&params, 0, sizeof(struct params_st));
126
+ params.type = cmd_store;
127
+ params.bucket = bucket;
128
+ params.cmd.store.operation = cmd;
129
+ cb_params_build(&params, RARRAY_LEN(args), args);
130
+ ctx = xcalloc(1, sizeof(struct context_st));
131
+ if (ctx == NULL) {
132
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
133
+ }
134
+ rv = rb_hash_new();
135
+ ctx->rv = &rv;
136
+ ctx->bucket = bucket;
137
+ ctx->proc = cb_gc_protect(bucket, proc);
138
+ ctx->observe_options = cb_gc_protect(bucket, obs);
139
+ ctx->exception = Qnil;
140
+ ctx->nqueries = params.cmd.store.num;
141
+ err = lcb_store(bucket->handle, (const void *)ctx,
142
+ params.cmd.store.num, params.cmd.store.ptr);
143
+ cb_params_destroy(&params);
144
+ exc = cb_check_error(err, "failed to schedule set request", Qnil);
145
+ if (exc != Qnil) {
146
+ xfree(ctx);
147
+ rb_exc_raise(exc);
148
+ }
149
+ bucket->nbytes += params.npayload;
150
+ if (bucket->async) {
151
+ maybe_do_loop(bucket);
152
+ return Qnil;
153
+ } else {
154
+ if (ctx->nqueries > 0) {
155
+ /* we have some operations pending */
156
+ lcb_wait(bucket->handle);
157
+ }
158
+ exc = ctx->exception;
159
+ xfree(ctx);
160
+ if (exc != Qnil) {
161
+ cb_gc_unprotect(bucket, exc);
162
+ rb_exc_raise(exc);
163
+ }
164
+ if (bucket->exception != Qnil) {
165
+ rb_exc_raise(bucket->exception);
166
+ }
167
+ if (RTEST(obs)) {
168
+ cb_gc_unprotect(bucket, obs);
169
+ return rb_funcall(bucket->self, id_observe_and_wait, 2, rv, obs);
170
+ }
171
+ if (params.cmd.store.num > 1) {
172
+ return rv; /* return as a hash {key => cas, ...} */
173
+ } else {
174
+ VALUE vv = Qnil;
175
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
176
+ return vv;
177
+ }
178
+ }
179
+ }
180
+
181
+ /*
182
+ * Unconditionally store the object in the Couchbase
183
+ *
184
+ * @since 1.0.0
185
+ *
186
+ * @overload set(key, value, options = {})
187
+ *
188
+ * @param key [String, Symbol] Key used to reference the value.
189
+ * @param value [Object] Value to be stored
190
+ * @param options [Hash] Options for operation.
191
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
192
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
193
+ * absolute times (from the epoch).
194
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
195
+ * options. Flags are ignored by the server but preserved for use by the
196
+ * client. For more info see {Bucket#default_flags}.
197
+ * @option options [Symbol] :format (self.default_format) The
198
+ * representation for storing the value in the bucket. For more info see
199
+ * {Bucket#default_format}.
200
+ * @option options [Fixnum] :cas The CAS value for an object. This value
201
+ * created on the server and is guaranteed to be unique for each value of
202
+ * a given key. This value is used to provide simple optimistic
203
+ * concurrency control when multiple clients or threads try to update an
204
+ * item simultaneously.
205
+ * @option options [Hash] :observe Apply persistence condition before
206
+ * returning result. When this option specified the library will observe
207
+ * given condition. See {Bucket#observe_and_wait}.
208
+ *
209
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
210
+ * (valid attributes: +error+, +operation+, +key+).
211
+ *
212
+ * @return [Fixnum] The CAS value of the object.
213
+ *
214
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect}).
215
+ * @raise [Couchbase::Error::KeyExists] if the key already exists on the
216
+ * server.
217
+ * @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
218
+ * with chosen encoder, e.g. if you try to store the Hash in +:plain+
219
+ * mode.
220
+ * @raise [ArgumentError] when passing the block in synchronous mode
221
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
222
+ * exceeds
223
+ *
224
+ * @example Store the key which will be expired in 2 seconds using relative TTL.
225
+ * c.set("foo", "bar", :ttl => 2)
226
+ *
227
+ * @example Store the key which will be expired in 2 seconds using absolute TTL.
228
+ * c.set("foo", "bar", :ttl => Time.now.to_i + 2)
229
+ *
230
+ * @example Force JSON document format for value
231
+ * c.set("foo", {"bar" => "baz}, :format => :document)
232
+ *
233
+ * @example Use hash-like syntax to store the value
234
+ * c.set["foo"] = {"bar" => "baz}
235
+ *
236
+ * @example Use extended hash-like syntax
237
+ * c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
238
+ * c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
239
+ *
240
+ * @example Set application specific flags (note that it will be OR-ed with format flags)
241
+ * c.set("foo", "bar", :flags => 0x1000)
242
+ *
243
+ * @example Perform optimistic locking by specifying last known CAS version
244
+ * c.set("foo", "bar", :cas => 8835713818674332672)
245
+ *
246
+ * @example Perform asynchronous call
247
+ * c.run do
248
+ * c.set("foo", "bar") do |ret|
249
+ * ret.operation #=> :set
250
+ * ret.success? #=> true
251
+ * ret.key #=> "foo"
252
+ * ret.cas
253
+ * end
254
+ * end
255
+ *
256
+ * @example Ensure that the key will be persisted at least on the one node
257
+ * c.set("foo", "bar", :observe => {:persisted => 1})
258
+ */
259
+ VALUE
260
+ cb_bucket_set(int argc, VALUE *argv, VALUE self)
261
+ {
262
+ return cb_bucket_store(LCB_SET, argc, argv, self);
263
+ }
264
+
265
+ /*
266
+ * Add the item to the database, but fail if the object exists already
267
+ *
268
+ * @since 1.0.0
269
+ *
270
+ * @overload add(key, value, options = {})
271
+ *
272
+ * @param key [String, Symbol] Key used to reference the value.
273
+ * @param value [Object] Value to be stored
274
+ * @param options [Hash] Options for operation.
275
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
276
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
277
+ * absolute times (from the epoch).
278
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
279
+ * options. Flags are ignored by the server but preserved for use by the
280
+ * client. For more info see {Bucket#default_flags}.
281
+ * @option options [Symbol] :format (self.default_format) The
282
+ * representation for storing the value in the bucket. For more info see
283
+ * {Bucket#default_format}.
284
+ * @option options [Fixnum] :cas The CAS value for an object. This value
285
+ * created on the server and is guaranteed to be unique for each value of
286
+ * a given key. This value is used to provide simple optimistic
287
+ * concurrency control when multiple clients or threads try to update an
288
+ * item simultaneously.
289
+ * @option options [Hash] :observe Apply persistence condition before
290
+ * returning result. When this option specified the library will observe
291
+ * given condition. See {Bucket#observe_and_wait}.
292
+ *
293
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
294
+ * (valid attributes: +error+, +operation+, +key+).
295
+ *
296
+ * @return [Fixnum] The CAS value of the object.
297
+ *
298
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
299
+ * @raise [Couchbase::Error::KeyExists] if the key already exists on the
300
+ * server
301
+ * @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
302
+ * with chosen encoder, e.g. if you try to store the Hash in +:plain+
303
+ * mode.
304
+ * @raise [ArgumentError] when passing the block in synchronous mode
305
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
306
+ * exceeds
307
+ *
308
+ * @example Add the same key twice
309
+ * c.add("foo", "bar") #=> stored successully
310
+ * c.add("foo", "baz") #=> will raise Couchbase::Error::KeyExists: failed to store value (key="foo", error=0x0c)
311
+ *
312
+ * @example Ensure that the key will be persisted at least on the one node
313
+ * c.add("foo", "bar", :observe => {:persisted => 1})
314
+ */
315
+ VALUE
316
+ cb_bucket_add(int argc, VALUE *argv, VALUE self)
317
+ {
318
+ return cb_bucket_store(LCB_ADD, argc, argv, self);
319
+ }
320
+
321
+ /*
322
+ * Replace the existing object in the database
323
+ *
324
+ * @since 1.0.0
325
+ *
326
+ * @overload replace(key, value, options = {})
327
+ * @param key [String, Symbol] Key used to reference the value.
328
+ * @param value [Object] Value to be stored
329
+ * @param options [Hash] Options for operation.
330
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
331
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
332
+ * absolute times (from the epoch).
333
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
334
+ * options. Flags are ignored by the server but preserved for use by the
335
+ * client. For more info see {Bucket#default_flags}.
336
+ * @option options [Symbol] :format (self.default_format) The
337
+ * representation for storing the value in the bucket. For more info see
338
+ * {Bucket#default_format}.
339
+ * @option options [Fixnum] :cas The CAS value for an object. This value
340
+ * created on the server and is guaranteed to be unique for each value of
341
+ * a given key. This value is used to provide simple optimistic
342
+ * concurrency control when multiple clients or threads try to update an
343
+ * item simultaneously.
344
+ * @option options [Hash] :observe Apply persistence condition before
345
+ * returning result. When this option specified the library will observe
346
+ * given condition. See {Bucket#observe_and_wait}.
347
+ *
348
+ * @return [Fixnum] The CAS value of the object.
349
+ *
350
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
351
+ * @raise [Couchbase::Error::NotFound] if the key doesn't exists
352
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
353
+ * @raise [ArgumentError] when passing the block in synchronous mode
354
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
355
+ * exceeds
356
+ *
357
+ * @example Replacing missing key
358
+ * c.replace("foo", "baz") #=> will raise Couchbase::Error::NotFound: failed to store value (key="foo", error=0x0d)
359
+ *
360
+ * @example Ensure that the key will be persisted at least on the one node
361
+ * c.replace("foo", "bar", :observe => {:persisted => 1})
362
+ */
363
+ VALUE
364
+ cb_bucket_replace(int argc, VALUE *argv, VALUE self)
365
+ {
366
+ return cb_bucket_store(LCB_REPLACE, argc, argv, self);
367
+ }
368
+
369
+ /*
370
+ * Append this object to the existing object
371
+ *
372
+ * @since 1.0.0
373
+ *
374
+ * @note This operation is kind of data-aware from server point of view.
375
+ * This mean that the server treats value as binary stream and just
376
+ * perform concatenation, therefore it won't work with +:marshal+ and
377
+ * +:document+ formats, because of lack of knowledge how to merge values
378
+ * in these formats. See {Bucket#cas} for workaround.
379
+ *
380
+ * @overload append(key, value, options = {})
381
+ * @param key [String, Symbol] Key used to reference the value.
382
+ * @param value [Object] Value to be stored
383
+ * @param options [Hash] Options for operation.
384
+ * @option options [Fixnum] :cas The CAS value for an object. This value
385
+ * created on the server and is guaranteed to be unique for each value of
386
+ * a given key. This value is used to provide simple optimistic
387
+ * concurrency control when multiple clients or threads try to update an
388
+ * item simultaneously.
389
+ * @option options [Symbol] :format (self.default_format) The
390
+ * representation for storing the value in the bucket. For more info see
391
+ * {Bucket#default_format}.
392
+ * @option options [Hash] :observe Apply persistence condition before
393
+ * returning result. When this option specified the library will observe
394
+ * given condition. See {Bucket#observe_and_wait}.
395
+ *
396
+ * @return [Fixnum] The CAS value of the object.
397
+ *
398
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
399
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
400
+ * @raise [Couchbase::Error::NotStored] if the key doesn't exist
401
+ * @raise [ArgumentError] when passing the block in synchronous mode
402
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
403
+ * exceeds
404
+ *
405
+ * @example Simple append
406
+ * c.set("foo", "aaa")
407
+ * c.append("foo", "bbb")
408
+ * c.get("foo") #=> "aaabbb"
409
+ *
410
+ * @example Implementing sets using append
411
+ * def set_add(key, *values)
412
+ * encoded = values.flatten.map{|v| "+#{v} "}.join
413
+ * append(key, encoded)
414
+ * end
415
+ *
416
+ * def set_remove(key, *values)
417
+ * encoded = values.flatten.map{|v| "-#{v} "}.join
418
+ * append(key, encoded)
419
+ * end
420
+ *
421
+ * def set_get(key)
422
+ * encoded = get(key)
423
+ * ret = Set.new
424
+ * encoded.split(' ').each do |v|
425
+ * op, val = v[0], v[1..-1]
426
+ * case op
427
+ * when "-"
428
+ * ret.delete(val)
429
+ * when "+"
430
+ * ret.add(val)
431
+ * end
432
+ * end
433
+ * ret
434
+ * end
435
+ *
436
+ * @example Using optimistic locking. The operation will fail on CAS mismatch
437
+ * ver = c.set("foo", "aaa")
438
+ * c.append("foo", "bbb", :cas => ver)
439
+ *
440
+ * @example Ensure that the key will be persisted at least on the one node
441
+ * c.append("foo", "bar", :observe => {:persisted => 1})
442
+ */
443
+ VALUE
444
+ cb_bucket_append(int argc, VALUE *argv, VALUE self)
445
+ {
446
+ return cb_bucket_store(LCB_APPEND, argc, argv, self);
447
+ }
448
+
449
+ /*
450
+ * Prepend this object to the existing object
451
+ *
452
+ * @since 1.0.0
453
+ *
454
+ * @note This operation is kind of data-aware from server point of view.
455
+ * This mean that the server treats value as binary stream and just
456
+ * perform concatenation, therefore it won't work with +:marshal+ and
457
+ * +:document+ formats, because of lack of knowledge how to merge values
458
+ * in these formats. See {Bucket#cas} for workaround.
459
+ *
460
+ * @overload prepend(key, value, options = {})
461
+ * @param key [String, Symbol] Key used to reference the value.
462
+ * @param value [Object] Value to be stored
463
+ * @param options [Hash] Options for operation.
464
+ * @option options [Fixnum] :cas The CAS value for an object. This value
465
+ * created on the server and is guaranteed to be unique for each value of
466
+ * a given key. This value is used to provide simple optimistic
467
+ * concurrency control when multiple clients or threads try to update an
468
+ * item simultaneously.
469
+ * @option options [Symbol] :format (self.default_format) The
470
+ * representation for storing the value in the bucket. For more info see
471
+ * {Bucket#default_format}.
472
+ * @option options [Hash] :observe Apply persistence condition before
473
+ * returning result. When this option specified the library will observe
474
+ * given condition. See {Bucket#observe_and_wait}.
475
+ *
476
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
477
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
478
+ * @raise [Couchbase::Error::NotStored] if the key doesn't exist
479
+ * @raise [ArgumentError] when passing the block in synchronous mode
480
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
481
+ * exceeds
482
+ *
483
+ * @example Simple prepend example
484
+ * c.set("foo", "aaa")
485
+ * c.prepend("foo", "bbb")
486
+ * c.get("foo") #=> "bbbaaa"
487
+ *
488
+ * @example Using explicit format option
489
+ * c.default_format #=> :document
490
+ * c.set("foo", {"y" => "z"})
491
+ * c.prepend("foo", '[', :format => :plain)
492
+ * c.append("foo", ', {"z": "y"}]', :format => :plain)
493
+ * c.get("foo") #=> [{"y"=>"z"}, {"z"=>"y"}]
494
+ *
495
+ * @example Using optimistic locking. The operation will fail on CAS mismatch
496
+ * ver = c.set("foo", "aaa")
497
+ * c.prepend("foo", "bbb", :cas => ver)
498
+ *
499
+ * @example Ensure that the key will be persisted at least on the one node
500
+ * c.prepend("foo", "bar", :observe => {:persisted => 1})
501
+ */
502
+ VALUE
503
+ cb_bucket_prepend(int argc, VALUE *argv, VALUE self)
504
+ {
505
+ return cb_bucket_store(LCB_PREPEND, argc, argv, self);
506
+ }
507
+
508
+ VALUE
509
+ cb_bucket_aset(int argc, VALUE *argv, VALUE self)
510
+ {
511
+ VALUE temp;
512
+
513
+ if (argc == 3) {
514
+ /* swap opts and value, because value goes last for []= */
515
+ temp = argv[2];
516
+ argv[2] = argv[1];
517
+ argv[1] = temp;
518
+ }
519
+ return cb_bucket_set(argc, argv, self);
520
+ }
521
+
522
+