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,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 (CLASS_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
+