fauna 2.0.0 → 2.1.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.
@@ -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