fauna 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,8 +43,12 @@ module Fauna
43
43
  # Construct a ref value
44
44
  #
45
45
  # Reference: {FaunaDB Values}[https://faunadb.com/documentation/queries#values]
46
- def ref(*args)
47
- Ref.new(*args)
46
+ def ref(str, id = nil)
47
+ if id.nil?
48
+ Ref.new(str)
49
+ else
50
+ Expr.new ref: Expr.wrap(str), id: Expr.wrap(id)
51
+ end
48
52
  end
49
53
 
50
54
  ##
@@ -134,6 +138,7 @@ module Fauna
134
138
  # If this takes more than one argument, the lambda destructures an array argument.
135
139
  # (To destructure single-element arrays use #lambda_expr.)
136
140
  def lambda(&block)
141
+ dsl = Query::QueryDSLContext.new
137
142
  vars =
138
143
  block.parameters.map do |kind, name|
139
144
  fail ArgumentError, 'Splat parameters are not supported in lambda expressions.' if kind == :rest
@@ -145,9 +150,9 @@ module Fauna
145
150
  fail ArgumentError, 'Block must take at least 1 argument.'
146
151
  when 1
147
152
  # When there's only 1 parameter, don't use an array pattern.
148
- lambda_expr vars[0], block.call(var(vars[0]))
153
+ lambda_expr vars[0], DSLContext.eval_dsl(dsl, var(vars[0]), &block)
149
154
  else
150
- lambda_expr vars, block.call(*(vars.map { |v| var(v) }))
155
+ lambda_expr vars, DSLContext.eval_dsl(dsl, *(vars.map { |v| var(v) }), &block)
151
156
  end
152
157
  end
153
158
 
@@ -441,6 +446,14 @@ module Fauna
441
446
 
442
447
  # :section: Miscellaneous Functions
443
448
 
449
+ ##
450
+ # A next_id function
451
+ #
452
+ # Reference: {FaunaDB Miscellaneous Functions}[https://faunadb.com/documentation#queries-misc_functions]
453
+ def next_id
454
+ Expr.new next_id: nil
455
+ end
456
+
444
457
  ##
445
458
  # An equals function
446
459
  #
@@ -575,6 +588,13 @@ module Fauna
575
588
 
576
589
  alias_method :inspect, :to_s
577
590
 
591
+ def ==(other)
592
+ return false unless other.is_a? Expr
593
+ raw == other.raw
594
+ end
595
+
596
+ alias_method :eql?, :==
597
+
578
598
  def self.wrap(obj)
579
599
  if obj.is_a? Expr
580
600
  obj
@@ -16,7 +16,7 @@ module Fauna
16
16
  end
17
17
 
18
18
  class DSLContext # :nodoc:
19
- def self.eval_dsl(dsl, &blk)
19
+ def self.eval_dsl(dsl, *args, &blk)
20
20
  ctx = eval('self', blk.binding)
21
21
  dsl.instance_variable_set(:@__ctx__, ctx)
22
22
 
@@ -24,7 +24,7 @@ module Fauna
24
24
  dsl.instance_variable_set(iv, ctx.instance_variable_get(iv))
25
25
  end
26
26
 
27
- dsl.instance_exec(&blk)
27
+ dsl.instance_exec(*args, &blk)
28
28
 
29
29
  ensure
30
30
  dsl.instance_variables.each do |iv|
@@ -1,4 +1,4 @@
1
1
  module Fauna
2
2
  # The version of the Fauna gem
3
- VERSION = '2.0.0'
3
+ VERSION = '2.1.0'.freeze
4
4
  end
@@ -36,7 +36,7 @@ RSpec.describe Fauna::Client do
36
36
 
37
37
  describe 'serialization' do
38
38
  it 'decodes ref' do
39
- ref = Fauna::Ref.new('classes', random_string, random_number)
39
+ ref = Fauna::Ref.new("classes/#{random_string}/#{random_number}")
40
40
  test_client = stub_client(:get, 'tests/ref', to_json(resource: ref))
41
41
 
42
42
  response = test_client.get('tests/ref')
