opal-pouchdb 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ module PouchDB
2
+ class EventEmitter
3
+ include Conversion
4
+
5
+ def initialize(stream)
6
+ @native = stream
7
+ end
8
+
9
+ def on(event, &blk)
10
+ %x{
11
+ #{@native}.on(event, function(change) {
12
+ #{blk.call(OBJECT_CONVERSION.call(`change`))}
13
+ })
14
+ }
15
+
16
+ self
17
+ end
18
+
19
+ def then
20
+ as_opal_promise(`#{@native}`)
21
+ end
22
+
23
+ def cancel
24
+ `#{@native}.cancel()`
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ module PouchDB
2
+ class Replication
3
+ include Conversion
4
+
5
+ def initialize(native)
6
+ @native = native
7
+ end
8
+
9
+ def to(db, options = {})
10
+ EventEmitter.new(`#{@native}.replicate.to(#{database_as_string(db)}, #{options.to_n})`)
11
+ end
12
+
13
+ def from(db, options = {})
14
+ EventEmitter.new(`#{@native}.replicate.from(#{database_as_string(db)}, #{options.to_n})`)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module PouchDB
2
+ class Row
3
+ include Native
4
+
5
+ def initialize(native_row)
6
+ super native_row
7
+ end
8
+
9
+ native_reader :id, :key, :value
10
+
11
+ def document
12
+ Hash.new(`#{@native}.doc`)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,81 @@
1
+ require "spec_helper"
2
+
3
+ describe "PouchDB::Database#changes" do
4
+ describe "changes" do
5
+ async "calls a block when a 'change' event is emitted" do
6
+ with_new_database do |db|
7
+ db.post(name: "I Change Things")
8
+ db.post(name: "I Change Things Too")
9
+
10
+ stream = db.changes
11
+ count = 0
12
+ stream.on "change" do count += 1 end
13
+
14
+ delayed(1.2) do |p|
15
+ p.resolve(count == 2)
16
+ end.then do |c|
17
+ async do
18
+ expect(c).to be(true)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "cancellation" do
26
+ async "calls a block when the 'complete' event is emitted" do
27
+ with_new_database do |db|
28
+ stream = db.changes
29
+ cancelled = false
30
+ stream.on "complete" do cancelled = true end
31
+
32
+ delayed(1) do |p|
33
+ p.resolve(cancelled)
34
+ end.then do |c|
35
+ async do
36
+ expect(c).to be(true)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "single-shot" do
44
+ # TODO: Make this pass. I think I'm missing some important
45
+ # point of the "Single-shot" mode.
46
+ pending "calls a block with all the changes" do
47
+ with_new_database do |db|
48
+ db.post(classification: "Important Stuff")
49
+ db.post(classification: "REALLY Important Stuff")
50
+
51
+ db.changes(limit: 1, since: 0).then do |cs|
52
+ async do
53
+ expect(cs).to eq(1)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ async "passes options along" do
61
+ with_new_database do |db|
62
+ db.post(name: "Ishmael")
63
+ db.post(name: "Fishmael")
64
+
65
+ changes = []
66
+ db.changes(include_docs: true).on "change" do |c|
67
+ changes << c
68
+ end
69
+
70
+ delayed(1.2) do |p|
71
+ p.resolve(changes)
72
+ end.then do |c|
73
+ async do
74
+ expect(c.size).to eq(2)
75
+ expect(c.first["doc"]["name"]).to eq("Ishmael")
76
+ expect(c.last["doc"]["name"]).to eq("Fishmael")
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,331 @@
1
+ require "spec_helper"
2
+ require "securerandom"
3
+
4
+ describe PouchDB::Database do
5
+ let(:docs) {
6
+ [
7
+ { name: "Banana", color: "yellow" },
8
+ { name: "Apple", color: "red" },
9
+ { name: "Green Apple", color: "green" }
10
+ ]
11
+ }
12
+
13
+ let(:docs_with_ids) {
14
+ docs.map { |d|
15
+ d.merge(_id: d[:name].downcase.gsub(/\s+/, "-"))
16
+ }
17
+ }
18
+
19
+ let(:sorted_ids) {
20
+ docs_with_ids.map { |d| d[:_id] }.sort
21
+ }
22
+
23
+ describe "#initialize" do
24
+ it "requires a name" do
25
+ expect { PouchDB::Database.new }.to raise_error(KeyError)
26
+ end
27
+ end
28
+
29
+ describe "#destroy" do
30
+ let(:database_name) { "throaway_test_database" }
31
+
32
+ async "calls the returned Promise's success handler" do
33
+ with_new_database do |db|
34
+ db.destroy.then do |response|
35
+ run_async do
36
+ expect(response["ok"]).to be(true)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#put" do
44
+ async "creating a new Document calls the returned Promise's success handler" do
45
+ with_new_database do |db|
46
+ promise = db.put(docs_with_ids.first)
47
+
48
+ promise.then do |response|
49
+ run_async do
50
+ expect(response).not_to be_nil
51
+ expect(response["rev"]).not_to be_nil
52
+ expect(response["id"]).to eq("banana")
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ async "updating an existing Document calls the returned Promise's success handler" do
59
+ with_new_database do |db|
60
+ db.put(docs_with_ids.first).then do |created|
61
+ update = { name: "Bananananas" }
62
+
63
+ db.put(update, doc_id: created["id"], doc_rev: created["rev"]).then do |updated|
64
+ run_async do
65
+ expect(updated["rev"]).not_to eq(created["rev"])
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ async "calls the returned Promise's error handler" do
73
+ with_new_database(false) do |db|
74
+ db.put(docs.first).fail do |error|
75
+ run_async do
76
+ expect(error.message).to match(/_id is required/)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "#post" do
84
+ async "posting new Document generates an id" do
85
+ with_new_database do |db|
86
+ promise = db.post(docs.first)
87
+
88
+ promise.then do |response|
89
+ run_async do
90
+ expect(response["rev"]).not_to be_nil
91
+ expect(response["id"]).not_to be_nil
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#bulk_docs" do
99
+ async "generates ids if Documents don't have them" do
100
+ with_new_database do |db|
101
+ db.bulk_docs(docs).then do |response|
102
+ run_async do
103
+ expect(response.size).to eq(3)
104
+ expect(response.all? { |r| r["ok"] }).to be(true)
105
+ expect(response.map { |r| r["id"] }.none?(&:empty?)).to be(true)
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ async "keeps passed-in ids if Documents have them" do
112
+ with_new_database do |db|
113
+ db.bulk_docs(docs_with_ids).then do |response|
114
+ run_async do
115
+ expect(response.map { |r| r["id"] }.sort).to eq(sorted_ids)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ async "updates sets of Documents" do
122
+ with_new_database do |db|
123
+ by_id = Hash[docs_with_ids.map { |d| [d[:_id], d] }]
124
+
125
+ db.bulk_docs(docs_with_ids).then do |created|
126
+ new_versions = created.map { |r|
127
+ d = by_id[r["id"]]
128
+ d.merge(_id: r["id"], _rev: r["rev"], name: d[:name].reverse)
129
+ }
130
+
131
+ created_by_id = Hash[created.map { |c| [c[:id], c] }]
132
+
133
+ db.bulk_docs(new_versions).then do |updated|
134
+ run_async do
135
+ updated.each do |ud|
136
+ cd = created_by_id[ud["id"]]
137
+ expect(ud["rev"]).not_to eq(cd["rev"])
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ async "mixes errors with successes (non-transactional)" do
146
+ with_new_database do |db|
147
+ db.put(docs_with_ids.first).then do
148
+ db.bulk_docs(docs_with_ids)
149
+ end.then do |response|
150
+ run_async do
151
+ errors = response.select { |r| r.is_a?(Exception) }
152
+ ok = response - errors
153
+
154
+ expect(ok.size).to eq(2)
155
+ expect(errors.size).to eq(1)
156
+ expect(errors.first.message).to match(/conflict/)
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ describe "#get" do
164
+ async "calls the returned Promise's success handler with a Document" do
165
+ with_new_database do |db|
166
+ db.put(_id: "magic_object", contents: "It's Magic").then do
167
+ db.get("magic_object")
168
+ end.then do |doc|
169
+ run_async do
170
+ expect(doc["_id"]).to eq("magic_object")
171
+ expect(doc["contents"]).to eq("It's Magic")
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ async "correctly serializes/deserializes nested Hashes" do
178
+ with_new_database do |db|
179
+ promise = db.put(_id: "nasty_nested",
180
+ contents: { foo: { bar: { baz: 1 } } })
181
+
182
+ promise.then do
183
+ db.get("nasty_nested")
184
+ end.then do |document|
185
+ run_async do
186
+ expect(document["contents"]["foo"]["bar"]["baz"]).to eq(1)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ describe "#all_docs" do
194
+ async "fetches every Document by default" do
195
+ with_new_database do |db|
196
+ db.bulk_docs(docs_with_ids).then do
197
+ db.all_docs
198
+ end.then do |rows|
199
+ run_async do
200
+ expect(rows.size).to eq(sorted_ids.size)
201
+ expect(rows.map(&:id).sort).to eq(sorted_ids)
202
+ expect(rows.first.document).to eq({})
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ # dodecaphonic: This is non-exhaustive. I just want to check if
209
+ # passing options actually goes through.
210
+ describe "passing options along" do
211
+ async "allows for full Documents to come with a Row" do
212
+ with_new_database do |db|
213
+ db.bulk_docs(docs_with_ids).then do
214
+ db.all_docs(include_docs: true)
215
+ end.then do |rows|
216
+ run_async do
217
+ expect(rows.first.document["name"]).not_to be_empty
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ async "can limit the number of Rows to return" do
224
+ with_new_database do |db|
225
+ db.bulk_docs(docs_with_ids).then do
226
+ db.all_docs(key: "banana").then do |rows|
227
+ run_async do
228
+ expect(rows.size).to eq(1)
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ describe "#remove" do
238
+ let(:doc) { docs_with_ids.first }
239
+
240
+ describe "with a Document containing an _id and _rev" do
241
+ async "works correctly" do
242
+ with_new_database do |db|
243
+ db.put(doc).then do |created|
244
+ run_async do
245
+ to_remove = { _id: created["id"], _rev: created["rev"] }
246
+ db.remove(doc: to_remove)
247
+ end
248
+ end
249
+ end.then do |removed|
250
+ run_async do
251
+ expect(removed["ok"]).to be(true)
252
+ end
253
+ end
254
+ end
255
+
256
+ async "fails if _id is missing" do
257
+ with_new_database(false) do |db|
258
+ db.put(doc).then do |created|
259
+ run_async do
260
+ to_remove = { _rev: created["rev"] }
261
+ db.remove(doc: to_remove)
262
+ end
263
+ end
264
+ end.fail do |error|
265
+ run_async do
266
+ expect(error.message).to match(/missing/)
267
+ end
268
+ end
269
+ end
270
+
271
+ async "fails if _rev is missing" do
272
+ with_new_database(false) do |db|
273
+ db.put(doc).then do |created|
274
+ run_async do
275
+ to_remove = { _id: created["id"] }
276
+ db.remove(doc: to_remove)
277
+ end
278
+ end
279
+ end.fail do |error|
280
+ run_async do
281
+ expect(error.message).to match(/missing/)
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ describe "passing its _id and _rev explicitly" do
288
+ async "works correctly" do
289
+ with_new_database do |db|
290
+ db.put(doc).then do |created|
291
+ run_async do
292
+ db.remove(doc_id: created["id"], doc_rev: created["rev"])
293
+ end
294
+ end
295
+ end.then do |removed|
296
+ run_async do
297
+ expect(removed.ok).to be(true)
298
+ end
299
+ end
300
+ end
301
+
302
+ async "fails if _id is missing" do
303
+ with_new_database(false) do |db|
304
+ db.put(doc).then do |created|
305
+ run_async do
306
+ db.remove(doc_rev: created["rev"])
307
+ end
308
+ end
309
+ end.fail do |error|
310
+ run_async do
311
+ expect(error.message).to match(/missing/)
312
+ end
313
+ end
314
+ end
315
+
316
+ async "fails if _rev is missing" do
317
+ with_new_database(false) do |db|
318
+ db.put(doc).then do |created|
319
+ run_async do
320
+ db.remove(doc_id: created["id"])
321
+ end
322
+ end
323
+ end.fail do |error|
324
+ run_async do
325
+ expect(error.message).to match(/missing/)
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
331
+ end