couchbase 0.9.8 → 1.0.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.
Files changed (51) hide show
  1. data/.gitignore +8 -0
  2. data/.yardopts +5 -0
  3. data/HISTORY.markdown +14 -10
  4. data/README.markdown +293 -98
  5. data/Rakefile +19 -24
  6. data/couchbase.gemspec +25 -7
  7. data/ext/couchbase_ext/couchbase_ext.c +2332 -0
  8. data/ext/couchbase_ext/extconf.rb +102 -0
  9. data/lib/couchbase.rb +20 -30
  10. data/lib/couchbase/bucket.rb +43 -112
  11. data/lib/couchbase/version.rb +3 -2
  12. data/tasks/benchmark.rake +6 -0
  13. data/tasks/compile.rake +52 -0
  14. data/tasks/doc.rake +27 -0
  15. data/tasks/test.rake +94 -0
  16. data/tasks/util.rake +21 -0
  17. data/test/profile/.gitignore +1 -0
  18. data/test/profile/Gemfile +6 -0
  19. data/test/profile/benchmark.rb +195 -0
  20. data/test/setup.rb +107 -18
  21. data/test/test_arithmetic.rb +98 -0
  22. data/test/test_async.rb +211 -0
  23. data/test/test_bucket.rb +126 -23
  24. data/test/test_cas.rb +59 -0
  25. data/test/test_couchbase.rb +22 -3
  26. data/test/test_delete.rb +63 -0
  27. data/test/test_errors.rb +82 -0
  28. data/test/test_flush.rb +49 -0
  29. data/test/test_format.rb +98 -0
  30. data/test/test_get.rb +236 -0
  31. data/test/test_stats.rb +53 -0
  32. data/test/test_store.rb +186 -0
  33. data/test/test_touch.rb +57 -0
  34. data/test/test_version.rb +17 -0
  35. metadata +72 -58
  36. data/lib/couchbase/couchdb.rb +0 -107
  37. data/lib/couchbase/document.rb +0 -71
  38. data/lib/couchbase/http_status.rb +0 -118
  39. data/lib/couchbase/latch.rb +0 -71
  40. data/lib/couchbase/memcached.rb +0 -372
  41. data/lib/couchbase/node.rb +0 -49
  42. data/lib/couchbase/rest_client.rb +0 -124
  43. data/lib/couchbase/view.rb +0 -182
  44. data/test/support/buckets-config.json +0 -843
  45. data/test/support/sample_design_doc.json +0 -9
  46. data/test/test_couchdb.rb +0 -98
  47. data/test/test_document.rb +0 -11
  48. data/test/test_latch.rb +0 -88
  49. data/test/test_memcached.rb +0 -59
  50. data/test/test_rest_client.rb +0 -14
  51. 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")
@@ -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
- module Couchbase
19
- autoload :Bucket, 'couchbase/bucket'
20
- autoload :Couchdb, 'couchbase/couchdb'
21
- autoload :Document, 'couchbase/document'
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
- def initialize(from, reason)
39
- @from = from
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
- # === Examples
49
- # Couchbase.new("http://localhost:8091/pools/default") #=> establish connection with couchbase default pool and default bucket
50
- # Couchbase.new("http://localhost:8091/pools/default", :bucket_name => 'blog') #=> select custom bucket
51
- # Couchbase.new("http://localhost:8091/pools/default", :bucket_name => 'blog', :bucket_password => 'secret') #=> specify password for bucket (and SASL auth for memcached client)
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
@@ -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
- # Initializes connection using +pool_uri+ and optional
35
- # +:bucket_name+ and +:bucket_password+ (for protected buckets). Bucket
36
- # name will be used as a username for all authorizations (SASL for
37
- # Memcached API and Basic for HTTP). It also accepts +:environment+
38
- # parameter wich intended to let library know what mode it should
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
- # Also starts thread which will simultanuously listen for
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
- # Raises ArgumentError when it cannot find specified bucket in given
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
- # You can override this method in included modules or descendants if
91
- # you'd like to reconfigure them when new configuration arrives from
92
- # server.
93
- def setup(config)
94
- @type = config['bucketType']
95
- @nodes = config['nodes'].map do |node|
96
- Node.new(node['status'],
97
- node['hostname'].split(':').first,
98
- node['ports'],
99
- node['couchApiBase'])
100
- end
101
- if @type == 'membase'
102
- @vbuckets = config['vBucketServerMap']['vBucketMap']
103
- end
104
- super
105
- @latch.toggle
106
- end
107
-
108
- private
109
-
110
- # Run background thread to listen for configuration changes.
111
- # Rewrites configuration for each update. Curl::Multi uses select()
112
- # call when waiting for data, so is should be efficient use ruby
113
- # threads here.
114
- def listen_for_config_changes
115
- Thread.new do
116
- multi = Curl::Multi.new
117
- multi.add(mk_curl(@streaming_uri.to_s))
118
- multi.perform
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
@@ -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.9.8"
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
@@ -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
@@ -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'