@@ -45,7 +45,7 @@ RSpec.describe Fauna::Client do
45
45
  end
46
46
 
47
47
  it 'decodes set' do
48
- set = Fauna::SetRef.new(match: random_string, index: Fauna::Ref.new('indexes', random_string))
48
+ set = Fauna::SetRef.new(match: Fauna::Ref.new("indexes/#{random_string}"), terms: random_string)
49
49
  test_client = stub_client(:get, 'tests/set', to_json(resource: set))
50
50
 
51
51
  response = test_client.get('tests/set')
@@ -68,6 +68,14 @@ RSpec.describe Fauna::Context do
68
68
  end
69
69
  end
70
70
 
71
+ describe '#paginate' do
72
+ it 'performs paginate' do
73
+ Fauna::Context.block(client) do
74
+ expect(Fauna::Context.paginate(Fauna::Query.expr { ref('classes') }).data).to eq([@test_class])
75
+ end
76
+ end
77
+ end
78
+
71
79
  describe '#push' do
72
80
  it 'pushes new client' do
73
81
  new = Fauna::Client.new
@@ -140,5 +140,9 @@ RSpec.describe 'Fauna Errors' do
140
140
  it 'raised for bad errors format' do
141
141
  expect { stub_get(500, '{"errors": true}').get('') }.to raise_error(Fauna::UnexpectedError, /unexpected format/)
142
142
  end
143
+
144
+ it 'raised for empty errors' do
145
+ expect { stub_get(500, '{"errors": []}').get('') }.to raise_error(Fauna::UnexpectedError, /blank/)
146
+ end
143
147
  end
144
148
  end
@@ -24,7 +24,7 @@ module FaunaTestHelpers
24
24
  end
25
25
 
26
26
  def create_test_db
27
- @db_ref = Fauna::Ref.new 'databases', "faunadb-ruby-test-#{random_string}"
27
+ @db_ref = Fauna::Ref.new "databases/faunadb-ruby-test-#{random_string}"
28
28
 
29
29
  root = root_client
30
30
  root.query { create ref('databases'), name: @db_ref.id }
@@ -65,14 +65,10 @@ module FaunaTestHelpers
65
65
  SecureRandom.random_number(1_000_000)
66
66
  end
67
67
 
68
- def random_ref
68
+ def random_ref_string
69
69
  "classes/#{random_string}/#{random_number}"
70
70
  end
71
71
 
72
- def random_class_ref
73
- "classes/#{random_string}"
74
- end
75
-
76
72
  def to_json(value)
77
73
  Fauna::FaunaJson.to_json(value)
78
74
  end
@@ -81,7 +77,7 @@ module FaunaTestHelpers
81
77
  Fauna::FaunaJson.json_load(value)
82
78
  end
83
79
 
84
- def wait_for_active(ref)
85
- sleep 1 until client.query { get(ref) }[:active]
80
+ def wait_for_index(*refs)
81
+ sleep 1 until client.query { map(refs) { |ref| select([:active], get(ref)) } }.all? { |active| active }
86
82
  end
87
83
  end
@@ -1,7 +1,7 @@
1
1
  RSpec.describe Fauna::FaunaJson do
2
2
  describe '#deserialize' do
3
3
  it 'deserializes ref' do
4
- ref = random_ref
4
+ ref = random_ref_string
5
5
 
6
6
  data = { :@ref => ref }
7
7
  obj = Fauna::Ref.new(ref)
@@ -10,7 +10,7 @@ RSpec.describe Fauna::FaunaJson do
10
10
  end
11
11
 
12
12
  it 'deserializes set' do
13
- ref = random_ref
13
+ ref = random_ref_string
14
14
  terms = random_string
15
15
 
16
16
  data = { :@set => { match: { :@ref => ref }, terms: terms } }
@@ -41,7 +41,7 @@ RSpec.describe Fauna::FaunaJson do
41
41
  end
42
42
 
43
43
  it 'recursively deserializes hashes' do
44
- ref = random_ref
44
+ ref = random_ref_string
45
45
 
46
46
  data = { test: { :@obj => { :@ref => ref } } }
