postgres_ext 2.1.3 → 2.2.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/.gitignore +1 -0
- data/.travis.yml +13 -22
- data/CHANGELOG.md +45 -0
- data/README.md +13 -0
- data/Rakefile +83 -7
- data/docs/querying.md +76 -6
- data/lib/postgres_ext/active_record/relation/predicate_builder.rb +2 -2
- data/lib/postgres_ext/active_record/relation/query_methods.rb +44 -11
- data/lib/postgres_ext/arel/predications.rb +10 -0
- data/lib/postgres_ext/arel/visitors/to_sql.rb +1 -1
- data/lib/postgres_ext/version.rb +1 -1
- data/postgres_ext.gemspec +4 -2
- data/test/arel/array_test.rb +92 -0
- data/{spec/arel/inet_spec.rb → test/arel/inet_test.rb} +6 -6
- data/{spec/queries/array_queries_spec.rb → test/queries/array_queries_test.rb} +22 -14
- data/test/queries/common_table_expression_test.rb +49 -0
- data/{spec/queries/contains_querie_spec.rb → test/queries/contains_test.rb} +5 -5
- data/test/queries/sanity_test.rb +32 -0
- data/test/queries/window_functions_test.rb +64 -0
- data/test/test_helper.rb +38 -0
- metadata +74 -150
- data/spec/arel/array_spec.rb +0 -77
- data/spec/arel/rank_spec.rb +0 -0
- data/spec/dummy/.gitignore +0 -15
- data/spec/dummy/README.rdoc +0 -261
- data/spec/dummy/Rakefile +0 -7
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +0 -15
- data/spec/dummy/app/assets/stylesheets/application.css +0 -13
- data/spec/dummy/app/controllers/application_controller.rb +0 -3
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/person.rb +0 -2
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/config/application.rb +0 -56
- data/spec/dummy/config/boot.rb +0 -6
- data/spec/dummy/config/database.yml.example +0 -14
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -33
- data/spec/dummy/config/environments/production.rb +0 -68
- data/spec/dummy/config/environments/test.rb +0 -33
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/inflections.rb +0 -15
- data/spec/dummy/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/config/initializers/session_store.rb +0 -8
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/config/locales/en.yml +0 -5
- data/spec/dummy/config/routes.rb +0 -58
- data/spec/dummy/db/migrate/20120501163758_create_people.rb +0 -15
- data/spec/dummy/db/schema.rb +0 -31
- data/spec/dummy/db/seeds.rb +0 -7
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -25
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +0 -241
- data/spec/dummy/public/robots.txt +0 -5
- data/spec/dummy/script/rails +0 -6
- data/spec/dummy/spec/factories/people.rb +0 -7
- data/spec/dummy/test/fixtures/.gitkeep +0 -0
- data/spec/dummy/test/functional/.gitkeep +0 -0
- data/spec/dummy/test/integration/.gitkeep +0 -0
- data/spec/dummy/test/performance/browsing_test.rb +0 -12
- data/spec/dummy/test/test_helper.rb +0 -13
- data/spec/dummy/test/unit/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy/vendor/plugins/.gitkeep +0 -0
- data/spec/queries/common_table_expression_spec.rb +0 -36
- data/spec/queries/sanity_spec.rb +0 -17
- data/spec/queries/window_functions_spec.rb +0 -54
- data/spec/spec_helper.rb +0 -36
@@ -21,5 +21,15 @@ module Arel
|
|
21
21
|
def overlap(other)
|
22
22
|
Nodes::Overlap.new self, other
|
23
23
|
end
|
24
|
+
|
25
|
+
def any(other)
|
26
|
+
any_tags_function = Arel::Nodes::NamedFunction.new('ANY', [self])
|
27
|
+
Arel::Nodes::Equality.new(other, any_tags_function)
|
28
|
+
end
|
29
|
+
|
30
|
+
def all(other)
|
31
|
+
any_tags_function = Arel::Nodes::NamedFunction.new('ALL', [self])
|
32
|
+
Arel::Nodes::Equality.new(other, any_tags_function)
|
33
|
+
end
|
24
34
|
end
|
25
35
|
end
|
@@ -4,7 +4,7 @@ module Arel
|
|
4
4
|
module Visitors
|
5
5
|
class ToSql
|
6
6
|
def visit_Array o, a
|
7
|
-
column = a.relation.engine.columns.find { |col| col.name == a.name.to_s } if a
|
7
|
+
column = a.relation.engine.connection.columns(a.relation.name).find { |col| col.name == a.name.to_s } if a
|
8
8
|
if column && column.respond_to?(:array) && column.array
|
9
9
|
quoted o, a
|
10
10
|
else
|
data/lib/postgres_ext/version.rb
CHANGED
data/postgres_ext.gemspec
CHANGED
@@ -20,10 +20,12 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_dependency 'arel', '~> 4.0.1'
|
21
21
|
gem.add_dependency 'pg_array_parser', '~> 0.0.9'
|
22
22
|
|
23
|
-
gem.add_development_dependency '
|
24
|
-
gem.add_development_dependency '
|
23
|
+
gem.add_development_dependency 'rake', '~> 10.1.0'
|
24
|
+
gem.add_development_dependency 'minitest'
|
25
|
+
gem.add_development_dependency 'm'
|
25
26
|
gem.add_development_dependency 'bourne', '~> 1.3.0'
|
26
27
|
gem.add_development_dependency 'database_cleaner'
|
28
|
+
gem.add_development_dependency 'dotenv'
|
27
29
|
if RUBY_PLATFORM =~ /java/
|
28
30
|
gem.add_development_dependency 'activerecord-jdbcpostgresql-adapter', '1.3.0.beta2'
|
29
31
|
else
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Array Column Predicates' do
|
4
|
+
let(:arel_table) { Person.arel_table }
|
5
|
+
describe 'Array Any' do
|
6
|
+
context 'string value' do
|
7
|
+
it 'creates any predicates' do
|
8
|
+
arel_table.where(arel_table[:tags].any('tag')).to_sql.must_match /'tag' = ANY\("people"\."tags"\)/
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'integer value' do
|
13
|
+
it 'creates any predicates' do
|
14
|
+
arel_table.where(arel_table[:tag_ids].any(1)).to_sql.must_match /1 = ANY\("people"\."tag_ids"\)/
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'Array All' do
|
20
|
+
context 'string value' do
|
21
|
+
it 'creates all predicates' do
|
22
|
+
arel_table.where(arel_table[:tags].all('tag')).to_sql.must_match /'tag' = ALL\("people"\."tags"\)/
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'integer value' do
|
27
|
+
it 'creates all predicates' do
|
28
|
+
arel_table.where(arel_table[:tag_ids].all(1)).to_sql.must_match /1 = ALL\("people"\."tag_ids"\)/
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'Array Overlap' do
|
34
|
+
it 'converts Arel overlap statement' do
|
35
|
+
arel_table.where(arel_table[:tags].overlap(['tag','tag 2'])).to_sql.must_match /&& '\{"tag","tag 2"\}'/
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'converts Arel overlap statement' do
|
39
|
+
arel_table.where(arel_table[:tag_ids].overlap([1,2])).to_sql.must_match /&& '\{1,2\}'/
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'works with count (and other predicates)' do
|
43
|
+
Person.where(arel_table[:tag_ids].overlap([1,2])).count.must_equal 0
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns matched records' do
|
47
|
+
one = Person.create!(:tags => ['one'])
|
48
|
+
two = Person.create!(:tags => ['two'])
|
49
|
+
|
50
|
+
query = arel_table.where(arel_table[:tags].overlap(['one'])).project(Arel.sql('*'))
|
51
|
+
Person.find_by_sql(query.to_sql).must_include(one)
|
52
|
+
|
53
|
+
query = arel_table.where(arel_table[:tags].overlap(['two'])).project(Arel.sql('*'))
|
54
|
+
Person.find_by_sql(query.to_sql).must_include(two)
|
55
|
+
|
56
|
+
query = arel_table.where(arel_table[:tags].overlap(['two','one'])).project(Arel.sql('*'))
|
57
|
+
Person.find_by_sql(query.to_sql).must_include(two)
|
58
|
+
Person.find_by_sql(query.to_sql).must_include(one)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'Array Contains' do
|
63
|
+
it 'converts Arel contains statement and escapes strings' do
|
64
|
+
arel_table.where(arel_table[:tags].contains(['tag','tag 2'])).to_sql.must_match /@> '\{"tag","tag 2"\}'/
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'converts Arel contains statement with numbers' do
|
68
|
+
arel_table.where(arel_table[:tag_ids].contains([1,2])).to_sql.must_match /@> '\{1,2\}'/
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'works with count (and other predicates)' do
|
72
|
+
Person.where(arel_table[:tag_ids].contains([1,2])).count.must_equal 0
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns matched records' do
|
76
|
+
one = Person.create!(:tags => ['one', 'two', 'three'])
|
77
|
+
two = Person.create!(:tags => ['one', 'three'])
|
78
|
+
|
79
|
+
query = arel_table.where(arel_table[:tags].contains(['one', 'two'])).project(Arel.sql('*'))
|
80
|
+
Person.find_by_sql(query.to_sql).must_include one
|
81
|
+
Person.find_by_sql(query.to_sql).wont_include two
|
82
|
+
|
83
|
+
query = arel_table.where(arel_table[:tags].contains(['one', 'three'])).project(Arel.sql('*'))
|
84
|
+
Person.find_by_sql(query.to_sql).must_include one
|
85
|
+
Person.find_by_sql(query.to_sql).must_include two
|
86
|
+
|
87
|
+
query = arel_table.where(arel_table[:tags].contains(['two'])).project(Arel.sql('*'))
|
88
|
+
Person.find_by_sql(query.to_sql).must_include one
|
89
|
+
Person.find_by_sql(query.to_sql).wont_include two
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
describe 'INET related AREL functions' do
|
4
4
|
describe 'quoting IPAddr in sql statement' do
|
5
5
|
it 'properly converts IPAddr to quoted strings when passed as an argument to a where clause' do
|
6
|
-
Person.where(:ip => IPAddr.new('127.0.0.1')).to_sql.
|
6
|
+
Person.where(:ip => IPAddr.new('127.0.0.1')).to_sql.must_include("'127.0.0.1/32'")
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -11,7 +11,7 @@ describe 'INET related AREL functions' do
|
|
11
11
|
it 'converts Arel contained_within statements to <<' do
|
12
12
|
arel_table = Person.arel_table
|
13
13
|
|
14
|
-
arel_table.where(arel_table[:ip].contained_within(IPAddr.new('127.0.0.1/24'))).to_sql.
|
14
|
+
arel_table.where(arel_table[:ip].contained_within(IPAddr.new('127.0.0.1/24'))).to_sql.must_match /<< '127.0.0.0\/24'/
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -19,14 +19,14 @@ describe 'INET related AREL functions' do
|
|
19
19
|
it 'converts Arel contained_within_or_equals statements to <<=' do
|
20
20
|
arel_table = Person.arel_table
|
21
21
|
|
22
|
-
arel_table.where(arel_table[:ip].contained_within_or_equals(IPAddr.new('127.0.0.1/24'))).to_sql.
|
22
|
+
arel_table.where(arel_table[:ip].contained_within_or_equals(IPAddr.new('127.0.0.1/24'))).to_sql.must_match /<<= '127.0.0.0\/24'/
|
23
23
|
end
|
24
24
|
end
|
25
25
|
describe 'contains (>>) operator' do
|
26
26
|
it 'converts Arel contains statements to >>' do
|
27
27
|
arel_table = Person.arel_table
|
28
28
|
|
29
|
-
arel_table.where(arel_table[:ip].contains(IPAddr.new('127.0.0.1/24'))).to_sql.
|
29
|
+
arel_table.where(arel_table[:ip].contains(IPAddr.new('127.0.0.1/24'))).to_sql.must_match />> '127.0.0.0\/24'/
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -34,7 +34,7 @@ describe 'INET related AREL functions' do
|
|
34
34
|
it 'converts Arel contains_or_equals statements to >>=' do
|
35
35
|
arel_table = Person.arel_table
|
36
36
|
|
37
|
-
arel_table.where(arel_table[:ip].contains_or_equals(IPAddr.new('127.0.0.1/24'))).to_sql.
|
37
|
+
arel_table.where(arel_table[:ip].contains_or_equals(IPAddr.new('127.0.0.1/24'))).to_sql.must_match />>= '127.0.0.0\/24'/
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
describe 'Array queries' do
|
4
4
|
let(:equality_regex) { %r{\"people\"\.\"tags\" = '\{\"working\"\}'} }
|
@@ -10,63 +10,71 @@ describe 'Array queries' do
|
|
10
10
|
describe '.where(:array_column => [])' do
|
11
11
|
it 'returns an array string instead of IN ()' do
|
12
12
|
query = Person.where(:tags => ['working']).to_sql
|
13
|
-
query.
|
13
|
+
query.must_match equality_regex
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.where(joins: { array_column: [] })' do
|
18
|
+
it 'returns an array string instead of IN ()' do
|
19
|
+
skip
|
20
|
+
query = Person.joins(:hm_tags).where(tags: { categories: ['working'] }).to_sql
|
21
|
+
query.must_match equality_regex
|
14
22
|
end
|
15
23
|
end
|
16
24
|
|
17
25
|
describe '.where.overlap(:column => value)' do
|
18
26
|
it 'generates the appropriate where clause' do
|
19
27
|
query = Person.where.overlap(:tag_ids => [1,2])
|
20
|
-
query.to_sql.
|
28
|
+
query.to_sql.must_match overlap_regex
|
21
29
|
end
|
22
30
|
|
23
31
|
it 'allows chaining' do
|
24
32
|
query = Person.where.overlap(:tag_ids => [1,2]).where(:tags => ['working']).to_sql
|
25
33
|
|
26
|
-
query.
|
27
|
-
query.
|
34
|
+
query.must_match overlap_regex
|
35
|
+
query.must_match equality_regex
|
28
36
|
end
|
29
37
|
end
|
30
38
|
|
31
39
|
describe '.where.contains(:column => value)' do
|
32
40
|
it 'generates the appropriate where clause' do
|
33
41
|
query = Person.where.contains(:tag_ids => [1,2])
|
34
|
-
query.to_sql.
|
42
|
+
query.to_sql.must_match contains_regex
|
35
43
|
end
|
36
44
|
|
37
45
|
it 'allows chaining' do
|
38
46
|
query = Person.where.contains(:tag_ids => [1,2]).where(:tags => ['working']).to_sql
|
39
47
|
|
40
|
-
query.
|
41
|
-
query.
|
48
|
+
query.must_match contains_regex
|
49
|
+
query.must_match equality_regex
|
42
50
|
end
|
43
51
|
end
|
44
52
|
|
45
53
|
describe '.where.any(:column => value)' do
|
46
54
|
it 'generates the appropriate where clause' do
|
47
55
|
query = Person.where.any(:tag_ids => 2)
|
48
|
-
query.to_sql.
|
56
|
+
query.to_sql.must_match any_regex
|
49
57
|
end
|
50
58
|
|
51
59
|
it 'allows chaining' do
|
52
60
|
query = Person.where.any(:tag_ids => 2).where(:tags => ['working']).to_sql
|
53
61
|
|
54
|
-
query.
|
55
|
-
query.
|
62
|
+
query.must_match any_regex
|
63
|
+
query.must_match equality_regex
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
59
67
|
describe '.where.all(:column => value)' do
|
60
68
|
it 'generates the appropriate where clause' do
|
61
69
|
query = Person.where.all(:tag_ids => 2)
|
62
|
-
query.to_sql.
|
70
|
+
query.to_sql.must_match all_regex
|
63
71
|
end
|
64
72
|
|
65
73
|
it 'allows chaining' do
|
66
74
|
query = Person.where.all(:tag_ids => 2).where(:tags => ['working']).to_sql
|
67
75
|
|
68
|
-
query.
|
69
|
-
query.
|
76
|
+
query.must_match all_regex
|
77
|
+
query.must_match equality_regex
|
70
78
|
end
|
71
79
|
end
|
72
80
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Common Table Expression queries' do
|
4
|
+
describe '.with(common_table_expression_hash)' do
|
5
|
+
it 'generates an expression with the CTE' do
|
6
|
+
query = Person.with(lucky_number_seven: Person.where(lucky_number: 7)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id')
|
7
|
+
query.to_sql.must_equal 'WITH lucky_number_seven AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'generates an expression with multiple CTEs' do
|
11
|
+
query = Person.with(lucky_number_seven: Person.where(lucky_number: 7), lucky_number_three: Person.where(lucky_number: 3)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id').joins('JOIN lucky_number_three ON lucky_number_three.id = people.id')
|
12
|
+
query.to_sql.must_equal 'WITH lucky_number_seven AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7), lucky_number_three AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 3) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id JOIN lucky_number_three ON lucky_number_three.id = people.id'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'generates an expression with multiple with calls' do
|
16
|
+
query = Person.with(lucky_number_seven: Person.where(lucky_number: 7)).with(lucky_number_three: Person.where(lucky_number: 3)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id').joins('JOIN lucky_number_three ON lucky_number_three.id = people.id')
|
17
|
+
query.to_sql.must_equal 'WITH lucky_number_seven AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7), lucky_number_three AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 3) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id JOIN lucky_number_three ON lucky_number_three.id = people.id'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'generates an expression with recursive' do
|
21
|
+
query = Person.with.recursive(lucky_number_seven: Person.where(lucky_number: 7)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id')
|
22
|
+
query.to_sql.must_equal 'WITH RECURSIVE lucky_number_seven AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'accepts Arel::SelectMangers' do
|
26
|
+
arel_table = Arel::Table.new 'test'
|
27
|
+
arel_manager = arel_table.project arel_table[:foo]
|
28
|
+
|
29
|
+
query = Person.with(testing: arel_manager)
|
30
|
+
query.to_sql.must_equal 'WITH testing AS (SELECT "test"."foo" FROM "test") SELECT "people".* FROM "people"'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.from_cte(common_table_expression_hash)' do
|
35
|
+
it 'generates an expression with the CTE as the main table' do
|
36
|
+
query = Person.from_cte('lucky_number_seven', Person.where(lucky_number: 7)).where(id: 5)
|
37
|
+
query.to_sql.must_equal 'WITH lucky_number_seven AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7) SELECT "lucky_number_seven".* FROM "lucky_number_seven" WHERE "lucky_number_seven"."id" = 5'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns instances of the model' do
|
41
|
+
3.times { Person.create! lucky_number: 7 }
|
42
|
+
3.times { Person.create! lucky_number: 3 }
|
43
|
+
people = Person.from_cte('lucky_number_seven', Person.where(lucky_number: 7))
|
44
|
+
|
45
|
+
people.count.must_equal 3
|
46
|
+
people.first.lucky_number.must_equal 7
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
describe 'Contains queries' do
|
4
4
|
let(:contained_within_regex) { %r{\"people\"\.\"ip\" << '127.0.0.1/24'} }
|
@@ -9,28 +9,28 @@ describe 'Contains queries' do
|
|
9
9
|
describe '.where.contained_within(:column, value)' do
|
10
10
|
it 'generates the appropriate where clause' do
|
11
11
|
query = Person.where.contained_within(:ip => '127.0.0.1/24')
|
12
|
-
query.to_sql.
|
12
|
+
query.to_sql.must_match contained_within_regex
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
describe '.where.contained_within_or_equals(:column, value)' do
|
17
17
|
it 'generates the appropriate where clause' do
|
18
18
|
query = Person.where.contained_within_or_equals(:ip => '127.0.0.1/24')
|
19
|
-
query.to_sql.
|
19
|
+
query.to_sql.must_match contained_within_equals_regex
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
describe '.where.contains(:column, value)' do
|
24
24
|
it 'generates the appropriate where clause' do
|
25
25
|
query = Person.where.contains(:ip => '127.0.0.1')
|
26
|
-
query.to_sql.
|
26
|
+
query.to_sql.must_match contains_regex
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
describe '.where.contained_within_or_equals(:column, value)' do
|
31
31
|
it 'generates the appropriate where clause' do
|
32
32
|
query = Person.where.contains_or_equals(:ip => '127.0.0.1')
|
33
|
-
query.to_sql.
|
33
|
+
query.to_sql.must_match contains_equals_regex
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Ensure that we don\'t stomp on Active Record\'s queries' do
|
4
|
+
describe '.where' do
|
5
|
+
it 'generates IN clauses for non array columns' do
|
6
|
+
query = Person.where(:id => [1,2,3]).to_sql
|
7
|
+
|
8
|
+
query.must_match /IN \(1, 2, 3\)/
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'generates IN clauses for non array columns' do
|
12
|
+
query = Person.where(:id => []).to_sql
|
13
|
+
|
14
|
+
query.must_match /WHERE 1=0/
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.where(joins: { column: [] })' do
|
19
|
+
it 'generates IN clause for non array columns' do
|
20
|
+
query = Person.joins(:hm_tags).where(tags: { id: ['working'] }).to_sql
|
21
|
+
query.must_match /WHERE "tags"\."id" IN \('working'\)/
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#habtm_association_ids=' do
|
26
|
+
it 'properly deletes records through HABTM association method' do
|
27
|
+
person = Person.create(habtm_tag_ids: [Tag.create.id])
|
28
|
+
person.update_attributes(habtm_tag_ids: [])
|
29
|
+
person.habtm_tag_ids.must_be_empty
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Window functions' do
|
4
|
+
describe 'ranked' do
|
5
|
+
it 'uses the order when no order is passed to ranked' do
|
6
|
+
query = Person.order(lucky_number: :desc).ranked
|
7
|
+
query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people" ORDER BY "people"."lucky_number" DESC'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'uses the order when no order is passed to ranked, swapped calls' do
|
11
|
+
query = Person.ranked.order(lucky_number: :desc)
|
12
|
+
query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people" ORDER BY "people"."lucky_number" DESC'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'uses the rank value when there is an order passed to it' do
|
16
|
+
query = Person.ranked(lucky_number: :desc)
|
17
|
+
query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people"'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'uses the rank value when a symbol passed to it' do
|
21
|
+
query = Person.ranked(:lucky_number)
|
22
|
+
query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" ASC) FROM "people"'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'uses the rank value when a string passed to it' do
|
26
|
+
query = Person.ranked('ORDER BY lucky_number DESC')
|
27
|
+
query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY lucky_number DESC) FROM "people"'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'combines the order and rank' do
|
31
|
+
query = Person.ranked(lucky_number: :desc).order(id: :asc)
|
32
|
+
query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people" ORDER BY "people"."id" ASC'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'executes the query with the rank' do
|
36
|
+
Person.create!
|
37
|
+
Person.create!
|
38
|
+
|
39
|
+
ranked_people = Person.ranked(:id)
|
40
|
+
ranked_people[0].rank.must_equal 1
|
41
|
+
ranked_people[1].rank.must_equal 2
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not apply the rank when performing a count' do
|
45
|
+
query = Person.ranked(lucky_number: :desc).count
|
46
|
+
query.must_equal 0
|
47
|
+
|
48
|
+
Person.create!
|
49
|
+
|
50
|
+
query = Person.ranked(lucky_number: :desc).count
|
51
|
+
query.must_equal 1
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'does not apply the rank when checking presence' do
|
55
|
+
query = Person.ranked(lucky_number: :desc).present?
|
56
|
+
query.must_equal false
|
57
|
+
|
58
|
+
Person.create!
|
59
|
+
|
60
|
+
query = Person.ranked(lucky_number: :desc).present?
|
61
|
+
query.must_equal true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|