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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9179efc5b2e53c99b6d398de61902fd963fefea7
4
- data.tar.gz: cfbee10e91d3cdfe4baabc212b34cfd1ae348ea9
3
+ metadata.gz: c9cd9dd6bfc9acdf8f25a0bfe186e4afcff76647
4
+ data.tar.gz: 5072c863a7172f7925aa15ad68f1af39e6004fee
5
5
  SHA512:
6
- metadata.gz: c143182304816bde8ed2b057cfd9e0c0a267e382e810c7c651139d7efcfe2aedee343fdae75c76816a4dfe90a775d6cdf921f295cc94f7c7241038fa06ed2587
7
- data.tar.gz: 534a5305cacdc340f870f975435c8a69cd0aa3647ddfbf4324226d3171f3ab0f16f1280e96e4551fc1cde9b6ee01691933a016f122be7ec25ecc658c802ae150
6
+ metadata.gz: 479a37575bccd657be309c20cfbdf15be951ad86a4694368280b74027c8fa70d02b069af82f20742e973fb658cc4d31a82701c2d350b3950f8ab104c21ba6136
7
+ data.tar.gz: beb72b4e1fde21d0e50f08157252484406c666e47fb1763f20186a09ec8c4e8b93a5515282e069c99b7c61613f067fa0c2e0ae81f4c2f147368489cd537a09de
data/CHANGELOG CHANGED
@@ -1,36 +1,7 @@
1
- v2.0.0 Complete rewrite for API 2.0. Not backwards compatible with the old client or api.
1
+ 2.1.0
2
+ * Added paginate helper.
3
+ * Improved exception messages (now include FaunaDB errors).
4
+ * Added `ref` and `next_id` query functions.
2
5
 
3
- v1.3.4 Index support.
4
-
5
- v1.3.3 Fix #61.
6
-
7
- v1.3.2 Fix patch.
8
-
9
- v1.3.1 Switch to Faraday http client.
10
-
11
- v1.3.0 Add support for PATCH, minor error handling improvements.
12
-
13
- v1.2.0 Switch to Typhoeus HTTP client.
14
-
15
- v1.1.1 Remove last bit of ORM-ness.
16
-
17
- v1.1.0 Updates for Fauna 1.1. Remove schema configuration.
18
-
19
- v0.2.6 Implement set paging.
20
-
21
- v0.2.5 Anonymous class configuration.
22
-
23
- v0.2.4 Event set query API.
24
-
25
- v0.2.3 Event set API enhancements.
26
-
27
- v0.2.2 Support for Fauna API version 1.
28
-
29
- v0.1.2 Fauna::User.find_by_external_id returns a User or nil, not an Array.
30
-
31
- v0.1.1 Fix ActionDispatch::Reloader bug.
32
-
33
- v0.1.1 Refactor resource hierarchy. Add cache. Flesh out ActiveModel
34
- support.
35
-
36
- v0.0.0 First release.
6
+ 2.0.0
7
+ * Complete rewrite for API 2.0. Not backwards compatible with the old client or api.
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # FaunaDB
2
2
 