47
47
  obj = { test: Fauna::Ref.new(ref) }
@@ -50,8 +50,8 @@ RSpec.describe Fauna::FaunaJson do
50
50
  end
51
51
 
52
52
  it 'recursively deserializes arrays' do
53
- ref1 = random_ref
54
- ref2 = random_ref
53
+ ref1 = random_ref_string
54
+ ref2 = random_ref_string
55
55
 
56
56
  data = [{ :@ref => ref1 }, { :@ref => ref2 }]
57
57
  obj = [Fauna::Ref.new(ref1), Fauna::Ref.new(ref2)]
@@ -62,7 +62,7 @@ RSpec.describe Fauna::FaunaJson do
62
62
 
63
63
  describe '#to_json' do
64
64
  it 'serializes ref' do
65
- ref = random_ref
65
+ ref = random_ref_string
66
66
 
67
67
  data = { :@ref => ref }
68
68
  obj = Fauna::Ref.new(ref)
@@ -71,7 +71,7 @@ RSpec.describe Fauna::FaunaJson do
71
71
  end
72
72
 
73
73
  it 'serializes set' do
74
- ref = random_ref
74
+ ref = random_ref_string
75
75
 
76
76
  data = { :@ref => ref }
77
77
  obj = Fauna::Ref.new(ref)
@@ -101,7 +101,7 @@ RSpec.describe Fauna::FaunaJson do
101
101
  end
102
102
 
103
103
  it 'recursively serializes hashes' do
104
- ref = random_ref
104
+ ref = random_ref_string
105
105
  terms = random_string
106
106
 
107
107
  data = { a: { time: { :@ts => '1970-01-01T00:00:00.000000000Z' } }, b: { :@set => { match: { :@ref => ref }, terms: terms } } }
@@ -111,8 +111,8 @@ RSpec.describe Fauna::FaunaJson do
111
111
  end
112
112
 
113
113
  it 'recursively serializes arrays' do
114
- ref1 = random_ref
115
- ref2 = random_ref
114
+ ref1 = random_ref_string
115
+ ref2 = random_ref_string
116
116
 
117
117
  data = [{ :@ref => ref1 }, { :@ref => ref2 }]
118
118
  obj = [Fauna::Ref.new(ref1), Fauna::Ref.new(ref2)]
