mattly-exegesis 0.2.0 → 0.2.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/README.rdoc +5 -9
- data/Rakefile +32 -0
- data/VERSION.yml +1 -1
- data/lib/exegesis/database.rb +38 -14
- data/lib/exegesis/design.rb +57 -21
- data/lib/exegesis/document/attachment.rb +44 -0
- data/lib/exegesis/document/attachments.rb +53 -0
- data/lib/exegesis/document/collection.rb +78 -0
- data/lib/exegesis/document/generic_document.rb +5 -0
- data/lib/exegesis/document.rb +30 -2
- data/lib/exegesis/model.rb +20 -2
- data/lib/exegesis/server.rb +1 -1
- data/lib/exegesis/utils/http.rb +15 -9
- data/lib/exegesis.rb +33 -16
- data/lib/monkeypatches/time.rb +5 -1
- data/test/attachments_test.rb +106 -0
- data/test/database_test.rb +57 -0
- data/test/design_test.rb +91 -49
- data/test/document_collection_test.rb +97 -0
- data/test/document_test.rb +21 -2
- data/test/fixtures/attachments/flavakitten.jpg +0 -0
- data/test/fixtures/designs/things/views/by_name/map.js +5 -0
- data/test/fixtures/designs/things/views/by_tag/map.js +13 -0
- data/test/fixtures/designs/{tags → things}/views/by_tag/reduce.js +0 -0
- data/test/fixtures/designs/things/views/for_path/map.js +11 -0
- data/test/http_test.rb +2 -2
- data/test/model_test.rb +75 -4
- metadata +27 -19
- data/test/fixtures/designs/foos.js +0 -12
- data/test/fixtures/designs/tags/views/by_tag/map.js +0 -8
data/lib/exegesis.rb
CHANGED
|
@@ -8,29 +8,46 @@ $:.unshift File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__)) ||
|
|
|
8
8
|
require 'monkeypatches/time'
|
|
9
9
|
|
|
10
10
|
module Exegesis
|
|
11
|
-
autoload :Http,
|
|
12
|
-
|
|
13
|
-
autoload :Server,
|
|
14
|
-
autoload :Database,
|
|
15
|
-
|
|
16
|
-
autoload :Model,
|
|
17
|
-
autoload :Document,
|
|
18
|
-
|
|
19
|
-
autoload :Designs, 'exegesis/designs'
|
|
20
|
-
autoload :Design, 'exegesis/design'
|
|
11
|
+
autoload :Http, 'exegesis/utils/http'
|
|
12
|
+
|
|
13
|
+
autoload :Server, 'exegesis/server'
|
|
14
|
+
autoload :Database, 'exegesis/database'
|
|
15
|
+
|
|
16
|
+
autoload :Model, 'exegesis/model'
|
|
17
|
+
autoload :Document, 'exegesis/document'
|
|
18
|
+
autoload :GenericDocument, 'exegesis/document/generic_document'
|
|
21
19
|
|
|
20
|
+
autoload :Design, 'exegesis/design'
|
|
21
|
+
autoload :DocumentCollection, 'exegesis/document/collection'
|
|
22
|
+
|
|
22
23
|
extend self
|
|
23
24
|
|
|
24
25
|
def model_classes
|
|
25
26
|
@model_classes ||= {}
|
|
26
27
|
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
# extracted from Extlib
|
|
30
|
+
#
|
|
31
|
+
# Constantize tries to find a declared constant with the name specified
|
|
32
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
|
33
|
+
# or is not initialized.
|
|
34
|
+
#
|
|
35
|
+
# @example
|
|
36
|
+
# "Module".constantize #=> Module
|
|
37
|
+
# "Class".constantize #=> Class
|
|
38
|
+
def constantize(camel_cased_word)
|
|
39
|
+
return Exegesis::GenericDocument if camel_cased_word.nil?
|
|
40
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
|
41
|
+
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def instantiate(doc, database)
|
|
48
|
+
doc = constantize(doc['class']).new(doc)
|
|
49
|
+
doc.database = database if doc.respond_to?(:database=)
|
|
50
|
+
doc
|
|
34
51
|
end
|
|
35
52
|
|
|
36
53
|
end
|
data/lib/monkeypatches/time.rb
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class AttachmentsDocumentTest
|
|
4
|
+
include Exegesis::Document
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class ExegesisAttachmentsTest < Test::Unit::TestCase
|
|
8
|
+
before do
|
|
9
|
+
reset_db
|
|
10
|
+
@doc = AttachmentsDocumentTest.new({}, @db)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "document methods" do
|
|
14
|
+
expect { @doc.attachments.kind_of?(Exegesis::Document::Attachments).will == true }
|
|
15
|
+
expect { @doc.attachments.size.will == 0 }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context "reading existing attachments" do
|
|
19
|
+
before do
|
|
20
|
+
@doc.save
|
|
21
|
+
@text = "this is a file"
|
|
22
|
+
RestClient.put("#{@db.uri}/#{@doc['_id']}/file.txt?rev=#{@doc['_rev']}", @text, {'Content-Type'=>'text/plain'})
|
|
23
|
+
@doc = @db.get(@doc['_id'])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
expect { @doc.attachments.size.will == 1 }
|
|
27
|
+
expect { @doc.attachments.keys.will == %w(file.txt) }
|
|
28
|
+
expect { @doc.attachments['file.txt'].content_type.will == 'text/plain' }
|
|
29
|
+
expect { @doc.attachments['file.txt'].length.will == @text.length }
|
|
30
|
+
expect { @doc.attachments['file.txt'].stub?.will == true }
|
|
31
|
+
expect { @doc.attachments['file.txt'].file.will == @text }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "writing attachments" do
|
|
35
|
+
before do
|
|
36
|
+
@doc.save
|
|
37
|
+
end
|
|
38
|
+
context "directly using attachments put" do
|
|
39
|
+
context "with the file's contents as a string" do
|
|
40
|
+
before do
|
|
41
|
+
@contents = "this is the contents of a text file"
|
|
42
|
+
@type = 'text/plain'
|
|
43
|
+
@putting = lambda {|name, contents, type| @doc.attachments.put(name, contents, type) }
|
|
44
|
+
@putting.call 'f.txt', @contents, @type
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context "when they don't exist yet" do
|
|
48
|
+
expect { RestClient.get("#{@doc.uri}/f.txt").will == @contents }
|
|
49
|
+
expect { @doc.attachments['f.txt'].file.will == @contents }
|
|
50
|
+
expect { @doc.rev.will == @db.raw_get(@doc.id)['_rev'] }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context "when they do exist" do
|
|
54
|
+
before do
|
|
55
|
+
@putting.call 'f.txt', "foo", @type
|
|
56
|
+
end
|
|
57
|
+
expect { @doc.attachments['f.txt'].file.will == "foo" }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# it turns out rest-client doesn't actually support streaming uploads/downloads yet
|
|
62
|
+
# context "streaming the file as a block given" do
|
|
63
|
+
# before do
|
|
64
|
+
# @file = File.open(fixtures_path('attachments/flavakitten.jpg'))
|
|
65
|
+
# @type = 'image/jpeg'
|
|
66
|
+
# @doc.attachments.put('kitten.jpg', @type) { @file.read }
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# expect { @doc.will satisfy(lambda{|e| e.attachments['kitten.jpg'].file == @file.read })}
|
|
70
|
+
# end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "indirectly, saved with the document" do
|
|
74
|
+
before do
|
|
75
|
+
@content = "this is an example file"
|
|
76
|
+
@doc.attachments['file.txt'] = @content, 'text/plain'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
expect { @doc.attachments['file.txt'].content_type.will == 'text/plain' }
|
|
80
|
+
expect { @doc.attachments['file.txt'].metadata['data'].will == Base64.encode64(@content).gsub(/\s/,'') }
|
|
81
|
+
expect { @doc.attachments['file.txt'].length.will == @content.length }
|
|
82
|
+
expect { @doc.attachments.dirty?.will == true }
|
|
83
|
+
|
|
84
|
+
context "when saving" do
|
|
85
|
+
before do
|
|
86
|
+
@doc.save
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
expect { @doc.attachments['file.txt'].file.will == @content }
|
|
90
|
+
expect { @doc.attachments['file.txt'].stub?.will == true }
|
|
91
|
+
expect { @doc.attachments['file.txt'].metadata.has_key?('data').will == false }
|
|
92
|
+
expect { @doc.attachments.dirty?.will == false }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context "removing attachments" do
|
|
98
|
+
context "from the document" do
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context "directly from the database" do
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
data/test/database_test.rb
CHANGED
|
@@ -7,6 +7,16 @@ class CustomDesignDirDatabaseTest
|
|
|
7
7
|
include Exegesis::Database
|
|
8
8
|
designs_directory 'app/designs'
|
|
9
9
|
end
|
|
10
|
+
class NamedDocumentDatabaseTest
|
|
11
|
+
include Exegesis::Database
|
|
12
|
+
named_document :settings do
|
|
13
|
+
expose :things
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
class NamedDocumentWithoutBlockDatabaseTest
|
|
17
|
+
include Exegesis::Database
|
|
18
|
+
named_document :blah
|
|
19
|
+
end
|
|
10
20
|
class DatabaseTestDocument
|
|
11
21
|
include Exegesis::Document
|
|
12
22
|
end
|
|
@@ -81,6 +91,16 @@ class ExegesisDatabaseTest < Test::Unit::TestCase
|
|
|
81
91
|
expect { @doc['key'].will == 'value' }
|
|
82
92
|
expect { @doc['class'].will == 'DatabaseTestDocument' }
|
|
83
93
|
end
|
|
94
|
+
|
|
95
|
+
context "retrieving multiple documents" do
|
|
96
|
+
before do
|
|
97
|
+
docs = [{"_id"=>"a"},{"_id"=>"b"},{"_id"=>"c"}].map{|d| d.update('class' => 'DatabaseTestDocument')}
|
|
98
|
+
RestClient.post("#{@db.uri}/_bulk_docs", {"docs"=>docs}.to_json)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
expect { @db.get(%w(a b c)).size.will == 3 }
|
|
102
|
+
expect { @db.get(%w(a b c)).all?{|doc| doc.is_a?(DatabaseTestDocument)}.will == true }
|
|
103
|
+
end
|
|
84
104
|
end
|
|
85
105
|
|
|
86
106
|
context "saving docs" do
|
|
@@ -128,6 +148,7 @@ class ExegesisDatabaseTest < Test::Unit::TestCase
|
|
|
128
148
|
expect { lambda { @db.save(@doc) }.will raise_error }
|
|
129
149
|
end
|
|
130
150
|
end
|
|
151
|
+
|
|
131
152
|
end
|
|
132
153
|
end
|
|
133
154
|
|
|
@@ -158,4 +179,40 @@ class ExegesisDatabaseTest < Test::Unit::TestCase
|
|
|
158
179
|
expect { CustomDesignDirDatabaseTest.designs_directory.will == Pathname.new('app/designs') }
|
|
159
180
|
end
|
|
160
181
|
|
|
182
|
+
context "with a named document" do
|
|
183
|
+
context "that doesn't exist yet" do
|
|
184
|
+
before do
|
|
185
|
+
reset_db
|
|
186
|
+
@db = NamedDocumentDatabaseTest.new('exegesis-test')
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
expect { @db.settings.kind_of?(NamedDocumentDatabaseTest::Settings).will == true }
|
|
190
|
+
expect { @db.settings.rev.will =~ /1-\d{7,12}/ }
|
|
191
|
+
expect { @db.settings.respond_to?(:things).will == true }
|
|
192
|
+
expect { @db.settings; lambda{ @db.get('settings') }.wont raise_error(RestClient::ResourceNotFound) }
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
context "that does exist" do
|
|
196
|
+
before do
|
|
197
|
+
reset_db
|
|
198
|
+
@db = NamedDocumentDatabaseTest.new('exegesis-test')
|
|
199
|
+
@doc = @db.save({'_id' => 'settings', 'things' => %w(foo bar baz), 'class' => 'NamedDocumentDatabaseTest::Settings'})
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
expect { lambda { @db.get('settings') }.wont raise_error(RestClient::ResourceNotFound) }
|
|
203
|
+
expect { @db.settings.rev.will == @doc['_rev'] }
|
|
204
|
+
expect { @db.settings.rev.will =~ /1-\d{7,12}/ }
|
|
205
|
+
expect { @db.settings.things.will == %w(foo bar baz) }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context "when the declaration does not have a block" do
|
|
209
|
+
before do
|
|
210
|
+
reset_db
|
|
211
|
+
@db = NamedDocumentWithoutBlockDatabaseTest.new('exegesis-test')
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
expect { @db.blah.kind_of?(NamedDocumentWithoutBlockDatabaseTest::Blah).will == true }
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
161
218
|
end
|
data/test/design_test.rb
CHANGED
|
@@ -8,9 +8,10 @@ end
|
|
|
8
8
|
class DesignTestDatabase
|
|
9
9
|
include Exegesis::Database
|
|
10
10
|
|
|
11
|
-
designs_directory "
|
|
11
|
+
designs_directory "test/fixtures/designs"
|
|
12
12
|
|
|
13
|
-
design :
|
|
13
|
+
design :things do
|
|
14
|
+
view :by_name
|
|
14
15
|
docs :by_tag
|
|
15
16
|
hash :count, :view => :by_tag
|
|
16
17
|
end
|
|
@@ -27,7 +28,7 @@ class ExegesisDesignTest < Test::Unit::TestCase
|
|
|
27
28
|
@db = DesignTestDatabase.new('exegesis-test')
|
|
28
29
|
if with_doc
|
|
29
30
|
@db.save({
|
|
30
|
-
'_id' => '_design/
|
|
31
|
+
'_id' => '_design/things',
|
|
31
32
|
'views' => {
|
|
32
33
|
'by_tag' => {
|
|
33
34
|
'map' => 'function(doc) { for (var tag in doc.tags) { emit(doc.tags[tag], 1); } }',
|
|
@@ -39,53 +40,72 @@ class ExegesisDesignTest < Test::Unit::TestCase
|
|
|
39
40
|
|
|
40
41
|
context "design instances" do
|
|
41
42
|
before { setup_db }
|
|
42
|
-
expect { @db.
|
|
43
|
+
expect { @db.things.database.will == @db }
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
context "design declarations" do
|
|
46
47
|
before { setup_db }
|
|
47
|
-
expect { @db.
|
|
48
|
-
expect { @db.
|
|
49
|
-
expect { @db.
|
|
48
|
+
expect { @db.things.class.will == DesignTestDatabase::ThingsDesign }
|
|
49
|
+
expect { @db.things.is_a?(Exegesis::Design).will == true }
|
|
50
|
+
expect { @db.things.design_name.will == 'things' }
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
context "view declarations" do
|
|
53
54
|
before do
|
|
54
55
|
setup_db
|
|
55
56
|
@docs = [
|
|
56
|
-
{'class' => 'DesignTestDoc', 'tags' => %w(foo bar bee)},
|
|
57
|
-
{'class' => 'DesignTestDoc', 'tags' => %w(foo bar baz)},
|
|
58
|
-
{'class' => 'DesignTestDoc', 'tags' => %w(foo bee ruby)}
|
|
57
|
+
{'class' => 'DesignTestDoc', 'path' => 'a', 'date' => '2009/04/10', 'tags' => %w(foo bar bee)},
|
|
58
|
+
{'class' => 'DesignTestDoc', 'path' => 'b', 'date' => '2009/04/11', 'tags' => %w(foo bar baz)},
|
|
59
|
+
{'class' => 'DesignTestDoc', 'path' => 'c', 'date' => '2009/04/12', 'tags' => %w(foo bee ruby)}
|
|
59
60
|
]
|
|
60
61
|
@db.save(@docs)
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
context "declared docs" do
|
|
64
65
|
describe "with default key" do
|
|
65
|
-
before { @response = @db.
|
|
66
|
-
expect { @response.kind_of?(
|
|
66
|
+
before { @response = @db.things.by_tag('foo') }
|
|
67
|
+
expect { @response.kind_of?(Exegesis::DocumentCollection).will == true }
|
|
67
68
|
expect { @response.size.will == @docs.select{|d| d['tags'].include?('foo')}.size }
|
|
68
|
-
expect { @response.all? {|d| d.kind_of?(DesignTestDoc) }.will == true }
|
|
69
|
-
expect { @response.all? {|d| d['tags'].include?('foo') }.will == true }
|
|
69
|
+
expect { @response.documents.all? {|id,d| d.kind_of?(DesignTestDoc) }.will == true }
|
|
70
|
+
expect { @response.documents.all? {|id,d| d['tags'].include?('foo') }.will == true }
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
describe "with multiple keys" do
|
|
73
|
-
before { @response = @db.
|
|
74
|
-
expect { @response.kind_of?(
|
|
75
|
-
expect { @response.size.will ==
|
|
76
|
-
|
|
77
|
-
expect { @response.all? {|d| d.kind_of?(DesignTestDoc) }.will == true }
|
|
74
|
+
before { @response = @db.things.by_tag :keys => %w(bar bee) }
|
|
75
|
+
expect { @response.kind_of?(Exegesis::DocumentCollection).will == true }
|
|
76
|
+
expect { @response.size.will == @docs.inject(0){|sum,d| sum+=(d['tags'] & %w(bar bee)).size } }
|
|
77
|
+
expect { @response.documents.size.will == @docs.select{|d| (d['tags'] & %w(bar bee)).size > 0}.size }
|
|
78
|
+
expect { @response.documents.all? {|id,d| d.kind_of?(DesignTestDoc) }.will == true }
|
|
78
79
|
end
|
|
80
|
+
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
context "declared hashes" do
|
|
82
84
|
before do
|
|
83
85
|
@counts = Hash.new(0)
|
|
84
|
-
@docs.each
|
|
86
|
+
@docs.each do |doc|
|
|
87
|
+
tags = doc['tags'].sort
|
|
88
|
+
tags.each_with_index do |tag, index|
|
|
89
|
+
@counts[tag] += 1
|
|
90
|
+
(tags.length - index).times do |second|
|
|
91
|
+
next if second.zero?
|
|
92
|
+
@counts[tags.slice(index, second+1)] += 1
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
expect { @db.things.count.should == @counts }
|
|
98
|
+
expect { @db.things.count('foo').should == @counts['foo'] }
|
|
99
|
+
|
|
100
|
+
context "invalid options" do
|
|
101
|
+
expect { lambda{@db.things.count(:group=>false)}.will raise_error(ArgumentError) }
|
|
102
|
+
expect { lambda{@db.things.count(:include_docs=>true)}.will raise_error(ArgumentError) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
context "for views without reduce" do
|
|
106
|
+
before { @klass = DesignTestDatabase::StuffDesign }
|
|
107
|
+
expect { lambda{@klass.class_eval{hash(:name_count, :view=>:by_name)}}.will raise_error }
|
|
85
108
|
end
|
|
86
|
-
expect { @db.tags.count.should == @counts }
|
|
87
|
-
expect { @db.tags.count(:group => false).should == @counts.values.inject(0){|sum,n| sum+=n } }
|
|
88
|
-
expect { @db.tags.count('foo').should == @counts['foo'] }
|
|
89
109
|
end
|
|
90
110
|
end
|
|
91
111
|
|
|
@@ -93,45 +113,57 @@ class ExegesisDesignTest < Test::Unit::TestCase
|
|
|
93
113
|
before { setup_db }
|
|
94
114
|
|
|
95
115
|
context "with a key as an initial arguemnt" do
|
|
96
|
-
expect { @db.
|
|
97
|
-
expect { @db.
|
|
98
|
-
expect { @db.
|
|
116
|
+
expect { @db.things.parse_opts('foo').will == {:key => 'foo'} }
|
|
117
|
+
expect { @db.things.parse_opts('foo', :include_docs => true).will == {:key => 'foo', :include_docs => true} }
|
|
118
|
+
expect { @db.things.parse_opts('foo', {:stale => 'ok'}, {:include_docs => true}).will == {:key => 'foo', :stale => 'ok', :include_docs => true }}
|
|
99
119
|
end
|
|
100
120
|
|
|
101
121
|
context "without an implied key" do
|
|
102
|
-
expect { @db.
|
|
103
|
-
expect { @db.
|
|
122
|
+
expect { @db.things.parse_opts(:key => 'foo').will == {:key => 'foo'} }
|
|
123
|
+
expect { @db.things.parse_opts({:key => 'foo'}, nil, {:stale => 'ok'}).will == {:key => 'foo', :stale => 'ok'} }
|
|
104
124
|
end
|
|
105
125
|
|
|
106
126
|
context "when a keys option is empty" do
|
|
107
|
-
expect { @db.
|
|
127
|
+
expect { @db.things.parse_opts(:keys => []).will == {} }
|
|
108
128
|
end
|
|
109
129
|
|
|
110
130
|
context "for ranges" do
|
|
111
131
|
context "when the key _is_ a range" do
|
|
112
|
-
before { @opts = @db.
|
|
132
|
+
before { @opts = @db.things.parse_opts(:key => 'bar'..'baz') }
|
|
113
133
|
expect { @opts.has_key?(:key).will == false }
|
|
114
134
|
expect { @opts[:startkey].will == 'bar' }
|
|
115
135
|
expect { @opts[:endkey].will == 'baz'}
|
|
116
136
|
end
|
|
117
137
|
|
|
118
138
|
context "when the key is an array that includes a range" do
|
|
119
|
-
before { @opts = @db.
|
|
139
|
+
before { @opts = @db.things.parse_opts(:key => ['published', '2009'..'2009/04']) }
|
|
120
140
|
expect { @opts.has_key?(:key).will == false }
|
|
121
141
|
expect { @opts[:startkey].will == ['published', '2009'] }
|
|
122
142
|
expect { @opts[:endkey].will == ['published', '2009/04'] }
|
|
123
143
|
end
|
|
124
144
|
|
|
125
|
-
context "for non inclusive ranges" do
|
|
145
|
+
context "for non inclusive ranges" do
|
|
146
|
+
end
|
|
126
147
|
context "when descending:true is an option" do
|
|
127
|
-
context "and first value is greater than the end value" do
|
|
148
|
+
context "and first value is greater than the end value" do
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
context "when the first value is greater than the end value" do
|
|
128
152
|
end
|
|
129
|
-
context "when the first value is greater than the end value" do; end
|
|
130
153
|
|
|
131
154
|
context "invalid option configurations" do
|
|
132
|
-
expect { lambda {@db.
|
|
155
|
+
expect { lambda {@db.things.parse_opts(:startkey => 'foo')}.will raise_error(ArgumentError) }
|
|
133
156
|
end
|
|
134
157
|
end
|
|
158
|
+
|
|
159
|
+
context "reducing" do
|
|
160
|
+
before { @parsing = lambda{|opts| @db.things.parse_opts(opts) } }
|
|
161
|
+
expect { @parsing.call(:group => 3).will == {:group_level => 3}}
|
|
162
|
+
expect { lambda{@parsing.call(:group => true, :reduce => false)}.will raise_error(ArgumentError) }
|
|
163
|
+
expect { lambda{@parsing.call(:group => true, :include_docs => true)}.will raise_error(ArgumentError) }
|
|
164
|
+
expect { lambda{@parsing.call(:group => 1, :reduce => false)}.will raise_error(ArgumentError) }
|
|
165
|
+
expect { lambda{@parsing.call(:group => 1, :include_docs => true)}.will raise_error(ArgumentError) }
|
|
166
|
+
end
|
|
135
167
|
end
|
|
136
168
|
|
|
137
169
|
context "design doc meta declarations" do
|
|
@@ -141,13 +173,13 @@ class ExegesisDesignTest < Test::Unit::TestCase
|
|
|
141
173
|
|
|
142
174
|
context "the design document" do
|
|
143
175
|
before do
|
|
144
|
-
@canonical = DesignTestDatabase::
|
|
176
|
+
@canonical = DesignTestDatabase::ThingsDesign.canonical_design
|
|
145
177
|
end
|
|
146
178
|
|
|
147
179
|
context "composing the canonical version" do
|
|
148
180
|
context "from files" do
|
|
149
|
-
expect { @canonical['views']['by_tag']['map'].will == File.read(fixtures_path('designs/
|
|
150
|
-
expect { @canonical['views']['by_tag']['reduce'].will == File.read(fixtures_path('designs/
|
|
181
|
+
expect { @canonical['views']['by_tag']['map'].will == File.read(fixtures_path('designs/things/views/by_tag/map.js')) }
|
|
182
|
+
expect { @canonical['views']['by_tag']['reduce'].will == File.read(fixtures_path('designs/things/views/by_tag/reduce.js')) }
|
|
151
183
|
end
|
|
152
184
|
|
|
153
185
|
context "from class declarations" do
|
|
@@ -159,34 +191,44 @@ class ExegesisDesignTest < Test::Unit::TestCase
|
|
|
159
191
|
context "when the design_doc doesn't exist in the db yet" do
|
|
160
192
|
before do
|
|
161
193
|
setup_db(false)
|
|
162
|
-
@db.
|
|
194
|
+
@db.things
|
|
163
195
|
end
|
|
164
|
-
expect { lambda{@db.get('_design/
|
|
165
|
-
expect { @db.
|
|
166
|
-
expect { @db.
|
|
196
|
+
expect { lambda{@db.get('_design/things')}.wont raise_error }
|
|
197
|
+
expect { @db.things['views'].will == @canonical['views'] }
|
|
198
|
+
expect { @db.things.rev.will =~ /\d-\d{6,12}/ }
|
|
167
199
|
end
|
|
168
200
|
|
|
169
201
|
context "when the design_doc exists but is not canonical" do
|
|
170
202
|
before do
|
|
171
203
|
# there are no line breaks in the version that setup_db posts
|
|
172
204
|
setup_db
|
|
173
|
-
@old = @db.get('_design/
|
|
174
|
-
@db.
|
|
205
|
+
@old = @db.get('_design/things')
|
|
206
|
+
@db.things
|
|
175
207
|
end
|
|
176
208
|
|
|
177
|
-
expect { @db.
|
|
209
|
+
expect { @db.things.rev.wont == @old['_rev'] }
|
|
178
210
|
end
|
|
179
211
|
|
|
180
212
|
context "when the design_doc exists and is canonical" do
|
|
181
213
|
before do
|
|
182
214
|
setup_db(false)
|
|
183
|
-
@db.put('_design/
|
|
184
|
-
@old = @db.get('_design/
|
|
185
|
-
@db.
|
|
215
|
+
@db.put('_design/things', DesignTestDatabase::ThingsDesign.canonical_design)
|
|
216
|
+
@old = @db.get('_design/things')
|
|
217
|
+
@db.things
|
|
186
218
|
end
|
|
187
219
|
|
|
188
|
-
expect { @db.
|
|
220
|
+
expect { @db.things.rev.will == @old['_rev'] }
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
context "knowing the views" do
|
|
225
|
+
before do
|
|
226
|
+
@klass = DesignTestDatabase::ThingsDesign
|
|
189
227
|
end
|
|
228
|
+
|
|
229
|
+
expect { @klass.views.will == @canonical['views'].keys }
|
|
230
|
+
expect { @klass.reduceable?('by_tag').will be(true) }
|
|
231
|
+
expect { @klass.reduceable?('by_name').will be(false) }
|
|
190
232
|
end
|
|
191
233
|
end
|
|
192
234
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class DocCollectionTestDoc
|
|
4
|
+
include Exegesis::Document
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class ExegesisDocumentCollectionTest < Test::Unit::TestCase
|
|
8
|
+
|
|
9
|
+
context "when dealing with view response rows" do
|
|
10
|
+
before do
|
|
11
|
+
reset_db
|
|
12
|
+
@docs = [{'_id' => 'foo'}, {'_id' => 'bar'}, {'_id' => 'bee'}].map {|d| d.update('class' => 'DocCollectionTestDoc') }
|
|
13
|
+
@db.save(@docs)
|
|
14
|
+
@rows = [['bar',5,'bar'], ['bee',5,'bee'], ['foo',3,'foo'], ['foo',10,'foo']]
|
|
15
|
+
@response_rows = @rows.map {|r| {'key' => r[0], 'value' => r[1], 'id' => r[2]} }
|
|
16
|
+
@collection = Exegesis::DocumentCollection.new(@response_rows, @db)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
expect { @collection.size.will == @rows.size }
|
|
20
|
+
expect { @collection.keys.will == @rows.map{|r| r.first}.uniq }
|
|
21
|
+
expect { @collection.values.will == @rows.map{|r| r[1]} }
|
|
22
|
+
expect { @collection.documents.size.will == @rows.map{|r| r[2]}.uniq.size }
|
|
23
|
+
expect { @collection.documents.all? {|id,d| d.kind_of?(DocCollectionTestDoc) }.will == true }
|
|
24
|
+
expect { @collection.documents['foo'].id.will == @docs.first['_id'] }
|
|
25
|
+
|
|
26
|
+
context "when documents are already in the rows" do
|
|
27
|
+
before do
|
|
28
|
+
# "thing":"foobar" is not in the docs that have been saved to the database
|
|
29
|
+
@docs.each {|d| d.update('thing' => 'foobar') }
|
|
30
|
+
@response_rows = @docs.map {|doc| {'key' => doc['_id'], 'value' => nil, 'id' => doc['_id'], 'doc' => doc } }
|
|
31
|
+
@collection = Exegesis::DocumentCollection.new(@response_rows, @db)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
expect { @collection.documents.all?{|id,d| d['thing'] == 'foobar' }.will == true }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "filtering to a specific key" do
|
|
38
|
+
before do
|
|
39
|
+
@rows = @rows.select {|r| r[0]=="foo" }
|
|
40
|
+
@foos = @collection['foo']
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
expect { @foos.size.will == @rows.size }
|
|
44
|
+
expect { @foos.values.will == @rows.map{|r| r[1]} }
|
|
45
|
+
expect { @foos.documents.size.will == @rows.map{|r| r[2]}.uniq.size }
|
|
46
|
+
expect { @foos.documents.all? {|id,d| d.kind_of?(DocCollectionTestDoc) }.will == true }
|
|
47
|
+
expect { @foos.documents['foo'].object_id.will == @collection.documents['foo'].object_id }
|
|
48
|
+
|
|
49
|
+
context "with array keys" do
|
|
50
|
+
before do
|
|
51
|
+
@rows = [ [%w(bar baz), 5, 'bar'],
|
|
52
|
+
[%w(bar bee), 5, 'bee'],
|
|
53
|
+
[%w(bee bar), 3, 'bee'],
|
|
54
|
+
[%w(foo bar), 9, 'foo'],
|
|
55
|
+
[%w(foo bar), 2, 'bar'],
|
|
56
|
+
[%w(foo bee), 1, 'foo'],
|
|
57
|
+
[%w(foo bee), 8, 'bee']
|
|
58
|
+
]
|
|
59
|
+
@response_rows = @rows.map {|r| {'key' => r[0], 'value' => r[1], 'id' => r[2] }}
|
|
60
|
+
@collection = Exegesis::DocumentCollection.new(@response_rows, @db)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
expect { @collection['foo'].size.will == @rows.select{|r| r[0][0]=='foo'}.size }
|
|
64
|
+
expect { @collection['foo'].values.will == @rows.select{|r| r[0][0]=='foo' }.map{|r| r[1]} }
|
|
65
|
+
expect { @collection['foo']['bar'].size.will == @rows.select{|r| r[0][0]=='foo' && r[0][1]=='bar'}.size }
|
|
66
|
+
expect { @collection['foo']['bar'].values.will == @rows.select{|r| r[0][0]=='foo'&&r[0][1]=='bar'}.map{|r| r[1]} }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "iterating" do
|
|
74
|
+
before do
|
|
75
|
+
reset_db
|
|
76
|
+
@docs = [%w(bar bee), %w(bee foo), %w(foo bar)].map {|k,t| {'_id'=>k, 'thing'=>t, 'class'=>'DocCollectionTestDoc'} }
|
|
77
|
+
@db.save(@docs)
|
|
78
|
+
@rows = @docs.map {|doc| {'key'=>doc['_id'], 'value'=>doc['thing'], 'id'=>doc['_id']} }
|
|
79
|
+
@collection = Exegesis::DocumentCollection.new(@rows, @db)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context "each" do
|
|
83
|
+
before do
|
|
84
|
+
@counter = 0
|
|
85
|
+
@bin = []
|
|
86
|
+
@counting = lambda{ @collection.each{|k,v,d| @counter+=1 }; @counter }
|
|
87
|
+
@keybinning = lambda{ @collection.each{|k,v,d| @bin << k }; @bin }
|
|
88
|
+
@valbinning = lambda{ @collection.each{|k,v,d| @bin << v }; @bin }
|
|
89
|
+
@docbinning = lambda{ @collection.each{|k,v,d| @bin << d.id }; @bin }
|
|
90
|
+
end
|
|
91
|
+
expect { @counting.call.will == 3 }
|
|
92
|
+
expect { @keybinning.call.will == @rows.map{|r| r['key']} }
|
|
93
|
+
expect { @valbinning.call.will == @rows.map{|r| r['value']} }
|
|
94
|
+
expect { @docbinning.call.will == @rows.map{|r| @db.get(r['id']).id} }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/test/document_test.rb
CHANGED
|
@@ -121,7 +121,7 @@ class ExegesisDocumentClassDefinitionsTest < Test::Unit::TestCase
|
|
|
121
121
|
reset_db
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
context "
|
|
124
|
+
context "updating attributes" do
|
|
125
125
|
|
|
126
126
|
context "an existing doc" do
|
|
127
127
|
before do
|
|
@@ -152,7 +152,26 @@ class ExegesisDocumentClassDefinitionsTest < Test::Unit::TestCase
|
|
|
152
152
|
expect { @action.will raise_error(NoMethodError) }
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
|
-
|
|
155
|
+
|
|
156
|
+
context "a new doc" do
|
|
157
|
+
before { @doc = TestDocument.new({'foo' => 'bar'}, @db) }
|
|
158
|
+
|
|
159
|
+
context "without a rev" do
|
|
160
|
+
before { @doc.update_attributes({'foo' => 'baz'}) }
|
|
161
|
+
expect { @doc['foo'].will == 'baz' }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "with a blank rev" do
|
|
165
|
+
before { @doc.update_attributes({'foo' => 'baz', '_rev' => ''}) }
|
|
166
|
+
expect { @doc['foo'].will == 'baz' }
|
|
167
|
+
end
|
|
168
|
+
context "with a non blank rev" do
|
|
169
|
+
before { @action = lambda{@doc.update_attributes({'foo'=>'baz', '_rev'=>'1-3034523523'})} }
|
|
170
|
+
expect { @action.will raise_error(ArgumentError)}
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
|
|
156
175
|
end
|
|
157
176
|
end
|
|
158
177
|
|
|
Binary file
|