couchbase-jruby-client 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.jrubyrc +722 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +203 -0
  7. data/README.md +349 -0
  8. data/Rakefile +10 -0
  9. data/couchbase-jruby-client.gemspec +31 -0
  10. data/lib/couchbase/async/callback.rb +19 -0
  11. data/lib/couchbase/async/queue.rb +26 -0
  12. data/lib/couchbase/async.rb +140 -0
  13. data/lib/couchbase/bucket.rb +556 -0
  14. data/lib/couchbase/cluster.rb +105 -0
  15. data/lib/couchbase/constants.rb +12 -0
  16. data/lib/couchbase/design_doc.rb +61 -0
  17. data/lib/couchbase/error.rb +43 -0
  18. data/lib/couchbase/jruby/couchbase_client.rb +22 -0
  19. data/lib/couchbase/jruby/future.rb +8 -0
  20. data/lib/couchbase/operations/arithmetic.rb +301 -0
  21. data/lib/couchbase/operations/delete.rb +104 -0
  22. data/lib/couchbase/operations/design_docs.rb +99 -0
  23. data/lib/couchbase/operations/get.rb +282 -0
  24. data/lib/couchbase/operations/stats.rb +26 -0
  25. data/lib/couchbase/operations/store.rb +461 -0
  26. data/lib/couchbase/operations/touch.rb +136 -0
  27. data/lib/couchbase/operations/unlock.rb +192 -0
  28. data/lib/couchbase/operations/utils.rb +44 -0
  29. data/lib/couchbase/operations.rb +27 -0
  30. data/lib/couchbase/query.rb +73 -0
  31. data/lib/couchbase/result.rb +43 -0
  32. data/lib/couchbase/transcoder.rb +77 -0
  33. data/lib/couchbase/utils.rb +62 -0
  34. data/lib/couchbase/version.rb +3 -0
  35. data/lib/couchbase/view.rb +367 -0
  36. data/lib/couchbase/view_row.rb +193 -0
  37. data/lib/couchbase.rb +157 -0
  38. data/lib/jars/commons-codec-1.5.jar +0 -0
  39. data/lib/jars/couchbase-client-1.2.0-javadoc.jar +0 -0
  40. data/lib/jars/couchbase-client-1.2.0-sources.jar +0 -0
  41. data/lib/jars/couchbase-client-1.2.0.jar +0 -0
  42. data/lib/jars/httpcore-4.1.1.jar +0 -0
  43. data/lib/jars/httpcore-nio-4.1.1.jar +0 -0
  44. data/lib/jars/jettison-1.1.jar +0 -0
  45. data/lib/jars/netty-3.5.5.Final.jar +0 -0
  46. data/lib/jars/spymemcached-2.10.0-javadoc.jar +0 -0
  47. data/lib/jars/spymemcached-2.10.0-sources.jar +0 -0
  48. data/lib/jars/spymemcached-2.10.0.jar +0 -0
  49. data/test/profile/.gitignore +1 -0
  50. data/test/profile/.jrubyrc +722 -0
  51. data/test/profile/Gemfile +6 -0
  52. data/test/profile/benchmark.rb +168 -0
  53. data/test/profile/profile.rb +59 -0
  54. data/test/setup.rb +203 -0
  55. data/test/test_arithmetic.rb +177 -0
  56. data/test/test_async.rb +324 -0
  57. data/test/test_bucket.rb +213 -0
  58. data/test/test_cas.rb +79 -0
  59. data/test/test_couchbase.rb +29 -0
  60. data/test/test_couchbase_rails_cache_store.rb +341 -0
  61. data/test/test_delete.rb +125 -0
  62. data/test/test_design_docs.rb +72 -0
  63. data/test/test_errors.rb +82 -0
  64. data/test/test_format.rb +161 -0
  65. data/test/test_get.rb +417 -0
  66. data/test/test_query.rb +23 -0
  67. data/test/test_stats.rb +57 -0
  68. data/test/test_store.rb +213 -0
  69. data/test/test_timer.rb +43 -0
  70. data/test/test_touch.rb +97 -0
  71. data/test/test_unlock.rb +121 -0
  72. data/test/test_utils.rb +58 -0
  73. data/test/test_version.rb +53 -0
  74. data/test/test_view.rb +94 -0
  75. metadata +255 -0
