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

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.
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,26 @@
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
+ class Result
20
+ def initialize(attrs = {})
21
+ attrs.each do |k, v|
22
+ instance_variable_set("@#{k}", v) if respond_to?(k)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,59 @@
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
+
20
+ class Utils
21
+
22
+ def self.build_query(uri, params = nil)
23
+ uri = uri.dup
24
+ return uri if params.nil? || params.empty?
25
+ uri << "?"
26
+ uri << params.map do |k, v|
27
+ next if !v && k.to_s == "group"
28
+ if %w{key keys startkey endkey start_key end_key}.include?(k.to_s)
29
+ v = MultiJson.dump(v)
30
+ end
31
+ if v.class == Array
32
+ build_query(v.map { |x| [k, x] })
33
+ else
34
+ "#{escape(k)}=#{escape(v)}"
35
+ end
36
+ end.compact.join("&")
37
+ end
38
+
39
+ def self.escape(s)
40
+ s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/nu) {
41
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
42
+ }.tr(' ', '+')
43
+ end
44
+
45
+ # Return the bytesize of String; uses String#size under Ruby 1.8 and
46
+ # String#bytesize under 1.9.
47
+ if ''.respond_to?(:bytesize)
48
+ def self.bytesize(string)
49
+ string.bytesize
50
+ end
51
+ else
52
+ def self.bytesize(string)
53
+ string.size
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -17,5 +17,5 @@
17
17
 
18
18
  # Couchbase ruby client
19
19
  module Couchbase
20
- VERSION = "1.1.5"
20
+ VERSION = "1.2.0.beta"
21
21
  end
