jmoses-couchbase 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +15 -0
  2. data/.travis.yml +22 -0
  3. data/.yardopts +5 -0
  4. data/CONTRIBUTING.markdown +75 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +201 -0
  7. data/Makefile +3 -0
  8. data/README.markdown +665 -0
  9. data/RELEASE_NOTES.markdown +819 -0
  10. data/Rakefile +20 -0
  11. data/couchbase.gemspec +49 -0
  12. data/examples/chat-em/Gemfile +7 -0
  13. data/examples/chat-em/README.markdown +45 -0
  14. data/examples/chat-em/server.rb +82 -0
  15. data/examples/chat-goliath-grape/Gemfile +5 -0
  16. data/examples/chat-goliath-grape/README.markdown +50 -0
  17. data/examples/chat-goliath-grape/app.rb +67 -0
  18. data/examples/chat-goliath-grape/config/app.rb +20 -0
  19. data/examples/transcoders/Gemfile +3 -0
  20. data/examples/transcoders/README.markdown +59 -0
  21. data/examples/transcoders/cb-zcat +40 -0
  22. data/examples/transcoders/cb-zcp +45 -0
  23. data/examples/transcoders/gzip_transcoder.rb +49 -0
  24. data/examples/transcoders/options.rb +54 -0
  25. data/ext/couchbase_ext/.gitignore +4 -0
  26. data/ext/couchbase_ext/arguments.c +956 -0
  27. data/ext/couchbase_ext/arithmetic.c +316 -0
  28. data/ext/couchbase_ext/bucket.c +1373 -0
  29. data/ext/couchbase_ext/context.c +65 -0
  30. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  31. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  32. data/ext/couchbase_ext/delete.c +163 -0
  33. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  34. data/ext/couchbase_ext/extconf.rb +169 -0
  35. data/ext/couchbase_ext/get.c +316 -0
  36. data/ext/couchbase_ext/gethrtime.c +129 -0
  37. data/ext/couchbase_ext/http.c +432 -0
  38. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  39. data/ext/couchbase_ext/observe.c +171 -0
  40. data/ext/couchbase_ext/plugin_common.c +171 -0
  41. data/ext/couchbase_ext/result.c +129 -0
  42. data/ext/couchbase_ext/stats.c +163 -0
  43. data/ext/couchbase_ext/store.c +542 -0
  44. data/ext/couchbase_ext/timer.c +192 -0
  45. data/ext/couchbase_ext/touch.c +186 -0
  46. data/ext/couchbase_ext/unlock.c +176 -0
  47. data/ext/couchbase_ext/utils.c +551 -0
  48. data/ext/couchbase_ext/version.c +142 -0
  49. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  50. data/lib/active_support/cache/couchbase_store.rb +430 -0
  51. data/lib/couchbase.rb +155 -0
  52. data/lib/couchbase/bucket.rb +457 -0
  53. data/lib/couchbase/cluster.rb +119 -0
  54. data/lib/couchbase/connection_pool.rb +58 -0
  55. data/lib/couchbase/constants.rb +12 -0
  56. data/lib/couchbase/result.rb +26 -0
  57. data/lib/couchbase/transcoder.rb +120 -0
  58. data/lib/couchbase/utils.rb +62 -0
  59. data/lib/couchbase/version.rb +21 -0
  60. data/lib/couchbase/view.rb +506 -0
  61. data/lib/couchbase/view_row.rb +272 -0
  62. data/lib/ext/multi_json_fix.rb +56 -0
  63. data/lib/rack/session/couchbase.rb +108 -0
  64. data/tasks/benchmark.rake +6 -0
  65. data/tasks/compile.rake +160 -0
  66. data/tasks/test.rake +100 -0
  67. data/tasks/util.rake +21 -0
  68. data/test/profile/.gitignore +1 -0
  69. data/test/profile/Gemfile +6 -0
  70. data/test/profile/benchmark.rb +195 -0
  71. data/test/setup.rb +178 -0
  72. data/test/test_arithmetic.rb +185 -0
  73. data/test/test_async.rb +316 -0
  74. data/test/test_bucket.rb +276 -0
  75. data/test/test_cas.rb +235 -0
  76. data/test/test_couchbase.rb +77 -0
  77. data/test/test_couchbase_connection_pool.rb +77 -0
  78. data/test/test_couchbase_rails_cache_store.rb +361 -0
  79. data/test/test_delete.rb +120 -0
  80. data/test/test_errors.rb +82 -0
  81. data/test/test_eventmachine.rb +70 -0
  82. data/test/test_format.rb +164 -0
  83. data/test/test_get.rb +407 -0
  84. data/test/test_stats.rb +57 -0
  85. data/test/test_store.rb +216 -0
  86. data/test/test_timer.rb +42 -0
  87. data/test/test_touch.rb +97 -0
  88. data/test/test_unlock.rb +119 -0
  89. data/test/test_utils.rb +58 -0
  90. data/test/test_version.rb +52 -0
  91. metadata +353 -0
