fauna 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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