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.
- 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
data/lib/couchbase/couchdb.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 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
|
-
module Couchbase
|
19
|
-
module Couchdb
|
20
|
-
|
21
|
-
# Initializes CouchDB related part of connection.
|
22
|
-
#
|
23
|
-
# @param [ String ] pool_uri Couchbase pool URI.
|
24
|
-
#
|
25
|
-
# @param [ Hash ] options Connection options. This is the hash the client
|
26
|
-
# passed to Couchbase.new to start the session
|
27
|
-
def initialize(pool_uri, options = {})
|
28
|
-
super
|
29
|
-
end
|
30
|
-
|
31
|
-
# Fetch design docs stored in current bucket
|
32
|
-
#
|
33
|
-
# @return [ Hash ]
|
34
|
-
def design_docs
|
35
|
-
docs = http_get("#{next_node.couch_api_base}/_all_docs",
|
36
|
-
:params => {:startkey => "_design/", :endkey => "_design0", :include_docs => true})
|
37
|
-
result = {}
|
38
|
-
docs['rows'].each do |doc|
|
39
|
-
doc = Document.wrap(self, doc)
|
40
|
-
key = doc['_id'].sub(/^_design\//, '')
|
41
|
-
next if @environment == :production && key =~ /dev_/
|
42
|
-
result[key] = doc
|
43
|
-
end
|
44
|
-
result
|
45
|
-
end
|
46
|
-
|
47
|
-
# Update or create design doc with supplied views
|
48
|
-
#
|
49
|
-
# @param [ Hash, IO, String ] data The source object containing JSON
|
50
|
-
# encoded design document. It must have
|
51
|
-
# <tt>_id</tt> key set, this key should
|
52
|
-
# start with <tt>_design/</tt>.
|
53
|
-
#
|
54
|
-
# @return [ Couchbase::Document ] instance
|
55
|
-
def save_design_doc(data)
|
56
|
-
doc = parse_design_document(data)
|
57
|
-
rv = http_put("#{next_node.couch_api_base}/#{doc['_id']}", {}, doc)
|
58
|
-
doc['_rev'] = rv['rev']
|
59
|
-
doc
|
60
|
-
end
|
61
|
-
|
62
|
-
# Fetch all documents from the bucket.
|
63
|
-
#
|
64
|
-
# @param [ Hash ] params Params for CouchDB <tt>/_all_docs</tt> query
|
65
|
-
#
|
66
|
-
# @return [ Couchbase::View ] View object
|
67
|
-
def all_docs(params = {})
|
68
|
-
View.new(self, "#{next_node.couch_api_base}/_all_docs", params)
|
69
|
-
end
|
70
|
-
|
71
|
-
# Delete design doc with given id and revision.
|
72
|
-
#
|
73
|
-
# @param [ String ] id Design document id. It might have '_design/'
|
74
|
-
# prefix.
|
75
|
-
#
|
76
|
-
# @param [ String ] rev Document revision. It uses latest revision if
|
77
|
-
# <tt>rev</tt> parameter is nil.
|
78
|
-
#
|
79
|
-
def delete_design_doc(id, rev = nil)
|
80
|
-
ddoc = design_docs[id.sub(/^_design\//, '')]
|
81
|
-
return nil unless ddoc
|
82
|
-
http_delete("#{next_node.couch_api_base}/#{ddoc['_id']}",
|
83
|
-
:params => {:rev => rev || ddoc['_rev']})
|
84
|
-
end
|
85
|
-
|
86
|
-
protected
|
87
|
-
|
88
|
-
def parse_design_document(doc)
|
89
|
-
data = case doc
|
90
|
-
when String
|
91
|
-
Yajl::Parser.parse(doc)
|
92
|
-
when IO
|
93
|
-
Yajl::Parser.parse(doc.read)
|
94
|
-
when Hash
|
95
|
-
doc
|
96
|
-
else
|
97
|
-
raise ArgumentError, "Document should be Hash, String or IO instance"
|
98
|
-
end
|
99
|
-
|
100
|
-
if data['_id'].to_s !~ /^_design\//
|
101
|
-
raise ArgumentError, "'_id' key must be set and start with '_design/'."
|
102
|
-
end
|
103
|
-
data['language'] ||= 'javascript'
|
104
|
-
Document.wrap(self, data)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
data/lib/couchbase/document.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 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
|
-
module Couchbase
|
19
|
-
class Document
|
20
|
-
# Undefine as much methods as we can to free names for views
|
21
|
-
instance_methods.each do |m|
|
22
|
-
undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$|^class$|)/
|
23
|
-
end
|
24
|
-
|
25
|
-
attr_accessor :data, :views
|
26
|
-
|
27
|
-
def initialize(connection, data)
|
28
|
-
@data = data
|
29
|
-
@connection = connection
|
30
|
-
@views = []
|
31
|
-
begin
|
32
|
-
if design_doc?
|
33
|
-
data['views'].each do |name, funs|
|
34
|
-
@views << name
|
35
|
-
self.instance_eval <<-EOV, __FILE__, __LINE__ + 1
|
36
|
-
def #{name}(params = {})
|
37
|
-
endpoint = "\#{@connection.next_node.couch_api_base}/\#{@data['_id']}/_view/#{name}"
|
38
|
-
View.new(@connection, endpoint, params)
|
39
|
-
end
|
40
|
-
EOV
|
41
|
-
end
|
42
|
-
end
|
43
|
-
rescue NoMethodError
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.wrap(connection, data)
|
48
|
-
Document.new(connection, data['doc'] || data)
|
49
|
-
end
|
50
|
-
|
51
|
-
def [](key)
|
52
|
-
@data[key]
|
53
|
-
end
|
54
|
-
|
55
|
-
def []=(key, value)
|
56
|
-
@data[key] = value
|
57
|
-
end
|
58
|
-
|
59
|
-
def design_doc?
|
60
|
-
!!(@data['_id'] =~ %r(_design/))
|
61
|
-
end
|
62
|
-
|
63
|
-
def has_views?
|
64
|
-
!!(design_doc? && !@views.empty?)
|
65
|
-
end
|
66
|
-
|
67
|
-
def inspect
|
68
|
-
%(#<#{self.class.name}:#{self.object_id} #{@data.inspect}>)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 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
|
-
module Couchbase
|
19
|
-
|
20
|
-
# This module contains definitions for exceptions which wrap HTTP
|
21
|
-
# errors. Also it collect them in useful structure
|
22
|
-
# +HttpStatus::Errors+ which is map with status code as a key and
|
23
|
-
# exception class as a value. See the usage example below.
|
24
|
-
#
|
25
|
-
# === Example
|
26
|
-
#
|
27
|
-
# data = Yajl::Parser.parse(curl.body_str)
|
28
|
-
# if error_class = HttpStatus::Errors[curl.response_code]
|
29
|
-
# raise error_class.new(data['error'], data['reason'])
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
|
33
|
-
module HttpStatus
|
34
|
-
|
35
|
-
# Base class for all HTTP error codes. It povides handy methods for
|
36
|
-
# investigating what happened. For example, it stores +status_code+
|
37
|
-
# and human readable +status_message+ automatically and allows to
|
38
|
-
# provide context specific code and message (+error+ and +reason+
|
39
|
-
# correspondingly)
|
40
|
-
|
41
|
-
class Status < Exception
|
42
|
-
class << self
|
43
|
-
attr_accessor :status_code, :status_message
|
44
|
-
alias_method :to_i, :status_code
|
45
|
-
|
46
|
-
def status_line
|
47
|
-
"#{status_code} #{status_message}"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
attr_reader :error, :reason
|
52
|
-
def initialize(error, reason)
|
53
|
-
@error = error
|
54
|
-
@reason = reason
|
55
|
-
super("#{error}: #{reason}")
|
56
|
-
end
|
57
|
-
|
58
|
-
def status_code
|
59
|
-
self.class.status_code
|
60
|
-
end
|
61
|
-
|
62
|
-
def status_message
|
63
|
-
self.class.status_message
|
64
|
-
end
|
65
|
-
|
66
|
-
def status_line
|
67
|
-
self.class.status_line
|
68
|
-
end
|
69
|
-
|
70
|
-
def to_i
|
71
|
-
self.class.to_i
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
StatusMessage = {
|
76
|
-
400 => 'Bad Request',
|
77
|
-
401 => 'Unauthorized',
|
78
|
-
402 => 'Payment Required',
|
79
|
-
403 => 'Forbidden',
|
80
|
-
404 => 'Not Found',
|
81
|
-
405 => 'Method Not Allowed',
|
82
|
-
406 => 'Not Acceptable',
|
83
|
-
407 => 'Proxy Authentication Required',
|
84
|
-
408 => 'Request Timeout',
|
85
|
-
409 => 'Conflict',
|
86
|
-
410 => 'Gone',
|
87
|
-
411 => 'Length Required',
|
88
|
-
412 => 'Precondition Failed',
|
89
|
-
413 => 'Request Entity Too Large',
|
90
|
-
414 => 'Request-URI Too Large',
|
91
|
-
415 => 'Unsupported Media Type',
|
92
|
-
416 => 'Request Range Not Satisfiable',
|
93
|
-
417 => 'Expectation Failed',
|
94
|
-
422 => 'Unprocessable Entity',
|
95
|
-
423 => 'Locked',
|
96
|
-
424 => 'Failed Dependency',
|
97
|
-
500 => 'Internal Server Error',
|
98
|
-
501 => 'Not Implemented',
|
99
|
-
502 => 'Bad Gateway',
|
100
|
-
503 => 'Service Unavailable',
|
101
|
-
504 => 'Gateway Timeout',
|
102
|
-
505 => 'HTTP Version Not Supported',
|
103
|
-
507 => 'Insufficient Storage'
|
104
|
-
}
|
105
|
-
|
106
|
-
# Hash with error code as a key and exceptin class as a value
|
107
|
-
Errors = {}
|
108
|
-
|
109
|
-
StatusMessage.each do |status_code, status_message|
|
110
|
-
klass = Class.new(Status)
|
111
|
-
klass.status_code = status_code
|
112
|
-
klass.status_message = status_message
|
113
|
-
klass_name = status_message.gsub(/[ \-]/,'')
|
114
|
-
const_set(klass_name, klass)
|
115
|
-
HttpStatus::Errors[status_code] = const_get(klass_name)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
data/lib/couchbase/latch.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 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 'thread'
|
19
|
-
|
20
|
-
module Couchbase
|
21
|
-
class Latch
|
22
|
-
attr_reader :state, :target
|
23
|
-
|
24
|
-
# Takes initial pair of possible states and set latch in the first.
|
25
|
-
#
|
26
|
-
# @example Read an attribute.
|
27
|
-
# Latch.new(false, true)
|
28
|
-
# Latch.new(:closed, :opened)
|
29
|
-
#
|
30
|
-
# @param [ Object ] from Initial state
|
31
|
-
#
|
32
|
-
# @param [ Object ] to Target state
|
33
|
-
def initialize(from, to)
|
34
|
-
@state = from
|
35
|
-
@target = to
|
36
|
-
@lock = Mutex.new
|
37
|
-
@condition = ConditionVariable.new
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
|
-
# Turn latch to target state.
|
42
|
-
#
|
43
|
-
# @example
|
44
|
-
# l = Latch.new(:opened, :closed)
|
45
|
-
# l.state #=> :opened
|
46
|
-
# l.toggle #=> :closed
|
47
|
-
#
|
48
|
-
# @return [ Object ] Target state
|
49
|
-
def toggle
|
50
|
-
@lock.synchronize do
|
51
|
-
@state = @target
|
52
|
-
@condition.broadcast
|
53
|
-
end
|
54
|
-
@state
|
55
|
-
end
|
56
|
-
|
57
|
-
# Perform blocking wait operation until state will be toggled.
|
58
|
-
#
|
59
|
-
# @example
|
60
|
-
# l = Latch.new(:opened, :closed)
|
61
|
-
# l.wait #=> :closed
|
62
|
-
#
|
63
|
-
# @return [ Object ] Target state
|
64
|
-
def wait
|
65
|
-
@lock.synchronize do
|
66
|
-
@condition.wait(@lock) while @state != @target
|
67
|
-
end
|
68
|
-
@state
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
data/lib/couchbase/memcached.rb
DELETED
@@ -1,372 +0,0 @@
|
|
1
|
-
# Author:: Couchbase <info@couchbase.com>
|
2
|
-
# Copyright:: 2011 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 'memcached'
|
19
|
-
|
20
|
-
module Couchbase
|
21
|
-
|
22
|
-
# This module is included in the Couchbase::Connection and provides
|
23
|
-
# routines for Memcached API
|
24
|
-
|
25
|
-
module Memcached
|
26
|
-
|
27
|
-
attr_reader :memcached, :default_format, :default_flags, :default_ttl
|
28
|
-
|
29
|
-
# Initializes Memcached API. It builds a server list using moxi ports.
|
30
|
-
#
|
31
|
-
# @param [Hash] options The options for module
|
32
|
-
# @option options [Symbol] :format format of the values. It can be on of
|
33
|
-
# the <tt>[:plain, :document, :marshal]</tt>. You can choose
|
34
|
-
# <tt>:plain</tt> if you no need any conversions to be applied to your
|
35
|
-
# data, but your data should be passed as String. Choose
|
36
|
-
# <tt>:document</tt> (default) format when you'll store hashes and plan
|
37
|
-
# to execute CouchDB views with map/reduce operations on them. And
|
38
|
-
# finally use <tt>:marshal</tt> format if you'd like to transparently
|
39
|
-
# serialize almost any ruby object with standard <tt>Marshal.dump</tt>
|
40
|
-
# and <tt>Marhal.load</tt> methods.
|
41
|
-
def initialize(pool_uri, options = {})
|
42
|
-
@default_format = options[:format] || :document
|
43
|
-
@default_flags = ::Memcached::FLAGS
|
44
|
-
@options = {
|
45
|
-
:binary_protocol => true,
|
46
|
-
:support_cas => true,
|
47
|
-
:default_ttl => 0
|
48
|
-
}.merge(options || {})
|
49
|
-
@options[:experimental_features] = true
|
50
|
-
if @credentials
|
51
|
-
@options[:credentials] = [@credentials[:username], @credentials[:password]]
|
52
|
-
end
|
53
|
-
super
|
54
|
-
end
|
55
|
-
|
56
|
-
# Returns effective options from Memcached instance
|
57
|
-
#
|
58
|
-
# @return [Hash]
|
59
|
-
def options
|
60
|
-
@memcached.options
|
61
|
-
end
|
62
|
-
|
63
|
-
# Return the array of server strings used to configure this instance.
|
64
|
-
#
|
65
|
-
# @return [Array]
|
66
|
-
def servers
|
67
|
-
@memcached.servers
|
68
|
-
end
|
69
|
-
|
70
|
-
# Set the prefix key.
|
71
|
-
#
|
72
|
-
# @param [String] prefix the string to prepend before each key.
|
73
|
-
def prefix_key=(prefix)
|
74
|
-
@memcached.prefix_key(prefix)
|
75
|
-
end
|
76
|
-
alias :namespace= :prefix_key=
|
77
|
-
|
78
|
-
# Return the current prefix key.
|
79
|
-
#
|
80
|
-
# @return [String]
|
81
|
-
def prefix_key
|
82
|
-
@memcached.prefix_key
|
83
|
-
end
|
84
|
-
alias :namespace :prefix_key
|
85
|
-
|
86
|
-
# Return a hash of statistics responses from the set of servers. Each
|
87
|
-
# value is an array with one entry for each server, in the same order the
|
88
|
-
# servers were defined.
|
89
|
-
#
|
90
|
-
# @param [String] key The name of the statistical item. When key is nil,
|
91
|
-
# the server will return all set of statistics information.
|
92
|
-
#
|
93
|
-
# @return [Hash]
|
94
|
-
def stats(key = nil)
|
95
|
-
@memcached.stats(key)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Flushes all key/value pairs from all the servers.
|
99
|
-
def flush
|
100
|
-
@memcached.flush
|
101
|
-
end
|
102
|
-
|
103
|
-
# Gets a key's value from the server. It will use <tt>multiget</tt>
|
104
|
-
# behaviour if you pass Array of keys, which is much faster than normal
|
105
|
-
# mode.
|
106
|
-
#
|
107
|
-
# @overload get(key, options = {})
|
108
|
-
# Get single key.
|
109
|
-
#
|
110
|
-
# @param [String] key
|
111
|
-
# @param [Hash] options the options for operation
|
112
|
-
# @option options [Symbol] :format (self.default_format) format of the value
|
113
|
-
# @return [Object] the value is associated with the key
|
114
|
-
# @raise [Memcached::NotFound] if the key does not exist on the server.
|
115
|
-
#
|
116
|
-
# @overload get(*keys, options = {})
|
117
|
-
# Get multiple keys aka multiget (mget).
|
118
|
-
#
|
119
|
-
# @param [Array] keys the list of keys
|
120
|
-
# @param [Hash] options the options for operation
|
121
|
-
# @option options [Symbol] :format (self.default_format) format of the value
|
122
|
-
# @return [Hash] result map, where keys are keys which were requested
|
123
|
-
# and values are the values from the storage. Result will contain only
|
124
|
-
# existing keys and in this case no exception will be raised.
|
125
|
-
def get(*args)
|
126
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
127
|
-
raise ArgumentError, "You must provide at least one key" if args.empty?
|
128
|
-
keys = args.length == 1 ? args.pop : args
|
129
|
-
format = options[:format] || @default_format
|
130
|
-
rv = @memcached.get(keys, format == :marshal)
|
131
|
-
if keys.is_a?(Array)
|
132
|
-
rv.keys.each do |key|
|
133
|
-
rv[key] = decode(rv[key], format)
|
134
|
-
end
|
135
|
-
rv
|
136
|
-
else
|
137
|
-
decode(rv, format)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Shortcut to <tt>#get</tt> operation. Gets a key's value from the server
|
142
|
-
# with default options. (@see #get method for additional info)
|
143
|
-
#
|
144
|
-
# @param [Array, String] keys list of String keys or single key
|
145
|
-
#
|
146
|
-
# @raise [Memcached::NotFound] if the key does not exist on the server.
|
147
|
-
def [](keys)
|
148
|
-
decode(@memcached.get(keys, @default_format == :marshal), @default_format)
|
149
|
-
end
|
150
|
-
|
151
|
-
# Set new expiration time for existing item. The <tt>ttl</tt> parameter
|
152
|
-
# will use <tt>#default_ttl</tt> value if it is nil.
|
153
|
-
#
|
154
|
-
# @param [String] key
|
155
|
-
#
|
156
|
-
# @param [Fixnum] ttl
|
157
|
-
def touch(key, ttl = @default_ttl)
|
158
|
-
@memcached.touch(key, ttl)
|
159
|
-
end
|
160
|
-
|
161
|
-
# Set a key/value pair. Overwrites any existing value on the server.
|
162
|
-
#
|
163
|
-
# @param [String] key
|
164
|
-
#
|
165
|
-
# @param [Object] value
|
166
|
-
#
|
167
|
-
# @param [Hash] options the options for operation
|
168
|
-
# @option options [String] :ttl (self.default_ttl) the time to live of this key
|
169
|
-
# @option options [Symbol] :format (self.default_format) format of the value
|
170
|
-
# @option options [Fixnum] :flags (self.default_flags) flags for this key
|
171
|
-
def set(key, value, options = {})
|
172
|
-
ttl = options[:ttl] || @default_ttl
|
173
|
-
format = options[:format] || @default_format
|
174
|
-
flags = options[:flags] || @default_flags
|
175
|
-
@memcached.set(key, encode(value, format), ttl, format == :marshal, flags)
|
176
|
-
end
|
177
|
-
|
178
|
-
# Shortcut to <tt>#set</tt> operation. Sets key to given value using
|
179
|
-
# default options.
|
180
|
-
#
|
181
|
-
# @param [String] key
|
182
|
-
#
|
183
|
-
# @param [Object] value
|
184
|
-
def []=(key, value)
|
185
|
-
@memcached.set(key, encode(value, @default_format),
|
186
|
-
@default_ttl, @default_format == :marshal, @default_flags)
|
187
|
-
end
|
188
|
-
|
189
|
-
# Reads a key's value from the server and yields it to a block. Replaces
|
190
|
-
# the key's value with the result of the block as long as the key hasn't
|
191
|
-
# been updated in the meantime, otherwise raises
|
192
|
-
# <b>Memcached::NotStored</b>. CAS stands for "compare and swap", and
|
193
|
-
# avoids the need for manual key mutexing. Read more info here:
|
194
|
-
#
|
195
|
-
# http://docs.couchbase.org/memcached-api/memcached-api-protocol-text.html#memcached-api-protocol-text_cas
|
196
|
-
#
|
197
|
-
# @param [String] key
|
198
|
-
#
|
199
|
-
# @param [Hash] options the options for operation
|
200
|
-
# @option options [String] :ttl (self.default_ttl) the time to live of this key
|
201
|
-
# @option options [Symbol] :format (self.default_format) format of the value
|
202
|
-
# @option options [Fixnum] :flags (self.default_flags) flags for this key
|
203
|
-
#
|
204
|
-
# @yieldparam [Object] value old value.
|
205
|
-
# @yieldreturn [Object] new value.
|
206
|
-
#
|
207
|
-
# @raise [Memcached::ClientError] if CAS doesn't enabled for current
|
208
|
-
# connection. (:support_cas is true by default)
|
209
|
-
# @raise [Memcached::NotStored] if the key was updated before the the
|
210
|
-
# code in block has been completed (the CAS value has been changed).
|
211
|
-
#
|
212
|
-
# @example Implement append to JSON encoded value
|
213
|
-
#
|
214
|
-
# c.default_format #=> :json
|
215
|
-
# c.set("foo", {"bar" => 1})
|
216
|
-
# c.cas("foo") do |val|
|
217
|
-
# val["baz"] = 2
|
218
|
-
# val
|
219
|
-
# end
|
220
|
-
# c.get("foo") #=> {"bar" => 1, "baz" => 2}
|
221
|
-
#
|
222
|
-
def cas(key, options = {}, &block)
|
223
|
-
ttl = options[:ttl] || @default_ttl
|
224
|
-
format = options[:format] || @default_format
|
225
|
-
flags = options[:flags] || @default_flags
|
226
|
-
@memcached.cas(key, ttl, format == :marshal, flags) do |value|
|
227
|
-
value = decode(value, format)
|
228
|
-
encode(block.call(value), format)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
alias :compare_and_swap :cas
|
232
|
-
|
233
|
-
# Add a key/value pair.
|
234
|
-
#
|
235
|
-
# @param [String] key
|
236
|
-
#
|
237
|
-
# @param [Object] value
|
238
|
-
#
|
239
|
-
# @param [Hash] options the options for operation
|
240
|
-
# @option options [String] :ttl (self.default_ttl) the time to live of this key
|
241
|
-
# @option options [Symbol] :format (self.default_format) format of the value
|
242
|
-
# @option options [Fixnum] :flags (self.default_flags) flags for this key
|
243
|
-
#
|
244
|
-
# @raise [Memcached::NotStored] if the key already exists on the server.
|
245
|
-
def add(key, value, options = {})
|
246
|
-
ttl = options[:ttl] || @default_ttl
|
247
|
-
format = options[:format] || @default_format
|
248
|
-
flags = options[:flags] || @default_flags
|
249
|
-
@memcached.add(key, encode(value, format), ttl, format == :marshal, flags)
|
250
|
-
end
|
251
|
-
|
252
|
-
|
253
|
-
# Replace a key/value pair.
|
254
|
-
#
|
255
|
-
# @param [String] key
|
256
|
-
#
|
257
|
-
# @param [Object] value
|
258
|
-
#
|
259
|
-
# @param [Hash] options the options for operation
|
260
|
-
# @option options [String] :ttl (self.default_ttl) the time to live of this key
|
261
|
-
# @option options [Symbol] :format (self.default_format) format of the value
|
262
|
-
# @option options [Fixnum] :flags (self.default_flags) flags for this key
|
263
|
-
#
|
264
|
-
# @raise [Memcached::NotFound] if the key doesn't exists on the server.
|
265
|
-
def replace(key, value, options = {})
|
266
|
-
ttl = options[:ttl] || @default_ttl
|
267
|
-
format = options[:format] || @default_format
|
268
|
-
flags = options[:flags] || @default_flags
|
269
|
-
@memcached.replace(key, encode(value, format), ttl, format == :marshal, flags)
|
270
|
-
end
|
271
|
-
|
272
|
-
# Appends a string to a key's value. Make sense for <tt>:plain</tt>
|
273
|
-
# format, because server doesn't make assumptions about value structure
|
274
|
-
# here.
|
275
|
-
#
|
276
|
-
# @param [String] key
|
277
|
-
#
|
278
|
-
# @param [Object] value
|
279
|
-
#
|
280
|
-
# @raise [Memcached::NotFound] if the key doesn't exists on the server.
|
281
|
-
def append(key, value)
|
282
|
-
@memcached.append(key, value)
|
283
|
-
end
|
284
|
-
|
285
|
-
|
286
|
-
# Prepends a string to a key's value. Make sense for <tt>:plain</tt>
|
287
|
-
# format, because server doesn't make assumptions about value structure
|
288
|
-
# here.
|
289
|
-
#
|
290
|
-
# @param [String] key
|
291
|
-
#
|
292
|
-
# @param [Object] value
|
293
|
-
#
|
294
|
-
# @raise [Memcached::NotFound] if the key doesn't exists on the server.
|
295
|
-
def prepend(key, value)
|
296
|
-
@memcached.prepend(key, value)
|
297
|
-
end
|
298
|
-
|
299
|
-
# Deletes a key/value pair from the server.
|
300
|
-
#
|
301
|
-
# @param [String] key
|
302
|
-
#
|
303
|
-
# @raise [Memcached::NotFound] if the key doesn't exists on the server.
|
304
|
-
def delete(key)
|
305
|
-
@memcached.delete(key)
|
306
|
-
end
|
307
|
-
|
308
|
-
# Increment a key's value. The key must be initialized to a plain integer
|
309
|
-
# first via <tt>#set</tt>, <tt>#add</tt>, or <tt>#replace</tt> with
|
310
|
-
# <tt>:format</tt> set to <tt>:plain</tt>.
|
311
|
-
#
|
312
|
-
# @param [String] key
|
313
|
-
#
|
314
|
-
# @param [Fixnum] offset the value to add
|
315
|
-
def increment(key, offset = 1)
|
316
|
-
@memcached.increment(key, offset)
|
317
|
-
end
|
318
|
-
alias :incr :increment
|
319
|
-
|
320
|
-
# Decrement a key's value. The key must be initialized to a plain integer
|
321
|
-
# first via <tt>#set</tt>, <tt>#add</tt>, or <tt>#replace</tt> with
|
322
|
-
# <tt>:format</tt> set to <tt>:plain</tt>.
|
323
|
-
#
|
324
|
-
# @param [String] key
|
325
|
-
#
|
326
|
-
# @param [Fixnum] offset the value to substract
|
327
|
-
def decrement(key, offset = 1)
|
328
|
-
@memcached.decrement(key, offset)
|
329
|
-
end
|
330
|
-
alias :decr :decrement
|
331
|
-
|
332
|
-
# Safely copy this instance.
|
333
|
-
#
|
334
|
-
# <tt>clone</tt> is useful for threading, since each thread must have its own unshared object.
|
335
|
-
def clone
|
336
|
-
double = super
|
337
|
-
double.instance_variable_set("@memcached", @memcached.clone)
|
338
|
-
double
|
339
|
-
end
|
340
|
-
alias :dup :clone #:nodoc:
|
341
|
-
|
342
|
-
private
|
343
|
-
|
344
|
-
# Setups memcached instance. Used for dynamic client reconfiguration
|
345
|
-
# when server pushes new config.
|
346
|
-
def setup(not_used)
|
347
|
-
servers = nodes.map do |n|
|
348
|
-
"#{n.hostname}:#{n.ports['proxy']}" if n.healthy?
|
349
|
-
end.compact
|
350
|
-
@memcached = ::Memcached.new(servers, @options)
|
351
|
-
@default_ttl = @memcached.options[:default_ttl]
|
352
|
-
end
|
353
|
-
|
354
|
-
def encode(value, mode)
|
355
|
-
case mode
|
356
|
-
when :document
|
357
|
-
Yajl::Encoder.encode(value)
|
358
|
-
when :marshal, :plain
|
359
|
-
value # encoding handled by memcached library internals
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
def decode(value, mode)
|
364
|
-
case mode
|
365
|
-
when :document
|
366
|
-
Yajl::Parser.parse(value)
|
367
|
-
when :marshal, :plain
|
368
|
-
value # encoding handled by memcached library internals
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
372
|
-
end
|