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/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, '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
-
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
- def instantiate hash, database=nil
29
- return nil if hash.nil?
30
- klass = model_classes[hash['class']]
31
- obj = klass.nil? ? hash : klass.new(hash)
32
- obj.database = database if obj.respond_to?(:database=)
33
- obj
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
@@ -1,5 +1,9 @@
1
1
  class Time
2
+ def to_json_format
3
+ self.getutc.strftime("%Y/%m/%d %H:%M:%S +0000")
4
+ end
5
+
2
6
  def to_json(options = nil)
3
- %("#{self.dup.utc.strftime("%Y/%m/%d %H:%M:%S +0000")}")
7
+ %("#{to_json_format}")
4
8
  end
5
9
  end
@@ -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
@@ -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 "#{File.dirname(__FILE__)}/fixtures/designs"
11
+ designs_directory "test/fixtures/designs"
12
12
 
13
- design :tags do
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/tags',
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.tags.database.will == @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.tags.class.will == DesignTestDatabase::TagsDesign }
48
- expect { @db.tags.is_a?(Exegesis::Design).will == true }
49
- expect { @db.tags.design_name.will == 'tags' }
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.tags.by_tag('foo') }
66
- expect { @response.kind_of?(Array).will == true }
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.tags.by_tag :keys => %w(bar bee) }
74
- expect { @response.kind_of?(Array).will == true }
75
- expect { @response.size.will == 3 }
76
- # expect { @response.size.will == @docs.select{|d| (d['tags'] & %w(bar bee)).size > 0}.size }
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 {|doc| doc['tags'].each {|tag| @counts[tag] += 1 } }
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.tags.parse_opts('foo').will == {:key => 'foo'} }
97
- expect { @db.tags.parse_opts('foo', :include_docs => true).will == {:key => 'foo', :include_docs => true} }
98
- expect { @db.tags.parse_opts('foo', {:stale => 'ok'}, {:include_docs => true}).will == {:key => 'foo', :stale => 'ok', :include_docs => true }}
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.tags.parse_opts(:key => 'foo').will == {:key => 'foo'} }
103
- expect { @db.tags.parse_opts({:key => 'foo'}, nil, {:stale => 'ok'}).will == {:key => 'foo', :stale => 'ok'} }
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.tags.parse_opts(:keys => []).will == {} }
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.tags.parse_opts(:key => 'bar'..'baz') }
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.tags.parse_opts(:key => ['published', '2009'..'2009/04']) }
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; end
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; end
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.tags.parse_opts(:startkey => 'foo')}.will raise_error(ArgumentError) }
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::TagsDesign.canonical_design
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/tags/views/by_tag/map.js')) }
150
- expect { @canonical['views']['by_tag']['reduce'].will == File.read(fixtures_path('designs/tags/views/by_tag/reduce.js')) }
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.tags
194
+ @db.things
163
195
  end
164
- expect { lambda{@db.get('_design/tags')}.wont raise_error }
165
- expect { @db.tags['views'].will == @canonical['views'] }
166
- expect { @db.tags.rev.will =~ /\d-\d{10}/ }
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/tags')
174
- @db.tags
205
+ @old = @db.get('_design/things')
206
+ @db.things
175
207
  end
176
208
 
177
- expect { @db.tags.rev.wont == @old['_rev'] }
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/tags', DesignTestDatabase::TagsDesign.canonical_design)
184
- @old = @db.get('_design/tags')
185
- @db.tags
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.tags.rev.will == @old['_rev'] }
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
@@ -121,7 +121,7 @@ class ExegesisDocumentClassDefinitionsTest < Test::Unit::TestCase
121
121
  reset_db
122
122
  end
123
123
 
124
- context "updating_attributes" do
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
 
@@ -0,0 +1,5 @@
1
+ function(doc) {
2
+ if (doc.name) {
3
+ emit(doc.name, null);
4
+ }
5
+ }