fauna 2.4.0 → 3.0.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: aeba4b71fb3aa58412e5f78514faa422f3d71eab
4
- data.tar.gz: 0a53bd599a46461131c1c552b7ef6e1893833cd0
3
+ metadata.gz: dfcff73c948e3e884ab373e09ff77948e625a4f7
4
+ data.tar.gz: 43059c04a28d0fa4b3d03cf50827194372f67508
5
5
  SHA512:
6
- metadata.gz: 5f4b77c6451c409b30135a918ddacd7bc6751658d9ddb4c38733b8503032be6c148a0cf9e78b105259e444150b68a0a0054b909fac48ce977c2386136afaf9d4
7
- data.tar.gz: 524c60bcada18bc69e7411cac1fbc17afc58624aaa448763bf38f467b43edd4063f5a5d0eed6b0c9ba834fb4f5bb3d452725dbd4e83c300489cf49ad45182317
6
+ metadata.gz: 12916b7c045128f1e0f311f2b83e2dce14bf188268c28b086c19af48e946139d132a160b1a0692d4d75f077d7e078a9becf02cc25407154bdd7198f4fa37825c
7
+ data.tar.gz: c6a24431d0f0a50ef6e327318ac8beb698beb11d9fa845f35eb23de454bab5b49911e8c433366f7f757d914e8ec869d4bafddbd44cde9dc2b175cf089798d42b
data/CHANGELOG CHANGED
@@ -1,3 +1,14 @@
1
+ 3.0.0
2
+ * Adds support for recursive references.
3
+ * Removed REST api support from `Client`.
4
+ * Added `abort` function.
5
+ * Added `normalizer` argument to `casefold` function.
6
+ * Added `new_id` function.
7
+ * Deprecated `next_id` function in favor of `new_id`.
8
+ * Added `identity` and `has_identity` functions.
9
+ * Added `singleton` and `events` functions.
10
+ * Added `select_all` function.
11
+
1
12
  2.4.0
2
13
  * Handle HTTP errors 502 and 504 as `Fauna::UnavailableError`.
3
14
  * Added support for user-defined functions.
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # FaunaDB
2
2
 
