couchbase 1.1.0-x86-mingw32

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