activerecord-refined 0.6.1 → 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/.github/workflows/sandbox.yml +1 -1
- data/README.md +114 -35
- data/activerecord-refined.gemspec +4 -2
- data/benchmark/query_building.rb +1 -1
- data/examples/complex_joins.rb +30 -0
- data/examples/ctes.rb +2 -2
- data/examples/json.rb +45 -17
- data/examples/postgresql.rb +1 -1
- data/examples/subqueries.rb +1 -1
- data/examples/windows.rb +1 -1
- data/examples/writes.rb +4 -4
- data/lib/active_record/refined/ast.rb +155 -20
- data/lib/active_record/refined.rb +94 -13
- data/lib/activerecord-refined/version.rb +1 -1
- data/lib/activerecord-refined.rb +5 -3
- data/test/test_block_syntax.rb +214 -29
- data/test/test_helper.rb +6 -1
- metadata +19 -3
data/test/test_block_syntax.rb
CHANGED
|
@@ -697,7 +697,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
697
697
|
end
|
|
698
698
|
|
|
699
699
|
# A relation without an explicit select list selects its primary key, the
|
|
700
|
-
# same way
|
|
700
|
+
# same way Active Record's own where(id: relation) does.
|
|
701
701
|
def test_in_subquery_selects_primary_key_by_default
|
|
702
702
|
assert_sql(/WHERE "authors"."id" IN \(SELECT "posts"."id" FROM "posts"\)/,
|
|
703
703
|
Author.where { :id.in?(Post.all) }.to_sql)
|
|
@@ -878,6 +878,80 @@ class TestBlockSyntax < Minitest::Test
|
|
|
878
878
|
assert_raises(ArgumentError) { Author.left_outer_joins(:posts, as: :p) }
|
|
879
879
|
end
|
|
880
880
|
|
|
881
|
+
# The other two outer joins, which Active Record has no method for. MySQL
|
|
882
|
+
# has no FULL OUTER JOIN either, and says so before the SQL is built.
|
|
883
|
+
def test_right_outer_joins_with_block
|
|
884
|
+
assert_sql(/RIGHT OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"/,
|
|
885
|
+
Author.right_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }.to_sql)
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
def test_full_outer_joins_with_block
|
|
889
|
+
skip_without_full_outer_joins
|
|
890
|
+
assert_sql(/FULL OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"/,
|
|
891
|
+
Author.full_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }.to_sql)
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def test_full_outer_joins_says_where_it_cannot_go
|
|
895
|
+
skip "#{ADAPTER} has FULL OUTER JOIN" unless ADAPTER == 'mysql2'
|
|
896
|
+
e = assert_raises(NotImplementedError) do
|
|
897
|
+
Author.full_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }
|
|
898
|
+
end
|
|
899
|
+
assert_match(/no equivalent on MySQL/, e.message)
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
def test_right_outer_joins_with_alias
|
|
903
|
+
assert_sql(
|
|
904
|
+
/RIGHT OUTER JOIN "authors" (?:AS )?"mentors" ON "mentors"."id" = "authors"."id"/,
|
|
905
|
+
Author.right_outer_joins(:authors, as: :mentors) { :mentors[:id] == :authors[:id] }.to_sql)
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
# An association is what joins and left_outer_joins read; there is nothing
|
|
909
|
+
# for these two to read one as.
|
|
910
|
+
def test_the_other_outer_joins_need_a_block
|
|
911
|
+
e = assert_raises(ArgumentError) { Author.right_outer_joins(:posts) }
|
|
912
|
+
assert_match(/takes a table and the block/, e.message)
|
|
913
|
+
unless ADAPTER == 'mysql2'
|
|
914
|
+
assert_raises(ArgumentError) { Author.full_outer_joins(:posts) }
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
# CROSS JOIN: every row against every row, so there is no condition to give.
|
|
919
|
+
def test_cross_joins
|
|
920
|
+
assert_sql(/FROM "authors" CROSS JOIN "posts"/, Author.cross_joins(:posts).to_sql)
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
def test_cross_joins_with_alias
|
|
924
|
+
assert_sql(/CROSS JOIN "authors" "others"/,
|
|
925
|
+
Author.cross_joins(:authors, as: :others).to_sql)
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
def test_cross_joins_takes_no_block
|
|
929
|
+
e = assert_raises(ArgumentError) { Author.cross_joins(:posts) { :id == 1 } }
|
|
930
|
+
assert_match(/no condition/, e.message)
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
def test_cross_joins_execution
|
|
934
|
+
Author.delete_all
|
|
935
|
+
Post.delete_all
|
|
936
|
+
Author.create!(name: 'a')
|
|
937
|
+
Author.create!(name: 'b')
|
|
938
|
+
Post.create!(title: 'one')
|
|
939
|
+
Post.create!(title: 'two')
|
|
940
|
+
Post.create!(title: 'three')
|
|
941
|
+
assert_equal(6, Author.cross_joins(:posts).count)
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
def test_right_outer_joins_execution
|
|
945
|
+
Author.delete_all
|
|
946
|
+
Post.delete_all
|
|
947
|
+
author = Author.create!(name: 'a')
|
|
948
|
+
Post.create!(title: 'hers', author_id: author.id)
|
|
949
|
+
Post.create!(title: 'nobody\'s', author_id: nil)
|
|
950
|
+
assert_equal(['a', nil],
|
|
951
|
+
Author.right_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }.
|
|
952
|
+
order { :posts[:title] }.pluck(:'authors.name'))
|
|
953
|
+
end
|
|
954
|
+
|
|
881
955
|
def test_joins_without_alias_still_delegates
|
|
882
956
|
assert_sql(/INNER JOIN "posts" ON "posts"."author_id" = "authors"."id"/,
|
|
883
957
|
Author.joins(:posts).to_sql)
|
|
@@ -892,7 +966,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
892
966
|
pluck(:name).sort)
|
|
893
967
|
end
|
|
894
968
|
|
|
895
|
-
#
|
|
969
|
+
# Active Record's from only takes a table name as a string.
|
|
896
970
|
def test_from_symbol
|
|
897
971
|
assert_sql(/FROM "tree"/, Node.from(:tree).to_sql)
|
|
898
972
|
end
|
|
@@ -1817,48 +1891,115 @@ class TestBlockSyntax < Minitest::Test
|
|
|
1817
1891
|
Doc.create!(name: 'two', meta: json_document({ 'n' => 9 }))
|
|
1818
1892
|
end
|
|
1819
1893
|
|
|
1820
|
-
def
|
|
1894
|
+
def test_dig_text_a_key
|
|
1821
1895
|
seed_docs
|
|
1822
|
-
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))
|
|
1823
1897
|
end
|
|
1824
1898
|
|
|
1825
|
-
def
|
|
1899
|
+
def test_dig_text_a_path
|
|
1826
1900
|
seed_docs
|
|
1827
1901
|
assert_equal(['deep', nil],
|
|
1828
|
-
Doc.order(:name).select { :meta.
|
|
1902
|
+
Doc.order(:name).select { :meta.dig_text(:a, :b).as(:v) }.map(&:v))
|
|
1829
1903
|
end
|
|
1830
1904
|
|
|
1831
|
-
def
|
|
1905
|
+
def test_dig_text_an_array_index
|
|
1832
1906
|
seed_docs
|
|
1833
1907
|
assert_equal(['x', nil],
|
|
1834
|
-
Doc.order(:name).select { :meta.
|
|
1908
|
+
Doc.order(:name).select { :meta.dig_text(:tags, 0).as(:v) }.map(&:v))
|
|
1835
1909
|
end
|
|
1836
1910
|
|
|
1837
1911
|
# A key that is not a plain name travels as itself rather than being refused.
|
|
1838
|
-
def
|
|
1912
|
+
def test_dig_text_a_key_that_needs_quoting
|
|
1839
1913
|
seed_docs
|
|
1840
1914
|
assert_equal(['1', nil],
|
|
1841
|
-
Doc.order(:name).select { :meta.
|
|
1915
|
+
Doc.order(:name).select { :meta.dig_text(:'odd key').as(:v) }.map(&:v))
|
|
1842
1916
|
end
|
|
1843
1917
|
|
|
1844
|
-
#
|
|
1918
|
+
# dig_text gives text on every adapter -- SQLite's ->> would otherwise give the
|
|
1845
1919
|
# value with its type -- so a number is compared through a cast.
|
|
1846
|
-
def
|
|
1920
|
+
def test_dig_text_is_text_everywhere
|
|
1847
1921
|
seed_docs
|
|
1848
|
-
assert_equal(['one'], Doc.where { :meta.
|
|
1922
|
+
assert_equal(['one'], Doc.where { :meta.dig_text(:n) == '5' }.pluck(:name))
|
|
1849
1923
|
type = integer_type
|
|
1850
|
-
assert_equal(['two'], Doc.where { cast(:meta.
|
|
1924
|
+
assert_equal(['two'], Doc.where { cast(:meta.dig_text(:n), type) > 6 }.pluck(:name))
|
|
1851
1925
|
end
|
|
1852
1926
|
|
|
1853
|
-
def
|
|
1927
|
+
def test_dig_keeps_the_json
|
|
1854
1928
|
seed_docs
|
|
1855
|
-
value = Doc.where { :name == 'one' }.select { :meta.
|
|
1929
|
+
value = Doc.where { :name == 'one' }.select { :meta.dig(:tags).as(:v) }.first.v
|
|
1856
1930
|
assert_equal(%w[x y], value.is_a?(String) ? JSON.parse(value) : value)
|
|
1857
1931
|
end
|
|
1858
1932
|
|
|
1859
|
-
|
|
1933
|
+
# What text compared with a number means is a question the three adapters
|
|
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
|
+
# and false -- so the comparison is refused rather than left to them.
|
|
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
|
+
assert_match(/cast/, e.message)
|
|
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
|
+
end
|
|
1946
|
+
|
|
1947
|
+
# A string is what a dug value compares to; so is anything the block built
|
|
1948
|
+
# rather than wrote as a literal, since that is nobody's guess to make.
|
|
1949
|
+
def test_dig_text_compares_with_text_and_with_expressions
|
|
1860
1950
|
seed_docs
|
|
1861
|
-
assert_equal(['one'], Doc.where { :
|
|
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
|
+
type = integer_type
|
|
1955
|
+
assert_equal(['one'], Doc.where { cast(:meta.dig_text(:n), type) == 5 }.pluck(:name))
|
|
1956
|
+
end
|
|
1957
|
+
|
|
1958
|
+
# The JSON for a string carries its quotes, so the same comparison is
|
|
1959
|
+
# refused the other way about: false on SQLite, an error on PostgreSQL and
|
|
1960
|
+
# true on MySQL.
|
|
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
|
+
end
|
|
1966
|
+
|
|
1967
|
+
# What dig gives is a document, so the JSON operations read it: the
|
|
1968
|
+
# same question asked of a part rather than of the whole.
|
|
1969
|
+
def test_the_json_operations_read_what_dig_kept
|
|
1970
|
+
seed_docs
|
|
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
|
+
assert_equal(['one'],
|
|
1974
|
+
Doc.where { :meta.dig(:a).dig_text(:b) == 'deep' }.pluck(:name))
|
|
1975
|
+
value = Doc.where { :name == 'one' }.
|
|
1976
|
+
select { :meta.dig(:a).bury(:b, 'x').as(:v) }.first.v
|
|
1977
|
+
assert_equal('x', (value.is_a?(String) ? JSON.parse(value) : value)['b'])
|
|
1978
|
+
end
|
|
1979
|
+
|
|
1980
|
+
def test_containment_reads_what_dig_kept
|
|
1981
|
+
skip_without_json_containment
|
|
1982
|
+
seed_docs
|
|
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
|
+
end
|
|
1986
|
+
|
|
1987
|
+
# Reading text back as a document is where the adapters part company:
|
|
1988
|
+
# SQLite parses it, MySQL takes it as written, PostgreSQL has no such
|
|
1989
|
+
# function for text at all.
|
|
1990
|
+
def test_the_json_operations_are_refused_on_a_dug_value
|
|
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
|
+
end
|
|
1999
|
+
|
|
2000
|
+
def test_dig_text_from_a_qualified_column
|
|
2001
|
+
seed_docs
|
|
2002
|
+
assert_equal(['one'], Doc.where { :docs[:meta].dig_text(:a, :b) == 'deep' }.pluck(:name))
|
|
1862
2003
|
end
|
|
1863
2004
|
|
|
1864
2005
|
def test_key
|
|
@@ -1881,7 +2022,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
1881
2022
|
|
|
1882
2023
|
def test_dig_needs_a_path
|
|
1883
2024
|
assert_raises(ArgumentError) { Doc.select { :meta.dig } }
|
|
1884
|
-
e = assert_raises(ArgumentError) { Doc.select { :meta.
|
|
2025
|
+
e = assert_raises(ArgumentError) { Doc.select { :meta.dig_text(1.5) } }
|
|
1885
2026
|
assert_match(/key or an array index/, e.message)
|
|
1886
2027
|
end
|
|
1887
2028
|
|
|
@@ -2012,7 +2153,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2012
2153
|
def test_lateral_join
|
|
2013
2154
|
skip_without_lateral
|
|
2014
2155
|
seed_for_lateral
|
|
2015
|
-
rows = Author.joins(top_post, as: :top
|
|
2156
|
+
rows = Author.joins(top_post.lateral, as: :top).
|
|
2016
2157
|
select { [:name, :top[:title].as(:v)] }.map {|r| [r.name, r.v] }
|
|
2017
2158
|
assert_equal([['writes', 'b']], rows)
|
|
2018
2159
|
end
|
|
@@ -2021,7 +2162,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2021
2162
|
def test_left_outer_lateral_join
|
|
2022
2163
|
skip_without_lateral
|
|
2023
2164
|
seed_for_lateral
|
|
2024
|
-
rows = Author.left_outer_joins(top_post, as: :top
|
|
2165
|
+
rows = Author.left_outer_joins(top_post.lateral, as: :top).
|
|
2025
2166
|
select { [:name, :top[:title].as(:v)] }.order { :name }.map {|r| [r.name, r.v] }
|
|
2026
2167
|
assert_equal([['does not', nil], ['writes', 'b']], rows)
|
|
2027
2168
|
end
|
|
@@ -2031,22 +2172,28 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2031
2172
|
def test_lateral_join_takes_an_on_clause
|
|
2032
2173
|
skip_without_lateral
|
|
2033
2174
|
seed_for_lateral
|
|
2034
|
-
assert_equal(0, Author.joins(top_post, as: :top
|
|
2175
|
+
assert_equal(0, Author.joins(top_post.lateral, as: :top) {
|
|
2035
2176
|
:top[:title] == 'nothing'
|
|
2036
2177
|
}.count)
|
|
2037
|
-
assert_sql(/ON TRUE/, Author.joins(top_post, as: :top
|
|
2178
|
+
assert_sql(/ON TRUE/, Author.joins(top_post.lateral, as: :top).to_sql)
|
|
2038
2179
|
end
|
|
2039
2180
|
|
|
2040
|
-
def
|
|
2041
|
-
e = assert_raises(ArgumentError) { Author.joins(
|
|
2042
|
-
assert_match(/
|
|
2043
|
-
e = assert_raises(ArgumentError) { Author.joins(top_post
|
|
2181
|
+
def test_lateral_join_needs_the_mark_and_a_name
|
|
2182
|
+
e = assert_raises(ArgumentError) { Author.joins(top_post, as: :top) }
|
|
2183
|
+
assert_match(/mark it/, e.message)
|
|
2184
|
+
e = assert_raises(ArgumentError) { Author.joins(top_post.lateral) }
|
|
2044
2185
|
assert_match(/needs a name/, e.message)
|
|
2045
2186
|
end
|
|
2046
2187
|
|
|
2188
|
+
def test_lateral_spawns
|
|
2189
|
+
relation = top_post
|
|
2190
|
+
assert(relation.lateral.lateral_value)
|
|
2191
|
+
refute(relation.lateral_value)
|
|
2192
|
+
end
|
|
2193
|
+
|
|
2047
2194
|
def test_lateral_join_says_where_it_cannot_go
|
|
2048
2195
|
skip 'this one has LATERAL' if ADAPTER == 'postgresql' || (ADAPTER == 'mysql2' && !mariadb?)
|
|
2049
|
-
e = assert_raises(NotImplementedError) { Author.joins(top_post, as: :top
|
|
2196
|
+
e = assert_raises(NotImplementedError) { Author.joins(top_post.lateral, as: :top) }
|
|
2050
2197
|
assert_match(/lateral join has no equivalent/, e.message)
|
|
2051
2198
|
end
|
|
2052
2199
|
|
|
@@ -2136,7 +2283,7 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2136
2283
|
|
|
2137
2284
|
# The value can be read out of the document it is going into.
|
|
2138
2285
|
def test_bury_an_expression
|
|
2139
|
-
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)
|
|
2140
2287
|
end
|
|
2141
2288
|
|
|
2142
2289
|
# It is an expression, so it does not have to be written anywhere.
|
|
@@ -2151,6 +2298,44 @@ class TestBlockSyntax < Minitest::Test
|
|
|
2151
2298
|
assert_raises(ArgumentError) { Doc.select { :meta.bury(1.5, 'v') } }
|
|
2152
2299
|
end
|
|
2153
2300
|
|
|
2301
|
+
# except takes keys out, by the name of what Hash does. PostgreSQL
|
|
2302
|
+
# subtracts them where the others remove a path apiece, and what comes back
|
|
2303
|
+
# is the same document on all three.
|
|
2304
|
+
def test_except_a_key
|
|
2305
|
+
assert_equal({ 'a' => { 'b' => 'deep' }, 'tags' => %w[x y], 'odd key' => 1 },
|
|
2306
|
+
buried { { meta: :meta.except(:n) } })
|
|
2307
|
+
end
|
|
2308
|
+
|
|
2309
|
+
def test_except_several_keys
|
|
2310
|
+
assert_equal({ 'a' => { 'b' => 'deep' } },
|
|
2311
|
+
buried { { meta: :meta.except(:n, :tags, :'odd key') } })
|
|
2312
|
+
end
|
|
2313
|
+
|
|
2314
|
+
# A key that is not there is not an error, as Hash#except has none for it.
|
|
2315
|
+
def test_except_a_key_that_is_not_there
|
|
2316
|
+
assert_equal(5, buried { { meta: :meta.except(:nothing) } }['n'])
|
|
2317
|
+
end
|
|
2318
|
+
|
|
2319
|
+
# The document a bury gives back is one to take keys out of.
|
|
2320
|
+
def test_except_after_bury
|
|
2321
|
+
document = buried { { meta: :meta.bury(:fresh, 9).except(:n) } }
|
|
2322
|
+
assert_equal(9, document['fresh'])
|
|
2323
|
+
assert_nil(document['n'])
|
|
2324
|
+
end
|
|
2325
|
+
|
|
2326
|
+
def test_except_in_a_select
|
|
2327
|
+
seed_docs
|
|
2328
|
+
value = Doc.where { :name == 'one' }.select { :meta.except(:n).as(:v) }.first.v
|
|
2329
|
+
assert_nil((value.is_a?(String) ? JSON.parse(value) : value)['n'])
|
|
2330
|
+
end
|
|
2331
|
+
|
|
2332
|
+
# An index is not what the name says anywhere, and a path is bury's.
|
|
2333
|
+
def test_except_takes_keys
|
|
2334
|
+
assert_raises(ArgumentError) { Doc.select { :meta.except } }
|
|
2335
|
+
e = assert_raises(ArgumentError) { Doc.select { :meta.except(0) } }
|
|
2336
|
+
assert_match(/keys of the document/, e.message)
|
|
2337
|
+
end
|
|
2338
|
+
|
|
2154
2339
|
def test_default_where_syntax
|
|
2155
2340
|
assert_sql(/WHERE "users"."name" = 'Ruby' AND "users"."age" = 19/,
|
|
2156
2341
|
User.where(name: 'Ruby', age: 19).to_sql)
|
data/test/test_helper.rb
CHANGED
|
@@ -74,7 +74,12 @@ module SqlAssertions
|
|
|
74
74
|
skip "#{ADAPTER} has no JSON containment" if ADAPTER == 'sqlite3'
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
#
|
|
77
|
+
# MySQL is the one without a FULL OUTER JOIN, and so is MariaDB.
|
|
78
|
+
def skip_without_full_outer_joins
|
|
79
|
+
skip "#{ADAPTER} has no full outer join" if ADAPTER == 'mysql2'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# MariaDB's json is a checked longtext, which Active Record sees as a string
|
|
78
83
|
# and does not serialise a hash into -- what would go in is Ruby's inspect,
|
|
79
84
|
# which the check refuses. MySQL's json is a type of its own and takes the
|
|
80
85
|
# hash; handing that one a string would store the document as a JSON string
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-refined
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugo Maeda
|
|
@@ -73,7 +73,23 @@ dependencies:
|
|
|
73
73
|
- ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
75
|
version: "0"
|
|
76
|
-
|
|
76
|
+
- !ruby/object:Gem::Dependency
|
|
77
|
+
name: bump
|
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
-
|
|
81
|
+
- ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: "0"
|
|
84
|
+
type: :development
|
|
85
|
+
prerelease: false
|
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
-
|
|
89
|
+
- ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: "0"
|
|
92
|
+
description: Adding clean and powerful query syntax on Active Record using refinements
|
|
77
93
|
email:
|
|
78
94
|
- shugo@ruby-lang.org
|
|
79
95
|
executables: []
|
|
@@ -127,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
127
143
|
requirements: []
|
|
128
144
|
rubygems_version: 4.1.0.dev
|
|
129
145
|
specification_version: 4
|
|
130
|
-
summary:
|
|
146
|
+
summary: Write Active Record queries as Ruby expressions
|
|
131
147
|
test_files:
|
|
132
148
|
- test/test_block_syntax.rb
|
|
133
149
|
- test/test_helper.rb
|