opal-pouchdb 0.1.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.
@@ -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