couchbase 1.3.4-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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
+