couchbase 1.0.0 → 1.1.0
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.
- data/.gitignore +2 -0
- data/.travis.yml +11 -0
- data/HISTORY.markdown +17 -0
- data/README.markdown +99 -59
- data/couchbase.gemspec +1 -0
- data/ext/couchbase_ext/couchbase_ext.c +1557 -425
- data/ext/couchbase_ext/extconf.rb +60 -49
- data/lib/couchbase.rb +41 -7
- data/lib/couchbase/bucket.rb +5 -3
- data/lib/couchbase/version.rb +1 -1
- data/tasks/compile.rake +72 -0
- data/tasks/test.rake +2 -2
- data/test/setup.rb +52 -2
- data/test/test_arithmetic.rb +37 -26
- data/test/test_async.rb +68 -44
- data/test/test_bucket.rb +130 -45
- data/test/test_cas.rb +8 -8
- data/test/test_couchbase.rb +1 -1
- data/test/test_delete.rb +15 -15
- data/test/test_errors.rb +6 -6
- data/test/test_flush.rb +3 -3
- data/test/test_format.rb +14 -14
- data/test/test_get.rb +144 -69
- data/test/test_stats.rb +18 -14
- data/test/test_store.rb +40 -40
- data/test/test_touch.rb +26 -14
- data/test/test_version.rb +30 -2
- metadata +34 -17
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
before_install:
|
2
|
+
- wget -O- http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add -
|
3
|
+
- echo deb http://packages.couchbase.com/ubuntu lucid lucid/main | sudo tee /etc/apt/sources.list.d/couchbase.list
|
4
|
+
- sudo apt-get update
|
5
|
+
- sudo apt-get -y install libevent-dev libvbucket-dev libcouchbase-dev
|
6
|
+
|
7
|
+
rvm:
|
8
|
+
- 1.8.7
|
9
|
+
- 1.9.2
|
10
|
+
- 1.9.3
|
11
|
+
- ree
|
data/HISTORY.markdown
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 1.1.0 / 2012-03-07
|
2
|
+
|
3
|
+
* Timeout support (CCBC-20)
|
4
|
+
* Implement disconnect/reconnect interface
|
5
|
+
* Improve error handling code
|
6
|
+
* Use URI parser from stdlib
|
7
|
+
* Improve the documentation and the tests
|
8
|
+
* Remove direct dependency on libevent
|
9
|
+
* Remove sasl dependency
|
10
|
+
* Fix storing empty line and nil
|
11
|
+
* Allow running tests on real cluster
|
12
|
+
* Cross-build for windows
|
13
|
+
* Keep connections in thread local storage
|
14
|
+
* Add block execution time to timeout
|
15
|
+
* Implement VERSION command
|
16
|
+
* Configure Travis-CI
|
17
|
+
|
1
18
|
## 1.0.0 / 2011-12-23
|
2
19
|
|
3
20
|
* Implement all operations using libcouchbase as backend
|
data/README.markdown
CHANGED
@@ -1,59 +1,67 @@
|
|
1
|
-
# Couchbase Ruby Client
|
1
|
+
# Couchbase Ruby Client [](http://travis-ci.org/avsej/couchbase-ruby-client)
|
2
2
|
|
3
3
|
This is the official client library for use with Couchbase Server.
|
4
4
|
|
5
|
-
##
|
5
|
+
## SUPPORT
|
6
|
+
|
7
|
+
If you found an issue, please file it in our [JIRA][1]. Also you are
|
8
|
+
always welcome on `#libcouchbase` channel at [freenode.net IRC servers][2].
|
6
9
|
|
7
|
-
|
8
|
-
Couchbase Server. For JSON it uses [yajl-ruby][1] which is built atop
|
9
|
-
[yajl][2]. For Couchbase iteraction it uses [libcouchbase][3]. The first
|
10
|
-
dependency shouldn't cause any issues because it will bundle yajl in the c
|
11
|
-
extensions. To install yajl-ruby use following command:
|
10
|
+
Documentation: [http://rdoc.info/gems/couchbase](http://rdoc.info/gems/couchbase)
|
12
11
|
|
13
|
-
|
12
|
+
## INSTALL
|
14
13
|
|
15
|
-
|
14
|
+
This gem depends [libcouchbase][3]. In most cases installing
|
15
|
+
libcouchbase doesn't take much effort.
|
16
16
|
|
17
17
|
### MacOS (Homebrew)
|
18
18
|
|
19
19
|
$ brew install libcouchbase
|
20
20
|
|
21
|
-
Or if our pull
|
21
|
+
Or if [our pull requests][4] for isn't yet merged:
|
22
22
|
|
23
|
-
$ brew install
|
24
|
-
$ brew install
|
23
|
+
$ brew install https://github.com/avsej/homebrew/raw/libvbucket/Library/Formula/libvbucket.rb
|
24
|
+
$ brew install https://github.com/avsej/homebrew/raw/libcouchbase/Library/Formula/libcouchbase.rb
|
25
25
|
|
26
26
|
### Debian (Ubuntu)
|
27
27
|
|
28
|
-
|
28
|
+
Add the appropriate line to /etc/apt/sources.list.d/couchbase.list for
|
29
|
+
your OS release:
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
# Ubuntu 11.10 Oneiric Ocelot (Debian unstable)
|
32
|
+
deb http://packages.couchbase.com/ubuntu oneiric oneiric/main
|
32
33
|
|
33
|
-
or
|
34
|
+
# Ubuntu 10.04 Lucid Lynx (Debian stable or testing)
|
35
|
+
deb http://packages.couchbase.com/ubuntu lucid lucid/main
|
34
36
|
|
35
|
-
|
36
|
-
$ wget http://packages.couchbase.com/clients/c/libcouchbase{1,1-dbg,-dev}_1.0.0-1_i386.deb
|
37
|
+
Import Couchbase PGP key:
|
37
38
|
|
38
|
-
|
39
|
+
wget -O- http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add -
|
39
40
|
|
40
|
-
|
41
|
+
Then install them
|
42
|
+
|
43
|
+
$ sudo apt-get update && sudo apt-get install libcouchbase-dev
|
41
44
|
|
42
45
|
### Centos (Redhat and rpm-based systems)
|
43
46
|
|
44
|
-
|
47
|
+
Add these lines to /etc/yum.repos.d/couchbase.repo using the correct architecture
|
48
|
+
|
49
|
+
[couchbase]
|
50
|
+
name = Couchbase package repository
|
51
|
+
baseurl = http://packages.couchbase.com/rpm/5.5/i386
|
45
52
|
|
46
|
-
|
47
|
-
|
53
|
+
[couchbase]
|
54
|
+
name = Couchbase package repository
|
55
|
+
baseurl = http:///packages.couchbase.com/rpm/5.5/x86_64
|
48
56
|
|
49
|
-
|
57
|
+
Then to install libcouchbase itself, run:
|
50
58
|
|
51
|
-
$
|
52
|
-
$ wget http://packages.couchbase.com/clients/c/libcouchbase{1,-debuginfo,-devel}-1.0.0-1.i386.rpm
|
59
|
+
$ sudo yum update && sudo yum install libcouchbase-devel
|
53
60
|
|
54
|
-
|
61
|
+
### Windows
|
55
62
|
|
56
|
-
|
63
|
+
There no additional dependencies for Windows systems. The gem carry
|
64
|
+
prebuilt binary for it.
|
57
65
|
|
58
66
|
### Couchbase gem
|
59
67
|
|
@@ -61,7 +69,6 @@ Now install the couchbase gem itself
|
|
61
69
|
|
62
70
|
$ gem install couchbase
|
63
71
|
|
64
|
-
|
65
72
|
## USAGE
|
66
73
|
|
67
74
|
First of all you need to load library:
|
@@ -87,20 +94,13 @@ This is equivalent to following forms:
|
|
87
94
|
|
88
95
|
The hash parameters take precedence on string URL.
|
89
96
|
|
90
|
-
The library supports both synchronous and asynchronous mode.
|
91
|
-
|
92
|
-
|
93
|
-
c = Couchbase.new(:async => true)
|
94
|
-
# ... asynchronous mode
|
95
|
-
c.async = false
|
96
|
-
# ... synchronous mode
|
97
|
-
|
98
|
-
In asynchronous mode all operations will return control to caller
|
97
|
+
The library supports both synchronous and asynchronous mode. In
|
98
|
+
asynchronous mode all operations will return control to caller
|
99
99
|
without blocking current thread. You can pass the block to method and it
|
100
100
|
will be called with result when the operation will be completed. You
|
101
101
|
need to run event loop when you scheduled your operations:
|
102
102
|
|
103
|
-
c = Couchbase.new
|
103
|
+
c = Couchbase.new
|
104
104
|
c.run do |conn|
|
105
105
|
conn.get("foo") {|ret| puts ret.value}
|
106
106
|
conn.set("bar", "baz")
|
@@ -139,10 +139,10 @@ To handle global errors in async mode `#on_error` callback should be
|
|
139
139
|
used. It can be set in following fashions:
|
140
140
|
|
141
141
|
c.on_error do |opcode, key, exc|
|
142
|
-
...
|
142
|
+
# ...
|
143
143
|
end
|
144
144
|
|
145
|
-
handler = lambda {|opcode, key, exc|
|
145
|
+
handler = lambda {|opcode, key, exc| }
|
146
146
|
c.on_error = handler
|
147
147
|
|
148
148
|
By default connection uses `:quiet` mode. This mean it won't raise
|
@@ -156,7 +156,7 @@ You can turn on these exception by passing `:quiet => false` when you
|
|
156
156
|
are instantiating the connection or change corresponding attribute:
|
157
157
|
|
158
158
|
c.quiet = false
|
159
|
-
c.get("missing-key")
|
159
|
+
c.get("missing-key") #=> raise Couchbase::Error::NotFound
|
160
160
|
c.get("missing-key", :quiet => true) #=> nil
|
161
161
|
|
162
162
|
The library supports three different formats for representing values:
|
@@ -174,7 +174,7 @@ The library supports three different formats for representing values:
|
|
174
174
|
* `:plain` Use this format if you'd like to transparently serialize your
|
175
175
|
ruby object with standard `Marshal.dump` and `Marshal.load` methods
|
176
176
|
|
177
|
-
The couchbase API is the superset of [Memcached binary protocol][
|
177
|
+
The couchbase API is the superset of [Memcached binary protocol][5], so
|
178
178
|
you can use its operations.
|
179
179
|
|
180
180
|
### Get
|
@@ -190,8 +190,17 @@ Get multiple values. In quiet mode will put `nil` values on missing
|
|
190
190
|
positions:
|
191
191
|
|
192
192
|
vals = c.get("foo", "bar", "baz")
|
193
|
-
c.get("foo"
|
194
|
-
c.
|
193
|
+
val_foo, val_bar, val_baz = c.get("foo", "bar", "baz")
|
194
|
+
c.run do
|
195
|
+
c.get("foo") do |ret|
|
196
|
+
ret.success?
|
197
|
+
ret.error
|
198
|
+
ret.key
|
199
|
+
ret.value
|
200
|
+
ret.flags
|
201
|
+
ret.cas
|
202
|
+
end
|
203
|
+
end
|
195
204
|
|
196
205
|
Get multiple values with extended information. The result will
|
197
206
|
represented by hash with tuples `[value, flags, cas]` as a value.
|
@@ -214,7 +223,7 @@ Hash-like syntax
|
|
214
223
|
c.touch("foo", 10)
|
215
224
|
c.touch("foo", :ttl => 10)
|
216
225
|
c.touch("foo" => 10, "bar" => 20)
|
217
|
-
c.touch("foo" => 10, "bar" => 20){|key, success|
|
226
|
+
c.touch("foo" => 10, "bar" => 20){|key, success| }
|
218
227
|
|
219
228
|
### Set
|
220
229
|
|
@@ -222,9 +231,9 @@ Hash-like syntax
|
|
222
231
|
c.set("foo", "bar", :flags => 0x1000, :ttl => 30, :format => :plain)
|
223
232
|
c["foo"] = "bar"
|
224
233
|
c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
|
225
|
-
c["foo", :flags => 0x1000] = "bar"
|
234
|
+
c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
|
226
235
|
c.set("foo", "bar", :cas => 8835713818674332672)
|
227
|
-
c.set("foo", "bar"){|cas, key, operation|
|
236
|
+
c.set("foo", "bar"){|cas, key, operation| }
|
228
237
|
|
229
238
|
### Add
|
230
239
|
|
@@ -264,12 +273,24 @@ Couchbase::Error::DeltaBadval if the delta or value is not a number.
|
|
264
273
|
c.incr("foo", 4) #=> 8
|
265
274
|
c.incr("foo", -1) #=> 7
|
266
275
|
c.incr("foo", -100) #=> 0
|
267
|
-
c.
|
276
|
+
c.run do
|
277
|
+
c.incr("foo") do |ret|
|
278
|
+
ret.success?
|
279
|
+
ret.value
|
280
|
+
ret.cas
|
281
|
+
end
|
282
|
+
end
|
268
283
|
|
269
284
|
c.set("foo", 10)
|
270
285
|
c.decr("foo", 1) #=> 9
|
271
286
|
c.decr("foo", 100) #=> 0
|
272
|
-
c.
|
287
|
+
c.run do
|
288
|
+
c.decr("foo") do |ret|
|
289
|
+
ret.success?
|
290
|
+
ret.value
|
291
|
+
ret.cas
|
292
|
+
end
|
293
|
+
end
|
273
294
|
|
274
295
|
c.incr("missing1", :initial => 10) #=> 10
|
275
296
|
c.incr("missing1", :initial => 10) #=> 11
|
@@ -287,21 +308,39 @@ performed on client side with following `set` operation:
|
|
287
308
|
c.delete("foo")
|
288
309
|
c.delete("foo", :cas => 8835713818674332672)
|
289
310
|
c.delete("foo", 8835713818674332672)
|
290
|
-
c.
|
311
|
+
c.run do
|
312
|
+
c.delete do |ret|
|
313
|
+
ret.success?
|
314
|
+
ret.key
|
315
|
+
end
|
316
|
+
end
|
291
317
|
|
292
318
|
### Flush
|
293
319
|
|
294
320
|
Flush the items in the cluster.
|
295
321
|
|
296
322
|
c.flush
|
297
|
-
c.
|
323
|
+
c.run do
|
324
|
+
c.flush do |ret|
|
325
|
+
ret.success?
|
326
|
+
ret.node
|
327
|
+
end
|
328
|
+
end
|
298
329
|
|
299
330
|
### Stats
|
300
331
|
|
301
332
|
Return statistics from each node in the cluster
|
302
333
|
|
303
334
|
c.stats
|
304
|
-
c.stats
|
335
|
+
c.stats(:memory)
|
336
|
+
c.run do
|
337
|
+
c.stats do |ret|
|
338
|
+
ret.success?
|
339
|
+
ret.node
|
340
|
+
ret.key
|
341
|
+
ret.value
|
342
|
+
end
|
343
|
+
end
|
305
344
|
|
306
345
|
The result is represented as a hash with the server node address as
|
307
346
|
the key and stats as key-value pairs.
|
@@ -312,19 +351,20 @@ the key and stats as key-value pairs.
|
|
312
351
|
"threads"=>"4",
|
313
352
|
"connection_structures"=>"22",
|
314
353
|
"ep_max_txn_size"=>"10000",
|
315
|
-
...
|
354
|
+
# ...
|
316
355
|
},
|
317
356
|
"172.16.16.76:12000"=>
|
318
357
|
{
|
319
358
|
"threads"=>"4",
|
320
359
|
"connection_structures"=>"447",
|
321
360
|
"ep_max_txn_size"=>"10000",
|
322
|
-
...
|
361
|
+
# ...
|
323
362
|
},
|
324
|
-
...
|
363
|
+
# ...
|
325
364
|
}
|
326
365
|
|
327
|
-
[1]:
|
328
|
-
[2]: http://
|
366
|
+
[1]: http://couchbase.com/issues/browse/RCBC
|
367
|
+
[2]: http://freenode.net/irc_servers.shtml
|
329
368
|
[3]: http://www.couchbase.com/develop/c/current
|
330
|
-
[4]:
|
369
|
+
[4]: https://github.com/mxcl/homebrew/pulls/avsej
|
370
|
+
[5]: http://code.google.com/p/memcached/wiki/BinaryProtocolRevamped
|
data/couchbase.gemspec
CHANGED
@@ -42,5 +42,6 @@ Gem::Specification.new do |s|
|
|
42
42
|
s.add_development_dependency 'rake-compiler', '>= 0.7.5'
|
43
43
|
s.add_development_dependency 'rdiscount'
|
44
44
|
s.add_development_dependency 'yard'
|
45
|
+
s.add_development_dependency 'mini_portile'
|
45
46
|
s.add_development_dependency RUBY_VERSION =~ /^1\.9/ ? 'ruby-debug19' : 'ruby-debug'
|
46
47
|
end
|
@@ -20,8 +20,8 @@
|
|
20
20
|
#include <st.h>
|
21
21
|
#endif
|
22
22
|
|
23
|
+
#include <time.h>
|
23
24
|
#include <libcouchbase/couchbase.h>
|
24
|
-
#include <event.h>
|
25
25
|
#include "couchbase_config.h"
|
26
26
|
|
27
27
|
#ifdef HAVE_STDARG_PROTOTYPES
|
@@ -60,7 +60,8 @@ typedef struct
|
|
60
60
|
long seqno;
|
61
61
|
VALUE default_format; /* should update +default_flags+ on change */
|
62
62
|
uint32_t default_flags;
|
63
|
-
|
63
|
+
time_t default_ttl;
|
64
|
+
uint32_t timeout;
|
64
65
|
VALUE exception; /* error delivered by error_callback */
|
65
66
|
VALUE on_error_proc; /* is using to deliver errors in async mode */
|
66
67
|
} bucket_t;
|
@@ -81,14 +82,15 @@ struct key_traits
|
|
81
82
|
VALUE keys_ary;
|
82
83
|
size_t nkeys;
|
83
84
|
char **keys;
|
84
|
-
|
85
|
+
libcouchbase_size_t *lens;
|
85
86
|
time_t *ttls;
|
86
87
|
int extended;
|
87
88
|
int explicit_ttl;
|
88
89
|
int quiet;
|
90
|
+
int mgat;
|
89
91
|
};
|
90
92
|
|
91
|
-
static VALUE mCouchbase, mError, mJSON, mMarshal, cBucket, cResult;
|
93
|
+
static VALUE mCouchbase, mError, mJSON, mURI, mMarshal, cBucket, cResult;
|
92
94
|
static VALUE object_space;
|
93
95
|
|
94
96
|
static ID sym_add,
|
@@ -99,6 +101,7 @@ static ID sym_add,
|
|
99
101
|
sym_decrement,
|
100
102
|
sym_default_flags,
|
101
103
|
sym_default_format,
|
104
|
+
sym_default_ttl,
|
102
105
|
sym_delete,
|
103
106
|
sym_document,
|
104
107
|
sym_extended,
|
@@ -119,35 +122,33 @@ static ID sym_add,
|
|
119
122
|
sym_replace,
|
120
123
|
sym_set,
|
121
124
|
sym_stats,
|
125
|
+
sym_timeout,
|
122
126
|
sym_touch,
|
123
127
|
sym_ttl,
|
124
128
|
sym_username,
|
129
|
+
sym_version,
|
125
130
|
id_arity,
|
126
131
|
id_call,
|
127
132
|
id_dump,
|
128
133
|
id_flatten_bang,
|
129
134
|
id_has_key_p,
|
130
|
-
|
131
|
-
id_to_s,
|
132
|
-
id_iv_authority,
|
133
|
-
id_iv_bucket,
|
135
|
+
id_host,
|
134
136
|
id_iv_cas,
|
135
|
-
id_iv_default_flags,
|
136
|
-
id_iv_default_format,
|
137
137
|
id_iv_error,
|
138
138
|
id_iv_flags,
|
139
|
-
id_iv_hostname,
|
140
139
|
id_iv_key,
|
141
140
|
id_iv_node,
|
142
|
-
id_iv_on_error,
|
143
141
|
id_iv_operation,
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
142
|
+
id_iv_value,
|
143
|
+
id_load,
|
144
|
+
id_match,
|
145
|
+
id_parse,
|
146
|
+
id_password,
|
147
|
+
id_path,
|
148
|
+
id_port,
|
149
|
+
id_scheme,
|
150
|
+
id_to_s,
|
151
|
+
id_user;
|
151
152
|
|
152
153
|
/* base error */
|
153
154
|
static VALUE eBaseError;
|
@@ -176,6 +177,9 @@ static VALUE eNotSupportedError; /*LIBCOUCHBASE_NOT_SUPPORTED = 0x12*/
|
|
176
177
|
static VALUE eUnknownCommandError; /*LIBCOUCHBASE_UNKNOWN_COMMAND = 0x13*/
|
177
178
|
static VALUE eUnknownHostError; /*LIBCOUCHBASE_UNKNOWN_HOST = 0x14*/
|
178
179
|
static VALUE eProtocolError; /*LIBCOUCHBASE_PROTOCOL_ERROR = 0x15*/
|
180
|
+
static VALUE eTimeoutError; /*LIBCOUCHBASE_ETIMEDOUT = 0x16*/
|
181
|
+
static VALUE eConnectError; /*LIBCOUCHBASE_CONNECT_ERROR = 0x17*/
|
182
|
+
static VALUE eBucketNotFoundError; /*LIBCOUCHBASE_BUCKET_ENOENT = 0x18*/
|
179
183
|
|
180
184
|
static VALUE
|
181
185
|
cb_proc_call(VALUE recv, int argc, ...)
|
@@ -274,6 +278,16 @@ cb_check_error(libcouchbase_error_t rc, const char *msg, VALUE key)
|
|
274
278
|
break;
|
275
279
|
case LIBCOUCHBASE_PROTOCOL_ERROR:
|
276
280
|
klass = eProtocolError;
|
281
|
+
break;
|
282
|
+
case LIBCOUCHBASE_ETIMEDOUT:
|
283
|
+
klass = eTimeoutError;
|
284
|
+
break;
|
285
|
+
case LIBCOUCHBASE_CONNECT_ERROR:
|
286
|
+
klass = eConnectError;
|
287
|
+
break;
|
288
|
+
case LIBCOUCHBASE_BUCKET_ENOENT:
|
289
|
+
klass = eBucketNotFoundError;
|
290
|
+
break;
|
277
291
|
case LIBCOUCHBASE_ERROR:
|
278
292
|
/* fall through */
|
279
293
|
default:
|
@@ -286,7 +300,7 @@ cb_check_error(libcouchbase_error_t rc, const char *msg, VALUE key)
|
|
286
300
|
snprintf(buf, 300, "key=\"%s\", ", RSTRING_PTR(key));
|
287
301
|
rb_str_buf_cat2(str, buf);
|
288
302
|
}
|
289
|
-
snprintf(buf, 300, "error=0x%
|
303
|
+
snprintf(buf, 300, "error=0x%02x)", rc);
|
290
304
|
rb_str_buf_cat2(str, buf);
|
291
305
|
exc = rb_exc_new3(klass, str);
|
292
306
|
rb_ivar_set(exc, id_iv_error, INT2FIX(rc));
|
@@ -433,44 +447,20 @@ cb_extract_keys_i(VALUE key, VALUE value, VALUE arg)
|
|
433
447
|
static long
|
434
448
|
cb_args_scan_keys(long argc, VALUE argv, bucket_t *bucket, struct key_traits *traits)
|
435
449
|
{
|
436
|
-
VALUE
|
450
|
+
VALUE key, *keys_ptr, opts, ttl, ext;
|
437
451
|
long nn = 0, ii;
|
438
452
|
time_t exp;
|
439
453
|
|
440
454
|
traits->keys_ary = rb_ary_new();
|
441
455
|
traits->quiet = bucket->quiet;
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
case T_HASH:
|
446
|
-
/* hash of key-ttl pairs */
|
447
|
-
nn = RHASH_SIZE(arg);
|
448
|
-
traits->keys = calloc(nn, sizeof(char *));
|
449
|
-
traits->lens = calloc(nn, sizeof(size_t));
|
450
|
-
traits->explicit_ttl = 1;
|
451
|
-
traits->ttls = calloc(nn, sizeof(time_t));
|
452
|
-
rb_hash_foreach(arg, cb_extract_keys_i, (VALUE)traits);
|
453
|
-
break;
|
454
|
-
case T_STRING:
|
455
|
-
case T_SYMBOL:
|
456
|
-
/* single key with default expiration */
|
457
|
-
nn = traits->nkeys = 1;
|
458
|
-
traits->keys = calloc(nn, sizeof(char *));
|
459
|
-
traits->lens = calloc(nn, sizeof(size_t));
|
460
|
-
traits->ttls = calloc(nn, sizeof(time_t));
|
461
|
-
key = unify_key(arg);
|
462
|
-
rb_ary_push(traits->keys_ary, key);
|
463
|
-
traits->keys[0] = RSTRING_PTR(key);
|
464
|
-
traits->lens[0] = RSTRING_LEN(key);
|
465
|
-
traits->ttls[0] = bucket->default_ttl;
|
466
|
-
break;
|
467
|
-
}
|
468
|
-
} else if (argc > 1) {
|
456
|
+
traits->mgat = 0;
|
457
|
+
|
458
|
+
if (argc > 0) {
|
469
459
|
/* keys with custom options */
|
470
460
|
opts = RARRAY_PTR(argv)[argc-1];
|
471
461
|
exp = bucket->default_ttl;
|
472
462
|
ext = Qfalse;
|
473
|
-
if (TYPE(opts) == T_HASH) {
|
463
|
+
if (argc > 1 && TYPE(opts) == T_HASH) {
|
474
464
|
(void)rb_ary_pop(argv);
|
475
465
|
if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
|
476
466
|
traits->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
|
@@ -488,18 +478,30 @@ cb_args_scan_keys(long argc, VALUE argv, bucket_t *bucket, struct key_traits *tr
|
|
488
478
|
if (nn < 1) {
|
489
479
|
rb_raise(rb_eArgError, "must be at least one key");
|
490
480
|
}
|
491
|
-
traits->nkeys = nn;
|
492
|
-
traits->extended = RTEST(ext) ? 1 : 0;
|
493
|
-
traits->keys = calloc(nn, sizeof(char *));
|
494
|
-
traits->lens = calloc(nn, sizeof(size_t));
|
495
|
-
traits->ttls = calloc(nn, sizeof(time_t));
|
496
481
|
keys_ptr = RARRAY_PTR(argv);
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
traits->
|
502
|
-
traits->
|
482
|
+
traits->extended = RTEST(ext) ? 1 : 0;
|
483
|
+
if (nn == 1 && TYPE(keys_ptr[0]) == T_HASH) {
|
484
|
+
/* hash of key-ttl pairs */
|
485
|
+
nn = RHASH_SIZE(keys_ptr[0]);
|
486
|
+
traits->keys = calloc(nn, sizeof(char *));
|
487
|
+
traits->lens = calloc(nn, sizeof(size_t));
|
488
|
+
traits->explicit_ttl = 1;
|
489
|
+
traits->mgat = 1;
|
490
|
+
traits->ttls = calloc(nn, sizeof(time_t));
|
491
|
+
rb_hash_foreach(keys_ptr[0], cb_extract_keys_i, (VALUE)traits);
|
492
|
+
} else {
|
493
|
+
/* the list of keys */
|
494
|
+
traits->nkeys = nn;
|
495
|
+
traits->keys = calloc(nn, sizeof(char *));
|
496
|
+
traits->lens = calloc(nn, sizeof(size_t));
|
497
|
+
traits->ttls = calloc(nn, sizeof(time_t));
|
498
|
+
for (ii = 0; ii < nn; ii++) {
|
499
|
+
key = unify_key(keys_ptr[ii]);
|
500
|
+
rb_ary_push(traits->keys_ary, key);
|
501
|
+
traits->keys[ii] = RSTRING_PTR(key);
|
502
|
+
traits->lens[ii] = RSTRING_LEN(key);
|
503
|
+
traits->ttls[ii] = exp;
|
504
|
+
}
|
503
505
|
}
|
504
506
|
}
|
505
507
|
|
@@ -518,7 +520,7 @@ error_callback(libcouchbase_t handle, libcouchbase_error_t error, const char *er
|
|
518
520
|
static void
|
519
521
|
storage_callback(libcouchbase_t handle, const void *cookie,
|
520
522
|
libcouchbase_storage_t operation, libcouchbase_error_t error,
|
521
|
-
const void *key,
|
523
|
+
const void *key, libcouchbase_size_t nkey, libcouchbase_cas_t cas)
|
522
524
|
{
|
523
525
|
context_t *ctx = (context_t *)cookie;
|
524
526
|
bucket_t *bucket = ctx->bucket;
|
@@ -578,7 +580,8 @@ storage_callback(libcouchbase_t handle, const void *cookie,
|
|
578
580
|
|
579
581
|
static void
|
580
582
|
delete_callback(libcouchbase_t handle, const void *cookie,
|
581
|
-
libcouchbase_error_t error, const void *key,
|
583
|
+
libcouchbase_error_t error, const void *key,
|
584
|
+
libcouchbase_size_t nkey)
|
582
585
|
{
|
583
586
|
context_t *ctx = (context_t *)cookie;
|
584
587
|
bucket_t *bucket = ctx->bucket;
|
@@ -616,8 +619,10 @@ delete_callback(libcouchbase_t handle, const void *cookie,
|
|
616
619
|
|
617
620
|
static void
|
618
621
|
get_callback(libcouchbase_t handle, const void *cookie,
|
619
|
-
libcouchbase_error_t error, const void *key,
|
620
|
-
const void *bytes,
|
622
|
+
libcouchbase_error_t error, const void *key,
|
623
|
+
libcouchbase_size_t nkey, const void *bytes,
|
624
|
+
libcouchbase_size_t nbytes, libcouchbase_uint32_t flags,
|
625
|
+
libcouchbase_cas_t cas)
|
621
626
|
{
|
622
627
|
context_t *ctx = (context_t *)cookie;
|
623
628
|
bucket_t *bucket = ctx->bucket;
|
@@ -645,7 +650,11 @@ get_callback(libcouchbase_t handle, const void *cookie,
|
|
645
650
|
v = Qnil;
|
646
651
|
}
|
647
652
|
} else {
|
648
|
-
|
653
|
+
if (flags_get_format(flags) == sym_plain) {
|
654
|
+
v = rb_str_new2("");
|
655
|
+
} else {
|
656
|
+
v = Qnil;
|
657
|
+
}
|
649
658
|
}
|
650
659
|
if (bucket->async) { /* asynchronous */
|
651
660
|
if (ctx->proc != Qnil) {
|
@@ -659,7 +668,7 @@ get_callback(libcouchbase_t handle, const void *cookie,
|
|
659
668
|
cb_proc_call(ctx->proc, 1, res);
|
660
669
|
}
|
661
670
|
} else { /* synchronous */
|
662
|
-
if (NIL_P(exc) &&
|
671
|
+
if (NIL_P(exc) && error != LIBCOUCHBASE_KEY_ENOENT) {
|
663
672
|
if (ctx->extended) {
|
664
673
|
rb_hash_aset(*rv, k, rb_ary_new3(3, v, f, c));
|
665
674
|
} else {
|
@@ -716,13 +725,59 @@ flush_callback(libcouchbase_t handle, const void* cookie,
|
|
716
725
|
}
|
717
726
|
}
|
718
727
|
|
728
|
+
(void)handle;
|
729
|
+
}
|
730
|
+
|
731
|
+
static void
|
732
|
+
version_callback(libcouchbase_t handle, const void *cookie,
|
733
|
+
const char *authority, libcouchbase_error_t error,
|
734
|
+
const char *bytes, libcouchbase_size_t nbytes)
|
735
|
+
{
|
736
|
+
context_t *ctx = (context_t *)cookie;
|
737
|
+
bucket_t *bucket = ctx->bucket;
|
738
|
+
VALUE node, v, *rv = ctx->rv, exc, res;
|
739
|
+
|
740
|
+
node = authority ? rb_str_new2(authority) : Qnil;
|
741
|
+
exc = cb_check_error(error, "failed to get version", node);
|
742
|
+
if (exc != Qnil) {
|
743
|
+
rb_ivar_set(exc, id_iv_operation, sym_flush);
|
744
|
+
if (NIL_P(ctx->exception)) {
|
745
|
+
ctx->exception = exc;
|
746
|
+
}
|
747
|
+
}
|
748
|
+
|
749
|
+
if (authority) {
|
750
|
+
v = rb_str_new((const char*)bytes, nbytes);
|
751
|
+
if (bucket->async) { /* asynchronous */
|
752
|
+
if (ctx->proc != Qnil) {
|
753
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
754
|
+
rb_ivar_set(res, id_iv_error, exc);
|
755
|
+
rb_ivar_set(res, id_iv_operation, sym_version);
|
756
|
+
rb_ivar_set(res, id_iv_node, node);
|
757
|
+
rb_ivar_set(res, id_iv_value, v);
|
758
|
+
cb_proc_call(ctx->proc, 1, res);
|
759
|
+
}
|
760
|
+
} else { /* synchronous */
|
761
|
+
if (NIL_P(exc)) {
|
762
|
+
rb_hash_aset(*rv, node, v);
|
763
|
+
}
|
764
|
+
}
|
765
|
+
} else {
|
766
|
+
bucket->seqno--;
|
767
|
+
if (bucket->seqno == 0) {
|
768
|
+
bucket->io->stop_event_loop(bucket->io);
|
769
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
770
|
+
}
|
771
|
+
}
|
772
|
+
|
719
773
|
(void)handle;
|
720
774
|
}
|
721
775
|
|
722
776
|
static void
|
723
777
|
stat_callback(libcouchbase_t handle, const void* cookie,
|
724
778
|
const char* authority, libcouchbase_error_t error, const void* key,
|
725
|
-
|
779
|
+
libcouchbase_size_t nkey, const void* bytes,
|
780
|
+
libcouchbase_size_t nbytes)
|
726
781
|
{
|
727
782
|
context_t *ctx = (context_t *)cookie;
|
728
783
|
bucket_t *bucket = ctx->bucket;
|
@@ -751,12 +806,12 @@ stat_callback(libcouchbase_t handle, const void* cookie,
|
|
751
806
|
}
|
752
807
|
} else { /* synchronous */
|
753
808
|
if (NIL_P(exc)) {
|
754
|
-
stats = rb_hash_aref(*rv,
|
809
|
+
stats = rb_hash_aref(*rv, k);
|
755
810
|
if (NIL_P(stats)) {
|
756
811
|
stats = rb_hash_new();
|
757
|
-
rb_hash_aset(*rv,
|
812
|
+
rb_hash_aset(*rv, k, stats);
|
758
813
|
}
|
759
|
-
rb_hash_aset(stats,
|
814
|
+
rb_hash_aset(stats, node, v);
|
760
815
|
}
|
761
816
|
}
|
762
817
|
} else {
|
@@ -771,7 +826,8 @@ stat_callback(libcouchbase_t handle, const void* cookie,
|
|
771
826
|
|
772
827
|
static void
|
773
828
|
touch_callback(libcouchbase_t handle, const void *cookie,
|
774
|
-
libcouchbase_error_t error, const void *key,
|
829
|
+
libcouchbase_error_t error, const void *key,
|
830
|
+
libcouchbase_size_t nkey)
|
775
831
|
{
|
776
832
|
context_t *ctx = (context_t *)cookie;
|
777
833
|
bucket_t *bucket = ctx->bucket;
|
@@ -800,7 +856,7 @@ touch_callback(libcouchbase_t handle, const void *cookie,
|
|
800
856
|
} else { /* synchronous */
|
801
857
|
if (NIL_P(exc)) {
|
802
858
|
success = (error == LIBCOUCHBASE_KEY_ENOENT) ? Qfalse : Qtrue;
|
803
|
-
|
859
|
+
rb_hash_aset(*rv, k, success);
|
804
860
|
}
|
805
861
|
}
|
806
862
|
if (bucket->seqno == 0) {
|
@@ -812,8 +868,9 @@ touch_callback(libcouchbase_t handle, const void *cookie,
|
|
812
868
|
|
813
869
|
static void
|
814
870
|
arithmetic_callback(libcouchbase_t handle, const void *cookie,
|
815
|
-
libcouchbase_error_t error, const void *key,
|
816
|
-
|
871
|
+
libcouchbase_error_t error, const void *key,
|
872
|
+
libcouchbase_size_t nkey, libcouchbase_uint64_t value,
|
873
|
+
libcouchbase_cas_t cas)
|
817
874
|
{
|
818
875
|
context_t *ctx = (context_t *)cookie;
|
819
876
|
bucket_t *bucket = ctx->bucket;
|
@@ -869,79 +926,6 @@ arithmetic_callback(libcouchbase_t handle, const void *cookie,
|
|
869
926
|
(void)handle;
|
870
927
|
}
|
871
928
|
|
872
|
-
static char *
|
873
|
-
parse_path_segment(char *source, const char *key, char **result)
|
874
|
-
{
|
875
|
-
size_t len;
|
876
|
-
char *eot;
|
877
|
-
|
878
|
-
if (source == NULL) {
|
879
|
-
return NULL;
|
880
|
-
}
|
881
|
-
eot = strchr(source, '/');
|
882
|
-
if (eot > source && strncmp(source, key, eot - source) == 0) {
|
883
|
-
*eot = '\0';
|
884
|
-
source = eot + 1;
|
885
|
-
eot = strchr(source, '/');
|
886
|
-
len = strlen(source);
|
887
|
-
if (eot > source || len) {
|
888
|
-
if (eot) {
|
889
|
-
*eot = '\0';
|
890
|
-
eot++;
|
891
|
-
}
|
892
|
-
*result = strdup(source);
|
893
|
-
}
|
894
|
-
}
|
895
|
-
return eot;
|
896
|
-
}
|
897
|
-
|
898
|
-
static void
|
899
|
-
parse_bucket_uri(VALUE uri, bucket_t *bucket)
|
900
|
-
{
|
901
|
-
char *src, *ptr, *eot, sep = '\0';
|
902
|
-
|
903
|
-
ptr = src = strdup(StringValueCStr(uri));
|
904
|
-
eot = strchr(ptr, ':');
|
905
|
-
if (eot < ptr || strncmp(ptr, "http", eot - ptr) != 0) {
|
906
|
-
free(src);
|
907
|
-
rb_raise(rb_eArgError, "invalid URI format: missing schema");
|
908
|
-
return;
|
909
|
-
}
|
910
|
-
ptr = eot + 1;
|
911
|
-
if (ptr[0] != '/' || ptr[1] != '/') {
|
912
|
-
free(src);
|
913
|
-
rb_raise(rb_eArgError, "invalid URI format.");
|
914
|
-
return;
|
915
|
-
}
|
916
|
-
ptr += 2;
|
917
|
-
eot = ptr;
|
918
|
-
while (*eot) {
|
919
|
-
if (*eot == '?' || *eot == '#' || *eot == ':' || *eot == '/') {
|
920
|
-
break;
|
921
|
-
}
|
922
|
-
++eot;
|
923
|
-
}
|
924
|
-
if (eot > ptr) {
|
925
|
-
sep = *eot;
|
926
|
-
*eot = '\0';
|
927
|
-
bucket->hostname = strdup(ptr);
|
928
|
-
}
|
929
|
-
ptr = eot + 1;
|
930
|
-
eot = strchr(ptr, '/');
|
931
|
-
if (sep == ':') {
|
932
|
-
if (eot > ptr) {
|
933
|
-
*eot = '\0';
|
934
|
-
}
|
935
|
-
bucket->port = (uint16_t)atoi(ptr);
|
936
|
-
if (eot > ptr) {
|
937
|
-
ptr = eot + 1;
|
938
|
-
}
|
939
|
-
}
|
940
|
-
ptr = parse_path_segment(ptr, "pools", &bucket->pool);
|
941
|
-
parse_path_segment(ptr, "buckets", &bucket->bucket);
|
942
|
-
free(src);
|
943
|
-
}
|
944
|
-
|
945
929
|
static int
|
946
930
|
cb_first_value_i(VALUE key, VALUE value, VALUE arg)
|
947
931
|
{
|
@@ -952,28 +936,8 @@ cb_first_value_i(VALUE key, VALUE value, VALUE arg)
|
|
952
936
|
return ST_STOP;
|
953
937
|
}
|
954
938
|
|
955
|
-
static VALUE
|
956
|
-
cb_bucket_inspect(VALUE self)
|
957
|
-
{
|
958
|
-
VALUE str;
|
959
|
-
bucket_t *bucket = DATA_PTR(self);
|
960
|
-
char buf[200];
|
961
|
-
|
962
|
-
str = rb_str_buf_new2("#<");
|
963
|
-
rb_str_buf_cat2(str, rb_obj_classname(self));
|
964
|
-
snprintf(buf, 25, ":%p \"", (void *)self);
|
965
|
-
rb_str_buf_cat2(str, buf);
|
966
|
-
rb_str_append(str, rb_ivar_get(self, id_iv_url));
|
967
|
-
snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s>",
|
968
|
-
rb_id2name(SYM2ID(bucket->default_format)),
|
969
|
-
bucket->default_flags,
|
970
|
-
bucket->quiet ? "true" : "false");
|
971
|
-
rb_str_buf_cat2(str, buf);
|
972
|
-
|
973
|
-
return str;
|
974
|
-
}
|
975
|
-
|
976
939
|
/*
|
940
|
+
* @private
|
977
941
|
* @return [Fixnum] number of scheduled operations
|
978
942
|
*/
|
979
943
|
static VALUE
|
@@ -992,13 +956,13 @@ cb_bucket_free(void *ptr)
|
|
992
956
|
if (bucket) {
|
993
957
|
if (bucket->handle) {
|
994
958
|
libcouchbase_destroy(bucket->handle);
|
995
|
-
free(bucket->authority);
|
996
|
-
free(bucket->hostname);
|
997
|
-
free(bucket->pool);
|
998
|
-
free(bucket->bucket);
|
999
|
-
free(bucket->username);
|
1000
|
-
free(bucket->password);
|
1001
959
|
}
|
960
|
+
free(bucket->authority);
|
961
|
+
free(bucket->hostname);
|
962
|
+
free(bucket->pool);
|
963
|
+
free(bucket->bucket);
|
964
|
+
free(bucket->username);
|
965
|
+
free(bucket->password);
|
1002
966
|
free(bucket);
|
1003
967
|
}
|
1004
968
|
}
|
@@ -1014,97 +978,69 @@ cb_bucket_mark(void *ptr)
|
|
1014
978
|
}
|
1015
979
|
}
|
1016
980
|
|
1017
|
-
|
1018
|
-
*
|
1019
|
-
*/
|
1020
|
-
static VALUE
|
1021
|
-
cb_bucket_new(int argc, VALUE *argv, VALUE klass)
|
1022
|
-
{
|
1023
|
-
VALUE obj;
|
1024
|
-
bucket_t *bucket;
|
1025
|
-
|
1026
|
-
/* allocate new bucket struct and set it to zero */
|
1027
|
-
obj = Data_Make_Struct(klass, bucket_t, cb_bucket_mark, cb_bucket_free,
|
1028
|
-
bucket);
|
1029
|
-
rb_obj_call_init(obj, argc, argv);
|
1030
|
-
return obj;
|
1031
|
-
}
|
1032
|
-
|
1033
|
-
/*
|
1034
|
-
* @overload initialize(url, options = {})
|
1035
|
-
* Initialize bucket using URI of the cluster and options. It is possible
|
1036
|
-
* to override some parts of URI using the options keys (e.g. :host or
|
1037
|
-
* :port)
|
1038
|
-
*
|
1039
|
-
* @param [String] url The full URL of management API of the cluster.
|
1040
|
-
* @param [Hash] options The options for connection. See options definition
|
1041
|
-
* below.
|
1042
|
-
*
|
1043
|
-
* @overload initialize(options = {})
|
1044
|
-
* Initialize bucket using options only.
|
1045
|
-
*
|
1046
|
-
* @param [Hash] options The options for operation for connection
|
1047
|
-
* @option options [String] :host ("localhost") the hostname or IP address
|
1048
|
-
* of the node
|
1049
|
-
* @option options [Fixnum] :port (8091) the port of the managemenent API
|
1050
|
-
* @option options [String] :pool ("default") the pool name
|
1051
|
-
* @option options [String] :bucket ("default") the bucket name
|
1052
|
-
* @option options [Fixnum] :default_ttl (0) the TTL used by default during
|
1053
|
-
* storing key-value pairs.
|
1054
|
-
* @option options [Fixnum] :default_flags (0) the default flags.
|
1055
|
-
* @option options [Symbol] :default_format (:document) the format, which
|
1056
|
-
* will be used for values by default. Note that changing format will
|
1057
|
-
* amend flags. (see Bucket#default_format)
|
1058
|
-
* @option options [String] :username (nil) the user name to connect to the
|
1059
|
-
* cluster. Used to authenticate on management API.
|
1060
|
-
* @option options [String] :password (nil) the password of the user.
|
1061
|
-
* @option options [Boolean] :quiet (true) the flag controlling if raising
|
1062
|
-
* exception when the client executes operations on unexising keys. If it
|
1063
|
-
* is +true+ it will raise +Couchbase::NotFoundError+ exceptions. The
|
1064
|
-
* default behaviour is to return +nil+ value silently (might be useful in
|
1065
|
-
* Rails cache).
|
1066
|
-
*
|
1067
|
-
* @example Initialize connection using default options
|
1068
|
-
* Couchbase.new
|
1069
|
-
*
|
1070
|
-
* @example Select custom bucket
|
1071
|
-
* Couchbase.new(:bucket => 'foo')
|
1072
|
-
* Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
|
1073
|
-
*
|
1074
|
-
* @example Connect to protected bucket
|
1075
|
-
* Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
|
1076
|
-
* Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
|
1077
|
-
* :username => 'protected', :password => 'secret')
|
1078
|
-
*
|
1079
|
-
* @return [Bucket]
|
1080
|
-
*/
|
1081
|
-
static VALUE
|
1082
|
-
cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
981
|
+
static void
|
982
|
+
do_scan_connection_options(bucket_t *bucket, int argc, VALUE *argv)
|
1083
983
|
{
|
1084
|
-
VALUE uri, opts, arg
|
1085
|
-
libcouchbase_error_t err;
|
1086
|
-
bucket_t *bucket = DATA_PTR(self);
|
984
|
+
VALUE uri, opts, arg;
|
1087
985
|
size_t len;
|
1088
986
|
|
1089
|
-
bucket->exception = Qnil;
|
1090
|
-
bucket->hostname = strdup("localhost");
|
1091
|
-
bucket->port = 8091;
|
1092
|
-
bucket->pool = strdup("default");
|
1093
|
-
bucket->bucket = strdup("default");
|
1094
|
-
bucket->async = 0;
|
1095
|
-
bucket->quiet = 1;
|
1096
|
-
bucket->default_flags = 0;
|
1097
|
-
bucket->default_format = sym_document;
|
1098
|
-
bucket->on_error_proc = Qnil;
|
1099
|
-
|
1100
987
|
if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
|
1101
988
|
if (TYPE(uri) == T_HASH && argc == 1) {
|
1102
989
|
opts = uri;
|
1103
990
|
uri = Qnil;
|
1104
991
|
}
|
1105
992
|
if (uri != Qnil) {
|
993
|
+
const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
|
994
|
+
VALUE match, uri_obj, re;
|
995
|
+
|
1106
996
|
Check_Type(uri, T_STRING);
|
1107
|
-
|
997
|
+
uri_obj = rb_funcall(mURI, id_parse, 1, uri);
|
998
|
+
|
999
|
+
arg = rb_funcall(uri_obj, id_scheme, 0);
|
1000
|
+
if (arg == Qnil || rb_str_cmp(arg, rb_str_new2("http"))) {
|
1001
|
+
rb_raise(rb_eArgError, "invalid URI: invalid scheme");
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
arg = rb_funcall(uri_obj, id_user, 0);
|
1005
|
+
if (arg != Qnil) {
|
1006
|
+
free(bucket->username);
|
1007
|
+
bucket->username = strdup(RSTRING_PTR(arg));
|
1008
|
+
if (bucket->username == NULL) {
|
1009
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1010
|
+
}
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
arg = rb_funcall(uri_obj, id_password, 0);
|
1014
|
+
if (arg != Qnil) {
|
1015
|
+
free(bucket->password);
|
1016
|
+
bucket->password = strdup(RSTRING_PTR(arg));
|
1017
|
+
if (bucket->password == NULL) {
|
1018
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1019
|
+
}
|
1020
|
+
}
|
1021
|
+
arg = rb_funcall(uri_obj, id_host, 0);
|
1022
|
+
if (arg != Qnil) {
|
1023
|
+
free(bucket->hostname);
|
1024
|
+
bucket->hostname = strdup(RSTRING_PTR(arg));
|
1025
|
+
if (bucket->hostname == NULL) {
|
1026
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1027
|
+
}
|
1028
|
+
} else {
|
1029
|
+
rb_raise(rb_eArgError, "invalid URI: missing hostname");
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
arg = rb_funcall(uri_obj, id_port, 0);
|
1033
|
+
bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
|
1034
|
+
|
1035
|
+
arg = rb_funcall(uri_obj, id_path, 0);
|
1036
|
+
re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
|
1037
|
+
match = rb_funcall(re, id_match, 1, arg);
|
1038
|
+
arg = rb_reg_nth_match(2, match);
|
1039
|
+
free(bucket->pool);
|
1040
|
+
bucket->pool = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
|
1041
|
+
arg = rb_reg_nth_match(4, match);
|
1042
|
+
free(bucket->bucket);
|
1043
|
+
bucket->bucket = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
|
1108
1044
|
}
|
1109
1045
|
if (TYPE(opts) == T_HASH) {
|
1110
1046
|
arg = rb_hash_aref(opts, sym_hostname);
|
@@ -1130,10 +1066,16 @@ cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
|
1130
1066
|
}
|
1131
1067
|
arg = rb_hash_aref(opts, sym_username);
|
1132
1068
|
if (arg != Qnil) {
|
1069
|
+
if (bucket->username) {
|
1070
|
+
free(bucket->username);
|
1071
|
+
}
|
1133
1072
|
bucket->username = strdup(StringValueCStr(arg));
|
1134
1073
|
}
|
1135
1074
|
arg = rb_hash_aref(opts, sym_password);
|
1136
1075
|
if (arg != Qnil) {
|
1076
|
+
if (bucket->password) {
|
1077
|
+
free(bucket->password);
|
1078
|
+
}
|
1137
1079
|
bucket->password = strdup(StringValueCStr(arg));
|
1138
1080
|
}
|
1139
1081
|
arg = rb_hash_aref(opts, sym_port);
|
@@ -1143,6 +1085,14 @@ cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
|
1143
1085
|
if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
|
1144
1086
|
bucket->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
|
1145
1087
|
}
|
1088
|
+
arg = rb_hash_aref(opts, sym_timeout);
|
1089
|
+
if (arg != Qnil) {
|
1090
|
+
bucket->timeout = (uint32_t)NUM2ULONG(arg);
|
1091
|
+
}
|
1092
|
+
arg = rb_hash_aref(opts, sym_default_ttl);
|
1093
|
+
if (arg != Qnil) {
|
1094
|
+
bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
|
1095
|
+
}
|
1146
1096
|
arg = rb_hash_aref(opts, sym_default_flags);
|
1147
1097
|
if (arg != Qnil) {
|
1148
1098
|
bucket->default_flags = (uint32_t)NUM2ULONG(arg);
|
@@ -1172,13 +1122,28 @@ cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
|
1172
1122
|
}
|
1173
1123
|
}
|
1174
1124
|
len = strlen(bucket->hostname) + 10;
|
1125
|
+
if (bucket->authority) {
|
1126
|
+
free(bucket->authority);
|
1127
|
+
}
|
1175
1128
|
bucket->authority = calloc(len, sizeof(char));
|
1176
1129
|
if (bucket->authority == NULL) {
|
1177
1130
|
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1178
1131
|
}
|
1179
1132
|
snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
|
1133
|
+
}
|
1134
|
+
|
1135
|
+
static void
|
1136
|
+
do_connect(bucket_t *bucket)
|
1137
|
+
{
|
1138
|
+
libcouchbase_error_t err;
|
1139
|
+
|
1140
|
+
if (bucket->handle) {
|
1141
|
+
libcouchbase_destroy(bucket->handle);
|
1142
|
+
bucket->handle = NULL;
|
1143
|
+
bucket->io = NULL;
|
1144
|
+
}
|
1180
1145
|
bucket->io = libcouchbase_create_io_ops(LIBCOUCHBASE_IO_OPS_DEFAULT, NULL, &err);
|
1181
|
-
if (bucket->io == NULL) {
|
1146
|
+
if (bucket->io == NULL && err != LIBCOUCHBASE_SUCCESS) {
|
1182
1147
|
rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
|
1183
1148
|
}
|
1184
1149
|
bucket->handle = libcouchbase_create(bucket->authority,
|
@@ -1195,78 +1160,241 @@ cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
|
1195
1160
|
(void)libcouchbase_set_stat_callback(bucket->handle, stat_callback);
|
1196
1161
|
(void)libcouchbase_set_flush_callback(bucket->handle, flush_callback);
|
1197
1162
|
(void)libcouchbase_set_arithmetic_callback(bucket->handle, arithmetic_callback);
|
1163
|
+
(void)libcouchbase_set_version_callback(bucket->handle, version_callback);
|
1198
1164
|
|
1199
1165
|
err = libcouchbase_connect(bucket->handle);
|
1200
1166
|
if (err != LIBCOUCHBASE_SUCCESS) {
|
1167
|
+
libcouchbase_destroy(bucket->handle);
|
1168
|
+
bucket->handle = NULL;
|
1169
|
+
bucket->io = NULL;
|
1201
1170
|
rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
|
1202
1171
|
}
|
1172
|
+
bucket->exception = Qnil;
|
1203
1173
|
libcouchbase_wait(bucket->handle);
|
1204
1174
|
if (bucket->exception != Qnil) {
|
1175
|
+
libcouchbase_destroy(bucket->handle);
|
1176
|
+
bucket->handle = NULL;
|
1177
|
+
bucket->io = NULL;
|
1205
1178
|
rb_exc_raise(bucket->exception);
|
1206
1179
|
}
|
1207
1180
|
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
rb_ivar_set(self, id_iv_port, UINT2NUM(bucket->port));
|
1214
|
-
rb_ivar_set(self, id_iv_username, bucket->username ? rb_str_new2(bucket->username) : Qnil);
|
1215
|
-
rb_ivar_set(self, id_iv_quiet, bucket->quiet ? Qtrue : Qfalse);
|
1216
|
-
rb_ivar_set(self, id_iv_default_flags, ULONG2NUM(bucket->default_flags));
|
1217
|
-
rb_ivar_set(self, id_iv_default_format, bucket->default_format);
|
1218
|
-
rb_ivar_set(self, id_iv_on_error, bucket->on_error_proc);
|
1219
|
-
|
1220
|
-
buf = rb_str_buf_new2("http://");
|
1221
|
-
rb_str_buf_cat2(buf, bucket->authority);
|
1222
|
-
rb_str_buf_cat2(buf, "/pools/");
|
1223
|
-
rb_str_buf_cat2(buf, bucket->pool);
|
1224
|
-
rb_str_buf_cat2(buf, "/buckets/");
|
1225
|
-
rb_str_buf_cat2(buf, bucket->bucket);
|
1226
|
-
rb_str_buf_cat2(buf, "/");
|
1227
|
-
rb_ivar_set(self, id_iv_url, buf);
|
1228
|
-
|
1229
|
-
return self;
|
1230
|
-
}
|
1231
|
-
|
1232
|
-
static VALUE
|
1233
|
-
cb_bucket_async_p(VALUE self)
|
1234
|
-
{
|
1235
|
-
bucket_t *bucket = DATA_PTR(self);
|
1236
|
-
return bucket->async ? Qtrue : Qfalse;
|
1237
|
-
}
|
1238
|
-
|
1239
|
-
static VALUE
|
1240
|
-
cb_bucket_quiet_set(VALUE self, VALUE val)
|
1241
|
-
{
|
1242
|
-
bucket_t *bucket = DATA_PTR(self);
|
1243
|
-
VALUE new;
|
1244
|
-
|
1245
|
-
bucket->quiet = RTEST(val);
|
1246
|
-
new = bucket->quiet ? Qtrue : Qfalse;
|
1247
|
-
rb_ivar_set(self, id_iv_quiet, new);
|
1248
|
-
return new;
|
1181
|
+
if (bucket->timeout > 0) {
|
1182
|
+
libcouchbase_set_timeout(bucket->handle, bucket->timeout);
|
1183
|
+
} else {
|
1184
|
+
bucket->timeout = libcouchbase_get_timeout(bucket->handle);
|
1185
|
+
}
|
1249
1186
|
}
|
1250
1187
|
|
1188
|
+
/*
|
1189
|
+
* Create and initialize new Bucket.
|
1190
|
+
*
|
1191
|
+
* @return [Bucket] new instance
|
1192
|
+
*
|
1193
|
+
* @see Bucket#initialize
|
1194
|
+
*/
|
1251
1195
|
static VALUE
|
1252
|
-
|
1196
|
+
cb_bucket_new(int argc, VALUE *argv, VALUE klass)
|
1253
1197
|
{
|
1254
|
-
|
1198
|
+
VALUE obj;
|
1199
|
+
bucket_t *bucket;
|
1255
1200
|
|
1256
|
-
bucket
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
return
|
1201
|
+
/* allocate new bucket struct and set it to zero */
|
1202
|
+
obj = Data_Make_Struct(klass, bucket_t, cb_bucket_mark, cb_bucket_free,
|
1203
|
+
bucket);
|
1204
|
+
rb_obj_call_init(obj, argc, argv);
|
1205
|
+
return obj;
|
1261
1206
|
}
|
1262
1207
|
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1208
|
+
/*
|
1209
|
+
* Initialize new Bucket.
|
1210
|
+
*
|
1211
|
+
* @overload initialize(url, options = {})
|
1212
|
+
* Initialize bucket using URI of the cluster and options. It is possible
|
1213
|
+
* to override some parts of URI using the options keys (e.g. :host or
|
1214
|
+
* :port)
|
1215
|
+
*
|
1216
|
+
* @param [String] url The full URL of management API of the cluster.
|
1217
|
+
* @param [Hash] options The options for connection. See options definition
|
1218
|
+
* below.
|
1219
|
+
*
|
1220
|
+
* @overload initialize(options = {})
|
1221
|
+
* Initialize bucket using options only.
|
1222
|
+
*
|
1223
|
+
* @param [Hash] options The options for operation for connection
|
1224
|
+
* @option options [String] :host ("localhost") the hostname or IP address
|
1225
|
+
* of the node
|
1226
|
+
* @option options [Fixnum] :port (8091) the port of the managemenent API
|
1227
|
+
* @option options [String] :pool ("default") the pool name
|
1228
|
+
* @option options [String] :bucket ("default") the bucket name
|
1229
|
+
* @option options [Fixnum] :default_ttl (0) the TTL used by default during
|
1230
|
+
* storing key-value pairs.
|
1231
|
+
* @option options [Fixnum] :default_flags (0) the default flags.
|
1232
|
+
* @option options [Symbol] :default_format (:document) the format, which
|
1233
|
+
* will be used for values by default. Note that changing format will
|
1234
|
+
* amend flags. (see {Bucket#default_format})
|
1235
|
+
* @option options [String] :username (nil) the user name to connect to the
|
1236
|
+
* cluster. Used to authenticate on management API.
|
1237
|
+
* @option options [String] :password (nil) the password of the user.
|
1238
|
+
* @option options [Boolean] :quiet (true) the flag controlling if raising
|
1239
|
+
* exception when the client executes operations on unexising keys. If it
|
1240
|
+
* is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
|
1241
|
+
* default behaviour is to return +nil+ value silently (might be useful in
|
1242
|
+
* Rails cache).
|
1243
|
+
*
|
1244
|
+
* @example Initialize connection using default options
|
1245
|
+
* Couchbase.new
|
1246
|
+
*
|
1247
|
+
* @example Select custom bucket
|
1248
|
+
* Couchbase.new(:bucket => 'foo')
|
1249
|
+
* Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
|
1250
|
+
*
|
1251
|
+
* @example Connect to protected bucket
|
1252
|
+
* Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
|
1253
|
+
* Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
|
1254
|
+
* :username => 'protected', :password => 'secret')
|
1255
|
+
*
|
1256
|
+
* @return [Bucket]
|
1257
|
+
*/
|
1258
|
+
static VALUE
|
1259
|
+
cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
1260
|
+
{
|
1261
|
+
bucket_t *bucket = DATA_PTR(self);
|
1262
|
+
|
1263
|
+
bucket->exception = Qnil;
|
1264
|
+
bucket->hostname = strdup("localhost");
|
1265
|
+
bucket->port = 8091;
|
1266
|
+
bucket->pool = strdup("default");
|
1267
|
+
bucket->bucket = strdup("default");
|
1268
|
+
bucket->async = 0;
|
1269
|
+
bucket->quiet = 1;
|
1270
|
+
bucket->default_ttl = 0;
|
1271
|
+
bucket->default_flags = 0;
|
1272
|
+
bucket->default_format = sym_document;
|
1273
|
+
bucket->on_error_proc = Qnil;
|
1274
|
+
bucket->timeout = 0;
|
1275
|
+
|
1276
|
+
do_scan_connection_options(bucket, argc, argv);
|
1277
|
+
do_connect(bucket);
|
1278
|
+
|
1279
|
+
return self;
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
/*
|
1283
|
+
* Reconnect the bucket
|
1284
|
+
*
|
1285
|
+
* Reconnect the bucket using initial configuration with optional
|
1286
|
+
* redefinition.
|
1287
|
+
*
|
1288
|
+
* @overload reconnect(url, options = {})
|
1289
|
+
* see {Bucket#initialize Bucket#initialize(url, options)}
|
1290
|
+
*
|
1291
|
+
* @overload reconnect(options = {})
|
1292
|
+
* see {Bucket#initialize Bucket#initialize(options)}
|
1293
|
+
*
|
1294
|
+
* @example reconnect with current parameters
|
1295
|
+
* c.reconnect
|
1296
|
+
*
|
1297
|
+
* @example reconnect the instance to another bucket
|
1298
|
+
* c.reconnect(:bucket => 'new')
|
1299
|
+
*/
|
1300
|
+
static VALUE
|
1301
|
+
cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
|
1302
|
+
{
|
1303
|
+
bucket_t *bucket = DATA_PTR(self);
|
1304
|
+
|
1305
|
+
do_scan_connection_options(bucket, argc, argv);
|
1306
|
+
do_connect(bucket);
|
1307
|
+
|
1308
|
+
return self;
|
1309
|
+
}
|
1310
|
+
|
1311
|
+
/* Document-method: connected?
|
1312
|
+
* Check whether the instance connected to the cluster.
|
1313
|
+
*
|
1314
|
+
* @return [Boolean] +true+ if the instance connected to the cluster
|
1315
|
+
*/
|
1316
|
+
static VALUE
|
1317
|
+
cb_bucket_connected_p(VALUE self)
|
1318
|
+
{
|
1319
|
+
bucket_t *bucket = DATA_PTR(self);
|
1320
|
+
return bucket->handle ? Qtrue : Qfalse;
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
/* Document-method: async?
|
1324
|
+
* Check whether the connection asynchronous.
|
1325
|
+
*
|
1326
|
+
* By default all operations are synchronous and block waiting for
|
1327
|
+
* results, but you can make them asynchronous and run event loop
|
1328
|
+
* explicitly. (see {Bucket#run})
|
1329
|
+
*
|
1330
|
+
* @example Return value of #get operation depending on async flag
|
1331
|
+
* connection = Connection.new
|
1332
|
+
* connection.async? #=> false
|
1333
|
+
*
|
1334
|
+
* connection.run do |conn|
|
1335
|
+
* conn.async? #=> true
|
1336
|
+
* end
|
1337
|
+
*
|
1338
|
+
* @return [Boolean] +true+ if the connection if asynchronous
|
1339
|
+
*
|
1340
|
+
* @see Bucket#run
|
1341
|
+
*/
|
1342
|
+
static VALUE
|
1343
|
+
cb_bucket_async_p(VALUE self)
|
1344
|
+
{
|
1345
|
+
bucket_t *bucket = DATA_PTR(self);
|
1346
|
+
return bucket->async ? Qtrue : Qfalse;
|
1347
|
+
}
|
1348
|
+
|
1349
|
+
static VALUE
|
1350
|
+
cb_bucket_quiet_get(VALUE self)
|
1351
|
+
{
|
1352
|
+
bucket_t *bucket = DATA_PTR(self);
|
1353
|
+
return bucket->quiet ? Qtrue : Qfalse;
|
1354
|
+
}
|
1355
|
+
|
1356
|
+
static VALUE
|
1357
|
+
cb_bucket_quiet_set(VALUE self, VALUE val)
|
1358
|
+
{
|
1359
|
+
bucket_t *bucket = DATA_PTR(self);
|
1360
|
+
VALUE new;
|
1361
|
+
|
1362
|
+
bucket->quiet = RTEST(val);
|
1363
|
+
new = bucket->quiet ? Qtrue : Qfalse;
|
1364
|
+
return new;
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
static VALUE
|
1368
|
+
cb_bucket_default_flags_get(VALUE self)
|
1369
|
+
{
|
1370
|
+
bucket_t *bucket = DATA_PTR(self);
|
1371
|
+
return ULONG2NUM(bucket->default_flags);
|
1372
|
+
}
|
1373
|
+
|
1374
|
+
static VALUE
|
1375
|
+
cb_bucket_default_flags_set(VALUE self, VALUE val)
|
1376
|
+
{
|
1377
|
+
bucket_t *bucket = DATA_PTR(self);
|
1378
|
+
|
1379
|
+
bucket->default_flags = (uint32_t)NUM2ULONG(val);
|
1380
|
+
bucket->default_format = flags_get_format(bucket->default_flags);
|
1381
|
+
return val;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
static VALUE
|
1385
|
+
cb_bucket_default_format_get(VALUE self)
|
1386
|
+
{
|
1387
|
+
bucket_t *bucket = DATA_PTR(self);
|
1388
|
+
return bucket->default_format;
|
1389
|
+
}
|
1390
|
+
|
1391
|
+
static VALUE
|
1392
|
+
cb_bucket_default_format_set(VALUE self, VALUE val)
|
1393
|
+
{
|
1394
|
+
bucket_t *bucket = DATA_PTR(self);
|
1395
|
+
|
1396
|
+
if (TYPE(val) == T_FIXNUM) {
|
1397
|
+
switch (FIX2INT(val)) {
|
1270
1398
|
case FMT_DOCUMENT:
|
1271
1399
|
val = sym_document;
|
1272
1400
|
break;
|
@@ -1281,8 +1409,6 @@ cb_bucket_default_format_set(VALUE self, VALUE val)
|
|
1281
1409
|
if (val == sym_document || val == sym_marshal || val == sym_plain) {
|
1282
1410
|
bucket->default_format = val;
|
1283
1411
|
bucket->default_flags = flags_set_format(bucket->default_flags, val);
|
1284
|
-
rb_ivar_set(self, id_iv_default_format, val);
|
1285
|
-
rb_ivar_set(self, id_iv_default_flags, ULONG2NUM(bucket->default_flags));
|
1286
1412
|
}
|
1287
1413
|
|
1288
1414
|
return val;
|
@@ -1298,7 +1424,6 @@ cb_bucket_on_error_set(VALUE self, VALUE val)
|
|
1298
1424
|
} else {
|
1299
1425
|
bucket->on_error_proc = Qnil;
|
1300
1426
|
}
|
1301
|
-
rb_ivar_set(self, id_iv_on_error, bucket->on_error_proc);
|
1302
1427
|
|
1303
1428
|
return bucket->on_error_proc;
|
1304
1429
|
}
|
@@ -1315,6 +1440,212 @@ cb_bucket_on_error_get(VALUE self)
|
|
1315
1440
|
}
|
1316
1441
|
}
|
1317
1442
|
|
1443
|
+
static VALUE
|
1444
|
+
cb_bucket_timeout_get(VALUE self)
|
1445
|
+
{
|
1446
|
+
bucket_t *bucket = DATA_PTR(self);
|
1447
|
+
return ULONG2NUM(bucket->timeout);
|
1448
|
+
}
|
1449
|
+
|
1450
|
+
static VALUE
|
1451
|
+
cb_bucket_timeout_set(VALUE self, VALUE val)
|
1452
|
+
{
|
1453
|
+
bucket_t *bucket = DATA_PTR(self);
|
1454
|
+
VALUE tmval;
|
1455
|
+
|
1456
|
+
bucket->timeout = (uint32_t)NUM2ULONG(val);
|
1457
|
+
libcouchbase_set_timeout(bucket->handle, bucket->timeout);
|
1458
|
+
tmval = ULONG2NUM(bucket->timeout);
|
1459
|
+
|
1460
|
+
return tmval;
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
/* Document-method: hostname
|
1464
|
+
* @return [String] the host name of the management interface (default: "localhost")
|
1465
|
+
*/
|
1466
|
+
static VALUE
|
1467
|
+
cb_bucket_hostname_get(VALUE self)
|
1468
|
+
{
|
1469
|
+
bucket_t *bucket = DATA_PTR(self);
|
1470
|
+
if (bucket->handle) {
|
1471
|
+
if (bucket->hostname) {
|
1472
|
+
free(bucket->hostname);
|
1473
|
+
bucket->hostname = NULL;
|
1474
|
+
}
|
1475
|
+
bucket->hostname = strdup(libcouchbase_get_host(bucket->handle));
|
1476
|
+
if (bucket->hostname == NULL) {
|
1477
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1478
|
+
}
|
1479
|
+
}
|
1480
|
+
return rb_str_new2(bucket->hostname);
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
/* Document-method: port
|
1484
|
+
* @return [Fixnum] the port number of the management interface (default: 8091)
|
1485
|
+
*/
|
1486
|
+
static VALUE
|
1487
|
+
cb_bucket_port_get(VALUE self)
|
1488
|
+
{
|
1489
|
+
bucket_t *bucket = DATA_PTR(self);
|
1490
|
+
if (bucket->handle) {
|
1491
|
+
bucket->port = atoi(libcouchbase_get_port(bucket->handle));
|
1492
|
+
}
|
1493
|
+
return UINT2NUM(bucket->port);
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
/* Document-method: authority
|
1497
|
+
* @return [String] host with port
|
1498
|
+
*/
|
1499
|
+
static VALUE
|
1500
|
+
cb_bucket_authority_get(VALUE self)
|
1501
|
+
{
|
1502
|
+
bucket_t *bucket = DATA_PTR(self);
|
1503
|
+
size_t len;
|
1504
|
+
|
1505
|
+
(void)cb_bucket_hostname_get(self);
|
1506
|
+
(void)cb_bucket_port_get(self);
|
1507
|
+
len = strlen(bucket->hostname) + 10;
|
1508
|
+
bucket->authority = calloc(len, sizeof(char));
|
1509
|
+
if (bucket->authority == NULL) {
|
1510
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1511
|
+
}
|
1512
|
+
snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
|
1513
|
+
return rb_str_new2(bucket->authority);
|
1514
|
+
}
|
1515
|
+
|
1516
|
+
/* Document-method: bucket
|
1517
|
+
* @return [String] the bucket name
|
1518
|
+
*/
|
1519
|
+
static VALUE
|
1520
|
+
cb_bucket_bucket_get(VALUE self)
|
1521
|
+
{
|
1522
|
+
bucket_t *bucket = DATA_PTR(self);
|
1523
|
+
return rb_str_new2(bucket->bucket);
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
/* Document-method: pool
|
1527
|
+
* @return [String] the pool name (usually "default")
|
1528
|
+
*/
|
1529
|
+
static VALUE
|
1530
|
+
cb_bucket_pool_get(VALUE self)
|
1531
|
+
{
|
1532
|
+
bucket_t *bucket = DATA_PTR(self);
|
1533
|
+
return rb_str_new2(bucket->pool);
|
1534
|
+
}
|
1535
|
+
|
1536
|
+
/* Document-method: username
|
1537
|
+
* @return [String] the username for protected buckets (usually matches
|
1538
|
+
* the bucket name)
|
1539
|
+
*/
|
1540
|
+
static VALUE
|
1541
|
+
cb_bucket_username_get(VALUE self)
|
1542
|
+
{
|
1543
|
+
bucket_t *bucket = DATA_PTR(self);
|
1544
|
+
return rb_str_new2(bucket->username);
|
1545
|
+
}
|
1546
|
+
|
1547
|
+
/* Document-method: password
|
1548
|
+
* @return [String] the password for protected buckets
|
1549
|
+
*/
|
1550
|
+
static VALUE
|
1551
|
+
cb_bucket_password_get(VALUE self)
|
1552
|
+
{
|
1553
|
+
bucket_t *bucket = DATA_PTR(self);
|
1554
|
+
return rb_str_new2(bucket->password);
|
1555
|
+
}
|
1556
|
+
|
1557
|
+
/* Document-method: url
|
1558
|
+
* @return [String] the address of the cluster management interface
|
1559
|
+
*/
|
1560
|
+
static VALUE
|
1561
|
+
cb_bucket_url_get(VALUE self)
|
1562
|
+
{
|
1563
|
+
bucket_t *bucket = DATA_PTR(self);
|
1564
|
+
VALUE str;
|
1565
|
+
|
1566
|
+
(void)cb_bucket_authority_get(self);
|
1567
|
+
str = rb_str_buf_new2("http://");
|
1568
|
+
rb_str_buf_cat2(str, bucket->authority);
|
1569
|
+
rb_str_buf_cat2(str, "/pools/");
|
1570
|
+
rb_str_buf_cat2(str, bucket->pool);
|
1571
|
+
rb_str_buf_cat2(str, "/buckets/");
|
1572
|
+
rb_str_buf_cat2(str, bucket->bucket);
|
1573
|
+
rb_str_buf_cat2(str, "/");
|
1574
|
+
return str;
|
1575
|
+
}
|
1576
|
+
|
1577
|
+
/*
|
1578
|
+
* Returns a string containing a human-readable representation of the
|
1579
|
+
* Bucket.
|
1580
|
+
*
|
1581
|
+
* @return [String]
|
1582
|
+
*/
|
1583
|
+
static VALUE
|
1584
|
+
cb_bucket_inspect(VALUE self)
|
1585
|
+
{
|
1586
|
+
VALUE str;
|
1587
|
+
bucket_t *bucket = DATA_PTR(self);
|
1588
|
+
char buf[200];
|
1589
|
+
|
1590
|
+
str = rb_str_buf_new2("#<");
|
1591
|
+
rb_str_buf_cat2(str, rb_obj_classname(self));
|
1592
|
+
snprintf(buf, 25, ":%p \"", (void *)self);
|
1593
|
+
(void)cb_bucket_authority_get(self);
|
1594
|
+
rb_str_buf_cat2(str, buf);
|
1595
|
+
rb_str_buf_cat2(str, "http://");
|
1596
|
+
rb_str_buf_cat2(str, bucket->authority);
|
1597
|
+
rb_str_buf_cat2(str, "/pools/");
|
1598
|
+
rb_str_buf_cat2(str, bucket->pool);
|
1599
|
+
rb_str_buf_cat2(str, "/buckets/");
|
1600
|
+
rb_str_buf_cat2(str, bucket->bucket);
|
1601
|
+
rb_str_buf_cat2(str, "/");
|
1602
|
+
snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s, connected=%s, timeout=%u>",
|
1603
|
+
rb_id2name(SYM2ID(bucket->default_format)),
|
1604
|
+
bucket->default_flags,
|
1605
|
+
bucket->quiet ? "true" : "false",
|
1606
|
+
bucket->handle ? "true" : "false",
|
1607
|
+
bucket->timeout);
|
1608
|
+
rb_str_buf_cat2(str, buf);
|
1609
|
+
|
1610
|
+
return str;
|
1611
|
+
}
|
1612
|
+
|
1613
|
+
/*
|
1614
|
+
* Delete the specified key
|
1615
|
+
*
|
1616
|
+
* @overload delete(key, options = {})
|
1617
|
+
* @param key [String, Symbol] Key used to reference the value.
|
1618
|
+
* @param options [Hash] Options for operation.
|
1619
|
+
* @option options [Boolean] :quiet (self.quiet) If set to +true+, the
|
1620
|
+
* operation won't raise error for missing key, it will return +nil+.
|
1621
|
+
* Otherwise it will raise error in synchronous mode. In asynchronous
|
1622
|
+
* mode this option ignored.
|
1623
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
1624
|
+
* created on the server and is guaranteed to be unique for each value of
|
1625
|
+
* a given key. This value is used to provide simple optimistic
|
1626
|
+
* concurrency control when multiple clients or threads try to
|
1627
|
+
* update/delete an item simultaneously.
|
1628
|
+
*
|
1629
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1630
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
1631
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
1632
|
+
* @raise [Couchbase::Error::NotFound] if key is missing in verbose mode
|
1633
|
+
*
|
1634
|
+
* @example Delete the key in quiet mode (default)
|
1635
|
+
* c.set("foo", "bar")
|
1636
|
+
* c.delete("foo") #=> true
|
1637
|
+
* c.delete("foo") #=> false
|
1638
|
+
*
|
1639
|
+
* @example Delete the key verbosely
|
1640
|
+
* c.set("foo", "bar")
|
1641
|
+
* c.delete("foo", :quiet => false) #=> true
|
1642
|
+
* c.delete("foo", :quiet => false) #=> will raise Couchbase::Error::NotFound
|
1643
|
+
*
|
1644
|
+
* @example Delete the key with version check
|
1645
|
+
* ver = c.set("foo", "bar") #=> 5992859822302167040
|
1646
|
+
* c.delete("foo", :cas => 123456) #=> will raise Couchbase::Error::KeyExists
|
1647
|
+
* c.delete("foo", :cas => ver) #=> true
|
1648
|
+
*/
|
1318
1649
|
static VALUE
|
1319
1650
|
cb_bucket_delete(int argc, VALUE *argv, VALUE self)
|
1320
1651
|
{
|
@@ -1323,9 +1654,13 @@ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
|
|
1323
1654
|
VALUE k, c, rv, proc, exc, opts;
|
1324
1655
|
char *key;
|
1325
1656
|
size_t nkey;
|
1326
|
-
|
1657
|
+
libcouchbase_cas_t cas = 0;
|
1327
1658
|
libcouchbase_error_t err;
|
1659
|
+
long seqno;
|
1328
1660
|
|
1661
|
+
if (bucket->handle == NULL) {
|
1662
|
+
rb_raise(eConnectError, "closed connection");
|
1663
|
+
}
|
1329
1664
|
rb_scan_args(argc, argv, "11&", &k, &opts, &proc);
|
1330
1665
|
if (!bucket->async && proc != Qnil) {
|
1331
1666
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
@@ -1357,6 +1692,8 @@ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
|
|
1357
1692
|
ctx->rv = &rv;
|
1358
1693
|
ctx->bucket = bucket;
|
1359
1694
|
ctx->exception = Qnil;
|
1695
|
+
seqno = bucket->seqno;
|
1696
|
+
bucket->seqno++;
|
1360
1697
|
err = libcouchbase_remove(bucket->handle, (const void *)ctx,
|
1361
1698
|
(const void *)key, nkey, cas);
|
1362
1699
|
exc = cb_check_error(err, "failed to schedule delete request", Qnil);
|
@@ -1364,11 +1701,13 @@ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
|
|
1364
1701
|
free(ctx);
|
1365
1702
|
rb_exc_raise(exc);
|
1366
1703
|
}
|
1367
|
-
bucket->seqno++;
|
1368
1704
|
if (bucket->async) {
|
1369
1705
|
return Qnil;
|
1370
1706
|
} else {
|
1371
|
-
|
1707
|
+
if (bucket->seqno - seqno > 0) {
|
1708
|
+
/* we have some operations pending */
|
1709
|
+
bucket->io->run_event_loop(bucket->io);
|
1710
|
+
}
|
1372
1711
|
exc = ctx->exception;
|
1373
1712
|
free(ctx);
|
1374
1713
|
if (exc != Qnil) {
|
@@ -1388,9 +1727,13 @@ cb_bucket_store(libcouchbase_storage_t cmd, int argc, VALUE *argv, VALUE self)
|
|
1388
1727
|
size_t nkey, nbytes;
|
1389
1728
|
uint32_t flags;
|
1390
1729
|
time_t exp = 0;
|
1391
|
-
|
1730
|
+
libcouchbase_cas_t cas = 0;
|
1392
1731
|
libcouchbase_error_t err;
|
1732
|
+
long seqno;
|
1393
1733
|
|
1734
|
+
if (bucket->handle == NULL) {
|
1735
|
+
rb_raise(eConnectError, "closed connection");
|
1736
|
+
}
|
1394
1737
|
rb_scan_args(argc, argv, "21&", &k, &v, &opts, &proc);
|
1395
1738
|
if (!bucket->async && proc != Qnil) {
|
1396
1739
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
@@ -1435,6 +1778,8 @@ cb_bucket_store(libcouchbase_storage_t cmd, int argc, VALUE *argv, VALUE self)
|
|
1435
1778
|
ctx->proc = proc;
|
1436
1779
|
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1437
1780
|
ctx->exception = Qnil;
|
1781
|
+
seqno = bucket->seqno;
|
1782
|
+
bucket->seqno++;
|
1438
1783
|
err = libcouchbase_store(bucket->handle, (const void *)ctx, cmd,
|
1439
1784
|
(const void *)key, nkey, bytes, nbytes, flags, exp, cas);
|
1440
1785
|
exc = cb_check_error(err, "failed to schedule set request", Qnil);
|
@@ -1442,11 +1787,13 @@ cb_bucket_store(libcouchbase_storage_t cmd, int argc, VALUE *argv, VALUE self)
|
|
1442
1787
|
free(ctx);
|
1443
1788
|
rb_exc_raise(exc);
|
1444
1789
|
}
|
1445
|
-
bucket->seqno++;
|
1446
1790
|
if (bucket->async) {
|
1447
1791
|
return Qnil;
|
1448
1792
|
} else {
|
1449
|
-
|
1793
|
+
if (bucket->seqno - seqno > 0) {
|
1794
|
+
/* we have some operations pending */
|
1795
|
+
bucket->io->run_event_loop(bucket->io);
|
1796
|
+
}
|
1450
1797
|
exc = ctx->exception;
|
1451
1798
|
free(ctx);
|
1452
1799
|
if (exc != Qnil) {
|
@@ -1471,7 +1818,11 @@ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
|
1471
1818
|
uint64_t delta = 0, initial = 0;
|
1472
1819
|
int create = 0;
|
1473
1820
|
libcouchbase_error_t err;
|
1821
|
+
long seqno;
|
1474
1822
|
|
1823
|
+
if (bucket->handle == NULL) {
|
1824
|
+
rb_raise(eConnectError, "closed connection");
|
1825
|
+
}
|
1475
1826
|
rb_scan_args(argc, argv, "12&", &k, &d, &opts, &proc);
|
1476
1827
|
if (!bucket->async && proc != Qnil) {
|
1477
1828
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
@@ -1514,6 +1865,8 @@ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
|
1514
1865
|
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1515
1866
|
ctx->exception = Qnil;
|
1516
1867
|
ctx->arithm = sign;
|
1868
|
+
seqno = bucket->seqno;
|
1869
|
+
bucket->seqno++;
|
1517
1870
|
err = libcouchbase_arithmetic(bucket->handle, (const void *)ctx,
|
1518
1871
|
(const void *)key, nkey, delta, exp, create, initial);
|
1519
1872
|
exc = cb_check_error(err, "failed to schedule arithmetic request", k);
|
@@ -1521,11 +1874,13 @@ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
|
1521
1874
|
free(ctx);
|
1522
1875
|
rb_exc_raise(exc);
|
1523
1876
|
}
|
1524
|
-
bucket->seqno++;
|
1525
1877
|
if (bucket->async) {
|
1526
1878
|
return Qnil;
|
1527
1879
|
} else {
|
1528
|
-
|
1880
|
+
if (bucket->seqno - seqno > 0) {
|
1881
|
+
/* we have some operations pending */
|
1882
|
+
bucket->io->run_event_loop(bucket->io);
|
1883
|
+
}
|
1529
1884
|
exc = ctx->exception;
|
1530
1885
|
free(ctx);
|
1531
1886
|
if (exc != Qnil) {
|
@@ -1535,29 +1890,282 @@ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
|
1535
1890
|
}
|
1536
1891
|
}
|
1537
1892
|
|
1893
|
+
/*
|
1894
|
+
* Increment the value of an existing numeric key
|
1895
|
+
*
|
1896
|
+
* The increment methods enable you to increase a given stored integer
|
1897
|
+
* value. These are the incremental equivalent of the decrement operations
|
1898
|
+
* and work on the same basis; updating the value of a key if it can be
|
1899
|
+
* parsed to an integer. The update operation occurs on the server and is
|
1900
|
+
* provided at the protocol level. This simplifies what would otherwise be a
|
1901
|
+
* two-stage get and set operation.
|
1902
|
+
*
|
1903
|
+
* @note that server values stored and transmitted as unsigned numbers,
|
1904
|
+
* therefore if you try to store negative number and then increment or
|
1905
|
+
* decrement it will cause overflow. (see "Integer overflow" example
|
1906
|
+
* below)
|
1907
|
+
*
|
1908
|
+
* @overload incr(key, delta = 1, options = {})
|
1909
|
+
* @param key [String, Symbol] Key used to reference the value.
|
1910
|
+
* @param delta [Fixnum] Integer (up to 64 bits) value to increment
|
1911
|
+
* @param options [Hash] Options for operation.
|
1912
|
+
* @option options [Boolean] :create (false) If set to +true+, it will
|
1913
|
+
* initialize the key with zero value and zero flags (use +:initial+
|
1914
|
+
* option to set another initial value). Note: it won't increment the
|
1915
|
+
* missing value.
|
1916
|
+
* @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
|
1917
|
+
* missing key initialization. This option imply +:create+ option is
|
1918
|
+
* +true+.
|
1919
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
1920
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
1921
|
+
* absolute times (from the epoch). This option ignored for existent
|
1922
|
+
* keys.
|
1923
|
+
* @option options [Boolean] :extended (false) If set to +true+, the
|
1924
|
+
* operation will return tuple +[value, cas]+, otherwise (by default) it
|
1925
|
+
* returns just value.
|
1926
|
+
*
|
1927
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
1928
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
|
1929
|
+
*
|
1930
|
+
* @return [Fixnum] the actual value of the key.
|
1931
|
+
*
|
1932
|
+
* @raise [Couchbase::Error::NotFound] if key is missing and +:create+
|
1933
|
+
* option isn't +true+.
|
1934
|
+
*
|
1935
|
+
* @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
|
1936
|
+
* value
|
1937
|
+
*
|
1938
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1939
|
+
*
|
1940
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
1941
|
+
*
|
1942
|
+
* @example Increment key by one
|
1943
|
+
* c.incr("foo")
|
1944
|
+
*
|
1945
|
+
* @example Increment key by 50
|
1946
|
+
* c.incr("foo", 50)
|
1947
|
+
*
|
1948
|
+
* @example Increment key by one <b>OR</b> initialize with zero
|
1949
|
+
* c.incr("foo", :create => true) #=> will return old+1 or 0
|
1950
|
+
*
|
1951
|
+
* @example Increment key by one <b>OR</b> initialize with three
|
1952
|
+
* c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
|
1953
|
+
*
|
1954
|
+
* @example Increment key and get its CAS value
|
1955
|
+
* val, cas = c.incr("foo", :extended => true)
|
1956
|
+
*
|
1957
|
+
* @example Integer overflow
|
1958
|
+
* c.set("foo", -100)
|
1959
|
+
* c.get("foo") #=> -100
|
1960
|
+
* c.incr("foo") #=> 18446744073709551517
|
1961
|
+
*
|
1962
|
+
* @example Asynchronous invocation
|
1963
|
+
* c.run do
|
1964
|
+
* c.incr("foo") do |ret|
|
1965
|
+
* ret.operation #=> :increment
|
1966
|
+
* ret.success? #=> true
|
1967
|
+
* ret.key #=> "foo"
|
1968
|
+
* ret.value
|
1969
|
+
* ret.cas
|
1970
|
+
* end
|
1971
|
+
* end
|
1972
|
+
*
|
1973
|
+
*/
|
1538
1974
|
static VALUE
|
1539
1975
|
cb_bucket_incr(int argc, VALUE *argv, VALUE self)
|
1540
1976
|
{
|
1541
1977
|
return cb_bucket_arithmetic(+1, argc, argv, self);
|
1542
1978
|
}
|
1543
1979
|
|
1980
|
+
/*
|
1981
|
+
* Decrement the value of an existing numeric key
|
1982
|
+
*
|
1983
|
+
* The decrement methods reduce the value of a given key if the
|
1984
|
+
* corresponding value can be parsed to an integer value. These operations
|
1985
|
+
* are provided at a protocol level to eliminate the need to get, update,
|
1986
|
+
* and reset a simple integer value in the database. It supports the use of
|
1987
|
+
* an explicit offset value that will be used to reduce the stored value in
|
1988
|
+
* the database.
|
1989
|
+
*
|
1990
|
+
* @note that server values stored and transmitted as unsigned numbers,
|
1991
|
+
* therefore if you try to decrement negative or zero key, you will always
|
1992
|
+
* get zero.
|
1993
|
+
*
|
1994
|
+
* @overload decr(key, delta = 1, options = {})
|
1995
|
+
* @param key [String, Symbol] Key used to reference the value.
|
1996
|
+
* @param delta [Fixnum] Integer (up to 64 bits) value to decrement
|
1997
|
+
* @param options [Hash] Options for operation.
|
1998
|
+
* @option options [Boolean] :create (false) If set to +true+, it will
|
1999
|
+
* initialize the key with zero value and zero flags (use +:initial+
|
2000
|
+
* option to set another initial value). Note: it won't decrement the
|
2001
|
+
* missing value.
|
2002
|
+
* @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
|
2003
|
+
* missing key initialization. This option imply +:create+ option is
|
2004
|
+
* +true+.
|
2005
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2006
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2007
|
+
* absolute times (from the epoch). This option ignored for existent
|
2008
|
+
* keys.
|
2009
|
+
* @option options [Boolean] :extended (false) If set to +true+, the
|
2010
|
+
* operation will return tuple +[value, cas]+, otherwise (by default) it
|
2011
|
+
* returns just value.
|
2012
|
+
*
|
2013
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2014
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
|
2015
|
+
*
|
2016
|
+
* @return [Fixnum] the actual value of the key.
|
2017
|
+
*
|
2018
|
+
* @raise [Couchbase::Error::NotFound] if key is missing and +:create+
|
2019
|
+
* option isn't +true+.
|
2020
|
+
*
|
2021
|
+
* @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
|
2022
|
+
* value
|
2023
|
+
*
|
2024
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2025
|
+
*
|
2026
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2027
|
+
*
|
2028
|
+
* @example Decrement key by one
|
2029
|
+
* c.decr("foo")
|
2030
|
+
*
|
2031
|
+
* @example Decrement key by 50
|
2032
|
+
* c.decr("foo", 50)
|
2033
|
+
*
|
2034
|
+
* @example Decrement key by one <b>OR</b> initialize with zero
|
2035
|
+
* c.decr("foo", :create => true) #=> will return old-1 or 0
|
2036
|
+
*
|
2037
|
+
* @example Decrement key by one <b>OR</b> initialize with three
|
2038
|
+
* c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
|
2039
|
+
*
|
2040
|
+
* @example Decrement key and get its CAS value
|
2041
|
+
* val, cas = c.decr("foo", :extended => true)
|
2042
|
+
*
|
2043
|
+
* @example Decrementing zero
|
2044
|
+
* c.set("foo", 0)
|
2045
|
+
* c.decrement("foo", 100500) #=> 0
|
2046
|
+
*
|
2047
|
+
* @example Decrementing negative value
|
2048
|
+
* c.set("foo", -100)
|
2049
|
+
* c.decrement("foo", 100500) #=> 0
|
2050
|
+
*
|
2051
|
+
* @example Asynchronous invocation
|
2052
|
+
* c.run do
|
2053
|
+
* c.decr("foo") do |ret|
|
2054
|
+
* ret.operation #=> :decrement
|
2055
|
+
* ret.success? #=> true
|
2056
|
+
* ret.key #=> "foo"
|
2057
|
+
* ret.value
|
2058
|
+
* ret.cas
|
2059
|
+
* end
|
2060
|
+
* end
|
2061
|
+
*
|
2062
|
+
*/
|
1544
2063
|
static VALUE
|
1545
2064
|
cb_bucket_decr(int argc, VALUE *argv, VALUE self)
|
1546
2065
|
{
|
1547
2066
|
return cb_bucket_arithmetic(-1, argc, argv, self);
|
1548
2067
|
}
|
1549
2068
|
|
2069
|
+
/*
|
2070
|
+
* Obtain an object stored in Couchbase by given key.
|
2071
|
+
*
|
2072
|
+
* @overload get(*keys, options = {})
|
2073
|
+
* @param keys [String, Symbol, Array] One or several keys to fetch
|
2074
|
+
* @param options [Hash] Options for operation.
|
2075
|
+
* @option options [Boolean] :extended (false) If set to +true+, the
|
2076
|
+
* operation will return tuple +[value, flags, cas]+, otherwise (by
|
2077
|
+
* default) it returns just value.
|
2078
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2079
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2080
|
+
* absolute times (from the epoch).
|
2081
|
+
* @option options [Boolean] :quiet (self.quiet) If set to +true+, the
|
2082
|
+
* operation won't raise error for missing key, it will return +nil+.
|
2083
|
+
* Otherwise it will raise error in synchronous mode. In asynchronous
|
2084
|
+
* mode this option ignored.
|
2085
|
+
*
|
2086
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2087
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +flags+,
|
2088
|
+
* +cas+).
|
2089
|
+
*
|
2090
|
+
* @return [Object, Array, Hash] the value(s) (or tuples in extended mode)
|
2091
|
+
* assiciated with the key.
|
2092
|
+
*
|
2093
|
+
* @raise [Couchbase::Error::NotFound] if the key is missing in the
|
2094
|
+
* bucket.
|
2095
|
+
*
|
2096
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2097
|
+
*
|
2098
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2099
|
+
*
|
2100
|
+
* @example Get single value in quite mode (the default)
|
2101
|
+
* c.get("foo") #=> the associated value or nil
|
2102
|
+
*
|
2103
|
+
* @example Use alternative hash-like syntax
|
2104
|
+
* c["foo"] #=> the associated value or nil
|
2105
|
+
*
|
2106
|
+
* @example Get single value in verbose mode
|
2107
|
+
* c.get("missing-foo", :quiet => false) #=> raises Couchbase::NotFound
|
2108
|
+
*
|
2109
|
+
* @example Get and touch single value. The key won't be accessible after 10 seconds
|
2110
|
+
* c.get("foo", :ttl => 10)
|
2111
|
+
*
|
2112
|
+
* @example Extended get
|
2113
|
+
* val, flags, cas = c.get("foo", :extended => true)
|
2114
|
+
*
|
2115
|
+
* @example Get multiple keys
|
2116
|
+
* c.get("foo", "bar", "baz") #=> [val1, val2, val3]
|
2117
|
+
*
|
2118
|
+
* @example Extended get multiple keys
|
2119
|
+
* c.get("foo", "bar", :extended => true)
|
2120
|
+
* #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
|
2121
|
+
*
|
2122
|
+
* @example Asynchronous get
|
2123
|
+
* c.run do
|
2124
|
+
* c.get("foo", "bar", "baz") do |res|
|
2125
|
+
* ret.operation #=> :get
|
2126
|
+
* ret.success? #=> true
|
2127
|
+
* ret.key #=> "foo", "bar" or "baz" in separate calls
|
2128
|
+
* ret.value
|
2129
|
+
* ret.flags
|
2130
|
+
* ret.cas
|
2131
|
+
* end
|
2132
|
+
* end
|
2133
|
+
*
|
2134
|
+
* @overload get(keys, options = {})
|
2135
|
+
* When the method receive hash map, it will behave like it receive list
|
2136
|
+
* of keys (+keys.keys+), but also touch each key setting expiry time to
|
2137
|
+
* the corresponding value. But unlike usual get this command always
|
2138
|
+
* return hash map +{key => value}+ or +{key => [value, flags, cas]}+.
|
2139
|
+
*
|
2140
|
+
* @param keys [Hash] Map key-ttl
|
2141
|
+
* @param options [Hash] Options for operation. (see options definition
|
2142
|
+
* above)
|
2143
|
+
*
|
2144
|
+
* @return [Hash] the values (or tuples in extended mode) assiciated with
|
2145
|
+
* the keys.
|
2146
|
+
*
|
2147
|
+
* @example Get and touch multiple keys
|
2148
|
+
* c.get("foo" => 10, "bar" => 20) #=> {"foo" => val1, "bar" => val2}
|
2149
|
+
*
|
2150
|
+
* @example Extended get and touch multiple keys
|
2151
|
+
* c.get({"foo" => 10, "bar" => 20}, :extended => true)
|
2152
|
+
* #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
|
2153
|
+
*/
|
1550
2154
|
static VALUE
|
1551
2155
|
cb_bucket_get(int argc, VALUE *argv, VALUE self)
|
1552
2156
|
{
|
1553
2157
|
bucket_t *bucket = DATA_PTR(self);
|
1554
2158
|
context_t *ctx;
|
1555
|
-
VALUE args, rv, proc, exc,
|
2159
|
+
VALUE args, rv, proc, exc, keys;
|
1556
2160
|
long nn;
|
1557
2161
|
libcouchbase_error_t err;
|
1558
2162
|
struct key_traits *traits;
|
1559
|
-
int extended;
|
2163
|
+
int extended, mgat;
|
2164
|
+
long seqno;
|
1560
2165
|
|
2166
|
+
if (bucket->handle == NULL) {
|
2167
|
+
rb_raise(eConnectError, "closed connection");
|
2168
|
+
}
|
1561
2169
|
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
1562
2170
|
if (!bucket->async && proc != Qnil) {
|
1563
2171
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
@@ -1569,6 +2177,7 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
|
|
1569
2177
|
if (ctx == NULL) {
|
1570
2178
|
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
1571
2179
|
}
|
2180
|
+
mgat = traits->mgat;
|
1572
2181
|
keys = traits->keys_ary;
|
1573
2182
|
ctx->proc = proc;
|
1574
2183
|
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
@@ -1578,9 +2187,8 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
|
|
1578
2187
|
rv = rb_hash_new();
|
1579
2188
|
ctx->rv = &rv;
|
1580
2189
|
ctx->exception = Qnil;
|
1581
|
-
|
1582
|
-
|
1583
|
-
}
|
2190
|
+
seqno = bucket->seqno;
|
2191
|
+
bucket->seqno += nn;
|
1584
2192
|
err = libcouchbase_mget(bucket->handle, (const void *)ctx,
|
1585
2193
|
traits->nkeys, (const void * const *)traits->keys,
|
1586
2194
|
traits->lens, (traits->explicit_ttl) ? traits->ttls : NULL);
|
@@ -1593,11 +2201,13 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
|
|
1593
2201
|
free(ctx);
|
1594
2202
|
rb_exc_raise(exc);
|
1595
2203
|
}
|
1596
|
-
bucket->seqno += nn;
|
1597
2204
|
if (bucket->async) {
|
1598
2205
|
return Qnil;
|
1599
2206
|
} else {
|
1600
|
-
|
2207
|
+
if (bucket->seqno - seqno > 0) {
|
2208
|
+
/* we have some operations pending */
|
2209
|
+
bucket->io->run_event_loop(bucket->io);
|
2210
|
+
}
|
1601
2211
|
exc = ctx->exception;
|
1602
2212
|
extended = ctx->extended;
|
1603
2213
|
free(ctx);
|
@@ -1607,27 +2217,88 @@ cb_bucket_get(int argc, VALUE *argv, VALUE self)
|
|
1607
2217
|
if (bucket->exception != Qnil) {
|
1608
2218
|
rb_exc_raise(bucket->exception);
|
1609
2219
|
}
|
2220
|
+
if (mgat || (extended && nn > 1)) {
|
2221
|
+
return rv; /* return as a hash {key => [value, flags, cas], ...} */
|
2222
|
+
}
|
1610
2223
|
if (nn > 1) {
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
ret
|
1617
|
-
keys_ptr = RARRAY_PTR(keys);
|
1618
|
-
for (ii = 0; ii < nn; ii++) {
|
1619
|
-
rb_ary_push(ret, rb_hash_aref(rv, keys_ptr[ii]));
|
1620
|
-
}
|
1621
|
-
return ret; /* return as an array [value1, value2, ...] */
|
2224
|
+
long ii;
|
2225
|
+
VALUE *keys_ptr, ret;
|
2226
|
+
ret = rb_ary_new();
|
2227
|
+
keys_ptr = RARRAY_PTR(keys);
|
2228
|
+
for (ii = 0; ii < nn; ii++) {
|
2229
|
+
rb_ary_push(ret, rb_hash_aref(rv, keys_ptr[ii]));
|
1622
2230
|
}
|
2231
|
+
return ret; /* return as an array [value1, value2, ...] */
|
1623
2232
|
} else {
|
2233
|
+
VALUE vv = Qnil;
|
1624
2234
|
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
1625
2235
|
return vv;
|
1626
2236
|
}
|
1627
2237
|
}
|
1628
2238
|
}
|
1629
2239
|
|
1630
|
-
|
2240
|
+
/*
|
2241
|
+
* Update the expiry time of an item
|
2242
|
+
*
|
2243
|
+
* The +touch+ method allow you to update the expiration time on a given
|
2244
|
+
* key. This can be useful for situations where you want to prevent an item
|
2245
|
+
* from expiring without resetting the associated value. For example, for a
|
2246
|
+
* session database you might want to keep the session alive in the database
|
2247
|
+
* each time the user accesses a web page without explicitly updating the
|
2248
|
+
* session value, keeping the user's session active and available.
|
2249
|
+
*
|
2250
|
+
* @overload touch(key, options = {})
|
2251
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2252
|
+
* @param options [Hash] Options for operation.
|
2253
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2254
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2255
|
+
* absolute times (from the epoch).
|
2256
|
+
*
|
2257
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2258
|
+
* (valid attributes: +error+, +operation+, +key+).
|
2259
|
+
*
|
2260
|
+
* @return [Boolean] +true+ if the operation was successful and +false+
|
2261
|
+
* otherwise.
|
2262
|
+
*
|
2263
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2264
|
+
*
|
2265
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2266
|
+
*
|
2267
|
+
* @example Touch value using +default_ttl+
|
2268
|
+
* c.touch("foo")
|
2269
|
+
*
|
2270
|
+
* @example Touch value using custom TTL (10 seconds)
|
2271
|
+
* c.touch("foo", :ttl => 10)
|
2272
|
+
*
|
2273
|
+
* @overload touch(keys)
|
2274
|
+
* @param keys [Hash] The Hash where keys represent the keys in the
|
2275
|
+
* database, values -- the expiry times for corresponding key. See
|
2276
|
+
* description of +:ttl+ argument above for more information about TTL
|
2277
|
+
* values.
|
2278
|
+
*
|
2279
|
+
* @yieldparam ret [Result] the result of operation for each key in
|
2280
|
+
* asynchronous mode (valid attributes: +error+, +operation+, +key+).
|
2281
|
+
*
|
2282
|
+
* @return [Hash] Mapping keys to result of touch operation (+true+ if the
|
2283
|
+
* operation was successful and +false+ otherwise)
|
2284
|
+
*
|
2285
|
+
* @example Touch several values
|
2286
|
+
* c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
|
2287
|
+
*
|
2288
|
+
* @example Touch several values in async mode
|
2289
|
+
* c.run do
|
2290
|
+
* c.touch("foo" => 10, :bar => 20) do |ret|
|
2291
|
+
* ret.operation #=> :touch
|
2292
|
+
* ret.success? #=> true
|
2293
|
+
* ret.key #=> "foo" and "bar" in separate calls
|
2294
|
+
* end
|
2295
|
+
* end
|
2296
|
+
*
|
2297
|
+
* @example Touch single value
|
2298
|
+
* c.touch("foo" => 10) #=> true
|
2299
|
+
*
|
2300
|
+
*/
|
2301
|
+
static VALUE
|
1631
2302
|
cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
1632
2303
|
{
|
1633
2304
|
bucket_t *bucket = DATA_PTR(self);
|
@@ -1636,7 +2307,11 @@ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
|
1636
2307
|
size_t nn;
|
1637
2308
|
libcouchbase_error_t err;
|
1638
2309
|
struct key_traits *traits;
|
2310
|
+
long seqno;
|
1639
2311
|
|
2312
|
+
if (bucket->handle == NULL) {
|
2313
|
+
rb_raise(eConnectError, "closed connection");
|
2314
|
+
}
|
1640
2315
|
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
1641
2316
|
if (!bucket->async && proc != Qnil) {
|
1642
2317
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
@@ -1651,26 +2326,29 @@ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
|
1651
2326
|
ctx->proc = proc;
|
1652
2327
|
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1653
2328
|
ctx->bucket = bucket;
|
1654
|
-
rv =
|
2329
|
+
rv = rb_hash_new();
|
1655
2330
|
ctx->rv = &rv;
|
1656
2331
|
ctx->exception = Qnil;
|
1657
|
-
|
1658
|
-
|
1659
|
-
}
|
2332
|
+
seqno = bucket->seqno;
|
2333
|
+
bucket->seqno += nn;
|
1660
2334
|
err = libcouchbase_mtouch(bucket->handle, (const void *)ctx,
|
1661
2335
|
traits->nkeys, (const void * const *)traits->keys,
|
1662
2336
|
traits->lens, traits->ttls);
|
2337
|
+
free(traits->keys);
|
2338
|
+
free(traits->lens);
|
1663
2339
|
free(traits);
|
1664
2340
|
exc = cb_check_error(err, "failed to schedule touch request", Qnil);
|
1665
2341
|
if (exc != Qnil) {
|
1666
2342
|
free(ctx);
|
1667
2343
|
rb_exc_raise(exc);
|
1668
2344
|
}
|
1669
|
-
bucket->seqno += nn;
|
1670
2345
|
if (bucket->async) {
|
1671
2346
|
return Qnil;
|
1672
2347
|
} else {
|
1673
|
-
|
2348
|
+
if (bucket->seqno - seqno > 0) {
|
2349
|
+
/* we have some operations pending */
|
2350
|
+
bucket->io->run_event_loop(bucket->io);
|
2351
|
+
}
|
1674
2352
|
exc = ctx->exception;
|
1675
2353
|
free(ctx);
|
1676
2354
|
if (exc != Qnil) {
|
@@ -1680,13 +2358,39 @@ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
|
1680
2358
|
rb_exc_raise(bucket->exception);
|
1681
2359
|
}
|
1682
2360
|
if (nn > 1) {
|
1683
|
-
return rv;
|
2361
|
+
return rv; /* return as a hash {key => true, ...} */
|
1684
2362
|
} else {
|
1685
|
-
|
2363
|
+
VALUE vv = Qnil;
|
2364
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
2365
|
+
return vv;
|
1686
2366
|
}
|
1687
2367
|
}
|
1688
2368
|
}
|
1689
2369
|
|
2370
|
+
/*
|
2371
|
+
* Deletes all values from a server
|
2372
|
+
*
|
2373
|
+
* @overload flush
|
2374
|
+
* @yieldparam [Result] ret the object with +error+, +node+ and +operation+
|
2375
|
+
* attributes.
|
2376
|
+
*
|
2377
|
+
* @return [Boolean] +true+ on success
|
2378
|
+
*
|
2379
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2380
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2381
|
+
*
|
2382
|
+
* @example Simple flush the bucket
|
2383
|
+
* c.flush #=> true
|
2384
|
+
*
|
2385
|
+
* @example Asynchronous flush
|
2386
|
+
* c.run do
|
2387
|
+
* c.flush do |ret|
|
2388
|
+
* ret.operation #=> :flush
|
2389
|
+
* ret.success? #=> true
|
2390
|
+
* ret.node #=> "localhost:11211"
|
2391
|
+
* end
|
2392
|
+
* end
|
2393
|
+
*/
|
1690
2394
|
static VALUE
|
1691
2395
|
cb_bucket_flush(VALUE self)
|
1692
2396
|
{
|
@@ -1694,7 +2398,11 @@ cb_bucket_flush(VALUE self)
|
|
1694
2398
|
context_t *ctx;
|
1695
2399
|
VALUE rv, exc;
|
1696
2400
|
libcouchbase_error_t err;
|
2401
|
+
long seqno;
|
1697
2402
|
|
2403
|
+
if (bucket->handle == NULL) {
|
2404
|
+
rb_raise(eConnectError, "closed connection");
|
2405
|
+
}
|
1698
2406
|
if (!bucket->async && rb_block_given_p()) {
|
1699
2407
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
1700
2408
|
}
|
@@ -1712,17 +2420,99 @@ cb_bucket_flush(VALUE self)
|
|
1712
2420
|
ctx->proc = Qnil;
|
1713
2421
|
}
|
1714
2422
|
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2423
|
+
seqno = bucket->seqno;
|
2424
|
+
bucket->seqno++;
|
1715
2425
|
err = libcouchbase_flush(bucket->handle, (const void *)ctx);
|
1716
2426
|
exc = cb_check_error(err, "failed to schedule flush request", Qnil);
|
1717
2427
|
if (exc != Qnil) {
|
1718
2428
|
free(ctx);
|
1719
2429
|
rb_exc_raise(exc);
|
1720
2430
|
}
|
2431
|
+
if (bucket->async) {
|
2432
|
+
return Qnil;
|
2433
|
+
} else {
|
2434
|
+
if (bucket->seqno - seqno > 0) {
|
2435
|
+
/* we have some operations pending */
|
2436
|
+
bucket->io->run_event_loop(bucket->io);
|
2437
|
+
}
|
2438
|
+
exc = ctx->exception;
|
2439
|
+
free(ctx);
|
2440
|
+
if (exc != Qnil) {
|
2441
|
+
rb_exc_raise(exc);
|
2442
|
+
}
|
2443
|
+
return rv;
|
2444
|
+
}
|
2445
|
+
}
|
2446
|
+
|
2447
|
+
/*
|
2448
|
+
* Returns versions of the server for each node in the cluster
|
2449
|
+
*
|
2450
|
+
* @overload version
|
2451
|
+
* @yieldparam [Result] ret the object with +error+, +node+, +operation+
|
2452
|
+
* and +value+ attributes.
|
2453
|
+
*
|
2454
|
+
* @return [Hash] node-version pairs
|
2455
|
+
*
|
2456
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2457
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2458
|
+
*
|
2459
|
+
* @example Synchronous version request
|
2460
|
+
* c.version #=> will render version
|
2461
|
+
*
|
2462
|
+
* @example Asynchronous version request
|
2463
|
+
* c.run do
|
2464
|
+
* c.version do |ret|
|
2465
|
+
* ret.operation #=> :version
|
2466
|
+
* ret.success? #=> true
|
2467
|
+
* ret.node #=> "localhost:11211"
|
2468
|
+
* ret.value #=> will render version
|
2469
|
+
* end
|
2470
|
+
* end
|
2471
|
+
*/
|
2472
|
+
static VALUE
|
2473
|
+
cb_bucket_version(VALUE self)
|
2474
|
+
{
|
2475
|
+
bucket_t *bucket = DATA_PTR(self);
|
2476
|
+
context_t *ctx;
|
2477
|
+
VALUE rv, exc;
|
2478
|
+
libcouchbase_error_t err;
|
2479
|
+
long seqno;
|
2480
|
+
|
2481
|
+
if (bucket->handle == NULL) {
|
2482
|
+
rb_raise(eConnectError, "closed connection");
|
2483
|
+
}
|
2484
|
+
if (!bucket->async && rb_block_given_p()) {
|
2485
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
2486
|
+
}
|
2487
|
+
ctx = calloc(1, sizeof(context_t));
|
2488
|
+
if (ctx == NULL) {
|
2489
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
2490
|
+
}
|
2491
|
+
rv = rb_hash_new();
|
2492
|
+
ctx->rv = &rv;
|
2493
|
+
ctx->bucket = bucket;
|
2494
|
+
ctx->exception = Qnil;
|
2495
|
+
if (rb_block_given_p()) {
|
2496
|
+
ctx->proc = rb_block_proc();
|
2497
|
+
} else {
|
2498
|
+
ctx->proc = Qnil;
|
2499
|
+
}
|
2500
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2501
|
+
seqno = bucket->seqno;
|
1721
2502
|
bucket->seqno++;
|
2503
|
+
err = libcouchbase_server_versions(bucket->handle, (const void *)ctx);
|
2504
|
+
exc = cb_check_error(err, "failed to schedule version request", Qnil);
|
2505
|
+
if (exc != Qnil) {
|
2506
|
+
free(ctx);
|
2507
|
+
rb_exc_raise(exc);
|
2508
|
+
}
|
1722
2509
|
if (bucket->async) {
|
1723
2510
|
return Qnil;
|
1724
2511
|
} else {
|
1725
|
-
|
2512
|
+
if (bucket->seqno - seqno > 0) {
|
2513
|
+
/* we have some operations pending */
|
2514
|
+
bucket->io->run_event_loop(bucket->io);
|
2515
|
+
}
|
1726
2516
|
exc = ctx->exception;
|
1727
2517
|
free(ctx);
|
1728
2518
|
if (exc != Qnil) {
|
@@ -1732,6 +2522,46 @@ cb_bucket_flush(VALUE self)
|
|
1732
2522
|
}
|
1733
2523
|
}
|
1734
2524
|
|
2525
|
+
/*
|
2526
|
+
* Request server statistics.
|
2527
|
+
*
|
2528
|
+
* Fetches stats from each node in cluster. Without a key specified the
|
2529
|
+
* server will respond with a "default" set of statistical information. In
|
2530
|
+
* asynchronous mode each statistic is returned in separate call where the
|
2531
|
+
* Result object yielded (+#key+ contains the name of the statistical item
|
2532
|
+
* and the +#value+ contains the value, the +#node+ will indicate the server
|
2533
|
+
* address). In synchronous mode it returns the hash of stats keys and
|
2534
|
+
* node-value pairs as a value.
|
2535
|
+
*
|
2536
|
+
* @overload stats(arg = nil)
|
2537
|
+
* @param [String] arg argument to STATS query
|
2538
|
+
* @yieldparam [Result] ret the object with +node+, +key+ and +value+
|
2539
|
+
* attributes.
|
2540
|
+
*
|
2541
|
+
* @example Found how many items in the bucket
|
2542
|
+
* total = 0
|
2543
|
+
* c.stats["total_items"].each do |key, value|
|
2544
|
+
* total += value.to_i
|
2545
|
+
* end
|
2546
|
+
*
|
2547
|
+
* @example Found total items number asynchronously
|
2548
|
+
* total = 0
|
2549
|
+
* c.run do
|
2550
|
+
* c.stats do |ret|
|
2551
|
+
* if ret.key == "total_items"
|
2552
|
+
* total += ret.value.to_i
|
2553
|
+
* end
|
2554
|
+
* end
|
2555
|
+
* end
|
2556
|
+
*
|
2557
|
+
* @example Get memory stats (works on couchbase buckets)
|
2558
|
+
* c.stats(:memory) #=> {"mem_used"=>{...}, ...}
|
2559
|
+
*
|
2560
|
+
* @return [Hash] where keys are stat keys, values are host-value pairs
|
2561
|
+
*
|
2562
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2563
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2564
|
+
*/
|
1735
2565
|
static VALUE
|
1736
2566
|
cb_bucket_stats(int argc, VALUE *argv, VALUE self)
|
1737
2567
|
{
|
@@ -1741,7 +2571,11 @@ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
|
|
1741
2571
|
char *key;
|
1742
2572
|
size_t nkey;
|
1743
2573
|
libcouchbase_error_t err;
|
2574
|
+
long seqno;
|
1744
2575
|
|
2576
|
+
if (bucket->handle == NULL) {
|
2577
|
+
rb_raise(eConnectError, "closed connection");
|
2578
|
+
}
|
1745
2579
|
rb_scan_args(argc, argv, "01&", &arg, &proc);
|
1746
2580
|
if (!bucket->async && proc != Qnil) {
|
1747
2581
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
@@ -1758,12 +2592,15 @@ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
|
|
1758
2592
|
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1759
2593
|
ctx->exception = Qnil;
|
1760
2594
|
if (arg != Qnil) {
|
2595
|
+
arg = unify_key(arg);
|
1761
2596
|
key = RSTRING_PTR(arg);
|
1762
2597
|
nkey = RSTRING_LEN(arg);
|
1763
2598
|
} else {
|
1764
2599
|
key = NULL;
|
1765
2600
|
nkey = 0;
|
1766
2601
|
}
|
2602
|
+
seqno = bucket->seqno;
|
2603
|
+
bucket->seqno++;
|
1767
2604
|
err = libcouchbase_server_stats(bucket->handle, (const void *)ctx,
|
1768
2605
|
key, nkey);
|
1769
2606
|
exc = cb_check_error(err, "failed to schedule stat request", Qnil);
|
@@ -1771,11 +2608,13 @@ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
|
|
1771
2608
|
free(ctx);
|
1772
2609
|
rb_exc_raise(exc);
|
1773
2610
|
}
|
1774
|
-
bucket->seqno++;
|
1775
2611
|
if (bucket->async) {
|
1776
2612
|
return Qnil;
|
1777
2613
|
} else {
|
1778
|
-
|
2614
|
+
if (bucket->seqno - seqno > 0) {
|
2615
|
+
/* we have some operations pending */
|
2616
|
+
bucket->io->run_event_loop(bucket->io);
|
2617
|
+
}
|
1779
2618
|
exc = ctx->exception;
|
1780
2619
|
free(ctx);
|
1781
2620
|
if (exc != Qnil) {
|
@@ -1795,12 +2634,31 @@ do_run(VALUE *args)
|
|
1795
2634
|
{
|
1796
2635
|
VALUE self = args[0], proc = args[1], exc;
|
1797
2636
|
bucket_t *bucket = DATA_PTR(self);
|
2637
|
+
time_t tm;
|
2638
|
+
uint32_t old_tmo, new_tmo, diff;
|
1798
2639
|
|
2640
|
+
if (bucket->handle == NULL) {
|
2641
|
+
rb_raise(eConnectError, "closed connection");
|
2642
|
+
}
|
2643
|
+
if (bucket->async) {
|
2644
|
+
rb_raise(eInvalidError, "nested #run");
|
2645
|
+
}
|
1799
2646
|
bucket->seqno = 0;
|
1800
2647
|
bucket->async = 1;
|
2648
|
+
|
2649
|
+
tm = time(NULL);
|
1801
2650
|
cb_proc_call(proc, 1, self);
|
1802
2651
|
if (bucket->seqno > 0) {
|
2652
|
+
old_tmo = libcouchbase_get_timeout(bucket->handle);
|
2653
|
+
diff = (uint32_t)(time(NULL) - tm + 1);
|
2654
|
+
diff *= 1000000;
|
2655
|
+
new_tmo = bucket->timeout += diff;
|
2656
|
+
libcouchbase_set_timeout(bucket->handle, bucket->timeout);
|
1803
2657
|
bucket->io->run_event_loop(bucket->io);
|
2658
|
+
/* restore timeout if it wasn't changed */
|
2659
|
+
if (bucket->timeout == new_tmo) {
|
2660
|
+
libcouchbase_set_timeout(bucket->handle, old_tmo);
|
2661
|
+
}
|
1804
2662
|
if (bucket->exception != Qnil) {
|
1805
2663
|
exc = bucket->exception;
|
1806
2664
|
bucket->exception = Qnil;
|
@@ -1823,7 +2681,7 @@ ensure_run(VALUE *args)
|
|
1823
2681
|
/*
|
1824
2682
|
* Run the event loop.
|
1825
2683
|
*
|
1826
|
-
* @yieldparam [Bucket] the bucket instance
|
2684
|
+
* @yieldparam [Bucket] bucket the bucket instance
|
1827
2685
|
*
|
1828
2686
|
* @example Use block to run the loop
|
1829
2687
|
* c = Couchbase.new
|
@@ -1838,6 +2696,9 @@ ensure_run(VALUE *args)
|
|
1838
2696
|
* end
|
1839
2697
|
* c.run(&operations)
|
1840
2698
|
*
|
2699
|
+
* @return [nil]
|
2700
|
+
*
|
2701
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1841
2702
|
*/
|
1842
2703
|
static VALUE
|
1843
2704
|
cb_bucket_run(VALUE self)
|
@@ -1852,7 +2713,72 @@ cb_bucket_run(VALUE self)
|
|
1852
2713
|
}
|
1853
2714
|
|
1854
2715
|
/*
|
1855
|
-
* Unconditionally
|
2716
|
+
* Unconditionally store the object in the Couchbase
|
2717
|
+
*
|
2718
|
+
* @overload set(key, value, options = {})
|
2719
|
+
*
|
2720
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2721
|
+
* @param value [Object] Value to be stored
|
2722
|
+
* @param options [Hash] Options for operation.
|
2723
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2724
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2725
|
+
* absolute times (from the epoch).
|
2726
|
+
* @option options [Fixnum] :flags (self.default_flags) Flags for storage
|
2727
|
+
* options. Flags are ignored by the server but preserved for use by the
|
2728
|
+
* client. For more info see {Bucket#default_flags}.
|
2729
|
+
* @option options [Symbol] :format (self.default_format) The
|
2730
|
+
* representation for storing the value in the bucket. For more info see
|
2731
|
+
* {Bucket#default_format}.
|
2732
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2733
|
+
* created on the server and is guaranteed to be unique for each value of
|
2734
|
+
* a given key. This value is used to provide simple optimistic
|
2735
|
+
* concurrency control when multiple clients or threads try to update an
|
2736
|
+
* item simultaneously.
|
2737
|
+
*
|
2738
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2739
|
+
* (valid attributes: +error+, +operation+, +key+).
|
2740
|
+
*
|
2741
|
+
* @return [Fixnum] The CAS value of the object.
|
2742
|
+
*
|
2743
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect}).
|
2744
|
+
* @raise [Couchbase::Error::KeyExists] if the key already exists on the
|
2745
|
+
* server.
|
2746
|
+
* @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
|
2747
|
+
* with chosen encoder, e.g. if you try to store the Hash in +:plain+
|
2748
|
+
* mode.
|
2749
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2750
|
+
*
|
2751
|
+
* @example Store the key which will be expired in 2 seconds using relative TTL.
|
2752
|
+
* c.set("foo", "bar", :ttl => 2)
|
2753
|
+
*
|
2754
|
+
* @example Store the key which will be expired in 2 seconds using absolute TTL.
|
2755
|
+
* c.set("foo", "bar", :ttl => Time.now.to_i + 2)
|
2756
|
+
*
|
2757
|
+
* @example Force JSON document format for value
|
2758
|
+
* c.set("foo", {"bar" => "baz}, :format => :document)
|
2759
|
+
*
|
2760
|
+
* @example Use hash-like syntax to store the value
|
2761
|
+
* c.set["foo"] = {"bar" => "baz}
|
2762
|
+
*
|
2763
|
+
* @example Use extended hash-like syntax
|
2764
|
+
* c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
|
2765
|
+
* c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
|
2766
|
+
*
|
2767
|
+
* @example Set application specific flags (note that it will be OR-ed with format flags)
|
2768
|
+
* c.set("foo", "bar", :flags => 0x1000)
|
2769
|
+
*
|
2770
|
+
* @example Perform optimistic locking by specifying last known CAS version
|
2771
|
+
* c.set("foo", "bar", :cas => 8835713818674332672)
|
2772
|
+
*
|
2773
|
+
* @example Perform asynchronous call
|
2774
|
+
* c.run do
|
2775
|
+
* c.set("foo", "bar") do |ret|
|
2776
|
+
* ret.operation #=> :set
|
2777
|
+
* ret.success? #=> true
|
2778
|
+
* ret.key #=> "foo"
|
2779
|
+
* ret.cas
|
2780
|
+
* end
|
2781
|
+
* end
|
1856
2782
|
*/
|
1857
2783
|
static VALUE
|
1858
2784
|
cb_bucket_set(int argc, VALUE *argv, VALUE self)
|
@@ -1861,7 +2787,43 @@ cb_bucket_set(int argc, VALUE *argv, VALUE self)
|
|
1861
2787
|
}
|
1862
2788
|
|
1863
2789
|
/*
|
1864
|
-
* Add the item to the
|
2790
|
+
* Add the item to the database, but fail if the object exists already
|
2791
|
+
*
|
2792
|
+
* @overload add(key, value, options = {})
|
2793
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2794
|
+
* @param value [Object] Value to be stored
|
2795
|
+
* @param options [Hash] Options for operation.
|
2796
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2797
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2798
|
+
* absolute times (from the epoch).
|
2799
|
+
* @option options [Fixnum] :flags (self.default_flags) Flags for storage
|
2800
|
+
* options. Flags are ignored by the server but preserved for use by the
|
2801
|
+
* client. For more info see {Bucket#default_flags}.
|
2802
|
+
* @option options [Symbol] :format (self.default_format) The
|
2803
|
+
* representation for storing the value in the bucket. For more info see
|
2804
|
+
* {Bucket#default_format}.
|
2805
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2806
|
+
* created on the server and is guaranteed to be unique for each value of
|
2807
|
+
* a given key. This value is used to provide simple optimistic
|
2808
|
+
* concurrency control when multiple clients or threads try to update an
|
2809
|
+
* item simultaneously.
|
2810
|
+
*
|
2811
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2812
|
+
* (valid attributes: +error+, +operation+, +key+).
|
2813
|
+
*
|
2814
|
+
* @return [Fixnum] The CAS value of the object.
|
2815
|
+
*
|
2816
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2817
|
+
* @raise [Couchbase::Error::KeyExists] if the key already exists on the
|
2818
|
+
* server
|
2819
|
+
* @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
|
2820
|
+
* with chosen encoder, e.g. if you try to store the Hash in +:plain+
|
2821
|
+
* mode.
|
2822
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2823
|
+
*
|
2824
|
+
* @example Add the same key twice
|
2825
|
+
* c.add("foo", "bar") #=> stored successully
|
2826
|
+
* c.add("foo", "baz") #=> will raise Couchbase::Error::KeyExists: failed to store value (key="foo", error=0x0c)
|
1865
2827
|
*/
|
1866
2828
|
static VALUE
|
1867
2829
|
cb_bucket_add(int argc, VALUE *argv, VALUE self)
|
@@ -1870,7 +2832,31 @@ cb_bucket_add(int argc, VALUE *argv, VALUE self)
|
|
1870
2832
|
}
|
1871
2833
|
|
1872
2834
|
/*
|
1873
|
-
* Replace the existing object in the
|
2835
|
+
* Replace the existing object in the database
|
2836
|
+
*
|
2837
|
+
* @overload replace(key, value, options = {})
|
2838
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2839
|
+
* @param value [Object] Value to be stored
|
2840
|
+
* @param options [Hash] Options for operation.
|
2841
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2842
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2843
|
+
* absolute times (from the epoch).
|
2844
|
+
* @option options [Fixnum] :flags (self.default_flags) Flags for storage
|
2845
|
+
* options. Flags are ignored by the server but preserved for use by the
|
2846
|
+
* client. For more info see {Bucket#default_flags}.
|
2847
|
+
* @option options [Symbol] :format (self.default_format) The
|
2848
|
+
* representation for storing the value in the bucket. For more info see
|
2849
|
+
* {Bucket#default_format}.
|
2850
|
+
*
|
2851
|
+
* @return [Fixnum] The CAS value of the object.
|
2852
|
+
*
|
2853
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2854
|
+
* @raise [Couchbase::Error::NotFound] if the key doesn't exists
|
2855
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
2856
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2857
|
+
*
|
2858
|
+
* @example Replacing missing key
|
2859
|
+
* c.replace("foo", "baz") #=> will raise Couchbase::Error::NotFound: failed to store value (key="foo", error=0x0d)
|
1874
2860
|
*/
|
1875
2861
|
static VALUE
|
1876
2862
|
cb_bucket_replace(int argc, VALUE *argv, VALUE self)
|
@@ -1880,6 +2866,67 @@ cb_bucket_replace(int argc, VALUE *argv, VALUE self)
|
|
1880
2866
|
|
1881
2867
|
/*
|
1882
2868
|
* Append this object to the existing object
|
2869
|
+
*
|
2870
|
+
* @note This operation is kind of data-aware from server point of view.
|
2871
|
+
* This mean that the server treats value as binary stream and just
|
2872
|
+
* perform concatenation, therefore it won't work with +:marshal+ and
|
2873
|
+
* +:document+ formats, because of lack of knowledge how to merge values
|
2874
|
+
* in these formats. See Bucket#cas for workaround.
|
2875
|
+
*
|
2876
|
+
* @overload append(key, value, options = {})
|
2877
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2878
|
+
* @param value [Object] Value to be stored
|
2879
|
+
* @param options [Hash] Options for operation.
|
2880
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2881
|
+
* created on the server and is guaranteed to be unique for each value of
|
2882
|
+
* a given key. This value is used to provide simple optimistic
|
2883
|
+
* concurrency control when multiple clients or threads try to update an
|
2884
|
+
* item simultaneously.
|
2885
|
+
* @option options [Symbol] :format (self.default_format) The
|
2886
|
+
* representation for storing the value in the bucket. For more info see
|
2887
|
+
* {Bucket#default_format}.
|
2888
|
+
*
|
2889
|
+
* @return [Fixnum] The CAS value of the object.
|
2890
|
+
*
|
2891
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2892
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
2893
|
+
* @raise [Couchbase::Error::NotStored] if the key doesn't exist
|
2894
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2895
|
+
*
|
2896
|
+
* @example Simple append
|
2897
|
+
* c.set("foo", "aaa")
|
2898
|
+
* c.append("foo", "bbb")
|
2899
|
+
* c.get("foo") #=> "aaabbb"
|
2900
|
+
*
|
2901
|
+
* @example Implementing sets using append
|
2902
|
+
* def set_add(key, *values)
|
2903
|
+
* encoded = values.flatten.map{|v| "+#{v} "}.join
|
2904
|
+
* append(key, encoded)
|
2905
|
+
* end
|
2906
|
+
*
|
2907
|
+
* def set_remove(key, *values)
|
2908
|
+
* encoded = values.flatten.map{|v| "-#{v} "}.join
|
2909
|
+
* append(key, encoded)
|
2910
|
+
* end
|
2911
|
+
*
|
2912
|
+
* def set_get(key)
|
2913
|
+
* encoded = get(key)
|
2914
|
+
* ret = Set.new
|
2915
|
+
* encoded.split(' ').each do |v|
|
2916
|
+
* op, val = v[0], v[1..-1]
|
2917
|
+
* case op
|
2918
|
+
* when "-"
|
2919
|
+
* ret.delete(val)
|
2920
|
+
* when "+"
|
2921
|
+
* ret.add(val)
|
2922
|
+
* end
|
2923
|
+
* end
|
2924
|
+
* ret
|
2925
|
+
* end
|
2926
|
+
*
|
2927
|
+
* @example Using optimistic locking. The operation will fail on CAS mismatch
|
2928
|
+
* ver = c.set("foo", "aaa")
|
2929
|
+
* c.append("foo", "bbb", :cas => ver)
|
1883
2930
|
*/
|
1884
2931
|
static VALUE
|
1885
2932
|
cb_bucket_append(int argc, VALUE *argv, VALUE self)
|
@@ -1889,6 +2936,46 @@ cb_bucket_append(int argc, VALUE *argv, VALUE self)
|
|
1889
2936
|
|
1890
2937
|
/*
|
1891
2938
|
* Prepend this object to the existing object
|
2939
|
+
*
|
2940
|
+
* @note This operation is kind of data-aware from server point of view.
|
2941
|
+
* This mean that the server treats value as binary stream and just
|
2942
|
+
* perform concatenation, therefore it won't work with +:marshal+ and
|
2943
|
+
* +:document+ formats, because of lack of knowledge how to merge values
|
2944
|
+
* in these formats. See Bucket#cas for workaround.
|
2945
|
+
*
|
2946
|
+
* @overload prepend(key, value, options = {})
|
2947
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2948
|
+
* @param value [Object] Value to be stored
|
2949
|
+
* @param options [Hash] Options for operation.
|
2950
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2951
|
+
* created on the server and is guaranteed to be unique for each value of
|
2952
|
+
* a given key. This value is used to provide simple optimistic
|
2953
|
+
* concurrency control when multiple clients or threads try to update an
|
2954
|
+
* item simultaneously.
|
2955
|
+
* @option options [Symbol] :format (self.default_format) The
|
2956
|
+
* representation for storing the value in the bucket. For more info see
|
2957
|
+
* {Bucket#default_format}.
|
2958
|
+
*
|
2959
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2960
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
2961
|
+
* @raise [Couchbase::Error::NotStored] if the key doesn't exist
|
2962
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2963
|
+
*
|
2964
|
+
* @example Simple prepend example
|
2965
|
+
* c.set("foo", "aaa")
|
2966
|
+
* c.prepend("foo", "bbb")
|
2967
|
+
* c.get("foo") #=> "bbbaaa"
|
2968
|
+
*
|
2969
|
+
* @example Using explicit format option
|
2970
|
+
* c.default_format #=> :document
|
2971
|
+
* c.set("foo", {"y" => "z"})
|
2972
|
+
* c.prepend("foo", '[', :format => :plain)
|
2973
|
+
* c.append("foo", ', {"z": "y"}]', :format => :plain)
|
2974
|
+
* c.get("foo") #=> [{"y"=>"z"}, {"z"=>"y"}]
|
2975
|
+
*
|
2976
|
+
* @example Using optimistic locking. The operation will fail on CAS mismatch
|
2977
|
+
* ver = c.set("foo", "aaa")
|
2978
|
+
* c.prepend("foo", "bbb", :cas => ver)
|
1892
2979
|
*/
|
1893
2980
|
static VALUE
|
1894
2981
|
cb_bucket_prepend(int argc, VALUE *argv, VALUE self)
|
@@ -1910,6 +2997,28 @@ cb_bucket_aset(int argc, VALUE *argv, VALUE self)
|
|
1910
2997
|
return cb_bucket_set(argc, argv, self);
|
1911
2998
|
}
|
1912
2999
|
|
3000
|
+
/*
|
3001
|
+
* Close the connection to the cluster
|
3002
|
+
*
|
3003
|
+
* @return [true]
|
3004
|
+
*
|
3005
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
3006
|
+
*/
|
3007
|
+
static VALUE
|
3008
|
+
cb_bucket_disconnect(VALUE self)
|
3009
|
+
{
|
3010
|
+
bucket_t *bucket = DATA_PTR(self);
|
3011
|
+
|
3012
|
+
if (bucket->handle) {
|
3013
|
+
libcouchbase_destroy(bucket->handle);
|
3014
|
+
bucket->handle = NULL;
|
3015
|
+
bucket->io = NULL;
|
3016
|
+
return Qtrue;
|
3017
|
+
} else {
|
3018
|
+
rb_raise(eConnectError, "closed connection");
|
3019
|
+
}
|
3020
|
+
}
|
3021
|
+
|
1913
3022
|
/*
|
1914
3023
|
* Check if result of operation was successful.
|
1915
3024
|
*
|
@@ -1922,10 +3031,15 @@ cb_result_success_p(VALUE self)
|
|
1922
3031
|
return RTEST(rb_ivar_get(self, id_iv_error)) ? Qfalse : Qtrue;
|
1923
3032
|
}
|
1924
3033
|
|
3034
|
+
/*
|
3035
|
+
* Returns a string containing a human-readable representation of the Result.
|
3036
|
+
*
|
3037
|
+
* @return [String]
|
3038
|
+
*/
|
1925
3039
|
static VALUE
|
1926
3040
|
cb_result_inspect(VALUE self)
|
1927
3041
|
{
|
1928
|
-
VALUE str, attr,
|
3042
|
+
VALUE str, attr, error;
|
1929
3043
|
char buf[100];
|
1930
3044
|
|
1931
3045
|
str = rb_str_buf_new2("#<");
|
@@ -1935,12 +3049,12 @@ cb_result_inspect(VALUE self)
|
|
1935
3049
|
|
1936
3050
|
attr = rb_ivar_get(self, id_iv_error);
|
1937
3051
|
if (RTEST(attr)) {
|
1938
|
-
|
3052
|
+
error = rb_ivar_get(attr, id_iv_error);
|
1939
3053
|
} else {
|
1940
|
-
|
3054
|
+
error = INT2FIX(0);
|
1941
3055
|
}
|
1942
3056
|
rb_str_buf_cat2(str, " error=0x");
|
1943
|
-
rb_str_append(str, rb_funcall(
|
3057
|
+
rb_str_append(str, rb_funcall(error, id_to_s, 1, INT2FIX(16)));
|
1944
3058
|
|
1945
3059
|
attr = rb_ivar_get(self, id_iv_key);
|
1946
3060
|
if (RTEST(attr)) {
|
@@ -1975,6 +3089,7 @@ cb_result_inspect(VALUE self)
|
|
1975
3089
|
Init_couchbase_ext(void)
|
1976
3090
|
{
|
1977
3091
|
mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
|
3092
|
+
mURI = rb_const_get(rb_cObject, rb_intern("URI"));
|
1978
3093
|
mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
|
1979
3094
|
mCouchbase = rb_define_module("Couchbase");
|
1980
3095
|
|
@@ -1985,6 +3100,9 @@ Init_couchbase_ext(void)
|
|
1985
3100
|
/* Document-class: Couchbase::Error::Auth
|
1986
3101
|
* Authentication error */
|
1987
3102
|
eAuthError = rb_define_class_under(mError, "Auth", eBaseError);
|
3103
|
+
/* Document-class: Couchbase::Error::BucketNotFound
|
3104
|
+
* The given bucket not found in the cluster */
|
3105
|
+
eBucketNotFoundError = rb_define_class_under(mError, "BucketNotFound", eBaseError);
|
1988
3106
|
/* Document-class: Couchbase::Error::Busy
|
1989
3107
|
* The cluster is too busy now. Try again later */
|
1990
3108
|
eBusyError = rb_define_class_under(mError, "Busy", eBaseError);
|
@@ -2045,6 +3163,12 @@ Init_couchbase_ext(void)
|
|
2045
3163
|
/* Document-class: Couchbase::Error::Protocol
|
2046
3164
|
* Protocol error */
|
2047
3165
|
eProtocolError = rb_define_class_under(mError, "Protocol", eBaseError);
|
3166
|
+
/* Document-class: Couchbase::Error::Timeout
|
3167
|
+
* Timeout error */
|
3168
|
+
eTimeoutError = rb_define_class_under(mError, "Timeout", eBaseError);
|
3169
|
+
/* Document-class: Couchbase::Error::Connect
|
3170
|
+
* Connect error */
|
3171
|
+
eConnectError = rb_define_class_under(mError, "Connect", eBaseError);
|
2048
3172
|
|
2049
3173
|
/* Document-method: error
|
2050
3174
|
* @return [Boolean] the error code from libcouchbase */
|
@@ -2064,7 +3188,7 @@ Init_couchbase_ext(void)
|
|
2064
3188
|
id_iv_operation = rb_intern("@operation");
|
2065
3189
|
|
2066
3190
|
/* Document-class: Couchbase::Result
|
2067
|
-
*
|
3191
|
+
* The object which yielded to asynchronous callbacks */
|
2068
3192
|
cResult = rb_define_class_under(mCouchbase, "Result", rb_cObject);
|
2069
3193
|
rb_define_method(cResult, "inspect", cb_result_inspect, 0);
|
2070
3194
|
rb_define_method(cResult, "success?", cb_result_success_p, 0);
|
@@ -2072,7 +3196,7 @@ Init_couchbase_ext(void)
|
|
2072
3196
|
* @return [Symbol] */
|
2073
3197
|
rb_define_attr(cResult, "operation", 1, 0);
|
2074
3198
|
/* Document-method: error
|
2075
|
-
* @return [Error::Base] */
|
3199
|
+
* @return [Couchbase::Error::Base] */
|
2076
3200
|
rb_define_attr(cResult, "error", 1, 0);
|
2077
3201
|
/* Document-method: key
|
2078
3202
|
* @return [String] */
|
@@ -2100,17 +3224,35 @@ Init_couchbase_ext(void)
|
|
2100
3224
|
* Couchbase. */
|
2101
3225
|
cBucket = rb_define_class_under(mCouchbase, "Bucket", rb_cObject);
|
2102
3226
|
object_space = rb_hash_new();
|
3227
|
+
/* @private Hack to avoid GC in some cases */
|
2103
3228
|
rb_define_const(cBucket, "OBJECT_SPACE", object_space);
|
2104
3229
|
|
3230
|
+
/* 0x03: Bitmask for flag bits responsible for format */
|
2105
3231
|
rb_define_const(cBucket, "FMT_MASK", INT2FIX(FMT_MASK));
|
3232
|
+
/* 0x00: Document format. The (default) format supports most of ruby
|
3233
|
+
* types which could be mapped to JSON data (hashes, arrays, strings,
|
3234
|
+
* numbers). Future version will be able to run map/reduce queries on
|
3235
|
+
* the values in the document form (hashes). */
|
2106
3236
|
rb_define_const(cBucket, "FMT_DOCUMENT", INT2FIX(FMT_DOCUMENT));
|
3237
|
+
/* 0x01: Marshal format. The format which supports transparent
|
3238
|
+
* serialization of ruby objects with standard <tt>Marshal.dump</tt> and
|
3239
|
+
* <tt>Marhal.load</tt> methods. */
|
2107
3240
|
rb_define_const(cBucket, "FMT_MARSHAL", INT2FIX(FMT_MARSHAL));
|
3241
|
+
/* 0x02: Plain format. The format which force client don't apply any
|
3242
|
+
* conversions to the value, but it should be passed as String. It
|
3243
|
+
* could be useful for building custom algorithms or formats. For
|
3244
|
+
* example implement set:
|
3245
|
+
* http://dustin.github.com/2011/02/17/memcached-set.html */
|
2108
3246
|
rb_define_const(cBucket, "FMT_PLAIN", INT2FIX(FMT_PLAIN));
|
2109
3247
|
|
2110
3248
|
rb_define_singleton_method(cBucket, "new", cb_bucket_new, -1);
|
2111
3249
|
|
2112
3250
|
rb_define_method(cBucket, "initialize", cb_bucket_init, -1);
|
2113
3251
|
rb_define_method(cBucket, "inspect", cb_bucket_inspect, 0);
|
3252
|
+
|
3253
|
+
/* Document-method: seqno
|
3254
|
+
* The number of scheduled commands */
|
3255
|
+
/* rb_define_attr(cBucket, "seqno", 1, 0); */
|
2114
3256
|
rb_define_method(cBucket, "seqno", cb_bucket_seqno, 0);
|
2115
3257
|
|
2116
3258
|
rb_define_method(cBucket, "add", cb_bucket_add, -1);
|
@@ -2124,39 +3266,27 @@ Init_couchbase_ext(void)
|
|
2124
3266
|
rb_define_method(cBucket, "delete", cb_bucket_delete, -1);
|
2125
3267
|
rb_define_method(cBucket, "stats", cb_bucket_stats, -1);
|
2126
3268
|
rb_define_method(cBucket, "flush", cb_bucket_flush, 0);
|
3269
|
+
rb_define_method(cBucket, "version", cb_bucket_version, 0);
|
2127
3270
|
rb_define_method(cBucket, "incr", cb_bucket_incr, -1);
|
2128
3271
|
rb_define_method(cBucket, "decr", cb_bucket_decr, -1);
|
3272
|
+
rb_define_method(cBucket, "disconnect", cb_bucket_disconnect, 0);
|
3273
|
+
rb_define_method(cBucket, "reconnect", cb_bucket_reconnect, -1);
|
2129
3274
|
|
2130
3275
|
rb_define_alias(cBucket, "decrement", "decr");
|
2131
3276
|
rb_define_alias(cBucket, "increment", "incr");
|
2132
3277
|
|
2133
3278
|
rb_define_alias(cBucket, "[]", "get");
|
2134
|
-
|
3279
|
+
rb_define_alias(cBucket, "[]=", "set");
|
2135
3280
|
rb_define_method(cBucket, "[]=", cb_bucket_aset, -1);
|
2136
3281
|
|
2137
|
-
|
2138
|
-
* Flag specifying if the connection asynchronous.
|
2139
|
-
*
|
2140
|
-
* By default all operations are synchronous and block waiting for
|
2141
|
-
* results, but you can make them asynchronous and run event loop
|
2142
|
-
* explicitly. (see Bucket#run)
|
2143
|
-
*
|
2144
|
-
* @example Return value of #get operation depending on async flag
|
2145
|
-
* connection = Connection.new
|
2146
|
-
* connection.async? #=> false
|
2147
|
-
*
|
2148
|
-
* connection.run do |conn|
|
2149
|
-
* conn.async? #=> true
|
2150
|
-
* end
|
2151
|
-
*
|
2152
|
-
* @return [Boolean] */
|
3282
|
+
rb_define_method(cBucket, "connected?", cb_bucket_connected_p, 0);
|
2153
3283
|
rb_define_method(cBucket, "async?", cb_bucket_async_p, 0);
|
2154
3284
|
|
2155
3285
|
/* Document-method: quiet
|
2156
3286
|
* Flag specifying behaviour for operations on missing keys
|
2157
3287
|
*
|
2158
3288
|
* If it is +true+, the operations will silently return +nil+ or +false+
|
2159
|
-
* instead of raising Couchbase::Error::
|
3289
|
+
* instead of raising {Couchbase::Error::NotFound}.
|
2160
3290
|
*
|
2161
3291
|
* @example Hiding cache miss (considering "miss" key is not stored)
|
2162
3292
|
* connection.quiet = true
|
@@ -2164,13 +3294,13 @@ Init_couchbase_ext(void)
|
|
2164
3294
|
*
|
2165
3295
|
* @example Raising errors on miss (considering "miss" key is not stored)
|
2166
3296
|
* connection.quiet = false
|
2167
|
-
* connection.get("miss") #=> will raise Couchbase::Error::
|
3297
|
+
* connection.get("miss") #=> will raise Couchbase::Error::NotFound
|
2168
3298
|
*
|
2169
3299
|
* @return [Boolean] */
|
2170
|
-
rb_define_attr(cBucket, "quiet", 1, 1);
|
3300
|
+
/* rb_define_attr(cBucket, "quiet", 1, 1); */
|
3301
|
+
rb_define_method(cBucket, "quiet", cb_bucket_quiet_get, 0);
|
2171
3302
|
rb_define_method(cBucket, "quiet=", cb_bucket_quiet_set, 1);
|
2172
3303
|
rb_define_alias(cBucket, "quiet?", "quiet");
|
2173
|
-
id_iv_quiet = rb_intern("@quiet");
|
2174
3304
|
|
2175
3305
|
/* Document-method: default_flags
|
2176
3306
|
* Default flags for new values.
|
@@ -2187,9 +3317,9 @@ Init_couchbase_ext(void)
|
|
2187
3317
|
* @note Amending format bit will also change #default_format value
|
2188
3318
|
*
|
2189
3319
|
* @return [Fixnum] the effective flags */
|
2190
|
-
rb_define_attr(cBucket, "default_flags", 1, 1);
|
3320
|
+
/* rb_define_attr(cBucket, "default_flags", 1, 1); */
|
3321
|
+
rb_define_method(cBucket, "default_flags", cb_bucket_default_flags_get, 0);
|
2191
3322
|
rb_define_method(cBucket, "default_flags=", cb_bucket_default_flags_set, 1);
|
2192
|
-
id_iv_default_flags = rb_intern("@default_flags");
|
2193
3323
|
|
2194
3324
|
/* Document-method: default_format
|
2195
3325
|
* Default format for new values.
|
@@ -2202,7 +3332,7 @@ Init_couchbase_ext(void)
|
|
2202
3332
|
* Here is some notes regarding how to choose the format:
|
2203
3333
|
*
|
2204
3334
|
* * <tt>:document</tt> (default) format supports most of ruby types
|
2205
|
-
* which could be mapped to JSON data (hashes, arrays,
|
3335
|
+
* which could be mapped to JSON data (hashes, arrays, strings,
|
2206
3336
|
* numbers). Future version will be able to run map/reduce queries on
|
2207
3337
|
* the values in the document form (hashes).
|
2208
3338
|
*
|
@@ -2224,9 +3354,18 @@ Init_couchbase_ext(void)
|
|
2224
3354
|
* @note Amending default_format will also change #default_flags value
|
2225
3355
|
*
|
2226
3356
|
* @return [Symbol] the effective format */
|
2227
|
-
rb_define_attr(cBucket, "default_format", 1, 1);
|
3357
|
+
/* rb_define_attr(cBucket, "default_format", 1, 1); */
|
3358
|
+
rb_define_method(cBucket, "default_format", cb_bucket_default_format_get, 0);
|
2228
3359
|
rb_define_method(cBucket, "default_format=", cb_bucket_default_format_set, 1);
|
2229
|
-
|
3360
|
+
|
3361
|
+
/* Document-method: timeout
|
3362
|
+
* @return [Fixnum] The timeout for the operations. The client will
|
3363
|
+
* raise {Couchbase::Error::Timeout} exception for all commands which
|
3364
|
+
* weren't completed in given timeslot. */
|
3365
|
+
/* rb_define_attr(cBucket, "timeout", 1, 1); */
|
3366
|
+
rb_define_method(cBucket, "timeout", cb_bucket_timeout_get, 0);
|
3367
|
+
rb_define_method(cBucket, "timeout=", cb_bucket_timeout_set, 1);
|
3368
|
+
|
2230
3369
|
/* Document-method: on_error
|
2231
3370
|
* Error callback for asynchronous mode.
|
2232
3371
|
*
|
@@ -2249,54 +3388,44 @@ Init_couchbase_ext(void)
|
|
2249
3388
|
* ...
|
2250
3389
|
*
|
2251
3390
|
* @return [Proc] the effective callback */
|
2252
|
-
rb_define_attr(cBucket, "on_error", 1, 1);
|
3391
|
+
/* rb_define_attr(cBucket, "on_error", 1, 1); */
|
2253
3392
|
rb_define_method(cBucket, "on_error", cb_bucket_on_error_get, 0);
|
2254
3393
|
rb_define_method(cBucket, "on_error=", cb_bucket_on_error_set, 1);
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2266
|
-
* @return [Fixnum] the port number of the management interface (default: 8091) */
|
2267
|
-
rb_define_attr(cBucket, "port", 1, 0);
|
2268
|
-
id_iv_port = rb_intern("@port");
|
2269
|
-
/* Document-method: authority
|
2270
|
-
* @return [String] host with port. */
|
2271
|
-
rb_define_attr(cBucket, "authority", 1, 0);
|
2272
|
-
id_iv_authority = rb_intern("@authority");
|
2273
|
-
/* Document-method: bucket
|
2274
|
-
* @return [String] the bucket name */
|
2275
|
-
rb_define_attr(cBucket, "bucket", 1, 0);
|
3394
|
+
|
3395
|
+
/* rb_define_attr(cBucket, "url", 1, 0); */
|
3396
|
+
rb_define_method(cBucket, "url", cb_bucket_url_get, 0);
|
3397
|
+
/* rb_define_attr(cBucket, "hostname", 1, 0); */
|
3398
|
+
rb_define_method(cBucket, "hostname", cb_bucket_hostname_get, 0);
|
3399
|
+
/* rb_define_attr(cBucket, "port", 1, 0); */
|
3400
|
+
rb_define_method(cBucket, "port", cb_bucket_port_get, 0);
|
3401
|
+
/* rb_define_attr(cBucket, "authority", 1, 0); */
|
3402
|
+
rb_define_method(cBucket, "authority", cb_bucket_authority_get, 0);
|
3403
|
+
/* rb_define_attr(cBucket, "bucket", 1, 0); */
|
3404
|
+
rb_define_method(cBucket, "bucket", cb_bucket_bucket_get, 0);
|
2276
3405
|
rb_define_alias(cBucket, "name", "bucket");
|
2277
|
-
|
2278
|
-
|
2279
|
-
|
2280
|
-
|
2281
|
-
|
2282
|
-
|
2283
|
-
* @return [String] the username for protected buckets (usually matches
|
2284
|
-
* the bucket name) */
|
2285
|
-
rb_define_attr(cBucket, "username", 1, 0);
|
2286
|
-
id_iv_username = rb_intern("@username");
|
2287
|
-
/* Document-method: password
|
2288
|
-
* @return [String] the password for protected buckets */
|
2289
|
-
rb_define_attr(cBucket, "password", 1, 0);
|
2290
|
-
id_iv_password = rb_intern("@password");
|
3406
|
+
/* rb_define_attr(cBucket, "pool", 1, 0); */
|
3407
|
+
rb_define_method(cBucket, "pool", cb_bucket_pool_get, 0);
|
3408
|
+
/* rb_define_attr(cBucket, "username", 1, 0); */
|
3409
|
+
rb_define_method(cBucket, "username", cb_bucket_username_get, 0);
|
3410
|
+
/* rb_define_attr(cBucket, "password", 1, 0); */
|
3411
|
+
rb_define_method(cBucket, "password", cb_bucket_password_get, 0);
|
2291
3412
|
|
2292
3413
|
/* Define symbols */
|
2293
3414
|
id_arity = rb_intern("arity");
|
2294
3415
|
id_call = rb_intern("call");
|
2295
|
-
id_load = rb_intern("load");
|
2296
3416
|
id_dump = rb_intern("dump");
|
2297
3417
|
id_flatten_bang = rb_intern("flatten!");
|
2298
3418
|
id_has_key_p = rb_intern("has_key?");
|
3419
|
+
id_host = rb_intern("host");
|
3420
|
+
id_load = rb_intern("load");
|
3421
|
+
id_match = rb_intern("match");
|
3422
|
+
id_parse = rb_intern("parse");
|
3423
|
+
id_password = rb_intern("password");
|
3424
|
+
id_path = rb_intern("path");
|
3425
|
+
id_port = rb_intern("port");
|
3426
|
+
id_scheme = rb_intern("scheme");
|
2299
3427
|
id_to_s = rb_intern("to_s");
|
3428
|
+
id_user = rb_intern("user");
|
2300
3429
|
|
2301
3430
|
sym_add = ID2SYM(rb_intern("add"));
|
2302
3431
|
sym_append = ID2SYM(rb_intern("append"));
|
@@ -2306,6 +3435,7 @@ Init_couchbase_ext(void)
|
|
2306
3435
|
sym_decrement = ID2SYM(rb_intern("decrement"));
|
2307
3436
|
sym_default_flags = ID2SYM(rb_intern("default_flags"));
|
2308
3437
|
sym_default_format = ID2SYM(rb_intern("default_format"));
|
3438
|
+
sym_default_ttl = ID2SYM(rb_intern("default_ttl"));
|
2309
3439
|
sym_delete = ID2SYM(rb_intern("delete"));
|
2310
3440
|
sym_document = ID2SYM(rb_intern("document"));
|
2311
3441
|
sym_extended = ID2SYM(rb_intern("extended"));
|
@@ -2326,7 +3456,9 @@ Init_couchbase_ext(void)
|
|
2326
3456
|
sym_replace = ID2SYM(rb_intern("replace"));
|
2327
3457
|
sym_set = ID2SYM(rb_intern("set"));
|
2328
3458
|
sym_stats = ID2SYM(rb_intern("stats"));
|
3459
|
+
sym_timeout = ID2SYM(rb_intern("timeout"));
|
2329
3460
|
sym_touch = ID2SYM(rb_intern("touch"));
|
2330
3461
|
sym_ttl = ID2SYM(rb_intern("ttl"));
|
2331
3462
|
sym_username = ID2SYM(rb_intern("username"));
|
3463
|
+
sym_version = ID2SYM(rb_intern("version"));
|
2332
3464
|
}
|