3
- [![Build Status](https://img.shields.io/travis/fauna/faunadb-ruby/master.svg?maxAge=21600)](https://travis-ci.org/fauna/faunadb-ruby)
4
3
  [![Coverage Status](https://img.shields.io/codecov/c/github/fauna/faunadb-ruby/master.svg?maxAge=21600)](https://codecov.io/gh/fauna/faunadb-ruby/branch/master)
5
4
  [![Gem Version](https://img.shields.io/gem/v/fauna.svg?maxAge=21600)](https://rubygems.org/gems/fauna)
6
5
  [![License](https://img.shields.io/badge/license-MPL_2.0-blue.svg?maxAge=2592000)](https://raw.githubusercontent.com/fauna/faunadb-ruby/master/LICENSE)
@@ -124,6 +123,10 @@ Coverage is automatically run as part of the tests. After running tests, check
124
123
  `JRUBY_OPTS="--debug" bundle exec rake spec` to ensure coverage is generated
125
124
  correctly.
126
125
 
126
+ Tests can also be run via a Docker container with
127
+ `FAUNA_ROOT_KEY="your-cloud-secret" make docker-test` (an alternate
128
+ Alpine-based Ruby image can be provided via `RUNTIME_IMAGE`).
129
+
127
130
  ## Contributing
128
131
 
129
132
  GitHub pull requests are very welcome.
@@ -22,5 +22,6 @@ Gem::Specification.new do |s|
22
22
  s.add_runtime_dependency 'json', '~> 1.8'
23
23
  s.add_development_dependency 'rspec', '~> 3.4'
24
24
  s.add_development_dependency 'rubocop', '~> 0.38.0'
25
- s.add_development_dependency 'codecov', '~> 0.1.5'
25
+ s.add_development_dependency 'simplecov-json', '~> 0.2.0'
26
+ s.add_development_dependency 'rspec_junit_formatter', '~> 0.3.0'
26
27
  end
@@ -11,6 +11,7 @@ require 'base64'
11
11
  # Main namespace for the FaunaDB driver.
12
12
  module Fauna; end
13
13
 
14
+ require 'fauna/deprecate.rb'
14
15
  require 'fauna/version'
15
16
  require 'fauna/json'
16
17
  require 'fauna/util'
@@ -7,8 +7,8 @@ module Fauna
7
7
  # Hash keys are always Symbols.
8
8
  #
9
9
  # Any Ref, SetRef, Time or Date values in it will also be parsed.
10
- # (So instead of <code>{ "@ref": "classes/frogs/123" }</code>,
11
- # you will get <code>Fauna::Ref.new("classes/frogs/123")</code>).
10
+ # (So instead of <code>{ "@ref": { "id": "123", "class": { "@ref": { "id": "frogs", "class": { "@ref": { "id": "classes" } } } } } }</code>,
11
+ # you will get <code>Fauna::Ref.new("123", Fauna::Ref.new("frogs", Fauna::Native.classes))</code>).
12
12
  class Client
13
13
  # The domain requests will be sent to.
14
14
  attr_reader :domain
@@ -84,9 +84,9 @@ module Fauna
84
84
  # :category: Query Methods
85
85
  def query(expression = nil, &expr_block)
86
86
  if expr_block.nil?
87
- post '', Fauna::Query::Expr.wrap(expression)
87
+ execute(:post, :'', nil, Fauna::Query::Expr.wrap(expression))
88
88
  else
89
- post '', Fauna::Query.expr(&expr_block)
89
+ execute(:post, :'', nil, Fauna::Query.expr(&expr_block))
90
90
  end
91
91
  end
92
92
 
@@ -101,70 +101,6 @@ module Fauna
101
101
  Fauna::Page.new(self, set, params, &fauna_map)
102
102
  end
103
103
 
104
- ##
105
- # Performs a +GET+ request for a REST endpoint.
106
- #
107
- # +path+:: Path to +GET+.
108
- # +query+:: Query parameters to append to the path.
109
- #
110
- # Reference: {FaunaDB REST API}[https://fauna.com/documentation/rest]
111
- #
112
- # :category: REST Methods
113
- def get(path, query = {})
114
- execute(:get, path.to_s, query)
115
- end
116
-
117
- ##
118
- # Performs a +POST+ request for a REST endpoint.
119
- #
120
- # +path+:: Path to +POST+.
121
- # +data+:: Data to post as the body. +data+ is automatically converted to JSON.
122
- #
123
- # Reference: {FaunaDB REST API}[https://fauna.com/documentation/rest]
124
- #
125
- # :category: REST Methods
126
- def post(path, data = {})
127
- execute(:post, path, nil, data)
128
- end
129
-
130
- ##
131
- # Performs a +PUT+ request for a REST endpoint.
132
- #
133
- # +path+:: Path to +PUT+.
134
- # +data+:: Data to post as the body. +data+ is automatically converted to JSON.
135
- #
136
- # Reference: {FaunaDB REST API}[https://fauna.com/documentation/rest]
137
- #
138
- # :category: REST Methods
139
- def put(path, data = {})
140
- execute(:put, path, nil, data)
141
- end
142
-
143
- ##
144
- # Performs a +PATCH+ request for a REST endpoint.
145
- #
146
- # +path+:: Path to +PATCH+.
147
- # +data+:: Data to post as the body. +data+ is automatically converted to JSON.
148
- #
149
- # Reference: {FaunaDB REST API}[https://fauna.com/documentation/rest]
150
- #
151
- # :category: REST Methods
152
- def patch(path, data = {})
153
- execute(:patch, path, nil, data)
154
- end
155
-
156
- ##
157
- # Performs a +DELETE+ request for a REST endpoint.
158
- #
159
- # +path+:: Path to +DELETE+.
160
- #
161
- # Reference: {FaunaDB REST API}[https://fauna.com/documentation/rest]
162
- #
163
- # :category: REST Methods
164
- def delete(path)
165
- execute(:delete, path)
166
- end
167
-
168
104
  ##
169
105
  # Ping FaunaDB.
170
106
  #
@@ -172,7 +108,7 @@ module Fauna
172
108
  #
173
109
  # :category: REST Methods
174
110
  def ping(params = {})
175
- get 'ping', params
111
+ execute(:get, :ping, params)
176
112
  end
177
113
 
178
114
  private
@@ -195,6 +131,7 @@ module Fauna
195
131
  'Accept-Encoding' => 'gzip,deflate',
196
132
  'Content-Type' => 'application/json;charset=utf-8',
197
133
  'User-Agent' => "FaunaDB-Ruby/#{Fauna::VERSION}",
134
+ 'X-FaunaDB-API-Version' => '2.1'
198
135
  },
199
136
  request: { timeout: read_timeout, open_timeout: connection_timeout },
200
137
  ) do |conn|
@@ -0,0 +1,29 @@
1
+ module Fauna
2
+ module Deprecate
3
+ ##
4
+ # Deprecates a method
5
+ #
6
+ # class AClass
7
+ # extend Fauna::Deprecate
8
+ #
9
+ # def method
10
+ # end
11
+ #
12
+ # deprecate :method, :new_method
13
+ #
14
+ # def new_method
15
+ # end
16
+ # end
17
+ #
18
+ # +name+:: The method name to be deprecated
19
+ # +replacement+:: The new method that should be used instead
20
+ def deprecate(name, replacement)
21
+ old_name = "deprecated_#{name}"
22
+ alias_method old_name, name
23
+ define_method name do |*args, &block|
24
+ warn "Method #{name} called from #{Gem.location_of_caller.join(':')} is deprecated. Use #{replacement} instead"
25
+ self.__send__ old_name, *args, &block
26
+ end
27
+ end
28
+ end
29
+ end
@@ -11,7 +11,16 @@ module Fauna
11
11
  def self.deserialize(obj)
12
12
  if obj.is_a?(Hash)
13
13
  if obj.key? :@ref
14
- Ref.new obj[:@ref]
14
+ ref = obj[:@ref]
15
+ id = ref[:id]
16
+
17
+ if !ref.key?(:class) && !ref.key?(:database)
18
+ Native.from_name(id)
19
+ else
20
+ cls = self.deserialize(ref[:class])
21
+ db = self.deserialize(ref[:database])
22
+ Ref.new(id, cls, db)
23
+ end
15
24
  elsif obj.key? :@set
16
25
  SetRef.new deserialize(obj[:@set])
17
26
  elsif obj.key? :@obj
@@ -35,7 +44,7 @@ module Fauna
35
44
  end
36
45
 
37
46
  def self.json_load(body)
38
- JSON.load body, nil, max_nesting: false, symbolize_names: true
47
+ JSON.load body, nil, max_nesting: false, symbolize_names: true, create_additions: false
39
48
  end
40
49
 
41
50
  def self.json_load_or_nil(body)
@@ -4,62 +4,64 @@ module Fauna
4
4
  #
5
5
  # Reference: {FaunaDB Special Types}[https://fauna.com/documentation/queries#values-special_types]
6
6
  class Ref
7
- # The raw ref string.
8
- attr_accessor :value
7
+ # The raw attributes of ref.
8
+ attr_accessor :id, :class_, :database
9
9
 
10
10
  ##
11
11
  # Creates a Ref object.
12
12
  #
13
13
  # :call-seq:
14
- # Ref.new('databases/prydain')
14
+ # Ref.new('prydain', Native.databases)
15
15
  #
16
- # +value+: A string.
17
- def initialize(value)
18
- @value = value
19
- end
16
+ # +id+: A string.
17
+ # +class_+: A Ref.
18
+ # +database+: A Ref.
19
+ def initialize(id, class_ = nil, database = nil)
20
+ fail ArgumentError.new 'id cannot be nil' if id.nil?
20
21
 
21
- ##
22
- # Gets the class part out of the Ref.
23
- # This is done by removing ref.id().
24
- # So <code>Fauna::Ref.new('a/b/c').to_class</code> will be
25
- # <code>Fauna::Ref.new('a/b')</code>.
26
- def to_class
27
- parts = value.split '/'
28
- if parts.length == 1
29
- self
30
- else
31
- Fauna::Ref.new(parts[0...-1].join('/'))
32
- end
33
- end
34
-
35
- ##
36
- # Removes the class part of the ref, leaving only the id.
37
- # This is everything after the last /.
38
- def id
39
- parts = value.split '/'
40
- fail ArgumentError.new 'The Ref does not have an id.' if parts.length == 1
41
- parts.last
22
+ @id = id
23
+ @class_ = class_ unless class_.nil?
24
+ @database = database unless database.nil?
42
25
  end
43
26
 
44
27
  # Converts the Ref to a string
45
28
  def to_s
46
- value
29
+ cls = class_.nil? ? '' : ",class=#{class_.to_s}"
30
+ db = database.nil? ? '' : ",database=#{database.to_s}"
31
+ "Ref(id=#{id}#{cls}#{db})"
47
32
  end
48
33
 
49
34
  # Converts the Ref in Hash form.
50
35
  def to_hash
51
- { :@ref => value }
36
+ ref = { id: id }
37
+ ref[:class] = class_ unless class_.nil?
38
+ ref[:database] = database unless database.nil?
39
+ { :@ref => ref }
52
40
  end
53
41
 
54
42
  # Returns +true+ if +other+ is a Ref and contains the same value.
55
43
  def ==(other)
56
44
  return false unless other.is_a? Ref
57
- value == other.value
45
+ id == other.id && class_ == other.class_ && database == other.database
58
46
  end
59
47
 
60
48
  alias_method :eql?, :==
61
49
  end
62
50
 
51
+ class Native
52
+ @@natives = %w(classes indexes databases functions keys tokens credentials).freeze
53
+
54
+ @@natives.each do |id|
55
+ instance_variable_set "@#{id}", Ref.new(id).freeze
56
+ self.class.send :attr_reader, id.to_sym
57
+ end
58
+
59
+ def self.from_name(id)
60
+ return Ref.new(id) unless @@natives.include? id
61
+ send id.to_sym
62
+ end
63
+ end
64
+
63
65
  ##
64
66
  # A SetRef.
65
67
  #
@@ -16,24 +16,24 @@ module Fauna
16
16
  #
17
17
  # Paging over a class index
18
18
  #
19
- # page = Page.new(client, Query.match(Ref.new('indexes/items')))
19
+ # page = Page.new(client, Query.match(Query.index('items')))
20
20
  #
21
21
  # Paging over a class index 5 at a time, mapping the refs to the +data.value+ for each instance
22
22
  #
23
- # page = Page.new(client, Query.match(Ref.new('indexes/items')), size: 5) do |ref|
23
+ # page = Page.new(client, Query.match(Query.index('items')), size: 5) do |ref|
24
24
  # select ['data', 'value'], get(ref)
25
25
  # end
26
26
  #
27
27
  # # Same thing, but using builders instead
28
28
  #
29
- # page = Page.new(client, Query.match(Ref.new('indexes/items'))).with_params(size: 5).map do |ref|
29
+ # page = Page.new(client, Query.match(Query.index('items'))).with_params(size: 5).map do |ref|
30
30
  # select ['data', 'value'], get(ref)
31
31
  # end
32
32
  #
33
33
  # Paging over a class index, mapping refs to the +data.value+ for each instance, filtering out odd numbers, and
34
34
  # multiplying the value:
35
35
  #
36
- # page = Page.new(client, Query.match(Ref.new('indexes/items'))).map do |ref|
36
+ # page = Page.new(client, Query.match(Query.index('items'))).map do |ref|
37
37
  # select ['data', 'value'], get(ref)
38
38
  # end.filter do |value|
39
39
  # equals modulo(value, 2), 0
@@ -5,17 +5,17 @@ module Fauna
5
5
  # Helpers are usually used via a concise DSL notation. A DSL block
6
6
  # may be used directly with Fauna::Client
7
7
  #
8
- # client.query { create(ref('classes', 'spells'), { data: { name: 'Magic Missile' } }) }
8
+ # client.query { create(class_('spells'), { data: { name: 'Magic Missile' } }) }
9
9
  #
10
10
  # To build and return an query expression to execute later, use Fauna::Query.expr
11
11
  #
12
- # Fauna::Query.expr { create(ref('classes', 'spells'), { data: { name: 'Magic Missile' } }) }
12
+ # Fauna::Query.expr { create(class_('spells'), { data: { name: 'Magic Missile' } }) }
13
13
  #
14
14
  # Or, you may directly use the helper methods:
15
15
  #
16
- # Fauna::Query.create(Fauna::Query.ref('classes', 'spells'), { data: { name: 'Magic Missile' } })
16
+ # Fauna::Query.create(Fauna::Query.class_('spells'), { data: { name: 'Magic Missile' } })
17
17
  module Query
18
- extend self
18
+ extend self, Deprecate
19
19
 
20
20
  class QueryDSLContext < DSLContext # :nodoc:
21
21
  include Query
@@ -45,12 +45,20 @@ module Fauna
45
45
  # Reference: {FaunaDB Values}[https://fauna.com/documentation/queries#values]
46
46
  def ref(str, id = nil)
47
47
  if id.nil?
48
- Ref.new(str)
48
+ Expr.new :@ref => Expr.wrap(str)
49
49
  else
50
50
  Expr.new ref: Expr.wrap(str), id: Expr.wrap(id)
51
51
  end
52
52
  end
53
53
 
54
+ ##
55
+ # An abort expression
56
+ #
57
+ # Reference: {FaunaDB Basic Forms}[https://fauna.com/documentation/queries#basic_forms]
58
+ def abort(msg)
59
+ Expr.new abort: Expr.wrap(msg)
60
+ end
61
+
54
62
  ##
55
63
  # An object expression
56
64
  #
@@ -383,6 +391,22 @@ module Fauna
383
391
 
384
392
  # :section: Set Functions
385
393
 
394
+ ##
395
+ # A singleton expression
396
+ #
397
+ # Reference: {FaunaDB Sets}[https://fauna.com/documentation/queries#sets]
398
+ def singleton(ref)
399
+ Expr.new singleton: Expr.wrap(ref)
400
+ end
401
+
402
+ ##
403
+ # An events expression
404
+ #
405
+ # Reference: {FaunaDB Sets}[https://fauna.com/documentation/queries#sets]
406
+ def events(ref_set)
407
+ Expr.new events: Expr.wrap(ref_set)
408
+ end
409
+
386
410
  ##
387
411
  # A match expression
388
412
  #
@@ -460,6 +484,22 @@ module Fauna
460
484
  Expr.new identify: Expr.wrap(ref), password: Expr.wrap(password)
461
485
  end
462
486
 
487
+ ##
488
+ # An identity function
489
+ #
490
+ # Reference: {FaunaDB Authentication}[https://fauna.com/documentation/queries#auth_functions]
491
+ def identity
492
+ Expr.new identity: nil
493
+ end
494
+
495
+ ##
496
+ # A has_identity function
497
+ #
498
+ # Reference: {FaunaDB Authentication}[https://fauna.com/documentation/queries#auth_functions]
499
+ def has_identity
500
+ Expr.new has_identity: nil
501
+ end
502
+
463
503
  # :section: String Functions
464
504
 
465
505
  ##
@@ -478,8 +518,12 @@ module Fauna
478
518
  # A casefold function
479
519
  #
480
520
  # Reference: {FaunaDB String Functions}[https://fauna.com/documentation/queries#string_functions]
481
- def casefold(string)
482
- Expr.new casefold: Expr.wrap(string)
521
+ def casefold(string, normalizer = nil)
522
+ if normalizer.nil?
523
+ Expr.new casefold: Expr.wrap(string)
524
+ else
525
+ Expr.new casefold: Expr.wrap(string), normalizer: Expr.wrap(normalizer)
526
+ end
483
527
  end
484
528
 
485
529
  # :section: Time and Date Functions
@@ -518,36 +562,110 @@ module Fauna
518
562
  Expr.new next_id: nil
519
563
  end
520
564
 
565
+ deprecate :next_id, :new_id
566
+
567
+ ##
568
+ # A new_id function
569
+ #
570
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
571
+ def new_id
572
+ Expr.new new_id: nil
573
+ end
574
+
521
575
  ##
522
576
  # A database function
523
577
  #
524
578
  # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
525
- def database(name)
526
- Expr.new database: Expr.wrap(name)
579
+ def database(name, scope = nil)
580
+ return Expr.new database: Expr.wrap(name) if scope.nil?
581
+
582
+ Expr.new database: Expr.wrap(name), scope: Expr.wrap(scope)
527
583
  end
528
584
 
529
585
  ##
530
586
  # A class function
531
587
  #
532
588
  # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
533
- def class_(name)
534
- Expr.new class: Expr.wrap(name)
589
+ def class_(name, scope = nil)
590
+ return Expr.new class: Expr.wrap(name) if scope.nil?
591
+
592
+ Expr.new class: Expr.wrap(name), scope: Expr.wrap(scope)
535
593
  end
536
594
 
537
595
  ##
538
596
  # An index function
539
597
  #
540
598
  # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
541
- def index(name)
542
- Expr.new index: Expr.wrap(name)
599
+ def index(name, scope = nil)
600
+ return Expr.new index: Expr.wrap(name) if scope.nil?
601
+
602
+ Expr.new index: Expr.wrap(name), scope: Expr.wrap(scope)
543
603
  end
544
604
 
545
605
  ##
546
606
  # A function function
547
607
  #
548
608
  # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
549
- def function(name)
550
- Expr.new function: Expr.wrap(name)
609
+ def function(name, scope = nil)
610
+ return Expr.new function: Expr.wrap(name) if scope.nil?
611
+
612
+ Expr.new function: Expr.wrap(name), scope: Expr.wrap(scope)
613
+ end
614
+
615
+ ##
616
+ # A databases function
617
+ #
618
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
619
+ def databases(scope = nil)
620
+ Expr.new databases: Expr.wrap(scope)
621
+ end
622
+
623
+ ##
624
+ # A classes function
625
+ #
626
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
627
+ def classes(scope = nil)
628
+ Expr.new classes: Expr.wrap(scope)
629
+ end
630
+
631
+ ##
632
+ # An indexes function
633
+ #
634
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
635
+ def indexes(scope = nil)
636
+ Expr.new indexes: Expr.wrap(scope)
637
+ end
638
+
639
+ ##
640
+ # A functions function
641
+ #
642
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
643
+ def functions(scope = nil)
644
+ Expr.new functions: Expr.wrap(scope)
645
+ end
646
+
647
+ ##
648
+ # A tokens function
649
+ #
650
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
651
+ def tokens(scope = nil)
652
+ Expr.new tokens: Expr.wrap(scope)
653
+ end
654
+
655
+ ##
656
+ # A keys function
657
+ #
658
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
659
+ def keys(scope = nil)
660
+ Expr.new keys: Expr.wrap(scope)
661
+ end
662
+
663
+ ##
664
+ # A credentials function
665
+ #
666
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation#queries-misc_functions]
667
+ def credentials(scope = nil)
668
+ Expr.new credentials: Expr.wrap(scope)
551
669
  end
552
670
 
553
671
  ##
@@ -574,6 +692,14 @@ module Fauna
574
692
  Expr.new Expr.wrap_values(params).merge(select: Expr.wrap(path), from: Expr.wrap(from))
575
693
  end
576
694
 
695
+ ##
696
+ # A select_all function
697
+ #
698
+ # Reference: {FaunaDB Miscellaneous Functions}[https://fauna.com/documentation/queries#misc_functions]
699
+ def select_all(path, from)
700
+ Expr.new select_all: Expr.wrap(path), from: Expr.wrap(from)
701
+ end
702
+
577
703
  ##
578
704
  # An add function
579
705
  #