activerecord-refined 0.7.0 → 0.8.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 +4 -4
- data/README.md +38 -30
- data/examples/json.rb +20 -20
- data/lib/active_record/refined/ast.rb +28 -27
- data/lib/activerecord-refined/version.rb +1 -1
- data/test/test_block_syntax.rb +52 -52
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aaf1859c70b9ca6e5c245343e664fffbd483d395266a4d442eaa0d5c4107595c
|
|
4
|
+
data.tar.gz: da1ddb190628a8a11ced1d105d0a73bf164892cf9e78dfd68db7fdf135047d95
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5b075b93194651859a7411d2465fec0c2d0a504eaf9da7d4d296ddea1cc91a366af68fea0b37cbea230e44da9e4bf480ecfd047571c1b49601b9bfa7c3dc2780
|
|
7
|
+
data.tar.gz: c35194020d37a97de9f5816b965f391a47c5e3d12ef7192bfd2c91ff16ea72689e122fc29daba3c6096aceac22dd7fbb535d562fe0c0ba2f3edbcbbb9904c22c
|
data/README.md
CHANGED
|
@@ -706,11 +706,14 @@ Author.select { sum(case_when { :age >= 60 }.then(1).else(0)).as(:seniors) }
|
|
|
706
706
|
### JSON
|
|
707
707
|
|
|
708
708
|
`dig` reads inside a JSON document, by the name of what `Hash` does. A string
|
|
709
|
-
or symbol steps into an object, an integer into an array
|
|
709
|
+
or symbol steps into an object, an integer into an array, and what comes back
|
|
710
|
+
is still JSON — the way `Hash#dig` hands back the structure itself — for a
|
|
711
|
+
document to be dug into further or asked the JSON questions. `dig_text` gives
|
|
712
|
+
the value as text instead, which is what a comparison wants:
|
|
710
713
|
|
|
711
714
|
```ruby
|
|
712
|
-
Post.where { :meta.
|
|
713
|
-
Post.select { :meta.dig(:
|
|
715
|
+
Post.where { :meta.dig_text(:author, :name) == 'alice' }
|
|
716
|
+
Post.select { :meta.dig(:author).as(:author) }
|
|
714
717
|
Post.where { :meta.key?(:draft) }
|
|
715
718
|
Post.where { :meta.contains?(status: 'open') }
|
|
716
719
|
```
|
|
@@ -720,34 +723,40 @@ three:
|
|
|
720
723
|
|
|
721
724
|
| | PostgreSQL | SQLite | MySQL |
|
|
722
725
|
| --- | --- | --- | --- |
|
|
723
|
-
| `dig(:a
|
|
724
|
-
| `
|
|
726
|
+
| `dig(:a)` | `#> '{a}'` | `-> '$.a'` | `JSON_EXTRACT(…, '$.a')` |
|
|
727
|
+
| `dig_text(:a, :b)` | `#>> '{a,b}'` | `->> '$.a.b'` | `JSON_UNQUOTE(JSON_EXTRACT(…, '$.a.b'))` |
|
|
725
728
|
| `key?(:a)` | `jsonb_exists(…, 'a')` | `json_type(…, '$.a') IS NOT NULL` | `JSON_CONTAINS_PATH(…, 'one', '$.a')` |
|
|
726
729
|
| `contains?(…)` | `@>` | — | `JSON_CONTAINS` |
|
|
727
730
|
|
|
728
731
|
MariaDB answers to the `mysql2` adapter and has none of `->` or `->>`, so the
|
|
729
732
|
MySQL family goes through the functions, which both have.
|
|
730
733
|
|
|
731
|
-
`
|
|
732
|
-
with its type, so a comparison that worked there would fail on the other
|
|
733
|
-
a number is compared through a `cast` on all three:
|
|
734
|
+
`dig_text` gives text everywhere. SQLite's `->>` would otherwise hand back the
|
|
735
|
+
value with its type, so a comparison that worked there would fail on the other
|
|
736
|
+
two; a number is compared through a `cast` on all three:
|
|
734
737
|
|
|
735
738
|
```ruby
|
|
736
|
-
Post.where { :meta.
|
|
737
|
-
Post.where { cast(:meta.
|
|
739
|
+
Post.where { :meta.dig_text(:n) == '5' }
|
|
740
|
+
Post.where { cast(:meta.dig_text(:n), 'integer') > 6 } # 'signed' on MySQL
|
|
738
741
|
```
|
|
739
742
|
|
|
740
743
|
The type is the adapter's own name for it, here as everywhere `cast` is used.
|
|
741
744
|
|
|
745
|
+
Strings and numbers are where the adapters agree. A JSON boolean comes back as
|
|
746
|
+
`"1"` on SQLite, which turns `true` into SQL's `1` before the text cast, and
|
|
747
|
+
as `"true"` on the other two; a JSON `null` is SQL `NULL` everywhere but
|
|
748
|
+
MariaDB, which spells it `"null"`. A key that is not there is `NULL` on all
|
|
749
|
+
three.
|
|
750
|
+
|
|
742
751
|
Comparing a dug value with anything but a string raises `ArgumentError` rather
|
|
743
|
-
than being left to the adapters, which answer it three ways: `
|
|
744
|
-
true on SQLite, an error on PostgreSQL and true on MySQL, and
|
|
745
|
-
`
|
|
746
|
-
type was meant, and then all three agree. `
|
|
747
|
-
about — the JSON for a string carries its quotes, so `
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
752
|
+
than being left to the adapters, which answer it three ways: `dig_text(:n) ==
|
|
753
|
+
5` is true on SQLite, an error on PostgreSQL and true on MySQL, and
|
|
754
|
+
`dig_text(:flag) == true` is true, an error and false. `cast` is what says
|
|
755
|
+
which type was meant, and then all three agree. `dig` is refused the other way
|
|
756
|
+
about — the JSON for a string carries its quotes, so `dig(:name) == 'alice'`
|
|
757
|
+
is false, an error and true — and `dig_text` is the one that gives the value.
|
|
758
|
+
A column, a function or another dug value on the right goes through untouched;
|
|
759
|
+
only a Ruby literal is refused.
|
|
751
760
|
|
|
752
761
|
`bury` sets what `dig` reads: the last argument is the value and the rest are
|
|
753
762
|
the path to it. The document comes back changed rather than being written
|
|
@@ -759,7 +768,7 @@ Post.update_all { { meta: :meta.bury(:author, :name, 'alice') } }
|
|
|
759
768
|
# ... JSON_SET("meta", '$.author.name', 'alice') elsewhere
|
|
760
769
|
|
|
761
770
|
Post.update_all { { meta: :meta.bury(:tags, ['ruby', 'sql']) } }
|
|
762
|
-
Post.update_all { { meta: :meta.bury(:copy, :meta.
|
|
771
|
+
Post.update_all { { meta: :meta.bury(:copy, :meta.dig_text(:n)) } }
|
|
763
772
|
```
|
|
764
773
|
|
|
765
774
|
A whole document goes in as one — an object or an array rather than the string
|
|
@@ -787,26 +796,25 @@ keys, an element by index — and an array literal written without a type is
|
|
|
787
796
|
read as the first of them, so `"meta" - '{draft}'` takes out the key spelled
|
|
788
797
|
`{draft}`, which is nothing, and says nothing about it.
|
|
789
798
|
|
|
790
|
-
`
|
|
791
|
-
|
|
792
|
-
asked of a part of the document rather than of all of it:
|
|
799
|
+
What `dig` gives is a document, so the JSON operations read it — the same
|
|
800
|
+
question asked of a part of the document rather than of all of it:
|
|
793
801
|
|
|
794
802
|
```ruby
|
|
795
|
-
Post.where { :meta.
|
|
796
|
-
Post.where { :meta.
|
|
797
|
-
Post.update_all { { meta: :meta.
|
|
803
|
+
Post.where { :meta.dig(:author).key?(:email) }
|
|
804
|
+
Post.where { :meta.dig(:author).dig_text(:name) == 'alice' }
|
|
805
|
+
Post.update_all { { meta: :meta.dig(:author).bury(:name, 'alice') } }
|
|
798
806
|
```
|
|
799
807
|
|
|
800
808
|
Containment reads it too, on the adapters that have containment at all:
|
|
801
809
|
|
|
802
810
|
```ruby
|
|
803
|
-
Post.where { :meta.
|
|
811
|
+
Post.where { :meta.dig(:tags).contains?(['ruby']) }
|
|
804
812
|
```
|
|
805
813
|
|
|
806
|
-
Asking the same of `
|
|
807
|
-
reading text back as a document is where the adapters part company —
|
|
808
|
-
parses it, MySQL takes it as written, and PostgreSQL has no such
|
|
809
|
-
text at all.
|
|
814
|
+
Asking the same of `dig_text` raises `ArgumentError`: what it gives is text,
|
|
815
|
+
and reading text back as a document is where the adapters part company —
|
|
816
|
+
SQLite parses it, MySQL takes it as written, and PostgreSQL has no such
|
|
817
|
+
function for text at all.
|
|
810
818
|
|
|
811
819
|
`contains?` has no equivalent on SQLite and raises `NotImplementedError`
|
|
812
820
|
there — later than the rest, since the adapter is only known when the SQL is
|
data/examples/json.rb
CHANGED
|
@@ -30,45 +30,45 @@ def show(title, relation, rows = nil)
|
|
|
30
30
|
puts
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
# 1. Reading.
|
|
34
|
-
# into an object, an integer into an array. What comes back is the
|
|
35
|
-
# rather than the JSON around it, which is what a comparison wants.
|
|
36
|
-
show('
|
|
37
|
-
Document.select { [:name, :meta.
|
|
38
|
-
Document.select { [:name, :meta.
|
|
33
|
+
# 1. Reading. dig_text takes the path Hash#dig takes: a string or symbol
|
|
34
|
+
# steps into an object, an integer into an array. What comes back is the
|
|
35
|
+
# value rather than the JSON around it, which is what a comparison wants.
|
|
36
|
+
show('dig_text reads a value out of the document',
|
|
37
|
+
Document.select { [:name, :meta.dig_text(:author, :name).as(:author)] },
|
|
38
|
+
Document.select { [:name, :meta.dig_text(:author, :name).as(:author)] }.
|
|
39
39
|
map {|d| [d.name, d.author] })
|
|
40
40
|
|
|
41
41
|
show('an integer steps into an array',
|
|
42
|
-
Document.select { [:name, :meta.
|
|
43
|
-
Document.select { [:name, :meta.
|
|
42
|
+
Document.select { [:name, :meta.dig_text(:tags, 0).as(:first_tag)] },
|
|
43
|
+
Document.select { [:name, :meta.dig_text(:tags, 0).as(:first_tag)] }.
|
|
44
44
|
map {|d| [d.name, d.first_tag] })
|
|
45
45
|
|
|
46
46
|
# A dug value is an expression like any other, so it compares and orders.
|
|
47
|
-
show('
|
|
48
|
-
Document.where { :meta.
|
|
49
|
-
Document.where { :meta.
|
|
47
|
+
show('dig_text in a condition',
|
|
48
|
+
Document.where { :meta.dig_text(:author, :country) == 'JP' },
|
|
49
|
+
Document.where { :meta.dig_text(:author, :country) == 'JP' }.pluck(:name))
|
|
50
50
|
|
|
51
51
|
# A dug value is text, so a number goes through a cast. Comparing it with
|
|
52
52
|
# one instead is refused rather than left to the adapters, which answer that
|
|
53
53
|
# three ways: true here, an error on PostgreSQL, true on MySQL.
|
|
54
54
|
show('a number wants a cast',
|
|
55
|
-
Document.where { cast(:meta.
|
|
56
|
-
Document.where { cast(:meta.
|
|
55
|
+
Document.where { cast(:meta.dig_text(:views), 'integer') > 100 },
|
|
56
|
+
Document.where { cast(:meta.dig_text(:views), 'integer') > 100 }.pluck(:name))
|
|
57
57
|
|
|
58
58
|
begin
|
|
59
|
-
Document.where { :meta.
|
|
59
|
+
Document.where { :meta.dig_text(:views) > 100 }
|
|
60
60
|
rescue ArgumentError => e
|
|
61
61
|
puts '--- and without one it says so ---'
|
|
62
62
|
puts " #{e.message}"
|
|
63
63
|
puts
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
#
|
|
67
|
-
# or
|
|
68
|
-
# the reason a Ruby value is refused on this side too.
|
|
69
|
-
show('
|
|
70
|
-
Document.select { [:name, :meta.
|
|
71
|
-
Document.select { [:name, :meta.
|
|
66
|
+
# dig keeps the JSON, for a part of the document to be dug into further
|
|
67
|
+
# or asked the JSON questions. The quotes around the string are the sign of
|
|
68
|
+
# it -- and the reason a Ruby value is refused on this side too.
|
|
69
|
+
show('dig keeps the JSON',
|
|
70
|
+
Document.select { [:name, :meta.dig(:author).as(:author)] },
|
|
71
|
+
Document.select { [:name, :meta.dig(:author).as(:author)] }.
|
|
72
72
|
map {|d| [d.name, d.author] })
|
|
73
73
|
|
|
74
74
|
# 2. Asking whether a key is there at all, which is not the same as asking
|
|
@@ -217,15 +217,16 @@ module ActiveRecord
|
|
|
217
217
|
|
|
218
218
|
# Reading inside a JSON document, by the name of what Hash does. A
|
|
219
219
|
# string or symbol steps into an object, an integer into an array, and
|
|
220
|
-
# what comes back is
|
|
221
|
-
#
|
|
222
|
-
#
|
|
220
|
+
# what comes back is still JSON, the way Hash#dig hands back the
|
|
221
|
+
# structure itself -- for a document to be dug into further or asked
|
|
222
|
+
# the JSON questions. dig_text gives the value as text instead,
|
|
223
|
+
# which is what a comparison wants.
|
|
223
224
|
def dig(*path)
|
|
224
|
-
JsonPath.new(self, path)
|
|
225
|
+
JsonPath.new(self, path, as_json: true)
|
|
225
226
|
end
|
|
226
227
|
|
|
227
|
-
def
|
|
228
|
-
JsonPath.new(self, path
|
|
228
|
+
def dig_text(*path)
|
|
229
|
+
JsonPath.new(self, path)
|
|
229
230
|
end
|
|
230
231
|
|
|
231
232
|
# Keys taken out of a JSON document, by the name of what Hash does,
|
|
@@ -513,20 +514,20 @@ module ActiveRecord
|
|
|
513
514
|
#
|
|
514
515
|
# The path is turned into a string either way, so a key with a space or
|
|
515
516
|
# a quote in it travels as itself rather than having to be refused.
|
|
516
|
-
# What a dug value may be compared with.
|
|
517
|
+
# What a dug value may be compared with. dig_text gives text on every
|
|
517
518
|
# adapter, and what a text value compared with a number means is a
|
|
518
|
-
# question the three answer three ways: `
|
|
519
|
+
# question the three answer three ways: `dig_text(:n) == 5` is true on
|
|
519
520
|
# SQLite, an error on PostgreSQL and true on MySQL, while
|
|
520
|
-
# `
|
|
521
|
-
# which type was meant, and then all three agree.
|
|
521
|
+
# `dig_text(:flag) == true` is true, an error, and false. cast is what
|
|
522
|
+
# says which type was meant, and then all three agree.
|
|
522
523
|
#
|
|
523
|
-
#
|
|
524
|
-
# its quotes, so `
|
|
525
|
-
# error on PostgreSQL and true on MySQL.
|
|
526
|
-
# value.
|
|
524
|
+
# dig is refused the other way about: the JSON for a string carries
|
|
525
|
+
# its quotes, so `dig(:name) == 'alice'` is false on SQLite, an
|
|
526
|
+
# error on PostgreSQL and true on MySQL. dig_text is the one that
|
|
527
|
+
# gives the value.
|
|
527
528
|
#
|
|
528
|
-
# A string against
|
|
529
|
-
# a function, another dug value -- go through untouched.
|
|
529
|
+
# A string against dig_text, and anything the block itself built -- a
|
|
530
|
+
# column, a function, another dug value -- go through untouched.
|
|
530
531
|
module JsonComparable
|
|
531
532
|
%i[== != < <= > >=].each do |operator|
|
|
532
533
|
define_method(operator) do |other|
|
|
@@ -552,9 +553,9 @@ module ActiveRecord
|
|
|
552
553
|
return if other.is_a?(::String) && !as_json
|
|
553
554
|
|
|
554
555
|
raise ArgumentError, as_json ?
|
|
555
|
-
"
|
|
556
|
-
"means something different on every adapter;
|
|
557
|
-
"
|
|
556
|
+
"dig gives JSON, and comparing it with #{other.inspect} " \
|
|
557
|
+
"means something different on every adapter; dig_text gives the value" :
|
|
558
|
+
"dig_text gives text, and comparing it with #{other.inspect} means " \
|
|
558
559
|
"something different on every adapter; cast it to the type meant"
|
|
559
560
|
end
|
|
560
561
|
|
|
@@ -568,18 +569,18 @@ module ActiveRecord
|
|
|
568
569
|
end
|
|
569
570
|
end
|
|
570
571
|
|
|
571
|
-
# The JSON operations read a document, and what
|
|
572
|
-
# `
|
|
573
|
-
#
|
|
574
|
-
#
|
|
572
|
+
# The JSON operations read a document, and what dig gives is one:
|
|
573
|
+
# `dig(:author).key?(:email)` and `dig(:tags).contains?(...)` are the
|
|
574
|
+
# same question asked of a part of it, and the adapters answer them
|
|
575
|
+
# alike. What dig_text gives is text, and reading that as a document
|
|
575
576
|
# again is where they part company: SQLite parses it back and MySQL
|
|
576
577
|
# takes it as written, where PostgreSQL has no such function for text.
|
|
577
578
|
module JsonDocument
|
|
578
|
-
%i[dig
|
|
579
|
+
%i[dig dig_text key? contains? bury except].each do |name|
|
|
579
580
|
define_method(name) do |*args|
|
|
580
581
|
unless as_json
|
|
581
582
|
raise ArgumentError,
|
|
582
|
-
"
|
|
583
|
+
"dig_text gives text, and #{name} reads JSON; dig keeps it"
|
|
583
584
|
end
|
|
584
585
|
super(*args)
|
|
585
586
|
end
|
|
@@ -615,8 +616,8 @@ module ActiveRecord
|
|
|
615
616
|
extracted = Arel::Nodes::InfixOperation.new(
|
|
616
617
|
as_json ? :"->" : :"->>", document, Arel::Nodes.build_quoted(dollar_path))
|
|
617
618
|
# SQLite's ->> gives back the value with its type, where the other
|
|
618
|
-
# two give text. Cast so that `
|
|
619
|
-
# thing everywhere, and a number wants a cast everywhere too.
|
|
619
|
+
# two give text. Cast so that `dig_text(:n) == '5'` means the
|
|
620
|
+
# same thing everywhere, and a number wants a cast everywhere too.
|
|
620
621
|
as_json ? extracted : Arel::Nodes::NamedFunction.new(
|
|
621
622
|
'CAST', [Arel::Nodes::As.new(extracted, Arel::Nodes::SqlLiteral.new('text'))])
|
|
622
623
|
end
|
data/test/test_block_syntax.rb
CHANGED
|
@@ -1891,115 +1891,115 @@ class TestBlockSyntax < Minitest::Test
|
|
|
1891
1891
|
Doc.create!(name: 'two', meta: json_document({ 'n' => 9 }))
|
|
1892
1892
|
end
|
|
1893
1893
|
|
|
1894
|
-
def
|
|
1894
|
+
def test_dig_text_a_key
|
|
1895
1895
|
seed_docs
|
|
1896
|
-
assert_equal(%w[5 9], Doc.order(:name).select { :meta.
|
|
1896
|
+
assert_equal(%w[5 9], Doc.order(:name).select { :meta.dig_text(:n).as(:v) }.map(&:v))
|
|
1897
1897
|
end
|
|
1898
1898
|
|
|
1899
|
-
def
|
|
1899
|
+
def test_dig_text_a_path
|
|
1900
1900
|
seed_docs
|
|
1901
1901
|
assert_equal(['deep', nil],
|
|
1902
|
-
Doc.order(:name).select { :meta.
|
|
1902
|
+
Doc.order(:name).select { :meta.dig_text(:a, :b).as(:v) }.map(&:v))
|
|
1903
1903
|
end
|
|
1904
1904
|
|
|
1905
|
-
def
|
|
1905
|
+
def test_dig_text_an_array_index
|
|
1906
1906
|
seed_docs
|
|
1907
1907
|
assert_equal(['x', nil],
|
|
1908
|
-
Doc.order(:name).select { :meta.
|
|
1908
|
+
Doc.order(:name).select { :meta.dig_text(:tags, 0).as(:v) }.map(&:v))
|
|
1909
1909
|
end
|
|
1910
1910
|
|
|
1911
1911
|
# A key that is not a plain name travels as itself rather than being refused.
|
|
1912
|
-
def
|
|
1912
|
+
def test_dig_text_a_key_that_needs_quoting
|
|
1913
1913
|
seed_docs
|
|
1914
1914
|
assert_equal(['1', nil],
|
|
1915
|
-
Doc.order(:name).select { :meta.
|
|
1915
|
+
Doc.order(:name).select { :meta.dig_text(:'odd key').as(:v) }.map(&:v))
|
|
1916
1916
|
end
|
|
1917
1917
|
|
|
1918
|
-
#
|
|
1918
|
+
# dig_text gives text on every adapter -- SQLite's ->> would otherwise give the
|
|
1919
1919
|
# value with its type -- so a number is compared through a cast.
|
|
1920
|
-
def
|
|
1920
|
+
def test_dig_text_is_text_everywhere
|
|
1921
1921
|
seed_docs
|
|
1922
|
-
assert_equal(['one'], Doc.where { :meta.
|
|
1922
|
+
assert_equal(['one'], Doc.where { :meta.dig_text(:n) == '5' }.pluck(:name))
|
|
1923
1923
|
type = integer_type
|
|
1924
|
-
assert_equal(['two'], Doc.where { cast(:meta.
|
|
1924
|
+
assert_equal(['two'], Doc.where { cast(:meta.dig_text(:n), type) > 6 }.pluck(:name))
|
|
1925
1925
|
end
|
|
1926
1926
|
|
|
1927
|
-
def
|
|
1927
|
+
def test_dig_keeps_the_json
|
|
1928
1928
|
seed_docs
|
|
1929
|
-
value = Doc.where { :name == 'one' }.select { :meta.
|
|
1929
|
+
value = Doc.where { :name == 'one' }.select { :meta.dig(:tags).as(:v) }.first.v
|
|
1930
1930
|
assert_equal(%w[x y], value.is_a?(String) ? JSON.parse(value) : value)
|
|
1931
1931
|
end
|
|
1932
1932
|
|
|
1933
1933
|
# What text compared with a number means is a question the three adapters
|
|
1934
|
-
# answer three ways -- `
|
|
1935
|
-
# PostgreSQL and true on MySQL, and `
|
|
1934
|
+
# answer three ways -- `dig_text(:n) == 5` is true on SQLite, an error on
|
|
1935
|
+
# PostgreSQL and true on MySQL, and `dig_text(:flag) == true` is true, an error
|
|
1936
1936
|
# and false -- so the comparison is refused rather than left to them.
|
|
1937
|
-
def
|
|
1938
|
-
e = assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1937
|
+
def test_dig_text_refuses_a_comparison_with_anything_but_text
|
|
1938
|
+
e = assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:n) == 5 } }
|
|
1939
1939
|
assert_match(/cast/, e.message)
|
|
1940
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1941
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1942
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1943
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1944
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1940
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:n) != 5 } }
|
|
1941
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:n) > 6 } }
|
|
1942
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:flag) == true } }
|
|
1943
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:n).in?([1, 2]) } }
|
|
1944
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:n).between?(1, 9) } }
|
|
1945
1945
|
end
|
|
1946
1946
|
|
|
1947
1947
|
# A string is what a dug value compares to; so is anything the block built
|
|
1948
1948
|
# rather than wrote as a literal, since that is nobody's guess to make.
|
|
1949
|
-
def
|
|
1949
|
+
def test_dig_text_compares_with_text_and_with_expressions
|
|
1950
1950
|
seed_docs
|
|
1951
|
-
assert_equal(['one'], Doc.where { :meta.
|
|
1952
|
-
assert_equal([], Doc.where { :meta.
|
|
1953
|
-
assert_equal(['one'], Doc.where { :meta.
|
|
1951
|
+
assert_equal(['one'], Doc.where { :meta.dig_text(:n) == '5' }.pluck(:name))
|
|
1952
|
+
assert_equal([], Doc.where { :meta.dig_text(:n) == :name }.pluck(:name))
|
|
1953
|
+
assert_equal(['one'], Doc.where { :meta.dig_text(:n) == upper('5') }.pluck(:name))
|
|
1954
1954
|
type = integer_type
|
|
1955
|
-
assert_equal(['one'], Doc.where { cast(:meta.
|
|
1955
|
+
assert_equal(['one'], Doc.where { cast(:meta.dig_text(:n), type) == 5 }.pluck(:name))
|
|
1956
1956
|
end
|
|
1957
1957
|
|
|
1958
1958
|
# The JSON for a string carries its quotes, so the same comparison is
|
|
1959
1959
|
# refused the other way about: false on SQLite, an error on PostgreSQL and
|
|
1960
1960
|
# true on MySQL.
|
|
1961
|
-
def
|
|
1962
|
-
e = assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1963
|
-
assert_match(/
|
|
1964
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1961
|
+
def test_dig_refuses_a_comparison_with_a_ruby_value
|
|
1962
|
+
e = assert_raises(ArgumentError) { Doc.where { :meta.dig(:a) == 'deep' } }
|
|
1963
|
+
assert_match(/dig_text gives the value/, e.message)
|
|
1964
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig(:n) == 5 } }
|
|
1965
1965
|
end
|
|
1966
1966
|
|
|
1967
|
-
# What
|
|
1967
|
+
# What dig gives is a document, so the JSON operations read it: the
|
|
1968
1968
|
# same question asked of a part rather than of the whole.
|
|
1969
|
-
def
|
|
1969
|
+
def test_the_json_operations_read_what_dig_kept
|
|
1970
1970
|
seed_docs
|
|
1971
|
-
assert_equal(['one'], Doc.where { :meta.
|
|
1972
|
-
assert_equal(%w[one two], Doc.where { :meta.
|
|
1971
|
+
assert_equal(['one'], Doc.where { :meta.dig(:a).key?(:b) }.pluck(:name))
|
|
1972
|
+
assert_equal(%w[one two], Doc.where { :meta.dig(:n).not_null? }.order(:name).pluck(:name))
|
|
1973
1973
|
assert_equal(['one'],
|
|
1974
|
-
Doc.where { :meta.
|
|
1974
|
+
Doc.where { :meta.dig(:a).dig_text(:b) == 'deep' }.pluck(:name))
|
|
1975
1975
|
value = Doc.where { :name == 'one' }.
|
|
1976
|
-
select { :meta.
|
|
1976
|
+
select { :meta.dig(:a).bury(:b, 'x').as(:v) }.first.v
|
|
1977
1977
|
assert_equal('x', (value.is_a?(String) ? JSON.parse(value) : value)['b'])
|
|
1978
1978
|
end
|
|
1979
1979
|
|
|
1980
|
-
def
|
|
1980
|
+
def test_containment_reads_what_dig_kept
|
|
1981
1981
|
skip_without_json_containment
|
|
1982
1982
|
seed_docs
|
|
1983
|
-
assert_equal(['one'], Doc.where { :meta.
|
|
1984
|
-
assert_equal([], Doc.where { :meta.
|
|
1983
|
+
assert_equal(['one'], Doc.where { :meta.dig(:tags).contains?(['x']) }.pluck(:name))
|
|
1984
|
+
assert_equal([], Doc.where { :meta.dig(:tags).contains?(['z']) }.pluck(:name))
|
|
1985
1985
|
end
|
|
1986
1986
|
|
|
1987
1987
|
# Reading text back as a document is where the adapters part company:
|
|
1988
1988
|
# SQLite parses it, MySQL takes it as written, PostgreSQL has no such
|
|
1989
1989
|
# function for text at all.
|
|
1990
1990
|
def test_the_json_operations_are_refused_on_a_dug_value
|
|
1991
|
-
e = assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1992
|
-
assert_match(/
|
|
1993
|
-
assert_raises(ArgumentError) { Doc.where { :meta.
|
|
1994
|
-
assert_raises(ArgumentError) { Doc.select { :meta.
|
|
1995
|
-
assert_raises(ArgumentError) { Doc.select { :meta.
|
|
1996
|
-
assert_raises(ArgumentError) { Doc.select { :meta.
|
|
1997
|
-
assert_raises(ArgumentError) { Doc.select { :meta.
|
|
1991
|
+
e = assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:a).key?(:b) } }
|
|
1992
|
+
assert_match(/dig keeps it/, e.message)
|
|
1993
|
+
assert_raises(ArgumentError) { Doc.where { :meta.dig_text(:a).contains?(b: 1) } }
|
|
1994
|
+
assert_raises(ArgumentError) { Doc.select { :meta.dig_text(:a).dig_text(:b) } }
|
|
1995
|
+
assert_raises(ArgumentError) { Doc.select { :meta.dig_text(:a).dig(:b) } }
|
|
1996
|
+
assert_raises(ArgumentError) { Doc.select { :meta.dig_text(:a).bury(:b, 'x') } }
|
|
1997
|
+
assert_raises(ArgumentError) { Doc.select { :meta.dig_text(:a).except(:b) } }
|
|
1998
1998
|
end
|
|
1999
1999
|
|
|
2000
|
-
def
|
|
2000
|
+
def test_dig_text_from_a_qualified_column
|
|
2001
2001
|
seed_docs
|
|
2002
|
-
assert_equal(['one'], Doc.where { :docs[:meta].
|
|
2002
|
+
assert_equal(['one'], Doc.where { :docs[:meta].dig_text(:a, :b) == 'deep' }.pluck(:name))
|
|
2003
2003
|
end
|
|
2004
2004
|
|
|
2005
2005
|
def test_key
|
|
@@ -2022,7 +2022,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2022
2022
|
|
|
2023
2023
|
def test_dig_needs_a_path
|
|
2024
2024
|
assert_raises(ArgumentError) { Doc.select { :meta.dig } }
|
|
2025
|
-
e = assert_raises(ArgumentError) { Doc.select { :meta.
|
|
2025
|
+
e = assert_raises(ArgumentError) { Doc.select { :meta.dig_text(1.5) } }
|
|
2026
2026
|
assert_match(/key or an array index/, e.message)
|
|
2027
2027
|
end
|
|
2028
2028
|
|
|
@@ -2283,7 +2283,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2283
2283
|
|
|
2284
2284
|
# The value can be read out of the document it is going into.
|
|
2285
2285
|
def test_bury_an_expression
|
|
2286
|
-
assert_equal('5', buried { { meta: :meta.bury(:copy, :meta.
|
|
2286
|
+
assert_equal('5', buried { { meta: :meta.bury(:copy, :meta.dig_text(:n)) } }['copy'].to_s)
|
|
2287
2287
|
end
|
|
2288
2288
|
|
|
2289
2289
|
# It is an expression, so it does not have to be written anywhere.
|