couchbase 1.3.4-x64-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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +22 -0
  4. data/.yardopts +5 -0
  5. data/CONTRIBUTING.markdown +75 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +201 -0
  8. data/Makefile +3 -0
  9. data/README.markdown +649 -0
  10. data/RELEASE_NOTES.markdown +796 -0
  11. data/Rakefile +20 -0
  12. data/couchbase.gemspec +49 -0
  13. data/examples/chat-em/Gemfile +7 -0
  14. data/examples/chat-em/README.markdown +45 -0
  15. data/examples/chat-em/server.rb +82 -0
  16. data/examples/chat-goliath-grape/Gemfile +5 -0
  17. data/examples/chat-goliath-grape/README.markdown +50 -0
  18. data/examples/chat-goliath-grape/app.rb +67 -0
  19. data/examples/chat-goliath-grape/config/app.rb +20 -0
  20. data/examples/transcoders/Gemfile +3 -0
  21. data/examples/transcoders/README.markdown +59 -0
  22. data/examples/transcoders/cb-zcat +40 -0
  23. data/examples/transcoders/cb-zcp +45 -0
  24. data/examples/transcoders/gzip_transcoder.rb +49 -0
  25. data/examples/transcoders/options.rb +54 -0
  26. data/ext/couchbase_ext/.gitignore +4 -0
  27. data/ext/couchbase_ext/arguments.c +956 -0
  28. data/ext/couchbase_ext/arithmetic.c +307 -0
  29. data/ext/couchbase_ext/bucket.c +1370 -0
  30. data/ext/couchbase_ext/context.c +65 -0
  31. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  32. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  33. data/ext/couchbase_ext/delete.c +163 -0
  34. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  35. data/ext/couchbase_ext/extconf.rb +168 -0
  36. data/ext/couchbase_ext/get.c +316 -0
  37. data/ext/couchbase_ext/gethrtime.c +129 -0
  38. data/ext/couchbase_ext/http.c +432 -0
  39. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  40. data/ext/couchbase_ext/observe.c +171 -0
  41. data/ext/couchbase_ext/plugin_common.c +171 -0
  42. data/ext/couchbase_ext/result.c +129 -0
  43. data/ext/couchbase_ext/stats.c +163 -0
  44. data/ext/couchbase_ext/store.c +542 -0
  45. data/ext/couchbase_ext/timer.c +192 -0
  46. data/ext/couchbase_ext/touch.c +186 -0
  47. data/ext/couchbase_ext/unlock.c +176 -0
  48. data/ext/couchbase_ext/utils.c +551 -0
  49. data/ext/couchbase_ext/version.c +142 -0
  50. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  51. data/lib/active_support/cache/couchbase_store.rb +430 -0
  52. data/lib/couchbase.rb +155 -0
  53. data/lib/couchbase/bucket.rb +457 -0
  54. data/lib/couchbase/cluster.rb +119 -0
  55. data/lib/couchbase/connection_pool.rb +58 -0
  56. data/lib/couchbase/constants.rb +12 -0
  57. data/lib/couchbase/result.rb +26 -0
  58. data/lib/couchbase/transcoder.rb +120 -0
  59. data/lib/couchbase/utils.rb +62 -0
  60. data/lib/couchbase/version.rb +21 -0
  61. data/lib/couchbase/view.rb +506 -0
  62. data/lib/couchbase/view_row.rb +272 -0
  63. data/lib/ext/multi_json_fix.rb +56 -0
  64. data/lib/rack/session/couchbase.rb +108 -0
  65. data/tasks/benchmark.rake +6 -0
  66. data/tasks/compile.rake +158 -0
  67. data/tasks/test.rake +100 -0
  68. data/tasks/util.rake +21 -0
  69. data/test/profile/.gitignore +1 -0
  70. data/test/profile/Gemfile +6 -0
  71. data/test/profile/benchmark.rb +195 -0
  72. data/test/setup.rb +178 -0
  73. data/test/test_arithmetic.rb +185 -0
  74. data/test/test_async.rb +316 -0
  75. data/test/test_bucket.rb +250 -0
  76. data/test/test_cas.rb +235 -0
  77. data/test/test_couchbase.rb +77 -0
  78. data/test/test_couchbase_connection_pool.rb +77 -0
  79. data/test/test_couchbase_rails_cache_store.rb +361 -0
  80. data/test/test_delete.rb +120 -0
  81. data/test/test_errors.rb +82 -0
  82. data/test/test_eventmachine.rb +70 -0
  83. data/test/test_format.rb +164 -0
  84. data/test/test_get.rb +407 -0
  85. data/test/test_stats.rb +57 -0
  86. data/test/test_store.rb +216 -0
  87. data/test/test_timer.rb +42 -0
  88. data/test/test_touch.rb +97 -0
  89. data/test/test_unlock.rb +119 -0
  90. data/test/test_utils.rb +58 -0
  91. data/test/test_version.rb +52 -0
  92. metadata +336 -0