@@ -0,0 +1,305 @@
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
+ module Error
21
+ class View < Base
22
+ attr_reader :from, :reason
23
+
24
+ def initialize(from, reason, prefix = "SERVER: ")
25
+ @from = from
26
+ @reason = reason
27
+ super("#{prefix}#{from}: #{reason}")
28
+ end
29
+ end
30
+ end
31
+
32
+ # This class implements Couchbase View execution
33
+ #
34
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views.html
35
+ class View
36
+ include Enumerable
37
+
38
+ attr_reader :params
39
+
40
+ # Set up view endpoint and optional params
41
+ #
42
+ # @param [Couchbase::Bucket] bucket Connection object which
43
+ # stores all info about how to make requests to Couchbase views.
44
+ #
45
+ # @param [String] endpoint Full Couchbase View URI.
46
+ #
47
+ # @param [Hash] params Optional parameter which will be passed to
48
+ # {View#fetch}
49
+ #
50
+ def initialize(bucket, endpoint, params = {})
51
+ @bucket = bucket
52
+ @endpoint = endpoint
53
+ @params = params
54
+ @wrapper_class = params.delete(:wrapper_class) || ViewRow
55
+ unless @wrapper_class.respond_to?(:wrap)
56
+ raise ArgumentError, "wrapper class should reposond to :wrap, check the options"
57
+ end
58
+ end
59
+
60
+ # Yields each document that was fetched by view. It doesn't instantiate
61
+ # all the results because of streaming JSON parser. Returns Enumerator
62
+ # unless block given.
63
+ #
64
+ # @param [Hash] params Params for Couchdb query. Some useful are:
65
+ # :start_key, :start_key_doc_id, :descending. See {View#fetch}.
66
+ #
67
+ # @example Use each method with block
68
+ #
69
+ # view.each do |doc|
70
+ # # do something with doc
71
+ # end
72
+ #
73
+ # @example Use Enumerator version
74
+ #
75
+ # enum = view.each # request hasn't issued yet
76
+ # enum.map{|doc| doc.title.upcase}
77
+ #
78
+ # @example Pass options during view initialization
79
+ #
80
+ # endpoint = "http://localhost:5984/default/_design/blog/_view/recent"
81
+ # view = View.new(conn, endpoint, :descending => true)
82
+ # view.each do |document|
83
+ # # do something with document
84
+ # end
85
+ #
86
+ def each(params = {})
87
+ return enum_for(:each, params) unless block_given?
88
+ fetch(params) {|doc| yield(doc)}
89
+ end
90
+
91
+ # Registers callback function for handling error objects in view
92
+ # results stream.
93
+ #
94
+ # @yieldparam [String] from Location of the node where error occured
95
+ # @yieldparam [String] reason The reason message describing what
96
+ # happened.
97
+ #
98
+ # @example Using +#on_error+ to log all errors in view result
99
+ #
100
+ # # JSON-encoded view result
101
+ # #
102
+ # # {
103
+ # # "total_rows": 0,
104
+ # # "rows": [ ],
105
+ # # "errors": [
106
+ # # {
107
+ # # "from": "127.0.0.1:5984",
108
+ # # "reason": "Design document `_design/testfoobar` missing in database `test_db_b`."
109
+ # # },
110
+ # # {
111
+ # # "from": "http:// localhost:5984/_view_merge/",
112
+ # # "reason": "Design document `_design/testfoobar` missing in database `test_db_c`."
113
+ # # }
114
+ # # ]
115
+ # # }
116
+ #
117
+ # view.on_error do |from, reason|
118
+ # logger.warn("#{view.inspect} received the error '#{reason}' from #{from}")
119
+ # end
120
+ # docs = view.fetch
121
+ #
122
+ # @example More concise example to just count errors
123
+ #
124
+ # errcount = 0
125
+ # view.on_error{|f,r| errcount += 1}.fetch
126
+ #
127
+ def on_error(&callback)
128
+ @on_error = callback
129
+ self # enable call chains
130
+ end
131
+
132
+ # Performs query to Couchbase view. This method will stream results if block
133
+ # given or return complete result set otherwise. In latter case it defines
134
+ # method +total_rows+ returning corresponding entry from
135
+ # Couchbase result object.
136
+ #
137
+ # @note Avoid using +$+ symbol as prefix for properties in your
138
+ # documents, because server marks with it meta fields like flags and
139
+ # expiration, therefore dollar prefix is some kind of reserved. It
140
+ # won't hurt your application. Currently the {ViewRow}
141
+ # class extracts +$flags+, +$cas+ and +$expiration+ properties from
142
+ # the document and store them in {ViewRow#meta} hash.
143
+ #
144
+ # @param [Hash] params parameters for Couchbase query.
145
+ # @option params [true, false] :include_docs (false) Include the full
146
+ # content of the documents in the return.
147
+ # @option params [true, false] :descending (false) Return the documents
148
+ # in descending by key order
149
+ # @option params [String, Fixnum, Hash, Array] :key Return only
150
+ # documents that match the specified key. Will be JSON encoded.
151
+ # @option params [Array] :keys The same as +:key+, but will work for
152
+ # set of keys. Will be JSON encoded.
153
+ # @option params [String, Fixnum, Hash, Array] :startkey Return
154
+ # records starting with the specified key. +:start_key+ option should
155
+ # also work here. Will be JSON encoded.
156
+ # @option params [String] :startkey_docid Document id to start with
157
+ # (to allow pagination for duplicate startkeys). +:start_key_doc_id+
158
+ # also should work.
159
+ # @option params [String, Fixnum, Hash, Array] :endkey Stop returning
160
+ # records when the specified key is reached. +:end_key+ option should
161
+ # also work here. Will be JSON encoded.
162
+ # @option params [String] :endkey_docid Last document id to include
163
+ # in the output (to allow pagination for duplicate startkeys).
164
+ # +:end_key_doc_id+ also should work.
165
+ # @option params [true, false] :inclusive_end (true) Specifies whether
166
+ # the specified end key should be included in the result
167
+ # @option params [Fixnum] :limit Limit the number of documents in the
168
+ # output.
169
+ # @option params [Fixnum] :skip Skip this number of records before
170
+ # starting to return the results.
171
+ # @option params [String, Symbol] :on_error (:continue) Sets the
172
+ # response in the event of an error. Supported values:
173
+ # :continue:: Continue to generate view information in the event of an
174
+ # error, including the error information in the view
175
+ # response stream.
176
+ # :stop:: Stop immediately when an error condition occurs. No
177
+ # further view information will be returned.
178
+ # @option params [Fixnum] :connection_timeout (60000) Timeout before the
179
+ # view request is dropped (milliseconds)
180
+ # @option params [true, false] :reduce (true) Use the reduction function
181
+ # @option params [true, false] :group (false) Group the results using
182
+ # the reduce function to a group or single row.
183
+ # @option params [Fixnum] :group_level Specify the group level to be
184
+ # used.
185
+ # @option params [String, Symbol, false] :stale (:update_after) Allow
186
+ # the results from a stale view to be used. Supported values:
187
+ # false:: Force a view update before returning data
188
+ # :ok:: Allow stale views
189
+ # :update_after:: Allow stale view, update view after it has been
190
+ # accessed
191
+ # @option params [Hash] :body Accepts the same parameters, except
192
+ # +:body+ of course, but sends them in POST body instead of query
193
+ # string. It could be useful for really large and complex parameters.
194
+ #
195
+ # @yieldparam [Couchbase::ViewRow] document
196
+ #
197
+ # @return [Array] with documents. There will be +total_entries+
198
+ # method defined on this array if it's possible.
199
+ #
200
+ # @raise [Couchbase::Error::View] when +on_error+ callback is nil and
201
+ # error object found in the result stream.
202
+ #
203
+ # @example Query +recent_posts+ view with key filter
204
+ # doc.recent_posts(:body => {:keys => ["key1", "key2"]})
205
+ #
206
+ # @example Fetch second page of result set (splitted in 10 items per page)
207
+ # page = 2
208
+ # per_page = 10
209
+ # doc.recent_posts(:skip => (page - 1) * per_page, :limit => per_page)
210
+ #
211
+ # @example Simple join using Map/Reduce
212
+ # # Given the bucket with Posts(:id, :type, :title, :body) and
213
+ # # Comments(:id, :type, :post_id, :author, :body). The map function
214
+ # # below (in javascript) will build the View index called
215
+ # # "recent_posts_with_comments" which will behave like left inner join.
216
+ # #
217
+ # # function(doc) {
218
+ # # switch (doc.type) {
219
+ # # case "Post":
220
+ # # emit([doc.id, 0], null);
221
+ # # break;
222
+ # # case "Comment":
223
+ # # emit([doc.post_id, 1], null);
224
+ # # break;
225
+ # # }
226
+ # # }
227
+ # #
228
+ # post_id = 42
229
+ # doc.recent_posts_with_comments(:start_key => [post_id, 0],
230
+ # :end_key => [post_id, 1],
231
+ # :include_docs => true)
232
+ def fetch(params = {})
233
+ params = @params.merge(params)
234
+ options = {:chunked => true, :extended => true, :type => :view}
235
+ if body = params.delete(:body)
236
+ body = MultiJson.dump(body) unless body.is_a?(String)
237
+ options.update(:body => body, :method => params.delete(:method) || :post)
238
+ end
239
+ path = Utils.build_query(@endpoint, params)
240
+ request = @bucket.make_http_request(path, options)
241
+ res = []
242
+ request.on_body do |chunk|
243
+ res << chunk
244
+ request.pause if chunk.value.nil? || chunk.error
245
+ end
246
+ filter = ["/rows/", "/errors/"]
247
+ filter << "/total_rows" unless block_given?
248
+ parser = YAJI::Parser.new(:filter => filter, :with_path => true)
249
+ docs = []
250
+ parser.on_object do |path, obj|
251
+ case path
252
+ when "/total_rows"
253
+ # if total_rows key present, save it and take next object
254
+ docs.instance_eval("def total_rows; #{obj}; end")
255
+ when "/errors/"
256
+ from, reason = obj["from"], obj["reason"]
257
+ if @on_error
258
+ @on_error.call(from, reason)
259
+ else
260
+ raise Error::View.new(from, reason)
261
+ end
262
+ else
263
+ if block_given?
264
+ yield @wrapper_class.wrap(@bucket, obj)
265
+ else
266
+ docs << @wrapper_class.wrap(@bucket, obj)
267
+ end
268
+ end
269
+ end
270
+ # run event loop until the terminating chunk will be found
271
+ # last_res variable keeps latest known chunk of the result
272
+ last_res = nil
273
+ loop do
274
+ # feed response received chunks to the parser
275
+ while r = res.shift
276
+ if r.error
277
+ if @on_error
278
+ @on_error.call("http_error", r.error)
279
+ break
280
+ else
281
+ raise Error::View.new("http_error", r.error, nil)
282
+ end
283
+ end
284
+ last_res = r
285
+ parser << r.value
286
+ end
287
+ if last_res.nil? || !last_res.completed? # shall we run the event loop?
288
+ request.continue
289
+ else
290
+ break
291
+ end
292
+ end
293
+ # return nil for call with block
294
+ block_given? ? nil : docs
295
+ end
296
+
297
+
298
+ # Returns a string containing a human-readable representation of the {View}
299
+ #
300
+ # @return [String]
301
+ def inspect
302
+ %(#<#{self.class.name}:#{self.object_id} @endpoint=#{@endpoint.inspect} @params=#{@params.inspect}>)
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,230 @@
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, but also defines special methods for each view if
24
+ # the documnent considered as Design document.
25
+ #
26
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-datastore.html
27
+ class ViewRow
28
+
29
+ # Undefine as much methods as we can to free names for views
30
+ instance_methods.each do |m|
31
+ undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$|^class$|)/
32
+ end
33
+
34
+ # The hash built from JSON document.
35
+ #
36
+ # @since 1.2.0
37
+ #
38
+ # This is complete response from the Couchbase
39
+ #
40
+ # @return [Hash]
41
+ attr_accessor :data
42
+
43
+ # The key which was emitted by map function
44
+ #
45
+ # @since 1.2.0
46
+ #
47
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing-map.html
48
+ #
49
+ # Usually it is String (the object +_id+) but it could be also any
50
+ # compount JSON value.
51
+ #
52
+ # @return [Object]
53
+ attr_accessor :key
54
+
55
+ # The value which was emitted by map function
56
+ #
57
+ # @since 1.2.0
58
+ #
59
+ # @see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing-map.html
60
+ #
61
+ # @return [Object]
62
+ attr_accessor :value
63
+
64
+ # The document hash.
65
+ #
66
+ # @since 1.2.0
67
+ #
68
+ # It usually available when view executed with +:include_doc+ argument.
69
+ #
70
+ # @return [Hash]
71
+ attr_accessor :doc
72
+
73
+ # The identificator of the document
74
+ #
75
+ # @since 1.2.0
76
+ #
77
+ # @return [String]
78
+ attr_accessor :id
79
+
80
+ # The meta data linked to the document
81
+ #
82
+ # @since 1.2.0
83
+ #
84
+ # @return [Hash]
85
+ attr_accessor :meta
86
+
87
+ # The list of views defined or empty array
88
+ #
89
+ # @since 1.2.0
90
+ #
91
+ # @return [Array<View>]
92
+ attr_accessor :views
93
+
94
+ # The list of spatial views defined or empty array
95
+ #
96
+ # @since 1.2.0
97
+ #
98
+ # @return [Array<View>]
99
+ attr_accessor :spatial
100
+
101
+ # Initialize the document instance
102
+ #
103
+ # @since 1.2.0
104
+ #
105
+ # It takes reference to the bucket, data hash. It will define view
106
+ # methods if the data object looks like design document.
107
+ #
108
+ # @param [Couchbase::Bucket] bucket the reference to connection
109
+ # @param [Hash] data the data hash, which was built from JSON document
110
+ # representation
111
+ def initialize(bucket, data)
112
+ @bucket = bucket
113
+ @data = data
114
+ @key = data['key']
115
+ @value = data['value']
116
+ if data['doc']
117
+ @meta = data['doc']['meta']
118
+ @doc = data['doc']['json']
119
+ end
120
+ @id = data['id'] || @meta && @meta['id']
121
+ @views = []
122
+ @spatial = []
123
+ if design_doc?
124
+ if @doc.has_key?('views')
125
+ @doc['views'].each do |name, _|
126
+ @views << name
127
+ self.instance_eval <<-EOV, __FILE__, __LINE__ + 1
128
+ def #{name}(params = {})
129
+ View.new(@bucket, "\#{@id}/_view/#{name}", params)
130
+ end
131
+ EOV
132
+ end
133
+ end
134
+ if @doc.has_key?('spatial')
135
+ @doc['spatial'].each do |name, _|
136
+ @spatial << name
137
+ self.instance_eval <<-EOV, __FILE__, __LINE__ + 1
138
+ def #{name}(params = {})
139
+ View.new(@bucket, "\#{@id}/_spatial/#{name}", params)
140
+ end
141
+ EOV
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ # Wraps data hash into ViewRow instance
148
+ #
149
+ # @since 1.2.0
150
+ #
151
+ # @see ViewRow#initialize
152
+ #
153
+ # @param [Couchbase::Bucket] bucket the reference to connection
154
+ # @param [Hash] data the data hash, which was built from JSON document
155
+ # representation
156
+ #
157
+ # @return [ViewRow]
158
+ def self.wrap(bucket, data)
159
+ ViewRow.new(bucket, data)
160
+ end
161
+
162
+ # Get attribute of the document
163
+ #
164
+ # @since 1.2.0
165
+ #
166
+ # Fetches attribute from underlying document hash
167
+ #
168
+ # @param [String] key the attribute name
169
+ #
170
+ # @return [Object] property value or nil
171
+ def [](key)
172
+ @doc[key]
173
+ end
174
+
175
+ # Check attribute existence
176
+ #
177
+ # @since 1.2.0
178
+ #
179
+ # @param [String] key the attribute name
180
+ #
181
+ # @return [true, false] +true+ if the given attribute is present in in
182
+ # the document.
183
+ def has_key?(key)
184
+ @doc.has_key?(key)
185
+ end
186
+
187
+ # Set document attribute
188
+ #
189
+ # @since 1.2.0
190
+ #
191
+ # Set or update the attribute in the document hash
192
+ #
193
+ # @param [String] key the attribute name
194
+ # @param [Object] value the attribute value
195
+ #
196
+ # @return [Object] the value
197
+ def []=(key, value)
198
+ @doc[key] = value
199
+ end
200
+
201
+ # Check if the document is design
202
+ #
203
+ # @since 1.2.0
204
+ #
205
+ # @return [true, false]
206
+ def design_doc?
207
+ !!(@doc && @id =~ %r(_design/))
208
+ end
209
+
210
+ # Check if the document has views defines
211
+ #
212
+ # @since 1.2.0
213
+ #
214
+ # @see ViewRow#views
215
+ #
216
+ # @return [true, false] +true+ if the document have views
217
+ def has_views?
218
+ !!(design_doc? && !@views.empty?)
219
+ end
220
+
221
+ def inspect
222
+ desc = "#<#{self.class.name}:#{self.object_id} "
223
+ desc << [:@id, :@key, :@value, :@doc, :@meta, :@views].map do |iv|
224
+ "#{iv}=#{instance_variable_get(iv).inspect}"
225
+ end.join(' ')
226
+ desc << ">"
227
+ desc
228
+ end
229
+ end
230
+ end