couchbase 0.9.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/.gitignore +8 -0
  2. data/.yardopts +5 -0
  3. data/HISTORY.markdown +14 -10
  4. data/README.markdown +293 -98
  5. data/Rakefile +19 -24
  6. data/couchbase.gemspec +25 -7
  7. data/ext/couchbase_ext/couchbase_ext.c +2332 -0
  8. data/ext/couchbase_ext/extconf.rb +102 -0
  9. data/lib/couchbase.rb +20 -30
  10. data/lib/couchbase/bucket.rb +43 -112
  11. data/lib/couchbase/version.rb +3 -2
  12. data/tasks/benchmark.rake +6 -0
  13. data/tasks/compile.rake +52 -0
  14. data/tasks/doc.rake +27 -0
  15. data/tasks/test.rake +94 -0
  16. data/tasks/util.rake +21 -0
  17. data/test/profile/.gitignore +1 -0
  18. data/test/profile/Gemfile +6 -0
  19. data/test/profile/benchmark.rb +195 -0
  20. data/test/setup.rb +107 -18
  21. data/test/test_arithmetic.rb +98 -0
  22. data/test/test_async.rb +211 -0
  23. data/test/test_bucket.rb +126 -23
  24. data/test/test_cas.rb +59 -0
  25. data/test/test_couchbase.rb +22 -3
  26. data/test/test_delete.rb +63 -0
  27. data/test/test_errors.rb +82 -0
  28. data/test/test_flush.rb +49 -0
  29. data/test/test_format.rb +98 -0
  30. data/test/test_get.rb +236 -0
  31. data/test/test_stats.rb +53 -0
  32. data/test/test_store.rb +186 -0
  33. data/test/test_touch.rb +57 -0
  34. data/test/test_version.rb +17 -0
  35. metadata +72 -58
  36. data/lib/couchbase/couchdb.rb +0 -107
  37. data/lib/couchbase/document.rb +0 -71
  38. data/lib/couchbase/http_status.rb +0 -118
  39. data/lib/couchbase/latch.rb +0 -71
  40. data/lib/couchbase/memcached.rb +0 -372
  41. data/lib/couchbase/node.rb +0 -49
  42. data/lib/couchbase/rest_client.rb +0 -124
  43. data/lib/couchbase/view.rb +0 -182
  44. data/test/support/buckets-config.json +0 -843
  45. data/test/support/sample_design_doc.json +0 -9
  46. data/test/test_couchdb.rb +0 -98
  47. data/test/test_document.rb +0 -11
  48. data/test/test_latch.rb +0 -88
  49. data/test/test_memcached.rb +0 -59
  50. data/test/test_rest_client.rb +0 -14
  51. data/test/test_view.rb +0 -98
data/Rakefile CHANGED
@@ -1,27 +1,22 @@
1
- require 'bundler/gem_tasks'
2
-
3
- require 'rake/testtask'
4
- Rake::TestTask.new do |test|
5
- test.libs << "test" << "."
6
- test.ruby_opts << "-rruby-debug" if ENV['DEBUG']
7
- test.pattern = 'test/test_*.rb'
8
- test.verbose = true
9
- end
10
-
11
- require 'rubygems/package_task'
12
- def gemspec
13
- @clean_gemspec ||= eval(File.read(File.expand_path('../couchbase.gemspec', __FILE__)))
14
- end
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
+ #
15
17
 
16
- Gem::PackageTask.new(gemspec) do |pkg|
17
- pkg.need_zip = true
18
- pkg.need_tar = true
19
- end
20
-
21
- desc 'Start an irb session and load the library.'
22
- task :console do
23
- exec "irb -I lib -rcouchbase"
24
- end
18
+ require 'bundler/gem_tasks'
25
19
 
26
- task :default => :test
20
+ Dir['tasks/*.rake'].sort.each { |f| load f }
27
21
 
22
+ task :default => [:clobber, :compile, :test]
@@ -1,4 +1,21 @@
1
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
+
2
19
  $:.push File.expand_path('../lib', __FILE__)
3
20
  require 'couchbase/version'
4
21
 
@@ -6,7 +23,7 @@ Gem::Specification.new do |s|
6
23
  s.name = 'couchbase'
7
24
  s.version = Couchbase::VERSION
8
25
  s.author = 'Couchbase'
9
- s.email = 'info@couchbase.com'
26
+ s.email = 'support@couchbase.com'
10
27
  s.license = 'ASL-2'
11
28
  s.homepage = 'http://couchbase.org'
12
29
  s.summary = %q{Couchbase ruby driver}
@@ -15,14 +32,15 @@ Gem::Specification.new do |s|
15
32
  s.files = `git ls-files`.split("\n")
16
33
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
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")
18
36
  s.require_paths = ['lib']
19
37
 
20
- s.add_runtime_dependency 'memcached', '~> 1.3'
21
- s.add_runtime_dependency 'yajl-ruby', '~> 0.8.2'
22
- s.add_runtime_dependency 'curb', '~> 0.7.15'
23
- s.add_runtime_dependency 'yaji', '~> 0.0.9'
38
+ s.add_runtime_dependency 'yajl-ruby', '~> 1.1.0'
24
39
 
25
- s.add_development_dependency 'rake'
40
+ s.add_development_dependency 'rake', '~> 0.8.7'
26
41
  s.add_development_dependency 'minitest'
27
- s.add_development_dependency 'mocha'
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 RUBY_VERSION =~ /^1\.9/ ? 'ruby-debug19' : 'ruby-debug'
28
46
  end
