couchbase 0.9.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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'