@@ -0,0 +1,367 @@
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 'base64'
19
+
20
+ module Couchbase
21
+
22
+ module Error
23
+ class View < Base
24
+ attr_reader :from, :reason
25
+
26
+ def initialize(from, reason, prefix = "SERVER: ")
27
+ @from = from
28
+ @reason = reason
29
+ super("#{prefix}#{from}: #{reason}")
30
+ end
31
+ end
32
+
33
+ class HTTP < Base
34
+ attr_reader :type, :reason
35
+
36
+ def parse_body!
37
+ if @body
38
+ hash = MultiJson.load(@body)
39
+ if hash["errors"]
40
+ @type = :invalid_arguments
41
+ @reason = hash["errors"].values.join(" ")
42
+ else
43
+ @type = hash["error"]
44
+ @reason = hash["reason"]
45
+ end
46
+ end
47
+ rescue MultiJson::DecodeError
48
+ @type = @reason = nil
49
+ end
50
+
51
+ def to_s
52
+ str = super
53
+ if @type || @reason
54
+ str.sub(/ \(/, ": #{[@type, @reason].compact.join(": ")} (")
55
+ else
56
+ str
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # This class implements Couchbase View execution
63
+ #
64
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views.html
65
+ class View
66
+
67
+ include Enumerable
68
+ include Constants
69
+
70
+ class ArrayWithTotalRows < Array # :nodoc:
71
+ attr_accessor :total_rows
72
+ alias total_entries total_rows
73
+ end
74
+
75
+ attr_reader :params, :design_doc, :name
76
+
77
+ # Set up view endpoint and optional params
78
+ #
79
+ # @param [Couchbase::Bucket] bucket Connection object which
80
+ # stores all info about how to make requests to Couchbase views.
81
+ #
82
+ # @param [String] endpoint Full Couchbase View URI.
83
+ #
84
+ # @param [Hash] params Optional parameter which will be passed to
85
+ # {View#fetch}
86
+ #
87
+ def initialize(bucket, endpoint, params = {})
88
+ @bucket = bucket
89
+ @endpoint = endpoint
90
+ @design_doc, @name = parse_endpoint(endpoint)
91
+ @params = { :connection_timeout => 75_000 }.merge(params)
92
+ @wrapper_class = params.delete(:wrapper_class) || ViewRow
93
+ unless @wrapper_class.respond_to?(:wrap)
94
+ raise ArgumentError, "wrapper class should reposond to :wrap, check the options"
95
+ end
96
+ end
97
+
98
+ # Yields each document that was fetched by view. It doesn't instantiate
99
+ # all the results because of streaming JSON parser. Returns Enumerator
100
+ # unless block given.
101
+ #
102
+ # @param [Hash] params Params for Couchdb query. Some useful are:
103
+ # :start_key, :start_key_doc_id, :descending. See {View#fetch}.
104
+ #
105
+ # @example Use each method with block
106
+ #
107
+ # view.each do |doc|
108
+ # # do something with doc
109
+ # end
110
+ #
111
+ # @example Use Enumerator version
112
+ #
113
+ # enum = view.each # request hasn't issued yet
114
+ # enum.map{|doc| doc.title.upcase}
115
+ #
116
+ # @example Pass options during view initialization
117
+ #
118
+ # endpoint = "http://localhost:5984/default/_design/blog/_view/recent"
119
+ # view = View.new(conn, endpoint, :descending => true)
120
+ # view.each do |document|
121
+ # # do something with document
122
+ # end
123
+ #
124
+ def each(params = {})
125
+ return enum_for(:each, params) unless block_given?
126
+ fetch(params) { |doc| yield(doc) }
127
+ end
128
+
129
+ def first(params = {})
130
+ params = params.merge(:limit => 1)
131
+ fetch(params).first
132
+ end
133
+
134
+ def take(n, params = {})
135
+ params = params.merge(:limit => n)
136
+ fetch(params)
137
+ end
138
+
139
+ # Registers callback function for handling error objects in view
140
+ # results stream.
141
+ #
142
+ # @yieldparam [String] from Location of the node where error occured
143
+ # @yieldparam [String] reason The reason message describing what
144
+ # happened.
145
+ #
146
+ # @example Using +#on_error+ to log all errors in view result
147
+ #
148
+ # # JSON-encoded view result
149
+ # #
150
+ # # {
151
+ # # "total_rows": 0,
152
+ # # "rows": [ ],
153
+ # # "errors": [
154
+ # # {
155
+ # # "from": "127.0.0.1:5984",
156
+ # # "reason": "Design document `_design/testfoobar` missing in database `test_db_b`."
157
+ # # },
158
+ # # {
159
+ # # "from": "http:// localhost:5984/_view_merge/",
160
+ # # "reason": "Design document `_design/testfoobar` missing in database `test_db_c`."
161
+ # # }
162
+ # # ]
163
+ # # }
164
+ #
165
+ # view.on_error do |from, reason|
166
+ # logger.warn("#{view.inspect} received the error '#{reason}' from #{from}")
167
+ # end
168
+ # docs = view.fetch
169
+ #
170
+ # @example More concise example to just count errors
171
+ #
172
+ # errcount = 0
173
+ # view.on_error{|f,r| errcount += 1}.fetch
174
+ #
175
+ def on_error(&callback)
176
+ @on_error = callback
177
+ self # enable call chains
178
+ end
179
+
180
+ # Performs query to Couchbase view. This method will stream results if block
181
+ # given or return complete result set otherwise. In latter case it defines
182
+ # method +total_rows+ returning corresponding entry from
183
+ # Couchbase result object.
184
+ #
185
+ # @note Avoid using +$+ symbol as prefix for properties in your
186
+ # documents, because server marks with it meta fields like flags and
187
+ # expiration, therefore dollar prefix is some kind of reserved. It
188
+ # won't hurt your application. Currently the {ViewRow}
189
+ # class extracts +$flags+, +$cas+ and +$expiration+ properties from
190
+ # the document and store them in {ViewRow#meta} hash.
191
+ #
192
+ # @param [Hash] params parameters for Couchbase query.
193
+ # @option params [true, false] :include_docs (false) Include the
194
+ # full content of the documents in the return. Note that the document
195
+ # is fetched from the in memory cache where it may have been changed
196
+ # or even deleted. See also +:quiet+ parameter below to control error
197
+ # reporting during fetch.
198
+ # @option params [true, false] :quiet (true) Do not raise error if
199
+ # associated document not found in the memory. If the parameter +true+
200
+ # will use +nil+ value instead.
201
+ # @option params [true, false] :descending (false) Return the documents
202
+ # in descending by key order
203
+ # @option params [String, Fixnum, Hash, Array] :key Return only
204
+ # documents that match the specified key. Will be JSON encoded.
205
+ # @option params [Array] :keys The same as +:key+, but will work for
206
+ # set of keys. Will be JSON encoded.
207
+ # @option params [String, Fixnum, Hash, Array] :startkey Return
208
+ # records starting with the specified key. +:start_key+ option should
209
+ # also work here. Will be JSON encoded.
210
+ # @option params [String] :startkey_docid Document id to start with
211
+ # (to allow pagination for duplicate startkeys). +:start_key_doc_id+
212
+ # also should work.
213
+ # @option params [String, Fixnum, Hash, Array] :endkey Stop returning
214
+ # records when the specified key is reached. +:end_key+ option should
215
+ # also work here. Will be JSON encoded.
216
+ # @option params [String] :endkey_docid Last document id to include
217
+ # in the output (to allow pagination for duplicate startkeys).
218
+ # +:end_key_doc_id+ also should work.
219
+ # @option params [true, false] :inclusive_end (true) Specifies whether
220
+ # the specified end key should be included in the result
221
+ # @option params [Fixnum] :limit Limit the number of documents in the
222
+ # output.
223
+ # @option params [Fixnum] :skip Skip this number of records before
224
+ # starting to return the results.
225
+ # @option params [String, Symbol] :on_error (:continue) Sets the
226
+ # response in the event of an error. Supported values:
227
+ # :continue:: Continue to generate view information in the event of an
228
+ # error, including the error information in the view
229
+ # response stream.
230
+ # :stop:: Stop immediately when an error condition occurs. No
231
+ # further view information will be returned.
232
+ # @option params [Fixnum] :connection_timeout (75000) Timeout before the
233
+ # view request is dropped (milliseconds)
234
+ # @option params [true, false] :reduce (true) Use the reduction function
235
+ # @option params [true, false] :group (false) Group the results using
236
+ # the reduce function to a group or single row.
237
+ # @option params [Fixnum] :group_level Specify the group level to be
238
+ # used.
239
+ # @option params [String, Symbol, false] :stale (:update_after) Allow
240
+ # the results from a stale view to be used. Supported values:
241
+ # false:: Force a view update before returning data
242
+ # :ok:: Allow stale views
243
+ # :update_after:: Allow stale view, update view after it has been
244
+ # accessed
245
+ # @option params [Hash] :body Accepts the same parameters, except
246
+ # +:body+ of course, but sends them in POST body instead of query
247
+ # string. It could be useful for really large and complex parameters.
248
+ #
249
+ # @yieldparam [Couchbase::ViewRow] document
250
+ #
251
+ # @return [Array] with documents. There will be +total_entries+
252
+ # method defined on this array if it's possible.
253
+ #
254
+ # @raise [Couchbase::Error::View] when +on_error+ callback is nil and
255
+ # error object found in the result stream.
256
+ #
257
+ # @example Query +recent_posts+ view with key filter
258
+ # doc.recent_posts(:body => {:keys => ["key1", "key2"]})
259
+ #
260
+ # @example Fetch second page of result set (splitted in 10 items per page)
261
+ # page = 2
262
+ # per_page = 10
263
+ # doc.recent_posts(:skip => (page - 1) * per_page, :limit => per_page)
264
+ #
265
+ # @example Simple join using Map/Reduce
266
+ # # Given the bucket with Posts(:id, :type, :title, :body) and
267
+ # # Comments(:id, :type, :post_id, :author, :body). The map function
268
+ # # below (in javascript) will build the View index called
269
+ # # "recent_posts_with_comments" which will behave like left inner join.
270
+ # #
271
+ # # function(doc) {
272
+ # # switch (doc.type) {
273
+ # # case "Post":
274
+ # # emit([doc.id, 0], null);
275
+ # # break;
276
+ # # case "Comment":
277
+ # # emit([doc.post_id, 1], null);
278
+ # # break;
279
+ # # }
280
+ # # }
281
+ # #
282
+ # post_id = 42
283
+ # doc.recent_posts_with_comments(:start_key => [post_id, 0],
284
+ # :end_key => [post_id, 1],
285
+ # :include_docs => true)
286
+ def fetch(params = {})
287
+ params = @params.merge(params)
288
+ include_docs = params[:include_docs]
289
+ quiet = params.fetch(:quiet, true)
290
+
291
+ view = @bucket.client.getView(@design_doc, @name)
292
+
293
+ query = Query.new(params)
294
+
295
+ request = @bucket.client.query(view, query.generate)
296
+
297
+ if block_given?
298
+ block = Proc.new
299
+ request.each do |data|
300
+ doc = @wrapper_class.wrap(@bucket, data)
301
+ block.call(doc)
302
+ end
303
+ nil
304
+ else
305
+ docs = request.to_a.map { |data|
306
+ @wrapper_class.wrap(@bucket, data)
307
+ }
308
+ docs = ArrayWithTotalRows.new(docs)
309
+ docs.total_rows = request.size
310
+ docs
311
+ end
312
+ end
313
+
314
+ # Method for fetching asynchronously all rows and passing array to callback
315
+ #
316
+ # Parameters are same as for {View#fetch} method, but callback is called for whole set for
317
+ # rows instead of one by each.
318
+ #
319
+ # @example
320
+ # con.run do
321
+ # doc.recent_posts.fetch_all do |posts|
322
+ # do_something_with_all_posts(posts)
323
+ # end
324
+ # end
325
+ def fetch_all(params = {}, &block)
326
+ return fetch(params) unless @bucket.async?
327
+ raise ArgumentError, "Block needed for fetch_all in async mode" unless block
328
+
329
+ all = []
330
+ fetch(params) do |row|
331
+ all << row
332
+ if row.last?
333
+ @bucket.create_timer(0) { block.call(all) }
334
+ end
335
+ end
336
+ end
337
+
338
+
339
+ # Returns a string containing a human-readable representation of the {View}
340
+ #
341
+ # @return [String]
342
+ def inspect
343
+ %(#<#{self.class.name}:#{self.object_id} @endpoint=#{@endpoint.inspect} @params=#{@params.inspect}>)
344
+ end
345
+
346
+ private
347
+
348
+ def send_error(*args)
349
+ if @on_error
350
+ @on_error.call(*args.take(2))
351
+ else
352
+ raise Error::View.new(*args)
353
+ end
354
+ end
355
+
356
+ private
357
+
358
+ def parse_endpoint(endpoint)
359
+ parts = endpoint.split('/')
360
+ if endpoint =~ /^_design/
361
+ [parts[1], parts[3]]
362
+ else
363
+ [parts[0], parts[2]]
364
+ end
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,193 @@
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
+ module Couchbase
19
+ # This class encapsulates structured JSON document
20
+ #
21
+ # @since 1.2.0
22
+ #
23
+ # It behaves like Hash for document included into row, and has access methods to row data as well.
24
+ #
25
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-datastore.html
26
+ class ViewRow
27
+
28
+ java_import com.couchbase.client.protocol.views.ViewRowNoDocs
29
+ java_import com.couchbase.client.protocol.views.ViewRowWithDocs
30
+ java_import com.couchbase.client.protocol.views.ViewRowReduced
31
+ java_import com.couchbase.client.protocol.views.SpatialViewRowNoDocs
32
+ java_import com.couchbase.client.protocol.views.SpatialViewRowWithDocs
33
+
34
+ include Constants
35
+
36
+ # Undefine as much methods as we can to free names for views
37
+ instance_methods.each do |m|
38
+ undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$|^class$|)/
39
+ end
40
+
41
+ # The hash built from JSON document.
42
+ #
43
+ # @since 1.2.0
44
+ #
45
+ # This is complete response from the Couchbase
46
+ #
47
+ # @return [Hash]
48
+ attr_accessor :data
49
+
50
+ # The key which was emitted by map function
51
+ #
52
+ # @since 1.2.0
53
+ #
54
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing-map.html
55
+ #
56
+ # Usually it is String (the object +_id+) but it could be also any
57
+ # compount JSON value.
58
+ #
59
+ # @return [Object]
60
+ attr_accessor :key
61
+
62
+ # The value which was emitted by map function
63
+ #
64
+ # @since 1.2.0
65
+ #
66
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing-map.html
67
+ #
68
+ # @return [Object]
69
+ attr_accessor :value
70
+
71
+ # The document hash.
72
+ #
73
+ # @since 1.2.0
74
+ #
75
+ # It usually available when view executed with +:include_doc+ argument.
76
+ #
77
+ # @return [Hash]
78
+ attr_accessor :doc
79
+
80
+ # The identificator of the document
81
+ #
82
+ # @since 1.2.0
83
+ #
84
+ # @return [String]
85
+ attr_accessor :id
86
+
87
+ # The meta data linked to the document
88
+ #
89
+ # @since 1.2.0
90
+ #
91
+ # @return [Hash]
92
+ attr_accessor :meta
93
+
94
+ # Initialize the document instance
95
+ #
96
+ # @since 1.2.0
97
+ #
98
+ # It takes reference to the bucket, data hash.
99
+ #
100
+ # @param [Couchbase::Bucket] bucket the reference to connection
101
+ # @param [Hash] data the data hash, which was built from JSON document
102
+ # representation
103
+ def initialize(bucket, data)
104
+ @bucket = bucket
105
+ @data = data
106
+ @key = data.key
107
+ @value = data.value
108
+ @id = data.id
109
+ @last = false
110
+
111
+ case data
112
+ when ViewRowWithDocs, SpatialViewRowWithDocs
113
+ @doc = data.document
114
+ when SpatialViewRowNoDocs, SpatialViewRowWithDocs
115
+ @geometry = data.geometry
116
+ @bbox = data.bbox
117
+ end
118
+ end
119
+
120
+ # Wraps data hash into ViewRow instance
121
+ #
122
+ # @since 1.2.0
123
+ #
124
+ # @see ViewRow#initialize
125
+ #
126
+ # @param [Couchbase::Bucket] bucket the reference to connection
127
+ # @param [Hash] data the data hash, which was built from JSON document
128
+ # representation
129
+ #
130
+ # @return [ViewRow]
131
+ def self.wrap(bucket, data)
132
+ self.new(bucket, data)
133
+ end
134
+
135
+ # Get attribute of the document
136
+ #
137
+ # @since 1.2.0
138
+ #
139
+ # Fetches attribute from underlying document hash
140
+ #
141
+ # @param [String] key the attribute name
142
+ #
143
+ # @return [Object] property value or nil
144
+ def [](key)
145
+ @doc[key]
146
+ end
147
+
148
+ # Check attribute existence
149
+ #
150
+ # @since 1.2.0
151
+ #
152
+ # @param [String] key the attribute name
153
+ #
154
+ # @return [true, false] +true+ if the given attribute is present in in
155
+ # the document.
156
+ def has_key?(key)
157
+ @doc.has_key?(key)
158
+ end
159
+
160
+ # Set document attribute
161
+ #
162
+ # @since 1.2.0
163
+ #
164
+ # Set or update the attribute in the document hash
165
+ #
166
+ # @param [String] key the attribute name
167
+ # @param [Object] value the attribute value
168
+ #
169
+ # @return [Object] the value
170
+ def []=(key, value)
171
+ @doc[key] = value
172
+ end
173
+
174
+ # Signals if this row is last in a stream
175
+ #
176
+ # @since 1.2.1
177
+ #
178
+ # @return [true, false] +true+ if this row is last in a stream
179
+ def last?
180
+ @last
181
+ end
182
+
183
+ def inspect
184
+ desc = "#<#{self.class.name}:#{self.object_id}"
185
+ [:@id, :@key, :@value, :@doc, :@meta].each do |iv|
186
+ desc << " #{iv}=#{instance_variable_get(iv).inspect}"
187
+ end
188
+ desc << ">"
189
+ desc
190
+ end
191
+ end
192
+
193
+ end