3
- Ruby client for [FaunaDB](https://faunadb.com).
3
+ Ruby driver for [FaunaDB](https://faunadb.com).
4
4
 
5
5
  ## Installation
6
6
 
7
- The FaunaDB ruby client is distributed as a gem. Install it via:
7
+ The FaunaDB ruby driver is distributed as a gem. Install it via:
8
8
 
9
9
  $ gem install fauna
10
10
 
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.version = Fauna::VERSION
7
7
  s.author = 'Fauna, Inc.'
8
8
  s.email = 'priority@faunadb.com'
9
- s.summary = 'FaunaDB Ruby client'
10
- s.description = 'Ruby client for the Fauna distributed database.'
9
+ s.summary = 'FaunaDB Ruby driver'
10
+ s.description = 'Ruby driver for FaunaDB.'
11
11
  s.homepage = 'https://github.com/faunadb/faunadb-ruby'
12
12
  s.license = 'MPL-2.0'
13
13
 
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
20
20
  s.add_runtime_dependency 'faraday', '~> 0.9.0'
21
21
  s.add_runtime_dependency 'json', '~> 1.8'
22
22
  s.add_development_dependency 'rspec', '~> 3.4'
23
- s.add_development_dependency 'rubocop', '~> 0.35.0'
24
- s.add_development_dependency 'coveralls', '~> 0.8.10'
23
+ s.add_development_dependency 'rubocop', '~> 0.38.0'
24
+ s.add_development_dependency 'coveralls', '~> 0.8.13'
25
25
  end
@@ -19,4 +19,5 @@ require 'fauna/client_logger'
19
19
  require 'fauna/context'
20
20
  require 'fauna/objects'
21
21
  require 'fauna/query'
22
+ require 'fauna/page'
22
23
  require 'fauna/request_result'
@@ -90,6 +90,17 @@ module Fauna
90
90
  end
91
91
  end
92
92
 
93
+ ##
94
+ # Creates a Fauna::Page for paging/iterating over a set.
95
+ #
96
+ # +set+:: A set query to paginate over.
97
+ # +params+:: A list of parameters to pass to {paginate}[https://faunadb.com/documentation/queries#read_functions-paginate_set].
98
+ # +fauna_map+:: Optional block to wrap the generated paginate query with. The block will be run in a query context.
99
+ # The paginate query will be passed into the block as an argument.
100
+ def paginate(set, params = {}, &fauna_map)
101
+ Fauna::Page.new(self, set, params, &fauna_map)
102
+ end
103
+
93
104
  ##
94
105
  # Performs a +GET+ request for a REST endpoint.
95
106
  #
@@ -235,15 +246,15 @@ module Fauna
235
246
  @app.call(env).on_complete do |response_env|
236
247
  raw_body = response_env[:body]
237
248
  response_env[:body] =
238
- case response_env[:response_headers]['Content-Encoding']
239
- when 'gzip'
240
- io = StringIO.new raw_body
241
- Zlib::GzipReader.new(io, external_encoding: Encoding::UTF_8).read
242
- when 'deflate'
243
- Zlib::Inflate.inflate raw_body
244
- else
245
- raw_body
246
- end
249
+ case response_env[:response_headers]['Content-Encoding']
250
+ when 'gzip'
251
+ io = StringIO.new raw_body
252
+ Zlib::GzipReader.new(io, external_encoding: Encoding::UTF_8).read
253
+ when 'deflate'
254
+ Zlib::Inflate.inflate raw_body
255
+ else
256
+ raw_body
257
+ end
247
258
  end
248
259
  end
249
260
  end
@@ -118,6 +118,17 @@ module Fauna
118
118
  client.query(expression, &expr_block)
119
119
  end
120
120
 
121
+ ##
122
+ # Creates a Fauna::Page for paging/iterating over a set with the current client context.
123
+ #
124
+ # +set+:: A set query to paginate over.
125
+ # +params+:: A list of parameters to pass to {paginate}[https://faunadb.com/documentation/queries#read_functions-paginate_set].
126
+ # +fauna_map+:: Optional block to wrap the generated paginate query with. The block will be run in a query context.
127
+ # The paginate query will be passed into the block as an argument.
128
+ def self.paginate(set, params = {}, &fauna_map)
129
+ client.paginate(set, params, &fauna_map)
130
+ end
131
+
121
132
  ##
122
133
  # Returns the current context's client, or if there is none, raises NoContextError.
123
134
  def self.client
@@ -66,9 +66,25 @@ module Fauna
66
66
 
67
67
  if @errors.nil?
68
68
  fail UnexpectedError.new('Error data has an unexpected format.', request_result)
69
+ elsif @errors.empty?
70
+ fail UnexpectedError.new('Error data returned was blank.', request_result)
69
71
  end
70
72
 
71
- super(@errors ? @errors[0].description : '(empty `errors`)')
73
+ message = @errors.map do |error|
74
+ msg = 'Error'
75
+ msg += " at #{error.position}" unless error.position.nil?
76
+ msg += ": #{error.code} - #{error.description}"
77
+
78
+ unless error.failures.nil?
79
+ msg += ' (' + error.failures.map do |failure|
80
+ "Failure at #{failure.field}: #{failure.code} - #{failure.description}"
81
+ end.join(' ') + ')'
82
+ end
83
+
84
+ msg
85
+ end.join(' ')
86
+
87
+ super(message)
72
88
  end
73
89
  end
74
90
 
@@ -12,25 +12,23 @@ module Fauna
12
12
  #
13
13
  # :call-seq:
14
14
  # Ref.new('databases/prydain')
15
- # Ref.new('databases', 'prydain')
16
- # Ref.new(Ref.new('databases'), 'prydain')
17
15
  #
18
- # +parts+: A string, or a list of strings/refs to be joined.
19
- def initialize(*parts)
20
- @value = parts.join '/'
16
+ # +value+: A string.
17
+ def initialize(value)
18
+ @value = value
21
19
  end
22
20
 
23
21
  ##
24
22
  # Gets the class part out of the Ref.
25
23
  # This is done by removing ref.id().
26
- # So <code>Fauna::Ref.new('a', 'b/c').to_class</code> will be
24
+ # So <code>Fauna::Ref.new('a/b/c').to_class</code> will be
27
25
  # <code>Fauna::Ref.new('a/b')</code>.
28
26
  def to_class
29
27
  parts = value.split '/'
30
28
  if parts.length == 1
31
29
  self
32
30
  else
33
- Fauna::Ref.new(*parts[0...-1])
31
+ Fauna::Ref.new(parts[0...-1].join('/'))
34
32
  end
35
33
  end
36
34
 
@@ -0,0 +1,374 @@
1
+ module Fauna
2
+ ##
3
+ # Helper for handling pagination over sets.
4
+ #
5
+ # Given a client and a set, allows you to iterate as well as individually move page by page over a set.
6
+ #
7
+ # Pages lazily load the contents of the page. Loading will occur when +data+, +before+, or +after+ are first accessed
8
+ # for a new page. Additionally this will occur when calling +page_before+ or +page_after+ without calling one of the
9
+ # data methods first (as the first page must be checked to find the next page). Pages created by builders will unload
10
+ # any data from the current page. Pages will always proceed in the requested direction.
11
+ #
12
+ # Explicit paging is done via the +page_after+ and +page_before+ methods. Iteration can be done via the +each+ and
13
+ # +reverse_each+ enumerators. A single page can be retrieved by passing a cursor and then accessing it's data.
14
+ #
15
+ # Examples:
16
+ #
17
+ # Paging over a class index
18
+ #
19
+ # page = Page.new(client, Query.match(Ref('indexes/items')))
20
+ #
21
+ # Paging over a class index 5 at a time, mapping the refs to the +data.value+ for each instance
22
+ #
23
+ # page = Page.new(client, Query.match(Ref('indexes/items')), size: 5) do |ref|
24
+ # select ['data', 'value'], get(ref)
25
+ # end
26
+ #
27
+ # # Same thing, but using builders instead
28
+ #
29
+ # page = Page.new(client, Query.match(Ref('indexes/items'))).with_params(size: 5).map do |ref|
30
+ # select ['data', 'value'], get(ref)
31
+ # end
32
+ #
33
+ # Paging over a class index, mapping refs to the +data.value+ for each instance, filtering out odd numbers, and
34
+ # multiplying the value:
35
+ #
36
+ # page = Page.new(client, Query.match(Ref('indexes/items'))).map do |ref|
37
+ # select ['data', 'value'], get(ref)
38
+ # end.filter do |value|
39
+ # equals modulo(value, 2), 0
40
+ # end.map do |value|
41
+ # multiply value, 2
42
+ # end
43
+ class Page
44
+ ##
45
+ # Creates a pagination helper for paging/iterating over a set.
46
+ #
47
+ # +client+:: Client to execute queries with.
48
+ # +set+:: A set query to paginate over.
49
+ # +params+:: A list of parameters to pass to {paginate}[https://faunadb.com/documentation/queries#read_functions-paginate_set].
50
+ # +lambda+:: Optional lambda to map the generated paginate query with. The block will be run in a query context.
51
+ # An element from the current page will be passed into the block as an argument. See #map for more info.
52
+ def initialize(client, set, params = {}, &lambda)
53
+ @client = client
54
+ @set = set
55
+ @params = params.dup
56
+ @fauna_funcs = []
57
+ @postprocessing_map = nil
58
+
59
+ @fauna_funcs << proc { |query| map(query, &lambda) } unless lambda.nil?
60
+
61
+ unload_page
62
+ @params.freeze
63
+ end
64
+
65
+ # Returns +true+ if +other+ is a Page and contains the same configuration and data.
66
+ def ==(other)
67
+ return false unless other.is_a? Page
68
+ @populated == other.instance_variable_get(:@populated) &&
69
+ @data == other.instance_variable_get(:@data) &&
70
+ @before == other.instance_variable_get(:@before) &&
71
+ @after == other.instance_variable_get(:@after) &&
72
+ @client == other.instance_variable_get(:@client) &&
73
+ @set == other.instance_variable_get(:@set) &&
74
+ @params == other.instance_variable_get(:@params) &&
75
+ @fauna_funcs == other.instance_variable_get(:@fauna_funcs) &&
76
+ @postprocessing_map == other.instance_variable_get(:@postprocessing_map)
77
+ end
78
+
79
+ alias_method :eql?, :==
80
+
81
+ # The configured params used for the current pagination.
82
+ attr_reader :params
83
+
84
+ ##
85
+ # Explicitly loads data for the current page if it has not already been loaded.
86
+ #
87
+ # Returns +true+ if the data was just loaded and +false+ if it was already loaded.
88
+ def load!
89
+ if @populated
90
+ false
91
+ else
92
+ load_page(get_page(@params))
93
+ true
94
+ end
95
+ end
96
+
97
+ # :section: Data
98
+
99
+ ##
100
+ # Data contained within the current page.
101
+ #
102
+ # Lazily loads the page data if it has not already been loaded.
103
+ def data
104
+ load!
105
+ @data
106
+ end
107
+
108
+ ##
109
+ # Before cursor for the current page.
110
+ #
111
+ # Lazily loads the page data if it has not already been loaded.
112
+ def before
113
+ load!
114
+ @before
115
+ end
116
+
117
+ ##
118
+ # After cursor for the current page.
119
+ #
120
+ # Lazily loads the page data if it has not already been loaded.
121
+ def after
122
+ load!
123
+ @after
124
+ end
125
+
126
+ # :section: Builders
127
+
128
+ ##
129
+ # Returns a copy of the page with the given +params+ set.
130
+ #
131
+ # See {paginate}[https://faunadb.com/documentation/queries#read_functions-paginate_set] for more details.
132
+ def with_params(params = {})
133
+ with_dup do |page|
134
+ page_params = page.instance_variable_get(:@params)
135
+
136
+ if CURSOR_KEYS.any? { |key| params.include? key }
137
+ # Remove previous cursor
138
+ CURSOR_KEYS.each { |key| page_params.delete key }
139
+ end
140
+
141
+ # Update params
142
+ page_params.merge!(params)
143
+ end
144
+ end
145
+
146
+ ##
147
+ # Returns a copy of the page with a fauna +map+ using the given lambda chained onto the paginate query.
148
+ #
149
+ # The lambda will be passed into a +map+ function that wraps the generated paginate query. Additional collection
150
+ # functions may be combined by chaining them together.
151
+ #
152
+ # The lambda will be run in a Query.expr context, and passed an element from the current page as an argument.
153
+ #
154
+ # Example of mapping a set of refs to their instances:
155
+ #
156
+ # page.map { |ref| get ref }
157
+ def map(&lambda)
158
+ with_dup do |page|
159
+ page.instance_variable_get(:@fauna_funcs) << proc { |query| map(query, &lambda) }
160
+ end
161
+ end
162
+
163
+ ##
164
+ # Returns a copy of the page with a fauna +filter+ using the given lambda chained onto the paginate query.
165
+ #
166
+ # The lambda will be passed into a +filter+ function that wraps the generated paginate query. Additional collection
167
+ # functions may be combined by chaining them together.
168
+ #
169
+ # The lambda will be run in a Query.expr context, and passed an element from the current page as an argument.
170
+ #
171
+ # Example of filtering out odd numbers from a set of numbers:
172
+ #
173
+ # page.filter { |value| equals(modulo(value, 2), 0) }
174
+ def filter(&lambda)
175
+ with_dup do |page|
176
+ page.instance_variable_get(:@fauna_funcs) << proc { |query| filter(query, &lambda) }
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Returns a copy of the page with the given ruby block set.
182
+ #
183
+ # The block will be used to map the returned data elements from the executed fauna query. Only one postprocessing
184
+ # map can be configured at a time.
185
+ #
186
+ # Intended for use when the elements in a page need to be converted within ruby (ie loading into a model). Wherever
187
+ # the operation can be performed from within FaunaDB, +map+ should be used instead.
188
+ #
189
+ # The block will be passed the each element as a parameter from the data of the page currently being loaded.
190
+ #
191
+ # Example of loading instances into your own model:
192
+ #
193
+ # page.postprocessing_map { |instance| YourModel.load(instance) }
194
+ def postprocessing_map(&block)
195
+ with_dup do |page|
196
+ page.instance_variable_set(:@postprocessing_map, block)
197
+ end
198
+ end
199
+
200
+ # :section: Pagination
201
+
202
+ ##
203
+ # The page after the current one in the set.
204
+ #
205
+ # Returns +nil+ when there are no more pages after the current page. Lazily loads the current page if it has not
206
+ # already been loaded in order to determine the page after.
207
+ def page_after
208
+ new_page(:after)
209
+ end
210
+
211
+ ##
212
+ # The page before the current one in the set.
213
+ #
214
+ # Returns +nil+ when there are no more pages before the current page. Lazily loads the current page if it has not
215
+ # already been loaded in order to determine the page before.
216
+ def page_before
217
+ new_page(:before)
218
+ end
219
+
220
+ ##
221
+ # Returns an enumerator that iterates in the +after+ direction.
222
+ #
223
+ # When a block is provided, the return of the block will always be +nil+ (to avoid loading large sets into memory).
224
+ def each
225
+ return enum_for(:each) unless block_given?
226
+
227
+ # Return current page
228
+ yield data
229
+
230
+ # Begin returning pages after
231
+ page = self.page_after
232
+ until page.nil?
233
+ yield page.data
234
+ page = page.page_after
235
+ end
236
+ end
237
+
238
+ ##
239
+ # Returns an enumerator that iterates in the +before+ direction.
240
+ #
241
+ # When a block is provided, the return of the block will always be +nil+ (to avoid loading large sets into memory).
242
+ #
243
+ # While the paging will occur in the reverse direction, the data returned will still be in the normal direction.
244
+ def reverse_each
245
+ return enum_for(:reverse_each) unless block_given?
246
+
247
+ # Return current page
248
+ yield data
249
+
250
+ # Begin returning pages before
251
+ page = self.page_before
252
+ until page.nil?
253
+ yield page.data
254
+ page = page.page_before
255
+ end
256
+ end
257
+
258
+ ##
259
+ # Returns the flattened contents of the set as an array.
260
+ #
261
+ # Ideal for when you need the full contents of a set with a known small size. If you need to iterate over a set
262
+ # of large or unknown size, it is recommended to use +each+ instead.
263
+ #
264
+ # The set is paged in the +after+ direction.
265
+ def all
266
+ each.flat_map { |x| x }
267
+ end
268
+
269
+ ##
270
+ # Iterates over the entire set, applying the configured lambda in a foreach block, and discarding the result.
271
+ #
272
+ # Ideal for performing a +foreach+ over an entire set (like deleting all instances in a set). The set is iterated in
273
+ # the +after+ direction. The +foreach+ will be chained on top of any previously configured collection functions.
274
+ #
275
+ # Example of deleting every instance in a set:
276
+ #
277
+ # page.foreach! { |ref| delete ref }
278
+ def foreach!(&lambda)
279
+ # Create new page with foreach block
280
+ iter_page = with_dup do |page|
281
+ page.instance_variable_get(:@fauna_funcs) << proc { |query| foreach(query, &lambda) }
282
+ end
283
+
284
+ # Apply to all pages in the set
285
+ until iter_page.nil?
286
+ iter_page.load!
287
+ iter_page = iter_page.page_after
288
+ end
289
+ nil
290
+ end
291
+
292
+ def dup # :nodoc:
293
+ page = super
294
+ page.instance_variable_set(:@params, @params.dup)
295
+ page.instance_variable_set(:@fauna_funcs, @fauna_funcs.dup)
296
+ page
297
+ end
298
+
299
+ private
300
+
301
+ CURSOR_KEYS = [:before, :after].freeze # :nodoc:
302
+
303
+ def with_dup
304
+ # Create a copy and drop loaded data
305
+ page = self.dup
306
+ page.send(:unload_page)
307
+
308
+ # Yield page for manipulation
309
+ yield page
310
+
311
+ # Freeze params and return page
312
+ page.params.freeze
313
+ page
314
+ end
315
+
316
+ def get_page(params)
317
+ # Create query
318
+ query = Query.paginate @set, params
319
+
320
+ unless @fauna_funcs.empty?
321
+ # Wrap paginate query with the fauna maps
322
+ dsl = Query::QueryDSLContext.new
323
+ @fauna_funcs.each do |proc|
324
+ query = DSLContext.eval_dsl(dsl, query, &proc)
325
+ end
326
+ query = Query::Expr.wrap query
327
+ end
328
+
329
+ # Execute query
330
+ result = @client.query query
331
+
332
+ unless @postprocessing_map.nil?
333
+ # Map the resulting data with the ruby block
334
+ result[:data].map! { |element| @postprocessing_map.call(element) }
335
+ end
336
+
337
+ # Return result
338
+ result
339
+ end
340
+
341
+ def load_page(page)
342
+ # Not initial after the first page
343
+ @populated = true
344
+
345
+ # Update the page fields
346
+ @data = page[:data]
347
+ @before = page[:before]
348
+ @after = page[:after]
349
+ end
350
+
351
+ def unload_page
352
+ # Reset paging
353
+ @populated = false
354
+
355
+ # Reset data
356
+ @data = nil
357
+ @before = nil
358
+ @after = nil
359
+ end
360
+
361
+ def new_page(direction)
362
+ fail "Invalid direction; must be one of #{CURSOR_KEYS}" unless CURSOR_KEYS.include?(direction)
363
+
364
+ cursor = self.send(direction)
365
+
366
+ # If there is no next cursor, we have reached the end of the set.
367
+ # Return +nil+.
368
+ return nil if cursor.nil?
369
+
370
+ # Use the configured cursor to fetch the first page.
371
+ with_params(direction => cursor)
372
+ end
373
+ end
374
+ end