@@ -0,0 +1,192 @@
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_timer_free(void *ptr)
22
+ {
23
+ xfree(ptr);
24
+ }
25
+
26
+ void
27
+ cb_timer_mark(void *ptr)
28
+ {
29
+ struct cb_timer_st *timer = ptr;
30
+ if (timer) {
31
+ rb_gc_mark(timer->callback);
32
+ }
33
+ }
34
+
35
+ VALUE
36
+ cb_timer_alloc(VALUE klass)
37
+ {
38
+ VALUE obj;
39
+ struct cb_timer_st *timer;
40
+
41
+ /* allocate new bucket struct and set it to zero */
42
+ obj = Data_Make_Struct(klass, struct cb_timer_st, cb_timer_mark,
43
+ cb_timer_free, timer);
44
+ return obj;
45
+ }
46
+
47
+ /*
48
+ * Returns a string containing a human-readable representation of the
49
+ * Timer.
50
+ *
51
+ * @since 1.2.0.dp6
52
+ *
53
+ * @return [String]
54
+ */
55
+ VALUE
56
+ cb_timer_inspect(VALUE self)
57
+ {
58
+ VALUE str;
59
+ struct cb_timer_st *tm = DATA_PTR(self);
60
+ char buf[200];
61
+
62
+ str = rb_str_buf_new2("#<");
63
+ rb_str_buf_cat2(str, rb_obj_classname(self));
64
+ snprintf(buf, 20, ":%p", (void *)self);
65
+ rb_str_buf_cat2(str, buf);
66
+ snprintf(buf, 100, " timeout:%u periodic:%s>",
67
+ tm->usec, tm->periodic ? "true" : "false");
68
+ rb_str_buf_cat2(str, buf);
69
+
70
+ return str;
71
+ }
72
+
73
+ /*
74
+ * Cancel the timer.
75
+ *
76
+ * @since 1.2.0.dp6
77
+ *
78
+ * This operation makes sense for periodic timers or if one need to cancel
79
+ * regular timer before it will be triggered.
80
+ *
81
+ * @example Cancel periodic timer
82
+ * n = 1
83
+ * c.run do
84
+ * tm = c.create_periodic_timer(500000) do
85
+ * c.incr("foo") do
86
+ * if n == 5
87
+ * tm.cancel
88
+ * else
89
+ * n += 1
90
+ * end
91
+ * end
92
+ * end
93
+ * end
94
+ *
95
+ * @return [String]
96
+ */
97
+ VALUE
98
+ cb_timer_cancel(VALUE self)
99
+ {
100
+ struct cb_timer_st *tm = DATA_PTR(self);
101
+ lcb_timer_destroy(tm->bucket->handle, tm->timer);
102
+ return self;
103
+ }
104
+
105
+ static VALUE
106
+ trigger_timer(VALUE timer)
107
+ {
108
+ struct cb_timer_st *tm = DATA_PTR(timer);
109
+ return cb_proc_call(tm->bucket, tm->callback, 1, timer);
110
+ }
111
+
112
+ static void
113
+ timer_callback(lcb_timer_t timer, lcb_t instance,
114
+ const void *cookie)
115
+ {
116
+ struct cb_timer_st *tm = (struct cb_timer_st *)cookie;
117
+ int error = 0;
118
+
119
+ rb_protect(trigger_timer, tm->self, &error);
120
+ if (error) {
121
+ lcb_timer_destroy(instance, timer);
122
+ }
123
+ (void)cookie;
124
+ }
125
+
126
+ /*
127
+ * Initialize new Timer
128
+ *
129
+ * @since 1.2.0
130
+ *
131
+ * The timers could used to trigger reccuring events or implement timeouts.
132
+ * The library will call given block after time interval pass.
133
+ *
134
+ * @param bucket [Bucket] the connection object
135
+ * @param interval [Fixnum] the interval in microseconds
136
+ * @param options [Hash]
137
+ * @option options [Boolean] :periodic (false) set it to +true+ if the timer
138
+ * should be triggered until it will be canceled.
139
+ *
140
+ * @yieldparam [Timer] timer the current timer
141
+ *
142
+ * @example Create regular timer for 0.5 second
143
+ * c.run do
144
+ * Couchbase::Timer.new(c, 500000) do
145
+ * puts "ding-dong"
146
+ * end
147
+ * end
148
+ *
149
+ * @example Create periodic timer
150
+ * n = 10
151
+ * c.run do
152
+ * Couchbase::Timer.new(c, 500000, :periodic => true) do |tm|
153
+ * puts "#{n}"
154
+ * n -= 1
155
+ * tm.cancel if n.zero?
156
+ * end
157
+ * end
158
+ *
159
+ *
160
+ * @return [Couchbase::Timer]
161
+ */
162
+ VALUE
163
+ cb_timer_init(int argc, VALUE *argv, VALUE self)
164
+ {
165
+ struct cb_timer_st *tm = DATA_PTR(self);
166
+ VALUE bucket, opts, timeout, exc, cb;
167
+ lcb_error_t err;
168
+
169
+ rb_need_block();
170
+ rb_scan_args(argc, argv, "21&", &bucket, &timeout, &opts, &cb);
171
+
172
+ if (!RTEST(rb_obj_is_kind_of(bucket, cb_cBucket))) {
173
+ rb_raise(rb_eTypeError, "wrong argument type (expected Couchbase::Bucket)");
174
+ }
175
+ tm->self = self;
176
+ tm->callback = cb;
177
+ tm->usec = NUM2ULONG(timeout);
178
+ tm->bucket = DATA_PTR(bucket);
179
+ if (opts != Qnil) {
180
+ Check_Type(opts, T_HASH);
181
+ tm->periodic = RTEST(rb_hash_aref(opts, cb_sym_periodic));
182
+ }
183
+ tm->timer = lcb_timer_create(tm->bucket->handle, tm, tm->usec,
184
+ tm->periodic, timer_callback, &err);
185
+ exc = cb_check_error(err, "failed to attach the timer", Qnil);
186
+ if (exc != Qnil) {
187
+ rb_exc_raise(exc);
188
+ }
189
+
190
+ return self;
191
+ }
192
+
@@ -0,0 +1,186 @@
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_touch_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_touch_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 key, exc = Qnil, res;
26
+
27
+ ctx->nqueries--;
28
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
29
+ cb_strip_key_prefix(bucket, key);
30
+
31
+ if (error != LCB_KEY_ENOENT || !ctx->quiet) {
32
+ exc = cb_check_error(error, "failed to touch value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, cb_id_iv_operation, cb_sym_touch);
35
+ ctx->exception = exc;
36
+ }
37
+ }
38
+
39
+ if (bucket->async) { /* asynchronous */
40
+ if (ctx->proc != Qnil) {
41
+ res = rb_class_new_instance(0, NULL, cb_cResult);
42
+ rb_ivar_set(res, cb_id_iv_error, exc);
43
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_touch);
44
+ rb_ivar_set(res, cb_id_iv_key, key);
45
+ cb_proc_call(bucket, ctx->proc, 1, res);
46
+ }
47
+ } else { /* synchronous */
48
+ rb_hash_aset(ctx->rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
49
+ }
50
+ if (ctx->nqueries == 0) {
51
+ ctx->proc = Qnil;
52
+ if (bucket->async) {
53
+ cb_context_free(ctx);
54
+ }
55
+ }
56
+ (void)handle;
57
+ }
58
+
59
+ /*
60
+ * Update the expiry time of an item
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * The +touch+ method allow you to update the expiration time on a given
65
+ * key. This can be useful for situations where you want to prevent an item
66
+ * from expiring without resetting the associated value. For example, for a
67
+ * session database you might want to keep the session alive in the database
68
+ * each time the user accesses a web page without explicitly updating the
69
+ * session value, keeping the user's session active and available.
70
+ *
71
+ * @overload touch(key, options = {})
72
+ * @param key [String, Symbol] Key used to reference the value.
73
+ * @param options [Hash] Options for operation.
74
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
75
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
76
+ * absolute times (from the epoch).
77
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
78
+ * operation won't raise error for missing key, it will return +nil+.
79
+ *
80
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
81
+ * (valid attributes: +error+, +operation+, +key+).
82
+ *
83
+ * @return [true, false] +true+ if the operation was successful and +false+
84
+ * otherwise.
85
+ *
86
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
87
+ *
88
+ * @raise [ArgumentError] when passing the block in synchronous mode
89
+ *
90
+ * @example Touch value using +default_ttl+
91
+ * c.touch("foo")
92
+ *
93
+ * @example Touch value using custom TTL (10 seconds)
94
+ * c.touch("foo", :ttl => 10)
95
+ *
96
+ * @overload touch(keys)
97
+ * @param keys [Hash] The Hash where keys represent the keys in the
98
+ * database, values -- the expiry times for corresponding key. See
99
+ * description of +:ttl+ argument above for more information about TTL
100
+ * values.
101
+ *
102
+ * @yieldparam ret [Result] the result of operation for each key in
103
+ * asynchronous mode (valid attributes: +error+, +operation+, +key+).
104
+ *
105
+ * @return [Hash] Mapping keys to result of touch operation (+true+ if the
106
+ * operation was successful and +false+ otherwise)
107
+ *
108
+ * @example Touch several values
109
+ * c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
110
+ *
111
+ * @example Touch several values in async mode
112
+ * c.run do
113
+ * c.touch("foo" => 10, :bar => 20) do |ret|
114
+ * ret.operation #=> :touch
115
+ * ret.success? #=> true
116
+ * ret.key #=> "foo" and "bar" in separate calls
117
+ * end
118
+ * end
119
+ *
120
+ * @example Touch single value
121
+ * c.touch("foo" => 10) #=> true
122
+ *
123
+ */
124
+ VALUE
125
+ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
126
+ {
127
+ struct cb_bucket_st *bucket = DATA_PTR(self);
128
+ struct cb_context_st *ctx;
129
+ VALUE rv, proc, exc;
130
+ lcb_error_t err;
131
+ struct cb_params_st params;
132
+
133
+ if (!cb_bucket_connected_bang(bucket, cb_sym_touch)) {
134
+ return Qnil;
135
+ }
136
+
137
+ memset(&params, 0, sizeof(struct cb_params_st));
138
+ rb_scan_args(argc, argv, "0*&", &params.args, &proc);
139
+ if (!bucket->async && proc != Qnil) {
140
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
141
+ }
142
+ rb_funcall(params.args, cb_id_flatten_bang, 0);
143
+ params.type = cb_cmd_touch;
144
+ params.bucket = bucket;
145
+ cb_params_build(&params);
146
+ ctx = cb_context_alloc_common(bucket, proc, params.cmd.touch.num);
147
+ ctx->quiet = params.cmd.touch.quiet;
148
+ err = lcb_touch(bucket->handle, (const void *)ctx,
149
+ params.cmd.touch.num, params.cmd.touch.ptr);
150
+ cb_params_destroy(&params);
151
+ exc = cb_check_error(err, "failed to schedule touch request", Qnil);
152
+ if (exc != Qnil) {
153
+ cb_context_free(ctx);
154
+ rb_exc_raise(exc);
155
+ }
156
+ bucket->nbytes += params.npayload;
157
+ if (bucket->async) {
158
+ cb_maybe_do_loop(bucket);
159
+ return Qnil;
160
+ } else {
161
+ if (ctx->nqueries > 0) {
162
+ /* we have some operations pending */
163
+ lcb_wait(bucket->handle);
164
+ }
165
+ exc = ctx->exception;
166
+ rv = ctx->rv;
167
+ cb_context_free(ctx);
168
+ if (exc != Qnil) {
169
+ rb_exc_raise(exc);
170
+ }
171
+ exc = bucket->exception;
172
+ if (exc != Qnil) {
173
+ bucket->exception = Qnil;
174
+ rb_exc_raise(exc);
175
+ }
176
+ if (params.cmd.touch.num > 1) {
177
+ return rv; /* return as a hash {key => true, ...} */
178
+ } else {
179
+ VALUE vv = Qnil;
180
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
181
+ return vv;
182
+ }
183
+ }
184
+ }
185
+
186
+
@@ -0,0 +1,176 @@
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_unlock_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_unlock_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 key, exc = Qnil, res;
26
+
27
+ ctx->nqueries--;
28
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
29
+ cb_strip_key_prefix(bucket, key);
30
+
31
+ if (error != LCB_KEY_ENOENT || !ctx->quiet) {
32
+ exc = cb_check_error(error, "failed to unlock value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, cb_id_iv_operation, cb_sym_unlock);
35
+ ctx->exception = exc;
36
+ }
37
+ }
38
+
39
+ if (bucket->async) { /* asynchronous */
40
+ if (ctx->proc != Qnil) {
41
+ res = rb_class_new_instance(0, NULL, cb_cResult);
42
+ rb_ivar_set(res, cb_id_iv_error, exc);
43
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_unlock);
44
+ rb_ivar_set(res, cb_id_iv_key, key);
45
+ cb_proc_call(bucket, ctx->proc, 1, res);
46
+ }
47
+ } else { /* synchronous */
48
+ rb_hash_aset(ctx->rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
49
+ }
50
+ if (ctx->nqueries == 0) {
51
+ ctx->proc = Qnil;
52
+ if (bucket->async) {
53
+ cb_context_free(ctx);
54
+ }
55
+ }
56
+ (void)handle;
57
+ }
58
+
59
+ /*
60
+ * Unlock key
61
+ *
62
+ * @since 1.2.0
63
+ *
64
+ * The +unlock+ method allow you to unlock key once locked by {Bucket#get}
65
+ * with +:lock+ option.
66
+ *
67
+ * @overload unlock(key, options = {})
68
+ * @param key [String, Symbol] Key used to reference the value.
69
+ * @param options [Hash] Options for operation.
70
+ * @option options [Fixnum] :cas The CAS value must match the current one
71
+ * from the storage.
72
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
73
+ * operation won't raise error for missing key, it will return +nil+.
74
+ *
75
+ * @return [true, false] +true+ if the operation was successful and +false+
76
+ * otherwise.
77
+ *
78
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
79
+ *
80
+ * @raise [ArgumentError] when passing the block in synchronous mode
81
+ *
82
+ * @raise [Couchbase::Error::NotFound] if key(s) not found in the storage
83
+ *
84
+ * @raise [Couchbase::Error::TemporaryFail] if either the key wasn't
85
+ * locked or given CAS value doesn't match to actual in the storage
86
+ *
87
+ * @example Unlock the single key
88
+ * val, _, cas = c.get("foo", :lock => true, :extended => true)
89
+ * c.unlock("foo", :cas => cas)
90
+ *
91
+ * @overload unlock(keys)
92
+ * @param keys [Hash] The Hash where keys represent the keys in the
93
+ * database, values -- the CAS for corresponding key.
94
+ *
95
+ * @yieldparam ret [Result] the result of operation for each key in
96
+ * asynchronous mode (valid attributes: +error+, +operation+, +key+).
97
+ *
98
+ * @return [Hash] Mapping keys to result of unlock operation (+true+ if the
99
+ * operation was successful and +false+ otherwise)
100
+ *
101
+ * @example Unlock several keys
102
+ * c.unlock("foo" => cas1, :bar => cas2) #=> {"foo" => true, "bar" => true}
103
+ *
104
+ * @example Unlock several values in async mode
105
+ * c.run do
106
+ * c.unlock("foo" => 10, :bar => 20) do |ret|
107
+ * ret.operation #=> :unlock
108
+ * ret.success? #=> true
109
+ * ret.key #=> "foo" and "bar" in separate calls
110
+ * end
111
+ * end
112
+ *
113
+ */
114
+ VALUE
115
+ cb_bucket_unlock(int argc, VALUE *argv, VALUE self)
116
+ {
117
+ struct cb_bucket_st *bucket = DATA_PTR(self);
118
+ struct cb_context_st *ctx;
119
+ VALUE rv, proc, exc;
120
+ lcb_error_t err;
121
+ struct cb_params_st params;
122
+
123
+ if (!cb_bucket_connected_bang(bucket, cb_sym_unlock)) {
124
+ return Qnil;
125
+ }
126
+
127
+ memset(&params, 0, sizeof(struct cb_params_st));
128
+ rb_scan_args(argc, argv, "0*&", &params.args, &proc);
129
+ if (!bucket->async && proc != Qnil) {
130
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
131
+ }
132
+ rb_funcall(params.args, cb_id_flatten_bang, 0);
133
+ params.type = cb_cmd_unlock;
134
+ params.bucket = bucket;
135
+ cb_params_build(&params);
136
+ ctx = cb_context_alloc_common(bucket, proc, params.cmd.unlock.num);
137
+ ctx->quiet = params.cmd.unlock.quiet;
138
+ err = lcb_unlock(bucket->handle, (const void *)ctx,
139
+ params.cmd.unlock.num, params.cmd.unlock.ptr);
140
+ cb_params_destroy(&params);
141
+ exc = cb_check_error(err, "failed to schedule unlock request", Qnil);
142
+ if (exc != Qnil) {
143
+ cb_context_free(ctx);
144
+ rb_exc_raise(exc);
145
+ }
146
+ bucket->nbytes += params.npayload;
147
+ if (bucket->async) {
148
+ cb_maybe_do_loop(bucket);
149
+ return Qnil;
150
+ } else {
151
+ if (ctx->nqueries > 0) {
152
+ /* we have some operations pending */
153
+ lcb_wait(bucket->handle);
154
+ }
155
+ exc = ctx->exception;
156
+ rv = ctx->rv;
157
+ cb_context_free(ctx);
158
+ if (exc != Qnil) {
159
+ rb_exc_raise(exc);
160
+ }
161
+ exc = bucket->exception;
162
+ if (exc != Qnil) {
163
+ bucket->exception = Qnil;
164
+ rb_exc_raise(exc);
165
+ }
166
+ if (params.cmd.unlock.num > 1) {
167
+ return rv; /* return as a hash {key => true, ...} */
168
+ } else {
169
+ VALUE vv = Qnil;
170
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
171
+ return vv;
172
+ }
173
+ }
174
+ }
175
+
176
+