couch_docs 1.2.1 → 1.3.0
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/.bnsignore +16 -0
- data/.gitignore +4 -2
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/History.txt +12 -0
- data/README.rdoc +56 -27
- data/Rakefile +15 -34
- data/VERSION +1 -0
- data/couch_docs.gemspec +19 -40
- data/fixtures/_design/a/e.json +1 -0
- data/fixtures/_design/j/q.json +1 -0
- data/fixtures/baz_with_attachments/spacer.gif +0 -0
- data/fixtures/baz_with_attachments.json +1 -0
- data/lib/couch_docs/command_line.rb +2 -0
- data/lib/couch_docs/design_directory.rb +44 -18
- data/lib/couch_docs/document_directory.rb +57 -2
- data/lib/couch_docs/version.rb +3 -0
- data/lib/couch_docs.rb +0 -1
- data/spec/couch_docs/command_line_spec.rb +170 -0
- data/spec/couch_docs/design_directory_spec.rb +197 -0
- data/spec/couch_docs/document_directory_spec.rb +212 -0
- data/spec/couch_docs/store_spec.rb +98 -0
- data/spec/couch_docs_spec.rb +9 -482
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +3 -5
- metadata +60 -50
- data/couch_docs-1.1.0.gem +0 -0
- data/couch_docs-1.1.1.gem +0 -0
- data/couch_docs-1.2.0.gem +0 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe CouchDocs::CommandLine do
|
4
|
+
it "should be able to run a single instance of a command line" do
|
5
|
+
CouchDocs::CommandLine.
|
6
|
+
should_receive(:new).
|
7
|
+
with('foo', 'bar').
|
8
|
+
and_return(mock("Command Line").as_null_object)
|
9
|
+
|
10
|
+
CouchDocs::CommandLine.run('foo', 'bar')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should run the command line instance" do
|
14
|
+
command_line = mock("Command Line").as_null_object
|
15
|
+
command_line.
|
16
|
+
should_receive(:run)
|
17
|
+
|
18
|
+
CouchDocs::CommandLine.stub!(:new).and_return(command_line)
|
19
|
+
|
20
|
+
CouchDocs::CommandLine.run('foo', 'bar')
|
21
|
+
end
|
22
|
+
|
23
|
+
context "an instance that dumps a CouchDB database" do
|
24
|
+
it "should dump CouchDB documents from uri to dir when run" do
|
25
|
+
@it = CouchDocs::CommandLine.new(['dump', 'uri', 'dir'])
|
26
|
+
|
27
|
+
CouchDocs.
|
28
|
+
should_receive(:dump).
|
29
|
+
with("uri", "dir", nil)
|
30
|
+
|
31
|
+
@it.run
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to dump only design documents" do
|
35
|
+
@it = CouchDocs::CommandLine.new(['dump', 'uri', 'dir', '-d'])
|
36
|
+
|
37
|
+
CouchDocs.
|
38
|
+
should_receive(:dump).
|
39
|
+
with("uri", "dir", :design)
|
40
|
+
|
41
|
+
@it.run
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be able to dump only regular documents" do
|
45
|
+
@it = CouchDocs::CommandLine.new(['dump', 'uri', 'dir', '-D'])
|
46
|
+
|
47
|
+
CouchDocs.
|
48
|
+
should_receive(:dump).
|
49
|
+
with("uri", "dir", :doc)
|
50
|
+
|
51
|
+
@it.run
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be an initial add if everything is an add" do
|
55
|
+
@it = CouchDocs::CommandLine.new(['push', 'uri'])
|
56
|
+
args = [mock(:type => :added),
|
57
|
+
mock(:type => :added)]
|
58
|
+
@it.should be_initial_add(args)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not be an initial add if something is not an add" do
|
62
|
+
@it = CouchDocs::CommandLine.new(['push', 'uri'])
|
63
|
+
args = [mock(:type => :foo),
|
64
|
+
mock(:type => :added)]
|
65
|
+
@it.should_not be_initial_add(args)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should be a design docs update if something changes in _design" do
|
69
|
+
@it = CouchDocs::CommandLine.new(['push', 'uri'])
|
70
|
+
args = [mock(:path => "foo"),
|
71
|
+
mock(:path => "_design")]
|
72
|
+
@it.should be_design_doc_update(args)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should know document updates" do
|
76
|
+
@it = CouchDocs::CommandLine.new(['push', 'uri'])
|
77
|
+
doc_update = mock(:path => "foo")
|
78
|
+
args = [doc_update,
|
79
|
+
mock(:path => "_design")]
|
80
|
+
|
81
|
+
@it.
|
82
|
+
documents(args).
|
83
|
+
should == [doc_update]
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
context "updates on the filesystem" do
|
88
|
+
before(:each) do
|
89
|
+
@args = mock("args")
|
90
|
+
@it = CouchDocs::CommandLine.new(%w(push uri dir))
|
91
|
+
end
|
92
|
+
it "should only update design docs if only local design docs have changed" do
|
93
|
+
CouchDocs.
|
94
|
+
should_receive(:put_dir)
|
95
|
+
|
96
|
+
@it.stub!(:initial_add?).and_return(true)
|
97
|
+
@it.directory_watcher_update(@args)
|
98
|
+
end
|
99
|
+
context "not an inital add" do
|
100
|
+
before(:each) do
|
101
|
+
@it.stub!(:initial_add?).and_return(false)
|
102
|
+
@it.stub!(:design_doc_update?).and_return(false)
|
103
|
+
@it.stub!(:documents).and_return([])
|
104
|
+
CouchDocs.stub!(:put_design_dir)
|
105
|
+
end
|
106
|
+
it "should update design docs if there are design document updates" do
|
107
|
+
CouchDocs.
|
108
|
+
should_receive(:put_design_dir)
|
109
|
+
|
110
|
+
@it.stub!(:design_doc_update?).and_return(true)
|
111
|
+
@it.directory_watcher_update(@args)
|
112
|
+
end
|
113
|
+
it "should update documents (if any)" do
|
114
|
+
file_mock = mock("File", :path => "/foo")
|
115
|
+
@it.stub!(:documents).and_return([file_mock])
|
116
|
+
|
117
|
+
CouchDocs.
|
118
|
+
should_receive(:put_file).
|
119
|
+
with("uri", "/foo")
|
120
|
+
|
121
|
+
@it.directory_watcher_update(@args)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "pushing" do
|
128
|
+
before(:each) do
|
129
|
+
CouchDocs.stub!(:put_dir)
|
130
|
+
|
131
|
+
@dw = mock("Directory Watcher").as_null_object
|
132
|
+
DirectoryWatcher.stub!(:new).and_return(@dw)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should know watch" do
|
136
|
+
@it = CouchDocs::CommandLine.new(%w(push uri dir -w))
|
137
|
+
@it.options[:watch].should be_true
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should run once normally" do
|
141
|
+
@dw.should_receive(:run_once)
|
142
|
+
|
143
|
+
@it = CouchDocs::CommandLine.new(%w(push uri dir))
|
144
|
+
@it.run
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should start a watcher with -w" do
|
148
|
+
@dw.should_receive(:start)
|
149
|
+
|
150
|
+
@it = CouchDocs::CommandLine.new(%w(push uri dir -w))
|
151
|
+
@it.stub!(:active?).and_return(false)
|
152
|
+
@it.run
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "an instance that uploads to a CouchDB database" do
|
157
|
+
before(:each) do
|
158
|
+
@it = CouchDocs::CommandLine.new(['load', 'dir', 'uri'])
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should load CouchDB documents from dir to uri when run" do
|
162
|
+
CouchDocs.
|
163
|
+
should_receive(:put_dir).
|
164
|
+
with("uri", "dir")
|
165
|
+
|
166
|
+
@it.run
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe CouchDocs::DesignDirectory do
|
4
|
+
it "should require a root directory for instantiation" do
|
5
|
+
lambda { CouchDocs::DesignDirectory.new }.
|
6
|
+
should raise_error
|
7
|
+
|
8
|
+
lambda { CouchDocs::DesignDirectory.new("foo") }.
|
9
|
+
should raise_error
|
10
|
+
|
11
|
+
lambda { CouchDocs::DesignDirectory.new("fixtures/_design")}.
|
12
|
+
should_not raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should convert arrays into deep hashes" do
|
16
|
+
CouchDocs::DesignDirectory.
|
17
|
+
a_to_hash(%w{a b c d}).
|
18
|
+
should == {
|
19
|
+
'a' => {
|
20
|
+
'b' => {
|
21
|
+
'c' => 'd'
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
context "a valid directory" do
|
28
|
+
before(:each) do
|
29
|
+
@it = CouchDocs::DesignDirectory.new("fixtures/_design")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should list dirs, basename and contents of a js file" do
|
33
|
+
@it.expand_file("fixtures/_design/a/b/c.js").
|
34
|
+
should == ['a', 'b', 'c', 'function(doc) { return true; }']
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should list dirs, basename and contents of a json file" do
|
38
|
+
@it.expand_file("fixtures/_design/a/e.json").
|
39
|
+
should == ['a', 'e', [{"one" => "2"}]]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should assemble all documents into a single docs structure" do
|
43
|
+
@it.to_hash['a'].
|
44
|
+
should == {
|
45
|
+
'b' => {
|
46
|
+
'c' => 'function(doc) { return true; }',
|
47
|
+
'd' => 'function(doc) { return true; }'
|
48
|
+
},
|
49
|
+
'e' => [{"one" => "2"}]
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should process code macros when assembling" do
|
54
|
+
@it.to_hash['x'].
|
55
|
+
should == {
|
56
|
+
'z' =>
|
57
|
+
"// !begin code foo.js\n" +
|
58
|
+
"function foo () { return \"foo\"; }\n" +
|
59
|
+
"// !end code foo.js\n" +
|
60
|
+
"function bar () { return \"bar\"; }\n"
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should ignore macro escape sequence when reading JSON" do
|
65
|
+
@it.to_hash['j'].
|
66
|
+
should == {'q' => ["!code foo.js"]}
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should work with absolute !code paths"
|
70
|
+
|
71
|
+
it "should replace !code macros with the contents of the referenced file in lib" do
|
72
|
+
@it.stub!(:read_from_lib).and_return("awesome javascript")
|
73
|
+
|
74
|
+
@it.
|
75
|
+
process_code_macro(" // !code foo/bar.js ").
|
76
|
+
should =~ /awesome javascript/
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should not affect normal lines when processing macros" do
|
80
|
+
@it.
|
81
|
+
process_code_macro(" var foo = 'bar'; ").
|
82
|
+
should == " var foo = 'bar'; "
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should find files with relative paths in __lib" do
|
86
|
+
File.
|
87
|
+
should_receive(:read).
|
88
|
+
with("fixtures/_design/__lib/foo.js")
|
89
|
+
|
90
|
+
@it.read_from_lib("foo.js")
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
context "saving a JSON attribute" do
|
96
|
+
before(:each) do
|
97
|
+
@it = CouchDocs::DesignDirectory.new("/tmp")
|
98
|
+
|
99
|
+
FileUtils.stub!(:mkdir_p)
|
100
|
+
@file = mock("File").as_null_object
|
101
|
+
File.stub!(:new).and_return(@file)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should not mangle json valued attributes" do
|
105
|
+
@file.
|
106
|
+
should_receive(:write).
|
107
|
+
with(%{["bar","baz"]})
|
108
|
+
|
109
|
+
@it.save_js(nil, "_design/foo", { "foo" => ["bar","baz"] })
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should save in a .json file" do
|
113
|
+
File.
|
114
|
+
should_receive(:new).
|
115
|
+
with("/tmp/_design/foo/foo.json", "w+").
|
116
|
+
and_return(@file)
|
117
|
+
|
118
|
+
@it.save_js(nil, "_design/foo", { "foo" => ["bar","baz"] })
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "saving a JS attribute" do
|
123
|
+
before(:each) do
|
124
|
+
@it = CouchDocs::DesignDirectory.new("/tmp")
|
125
|
+
|
126
|
+
FileUtils.stub!(:mkdir_p)
|
127
|
+
@file = mock("File").as_null_object
|
128
|
+
File.stub!(:new).and_return(@file)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should not store _id" do
|
132
|
+
File.
|
133
|
+
should_not_receive(:new).
|
134
|
+
with("/tmp/_design/foo/_id.js", "w+")
|
135
|
+
|
136
|
+
@it.save_js(nil, "_design/foo", { "_id" => "_design/foo"})
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should create map the design document attribute to the filesystem" do
|
140
|
+
FileUtils.
|
141
|
+
should_receive(:mkdir_p).
|
142
|
+
with("/tmp/_design/foo")
|
143
|
+
|
144
|
+
@it.save_js("_design/foo", "bar", "json")
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should store the attribute to the filesystem" do
|
148
|
+
File.
|
149
|
+
should_receive(:new).
|
150
|
+
with("/tmp/_design/foo/bar.js", "w+")
|
151
|
+
|
152
|
+
@it.save_js("_design/foo", "bar", "json")
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should store hash values to the filesystem" do
|
156
|
+
File.
|
157
|
+
should_receive(:new).
|
158
|
+
with("/tmp/_design/foo/bar/baz.js", "w+")
|
159
|
+
|
160
|
+
@it.save_js("_design/foo", "bar", { "baz" => "json" })
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should store the attribute to the filesystem" do
|
164
|
+
@file.
|
165
|
+
should_receive(:write).
|
166
|
+
with("json")
|
167
|
+
|
168
|
+
@it.save_js("_design/foo", "bar", "json")
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should store the attributes with slashes to the filesystem" do
|
172
|
+
File.
|
173
|
+
should_receive(:new).
|
174
|
+
with("/tmp/_design/foo/bar%2Fbaz.js", "w+")
|
175
|
+
|
176
|
+
@it.save_js("_design/foo", "bar/baz", "json")
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should strip lib code when dumping" do
|
180
|
+
js = <<_JS
|
181
|
+
// !begin code foo.js
|
182
|
+
function foo () { return 'foo'; }
|
183
|
+
// !end code foo.js
|
184
|
+
// !begin code bar.js
|
185
|
+
function bar () { return 'bar'; }
|
186
|
+
// !end code bar.js
|
187
|
+
function baz () { return 'baz'; }
|
188
|
+
_JS
|
189
|
+
|
190
|
+
@it.
|
191
|
+
remove_code_macros(js).
|
192
|
+
should == "// !code foo.js\n" +
|
193
|
+
"// !code bar.js\n" +
|
194
|
+
"function baz () { return 'baz'; }\n"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe CouchDocs::DocumentDirectory do
|
4
|
+
it "should require a root directory for instantiation" do
|
5
|
+
lambda { CouchDocs::DocumentDirectory.new }.
|
6
|
+
should raise_error
|
7
|
+
|
8
|
+
lambda { CouchDocs::DocumentDirectory.new("foo") }.
|
9
|
+
should raise_error
|
10
|
+
|
11
|
+
lambda { CouchDocs::DocumentDirectory.new("fixtures")}.
|
12
|
+
should_not raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
context "a valid directory" do
|
16
|
+
before(:each) do
|
17
|
+
@it = CouchDocs::DocumentDirectory.new("fixtures")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to iterate over the documents" do
|
21
|
+
everything = []
|
22
|
+
@it.each_document do |name, contents|
|
23
|
+
everything << [name, contents]
|
24
|
+
end
|
25
|
+
everything.
|
26
|
+
should include ['bar', {"bar" => "2"}]
|
27
|
+
|
28
|
+
everything.
|
29
|
+
should include ['foo', {"foo" => "1"}]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be able to store a document" do
|
33
|
+
file = mock("File", :write => 42, :close => true)
|
34
|
+
File.
|
35
|
+
should_receive(:new).
|
36
|
+
with("fixtures/foo.json", "w+").
|
37
|
+
and_return(file)
|
38
|
+
|
39
|
+
@it.store_document({'_id' => 'foo'})
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be able to save a document as JSON" do
|
43
|
+
file = mock("File", :close => true)
|
44
|
+
File.stub!(:new).and_return(file)
|
45
|
+
|
46
|
+
file.should_receive(:write).with(%Q|{"_id":"foo"}|)
|
47
|
+
|
48
|
+
@it.store_document({'_id' => 'foo'})
|
49
|
+
end
|
50
|
+
|
51
|
+
context "pushing attachments to CouchDB" do
|
52
|
+
before(:each) do
|
53
|
+
@spacer_b64 = "R0lGODlhAQABAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgICAgMDAwP8AAAD/AP//AAAA//8A/wD//////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMwAzZgAzmQAzzAAz/wBmAABmMwBmZgBmmQBmzABm/wCZAACZMwCZZgCZmQCZzACZ/wDMAADMMwDMZgDMmQDMzADM/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMzADMzMzMzZjMzmTMzzDMz/zNmADNmMzNmZjNmmTNmzDNm/zOZADOZMzOZZjOZmTOZzDOZ/zPMADPMMzPMZjPMmTPMzDPM/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YzAGYzM2YzZmYzmWYzzGYz/2ZmAGZmM2ZmZmZmmWZmzGZm/2aZAGaZM2aZZmaZmWaZzGaZ/2bMAGbMM2bMZmbMmWbMzGbM/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kAZpkAmZkAzJkA/5kzAJkzM5kzZpkzmZkzzJkz/5lmAJlmM5lmZplmmZlmzJlm/5mZAJmZM5mZZpmZmZmZzJmZ/5nMAJnMM5nMZpnMmZnMzJnM/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwAM8wAZswAmcwAzMwA/8wzAMwzM8wzZswzmcwzzMwz/8xmAMxmM8xmZsxmmcxmzMxm/8yZAMyZM8yZZsyZmcyZzMyZ/8zMAMzMM8zMZszMmczMzMzM/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8zAP8zM/8zZv8zmf8zzP8z//9mAP9mM/9mZv9mmf9mzP9m//+ZAP+ZM/+ZZv+Zmf+ZzP+Z///MAP/MM//MZv/Mmf/MzP/M////AP//M///Zv//mf//zP///yH5BAEAABAALAAAAAABAAEAAAgEALkFBAA7"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should connect attachments by sub-directory name (foo.json => foo/)" do
|
57
|
+
@it.stub!(:mime_type)
|
58
|
+
|
59
|
+
everything = []
|
60
|
+
@it.each_document do |name, contents|
|
61
|
+
everything << [name, contents]
|
62
|
+
end
|
63
|
+
|
64
|
+
everything.
|
65
|
+
should include(['baz_with_attachments',
|
66
|
+
{'baz' => '3',
|
67
|
+
"_attachments" => { "spacer.gif" => {"data" => @spacer_b64} } }])
|
68
|
+
end
|
69
|
+
it "should mime 64 encode attachments" do
|
70
|
+
# covered above
|
71
|
+
end
|
72
|
+
it "should ignore non-file attachments" do
|
73
|
+
# covered above
|
74
|
+
end
|
75
|
+
|
76
|
+
context "infering mime type" do
|
77
|
+
before(:each) do
|
78
|
+
File.stub!(:read).and_return("asdf")
|
79
|
+
Base64.stub!(:encode64).and_return("asdf")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should guess gif mime type" do
|
83
|
+
@it.file_as_attachment("spacer.gif").
|
84
|
+
should == {
|
85
|
+
"data" => "asdf",
|
86
|
+
"content_type" => "image/gif"
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should guess jpeg mime type" do
|
91
|
+
@it.file_as_attachment("spacer.jpg").
|
92
|
+
should == {
|
93
|
+
"data" => "asdf",
|
94
|
+
"content_type" => "image/jpeg"
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should guess png mime type" do
|
99
|
+
@it.file_as_attachment("spacer.png").
|
100
|
+
should == {
|
101
|
+
"data" => "asdf",
|
102
|
+
"content_type" => "image/png"
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should default to no mime type" do
|
107
|
+
@it.file_as_attachment("spacer.foo").
|
108
|
+
should == {
|
109
|
+
"data" => "asdf"
|
110
|
+
}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should give precedence to filesystem attachments" do
|
115
|
+
@it.stub!(:mime_type)
|
116
|
+
|
117
|
+
JSON.stub!(:parse).
|
118
|
+
and_return({ "baz" => "3",
|
119
|
+
"_attachments" => {
|
120
|
+
"spacer.gif" => "asdf",
|
121
|
+
"baz.jpg" => "asdf"
|
122
|
+
}
|
123
|
+
})
|
124
|
+
|
125
|
+
everything = []
|
126
|
+
@it.each_document do |name, contents|
|
127
|
+
everything << [name, contents]
|
128
|
+
end
|
129
|
+
|
130
|
+
everything.
|
131
|
+
should include(['baz_with_attachments',
|
132
|
+
{'baz' => '3',
|
133
|
+
"_attachments" => { "spacer.gif" => {"data" => @spacer_b64}, "baz.jpg" => "asdf" } }])
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
context "dump attachments from CouchDB" do
|
138
|
+
before(:each) do
|
139
|
+
FileUtils.stub!(:mkdir_p)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should store attachments" do
|
143
|
+
file = mock("File").as_null_object
|
144
|
+
File.stub!(:new).and_return(file)
|
145
|
+
|
146
|
+
@it.
|
147
|
+
should_receive(:store_attachments).
|
148
|
+
with('foo', 'bar')
|
149
|
+
|
150
|
+
@it.store_document({'_id' => 'foo',
|
151
|
+
'_attachments' => 'bar'})
|
152
|
+
end
|
153
|
+
|
154
|
+
context "storing attachments" do
|
155
|
+
before(:each) do
|
156
|
+
@attachments = { 'foo.txt' => { 'data' => 'attachment data' } }
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should make a directory to hold the attachments" do
|
160
|
+
@it.
|
161
|
+
should_receive(:make_attachment_dir).
|
162
|
+
with('foo')
|
163
|
+
|
164
|
+
@it.stub!(:save_attachment)
|
165
|
+
@it.store_attachments('foo', @attachments)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should create a sub-directory with document ID" do
|
169
|
+
FileUtils.
|
170
|
+
should_receive(:mkdir_p).
|
171
|
+
with("fixtures/foo")
|
172
|
+
|
173
|
+
@it.stub!(:save_attachment)
|
174
|
+
@it.make_attachment_dir('foo')
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should save attachments to the filesystem" do
|
178
|
+
@it.
|
179
|
+
should_receive(:save_attachment).
|
180
|
+
with('foo', 'foo.txt', 'attachment data')
|
181
|
+
|
182
|
+
@it.stub!(:save_attachment)
|
183
|
+
@it.store_attachments('foo', @attachments)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should dump with native encoding (non-mime64)" do
|
187
|
+
file = mock("File").as_null_object
|
188
|
+
File.stub!(:new).and_return(file)
|
189
|
+
|
190
|
+
file.
|
191
|
+
should_receive(:write)
|
192
|
+
|
193
|
+
@it.save_attachment('foo', 'foo.txt', 'ZGF0YQ==')
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should not include the attachments attribute" do
|
198
|
+
file = mock("File", :close => true)
|
199
|
+
File.stub!(:new).and_return(file)
|
200
|
+
|
201
|
+
file.
|
202
|
+
should_receive(:write).
|
203
|
+
with('{"_id":"foo"}')
|
204
|
+
|
205
|
+
@it.stub!(:store_attachments)
|
206
|
+
|
207
|
+
@it.store_document({'_id' => 'foo',
|
208
|
+
'_attachments' => 'foo'})
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe CouchDocs::Store do
|
4
|
+
it "should require a CouchDB URL Root for instantiation" do
|
5
|
+
lambda { CouchDocs::Store.new }.
|
6
|
+
should raise_error
|
7
|
+
|
8
|
+
lambda { CouchDocs::Store.new("uri") }.
|
9
|
+
should_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
context "a valid store" do
|
13
|
+
before(:each) do
|
14
|
+
@it = CouchDocs::Store.new("uri")
|
15
|
+
|
16
|
+
@hash = {
|
17
|
+
'a' => {
|
18
|
+
'b' => {
|
19
|
+
'c' => 'function(doc) { return true; }'
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be able to put a new document" do
|
26
|
+
CouchDocs::Store.
|
27
|
+
should_receive(:put).
|
28
|
+
with("uri", { })
|
29
|
+
|
30
|
+
CouchDocs::Store.put!("uri", { })
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should delete existing docs if first put fails" do
|
34
|
+
CouchDocs::Store.
|
35
|
+
stub!(:put).
|
36
|
+
and_raise(RestClient::RequestFailed)
|
37
|
+
|
38
|
+
CouchDocs::Store.
|
39
|
+
should_receive(:delete_and_put).
|
40
|
+
with("uri", { })
|
41
|
+
|
42
|
+
CouchDocs::Store.put!("uri", { })
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be able to delete and put" do
|
46
|
+
CouchDocs::Store.
|
47
|
+
should_receive(:delete).
|
48
|
+
with("uri")
|
49
|
+
|
50
|
+
CouchDocs::Store.
|
51
|
+
should_receive(:put).
|
52
|
+
with("uri", { })
|
53
|
+
|
54
|
+
CouchDocs::Store.delete_and_put("uri", { })
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should be able to load a hash into design docs" do
|
58
|
+
RestClient.
|
59
|
+
should_receive(:put).
|
60
|
+
with("uri/_design/a",
|
61
|
+
'{"b":{"c":"function(doc) { return true; }"}}',
|
62
|
+
:content_type => 'application/json')
|
63
|
+
@it.put_design_documents(@hash)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be able to retrieve an existing document" do
|
67
|
+
RestClient.
|
68
|
+
stub!(:get).
|
69
|
+
and_return('{"_rev":"1234"}')
|
70
|
+
|
71
|
+
CouchDocs::Store.get("uri").should == { '_rev' => "1234" }
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should be able to delete an existing document" do
|
75
|
+
CouchDocs::Store.stub!(:get).and_return({ '_rev' => '1234' })
|
76
|
+
|
77
|
+
RestClient.
|
78
|
+
should_receive(:delete).
|
79
|
+
with("uri?rev=1234")
|
80
|
+
|
81
|
+
CouchDocs::Store.delete("uri")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should be able to load each document" do
|
85
|
+
CouchDocs::Store.stub!(:get).
|
86
|
+
with("uri/_all_docs").
|
87
|
+
and_return({ "total_rows" => 2,
|
88
|
+
"offset" => 0,
|
89
|
+
"rows" => [{"id"=>"1", "value"=>{}, "key"=>"1"},
|
90
|
+
{"id"=>"2", "value"=>{}, "key"=>"2"}]})
|
91
|
+
|
92
|
+
CouchDocs::Store.stub!(:get).with("uri/1?attachments=true")
|
93
|
+
CouchDocs::Store.should_receive(:get).with("uri/2?attachments=true")
|
94
|
+
|
95
|
+
@it.each { }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|