arel-extensions 8.0.2 → 9.0.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/CHANGELOG.md +41 -0
- data/README.md +83 -2
- data/ext/arel/nodes/ascending.rb +2 -0
- data/ext/arel/nodes/descending.rb +2 -0
- data/ext/arel/order_predications.rb +2 -0
- data/lib/active_record/query_methods.rb +33 -31
- data/lib/arel/array_predications.rb +2 -0
- data/lib/arel/attributes/cast.rb +2 -0
- data/lib/arel/attributes/key.rb +2 -0
- data/lib/arel/extensions/version.rb +3 -1
- data/lib/arel/extensions.rb +2 -0
- data/lib/arel/gis_predications.rb +2 -0
- data/lib/arel/json_predications.rb +2 -0
- data/lib/arel/nodes/binary_value.rb +2 -0
- data/lib/arel/nodes/contained_by.rb +2 -0
- data/lib/arel/nodes/excludes.rb +2 -0
- data/lib/arel/nodes/geometry.rb +2 -0
- data/lib/arel/nodes/has_any_key.rb +2 -0
- data/lib/arel/nodes/has_key.rb +2 -0
- data/lib/arel/nodes/has_keys.rb +2 -0
- data/lib/arel/nodes/hex_encoded_binary_value.rb +2 -0
- data/lib/arel/nodes/intersects.rb +2 -0
- data/lib/arel/nodes/random.rb +2 -0
- data/lib/arel/nodes/relation.rb +2 -0
- data/lib/arel/nodes/ts_match.rb +2 -0
- data/lib/arel/nodes/ts_query.rb +2 -0
- data/lib/arel/nodes/ts_rank.rb +2 -0
- data/lib/arel/nodes/ts_rank_cd.rb +2 -0
- data/lib/arel/nodes/ts_vector.rb +2 -0
- data/lib/arel/nodes/within.rb +2 -0
- data/lib/arel/ts_predications.rb +2 -0
- data/lib/arel/visitors/postgresql_extensions.rb +2 -0
- data/lib/arel/visitors/sunstone_extensions.rb +2 -0
- data/lib/arel/visitors/to_sql_extensions.rb +2 -0
- metadata +15 -45
- data/.github/workflows/ci.yml +0 -212
- data/.gitignore +0 -3
- data/Gemfile +0 -5
- data/Rakefile +0 -14
- data/arel-extensions.gemspec +0 -29
- data/test/binary_value_test.rb +0 -23
- data/test/database.rb +0 -51
- data/test/gis_test.rb +0 -23
- data/test/order_test.rb +0 -83
- data/test/sunstone_test.rb +0 -149
- data/test/test_helper.rb +0 -210
- data/test/ts_test.rb +0 -151
data/test/test_helper.rb
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
# To make testing/debugging easier, test within this source tree versus an
|
|
2
|
-
# installed gem
|
|
3
|
-
$LOAD_PATH << File.expand_path('../lib', __FILE__)
|
|
4
|
-
|
|
5
|
-
# require 'simplecov'
|
|
6
|
-
# SimpleCov.start do
|
|
7
|
-
# add_group 'lib', 'sunstone/lib'
|
|
8
|
-
# add_group 'ext', 'sunstone/ext'
|
|
9
|
-
# end
|
|
10
|
-
|
|
11
|
-
require 'rgeo'
|
|
12
|
-
require 'debug'
|
|
13
|
-
require "minitest/autorun"
|
|
14
|
-
require 'minitest/unit'
|
|
15
|
-
require 'minitest/reporters'
|
|
16
|
-
require 'webmock/minitest'
|
|
17
|
-
# require 'mocha/minitest'
|
|
18
|
-
require 'active_record'
|
|
19
|
-
require 'sunstone'
|
|
20
|
-
require 'arel/extensions'
|
|
21
|
-
require 'activerecord-postgis-adapter'
|
|
22
|
-
|
|
23
|
-
# Setup the test db
|
|
24
|
-
ActiveSupport.test_order = :random
|
|
25
|
-
require File.expand_path('../database', __FILE__)
|
|
26
|
-
|
|
27
|
-
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
|
28
|
-
|
|
29
|
-
$debugging = false
|
|
30
|
-
|
|
31
|
-
# File 'lib/active_support/testing/declarative.rb', somewhere in rails....
|
|
32
|
-
class ActiveSupport::TestCase
|
|
33
|
-
include WebMock::API
|
|
34
|
-
|
|
35
|
-
# File 'lib/active_support/testing/declarative.rb'
|
|
36
|
-
def self.test(name, &block)
|
|
37
|
-
test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
|
|
38
|
-
defined = method_defined? test_name
|
|
39
|
-
raise "#{test_name} is already defined in #{self}" if defined
|
|
40
|
-
if block_given?
|
|
41
|
-
define_method(test_name, &block)
|
|
42
|
-
else
|
|
43
|
-
define_method(test_name) do
|
|
44
|
-
skip "No implementation provided for #{name}"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def webmock(method, path, query=nil)
|
|
50
|
-
query = deep_transform_query(query) if query
|
|
51
|
-
|
|
52
|
-
stub_request(method, /^#{ActiveRecord::Base.connection.instance_variable_get(:@connection).url}/).with do |req|
|
|
53
|
-
if query
|
|
54
|
-
req&.uri&.path == path && req.uri.query && unpack(req.uri.query.sub(/=true$/, '')) == query
|
|
55
|
-
else
|
|
56
|
-
req&.uri&.path == path && req.uri.query.nil?
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def setup
|
|
62
|
-
sunstone_schema = {
|
|
63
|
-
addresses: {
|
|
64
|
-
columns: {
|
|
65
|
-
id: { type: :integer, primary_key: true, null: false, array: false },
|
|
66
|
-
name: { type: :string, primary_key: false, null: true, array: false },
|
|
67
|
-
property_id: { type: :integer, primary_key: false, null: true, array: false }
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
properties: {
|
|
71
|
-
columns: {
|
|
72
|
-
id: { type: :integer, primary_key: true, null: false, array: false },
|
|
73
|
-
name: { type: :string, primary_key: false, null: true, array: false },
|
|
74
|
-
metadata: { type: :json, primary_key: false, null: true, array: false }
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
req_stub = stub_request(:get, /^http:\/\/example.com/).with do |req|
|
|
80
|
-
case req.uri.path
|
|
81
|
-
when '/tables'
|
|
82
|
-
true
|
|
83
|
-
when /^\/\w+\/schema$/i
|
|
84
|
-
true
|
|
85
|
-
else
|
|
86
|
-
false
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
req_stub.to_return do |req|
|
|
91
|
-
case req.uri.path
|
|
92
|
-
when '/tables'
|
|
93
|
-
{
|
|
94
|
-
body: sunstone_schema.keys.to_json,
|
|
95
|
-
headers: { 'StandardAPI-Version' => '5.0.0.5' }
|
|
96
|
-
}
|
|
97
|
-
when /^\/(\w+)\/schema$/i
|
|
98
|
-
{
|
|
99
|
-
body: sunstone_schema[$1.to_sym].to_json,
|
|
100
|
-
headers: { 'StandardAPI-Version' => '5.0.0.5' }
|
|
101
|
-
}
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def debug
|
|
107
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
108
|
-
$debugging = true
|
|
109
|
-
yield
|
|
110
|
-
ensure
|
|
111
|
-
ActiveRecord::Base.logger = nil
|
|
112
|
-
$debugging = false
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class SQLLogger
|
|
117
|
-
class << self
|
|
118
|
-
attr_accessor :ignored_sql, :log, :log_all
|
|
119
|
-
def clear_log; self.log = []; self.log_all = []; end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
self.clear_log
|
|
123
|
-
|
|
124
|
-
self.ignored_sql = [/^PRAGMA/i, /^SELECT currval/i, /^SELECT CAST/i, /^SELECT @@IDENTITY/i, /^SELECT @@ROWCOUNT/i, /^SAVEPOINT/i, /^ROLLBACK TO SAVEPOINT/i, /^RELEASE SAVEPOINT/i, /^SHOW max_identifier_length/i, /^BEGIN/i, /^COMMIT/i]
|
|
125
|
-
|
|
126
|
-
# FIXME: this needs to be refactored so specific database can add their own
|
|
127
|
-
# ignored SQL, or better yet, use a different notification for the queries
|
|
128
|
-
# instead examining the SQL content.
|
|
129
|
-
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
|
|
130
|
-
mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im]
|
|
131
|
-
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
|
|
132
|
-
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
|
|
133
|
-
|
|
134
|
-
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
|
|
135
|
-
ignored_sql.concat db_ignored_sql
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
attr_reader :ignore
|
|
139
|
-
|
|
140
|
-
def initialize(ignore = Regexp.union(self.class.ignored_sql))
|
|
141
|
-
@ignore = ignore
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def call(name, start, finish, message_id, values)
|
|
145
|
-
sql = values[:sql]
|
|
146
|
-
|
|
147
|
-
# FIXME: this seems bad. we should probably have a better way to indicate
|
|
148
|
-
# the query was cached
|
|
149
|
-
return if 'CACHE' == values[:name]
|
|
150
|
-
|
|
151
|
-
self.class.log_all << sql
|
|
152
|
-
unless ignore =~ sql
|
|
153
|
-
if $debugging
|
|
154
|
-
puts caller.select { |l| l.starts_with?(File.expand_path('../../lib', __FILE__)) }
|
|
155
|
-
puts "\n\n"
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
self.class.log << sql unless ignore =~ sql
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
ActiveSupport::Notifications.subscribe('sql.active_record', SQLLogger.new)
|
|
162
|
-
|
|
163
|
-
def deep_transform_query(object)
|
|
164
|
-
case object
|
|
165
|
-
when Hash
|
|
166
|
-
object.each_with_object({}) do |(key, value), result|
|
|
167
|
-
result[key.to_s] = deep_transform_query(value)
|
|
168
|
-
end
|
|
169
|
-
when Array
|
|
170
|
-
object.map {|e| deep_transform_query(e) }
|
|
171
|
-
when Symbol
|
|
172
|
-
object.to_s
|
|
173
|
-
else
|
|
174
|
-
object
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def assert_sar(query, method, path, query_params = {}, body = nil)
|
|
179
|
-
sar = query.to_sar
|
|
180
|
-
|
|
181
|
-
assert_equal method, sar.method
|
|
182
|
-
assert_equal path, sar.path.split('?').first
|
|
183
|
-
assert_equal deep_transform_query(query_params), MessagePack.unpack(CGI::unescape(sar.path.split('?').last))
|
|
184
|
-
if body.nil?
|
|
185
|
-
assert_nil sar.body
|
|
186
|
-
else
|
|
187
|
-
assert_equal body, sar.body
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def assert_sql(expected, query)
|
|
192
|
-
assert_equal expected.strip, query.to_sql.strip.gsub(/\s+/, ' ')
|
|
193
|
-
end
|
|
194
|
-
# test/unit backwards compatibility methods
|
|
195
|
-
alias :assert_raise :assert_raises
|
|
196
|
-
alias :assert_not_empty :refute_empty
|
|
197
|
-
alias :assert_not_equal :refute_equal
|
|
198
|
-
alias :assert_not_in_delta :refute_in_delta
|
|
199
|
-
alias :assert_not_in_epsilon :refute_in_epsilon
|
|
200
|
-
alias :assert_not_includes :refute_includes
|
|
201
|
-
alias :assert_not_instance_of :refute_instance_of
|
|
202
|
-
alias :assert_not_kind_of :refute_kind_of
|
|
203
|
-
alias :assert_no_match :refute_match
|
|
204
|
-
alias :assert_not_nil :refute_nil
|
|
205
|
-
alias :assert_not_operator :refute_operator
|
|
206
|
-
alias :assert_not_predicate :refute_predicate
|
|
207
|
-
alias :assert_not_respond_to :refute_respond_to
|
|
208
|
-
alias :assert_not_same :refute_same
|
|
209
|
-
|
|
210
|
-
end
|
data/test/ts_test.rb
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
require 'test_helper'
|
|
2
|
-
|
|
3
|
-
class TSTest < ActiveSupport::TestCase
|
|
4
|
-
|
|
5
|
-
test 'tsquery(query)' do
|
|
6
|
-
query = Property.where( Arel::Nodes::TSQuery.new('query') )
|
|
7
|
-
|
|
8
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
9
|
-
SELECT "properties".* FROM "properties"
|
|
10
|
-
WHERE to_tsquery('query')
|
|
11
|
-
SQL
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
test 'tsquery(query, language: lang)' do
|
|
15
|
-
query = Property.where( Arel::Nodes::TSQuery.new('query', language: 'lang') )
|
|
16
|
-
|
|
17
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
18
|
-
SELECT "properties".* FROM "properties"
|
|
19
|
-
WHERE to_tsquery('lang', 'query')
|
|
20
|
-
SQL
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
test 'tsvector(attribute)' do
|
|
24
|
-
query = Property.where( Arel::Nodes::TSVector.new(Property.arel_table[:name]) )
|
|
25
|
-
|
|
26
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
27
|
-
SELECT "properties".* FROM "properties"
|
|
28
|
-
WHERE to_tsvector("properties"."name")
|
|
29
|
-
SQL
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
test 'tsvector(text)' do
|
|
33
|
-
query = Property.where( Arel::Nodes::TSVector.new(
|
|
34
|
-
Arel::Nodes::Quoted.new("my data O'Malley")
|
|
35
|
-
))
|
|
36
|
-
|
|
37
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
38
|
-
SELECT "properties".* FROM "properties"
|
|
39
|
-
WHERE to_tsvector('my data O''Malley')
|
|
40
|
-
SQL
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
test 'tsvector(attribute, language: lang)' do
|
|
44
|
-
query = Property.where( Arel::Nodes::TSVector.new(Property.arel_table[:name], language: 'lang') )
|
|
45
|
-
|
|
46
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
47
|
-
SELECT "properties".* FROM "properties"
|
|
48
|
-
WHERE to_tsvector('lang', "properties"."name")
|
|
49
|
-
SQL
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
test 'ts_match(attrbute, tsquery)' do
|
|
53
|
-
query = Property.where(
|
|
54
|
-
Arel::Nodes::TSMatch.new(
|
|
55
|
-
Property.arel_table[:name],
|
|
56
|
-
Arel::Nodes::TSQuery.new('query')
|
|
57
|
-
)
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
61
|
-
SELECT "properties".* FROM "properties"
|
|
62
|
-
WHERE "properties"."name" @@ to_tsquery('query')
|
|
63
|
-
SQL
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
test 'ts_match(tsvector, tsquery)' do
|
|
67
|
-
query = Property.where(
|
|
68
|
-
Arel::Nodes::TSMatch.new(
|
|
69
|
-
Arel::Nodes::TSVector.new(Property.arel_table[:name]),
|
|
70
|
-
Arel::Nodes::TSQuery.new('query')
|
|
71
|
-
)
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
75
|
-
SELECT "properties".* FROM "properties"
|
|
76
|
-
WHERE to_tsvector("properties"."name") @@ to_tsquery('query')
|
|
77
|
-
SQL
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
test 'ts_rank(attribute, tsquery)' do
|
|
81
|
-
query = Property.where(
|
|
82
|
-
Arel::Nodes::TSRank.new(
|
|
83
|
-
Property.arel_table[:name],
|
|
84
|
-
Arel::Nodes::TSQuery.new('query')
|
|
85
|
-
)
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
89
|
-
SELECT "properties".* FROM "properties"
|
|
90
|
-
WHERE ts_rank("properties"."name", to_tsquery('query'))
|
|
91
|
-
SQL
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
test 'ts_rank(tsvector, tsquery)' do
|
|
95
|
-
query = Property.where(
|
|
96
|
-
Arel::Nodes::TSRank.new(
|
|
97
|
-
Arel::Nodes::TSVector.new(Property.arel_table[:name]),
|
|
98
|
-
Arel::Nodes::TSQuery.new('query')
|
|
99
|
-
)
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
103
|
-
SELECT "properties".* FROM "properties"
|
|
104
|
-
WHERE ts_rank(to_tsvector("properties"."name"), to_tsquery('query'))
|
|
105
|
-
SQL
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
test 'ts_rank_cd(attribute, tsquery)' do
|
|
109
|
-
query = Property.where(
|
|
110
|
-
Arel::Nodes::TSRankCD.new(
|
|
111
|
-
Property.arel_table[:name],
|
|
112
|
-
Arel::Nodes::TSQuery.new('query')
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
117
|
-
SELECT "properties".* FROM "properties"
|
|
118
|
-
WHERE ts_rank_cd("properties"."name", to_tsquery('query'))
|
|
119
|
-
SQL
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
test 'ts_rank_cd(tsvector, tsquery)' do
|
|
123
|
-
query = Property.where(
|
|
124
|
-
Arel::Nodes::TSRankCD.new(
|
|
125
|
-
Arel::Nodes::TSVector.new(Property.arel_table[:name]),
|
|
126
|
-
Arel::Nodes::TSQuery.new('query')
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
131
|
-
SELECT "properties".* FROM "properties"
|
|
132
|
-
WHERE ts_rank_cd(to_tsvector("properties"."name"), to_tsquery('query'))
|
|
133
|
-
SQL
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
test 'ts_rank_cd(tsvector, tsquery, normalization)' do
|
|
137
|
-
query = Property.where(
|
|
138
|
-
Arel::Nodes::TSRankCD.new(
|
|
139
|
-
Arel::Nodes::TSVector.new(Property.arel_table[:name]),
|
|
140
|
-
Arel::Nodes::TSQuery.new('query'),
|
|
141
|
-
5
|
|
142
|
-
)
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
assert_equal(<<~SQL.gsub(/( +|\n)/, ' ').strip, query.to_sql)
|
|
146
|
-
SELECT "properties".* FROM "properties"
|
|
147
|
-
WHERE ts_rank_cd(to_tsvector("properties"."name"), to_tsquery('query'), 5)
|
|
148
|
-
SQL
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
end
|