couchbase 0.9.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/.yardopts +5 -0
- data/HISTORY.markdown +14 -10
- data/README.markdown +293 -98
- data/Rakefile +19 -24
- data/couchbase.gemspec +25 -7
- data/ext/couchbase_ext/couchbase_ext.c +2332 -0
- data/ext/couchbase_ext/extconf.rb +102 -0
- data/lib/couchbase.rb +20 -30
- data/lib/couchbase/bucket.rb +43 -112
- data/lib/couchbase/version.rb +3 -2
- data/tasks/benchmark.rake +6 -0
- data/tasks/compile.rake +52 -0
- data/tasks/doc.rake +27 -0
- data/tasks/test.rake +94 -0
- data/tasks/util.rake +21 -0
- data/test/profile/.gitignore +1 -0
- data/test/profile/Gemfile +6 -0
- data/test/profile/benchmark.rb +195 -0
- data/test/setup.rb +107 -18
- data/test/test_arithmetic.rb +98 -0
- data/test/test_async.rb +211 -0
- data/test/test_bucket.rb +126 -23
- data/test/test_cas.rb +59 -0
- data/test/test_couchbase.rb +22 -3
- data/test/test_delete.rb +63 -0
- data/test/test_errors.rb +82 -0
- data/test/test_flush.rb +49 -0
- data/test/test_format.rb +98 -0
- data/test/test_get.rb +236 -0
- data/test/test_stats.rb +53 -0
- data/test/test_store.rb +186 -0
- data/test/test_touch.rb +57 -0
- data/test/test_version.rb +17 -0
- metadata +72 -58
- data/lib/couchbase/couchdb.rb +0 -107
- data/lib/couchbase/document.rb +0 -71
- data/lib/couchbase/http_status.rb +0 -118
- data/lib/couchbase/latch.rb +0 -71
- data/lib/couchbase/memcached.rb +0 -372
- data/lib/couchbase/node.rb +0 -49
- data/lib/couchbase/rest_client.rb +0 -124
- data/lib/couchbase/view.rb +0 -182
- data/test/support/buckets-config.json +0 -843
- data/test/support/sample_design_doc.json +0 -9
- data/test/test_couchdb.rb +0 -98
- data/test/test_document.rb +0 -11
- data/test/test_latch.rb +0 -88
- data/test/test_memcached.rb +0 -59
- data/test/test_rest_client.rb +0 -14
- data/test/test_view.rb +0 -98
@@ -0,0 +1,102 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# Author:: Couchbase <info@couchbase.com>
|
3
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
20
|
+
|
21
|
+
require 'mkmf'
|
22
|
+
|
23
|
+
LIBDIR = Config::CONFIG['libdir']
|
24
|
+
INCLUDEDIR = Config::CONFIG['includedir']
|
25
|
+
|
26
|
+
HEADER_DIRS = [
|
27
|
+
# First search /opt/local for macports
|
28
|
+
'/opt/local/include',
|
29
|
+
# Then search /usr/local for people that installed from source
|
30
|
+
'/usr/local/include',
|
31
|
+
# Check the ruby install locations
|
32
|
+
INCLUDEDIR,
|
33
|
+
# Finally fall back to /usr
|
34
|
+
'/usr/include'
|
35
|
+
]
|
36
|
+
|
37
|
+
LIB_DIRS = [
|
38
|
+
# First search /opt/local for macports
|
39
|
+
'/opt/local/lib',
|
40
|
+
# Then search /usr/local for people that installed from source
|
41
|
+
'/usr/local/lib',
|
42
|
+
# Check the ruby install locations
|
43
|
+
LIBDIR,
|
44
|
+
# Finally fall back to /usr
|
45
|
+
'/usr/lib'
|
46
|
+
]
|
47
|
+
|
48
|
+
# For people using homebrew
|
49
|
+
brew_prefix = `brew --prefix libevent 2> /dev/null`.chomp
|
50
|
+
unless brew_prefix.empty?
|
51
|
+
LIB_DIRS.unshift File.join(brew_prefix, 'lib')
|
52
|
+
HEADER_DIRS.unshift File.join(brew_prefix, 'include')
|
53
|
+
end
|
54
|
+
|
55
|
+
HEADER_DIRS.delete_if{|d| !File.exists?(d)}
|
56
|
+
LIB_DIRS.delete_if{|d| !File.exists?(d)}
|
57
|
+
|
58
|
+
def define(macro, value = nil)
|
59
|
+
$defs.push("-D #{[macro.upcase, value].compact.join('=')}")
|
60
|
+
end
|
61
|
+
|
62
|
+
# it will find the libcouchbase likely. you can specify its path otherwise
|
63
|
+
#
|
64
|
+
# ruby extconf.rb [--with-libcouchbase-include=<dir>] [--with-libcouchbase-lib=<dir>]
|
65
|
+
#
|
66
|
+
# or
|
67
|
+
#
|
68
|
+
# ruby extconf.rb [--with-libcouchbase-dir=<dir>]
|
69
|
+
#
|
70
|
+
dir_config("libcouchbase", HEADER_DIRS, LIB_DIRS)
|
71
|
+
|
72
|
+
if COMMON_HEADERS !~ /"ruby\.h"/
|
73
|
+
COMMON_HEADERS << %(#include "ruby.h"\n)
|
74
|
+
end
|
75
|
+
|
76
|
+
$CFLAGS << ' -std=c99 -Wall -Wextra '
|
77
|
+
if ENV['DEBUG']
|
78
|
+
$CFLAGS << ' -O0 -ggdb3 -pedantic'
|
79
|
+
end
|
80
|
+
|
81
|
+
if try_compile(<<-SRC)
|
82
|
+
#include <stdarg.h>
|
83
|
+
int foo(int x, ...) {
|
84
|
+
va_list va;
|
85
|
+
va_start(va, x);
|
86
|
+
va_arg(va, int);
|
87
|
+
va_arg(va, char *);
|
88
|
+
va_arg(va, double);
|
89
|
+
return 0;
|
90
|
+
}
|
91
|
+
int main() {
|
92
|
+
return foo(10, "", 3.14);
|
93
|
+
return 0;
|
94
|
+
}
|
95
|
+
SRC
|
96
|
+
define("HAVE_STDARG_PROTOTYPES")
|
97
|
+
end
|
98
|
+
|
99
|
+
have_library("event", "event_init", "event.h") || abort
|
100
|
+
have_library("couchbase", "libcouchbase_create", "libcouchbase/couchbase.h") || abort
|
101
|
+
create_header("couchbase_config.h")
|
102
|
+
create_makefile("couchbase_ext")
|
data/lib/couchbase.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 Couchbase, Inc.
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
3
|
# License:: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,40 +15,30 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
autoload :HttpStatus, 'couchbase/http_status'
|
23
|
-
autoload :Latch, 'couchbase/latch'
|
24
|
-
autoload :Memcached, 'couchbase/memcached'
|
25
|
-
autoload :Node, 'couchbase/node'
|
26
|
-
autoload :RestClient, 'couchbase/rest_client'
|
27
|
-
autoload :VERSION, 'couchbase/version'
|
28
|
-
autoload :View, 'couchbase/view'
|
29
|
-
|
30
|
-
# This error is raising when library detects that some operation
|
31
|
-
# doesn't implemented by the server. For example views API doesn't
|
32
|
-
# implemented by Membase 1.7.x
|
33
|
-
class NotImplemented < Exception; end
|
34
|
-
|
35
|
-
class ViewError < Exception
|
36
|
-
attr_reader :from, :reason
|
18
|
+
require 'couchbase/version'
|
19
|
+
require 'yajl/json_gem'
|
20
|
+
require 'couchbase_ext'
|
21
|
+
require 'couchbase/bucket'
|
37
22
|
|
38
|
-
|
39
|
-
|
40
|
-
@reason = reason
|
41
|
-
super("#{from}: #{reason}")
|
42
|
-
end
|
43
|
-
end
|
23
|
+
# Couchbase ruby client
|
24
|
+
module Couchbase
|
44
25
|
|
45
26
|
class << self
|
46
27
|
# The method +new+ initializes new Bucket instance with all arguments passed.
|
47
28
|
#
|
48
|
-
#
|
49
|
-
# Couchbase.new
|
50
|
-
#
|
51
|
-
#
|
29
|
+
# @example Use default values for all options
|
30
|
+
# Couchbase.new
|
31
|
+
#
|
32
|
+
# @example Establish connection with couchbase default pool and default bucket
|
33
|
+
# Couchbase.new("http://localhost:8091/pools/default")
|
34
|
+
#
|
35
|
+
# @example Select custom bucket
|
36
|
+
# Couchbase.new("http://localhost:8091/pools/default", :bucket => 'blog')
|
37
|
+
#
|
38
|
+
# @example Specify bucket credentials
|
39
|
+
# Couchbase.new("http://localhost:8091/pools/default", :bucket => 'blog', :username => 'bucket', :password => 'secret')
|
40
|
+
#
|
41
|
+
# @return [Bucket] connection instance
|
52
42
|
def new(*args)
|
53
43
|
Bucket.new(*args)
|
54
44
|
end
|
data/lib/couchbase/bucket.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 Couchbase, Inc.
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
3
|
# License:: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,125 +15,56 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
17
|
|
18
|
-
require 'uri'
|
19
|
-
|
20
18
|
module Couchbase
|
21
|
-
|
22
|
-
# This class in charge of all stuff connected to communication with
|
23
|
-
# Couchbase. It includes CouchDB and Memcached APIs. Also it includes
|
24
|
-
# methods for HTTP transport from RestClient.
|
25
|
-
|
26
19
|
class Bucket
|
27
|
-
include RestClient
|
28
|
-
include Couchdb
|
29
|
-
include Memcached
|
30
|
-
|
31
|
-
attr_accessor :pool_uri, :environment, :type, :nodes,
|
32
|
-
:streaming_uri, :name, :uri, :vbuckets
|
33
20
|
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# use when it applicable (for example it skips/preserves design
|
40
|
-
# documents with 'dev_' prefix for CouchDB API). You can specify
|
41
|
-
# any string starting from 'dev' or 'test' to activate development
|
42
|
-
# mode.
|
21
|
+
# Reads a key's value from the server and yields it to a block. Replaces
|
22
|
+
# the key's value with the result of the block as long as the key hasn't
|
23
|
+
# been updated in the meantime, otherwise raises
|
24
|
+
# Couchbase::Error::KeyExists. CAS stands for "compare and swap", and
|
25
|
+
# avoids the need for manual key mutexing. Read more info here:
|
43
26
|
#
|
44
|
-
#
|
45
|
-
# configuration update via +streaming_uri+. Server should push
|
46
|
-
# update notification about adding or removing nodes from cluster.
|
27
|
+
# http://docs.couchbase.org/memcached-api/memcached-api-protocol-text_cas.html
|
47
28
|
#
|
48
|
-
#
|
49
|
-
# pool.
|
50
|
-
def initialize(pool_uri, options = {})
|
51
|
-
@latch = Latch.new(:in_progress, :ready)
|
52
|
-
@name = options[:bucket_name] || "default"
|
53
|
-
@pool_uri = URI.parse(pool_uri)
|
54
|
-
@environment = if options[:environment].to_s =~ /^(dev|test)/
|
55
|
-
:development
|
56
|
-
else
|
57
|
-
:production
|
58
|
-
end
|
59
|
-
config = http_get("#{@pool_uri}/buckets").detect do |bucket|
|
60
|
-
bucket['name'] == @name
|
61
|
-
end
|
62
|
-
unless config
|
63
|
-
raise ArgumentError,
|
64
|
-
"There no such bucket with name '#{@name}' in pool #{pool_uri}"
|
65
|
-
end
|
66
|
-
@uri = @pool_uri.merge(config['uri'])
|
67
|
-
@streaming_uri = @pool_uri.merge(config['streamingUri'])
|
68
|
-
# credentials is used to choose the bucket
|
69
|
-
if @name != "default"
|
70
|
-
@credentials = {:username => @name, :password => options[:bucket_password] || ''}
|
71
|
-
end
|
72
|
-
super
|
73
|
-
|
74
|
-
# Considering all initialization stuff completed and now we can
|
75
|
-
# start config listener
|
76
|
-
listen_for_config_changes
|
77
|
-
|
78
|
-
@latch.wait
|
79
|
-
end
|
80
|
-
|
81
|
-
# Select next node for work with Couchbase. Currently it makes sense
|
82
|
-
# only for couchdb API, because memcached client works using moxi.
|
83
|
-
def next_node
|
84
|
-
nodes.shuffle.first
|
85
|
-
end
|
86
|
-
|
87
|
-
# Perform configuration using configuration cache. It turn all URIs
|
88
|
-
# into full form (with schema, host and port).
|
29
|
+
# @param [String] key
|
89
30
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def mk_curl(url)
|
123
|
-
Curl::Easy.new(url) do |curl|
|
124
|
-
curl.useragent = "couchbase-ruby-client/#{Couchbase::VERSION}"
|
125
|
-
if @credentials
|
126
|
-
curl.http_auth_types = :basic
|
127
|
-
curl.username = @credentials[:username]
|
128
|
-
curl.password = @credentials[:password]
|
129
|
-
end
|
130
|
-
curl.verbose = true if Kernel.respond_to?(:debugger)
|
131
|
-
curl.on_body do |data|
|
132
|
-
config = Yajl::Parser.parse(data)
|
133
|
-
setup(config) if config
|
134
|
-
data.bytesize
|
31
|
+
# @param [Hash] options the options for operation
|
32
|
+
# @option options [String] :ttl (self.default_ttl) the time to live of this key
|
33
|
+
# @option options [Symbol] :format (self.default_format) format of the value
|
34
|
+
# @option options [Fixnum] :flags (self.default_flags) flags for this key
|
35
|
+
#
|
36
|
+
# @yieldparam [Object, Result] value old value in synchronous mode and
|
37
|
+
# +Result+ object in asynchronous mode.
|
38
|
+
# @yieldreturn [Object] new value.
|
39
|
+
#
|
40
|
+
# @raise [Couchbase::Errors:KeyExists] if the key was updated before the the
|
41
|
+
# code in block has been completed (the CAS value has been changed).
|
42
|
+
#
|
43
|
+
# @example Implement append to JSON encoded value
|
44
|
+
#
|
45
|
+
# c.default_format = :document
|
46
|
+
# c.set("foo", {"bar" => 1})
|
47
|
+
# c.cas("foo") do |val|
|
48
|
+
# val["baz"] = 2
|
49
|
+
# val
|
50
|
+
# end
|
51
|
+
# c.get("foo") #=> {"bar" => 1, "baz" => 2}
|
52
|
+
#
|
53
|
+
# @return [Fixnum] the CAS of new value
|
54
|
+
def cas(key, options = {})
|
55
|
+
options = options.merge(:extended => true)
|
56
|
+
if async?
|
57
|
+
get(key, options) do |ret|
|
58
|
+
val = yield(ret) # get new value from caller
|
59
|
+
set(ret.key, val, :cas => ret.cas)
|
135
60
|
end
|
61
|
+
else
|
62
|
+
val, flags, ver = get(key, options)
|
63
|
+
val = yield(val) # get new value from caller
|
64
|
+
set(key, val, :cas => ver)
|
136
65
|
end
|
137
66
|
end
|
67
|
+
alias :compare_and_swap :cas
|
68
|
+
|
138
69
|
end
|
139
70
|
end
|
data/lib/couchbase/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 Couchbase, Inc.
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
3
|
# License:: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,6 +15,7 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
17
|
|
18
|
+
# Couchbase ruby client
|
18
19
|
module Couchbase
|
19
|
-
VERSION = "0.
|
20
|
+
VERSION = "1.0.0"
|
20
21
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
desc 'Run benchmarks and compare them to memcached and dalli gems'
|
2
|
+
task :benchmark => [:clean, :compile] do
|
3
|
+
cd File.expand_path(File.join(__FILE__, '..', '..', 'test', 'profile')) do
|
4
|
+
sh "bundle install && bundle exec ruby benchmark.rb | tee benchmark-#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}.log"
|
5
|
+
end
|
6
|
+
end
|
data/tasks/compile.rake
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
gem 'rake-compiler', '>= 0.7.5'
|
19
|
+
require "rake/extensiontask"
|
20
|
+
|
21
|
+
def gemspec
|
22
|
+
@clean_gemspec ||= eval(File.read(File.expand_path('../../couchbase.gemspec', __FILE__)))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Setup compile tasks. Configuration can be passed via ENV.
|
26
|
+
# Example:
|
27
|
+
# rake compile with_libcouchbase_include=/opt/couchbase/include
|
28
|
+
# with_libcouchbase_lib=/opt/couchbase/lib
|
29
|
+
#
|
30
|
+
# or
|
31
|
+
#
|
32
|
+
# rake compile with_libcouchbase_dir=/opt/couchbase
|
33
|
+
#
|
34
|
+
Rake::ExtensionTask.new("couchbase_ext", gemspec) do |ext|
|
35
|
+
CLEAN.include "#{ext.lib_dir}/*.#{RbConfig::CONFIG['DLEXT']}"
|
36
|
+
|
37
|
+
ENV.each do |key, val|
|
38
|
+
next unless key =~ /\Awith_(\w+)\z/i
|
39
|
+
opt = $1.downcase.tr('_', '-')
|
40
|
+
if File.directory?(path = File.expand_path(val))
|
41
|
+
ext.config_options << "--with-#{opt}=#{path}"
|
42
|
+
else
|
43
|
+
warn "No such directory: #{opt}: #{path}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'rubygems/package_task'
|
49
|
+
Gem::PackageTask.new(gemspec) do |pkg|
|
50
|
+
pkg.need_zip = true
|
51
|
+
pkg.need_tar = true
|
52
|
+
end
|
data/tasks/doc.rake
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'rake/clean'
|
19
|
+
require 'yard'
|
20
|
+
require 'yard/rake/yardoc_task'
|
21
|
+
|
22
|
+
YARD::Rake::YardocTask.new do |t|
|
23
|
+
t.options = %w(--protected --no-private)
|
24
|
+
t.files.push('-', 'README.markdown', 'HISTORY.markdown')
|
25
|
+
end
|
26
|
+
|
27
|
+
#CLOBBER << 'test/CouchbaseMock.jar'
|