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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +13 -22
  4. data/CHANGELOG.md +45 -0
  5. data/README.md +13 -0
  6. data/Rakefile +83 -7
  7. data/docs/querying.md +76 -6
  8. data/lib/postgres_ext/active_record/relation/predicate_builder.rb +2 -2
  9. data/lib/postgres_ext/active_record/relation/query_methods.rb +44 -11
  10. data/lib/postgres_ext/arel/predications.rb +10 -0
  11. data/lib/postgres_ext/arel/visitors/to_sql.rb +1 -1
  12. data/lib/postgres_ext/version.rb +1 -1
  13. data/postgres_ext.gemspec +4 -2
  14. data/test/arel/array_test.rb +92 -0
  15. data/{spec/arel/inet_spec.rb → test/arel/inet_test.rb} +6 -6
  16. data/{spec/queries/array_queries_spec.rb → test/queries/array_queries_test.rb} +22 -14
  17. data/test/queries/common_table_expression_test.rb +49 -0
  18. data/{spec/queries/contains_querie_spec.rb → test/queries/contains_test.rb} +5 -5
  19. data/test/queries/sanity_test.rb +32 -0
  20. data/test/queries/window_functions_test.rb +64 -0
  21. data/test/test_helper.rb +38 -0
  22. metadata +74 -150
  23. data/spec/arel/array_spec.rb +0 -77
  24. data/spec/arel/rank_spec.rb +0 -0
  25. data/spec/dummy/.gitignore +0 -15
  26. data/spec/dummy/README.rdoc +0 -261
  27. data/spec/dummy/Rakefile +0 -7
  28. data/spec/dummy/app/assets/images/rails.png +0 -0
  29. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  30. data/spec/dummy/app/assets/stylesheets/application.css +0 -13
  31. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  32. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  33. data/spec/dummy/app/mailers/.gitkeep +0 -0
  34. data/spec/dummy/app/models/.gitkeep +0 -0
  35. data/spec/dummy/app/models/person.rb +0 -2
  36. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  37. data/spec/dummy/config.ru +0 -4
  38. data/spec/dummy/config/application.rb +0 -56
  39. data/spec/dummy/config/boot.rb +0 -6
  40. data/spec/dummy/config/database.yml.example +0 -14
  41. data/spec/dummy/config/environment.rb +0 -5
  42. data/spec/dummy/config/environments/development.rb +0 -33
  43. data/spec/dummy/config/environments/production.rb +0 -68
  44. data/spec/dummy/config/environments/test.rb +0 -33
  45. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  46. data/spec/dummy/config/initializers/inflections.rb +0 -15
  47. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  48. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  49. data/spec/dummy/config/initializers/session_store.rb +0 -8
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  51. data/spec/dummy/config/locales/en.yml +0 -5
  52. data/spec/dummy/config/routes.rb +0 -58
  53. data/spec/dummy/db/migrate/20120501163758_create_people.rb +0 -15
  54. data/spec/dummy/db/schema.rb +0 -31
  55. data/spec/dummy/db/seeds.rb +0 -7
  56. data/spec/dummy/lib/assets/.gitkeep +0 -0
  57. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  58. data/spec/dummy/log/.gitkeep +0 -0
  59. data/spec/dummy/public/404.html +0 -26
  60. data/spec/dummy/public/422.html +0 -26
  61. data/spec/dummy/public/500.html +0 -25
  62. data/spec/dummy/public/favicon.ico +0 -0
  63. data/spec/dummy/public/index.html +0 -241
  64. data/spec/dummy/public/robots.txt +0 -5
  65. data/spec/dummy/script/rails +0 -6
  66. data/spec/dummy/spec/factories/people.rb +0 -7
  67. data/spec/dummy/test/fixtures/.gitkeep +0 -0
  68. data/spec/dummy/test/functional/.gitkeep +0 -0
  69. data/spec/dummy/test/integration/.gitkeep +0 -0
  70. data/spec/dummy/test/performance/browsing_test.rb +0 -12
  71. data/spec/dummy/test/test_helper.rb +0 -13
  72. data/spec/dummy/test/unit/.gitkeep +0 -0
  73. data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
  74. data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  75. data/spec/dummy/vendor/plugins/.gitkeep +0 -0
  76. data/spec/queries/common_table_expression_spec.rb +0 -36
  77. data/spec/queries/sanity_spec.rb +0 -17
  78. data/spec/queries/window_functions_spec.rb +0 -54
  79. 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
@@ -1,3 +1,3 @@
1
1
  module PostgresExt
2
- VERSION = '2.1.3'
2
+ VERSION = '2.2.0'
3
3
  end
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 'rails', '~> 4.0.0'
24
- gem.add_development_dependency 'rspec-rails', '~> 2.12.0'
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 'spec_helper'
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.should include("'127.0.0.1/32'")
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.should match /<< '127.0.0.0\/24'/
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.should match /<<= '127.0.0.0\/24'/
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.should match />> '127.0.0.0\/24'/
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.should match />>= '127.0.0.0\/24'/
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 'spec_helper'
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.should match equality_regex
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.should match overlap_regex
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.should match overlap_regex
27
- query.should match equality_regex
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.should match contains_regex
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.should match contains_regex
41
- query.should match equality_regex
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.should match any_regex
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.should match any_regex
55
- query.should match equality_regex
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.should match all_regex
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.should match all_regex
69
- query.should match equality_regex
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 'spec_helper'
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.should match contained_within_regex
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.should match contained_within_equals_regex
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.should match contains_regex
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.should match contains_equals_regex
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