couchbase-model 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://secure.travis-ci.org/avsej/ruby-couchbase-model.png?branch=master)](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
|