@@ -0,0 +1,307 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ cb_arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *resp)
22
+ {
23
+ struct cb_context_st *ctx = (struct cb_context_st *)cookie;
24
+ struct cb_bucket_st *bucket = ctx->bucket;
25
+ VALUE cas, key, val, exc, res;
26
+ ID o;
27
+
28
+ ctx->nqueries--;
29
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
30
+ cb_strip_key_prefix(bucket, key);
31
+
32
+ cas = resp->v.v0.cas > 0 ? ULL2NUM(resp->v.v0.cas) : Qnil;
33
+ o = ctx->arith > 0 ? cb_sym_increment : cb_sym_decrement;
34
+ exc = cb_check_error(error, "failed to perform arithmetic operation", key);
35
+ if (exc != Qnil) {
36
+ rb_ivar_set(exc, cb_id_iv_cas, cas);
37
+ rb_ivar_set(exc, cb_id_iv_operation, o);
38
+ ctx->exception = exc;
39
+ }
40
+ val = ULL2NUM(resp->v.v0.value);
41
+ if (bucket->async) { /* asynchronous */
42
+ if (ctx->proc != Qnil) {
43
+ res = rb_class_new_instance(0, NULL, cb_cResult);
44
+ rb_ivar_set(res, cb_id_iv_error, exc);
45
+ rb_ivar_set(res, cb_id_iv_operation, o);
46
+ rb_ivar_set(res, cb_id_iv_key, key);
47
+ rb_ivar_set(res, cb_id_iv_value, val);
48
+ rb_ivar_set(res, cb_id_iv_cas, cas);
49
+ cb_proc_call(bucket, ctx->proc, 1, res);
50
+ }
51
+ } else { /* synchronous */
52
+ if (NIL_P(exc)) {
53
+ if (ctx->extended) {
54
+ rb_hash_aset(ctx->rv, key, rb_ary_new3(2, val, cas));
55
+ } else {
56
+ rb_hash_aset(ctx->rv, key, val);
57
+ }
58
+ }
59
+ }
60
+ if (ctx->nqueries == 0) {
61
+ ctx->proc = Qnil;
62
+ if (bucket->async) {
63
+ cb_context_free(ctx);
64
+ }
65
+ }
66
+ (void)handle;
67
+ }
68
+
69
+ static inline VALUE
70
+ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
71
+ {
72
+ struct cb_bucket_st *bucket = DATA_PTR(self);
73
+ struct cb_context_st *ctx;
74
+ VALUE rv, proc, exc;
75
+ lcb_error_t err;
76
+ struct cb_params_st params;
77
+
78
+ if (!cb_bucket_connected_bang(bucket, sign > 0 ? cb_sym_increment : cb_sym_decrement)) {
79
+ return Qnil;
80
+ }
81
+
82
+ memset(&params, 0, sizeof(struct cb_params_st));
83
+ rb_scan_args(argc, argv, "0*&", &params.args, &proc);
84
+ if (!bucket->async && proc != Qnil) {
85
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
86
+ }
87
+ params.type = cb_cmd_arith;
88
+ params.bucket = bucket;
89
+ params.cmd.arith.sign = sign;
90
+ cb_params_build(&params);
91
+ ctx = cb_context_alloc_common(bucket, proc, params.cmd.arith.num);
92
+ ctx->extended = params.cmd.arith.extended;
93
+ err = lcb_arithmetic(bucket->handle, (const void *)ctx,
94
+ params.cmd.arith.num, params.cmd.arith.ptr);
95
+ cb_params_destroy(&params);
96
+ exc = cb_check_error(err, "failed to schedule arithmetic request", Qnil);
97
+ if (exc != Qnil) {
98
+ cb_context_free(ctx);
99
+ rb_exc_raise(exc);
100
+ }
101
+ bucket->nbytes += params.npayload;
102
+ if (bucket->async) {
103
+ cb_maybe_do_loop(bucket);
104
+ return Qnil;
105
+ } else {
106
+ if (ctx->nqueries > 0) {
107
+ /* we have some operations pending */
108
+ lcb_wait(bucket->handle);
109
+ }
110
+ exc = ctx->exception;
111
+ rv = ctx->rv;
112
+ cb_context_free(ctx);
113
+ if (exc != Qnil) {
114
+ rb_exc_raise(exc);
115
+ }
116
+ if (params.cmd.store.num > 1) {
117
+ return rv; /* return as a hash {key => cas, ...} */
118
+ } else {
119
+ VALUE vv = Qnil;
120
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
121
+ return vv;
122
+ }
123
+ return rv;
124
+ }
125
+ }
126
+
127
+ /*
128
+ * Increment the value of an existing numeric key
129
+ *
130
+ * @since 1.0.0
131
+ *
132
+ * The increment methods allow you to increase a given stored integer
133
+ * value. These are the incremental equivalent of the decrement operations
134
+ * and work on the same basis; updating the value of a key if it can be
135
+ * parsed to an integer. The update operation occurs on the server and is
136
+ * provided at the protocol level. This simplifies what would otherwise be a
137
+ * two-stage get and set operation.
138
+ *
139
+ * @note that server values stored and transmitted as unsigned numbers,
140
+ * therefore if you try to store negative number and then increment or
141
+ * decrement it will cause overflow. (see "Integer overflow" example
142
+ * below)
143
+ *
144
+ * @overload incr(key, delta = 1, options = {})
145
+ * @param key [String, Symbol] Key used to reference the value.
146
+ * @param delta [Fixnum] Integer (up to 64 bits) value to increment
147
+ * @param options [Hash] Options for operation.
148
+ * @option options [true, false] :create (false) If set to +true+, it will
149
+ * initialize the key with zero value and zero flags (use +:initial+
150
+ * option to set another initial value). Note: it won't increment the
151
+ * missing value.
152
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
153
+ * missing key initialization. This option imply +:create+ option is
154
+ * +true+.
155
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
156
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
157
+ * absolute times (from the epoch). This option ignored for existent
158
+ * keys.
159
+ * @option options [true, false] :extended (false) If set to +true+, the
160
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
161
+ * returns just value.
162
+ *
163
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
164
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
165
+ *
166
+ * @return [Fixnum] the actual value of the key.
167
+ *
168
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
169
+ * option isn't +true+.
170
+ *
171
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
172
+ * value
173
+ *
174
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
175
+ *
176
+ * @raise [ArgumentError] when passing the block in synchronous mode
177
+ *
178
+ * @example Increment key by one
179
+ * c.incr("foo")
180
+ *
181
+ * @example Increment key by 50
182
+ * c.incr("foo", 50)
183
+ *
184
+ * @example Increment key by one <b>OR</b> initialize with zero
185
+ * c.incr("foo", :create => true) #=> will return old+1 or 0
186
+ *
187
+ * @example Increment key by one <b>OR</b> initialize with three
188
+ * c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
189
+ *
190
+ * @example Increment key and get its CAS value
191
+ * val, cas = c.incr("foo", :extended => true)
192
+ *
193
+ * @example Integer overflow
194
+ * c.set("foo", -100)
195
+ * c.get("foo") #=> -100
196
+ * c.incr("foo") #=> 18446744073709551517
197
+ *
198
+ * @example Asynchronous invocation
199
+ * c.run do
200
+ * c.incr("foo") do |ret|
201
+ * ret.operation #=> :increment
202
+ * ret.success? #=> true
203
+ * ret.key #=> "foo"
204
+ * ret.value
205
+ * ret.cas
206
+ * end
207
+ * end
208
+ *
209
+ */
210
+ VALUE
211
+ cb_bucket_incr(int argc, VALUE *argv, VALUE self)
212
+ {
213
+ return cb_bucket_arithmetic(+1, argc, argv, self);
214
+ }
215
+
216
+ /*
217
+ * Decrement the value of an existing numeric key
218
+ *
219
+ * @since 1.0.0
220
+ *
221
+ * The decrement methods reduce the value of a given key if the
222
+ * corresponding value can be parsed to an integer value. These operations
223
+ * are provided at a protocol level to eliminate the need to get, update,
224
+ * and reset a simple integer value in the database. It supports the use of
225
+ * an explicit offset value that will be used to reduce the stored value in
226
+ * the database.
227
+ *
228
+ * @note that server values stored and transmitted as unsigned numbers,
229
+ * therefore if you try to decrement negative or zero key, you will always
230
+ * get zero.
231
+ *
232
+ * @overload decr(key, delta = 1, options = {})
233
+ * @param key [String, Symbol] Key used to reference the value.
234
+ * @param delta [Fixnum] Integer (up to 64 bits) value to decrement
235
+ * @param options [Hash] Options for operation.
236
+ * @option options [true, false] :create (false) If set to +true+, it will
237
+ * initialize the key with zero value and zero flags (use +:initial+
238
+ * option to set another initial value). Note: it won't decrement the
239
+ * missing value.
240
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
241
+ * missing key initialization. This option imply +:create+ option is
242
+ * +true+.
243
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
244
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
245
+ * absolute times (from the epoch). This option ignored for existent
246
+ * keys.
247
+ * @option options [true, false] :extended (false) If set to +true+, the
248
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
249
+ * returns just value.
250
+ *
251
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
252
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
253
+ *
254
+ * @return [Fixnum] the actual value of the key.
255
+ *
256
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
257
+ * option isn't +true+.
258
+ *
259
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
260
+ * value
261
+ *
262
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
263
+ *
264
+ * @raise [ArgumentError] when passing the block in synchronous mode
265
+ *
266
+ * @example Decrement key by one
267
+ * c.decr("foo")
268
+ *
269
+ * @example Decrement key by 50
270
+ * c.decr("foo", 50)
271
+ *
272
+ * @example Decrement key by one <b>OR</b> initialize with zero
273
+ * c.decr("foo", :create => true) #=> will return old-1 or 0
274
+ *
275
+ * @example Decrement key by one <b>OR</b> initialize with three
276
+ * c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
277
+ *
278
+ * @example Decrement key and get its CAS value
279
+ * val, cas = c.decr("foo", :extended => true)
280
+ *
281
+ * @example Decrementing zero
282
+ * c.set("foo", 0)
283
+ * c.decrement("foo", 100500) #=> 0
284
+ *
285
+ * @example Decrementing negative value
286
+ * c.set("foo", -100)
287
+ * c.decrement("foo", 100500) #=> 0
288
+ *
289
+ * @example Asynchronous invocation
290
+ * c.run do
291
+ * c.decr("foo") do |ret|
292
+ * ret.operation #=> :decrement
293
+ * ret.success? #=> true
294
+ * ret.key #=> "foo"
295
+ * ret.value
296
+ * ret.cas
297
+ * end
298
+ * end
299
+ *
300
+ */
301
+ VALUE
302
+ cb_bucket_decr(int argc, VALUE *argv, VALUE self)
303
+ {
304
+ return cb_bucket_arithmetic(-1, argc, argv, self);
305
+ }
306
+
307
+
@@ -0,0 +1,1370 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ static VALUE
21
+ trigger_on_connect_callback(VALUE self)
22
+ {
23
+ struct cb_bucket_st *bucket = DATA_PTR(self);
24
+ VALUE on_connect_proc = bucket->on_connect_proc;
25
+ if (RTEST(on_connect_proc)) {
26
+ VALUE res = rb_class_new_instance(0, NULL, cb_cResult);
27
+ rb_ivar_set(res, cb_id_iv_error, bucket->exception);
28
+ bucket->exception = Qnil;
29
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_connect);
30
+ rb_ivar_set(res, cb_id_iv_value, self);
31
+ return rb_funcall(on_connect_proc, cb_id_call, 1, res);
32
+ } else {
33
+ bucket->trigger_connect_cb_on_set = 1;
34
+ return Qnil;
35
+ }
36
+ }
37
+
38
+ static void
39
+ error_callback(lcb_t handle, lcb_error_t error, const char *errinfo)
40
+ {
41
+ struct cb_bucket_st *bucket = (struct cb_bucket_st *)lcb_get_cookie(handle);
42
+
43
+ lcb_breakout(handle);
44
+ bucket->exception = cb_check_error(error, errinfo, Qnil);
45
+ if (bucket->async && !bucket->connected) {
46
+ (void)trigger_on_connect_callback(bucket->self);
47
+ }
48
+ }
49
+
50
+ static void
51
+ configuration_callback(lcb_t handle, lcb_configuration_t config)
52
+ {
53
+ struct cb_bucket_st *bucket = (struct cb_bucket_st *)lcb_get_cookie(handle);
54
+
55
+ if (config == LCB_CONFIGURATION_NEW) {
56
+ bucket->connected = 1;
57
+ (void)trigger_on_connect_callback(bucket->self);
58
+ }
59
+ }
60
+
61
+ void
62
+ cb_bucket_free(void *ptr)
63
+ {
64
+ struct cb_bucket_st *bucket = ptr;
65
+
66
+ if (bucket) {
67
+ bucket->destroying = 1;
68
+ if (bucket->handle) {
69
+ lcb_destroy(bucket->handle);
70
+ lcb_destroy_io_ops(bucket->io);
71
+ }
72
+ st_free_table(bucket->object_space);
73
+ xfree(bucket);
74
+ }
75
+ }
76
+
77
+ static int
78
+ cb_bucket_mark_object_i(st_index_t key, st_data_t value, st_data_t arg)
79
+ {
80
+ ((mark_f)value)((void*)key, (struct cb_bucket_st*)arg);
81
+ return ST_CONTINUE;
82
+ }
83
+
84
+ void
85
+ cb_bucket_mark(void *ptr)
86
+ {
87
+ struct cb_bucket_st *bucket = ptr;
88
+
89
+ if (bucket) {
90
+ rb_gc_mark(bucket->authority);
91
+ rb_gc_mark(bucket->hostname);
92
+ rb_gc_mark(bucket->pool);
93
+ rb_gc_mark(bucket->bucket);
94
+ rb_gc_mark(bucket->username);
95
+ rb_gc_mark(bucket->password);
96
+ rb_gc_mark(bucket->exception);
97
+ rb_gc_mark(bucket->on_error_proc);
98
+ rb_gc_mark(bucket->on_connect_proc);
99
+ rb_gc_mark(bucket->key_prefix_val);
100
+ if (bucket->object_space) {
101
+ st_foreach(bucket->object_space, cb_bucket_mark_object_i, (st_data_t)bucket);
102
+ }
103
+ }
104
+ }
105
+
106
+ static void
107
+ do_scan_connection_options(struct cb_bucket_st *bucket, int argc, VALUE *argv)
108
+ {
109
+ VALUE uri, opts, arg;
110
+ char port_s[8];
111
+
112
+ if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
113
+ if (TYPE(uri) == T_HASH && argc == 1) {
114
+ opts = uri;
115
+ uri = Qnil;
116
+ }
117
+ if (uri != Qnil) {
118
+ const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
119
+ VALUE match, uri_obj, re;
120
+
121
+ Check_Type(uri, T_STRING);
122
+ uri_obj = rb_funcall(cb_mURI, cb_id_parse, 1, uri);
123
+
124
+ arg = rb_funcall(uri_obj, cb_id_scheme, 0);
125
+ if (arg == Qnil || rb_str_cmp(arg, STR_NEW_CSTR("http"))) {
126
+ rb_raise(rb_eArgError, "invalid URI: invalid scheme");
127
+ }
128
+
129
+ arg = rb_funcall(uri_obj, cb_id_user, 0);
130
+ if (arg != Qnil) {
131
+ bucket->username = rb_str_dup_frozen(StringValue(arg));
132
+ }
133
+
134
+ arg = rb_funcall(uri_obj, cb_id_password, 0);
135
+ if (arg != Qnil) {
136
+ bucket->password = rb_str_dup_frozen(StringValue(arg));
137
+ }
138
+ arg = rb_funcall(uri_obj, cb_id_host, 0);
139
+ if (arg != Qnil) {
140
+ bucket->hostname = rb_str_dup_frozen(StringValue(arg));
141
+ } else {
142
+ rb_raise(rb_eArgError, "invalid URI: missing hostname");
143
+ }
144
+
145
+ arg = rb_funcall(uri_obj, cb_id_port, 0);
146
+ bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
147
+
148
+ arg = rb_funcall(uri_obj, cb_id_path, 0);
149
+ re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
150
+ match = rb_funcall(re, cb_id_match, 1, arg);
151
+ arg = rb_reg_nth_match(2, match);
152
+ bucket->pool = NIL_P(arg) ? cb_vStrDefault : rb_str_dup_frozen(StringValue(arg));
153
+ rb_str_freeze(bucket->pool);
154
+ arg = rb_reg_nth_match(4, match);
155
+ bucket->bucket = NIL_P(arg) ? cb_vStrDefault : rb_str_dup_frozen(StringValue(arg));
156
+ rb_str_freeze(bucket->bucket);
157
+ }
158
+ if (TYPE(opts) == T_HASH) {
159
+ arg = rb_hash_aref(opts, cb_sym_type);
160
+ if (arg != Qnil) {
161
+ if (arg == cb_sym_cluster) {
162
+ bucket->type = LCB_TYPE_CLUSTER;
163
+ } else {
164
+ bucket->type = LCB_TYPE_BUCKET;
165
+ }
166
+ }
167
+ arg = rb_hash_aref(opts, cb_sym_node_list);
168
+ if (arg != Qnil) {
169
+ Check_Type(arg, T_ARRAY);
170
+ bucket->node_list = rb_ary_join(arg, STR_NEW_CSTR(";"));
171
+ rb_str_freeze(bucket->node_list);
172
+ }
173
+ arg = rb_hash_aref(opts, cb_sym_hostname);
174
+ if (arg != Qnil) {
175
+ bucket->hostname = rb_str_dup_frozen(StringValue(arg));
176
+ }
177
+ arg = rb_hash_aref(opts, cb_sym_pool);
178
+ if (arg != Qnil) {
179
+ bucket->pool = rb_str_dup_frozen(StringValue(arg));
180
+ }
181
+ arg = rb_hash_aref(opts, cb_sym_bucket);
182
+ if (arg != Qnil) {
183
+ bucket->bucket = rb_str_dup_frozen(StringValue(arg));
184
+ }
185
+ arg = rb_hash_aref(opts, cb_sym_username);
186
+ if (arg != Qnil) {
187
+ bucket->username = rb_str_dup_frozen(StringValue(arg));
188
+ }
189
+ arg = rb_hash_aref(opts, cb_sym_password);
190
+ if (arg != Qnil) {
191
+ bucket->password = rb_str_dup_frozen(StringValue(arg));
192
+ }
193
+ arg = rb_hash_aref(opts, cb_sym_port);
194
+ if (arg != Qnil) {
195
+ bucket->port = (uint16_t)NUM2UINT(arg);
196
+ }
197
+ arg = rb_hash_lookup2(opts, cb_sym_quiet, Qundef);
198
+ if (arg != Qundef) {
199
+ bucket->quiet = RTEST(arg);
200
+ }
201
+ arg = rb_hash_aref(opts, cb_sym_timeout);
202
+ if (arg != Qnil) {
203
+ bucket->timeout = (uint32_t)NUM2ULONG(arg);
204
+ }
205
+ arg = rb_hash_aref(opts, cb_sym_default_ttl);
206
+ if (arg != Qnil) {
207
+ bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
208
+ }
209
+ arg = rb_hash_aref(opts, cb_sym_default_observe_timeout);
210
+ if (arg != Qnil) {
211
+ bucket->default_observe_timeout = (uint32_t)NUM2ULONG(arg);
212
+ }
213
+ arg = rb_hash_aref(opts, cb_sym_default_flags);
214
+ if (arg != Qnil) {
215
+ bucket->default_flags = (uint32_t)NUM2ULONG(arg);
216
+ }
217
+ arg = rb_hash_aref(opts, cb_sym_default_format);
218
+ if (arg != Qnil) {
219
+ if (TYPE(arg) == T_FIXNUM) {
220
+ rb_warn("numeric argument to :default_format option is deprecated, use symbol");
221
+ switch (FIX2INT(arg)) {
222
+ case CB_FMT_DOCUMENT:
223
+ arg = cb_sym_document;
224
+ break;
225
+ case CB_FMT_MARSHAL:
226
+ arg = cb_sym_marshal;
227
+ break;
228
+ case CB_FMT_PLAIN:
229
+ arg = cb_sym_plain;
230
+ break;
231
+ }
232
+ }
233
+ if (arg == cb_sym_document) {
234
+ cb_bucket_transcoder_set(bucket->self, cb_mDocument);
235
+ } else if (arg == cb_sym_marshal) {
236
+ cb_bucket_transcoder_set(bucket->self, cb_mMarshal);
237
+ } else if (arg == cb_sym_plain) {
238
+ cb_bucket_transcoder_set(bucket->self, cb_mPlain);
239
+ }
240
+ }
241
+ arg = rb_hash_lookup2(opts, cb_sym_transcoder, Qundef);
242
+ if (arg != Qundef) {
243
+ cb_bucket_transcoder_set(bucket->self, arg);
244
+ }
245
+ if (arg != Qnil) {
246
+ if (arg == cb_sym_production || arg == cb_sym_development) {
247
+ bucket->environment = arg;
248
+ }
249
+ }
250
+ arg = rb_hash_aref(opts, cb_sym_key_prefix);
251
+ if (arg != Qnil) {
252
+ bucket->key_prefix_val = rb_str_dup_frozen(StringValue(arg));
253
+ }
254
+ arg = rb_hash_aref(opts, cb_sym_default_arithmetic_init);
255
+ if (arg != Qnil) {
256
+ bucket->default_arith_create = RTEST(arg);
257
+ if (TYPE(arg) == T_FIXNUM) {
258
+ bucket->default_arith_init = NUM2ULL(arg);
259
+ }
260
+ }
261
+ arg = rb_hash_aref(opts, cb_sym_engine);
262
+ if (arg != Qnil) {
263
+ if (arg == cb_sym_default) {
264
+ bucket->engine = cb_sym_default;
265
+ } else if (arg == cb_sym_select) {
266
+ bucket->engine = cb_sym_select;
267
+ #ifdef _WIN32
268
+ } else if (arg == cb_sym_iocp) {
269
+ bucket->engine = cb_sym_iocp;
270
+ #else
271
+ } else if (arg == cb_sym_libev) {
272
+ bucket->engine = cb_sym_libev;
273
+ } else if (arg == cb_sym_libevent) {
274
+ bucket->engine = cb_sym_libevent;
275
+ #ifdef BUILD_EVENTMACHINE_PLUGIN
276
+ } else if (arg == cb_sym_eventmachine) {
277
+ bucket->engine = cb_sym_eventmachine;
278
+ #endif
279
+ #endif
280
+ } else {
281
+ VALUE ins = rb_funcall(arg, rb_intern("inspect"), 0);
282
+ rb_raise(rb_eArgError, "Couchbase: unknown engine %s", RSTRING_PTR(ins));
283
+ }
284
+ }
285
+ bucket->async = RTEST(rb_hash_aref(opts, cb_sym_async));
286
+ arg = rb_hash_aref(opts, cb_sym_transcoder);
287
+ if (arg != Qnil) {
288
+ bucket->default_arith_create = RTEST(arg);
289
+ if (TYPE(arg) == T_FIXNUM) {
290
+ bucket->default_arith_init = NUM2ULL(arg);
291
+ }
292
+ }
293
+ } else {
294
+ opts = Qnil;
295
+ }
296
+ }
297
+ if (RTEST(bucket->password) && !RTEST(bucket->username)) {
298
+ bucket->username = bucket->bucket;
299
+ }
300
+ if (bucket->default_observe_timeout < 2) {
301
+ rb_raise(rb_eArgError, "default_observe_timeout is too low");
302
+ }
303
+ snprintf(port_s, sizeof(port_s), ":%u", bucket->port);
304
+ bucket->authority = rb_str_dup(bucket->hostname);
305
+ rb_str_cat2(bucket->authority, port_s);
306
+ rb_str_freeze(bucket->authority);
307
+ }
308
+
309
+ static VALUE
310
+ em_disconnect_block(VALUE unused, VALUE self)
311
+ {
312
+ struct cb_bucket_st *bucket = DATA_PTR(self);
313
+ if (bucket->handle) {
314
+ return cb_bucket_disconnect(self);
315
+ }
316
+ (void)unused;
317
+ return Qnil;
318
+ }
319
+
320
+ static void
321
+ do_connect(struct cb_bucket_st *bucket)
322
+ {
323
+ lcb_error_t err;
324
+ struct lcb_create_st create_opts;
325
+
326
+ if (bucket->handle) {
327
+ cb_bucket_disconnect(bucket->self);
328
+ }
329
+
330
+ {
331
+ struct lcb_create_io_ops_st ciops;
332
+ memset(&ciops, 0, sizeof(ciops));
333
+ ciops.version = 0;
334
+
335
+ if (bucket->engine == cb_sym_libevent) {
336
+ ciops.v.v0.type = LCB_IO_OPS_LIBEVENT;
337
+ } else if (bucket->engine == cb_sym_select) {
338
+ ciops.v.v0.type = LCB_IO_OPS_SELECT;
339
+ #ifdef _WIN32
340
+ } else if (bucket->engine == cb_sym_iocp) {
341
+ ciops.v.v0.type = LCB_IO_OPS_WINIOCP;
342
+ #endif
343
+ } else if (bucket->engine == cb_sym_libev) {
344
+ ciops.v.v0.type = LCB_IO_OPS_LIBEV;
345
+ } else if (bucket->engine == cb_sym_eventmachine) {
346
+ ciops.version = 1;
347
+ ciops.v.v1.sofile = NULL;
348
+ ciops.v.v1.symbol = "cb_create_ruby_em_io_opts";
349
+ ciops.v.v1.cookie = bucket;
350
+ } else {
351
+ #ifdef _WIN32
352
+ ciops.v.v0.type = LCB_IO_OPS_DEFAULT;
353
+ #else
354
+ ciops.version = 1;
355
+ ciops.v.v1.sofile = NULL;
356
+ ciops.v.v1.symbol = "cb_create_ruby_mt_io_opts";
357
+ ciops.v.v1.cookie = NULL;
358
+ #endif
359
+ }
360
+ err = lcb_create_io_ops(&bucket->io, &ciops);
361
+ if (err != LCB_SUCCESS) {
362
+ rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
363
+ }
364
+ }
365
+
366
+ memset(&create_opts, 0, sizeof(struct lcb_create_st));
367
+ create_opts.version = 1;
368
+ create_opts.v.v1.type = bucket->type;
369
+ create_opts.v.v1.host = RTEST(bucket->node_list) ? RSTRING_PTR(bucket-> node_list) : RSTRING_PTR(bucket->authority);
370
+ create_opts.v.v1.user = RTEST(bucket->username) ? RSTRING_PTR(bucket->username) : NULL;
371
+ create_opts.v.v1.passwd = RTEST(bucket->password) ? RSTRING_PTR(bucket->password) : NULL;
372
+ create_opts.v.v1.bucket = RSTRING_PTR(bucket->bucket);
373
+ create_opts.v.v1.io = bucket->io;
374
+ err = lcb_create(&bucket->handle, &create_opts);
375
+ if (err != LCB_SUCCESS) {
376
+ bucket->handle = NULL;
377
+ rb_exc_raise(cb_check_error(err, "failed to create libcouchbase instance", Qnil));
378
+ }
379
+ lcb_set_cookie(bucket->handle, bucket);
380
+ (void)lcb_set_error_callback(bucket->handle, error_callback);
381
+ (void)lcb_set_store_callback(bucket->handle, cb_storage_callback);
382
+ (void)lcb_set_get_callback(bucket->handle, cb_get_callback);
383
+ (void)lcb_set_touch_callback(bucket->handle, cb_touch_callback);
384
+ (void)lcb_set_remove_callback(bucket->handle, cb_delete_callback);
385
+ (void)lcb_set_stat_callback(bucket->handle, cb_stat_callback);
386
+ (void)lcb_set_arithmetic_callback(bucket->handle, cb_arithmetic_callback);
387
+ (void)lcb_set_version_callback(bucket->handle, cb_version_callback);
388
+ (void)lcb_set_http_complete_callback(bucket->handle, cb_http_complete_callback);
389
+ (void)lcb_set_http_data_callback(bucket->handle, cb_http_data_callback);
390
+ (void)lcb_set_observe_callback(bucket->handle, cb_observe_callback);
391
+ (void)lcb_set_unlock_callback(bucket->handle, cb_unlock_callback);
392
+ (void)lcb_set_configuration_callback(bucket->handle, configuration_callback);
393
+
394
+ if (bucket->timeout > 0) {
395
+ lcb_set_timeout(bucket->handle, bucket->timeout);
396
+ } else {
397
+ bucket->timeout = lcb_get_timeout(bucket->handle);
398
+ }
399
+ err = lcb_connect(bucket->handle);
400
+ if (err != LCB_SUCCESS) {
401
+ cb_bucket_disconnect(bucket->self);
402
+ rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
403
+ }
404
+ bucket->exception = Qnil;
405
+ if (bucket->engine == cb_sym_eventmachine && !bucket->async_disconnect_hook_set) {
406
+ bucket->async_disconnect_hook_set = 1;
407
+ rb_block_call(em_m, cb_id_add_shutdown_hook, 0, NULL, em_disconnect_block, bucket->self);
408
+ }
409
+ if (!bucket->async) {
410
+ lcb_wait(bucket->handle);
411
+ if (bucket->exception != Qnil) {
412
+ cb_bucket_disconnect(bucket->self);
413
+ rb_exc_raise(bucket->exception);
414
+ }
415
+ }
416
+ }
417
+
418
+ VALUE
419
+ cb_bucket_alloc(VALUE klass)
420
+ {
421
+ VALUE obj;
422
+ struct cb_bucket_st *bucket;
423
+
424
+ /* allocate new bucket struct and set it to zero */
425
+ obj = Data_Make_Struct(klass, struct cb_bucket_st, cb_bucket_mark, cb_bucket_free,
426
+ bucket);
427
+ return obj;
428
+ }
429
+
430
+ /*
431
+ * Initialize new Bucket.
432
+ *
433
+ * @since 1.0.0
434
+ *
435
+ * @overload initialize(url, options = {})
436
+ * Initialize bucket using URI of the cluster and options. It is possible
437
+ * to override some parts of URI using the options keys (e.g. :host or
438
+ * :port)
439
+ *
440
+ * @param [String] url The full URL of management API of the cluster.
441
+ * @param [Hash] options The options for connection. See options definition
442
+ * below.
443
+ *
444
+ * @overload initialize(options = {})
445
+ * Initialize bucket using options only.
446
+ *
447
+ * @param [Hash] options The options for operation for connection
448
+ * @option options [Array] :node_list (nil) the list of nodes to connect
449
+ * to. If specified it takes precedence over +:host+ option. The list
450
+ * must be array of strings in form of host names or host names with
451
+ * ports (in first case port 8091 will be used, see examples).
452
+ * @option options [String] :host ("localhost") the hostname or IP address
453
+ * of the node
454
+ * @option options [Fixnum] :port (8091) the port of the managemenent API
455
+ * @option options [String] :pool ("default") the pool name
456
+ * @option options [String] :bucket ("default") the bucket name
457
+ * @option options [Fixnum] :default_ttl (0) the TTL used by default during
458
+ * storing key-value pairs.
459
+ * @option options [Fixnum] :default_flags (0) the default flags.
460
+ * @option options [Symbol] :default_format (:document) the format, which
461
+ * will be used for values by default. Note that changing format will
462
+ * amend flags. (see {Bucket#default_format})
463
+ * @option options [String] :username (nil) the user name to connect to the
464
+ * cluster. Used to authenticate on management API. The username could
465
+ * be skipped for protected buckets, the bucket name will be used
466
+ * instead.
467
+ * @option options [String] :password (nil) the password of the user.
468
+ * @option options [true, false] :quiet (false) the flag controlling if raising
469
+ * exception when the client executes operations on non-existent keys. If it
470
+ * is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
471
+ * default behaviour is to return +nil+ value silently (might be useful in
472
+ * Rails cache).
473
+ * @option options [Symbol] :environment (:production) the mode of the
474
+ * connection. Currently it influences only on design documents set. If
475
+ * the environment is +:development+, you will able to get design
476
+ * documents with 'dev_' prefix, otherwise (in +:production+ mode) the
477
+ * library will hide them from you.
478
+ * @option options [String] :key_prefix (nil) the prefix string which will
479
+ * be prepended to each key before sending out, and sripped before
480
+ * returning back to the application.
481
+ * @option options [Fixnum] :timeout (2500000) the timeout for IO
482
+ * operations (in microseconds)
483
+ * @option options [Fixnum, true] :default_arithmetic_init (0) the default
484
+ * initial value for arithmetic operations. Setting this option to any
485
+ * non positive number forces creation missing keys with given default
486
+ * value. Setting it to +true+ will use zero as initial value. (see
487
+ * {Bucket#incr} and {Bucket#decr}).
488
+ * @option options [Symbol] :engine (:default) the IO engine to use
489
+ * Currently following engines are supported:
490
+ * :default :: Built-in engine (multi-thread friendly)
491
+ * :select :: select(2) IO plugin from libcouchbase
492
+ * :iocp :: "I/O Completion Ports" plugin from libcouchbase (windows only)
493
+ * :libevent :: libevent IO plugin from libcouchbase (optional)
494
+ * :libev :: libev IO plugin from libcouchbase (optional)
495
+ * :eventmachine :: EventMachine plugin (builtin, but requires EM gem and ruby 1.9+)
496
+ * @option options [true, false] :async (false) If true, the
497
+ * connection instance will be considered always asynchronous and
498
+ * IO interaction will be occured only when {Couchbase::Bucket#run}
499
+ * called. See {Couchbase::Bucket#on_connect} to hook your code
500
+ * after the instance will be connected.
501
+ *
502
+ * @example Initialize connection using default options
503
+ * Couchbase.new
504
+ *
505
+ * @example Select custom bucket
506
+ * Couchbase.new(:bucket => 'foo')
507
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
508
+ *
509
+ * @example Connect to protected bucket
510
+ * Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
511
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
512
+ * :username => 'protected', :password => 'secret')
513
+ *
514
+ * @example Use list of nodes, in case some nodes might be dead
515
+ * Couchbase.new(:node_list => ['example.com:8091', 'example.org:8091', 'example.net'])
516
+ *
517
+ * @raise [Couchbase::Error::BucketNotFound] if there is no such bucket to
518
+ * connect to
519
+ *
520
+ * @raise [Couchbase::Error::Connect] if the socket wasn't accessible
521
+ * (doesn't accept connections or doesn't respond in time)
522
+ *
523
+ * @return [Bucket]
524
+ */
525
+ VALUE
526
+ cb_bucket_init(int argc, VALUE *argv, VALUE self)
527
+ {
528
+ struct cb_bucket_st *bucket = DATA_PTR(self);
529
+ bucket->self = self;
530
+ bucket->exception = Qnil;
531
+ bucket->type = LCB_TYPE_BUCKET;
532
+ bucket->hostname = cb_vStrLocalhost;
533
+ bucket->port = 8091;
534
+ bucket->pool = cb_vStrDefault;
535
+ bucket->bucket = cb_vStrDefault;
536
+ bucket->username = Qnil;
537
+ bucket->password = Qnil;
538
+ bucket->engine = cb_sym_default;
539
+ bucket->async = 0;
540
+ bucket->quiet = 0;
541
+ bucket->default_ttl = 0;
542
+ bucket->default_flags = 0;
543
+ cb_bucket_transcoder_set(self, cb_mDocument);
544
+ bucket->default_observe_timeout = 2500000;
545
+ bucket->on_error_proc = Qnil;
546
+ bucket->on_connect_proc = Qnil;
547
+ bucket->timeout = 0;
548
+ bucket->environment = cb_sym_production;
549
+ bucket->key_prefix_val = Qnil;
550
+ bucket->node_list = Qnil;
551
+ bucket->object_space = st_init_numtable();
552
+ bucket->destroying = 0;
553
+ bucket->connected = 0;
554
+ bucket->on_connect_proc = Qnil;
555
+ bucket->async_disconnect_hook_set = 0;
556
+
557
+ do_scan_connection_options(bucket, argc, argv);
558
+ do_connect(bucket);
559
+
560
+ return self;
561
+ }
562
+
563
+ /*
564
+ * Initialize copy
565
+ *
566
+ * Initializes copy of the object, used by {Couchbase::Bucket#dup}
567
+ *
568
+ * @param orig [Couchbase::Bucket] the source for copy
569
+ *
570
+ * @return [Couchbase::Bucket]
571
+ */
572
+ VALUE
573
+ cb_bucket_init_copy(VALUE copy, VALUE orig)
574
+ {
575
+ struct cb_bucket_st *copy_b;
576
+ struct cb_bucket_st *orig_b;
577
+
578
+ if (copy == orig)
579
+ return copy;
580
+
581
+ if (TYPE(orig) != T_DATA || TYPE(copy) != T_DATA ||
582
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)cb_bucket_free) {
583
+ rb_raise(rb_eTypeError, "wrong argument type");
584
+ }
585
+
586
+ copy_b = DATA_PTR(copy);
587
+ orig_b = DATA_PTR(orig);
588
+
589
+ copy_b->self = copy_b->self;
590
+ copy_b->port = orig_b->port;
591
+ copy_b->authority = orig_b->authority;
592
+ copy_b->hostname = orig_b->hostname;
593
+ copy_b->pool = orig_b->pool;
594
+ copy_b->bucket = orig_b->bucket;
595
+ copy_b->username = orig_b->username;
596
+ copy_b->password = orig_b->password;
597
+ copy_b->engine = orig_b->engine;
598
+ copy_b->async = orig_b->async;
599
+ copy_b->quiet = orig_b->quiet;
600
+ copy_b->transcoder = orig_b->transcoder;
601
+ copy_b->default_flags = orig_b->default_flags;
602
+ copy_b->default_ttl = orig_b->default_ttl;
603
+ copy_b->environment = orig_b->environment;
604
+ copy_b->timeout = orig_b->timeout;
605
+ copy_b->exception = Qnil;
606
+ copy_b->async_disconnect_hook_set = 0;
607
+ if (orig_b->on_error_proc != Qnil) {
608
+ copy_b->on_error_proc = rb_funcall(orig_b->on_error_proc, cb_id_dup, 0);
609
+ }
610
+ if (orig_b->on_connect_proc != Qnil) {
611
+ copy_b->on_connect_proc = rb_funcall(orig_b->on_connect_proc, cb_id_dup, 0);
612
+ }
613
+ copy_b->key_prefix_val = orig_b->key_prefix_val;
614
+ copy_b->object_space = st_init_numtable();
615
+ copy_b->destroying = 0;
616
+ copy_b->connected = 0;
617
+
618
+ do_connect(copy_b);
619
+
620
+ return copy;
621
+ }
622
+
623
+ /*
624
+ * Reconnect the bucket
625
+ *
626
+ * @since 1.1.0
627
+ *
628
+ * Reconnect the bucket using initial configuration with optional
629
+ * redefinition.
630
+ *
631
+ * @overload reconnect(url, options = {})
632
+ * see {Bucket#initialize Bucket#initialize(url, options)}
633
+ * @return [Couchbase::Bucket]
634
+ *
635
+ * @overload reconnect(options = {})
636
+ * see {Bucket#initialize Bucket#initialize(options)}
637
+ * @return [Couchbase::Bucket]
638
+ *
639
+ * @example reconnect with current parameters
640
+ * c.reconnect
641
+ *
642
+ * @example reconnect the instance to another bucket
643
+ * c.reconnect(:bucket => 'new')
644
+ */
645
+ VALUE
646
+ cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
647
+ {
648
+ struct cb_bucket_st *bucket = DATA_PTR(self);
649
+
650
+ do_scan_connection_options(bucket, argc, argv);
651
+ do_connect(bucket);
652
+
653
+ return self;
654
+ }
655
+
656
+ /* Document-method: connected?
657
+ * Check whether the instance connected to the cluster.
658
+ *
659
+ * @since 1.1.0
660
+ *
661
+ * @return [true, false] +true+ if the instance connected to the cluster
662
+ */
663
+ VALUE
664
+ cb_bucket_connected_p(VALUE self)
665
+ {
666
+ struct cb_bucket_st *bucket = DATA_PTR(self);
667
+ return (bucket->handle && bucket->connected) ? Qtrue : Qfalse;
668
+ }
669
+
670
+ /* Document-method: async?
671
+ * Check whether the connection asynchronous.
672
+ *
673
+ * @since 1.0.0
674
+ *
675
+ * By default all operations are synchronous and block waiting for
676
+ * results, but you can make them asynchronous and run event loop
677
+ * explicitly. (see {Bucket#run})
678
+ *
679
+ * @example Return value of #get operation depending on async flag
680
+ * connection = Connection.new
681
+ * connection.async? #=> false
682
+ *
683
+ * connection.run do |conn|
684
+ * conn.async? #=> true
685
+ * end
686
+ *
687
+ * @return [true, false] +true+ if the connection if asynchronous
688
+ *
689
+ * @see Bucket#run
690
+ */
691
+ VALUE
692
+ cb_bucket_async_p(VALUE self)
693
+ {
694
+ struct cb_bucket_st *bucket = DATA_PTR(self);
695
+ return bucket->async ? Qtrue : Qfalse;
696
+ }
697
+
698
+ VALUE
699
+ cb_bucket_quiet_get(VALUE self)
700
+ {
701
+ struct cb_bucket_st *bucket = DATA_PTR(self);
702
+ return bucket->quiet ? Qtrue : Qfalse;
703
+ }
704
+
705
+ VALUE
706
+ cb_bucket_quiet_set(VALUE self, VALUE val)
707
+ {
708
+ struct cb_bucket_st *bucket = DATA_PTR(self);
709
+ VALUE new;
710
+
711
+ bucket->quiet = RTEST(val);
712
+ new = bucket->quiet ? Qtrue : Qfalse;
713
+ return new;
714
+ }
715
+
716
+ VALUE
717
+ cb_bucket_default_flags_get(VALUE self)
718
+ {
719
+ struct cb_bucket_st *bucket = DATA_PTR(self);
720
+ return ULONG2NUM(bucket->default_flags);
721
+ }
722
+
723
+ VALUE
724
+ cb_bucket_default_flags_set(VALUE self, VALUE val)
725
+ {
726
+ struct cb_bucket_st *bucket = DATA_PTR(self);
727
+
728
+ bucket->default_flags = (uint32_t)NUM2ULONG(val);
729
+ return val;
730
+ }
731
+
732
+ VALUE
733
+ cb_bucket_transcoder_get(VALUE self)
734
+ {
735
+ struct cb_bucket_st *bucket = DATA_PTR(self);
736
+ return bucket->transcoder;
737
+ }
738
+
739
+ VALUE
740
+ cb_bucket_transcoder_set(VALUE self, VALUE val)
741
+ {
742
+ struct cb_bucket_st *bucket = DATA_PTR(self);
743
+
744
+ if (val != Qnil && !rb_respond_to(val, cb_id_dump) && !rb_respond_to(val, cb_id_load)) {
745
+ rb_raise(rb_eArgError, "transcoder must respond to dump and load methods");
746
+ }
747
+ bucket->transcoder = val;
748
+
749
+ return bucket->transcoder;
750
+ }
751
+
752
+ VALUE
753
+ cb_bucket_default_format_get(VALUE self)
754
+ {
755
+ struct cb_bucket_st *bucket = DATA_PTR(self);
756
+
757
+ if (bucket->transcoder == cb_mDocument) {
758
+ return cb_sym_document;
759
+ } else if (bucket->transcoder == cb_mMarshal) {
760
+ return cb_sym_marshal;
761
+ } else if (bucket->transcoder == cb_mPlain) {
762
+ return cb_sym_plain;
763
+ }
764
+ return Qnil;
765
+ }
766
+
767
+ VALUE
768
+ cb_bucket_default_format_set(VALUE self, VALUE val)
769
+ {
770
+ if (TYPE(val) == T_FIXNUM) {
771
+ rb_warn("numeric argument to #default_format option is deprecated, use symbol");
772
+ switch (FIX2INT(val)) {
773
+ case CB_FMT_DOCUMENT:
774
+ val = cb_sym_document;
775
+ break;
776
+ case CB_FMT_MARSHAL:
777
+ val = cb_sym_marshal;
778
+ break;
779
+ case CB_FMT_PLAIN:
780
+ val = cb_sym_plain;
781
+ break;
782
+ }
783
+ }
784
+ if (val == cb_sym_document) {
785
+ cb_bucket_transcoder_set(self, cb_mDocument);
786
+ } else if (val == cb_sym_marshal) {
787
+ cb_bucket_transcoder_set(self, cb_mMarshal);
788
+ } else if (val == cb_sym_plain) {
789
+ cb_bucket_transcoder_set(self, cb_mPlain);
790
+ } else {
791
+ rb_raise(rb_eArgError, "unknown format");
792
+ }
793
+
794
+ return val;
795
+ }
796
+
797
+ VALUE
798
+ cb_bucket_on_error_set(VALUE self, VALUE val)
799
+ {
800
+ struct cb_bucket_st *bucket = DATA_PTR(self);
801
+
802
+ if (rb_respond_to(val, cb_id_call)) {
803
+ bucket->on_error_proc = val;
804
+ } else {
805
+ bucket->on_error_proc = Qnil;
806
+ }
807
+
808
+ return bucket->on_error_proc;
809
+ }
810
+
811
+ VALUE
812
+ cb_bucket_on_error_get(VALUE self)
813
+ {
814
+ struct cb_bucket_st *bucket = DATA_PTR(self);
815
+
816
+ if (rb_block_given_p()) {
817
+ return cb_bucket_on_error_set(self, rb_block_proc());
818
+ } else {
819
+ return bucket->on_error_proc;
820
+ }
821
+ }
822
+
823
+ static
824
+ VALUE trigger_on_connect_callback_block(VALUE nil, VALUE self)
825
+ {
826
+ (void)nil;
827
+ return trigger_on_connect_callback(self);
828
+ }
829
+
830
+ VALUE
831
+ cb_bucket_on_connect_set(VALUE self, VALUE val)
832
+ {
833
+ struct cb_bucket_st *bucket = DATA_PTR(self);
834
+
835
+ if (rb_respond_to(val, cb_id_call)) {
836
+ bucket->on_connect_proc = val;
837
+ if (bucket->trigger_connect_cb_on_set) {
838
+ bucket->trigger_connect_cb_on_set = 0;
839
+ if (bucket->async) {
840
+ VALUE args[] = {INT2FIX(0)};
841
+ /* setup timer with zero interval to call on_connect
842
+ * callback on the next tick */
843
+ rb_block_call(bucket->self, cb_id_create_timer, 1,
844
+ args, trigger_on_connect_callback_block, bucket->self);
845
+ } else {
846
+ trigger_on_connect_callback(self);
847
+ }
848
+ }
849
+ } else {
850
+ bucket->on_connect_proc = Qnil;
851
+ }
852
+
853
+ return bucket->on_connect_proc;
854
+ }
855
+
856
+ VALUE
857
+ cb_bucket_on_connect_get(VALUE self)
858
+ {
859
+ struct cb_bucket_st *bucket = DATA_PTR(self);
860
+
861
+ if (rb_block_given_p()) {
862
+ return cb_bucket_on_connect_set(self, rb_block_proc());
863
+ } else {
864
+ return bucket->on_connect_proc;
865
+ }
866
+ }
867
+
868
+ VALUE
869
+ cb_bucket_timeout_get(VALUE self)
870
+ {
871
+ struct cb_bucket_st *bucket = DATA_PTR(self);
872
+ return ULONG2NUM(bucket->timeout);
873
+ }
874
+
875
+ VALUE
876
+ cb_bucket_timeout_set(VALUE self, VALUE val)
877
+ {
878
+ struct cb_bucket_st *bucket = DATA_PTR(self);
879
+ VALUE tmval;
880
+
881
+ bucket->timeout = (uint32_t)NUM2ULONG(val);
882
+ lcb_set_timeout(bucket->handle, bucket->timeout);
883
+ tmval = ULONG2NUM(bucket->timeout);
884
+
885
+ return tmval;
886
+ }
887
+
888
+ VALUE
889
+ cb_bucket_default_arithmetic_init_get(VALUE self)
890
+ {
891
+ struct cb_bucket_st *bucket = DATA_PTR(self);
892
+ return ULL2NUM(bucket->default_arith_init);
893
+ }
894
+
895
+ VALUE
896
+ cb_bucket_default_arithmetic_init_set(VALUE self, VALUE val)
897
+ {
898
+ struct cb_bucket_st *bucket = DATA_PTR(self);
899
+
900
+ bucket->default_arith_create = RTEST(val);
901
+ if (bucket->default_arith_create) {
902
+ bucket->default_arith_init = NUM2ULL(val);
903
+ } else {
904
+ bucket->default_arith_init = 0;
905
+ }
906
+ return ULL2NUM(bucket->default_arith_init);
907
+ }
908
+
909
+ VALUE
910
+ cb_bucket_key_prefix_get(VALUE self)
911
+ {
912
+ struct cb_bucket_st *bucket = DATA_PTR(self);
913
+ return bucket->key_prefix_val;
914
+ }
915
+
916
+ VALUE
917
+ cb_bucket_key_prefix_set(VALUE self, VALUE val)
918
+ {
919
+ struct cb_bucket_st *bucket = DATA_PTR(self);
920
+
921
+ bucket->key_prefix_val = rb_str_dup_frozen(StringValue(val));
922
+
923
+ return bucket->key_prefix_val;
924
+ }
925
+
926
+ /* Document-method: hostname
927
+ *
928
+ * @since 1.0.0
929
+ *
930
+ * @return [String] the host name of the management interface (default: "localhost")
931
+ */
932
+ VALUE
933
+ cb_bucket_hostname_get(VALUE self)
934
+ {
935
+ struct cb_bucket_st *bucket = DATA_PTR(self);
936
+
937
+ if (bucket->handle) {
938
+ const char * host = lcb_get_host(bucket->handle);
939
+ unsigned long len = RSTRING_LEN(bucket->hostname);
940
+ if (len != strlen(host) || strncmp(RSTRING_PTR(bucket->hostname), host, len) != 0) {
941
+ bucket->hostname = STR_NEW_CSTR(host);
942
+ rb_str_freeze(bucket->hostname);
943
+ }
944
+ }
945
+ return bucket->hostname;
946
+ }
947
+
948
+ /* Document-method: port
949
+ *
950
+ * @since 1.0.0
951
+ *
952
+ * @return [Fixnum] the port number of the management interface (default: 8091)
953
+ */
954
+ VALUE
955
+ cb_bucket_port_get(VALUE self)
956
+ {
957
+ struct cb_bucket_st *bucket = DATA_PTR(self);
958
+ if (bucket->handle) {
959
+ bucket->port = atoi(lcb_get_port(bucket->handle));
960
+ }
961
+ return UINT2NUM(bucket->port);
962
+ }
963
+
964
+ /* Document-method: authority
965
+ *
966
+ * @since 1.0.0
967
+ *
968
+ * @return [String] host with port
969
+ */
970
+ VALUE
971
+ cb_bucket_authority_get(VALUE self)
972
+ {
973
+ struct cb_bucket_st *bucket = DATA_PTR(self);
974
+ VALUE old_hostname = bucket->hostname;
975
+ uint16_t old_port = bucket->port;
976
+ VALUE hostname = cb_bucket_hostname_get(self);
977
+ cb_bucket_port_get(self);
978
+
979
+ if (hostname != old_hostname || bucket->port != old_port) {
980
+ char port_s[8];
981
+ snprintf(port_s, sizeof(port_s), ":%u", bucket->port);
982
+ bucket->authority = rb_str_dup(hostname);
983
+ rb_str_cat2(bucket->authority, port_s);
984
+ rb_str_freeze(bucket->authority);
985
+ }
986
+ return bucket->authority;
987
+ }
988
+
989
+ /* Document-method: bucket
990
+ *
991
+ * @since 1.0.0
992
+ *
993
+ * @return [String] the bucket name
994
+ */
995
+ VALUE
996
+ cb_bucket_bucket_get(VALUE self)
997
+ {
998
+ struct cb_bucket_st *bucket = DATA_PTR(self);
999
+ return bucket->bucket;
1000
+ }
1001
+
1002
+ /* Document-method: pool
1003
+ *
1004
+ * @since 1.0.0
1005
+ *
1006
+ * @return [String] the pool name (usually "default")
1007
+ */
1008
+ VALUE
1009
+ cb_bucket_pool_get(VALUE self)
1010
+ {
1011
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1012
+ return bucket->pool;
1013
+ }
1014
+
1015
+ /* Document-method: username
1016
+ *
1017
+ * @since 1.0.0
1018
+ *
1019
+ * @return [String] the username for protected buckets (usually matches
1020
+ * the bucket name)
1021
+ */
1022
+ VALUE
1023
+ cb_bucket_username_get(VALUE self)
1024
+ {
1025
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1026
+ return bucket->username;
1027
+ }
1028
+
1029
+ /* Document-method: password
1030
+ *
1031
+ * @since 1.0.0
1032
+ *
1033
+ * @return [String] the password for protected buckets
1034
+ */
1035
+ VALUE
1036
+ cb_bucket_password_get(VALUE self)
1037
+ {
1038
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1039
+ return bucket->password;
1040
+ }
1041
+
1042
+ /* Document-method: environment
1043
+ *
1044
+ * @since 1.2.0
1045
+ *
1046
+ * @see Bucket#initialize
1047
+ *
1048
+ * @return [Symbol] the environment (+:development+ or +:production+)
1049
+ */
1050
+ VALUE
1051
+ cb_bucket_environment_get(VALUE self)
1052
+ {
1053
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1054
+ return bucket->environment;
1055
+ }
1056
+ /* Document-method: num_replicas
1057
+ *
1058
+ * @since 1.2.0.dp6
1059
+ *
1060
+ * The numbers of the replicas for each node in the cluster
1061
+ *
1062
+ * @return [Fixnum]
1063
+ */
1064
+ VALUE
1065
+ cb_bucket_num_replicas_get(VALUE self)
1066
+ {
1067
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1068
+ int32_t nr = lcb_get_num_replicas(bucket->handle);
1069
+ if (nr < 0) {
1070
+ return Qnil;
1071
+ } else {
1072
+ return INT2FIX(nr);
1073
+ }
1074
+ }
1075
+ /* Document-method: default_observe_timeout
1076
+ *
1077
+ * @since 1.2.0.dp6
1078
+ *
1079
+ * Get default timeout value for {Bucket#observe_and_wait} operation in
1080
+ * microseconds
1081
+ *
1082
+ * @return [Fixnum]
1083
+ */
1084
+ VALUE
1085
+ cb_bucket_default_observe_timeout_get(VALUE self)
1086
+ {
1087
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1088
+ return INT2FIX(bucket->default_observe_timeout);
1089
+ }
1090
+
1091
+ /* Document-method: default_observe_timeout=
1092
+ *
1093
+ * @since 1.2.0.dp6
1094
+ *
1095
+ * Set default timeout value for {Bucket#observe_and_wait} operation in
1096
+ * microseconds
1097
+ *
1098
+ * @return [Fixnum]
1099
+ */
1100
+ VALUE
1101
+ cb_bucket_default_observe_timeout_set(VALUE self, VALUE val)
1102
+ {
1103
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1104
+ bucket->default_observe_timeout = FIX2INT(val);
1105
+ return val;
1106
+ }
1107
+ /* Document-method: url
1108
+ *
1109
+ * @since 1.0.0
1110
+ *
1111
+ * @return [String] the address of the cluster management interface
1112
+ */
1113
+ VALUE
1114
+ cb_bucket_url_get(VALUE self)
1115
+ {
1116
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1117
+ VALUE str;
1118
+
1119
+ (void)cb_bucket_authority_get(self);
1120
+ str = rb_str_buf_new2("http://");
1121
+ rb_str_append(str, bucket->authority);
1122
+ rb_str_buf_cat2(str, "/pools/");
1123
+ rb_str_append(str, bucket->pool);
1124
+ rb_str_buf_cat2(str, "/buckets/");
1125
+ rb_str_append(str, bucket->bucket);
1126
+ rb_str_buf_cat2(str, "/");
1127
+ return str;
1128
+ }
1129
+
1130
+ /*
1131
+ * Returns a string containing a human-readable representation of the
1132
+ * {Bucket}.
1133
+ *
1134
+ * @since 1.0.0
1135
+ *
1136
+ * @return [String]
1137
+ */
1138
+ VALUE
1139
+ cb_bucket_inspect(VALUE self)
1140
+ {
1141
+ VALUE str;
1142
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1143
+ char buf[200];
1144
+
1145
+ str = rb_str_buf_new2("#<");
1146
+ rb_str_buf_cat2(str, rb_obj_classname(self));
1147
+ snprintf(buf, 25, ":%p \"", (void *)self);
1148
+ (void)cb_bucket_authority_get(self);
1149
+ rb_str_buf_cat2(str, buf);
1150
+ rb_str_buf_cat2(str, "http://");
1151
+ rb_str_append(str, bucket->authority);
1152
+ rb_str_buf_cat2(str, "/pools/");
1153
+ rb_str_append(str, bucket->pool);
1154
+ rb_str_buf_cat2(str, "/buckets/");
1155
+ rb_str_append(str, bucket->bucket);
1156
+ rb_str_buf_cat2(str, "/\" transcoder=");
1157
+ rb_str_append(str, rb_inspect(bucket->transcoder));
1158
+ snprintf(buf, 150, ", default_flags=0x%x, quiet=%s, connected=%s, timeout=%u",
1159
+ bucket->default_flags,
1160
+ bucket->quiet ? "true" : "false",
1161
+ (bucket->handle && bucket->connected) ? "true" : "false",
1162
+ bucket->timeout);
1163
+ rb_str_buf_cat2(str, buf);
1164
+ if (RTEST(bucket->key_prefix_val)) {
1165
+ rb_str_buf_cat2(str, ", key_prefix=");
1166
+ rb_str_append(str, rb_inspect(bucket->key_prefix_val));
1167
+ }
1168
+ rb_str_buf_cat2(str, ">");
1169
+
1170
+ return str;
1171
+ }
1172
+
1173
+ static void
1174
+ do_loop(struct cb_bucket_st *bucket)
1175
+ {
1176
+ lcb_wait(bucket->handle);
1177
+ bucket->nbytes = 0;
1178
+ }
1179
+
1180
+ void
1181
+ cb_maybe_do_loop(struct cb_bucket_st *bucket)
1182
+ {
1183
+ if (bucket->threshold != 0 && bucket->nbytes > bucket->threshold) {
1184
+ do_loop(bucket);
1185
+ }
1186
+ }
1187
+
1188
+ static VALUE
1189
+ do_run(VALUE *args)
1190
+ {
1191
+ VALUE self = args[0], opts = args[1], proc = args[2], exc;
1192
+ VALUE was_async = args[3];
1193
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1194
+
1195
+ if (bucket->handle == NULL) {
1196
+ rb_raise(cb_eConnectError, "closed connection");
1197
+ }
1198
+
1199
+ if (bucket->running) {
1200
+ rb_raise(cb_eInvalidError, "nested #run");
1201
+ }
1202
+ bucket->threshold = 0;
1203
+ if (opts != Qnil) {
1204
+ VALUE arg;
1205
+ Check_Type(opts, T_HASH);
1206
+ arg = rb_hash_aref(opts, cb_sym_send_threshold);
1207
+ if (arg != Qnil) {
1208
+ bucket->threshold = (uint32_t)NUM2ULONG(arg);
1209
+ }
1210
+ }
1211
+ bucket->async = 1;
1212
+ bucket->running = 1;
1213
+ if (proc != Qnil) {
1214
+ cb_proc_call(bucket, proc, 1, self);
1215
+ }
1216
+ if (bucket->exception != Qnil) {
1217
+ exc = bucket->exception;
1218
+ bucket->exception = Qnil;
1219
+ if (was_async) {
1220
+ cb_async_error_notify(bucket, exc);
1221
+ /* XXX return here? */
1222
+ } else {
1223
+ rb_exc_raise(exc);
1224
+ }
1225
+ }
1226
+ do_loop(bucket);
1227
+ if (bucket->exception != Qnil) {
1228
+ exc = bucket->exception;
1229
+ bucket->exception = Qnil;
1230
+ if (!was_async) {
1231
+ rb_exc_raise(exc);
1232
+ }
1233
+ /* async connections notified immediately from the callbacks
1234
+ * via cb_async_error_notify() */
1235
+ }
1236
+ return Qnil;
1237
+ }
1238
+
1239
+ static VALUE
1240
+ ensure_run(VALUE *args)
1241
+ {
1242
+ VALUE self = args[0];
1243
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1244
+
1245
+ bucket->running = 0;
1246
+ bucket->async = args[3];
1247
+ bucket->running = args[4];
1248
+ return Qnil;
1249
+ }
1250
+
1251
+ /*
1252
+ * Run the event loop.
1253
+ *
1254
+ * @since 1.0.0
1255
+ *
1256
+ * @param [Hash] options The options for operation for connection
1257
+ * @option options [Fixnum] :send_threshold (0) if the internal command
1258
+ * buffer will exceeds this value, then the library will start network
1259
+ * interaction and block the current thread until all scheduled commands
1260
+ * will be completed.
1261
+ *
1262
+ * @yieldparam [Bucket] bucket the bucket instance
1263
+ *
1264
+ * @example Use block to run the loop
1265
+ * c = Couchbase.new
1266
+ * c.run do
1267
+ * c.get("foo") {|ret| puts ret.value}
1268
+ * end
1269
+ *
1270
+ * @example Use lambda to run the loop
1271
+ * c = Couchbase.new
1272
+ * operations = lambda do |c|
1273
+ * c.get("foo") {|ret| puts ret.value}
1274
+ * end
1275
+ * c.run(&operations)
1276
+ *
1277
+ * @example Use threshold to send out commands automatically
1278
+ * c = Couchbase.connect
1279
+ * sent = 0
1280
+ * c.run(:send_threshold => 8192) do # 8Kb
1281
+ * c.set("foo1", "x" * 100) {|r| sent += 1}
1282
+ * # 128 bytes buffered, sent is 0 now
1283
+ * c.set("foo2", "x" * 10000) {|r| sent += 1}
1284
+ * # 10028 bytes added, sent is 2 now
1285
+ * c.set("foo3", "x" * 100) {|r| sent += 1}
1286
+ * end
1287
+ * # all commands were executed and sent is 3 now
1288
+ *
1289
+ * @example Use {Couchbase::Bucket#run} without block for async connection
1290
+ * c = Couchbase.new(:async => true)
1291
+ * c.run # ensure that instance connected
1292
+ * c.set("foo", "bar"){|r| puts r.cas}
1293
+ * c.run
1294
+ *
1295
+ * @return [nil]
1296
+ *
1297
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1298
+ */
1299
+ VALUE
1300
+ cb_bucket_run(int argc, VALUE *argv, VALUE self)
1301
+ {
1302
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1303
+ VALUE args[5];
1304
+
1305
+ /* it is allowed to omit block for async connections */
1306
+ if (!bucket->async) {
1307
+ rb_need_block();
1308
+ }
1309
+ args[0] = self;
1310
+ rb_scan_args(argc, argv, "01&", &args[1], &args[2]);
1311
+ args[3] = bucket->async;
1312
+ args[4] = bucket->running;
1313
+ rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
1314
+ return Qnil;
1315
+ }
1316
+
1317
+ /*
1318
+ * Stop the event loop.
1319
+ *
1320
+ * @since 1.2.0
1321
+ *
1322
+ * @example Breakout the event loop when 5th request is completed
1323
+ * c = Couchbase.connect
1324
+ * c.run do
1325
+ * 10.times do |ii|
1326
+ * c.get("foo") do |ret|
1327
+ * puts ii
1328
+ * c.stop if ii == 5
1329
+ * end
1330
+ * end
1331
+ * end
1332
+ *
1333
+ * @return [nil]
1334
+ */
1335
+ VALUE
1336
+ cb_bucket_stop(VALUE self)
1337
+ {
1338
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1339
+ lcb_breakout(bucket->handle);
1340
+ return Qnil;
1341
+ }
1342
+
1343
+ /*
1344
+ * Close the connection to the cluster
1345
+ *
1346
+ * @since 1.1.0
1347
+ *
1348
+ * @return [true]
1349
+ *
1350
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1351
+ */
1352
+ VALUE
1353
+ cb_bucket_disconnect(VALUE self)
1354
+ {
1355
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1356
+
1357
+ if (bucket->handle) {
1358
+ lcb_destroy(bucket->handle);
1359
+ lcb_destroy_io_ops(bucket->io);
1360
+ bucket->handle = NULL;
1361
+ bucket->io = NULL;
1362
+ bucket->connected = 0;
1363
+ return Qtrue;
1364
+ } else {
1365
+ rb_raise(cb_eConnectError, "closed connection");
1366
+ return Qfalse;
1367
+ }
1368
+ }
1369
+
1370
+