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.
- data/.gitignore +2 -1
- data/.travis.yml +12 -1
- data/HISTORY.markdown +112 -1
- data/README.markdown +149 -6
- data/couchbase.gemspec +5 -1
- data/ext/couchbase_ext/.gitignore +4 -0
- data/ext/couchbase_ext/arguments.c +973 -0
- data/ext/couchbase_ext/arithmetic.c +322 -0
- data/ext/couchbase_ext/bucket.c +1092 -0
- data/ext/couchbase_ext/couchbase_ext.c +618 -3247
- data/ext/couchbase_ext/couchbase_ext.h +519 -0
- data/ext/couchbase_ext/delete.c +167 -0
- data/ext/couchbase_ext/extconf.rb +24 -5
- data/ext/couchbase_ext/get.c +301 -0
- data/ext/couchbase_ext/gethrtime.c +124 -0
- data/ext/couchbase_ext/http.c +402 -0
- data/ext/couchbase_ext/observe.c +174 -0
- data/ext/couchbase_ext/result.c +126 -0
- data/ext/couchbase_ext/stats.c +169 -0
- data/ext/couchbase_ext/store.c +522 -0
- data/ext/couchbase_ext/timer.c +192 -0
- data/ext/couchbase_ext/touch.c +190 -0
- data/ext/couchbase_ext/unlock.c +180 -0
- data/ext/couchbase_ext/utils.c +471 -0
- data/ext/couchbase_ext/version.c +147 -0
- data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
- data/lib/active_support/cache/couchbase_store.rb +356 -0
- data/lib/couchbase.rb +24 -3
- data/lib/couchbase/bucket.rb +372 -9
- data/lib/couchbase/result.rb +26 -0
- data/lib/couchbase/utils.rb +59 -0
- data/lib/couchbase/version.rb +1 -1
- data/lib/couchbase/view.rb +305 -0
- data/lib/couchbase/view_row.rb +230 -0
- data/lib/ext/multi_json_fix.rb +47 -0
- data/lib/rack/session/couchbase.rb +104 -0
- data/tasks/compile.rake +5 -14
- data/test/setup.rb +6 -2
- data/test/test_arithmetic.rb +32 -2
- data/test/test_async.rb +18 -4
- data/test/test_bucket.rb +11 -1
- data/test/test_cas.rb +13 -3
- data/test/test_couchbase_rails_cache_store.rb +294 -0
- data/test/test_delete.rb +60 -3
- data/test/test_format.rb +28 -17
- data/test/test_get.rb +91 -14
- data/test/test_store.rb +31 -1
- data/test/{test_flush.rb → test_timer.rb} +11 -18
- data/test/test_touch.rb +33 -5
- data/test/test_unlock.rb +120 -0
- data/test/test_utils.rb +26 -0
- 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(¶ms, 0, sizeof(struct params_st));
|
126
|
+
params.type = cmd_store;
|
127
|
+
params.bucket = bucket;
|
128
|
+
params.cmd.store.operation = cmd;
|
129
|
+
cb_params_build(¶ms, 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(¶ms);
|
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
|
+
|