@@ -0,0 +1,357 @@
1
+ RSpec.describe Fauna::Page do
2
+ before(:all) do
3
+ create_test_db
4
+ @test_class = client.query { create ref('classes'), name: 'page_test' }[:ref]
5
+ @foreach_class = client.query { create ref('classes'), name: 'page_foreach' }[:ref]
6
+
7
+ index_refs = client.query { create ref('indexes'), name: 'page_refs', source: @test_class }
8
+ index_values = client.query { create ref('indexes'), name: 'page_values', source: @test_class, values: [{ path: 'data.value' }] }
9
+ index_foreach = client.query { create ref('indexes'), name: 'page_apply', source: @foreach_class }
10
+
11
+ wait_for_index(index_refs[:ref], index_values[:ref], index_foreach[:ref])
12
+
13
+ @refs_index = index_refs[:ref]
14
+ @values_index = index_values[:ref]
15
+ @foreach_index = index_foreach[:ref]
16
+
17
+ @instances = client.query { (1..6).collect { |x| create(@test_class, data: { value: x }) } }.sort_by { |inst| inst[:ref].id }
18
+ @instance_refs = @instances.collect { |instance| instance[:ref] }
19
+ @instance_values = @instances.collect { |instance| instance[:data][:value] }.sort
20
+
21
+ @refs_match = Fauna::Query.match(@refs_index)
22
+ @values_match = Fauna::Query.match(@values_index)
23
+ @foreach_match = Fauna::Query.match(@foreach_index)
24
+ end
25
+
26
+ after(:all) do
27
+ destroy_test_db
28
+ end
29
+
30
+ describe '#==' do
31
+ it 'equals identical page' do
32
+ page1 = Fauna::Page.new(client, @refs_match, size: 1)
33
+ page2 = Fauna::Page.new(client, @refs_match, size: 1)
34
+
35
+ expect(page1).to eq(page2)
36
+ end
37
+
38
+ it 'does not equal different page' do
39
+ page1 = Fauna::Page.new(client, @refs_match, size: 1234)
40
+ page2 = Fauna::Page.new(client, @refs_match, size: 4321)
41
+
42
+ expect(page1).not_to eq(page2)
43
+ end
44
+ end
45
+
46
+ it 'can\'t mutate params directly' do
47
+ page = client.paginate(@refs_match)
48
+
49
+ expect { page.params[:ts] = random_number }.to raise_error(RuntimeError, 'can\'t modify frozen Hash')
50
+
51
+ page = page.with_params(ts: random_number)
52
+
53
+ expect { page.params[:ts] = random_number }.to raise_error(RuntimeError, 'can\'t modify frozen Hash')
54
+ end
55
+
56
+ describe 'builders' do
57
+ def get_funcs(page)
58
+ page.instance_variable_get(:@fauna_funcs)
59
+ end
60
+
61
+ def get_postprocessing(page)
62
+ page.instance_variable_get(:@postprocessing_map)
63
+ end
64
+
65
+ describe '#with_params' do
66
+ let(:ref1) { random_ref_string }
67
+ let(:ref2) { random_ref_string }
68
+
69
+ it 'sets params on copy' do
70
+ ts1 = random_number
71
+ ts2 = random_number
72
+
73
+ page = client.paginate(@refs_match, ts: ts1)
74
+
75
+ expect(page.with_params(ts: ts2, sources: false).params).to eq(ts: ts2, sources: false)
76
+ expect(page.params).to eq(ts: ts1)
77
+ end
78
+
79
+ it 'reverses cursor' do
80
+ page = client.paginate(@refs_match, before: ref1)
81
+
82
+ expect(page.with_params(after: ref2).params).to eq(after: ref2)
83
+ expect(page.params).to eq(before: ref1)
84
+ end
85
+
86
+ it 'preserves nil' do
87
+ page = client.paginate(@refs_match, after: nil)
88
+
89
+ expect(page.with_params(before: nil).params).to eq(before: nil)
90
+ expect(page.params).to eq(after: nil)
91
+ end
92
+
93
+ it 'resets paging' do
94
+ page = client.paginate(@refs_match, size: 1)
95
+ page1 = page.page_after
96
+
97
+ page2 = page1.with_params(after: 0).page_after
98
+
99
+ expect(page2.data).to eq(page2.data)
100
+ end
101
+ end
102
+
103
+ describe '#map' do
104
+ it 'sets map on copy' do
105
+ page = client.paginate(@refs_match)
106
+
107
+ expect(get_funcs(page.map { |ref| get ref }).length).to be(1)
108
+ expect(get_funcs(page).length).to be(0)
109
+ end
110
+
111
+ it 'performs map when paging' do
112
+ page = client.paginate(@refs_match).map { |ref| get ref }
113
+
114
+ expect(page.all).to eq(@instances)
115
+ end
116
+ end
117
+
118
+ describe '#filter' do
119
+ it 'sets filter on copy' do
120
+ page = client.paginate(@values_match)
121
+
122
+ expect(get_funcs(page.filter { |value| equals(modulo(value, 2), 0) }).length).to be(1)
123
+ expect(get_funcs(page).length).to be(0)
124
+ end
125
+
126
+ it 'performs filter when paging' do
127
+ page = client.paginate(@values_match).filter { |value| equals(modulo(value, 2), 0) }
128
+
129
+ expect(page.all).to eq(@instance_values.find_all(&:even?))
130
+ end
131
+ end
132
+
133
+ describe '#postprocessing_map' do
134
+ it 'sets ruby map on copy' do
135
+ page = client.paginate(@refs_match)
136
+
137
+ expect(get_postprocessing(page.postprocessing_map(&:id))).to be_a(Proc)
138
+ expect(get_postprocessing(page)).to be_nil
139
+ end
140
+ end
141
+ end
142
+
143
+ describe '#load!' do
144
+ it 'explicitly loads page' do
145
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
146
+ expected = [@instance_refs[1]]
147
+
148
+ expect(page.instance_variable_get(:@data)).to be_nil
149
+ page.load!
150
+ expect(page.instance_variable_get(:@data)).to eq(expected)
151
+ end
152
+
153
+ it 'returns true when page was loaded' do
154
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
155
+
156
+ expect(page.instance_variable_get(:@populated)).to be(false)
157
+ expect(page.load!).to be(true)
158
+ expect(page.instance_variable_get(:@populated)).to be(true)
159
+ end
160
+
161
+ it 'returns false when page not loaded' do
162
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
163
+
164
+ page.load!
165
+ expect(page.instance_variable_get(:@populated)).to be(true)
166
+ expect(page.load!).to be(false)
167
+ end
168
+ end
169
+
170
+ describe '#data' do
171
+ it 'lazily loads page' do
172
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
173
+ expected = [@instance_refs[1]]
174
+
175
+ expect(page.instance_variable_get(:@data)).to be_nil
176
+ expect(page.data).to eq(expected)
177
+ expect(page.instance_variable_get(:@data)).to eq(expected)
178
+ end
179
+ end
180
+
181
+ describe '#before' do
182
+ it 'lazily loads page' do
183
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
184
+ expected = [@instance_refs[1]]
185
+
186
+ expect(page.instance_variable_get(:@before)).to be_nil
187
+ expect(page.before).to eq(expected)
188
+ expect(page.instance_variable_get(:@before)).to eq(expected)
189
+ end
190
+ end
191
+
192
+ describe '#after' do
193
+ it 'lazily loads page' do
194
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs[1])
195
+ expected = [@instance_refs[2]]
196
+
197
+ expect(page.instance_variable_get(:@after)).to be_nil
198
+ expect(page.after).to eq(expected)
199
+ expect(page.instance_variable_get(:@after)).to eq(expected)
200
+ end
201
+ end
202
+
203
+ describe '#page_after' do
204
+ it 'returns the page after' do
205
+ page = client.paginate(@refs_match, size: 1, after: 0)
206
+
207
+ @instance_refs.drop(1).each do |ref|
208
+ page = page.page_after
209
+ expect(page.data.first).to eq(ref)
210
+ end
211
+ end
212
+
213
+ it 'returns nil on last page' do
214
+ page = client.paginate(@refs_match, size: 1, after: @instance_refs.last)
215
+
216
+ expect(page.data.first).to eq(@instance_refs.last)
217
+ expect(page.page_after).to be_nil
218
+ end
219
+
220
+ it 'is not affected by lazy loading' do
221
+ page = client.paginate(@refs_match, size: 1, after: 0)
222
+
223
+ expect(page.data.first).to eq(@instance_refs[0])
224
+ expect(page.page_after.data.first).to eq(@instance_refs[1])
225
+
226
+ page = client.paginate(@refs_match, size: 1, after: 0)
227
+
228
+ expect(page.page_after.data.first).to eq(@instance_refs[1])
229
+ end
230
+ end
231
+
232
+ describe '#page_before' do
233
+ it 'returns the page before' do
234
+ page = client.paginate(@refs_match, size: 1, before: nil)
235
+
236
+ @instance_refs.reverse.drop(1).each do |ref|
237
+ page = page.page_before
238
+ expect(page.data.first).to eq(ref)
239
+ end
240
+ end
241
+
242
+ it 'returns nil on last page' do
243
+ page = client.paginate(@refs_match, size: 1, before: @instance_refs.first)
244
+
245
+ expect(page.page_before).to be_nil
246
+ end
247
+
248
+ it 'is not affected by lazy loading' do
249
+ page = client.paginate(@refs_match, size: 1, before: nil)
250
+
251
+ expect(page.data.first).to eq(@instance_refs[-1])
252
+ expect(page.page_before.data.first).to eq(@instance_refs[-2])
253
+
254
+ page = client.paginate(@refs_match, size: 1, before: nil)
255
+
256
+ expect(page.page_before.data.first).to eq(@instance_refs[-2])
257
+ end
258
+ end
259
+
260
+ it 'pages both directions' do
261
+ page = client.paginate(@refs_match, size: 1, after: 0)
262
+ expect(page.data.first).to eq(@instance_refs[0])
263
+
264
+ page = page.page_after
265
+ expect(page.data.first).to eq(@instance_refs[1])
266
+
267
+ page = page.page_before
268
+ expect(page.data.first).to eq(@instance_refs[0])
269
+ end
270
+
271
+ describe '#each' do
272
+ it 'iterates the set in the after direction' do
273
+ page = client.paginate(@refs_match, size: 1)
274
+ refs = @instance_refs.collect { |ref| [ref] }
275
+
276
+ expect(page.each.collect { |ref| ref }).to eq(refs)
277
+ end
278
+
279
+ it 'is not affected by lazy loading' do
280
+ page = client.paginate(@refs_match, size: 1)
281
+ refs = @instance_refs.collect { |ref| [ref] }
282
+
283
+ expect(page.each.collect { |ref| ref }).to eq(refs)
284
+
285
+ page = client.paginate(@refs_match, size: 1)
286
+ refs = @instance_refs.collect { |ref| [ref] }
287
+
288
+ expect(page.data).to eq([@instance_refs.first])
289
+ expect(page.each.collect { |ref| ref }).to eq(refs)
290
+ end
291
+
292
+ context 'with fauna map' do
293
+ it 'iterates the set using the fauna map' do
294
+ page = client.paginate(@refs_match, size: 1) { |ref| get(ref) }
295
+ instances = @instances.collect { |inst| [inst] }
296
+
297
+ expect(page.each.collect { |inst| inst }).to eq(instances)
298
+ end
299
+
300
+ it 'chains multiple collection functions' do
301
+ page = client.paginate(@refs_match, size: 1)
302
+ # Map ref to value
303
+ page = page.map { |ref| select(['data', 'value'], get(ref)) }
304
+ # Filter out odd numbers
305
+ page = page.filter { |value| equals(modulo(value, 2), 0) }
306
+ # Map to double the value
307
+ page = page.map { |value| multiply(value, 2) }
308
+
309
+ # We are using the index on refs, not value, so we need to expect the values to be sorted by instance ref
310
+ expected = @instances.collect { |inst| inst[:data][:value] }.find_all(&:even?).collect { |v| v * 2 }
311
+
312
+ expect(page.all).to eq(expected)
313
+ end
314
+ end
315
+
316
+ context 'with ruby map' do
317
+ it 'iterates the set using the ruby map' do
318
+ page = client.paginate(@refs_match, size: 1).postprocessing_map(&:id)
319
+ ids = @instance_refs.collect { |ref| [ref.id] }
320
+
321
+ expect(page.each.collect { |id| id }).to eq(ids)
322
+ end
323
+ end
324
+ end
325
+
326
+ describe '#reverse_each' do
327
+ it 'iterates the set in the before direction' do
328
+ page = client.paginate(@refs_match, size: 1, before: nil)
329
+ refs = @instance_refs.reverse.collect { |ref| [ref] }
330
+
331
+ expect(page.reverse_each.collect { |ref| ref }).to eq(refs)
332
+ end
333
+ end
334
+
335
+ describe '#all' do
336
+ it 'returns full contents of the set' do
337
+ page = client.paginate(@refs_match, size: 1)
338
+
339
+ expect(page.all).to eq(@instance_refs)
340
+ end
341
+ end
342
+
343
+ describe '#foreach!' do
344
+ before(:each) do
345
+ @apply_refs = client.query { (1..3).collect { |x| select([:ref], create(@foreach_class, data: { value: x })) } }
346
+ end
347
+
348
+ it 'applies foreach to set' do
349
+ # Sanity
350
+ expect(client.query { map(@apply_refs) { |ref| exists ref } }).to eq(@apply_refs.collect { true })
351
+
352
+ client.paginate(@foreach_match, size: 1).foreach! { |ref| delete ref }
353
+
354
+ expect(client.query { map(@apply_refs) { |ref| exists ref } }).to eq(@apply_refs.collect { false })
355
+ end
356
+ end
357
+ end