couchbase 1.1.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
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
+ require 'bundler/gem_tasks'
19
+
20
+ Dir['tasks/*.rake'].sort.each { |f| load f }
21
+
22
+ task :default => [:clobber, :compile, :test]
data/couchbase.gemspec ADDED
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Author:: Couchbase <info@couchbase.com>
3
+ # Copyright:: 2011, 2012 Couchbase, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ $:.push File.expand_path('../lib', __FILE__)
20
+ require 'couchbase/version'
21
+
22
+ Gem::Specification.new do |s|
23
+ s.name = 'couchbase'
24
+ s.version = Couchbase::VERSION
25
+ s.author = 'Couchbase'
26
+ s.email = 'support@couchbase.com'
27
+ s.license = 'ASL-2'
28
+ s.homepage = 'http://couchbase.org'
29
+ s.summary = %q{Couchbase ruby driver}
30
+ s.description = %q{The official client library for use with Couchbase Server.}
31
+
32
+ s.files = `git ls-files`.split("\n")
33
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
34
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
35
+ s.extensions = `git ls-files -- ext/**/extconf.rb`.split("\n")
36
+ s.require_paths = ['lib']
37
+
38
+ s.add_runtime_dependency 'yajl-ruby', '~> 1.1.0'
39
+
40
+ s.add_development_dependency 'rake', '~> 0.8.7'
41
+ s.add_development_dependency 'minitest'
42
+ s.add_development_dependency 'rake-compiler', '>= 0.7.5'
43
+ s.add_development_dependency 'rdiscount'
44
+ s.add_development_dependency 'yard'
45
+ s.add_development_dependency 'mini_portile'
46
+ s.add_development_dependency RUBY_VERSION =~ /^1\.9/ ? 'ruby-debug19' : 'ruby-debug'
47
+ end
@@ -0,0 +1,3464 @@
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 <ruby.h>
19
+ #ifndef RUBY_ST_H
20
+ #include <st.h>
21
+ #endif
22
+
23
+ #include <time.h>
24
+ #include <libcouchbase/couchbase.h>
25
+ #include "couchbase_config.h"
26
+
27
+ #ifdef HAVE_STDARG_PROTOTYPES
28
+ #include <stdarg.h>
29
+ #define va_init_list(a,b) va_start(a,b)
30
+ #else
31
+ #include <varargs.h>
32
+ #define va_init_list(a,b) va_start(a)
33
+ #endif
34
+
35
+ #define debug_object(OBJ) \
36
+ rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("object_id"), 0)); \
37
+ rb_funcall(rb_stderr, rb_intern("print"), 1, rb_str_new2(" ")); \
38
+ rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("class"), 0)); \
39
+ rb_funcall(rb_stderr, rb_intern("print"), 1, rb_str_new2(" ")); \
40
+ rb_funcall(rb_stderr, rb_intern("puts"), 1, rb_funcall(OBJ, rb_intern("inspect"), 0));
41
+
42
+ #define FMT_MASK 0x3
43
+ #define FMT_DOCUMENT 0x0
44
+ #define FMT_MARSHAL 0x1
45
+ #define FMT_PLAIN 0x2
46
+
47
+ typedef struct
48
+ {
49
+ libcouchbase_t handle;
50
+ struct libcouchbase_io_opt_st *io;
51
+ uint16_t port;
52
+ char *authority;
53
+ char *hostname;
54
+ char *pool;
55
+ char *bucket;
56
+ char *username;
57
+ char *password;
58
+ int async;
59
+ int quiet;
60
+ long seqno;
61
+ VALUE default_format; /* should update +default_flags+ on change */
62
+ uint32_t default_flags;
63
+ time_t default_ttl;
64
+ uint32_t timeout;
65
+ VALUE exception; /* error delivered by error_callback */
66
+ VALUE on_error_proc; /* is using to deliver errors in async mode */
67
+ } bucket_t;
68
+
69
+ typedef struct
70
+ {
71
+ bucket_t* bucket;
72
+ int extended;
73
+ VALUE proc;
74
+ void *rv;
75
+ VALUE exception;
76
+ int quiet;
77
+ int arithm; /* incr: +1, decr: -1, other: 0 */
78
+ } context_t;
79
+
80
+ struct key_traits
81
+ {
82
+ VALUE keys_ary;
83
+ size_t nkeys;
84
+ char **keys;
85
+ libcouchbase_size_t *lens;
86
+ time_t *ttls;
87
+ int extended;
88
+ int explicit_ttl;
89
+ int quiet;
90
+ int mgat;
91
+ };
92
+
93
+ static VALUE mCouchbase, mError, mJSON, mURI, mMarshal, cBucket, cResult;
94
+ static VALUE object_space;
95
+
96
+ static ID sym_add,
97
+ sym_append,
98
+ sym_bucket,
99
+ sym_cas,
100
+ sym_create,
101
+ sym_decrement,
102
+ sym_default_flags,
103
+ sym_default_format,
104
+ sym_default_ttl,
105
+ sym_delete,
106
+ sym_document,
107
+ sym_extended,
108
+ sym_flags,
109
+ sym_flush,
110
+ sym_format,
111
+ sym_get,
112
+ sym_hostname,
113
+ sym_increment,
114
+ sym_initial,
115
+ sym_marshal,
116
+ sym_password,
117
+ sym_plain,
118
+ sym_pool,
119
+ sym_port,
120
+ sym_prepend,
121
+ sym_quiet,
122
+ sym_replace,
123
+ sym_set,
124
+ sym_stats,
125
+ sym_timeout,
126
+ sym_touch,
127
+ sym_ttl,
128
+ sym_username,
129
+ sym_version,
130
+ id_arity,
131
+ id_call,
132
+ id_dump,
133
+ id_flatten_bang,
134
+ id_has_key_p,
135
+ id_host,
136
+ id_iv_cas,
137
+ id_iv_error,
138
+ id_iv_flags,
139
+ id_iv_key,
140
+ id_iv_node,
141
+ id_iv_operation,
142
+ id_iv_value,
143
+ id_load,
144
+ id_match,
145
+ id_parse,
146
+ id_password,
147
+ id_path,
148
+ id_port,
149
+ id_scheme,
150
+ id_to_s,
151
+ id_user;
152
+
153
+ /* base error */
154
+ static VALUE eBaseError;
155
+ static VALUE eValueFormatError;
156
+
157
+ /* libcouchbase errors */
158
+ /*LIBCOUCHBASE_SUCCESS = 0x00*/
159
+ /*LIBCOUCHBASE_AUTH_CONTINUE = 0x01*/
160
+ static VALUE eAuthError; /*LIBCOUCHBASE_AUTH_ERROR = 0x02*/
161
+ static VALUE eDeltaBadvalError; /*LIBCOUCHBASE_DELTA_BADVAL = 0x03*/
162
+ static VALUE eTooBigError; /*LIBCOUCHBASE_E2BIG = 0x04*/
163
+ static VALUE eBusyError; /*LIBCOUCHBASE_EBUSY = 0x05*/
164
+ static VALUE eInternalError; /*LIBCOUCHBASE_EINTERNAL = 0x06*/
165
+ static VALUE eInvalidError; /*LIBCOUCHBASE_EINVAL = 0x07*/
166
+ static VALUE eNoMemoryError; /*LIBCOUCHBASE_ENOMEM = 0x08*/
167
+ static VALUE eRangeError; /*LIBCOUCHBASE_ERANGE = 0x09*/
168
+ static VALUE eLibcouchbaseError; /*LIBCOUCHBASE_ERROR = 0x0a*/
169
+ static VALUE eTmpFailError; /*LIBCOUCHBASE_ETMPFAIL = 0x0b*/
170
+ static VALUE eKeyExistsError; /*LIBCOUCHBASE_KEY_EEXISTS = 0x0c*/
171
+ static VALUE eNotFoundError; /*LIBCOUCHBASE_KEY_ENOENT = 0x0d*/
172
+ static VALUE eLibeventError; /*LIBCOUCHBASE_LIBEVENT_ERROR = 0x0e*/
173
+ static VALUE eNetworkError; /*LIBCOUCHBASE_NETWORK_ERROR = 0x0f*/
174
+ static VALUE eNotMyVbucketError; /*LIBCOUCHBASE_NOT_MY_VBUCKET = 0x10*/
175
+ static VALUE eNotStoredError; /*LIBCOUCHBASE_NOT_STORED = 0x11*/
176
+ static VALUE eNotSupportedError; /*LIBCOUCHBASE_NOT_SUPPORTED = 0x12*/
177
+ static VALUE eUnknownCommandError; /*LIBCOUCHBASE_UNKNOWN_COMMAND = 0x13*/
178
+ static VALUE eUnknownHostError; /*LIBCOUCHBASE_UNKNOWN_HOST = 0x14*/
179
+ static VALUE eProtocolError; /*LIBCOUCHBASE_PROTOCOL_ERROR = 0x15*/
180
+ static VALUE eTimeoutError; /*LIBCOUCHBASE_ETIMEDOUT = 0x16*/
181
+ static VALUE eConnectError; /*LIBCOUCHBASE_CONNECT_ERROR = 0x17*/
182
+ static VALUE eBucketNotFoundError; /*LIBCOUCHBASE_BUCKET_ENOENT = 0x18*/
183
+
184
+ static VALUE
185
+ cb_proc_call(VALUE recv, int argc, ...)
186
+ {
187
+ VALUE *argv;
188
+ va_list ar;
189
+ int arity;
190
+ int ii;
191
+
192
+ arity = FIX2INT(rb_funcall(recv, id_arity, 0));
193
+ if (arity > 0) {
194
+ va_init_list(ar, argc);
195
+ argv = ALLOCA_N(VALUE, argc);
196
+ for (ii = 0; ii < arity; ++ii) {
197
+ if (ii < argc) {
198
+ argv[ii] = va_arg(ar, VALUE);
199
+ } else {
200
+ argv[ii] = Qnil;
201
+ }
202
+ }
203
+ va_end(ar);
204
+ } else {
205
+ arity = 0;
206
+ argv = NULL;
207
+ }
208
+ return rb_funcall2(recv, id_call, arity, argv);
209
+ }
210
+
211
+ /* Helper to conver return code from libcouchbase to meaningful exception.
212
+ * Returns nil if the code considering successful and exception object
213
+ * otherwise. Store given string to exceptions as message, and also
214
+ * initialize +error+ attribute with given return code. */
215
+ static VALUE
216
+ cb_check_error(libcouchbase_error_t rc, const char *msg, VALUE key)
217
+ {
218
+ VALUE klass, exc, str;
219
+ char buf[300];
220
+
221
+ if (rc == LIBCOUCHBASE_SUCCESS || rc == LIBCOUCHBASE_AUTH_CONTINUE) {
222
+ return Qnil;
223
+ }
224
+ switch (rc) {
225
+ case LIBCOUCHBASE_AUTH_ERROR:
226
+ klass = eAuthError;
227
+ break;
228
+ case LIBCOUCHBASE_DELTA_BADVAL:
229
+ klass = eDeltaBadvalError;
230
+ break;
231
+ case LIBCOUCHBASE_E2BIG:
232
+ klass = eTooBigError;
233
+ break;
234
+ case LIBCOUCHBASE_EBUSY:
235
+ klass = eBusyError;
236
+ break;
237
+ case LIBCOUCHBASE_EINTERNAL:
238
+ klass = eInternalError;
239
+ break;
240
+ case LIBCOUCHBASE_EINVAL:
241
+ klass = eInvalidError;
242
+ break;
243
+ case LIBCOUCHBASE_ENOMEM:
244
+ klass = eNoMemoryError;
245
+ break;
246
+ case LIBCOUCHBASE_ERANGE:
247
+ klass = eRangeError;
248
+ break;
249
+ case LIBCOUCHBASE_ETMPFAIL:
250
+ klass = eTmpFailError;
251
+ break;
252
+ case LIBCOUCHBASE_KEY_EEXISTS:
253
+ klass = eKeyExistsError;
254
+ break;
255
+ case LIBCOUCHBASE_KEY_ENOENT:
256
+ klass = eNotFoundError;
257
+ break;
258
+ case LIBCOUCHBASE_LIBEVENT_ERROR:
259
+ klass = eLibeventError;
260
+ break;
261
+ case LIBCOUCHBASE_NETWORK_ERROR:
262
+ klass = eNetworkError;
263
+ break;
264
+ case LIBCOUCHBASE_NOT_MY_VBUCKET:
265
+ klass = eNotMyVbucketError;
266
+ break;
267
+ case LIBCOUCHBASE_NOT_STORED:
268
+ klass = eNotStoredError;
269
+ break;
270
+ case LIBCOUCHBASE_NOT_SUPPORTED:
271
+ klass = eNotSupportedError;
272
+ break;
273
+ case LIBCOUCHBASE_UNKNOWN_COMMAND:
274
+ klass = eUnknownCommandError;
275
+ break;
276
+ case LIBCOUCHBASE_UNKNOWN_HOST:
277
+ klass = eUnknownHostError;
278
+ break;
279
+ case LIBCOUCHBASE_PROTOCOL_ERROR:
280
+ klass = eProtocolError;
281
+ break;
282
+ case LIBCOUCHBASE_ETIMEDOUT:
283
+ klass = eTimeoutError;
284
+ break;
285
+ case LIBCOUCHBASE_CONNECT_ERROR:
286
+ klass = eConnectError;
287
+ break;
288
+ case LIBCOUCHBASE_BUCKET_ENOENT:
289
+ klass = eBucketNotFoundError;
290
+ break;
291
+ case LIBCOUCHBASE_ERROR:
292
+ /* fall through */
293
+ default:
294
+ klass = eLibcouchbaseError;
295
+ }
296
+
297
+ str = rb_str_buf_new2(msg ? msg : "");
298
+ rb_str_buf_cat2(str, " (");
299
+ if (key != Qnil) {
300
+ snprintf(buf, 300, "key=\"%s\", ", RSTRING_PTR(key));
301
+ rb_str_buf_cat2(str, buf);
302
+ }
303
+ snprintf(buf, 300, "error=0x%02x)", rc);
304
+ rb_str_buf_cat2(str, buf);
305
+ exc = rb_exc_new3(klass, str);
306
+ rb_ivar_set(exc, id_iv_error, INT2FIX(rc));
307
+ rb_ivar_set(exc, id_iv_key, key);
308
+ rb_ivar_set(exc, id_iv_cas, Qnil);
309
+ rb_ivar_set(exc, id_iv_operation, Qnil);
310
+ return exc;
311
+ }
312
+
313
+ static inline uint32_t
314
+ flags_set_format(uint32_t flags, ID format)
315
+ {
316
+ flags &= ~((uint32_t)FMT_MASK); /* clear format bits */
317
+
318
+ if (format == sym_document) {
319
+ return flags | FMT_DOCUMENT;
320
+ } else if (format == sym_marshal) {
321
+ return flags | FMT_MARSHAL;
322
+ } else if (format == sym_plain) {
323
+ return flags | FMT_PLAIN;
324
+ }
325
+ return flags; /* document is the default */
326
+ }
327
+
328
+ static inline ID
329
+ flags_get_format(uint32_t flags)
330
+ {
331
+ flags &= FMT_MASK; /* select format bits */
332
+
333
+ switch (flags) {
334
+ case FMT_DOCUMENT:
335
+ return sym_document;
336
+ case FMT_MARSHAL:
337
+ return sym_marshal;
338
+ case FMT_PLAIN:
339
+ /* fall through */
340
+ default:
341
+ /* all other formats treated as plain */
342
+ return sym_plain;
343
+ }
344
+ }
345
+
346
+
347
+ static VALUE
348
+ do_encode(VALUE *args)
349
+ {
350
+ VALUE val = args[0];
351
+ uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
352
+
353
+ switch (flags) {
354
+ case FMT_DOCUMENT:
355
+ return rb_funcall(mJSON, id_dump, 1, val);
356
+ case FMT_MARSHAL:
357
+ return rb_funcall(mMarshal, id_dump, 1, val);
358
+ case FMT_PLAIN:
359
+ /* fall through */
360
+ default:
361
+ /* all other formats treated as plain */
362
+ return val;
363
+ }
364
+ }
365
+
366
+ static VALUE
367
+ do_decode(VALUE *args)
368
+ {
369
+ VALUE blob = args[0];
370
+ uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
371
+
372
+ switch (flags) {
373
+ case FMT_DOCUMENT:
374
+ return rb_funcall(mJSON, id_load, 1, blob);
375
+ case FMT_MARSHAL:
376
+ return rb_funcall(mMarshal, id_load, 1, blob);
377
+ case FMT_PLAIN:
378
+ /* fall through */
379
+ default:
380
+ /* all other formats treated as plain */
381
+ return blob;
382
+ }
383
+ }
384
+
385
+ static VALUE
386
+ coding_failed(void)
387
+ {
388
+ return Qundef;
389
+ }
390
+
391
+ static VALUE
392
+ encode_value(VALUE val, uint32_t flags)
393
+ {
394
+ VALUE blob, args[2];
395
+
396
+ args[0] = val;
397
+ args[1] = (VALUE)flags;
398
+ blob = rb_rescue(do_encode, (VALUE)args, coding_failed, 0);
399
+ /* it must be bytestring after all */
400
+ if (TYPE(blob) != T_STRING) {
401
+ return Qundef;
402
+ }
403
+ return blob;
404
+ }
405
+
406
+ static VALUE
407
+ decode_value(VALUE blob, uint32_t flags)
408
+ {
409
+ VALUE val, args[2];
410
+
411
+ /* first it must be bytestring */
412
+ if (TYPE(blob) != T_STRING) {
413
+ return Qundef;
414
+ }
415
+ args[0] = blob;
416
+ args[1] = (VALUE)flags;
417
+ val = rb_rescue(do_decode, (VALUE)args, coding_failed, 0);
418
+ return val;
419
+ }
420
+
421
+ static VALUE
422
+ unify_key(VALUE key)
423
+ {
424
+ switch (TYPE(key)) {
425
+ case T_STRING:
426
+ return key;
427
+ case T_SYMBOL:
428
+ return rb_str_new2(rb_id2name(SYM2ID(key)));
429
+ default: /* call #to_str or raise error */
430
+ return StringValue(key);
431
+ }
432
+ }
433
+
434
+ static int
435
+ cb_extract_keys_i(VALUE key, VALUE value, VALUE arg)
436
+ {
437
+ struct key_traits *traits = (struct key_traits *)arg;
438
+ key = unify_key(key);
439
+ rb_ary_push(traits->keys_ary, key);
440
+ traits->keys[traits->nkeys] = RSTRING_PTR(key);
441
+ traits->lens[traits->nkeys] = RSTRING_LEN(key);
442
+ traits->ttls[traits->nkeys] = NUM2ULONG(value);
443
+ traits->nkeys++;
444
+ return ST_CONTINUE;
445
+ }
446
+
447
+ static long
448
+ cb_args_scan_keys(long argc, VALUE argv, bucket_t *bucket, struct key_traits *traits)
449
+ {
450
+ VALUE key, *keys_ptr, opts, ttl, ext;
451
+ long nn = 0, ii;
452
+ time_t exp;
453
+
454
+ traits->keys_ary = rb_ary_new();
455
+ traits->quiet = bucket->quiet;
456
+ traits->mgat = 0;
457
+
458
+ if (argc > 0) {
459
+ /* keys with custom options */
460
+ opts = RARRAY_PTR(argv)[argc-1];
461
+ exp = bucket->default_ttl;
462
+ ext = Qfalse;
463
+ if (argc > 1 && TYPE(opts) == T_HASH) {
464
+ (void)rb_ary_pop(argv);
465
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
466
+ traits->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
467
+ }
468
+ ext = rb_hash_aref(opts, sym_extended);
469
+ ttl = rb_hash_aref(opts, sym_ttl);
470
+ if (ttl != Qnil) {
471
+ traits->explicit_ttl = 1;
472
+ exp = NUM2ULONG(ttl);
473
+ }
474
+ nn = RARRAY_LEN(argv);
475
+ } else {
476
+ nn = argc;
477
+ }
478
+ if (nn < 1) {
479
+ rb_raise(rb_eArgError, "must be at least one key");
480
+ }
481
+ keys_ptr = RARRAY_PTR(argv);
482
+ traits->extended = RTEST(ext) ? 1 : 0;
483
+ if (nn == 1 && TYPE(keys_ptr[0]) == T_HASH) {
484
+ /* hash of key-ttl pairs */
485
+ nn = RHASH_SIZE(keys_ptr[0]);
486
+ traits->keys = calloc(nn, sizeof(char *));
487
+ traits->lens = calloc(nn, sizeof(size_t));
488
+ traits->explicit_ttl = 1;
489
+ traits->mgat = 1;
490
+ traits->ttls = calloc(nn, sizeof(time_t));
491
+ rb_hash_foreach(keys_ptr[0], cb_extract_keys_i, (VALUE)traits);
492
+ } else {
493
+ /* the list of keys */
494
+ traits->nkeys = nn;
495
+ traits->keys = calloc(nn, sizeof(char *));
496
+ traits->lens = calloc(nn, sizeof(size_t));
497
+ traits->ttls = calloc(nn, sizeof(time_t));
498
+ for (ii = 0; ii < nn; ii++) {
499
+ key = unify_key(keys_ptr[ii]);
500
+ rb_ary_push(traits->keys_ary, key);
501
+ traits->keys[ii] = RSTRING_PTR(key);
502
+ traits->lens[ii] = RSTRING_LEN(key);
503
+ traits->ttls[ii] = exp;
504
+ }
505
+ }
506
+ }
507
+
508
+ return nn;
509
+ }
510
+
511
+ static void
512
+ error_callback(libcouchbase_t handle, libcouchbase_error_t error, const char *errinfo)
513
+ {
514
+ bucket_t *bucket = (bucket_t *)libcouchbase_get_cookie(handle);
515
+
516
+ bucket->io->stop_event_loop(bucket->io);
517
+ bucket->exception = cb_check_error(error, errinfo, Qnil);
518
+ }
519
+
520
+ static void
521
+ storage_callback(libcouchbase_t handle, const void *cookie,
522
+ libcouchbase_storage_t operation, libcouchbase_error_t error,
523
+ const void *key, libcouchbase_size_t nkey, libcouchbase_cas_t cas)
524
+ {
525
+ context_t *ctx = (context_t *)cookie;
526
+ bucket_t *bucket = ctx->bucket;
527
+ VALUE k, c, *rv = ctx->rv, exc, res;
528
+ ID o;
529
+
530
+ bucket->seqno--;
531
+
532
+ k = rb_str_new((const char*)key, nkey);
533
+ c = cas > 0 ? ULL2NUM(cas) : Qnil;
534
+ switch(operation) {
535
+ case LIBCOUCHBASE_ADD:
536
+ o = sym_add;
537
+ break;
538
+ case LIBCOUCHBASE_REPLACE:
539
+ o = sym_replace;
540
+ break;
541
+ case LIBCOUCHBASE_SET:
542
+ o = sym_set;
543
+ break;
544
+ case LIBCOUCHBASE_APPEND:
545
+ o = sym_append;
546
+ break;
547
+ case LIBCOUCHBASE_PREPEND:
548
+ o = sym_prepend;
549
+ break;
550
+ default:
551
+ o = Qnil;
552
+ }
553
+ exc = cb_check_error(error, "failed to store value", k);
554
+ if (exc != Qnil) {
555
+ rb_ivar_set(exc, id_iv_cas, c);
556
+ rb_ivar_set(exc, id_iv_operation, o);
557
+ if (NIL_P(ctx->exception)) {
558
+ ctx->exception = exc;
559
+ }
560
+ }
561
+ if (bucket->async) { /* asynchronous */
562
+ if (ctx->proc != Qnil) {
563
+ res = rb_class_new_instance(0, NULL, cResult);
564
+ rb_ivar_set(res, id_iv_error, exc);
565
+ rb_ivar_set(res, id_iv_key, k);
566
+ rb_ivar_set(res, id_iv_operation, o);
567
+ rb_ivar_set(res, id_iv_cas, c);
568
+ cb_proc_call(ctx->proc, 1, res);
569
+ }
570
+ } else { /* synchronous */
571
+ *rv = c;
572
+ }
573
+
574
+ if (bucket->seqno == 0) {
575
+ bucket->io->stop_event_loop(bucket->io);
576
+ rb_hash_delete(object_space, ctx->proc|1);
577
+ }
578
+ (void)handle;
579
+ }
580
+
581
+ static void
582
+ delete_callback(libcouchbase_t handle, const void *cookie,
583
+ libcouchbase_error_t error, const void *key,
584
+ libcouchbase_size_t nkey)
585
+ {
586
+ context_t *ctx = (context_t *)cookie;
587
+ bucket_t *bucket = ctx->bucket;
588
+ VALUE k, *rv = ctx->rv, exc = Qnil, res;
589
+
590
+ bucket->seqno--;
591
+
592
+ k = rb_str_new((const char*)key, nkey);
593
+ if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
594
+ exc = cb_check_error(error, "failed to remove value", k);
595
+ if (exc != Qnil) {
596
+ rb_ivar_set(exc, id_iv_operation, sym_delete);
597
+ if (NIL_P(ctx->exception)) {
598
+ ctx->exception = exc;
599
+ }
600
+ }
601
+ }
602
+ if (bucket->async) { /* asynchronous */
603
+ if (ctx->proc != Qnil) {
604
+ res = rb_class_new_instance(0, NULL, cResult);
605
+ rb_ivar_set(res, id_iv_error, exc);
606
+ rb_ivar_set(res, id_iv_operation, sym_delete);
607
+ rb_ivar_set(res, id_iv_key, k);
608
+ cb_proc_call(ctx->proc, 1, res);
609
+ }
610
+ } else { /* synchronous */
611
+ *rv = (error == LIBCOUCHBASE_SUCCESS) ? Qtrue : Qfalse;
612
+ }
613
+ if (bucket->seqno == 0) {
614
+ bucket->io->stop_event_loop(bucket->io);
615
+ rb_hash_delete(object_space, ctx->proc|1);
616
+ }
617
+ (void)handle;
618
+ }
619
+
620
+ static void
621
+ get_callback(libcouchbase_t handle, const void *cookie,
622
+ libcouchbase_error_t error, const void *key,
623
+ libcouchbase_size_t nkey, const void *bytes,
624
+ libcouchbase_size_t nbytes, libcouchbase_uint32_t flags,
625
+ libcouchbase_cas_t cas)
626
+ {
627
+ context_t *ctx = (context_t *)cookie;
628
+ bucket_t *bucket = ctx->bucket;
629
+ VALUE k, v, f, c, *rv = ctx->rv, exc = Qnil, res;
630
+
631
+ bucket->seqno--;
632
+
633
+ k = rb_str_new((const char*)key, nkey);
634
+ if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
635
+ exc = cb_check_error(error, "failed to get value", k);
636
+ if (exc != Qnil) {
637
+ rb_ivar_set(exc, id_iv_operation, sym_get);
638
+ if (NIL_P(ctx->exception)) {
639
+ ctx->exception = exc;
640
+ }
641
+ }
642
+ }
643
+
644
+ f = ULONG2NUM(flags);
645
+ c = ULL2NUM(cas);
646
+ if (nbytes != 0) {
647
+ v = decode_value(rb_str_new((const char*)bytes, nbytes), flags);
648
+ if (v == Qundef) {
649
+ ctx->exception = rb_exc_new2(eValueFormatError, "unable to convert value");
650
+ v = Qnil;
651
+ }
652
+ } else {
653
+ if (flags_get_format(flags) == sym_plain) {
654
+ v = rb_str_new2("");
655
+ } else {
656
+ v = Qnil;
657
+ }
658
+ }
659
+ if (bucket->async) { /* asynchronous */
660
+ if (ctx->proc != Qnil) {
661
+ res = rb_class_new_instance(0, NULL, cResult);
662
+ rb_ivar_set(res, id_iv_error, exc);
663
+ rb_ivar_set(res, id_iv_operation, sym_get);
664
+ rb_ivar_set(res, id_iv_key, k);
665
+ rb_ivar_set(res, id_iv_value, v);
666
+ rb_ivar_set(res, id_iv_flags, f);
667
+ rb_ivar_set(res, id_iv_cas, c);
668
+ cb_proc_call(ctx->proc, 1, res);
669
+ }
670
+ } else { /* synchronous */
671
+ if (NIL_P(exc) && error != LIBCOUCHBASE_KEY_ENOENT) {
672
+ if (ctx->extended) {
673
+ rb_hash_aset(*rv, k, rb_ary_new3(3, v, f, c));
674
+ } else {
675
+ rb_hash_aset(*rv, k, v);
676
+ }
677
+ }
678
+ }
679
+
680
+ if (bucket->seqno == 0) {
681
+ bucket->io->stop_event_loop(bucket->io);
682
+ rb_hash_delete(object_space, ctx->proc|1);
683
+ }
684
+ (void)handle;
685
+ }
686
+
687
+ static void
688
+ flush_callback(libcouchbase_t handle, const void* cookie,
689
+ const char* authority, libcouchbase_error_t error)
690
+ {
691
+ context_t *ctx = (context_t *)cookie;
692
+ bucket_t *bucket = ctx->bucket;
693
+ VALUE node, success = Qtrue, *rv = ctx->rv, exc, res;
694
+
695
+ node = authority ? rb_str_new2(authority) : Qnil;
696
+ exc = cb_check_error(error, "failed to flush bucket", node);
697
+ if (exc != Qnil) {
698
+ rb_ivar_set(exc, id_iv_operation, sym_flush);
699
+ if (NIL_P(ctx->exception)) {
700
+ ctx->exception = exc;
701
+ }
702
+ success = Qfalse;
703
+ }
704
+
705
+ if (authority) {
706
+ if (bucket->async) { /* asynchronous */
707
+ if (ctx->proc != Qnil) {
708
+ res = rb_class_new_instance(0, NULL, cResult);
709
+ rb_ivar_set(res, id_iv_error, exc);
710
+ rb_ivar_set(res, id_iv_operation, sym_flush);
711
+ rb_ivar_set(res, id_iv_node, node);
712
+ cb_proc_call(ctx->proc, 1, res);
713
+ }
714
+ } else { /* synchronous */
715
+ if (RTEST(*rv)) {
716
+ /* rewrite status for positive values only */
717
+ *rv = success;
718
+ }
719
+ }
720
+ } else {
721
+ bucket->seqno--;
722
+ if (bucket->seqno == 0) {
723
+ bucket->io->stop_event_loop(bucket->io);
724
+ rb_hash_delete(object_space, ctx->proc|1);
725
+ }
726
+ }
727
+
728
+ (void)handle;
729
+ }
730
+
731
+ static void
732
+ version_callback(libcouchbase_t handle, const void *cookie,
733
+ const char *authority, libcouchbase_error_t error,
734
+ const char *bytes, libcouchbase_size_t nbytes)
735
+ {
736
+ context_t *ctx = (context_t *)cookie;
737
+ bucket_t *bucket = ctx->bucket;
738
+ VALUE node, v, *rv = ctx->rv, exc, res;
739
+
740
+ node = authority ? rb_str_new2(authority) : Qnil;
741
+ exc = cb_check_error(error, "failed to get version", node);
742
+ if (exc != Qnil) {
743
+ rb_ivar_set(exc, id_iv_operation, sym_flush);
744
+ if (NIL_P(ctx->exception)) {
745
+ ctx->exception = exc;
746
+ }
747
+ }
748
+
749
+ if (authority) {
750
+ v = rb_str_new((const char*)bytes, nbytes);
751
+ if (bucket->async) { /* asynchronous */
752
+ if (ctx->proc != Qnil) {
753
+ res = rb_class_new_instance(0, NULL, cResult);
754
+ rb_ivar_set(res, id_iv_error, exc);
755
+ rb_ivar_set(res, id_iv_operation, sym_version);
756
+ rb_ivar_set(res, id_iv_node, node);
757
+ rb_ivar_set(res, id_iv_value, v);
758
+ cb_proc_call(ctx->proc, 1, res);
759
+ }
760
+ } else { /* synchronous */
761
+ if (NIL_P(exc)) {
762
+ rb_hash_aset(*rv, node, v);
763
+ }
764
+ }
765
+ } else {
766
+ bucket->seqno--;
767
+ if (bucket->seqno == 0) {
768
+ bucket->io->stop_event_loop(bucket->io);
769
+ rb_hash_delete(object_space, ctx->proc|1);
770
+ }
771
+ }
772
+
773
+ (void)handle;
774
+ }
775
+
776
+ static void
777
+ stat_callback(libcouchbase_t handle, const void* cookie,
778
+ const char* authority, libcouchbase_error_t error, const void* key,
779
+ libcouchbase_size_t nkey, const void* bytes,
780
+ libcouchbase_size_t nbytes)
781
+ {
782
+ context_t *ctx = (context_t *)cookie;
783
+ bucket_t *bucket = ctx->bucket;
784
+ VALUE stats, node, k, v, *rv = ctx->rv, exc = Qnil, res;
785
+
786
+ node = authority ? rb_str_new2(authority) : Qnil;
787
+ exc = cb_check_error(error, "failed to fetch stats", node);
788
+ if (exc != Qnil) {
789
+ rb_ivar_set(exc, id_iv_operation, sym_stats);
790
+ if (NIL_P(ctx->exception)) {
791
+ ctx->exception = exc;
792
+ }
793
+ }
794
+ if (authority) {
795
+ k = rb_str_new((const char*)key, nkey);
796
+ v = rb_str_new((const char*)bytes, nbytes);
797
+ if (bucket->async) { /* asynchronous */
798
+ if (ctx->proc != Qnil) {
799
+ res = rb_class_new_instance(0, NULL, cResult);
800
+ rb_ivar_set(res, id_iv_error, exc);
801
+ rb_ivar_set(res, id_iv_operation, sym_stats);
802
+ rb_ivar_set(res, id_iv_node, node);
803
+ rb_ivar_set(res, id_iv_key, k);
804
+ rb_ivar_set(res, id_iv_value, v);
805
+ cb_proc_call(ctx->proc, 1, res);
806
+ }
807
+ } else { /* synchronous */
808
+ if (NIL_P(exc)) {
809
+ stats = rb_hash_aref(*rv, k);
810
+ if (NIL_P(stats)) {
811
+ stats = rb_hash_new();
812
+ rb_hash_aset(*rv, k, stats);
813
+ }
814
+ rb_hash_aset(stats, node, v);
815
+ }
816
+ }
817
+ } else {
818
+ bucket->seqno--;
819
+ if (bucket->seqno == 0) {
820
+ bucket->io->stop_event_loop(bucket->io);
821
+ rb_hash_delete(object_space, ctx->proc|1);
822
+ }
823
+ }
824
+ (void)handle;
825
+ }
826
+
827
+ static void
828
+ touch_callback(libcouchbase_t handle, const void *cookie,
829
+ libcouchbase_error_t error, const void *key,
830
+ libcouchbase_size_t nkey)
831
+ {
832
+ context_t *ctx = (context_t *)cookie;
833
+ bucket_t *bucket = ctx->bucket;
834
+ VALUE k, success, *rv = ctx->rv, exc = Qnil, res;
835
+
836
+ bucket->seqno--;
837
+ k = rb_str_new((const char*)key, nkey);
838
+ if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
839
+ exc = cb_check_error(error, "failed to touch value", k);
840
+ if (exc != Qnil) {
841
+ rb_ivar_set(exc, id_iv_operation, sym_touch);
842
+ if (NIL_P(ctx->exception)) {
843
+ ctx->exception = exc;
844
+ }
845
+ }
846
+ }
847
+
848
+ if (bucket->async) { /* asynchronous */
849
+ if (ctx->proc != Qnil) {
850
+ res = rb_class_new_instance(0, NULL, cResult);
851
+ rb_ivar_set(res, id_iv_error, exc);
852
+ rb_ivar_set(res, id_iv_operation, sym_touch);
853
+ rb_ivar_set(res, id_iv_key, k);
854
+ cb_proc_call(ctx->proc, 1, res);
855
+ }
856
+ } else { /* synchronous */
857
+ if (NIL_P(exc)) {
858
+ success = (error == LIBCOUCHBASE_KEY_ENOENT) ? Qfalse : Qtrue;
859
+ rb_hash_aset(*rv, k, success);
860
+ }
861
+ }
862
+ if (bucket->seqno == 0) {
863
+ bucket->io->stop_event_loop(bucket->io);
864
+ rb_hash_delete(object_space, ctx->proc|1);
865
+ }
866
+ (void)handle;
867
+ }
868
+
869
+ static void
870
+ arithmetic_callback(libcouchbase_t handle, const void *cookie,
871
+ libcouchbase_error_t error, const void *key,
872
+ libcouchbase_size_t nkey, libcouchbase_uint64_t value,
873
+ libcouchbase_cas_t cas)
874
+ {
875
+ context_t *ctx = (context_t *)cookie;
876
+ bucket_t *bucket = ctx->bucket;
877
+ VALUE c, k, v, *rv = ctx->rv, exc, res;
878
+ ID o;
879
+
880
+ bucket->seqno--;
881
+
882
+ k = rb_str_new((const char*)key, nkey);
883
+ c = cas > 0 ? ULL2NUM(cas) : Qnil;
884
+ o = ctx->arithm > 0 ? sym_increment : sym_decrement;
885
+ exc = cb_check_error(error, "failed to perform arithmetic operation", k);
886
+ if (exc != Qnil) {
887
+ rb_ivar_set(exc, id_iv_cas, c);
888
+ rb_ivar_set(exc, id_iv_operation, o);
889
+ if (bucket->async) {
890
+ if (bucket->on_error_proc != Qnil) {
891
+ cb_proc_call(bucket->on_error_proc, 3, o, k, exc);
892
+ } else {
893
+ if (NIL_P(bucket->exception)) {
894
+ bucket->exception = exc;
895
+ }
896
+ }
897
+ }
898
+ if (NIL_P(ctx->exception)) {
899
+ ctx->exception = exc;
900
+ }
901
+ }
902
+ v = ULL2NUM(value);
903
+ if (bucket->async) { /* asynchronous */
904
+ if (ctx->proc != Qnil) {
905
+ res = rb_class_new_instance(0, NULL, cResult);
906
+ rb_ivar_set(res, id_iv_error, exc);
907
+ rb_ivar_set(res, id_iv_operation, o);
908
+ rb_ivar_set(res, id_iv_key, k);
909
+ rb_ivar_set(res, id_iv_value, v);
910
+ rb_ivar_set(res, id_iv_cas, c);
911
+ cb_proc_call(ctx->proc, 1, res);
912
+ }
913
+ } else { /* synchronous */
914
+ if (NIL_P(exc)) {
915
+ if (ctx->extended) {
916
+ *rv = rb_ary_new3(2, v, c);
917
+ } else {
918
+ *rv = v;
919
+ }
920
+ }
921
+ }
922
+ if (bucket->seqno == 0) {
923
+ bucket->io->stop_event_loop(bucket->io);
924
+ rb_hash_delete(object_space, ctx->proc|1);
925
+ }
926
+ (void)handle;
927
+ }
928
+
929
+ static int
930
+ cb_first_value_i(VALUE key, VALUE value, VALUE arg)
931
+ {
932
+ VALUE *val = (VALUE *)arg;
933
+
934
+ *val = value;
935
+ (void)key;
936
+ return ST_STOP;
937
+ }
938
+
939
+ /*
940
+ * @private
941
+ * @return [Fixnum] number of scheduled operations
942
+ */
943
+ static VALUE
944
+ cb_bucket_seqno(VALUE self)
945
+ {
946
+ bucket_t *bucket = DATA_PTR(self);
947
+
948
+ return LONG2FIX(bucket->seqno);
949
+ }
950
+
951
+ void
952
+ cb_bucket_free(void *ptr)
953
+ {
954
+ bucket_t *bucket = ptr;
955
+
956
+ if (bucket) {
957
+ if (bucket->handle) {
958
+ libcouchbase_destroy(bucket->handle);
959
+ }
960
+ free(bucket->authority);
961
+ free(bucket->hostname);
962
+ free(bucket->pool);
963
+ free(bucket->bucket);
964
+ free(bucket->username);
965
+ free(bucket->password);
966
+ free(bucket);
967
+ }
968
+ }
969
+
970
+ void
971
+ cb_bucket_mark(void *ptr)
972
+ {
973
+ bucket_t *bucket = ptr;
974
+
975
+ if (bucket) {
976
+ rb_gc_mark(bucket->exception);
977
+ rb_gc_mark(bucket->on_error_proc);
978
+ }
979
+ }
980
+
981
+ static void
982
+ do_scan_connection_options(bucket_t *bucket, int argc, VALUE *argv)
983
+ {
984
+ VALUE uri, opts, arg;
985
+ size_t len;
986
+
987
+ if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
988
+ if (TYPE(uri) == T_HASH && argc == 1) {
989
+ opts = uri;
990
+ uri = Qnil;
991
+ }
992
+ if (uri != Qnil) {
993
+ const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
994
+ VALUE match, uri_obj, re;
995
+
996
+ Check_Type(uri, T_STRING);
997
+ uri_obj = rb_funcall(mURI, id_parse, 1, uri);
998
+
999
+ arg = rb_funcall(uri_obj, id_scheme, 0);
1000
+ if (arg == Qnil || rb_str_cmp(arg, rb_str_new2("http"))) {
1001
+ rb_raise(rb_eArgError, "invalid URI: invalid scheme");
1002
+ }
1003
+
1004
+ arg = rb_funcall(uri_obj, id_user, 0);
1005
+ if (arg != Qnil) {
1006
+ free(bucket->username);
1007
+ bucket->username = strdup(RSTRING_PTR(arg));
1008
+ if (bucket->username == NULL) {
1009
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1010
+ }
1011
+ }
1012
+
1013
+ arg = rb_funcall(uri_obj, id_password, 0);
1014
+ if (arg != Qnil) {
1015
+ free(bucket->password);
1016
+ bucket->password = strdup(RSTRING_PTR(arg));
1017
+ if (bucket->password == NULL) {
1018
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1019
+ }
1020
+ }
1021
+ arg = rb_funcall(uri_obj, id_host, 0);
1022
+ if (arg != Qnil) {
1023
+ free(bucket->hostname);
1024
+ bucket->hostname = strdup(RSTRING_PTR(arg));
1025
+ if (bucket->hostname == NULL) {
1026
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1027
+ }
1028
+ } else {
1029
+ rb_raise(rb_eArgError, "invalid URI: missing hostname");
1030
+ }
1031
+
1032
+ arg = rb_funcall(uri_obj, id_port, 0);
1033
+ bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
1034
+
1035
+ arg = rb_funcall(uri_obj, id_path, 0);
1036
+ re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
1037
+ match = rb_funcall(re, id_match, 1, arg);
1038
+ arg = rb_reg_nth_match(2, match);
1039
+ free(bucket->pool);
1040
+ bucket->pool = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
1041
+ arg = rb_reg_nth_match(4, match);
1042
+ free(bucket->bucket);
1043
+ bucket->bucket = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
1044
+ }
1045
+ if (TYPE(opts) == T_HASH) {
1046
+ arg = rb_hash_aref(opts, sym_hostname);
1047
+ if (arg != Qnil) {
1048
+ if (bucket->hostname) {
1049
+ free(bucket->hostname);
1050
+ }
1051
+ bucket->hostname = strdup(StringValueCStr(arg));
1052
+ }
1053
+ arg = rb_hash_aref(opts, sym_pool);
1054
+ if (arg != Qnil) {
1055
+ if (bucket->pool) {
1056
+ free(bucket->pool);
1057
+ }
1058
+ bucket->pool = strdup(StringValueCStr(arg));
1059
+ }
1060
+ arg = rb_hash_aref(opts, sym_bucket);
1061
+ if (arg != Qnil) {
1062
+ if (bucket->bucket) {
1063
+ free(bucket->bucket);
1064
+ }
1065
+ bucket->bucket = strdup(StringValueCStr(arg));
1066
+ }
1067
+ arg = rb_hash_aref(opts, sym_username);
1068
+ if (arg != Qnil) {
1069
+ if (bucket->username) {
1070
+ free(bucket->username);
1071
+ }
1072
+ bucket->username = strdup(StringValueCStr(arg));
1073
+ }
1074
+ arg = rb_hash_aref(opts, sym_password);
1075
+ if (arg != Qnil) {
1076
+ if (bucket->password) {
1077
+ free(bucket->password);
1078
+ }
1079
+ bucket->password = strdup(StringValueCStr(arg));
1080
+ }
1081
+ arg = rb_hash_aref(opts, sym_port);
1082
+ if (arg != Qnil) {
1083
+ bucket->port = (uint16_t)NUM2UINT(arg);
1084
+ }
1085
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
1086
+ bucket->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
1087
+ }
1088
+ arg = rb_hash_aref(opts, sym_timeout);
1089
+ if (arg != Qnil) {
1090
+ bucket->timeout = (uint32_t)NUM2ULONG(arg);
1091
+ }
1092
+ arg = rb_hash_aref(opts, sym_default_ttl);
1093
+ if (arg != Qnil) {
1094
+ bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
1095
+ }
1096
+ arg = rb_hash_aref(opts, sym_default_flags);
1097
+ if (arg != Qnil) {
1098
+ bucket->default_flags = (uint32_t)NUM2ULONG(arg);
1099
+ }
1100
+ arg = rb_hash_aref(opts, sym_default_format);
1101
+ if (arg != Qnil) {
1102
+ if (TYPE(arg) == T_FIXNUM) {
1103
+ switch (FIX2INT(arg)) {
1104
+ case FMT_DOCUMENT:
1105
+ arg = sym_document;
1106
+ break;
1107
+ case FMT_MARSHAL:
1108
+ arg = sym_marshal;
1109
+ break;
1110
+ case FMT_PLAIN:
1111
+ arg = sym_plain;
1112
+ break;
1113
+ }
1114
+ }
1115
+ if (arg == sym_document || arg == sym_marshal || arg == sym_plain) {
1116
+ bucket->default_format = arg;
1117
+ bucket->default_flags = flags_set_format(bucket->default_flags, arg);
1118
+ }
1119
+ }
1120
+ } else {
1121
+ opts = Qnil;
1122
+ }
1123
+ }
1124
+ len = strlen(bucket->hostname) + 10;
1125
+ if (bucket->authority) {
1126
+ free(bucket->authority);
1127
+ }
1128
+ bucket->authority = calloc(len, sizeof(char));
1129
+ if (bucket->authority == NULL) {
1130
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1131
+ }
1132
+ snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
1133
+ }
1134
+
1135
+ static void
1136
+ do_connect(bucket_t *bucket)
1137
+ {
1138
+ libcouchbase_error_t err;
1139
+
1140
+ if (bucket->handle) {
1141
+ libcouchbase_destroy(bucket->handle);
1142
+ bucket->handle = NULL;
1143
+ bucket->io = NULL;
1144
+ }
1145
+ bucket->io = libcouchbase_create_io_ops(LIBCOUCHBASE_IO_OPS_DEFAULT, NULL, &err);
1146
+ if (bucket->io == NULL && err != LIBCOUCHBASE_SUCCESS) {
1147
+ rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
1148
+ }
1149
+ bucket->handle = libcouchbase_create(bucket->authority,
1150
+ bucket->username, bucket->password, bucket->bucket, bucket->io);
1151
+ if (bucket->handle == NULL) {
1152
+ rb_raise(eLibcouchbaseError, "failed to create libcouchbase instance");
1153
+ }
1154
+ libcouchbase_set_cookie(bucket->handle, bucket);
1155
+ (void)libcouchbase_set_error_callback(bucket->handle, error_callback);
1156
+ (void)libcouchbase_set_storage_callback(bucket->handle, storage_callback);
1157
+ (void)libcouchbase_set_get_callback(bucket->handle, get_callback);
1158
+ (void)libcouchbase_set_touch_callback(bucket->handle, touch_callback);
1159
+ (void)libcouchbase_set_remove_callback(bucket->handle, delete_callback);
1160
+ (void)libcouchbase_set_stat_callback(bucket->handle, stat_callback);
1161
+ (void)libcouchbase_set_flush_callback(bucket->handle, flush_callback);
1162
+ (void)libcouchbase_set_arithmetic_callback(bucket->handle, arithmetic_callback);
1163
+ (void)libcouchbase_set_version_callback(bucket->handle, version_callback);
1164
+
1165
+ err = libcouchbase_connect(bucket->handle);
1166
+ if (err != LIBCOUCHBASE_SUCCESS) {
1167
+ libcouchbase_destroy(bucket->handle);
1168
+ bucket->handle = NULL;
1169
+ bucket->io = NULL;
1170
+ rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
1171
+ }
1172
+ bucket->exception = Qnil;
1173
+ libcouchbase_wait(bucket->handle);
1174
+ if (bucket->exception != Qnil) {
1175
+ libcouchbase_destroy(bucket->handle);
1176
+ bucket->handle = NULL;
1177
+ bucket->io = NULL;
1178
+ rb_exc_raise(bucket->exception);
1179
+ }
1180
+
1181
+ if (bucket->timeout > 0) {
1182
+ libcouchbase_set_timeout(bucket->handle, bucket->timeout);
1183
+ } else {
1184
+ bucket->timeout = libcouchbase_get_timeout(bucket->handle);
1185
+ }
1186
+ }
1187
+
1188
+ /*
1189
+ * Create and initialize new Bucket.
1190
+ *
1191
+ * @return [Bucket] new instance
1192
+ *
1193
+ * @see Bucket#initialize
1194
+ */
1195
+ static VALUE
1196
+ cb_bucket_new(int argc, VALUE *argv, VALUE klass)
1197
+ {
1198
+ VALUE obj;
1199
+ bucket_t *bucket;
1200
+
1201
+ /* allocate new bucket struct and set it to zero */
1202
+ obj = Data_Make_Struct(klass, bucket_t, cb_bucket_mark, cb_bucket_free,
1203
+ bucket);
1204
+ rb_obj_call_init(obj, argc, argv);
1205
+ return obj;
1206
+ }
1207
+
1208
+ /*
1209
+ * Initialize new Bucket.
1210
+ *
1211
+ * @overload initialize(url, options = {})
1212
+ * Initialize bucket using URI of the cluster and options. It is possible
1213
+ * to override some parts of URI using the options keys (e.g. :host or
1214
+ * :port)
1215
+ *
1216
+ * @param [String] url The full URL of management API of the cluster.
1217
+ * @param [Hash] options The options for connection. See options definition
1218
+ * below.
1219
+ *
1220
+ * @overload initialize(options = {})
1221
+ * Initialize bucket using options only.
1222
+ *
1223
+ * @param [Hash] options The options for operation for connection
1224
+ * @option options [String] :host ("localhost") the hostname or IP address
1225
+ * of the node
1226
+ * @option options [Fixnum] :port (8091) the port of the managemenent API
1227
+ * @option options [String] :pool ("default") the pool name
1228
+ * @option options [String] :bucket ("default") the bucket name
1229
+ * @option options [Fixnum] :default_ttl (0) the TTL used by default during
1230
+ * storing key-value pairs.
1231
+ * @option options [Fixnum] :default_flags (0) the default flags.
1232
+ * @option options [Symbol] :default_format (:document) the format, which
1233
+ * will be used for values by default. Note that changing format will
1234
+ * amend flags. (see {Bucket#default_format})
1235
+ * @option options [String] :username (nil) the user name to connect to the
1236
+ * cluster. Used to authenticate on management API.
1237
+ * @option options [String] :password (nil) the password of the user.
1238
+ * @option options [Boolean] :quiet (true) the flag controlling if raising
1239
+ * exception when the client executes operations on unexising keys. If it
1240
+ * is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
1241
+ * default behaviour is to return +nil+ value silently (might be useful in
1242
+ * Rails cache).
1243
+ *
1244
+ * @example Initialize connection using default options
1245
+ * Couchbase.new
1246
+ *
1247
+ * @example Select custom bucket
1248
+ * Couchbase.new(:bucket => 'foo')
1249
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
1250
+ *
1251
+ * @example Connect to protected bucket
1252
+ * Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
1253
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
1254
+ * :username => 'protected', :password => 'secret')
1255
+ *
1256
+ * @return [Bucket]
1257
+ */
1258
+ static VALUE
1259
+ cb_bucket_init(int argc, VALUE *argv, VALUE self)
1260
+ {
1261
+ bucket_t *bucket = DATA_PTR(self);
1262
+
1263
+ bucket->exception = Qnil;
1264
+ bucket->hostname = strdup("localhost");
1265
+ bucket->port = 8091;
1266
+ bucket->pool = strdup("default");
1267
+ bucket->bucket = strdup("default");
1268
+ bucket->async = 0;
1269
+ bucket->quiet = 1;
1270
+ bucket->default_ttl = 0;
1271
+ bucket->default_flags = 0;
1272
+ bucket->default_format = sym_document;
1273
+ bucket->on_error_proc = Qnil;
1274
+ bucket->timeout = 0;
1275
+
1276
+ do_scan_connection_options(bucket, argc, argv);
1277
+ do_connect(bucket);
1278
+
1279
+ return self;
1280
+ }
1281
+
1282
+ /*
1283
+ * Reconnect the bucket
1284
+ *
1285
+ * Reconnect the bucket using initial configuration with optional
1286
+ * redefinition.
1287
+ *
1288
+ * @overload reconnect(url, options = {})
1289
+ * see {Bucket#initialize Bucket#initialize(url, options)}
1290
+ *
1291
+ * @overload reconnect(options = {})
1292
+ * see {Bucket#initialize Bucket#initialize(options)}
1293
+ *
1294
+ * @example reconnect with current parameters
1295
+ * c.reconnect
1296
+ *
1297
+ * @example reconnect the instance to another bucket
1298
+ * c.reconnect(:bucket => 'new')
1299
+ */
1300
+ static VALUE
1301
+ cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
1302
+ {
1303
+ bucket_t *bucket = DATA_PTR(self);
1304
+
1305
+ do_scan_connection_options(bucket, argc, argv);
1306
+ do_connect(bucket);
1307
+
1308
+ return self;
1309
+ }
1310
+
1311
+ /* Document-method: connected?
1312
+ * Check whether the instance connected to the cluster.
1313
+ *
1314
+ * @return [Boolean] +true+ if the instance connected to the cluster
1315
+ */
1316
+ static VALUE
1317
+ cb_bucket_connected_p(VALUE self)
1318
+ {
1319
+ bucket_t *bucket = DATA_PTR(self);
1320
+ return bucket->handle ? Qtrue : Qfalse;
1321
+ }
1322
+
1323
+ /* Document-method: async?
1324
+ * Check whether the connection asynchronous.
1325
+ *
1326
+ * By default all operations are synchronous and block waiting for
1327
+ * results, but you can make them asynchronous and run event loop
1328
+ * explicitly. (see {Bucket#run})
1329
+ *
1330
+ * @example Return value of #get operation depending on async flag
1331
+ * connection = Connection.new
1332
+ * connection.async? #=> false
1333
+ *
1334
+ * connection.run do |conn|
1335
+ * conn.async? #=> true
1336
+ * end
1337
+ *
1338
+ * @return [Boolean] +true+ if the connection if asynchronous
1339
+ *
1340
+ * @see Bucket#run
1341
+ */
1342
+ static VALUE
1343
+ cb_bucket_async_p(VALUE self)
1344
+ {
1345
+ bucket_t *bucket = DATA_PTR(self);
1346
+ return bucket->async ? Qtrue : Qfalse;
1347
+ }
1348
+
1349
+ static VALUE
1350
+ cb_bucket_quiet_get(VALUE self)
1351
+ {
1352
+ bucket_t *bucket = DATA_PTR(self);
1353
+ return bucket->quiet ? Qtrue : Qfalse;
1354
+ }
1355
+
1356
+ static VALUE
1357
+ cb_bucket_quiet_set(VALUE self, VALUE val)
1358
+ {
1359
+ bucket_t *bucket = DATA_PTR(self);
1360
+ VALUE new;
1361
+
1362
+ bucket->quiet = RTEST(val);
1363
+ new = bucket->quiet ? Qtrue : Qfalse;
1364
+ return new;
1365
+ }
1366
+
1367
+ static VALUE
1368
+ cb_bucket_default_flags_get(VALUE self)
1369
+ {
1370
+ bucket_t *bucket = DATA_PTR(self);
1371
+ return ULONG2NUM(bucket->default_flags);
1372
+ }
1373
+
1374
+ static VALUE
1375
+ cb_bucket_default_flags_set(VALUE self, VALUE val)
1376
+ {
1377
+ bucket_t *bucket = DATA_PTR(self);
1378
+
1379
+ bucket->default_flags = (uint32_t)NUM2ULONG(val);
1380
+ bucket->default_format = flags_get_format(bucket->default_flags);
1381
+ return val;
1382
+ }
1383
+
1384
+ static VALUE
1385
+ cb_bucket_default_format_get(VALUE self)
1386
+ {
1387
+ bucket_t *bucket = DATA_PTR(self);
1388
+ return bucket->default_format;
1389
+ }
1390
+
1391
+ static VALUE
1392
+ cb_bucket_default_format_set(VALUE self, VALUE val)
1393
+ {
1394
+ bucket_t *bucket = DATA_PTR(self);
1395
+
1396
+ if (TYPE(val) == T_FIXNUM) {
1397
+ switch (FIX2INT(val)) {
1398
+ case FMT_DOCUMENT:
1399
+ val = sym_document;
1400
+ break;
1401
+ case FMT_MARSHAL:
1402
+ val = sym_marshal;
1403
+ break;
1404
+ case FMT_PLAIN:
1405
+ val = sym_plain;
1406
+ break;
1407
+ }
1408
+ }
1409
+ if (val == sym_document || val == sym_marshal || val == sym_plain) {
1410
+ bucket->default_format = val;
1411
+ bucket->default_flags = flags_set_format(bucket->default_flags, val);
1412
+ }
1413
+
1414
+ return val;
1415
+ }
1416
+
1417
+ static VALUE
1418
+ cb_bucket_on_error_set(VALUE self, VALUE val)
1419
+ {
1420
+ bucket_t *bucket = DATA_PTR(self);
1421
+
1422
+ if (rb_respond_to(val, id_call)) {
1423
+ bucket->on_error_proc = val;
1424
+ } else {
1425
+ bucket->on_error_proc = Qnil;
1426
+ }
1427
+
1428
+ return bucket->on_error_proc;
1429
+ }
1430
+
1431
+ static VALUE
1432
+ cb_bucket_on_error_get(VALUE self)
1433
+ {
1434
+ bucket_t *bucket = DATA_PTR(self);
1435
+
1436
+ if (rb_block_given_p()) {
1437
+ return cb_bucket_on_error_set(self, rb_block_proc());
1438
+ } else {
1439
+ return bucket->on_error_proc;
1440
+ }
1441
+ }
1442
+
1443
+ static VALUE
1444
+ cb_bucket_timeout_get(VALUE self)
1445
+ {
1446
+ bucket_t *bucket = DATA_PTR(self);
1447
+ return ULONG2NUM(bucket->timeout);
1448
+ }
1449
+
1450
+ static VALUE
1451
+ cb_bucket_timeout_set(VALUE self, VALUE val)
1452
+ {
1453
+ bucket_t *bucket = DATA_PTR(self);
1454
+ VALUE tmval;
1455
+
1456
+ bucket->timeout = (uint32_t)NUM2ULONG(val);
1457
+ libcouchbase_set_timeout(bucket->handle, bucket->timeout);
1458
+ tmval = ULONG2NUM(bucket->timeout);
1459
+
1460
+ return tmval;
1461
+ }
1462
+
1463
+ /* Document-method: hostname
1464
+ * @return [String] the host name of the management interface (default: "localhost")
1465
+ */
1466
+ static VALUE
1467
+ cb_bucket_hostname_get(VALUE self)
1468
+ {
1469
+ bucket_t *bucket = DATA_PTR(self);
1470
+ if (bucket->handle) {
1471
+ if (bucket->hostname) {
1472
+ free(bucket->hostname);
1473
+ bucket->hostname = NULL;
1474
+ }
1475
+ bucket->hostname = strdup(libcouchbase_get_host(bucket->handle));
1476
+ if (bucket->hostname == NULL) {
1477
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1478
+ }
1479
+ }
1480
+ return rb_str_new2(bucket->hostname);
1481
+ }
1482
+
1483
+ /* Document-method: port
1484
+ * @return [Fixnum] the port number of the management interface (default: 8091)
1485
+ */
1486
+ static VALUE
1487
+ cb_bucket_port_get(VALUE self)
1488
+ {
1489
+ bucket_t *bucket = DATA_PTR(self);
1490
+ if (bucket->handle) {
1491
+ bucket->port = atoi(libcouchbase_get_port(bucket->handle));
1492
+ }
1493
+ return UINT2NUM(bucket->port);
1494
+ }
1495
+
1496
+ /* Document-method: authority
1497
+ * @return [String] host with port
1498
+ */
1499
+ static VALUE
1500
+ cb_bucket_authority_get(VALUE self)
1501
+ {
1502
+ bucket_t *bucket = DATA_PTR(self);
1503
+ size_t len;
1504
+
1505
+ (void)cb_bucket_hostname_get(self);
1506
+ (void)cb_bucket_port_get(self);
1507
+ len = strlen(bucket->hostname) + 10;
1508
+ bucket->authority = calloc(len, sizeof(char));
1509
+ if (bucket->authority == NULL) {
1510
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1511
+ }
1512
+ snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
1513
+ return rb_str_new2(bucket->authority);
1514
+ }
1515
+
1516
+ /* Document-method: bucket
1517
+ * @return [String] the bucket name
1518
+ */
1519
+ static VALUE
1520
+ cb_bucket_bucket_get(VALUE self)
1521
+ {
1522
+ bucket_t *bucket = DATA_PTR(self);
1523
+ return rb_str_new2(bucket->bucket);
1524
+ }
1525
+
1526
+ /* Document-method: pool
1527
+ * @return [String] the pool name (usually "default")
1528
+ */
1529
+ static VALUE
1530
+ cb_bucket_pool_get(VALUE self)
1531
+ {
1532
+ bucket_t *bucket = DATA_PTR(self);
1533
+ return rb_str_new2(bucket->pool);
1534
+ }
1535
+
1536
+ /* Document-method: username
1537
+ * @return [String] the username for protected buckets (usually matches
1538
+ * the bucket name)
1539
+ */
1540
+ static VALUE
1541
+ cb_bucket_username_get(VALUE self)
1542
+ {
1543
+ bucket_t *bucket = DATA_PTR(self);
1544
+ return rb_str_new2(bucket->username);
1545
+ }
1546
+
1547
+ /* Document-method: password
1548
+ * @return [String] the password for protected buckets
1549
+ */
1550
+ static VALUE
1551
+ cb_bucket_password_get(VALUE self)
1552
+ {
1553
+ bucket_t *bucket = DATA_PTR(self);
1554
+ return rb_str_new2(bucket->password);
1555
+ }
1556
+
1557
+ /* Document-method: url
1558
+ * @return [String] the address of the cluster management interface
1559
+ */
1560
+ static VALUE
1561
+ cb_bucket_url_get(VALUE self)
1562
+ {
1563
+ bucket_t *bucket = DATA_PTR(self);
1564
+ VALUE str;
1565
+
1566
+ (void)cb_bucket_authority_get(self);
1567
+ str = rb_str_buf_new2("http://");
1568
+ rb_str_buf_cat2(str, bucket->authority);
1569
+ rb_str_buf_cat2(str, "/pools/");
1570
+ rb_str_buf_cat2(str, bucket->pool);
1571
+ rb_str_buf_cat2(str, "/buckets/");
1572
+ rb_str_buf_cat2(str, bucket->bucket);
1573
+ rb_str_buf_cat2(str, "/");
1574
+ return str;
1575
+ }
1576
+
1577
+ /*
1578
+ * Returns a string containing a human-readable representation of the
1579
+ * Bucket.
1580
+ *
1581
+ * @return [String]
1582
+ */
1583
+ static VALUE
1584
+ cb_bucket_inspect(VALUE self)
1585
+ {
1586
+ VALUE str;
1587
+ bucket_t *bucket = DATA_PTR(self);
1588
+ char buf[200];
1589
+
1590
+ str = rb_str_buf_new2("#<");
1591
+ rb_str_buf_cat2(str, rb_obj_classname(self));
1592
+ snprintf(buf, 25, ":%p \"", (void *)self);
1593
+ (void)cb_bucket_authority_get(self);
1594
+ rb_str_buf_cat2(str, buf);
1595
+ rb_str_buf_cat2(str, "http://");
1596
+ rb_str_buf_cat2(str, bucket->authority);
1597
+ rb_str_buf_cat2(str, "/pools/");
1598
+ rb_str_buf_cat2(str, bucket->pool);
1599
+ rb_str_buf_cat2(str, "/buckets/");
1600
+ rb_str_buf_cat2(str, bucket->bucket);
1601
+ rb_str_buf_cat2(str, "/");
1602
+ snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s, connected=%s, timeout=%u>",
1603
+ rb_id2name(SYM2ID(bucket->default_format)),
1604
+ bucket->default_flags,
1605
+ bucket->quiet ? "true" : "false",
1606
+ bucket->handle ? "true" : "false",
1607
+ bucket->timeout);
1608
+ rb_str_buf_cat2(str, buf);
1609
+
1610
+ return str;
1611
+ }
1612
+
1613
+ /*
1614
+ * Delete the specified key
1615
+ *
1616
+ * @overload delete(key, options = {})
1617
+ * @param key [String, Symbol] Key used to reference the value.
1618
+ * @param options [Hash] Options for operation.
1619
+ * @option options [Boolean] :quiet (self.quiet) If set to +true+, the
1620
+ * operation won't raise error for missing key, it will return +nil+.
1621
+ * Otherwise it will raise error in synchronous mode. In asynchronous
1622
+ * mode this option ignored.
1623
+ * @option options [Fixnum] :cas The CAS value for an object. This value
1624
+ * created on the server and is guaranteed to be unique for each value of
1625
+ * a given key. This value is used to provide simple optimistic
1626
+ * concurrency control when multiple clients or threads try to
1627
+ * update/delete an item simultaneously.
1628
+ *
1629
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1630
+ * @raise [ArgumentError] when passing the block in synchronous mode
1631
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
1632
+ * @raise [Couchbase::Error::NotFound] if key is missing in verbose mode
1633
+ *
1634
+ * @example Delete the key in quiet mode (default)
1635
+ * c.set("foo", "bar")
1636
+ * c.delete("foo") #=> true
1637
+ * c.delete("foo") #=> false
1638
+ *
1639
+ * @example Delete the key verbosely
1640
+ * c.set("foo", "bar")
1641
+ * c.delete("foo", :quiet => false) #=> true
1642
+ * c.delete("foo", :quiet => false) #=> will raise Couchbase::Error::NotFound
1643
+ *
1644
+ * @example Delete the key with version check
1645
+ * ver = c.set("foo", "bar") #=> 5992859822302167040
1646
+ * c.delete("foo", :cas => 123456) #=> will raise Couchbase::Error::KeyExists
1647
+ * c.delete("foo", :cas => ver) #=> true
1648
+ */
1649
+ static VALUE
1650
+ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
1651
+ {
1652
+ bucket_t *bucket = DATA_PTR(self);
1653
+ context_t *ctx;
1654
+ VALUE k, c, rv, proc, exc, opts;
1655
+ char *key;
1656
+ size_t nkey;
1657
+ libcouchbase_cas_t cas = 0;
1658
+ libcouchbase_error_t err;
1659
+ long seqno;
1660
+
1661
+ if (bucket->handle == NULL) {
1662
+ rb_raise(eConnectError, "closed connection");
1663
+ }
1664
+ rb_scan_args(argc, argv, "11&", &k, &opts, &proc);
1665
+ if (!bucket->async && proc != Qnil) {
1666
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1667
+ }
1668
+ k = unify_key(k);
1669
+ key = RSTRING_PTR(k);
1670
+ nkey = RSTRING_LEN(k);
1671
+ ctx = calloc(1, sizeof(context_t));
1672
+ ctx->quiet = bucket->quiet;
1673
+ if (ctx == NULL) {
1674
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1675
+ }
1676
+ if (opts != Qnil) {
1677
+ if (TYPE(opts) == T_BIGNUM || TYPE(opts) == T_FIXNUM) {
1678
+ cas = NUM2ULL(opts);
1679
+ } else {
1680
+ Check_Type(opts, T_HASH);
1681
+ if ((c = rb_hash_aref(opts, sym_cas)) != Qnil) {
1682
+ cas = NUM2ULL(c);
1683
+ }
1684
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
1685
+ ctx->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
1686
+ }
1687
+ }
1688
+ }
1689
+ ctx->proc = proc;
1690
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1691
+ rv = rb_ary_new();
1692
+ ctx->rv = &rv;
1693
+ ctx->bucket = bucket;
1694
+ ctx->exception = Qnil;
1695
+ seqno = bucket->seqno;
1696
+ bucket->seqno++;
1697
+ err = libcouchbase_remove(bucket->handle, (const void *)ctx,
1698
+ (const void *)key, nkey, cas);
1699
+ exc = cb_check_error(err, "failed to schedule delete request", Qnil);
1700
+ if (exc != Qnil) {
1701
+ free(ctx);
1702
+ rb_exc_raise(exc);
1703
+ }
1704
+ if (bucket->async) {
1705
+ return Qnil;
1706
+ } else {
1707
+ if (bucket->seqno - seqno > 0) {
1708
+ /* we have some operations pending */
1709
+ bucket->io->run_event_loop(bucket->io);
1710
+ }
1711
+ exc = ctx->exception;
1712
+ free(ctx);
1713
+ if (exc != Qnil) {
1714
+ rb_exc_raise(exc);
1715
+ }
1716
+ return rv;
1717
+ }
1718
+ }
1719
+
1720
+ static inline VALUE
1721
+ cb_bucket_store(libcouchbase_storage_t cmd, int argc, VALUE *argv, VALUE self)
1722
+ {
1723
+ bucket_t *bucket = DATA_PTR(self);
1724
+ context_t *ctx;
1725
+ VALUE k, v, arg, opts, rv, proc, exc, fmt;
1726
+ char *key, *bytes;
1727
+ size_t nkey, nbytes;
1728
+ uint32_t flags;
1729
+ time_t exp = 0;
1730
+ libcouchbase_cas_t cas = 0;
1731
+ libcouchbase_error_t err;
1732
+ long seqno;
1733
+
1734
+ if (bucket->handle == NULL) {
1735
+ rb_raise(eConnectError, "closed connection");
1736
+ }
1737
+ rb_scan_args(argc, argv, "21&", &k, &v, &opts, &proc);
1738
+ if (!bucket->async && proc != Qnil) {
1739
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1740
+ }
1741
+ k = unify_key(k);
1742
+ flags = bucket->default_flags;
1743
+ if (opts != Qnil) {
1744
+ Check_Type(opts, T_HASH);
1745
+ arg = rb_hash_aref(opts, sym_flags);
1746
+ if (arg != Qnil) {
1747
+ flags = (uint32_t)NUM2ULONG(arg);
1748
+ }
1749
+ arg = rb_hash_aref(opts, sym_ttl);
1750
+ if (arg != Qnil) {
1751
+ exp = NUM2ULONG(arg);
1752
+ }
1753
+ arg = rb_hash_aref(opts, sym_cas);
1754
+ if (arg != Qnil) {
1755
+ cas = NUM2ULL(arg);
1756
+ }
1757
+ fmt = rb_hash_aref(opts, sym_format);
1758
+ if (fmt != Qnil) { /* rewrite format bits */
1759
+ flags = flags_set_format(flags, fmt);
1760
+ }
1761
+ }
1762
+ key = RSTRING_PTR(k);
1763
+ nkey = RSTRING_LEN(k);
1764
+ v = encode_value(v, flags);
1765
+ if (v == Qundef) {
1766
+ rb_raise(eValueFormatError,
1767
+ "unable to convert value for key '%s'", key);
1768
+ }
1769
+ bytes = RSTRING_PTR(v);
1770
+ nbytes = RSTRING_LEN(v);
1771
+ ctx = calloc(1, sizeof(context_t));
1772
+ if (ctx == NULL) {
1773
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1774
+ }
1775
+ rv = Qnil;
1776
+ ctx->rv = &rv;
1777
+ ctx->bucket = bucket;
1778
+ ctx->proc = proc;
1779
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1780
+ ctx->exception = Qnil;
1781
+ seqno = bucket->seqno;
1782
+ bucket->seqno++;
1783
+ err = libcouchbase_store(bucket->handle, (const void *)ctx, cmd,
1784
+ (const void *)key, nkey, bytes, nbytes, flags, exp, cas);
1785
+ exc = cb_check_error(err, "failed to schedule set request", Qnil);
1786
+ if (exc != Qnil) {
1787
+ free(ctx);
1788
+ rb_exc_raise(exc);
1789
+ }
1790
+ if (bucket->async) {
1791
+ return Qnil;
1792
+ } else {
1793
+ if (bucket->seqno - seqno > 0) {
1794
+ /* we have some operations pending */
1795
+ bucket->io->run_event_loop(bucket->io);
1796
+ }
1797
+ exc = ctx->exception;
1798
+ free(ctx);
1799
+ if (exc != Qnil) {
1800
+ rb_exc_raise(exc);
1801
+ }
1802
+ if (bucket->exception != Qnil) {
1803
+ rb_exc_raise(bucket->exception);
1804
+ }
1805
+ return rv;
1806
+ }
1807
+ }
1808
+
1809
+ static inline VALUE
1810
+ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
1811
+ {
1812
+ bucket_t *bucket = DATA_PTR(self);
1813
+ context_t *ctx;
1814
+ VALUE k, d, arg, opts, rv, proc, exc;
1815
+ char *key;
1816
+ size_t nkey;
1817
+ time_t exp;
1818
+ uint64_t delta = 0, initial = 0;
1819
+ int create = 0;
1820
+ libcouchbase_error_t err;
1821
+ long seqno;
1822
+
1823
+ if (bucket->handle == NULL) {
1824
+ rb_raise(eConnectError, "closed connection");
1825
+ }
1826
+ rb_scan_args(argc, argv, "12&", &k, &d, &opts, &proc);
1827
+ if (!bucket->async && proc != Qnil) {
1828
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1829
+ }
1830
+ k = unify_key(k);
1831
+ ctx = calloc(1, sizeof(context_t));
1832
+ if (ctx == NULL) {
1833
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1834
+ }
1835
+ if (argc == 2 && TYPE(d) == T_HASH) {
1836
+ opts = d;
1837
+ d = Qnil;
1838
+ }
1839
+ exp = bucket->default_ttl;
1840
+ if (opts != Qnil) {
1841
+ Check_Type(opts, T_HASH);
1842
+ create = RTEST(rb_hash_aref(opts, sym_create));
1843
+ ctx->extended = RTEST(rb_hash_aref(opts, sym_extended));
1844
+ arg = rb_hash_aref(opts, sym_ttl);
1845
+ if (arg != Qnil) {
1846
+ exp = NUM2ULONG(arg);
1847
+ }
1848
+ arg = rb_hash_aref(opts, sym_initial);
1849
+ if (arg != Qnil) {
1850
+ initial = NUM2ULL(arg);
1851
+ create = 1;
1852
+ }
1853
+ }
1854
+ key = RSTRING_PTR(k);
1855
+ nkey = RSTRING_LEN(k);
1856
+ if (NIL_P(d)) {
1857
+ delta = 1 * sign;
1858
+ } else {
1859
+ delta = NUM2ULL(d) * sign;
1860
+ }
1861
+ rv = Qnil;
1862
+ ctx->rv = &rv;
1863
+ ctx->bucket = bucket;
1864
+ ctx->proc = proc;
1865
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1866
+ ctx->exception = Qnil;
1867
+ ctx->arithm = sign;
1868
+ seqno = bucket->seqno;
1869
+ bucket->seqno++;
1870
+ err = libcouchbase_arithmetic(bucket->handle, (const void *)ctx,
1871
+ (const void *)key, nkey, delta, exp, create, initial);
1872
+ exc = cb_check_error(err, "failed to schedule arithmetic request", k);
1873
+ if (exc != Qnil) {
1874
+ free(ctx);
1875
+ rb_exc_raise(exc);
1876
+ }
1877
+ if (bucket->async) {
1878
+ return Qnil;
1879
+ } else {
1880
+ if (bucket->seqno - seqno > 0) {
1881
+ /* we have some operations pending */
1882
+ bucket->io->run_event_loop(bucket->io);
1883
+ }
1884
+ exc = ctx->exception;
1885
+ free(ctx);
1886
+ if (exc != Qnil) {
1887
+ rb_exc_raise(exc);
1888
+ }
1889
+ return rv;
1890
+ }
1891
+ }
1892
+
1893
+ /*
1894
+ * Increment the value of an existing numeric key
1895
+ *
1896
+ * The increment methods enable you to increase a given stored integer
1897
+ * value. These are the incremental equivalent of the decrement operations
1898
+ * and work on the same basis; updating the value of a key if it can be
1899
+ * parsed to an integer. The update operation occurs on the server and is
1900
+ * provided at the protocol level. This simplifies what would otherwise be a
1901
+ * two-stage get and set operation.
1902
+ *
1903
+ * @note that server values stored and transmitted as unsigned numbers,
1904
+ * therefore if you try to store negative number and then increment or
1905
+ * decrement it will cause overflow. (see "Integer overflow" example
1906
+ * below)
1907
+ *
1908
+ * @overload incr(key, delta = 1, options = {})
1909
+ * @param key [String, Symbol] Key used to reference the value.
1910
+ * @param delta [Fixnum] Integer (up to 64 bits) value to increment
1911
+ * @param options [Hash] Options for operation.
1912
+ * @option options [Boolean] :create (false) If set to +true+, it will
1913
+ * initialize the key with zero value and zero flags (use +:initial+
1914
+ * option to set another initial value). Note: it won't increment the
1915
+ * missing value.
1916
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
1917
+ * missing key initialization. This option imply +:create+ option is
1918
+ * +true+.
1919
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
1920
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
1921
+ * absolute times (from the epoch). This option ignored for existent
1922
+ * keys.
1923
+ * @option options [Boolean] :extended (false) If set to +true+, the
1924
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
1925
+ * returns just value.
1926
+ *
1927
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
1928
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
1929
+ *
1930
+ * @return [Fixnum] the actual value of the key.
1931
+ *
1932
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
1933
+ * option isn't +true+.
1934
+ *
1935
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
1936
+ * value
1937
+ *
1938
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1939
+ *
1940
+ * @raise [ArgumentError] when passing the block in synchronous mode
1941
+ *
1942
+ * @example Increment key by one
1943
+ * c.incr("foo")
1944
+ *
1945
+ * @example Increment key by 50
1946
+ * c.incr("foo", 50)
1947
+ *
1948
+ * @example Increment key by one <b>OR</b> initialize with zero
1949
+ * c.incr("foo", :create => true) #=> will return old+1 or 0
1950
+ *
1951
+ * @example Increment key by one <b>OR</b> initialize with three
1952
+ * c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
1953
+ *
1954
+ * @example Increment key and get its CAS value
1955
+ * val, cas = c.incr("foo", :extended => true)
1956
+ *
1957
+ * @example Integer overflow
1958
+ * c.set("foo", -100)
1959
+ * c.get("foo") #=> -100
1960
+ * c.incr("foo") #=> 18446744073709551517
1961
+ *
1962
+ * @example Asynchronous invocation
1963
+ * c.run do
1964
+ * c.incr("foo") do |ret|
1965
+ * ret.operation #=> :increment
1966
+ * ret.success? #=> true
1967
+ * ret.key #=> "foo"
1968
+ * ret.value
1969
+ * ret.cas
1970
+ * end
1971
+ * end
1972
+ *
1973
+ */
1974
+ static VALUE
1975
+ cb_bucket_incr(int argc, VALUE *argv, VALUE self)
1976
+ {
1977
+ return cb_bucket_arithmetic(+1, argc, argv, self);
1978
+ }
1979
+
1980
+ /*
1981
+ * Decrement the value of an existing numeric key
1982
+ *
1983
+ * The decrement methods reduce the value of a given key if the
1984
+ * corresponding value can be parsed to an integer value. These operations
1985
+ * are provided at a protocol level to eliminate the need to get, update,
1986
+ * and reset a simple integer value in the database. It supports the use of
1987
+ * an explicit offset value that will be used to reduce the stored value in
1988
+ * the database.
1989
+ *
1990
+ * @note that server values stored and transmitted as unsigned numbers,
1991
+ * therefore if you try to decrement negative or zero key, you will always
1992
+ * get zero.
1993
+ *
1994
+ * @overload decr(key, delta = 1, options = {})
1995
+ * @param key [String, Symbol] Key used to reference the value.
1996
+ * @param delta [Fixnum] Integer (up to 64 bits) value to decrement
1997
+ * @param options [Hash] Options for operation.
1998
+ * @option options [Boolean] :create (false) If set to +true+, it will
1999
+ * initialize the key with zero value and zero flags (use +:initial+
2000
+ * option to set another initial value). Note: it won't decrement the
2001
+ * missing value.
2002
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
2003
+ * missing key initialization. This option imply +:create+ option is
2004
+ * +true+.
2005
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
2006
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
2007
+ * absolute times (from the epoch). This option ignored for existent
2008
+ * keys.
2009
+ * @option options [Boolean] :extended (false) If set to +true+, the
2010
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
2011
+ * returns just value.
2012
+ *
2013
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
2014
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
2015
+ *
2016
+ * @return [Fixnum] the actual value of the key.
2017
+ *
2018
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
2019
+ * option isn't +true+.
2020
+ *
2021
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
2022
+ * value
2023
+ *
2024
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2025
+ *
2026
+ * @raise [ArgumentError] when passing the block in synchronous mode
2027
+ *
2028
+ * @example Decrement key by one
2029
+ * c.decr("foo")
2030
+ *
2031
+ * @example Decrement key by 50
2032
+ * c.decr("foo", 50)
2033
+ *
2034
+ * @example Decrement key by one <b>OR</b> initialize with zero
2035
+ * c.decr("foo", :create => true) #=> will return old-1 or 0
2036
+ *
2037
+ * @example Decrement key by one <b>OR</b> initialize with three
2038
+ * c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
2039
+ *
2040
+ * @example Decrement key and get its CAS value
2041
+ * val, cas = c.decr("foo", :extended => true)
2042
+ *
2043
+ * @example Decrementing zero
2044
+ * c.set("foo", 0)
2045
+ * c.decrement("foo", 100500) #=> 0
2046
+ *
2047
+ * @example Decrementing negative value
2048
+ * c.set("foo", -100)
2049
+ * c.decrement("foo", 100500) #=> 0
2050
+ *
2051
+ * @example Asynchronous invocation
2052
+ * c.run do
2053
+ * c.decr("foo") do |ret|
2054
+ * ret.operation #=> :decrement
2055
+ * ret.success? #=> true
2056
+ * ret.key #=> "foo"
2057
+ * ret.value
2058
+ * ret.cas
2059
+ * end
2060
+ * end
2061
+ *
2062
+ */
2063
+ static VALUE
2064
+ cb_bucket_decr(int argc, VALUE *argv, VALUE self)
2065
+ {
2066
+ return cb_bucket_arithmetic(-1, argc, argv, self);
2067
+ }
2068
+
2069
+ /*
2070
+ * Obtain an object stored in Couchbase by given key.
2071
+ *
2072
+ * @overload get(*keys, options = {})
2073
+ * @param keys [String, Symbol, Array] One or several keys to fetch
2074
+ * @param options [Hash] Options for operation.
2075
+ * @option options [Boolean] :extended (false) If set to +true+, the
2076
+ * operation will return tuple +[value, flags, cas]+, otherwise (by
2077
+ * default) it returns just value.
2078
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
2079
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
2080
+ * absolute times (from the epoch).
2081
+ * @option options [Boolean] :quiet (self.quiet) If set to +true+, the
2082
+ * operation won't raise error for missing key, it will return +nil+.
2083
+ * Otherwise it will raise error in synchronous mode. In asynchronous
2084
+ * mode this option ignored.
2085
+ *
2086
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
2087
+ * (valid attributes: +error+, +operation+, +key+, +value+, +flags+,
2088
+ * +cas+).
2089
+ *
2090
+ * @return [Object, Array, Hash] the value(s) (or tuples in extended mode)
2091
+ * assiciated with the key.
2092
+ *
2093
+ * @raise [Couchbase::Error::NotFound] if the key is missing in the
2094
+ * bucket.
2095
+ *
2096
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2097
+ *
2098
+ * @raise [ArgumentError] when passing the block in synchronous mode
2099
+ *
2100
+ * @example Get single value in quite mode (the default)
2101
+ * c.get("foo") #=> the associated value or nil
2102
+ *
2103
+ * @example Use alternative hash-like syntax
2104
+ * c["foo"] #=> the associated value or nil
2105
+ *
2106
+ * @example Get single value in verbose mode
2107
+ * c.get("missing-foo", :quiet => false) #=> raises Couchbase::NotFound
2108
+ *
2109
+ * @example Get and touch single value. The key won't be accessible after 10 seconds
2110
+ * c.get("foo", :ttl => 10)
2111
+ *
2112
+ * @example Extended get
2113
+ * val, flags, cas = c.get("foo", :extended => true)
2114
+ *
2115
+ * @example Get multiple keys
2116
+ * c.get("foo", "bar", "baz") #=> [val1, val2, val3]
2117
+ *
2118
+ * @example Extended get multiple keys
2119
+ * c.get("foo", "bar", :extended => true)
2120
+ * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
2121
+ *
2122
+ * @example Asynchronous get
2123
+ * c.run do
2124
+ * c.get("foo", "bar", "baz") do |res|
2125
+ * ret.operation #=> :get
2126
+ * ret.success? #=> true
2127
+ * ret.key #=> "foo", "bar" or "baz" in separate calls
2128
+ * ret.value
2129
+ * ret.flags
2130
+ * ret.cas
2131
+ * end
2132
+ * end
2133
+ *
2134
+ * @overload get(keys, options = {})
2135
+ * When the method receive hash map, it will behave like it receive list
2136
+ * of keys (+keys.keys+), but also touch each key setting expiry time to
2137
+ * the corresponding value. But unlike usual get this command always
2138
+ * return hash map +{key => value}+ or +{key => [value, flags, cas]}+.
2139
+ *
2140
+ * @param keys [Hash] Map key-ttl
2141
+ * @param options [Hash] Options for operation. (see options definition
2142
+ * above)
2143
+ *
2144
+ * @return [Hash] the values (or tuples in extended mode) assiciated with
2145
+ * the keys.
2146
+ *
2147
+ * @example Get and touch multiple keys
2148
+ * c.get("foo" => 10, "bar" => 20) #=> {"foo" => val1, "bar" => val2}
2149
+ *
2150
+ * @example Extended get and touch multiple keys
2151
+ * c.get({"foo" => 10, "bar" => 20}, :extended => true)
2152
+ * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
2153
+ */
2154
+ static VALUE
2155
+ cb_bucket_get(int argc, VALUE *argv, VALUE self)
2156
+ {
2157
+ bucket_t *bucket = DATA_PTR(self);
2158
+ context_t *ctx;
2159
+ VALUE args, rv, proc, exc, keys;
2160
+ long nn;
2161
+ libcouchbase_error_t err;
2162
+ struct key_traits *traits;
2163
+ int extended, mgat;
2164
+ long seqno;
2165
+
2166
+ if (bucket->handle == NULL) {
2167
+ rb_raise(eConnectError, "closed connection");
2168
+ }
2169
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
2170
+ if (!bucket->async && proc != Qnil) {
2171
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
2172
+ }
2173
+ rb_funcall(args, id_flatten_bang, 0);
2174
+ traits = calloc(1, sizeof(struct key_traits));
2175
+ nn = cb_args_scan_keys(RARRAY_LEN(args), args, bucket, traits);
2176
+ ctx = calloc(1, sizeof(context_t));
2177
+ if (ctx == NULL) {
2178
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
2179
+ }
2180
+ mgat = traits->mgat;
2181
+ keys = traits->keys_ary;
2182
+ ctx->proc = proc;
2183
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
2184
+ ctx->bucket = bucket;
2185
+ ctx->extended = traits->extended;
2186
+ ctx->quiet = traits->quiet;
2187
+ rv = rb_hash_new();
2188
+ ctx->rv = &rv;
2189
+ ctx->exception = Qnil;
2190
+ seqno = bucket->seqno;
2191
+ bucket->seqno += nn;
2192
+ err = libcouchbase_mget(bucket->handle, (const void *)ctx,
2193
+ traits->nkeys, (const void * const *)traits->keys,
2194
+ traits->lens, (traits->explicit_ttl) ? traits->ttls : NULL);
2195
+ free(traits->keys);
2196
+ free(traits->lens);
2197
+ free(traits->ttls);
2198
+ free(traits);
2199
+ exc = cb_check_error(err, "failed to schedule get request", Qnil);
2200
+ if (exc != Qnil) {
2201
+ free(ctx);
2202
+ rb_exc_raise(exc);
2203
+ }
2204
+ if (bucket->async) {
2205
+ return Qnil;
2206
+ } else {
2207
+ if (bucket->seqno - seqno > 0) {
2208
+ /* we have some operations pending */
2209
+ bucket->io->run_event_loop(bucket->io);
2210
+ }
2211
+ exc = ctx->exception;
2212
+ extended = ctx->extended;
2213
+ free(ctx);
2214
+ if (exc != Qnil) {
2215
+ rb_exc_raise(exc);
2216
+ }
2217
+ if (bucket->exception != Qnil) {
2218
+ rb_exc_raise(bucket->exception);
2219
+ }
2220
+ if (mgat || (extended && nn > 1)) {
2221
+ return rv; /* return as a hash {key => [value, flags, cas], ...} */
2222
+ }
2223
+ if (nn > 1) {
2224
+ long ii;
2225
+ VALUE *keys_ptr, ret;
2226
+ ret = rb_ary_new();
2227
+ keys_ptr = RARRAY_PTR(keys);
2228
+ for (ii = 0; ii < nn; ii++) {
2229
+ rb_ary_push(ret, rb_hash_aref(rv, keys_ptr[ii]));
2230
+ }
2231
+ return ret; /* return as an array [value1, value2, ...] */
2232
+ } else {
2233
+ VALUE vv = Qnil;
2234
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
2235
+ return vv;
2236
+ }
2237
+ }
2238
+ }
2239
+
2240
+ /*
2241
+ * Update the expiry time of an item
2242
+ *
2243
+ * The +touch+ method allow you to update the expiration time on a given
2244
+ * key. This can be useful for situations where you want to prevent an item
2245
+ * from expiring without resetting the associated value. For example, for a
2246
+ * session database you might want to keep the session alive in the database
2247
+ * each time the user accesses a web page without explicitly updating the
2248
+ * session value, keeping the user's session active and available.
2249
+ *
2250
+ * @overload touch(key, options = {})
2251
+ * @param key [String, Symbol] Key used to reference the value.
2252
+ * @param options [Hash] Options for operation.
2253
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
2254
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
2255
+ * absolute times (from the epoch).
2256
+ *
2257
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
2258
+ * (valid attributes: +error+, +operation+, +key+).
2259
+ *
2260
+ * @return [Boolean] +true+ if the operation was successful and +false+
2261
+ * otherwise.
2262
+ *
2263
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2264
+ *
2265
+ * @raise [ArgumentError] when passing the block in synchronous mode
2266
+ *
2267
+ * @example Touch value using +default_ttl+
2268
+ * c.touch("foo")
2269
+ *
2270
+ * @example Touch value using custom TTL (10 seconds)
2271
+ * c.touch("foo", :ttl => 10)
2272
+ *
2273
+ * @overload touch(keys)
2274
+ * @param keys [Hash] The Hash where keys represent the keys in the
2275
+ * database, values -- the expiry times for corresponding key. See
2276
+ * description of +:ttl+ argument above for more information about TTL
2277
+ * values.
2278
+ *
2279
+ * @yieldparam ret [Result] the result of operation for each key in
2280
+ * asynchronous mode (valid attributes: +error+, +operation+, +key+).
2281
+ *
2282
+ * @return [Hash] Mapping keys to result of touch operation (+true+ if the
2283
+ * operation was successful and +false+ otherwise)
2284
+ *
2285
+ * @example Touch several values
2286
+ * c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
2287
+ *
2288
+ * @example Touch several values in async mode
2289
+ * c.run do
2290
+ * c.touch("foo" => 10, :bar => 20) do |ret|
2291
+ * ret.operation #=> :touch
2292
+ * ret.success? #=> true
2293
+ * ret.key #=> "foo" and "bar" in separate calls
2294
+ * end
2295
+ * end
2296
+ *
2297
+ * @example Touch single value
2298
+ * c.touch("foo" => 10) #=> true
2299
+ *
2300
+ */
2301
+ static VALUE
2302
+ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
2303
+ {
2304
+ bucket_t *bucket = DATA_PTR(self);
2305
+ context_t *ctx;
2306
+ VALUE args, rv, proc, exc;
2307
+ size_t nn;
2308
+ libcouchbase_error_t err;
2309
+ struct key_traits *traits;
2310
+ long seqno;
2311
+
2312
+ if (bucket->handle == NULL) {
2313
+ rb_raise(eConnectError, "closed connection");
2314
+ }
2315
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
2316
+ if (!bucket->async && proc != Qnil) {
2317
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
2318
+ }
2319
+ rb_funcall(args, id_flatten_bang, 0);
2320
+ traits = calloc(1, sizeof(struct key_traits));
2321
+ nn = cb_args_scan_keys(RARRAY_LEN(args), args, bucket, traits);
2322
+ ctx = calloc(1, sizeof(context_t));
2323
+ if (ctx == NULL) {
2324
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
2325
+ }
2326
+ ctx->proc = proc;
2327
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
2328
+ ctx->bucket = bucket;
2329
+ rv = rb_hash_new();
2330
+ ctx->rv = &rv;
2331
+ ctx->exception = Qnil;
2332
+ seqno = bucket->seqno;
2333
+ bucket->seqno += nn;
2334
+ err = libcouchbase_mtouch(bucket->handle, (const void *)ctx,
2335
+ traits->nkeys, (const void * const *)traits->keys,
2336
+ traits->lens, traits->ttls);
2337
+ free(traits->keys);
2338
+ free(traits->lens);
2339
+ free(traits);
2340
+ exc = cb_check_error(err, "failed to schedule touch request", Qnil);
2341
+ if (exc != Qnil) {
2342
+ free(ctx);
2343
+ rb_exc_raise(exc);
2344
+ }
2345
+ if (bucket->async) {
2346
+ return Qnil;
2347
+ } else {
2348
+ if (bucket->seqno - seqno > 0) {
2349
+ /* we have some operations pending */
2350
+ bucket->io->run_event_loop(bucket->io);
2351
+ }
2352
+ exc = ctx->exception;
2353
+ free(ctx);
2354
+ if (exc != Qnil) {
2355
+ rb_exc_raise(exc);
2356
+ }
2357
+ if (bucket->exception != Qnil) {
2358
+ rb_exc_raise(bucket->exception);
2359
+ }
2360
+ if (nn > 1) {
2361
+ return rv; /* return as a hash {key => true, ...} */
2362
+ } else {
2363
+ VALUE vv = Qnil;
2364
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
2365
+ return vv;
2366
+ }
2367
+ }
2368
+ }
2369
+
2370
+ /*
2371
+ * Deletes all values from a server
2372
+ *
2373
+ * @overload flush
2374
+ * @yieldparam [Result] ret the object with +error+, +node+ and +operation+
2375
+ * attributes.
2376
+ *
2377
+ * @return [Boolean] +true+ on success
2378
+ *
2379
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2380
+ * @raise [ArgumentError] when passing the block in synchronous mode
2381
+ *
2382
+ * @example Simple flush the bucket
2383
+ * c.flush #=> true
2384
+ *
2385
+ * @example Asynchronous flush
2386
+ * c.run do
2387
+ * c.flush do |ret|
2388
+ * ret.operation #=> :flush
2389
+ * ret.success? #=> true
2390
+ * ret.node #=> "localhost:11211"
2391
+ * end
2392
+ * end
2393
+ */
2394
+ static VALUE
2395
+ cb_bucket_flush(VALUE self)
2396
+ {
2397
+ bucket_t *bucket = DATA_PTR(self);
2398
+ context_t *ctx;
2399
+ VALUE rv, exc;
2400
+ libcouchbase_error_t err;
2401
+ long seqno;
2402
+
2403
+ if (bucket->handle == NULL) {
2404
+ rb_raise(eConnectError, "closed connection");
2405
+ }
2406
+ if (!bucket->async && rb_block_given_p()) {
2407
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
2408
+ }
2409
+ ctx = calloc(1, sizeof(context_t));
2410
+ if (ctx == NULL) {
2411
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
2412
+ }
2413
+ rv = Qtrue; /* optimistic by default */
2414
+ ctx->rv = &rv;
2415
+ ctx->bucket = bucket;
2416
+ ctx->exception = Qnil;
2417
+ if (rb_block_given_p()) {
2418
+ ctx->proc = rb_block_proc();
2419
+ } else {
2420
+ ctx->proc = Qnil;
2421
+ }
2422
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
2423
+ seqno = bucket->seqno;
2424
+ bucket->seqno++;
2425
+ err = libcouchbase_flush(bucket->handle, (const void *)ctx);
2426
+ exc = cb_check_error(err, "failed to schedule flush request", Qnil);
2427
+ if (exc != Qnil) {
2428
+ free(ctx);
2429
+ rb_exc_raise(exc);
2430
+ }
2431
+ if (bucket->async) {
2432
+ return Qnil;
2433
+ } else {
2434
+ if (bucket->seqno - seqno > 0) {
2435
+ /* we have some operations pending */
2436
+ bucket->io->run_event_loop(bucket->io);
2437
+ }
2438
+ exc = ctx->exception;
2439
+ free(ctx);
2440
+ if (exc != Qnil) {
2441
+ rb_exc_raise(exc);
2442
+ }
2443
+ return rv;
2444
+ }
2445
+ }
2446
+
2447
+ /*
2448
+ * Returns versions of the server for each node in the cluster
2449
+ *
2450
+ * @overload version
2451
+ * @yieldparam [Result] ret the object with +error+, +node+, +operation+
2452
+ * and +value+ attributes.
2453
+ *
2454
+ * @return [Hash] node-version pairs
2455
+ *
2456
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2457
+ * @raise [ArgumentError] when passing the block in synchronous mode
2458
+ *
2459
+ * @example Synchronous version request
2460
+ * c.version #=> will render version
2461
+ *
2462
+ * @example Asynchronous version request
2463
+ * c.run do
2464
+ * c.version do |ret|
2465
+ * ret.operation #=> :version
2466
+ * ret.success? #=> true
2467
+ * ret.node #=> "localhost:11211"
2468
+ * ret.value #=> will render version
2469
+ * end
2470
+ * end
2471
+ */
2472
+ static VALUE
2473
+ cb_bucket_version(VALUE self)
2474
+ {
2475
+ bucket_t *bucket = DATA_PTR(self);
2476
+ context_t *ctx;
2477
+ VALUE rv, exc;
2478
+ libcouchbase_error_t err;
2479
+ long seqno;
2480
+
2481
+ if (bucket->handle == NULL) {
2482
+ rb_raise(eConnectError, "closed connection");
2483
+ }
2484
+ if (!bucket->async && rb_block_given_p()) {
2485
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
2486
+ }
2487
+ ctx = calloc(1, sizeof(context_t));
2488
+ if (ctx == NULL) {
2489
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
2490
+ }
2491
+ rv = rb_hash_new();
2492
+ ctx->rv = &rv;
2493
+ ctx->bucket = bucket;
2494
+ ctx->exception = Qnil;
2495
+ if (rb_block_given_p()) {
2496
+ ctx->proc = rb_block_proc();
2497
+ } else {
2498
+ ctx->proc = Qnil;
2499
+ }
2500
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
2501
+ seqno = bucket->seqno;
2502
+ bucket->seqno++;
2503
+ err = libcouchbase_server_versions(bucket->handle, (const void *)ctx);
2504
+ exc = cb_check_error(err, "failed to schedule version request", Qnil);
2505
+ if (exc != Qnil) {
2506
+ free(ctx);
2507
+ rb_exc_raise(exc);
2508
+ }
2509
+ if (bucket->async) {
2510
+ return Qnil;
2511
+ } else {
2512
+ if (bucket->seqno - seqno > 0) {
2513
+ /* we have some operations pending */
2514
+ bucket->io->run_event_loop(bucket->io);
2515
+ }
2516
+ exc = ctx->exception;
2517
+ free(ctx);
2518
+ if (exc != Qnil) {
2519
+ rb_exc_raise(exc);
2520
+ }
2521
+ return rv;
2522
+ }
2523
+ }
2524
+
2525
+ /*
2526
+ * Request server statistics.
2527
+ *
2528
+ * Fetches stats from each node in cluster. Without a key specified the
2529
+ * server will respond with a "default" set of statistical information. In
2530
+ * asynchronous mode each statistic is returned in separate call where the
2531
+ * Result object yielded (+#key+ contains the name of the statistical item
2532
+ * and the +#value+ contains the value, the +#node+ will indicate the server
2533
+ * address). In synchronous mode it returns the hash of stats keys and
2534
+ * node-value pairs as a value.
2535
+ *
2536
+ * @overload stats(arg = nil)
2537
+ * @param [String] arg argument to STATS query
2538
+ * @yieldparam [Result] ret the object with +node+, +key+ and +value+
2539
+ * attributes.
2540
+ *
2541
+ * @example Found how many items in the bucket
2542
+ * total = 0
2543
+ * c.stats["total_items"].each do |key, value|
2544
+ * total += value.to_i
2545
+ * end
2546
+ *
2547
+ * @example Found total items number asynchronously
2548
+ * total = 0
2549
+ * c.run do
2550
+ * c.stats do |ret|
2551
+ * if ret.key == "total_items"
2552
+ * total += ret.value.to_i
2553
+ * end
2554
+ * end
2555
+ * end
2556
+ *
2557
+ * @example Get memory stats (works on couchbase buckets)
2558
+ * c.stats(:memory) #=> {"mem_used"=>{...}, ...}
2559
+ *
2560
+ * @return [Hash] where keys are stat keys, values are host-value pairs
2561
+ *
2562
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2563
+ * @raise [ArgumentError] when passing the block in synchronous mode
2564
+ */
2565
+ static VALUE
2566
+ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
2567
+ {
2568
+ bucket_t *bucket = DATA_PTR(self);
2569
+ context_t *ctx;
2570
+ VALUE rv, exc, arg, proc;
2571
+ char *key;
2572
+ size_t nkey;
2573
+ libcouchbase_error_t err;
2574
+ long seqno;
2575
+
2576
+ if (bucket->handle == NULL) {
2577
+ rb_raise(eConnectError, "closed connection");
2578
+ }
2579
+ rb_scan_args(argc, argv, "01&", &arg, &proc);
2580
+ if (!bucket->async && proc != Qnil) {
2581
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
2582
+ }
2583
+
2584
+ ctx = calloc(1, sizeof(context_t));
2585
+ if (ctx == NULL) {
2586
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
2587
+ }
2588
+ rv = rb_hash_new();
2589
+ ctx->rv = &rv;
2590
+ ctx->bucket = bucket;
2591
+ ctx->proc = proc;
2592
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
2593
+ ctx->exception = Qnil;
2594
+ if (arg != Qnil) {
2595
+ arg = unify_key(arg);
2596
+ key = RSTRING_PTR(arg);
2597
+ nkey = RSTRING_LEN(arg);
2598
+ } else {
2599
+ key = NULL;
2600
+ nkey = 0;
2601
+ }
2602
+ seqno = bucket->seqno;
2603
+ bucket->seqno++;
2604
+ err = libcouchbase_server_stats(bucket->handle, (const void *)ctx,
2605
+ key, nkey);
2606
+ exc = cb_check_error(err, "failed to schedule stat request", Qnil);
2607
+ if (exc != Qnil) {
2608
+ free(ctx);
2609
+ rb_exc_raise(exc);
2610
+ }
2611
+ if (bucket->async) {
2612
+ return Qnil;
2613
+ } else {
2614
+ if (bucket->seqno - seqno > 0) {
2615
+ /* we have some operations pending */
2616
+ bucket->io->run_event_loop(bucket->io);
2617
+ }
2618
+ exc = ctx->exception;
2619
+ free(ctx);
2620
+ if (exc != Qnil) {
2621
+ rb_exc_raise(exc);
2622
+ }
2623
+ if (bucket->exception != Qnil) {
2624
+ rb_exc_raise(bucket->exception);
2625
+ }
2626
+ return rv;
2627
+ }
2628
+
2629
+ return Qnil;
2630
+ }
2631
+
2632
+ static VALUE
2633
+ do_run(VALUE *args)
2634
+ {
2635
+ VALUE self = args[0], proc = args[1], exc;
2636
+ bucket_t *bucket = DATA_PTR(self);
2637
+ time_t tm;
2638
+ uint32_t old_tmo, new_tmo, diff;
2639
+
2640
+ if (bucket->handle == NULL) {
2641
+ rb_raise(eConnectError, "closed connection");
2642
+ }
2643
+ if (bucket->async) {
2644
+ rb_raise(eInvalidError, "nested #run");
2645
+ }
2646
+ bucket->seqno = 0;
2647
+ bucket->async = 1;
2648
+
2649
+ tm = time(NULL);
2650
+ cb_proc_call(proc, 1, self);
2651
+ if (bucket->seqno > 0) {
2652
+ old_tmo = libcouchbase_get_timeout(bucket->handle);
2653
+ diff = (uint32_t)(time(NULL) - tm + 1);
2654
+ diff *= 1000000;
2655
+ new_tmo = bucket->timeout += diff;
2656
+ libcouchbase_set_timeout(bucket->handle, bucket->timeout);
2657
+ bucket->io->run_event_loop(bucket->io);
2658
+ /* restore timeout if it wasn't changed */
2659
+ if (bucket->timeout == new_tmo) {
2660
+ libcouchbase_set_timeout(bucket->handle, old_tmo);
2661
+ }
2662
+ if (bucket->exception != Qnil) {
2663
+ exc = bucket->exception;
2664
+ bucket->exception = Qnil;
2665
+ rb_exc_raise(exc);
2666
+ }
2667
+ }
2668
+ return Qnil;
2669
+ }
2670
+
2671
+ static VALUE
2672
+ ensure_run(VALUE *args)
2673
+ {
2674
+ VALUE self = args[0];
2675
+ bucket_t *bucket = DATA_PTR(self);
2676
+
2677
+ bucket->async = 0;
2678
+ return Qnil;
2679
+ }
2680
+
2681
+ /*
2682
+ * Run the event loop.
2683
+ *
2684
+ * @yieldparam [Bucket] bucket the bucket instance
2685
+ *
2686
+ * @example Use block to run the loop
2687
+ * c = Couchbase.new
2688
+ * c.run do
2689
+ * c.get("foo") {|ret| puts ret.value}
2690
+ * end
2691
+ *
2692
+ * @example Use lambda to run the loop
2693
+ * c = Couchbase.new
2694
+ * operations = lambda do |c|
2695
+ * c.get("foo") {|ret| puts ret.value}
2696
+ * end
2697
+ * c.run(&operations)
2698
+ *
2699
+ * @return [nil]
2700
+ *
2701
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2702
+ */
2703
+ static VALUE
2704
+ cb_bucket_run(VALUE self)
2705
+ {
2706
+ VALUE args[2];
2707
+
2708
+ rb_need_block();
2709
+ args[0] = self;
2710
+ args[1] = rb_block_proc();
2711
+ rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
2712
+ return Qnil;
2713
+ }
2714
+
2715
+ /*
2716
+ * Unconditionally store the object in the Couchbase
2717
+ *
2718
+ * @overload set(key, value, options = {})
2719
+ *
2720
+ * @param key [String, Symbol] Key used to reference the value.
2721
+ * @param value [Object] Value to be stored
2722
+ * @param options [Hash] Options for operation.
2723
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
2724
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
2725
+ * absolute times (from the epoch).
2726
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
2727
+ * options. Flags are ignored by the server but preserved for use by the
2728
+ * client. For more info see {Bucket#default_flags}.
2729
+ * @option options [Symbol] :format (self.default_format) The
2730
+ * representation for storing the value in the bucket. For more info see
2731
+ * {Bucket#default_format}.
2732
+ * @option options [Fixnum] :cas The CAS value for an object. This value
2733
+ * created on the server and is guaranteed to be unique for each value of
2734
+ * a given key. This value is used to provide simple optimistic
2735
+ * concurrency control when multiple clients or threads try to update an
2736
+ * item simultaneously.
2737
+ *
2738
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
2739
+ * (valid attributes: +error+, +operation+, +key+).
2740
+ *
2741
+ * @return [Fixnum] The CAS value of the object.
2742
+ *
2743
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect}).
2744
+ * @raise [Couchbase::Error::KeyExists] if the key already exists on the
2745
+ * server.
2746
+ * @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
2747
+ * with chosen encoder, e.g. if you try to store the Hash in +:plain+
2748
+ * mode.
2749
+ * @raise [ArgumentError] when passing the block in synchronous mode
2750
+ *
2751
+ * @example Store the key which will be expired in 2 seconds using relative TTL.
2752
+ * c.set("foo", "bar", :ttl => 2)
2753
+ *
2754
+ * @example Store the key which will be expired in 2 seconds using absolute TTL.
2755
+ * c.set("foo", "bar", :ttl => Time.now.to_i + 2)
2756
+ *
2757
+ * @example Force JSON document format for value
2758
+ * c.set("foo", {"bar" => "baz}, :format => :document)
2759
+ *
2760
+ * @example Use hash-like syntax to store the value
2761
+ * c.set["foo"] = {"bar" => "baz}
2762
+ *
2763
+ * @example Use extended hash-like syntax
2764
+ * c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
2765
+ * c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
2766
+ *
2767
+ * @example Set application specific flags (note that it will be OR-ed with format flags)
2768
+ * c.set("foo", "bar", :flags => 0x1000)
2769
+ *
2770
+ * @example Perform optimistic locking by specifying last known CAS version
2771
+ * c.set("foo", "bar", :cas => 8835713818674332672)
2772
+ *
2773
+ * @example Perform asynchronous call
2774
+ * c.run do
2775
+ * c.set("foo", "bar") do |ret|
2776
+ * ret.operation #=> :set
2777
+ * ret.success? #=> true
2778
+ * ret.key #=> "foo"
2779
+ * ret.cas
2780
+ * end
2781
+ * end
2782
+ */
2783
+ static VALUE
2784
+ cb_bucket_set(int argc, VALUE *argv, VALUE self)
2785
+ {
2786
+ return cb_bucket_store(LIBCOUCHBASE_SET, argc, argv, self);
2787
+ }
2788
+
2789
+ /*
2790
+ * Add the item to the database, but fail if the object exists already
2791
+ *
2792
+ * @overload add(key, value, options = {})
2793
+ * @param key [String, Symbol] Key used to reference the value.
2794
+ * @param value [Object] Value to be stored
2795
+ * @param options [Hash] Options for operation.
2796
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
2797
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
2798
+ * absolute times (from the epoch).
2799
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
2800
+ * options. Flags are ignored by the server but preserved for use by the
2801
+ * client. For more info see {Bucket#default_flags}.
2802
+ * @option options [Symbol] :format (self.default_format) The
2803
+ * representation for storing the value in the bucket. For more info see
2804
+ * {Bucket#default_format}.
2805
+ * @option options [Fixnum] :cas The CAS value for an object. This value
2806
+ * created on the server and is guaranteed to be unique for each value of
2807
+ * a given key. This value is used to provide simple optimistic
2808
+ * concurrency control when multiple clients or threads try to update an
2809
+ * item simultaneously.
2810
+ *
2811
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
2812
+ * (valid attributes: +error+, +operation+, +key+).
2813
+ *
2814
+ * @return [Fixnum] The CAS value of the object.
2815
+ *
2816
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2817
+ * @raise [Couchbase::Error::KeyExists] if the key already exists on the
2818
+ * server
2819
+ * @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
2820
+ * with chosen encoder, e.g. if you try to store the Hash in +:plain+
2821
+ * mode.
2822
+ * @raise [ArgumentError] when passing the block in synchronous mode
2823
+ *
2824
+ * @example Add the same key twice
2825
+ * c.add("foo", "bar") #=> stored successully
2826
+ * c.add("foo", "baz") #=> will raise Couchbase::Error::KeyExists: failed to store value (key="foo", error=0x0c)
2827
+ */
2828
+ static VALUE
2829
+ cb_bucket_add(int argc, VALUE *argv, VALUE self)
2830
+ {
2831
+ return cb_bucket_store(LIBCOUCHBASE_ADD, argc, argv, self);
2832
+ }
2833
+
2834
+ /*
2835
+ * Replace the existing object in the database
2836
+ *
2837
+ * @overload replace(key, value, options = {})
2838
+ * @param key [String, Symbol] Key used to reference the value.
2839
+ * @param value [Object] Value to be stored
2840
+ * @param options [Hash] Options for operation.
2841
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
2842
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
2843
+ * absolute times (from the epoch).
2844
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
2845
+ * options. Flags are ignored by the server but preserved for use by the
2846
+ * client. For more info see {Bucket#default_flags}.
2847
+ * @option options [Symbol] :format (self.default_format) The
2848
+ * representation for storing the value in the bucket. For more info see
2849
+ * {Bucket#default_format}.
2850
+ *
2851
+ * @return [Fixnum] The CAS value of the object.
2852
+ *
2853
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2854
+ * @raise [Couchbase::Error::NotFound] if the key doesn't exists
2855
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
2856
+ * @raise [ArgumentError] when passing the block in synchronous mode
2857
+ *
2858
+ * @example Replacing missing key
2859
+ * c.replace("foo", "baz") #=> will raise Couchbase::Error::NotFound: failed to store value (key="foo", error=0x0d)
2860
+ */
2861
+ static VALUE
2862
+ cb_bucket_replace(int argc, VALUE *argv, VALUE self)
2863
+ {
2864
+ return cb_bucket_store(LIBCOUCHBASE_REPLACE, argc, argv, self);
2865
+ }
2866
+
2867
+ /*
2868
+ * Append this object to the existing object
2869
+ *
2870
+ * @note This operation is kind of data-aware from server point of view.
2871
+ * This mean that the server treats value as binary stream and just
2872
+ * perform concatenation, therefore it won't work with +:marshal+ and
2873
+ * +:document+ formats, because of lack of knowledge how to merge values
2874
+ * in these formats. See Bucket#cas for workaround.
2875
+ *
2876
+ * @overload append(key, value, options = {})
2877
+ * @param key [String, Symbol] Key used to reference the value.
2878
+ * @param value [Object] Value to be stored
2879
+ * @param options [Hash] Options for operation.
2880
+ * @option options [Fixnum] :cas The CAS value for an object. This value
2881
+ * created on the server and is guaranteed to be unique for each value of
2882
+ * a given key. This value is used to provide simple optimistic
2883
+ * concurrency control when multiple clients or threads try to update an
2884
+ * item simultaneously.
2885
+ * @option options [Symbol] :format (self.default_format) The
2886
+ * representation for storing the value in the bucket. For more info see
2887
+ * {Bucket#default_format}.
2888
+ *
2889
+ * @return [Fixnum] The CAS value of the object.
2890
+ *
2891
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2892
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
2893
+ * @raise [Couchbase::Error::NotStored] if the key doesn't exist
2894
+ * @raise [ArgumentError] when passing the block in synchronous mode
2895
+ *
2896
+ * @example Simple append
2897
+ * c.set("foo", "aaa")
2898
+ * c.append("foo", "bbb")
2899
+ * c.get("foo") #=> "aaabbb"
2900
+ *
2901
+ * @example Implementing sets using append
2902
+ * def set_add(key, *values)
2903
+ * encoded = values.flatten.map{|v| "+#{v} "}.join
2904
+ * append(key, encoded)
2905
+ * end
2906
+ *
2907
+ * def set_remove(key, *values)
2908
+ * encoded = values.flatten.map{|v| "-#{v} "}.join
2909
+ * append(key, encoded)
2910
+ * end
2911
+ *
2912
+ * def set_get(key)
2913
+ * encoded = get(key)
2914
+ * ret = Set.new
2915
+ * encoded.split(' ').each do |v|
2916
+ * op, val = v[0], v[1..-1]
2917
+ * case op
2918
+ * when "-"
2919
+ * ret.delete(val)
2920
+ * when "+"
2921
+ * ret.add(val)
2922
+ * end
2923
+ * end
2924
+ * ret
2925
+ * end
2926
+ *
2927
+ * @example Using optimistic locking. The operation will fail on CAS mismatch
2928
+ * ver = c.set("foo", "aaa")
2929
+ * c.append("foo", "bbb", :cas => ver)
2930
+ */
2931
+ static VALUE
2932
+ cb_bucket_append(int argc, VALUE *argv, VALUE self)
2933
+ {
2934
+ return cb_bucket_store(LIBCOUCHBASE_APPEND, argc, argv, self);
2935
+ }
2936
+
2937
+ /*
2938
+ * Prepend this object to the existing object
2939
+ *
2940
+ * @note This operation is kind of data-aware from server point of view.
2941
+ * This mean that the server treats value as binary stream and just
2942
+ * perform concatenation, therefore it won't work with +:marshal+ and
2943
+ * +:document+ formats, because of lack of knowledge how to merge values
2944
+ * in these formats. See Bucket#cas for workaround.
2945
+ *
2946
+ * @overload prepend(key, value, options = {})
2947
+ * @param key [String, Symbol] Key used to reference the value.
2948
+ * @param value [Object] Value to be stored
2949
+ * @param options [Hash] Options for operation.
2950
+ * @option options [Fixnum] :cas The CAS value for an object. This value
2951
+ * created on the server and is guaranteed to be unique for each value of
2952
+ * a given key. This value is used to provide simple optimistic
2953
+ * concurrency control when multiple clients or threads try to update an
2954
+ * item simultaneously.
2955
+ * @option options [Symbol] :format (self.default_format) The
2956
+ * representation for storing the value in the bucket. For more info see
2957
+ * {Bucket#default_format}.
2958
+ *
2959
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
2960
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
2961
+ * @raise [Couchbase::Error::NotStored] if the key doesn't exist
2962
+ * @raise [ArgumentError] when passing the block in synchronous mode
2963
+ *
2964
+ * @example Simple prepend example
2965
+ * c.set("foo", "aaa")
2966
+ * c.prepend("foo", "bbb")
2967
+ * c.get("foo") #=> "bbbaaa"
2968
+ *
2969
+ * @example Using explicit format option
2970
+ * c.default_format #=> :document
2971
+ * c.set("foo", {"y" => "z"})
2972
+ * c.prepend("foo", '[', :format => :plain)
2973
+ * c.append("foo", ', {"z": "y"}]', :format => :plain)
2974
+ * c.get("foo") #=> [{"y"=>"z"}, {"z"=>"y"}]
2975
+ *
2976
+ * @example Using optimistic locking. The operation will fail on CAS mismatch
2977
+ * ver = c.set("foo", "aaa")
2978
+ * c.prepend("foo", "bbb", :cas => ver)
2979
+ */
2980
+ static VALUE
2981
+ cb_bucket_prepend(int argc, VALUE *argv, VALUE self)
2982
+ {
2983
+ return cb_bucket_store(LIBCOUCHBASE_PREPEND, argc, argv, self);
2984
+ }
2985
+
2986
+ static VALUE
2987
+ cb_bucket_aset(int argc, VALUE *argv, VALUE self)
2988
+ {
2989
+ VALUE temp;
2990
+
2991
+ if (argc == 3) {
2992
+ /* swap opts and value, because value goes last for []= */
2993
+ temp = argv[2];
2994
+ argv[2] = argv[1];
2995
+ argv[1] = temp;
2996
+ }
2997
+ return cb_bucket_set(argc, argv, self);
2998
+ }
2999
+
3000
+ /*
3001
+ * Close the connection to the cluster
3002
+ *
3003
+ * @return [true]
3004
+ *
3005
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
3006
+ */
3007
+ static VALUE
3008
+ cb_bucket_disconnect(VALUE self)
3009
+ {
3010
+ bucket_t *bucket = DATA_PTR(self);
3011
+
3012
+ if (bucket->handle) {
3013
+ libcouchbase_destroy(bucket->handle);
3014
+ bucket->handle = NULL;
3015
+ bucket->io = NULL;
3016
+ return Qtrue;
3017
+ } else {
3018
+ rb_raise(eConnectError, "closed connection");
3019
+ }
3020
+ }
3021
+
3022
+ /*
3023
+ * Check if result of operation was successful.
3024
+ *
3025
+ * @return [Boolean] +false+ if there is an +error+ object attached,
3026
+ * +false+ otherwise.
3027
+ */
3028
+ static VALUE
3029
+ cb_result_success_p(VALUE self)
3030
+ {
3031
+ return RTEST(rb_ivar_get(self, id_iv_error)) ? Qfalse : Qtrue;
3032
+ }
3033
+
3034
+ /*
3035
+ * Returns a string containing a human-readable representation of the Result.
3036
+ *
3037
+ * @return [String]
3038
+ */
3039
+ static VALUE
3040
+ cb_result_inspect(VALUE self)
3041
+ {
3042
+ VALUE str, attr, error;
3043
+ char buf[100];
3044
+
3045
+ str = rb_str_buf_new2("#<");
3046
+ rb_str_buf_cat2(str, rb_obj_classname(self));
3047
+ snprintf(buf, 100, ":%p", (void *)self);
3048
+ rb_str_buf_cat2(str, buf);
3049
+
3050
+ attr = rb_ivar_get(self, id_iv_error);
3051
+ if (RTEST(attr)) {
3052
+ error = rb_ivar_get(attr, id_iv_error);
3053
+ } else {
3054
+ error = INT2FIX(0);
3055
+ }
3056
+ rb_str_buf_cat2(str, " error=0x");
3057
+ rb_str_append(str, rb_funcall(error, id_to_s, 1, INT2FIX(16)));
3058
+
3059
+ attr = rb_ivar_get(self, id_iv_key);
3060
+ if (RTEST(attr)) {
3061
+ rb_str_buf_cat2(str, " key=");
3062
+ rb_str_append(str, rb_inspect(attr));
3063
+ }
3064
+
3065
+ attr = rb_ivar_get(self, id_iv_cas);
3066
+ if (RTEST(attr)) {
3067
+ rb_str_buf_cat2(str, " cas=");
3068
+ rb_str_append(str, rb_inspect(attr));
3069
+ }
3070
+
3071
+ attr = rb_ivar_get(self, id_iv_flags);
3072
+ if (RTEST(attr)) {
3073
+ rb_str_buf_cat2(str, " flags=0x");
3074
+ rb_str_append(str, rb_funcall(attr, id_to_s, 1, INT2FIX(16)));
3075
+ }
3076
+
3077
+ attr = rb_ivar_get(self, id_iv_node);
3078
+ if (RTEST(attr)) {
3079
+ rb_str_buf_cat2(str, " node=");
3080
+ rb_str_append(str, rb_inspect(attr));
3081
+ }
3082
+ rb_str_buf_cat2(str, ">");
3083
+
3084
+ return str;
3085
+ }
3086
+
3087
+ /* Ruby Extension initializer */
3088
+ void
3089
+ Init_couchbase_ext(void)
3090
+ {
3091
+ mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
3092
+ mURI = rb_const_get(rb_cObject, rb_intern("URI"));
3093
+ mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
3094
+ mCouchbase = rb_define_module("Couchbase");
3095
+
3096
+ mError = rb_define_module_under(mCouchbase, "Error");
3097
+ /* Document-class: Couchbase::Error::Base
3098
+ * The base error class */
3099
+ eBaseError = rb_define_class_under(mError, "Base", rb_eRuntimeError);
3100
+ /* Document-class: Couchbase::Error::Auth
3101
+ * Authentication error */
3102
+ eAuthError = rb_define_class_under(mError, "Auth", eBaseError);
3103
+ /* Document-class: Couchbase::Error::BucketNotFound
3104
+ * The given bucket not found in the cluster */
3105
+ eBucketNotFoundError = rb_define_class_under(mError, "BucketNotFound", eBaseError);
3106
+ /* Document-class: Couchbase::Error::Busy
3107
+ * The cluster is too busy now. Try again later */
3108
+ eBusyError = rb_define_class_under(mError, "Busy", eBaseError);
3109
+ /* Document-class: Couchbase::Error::DeltaBadval
3110
+ * The given value is not a number */
3111
+ eDeltaBadvalError = rb_define_class_under(mError, "DeltaBadval", eBaseError);
3112
+ /* Document-class: Couchbase::Error::Internal
3113
+ * Internal error */
3114
+ eInternalError = rb_define_class_under(mError, "Internal", eBaseError);
3115
+ /* Document-class: Couchbase::Error::Invalid
3116
+ * Invalid arguments */
3117
+ eInvalidError = rb_define_class_under(mError, "Invalid", eBaseError);
3118
+ /* Document-class: Couchbase::Error::KeyExists
3119
+ * Key already exists */
3120
+ eKeyExistsError = rb_define_class_under(mError, "KeyExists", eBaseError);
3121
+ /* Document-class: Couchbase::Error::Libcouchbase
3122
+ * Generic error */
3123
+ eLibcouchbaseError = rb_define_class_under(mError, "Libcouchbase", eBaseError);
3124
+ /* Document-class: Couchbase::Error::Libevent
3125
+ * Problem using libevent */
3126
+ eLibeventError = rb_define_class_under(mError, "Libevent", eBaseError);
3127
+ /* Document-class: Couchbase::Error::Network
3128
+ * Network error */
3129
+ eNetworkError = rb_define_class_under(mError, "Network", eBaseError);
3130
+ /* Document-class: Couchbase::Error::NoMemory
3131
+ * Out of memory error */
3132
+ eNoMemoryError = rb_define_class_under(mError, "NoMemory", eBaseError);
3133
+ /* Document-class: Couchbase::Error::NotFound
3134
+ * No such key */
3135
+ eNotFoundError = rb_define_class_under(mError, "NotFound", eBaseError);
3136
+ /* Document-class: Couchbase::Error::NotMyVbucket
3137
+ * The vbucket is not located on this server */
3138
+ eNotMyVbucketError = rb_define_class_under(mError, "NotMyVbucket", eBaseError);
3139
+ /* Document-class: Couchbase::Error::NotStored
3140
+ * Not stored */
3141
+ eNotStoredError = rb_define_class_under(mError, "NotStored", eBaseError);
3142
+ /* Document-class: Couchbase::Error::NotSupported
3143
+ * Not supported */
3144
+ eNotSupportedError = rb_define_class_under(mError, "NotSupported", eBaseError);
3145
+ /* Document-class: Couchbase::Error::Range
3146
+ * Invalid range */
3147
+ eRangeError = rb_define_class_under(mError, "Range", eBaseError);
3148
+ /* Document-class: Couchbase::Error::TemporaryFail
3149
+ * Temporary failure. Try again later */
3150
+ eTmpFailError = rb_define_class_under(mError, "TemporaryFail", eBaseError);
3151
+ /* Document-class: Couchbase::Error::TooBig
3152
+ * Object too big */
3153
+ eTooBigError = rb_define_class_under(mError, "TooBig", eBaseError);
3154
+ /* Document-class: Couchbase::Error::UnknownCommand
3155
+ * Unknown command */
3156
+ eUnknownCommandError = rb_define_class_under(mError, "UnknownCommand", eBaseError);
3157
+ /* Document-class: Couchbase::Error::UnknownHost
3158
+ * Unknown host */
3159
+ eUnknownHostError = rb_define_class_under(mError, "UnknownHost", eBaseError);
3160
+ /* Document-class: Couchbase::Error::ValueFormat
3161
+ * Failed to decode or encode value */
3162
+ eValueFormatError = rb_define_class_under(mError, "ValueFormat", eBaseError);
3163
+ /* Document-class: Couchbase::Error::Protocol
3164
+ * Protocol error */
3165
+ eProtocolError = rb_define_class_under(mError, "Protocol", eBaseError);
3166
+ /* Document-class: Couchbase::Error::Timeout
3167
+ * Timeout error */
3168
+ eTimeoutError = rb_define_class_under(mError, "Timeout", eBaseError);
3169
+ /* Document-class: Couchbase::Error::Connect
3170
+ * Connect error */
3171
+ eConnectError = rb_define_class_under(mError, "Connect", eBaseError);
3172
+
3173
+ /* Document-method: error
3174
+ * @return [Boolean] the error code from libcouchbase */
3175
+ rb_define_attr(eBaseError, "error", 1, 0);
3176
+ id_iv_error = rb_intern("@error");
3177
+ /* Document-method: key
3178
+ * @return [String] the key which generated error */
3179
+ rb_define_attr(eBaseError, "key", 1, 0);
3180
+ id_iv_key = rb_intern("@key");
3181
+ /* Document-method: cas
3182
+ * @return [Fixnum] the version of the key (+nil+ unless accessible) */
3183
+ rb_define_attr(eBaseError, "cas", 1, 0);
3184
+ id_iv_cas = rb_intern("@cas");
3185
+ /* Document-method: operation
3186
+ * @return [Symbol] the operation (+nil+ unless accessible) */
3187
+ rb_define_attr(eBaseError, "operation", 1, 0);
3188
+ id_iv_operation = rb_intern("@operation");
3189
+
3190
+ /* Document-class: Couchbase::Result
3191
+ * The object which yielded to asynchronous callbacks */
3192
+ cResult = rb_define_class_under(mCouchbase, "Result", rb_cObject);
3193
+ rb_define_method(cResult, "inspect", cb_result_inspect, 0);
3194
+ rb_define_method(cResult, "success?", cb_result_success_p, 0);
3195
+ /* Document-method: operation
3196
+ * @return [Symbol] */
3197
+ rb_define_attr(cResult, "operation", 1, 0);
3198
+ /* Document-method: error
3199
+ * @return [Couchbase::Error::Base] */
3200
+ rb_define_attr(cResult, "error", 1, 0);
3201
+ /* Document-method: key
3202
+ * @return [String] */
3203
+ rb_define_attr(cResult, "key", 1, 0);
3204
+ id_iv_key = rb_intern("@key");
3205
+ /* Document-method: value
3206
+ * @return [String] */
3207
+ rb_define_attr(cResult, "value", 1, 0);
3208
+ id_iv_value = rb_intern("@value");
3209
+ /* Document-method: cas
3210
+ * @return [Fixnum] */
3211
+ rb_define_attr(cResult, "cas", 1, 0);
3212
+ id_iv_cas = rb_intern("@cas");
3213
+ /* Document-method: flags
3214
+ * @return [Fixnum] */
3215
+ rb_define_attr(cResult, "flags", 1, 0);
3216
+ id_iv_flags = rb_intern("@flags");
3217
+ /* Document-method: node
3218
+ * @return [String] */
3219
+ rb_define_attr(cResult, "node", 1, 0);
3220
+ id_iv_node = rb_intern("@node");
3221
+
3222
+ /* Document-class: Couchbase::Bucket
3223
+ * This class in charge of all stuff connected to communication with
3224
+ * Couchbase. */
3225
+ cBucket = rb_define_class_under(mCouchbase, "Bucket", rb_cObject);
3226
+ object_space = rb_hash_new();
3227
+ /* @private Hack to avoid GC in some cases */
3228
+ rb_define_const(cBucket, "OBJECT_SPACE", object_space);
3229
+
3230
+ /* 0x03: Bitmask for flag bits responsible for format */
3231
+ rb_define_const(cBucket, "FMT_MASK", INT2FIX(FMT_MASK));
3232
+ /* 0x00: Document format. The (default) format supports most of ruby
3233
+ * types which could be mapped to JSON data (hashes, arrays, strings,
3234
+ * numbers). Future version will be able to run map/reduce queries on
3235
+ * the values in the document form (hashes). */
3236
+ rb_define_const(cBucket, "FMT_DOCUMENT", INT2FIX(FMT_DOCUMENT));
3237
+ /* 0x01: Marshal format. The format which supports transparent
3238
+ * serialization of ruby objects with standard <tt>Marshal.dump</tt> and
3239
+ * <tt>Marhal.load</tt> methods. */
3240
+ rb_define_const(cBucket, "FMT_MARSHAL", INT2FIX(FMT_MARSHAL));
3241
+ /* 0x02: Plain format. The format which force client don't apply any
3242
+ * conversions to the value, but it should be passed as String. It
3243
+ * could be useful for building custom algorithms or formats. For
3244
+ * example implement set:
3245
+ * http://dustin.github.com/2011/02/17/memcached-set.html */
3246
+ rb_define_const(cBucket, "FMT_PLAIN", INT2FIX(FMT_PLAIN));
3247
+
3248
+ rb_define_singleton_method(cBucket, "new", cb_bucket_new, -1);
3249
+
3250
+ rb_define_method(cBucket, "initialize", cb_bucket_init, -1);
3251
+ rb_define_method(cBucket, "inspect", cb_bucket_inspect, 0);
3252
+
3253
+ /* Document-method: seqno
3254
+ * The number of scheduled commands */
3255
+ /* rb_define_attr(cBucket, "seqno", 1, 0); */
3256
+ rb_define_method(cBucket, "seqno", cb_bucket_seqno, 0);
3257
+
3258
+ rb_define_method(cBucket, "add", cb_bucket_add, -1);
3259
+ rb_define_method(cBucket, "append", cb_bucket_append, -1);
3260
+ rb_define_method(cBucket, "prepend", cb_bucket_prepend, -1);
3261
+ rb_define_method(cBucket, "replace", cb_bucket_replace, -1);
3262
+ rb_define_method(cBucket, "set", cb_bucket_set, -1);
3263
+ rb_define_method(cBucket, "get", cb_bucket_get, -1);
3264
+ rb_define_method(cBucket, "run", cb_bucket_run, 0);
3265
+ rb_define_method(cBucket, "touch", cb_bucket_touch, -1);
3266
+ rb_define_method(cBucket, "delete", cb_bucket_delete, -1);
3267
+ rb_define_method(cBucket, "stats", cb_bucket_stats, -1);
3268
+ rb_define_method(cBucket, "flush", cb_bucket_flush, 0);
3269
+ rb_define_method(cBucket, "version", cb_bucket_version, 0);
3270
+ rb_define_method(cBucket, "incr", cb_bucket_incr, -1);
3271
+ rb_define_method(cBucket, "decr", cb_bucket_decr, -1);
3272
+ rb_define_method(cBucket, "disconnect", cb_bucket_disconnect, 0);
3273
+ rb_define_method(cBucket, "reconnect", cb_bucket_reconnect, -1);
3274
+
3275
+ rb_define_alias(cBucket, "decrement", "decr");
3276
+ rb_define_alias(cBucket, "increment", "incr");
3277
+
3278
+ rb_define_alias(cBucket, "[]", "get");
3279
+ rb_define_alias(cBucket, "[]=", "set");
3280
+ rb_define_method(cBucket, "[]=", cb_bucket_aset, -1);
3281
+
3282
+ rb_define_method(cBucket, "connected?", cb_bucket_connected_p, 0);
3283
+ rb_define_method(cBucket, "async?", cb_bucket_async_p, 0);
3284
+
3285
+ /* Document-method: quiet
3286
+ * Flag specifying behaviour for operations on missing keys
3287
+ *
3288
+ * If it is +true+, the operations will silently return +nil+ or +false+
3289
+ * instead of raising {Couchbase::Error::NotFound}.
3290
+ *
3291
+ * @example Hiding cache miss (considering "miss" key is not stored)
3292
+ * connection.quiet = true
3293
+ * connection.get("miss") #=> nil
3294
+ *
3295
+ * @example Raising errors on miss (considering "miss" key is not stored)
3296
+ * connection.quiet = false
3297
+ * connection.get("miss") #=> will raise Couchbase::Error::NotFound
3298
+ *
3299
+ * @return [Boolean] */
3300
+ /* rb_define_attr(cBucket, "quiet", 1, 1); */
3301
+ rb_define_method(cBucket, "quiet", cb_bucket_quiet_get, 0);
3302
+ rb_define_method(cBucket, "quiet=", cb_bucket_quiet_set, 1);
3303
+ rb_define_alias(cBucket, "quiet?", "quiet");
3304
+
3305
+ /* Document-method: default_flags
3306
+ * Default flags for new values.
3307
+ *
3308
+ * The library reserves last two lower bits to store the format of the
3309
+ * value. The can be masked via FMT_MASK constant.
3310
+ *
3311
+ * @example Selecting format bits
3312
+ * connection.default_flags & Couchbase::Bucket::FMT_MASK
3313
+ *
3314
+ * @example Set user defined bits
3315
+ * connection.default_flags |= 0x6660
3316
+ *
3317
+ * @note Amending format bit will also change #default_format value
3318
+ *
3319
+ * @return [Fixnum] the effective flags */
3320
+ /* rb_define_attr(cBucket, "default_flags", 1, 1); */
3321
+ rb_define_method(cBucket, "default_flags", cb_bucket_default_flags_get, 0);
3322
+ rb_define_method(cBucket, "default_flags=", cb_bucket_default_flags_set, 1);
3323
+
3324
+ /* Document-method: default_format
3325
+ * Default format for new values.
3326
+ *
3327
+ * It uses flags field to store the format. It accepts either the Symbol
3328
+ * (+:document+, +:marshal+, +:plain+) or Fixnum (use constants
3329
+ * FMT_DOCUMENT, FMT_MARSHAL, FMT_PLAIN) and silently ignores all
3330
+ * other value.
3331
+ *
3332
+ * Here is some notes regarding how to choose the format:
3333
+ *
3334
+ * * <tt>:document</tt> (default) format supports most of ruby types
3335
+ * which could be mapped to JSON data (hashes, arrays, strings,
3336
+ * numbers). Future version will be able to run map/reduce queries on
3337
+ * the values in the document form (hashes).
3338
+ *
3339
+ * * <tt>:plain</tt> format if you no need any conversions to be applied
3340
+ * to your data, but your data should be passed as String. It could be
3341
+ * useful for building custom algorithms or formats. For example
3342
+ * implement set: http://dustin.github.com/2011/02/17/memcached-set.html
3343
+ *
3344
+ * * <tt>:marshal</tt> format if you'd like to transparently serialize
3345
+ * your ruby object with standard <tt>Marshal.dump</tt> and
3346
+ * <tt>Marhal.load</tt> methods.
3347
+ *
3348
+ * @example Selecting plain format using symbol
3349
+ * connection.format = :document
3350
+ *
3351
+ * @example Selecting plain format using Fixnum constant
3352
+ * connection.format = Couchbase::Bucket::FMT_PLAIN
3353
+ *
3354
+ * @note Amending default_format will also change #default_flags value
3355
+ *
3356
+ * @return [Symbol] the effective format */
3357
+ /* rb_define_attr(cBucket, "default_format", 1, 1); */
3358
+ rb_define_method(cBucket, "default_format", cb_bucket_default_format_get, 0);
3359
+ rb_define_method(cBucket, "default_format=", cb_bucket_default_format_set, 1);
3360
+
3361
+ /* Document-method: timeout
3362
+ * @return [Fixnum] The timeout for the operations. The client will
3363
+ * raise {Couchbase::Error::Timeout} exception for all commands which
3364
+ * weren't completed in given timeslot. */
3365
+ /* rb_define_attr(cBucket, "timeout", 1, 1); */
3366
+ rb_define_method(cBucket, "timeout", cb_bucket_timeout_get, 0);
3367
+ rb_define_method(cBucket, "timeout=", cb_bucket_timeout_set, 1);
3368
+
3369
+ /* Document-method: on_error
3370
+ * Error callback for asynchronous mode.
3371
+ *
3372
+ * This callback is using to deliver exceptions in asynchronous mode.
3373
+ *
3374
+ * @yieldparam [Symbol] op The operation caused the error
3375
+ * @yieldparam [String] key The key which cause the error or +nil+
3376
+ * @yieldparam [Exception] exc The exception instance
3377
+ *
3378
+ * @example Using lambda syntax
3379
+ * connection = Couchbase.new(:async => true)
3380
+ * connection.on_error = lambda {|op, key, exc| ... }
3381
+ * connection.run do |conn|
3382
+ * conn.set("foo", "bar")
3383
+ * end
3384
+ *
3385
+ * @example Using block syntax
3386
+ * connection = Couchbase.new(:async => true)
3387
+ * connection.on_error {|op, key, exc| ... }
3388
+ * ...
3389
+ *
3390
+ * @return [Proc] the effective callback */
3391
+ /* rb_define_attr(cBucket, "on_error", 1, 1); */
3392
+ rb_define_method(cBucket, "on_error", cb_bucket_on_error_get, 0);
3393
+ rb_define_method(cBucket, "on_error=", cb_bucket_on_error_set, 1);
3394
+
3395
+ /* rb_define_attr(cBucket, "url", 1, 0); */
3396
+ rb_define_method(cBucket, "url", cb_bucket_url_get, 0);
3397
+ /* rb_define_attr(cBucket, "hostname", 1, 0); */
3398
+ rb_define_method(cBucket, "hostname", cb_bucket_hostname_get, 0);
3399
+ /* rb_define_attr(cBucket, "port", 1, 0); */
3400
+ rb_define_method(cBucket, "port", cb_bucket_port_get, 0);
3401
+ /* rb_define_attr(cBucket, "authority", 1, 0); */
3402
+ rb_define_method(cBucket, "authority", cb_bucket_authority_get, 0);
3403
+ /* rb_define_attr(cBucket, "bucket", 1, 0); */
3404
+ rb_define_method(cBucket, "bucket", cb_bucket_bucket_get, 0);
3405
+ rb_define_alias(cBucket, "name", "bucket");
3406
+ /* rb_define_attr(cBucket, "pool", 1, 0); */
3407
+ rb_define_method(cBucket, "pool", cb_bucket_pool_get, 0);
3408
+ /* rb_define_attr(cBucket, "username", 1, 0); */
3409
+ rb_define_method(cBucket, "username", cb_bucket_username_get, 0);
3410
+ /* rb_define_attr(cBucket, "password", 1, 0); */
3411
+ rb_define_method(cBucket, "password", cb_bucket_password_get, 0);
3412
+
3413
+ /* Define symbols */
3414
+ id_arity = rb_intern("arity");
3415
+ id_call = rb_intern("call");
3416
+ id_dump = rb_intern("dump");
3417
+ id_flatten_bang = rb_intern("flatten!");
3418
+ id_has_key_p = rb_intern("has_key?");
3419
+ id_host = rb_intern("host");
3420
+ id_load = rb_intern("load");
3421
+ id_match = rb_intern("match");
3422
+ id_parse = rb_intern("parse");
3423
+ id_password = rb_intern("password");
3424
+ id_path = rb_intern("path");
3425
+ id_port = rb_intern("port");
3426
+ id_scheme = rb_intern("scheme");
3427
+ id_to_s = rb_intern("to_s");
3428
+ id_user = rb_intern("user");
3429
+
3430
+ sym_add = ID2SYM(rb_intern("add"));
3431
+ sym_append = ID2SYM(rb_intern("append"));
3432
+ sym_bucket = ID2SYM(rb_intern("bucket"));
3433
+ sym_cas = ID2SYM(rb_intern("cas"));
3434
+ sym_create = ID2SYM(rb_intern("create"));
3435
+ sym_decrement = ID2SYM(rb_intern("decrement"));
3436
+ sym_default_flags = ID2SYM(rb_intern("default_flags"));
3437
+ sym_default_format = ID2SYM(rb_intern("default_format"));
3438
+ sym_default_ttl = ID2SYM(rb_intern("default_ttl"));
3439
+ sym_delete = ID2SYM(rb_intern("delete"));
3440
+ sym_document = ID2SYM(rb_intern("document"));
3441
+ sym_extended = ID2SYM(rb_intern("extended"));
3442
+ sym_flags = ID2SYM(rb_intern("flags"));
3443
+ sym_flush = ID2SYM(rb_intern("flush"));
3444
+ sym_format = ID2SYM(rb_intern("format"));
3445
+ sym_get = ID2SYM(rb_intern("get"));
3446
+ sym_hostname = ID2SYM(rb_intern("hostname"));
3447
+ sym_increment = ID2SYM(rb_intern("increment"));
3448
+ sym_initial = ID2SYM(rb_intern("initial"));
3449
+ sym_marshal = ID2SYM(rb_intern("marshal"));
3450
+ sym_password = ID2SYM(rb_intern("password"));
3451
+ sym_plain = ID2SYM(rb_intern("plain"));
3452
+ sym_pool = ID2SYM(rb_intern("pool"));
3453
+ sym_port = ID2SYM(rb_intern("port"));
3454
+ sym_prepend = ID2SYM(rb_intern("prepend"));
3455
+ sym_quiet = ID2SYM(rb_intern("quiet"));
3456
+ sym_replace = ID2SYM(rb_intern("replace"));
3457
+ sym_set = ID2SYM(rb_intern("set"));
3458
+ sym_stats = ID2SYM(rb_intern("stats"));
3459
+ sym_timeout = ID2SYM(rb_intern("timeout"));
3460
+ sym_touch = ID2SYM(rb_intern("touch"));
3461
+ sym_ttl = ID2SYM(rb_intern("ttl"));
3462
+ sym_username = ID2SYM(rb_intern("username"));
3463
+ sym_version = ID2SYM(rb_intern("version"));
3464
+ }