@@ -0,0 +1,2332 @@
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 <libcouchbase/couchbase.h>
24
+ #include <event.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
+ uint32_t default_ttl;
64
+ VALUE exception; /* error delivered by error_callback */
65
+ VALUE on_error_proc; /* is using to deliver errors in async mode */
66
+ } bucket_t;
67
+
68
+ typedef struct
69
+ {
70
+ bucket_t* bucket;
71
+ int extended;
72
+ VALUE proc;
73
+ void *rv;
74
+ VALUE exception;
75
+ int quiet;
76
+ int arithm; /* incr: +1, decr: -1, other: 0 */
77
+ } context_t;
78
+
79
+ struct key_traits
80
+ {
81
+ VALUE keys_ary;
82
+ size_t nkeys;
83
+ char **keys;
84
+ size_t *lens;
85
+ time_t *ttls;
86
+ int extended;
87
+ int explicit_ttl;
88
+ int quiet;
89
+ };
90
+
91
+ static VALUE mCouchbase, mError, mJSON, mMarshal, cBucket, cResult;
92
+ static VALUE object_space;
93
+
94
+ static ID sym_add,
95
+ sym_append,
96
+ sym_bucket,
97
+ sym_cas,
98
+ sym_create,
99
+ sym_decrement,
100
+ sym_default_flags,
101
+ sym_default_format,
102
+ sym_delete,
103
+ sym_document,
104
+ sym_extended,
105
+ sym_flags,
106
+ sym_flush,
107
+ sym_format,
108
+ sym_get,
109
+ sym_hostname,
110
+ sym_increment,
111
+ sym_initial,
112
+ sym_marshal,
113
+ sym_password,
114
+ sym_plain,
115
+ sym_pool,
116
+ sym_port,
117
+ sym_prepend,
118
+ sym_quiet,
119
+ sym_replace,
120
+ sym_set,
121
+ sym_stats,
122
+ sym_touch,
123
+ sym_ttl,
124
+ sym_username,
125
+ id_arity,
126
+ id_call,
127
+ id_dump,
128
+ id_flatten_bang,
129
+ id_has_key_p,
130
+ id_load,
131
+ id_to_s,
132
+ id_iv_authority,
133
+ id_iv_bucket,
134
+ id_iv_cas,
135
+ id_iv_default_flags,
136
+ id_iv_default_format,
137
+ id_iv_error,
138
+ id_iv_flags,
139
+ id_iv_hostname,
140
+ id_iv_key,
141
+ id_iv_node,
142
+ id_iv_on_error,
143
+ id_iv_operation,
144
+ id_iv_password,
145
+ id_iv_pool,
146
+ id_iv_port,
147
+ id_iv_quiet,
148
+ id_iv_url,
149
+ id_iv_username,
150
+ id_iv_value;
151
+
152
+ /* base error */
153
+ static VALUE eBaseError;
154
+ static VALUE eValueFormatError;
155
+
156
+ /* libcouchbase errors */
157
+ /*LIBCOUCHBASE_SUCCESS = 0x00*/
158
+ /*LIBCOUCHBASE_AUTH_CONTINUE = 0x01*/
159
+ static VALUE eAuthError; /*LIBCOUCHBASE_AUTH_ERROR = 0x02*/
160
+ static VALUE eDeltaBadvalError; /*LIBCOUCHBASE_DELTA_BADVAL = 0x03*/
161
+ static VALUE eTooBigError; /*LIBCOUCHBASE_E2BIG = 0x04*/
162
+ static VALUE eBusyError; /*LIBCOUCHBASE_EBUSY = 0x05*/
163
+ static VALUE eInternalError; /*LIBCOUCHBASE_EINTERNAL = 0x06*/
164
+ static VALUE eInvalidError; /*LIBCOUCHBASE_EINVAL = 0x07*/
165
+ static VALUE eNoMemoryError; /*LIBCOUCHBASE_ENOMEM = 0x08*/
166
+ static VALUE eRangeError; /*LIBCOUCHBASE_ERANGE = 0x09*/
167
+ static VALUE eLibcouchbaseError; /*LIBCOUCHBASE_ERROR = 0x0a*/
168
+ static VALUE eTmpFailError; /*LIBCOUCHBASE_ETMPFAIL = 0x0b*/
169
+ static VALUE eKeyExistsError; /*LIBCOUCHBASE_KEY_EEXISTS = 0x0c*/
170
+ static VALUE eNotFoundError; /*LIBCOUCHBASE_KEY_ENOENT = 0x0d*/
171
+ static VALUE eLibeventError; /*LIBCOUCHBASE_LIBEVENT_ERROR = 0x0e*/
172
+ static VALUE eNetworkError; /*LIBCOUCHBASE_NETWORK_ERROR = 0x0f*/
173
+ static VALUE eNotMyVbucketError; /*LIBCOUCHBASE_NOT_MY_VBUCKET = 0x10*/
174
+ static VALUE eNotStoredError; /*LIBCOUCHBASE_NOT_STORED = 0x11*/
175
+ static VALUE eNotSupportedError; /*LIBCOUCHBASE_NOT_SUPPORTED = 0x12*/
176
+ static VALUE eUnknownCommandError; /*LIBCOUCHBASE_UNKNOWN_COMMAND = 0x13*/
177
+ static VALUE eUnknownHostError; /*LIBCOUCHBASE_UNKNOWN_HOST = 0x14*/
178
+ static VALUE eProtocolError; /*LIBCOUCHBASE_PROTOCOL_ERROR = 0x15*/
179
+
180
+ static VALUE
181
+ cb_proc_call(VALUE recv, int argc, ...)
182
+ {
183
+ VALUE *argv;
184
+ va_list ar;
185
+ int arity;
186
+ int ii;
187
+
188
+ arity = FIX2INT(rb_funcall(recv, id_arity, 0));
189
+ if (arity > 0) {
190
+ va_init_list(ar, argc);
191
+ argv = ALLOCA_N(VALUE, argc);
192
+ for (ii = 0; ii < arity; ++ii) {
193
+ if (ii < argc) {
194
+ argv[ii] = va_arg(ar, VALUE);
195
+ } else {
196
+ argv[ii] = Qnil;
197
+ }
198
+ }
199
+ va_end(ar);
200
+ } else {
201
+ arity = 0;
202
+ argv = NULL;
203
+ }
204
+ return rb_funcall2(recv, id_call, arity, argv);
205
+ }
206
+
207
+ /* Helper to conver return code from libcouchbase to meaningful exception.
208
+ * Returns nil if the code considering successful and exception object
209
+ * otherwise. Store given string to exceptions as message, and also
210
+ * initialize +error+ attribute with given return code. */
211
+ static VALUE
212
+ cb_check_error(libcouchbase_error_t rc, const char *msg, VALUE key)
213
+ {
214
+ VALUE klass, exc, str;
215
+ char buf[300];
216
+
217
+ if (rc == LIBCOUCHBASE_SUCCESS || rc == LIBCOUCHBASE_AUTH_CONTINUE) {
218
+ return Qnil;
219
+ }
220
+ switch (rc) {
221
+ case LIBCOUCHBASE_AUTH_ERROR:
222
+ klass = eAuthError;
223
+ break;
224
+ case LIBCOUCHBASE_DELTA_BADVAL:
225
+ klass = eDeltaBadvalError;
226
+ break;
227
+ case LIBCOUCHBASE_E2BIG:
228
+ klass = eTooBigError;
229
+ break;
230
+ case LIBCOUCHBASE_EBUSY:
231
+ klass = eBusyError;
232
+ break;
233
+ case LIBCOUCHBASE_EINTERNAL:
234
+ klass = eInternalError;
235
+ break;
236
+ case LIBCOUCHBASE_EINVAL:
237
+ klass = eInvalidError;
238
+ break;
239
+ case LIBCOUCHBASE_ENOMEM:
240
+ klass = eNoMemoryError;
241
+ break;
242
+ case LIBCOUCHBASE_ERANGE:
243
+ klass = eRangeError;
244
+ break;
245
+ case LIBCOUCHBASE_ETMPFAIL:
246
+ klass = eTmpFailError;
247
+ break;
248
+ case LIBCOUCHBASE_KEY_EEXISTS:
249
+ klass = eKeyExistsError;
250
+ break;
251
+ case LIBCOUCHBASE_KEY_ENOENT:
252
+ klass = eNotFoundError;
253
+ break;
254
+ case LIBCOUCHBASE_LIBEVENT_ERROR:
255
+ klass = eLibeventError;
256
+ break;
257
+ case LIBCOUCHBASE_NETWORK_ERROR:
258
+ klass = eNetworkError;
259
+ break;
260
+ case LIBCOUCHBASE_NOT_MY_VBUCKET:
261
+ klass = eNotMyVbucketError;
262
+ break;
263
+ case LIBCOUCHBASE_NOT_STORED:
264
+ klass = eNotStoredError;
265
+ break;
266
+ case LIBCOUCHBASE_NOT_SUPPORTED:
267
+ klass = eNotSupportedError;
268
+ break;
269
+ case LIBCOUCHBASE_UNKNOWN_COMMAND:
270
+ klass = eUnknownCommandError;
271
+ break;
272
+ case LIBCOUCHBASE_UNKNOWN_HOST:
273
+ klass = eUnknownHostError;
274
+ break;
275
+ case LIBCOUCHBASE_PROTOCOL_ERROR:
276
+ klass = eProtocolError;
277
+ case LIBCOUCHBASE_ERROR:
278
+ /* fall through */
279
+ default:
280
+ klass = eLibcouchbaseError;
281
+ }
282
+
283
+ str = rb_str_buf_new2(msg ? msg : "");
284
+ rb_str_buf_cat2(str, " (");
285
+ if (key != Qnil) {
286
+ snprintf(buf, 300, "key=\"%s\", ", RSTRING_PTR(key));
287
+ rb_str_buf_cat2(str, buf);
288
+ }
289
+ snprintf(buf, 300, "error=0x%x)", rc);
290
+ rb_str_buf_cat2(str, buf);
291
+ exc = rb_exc_new3(klass, str);
292
+ rb_ivar_set(exc, id_iv_error, INT2FIX(rc));
293
+ rb_ivar_set(exc, id_iv_key, key);
294
+ rb_ivar_set(exc, id_iv_cas, Qnil);
295
+ rb_ivar_set(exc, id_iv_operation, Qnil);
296
+ return exc;
297
+ }
298
+
299
+ static inline uint32_t
300
+ flags_set_format(uint32_t flags, ID format)
301
+ {
302
+ flags &= ~((uint32_t)FMT_MASK); /* clear format bits */
303
+
304
+ if (format == sym_document) {
305
+ return flags | FMT_DOCUMENT;
306
+ } else if (format == sym_marshal) {
307
+ return flags | FMT_MARSHAL;
308
+ } else if (format == sym_plain) {
309
+ return flags | FMT_PLAIN;
310
+ }
311
+ return flags; /* document is the default */
312
+ }
313
+
314
+ static inline ID
315
+ flags_get_format(uint32_t flags)
316
+ {
317
+ flags &= FMT_MASK; /* select format bits */
318
+
319
+ switch (flags) {
320
+ case FMT_DOCUMENT:
321
+ return sym_document;
322
+ case FMT_MARSHAL:
323
+ return sym_marshal;
324
+ case FMT_PLAIN:
325
+ /* fall through */
326
+ default:
327
+ /* all other formats treated as plain */
328
+ return sym_plain;
329
+ }
330
+ }
331
+
332
+
333
+ static VALUE
334
+ do_encode(VALUE *args)
335
+ {
336
+ VALUE val = args[0];
337
+ uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
338
+
339
+ switch (flags) {
340
+ case FMT_DOCUMENT:
341
+ return rb_funcall(mJSON, id_dump, 1, val);
342
+ case FMT_MARSHAL:
343
+ return rb_funcall(mMarshal, id_dump, 1, val);
344
+ case FMT_PLAIN:
345
+ /* fall through */
346
+ default:
347
+ /* all other formats treated as plain */
348
+ return val;
349
+ }
350
+ }
351
+
352
+ static VALUE
353
+ do_decode(VALUE *args)
354
+ {
355
+ VALUE blob = args[0];
356
+ uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
357
+
358
+ switch (flags) {
359
+ case FMT_DOCUMENT:
360
+ return rb_funcall(mJSON, id_load, 1, blob);
361
+ case FMT_MARSHAL:
362
+ return rb_funcall(mMarshal, id_load, 1, blob);
363
+ case FMT_PLAIN:
364
+ /* fall through */
365
+ default:
366
+ /* all other formats treated as plain */
367
+ return blob;
368
+ }
369
+ }
370
+
371
+ static VALUE
372
+ coding_failed(void)
373
+ {
374
+ return Qundef;
375
+ }
376
+
377
+ static VALUE
378
+ encode_value(VALUE val, uint32_t flags)
379
+ {
380
+ VALUE blob, args[2];
381
+
382
+ args[0] = val;
383
+ args[1] = (VALUE)flags;
384
+ blob = rb_rescue(do_encode, (VALUE)args, coding_failed, 0);
385
+ /* it must be bytestring after all */
386
+ if (TYPE(blob) != T_STRING) {
387
+ return Qundef;
388
+ }
389
+ return blob;
390
+ }
391
+
392
+ static VALUE
393
+ decode_value(VALUE blob, uint32_t flags)
394
+ {
395
+ VALUE val, args[2];
396
+
397
+ /* first it must be bytestring */
398
+ if (TYPE(blob) != T_STRING) {
399
+ return Qundef;
400
+ }
401
+ args[0] = blob;
402
+ args[1] = (VALUE)flags;
403
+ val = rb_rescue(do_decode, (VALUE)args, coding_failed, 0);
404
+ return val;
405
+ }
406
+
407
+ static VALUE
408
+ unify_key(VALUE key)
409
+ {
410
+ switch (TYPE(key)) {
411
+ case T_STRING:
412
+ return key;
413
+ case T_SYMBOL:
414
+ return rb_str_new2(rb_id2name(SYM2ID(key)));
415
+ default: /* call #to_str or raise error */
416
+ return StringValue(key);
417
+ }
418
+ }
419
+
420
+ static int
421
+ cb_extract_keys_i(VALUE key, VALUE value, VALUE arg)
422
+ {
423
+ struct key_traits *traits = (struct key_traits *)arg;
424
+ key = unify_key(key);
425
+ rb_ary_push(traits->keys_ary, key);
426
+ traits->keys[traits->nkeys] = RSTRING_PTR(key);
427
+ traits->lens[traits->nkeys] = RSTRING_LEN(key);
428
+ traits->ttls[traits->nkeys] = NUM2ULONG(value);
429
+ traits->nkeys++;
430
+ return ST_CONTINUE;
431
+ }
432
+
433
+ static long
434
+ cb_args_scan_keys(long argc, VALUE argv, bucket_t *bucket, struct key_traits *traits)
435
+ {
436
+ VALUE arg, key, *keys_ptr, opts, ttl, ext;
437
+ long nn = 0, ii;
438
+ time_t exp;
439
+
440
+ traits->keys_ary = rb_ary_new();
441
+ traits->quiet = bucket->quiet;
442
+ if (argc == 1) {
443
+ arg = RARRAY_PTR(argv)[0];
444
+ switch (TYPE(arg)) {
445
+ case T_HASH:
446
+ /* hash of key-ttl pairs */
447
+ nn = RHASH_SIZE(arg);
448
+ traits->keys = calloc(nn, sizeof(char *));
449
+ traits->lens = calloc(nn, sizeof(size_t));
450
+ traits->explicit_ttl = 1;
451
+ traits->ttls = calloc(nn, sizeof(time_t));
452
+ rb_hash_foreach(arg, cb_extract_keys_i, (VALUE)traits);
453
+ break;
454
+ case T_STRING:
455
+ case T_SYMBOL:
456
+ /* single key with default expiration */
457
+ nn = traits->nkeys = 1;
458
+ traits->keys = calloc(nn, sizeof(char *));
459
+ traits->lens = calloc(nn, sizeof(size_t));
460
+ traits->ttls = calloc(nn, sizeof(time_t));
461
+ key = unify_key(arg);
462
+ rb_ary_push(traits->keys_ary, key);
463
+ traits->keys[0] = RSTRING_PTR(key);
464
+ traits->lens[0] = RSTRING_LEN(key);
465
+ traits->ttls[0] = bucket->default_ttl;
466
+ break;
467
+ }
468
+ } else if (argc > 1) {
469
+ /* keys with custom options */
470
+ opts = RARRAY_PTR(argv)[argc-1];
471
+ exp = bucket->default_ttl;
472
+ ext = Qfalse;
473
+ if (TYPE(opts) == T_HASH) {
474
+ (void)rb_ary_pop(argv);
475
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
476
+ traits->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
477
+ }
478
+ ext = rb_hash_aref(opts, sym_extended);
479
+ ttl = rb_hash_aref(opts, sym_ttl);
480
+ if (ttl != Qnil) {
481
+ traits->explicit_ttl = 1;
482
+ exp = NUM2ULONG(ttl);
483
+ }
484
+ nn = RARRAY_LEN(argv);
485
+ } else {
486
+ nn = argc;
487
+ }
488
+ if (nn < 1) {
489
+ rb_raise(rb_eArgError, "must be at least one key");
490
+ }
491
+ traits->nkeys = nn;
492
+ traits->extended = RTEST(ext) ? 1 : 0;
493
+ traits->keys = calloc(nn, sizeof(char *));
494
+ traits->lens = calloc(nn, sizeof(size_t));
495
+ traits->ttls = calloc(nn, sizeof(time_t));
496
+ keys_ptr = RARRAY_PTR(argv);
497
+ for (ii = 0; ii < nn; ii++) {
498
+ key = unify_key(keys_ptr[ii]);
499
+ rb_ary_push(traits->keys_ary, key);
500
+ traits->keys[ii] = RSTRING_PTR(key);
501
+ traits->lens[ii] = RSTRING_LEN(key);
502
+ traits->ttls[ii] = exp;
503
+ }
504
+ }
505
+
506
+ return nn;
507
+ }
508
+
509
+ static void
510
+ error_callback(libcouchbase_t handle, libcouchbase_error_t error, const char *errinfo)
511
+ {
512
+ bucket_t *bucket = (bucket_t *)libcouchbase_get_cookie(handle);
513
+
514
+ bucket->io->stop_event_loop(bucket->io);
515
+ bucket->exception = cb_check_error(error, errinfo, Qnil);
516
+ }
517
+
518
+ static void
519
+ storage_callback(libcouchbase_t handle, const void *cookie,
520
+ libcouchbase_storage_t operation, libcouchbase_error_t error,
521
+ const void *key, size_t nkey, uint64_t cas)
522
+ {
523
+ context_t *ctx = (context_t *)cookie;
524
+ bucket_t *bucket = ctx->bucket;
525
+ VALUE k, c, *rv = ctx->rv, exc, res;
526
+ ID o;
527
+
528
+ bucket->seqno--;
529
+
530
+ k = rb_str_new((const char*)key, nkey);
531
+ c = cas > 0 ? ULL2NUM(cas) : Qnil;
532
+ switch(operation) {
533
+ case LIBCOUCHBASE_ADD:
534
+ o = sym_add;
535
+ break;
536
+ case LIBCOUCHBASE_REPLACE:
537
+ o = sym_replace;
538
+ break;
539
+ case LIBCOUCHBASE_SET:
540
+ o = sym_set;
541
+ break;
542
+ case LIBCOUCHBASE_APPEND:
543
+ o = sym_append;
544
+ break;
545
+ case LIBCOUCHBASE_PREPEND:
546
+ o = sym_prepend;
547
+ break;
548
+ default:
549
+ o = Qnil;
550
+ }
551
+ exc = cb_check_error(error, "failed to store value", k);
552
+ if (exc != Qnil) {
553
+ rb_ivar_set(exc, id_iv_cas, c);
554
+ rb_ivar_set(exc, id_iv_operation, o);
555
+ if (NIL_P(ctx->exception)) {
556
+ ctx->exception = exc;
557
+ }
558
+ }
559
+ if (bucket->async) { /* asynchronous */
560
+ if (ctx->proc != Qnil) {
561
+ res = rb_class_new_instance(0, NULL, cResult);
562
+ rb_ivar_set(res, id_iv_error, exc);
563
+ rb_ivar_set(res, id_iv_key, k);
564
+ rb_ivar_set(res, id_iv_operation, o);
565
+ rb_ivar_set(res, id_iv_cas, c);
566
+ cb_proc_call(ctx->proc, 1, res);
567
+ }
568
+ } else { /* synchronous */
569
+ *rv = c;
570
+ }
571
+
572
+ if (bucket->seqno == 0) {
573
+ bucket->io->stop_event_loop(bucket->io);
574
+ rb_hash_delete(object_space, ctx->proc|1);
575
+ }
576
+ (void)handle;
577
+ }
578
+
579
+ static void
580
+ delete_callback(libcouchbase_t handle, const void *cookie,
581
+ libcouchbase_error_t error, const void *key, size_t nkey)
582
+ {
583
+ context_t *ctx = (context_t *)cookie;
584
+ bucket_t *bucket = ctx->bucket;
585
+ VALUE k, *rv = ctx->rv, exc = Qnil, res;
586
+
587
+ bucket->seqno--;
588
+
589
+ k = rb_str_new((const char*)key, nkey);
590
+ if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
591
+ exc = cb_check_error(error, "failed to remove value", k);
592
+ if (exc != Qnil) {
593
+ rb_ivar_set(exc, id_iv_operation, sym_delete);
594
+ if (NIL_P(ctx->exception)) {
595
+ ctx->exception = exc;
596
+ }
597
+ }
598
+ }
599
+ if (bucket->async) { /* asynchronous */
600
+ if (ctx->proc != Qnil) {
601
+ res = rb_class_new_instance(0, NULL, cResult);
602
+ rb_ivar_set(res, id_iv_error, exc);
603
+ rb_ivar_set(res, id_iv_operation, sym_delete);
604
+ rb_ivar_set(res, id_iv_key, k);
605
+ cb_proc_call(ctx->proc, 1, res);
606
+ }
607
+ } else { /* synchronous */
608
+ *rv = (error == LIBCOUCHBASE_SUCCESS) ? Qtrue : Qfalse;
609
+ }
610
+ if (bucket->seqno == 0) {
611
+ bucket->io->stop_event_loop(bucket->io);
612
+ rb_hash_delete(object_space, ctx->proc|1);
613
+ }
614
+ (void)handle;
615
+ }
616
+
617
+ static void
618
+ get_callback(libcouchbase_t handle, const void *cookie,
619
+ libcouchbase_error_t error, const void *key, size_t nkey,
620
+ const void *bytes, size_t nbytes, uint32_t flags, uint64_t cas)
621
+ {
622
+ context_t *ctx = (context_t *)cookie;
623
+ bucket_t *bucket = ctx->bucket;
624
+ VALUE k, v, f, c, *rv = ctx->rv, exc = Qnil, res;
625
+
626
+ bucket->seqno--;
627
+
628
+ k = rb_str_new((const char*)key, nkey);
629
+ if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
630
+ exc = cb_check_error(error, "failed to get value", k);
631
+ if (exc != Qnil) {
632
+ rb_ivar_set(exc, id_iv_operation, sym_get);
633
+ if (NIL_P(ctx->exception)) {
634
+ ctx->exception = exc;
635
+ }
636
+ }
637
+ }
638
+
639
+ f = ULONG2NUM(flags);
640
+ c = ULL2NUM(cas);
641
+ if (nbytes != 0) {
642
+ v = decode_value(rb_str_new((const char*)bytes, nbytes), flags);
643
+ if (v == Qundef) {
644
+ ctx->exception = rb_exc_new2(eValueFormatError, "unable to convert value");
645
+ v = Qnil;
646
+ }
647
+ } else {
648
+ v = Qnil;
649
+ }
650
+ if (bucket->async) { /* asynchronous */
651
+ if (ctx->proc != Qnil) {
652
+ res = rb_class_new_instance(0, NULL, cResult);
653
+ rb_ivar_set(res, id_iv_error, exc);
654
+ rb_ivar_set(res, id_iv_operation, sym_get);
655
+ rb_ivar_set(res, id_iv_key, k);
656
+ rb_ivar_set(res, id_iv_value, v);
657
+ rb_ivar_set(res, id_iv_flags, f);
658
+ rb_ivar_set(res, id_iv_cas, c);
659
+ cb_proc_call(ctx->proc, 1, res);
660
+ }
661
+ } else { /* synchronous */
662
+ if (NIL_P(exc) && v != Qnil) {
663
+ if (ctx->extended) {
664
+ rb_hash_aset(*rv, k, rb_ary_new3(3, v, f, c));
665
+ } else {
666
+ rb_hash_aset(*rv, k, v);
667
+ }
668
+ }
669
+ }
670
+
671
+ if (bucket->seqno == 0) {
672
+ bucket->io->stop_event_loop(bucket->io);
673
+ rb_hash_delete(object_space, ctx->proc|1);
674
+ }
675
+ (void)handle;
676
+ }
677
+
678
+ static void
679
+ flush_callback(libcouchbase_t handle, const void* cookie,
680
+ const char* authority, libcouchbase_error_t error)
681
+ {
682
+ context_t *ctx = (context_t *)cookie;
683
+ bucket_t *bucket = ctx->bucket;
684
+ VALUE node, success = Qtrue, *rv = ctx->rv, exc, res;
685
+
686
+ node = authority ? rb_str_new2(authority) : Qnil;
687
+ exc = cb_check_error(error, "failed to flush bucket", node);
688
+ if (exc != Qnil) {
689
+ rb_ivar_set(exc, id_iv_operation, sym_flush);
690
+ if (NIL_P(ctx->exception)) {
691
+ ctx->exception = exc;
692
+ }
693
+ success = Qfalse;
694
+ }
695
+
696
+ if (authority) {
697
+ if (bucket->async) { /* asynchronous */
698
+ if (ctx->proc != Qnil) {
699
+ res = rb_class_new_instance(0, NULL, cResult);
700
+ rb_ivar_set(res, id_iv_error, exc);
701
+ rb_ivar_set(res, id_iv_operation, sym_flush);
702
+ rb_ivar_set(res, id_iv_node, node);
703
+ cb_proc_call(ctx->proc, 1, res);
704
+ }
705
+ } else { /* synchronous */
706
+ if (RTEST(*rv)) {
707
+ /* rewrite status for positive values only */
708
+ *rv = success;
709
+ }
710
+ }
711
+ } else {
712
+ bucket->seqno--;
713
+ if (bucket->seqno == 0) {
714
+ bucket->io->stop_event_loop(bucket->io);
715
+ rb_hash_delete(object_space, ctx->proc|1);
716
+ }
717
+ }
718
+
719
+ (void)handle;
720
+ }
721
+
722
+ static void
723
+ stat_callback(libcouchbase_t handle, const void* cookie,
724
+ const char* authority, libcouchbase_error_t error, const void* key,
725
+ size_t nkey, const void* bytes, size_t nbytes)
726
+ {
727
+ context_t *ctx = (context_t *)cookie;
728
+ bucket_t *bucket = ctx->bucket;
729
+ VALUE stats, node, k, v, *rv = ctx->rv, exc = Qnil, res;
730
+
731
+ node = authority ? rb_str_new2(authority) : Qnil;
732
+ exc = cb_check_error(error, "failed to fetch stats", node);
733
+ if (exc != Qnil) {
734
+ rb_ivar_set(exc, id_iv_operation, sym_stats);
735
+ if (NIL_P(ctx->exception)) {
736
+ ctx->exception = exc;
737
+ }
738
+ }
739
+ if (authority) {
740
+ k = rb_str_new((const char*)key, nkey);
741
+ v = rb_str_new((const char*)bytes, nbytes);
742
+ if (bucket->async) { /* asynchronous */
743
+ if (ctx->proc != Qnil) {
744
+ res = rb_class_new_instance(0, NULL, cResult);
745
+ rb_ivar_set(res, id_iv_error, exc);
746
+ rb_ivar_set(res, id_iv_operation, sym_stats);
747
+ rb_ivar_set(res, id_iv_node, node);
748
+ rb_ivar_set(res, id_iv_key, k);
749
+ rb_ivar_set(res, id_iv_value, v);
750
+ cb_proc_call(ctx->proc, 1, res);
751
+ }
752
+ } else { /* synchronous */
753
+ if (NIL_P(exc)) {
754
+ stats = rb_hash_aref(*rv, node);
755
+ if (NIL_P(stats)) {
756
+ stats = rb_hash_new();
757
+ rb_hash_aset(*rv, node, stats);
758
+ }
759
+ rb_hash_aset(stats, k, v);
760
+ }
761
+ }
762
+ } else {
763
+ bucket->seqno--;
764
+ if (bucket->seqno == 0) {
765
+ bucket->io->stop_event_loop(bucket->io);
766
+ rb_hash_delete(object_space, ctx->proc|1);
767
+ }
768
+ }
769
+ (void)handle;
770
+ }
771
+
772
+ static void
773
+ touch_callback(libcouchbase_t handle, const void *cookie,
774
+ libcouchbase_error_t error, const void *key, size_t nkey)
775
+ {
776
+ context_t *ctx = (context_t *)cookie;
777
+ bucket_t *bucket = ctx->bucket;
778
+ VALUE k, success, *rv = ctx->rv, exc = Qnil, res;
779
+
780
+ bucket->seqno--;
781
+ k = rb_str_new((const char*)key, nkey);
782
+ if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
783
+ exc = cb_check_error(error, "failed to touch value", k);
784
+ if (exc != Qnil) {
785
+ rb_ivar_set(exc, id_iv_operation, sym_touch);
786
+ if (NIL_P(ctx->exception)) {
787
+ ctx->exception = exc;
788
+ }
789
+ }
790
+ }
791
+
792
+ if (bucket->async) { /* asynchronous */
793
+ if (ctx->proc != Qnil) {
794
+ res = rb_class_new_instance(0, NULL, cResult);
795
+ rb_ivar_set(res, id_iv_error, exc);
796
+ rb_ivar_set(res, id_iv_operation, sym_touch);
797
+ rb_ivar_set(res, id_iv_key, k);
798
+ cb_proc_call(ctx->proc, 1, res);
799
+ }
800
+ } else { /* synchronous */
801
+ if (NIL_P(exc)) {
802
+ success = (error == LIBCOUCHBASE_KEY_ENOENT) ? Qfalse : Qtrue;
803
+ rb_ary_push(*rv, success);
804
+ }
805
+ }
806
+ if (bucket->seqno == 0) {
807
+ bucket->io->stop_event_loop(bucket->io);
808
+ rb_hash_delete(object_space, ctx->proc|1);
809
+ }
810
+ (void)handle;
811
+ }
812
+
813
+ static void
814
+ arithmetic_callback(libcouchbase_t handle, const void *cookie,
815
+ libcouchbase_error_t error, const void *key, size_t nkey,
816
+ uint64_t value, uint64_t cas)
817
+ {
818
+ context_t *ctx = (context_t *)cookie;
819
+ bucket_t *bucket = ctx->bucket;
820
+ VALUE c, k, v, *rv = ctx->rv, exc, res;
821
+ ID o;
822
+
823
+ bucket->seqno--;
824
+
825
+ k = rb_str_new((const char*)key, nkey);
826
+ c = cas > 0 ? ULL2NUM(cas) : Qnil;
827
+ o = ctx->arithm > 0 ? sym_increment : sym_decrement;
828
+ exc = cb_check_error(error, "failed to perform arithmetic operation", k);
829
+ if (exc != Qnil) {
830
+ rb_ivar_set(exc, id_iv_cas, c);
831
+ rb_ivar_set(exc, id_iv_operation, o);
832
+ if (bucket->async) {
833
+ if (bucket->on_error_proc != Qnil) {
834
+ cb_proc_call(bucket->on_error_proc, 3, o, k, exc);
835
+ } else {
836
+ if (NIL_P(bucket->exception)) {
837
+ bucket->exception = exc;
838
+ }
839
+ }
840
+ }
841
+ if (NIL_P(ctx->exception)) {
842
+ ctx->exception = exc;
843
+ }
844
+ }
845
+ v = ULL2NUM(value);
846
+ if (bucket->async) { /* asynchronous */
847
+ if (ctx->proc != Qnil) {
848
+ res = rb_class_new_instance(0, NULL, cResult);
849
+ rb_ivar_set(res, id_iv_error, exc);
850
+ rb_ivar_set(res, id_iv_operation, o);
851
+ rb_ivar_set(res, id_iv_key, k);
852
+ rb_ivar_set(res, id_iv_value, v);
853
+ rb_ivar_set(res, id_iv_cas, c);
854
+ cb_proc_call(ctx->proc, 1, res);
855
+ }
856
+ } else { /* synchronous */
857
+ if (NIL_P(exc)) {
858
+ if (ctx->extended) {
859
+ *rv = rb_ary_new3(2, v, c);
860
+ } else {
861
+ *rv = v;
862
+ }
863
+ }
864
+ }
865
+ if (bucket->seqno == 0) {
866
+ bucket->io->stop_event_loop(bucket->io);
867
+ rb_hash_delete(object_space, ctx->proc|1);
868
+ }
869
+ (void)handle;
870
+ }
871
+
872
+ static char *
873
+ parse_path_segment(char *source, const char *key, char **result)
874
+ {
875
+ size_t len;
876
+ char *eot;
877
+
878
+ if (source == NULL) {
879
+ return NULL;
880
+ }
881
+ eot = strchr(source, '/');
882
+ if (eot > source && strncmp(source, key, eot - source) == 0) {
883
+ *eot = '\0';
884
+ source = eot + 1;
885
+ eot = strchr(source, '/');
886
+ len = strlen(source);
887
+ if (eot > source || len) {
888
+ if (eot) {
889
+ *eot = '\0';
890
+ eot++;
891
+ }
892
+ *result = strdup(source);
893
+ }
894
+ }
895
+ return eot;
896
+ }
897
+
898
+ static void
899
+ parse_bucket_uri(VALUE uri, bucket_t *bucket)
900
+ {
901
+ char *src, *ptr, *eot, sep = '\0';
902
+
903
+ ptr = src = strdup(StringValueCStr(uri));
904
+ eot = strchr(ptr, ':');
905
+ if (eot < ptr || strncmp(ptr, "http", eot - ptr) != 0) {
906
+ free(src);
907
+ rb_raise(rb_eArgError, "invalid URI format: missing schema");
908
+ return;
909
+ }
910
+ ptr = eot + 1;
911
+ if (ptr[0] != '/' || ptr[1] != '/') {
912
+ free(src);
913
+ rb_raise(rb_eArgError, "invalid URI format.");
914
+ return;
915
+ }
916
+ ptr += 2;
917
+ eot = ptr;
918
+ while (*eot) {
919
+ if (*eot == '?' || *eot == '#' || *eot == ':' || *eot == '/') {
920
+ break;
921
+ }
922
+ ++eot;
923
+ }
924
+ if (eot > ptr) {
925
+ sep = *eot;
926
+ *eot = '\0';
927
+ bucket->hostname = strdup(ptr);
928
+ }
929
+ ptr = eot + 1;
930
+ eot = strchr(ptr, '/');
931
+ if (sep == ':') {
932
+ if (eot > ptr) {
933
+ *eot = '\0';
934
+ }
935
+ bucket->port = (uint16_t)atoi(ptr);
936
+ if (eot > ptr) {
937
+ ptr = eot + 1;
938
+ }
939
+ }
940
+ ptr = parse_path_segment(ptr, "pools", &bucket->pool);
941
+ parse_path_segment(ptr, "buckets", &bucket->bucket);
942
+ free(src);
943
+ }
944
+
945
+ static int
946
+ cb_first_value_i(VALUE key, VALUE value, VALUE arg)
947
+ {
948
+ VALUE *val = (VALUE *)arg;
949
+
950
+ *val = value;
951
+ (void)key;
952
+ return ST_STOP;
953
+ }
954
+
955
+ static VALUE
956
+ cb_bucket_inspect(VALUE self)
957
+ {
958
+ VALUE str;
959
+ bucket_t *bucket = DATA_PTR(self);
960
+ char buf[200];
961
+
962
+ str = rb_str_buf_new2("#<");
963
+ rb_str_buf_cat2(str, rb_obj_classname(self));
964
+ snprintf(buf, 25, ":%p \"", (void *)self);
965
+ rb_str_buf_cat2(str, buf);
966
+ rb_str_append(str, rb_ivar_get(self, id_iv_url));
967
+ snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s>",
968
+ rb_id2name(SYM2ID(bucket->default_format)),
969
+ bucket->default_flags,
970
+ bucket->quiet ? "true" : "false");
971
+ rb_str_buf_cat2(str, buf);
972
+
973
+ return str;
974
+ }
975
+
976
+ /*
977
+ * @return [Fixnum] number of scheduled operations
978
+ */
979
+ static VALUE
980
+ cb_bucket_seqno(VALUE self)
981
+ {
982
+ bucket_t *bucket = DATA_PTR(self);
983
+
984
+ return LONG2FIX(bucket->seqno);
985
+ }
986
+
987
+ void
988
+ cb_bucket_free(void *ptr)
989
+ {
990
+ bucket_t *bucket = ptr;
991
+
992
+ if (bucket) {
993
+ if (bucket->handle) {
994
+ libcouchbase_destroy(bucket->handle);
995
+ free(bucket->authority);
996
+ free(bucket->hostname);
997
+ free(bucket->pool);
998
+ free(bucket->bucket);
999
+ free(bucket->username);
1000
+ free(bucket->password);
1001
+ }
1002
+ free(bucket);
1003
+ }
1004
+ }
1005
+
1006
+ void
1007
+ cb_bucket_mark(void *ptr)
1008
+ {
1009
+ bucket_t *bucket = ptr;
1010
+
1011
+ if (bucket) {
1012
+ rb_gc_mark(bucket->exception);
1013
+ rb_gc_mark(bucket->on_error_proc);
1014
+ }
1015
+ }
1016
+
1017
+ /*
1018
+ * @return [Bucket] new instance (see Bucket#initialize)
1019
+ */
1020
+ static VALUE
1021
+ cb_bucket_new(int argc, VALUE *argv, VALUE klass)
1022
+ {
1023
+ VALUE obj;
1024
+ bucket_t *bucket;
1025
+
1026
+ /* allocate new bucket struct and set it to zero */
1027
+ obj = Data_Make_Struct(klass, bucket_t, cb_bucket_mark, cb_bucket_free,
1028
+ bucket);
1029
+ rb_obj_call_init(obj, argc, argv);
1030
+ return obj;
1031
+ }
1032
+
1033
+ /*
1034
+ * @overload initialize(url, options = {})
1035
+ * Initialize bucket using URI of the cluster and options. It is possible
1036
+ * to override some parts of URI using the options keys (e.g. :host or
1037
+ * :port)
1038
+ *
1039
+ * @param [String] url The full URL of management API of the cluster.
1040
+ * @param [Hash] options The options for connection. See options definition
1041
+ * below.
1042
+ *
1043
+ * @overload initialize(options = {})
1044
+ * Initialize bucket using options only.
1045
+ *
1046
+ * @param [Hash] options The options for operation for connection
1047
+ * @option options [String] :host ("localhost") the hostname or IP address
1048
+ * of the node
1049
+ * @option options [Fixnum] :port (8091) the port of the managemenent API
1050
+ * @option options [String] :pool ("default") the pool name
1051
+ * @option options [String] :bucket ("default") the bucket name
1052
+ * @option options [Fixnum] :default_ttl (0) the TTL used by default during
1053
+ * storing key-value pairs.
1054
+ * @option options [Fixnum] :default_flags (0) the default flags.
1055
+ * @option options [Symbol] :default_format (:document) the format, which
1056
+ * will be used for values by default. Note that changing format will
1057
+ * amend flags. (see Bucket#default_format)
1058
+ * @option options [String] :username (nil) the user name to connect to the
1059
+ * cluster. Used to authenticate on management API.
1060
+ * @option options [String] :password (nil) the password of the user.
1061
+ * @option options [Boolean] :quiet (true) the flag controlling if raising
1062
+ * exception when the client executes operations on unexising keys. If it
1063
+ * is +true+ it will raise +Couchbase::NotFoundError+ exceptions. The
1064
+ * default behaviour is to return +nil+ value silently (might be useful in
1065
+ * Rails cache).
1066
+ *
1067
+ * @example Initialize connection using default options
1068
+ * Couchbase.new
1069
+ *
1070
+ * @example Select custom bucket
1071
+ * Couchbase.new(:bucket => 'foo')
1072
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
1073
+ *
1074
+ * @example Connect to protected bucket
1075
+ * Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
1076
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
1077
+ * :username => 'protected', :password => 'secret')
1078
+ *
1079
+ * @return [Bucket]
1080
+ */
1081
+ static VALUE
1082
+ cb_bucket_init(int argc, VALUE *argv, VALUE self)
1083
+ {
1084
+ VALUE uri, opts, arg, buf;
1085
+ libcouchbase_error_t err;
1086
+ bucket_t *bucket = DATA_PTR(self);
1087
+ size_t len;
1088
+
1089
+ bucket->exception = Qnil;
1090
+ bucket->hostname = strdup("localhost");
1091
+ bucket->port = 8091;
1092
+ bucket->pool = strdup("default");
1093
+ bucket->bucket = strdup("default");
1094
+ bucket->async = 0;
1095
+ bucket->quiet = 1;
1096
+ bucket->default_flags = 0;
1097
+ bucket->default_format = sym_document;
1098
+ bucket->on_error_proc = Qnil;
1099
+
1100
+ if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
1101
+ if (TYPE(uri) == T_HASH && argc == 1) {
1102
+ opts = uri;
1103
+ uri = Qnil;
1104
+ }
1105
+ if (uri != Qnil) {
1106
+ Check_Type(uri, T_STRING);
1107
+ parse_bucket_uri(uri, bucket);
1108
+ }
1109
+ if (TYPE(opts) == T_HASH) {
1110
+ arg = rb_hash_aref(opts, sym_hostname);
1111
+ if (arg != Qnil) {
1112
+ if (bucket->hostname) {
1113
+ free(bucket->hostname);
1114
+ }
1115
+ bucket->hostname = strdup(StringValueCStr(arg));
1116
+ }
1117
+ arg = rb_hash_aref(opts, sym_pool);
1118
+ if (arg != Qnil) {
1119
+ if (bucket->pool) {
1120
+ free(bucket->pool);
1121
+ }
1122
+ bucket->pool = strdup(StringValueCStr(arg));
1123
+ }
1124
+ arg = rb_hash_aref(opts, sym_bucket);
1125
+ if (arg != Qnil) {
1126
+ if (bucket->bucket) {
1127
+ free(bucket->bucket);
1128
+ }
1129
+ bucket->bucket = strdup(StringValueCStr(arg));
1130
+ }
1131
+ arg = rb_hash_aref(opts, sym_username);
1132
+ if (arg != Qnil) {
1133
+ bucket->username = strdup(StringValueCStr(arg));
1134
+ }
1135
+ arg = rb_hash_aref(opts, sym_password);
1136
+ if (arg != Qnil) {
1137
+ bucket->password = strdup(StringValueCStr(arg));
1138
+ }
1139
+ arg = rb_hash_aref(opts, sym_port);
1140
+ if (arg != Qnil) {
1141
+ bucket->port = (uint16_t)NUM2UINT(arg);
1142
+ }
1143
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
1144
+ bucket->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
1145
+ }
1146
+ arg = rb_hash_aref(opts, sym_default_flags);
1147
+ if (arg != Qnil) {
1148
+ bucket->default_flags = (uint32_t)NUM2ULONG(arg);
1149
+ }
1150
+ arg = rb_hash_aref(opts, sym_default_format);
1151
+ if (arg != Qnil) {
1152
+ if (TYPE(arg) == T_FIXNUM) {
1153
+ switch (FIX2INT(arg)) {
1154
+ case FMT_DOCUMENT:
1155
+ arg = sym_document;
1156
+ break;
1157
+ case FMT_MARSHAL:
1158
+ arg = sym_marshal;
1159
+ break;
1160
+ case FMT_PLAIN:
1161
+ arg = sym_plain;
1162
+ break;
1163
+ }
1164
+ }
1165
+ if (arg == sym_document || arg == sym_marshal || arg == sym_plain) {
1166
+ bucket->default_format = arg;
1167
+ bucket->default_flags = flags_set_format(bucket->default_flags, arg);
1168
+ }
1169
+ }
1170
+ } else {
1171
+ opts = Qnil;
1172
+ }
1173
+ }
1174
+ len = strlen(bucket->hostname) + 10;
1175
+ bucket->authority = calloc(len, sizeof(char));
1176
+ if (bucket->authority == NULL) {
1177
+ rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
1178
+ }
1179
+ snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
1180
+ bucket->io = libcouchbase_create_io_ops(LIBCOUCHBASE_IO_OPS_DEFAULT, NULL, &err);
1181
+ if (bucket->io == NULL) {
1182
+ rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
1183
+ }
1184
+ bucket->handle = libcouchbase_create(bucket->authority,
1185
+ bucket->username, bucket->password, bucket->bucket, bucket->io);
1186
+ if (bucket->handle == NULL) {
1187
+ rb_raise(eLibcouchbaseError, "failed to create libcouchbase instance");
1188
+ }
1189
+ libcouchbase_set_cookie(bucket->handle, bucket);
1190
+ (void)libcouchbase_set_error_callback(bucket->handle, error_callback);
1191
+ (void)libcouchbase_set_storage_callback(bucket->handle, storage_callback);
1192
+ (void)libcouchbase_set_get_callback(bucket->handle, get_callback);
1193
+ (void)libcouchbase_set_touch_callback(bucket->handle, touch_callback);
1194
+ (void)libcouchbase_set_remove_callback(bucket->handle, delete_callback);
1195
+ (void)libcouchbase_set_stat_callback(bucket->handle, stat_callback);
1196
+ (void)libcouchbase_set_flush_callback(bucket->handle, flush_callback);
1197
+ (void)libcouchbase_set_arithmetic_callback(bucket->handle, arithmetic_callback);
1198
+
1199
+ err = libcouchbase_connect(bucket->handle);
1200
+ if (err != LIBCOUCHBASE_SUCCESS) {
1201
+ rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
1202
+ }
1203
+ libcouchbase_wait(bucket->handle);
1204
+ if (bucket->exception != Qnil) {
1205
+ rb_exc_raise(bucket->exception);
1206
+ }
1207
+
1208
+ rb_ivar_set(self, id_iv_authority, rb_str_new2(bucket->authority));
1209
+ rb_ivar_set(self, id_iv_bucket, rb_str_new2(bucket->bucket));
1210
+ rb_ivar_set(self, id_iv_hostname, rb_str_new2(bucket->hostname));
1211
+ rb_ivar_set(self, id_iv_password, bucket->password ? rb_str_new2(bucket->password) : Qnil);
1212
+ rb_ivar_set(self, id_iv_pool, rb_str_new2(bucket->pool));
1213
+ rb_ivar_set(self, id_iv_port, UINT2NUM(bucket->port));
1214
+ rb_ivar_set(self, id_iv_username, bucket->username ? rb_str_new2(bucket->username) : Qnil);
1215
+ rb_ivar_set(self, id_iv_quiet, bucket->quiet ? Qtrue : Qfalse);
1216
+ rb_ivar_set(self, id_iv_default_flags, ULONG2NUM(bucket->default_flags));
1217
+ rb_ivar_set(self, id_iv_default_format, bucket->default_format);
1218
+ rb_ivar_set(self, id_iv_on_error, bucket->on_error_proc);
1219
+
1220
+ buf = rb_str_buf_new2("http://");
1221
+ rb_str_buf_cat2(buf, bucket->authority);
1222
+ rb_str_buf_cat2(buf, "/pools/");
1223
+ rb_str_buf_cat2(buf, bucket->pool);
1224
+ rb_str_buf_cat2(buf, "/buckets/");
1225
+ rb_str_buf_cat2(buf, bucket->bucket);
1226
+ rb_str_buf_cat2(buf, "/");
1227
+ rb_ivar_set(self, id_iv_url, buf);
1228
+
1229
+ return self;
1230
+ }
1231
+
1232
+ static VALUE
1233
+ cb_bucket_async_p(VALUE self)
1234
+ {
1235
+ bucket_t *bucket = DATA_PTR(self);
1236
+ return bucket->async ? Qtrue : Qfalse;
1237
+ }
1238
+
1239
+ static VALUE
1240
+ cb_bucket_quiet_set(VALUE self, VALUE val)
1241
+ {
1242
+ bucket_t *bucket = DATA_PTR(self);
1243
+ VALUE new;
1244
+
1245
+ bucket->quiet = RTEST(val);
1246
+ new = bucket->quiet ? Qtrue : Qfalse;
1247
+ rb_ivar_set(self, id_iv_quiet, new);
1248
+ return new;
1249
+ }
1250
+
1251
+ static VALUE
1252
+ cb_bucket_default_flags_set(VALUE self, VALUE val)
1253
+ {
1254
+ bucket_t *bucket = DATA_PTR(self);
1255
+
1256
+ bucket->default_flags = (uint32_t)NUM2ULONG(val);
1257
+ bucket->default_format = flags_get_format(bucket->default_flags);
1258
+ rb_ivar_set(self, id_iv_default_format, bucket->default_format);
1259
+ rb_ivar_set(self, id_iv_default_flags, val);
1260
+ return val;
1261
+ }
1262
+
1263
+ static VALUE
1264
+ cb_bucket_default_format_set(VALUE self, VALUE val)
1265
+ {
1266
+ bucket_t *bucket = DATA_PTR(self);
1267
+
1268
+ if (TYPE(val) == T_FIXNUM) {
1269
+ switch (FIX2INT(val)) {
1270
+ case FMT_DOCUMENT:
1271
+ val = sym_document;
1272
+ break;
1273
+ case FMT_MARSHAL:
1274
+ val = sym_marshal;
1275
+ break;
1276
+ case FMT_PLAIN:
1277
+ val = sym_plain;
1278
+ break;
1279
+ }
1280
+ }
1281
+ if (val == sym_document || val == sym_marshal || val == sym_plain) {
1282
+ bucket->default_format = val;
1283
+ bucket->default_flags = flags_set_format(bucket->default_flags, val);
1284
+ rb_ivar_set(self, id_iv_default_format, val);
1285
+ rb_ivar_set(self, id_iv_default_flags, ULONG2NUM(bucket->default_flags));
1286
+ }
1287
+
1288
+ return val;
1289
+ }
1290
+
1291
+ static VALUE
1292
+ cb_bucket_on_error_set(VALUE self, VALUE val)
1293
+ {
1294
+ bucket_t *bucket = DATA_PTR(self);
1295
+
1296
+ if (rb_respond_to(val, id_call)) {
1297
+ bucket->on_error_proc = val;
1298
+ } else {
1299
+ bucket->on_error_proc = Qnil;
1300
+ }
1301
+ rb_ivar_set(self, id_iv_on_error, bucket->on_error_proc);
1302
+
1303
+ return bucket->on_error_proc;
1304
+ }
1305
+
1306
+ static VALUE
1307
+ cb_bucket_on_error_get(VALUE self)
1308
+ {
1309
+ bucket_t *bucket = DATA_PTR(self);
1310
+
1311
+ if (rb_block_given_p()) {
1312
+ return cb_bucket_on_error_set(self, rb_block_proc());
1313
+ } else {
1314
+ return bucket->on_error_proc;
1315
+ }
1316
+ }
1317
+
1318
+ static VALUE
1319
+ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
1320
+ {
1321
+ bucket_t *bucket = DATA_PTR(self);
1322
+ context_t *ctx;
1323
+ VALUE k, c, rv, proc, exc, opts;
1324
+ char *key;
1325
+ size_t nkey;
1326
+ uint64_t cas = 0;
1327
+ libcouchbase_error_t err;
1328
+
1329
+ rb_scan_args(argc, argv, "11&", &k, &opts, &proc);
1330
+ if (!bucket->async && proc != Qnil) {
1331
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1332
+ }
1333
+ k = unify_key(k);
1334
+ key = RSTRING_PTR(k);
1335
+ nkey = RSTRING_LEN(k);
1336
+ ctx = calloc(1, sizeof(context_t));
1337
+ ctx->quiet = bucket->quiet;
1338
+ if (ctx == NULL) {
1339
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1340
+ }
1341
+ if (opts != Qnil) {
1342
+ if (TYPE(opts) == T_BIGNUM || TYPE(opts) == T_FIXNUM) {
1343
+ cas = NUM2ULL(opts);
1344
+ } else {
1345
+ Check_Type(opts, T_HASH);
1346
+ if ((c = rb_hash_aref(opts, sym_cas)) != Qnil) {
1347
+ cas = NUM2ULL(c);
1348
+ }
1349
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
1350
+ ctx->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
1351
+ }
1352
+ }
1353
+ }
1354
+ ctx->proc = proc;
1355
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1356
+ rv = rb_ary_new();
1357
+ ctx->rv = &rv;
1358
+ ctx->bucket = bucket;
1359
+ ctx->exception = Qnil;
1360
+ err = libcouchbase_remove(bucket->handle, (const void *)ctx,
1361
+ (const void *)key, nkey, cas);
1362
+ exc = cb_check_error(err, "failed to schedule delete request", Qnil);
1363
+ if (exc != Qnil) {
1364
+ free(ctx);
1365
+ rb_exc_raise(exc);
1366
+ }
1367
+ bucket->seqno++;
1368
+ if (bucket->async) {
1369
+ return Qnil;
1370
+ } else {
1371
+ bucket->io->run_event_loop(bucket->io);
1372
+ exc = ctx->exception;
1373
+ free(ctx);
1374
+ if (exc != Qnil) {
1375
+ rb_exc_raise(exc);
1376
+ }
1377
+ return rv;
1378
+ }
1379
+ }
1380
+
1381
+ static inline VALUE
1382
+ cb_bucket_store(libcouchbase_storage_t cmd, int argc, VALUE *argv, VALUE self)
1383
+ {
1384
+ bucket_t *bucket = DATA_PTR(self);
1385
+ context_t *ctx;
1386
+ VALUE k, v, arg, opts, rv, proc, exc, fmt;
1387
+ char *key, *bytes;
1388
+ size_t nkey, nbytes;
1389
+ uint32_t flags;
1390
+ time_t exp = 0;
1391
+ uint64_t cas = 0;
1392
+ libcouchbase_error_t err;
1393
+
1394
+ rb_scan_args(argc, argv, "21&", &k, &v, &opts, &proc);
1395
+ if (!bucket->async && proc != Qnil) {
1396
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1397
+ }
1398
+ k = unify_key(k);
1399
+ flags = bucket->default_flags;
1400
+ if (opts != Qnil) {
1401
+ Check_Type(opts, T_HASH);
1402
+ arg = rb_hash_aref(opts, sym_flags);
1403
+ if (arg != Qnil) {
1404
+ flags = (uint32_t)NUM2ULONG(arg);
1405
+ }
1406
+ arg = rb_hash_aref(opts, sym_ttl);
1407
+ if (arg != Qnil) {
1408
+ exp = NUM2ULONG(arg);
1409
+ }
1410
+ arg = rb_hash_aref(opts, sym_cas);
1411
+ if (arg != Qnil) {
1412
+ cas = NUM2ULL(arg);
1413
+ }
1414
+ fmt = rb_hash_aref(opts, sym_format);
1415
+ if (fmt != Qnil) { /* rewrite format bits */
1416
+ flags = flags_set_format(flags, fmt);
1417
+ }
1418
+ }
1419
+ key = RSTRING_PTR(k);
1420
+ nkey = RSTRING_LEN(k);
1421
+ v = encode_value(v, flags);
1422
+ if (v == Qundef) {
1423
+ rb_raise(eValueFormatError,
1424
+ "unable to convert value for key '%s'", key);
1425
+ }
1426
+ bytes = RSTRING_PTR(v);
1427
+ nbytes = RSTRING_LEN(v);
1428
+ ctx = calloc(1, sizeof(context_t));
1429
+ if (ctx == NULL) {
1430
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1431
+ }
1432
+ rv = Qnil;
1433
+ ctx->rv = &rv;
1434
+ ctx->bucket = bucket;
1435
+ ctx->proc = proc;
1436
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1437
+ ctx->exception = Qnil;
1438
+ err = libcouchbase_store(bucket->handle, (const void *)ctx, cmd,
1439
+ (const void *)key, nkey, bytes, nbytes, flags, exp, cas);
1440
+ exc = cb_check_error(err, "failed to schedule set request", Qnil);
1441
+ if (exc != Qnil) {
1442
+ free(ctx);
1443
+ rb_exc_raise(exc);
1444
+ }
1445
+ bucket->seqno++;
1446
+ if (bucket->async) {
1447
+ return Qnil;
1448
+ } else {
1449
+ bucket->io->run_event_loop(bucket->io);
1450
+ exc = ctx->exception;
1451
+ free(ctx);
1452
+ if (exc != Qnil) {
1453
+ rb_exc_raise(exc);
1454
+ }
1455
+ if (bucket->exception != Qnil) {
1456
+ rb_exc_raise(bucket->exception);
1457
+ }
1458
+ return rv;
1459
+ }
1460
+ }
1461
+
1462
+ static inline VALUE
1463
+ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
1464
+ {
1465
+ bucket_t *bucket = DATA_PTR(self);
1466
+ context_t *ctx;
1467
+ VALUE k, d, arg, opts, rv, proc, exc;
1468
+ char *key;
1469
+ size_t nkey;
1470
+ time_t exp;
1471
+ uint64_t delta = 0, initial = 0;
1472
+ int create = 0;
1473
+ libcouchbase_error_t err;
1474
+
1475
+ rb_scan_args(argc, argv, "12&", &k, &d, &opts, &proc);
1476
+ if (!bucket->async && proc != Qnil) {
1477
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1478
+ }
1479
+ k = unify_key(k);
1480
+ ctx = calloc(1, sizeof(context_t));
1481
+ if (ctx == NULL) {
1482
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1483
+ }
1484
+ if (argc == 2 && TYPE(d) == T_HASH) {
1485
+ opts = d;
1486
+ d = Qnil;
1487
+ }
1488
+ exp = bucket->default_ttl;
1489
+ if (opts != Qnil) {
1490
+ Check_Type(opts, T_HASH);
1491
+ create = RTEST(rb_hash_aref(opts, sym_create));
1492
+ ctx->extended = RTEST(rb_hash_aref(opts, sym_extended));
1493
+ arg = rb_hash_aref(opts, sym_ttl);
1494
+ if (arg != Qnil) {
1495
+ exp = NUM2ULONG(arg);
1496
+ }
1497
+ arg = rb_hash_aref(opts, sym_initial);
1498
+ if (arg != Qnil) {
1499
+ initial = NUM2ULL(arg);
1500
+ create = 1;
1501
+ }
1502
+ }
1503
+ key = RSTRING_PTR(k);
1504
+ nkey = RSTRING_LEN(k);
1505
+ if (NIL_P(d)) {
1506
+ delta = 1 * sign;
1507
+ } else {
1508
+ delta = NUM2ULL(d) * sign;
1509
+ }
1510
+ rv = Qnil;
1511
+ ctx->rv = &rv;
1512
+ ctx->bucket = bucket;
1513
+ ctx->proc = proc;
1514
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1515
+ ctx->exception = Qnil;
1516
+ ctx->arithm = sign;
1517
+ err = libcouchbase_arithmetic(bucket->handle, (const void *)ctx,
1518
+ (const void *)key, nkey, delta, exp, create, initial);
1519
+ exc = cb_check_error(err, "failed to schedule arithmetic request", k);
1520
+ if (exc != Qnil) {
1521
+ free(ctx);
1522
+ rb_exc_raise(exc);
1523
+ }
1524
+ bucket->seqno++;
1525
+ if (bucket->async) {
1526
+ return Qnil;
1527
+ } else {
1528
+ bucket->io->run_event_loop(bucket->io);
1529
+ exc = ctx->exception;
1530
+ free(ctx);
1531
+ if (exc != Qnil) {
1532
+ rb_exc_raise(exc);
1533
+ }
1534
+ return rv;
1535
+ }
1536
+ }
1537
+
1538
+ static VALUE
1539
+ cb_bucket_incr(int argc, VALUE *argv, VALUE self)
1540
+ {
1541
+ return cb_bucket_arithmetic(+1, argc, argv, self);
1542
+ }
1543
+
1544
+ static VALUE
1545
+ cb_bucket_decr(int argc, VALUE *argv, VALUE self)
1546
+ {
1547
+ return cb_bucket_arithmetic(-1, argc, argv, self);
1548
+ }
1549
+
1550
+ static VALUE
1551
+ cb_bucket_get(int argc, VALUE *argv, VALUE self)
1552
+ {
1553
+ bucket_t *bucket = DATA_PTR(self);
1554
+ context_t *ctx;
1555
+ VALUE args, rv, proc, exc, vv = Qnil, keys;
1556
+ long nn;
1557
+ libcouchbase_error_t err;
1558
+ struct key_traits *traits;
1559
+ int extended;
1560
+
1561
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
1562
+ if (!bucket->async && proc != Qnil) {
1563
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1564
+ }
1565
+ rb_funcall(args, id_flatten_bang, 0);
1566
+ traits = calloc(1, sizeof(struct key_traits));
1567
+ nn = cb_args_scan_keys(RARRAY_LEN(args), args, bucket, traits);
1568
+ ctx = calloc(1, sizeof(context_t));
1569
+ if (ctx == NULL) {
1570
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1571
+ }
1572
+ keys = traits->keys_ary;
1573
+ ctx->proc = proc;
1574
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1575
+ ctx->bucket = bucket;
1576
+ ctx->extended = traits->extended;
1577
+ ctx->quiet = traits->quiet;
1578
+ rv = rb_hash_new();
1579
+ ctx->rv = &rv;
1580
+ ctx->exception = Qnil;
1581
+ if (!bucket->async) {
1582
+ bucket->seqno = 0;
1583
+ }
1584
+ err = libcouchbase_mget(bucket->handle, (const void *)ctx,
1585
+ traits->nkeys, (const void * const *)traits->keys,
1586
+ traits->lens, (traits->explicit_ttl) ? traits->ttls : NULL);
1587
+ free(traits->keys);
1588
+ free(traits->lens);
1589
+ free(traits->ttls);
1590
+ free(traits);
1591
+ exc = cb_check_error(err, "failed to schedule get request", Qnil);
1592
+ if (exc != Qnil) {
1593
+ free(ctx);
1594
+ rb_exc_raise(exc);
1595
+ }
1596
+ bucket->seqno += nn;
1597
+ if (bucket->async) {
1598
+ return Qnil;
1599
+ } else {
1600
+ bucket->io->run_event_loop(bucket->io);
1601
+ exc = ctx->exception;
1602
+ extended = ctx->extended;
1603
+ free(ctx);
1604
+ if (exc != Qnil) {
1605
+ rb_exc_raise(exc);
1606
+ }
1607
+ if (bucket->exception != Qnil) {
1608
+ rb_exc_raise(bucket->exception);
1609
+ }
1610
+ if (nn > 1) {
1611
+ if (extended) {
1612
+ return rv; /* return as a hash {key => [value, flags, cas], ...} */
1613
+ } else {
1614
+ long ii;
1615
+ VALUE *keys_ptr, ret;
1616
+ ret = rb_ary_new();
1617
+ keys_ptr = RARRAY_PTR(keys);
1618
+ for (ii = 0; ii < nn; ii++) {
1619
+ rb_ary_push(ret, rb_hash_aref(rv, keys_ptr[ii]));
1620
+ }
1621
+ return ret; /* return as an array [value1, value2, ...] */
1622
+ }
1623
+ } else {
1624
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
1625
+ return vv;
1626
+ }
1627
+ }
1628
+ }
1629
+
1630
+ static VALUE
1631
+ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
1632
+ {
1633
+ bucket_t *bucket = DATA_PTR(self);
1634
+ context_t *ctx;
1635
+ VALUE args, rv, proc, exc;
1636
+ size_t nn;
1637
+ libcouchbase_error_t err;
1638
+ struct key_traits *traits;
1639
+
1640
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
1641
+ if (!bucket->async && proc != Qnil) {
1642
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1643
+ }
1644
+ rb_funcall(args, id_flatten_bang, 0);
1645
+ traits = calloc(1, sizeof(struct key_traits));
1646
+ nn = cb_args_scan_keys(RARRAY_LEN(args), args, bucket, traits);
1647
+ ctx = calloc(1, sizeof(context_t));
1648
+ if (ctx == NULL) {
1649
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1650
+ }
1651
+ ctx->proc = proc;
1652
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1653
+ ctx->bucket = bucket;
1654
+ rv = rb_ary_new();
1655
+ ctx->rv = &rv;
1656
+ ctx->exception = Qnil;
1657
+ if (!bucket->async) {
1658
+ bucket->seqno = 0;
1659
+ }
1660
+ err = libcouchbase_mtouch(bucket->handle, (const void *)ctx,
1661
+ traits->nkeys, (const void * const *)traits->keys,
1662
+ traits->lens, traits->ttls);
1663
+ free(traits);
1664
+ exc = cb_check_error(err, "failed to schedule touch request", Qnil);
1665
+ if (exc != Qnil) {
1666
+ free(ctx);
1667
+ rb_exc_raise(exc);
1668
+ }
1669
+ bucket->seqno += nn;
1670
+ if (bucket->async) {
1671
+ return Qnil;
1672
+ } else {
1673
+ bucket->io->run_event_loop(bucket->io);
1674
+ exc = ctx->exception;
1675
+ free(ctx);
1676
+ if (exc != Qnil) {
1677
+ rb_exc_raise(exc);
1678
+ }
1679
+ if (bucket->exception != Qnil) {
1680
+ rb_exc_raise(bucket->exception);
1681
+ }
1682
+ if (nn > 1) {
1683
+ return rv;
1684
+ } else {
1685
+ return rb_ary_pop(rv);
1686
+ }
1687
+ }
1688
+ }
1689
+
1690
+ static VALUE
1691
+ cb_bucket_flush(VALUE self)
1692
+ {
1693
+ bucket_t *bucket = DATA_PTR(self);
1694
+ context_t *ctx;
1695
+ VALUE rv, exc;
1696
+ libcouchbase_error_t err;
1697
+
1698
+ if (!bucket->async && rb_block_given_p()) {
1699
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1700
+ }
1701
+ ctx = calloc(1, sizeof(context_t));
1702
+ if (ctx == NULL) {
1703
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1704
+ }
1705
+ rv = Qtrue; /* optimistic by default */
1706
+ ctx->rv = &rv;
1707
+ ctx->bucket = bucket;
1708
+ ctx->exception = Qnil;
1709
+ if (rb_block_given_p()) {
1710
+ ctx->proc = rb_block_proc();
1711
+ } else {
1712
+ ctx->proc = Qnil;
1713
+ }
1714
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1715
+ err = libcouchbase_flush(bucket->handle, (const void *)ctx);
1716
+ exc = cb_check_error(err, "failed to schedule flush request", Qnil);
1717
+ if (exc != Qnil) {
1718
+ free(ctx);
1719
+ rb_exc_raise(exc);
1720
+ }
1721
+ bucket->seqno++;
1722
+ if (bucket->async) {
1723
+ return Qnil;
1724
+ } else {
1725
+ bucket->io->run_event_loop(bucket->io);
1726
+ exc = ctx->exception;
1727
+ free(ctx);
1728
+ if (exc != Qnil) {
1729
+ rb_exc_raise(exc);
1730
+ }
1731
+ return rv;
1732
+ }
1733
+ }
1734
+
1735
+ static VALUE
1736
+ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
1737
+ {
1738
+ bucket_t *bucket = DATA_PTR(self);
1739
+ context_t *ctx;
1740
+ VALUE rv, exc, arg, proc;
1741
+ char *key;
1742
+ size_t nkey;
1743
+ libcouchbase_error_t err;
1744
+
1745
+ rb_scan_args(argc, argv, "01&", &arg, &proc);
1746
+ if (!bucket->async && proc != Qnil) {
1747
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
1748
+ }
1749
+
1750
+ ctx = calloc(1, sizeof(context_t));
1751
+ if (ctx == NULL) {
1752
+ rb_raise(eNoMemoryError, "failed to allocate memory for context");
1753
+ }
1754
+ rv = rb_hash_new();
1755
+ ctx->rv = &rv;
1756
+ ctx->bucket = bucket;
1757
+ ctx->proc = proc;
1758
+ rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
1759
+ ctx->exception = Qnil;
1760
+ if (arg != Qnil) {
1761
+ key = RSTRING_PTR(arg);
1762
+ nkey = RSTRING_LEN(arg);
1763
+ } else {
1764
+ key = NULL;
1765
+ nkey = 0;
1766
+ }
1767
+ err = libcouchbase_server_stats(bucket->handle, (const void *)ctx,
1768
+ key, nkey);
1769
+ exc = cb_check_error(err, "failed to schedule stat request", Qnil);
1770
+ if (exc != Qnil) {
1771
+ free(ctx);
1772
+ rb_exc_raise(exc);
1773
+ }
1774
+ bucket->seqno++;
1775
+ if (bucket->async) {
1776
+ return Qnil;
1777
+ } else {
1778
+ bucket->io->run_event_loop(bucket->io);
1779
+ exc = ctx->exception;
1780
+ free(ctx);
1781
+ if (exc != Qnil) {
1782
+ rb_exc_raise(exc);
1783
+ }
1784
+ if (bucket->exception != Qnil) {
1785
+ rb_exc_raise(bucket->exception);
1786
+ }
1787
+ return rv;
1788
+ }
1789
+
1790
+ return Qnil;
1791
+ }
1792
+
1793
+ static VALUE
1794
+ do_run(VALUE *args)
1795
+ {
1796
+ VALUE self = args[0], proc = args[1], exc;
1797
+ bucket_t *bucket = DATA_PTR(self);
1798
+
1799
+ bucket->seqno = 0;
1800
+ bucket->async = 1;
1801
+ cb_proc_call(proc, 1, self);
1802
+ if (bucket->seqno > 0) {
1803
+ bucket->io->run_event_loop(bucket->io);
1804
+ if (bucket->exception != Qnil) {
1805
+ exc = bucket->exception;
1806
+ bucket->exception = Qnil;
1807
+ rb_exc_raise(exc);
1808
+ }
1809
+ }
1810
+ return Qnil;
1811
+ }
1812
+
1813
+ static VALUE
1814
+ ensure_run(VALUE *args)
1815
+ {
1816
+ VALUE self = args[0];
1817
+ bucket_t *bucket = DATA_PTR(self);
1818
+
1819
+ bucket->async = 0;
1820
+ return Qnil;
1821
+ }
1822
+
1823
+ /*
1824
+ * Run the event loop.
1825
+ *
1826
+ * @yieldparam [Bucket] the bucket instance
1827
+ *
1828
+ * @example Use block to run the loop
1829
+ * c = Couchbase.new
1830
+ * c.run do
1831
+ * c.get("foo") {|ret| puts ret.value}
1832
+ * end
1833
+ *
1834
+ * @example Use lambda to run the loop
1835
+ * c = Couchbase.new
1836
+ * operations = lambda do |c|
1837
+ * c.get("foo") {|ret| puts ret.value}
1838
+ * end
1839
+ * c.run(&operations)
1840
+ *
1841
+ */
1842
+ static VALUE
1843
+ cb_bucket_run(VALUE self)
1844
+ {
1845
+ VALUE args[2];
1846
+
1847
+ rb_need_block();
1848
+ args[0] = self;
1849
+ args[1] = rb_block_proc();
1850
+ rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
1851
+ return Qnil;
1852
+ }
1853
+
1854
+ /*
1855
+ * Unconditionally set the object in the cache
1856
+ */
1857
+ static VALUE
1858
+ cb_bucket_set(int argc, VALUE *argv, VALUE self)
1859
+ {
1860
+ return cb_bucket_store(LIBCOUCHBASE_SET, argc, argv, self);
1861
+ }
1862
+
1863
+ /*
1864
+ * Add the item to the cache, but fail if the object exists alread
1865
+ */
1866
+ static VALUE
1867
+ cb_bucket_add(int argc, VALUE *argv, VALUE self)
1868
+ {
1869
+ return cb_bucket_store(LIBCOUCHBASE_ADD, argc, argv, self);
1870
+ }
1871
+
1872
+ /*
1873
+ * Replace the existing object in the cache
1874
+ */
1875
+ static VALUE
1876
+ cb_bucket_replace(int argc, VALUE *argv, VALUE self)
1877
+ {
1878
+ return cb_bucket_store(LIBCOUCHBASE_REPLACE, argc, argv, self);
1879
+ }
1880
+
1881
+ /*
1882
+ * Append this object to the existing object
1883
+ */
1884
+ static VALUE
1885
+ cb_bucket_append(int argc, VALUE *argv, VALUE self)
1886
+ {
1887
+ return cb_bucket_store(LIBCOUCHBASE_APPEND, argc, argv, self);
1888
+ }
1889
+
1890
+ /*
1891
+ * Prepend this object to the existing object
1892
+ */
1893
+ static VALUE
1894
+ cb_bucket_prepend(int argc, VALUE *argv, VALUE self)
1895
+ {
1896
+ return cb_bucket_store(LIBCOUCHBASE_PREPEND, argc, argv, self);
1897
+ }
1898
+
1899
+ static VALUE
1900
+ cb_bucket_aset(int argc, VALUE *argv, VALUE self)
1901
+ {
1902
+ VALUE temp;
1903
+
1904
+ if (argc == 3) {
1905
+ /* swap opts and value, because value goes last for []= */
1906
+ temp = argv[2];
1907
+ argv[2] = argv[1];
1908
+ argv[1] = temp;
1909
+ }
1910
+ return cb_bucket_set(argc, argv, self);
1911
+ }
1912
+
1913
+ /*
1914
+ * Check if result of operation was successful.
1915
+ *
1916
+ * @return [Boolean] +false+ if there is an +error+ object attached,
1917
+ * +false+ otherwise.
1918
+ */
1919
+ static VALUE
1920
+ cb_result_success_p(VALUE self)
1921
+ {
1922
+ return RTEST(rb_ivar_get(self, id_iv_error)) ? Qfalse : Qtrue;
1923
+ }
1924
+
1925
+ static VALUE
1926
+ cb_result_inspect(VALUE self)
1927
+ {
1928
+ VALUE str, attr, errno;
1929
+ char buf[100];
1930
+
1931
+ str = rb_str_buf_new2("#<");
1932
+ rb_str_buf_cat2(str, rb_obj_classname(self));
1933
+ snprintf(buf, 100, ":%p", (void *)self);
1934
+ rb_str_buf_cat2(str, buf);
1935
+
1936
+ attr = rb_ivar_get(self, id_iv_error);
1937
+ if (RTEST(attr)) {
1938
+ errno = rb_ivar_get(attr, id_iv_error);
1939
+ } else {
1940
+ errno = INT2FIX(0);
1941
+ }
1942
+ rb_str_buf_cat2(str, " error=0x");
1943
+ rb_str_append(str, rb_funcall(errno, id_to_s, 1, INT2FIX(16)));
1944
+
1945
+ attr = rb_ivar_get(self, id_iv_key);
1946
+ if (RTEST(attr)) {
1947
+ rb_str_buf_cat2(str, " key=");
1948
+ rb_str_append(str, rb_inspect(attr));
1949
+ }
1950
+
1951
+ attr = rb_ivar_get(self, id_iv_cas);
1952
+ if (RTEST(attr)) {
1953
+ rb_str_buf_cat2(str, " cas=");
1954
+ rb_str_append(str, rb_inspect(attr));
1955
+ }
1956
+
1957
+ attr = rb_ivar_get(self, id_iv_flags);
1958
+ if (RTEST(attr)) {
1959
+ rb_str_buf_cat2(str, " flags=0x");
1960
+ rb_str_append(str, rb_funcall(attr, id_to_s, 1, INT2FIX(16)));
1961
+ }
1962
+
1963
+ attr = rb_ivar_get(self, id_iv_node);
1964
+ if (RTEST(attr)) {
1965
+ rb_str_buf_cat2(str, " node=");
1966
+ rb_str_append(str, rb_inspect(attr));
1967
+ }
1968
+ rb_str_buf_cat2(str, ">");
1969
+
1970
+ return str;
1971
+ }
1972
+
1973
+ /* Ruby Extension initializer */
1974
+ void
1975
+ Init_couchbase_ext(void)
1976
+ {
1977
+ mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
1978
+ mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
1979
+ mCouchbase = rb_define_module("Couchbase");
1980
+
1981
+ mError = rb_define_module_under(mCouchbase, "Error");
1982
+ /* Document-class: Couchbase::Error::Base
1983
+ * The base error class */
1984
+ eBaseError = rb_define_class_under(mError, "Base", rb_eRuntimeError);
1985
+ /* Document-class: Couchbase::Error::Auth
1986
+ * Authentication error */
1987
+ eAuthError = rb_define_class_under(mError, "Auth", eBaseError);
1988
+ /* Document-class: Couchbase::Error::Busy
1989
+ * The cluster is too busy now. Try again later */
1990
+ eBusyError = rb_define_class_under(mError, "Busy", eBaseError);
1991
+ /* Document-class: Couchbase::Error::DeltaBadval
1992
+ * The given value is not a number */
1993
+ eDeltaBadvalError = rb_define_class_under(mError, "DeltaBadval", eBaseError);
1994
+ /* Document-class: Couchbase::Error::Internal
1995
+ * Internal error */
1996
+ eInternalError = rb_define_class_under(mError, "Internal", eBaseError);
1997
+ /* Document-class: Couchbase::Error::Invalid
1998
+ * Invalid arguments */
1999
+ eInvalidError = rb_define_class_under(mError, "Invalid", eBaseError);
2000
+ /* Document-class: Couchbase::Error::KeyExists
2001
+ * Key already exists */
2002
+ eKeyExistsError = rb_define_class_under(mError, "KeyExists", eBaseError);
2003
+ /* Document-class: Couchbase::Error::Libcouchbase
2004
+ * Generic error */
2005
+ eLibcouchbaseError = rb_define_class_under(mError, "Libcouchbase", eBaseError);
2006
+ /* Document-class: Couchbase::Error::Libevent
2007
+ * Problem using libevent */
2008
+ eLibeventError = rb_define_class_under(mError, "Libevent", eBaseError);
2009
+ /* Document-class: Couchbase::Error::Network
2010
+ * Network error */
2011
+ eNetworkError = rb_define_class_under(mError, "Network", eBaseError);
2012
+ /* Document-class: Couchbase::Error::NoMemory
2013
+ * Out of memory error */
2014
+ eNoMemoryError = rb_define_class_under(mError, "NoMemory", eBaseError);
2015
+ /* Document-class: Couchbase::Error::NotFound
2016
+ * No such key */
2017
+ eNotFoundError = rb_define_class_under(mError, "NotFound", eBaseError);
2018
+ /* Document-class: Couchbase::Error::NotMyVbucket
2019
+ * The vbucket is not located on this server */
2020
+ eNotMyVbucketError = rb_define_class_under(mError, "NotMyVbucket", eBaseError);
2021
+ /* Document-class: Couchbase::Error::NotStored
2022
+ * Not stored */
2023
+ eNotStoredError = rb_define_class_under(mError, "NotStored", eBaseError);
2024
+ /* Document-class: Couchbase::Error::NotSupported
2025
+ * Not supported */
2026
+ eNotSupportedError = rb_define_class_under(mError, "NotSupported", eBaseError);
2027
+ /* Document-class: Couchbase::Error::Range
2028
+ * Invalid range */
2029
+ eRangeError = rb_define_class_under(mError, "Range", eBaseError);
2030
+ /* Document-class: Couchbase::Error::TemporaryFail
2031
+ * Temporary failure. Try again later */
2032
+ eTmpFailError = rb_define_class_under(mError, "TemporaryFail", eBaseError);
2033
+ /* Document-class: Couchbase::Error::TooBig
2034
+ * Object too big */
2035
+ eTooBigError = rb_define_class_under(mError, "TooBig", eBaseError);
2036
+ /* Document-class: Couchbase::Error::UnknownCommand
2037
+ * Unknown command */
2038
+ eUnknownCommandError = rb_define_class_under(mError, "UnknownCommand", eBaseError);
2039
+ /* Document-class: Couchbase::Error::UnknownHost
2040
+ * Unknown host */
2041
+ eUnknownHostError = rb_define_class_under(mError, "UnknownHost", eBaseError);
2042
+ /* Document-class: Couchbase::Error::ValueFormat
2043
+ * Failed to decode or encode value */
2044
+ eValueFormatError = rb_define_class_under(mError, "ValueFormat", eBaseError);
2045
+ /* Document-class: Couchbase::Error::Protocol
2046
+ * Protocol error */
2047
+ eProtocolError = rb_define_class_under(mError, "Protocol", eBaseError);
2048
+
2049
+ /* Document-method: error
2050
+ * @return [Boolean] the error code from libcouchbase */
2051
+ rb_define_attr(eBaseError, "error", 1, 0);
2052
+ id_iv_error = rb_intern("@error");
2053
+ /* Document-method: key
2054
+ * @return [String] the key which generated error */
2055
+ rb_define_attr(eBaseError, "key", 1, 0);
2056
+ id_iv_key = rb_intern("@key");
2057
+ /* Document-method: cas
2058
+ * @return [Fixnum] the version of the key (+nil+ unless accessible) */
2059
+ rb_define_attr(eBaseError, "cas", 1, 0);
2060
+ id_iv_cas = rb_intern("@cas");
2061
+ /* Document-method: operation
2062
+ * @return [Symbol] the operation (+nil+ unless accessible) */
2063
+ rb_define_attr(eBaseError, "operation", 1, 0);
2064
+ id_iv_operation = rb_intern("@operation");
2065
+
2066
+ /* Document-class: Couchbase::Result
2067
+ * Protocol error */
2068
+ cResult = rb_define_class_under(mCouchbase, "Result", rb_cObject);
2069
+ rb_define_method(cResult, "inspect", cb_result_inspect, 0);
2070
+ rb_define_method(cResult, "success?", cb_result_success_p, 0);
2071
+ /* Document-method: operation
2072
+ * @return [Symbol] */
2073
+ rb_define_attr(cResult, "operation", 1, 0);
2074
+ /* Document-method: error
2075
+ * @return [Error::Base] */
2076
+ rb_define_attr(cResult, "error", 1, 0);
2077
+ /* Document-method: key
2078
+ * @return [String] */
2079
+ rb_define_attr(cResult, "key", 1, 0);
2080
+ id_iv_key = rb_intern("@key");
2081
+ /* Document-method: value
2082
+ * @return [String] */
2083
+ rb_define_attr(cResult, "value", 1, 0);
2084
+ id_iv_value = rb_intern("@value");
2085
+ /* Document-method: cas
2086
+ * @return [Fixnum] */
2087
+ rb_define_attr(cResult, "cas", 1, 0);
2088
+ id_iv_cas = rb_intern("@cas");
2089
+ /* Document-method: flags
2090
+ * @return [Fixnum] */
2091
+ rb_define_attr(cResult, "flags", 1, 0);
2092
+ id_iv_flags = rb_intern("@flags");
2093
+ /* Document-method: node
2094
+ * @return [String] */
2095
+ rb_define_attr(cResult, "node", 1, 0);
2096
+ id_iv_node = rb_intern("@node");
2097
+
2098
+ /* Document-class: Couchbase::Bucket
2099
+ * This class in charge of all stuff connected to communication with
2100
+ * Couchbase. */
2101
+ cBucket = rb_define_class_under(mCouchbase, "Bucket", rb_cObject);
2102
+ object_space = rb_hash_new();
2103
+ rb_define_const(cBucket, "OBJECT_SPACE", object_space);
2104
+
2105
+ rb_define_const(cBucket, "FMT_MASK", INT2FIX(FMT_MASK));
2106
+ rb_define_const(cBucket, "FMT_DOCUMENT", INT2FIX(FMT_DOCUMENT));
2107
+ rb_define_const(cBucket, "FMT_MARSHAL", INT2FIX(FMT_MARSHAL));
2108
+ rb_define_const(cBucket, "FMT_PLAIN", INT2FIX(FMT_PLAIN));
2109
+
2110
+ rb_define_singleton_method(cBucket, "new", cb_bucket_new, -1);
2111
+
2112
+ rb_define_method(cBucket, "initialize", cb_bucket_init, -1);
2113
+ rb_define_method(cBucket, "inspect", cb_bucket_inspect, 0);
2114
+ rb_define_method(cBucket, "seqno", cb_bucket_seqno, 0);
2115
+
2116
+ rb_define_method(cBucket, "add", cb_bucket_add, -1);
2117
+ rb_define_method(cBucket, "append", cb_bucket_append, -1);
2118
+ rb_define_method(cBucket, "prepend", cb_bucket_prepend, -1);
2119
+ rb_define_method(cBucket, "replace", cb_bucket_replace, -1);
2120
+ rb_define_method(cBucket, "set", cb_bucket_set, -1);
2121
+ rb_define_method(cBucket, "get", cb_bucket_get, -1);
2122
+ rb_define_method(cBucket, "run", cb_bucket_run, 0);
2123
+ rb_define_method(cBucket, "touch", cb_bucket_touch, -1);
2124
+ rb_define_method(cBucket, "delete", cb_bucket_delete, -1);
2125
+ rb_define_method(cBucket, "stats", cb_bucket_stats, -1);
2126
+ rb_define_method(cBucket, "flush", cb_bucket_flush, 0);
2127
+ rb_define_method(cBucket, "incr", cb_bucket_incr, -1);
2128
+ rb_define_method(cBucket, "decr", cb_bucket_decr, -1);
2129
+
2130
+ rb_define_alias(cBucket, "decrement", "decr");
2131
+ rb_define_alias(cBucket, "increment", "incr");
2132
+
2133
+ rb_define_alias(cBucket, "[]", "get");
2134
+ /* rb_define_alias(cBucket, "[]=", "set"); */
2135
+ rb_define_method(cBucket, "[]=", cb_bucket_aset, -1);
2136
+
2137
+ /* Document-method: async?
2138
+ * Flag specifying if the connection asynchronous.
2139
+ *
2140
+ * By default all operations are synchronous and block waiting for
2141
+ * results, but you can make them asynchronous and run event loop
2142
+ * explicitly. (see Bucket#run)
2143
+ *
2144
+ * @example Return value of #get operation depending on async flag
2145
+ * connection = Connection.new
2146
+ * connection.async? #=> false
2147
+ *
2148
+ * connection.run do |conn|
2149
+ * conn.async? #=> true
2150
+ * end
2151
+ *
2152
+ * @return [Boolean] */
2153
+ rb_define_method(cBucket, "async?", cb_bucket_async_p, 0);
2154
+
2155
+ /* Document-method: quiet
2156
+ * Flag specifying behaviour for operations on missing keys
2157
+ *
2158
+ * If it is +true+, the operations will silently return +nil+ or +false+
2159
+ * instead of raising Couchbase::Error::NotFoundError.
2160
+ *
2161
+ * @example Hiding cache miss (considering "miss" key is not stored)
2162
+ * connection.quiet = true
2163
+ * connection.get("miss") #=> nil
2164
+ *
2165
+ * @example Raising errors on miss (considering "miss" key is not stored)
2166
+ * connection.quiet = false
2167
+ * connection.get("miss") #=> will raise Couchbase::Error::NotFoundError
2168
+ *
2169
+ * @return [Boolean] */
2170
+ rb_define_attr(cBucket, "quiet", 1, 1);
2171
+ rb_define_method(cBucket, "quiet=", cb_bucket_quiet_set, 1);
2172
+ rb_define_alias(cBucket, "quiet?", "quiet");
2173
+ id_iv_quiet = rb_intern("@quiet");
2174
+
2175
+ /* Document-method: default_flags
2176
+ * Default flags for new values.
2177
+ *
2178
+ * The library reserves last two lower bits to store the format of the
2179
+ * value. The can be masked via FMT_MASK constant.
2180
+ *
2181
+ * @example Selecting format bits
2182
+ * connection.default_flags & Couchbase::Bucket::FMT_MASK
2183
+ *
2184
+ * @example Set user defined bits
2185
+ * connection.default_flags |= 0x6660
2186
+ *
2187
+ * @note Amending format bit will also change #default_format value
2188
+ *
2189
+ * @return [Fixnum] the effective flags */
2190
+ rb_define_attr(cBucket, "default_flags", 1, 1);
2191
+ rb_define_method(cBucket, "default_flags=", cb_bucket_default_flags_set, 1);
2192
+ id_iv_default_flags = rb_intern("@default_flags");
2193
+
2194
+ /* Document-method: default_format
2195
+ * Default format for new values.
2196
+ *
2197
+ * It uses flags field to store the format. It accepts either the Symbol
2198
+ * (+:document+, +:marshal+, +:plain+) or Fixnum (use constants
2199
+ * FMT_DOCUMENT, FMT_MARSHAL, FMT_PLAIN) and silently ignores all
2200
+ * other value.
2201
+ *
2202
+ * Here is some notes regarding how to choose the format:
2203
+ *
2204
+ * * <tt>:document</tt> (default) format supports most of ruby types
2205
+ * which could be mapped to JSON data (hashes, arrays, string,
2206
+ * numbers). Future version will be able to run map/reduce queries on
2207
+ * the values in the document form (hashes).
2208
+ *
2209
+ * * <tt>:plain</tt> format if you no need any conversions to be applied
2210
+ * to your data, but your data should be passed as String. It could be
2211
+ * useful for building custom algorithms or formats. For example
2212
+ * implement set: http://dustin.github.com/2011/02/17/memcached-set.html
2213
+ *
2214
+ * * <tt>:marshal</tt> format if you'd like to transparently serialize
2215
+ * your ruby object with standard <tt>Marshal.dump</tt> and
2216
+ * <tt>Marhal.load</tt> methods.
2217
+ *
2218
+ * @example Selecting plain format using symbol
2219
+ * connection.format = :document
2220
+ *
2221
+ * @example Selecting plain format using Fixnum constant
2222
+ * connection.format = Couchbase::Bucket::FMT_PLAIN
2223
+ *
2224
+ * @note Amending default_format will also change #default_flags value
2225
+ *
2226
+ * @return [Symbol] the effective format */
2227
+ rb_define_attr(cBucket, "default_format", 1, 1);
2228
+ rb_define_method(cBucket, "default_format=", cb_bucket_default_format_set, 1);
2229
+ id_iv_default_format = rb_intern("@default_format");
2230
+ /* Document-method: on_error
2231
+ * Error callback for asynchronous mode.
2232
+ *
2233
+ * This callback is using to deliver exceptions in asynchronous mode.
2234
+ *
2235
+ * @yieldparam [Symbol] op The operation caused the error
2236
+ * @yieldparam [String] key The key which cause the error or +nil+
2237
+ * @yieldparam [Exception] exc The exception instance
2238
+ *
2239
+ * @example Using lambda syntax
2240
+ * connection = Couchbase.new(:async => true)
2241
+ * connection.on_error = lambda {|op, key, exc| ... }
2242
+ * connection.run do |conn|
2243
+ * conn.set("foo", "bar")
2244
+ * end
2245
+ *
2246
+ * @example Using block syntax
2247
+ * connection = Couchbase.new(:async => true)
2248
+ * connection.on_error {|op, key, exc| ... }
2249
+ * ...
2250
+ *
2251
+ * @return [Proc] the effective callback */
2252
+ rb_define_attr(cBucket, "on_error", 1, 1);
2253
+ rb_define_method(cBucket, "on_error", cb_bucket_on_error_get, 0);
2254
+ rb_define_method(cBucket, "on_error=", cb_bucket_on_error_set, 1);
2255
+ id_iv_on_error = rb_intern("@on_error");
2256
+
2257
+ /* Document-method: url
2258
+ * @return [String] the address of the cluster management interface. */
2259
+ rb_define_attr(cBucket, "url", 1, 0);
2260
+ id_iv_url = rb_intern("@url");
2261
+ /* Document-method: hostname
2262
+ * @return [String] the host name of the management interface (default: "localhost") */
2263
+ rb_define_attr(cBucket, "hostname", 1, 0);
2264
+ id_iv_hostname = rb_intern("@hostname");
2265
+ /* Document-method: port
2266
+ * @return [Fixnum] the port number of the management interface (default: 8091) */
2267
+ rb_define_attr(cBucket, "port", 1, 0);
2268
+ id_iv_port = rb_intern("@port");
2269
+ /* Document-method: authority
2270
+ * @return [String] host with port. */
2271
+ rb_define_attr(cBucket, "authority", 1, 0);
2272
+ id_iv_authority = rb_intern("@authority");
2273
+ /* Document-method: bucket
2274
+ * @return [String] the bucket name */
2275
+ rb_define_attr(cBucket, "bucket", 1, 0);
2276
+ rb_define_alias(cBucket, "name", "bucket");
2277
+ id_iv_bucket = rb_intern("@bucket");
2278
+ /* Document-method: pool
2279
+ * @return [String] the pool name (usually "default") */
2280
+ rb_define_attr(cBucket, "pool", 1, 0);
2281
+ id_iv_pool = rb_intern("@pool");
2282
+ /* Document-method: username
2283
+ * @return [String] the username for protected buckets (usually matches
2284
+ * the bucket name) */
2285
+ rb_define_attr(cBucket, "username", 1, 0);
2286
+ id_iv_username = rb_intern("@username");
2287
+ /* Document-method: password
2288
+ * @return [String] the password for protected buckets */
2289
+ rb_define_attr(cBucket, "password", 1, 0);
2290
+ id_iv_password = rb_intern("@password");
2291
+
2292
+ /* Define symbols */
2293
+ id_arity = rb_intern("arity");
2294
+ id_call = rb_intern("call");
2295
+ id_load = rb_intern("load");
2296
+ id_dump = rb_intern("dump");
2297
+ id_flatten_bang = rb_intern("flatten!");
2298
+ id_has_key_p = rb_intern("has_key?");
2299
+ id_to_s = rb_intern("to_s");
2300
+
2301
+ sym_add = ID2SYM(rb_intern("add"));
2302
+ sym_append = ID2SYM(rb_intern("append"));
2303
+ sym_bucket = ID2SYM(rb_intern("bucket"));
2304
+ sym_cas = ID2SYM(rb_intern("cas"));
2305
+ sym_create = ID2SYM(rb_intern("create"));
2306
+ sym_decrement = ID2SYM(rb_intern("decrement"));
2307
+ sym_default_flags = ID2SYM(rb_intern("default_flags"));
2308
+ sym_default_format = ID2SYM(rb_intern("default_format"));
2309
+ sym_delete = ID2SYM(rb_intern("delete"));
2310
+ sym_document = ID2SYM(rb_intern("document"));
2311
+ sym_extended = ID2SYM(rb_intern("extended"));
2312
+ sym_flags = ID2SYM(rb_intern("flags"));
2313
+ sym_flush = ID2SYM(rb_intern("flush"));
2314
+ sym_format = ID2SYM(rb_intern("format"));
2315
+ sym_get = ID2SYM(rb_intern("get"));
2316
+ sym_hostname = ID2SYM(rb_intern("hostname"));
2317
+ sym_increment = ID2SYM(rb_intern("increment"));
2318
+ sym_initial = ID2SYM(rb_intern("initial"));
2319
+ sym_marshal = ID2SYM(rb_intern("marshal"));
2320
+ sym_password = ID2SYM(rb_intern("password"));
2321
+ sym_plain = ID2SYM(rb_intern("plain"));
2322
+ sym_pool = ID2SYM(rb_intern("pool"));
2323
+ sym_port = ID2SYM(rb_intern("port"));
2324
+ sym_prepend = ID2SYM(rb_intern("prepend"));
2325
+ sym_quiet = ID2SYM(rb_intern("quiet"));
2326
+ sym_replace = ID2SYM(rb_intern("replace"));
2327
+ sym_set = ID2SYM(rb_intern("set"));
2328
+ sym_stats = ID2SYM(rb_intern("stats"));
2329
+ sym_touch = ID2SYM(rb_intern("touch"));
2330
+ sym_ttl = ID2SYM(rb_intern("ttl"));
2331
+ sym_username = ID2SYM(rb_intern("username"));
2332
+ }