couchbase 1.1.5-x86-mingw32 → 1.2.0.beta-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +12 -1
  3. data/HISTORY.markdown +112 -1
  4. data/README.markdown +149 -6
  5. data/couchbase.gemspec +5 -1
  6. data/ext/couchbase_ext/.gitignore +4 -0
  7. data/ext/couchbase_ext/arguments.c +973 -0
  8. data/ext/couchbase_ext/arithmetic.c +322 -0
  9. data/ext/couchbase_ext/bucket.c +1092 -0
  10. data/ext/couchbase_ext/couchbase_ext.c +618 -3247
  11. data/ext/couchbase_ext/couchbase_ext.h +519 -0
  12. data/ext/couchbase_ext/delete.c +167 -0
  13. data/ext/couchbase_ext/extconf.rb +24 -5
  14. data/ext/couchbase_ext/get.c +301 -0
  15. data/ext/couchbase_ext/gethrtime.c +124 -0
  16. data/ext/couchbase_ext/http.c +402 -0
  17. data/ext/couchbase_ext/observe.c +174 -0
  18. data/ext/couchbase_ext/result.c +126 -0
  19. data/ext/couchbase_ext/stats.c +169 -0
  20. data/ext/couchbase_ext/store.c +522 -0
  21. data/ext/couchbase_ext/timer.c +192 -0
  22. data/ext/couchbase_ext/touch.c +190 -0
  23. data/ext/couchbase_ext/unlock.c +180 -0
  24. data/ext/couchbase_ext/utils.c +471 -0
  25. data/ext/couchbase_ext/version.c +147 -0
  26. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  27. data/lib/active_support/cache/couchbase_store.rb +356 -0
  28. data/lib/couchbase.rb +24 -3
  29. data/lib/couchbase/bucket.rb +372 -9
  30. data/lib/couchbase/result.rb +26 -0
  31. data/lib/couchbase/utils.rb +59 -0
  32. data/lib/couchbase/version.rb +1 -1
  33. data/lib/couchbase/view.rb +305 -0
  34. data/lib/couchbase/view_row.rb +230 -0
  35. data/lib/ext/multi_json_fix.rb +47 -0
  36. data/lib/rack/session/couchbase.rb +104 -0
  37. data/tasks/compile.rake +5 -14
  38. data/test/setup.rb +6 -2
  39. data/test/test_arithmetic.rb +32 -2
  40. data/test/test_async.rb +18 -4
  41. data/test/test_bucket.rb +11 -1
  42. data/test/test_cas.rb +13 -3
  43. data/test/test_couchbase_rails_cache_store.rb +294 -0
  44. data/test/test_delete.rb +60 -3
  45. data/test/test_format.rb +28 -17
  46. data/test/test_get.rb +91 -14
  47. data/test/test_store.rb +31 -1
  48. data/test/{test_flush.rb → test_timer.rb} +11 -18
  49. data/test/test_touch.rb +33 -5
  50. data/test/test_unlock.rb +120 -0
  51. data/test/test_utils.rb +26 -0
  52. metadata +102 -12
