libcouchbase-mapo 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +38 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +24 -0
  8. data/README.md +445 -0
  9. data/Rakefile +76 -0
  10. data/ext/README.md +6 -0
  11. data/ext/Rakefile +19 -0
  12. data/lib/libcouchbase.rb +40 -0
  13. data/lib/libcouchbase/bucket.rb +825 -0
  14. data/lib/libcouchbase/callbacks.rb +69 -0
  15. data/lib/libcouchbase/connection.rb +886 -0
  16. data/lib/libcouchbase/design_docs.rb +92 -0
  17. data/lib/libcouchbase/error.rb +68 -0
  18. data/lib/libcouchbase/ext/libcouchbase.rb +1175 -0
  19. data/lib/libcouchbase/ext/libcouchbase/cmdbase.rb +23 -0
  20. data/lib/libcouchbase/ext/libcouchbase/cmdcounter.rb +36 -0
  21. data/lib/libcouchbase/ext/libcouchbase/cmdendure.rb +26 -0
  22. data/lib/libcouchbase/ext/libcouchbase/cmdfts.rb +24 -0
  23. data/lib/libcouchbase/ext/libcouchbase/cmdget.rb +30 -0
  24. data/lib/libcouchbase/ext/libcouchbase/cmdgetreplica.rb +49 -0
  25. data/lib/libcouchbase/ext/libcouchbase/cmdhttp.rb +58 -0
  26. data/lib/libcouchbase/ext/libcouchbase/cmdn1ql.rb +40 -0
  27. data/lib/libcouchbase/ext/libcouchbase/cmdobseqno.rb +33 -0
  28. data/lib/libcouchbase/ext/libcouchbase/cmdobserve.rb +30 -0
  29. data/lib/libcouchbase/ext/libcouchbase/cmdstore.rb +40 -0
  30. data/lib/libcouchbase/ext/libcouchbase/cmdstoredur.rb +45 -0
  31. data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +61 -0
  32. data/lib/libcouchbase/ext/libcouchbase/cmdverbosity.rb +29 -0
  33. data/lib/libcouchbase/ext/libcouchbase/cmdviewquery.rb +61 -0
  34. data/lib/libcouchbase/ext/libcouchbase/contigbuf.rb +14 -0
  35. data/lib/libcouchbase/ext/libcouchbase/create_st.rb +15 -0
  36. data/lib/libcouchbase/ext/libcouchbase/create_st0.rb +23 -0
  37. data/lib/libcouchbase/ext/libcouchbase/create_st1.rb +26 -0
  38. data/lib/libcouchbase/ext/libcouchbase/create_st2.rb +32 -0
  39. data/lib/libcouchbase/ext/libcouchbase/create_st3.rb +26 -0
  40. data/lib/libcouchbase/ext/libcouchbase/crst_u.rb +20 -0
  41. data/lib/libcouchbase/ext/libcouchbase/durability_opts_st_v.rb +11 -0
  42. data/lib/libcouchbase/ext/libcouchbase/durability_opts_t.rb +14 -0
  43. data/lib/libcouchbase/ext/libcouchbase/durabilityopt_sv0.rb +63 -0
  44. data/lib/libcouchbase/ext/libcouchbase/enums.rb +1007 -0
  45. data/lib/libcouchbase/ext/libcouchbase/fragbuf.rb +18 -0
  46. data/lib/libcouchbase/ext/libcouchbase/ftshandle.rb +7 -0
  47. data/lib/libcouchbase/ext/libcouchbase/histogram.rb +34 -0
  48. data/lib/libcouchbase/ext/libcouchbase/http_request_t.rb +7 -0
  49. data/lib/libcouchbase/ext/libcouchbase/keybuf.rb +20 -0
  50. data/lib/libcouchbase/ext/libcouchbase/multicmd_ctx.rb +30 -0
  51. data/lib/libcouchbase/ext/libcouchbase/mutation_token.rb +17 -0
  52. data/lib/libcouchbase/ext/libcouchbase/n1qlhandle.rb +7 -0
  53. data/lib/libcouchbase/ext/libcouchbase/n1qlparams.rb +7 -0
  54. data/lib/libcouchbase/ext/libcouchbase/respbase.rb +29 -0
  55. data/lib/libcouchbase/ext/libcouchbase/respcounter.rb +32 -0
  56. data/lib/libcouchbase/ext/libcouchbase/respendure.rb +49 -0
  57. data/lib/libcouchbase/ext/libcouchbase/respfts.rb +40 -0
  58. data/lib/libcouchbase/ext/libcouchbase/respget.rb +44 -0
  59. data/lib/libcouchbase/ext/libcouchbase/resphttp.rb +48 -0
  60. data/lib/libcouchbase/ext/libcouchbase/respmcversion.rb +38 -0
  61. data/lib/libcouchbase/ext/libcouchbase/respn1ql.rb +41 -0
  62. data/lib/libcouchbase/ext/libcouchbase/respobseqno.rb +52 -0
  63. data/lib/libcouchbase/ext/libcouchbase/respobserve.rb +41 -0
  64. data/lib/libcouchbase/ext/libcouchbase/respserverbase.rb +32 -0
  65. data/lib/libcouchbase/ext/libcouchbase/respstats.rb +38 -0
  66. data/lib/libcouchbase/ext/libcouchbase/respstore.rb +32 -0
  67. data/lib/libcouchbase/ext/libcouchbase/respstoredur.rb +38 -0
  68. data/lib/libcouchbase/ext/libcouchbase/respsubdoc.rb +35 -0
  69. data/lib/libcouchbase/ext/libcouchbase/respviewquery.rb +67 -0
  70. data/lib/libcouchbase/ext/libcouchbase/sdentry.rb +22 -0
  71. data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +31 -0
  72. data/lib/libcouchbase/ext/libcouchbase/t.rb +7 -0
  73. data/lib/libcouchbase/ext/libcouchbase/valbuf.rb +22 -0
  74. data/lib/libcouchbase/ext/libcouchbase/valbuf_u_buf.rb +14 -0
  75. data/lib/libcouchbase/ext/libcouchbase/viewhandle.rb +7 -0
  76. data/lib/libcouchbase/ext/libcouchbase_libuv.rb +22 -0
  77. data/lib/libcouchbase/ext/tasks.rb +39 -0
  78. data/lib/libcouchbase/n1ql.rb +78 -0
  79. data/lib/libcouchbase/query_full_text.rb +147 -0
  80. data/lib/libcouchbase/query_n1ql.rb +123 -0
  81. data/lib/libcouchbase/query_view.rb +135 -0
  82. data/lib/libcouchbase/results_fiber.rb +281 -0
  83. data/lib/libcouchbase/results_native.rb +220 -0
  84. data/lib/libcouchbase/subdoc_request.rb +139 -0
  85. data/lib/libcouchbase/version.rb +5 -0
  86. data/libcouchbase.gemspec +68 -0
  87. data/spec/bucket_spec.rb +290 -0
  88. data/spec/connection_spec.rb +257 -0
  89. data/spec/design_docs_spec.rb +31 -0
  90. data/spec/error_spec.rb +26 -0
  91. data/spec/fts_spec.rb +135 -0
  92. data/spec/n1ql_spec.rb +206 -0
  93. data/spec/results_libuv_spec.rb +244 -0
  94. data/spec/results_native_spec.rb +259 -0
  95. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/design.json +1 -0
  96. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/data-0000.cbb +0 -0
  97. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/failover.json +1 -0
  98. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/meta.json +1 -0
  99. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/seqno.json +1 -0
  100. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/snapshot_markers.json +1 -0
  101. data/spec/subdoc_spec.rb +192 -0
  102. data/spec/view_spec.rb +201 -0
  103. data/windows_build.md +36 -0
  104. metadata +265 -0
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
2
+
3
+ module Libcouchbase; end;
4
+ class Libcouchbase::SubdocRequest
5
+
6
+ def initialize(key, quiet, bucket: nil, exec_opts: nil)
7
+ @key = key.to_s
8
+ raise ArgumentError.new("invalid document key #{key.inspect}") unless @key.length > 0
9
+ @refs = []
10
+ @mode = nil
11
+ @quiet = quiet
12
+ @specs = []
13
+ @ignore = []
14
+
15
+ @bucket = bucket
16
+ @exec_opts = exec_opts
17
+ end
18
+
19
+ attr_reader :mode, :key, :ignore
20
+
21
+ # Internal use only
22
+ def to_specs_array
23
+ return @mem if @mem # effectively freezes this object
24
+ number = @specs.length
25
+ @mem = FFI::MemoryPointer.new(::Libcouchbase::Ext::SDSPEC, number, false)
26
+ @specs.each_with_index do |spec, index|
27
+ struct_bytes = spec.to_ptr.get_bytes(0, ::Libcouchbase::Ext::SDSPEC.size) # (offset, length)
28
+ @mem[index].put_bytes(0, struct_bytes) # (offset, byte_string)
29
+ end
30
+ @specs = nil
31
+ [@mem, number]
32
+ end
33
+
34
+ # Internal use only
35
+ def free_memory
36
+ @refs = nil
37
+ @mem = nil
38
+ end
39
+
40
+ # When not used in block form
41
+ def execute!(**opts)
42
+ opts = @exec_opts.merge(opts)
43
+ @exec_opts = nil
44
+ bucket = @bucket
45
+ @bucket = nil
46
+ bucket.subdoc_execute!(self, **opts)
47
+ end
48
+
49
+
50
+ # =========
51
+ # Lookups
52
+ # =========
53
+
54
+ [ :get, :exists, :get_count ].each do |cmd|
55
+ command = :"sdcmd_#{cmd}"
56
+ define_method cmd do |path, quiet: nil, **opts|
57
+ quiet = @quiet if quiet.nil?
58
+ new_spec(quiet, path, command, :lookup)
59
+ self
60
+ end
61
+ end
62
+ alias_method :exists?, :exists
63
+
64
+
65
+ # ===========
66
+ # Mutations
67
+ # ===========
68
+
69
+ def remove(path, quiet: nil, **opts)
70
+ quiet = @quiet if quiet.nil?
71
+ new_spec(quiet, path, :sdcmd_remove, :mutate)
72
+ self
73
+ end
74
+
75
+ [
76
+ :dict_add, :dict_upsert, :array_add_first, :array_add_last, :array_add_unique, :counter
77
+ ].each do |cmd|
78
+ command = :"sdcmd_#{cmd}"
79
+ define_method cmd do |path, value, create_intermediates: true, **opts|
80
+ spec = new_spec(false, path, command, :mutate, create_intermediates)
81
+ set_value(spec, value)
82
+ self
83
+ end
84
+ end
85
+
86
+ [
87
+ :replace, :array_insert
88
+ ].each do |cmd|
89
+ command = :"sdcmd_#{cmd}"
90
+ define_method cmd do |path, value, **opts|
91
+ spec = new_spec(false, path, command, :mutate, false)
92
+ set_value(spec, value)
93
+ self
94
+ end
95
+ end
96
+
97
+
98
+ protected
99
+
100
+
101
+ # http://docs.couchbase.com/sdk-api/couchbase-c-client-2.8.2/group__lcb-subdoc.html#ga53e89dd6b480e81b82fb305d04d92e18
102
+ def new_spec(quiet, path, cmd, mode, create_intermediates = false)
103
+ @mode ||= mode
104
+ raise "unable to perform #{cmd} as mode is currently #{@mode}" if @mode != mode
105
+
106
+ spec = ::Libcouchbase::Ext::SDSPEC.new
107
+ spec[:sdcmd] = ::Libcouchbase::Ext::SUBDOCOP[cmd]
108
+ spec[:options] = ::Libcouchbase::Ext::SDSPEC::MKINTERMEDIATES if create_intermediates
109
+
110
+ loc = path.to_s
111
+ str = ref(loc)
112
+ spec[:path][:type] = :kv_copy
113
+ spec[:path][:contig][:bytes] = str
114
+ spec[:path][:contig][:nbytes] = loc.bytesize
115
+
116
+ @ignore << quiet
117
+ @specs << spec
118
+ spec
119
+ end
120
+
121
+ # http://docs.couchbase.com/sdk-api/couchbase-c-client-2.8.2/group__lcb-subdoc.html#ga61009762f6b23ae2a9685ddb888dc406
122
+ def set_value(spec, value)
123
+ # Create a JSON version of the value.
124
+ # We throw it into an array so strings and numbers etc are valid, then we remove the array.
125
+ val = [value].to_json[1...-1]
126
+ str = ref(val)
127
+ spec[:value][:vtype] = :kv_copy
128
+ spec[:value][:u_buf][:contig][:bytes] = str
129
+ spec[:value][:u_buf][:contig][:nbytes] = val.bytesize
130
+ value
131
+ end
132
+
133
+ # We need to hold a reference to c-strings so they are not GC'd
134
+ def ref(string)
135
+ str = ::FFI::MemoryPointer.from_string(string)
136
+ @refs << str
137
+ str
138
+ end
139
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
2
+
3
+ module Libcouchbase
4
+ VERSION = '1.4.1'
5
+ end
@@ -0,0 +1,68 @@
1
+ require File.expand_path("../lib/libcouchbase/version", __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "libcouchbase-mapo"
5
+ gem.version = Libcouchbase::VERSION
6
+ gem.license = 'MIT'
7
+ gem.authors = ["Stephen von Takach"]
8
+ gem.email = ["steve@cotag.me"]
9
+ gem.homepage = "https://github.com/cotag/libcouchbase"
10
+ gem.summary = "libcouchbase bindings for Ruby"
11
+ gem.description = "A wrapper around libcouchbase for Ruby"
12
+
13
+ gem.extensions << "ext/Rakefile"
14
+
15
+ gem.required_ruby_version = '>= 2.1.0'
16
+ gem.require_paths = ["lib"]
17
+
18
+ gem.add_runtime_dependency 'ffi', '~> 1.9'
19
+ gem.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
20
+ gem.add_runtime_dependency 'libuv', '>= 3.2.2', '< 5'
21
+
22
+ gem.add_development_dependency 'rake', '~> 11.2'
23
+ gem.add_development_dependency 'rspec', '~> 3.5'
24
+ gem.add_development_dependency 'yard', '~> 0.9'
25
+ gem.add_development_dependency 'uv-rays', '~> 2.0' # for libuv results spec
26
+ gem.add_development_dependency 'em-synchrony', '~> 1.0' # for eventmachine spec
27
+
28
+ # https://github.com/stakach/ffi-gen
29
+ # gem.add_development_dependency 'ffi-gen'
30
+
31
+ gem.files = `git ls-files`.split("\n")
32
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
33
+
34
+ if File.exist? 'ext/libcouchbase.dll'
35
+ gem.files += ['ext/libcouchbase.dll', 'ext/libcouchbase_libuv.dll']
36
+ end
37
+
38
+ # Add the submodule to the gem
39
+ relative_path = File.expand_path("../", __FILE__) + '/'
40
+ `git submodule --quiet foreach pwd`.split($\).each do |submodule_path|
41
+
42
+ if (ENV['OS'] == 'Windows_NT') && submodule_path[0] == '/'
43
+ # Detect if cygwin path is being used by git
44
+ submodule_path = submodule_path[1..-1]
45
+ submodule_path.insert(1, ':')
46
+ end
47
+
48
+ # for each submodule, change working directory to that submodule
49
+ Dir.chdir(submodule_path) do
50
+ # Make the submodule path relative
51
+ submodule_path = submodule_path.gsub(/#{relative_path}/i, '')
52
+
53
+ # issue git ls-files in submodule's directory
54
+ submodule_files = `git ls-files`.split($\)
55
+
56
+ # prepend the submodule path to create relative file paths
57
+ submodule_files_paths = submodule_files.map do |filename|
58
+ File.join(submodule_path, filename)
59
+ end
60
+
61
+ # add relative paths to gem.files
62
+ gem.files += submodule_files_paths
63
+ end
64
+ end
65
+
66
+ # Remove sym link (blows up on windows)
67
+ gem.files.delete('ext/libcouchbase/configure.pl')
68
+ end
@@ -0,0 +1,290 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
2
+
3
+ require 'libcouchbase'
4
+ Libcouchbase::Defaults.username = 'tester'
5
+ Libcouchbase::Defaults.password = 'password123'
6
+
7
+ describe Libcouchbase::Bucket do
8
+ before :each do
9
+ # This will load the couchbase connection on a different thread
10
+ @bucket = Libcouchbase::Bucket.new
11
+ @reactor = ::Libuv::Reactor.default
12
+ @log = []
13
+ end
14
+
15
+ after :each do
16
+ @bucket = nil
17
+ @reactor = nil
18
+ @log = nil
19
+ end
20
+
21
+ describe 'reactor loop' do
22
+ it "should set a value" do
23
+ @reactor.run { |reactor|
24
+ result = @bucket.set('somekey', 'woop woop')
25
+ @log << result.key
26
+ @log << result.value
27
+ @log << @bucket.set('somekey2', 'woop woop2').value
28
+ }
29
+
30
+ expect(@log).to eq(['somekey', 'woop woop', 'woop woop2'])
31
+ end
32
+
33
+ it "should get a value" do
34
+ @reactor.run { |reactor|
35
+ @log << @bucket.get('somekey')
36
+ @log << @bucket.get('somekey', extended: true).value
37
+ }
38
+ expect(@log).to eq(['woop woop', 'woop woop'])
39
+ end
40
+
41
+ it "should get a value quietly" do
42
+ @reactor.run { |reactor|
43
+ @log << @bucket.get('somekey-noexist', quiet: true)
44
+ @log << @bucket[:noexist2]
45
+ }
46
+ expect(@log).to eq([nil, nil])
47
+ end
48
+
49
+ it "should get multiple values" do
50
+ @reactor.run { |reactor|
51
+ @log << @bucket.get('somekey', 'somekey2')
52
+ @log << @bucket.get('somekey', 'somekey2', 'no-exist-sgs', quiet: true)
53
+ }
54
+ expect(@log).to eq([['woop woop', 'woop woop2'], ['woop woop', 'woop woop2', nil]])
55
+ end
56
+
57
+ it "should get multiple values as a hash" do
58
+ @reactor.run { |reactor|
59
+ @log << @bucket.get('somekey', 'somekey2', assemble_hash: true)
60
+ @log << @bucket.get('somekey', 'somekey2', 'no-exist-sgs', quiet: true, assemble_hash: true)
61
+ @log << @bucket.get('somekey', assemble_hash: true)
62
+ @log << @bucket.get('no-exist-sgs', quiet: true, assemble_hash: true)
63
+ }
64
+ expect(@log).to eq([
65
+ {'somekey' => 'woop woop', 'somekey2' => 'woop woop2'},
66
+ {'somekey' => 'woop woop', 'somekey2' => 'woop woop2', 'no-exist-sgs' => nil},
67
+ {'somekey' => 'woop woop'},
68
+ {'no-exist-sgs' => nil}
69
+ ])
70
+ end
71
+
72
+ it "should compare and swap a value" do
73
+ @reactor.run { |reactor|
74
+ @bucket.set('somekey', 'woop woop')
75
+ result = @bucket.cas('somekey') do |current|
76
+ @log << current
77
+ "current #{current}"
78
+ end
79
+ @log << result.value
80
+ }
81
+ expect(@log).to eq(['woop woop', 'current woop woop'])
82
+ end
83
+
84
+ it "should fetch a value" do
85
+ @reactor.run { |reactor|
86
+ @bucket.delete('randomunknown')
87
+ @log << @bucket.fetch('randomunknown', 'woop woop')
88
+ @log << @bucket.fetch('randomunknown') { 'testing' }
89
+
90
+ @bucket.delete('otherunknown')
91
+ @log << @bucket.fetch('otherunknown') { 'testing' }
92
+ @log << @bucket.fetch('otherunknown', 'woop woop')
93
+ }
94
+ expect(@log).to eq(['woop woop', 'woop woop', 'testing', 'testing'])
95
+ end
96
+
97
+ it "should retry when performing a CAS operation" do
98
+ @reactor.run { |reactor|
99
+ begin
100
+ @bucket.set('somekey', 'woop woop')
101
+ result = @bucket.cas('somekey', retry: 2) do |current|
102
+ @log << current
103
+ # This ensures the operation fails
104
+ @bucket.set('somekey', 'woop woop1')
105
+ "current #{current}"
106
+ end
107
+ @log << result.value
108
+ rescue Libcouchbase::Error::KeyExists
109
+ @log << :error
110
+ end
111
+ }
112
+ expect(@log).to eq(['woop woop', 'woop woop1', 'woop woop1', :error])
113
+ end
114
+ end
115
+
116
+ describe 'native ruby' do
117
+ it "should set a value" do
118
+ result = @bucket.set('somekey', 'woop woop')
119
+ @log << result.key
120
+ @log << result.value
121
+ @log << @bucket.set('somekey2', 'woop woop2').value
122
+
123
+ expect(@log).to eq(['somekey', 'woop woop', 'woop woop2'])
124
+ end
125
+
126
+ it "should get a value" do
127
+ @log << @bucket.get('somekey')
128
+ @log << @bucket.get('somekey', extended: true).value
129
+
130
+ expect(@log).to eq(['woop woop', 'woop woop'])
131
+ end
132
+
133
+ it "should get a value quietly" do
134
+ @log << @bucket.get('somekey-noexist', quiet: true)
135
+ @log << @bucket[:noexist2]
136
+
137
+ expect(@log).to eq([nil, nil])
138
+ end
139
+
140
+ it "should get multiple values" do
141
+ @log << @bucket.get('somekey', 'somekey2')
142
+ @log << @bucket.get('somekey', 'somekey2', 'no-exist-sgs', quiet: true)
143
+
144
+ expect(@log).to eq([['woop woop', 'woop woop2'], ['woop woop', 'woop woop2', nil]])
145
+ end
146
+
147
+ it "should get multiple values as a hash" do
148
+ @log << @bucket.get('somekey', 'somekey2', assemble_hash: true)
149
+ @log << @bucket.get('somekey', 'somekey2', 'no-exist-sgs', quiet: true, assemble_hash: true)
150
+ @log << @bucket.get('somekey', assemble_hash: true)
151
+ @log << @bucket.get('no-exist-sgs', quiet: true, assemble_hash: true)
152
+
153
+ expect(@log).to eq([
154
+ {'somekey' => 'woop woop', 'somekey2' => 'woop woop2'},
155
+ {'somekey' => 'woop woop', 'somekey2' => 'woop woop2', 'no-exist-sgs' => nil},
156
+ {'somekey' => 'woop woop'},
157
+ {'no-exist-sgs' => nil}
158
+ ])
159
+ end
160
+
161
+ it "should get multiple results asynchronously and then wait for the results" do
162
+ results = []
163
+ results << @bucket.get('somekey', async: true)
164
+ results << @bucket.get('somekey2', async: true)
165
+
166
+ @log = @bucket.wait_results results
167
+
168
+ expect(@log).to eq(['woop woop', 'woop woop2'])
169
+ end
170
+
171
+ it "should compare and swap a value" do
172
+ @bucket.set('somekey', 'woop woop')
173
+ result = @bucket.cas('somekey') do |current|
174
+ @log << current
175
+ "current #{current}"
176
+ end
177
+ @log << result.value
178
+
179
+ expect(@log).to eq(['woop woop', 'current woop woop'])
180
+ end
181
+
182
+ it "should retry when performing a CAS operation" do
183
+ begin
184
+ @bucket.set('somekey', 'woop woop')
185
+ result = @bucket.cas('somekey', retry: 2) do |current|
186
+ @log << current
187
+ # This ensures the operation fails
188
+ @bucket.set('somekey', 'woop woop1')
189
+ "current #{current}"
190
+ end
191
+ @log << result.value
192
+ rescue Libcouchbase::Error::KeyExists
193
+ @log << :error
194
+ end
195
+
196
+ expect(@log).to eq(['woop woop', 'woop woop1', 'woop woop1', :error])
197
+ end
198
+ end
199
+
200
+ describe 'eventmachine loop' do
201
+ require 'em-synchrony'
202
+
203
+ it "should set a value" do
204
+ EM.synchrony {
205
+ result = @bucket.set('somekey', 'woop woop')
206
+ @log << result.key
207
+ @log << result.value
208
+ @log << @bucket.set('somekey2', 'woop woop2').value
209
+ EM.stop
210
+ }
211
+
212
+ expect(@log).to eq(['somekey', 'woop woop', 'woop woop2'])
213
+ end
214
+
215
+ it "should get a value" do
216
+ EM.synchrony {
217
+ @log << @bucket.get('somekey')
218
+ @log << @bucket.get('somekey', extended: true).value
219
+ EM.stop
220
+ }
221
+ expect(@log).to eq(['woop woop', 'woop woop'])
222
+ end
223
+
224
+ it "should get a value quietly" do
225
+ EM.synchrony {
226
+ @log << @bucket.get('somekey-noexist', quiet: true)
227
+ @log << @bucket[:noexist2]
228
+ EM.stop
229
+ }
230
+ expect(@log).to eq([nil, nil])
231
+ end
232
+
233
+ it "should get multiple values" do
234
+ EM.synchrony {
235
+ @log << @bucket.get('somekey', 'somekey2')
236
+ @log << @bucket.get('somekey', 'somekey2', 'no-exist-sgs', quiet: true)
237
+ EM.stop
238
+ }
239
+ expect(@log).to eq([['woop woop', 'woop woop2'], ['woop woop', 'woop woop2', nil]])
240
+ end
241
+
242
+ it "should get multiple values as a hash" do
243
+ EM.synchrony {
244
+ @log << @bucket.get('somekey', 'somekey2', assemble_hash: true)
245
+ @log << @bucket.get('somekey', 'somekey2', 'no-exist-sgs', quiet: true, assemble_hash: true)
246
+ @log << @bucket.get('somekey', assemble_hash: true)
247
+ @log << @bucket.get('no-exist-sgs', quiet: true, assemble_hash: true)
248
+ EM.stop
249
+ }
250
+ expect(@log).to eq([
251
+ {'somekey' => 'woop woop', 'somekey2' => 'woop woop2'},
252
+ {'somekey' => 'woop woop', 'somekey2' => 'woop woop2', 'no-exist-sgs' => nil},
253
+ {'somekey' => 'woop woop'},
254
+ {'no-exist-sgs' => nil}
255
+ ])
256
+ end
257
+
258
+ it "should compare and swap a value" do
259
+ EM.synchrony {
260
+ @bucket.set('somekey', 'woop woop')
261
+ result = @bucket.cas('somekey') do |current|
262
+ @log << current
263
+ "current #{current}"
264
+ end
265
+ @log << result.value
266
+ EM.stop
267
+ }
268
+ expect(@log).to eq(['woop woop', 'current woop woop'])
269
+ end
270
+
271
+ it "should retry when performing a CAS operation" do
272
+ EM.synchrony {
273
+ begin
274
+ @bucket.set('somekey', 'woop woop')
275
+ result = @bucket.cas('somekey', retry: 2) do |current|
276
+ @log << current
277
+ # This ensures the operation fails
278
+ @bucket.set('somekey', 'woop woop1')
279
+ "current #{current}"
280
+ end
281
+ @log << result.value
282
+ rescue Libcouchbase::Error::KeyExists
283
+ @log << :error
284
+ end
285
+ EM.stop
286
+ }
287
+ expect(@log).to eq(['woop woop', 'woop woop1', 'woop woop1', :error])
288
+ end
289
+ end
290
+ end