couchrest 0.24 → 0.26
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -1
- data/Rakefile +0 -1
- data/lib/couchrest/core/database.rb +6 -0
- data/lib/couchrest/mixins/class_proxy.rb +4 -0
- data/lib/couchrest/mixins/design_doc.rb +1 -2
- data/lib/couchrest/mixins/properties.rb +2 -2
- data/lib/couchrest/mixins/validation.rb +16 -1
- data/lib/couchrest/mixins/views.rb +22 -14
- data/lib/couchrest.rb +7 -5
- data/spec/couchrest/core/database_spec.rb +5 -0
- data/spec/couchrest/more/casted_model_spec.rb +35 -0
- data/spec/couchrest/more/extended_doc_spec.rb +4 -4
- data/spec/couchrest/more/extended_doc_view_spec.rb +10 -27
- data/spec/fixtures/more/cat.rb +18 -0
- metadata +2 -11
data/README.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# CouchRest: CouchDB, close to the metal
|
2
2
|
|
3
3
|
CouchRest is based on [CouchDB's couch.js test
|
4
|
-
library](http://svn.apache.org/repos/asf/
|
4
|
+
library](http://svn.apache.org/repos/asf/couchdb/trunk/share/www/script/couch.js),
|
5
5
|
which I find to be concise, clear, and well designed. CouchRest lightly wraps
|
6
6
|
CouchDB's HTTP API, managing JSON serialization, and remembering the URI-paths
|
7
7
|
to CouchDB's API endpoints so you don't have to.
|
8
8
|
|
9
9
|
CouchRest is designed to make a simple base for application and framework-specific object oriented APIs. CouchRest is Object-Mapper agnostic, the parsed JSON it returns from CouchDB shows up as subclasses of Ruby's Hash. Naked JSON, just as it was mean to be.
|
10
10
|
|
11
|
+
Note: CouchRest only support CouchDB 0.9.0 or newer.
|
12
|
+
|
11
13
|
## Easy Install
|
12
14
|
|
13
15
|
Easy Install is moving to RubyForge, heads up for the gem.
|
data/Rakefile
CHANGED
@@ -29,7 +29,6 @@ spec = Gem::Specification.new do |s|
|
|
29
29
|
Dir["spec/tmp"]
|
30
30
|
s.extra_rdoc_files = %w( README.md LICENSE THANKS.md )
|
31
31
|
s.require_path = "lib"
|
32
|
-
s.add_dependency("json", ">= 1.1.2")
|
33
32
|
s.add_dependency("rest-client", ">= 0.5")
|
34
33
|
s.add_dependency("mime-types", ">= 1.15")
|
35
34
|
end
|
@@ -44,6 +44,12 @@ module CouchRest
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
# load a set of documents by passing an array of ids
|
48
|
+
def get_bulk(ids)
|
49
|
+
documents(:keys => ids, :include_docs => true)
|
50
|
+
end
|
51
|
+
alias :bulk_load :get_bulk
|
52
|
+
|
47
53
|
# POST a temporary view function to CouchDB for querying. This is not
|
48
54
|
# recommended, as you don't get any performance benefit from CouchDB's
|
49
55
|
# materialized views. Can be quite slow on large databases.
|
@@ -25,8 +25,7 @@ module CouchRest
|
|
25
25
|
design_doc['views'].each do |name, view|
|
26
26
|
funcs << "#{name}/#{view['map']}#{view['reduce']}"
|
27
27
|
end
|
28
|
-
|
29
|
-
self.design_doc_slug_cache = "#{self.to_s}-#{md5}"
|
28
|
+
self.design_doc_slug_cache = self.to_s
|
30
29
|
end
|
31
30
|
|
32
31
|
def default_design_doc
|
@@ -23,8 +23,8 @@ module CouchRest
|
|
23
23
|
# TODO: cache the default object
|
24
24
|
self.class.properties.each do |property|
|
25
25
|
key = property.name.to_s
|
26
|
-
# let's make sure we have a default
|
27
|
-
if
|
26
|
+
# let's make sure we have a default
|
27
|
+
if property.default
|
28
28
|
if property.default.class == Proc
|
29
29
|
self[key] = property.default.call
|
30
30
|
else
|
@@ -115,7 +115,22 @@ module CouchRest
|
|
115
115
|
# Check if a resource is valid in a given context
|
116
116
|
#
|
117
117
|
def valid?(context = :default)
|
118
|
-
self.class.validators.execute(context, self)
|
118
|
+
result = self.class.validators.execute(context, self)
|
119
|
+
result && validate_casted_arrays
|
120
|
+
end
|
121
|
+
|
122
|
+
# checking on casted objects
|
123
|
+
def validate_casted_arrays
|
124
|
+
result = true
|
125
|
+
array_casted_properties = self.class.properties.select { |property| property.casted && property.type.instance_of?(Array) }
|
126
|
+
array_casted_properties.each do |property|
|
127
|
+
casted_values = self.send(property.name)
|
128
|
+
next unless casted_values.respond_to?(:each) && casted_values.first.respond_to?(:valid?)
|
129
|
+
casted_values.each do |value|
|
130
|
+
result = (result && value.valid?) if value.respond_to?(:valid?)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
result
|
119
134
|
end
|
120
135
|
|
121
136
|
# Begin a recursive walk of the model checking validity
|
@@ -104,26 +104,34 @@ module CouchRest
|
|
104
104
|
fetch_view_with_docs(db, name, query, raw, &block)
|
105
105
|
end
|
106
106
|
|
107
|
+
# DEPRECATED
|
108
|
+
# user model_design_doc to retrieve the current design doc
|
107
109
|
def all_design_doc_versions(db = database)
|
108
|
-
db.documents :startkey => "_design/#{self.to_s}
|
110
|
+
db.documents :startkey => "_design/#{self.to_s}",
|
109
111
|
:endkey => "_design/#{self.to_s}-\u9999"
|
110
112
|
end
|
113
|
+
|
114
|
+
def model_design_doc(db = database)
|
115
|
+
begin
|
116
|
+
@model_design_doc = db.get("_design/#{self.to_s}")
|
117
|
+
rescue
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
end
|
111
121
|
|
112
|
-
# Deletes
|
113
|
-
# Running
|
114
|
-
# and consistently using the latest code, is the way to clear out old design
|
115
|
-
# docs. Running it to early could mean that live code has to regenerate
|
122
|
+
# Deletes the current design doc for the current class.
|
123
|
+
# Running it to early could mean that live code has to regenerate
|
116
124
|
# potentially large indexes.
|
117
125
|
def cleanup_design_docs!(db = database)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
126
|
+
save_design_doc_on(db)
|
127
|
+
# db.refresh_design_doc
|
128
|
+
# db.save_design_doc
|
129
|
+
# design_doc = model_design_doc(db)
|
130
|
+
# if design_doc
|
131
|
+
# db.delete_doc(design_doc)
|
132
|
+
# else
|
133
|
+
# false
|
134
|
+
# end
|
127
135
|
end
|
128
136
|
|
129
137
|
private
|
data/lib/couchrest.rb
CHANGED
@@ -12,10 +12,12 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require
|
16
|
-
|
17
|
-
require 'json'
|
18
|
-
|
15
|
+
require 'rubygems'
|
16
|
+
begin
|
17
|
+
require 'json'
|
18
|
+
rescue LoadError
|
19
|
+
raise "You need install and require your own compatible json library since couchrest rest couldn't load the json/json_pure gem" unless Kernel.const_defined?("JSON")
|
20
|
+
end
|
19
21
|
require 'rest_client'
|
20
22
|
|
21
23
|
$:.unshift File.dirname(__FILE__) unless
|
@@ -28,7 +30,7 @@ require 'couchrest/monkeypatches'
|
|
28
30
|
|
29
31
|
# = CouchDB, close to the metal
|
30
32
|
module CouchRest
|
31
|
-
VERSION = '0.
|
33
|
+
VERSION = '0.26' unless self.const_defined?("VERSION")
|
32
34
|
|
33
35
|
autoload :Server, 'couchrest/core/server'
|
34
36
|
autoload :Database, 'couchrest/core/database'
|
@@ -599,6 +599,11 @@ describe CouchRest::Database do
|
|
599
599
|
ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3', :include_docs => true)
|
600
600
|
ds['rows'][0]['doc']['another'].should == "doc"
|
601
601
|
end
|
602
|
+
it "should have the bulk_load macro" do
|
603
|
+
rs = @db.bulk_load ["doc0", "doc7"]
|
604
|
+
rs['rows'].length.should == 2
|
605
|
+
rs['rows'][0]['doc']['another'].should == "doc"
|
606
|
+
end
|
602
607
|
end
|
603
608
|
|
604
609
|
|
@@ -1,5 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
|
2
4
|
require File.join(FIXTURE_PATH, 'more', 'card')
|
5
|
+
require File.join(FIXTURE_PATH, 'more', 'cat')
|
6
|
+
require File.join(FIXTURE_PATH, 'more', 'person')
|
7
|
+
|
3
8
|
|
4
9
|
class WithCastedModelMixin < Hash
|
5
10
|
include CouchRest::CastedModel
|
@@ -103,5 +108,35 @@ describe CouchRest::CastedModel do
|
|
103
108
|
end
|
104
109
|
|
105
110
|
end
|
111
|
+
|
112
|
+
describe "saving document with array of casted models and validation" do
|
113
|
+
before :each do
|
114
|
+
@cat = Cat.new
|
115
|
+
@cat.save
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should save" do
|
119
|
+
toy = CatToy.new :name => "Mouse"
|
120
|
+
@cat.toys.push(toy)
|
121
|
+
@cat.save.should be_true
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should fail because name is not present" do
|
125
|
+
toy = CatToy.new
|
126
|
+
@cat.toys.push(toy)
|
127
|
+
@cat.should_not be_valid
|
128
|
+
@cat.save.should be_false
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should not fail if the casted model doesn't have validation" do
|
132
|
+
Cat.property :masters, :cast_as => ['Person'], :default => []
|
133
|
+
Cat.validates_present :name
|
134
|
+
cat = Cat.new(:name => 'kitty')
|
135
|
+
cat.should be_valid
|
136
|
+
cat.masters.push Person.new
|
137
|
+
cat.should be_valid
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
106
141
|
|
107
142
|
end
|
@@ -10,7 +10,7 @@ describe "ExtendedDocument" do
|
|
10
10
|
property :preset, :default => {:right => 10, :top_align => false}
|
11
11
|
property :set_by_proc, :default => Proc.new{Time.now}, :cast_as => 'Time'
|
12
12
|
property :tags, :default => []
|
13
|
-
property :
|
13
|
+
property :read_only_with_default, :default => 'generic', :read_only => true
|
14
14
|
property :name
|
15
15
|
timestamps!
|
16
16
|
end
|
@@ -158,10 +158,10 @@ describe "ExtendedDocument" do
|
|
158
158
|
obj = WithDefaultValues.new(:tags => ['spec'])
|
159
159
|
obj.tags.should == ['spec']
|
160
160
|
end
|
161
|
-
|
162
|
-
it "should
|
161
|
+
|
162
|
+
it "should set default value of read-only property" do
|
163
163
|
obj = WithDefaultValues.new
|
164
|
-
obj.
|
164
|
+
obj.read_only_with_default.should == 'generic'
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
@@ -183,14 +183,13 @@ describe "ExtendedDocument views" do
|
|
183
183
|
it "should barf on all_design_doc_versions if no database given" do
|
184
184
|
lambda{Unattached.all_design_doc_versions}.should raise_error
|
185
185
|
end
|
186
|
-
it "should
|
187
|
-
|
188
|
-
Unattached.all_design_doc_versions(@db)["rows"].length.should == 1
|
186
|
+
it "should be able to cleanup the db/bump the revision number" do
|
187
|
+
# if the previous specs were not run, the model_design_doc will be blank
|
189
188
|
Unattached.view_by :questions
|
190
|
-
Unattached.by_questions
|
191
|
-
Unattached.
|
189
|
+
Unattached.by_questions(:database => @db)
|
190
|
+
original_revision = Unattached.model_design_doc(@db)['_rev']
|
192
191
|
Unattached.cleanup_design_docs!(@db)
|
193
|
-
Unattached.
|
192
|
+
Unattached.model_design_doc(@db)['_rev'].should_not == original_revision
|
194
193
|
end
|
195
194
|
end
|
196
195
|
|
@@ -246,12 +245,10 @@ describe "ExtendedDocument views" do
|
|
246
245
|
end
|
247
246
|
it "should clean up design docs left around on specific database" do
|
248
247
|
@us.by_title
|
249
|
-
@us.
|
248
|
+
original_id = @us.model_design_doc['_rev']
|
250
249
|
Unattached.view_by :professor
|
251
250
|
@us.by_professor
|
252
|
-
@us.
|
253
|
-
@us.cleanup_design_docs!
|
254
|
-
@us.all_design_doc_versions["rows"].length.should == 1
|
251
|
+
@us.model_design_doc['_rev'].should_not == original_id
|
255
252
|
end
|
256
253
|
end
|
257
254
|
|
@@ -321,6 +318,7 @@ describe "ExtendedDocument views" do
|
|
321
318
|
before(:each) do
|
322
319
|
reset_test_db!
|
323
320
|
Article.by_date
|
321
|
+
@original_doc_rev = Article.model_design_doc['_rev']
|
324
322
|
@design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
|
325
323
|
end
|
326
324
|
it "should not create a design doc on view definition" do
|
@@ -332,24 +330,9 @@ describe "ExtendedDocument views" do
|
|
332
330
|
ddocs = Article.all_design_doc_versions["rows"].length
|
333
331
|
Article.view_by :updated_at
|
334
332
|
Article.by_updated_at
|
335
|
-
Article.
|
333
|
+
@original_doc_rev.should_not == Article.model_design_doc['_rev']
|
336
334
|
Article.design_doc["views"].keys.should include("by_updated_at")
|
337
335
|
end
|
338
336
|
end
|
339
|
-
|
340
|
-
describe "with a lot of designs left around" do
|
341
|
-
before(:each) do
|
342
|
-
reset_test_db!
|
343
|
-
Article.by_date
|
344
|
-
Article.view_by :field
|
345
|
-
Article.by_field
|
346
|
-
end
|
347
|
-
it "should clean them up" do
|
348
|
-
Article.view_by :stream
|
349
|
-
Article.by_stream
|
350
|
-
Article.all_design_doc_versions["rows"].length.should > 1
|
351
|
-
Article.cleanup_design_docs!
|
352
|
-
Article.all_design_doc_versions["rows"].length.should == 1
|
353
|
-
end
|
354
|
-
end
|
337
|
+
|
355
338
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Cat < CouchRest::ExtendedDocument
|
2
|
+
include ::CouchRest::Validation
|
3
|
+
|
4
|
+
# Set the default database to use
|
5
|
+
use_database TEST_SERVER.default_database
|
6
|
+
|
7
|
+
property :name
|
8
|
+
property :toys, :cast_as => ['CatToy'], :default => []
|
9
|
+
end
|
10
|
+
|
11
|
+
class CatToy < Hash
|
12
|
+
include ::CouchRest::CastedModel
|
13
|
+
include ::CouchRest::Validation
|
14
|
+
|
15
|
+
property :name
|
16
|
+
|
17
|
+
validates_present :name
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: couchrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.26"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J. Chris Anderson
|
@@ -13,16 +13,6 @@ cert_chain: []
|
|
13
13
|
date: 2008-11-22 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
17
|
-
name: json
|
18
|
-
type: :runtime
|
19
|
-
version_requirement:
|
20
|
-
version_requirements: !ruby/object:Gem::Requirement
|
21
|
-
requirements:
|
22
|
-
- - ">="
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: 1.1.2
|
25
|
-
version:
|
26
16
|
- !ruby/object:Gem::Dependency
|
27
17
|
name: rest-client
|
28
18
|
type: :runtime
|
@@ -131,6 +121,7 @@ files:
|
|
131
121
|
- spec/fixtures/attachments/test.html
|
132
122
|
- spec/fixtures/more/article.rb
|
133
123
|
- spec/fixtures/more/card.rb
|
124
|
+
- spec/fixtures/more/cat.rb
|
134
125
|
- spec/fixtures/more/course.rb
|
135
126
|
- spec/fixtures/more/event.rb
|
136
127
|
- spec/fixtures/more/invoice.rb
|