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,163 @@
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_stat_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_server_stat_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 stats, node, key, val, exc = Qnil, res;
26
+
27
+ node = resp->v.v0.server_endpoint ? STR_NEW_CSTR(resp->v.v0.server_endpoint) : Qnil;
28
+ exc = cb_check_error(error, "failed to fetch stats", node);
29
+ if (exc != Qnil) {
30
+ rb_ivar_set(exc, cb_id_iv_operation, cb_sym_stats);
31
+ ctx->exception = exc;
32
+ }
33
+ if (node != Qnil) {
34
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
35
+ val = STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes);
36
+ if (bucket->async) { /* asynchronous */
37
+ if (ctx->proc != Qnil) {
38
+ res = rb_class_new_instance(0, NULL, cb_cResult);
39
+ rb_ivar_set(res, cb_id_iv_error, exc);
40
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_stats);
41
+ rb_ivar_set(res, cb_id_iv_node, node);
42
+ rb_ivar_set(res, cb_id_iv_key, key);
43
+ rb_ivar_set(res, cb_id_iv_value, val);
44
+ cb_proc_call(bucket, ctx->proc, 1, res);
45
+ }
46
+ } else { /* synchronous */
47
+ if (NIL_P(exc)) {
48
+ stats = rb_hash_aref(ctx->rv, key);
49
+ if (NIL_P(stats)) {
50
+ stats = rb_hash_new();
51
+ rb_hash_aset(ctx->rv, key, stats);
52
+ }
53
+ rb_hash_aset(stats, node, val);
54
+ }
55
+ }
56
+ } else {
57
+ ctx->proc = Qnil;
58
+ if (bucket->async) {
59
+ cb_context_free(ctx);
60
+ }
61
+ }
62
+ (void)handle;
63
+ }
64
+
65
+ /*
66
+ * Request server statistics.
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * Fetches stats from each node in cluster. Without a key specified the
71
+ * server will respond with a "default" set of statistical information. In
72
+ * asynchronous mode each statistic is returned in separate call where the
73
+ * Result object yielded (+#key+ contains the name of the statistical item
74
+ * and the +#value+ contains the value, the +#node+ will indicate the server
75
+ * address). In synchronous mode it returns the hash of stats keys and
76
+ * node-value pairs as a value.
77
+ *
78
+ * @overload stats(arg = nil)
79
+ * @param [String] arg argument to STATS query
80
+ * @yieldparam [Result] ret the object with +node+, +key+ and +value+
81
+ * attributes.
82
+ *
83
+ * @example Found how many items in the bucket
84
+ * total = 0
85
+ * c.stats["total_items"].each do |key, value|
86
+ * total += value.to_i
87
+ * end
88
+ *
89
+ * @example Found total items number asynchronously
90
+ * total = 0
91
+ * c.run do
92
+ * c.stats do |ret|
93
+ * if ret.key == "total_items"
94
+ * total += ret.value.to_i
95
+ * end
96
+ * end
97
+ * end
98
+ *
99
+ * @example Get memory stats (works on couchbase buckets)
100
+ * c.stats(:memory) #=> {"mem_used"=>{...}, ...}
101
+ *
102
+ * @return [Hash] where keys are stat keys, values are host-value pairs
103
+ *
104
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
105
+ * @raise [ArgumentError] when passing the block in synchronous mode
106
+ */
107
+ VALUE
108
+ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
109
+ {
110
+ struct cb_bucket_st *bucket = DATA_PTR(self);
111
+ struct cb_context_st *ctx;
112
+ VALUE rv, exc, proc;
113
+ lcb_error_t err;
114
+ struct cb_params_st params;
115
+
116
+ if (!cb_bucket_connected_bang(bucket, cb_sym_stats)) {
117
+ return Qnil;
118
+ }
119
+
120
+ memset(&params, 0, sizeof(struct cb_params_st));
121
+ rb_scan_args(argc, argv, "0*&", &params.args, &proc);
122
+ if (!bucket->async && proc != Qnil) {
123
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
124
+ }
125
+ params.type = cb_cmd_stats;
126
+ params.bucket = bucket;
127
+ cb_params_build(&params);
128
+ ctx = cb_context_alloc_common(bucket, proc, params.cmd.stats.num);
129
+ err = lcb_server_stats(bucket->handle, (const void *)ctx,
130
+ params.cmd.stats.num, params.cmd.stats.ptr);
131
+ exc = cb_check_error(err, "failed to schedule stat request", Qnil);
132
+ cb_params_destroy(&params);
133
+ if (exc != Qnil) {
134
+ cb_context_free(ctx);
135
+ rb_exc_raise(exc);
136
+ }
137
+ bucket->nbytes += params.npayload;
138
+ if (bucket->async) {
139
+ cb_maybe_do_loop(bucket);
140
+ return Qnil;
141
+ } else {
142
+ if (ctx->nqueries > 0) {
143
+ /* we have some operations pending */
144
+ lcb_wait(bucket->handle);
145
+ }
146
+ exc = ctx->exception;
147
+ rv = ctx->rv;
148
+ cb_context_free(ctx);
149
+ if (exc != Qnil) {
150
+ rb_exc_raise(exc);
151
+ }
152
+ exc = bucket->exception;
153
+ if (exc != Qnil) {
154
+ bucket->exception = Qnil;
155
+ rb_exc_raise(exc);
156
+ }
157
+ return rv;
158
+ }
159
+
160
+ return Qnil;
161
+ }
162
+
163
+
@@ -0,0 +1,542 @@
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
+ storage_observe_callback(VALUE args, VALUE cookie)
22
+ {
23
+ struct cb_context_st *ctx = (struct cb_context_st *)cookie;
24
+ struct cb_bucket_st *bucket = ctx->bucket;
25
+ VALUE res = rb_ary_shift(args);
26
+
27
+ if (ctx->proc != Qnil) {
28
+ rb_ivar_set(res, cb_id_iv_operation, ctx->operation);
29
+ cb_proc_call(bucket, ctx->proc, 1, res);
30
+ }
31
+ if (!RTEST(ctx->observe_options)) {
32
+ ctx->nqueries--;
33
+ if (ctx->nqueries == 0) {
34
+ ctx->proc = Qnil;
35
+ if (bucket->async) {
36
+ cb_context_free(ctx);
37
+ }
38
+ }
39
+ }
40
+ return Qnil;
41
+ }
42
+
43
+ VALUE
44
+ storage_opcode_to_sym(lcb_storage_t operation)
45
+ {
46
+ switch(operation) {
47
+ case LCB_ADD:
48
+ return cb_sym_add;
49
+ case LCB_REPLACE:
50
+ return cb_sym_replace;
51
+ case LCB_SET:
52
+ return cb_sym_set;
53
+ case LCB_APPEND:
54
+ return cb_sym_append;
55
+ case LCB_PREPEND:
56
+ return cb_sym_prepend;
57
+ default:
58
+ return Qnil;
59
+ }
60
+ }
61
+
62
+ void
63
+ cb_storage_callback(lcb_t handle, const void *cookie, lcb_storage_t operation,
64
+ lcb_error_t error, const lcb_store_resp_t *resp)
65
+ {
66
+ struct cb_context_st *ctx = (struct cb_context_st *)cookie;
67
+ struct cb_bucket_st *bucket = ctx->bucket;
68
+ VALUE key, cas, exc, res;
69
+
70
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
71
+ cb_strip_key_prefix(bucket, key);
72
+
73
+ cas = resp->v.v0.cas > 0 ? ULL2NUM(resp->v.v0.cas) : Qnil;
74
+ ctx->operation = storage_opcode_to_sym(operation);
75
+ exc = cb_check_error(error, "failed to store value", key);
76
+ if (exc != Qnil) {
77
+ rb_ivar_set(exc, cb_id_iv_cas, cas);
78
+ rb_ivar_set(exc, cb_id_iv_operation, ctx->operation);
79
+ ctx->exception = exc;
80
+ }
81
+
82
+ if (bucket->async) { /* asynchronous */
83
+ if (RTEST(ctx->observe_options)) {
84
+ VALUE args[2]; /* it's ok to pass pointer to stack struct here */
85
+ args[0] = rb_hash_new();
86
+ rb_hash_aset(args[0], key, cas);
87
+ args[1] = ctx->observe_options;
88
+ rb_block_call(bucket->self, cb_id_observe_and_wait, 2, args,
89
+ storage_observe_callback, (VALUE)ctx);
90
+ ctx->observe_options = Qnil;
91
+ } else if (ctx->proc != Qnil) {
92
+ res = rb_class_new_instance(0, NULL, cb_cResult);
93
+ rb_ivar_set(res, cb_id_iv_error, exc);
94
+ rb_ivar_set(res, cb_id_iv_key, key);
95
+ rb_ivar_set(res, cb_id_iv_operation, ctx->operation);
96
+ rb_ivar_set(res, cb_id_iv_cas, cas);
97
+ cb_proc_call(bucket, ctx->proc, 1, res);
98
+ }
99
+ } else { /* synchronous */
100
+ rb_hash_aset(ctx->rv, key, cas);
101
+ }
102
+
103
+ if (!RTEST(ctx->observe_options)) {
104
+ ctx->nqueries--;
105
+ if (ctx->nqueries == 0) {
106
+ ctx->proc = Qnil;
107
+ if (bucket->async) {
108
+ cb_context_free(ctx);
109
+ }
110
+ }
111
+ }
112
+ (void)handle;
113
+ }
114
+
115
+ static inline VALUE
116
+ cb_bucket_store(lcb_storage_t cmd, int argc, VALUE *argv, VALUE self)
117
+ {
118
+ struct cb_bucket_st *bucket = DATA_PTR(self);
119
+ struct cb_context_st *ctx;
120
+ VALUE rv, proc, exc, obs = Qnil;
121
+ lcb_error_t err;
122
+ struct cb_params_st params;
123
+
124
+ if (!cb_bucket_connected_bang(bucket, storage_opcode_to_sym(cmd))) {
125
+ return Qnil;
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
+ params.type = cb_cmd_store;
133
+ params.bucket = bucket;
134
+ params.cmd.store.operation = cmd;
135
+ cb_params_build(&params);
136
+ obs = params.cmd.store.observe;
137
+ ctx = cb_context_alloc(bucket);
138
+ if (!bucket->async) {
139
+ ctx->rv = rb_hash_new();
140
+ ctx->observe_options = obs;
141
+ }
142
+ ctx->proc = proc;
143
+ ctx->nqueries = params.cmd.store.num;
144
+ err = lcb_store(bucket->handle, (const void *)ctx,
145
+ params.cmd.store.num, params.cmd.store.ptr);
146
+ cb_params_destroy(&params);
147
+ exc = cb_check_error(err, "failed to schedule set request", Qnil);
148
+ if (exc != Qnil) {
149
+ cb_context_free(ctx);
150
+ rb_exc_raise(exc);
151
+ }
152
+ bucket->nbytes += params.npayload;
153
+ if (bucket->async) {
154
+ cb_maybe_do_loop(bucket);
155
+ return Qnil;
156
+ } else {
157
+ if (ctx->nqueries > 0) {
158
+ /* we have some operations pending */
159
+ lcb_wait(bucket->handle);
160
+ }
161
+ exc = ctx->exception;
162
+ rv = ctx->rv;
163
+ cb_context_free(ctx);
164
+ if (exc != Qnil) {
165
+ rb_exc_raise(exc);
166
+ }
167
+ exc = bucket->exception;
168
+ if (exc != Qnil) {
169
+ bucket->exception = Qnil;
170
+ rb_exc_raise(exc);
171
+ }
172
+ if (RTEST(obs)) {
173
+ rv = rb_funcall(bucket->self, cb_id_observe_and_wait, 2, rv, obs);
174
+ }
175
+ if (params.cmd.store.num > 1) {
176
+ return rv; /* return as a hash {key => cas, ...} */
177
+ } else {
178
+ VALUE vv = Qnil;
179
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
180
+ return vv;
181
+ }
182
+ }
183
+ }
184
+
185
+ /*
186
+ * Unconditionally store the object in the Couchbase
187
+ *
188
+ * @since 1.0.0
189
+ *
190
+ * @overload set(key, value, options = {})
191
+ *
192
+ * @param key [String, Symbol] Key used to reference the value.
193
+ * @param value [Object] Value to be stored
194
+ * @param options [Hash] Options for operation.
195
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
196
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
197
+ * absolute times (from the epoch).
198
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
199
+ * options. Flags are ignored by the server but preserved for use by the
200
+ * client. For more info see {Bucket#default_flags}.
201
+ * @option options [Symbol] :format (self.default_format) The
202
+ * representation for storing the value in the bucket. For more info see
203
+ * {Bucket#default_format}.
204
+ * @option options [Fixnum] :cas The CAS value for an object. This value is
205
+ * created on the server and is guaranteed to be unique for each value of
206
+ * a given key. This value is used to provide simple optimistic
207
+ * concurrency control when multiple clients or threads try to update an
208
+ * item simultaneously.
209
+ * @option options [Hash] :observe Apply persistence condition before
210
+ * returning result. When this option specified the library will observe
211
+ * given condition. See {Bucket#observe_and_wait}.
212
+ *
213
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
214
+ * (valid attributes: +error+, +operation+, +key+).
215
+ *
216
+ * @return [Fixnum] The CAS value of the object.
217
+ *
218
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect}).
219
+ * @raise [Couchbase::Error::KeyExists] if the key already exists on the
220
+ * server.
221
+ * @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
222
+ * with chosen encoder, e.g. if you try to store the Hash in +:plain+
223
+ * mode.
224
+ * @raise [ArgumentError] when passing the block in synchronous mode
225
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
226
+ * exceeds
227
+ *
228
+ * @example Store the key which will be expired in 2 seconds using relative TTL.
229
+ * c.set("foo", "bar", :ttl => 2)
230
+ *
231
+ * @example Perform multi-set operation. It takes a Hash store its keys/values into the bucket
232
+ * c.set("foo1" => "bar1", "foo2" => "bar2")
233
+ * #=> {"foo1" => cas1, "foo2" => cas2}
234
+ *
235
+ * @example More advanced multi-set using asynchronous mode
236
+ * c.run do
237
+ * # fire and forget
238
+ * c.set("foo1", "bar1", :ttl => 10)
239
+ * # receive result into the callback
240
+ * c.set("foo2", "bar2", :ttl => 10) do |ret|
241
+ * if ret.success?
242
+ * puts ret.cas
243
+ * end
244
+ * end
245
+ * end
246
+ *
247
+ * @example Store the key which will be expired in 2 seconds using absolute TTL.
248
+ * c.set("foo", "bar", :ttl => Time.now.to_i + 2)
249
+ *
250
+ * @example Force JSON document format for value
251
+ * c.set("foo", {"bar" => "baz}, :format => :document)
252
+ *
253
+ * @example Use hash-like syntax to store the value
254
+ * c["foo"] = {"bar" => "baz}
255
+ *
256
+ * @example Use extended hash-like syntax
257
+ * c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
258
+ * c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
259
+ *
260
+ * @example Set application specific flags (note that it will be OR-ed with format flags)
261
+ * c.set("foo", "bar", :flags => 0x1000)
262
+ *
263
+ * @example Perform optimistic locking by specifying last known CAS version
264
+ * c.set("foo", "bar", :cas => 8835713818674332672)
265
+ *
266
+ * @example Perform asynchronous call
267
+ * c.run do
268
+ * c.set("foo", "bar") do |ret|
269
+ * ret.operation #=> :set
270
+ * ret.success? #=> true
271
+ * ret.key #=> "foo"
272
+ * ret.cas
273
+ * end
274
+ * end
275
+ *
276
+ * @example Ensure that the key will be persisted at least on the one node
277
+ * c.set("foo", "bar", :observe => {:persisted => 1})
278
+ */
279
+ VALUE
280
+ cb_bucket_set(int argc, VALUE *argv, VALUE self)
281
+ {
282
+ return cb_bucket_store(LCB_SET, argc, argv, self);
283
+ }
284
+
285
+ /*
286
+ * Add the item to the database, but fail if the object exists already
287
+ *
288
+ * @since 1.0.0
289
+ *
290
+ * @overload add(key, value, options = {})
291
+ *
292
+ * @param key [String, Symbol] Key used to reference the value.
293
+ * @param value [Object] Value to be stored
294
+ * @param options [Hash] Options for operation.
295
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
296
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
297
+ * absolute times (from the epoch).
298
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
299
+ * options. Flags are ignored by the server but preserved for use by the
300
+ * client. For more info see {Bucket#default_flags}.
301
+ * @option options [Symbol] :format (self.default_format) The
302
+ * representation for storing the value in the bucket. For more info see
303
+ * {Bucket#default_format}.
304
+ * @option options [Fixnum] :cas The CAS value for an object. This value
305
+ * created on the server and is guaranteed to be unique for each value of
306
+ * a given key. This value is used to provide simple optimistic
307
+ * concurrency control when multiple clients or threads try to update an
308
+ * item simultaneously.
309
+ * @option options [Hash] :observe Apply persistence condition before
310
+ * returning result. When this option specified the library will observe
311
+ * given condition. See {Bucket#observe_and_wait}.
312
+ *
313
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
314
+ * (valid attributes: +error+, +operation+, +key+).
315
+ *
316
+ * @return [Fixnum] The CAS value of the object.
317
+ *
318
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
319
+ * @raise [Couchbase::Error::KeyExists] if the key already exists on the
320
+ * server
321
+ * @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
322
+ * with chosen encoder, e.g. if you try to store the Hash in +:plain+
323
+ * mode.
324
+ * @raise [ArgumentError] when passing the block in synchronous mode
325
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
326
+ * exceeds
327
+ *
328
+ * @example Add the same key twice
329
+ * c.add("foo", "bar") #=> stored successully
330
+ * c.add("foo", "baz") #=> will raise Couchbase::Error::KeyExists: failed to store value (key="foo", error=0x0c)
331
+ *
332
+ * @example Ensure that the key will be persisted at least on the one node
333
+ * c.add("foo", "bar", :observe => {:persisted => 1})
334
+ */
335
+ VALUE
336
+ cb_bucket_add(int argc, VALUE *argv, VALUE self)
337
+ {
338
+ return cb_bucket_store(LCB_ADD, argc, argv, self);
339
+ }
340
+
341
+ /*
342
+ * Replace the existing object in the database
343
+ *
344
+ * @since 1.0.0
345
+ *
346
+ * @overload replace(key, value, options = {})
347
+ * @param key [String, Symbol] Key used to reference the value.
348
+ * @param value [Object] Value to be stored
349
+ * @param options [Hash] Options for operation.
350
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
351
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
352
+ * absolute times (from the epoch).
353
+ * @option options [Fixnum] :flags (self.default_flags) Flags for storage
354
+ * options. Flags are ignored by the server but preserved for use by the
355
+ * client. For more info see {Bucket#default_flags}.
356
+ * @option options [Symbol] :format (self.default_format) The
357
+ * representation for storing the value in the bucket. For more info see
358
+ * {Bucket#default_format}.
359
+ * @option options [Fixnum] :cas The CAS value for an object. This value
360
+ * created on the server and is guaranteed to be unique for each value of
361
+ * a given key. This value is used to provide simple optimistic
362
+ * concurrency control when multiple clients or threads try to update an
363
+ * item simultaneously.
364
+ * @option options [Hash] :observe Apply persistence condition before
365
+ * returning result. When this option specified the library will observe
366
+ * given condition. See {Bucket#observe_and_wait}.
367
+ *
368
+ * @return [Fixnum] The CAS value of the object.
369
+ *
370
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
371
+ * @raise [Couchbase::Error::NotFound] if the key doesn't exists
372
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
373
+ * @raise [ArgumentError] when passing the block in synchronous mode
374
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
375
+ * exceeds
376
+ *
377
+ * @example Replacing missing key
378
+ * c.replace("foo", "baz") #=> will raise Couchbase::Error::NotFound: failed to store value (key="foo", error=0x0d)
379
+ *
380
+ * @example Ensure that the key will be persisted at least on the one node
381
+ * c.replace("foo", "bar", :observe => {:persisted => 1})
382
+ */
383
+ VALUE
384
+ cb_bucket_replace(int argc, VALUE *argv, VALUE self)
385
+ {
386
+ return cb_bucket_store(LCB_REPLACE, argc, argv, self);
387
+ }
388
+
389
+ /*
390
+ * Append this object to the existing object
391
+ *
392
+ * @since 1.0.0
393
+ *
394
+ * @note This operation is kind of data-aware from server point of view.
395
+ * This mean that the server treats value as binary stream and just
396
+ * perform concatenation, therefore it won't work with +:marshal+ and
397
+ * +:document+ formats, because of lack of knowledge how to merge values
398
+ * in these formats. See {Bucket#cas} for workaround.
399
+ *
400
+ * @overload append(key, value, options = {})
401
+ * @param key [String, Symbol] Key used to reference the value.
402
+ * @param value [Object] Value to be stored
403
+ * @param options [Hash] Options for operation.
404
+ * @option options [Fixnum] :cas The CAS value for an object. This value
405
+ * created on the server and is guaranteed to be unique for each value of
406
+ * a given key. This value is used to provide simple optimistic
407
+ * concurrency control when multiple clients or threads try to update an
408
+ * item simultaneously.
409
+ * @option options [Symbol] :format (self.default_format) The
410
+ * representation for storing the value in the bucket. For more info see
411
+ * {Bucket#default_format}.
412
+ * @option options [Hash] :observe Apply persistence condition before
413
+ * returning result. When this option specified the library will observe
414
+ * given condition. See {Bucket#observe_and_wait}.
415
+ *
416
+ * @return [Fixnum] The CAS value of the object.
417
+ *
418
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
419
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
420
+ * @raise [Couchbase::Error::NotStored] if the key doesn't exist
421
+ * @raise [ArgumentError] when passing the block in synchronous mode
422
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
423
+ * exceeds
424
+ *
425
+ * @example Simple append
426
+ * c.set("foo", "aaa")
427
+ * c.append("foo", "bbb")
428
+ * c.get("foo") #=> "aaabbb"
429
+ *
430
+ * @example Implementing sets using append
431
+ * def set_add(key, *values)
432
+ * encoded = values.flatten.map{|v| "+#{v} "}.join
433
+ * append(key, encoded)
434
+ * end
435
+ *
436
+ * def set_remove(key, *values)
437
+ * encoded = values.flatten.map{|v| "-#{v} "}.join
438
+ * append(key, encoded)
439
+ * end
440
+ *
441
+ * def set_get(key)
442
+ * encoded = get(key)
443
+ * ret = Set.new
444
+ * encoded.split(' ').each do |v|
445
+ * op, val = v[0], v[1..-1]
446
+ * case op
447
+ * when "-"
448
+ * ret.delete(val)
449
+ * when "+"
450
+ * ret.add(val)
451
+ * end
452
+ * end
453
+ * ret
454
+ * end
455
+ *
456
+ * @example Using optimistic locking. The operation will fail on CAS mismatch
457
+ * ver = c.set("foo", "aaa")
458
+ * c.append("foo", "bbb", :cas => ver)
459
+ *
460
+ * @example Ensure that the key will be persisted at least on the one node
461
+ * c.append("foo", "bar", :observe => {:persisted => 1})
462
+ */
463
+ VALUE
464
+ cb_bucket_append(int argc, VALUE *argv, VALUE self)
465
+ {
466
+ return cb_bucket_store(LCB_APPEND, argc, argv, self);
467
+ }
468
+
469
+ /*
470
+ * Prepend this object to the existing object
471
+ *
472
+ * @since 1.0.0
473
+ *
474
+ * @note This operation is kind of data-aware from server point of view.
475
+ * This mean that the server treats value as binary stream and just
476
+ * perform concatenation, therefore it won't work with +:marshal+ and
477
+ * +:document+ formats, because of lack of knowledge how to merge values
478
+ * in these formats. See {Bucket#cas} for workaround.
479
+ *
480
+ * @overload prepend(key, value, options = {})
481
+ * @param key [String, Symbol] Key used to reference the value.
482
+ * @param value [Object] Value to be stored
483
+ * @param options [Hash] Options for operation.
484
+ * @option options [Fixnum] :cas The CAS value for an object. This value
485
+ * created on the server and is guaranteed to be unique for each value of
486
+ * a given key. This value is used to provide simple optimistic
487
+ * concurrency control when multiple clients or threads try to update an
488
+ * item simultaneously.
489
+ * @option options [Symbol] :format (self.default_format) The
490
+ * representation for storing the value in the bucket. For more info see
491
+ * {Bucket#default_format}.
492
+ * @option options [Hash] :observe Apply persistence condition before
493
+ * returning result. When this option specified the library will observe
494
+ * given condition. See {Bucket#observe_and_wait}.
495
+ *
496
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
497
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
498
+ * @raise [Couchbase::Error::NotStored] if the key doesn't exist
499
+ * @raise [ArgumentError] when passing the block in synchronous mode
500
+ * @raise [Couchbase::Error::Timeout] if timeout interval for observe
501
+ * exceeds
502
+ *
503
+ * @example Simple prepend example
504
+ * c.set("foo", "aaa")
505
+ * c.prepend("foo", "bbb")
506
+ * c.get("foo") #=> "bbbaaa"
507
+ *
508
+ * @example Using explicit format option
509
+ * c.default_format #=> :document
510
+ * c.set("foo", {"y" => "z"})
511
+ * c.prepend("foo", '[', :format => :plain)
512
+ * c.append("foo", ', {"z": "y"}]', :format => :plain)
513
+ * c.get("foo") #=> [{"y"=>"z"}, {"z"=>"y"}]
514
+ *
515
+ * @example Using optimistic locking. The operation will fail on CAS mismatch
516
+ * ver = c.set("foo", "aaa")
517
+ * c.prepend("foo", "bbb", :cas => ver)
518
+ *
519
+ * @example Ensure that the key will be persisted at least on the one node
520
+ * c.prepend("foo", "bar", :observe => {:persisted => 1})
521
+ */
522
+ VALUE
523
+ cb_bucket_prepend(int argc, VALUE *argv, VALUE self)
524
+ {
525
+ return cb_bucket_store(LCB_PREPEND, argc, argv, self);
526
+ }
527
+
528
+ VALUE
529
+ cb_bucket_aset(int argc, VALUE *argv, VALUE self)
530
+ {
531
+ VALUE temp;
532
+
533
+ if (argc == 3) {
534
+ /* swap opts and value, because value goes last for []= */
535
+ temp = argv[2];
536
+ argv[2] = argv[1];
537
+ argv[1] = temp;
538
+ }
539
+ return cb_bucket_set(argc, argv, self);
540
+ }
541
+
542
+