couchbase-model 0.0.1
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 +5 -0
- data/.travis.yml +11 -0
- data/.yardopts +5 -0
- data/Gemfile +4 -0
- data/README.markdown +51 -0
- data/Rakefile +22 -0
- data/couchbase-model.gemspec +26 -0
- data/lib/couchbase-model.rb +18 -0
- data/lib/couchbase/model.rb +332 -0
- data/lib/couchbase/model/uuid.rb +103 -0
- data/lib/couchbase/model/version.rb +26 -0
- data/tasks/doc.rake +25 -0
- data/tasks/test.rake +35 -0
- data/tasks/util.rake +21 -0
- data/test/setup.rb +168 -0
- data/test/test_model.rb +114 -0
- data/test/test_uuid.rb +32 -0
- metadata +131 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
before_install:
|
2
|
+
- wget -O- http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add -
|
3
|
+
- echo deb http://packages.couchbase.com/ubuntu lucid lucid/main | sudo tee /etc/apt/sources.list.d/couchbase.list
|
4
|
+
- sudo apt-get update
|
5
|
+
- sudo apt-get -y install libevent-dev libvbucket-dev libcouchbase-dev
|
6
|
+
|
7
|
+
rvm:
|
8
|
+
- 1.8.7
|
9
|
+
- 1.9.2
|
10
|
+
- 1.9.3
|
11
|
+
- ree
|
data/.yardopts
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Couchbase Model [](http://travis-ci.org/avsej/ruby-couchbase-model)
|
2
|
+
|
3
|
+
This library allows to declare models for [couchbase gem][1]. Here are example:
|
4
|
+
|
5
|
+
require 'couchbase/model'
|
6
|
+
|
7
|
+
class Post < Couchbase::Model
|
8
|
+
attribute :title
|
9
|
+
attribute :body
|
10
|
+
attribute :draft
|
11
|
+
end
|
12
|
+
|
13
|
+
p = Post.new(:id => 'hello-world',
|
14
|
+
:title => 'Hello world',
|
15
|
+
:draft => true)
|
16
|
+
p.save
|
17
|
+
p = Post.find('hello-world')
|
18
|
+
p.body = "Once upon the times...."
|
19
|
+
p.save
|
20
|
+
p.update(:draft => false)
|
21
|
+
Post.bucket.get('hello-world') #=> {"title"=>"Hello world", "draft"=>false,
|
22
|
+
# "body"=>"Once upon the times...."}
|
23
|
+
|
24
|
+
You can also let the library generate the unique identifier for you:
|
25
|
+
|
26
|
+
p = Post.create(:title => 'How to generate ID',
|
27
|
+
:body => 'Open up the editor...')
|
28
|
+
p.id #=> "74f43c3116e788d09853226603000809"
|
29
|
+
|
30
|
+
There are several algorithms available. By default it use `:sequential`
|
31
|
+
algorithm, but you can change it to more suitable one for you:
|
32
|
+
|
33
|
+
class Post < Couchbase::Model
|
34
|
+
attribute :title
|
35
|
+
attribute :body
|
36
|
+
attribute :draft
|
37
|
+
|
38
|
+
uuid_algorithm :random
|
39
|
+
end
|
40
|
+
|
41
|
+
You can define connection options on per model basis:
|
42
|
+
|
43
|
+
class Post < Couchbase::Model
|
44
|
+
attribute :title
|
45
|
+
attribute :body
|
46
|
+
attribute :draft
|
47
|
+
|
48
|
+
connect :port => 80, :bucket => 'blog'
|
49
|
+
end
|
50
|
+
|
51
|
+
[1]: https://github.com/couchbase/couchbase-ruby-client/
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'bundler/gem_tasks'
|
19
|
+
|
20
|
+
Dir['tasks/*.rake'].sort.each { |f| load f }
|
21
|
+
|
22
|
+
task :default => :test
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "couchbase/model/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "couchbase-model"
|
7
|
+
s.version = Couchbase::Model::VERSION
|
8
|
+
s.authors = ["Sergey Avseyev"]
|
9
|
+
s.email = ["sergey.avseyev@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Declarative interface to Couchbase}
|
12
|
+
s.description = %q{ORM-like interface allows you to persist your models to Couchbase}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'couchbase', '~> 1.1.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'rake', '~> 0.8.7'
|
22
|
+
s.add_development_dependency 'minitest'
|
23
|
+
s.add_development_dependency 'rdiscount'
|
24
|
+
s.add_development_dependency 'yard'
|
25
|
+
s.add_development_dependency RUBY_VERSION =~ /^1\.9/ ? 'ruby-debug19' : 'ruby-debug'
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'couchbase/model'
|
@@ -0,0 +1,332 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'couchbase'
|
19
|
+
require 'couchbase/model/version'
|
20
|
+
require 'couchbase/model/uuid'
|
21
|
+
|
22
|
+
module Couchbase
|
23
|
+
|
24
|
+
class Error::MissingId < Error::Base; end
|
25
|
+
|
26
|
+
# Declarative layer for Couchbase gem
|
27
|
+
#
|
28
|
+
# require 'couchbase/model'
|
29
|
+
#
|
30
|
+
# class Post < Couchbase::Model
|
31
|
+
# attribute :title
|
32
|
+
# attribute :body
|
33
|
+
# attribute :draft
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# p = Post.new(:id => 'hello-world',
|
37
|
+
# :title => 'Hello world',
|
38
|
+
# :draft => true)
|
39
|
+
# p.save
|
40
|
+
# p = Post.find('hello-world')
|
41
|
+
# p.body = "Once upon the times...."
|
42
|
+
# p.save
|
43
|
+
# p.update(:draft => false)
|
44
|
+
# Post.bucket.get('hello-world') #=> {"title"=>"Hello world", "draft"=>false,
|
45
|
+
# # "body"=>"Once upon the times...."}
|
46
|
+
#
|
47
|
+
# You can also let the library generate the unique identifier for you:
|
48
|
+
#
|
49
|
+
# p = Post.create(:title => 'How to generate ID',
|
50
|
+
# :body => 'Open up the editor...')
|
51
|
+
# p.id #=> "74f43c3116e788d09853226603000809"
|
52
|
+
#
|
53
|
+
# There are several algorithms available. By default it use `:sequential`
|
54
|
+
# algorithm, but you can change it to more suitable one for you:
|
55
|
+
#
|
56
|
+
# class Post < Couchbase::Model
|
57
|
+
# attribute :title
|
58
|
+
# attribute :body
|
59
|
+
# attribute :draft
|
60
|
+
#
|
61
|
+
# uuid_algorithm :random
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# You can define connection options on per model basis:
|
65
|
+
#
|
66
|
+
# class Post < Couchbase::Model
|
67
|
+
# attribute :title
|
68
|
+
# attribute :body
|
69
|
+
# attribute :draft
|
70
|
+
#
|
71
|
+
# connect :port => 80, :bucket => 'blog'
|
72
|
+
# end
|
73
|
+
class Model
|
74
|
+
# Each model must have identifier
|
75
|
+
attr_accessor :id
|
76
|
+
|
77
|
+
# @private Container for all attributes of all subclasses
|
78
|
+
@@attributes = ::Hash.new {|hash, key| hash[key] = []}
|
79
|
+
|
80
|
+
# Use custom connection options
|
81
|
+
#
|
82
|
+
# @param [String, Hash, Array] options for establishing connection.
|
83
|
+
#
|
84
|
+
# @see Couchbase::Bucket#initialize
|
85
|
+
#
|
86
|
+
# @example Choose specific bucket
|
87
|
+
# class Post < Couchbase::Model
|
88
|
+
# connect :bucket => 'posts'
|
89
|
+
# ...
|
90
|
+
# end
|
91
|
+
def self.connect(*options)
|
92
|
+
self.bucket = Couchbase.connect(*options)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Choose the UUID generation algorithms
|
96
|
+
#
|
97
|
+
# @param [Symbol] algorithm (:sequential) one of the available
|
98
|
+
# algorithms.
|
99
|
+
#
|
100
|
+
# @see Couchbase::UUID#next
|
101
|
+
#
|
102
|
+
# @example Select :random UUID generation algorithm
|
103
|
+
# class Post < Couchbase::Model
|
104
|
+
# uuid_algorithm :random
|
105
|
+
# ...
|
106
|
+
# end
|
107
|
+
def self.uuid_algorithm(algorithm)
|
108
|
+
self.thread_storage[:uuid_algorithm] = algorithm
|
109
|
+
end
|
110
|
+
|
111
|
+
# Defines an attribute for the model
|
112
|
+
#
|
113
|
+
# @param [Symbol, String] name name of the attribute
|
114
|
+
#
|
115
|
+
# @example Define some attributes for a model
|
116
|
+
# class Post < Couchbase::Model
|
117
|
+
# attribute :title
|
118
|
+
# attribute :body
|
119
|
+
# attribute :published_at
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# post = Post.new(:title => 'Hello world',
|
123
|
+
# :body => 'This is the first example...',
|
124
|
+
# :published_at => Time.now)
|
125
|
+
def self.attribute(name)
|
126
|
+
define_method(name) do
|
127
|
+
@_attributes[name]
|
128
|
+
end
|
129
|
+
define_method(:"#{name}=") do |value|
|
130
|
+
@_attributes[name] = value
|
131
|
+
end
|
132
|
+
attributes << name unless attributes.include?(name)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Find the model using +id+ attribute
|
136
|
+
#
|
137
|
+
# @param [String, Symbol] id model identificator
|
138
|
+
# @return [Couchbase::Model] an instance of the model
|
139
|
+
#
|
140
|
+
# @example Find model using +id+
|
141
|
+
# post = Post.find('the-id')
|
142
|
+
def self.find(id)
|
143
|
+
if id && (obj = bucket.get(id))
|
144
|
+
new({:id => id}.merge(obj))
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Create the model with given attributes
|
149
|
+
#
|
150
|
+
# @param [Hash] args attribute-value pairs for the object
|
151
|
+
# @return [Couchbase::Model] an instance of the model
|
152
|
+
def self.create(*args)
|
153
|
+
new(*args).create
|
154
|
+
end
|
155
|
+
|
156
|
+
# Constructor for all subclasses of Couchbase::Model, which optionally
|
157
|
+
# takes a Hash of attribute value pairs.
|
158
|
+
#
|
159
|
+
# @param [Hash] attrs attribute-value pairs
|
160
|
+
def initialize(attrs = {})
|
161
|
+
@id = nil
|
162
|
+
@_attributes = ::Hash.new
|
163
|
+
update_attributes(attrs)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Create this model and assign new id if necessary
|
167
|
+
#
|
168
|
+
# @return [Couchbase::Model] newly created object
|
169
|
+
#
|
170
|
+
# @raise [Couchbase::Error::KeyExists] if model with the same +id+
|
171
|
+
# exists in the bucket
|
172
|
+
#
|
173
|
+
# @example Create the instance of the Post model
|
174
|
+
# p = Post.new(:title => 'Hello world', :draft => true)
|
175
|
+
# p.create
|
176
|
+
def create
|
177
|
+
@id ||= Couchbase::Model::UUID.generator.next(1, model.thread_storage[:uuid_algorithm])
|
178
|
+
model.bucket.add(@id, attributes)
|
179
|
+
self
|
180
|
+
end
|
181
|
+
|
182
|
+
# Create or update this object based on the state of #new?.
|
183
|
+
#
|
184
|
+
# @return [Couchbase::Model] The saved object
|
185
|
+
#
|
186
|
+
# @example Update the Post model
|
187
|
+
# p = Post.find('hello-world')
|
188
|
+
# p.draft = false
|
189
|
+
# p.save
|
190
|
+
def save
|
191
|
+
return create if new?
|
192
|
+
model.bucket.set(@id, attributes)
|
193
|
+
self
|
194
|
+
end
|
195
|
+
|
196
|
+
# Update this object, optionally accepting new attributes.
|
197
|
+
#
|
198
|
+
# @param [Hash] attrs Attribute value pairs to use for the updated
|
199
|
+
# version
|
200
|
+
# @return [Couchbase::Model] The updated object
|
201
|
+
def update(attrs)
|
202
|
+
update_attributes(attrs)
|
203
|
+
save
|
204
|
+
end
|
205
|
+
|
206
|
+
# Delete this object from the bucket
|
207
|
+
#
|
208
|
+
# @note This method will reset +id+ attribute
|
209
|
+
#
|
210
|
+
# @return [Couchbase::Model] Returns a reference of itself.
|
211
|
+
#
|
212
|
+
# @example Delete the Post model
|
213
|
+
# p = Post.find('hello-world')
|
214
|
+
# p.delete
|
215
|
+
def delete
|
216
|
+
raise Couchbase::Error::MissingId, "missing id attribute" unless @id
|
217
|
+
model.bucket.delete(@id)
|
218
|
+
@id = nil
|
219
|
+
self
|
220
|
+
end
|
221
|
+
|
222
|
+
# Check if the record have +id+ attribute
|
223
|
+
#
|
224
|
+
# @return [true, false] Whether or not this object has an id.
|
225
|
+
#
|
226
|
+
# @note +true+ doesn't mean that record exists in the database
|
227
|
+
#
|
228
|
+
# @see Couchbase::Model#exists?
|
229
|
+
def new?
|
230
|
+
!@id
|
231
|
+
end
|
232
|
+
|
233
|
+
# Check if the key exists in the bucket
|
234
|
+
#
|
235
|
+
# @param [String, Symbol] id the record identifier
|
236
|
+
# @return [true, false] Whether or not the object with given +id+
|
237
|
+
# presented in the bucket.
|
238
|
+
def self.exists?(id)
|
239
|
+
!!bucket.get(id, :quiet => true)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Check if this model exists in the bucket.
|
243
|
+
#
|
244
|
+
# @return [true, false] Whether or not this object presented in the
|
245
|
+
# bucket.
|
246
|
+
def exists?
|
247
|
+
model.exists?(@id)
|
248
|
+
end
|
249
|
+
|
250
|
+
# All the defined attributes within a class.
|
251
|
+
#
|
252
|
+
# @see Model.attribute
|
253
|
+
def self.attributes
|
254
|
+
@@attributes[self]
|
255
|
+
end
|
256
|
+
|
257
|
+
# All the attributes of the current instance
|
258
|
+
#
|
259
|
+
# @return [Hash]
|
260
|
+
def attributes
|
261
|
+
@_attributes
|
262
|
+
end
|
263
|
+
|
264
|
+
# Update all attributes without persisting the changes.
|
265
|
+
#
|
266
|
+
# @param [Hash] attrs attribute-value pairs.
|
267
|
+
def update_attributes(attrs)
|
268
|
+
if id = attrs.delete(:id)
|
269
|
+
@id = id
|
270
|
+
end
|
271
|
+
attrs.each do |key, value|
|
272
|
+
send(:"#{key}=", value)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Reload all the model attributes from the bucket
|
277
|
+
#
|
278
|
+
# @return [Model] the latest model state
|
279
|
+
#
|
280
|
+
# @raise [Error::MissingId] for records without +id+
|
281
|
+
# attribute
|
282
|
+
def reload
|
283
|
+
raise Couchbase::Error::MissingId, "missing id attribute" unless @id
|
284
|
+
attrs = model.find(@id).attributes
|
285
|
+
update_attributes(attrs)
|
286
|
+
self
|
287
|
+
end
|
288
|
+
|
289
|
+
# @private The thread local storage for model specific stuff
|
290
|
+
def self.thread_storage
|
291
|
+
Couchbase.thread_storage[self] ||= {:uuid_algorithm => :sequential}
|
292
|
+
end
|
293
|
+
|
294
|
+
# @private Fetch the current connection
|
295
|
+
def self.bucket
|
296
|
+
self.thread_storage[:bucket] ||= Couchbase.bucket
|
297
|
+
end
|
298
|
+
|
299
|
+
# @private Set the current connection
|
300
|
+
#
|
301
|
+
# @param [Bucket] connection the connection instance
|
302
|
+
def self.bucket=(connection)
|
303
|
+
self.thread_storage[:bucket] = connection
|
304
|
+
end
|
305
|
+
|
306
|
+
# @private Get model class
|
307
|
+
def model
|
308
|
+
self.class
|
309
|
+
end
|
310
|
+
|
311
|
+
# @private Wrap the hash to the model class
|
312
|
+
#
|
313
|
+
# @param [Model, Hash] the Couchbase::Model subclass or the
|
314
|
+
# attribute-value pairs
|
315
|
+
def self.wrap(object)
|
316
|
+
object.class == self ? object : new(object)
|
317
|
+
end
|
318
|
+
|
319
|
+
# @private Returns a string containing a human-readable representation
|
320
|
+
# of the record.
|
321
|
+
def inspect
|
322
|
+
attrs = model.attributes.sort.map do |attr|
|
323
|
+
[attr, @_attributes[attr].inspect]
|
324
|
+
end
|
325
|
+
sprintf("#<%s:%s %s>",
|
326
|
+
model, new? ? "?" : id,
|
327
|
+
attrs.map{|a| a.join("=")}.join(" "))
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'thread'
|
19
|
+
|
20
|
+
module Couchbase
|
21
|
+
|
22
|
+
class Model
|
23
|
+
|
24
|
+
# Generator of CouchDB specfic UUIDs. This is the ruby implementation of
|
25
|
+
# couch_uuids.erl from couchdb distribution. It is threadsafe.
|
26
|
+
|
27
|
+
class UUID
|
28
|
+
# Get default UUID generator. You can create your own if you like.
|
29
|
+
def self.generator
|
30
|
+
@generator ||= UUID.new
|
31
|
+
end
|
32
|
+
|
33
|
+
# Initialize generator.
|
34
|
+
#
|
35
|
+
# @param [Fixnum] seed seed for pseudorandom number generator.
|
36
|
+
def initialize(seed = nil)
|
37
|
+
seed ? srand(seed) : srand
|
38
|
+
@prefix, _ = rand_bytes(13).unpack("H26")
|
39
|
+
@inc = rand(0xfff) + 1
|
40
|
+
@lock = Mutex.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# Generate list of UUIDs.
|
44
|
+
#
|
45
|
+
# @param [Fixnum] count number of UUIDs you need
|
46
|
+
#
|
47
|
+
# @param [Symbol] algorithm Algorithm to use. Known algorithms:
|
48
|
+
# [:random]
|
49
|
+
# 128 bits of random awesome. All awesome, all the time.
|
50
|
+
# [:sequential]
|
51
|
+
# Monotonically increasing ids with random increments. First 26 hex
|
52
|
+
# characters are random. Last 6 increment in random amounts until an
|
53
|
+
# overflow occurs. On overflow, the random prefix is regenerated and
|
54
|
+
# the process starts over.
|
55
|
+
# [:utc_random]
|
56
|
+
# Time since Jan 1, 1970 UTC with microseconds. First 14 characters
|
57
|
+
# are the time in hex. Last 18 are random.
|
58
|
+
#
|
59
|
+
# @return [String, Array] single string value or array of strings. Where
|
60
|
+
# each value represents 128-bit number written in hexadecimal format.
|
61
|
+
def next(count = 1, algorithm = :sequential)
|
62
|
+
raise ArgumentError, "count should be a positive number" unless count > 0
|
63
|
+
uuids = case algorithm
|
64
|
+
when :random
|
65
|
+
rand_bytes(16 * count).unpack("H32" * count)
|
66
|
+
when :utc_random
|
67
|
+
now = Time.now.utc
|
68
|
+
prefix = "%014x" % [now.to_i * 1_000_000 + now.usec]
|
69
|
+
rand_bytes(9 * count).unpack("H18" * count).map do |tail|
|
70
|
+
"#{prefix}#{tail}"
|
71
|
+
end
|
72
|
+
when :sequential
|
73
|
+
(1..count).map{ next_seq }
|
74
|
+
else
|
75
|
+
raise ArgumentError, "Unknown algorithm #{algo}. Should be one :sequential, :random or :utc_random"
|
76
|
+
end
|
77
|
+
uuids.size == 1 ? uuids[0] : uuids
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def next_seq
|
83
|
+
@lock.synchronize do
|
84
|
+
if @inc >= 0xfff000
|
85
|
+
@prefix, _ = rand_bytes(13).unpack("H26")
|
86
|
+
@inc = rand(0xfff) + 1
|
87
|
+
end
|
88
|
+
@inc += rand(0xfff) + 1
|
89
|
+
"%s%06x" % [@prefix, @inc]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def rand_bytes(count)
|
94
|
+
bytes = ""
|
95
|
+
count.times { bytes << rand(256) }
|
96
|
+
bytes
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 Model
|
21
|
+
|
22
|
+
VERSION = "0.0.1"
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/tasks/doc.rake
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'rake/clean'
|
19
|
+
require 'yard'
|
20
|
+
require 'yard/rake/yardoc_task'
|
21
|
+
|
22
|
+
YARD::Rake::YardocTask.new do |t|
|
23
|
+
t.options = %w(--protected --no-private)
|
24
|
+
t.files.push('-', 'README.markdown', 'HISTORY.markdown')
|
25
|
+
end
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'rake/testtask'
|
19
|
+
require 'rake/clean'
|
20
|
+
|
21
|
+
rule 'test/CouchbaseMock.jar' do |task|
|
22
|
+
jar_path = "0.5-SNAPSHOT/CouchbaseMock-0.5-20120222.060643-15.jar"
|
23
|
+
sh %{wget -q -O test/CouchbaseMock.jar http://files.couchbase.com/maven2/org/couchbase/mock/CouchbaseMock/#{jar_path}}
|
24
|
+
end
|
25
|
+
|
26
|
+
CLOBBER << 'test/CouchbaseMock.jar'
|
27
|
+
|
28
|
+
Rake::TestTask.new do |test|
|
29
|
+
test.libs << "test" << "."
|
30
|
+
test.ruby_opts << "-rruby-debug" if ENV['DEBUG']
|
31
|
+
test.pattern = 'test/test_*.rb'
|
32
|
+
test.options = '--verbose'
|
33
|
+
end
|
34
|
+
|
35
|
+
Rake::Task['test'].prerequisites.unshift('test/CouchbaseMock.jar')
|
data/tasks/util.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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
|
+
desc 'Start an irb session and load the library.'
|
19
|
+
task :console do
|
20
|
+
exec "irb -I lib -rruby-debug -rcouchbase-model"
|
21
|
+
end
|
data/test/setup.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 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 'minitest/autorun'
|
19
|
+
require 'couchbase'
|
20
|
+
require 'couchbase/model'
|
21
|
+
|
22
|
+
require 'socket'
|
23
|
+
require 'open-uri'
|
24
|
+
|
25
|
+
class CouchbaseServer
|
26
|
+
attr_accessor :host, :port, :num_nodes, :buckets_spec
|
27
|
+
|
28
|
+
def real?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(params = {})
|
33
|
+
@host, @port = ENV['COUCHBASE_SERVER'].split(':')
|
34
|
+
@port = @port.to_i
|
35
|
+
|
36
|
+
if @host.nil? || @host.empty? || @port == 0
|
37
|
+
raise ArgumentError, "Check COUCHBASE_SERVER variable. It should be hostname:port"
|
38
|
+
end
|
39
|
+
|
40
|
+
@config = Yajl::Parser.parse(open("http://#{@host}:#{@port}/pools/default"))
|
41
|
+
@num_nodes = @config["nodes"].size
|
42
|
+
@buckets_spec = params[:buckets_spec] || "default:" # "default:,protected:secret,cache::memcache"
|
43
|
+
end
|
44
|
+
|
45
|
+
def start
|
46
|
+
# flush all buckets
|
47
|
+
@buckets_spec.split(',') do |bucket|
|
48
|
+
name, password, _ = bucket.split(':')
|
49
|
+
connection = Couchbase.new(:hostname => @host,
|
50
|
+
:port => @port,
|
51
|
+
:username => name,
|
52
|
+
:bucket => name,
|
53
|
+
:password => password)
|
54
|
+
connection.flush
|
55
|
+
end
|
56
|
+
end
|
57
|
+
def stop; end
|
58
|
+
end
|
59
|
+
|
60
|
+
class CouchbaseMock
|
61
|
+
Monitor = Struct.new(:pid, :client, :socket, :port)
|
62
|
+
|
63
|
+
attr_accessor :host, :port, :buckets_spec, :num_nodes, :num_vbuckets
|
64
|
+
|
65
|
+
def real?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(params = {})
|
70
|
+
@host = "127.0.0.1"
|
71
|
+
@port = 0
|
72
|
+
@num_nodes = 10
|
73
|
+
@num_vbuckets = 4096
|
74
|
+
@buckets_spec = "default:" # "default:,protected:secret,cache::memcache"
|
75
|
+
params.each do |key, value|
|
76
|
+
send("#{key}=", value)
|
77
|
+
end
|
78
|
+
yield self if block_given?
|
79
|
+
if @num_vbuckets < 1 || (@num_vbuckets & (@num_vbuckets - 1) != 0)
|
80
|
+
raise ArgumentError, "Number of vbuckets should be a power of two and greater than zero"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def start
|
85
|
+
@monitor = Monitor.new
|
86
|
+
@monitor.socket = TCPServer.new(nil, 0)
|
87
|
+
@monitor.socket.listen(10)
|
88
|
+
_, @monitor.port, _, _ = @monitor.socket.addr
|
89
|
+
trap("CLD") do
|
90
|
+
puts "CouchbaseMock.jar died unexpectedly during startup"
|
91
|
+
exit(1)
|
92
|
+
end
|
93
|
+
@monitor.pid = fork
|
94
|
+
if @monitor.pid.nil?
|
95
|
+
rc = exec(command_line("--harakiri-monitor=:#{@monitor.port}"))
|
96
|
+
else
|
97
|
+
trap("CLD", "SIG_DFL")
|
98
|
+
@monitor.client, _ = @monitor.socket.accept
|
99
|
+
@port = @monitor.client.recv(100).to_i
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def stop
|
104
|
+
@monitor.client.close
|
105
|
+
@monitor.socket.close
|
106
|
+
Process.kill("TERM", @monitor.pid)
|
107
|
+
Process.wait(@monitor.pid)
|
108
|
+
end
|
109
|
+
|
110
|
+
def failover_node(index, bucket = "default")
|
111
|
+
@monitor.client.send("failover,#{index},#{bucket}", 0)
|
112
|
+
end
|
113
|
+
|
114
|
+
def respawn_node(index, bucket = "default")
|
115
|
+
@monitor.client.send("respawn,#{index},#{bucket}", 0)
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
def command_line(extra = nil)
|
121
|
+
cmd = "java -jar #{File.dirname(__FILE__)}/CouchbaseMock.jar"
|
122
|
+
cmd << " --host #{@host}" if @host
|
123
|
+
cmd << " --port #{@port}" if @port
|
124
|
+
cmd << " --nodes #{@num_nodes}" if @num_nodes
|
125
|
+
cmd << " --vbuckets #{@num_vbuckets}" if @num_vbuckets
|
126
|
+
cmd << " --buckets #{@buckets_spec}" if @buckets_spec
|
127
|
+
cmd << " #{extra}"
|
128
|
+
cmd
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class MiniTest::Unit::TestCase
|
133
|
+
|
134
|
+
def start_mock(params = {})
|
135
|
+
mock = nil
|
136
|
+
if ENV['COUCHBASE_SERVER']
|
137
|
+
mock = CouchbaseServer.new(params)
|
138
|
+
if (params[:port] && mock.port != params[:port]) ||
|
139
|
+
(params[:host] && mock.host != params[:host]) ||
|
140
|
+
mock.buckets_spec != "default:"
|
141
|
+
skip("Unable to configure real cluster. Requested config is: #{params.inspect}")
|
142
|
+
end
|
143
|
+
else
|
144
|
+
mock = CouchbaseMock.new(params)
|
145
|
+
end
|
146
|
+
mock.start
|
147
|
+
mock
|
148
|
+
end
|
149
|
+
|
150
|
+
def stop_mock(mock)
|
151
|
+
assert(mock)
|
152
|
+
mock.stop
|
153
|
+
end
|
154
|
+
|
155
|
+
def with_mock(params = {})
|
156
|
+
mock = nil
|
157
|
+
if block_given?
|
158
|
+
mock = start_mock(params)
|
159
|
+
yield mock
|
160
|
+
end
|
161
|
+
ensure
|
162
|
+
stop_mock(mock) if mock
|
163
|
+
end
|
164
|
+
|
165
|
+
def uniq_id(*suffixes)
|
166
|
+
[caller.first[/.*[` ](.*)'/, 1], suffixes].join("_")
|
167
|
+
end
|
168
|
+
end
|
data/test/test_model.rb
ADDED
@@ -0,0 +1,114 @@
|
|
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
|
+
|
20
|
+
class Post < Couchbase::Model
|
21
|
+
attribute :title
|
22
|
+
attribute :body
|
23
|
+
end
|
24
|
+
|
25
|
+
class TestModel < MiniTest::Unit::TestCase
|
26
|
+
|
27
|
+
def setup
|
28
|
+
@mock = start_mock
|
29
|
+
Post.bucket = Couchbase.connect(:hostname => @mock.host,
|
30
|
+
:port => @mock.port)
|
31
|
+
end
|
32
|
+
|
33
|
+
def teardown
|
34
|
+
stop_mock(@mock)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_assigns_attributes_from_the_hash
|
38
|
+
post = Post.new(:title => "Hello, world")
|
39
|
+
assert_equal "Hello, world", post.title
|
40
|
+
refute post.body
|
41
|
+
refute post.id
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_assings_id_and_saves_the_object
|
45
|
+
post = Post.create(:title => "Hello, world")
|
46
|
+
assert post.id
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_updates_attributes
|
50
|
+
post = Post.create(:title => "Hello, world")
|
51
|
+
post.update(:body => "This is my first example")
|
52
|
+
assert_equal "This is my first example", post.body
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_refreshes_the_attributes_with_reload_method
|
56
|
+
orig = Post.create(:title => "Hello, world")
|
57
|
+
double = Post.find(orig.id)
|
58
|
+
double.update(:title => "Good bye, world")
|
59
|
+
orig.reload
|
60
|
+
assert_equal "Good bye, world", orig.title
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_doesnt_raise_if_the_attribute_redefined
|
64
|
+
eval <<-EOC
|
65
|
+
class RefinedPost < Couchbase::Model
|
66
|
+
attribute :title
|
67
|
+
attribute :title
|
68
|
+
end
|
69
|
+
EOC
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_allows_arbitrary_ids
|
73
|
+
Post.create(:id => uniq_id, :title => "Foo")
|
74
|
+
assert_equal "Foo", Post.find(uniq_id).title
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_returns_an_instance_of_post
|
78
|
+
Post.bucket.set(uniq_id, {:title => 'foo'})
|
79
|
+
assert Post.find(uniq_id).kind_of?(Post)
|
80
|
+
assert_equal uniq_id, Post.find(uniq_id).id
|
81
|
+
assert_equal "foo", Post.find(uniq_id).title
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_changes_its_attributes
|
85
|
+
post = Post.create(:title => "Hello, world")
|
86
|
+
post.title = "Good bye, world"
|
87
|
+
post.save.reload
|
88
|
+
assert_equal "Good bye, world", post.title
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_assings_a_new_id_to_each_record
|
92
|
+
post1 = Post.create
|
93
|
+
post2 = Post.create
|
94
|
+
|
95
|
+
refute post1.new?
|
96
|
+
refute post2.new?
|
97
|
+
refute_equal post1.id, post2.id
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_deletes_an_existent_model
|
101
|
+
post = Post.create(:id => uniq_id)
|
102
|
+
assert post.delete
|
103
|
+
refute Post.bucket.get(uniq_id)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_fails_to_delete_model_without_id
|
107
|
+
post = Post.new(:title => "Hello")
|
108
|
+
refute post.id
|
109
|
+
assert_raises Couchbase::Error::MissingId do
|
110
|
+
post.delete
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/test/test_uuid.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'setup')
|
2
|
+
|
3
|
+
class TestUUID < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def test_it_can_generate_10k_unique_ids
|
6
|
+
random = Couchbase::Model::UUID.new.next(10_000, :random)
|
7
|
+
assert_equal 10_000, random.uniq.size
|
8
|
+
|
9
|
+
utc_random = Couchbase::Model::UUID.new.next(10_000, :utc_random)
|
10
|
+
assert_equal 10_000, utc_random.uniq.size
|
11
|
+
|
12
|
+
sequential = Couchbase::Model::UUID.new.next(10_000, :sequential)
|
13
|
+
assert_equal 10_000, sequential.uniq.size
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_it_produces_monotonically_increasing_ids
|
17
|
+
utc_random = Couchbase::Model::UUID.new
|
18
|
+
assert utc_random.next(1, :utc_random) < utc_random.next(1, :utc_random)
|
19
|
+
|
20
|
+
sequential = Couchbase::Model::UUID.new
|
21
|
+
assert sequential.next(1, :sequential) < sequential.next(1, :sequential)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_it_roll_over
|
25
|
+
generator = Couchbase::Model::UUID.new
|
26
|
+
prefix = generator.next[0, 26]
|
27
|
+
n = 0
|
28
|
+
n += 1 while prefix == generator.next[0, 26]
|
29
|
+
assert(n >= 5000 && n <= 11000)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: couchbase-model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sergey Avseyev
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-07 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: couchbase
|
16
|
+
requirement: &8647480 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *8647480
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &8646760 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.8.7
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *8646760
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: minitest
|
38
|
+
requirement: &8575100 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *8575100
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdiscount
|
49
|
+
requirement: &8574240 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *8574240
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: yard
|
60
|
+
requirement: &8573480 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *8573480
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ruby-debug19
|
71
|
+
requirement: &8572660 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *8572660
|
80
|
+
description: ORM-like interface allows you to persist your models to Couchbase
|
81
|
+
email:
|
82
|
+
- sergey.avseyev@gmail.com
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .travis.yml
|
89
|
+
- .yardopts
|
90
|
+
- Gemfile
|
91
|
+
- README.markdown
|
92
|
+
- Rakefile
|
93
|
+
- couchbase-model.gemspec
|
94
|
+
- lib/couchbase-model.rb
|
95
|
+
- lib/couchbase/model.rb
|
96
|
+
- lib/couchbase/model/uuid.rb
|
97
|
+
- lib/couchbase/model/version.rb
|
98
|
+
- tasks/doc.rake
|
99
|
+
- tasks/test.rake
|
100
|
+
- tasks/util.rake
|
101
|
+
- test/setup.rb
|
102
|
+
- test/test_model.rb
|
103
|
+
- test/test_uuid.rb
|
104
|
+
homepage: ''
|
105
|
+
licenses: []
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 1.8.10
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Declarative interface to Couchbase
|
128
|
+
test_files:
|
129
|
+
- test/setup.rb
|
130
|
+
- test/test_model.rb
|
131
|
+
- test/test_uuid.rb
|