@@ -0,0 +1,47 @@
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
+ # This patch fixing loading primitives like strings, numbers and booleans
19
+ # using json_gem. It amends behaviour of MultiJson::Engines::JsonGem#load
20
+ # when it receives JSON string which neither Array nor Object.
21
+
22
+ if MultiJson.respond_to?(:engine)
23
+ multi_json_engine = MultiJson.send(:engine)
24
+ else
25
+ multi_json_engine = MultiJson.send(:adapter)
26
+ end
27
+ if multi_json_engine.name =~ /JsonGem$/
28
+ class << multi_json_engine
29
+ alias _load_object load
30
+ def load(string, options = {})
31
+ if string =~ /\A\s*[{\[]/
32
+ _load_object(string, options)
33
+ else
34
+ _load_object("[#{string}]", options)[0]
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # Patch for MultiJson versions < 1.3.3
41
+ if MultiJson.respond_to?(:decode) && MultiJson.respond_to?(:encode) &&
42
+ !MultiJson.respond_to?(:load) && !MultiJson.respond_to?(:dump)
43
+ class << MultiJson
44
+ alias :dump :encode
45
+ alias :load :decode
46
+ end
47
+ end
@@ -0,0 +1,104 @@
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 'rack/session/abstract/id'
19
+ require 'couchbase'
20
+ require 'thread'
21
+
22
+ module Rack
23
+ module Session
24
+
25
+ # This is Couchbase-powered session store for rack applications
26
+ #
27
+ # To use it just load it as usual middleware in your `config.ru` file
28
+ #
29
+ # require 'rack/session/couchbase'
30
+ # use Rack::Session::Couchbase
31
+ #
32
+ # You can also pass additional options:
33
+ #
34
+ # require 'rack/session/couchbase'
35
+ # use Rack::Session::Couchbase, :expire_after => 5.minutes,
36
+ # :couchbase => {:bucket => "sessions", :default_format => :marshal}
37
+ #
38
+ # By default sessions will be serialized to JSON, to allow analyse them
39
+ # using Map/Reduce.
40
+ #
41
+ class Couchbase < Abstract::ID
42
+ attr_reader :mutex, :pool
43
+
44
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge(
45
+ :couchbase => {:default_format => :document, :key_prefix => 'rack:session:'})
46
+
47
+ def initialize(app, options = {})
48
+ # Support old :expires option
49
+ options[:expire_after] ||= options[:expires]
50
+ super
51
+
52
+ @default_options[:couchbase][:default_ttl] ||= options[:expire_after]
53
+ @default_options[:couchbase][:key_prefix] ||= options[:namespace]
54
+ @namespace = @default_options[:couchbase][:key_prefix]
55
+ @mutex = Mutex.new
56
+ @pool = ::Couchbase.connect(@default_options[:couchbase])
57
+ end
58
+
59
+ def generate_sid
60
+ loop do
61
+ sid = super
62
+ break sid unless @pool.get(sid)
63
+ end
64
+ end
65
+
66
+ def get_session(env, sid)
67
+ with_lock(env, [nil, {}]) do
68
+ unless sid and session = @pool.get(sid)
69
+ sid, session = generate_sid, {}
70
+ @pool.set(sid, session)
71
+ end
72
+ [sid, session]
73
+ end
74
+ end
75
+
76
+ def set_session(env, session_id, new_session, options)
77
+ with_lock(env, false) do
78
+ @pool.set session_id, new_session, options
79
+ session_id
80
+ end
81
+ end
82
+
83
+ def destroy_session(env, session_id, options)
84
+ with_lock(env) do
85
+ @pool.delete(session_id)
86
+ generate_sid unless options[:drop]
87
+ end
88
+ end
89
+
90
+ def with_lock(env, default = nil)
91
+ @mutex.lock if env['rack.multithread']
92
+ yield
93
+ rescue Couchbase::Error::Connect, Couchbase::Error::Timeout
94
+ if $VERBOSE
95
+ warn "#{self} is unable to find Couchbase server."
96
+ warn $!.inspect
97
+ end
98
+ default
99
+ ensure
100
+ @mutex.unlock if @mutex.locked?
101
+ end
102
+ end
103
+ end
104
+ end
@@ -57,7 +57,6 @@ end
57
57
 
58
58
  require 'rubygems/package_task'
59
59
  Gem::PackageTask.new(gemspec) do |pkg|
60
- pkg.need_zip = true
61
60
  pkg.need_tar = true
62
61
  end
63
62
 
@@ -84,22 +83,14 @@ end
84
83
  namespace :ports do
85
84
  directory "ports"
86
85
 
87
- task :libvbucket => ["ports"] do
88
- recipe = MiniPortile.new "libvbucket", "1.8.0.3"
89
- recipe.files << "http://packages.couchbase.com/clients/c/#{recipe.name}-#{recipe.version}.tar.gz"
90
- recipe.configure_options.push("--disable-debug",
91
- "--without-docs",
92
- "--disable-dependency-tracking")
93
- recipe.cook
94
- recipe.activate
95
- end
96
-
97
- task :libcouchbase => [:libvbucket] do
98
- recipe = MiniPortile.new "libcouchbase", "1.0.6"
99
- recipe.files << "http://packages.couchbase.com/clients/c/#{recipe.name}-#{recipe.version}.tar.gz"
86
+ task :libcouchbase => ["ports"] do
87
+ recipe = MiniPortile.new "libcouchbase", "2.0.0beta_5_g970a292"
88
+ recipe.files << "http://packages.couchbase.com/clients/c/libcouchbase-#{recipe.version}.tar.gz"
100
89
  recipe.configure_options.push("--disable-debug",
101
90
  "--disable-dependency-tracking",
102
91
  "--disable-couchbasemock",
92
+ "--disable-cxx",
93
+ "--disable-examples",
103
94
  "--disable-tools")
104
95
  recipe.cook
105
96
  recipe.activate
@@ -36,7 +36,7 @@ class CouchbaseServer
36
36
  raise ArgumentError, "Check COUCHBASE_SERVER variable. It should be hostname:port"
37
37
  end
38
38
 
39
- @config = Yajl::Parser.parse(open("http://#{@host}:#{@port}/pools/default"))
39
+ @config = MultiJson.load(open("http://#{@host}:#{@port}/pools/default"))
40
40
  @num_nodes = @config["nodes"].size
41
41
  @buckets_spec = params[:buckets_spec] || "default:" # "default:,protected:secret,cache::memcache"
42
42
  end
@@ -50,7 +50,11 @@ class CouchbaseServer
50
50
  :username => name,
51
51
  :bucket => name,
52
52
  :password => password)
53
- connection.flush
53
+ begin
54
+ connection.flush
55
+ rescue Couchbase::Error::NotSupported
56
+ # on recent server flush is disabled
57
+ end
54
58
  end
55
59
  end
56
60
  def stop; end
@@ -84,7 +84,9 @@ class TestArithmetic < MiniTest::Unit::TestCase
84
84
  val = connection.incr(uniq_id(:missing), :create => true)
85
85
  assert_equal 1, val
86
86
  sleep(2)
87
- refute connection.get(uniq_id(:missing))
87
+ assert_raises(Couchbase::Error::NotFound) do
88
+ connection.get(uniq_id(:missing))
89
+ end
88
90
  end
89
91
 
90
92
  def test_decrement_with_absolute_ttl
@@ -95,7 +97,9 @@ class TestArithmetic < MiniTest::Unit::TestCase
95
97
  assert_equal 0, val
96
98
  assert_equal 0, connection.get(uniq_id)
97
99
  sleep(2)
98
- refute connection.get(uniq_id)
100
+ assert_raises(Couchbase::Error::NotFound) do
101
+ refute connection.get(uniq_id)
102
+ end
99
103
  end
100
104
 
101
105
  def test_it_allows_custom_delta
@@ -106,4 +110,30 @@ class TestArithmetic < MiniTest::Unit::TestCase
106
110
  assert_equal 22, val
107
111
  end
108
112
 
113
+ def test_it_allows_to_specify_delta_in_options
114
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
115
+
116
+ connection.set(uniq_id, 12)
117
+ options = {:delta => 10}
118
+ val = connection.incr(uniq_id, options)
119
+ assert_equal 22, val
120
+ end
121
+
122
+ def test_multi_incr
123
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
124
+ connection.set(uniq_id(:foo) => 1, uniq_id(:bar) => 1)
125
+
126
+ assert_equal [2, 2], connection.incr(uniq_id(:foo), uniq_id(:bar)).values.sort
127
+ assert_equal [12, 12], connection.incr(uniq_id(:foo), uniq_id(:bar), :delta => 10).values.sort
128
+ assert_equal [14, 15], connection.incr(uniq_id(:foo) => 2, uniq_id(:bar) => 3).values.sort
129
+ end
130
+
131
+ def test_multi_decr
132
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
133
+ connection.set(uniq_id(:foo) => 14, uniq_id(:bar) => 15)
134
+
135
+ assert_equal [12, 12], connection.decr(uniq_id(:foo) => 2, uniq_id(:bar) => 3).values.sort
136
+ assert_equal [2, 2], connection.decr(uniq_id(:foo), uniq_id(:bar), :delta => 10).values.sort
137
+ assert_equal [1, 1], connection.decr(uniq_id(:foo), uniq_id(:bar)).values.sort
138
+ end
109
139
  end
@@ -110,7 +110,9 @@ class TestAsync < MiniTest::Unit::TestCase
110
110
  assert success
111
111
  assert_equal "foo", val
112
112
  sleep(2)
113
- refute connection.get(uniq_id)
113
+ assert_raises(Couchbase::Error::NotFound) do
114
+ connection.get(uniq_id)
115
+ end
114
116
  end
115
117
 
116
118
  def test_nested_async_delete_get
@@ -167,7 +169,9 @@ class TestAsync < MiniTest::Unit::TestCase
167
169
  end
168
170
  end
169
171
 
170
- refute connection.get(uniq_id)
172
+ assert_raises(Couchbase::Error::NotFound) do
173
+ connection.get(uniq_id)
174
+ end
171
175
  res.keys.each do |key|
172
176
  assert res[key].is_a?(Numeric)
173
177
  assert connection.get(key)
@@ -222,13 +226,23 @@ class TestAsync < MiniTest::Unit::TestCase
222
226
  connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
223
227
  connection.set(uniq_id, "foo")
224
228
 
225
- connection.timeout = 100 # 100 us
229
+ connection.timeout = 100_000 # 100_000 us
226
230
  connection.run do
227
231
  connection.get(uniq_id) do |ret|
228
232
  assert ret.success?
229
233
  assert_equal "foo", ret.value
230
234
  end
231
- sleep(1.5)
235
+ sleep(1.5) # 1_500_000 us
236
+ end
237
+ end
238
+
239
+ def test_send_threshold
240
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
241
+
242
+ sent = false
243
+ connection.run(:send_threshold => 100) do # 100 bytes
244
+ connection.set(uniq_id, "foo" * 100) {|r| sent = true}
245
+ assert sent
232
246
  end
233
247
  end
234
248
 
@@ -151,7 +151,7 @@ class TestBucket < MiniTest::Unit::TestCase
151
151
  with_mock do |mock|
152
152
  connection = Couchbase.new(:hostname => mock.host,
153
153
  :port => mock.port)
154
- assert connection.quiet?
154
+ refute connection.quiet?
155
155
 
156
156
  connection = Couchbase.new(:hostname => mock.host,
157
157
  :port => mock.port,
@@ -224,4 +224,14 @@ class TestBucket < MiniTest::Unit::TestCase
224
224
  end
225
225
  end
226
226
 
227
+ def test_it_uses_bucket_name_as_username_if_username_is_empty
228
+ with_mock(:buckets_spec => 'protected:secret') do |mock|
229
+ connection = Couchbase.new(:hostname => mock.host,
230
+ :port => mock.port,
231
+ :bucket => 'protected',
232
+ :password => 'secret')
233
+ assert connection.connected?
234
+ end
235
+ end
236
+
227
237
  end
@@ -44,13 +44,23 @@ class TestCas < MiniTest::Unit::TestCase
44
44
  connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port,
45
45
  :default_format => :document)
46
46
  connection.set(uniq_id, {"bar" => 1})
47
+ calls = 0
47
48
  connection.run do |conn|
48
49
  conn.cas(uniq_id) do |ret|
49
- new_val = ret.value
50
- new_val["baz"] = 2
51
- new_val
50
+ calls += 1
51
+ case ret.operation
52
+ when :get
53
+ new_val = ret.value
54
+ new_val["baz"] = 2
55
+ new_val
56
+ when :set
57
+ assert ret.success?
58
+ else
59
+ flunk "Unexpected operation: #{ret.operation.inspect}"
60
+ end
52
61
  end
53
62
  end
63
+ assert_equal 2, calls
54
64
  val = connection.get(uniq_id)
55
65
  expected = {"bar" => 1, "baz" => 2}
56
66
  assert_equal expected, val
@@ -0,0 +1,294 @@
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 File.join(File.dirname(__FILE__), 'setup')
19
+ require 'active_support/cache/couchbase_store'
20
+ require 'active_support/notifications'
21
+ require 'ostruct'
22
+
23
+ class TestCouchbaseRailsCacheStore < MiniTest::Unit::TestCase
24
+
25
+ def setup
26
+ @mock = start_mock
27
+ @foo = OpenStruct.new :payload => "foo"
28
+ @foobar = OpenStruct.new :payload => "foobar"
29
+ end
30
+
31
+ def teardown
32
+ stop_mock(@mock)
33
+ end
34
+
35
+ def store
36
+ @store ||= ActiveSupport::Cache::CouchbaseStore.new(:hostname => @mock.host,
37
+ :port => @mock.port)
38
+ end
39
+
40
+ def test_it_supported_methods
41
+ supported_methods = store.public_methods(false).map(&:to_sym)
42
+ assert supported_methods.include?(:fetch)
43
+ assert supported_methods.include?(:write)
44
+ assert supported_methods.include?(:read)
45
+ assert supported_methods.include?(:read_multi)
46
+ assert supported_methods.include?(:increment)
47
+ assert supported_methods.include?(:decrement)
48
+ assert supported_methods.include?(:exists?)
49
+ assert supported_methods.include?(:delete)
50
+ assert supported_methods.include?(:stats)
51
+ refute supported_methods.include?(:clear)
52
+ assert_raises(NotImplementedError) do
53
+ store.clear
54
+ end
55
+ refute supported_methods.include?(:cleanup)
56
+ assert_raises(NotImplementedError) do
57
+ store.cleanup
58
+ end
59
+ end
60
+
61
+ def test_it_writes_and_reads_the_data
62
+ store.write uniq_id, @foobar
63
+ assert_equal @foobar, store.read(uniq_id)
64
+ end
65
+
66
+ def test_it_writes_the_data_with_expiration_time
67
+ store.write(uniq_id, @foobar, :expires_in => 1.second)
68
+ assert_equal @foobar, store.read(uniq_id)
69
+ sleep 2
70
+ refute store.read(uniq_id)
71
+ end
72
+
73
+ def test_it_doest_write_data_if_unless_exist_option_is_true
74
+ store.write uniq_id, @foo
75
+ [:unless_exist, :unless_exists].each do |unless_exists|
76
+ store.write uniq_id, @foobar, unless_exists => true
77
+ assert_equal @foo, store.read(uniq_id)
78
+ end
79
+ end
80
+
81
+ if RUBY_VERSION.match /1\.9/
82
+ def test_it_reads_raw_data
83
+ store.write uniq_id, @foo
84
+ assert_equal "\x04\bU:\x0FOpenStruct{\x06:\fpayloadI\"\bfoo\x06:\x06EF",
85
+ store.read(uniq_id, :raw => true)
86
+ end
87
+ else
88
+ def test_it_reads_raw_data
89
+ store.write uniq_id, @foo
90
+ assert_equal "\004\bU:\017OpenStruct{\006:\fpayload\"\bfoo",
91
+ store.read(uniq_id, :raw => true)
92
+ end
93
+ end
94
+
95
+ def test_it_writes_raw_data
96
+ store.write uniq_id, @foobar, :raw => true
97
+ assert_equal '#<OpenStruct payload="foobar">', store.read(uniq_id, :raw => true)
98
+ end
99
+
100
+ def test_it_deletes_data
101
+ store.write uniq_id, @foo
102
+ store.delete uniq_id
103
+ refute store.read(uniq_id)
104
+ end
105
+
106
+ def test_it_verifies_existence_of_an_object_in_the_store
107
+ store.write uniq_id, @foo
108
+ assert store.exist?(uniq_id)
109
+ refute store.exist?(uniq_id(:missing))
110
+ end
111
+
112
+ def test_it_initializes_key_on_first_increment_with_zero
113
+ store.increment(uniq_id)
114
+ assert_equal 0, store.read(uniq_id)
115
+ assert_equal "0", store.read(uniq_id, :raw => true)
116
+ end
117
+
118
+ def test_it_initializes_key_on_first_decrement_with_zero
119
+ store.decrement(uniq_id)
120
+ assert_equal 0, store.read(uniq_id)
121
+ assert_equal "0", store.read(uniq_id, :raw => true)
122
+ end
123
+
124
+ def test_it_initializes_key_with_given_value_on_increment
125
+ store.increment(uniq_id, 1, :initial => 5)
126
+ assert_equal 5, store.read(uniq_id)
127
+ assert_equal "5", store.read(uniq_id, :raw => true)
128
+ end
129
+
130
+ def test_it_initializes_key_with_given_value_on_decrement
131
+ store.decrement(uniq_id, 1, :initial => 5)
132
+ assert_equal 5, store.read(uniq_id)
133
+ assert_equal "5", store.read(uniq_id, :raw => true)
134
+ end
135
+
136
+ def test_it_increments_a_key
137
+ 3.times { store.increment uniq_id }
138
+ assert_equal 2, store.read(uniq_id)
139
+ assert_equal "2", store.read(uniq_id, :raw => true)
140
+ end
141
+
142
+ def test_it_decrements_a_key
143
+ 4.times { store.increment uniq_id }
144
+ 2.times { store.decrement uniq_id }
145
+ assert_equal 1, store.read(uniq_id)
146
+ assert_equal "1", store.read(uniq_id, :raw => true)
147
+ end
148
+
149
+ def test_it_increments_a_raw_key
150
+ assert store.write(uniq_id, 1, :raw => true)
151
+ store.increment(uniq_id, 2)
152
+ assert_equal 3, store.read(uniq_id, :raw => true).to_i
153
+ end
154
+
155
+ def test_it_decrements_a_raw_key
156
+ assert store.write(uniq_id, 3, :raw => true)
157
+ store.decrement(uniq_id, 2)
158
+ assert_equal 1, store.read(uniq_id, :raw => true).to_i
159
+ end
160
+
161
+ def test_it_increments_a_key_by_given_value
162
+ store.write(uniq_id, 0, :raw => true)
163
+ store.increment uniq_id, 3
164
+ assert_equal 3, store.read(uniq_id, :raw => true).to_i
165
+ end
166
+
167
+ def test_it_decrements_a_key_by_given_value
168
+ store.write(uniq_id, 0, :raw => true)
169
+ 3.times { store.increment uniq_id }
170
+ store.decrement uniq_id, 2
171
+ assert_equal 1, store.read(uniq_id, :raw => true).to_i
172
+ end
173
+
174
+ def test_it_provides_store_stats
175
+ refute store.stats.empty?
176
+ end
177
+
178
+ def test_it_fetches_data
179
+ assert store.write(uniq_id, @foo)
180
+ assert_equal @foo, store.fetch(uniq_id)
181
+ refute store.fetch("rub-a-dub")
182
+ store.fetch("rub-a-dub") { "Flora de Cana" }
183
+ assert_equal "Flora de Cana", store.fetch("rub-a-dub")
184
+ store.fetch(uniq_id, :force => true) # force cache miss
185
+ store.fetch(uniq_id, :force => true, :expires_in => 1.second) { @foobar }
186
+ assert_equal @foobar, store.fetch(uniq_id)
187
+ sleep 2
188
+ refute store.fetch(uniq_id)
189
+ end
190
+
191
+ def test_it_reads_multiple_keys
192
+ assert store.write(uniq_id(1), @foo)
193
+ assert store.write(uniq_id(2), "foo")
194
+ result = store.read_multi uniq_id(1), uniq_id(2)
195
+ assert_equal @foo, result[uniq_id(1)]
196
+ assert_equal "foo", result[uniq_id(2)]
197
+ end
198
+
199
+ def test_it_reads_multiple_keys_and_returns_only_the_matched_ones
200
+ assert store.write(uniq_id, @foo)
201
+ result = store.read_multi uniq_id, uniq_id(:missing)
202
+ assert result[uniq_id]
203
+ refute result[uniq_id(:missing)]
204
+ end
205
+
206
+ def test_it_notifies_on_fetch
207
+ collect_notifications do
208
+ store.fetch(uniq_id) { "foo" }
209
+ end
210
+
211
+ read, generate, write = @events
212
+
213
+ assert_equal 'cache_read.active_support', read.name
214
+ assert_equal({:key => uniq_id, :super_operation => :fetch}, read.payload)
215
+
216
+ assert_equal 'cache_generate.active_support', generate.name
217
+ assert_equal({:key => uniq_id}, generate.payload)
218
+
219
+ assert_equal 'cache_write.active_support', write.name
220
+ assert_equal({:key => uniq_id}, write.payload)
221
+ end
222
+
223
+ def test_it_notifies_on_read
224
+ collect_notifications do
225
+ store.read uniq_id
226
+ end
227
+
228
+ read = @events.first
229
+ assert_equal 'cache_read.active_support', read.name
230
+ assert_equal({:key => uniq_id, :hit => false}, read.payload)
231
+ end
232
+
233
+ def test_it_notifies_on_write
234
+ collect_notifications do
235
+ store.write uniq_id, "foo"
236
+ end
237
+
238
+ write = @events.first
239
+ assert_equal 'cache_write.active_support', write.name
240
+ assert_equal({:key => uniq_id}, write.payload)
241
+ end
242
+
243
+ def test_it_notifies_on_delete
244
+ collect_notifications do
245
+ store.delete uniq_id
246
+ end
247
+
248
+ delete = @events.first
249
+ assert_equal 'cache_delete.active_support', delete.name
250
+ assert_equal({:key => uniq_id}, delete.payload)
251
+ end
252
+
253
+ def test_it_notifies_on_exist?
254
+ collect_notifications do
255
+ store.exist? uniq_id
256
+ end
257
+
258
+ exist = @events.first
259
+ assert_equal 'cache_exists?.active_support', exist.name
260
+ assert_equal({:key => uniq_id}, exist.payload)
261
+ end
262
+
263
+ def test_it_notifies_on_increment
264
+ collect_notifications do
265
+ store.increment uniq_id
266
+ end
267
+
268
+ increment = @events.first
269
+ assert_equal 'cache_increment.active_support', increment.name
270
+ assert_equal({:key => uniq_id, :amount => 1, :create => true}, increment.payload)
271
+ end
272
+
273
+ def test_it_notifies_on_decrement
274
+ collect_notifications do
275
+ store.decrement uniq_id
276
+ end
277
+
278
+ decrement = @events.first
279
+ assert_equal 'cache_decrement.active_support', decrement.name
280
+ assert_equal({:key => uniq_id, :amount => 1, :create => true}, decrement.payload)
281
+ end
282
+
283
+ private
284
+
285
+ def collect_notifications
286
+ @events = [ ]
287
+ ActiveSupport::Cache::CouchbaseStore.instrument = true
288
+ ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
289
+ @events << ActiveSupport::Notifications::Event.new(*args)
290
+ end
291
+ yield
292
+ ActiveSupport::Cache::CouchbaseStore.instrument = false
293
+ end
294
+ end