squeel 1.1.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +36 -4
  5. data/CHANGELOG.md +15 -0
  6. data/Gemfile +1 -1
  7. data/README.md +47 -6
  8. data/Rakefile +14 -2
  9. data/lib/squeel.rb +9 -1
  10. data/lib/squeel/adapters/active_record.rb +0 -1
  11. data/lib/squeel/adapters/active_record/3.0/join_dependency_extensions.rb +1 -0
  12. data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +12 -1
  13. data/lib/squeel/adapters/active_record/3.1/join_dependency_extensions.rb +1 -0
  14. data/lib/squeel/adapters/active_record/3.2/join_dependency_extensions.rb +1 -0
  15. data/lib/squeel/adapters/active_record/4.0/join_dependency_extensions.rb +1 -0
  16. data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +92 -0
  17. data/lib/squeel/adapters/active_record/4.1/compat.rb +15 -0
  18. data/lib/squeel/adapters/active_record/4.1/context.rb +88 -0
  19. data/lib/squeel/adapters/active_record/4.1/preloader_extensions.rb +31 -0
  20. data/lib/squeel/adapters/active_record/4.1/reflection_extensions.rb +37 -0
  21. data/lib/squeel/adapters/active_record/4.1/relation_extensions.rb +307 -0
  22. data/lib/squeel/adapters/active_record/4.2/compat.rb +1 -0
  23. data/lib/squeel/adapters/active_record/4.2/context.rb +1 -0
  24. data/lib/squeel/adapters/active_record/4.2/preloader_extensions.rb +1 -0
  25. data/lib/squeel/adapters/active_record/4.2/relation_extensions.rb +108 -0
  26. data/lib/squeel/adapters/active_record/context.rb +7 -7
  27. data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +9 -13
  28. data/lib/squeel/adapters/active_record/relation_extensions.rb +38 -8
  29. data/lib/squeel/core_ext/symbol.rb +3 -3
  30. data/lib/squeel/dsl.rb +1 -1
  31. data/lib/squeel/nodes.rb +1 -0
  32. data/lib/squeel/nodes/as.rb +12 -0
  33. data/lib/squeel/nodes/join.rb +8 -4
  34. data/lib/squeel/nodes/key_path.rb +10 -1
  35. data/lib/squeel/nodes/node.rb +21 -0
  36. data/lib/squeel/nodes/stub.rb +8 -4
  37. data/lib/squeel/nodes/subquery_join.rb +44 -0
  38. data/lib/squeel/version.rb +1 -1
  39. data/lib/squeel/visitors.rb +2 -0
  40. data/lib/squeel/visitors/enumeration_visitor.rb +101 -0
  41. data/lib/squeel/visitors/order_visitor.rb +9 -2
  42. data/lib/squeel/visitors/predicate_visitor.rb +11 -0
  43. data/lib/squeel/visitors/preload_visitor.rb +12 -0
  44. data/lib/squeel/visitors/visitor.rb +89 -13
  45. data/spec/config.travis.yml +13 -0
  46. data/spec/config.yml +12 -0
  47. data/spec/console.rb +3 -12
  48. data/spec/core_ext/symbol_spec.rb +3 -3
  49. data/spec/helpers/squeel_helper.rb +8 -5
  50. data/spec/spec_helper.rb +4 -16
  51. data/spec/squeel/adapters/active_record/context_spec.rb +8 -4
  52. data/spec/squeel/adapters/active_record/join_dependency_extensions_spec.rb +123 -38
  53. data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +350 -124
  54. data/spec/squeel/core_ext/symbol_spec.rb +3 -3
  55. data/spec/squeel/nodes/join_spec.rb +4 -4
  56. data/spec/squeel/nodes/stub_spec.rb +3 -3
  57. data/spec/squeel/nodes/subquery_join_spec.rb +46 -0
  58. data/spec/squeel/visitors/order_visitor_spec.rb +3 -3
  59. data/spec/squeel/visitors/predicate_visitor_spec.rb +69 -36
  60. data/spec/squeel/visitors/select_visitor_spec.rb +1 -1
  61. data/spec/squeel/visitors/visitor_spec.rb +7 -6
  62. data/spec/support/models.rb +99 -15
  63. data/spec/support/schema.rb +109 -4
  64. data/squeel.gemspec +8 -6
  65. metadata +89 -107
  66. data/.ruby-gemset +0 -1
  67. data/.ruby-version +0 -1
  68. data/spec/blueprints/articles.rb +0 -5
  69. data/spec/blueprints/comments.rb +0 -5
  70. data/spec/blueprints/notes.rb +0 -3
  71. data/spec/blueprints/people.rb +0 -4
  72. data/spec/blueprints/tags.rb +0 -3
@@ -3,66 +3,151 @@ require 'spec_helper'
3
3
  module Squeel
4
4
  module Adapters
5
5
  module ActiveRecord
6
- describe JoinDependencyExtensions do
7
- before do
8
- @jd = new_join_dependency(Person, {}, [])
9
- end
10
-
6
+ describe "JoinDependencyExtensions" do
11
7
  it 'joins with symbols' do
12
- @jd.send(:build, :articles => :comments)
13
- @jd.join_associations.should have(2).associations
14
- @jd.join_associations.each do |association|
15
- association.join_type.should eq Arel::InnerJoin
8
+ @jd = new_join_dependency(Person, { :articles => :comments }, [])
9
+
10
+ if activerecord_version_at_least('4.2.0')
11
+ @jd.join_constraints([]).should have(2).join_info
12
+ @jd.join_constraints([]).map(&:joins).flatten.each do |join|
13
+ join.class.should eq Squeel::InnerJoin
14
+ end
15
+ elsif activerecord_version_at_least('4.1.0')
16
+ @jd.join_constraints([]).should have(2).joins
17
+ @jd.join_constraints([]).each do |join|
18
+ join.class.should eq Squeel::InnerJoin
19
+ end
20
+ else
21
+ @jd.join_associations.should have(2).associations
22
+ @jd.join_associations.each do |association|
23
+ association.join_type.should eq Squeel::InnerJoin
24
+ end
16
25
  end
17
26
  end
18
27
 
19
28
  it 'joins has_many :through associations' do
20
- @jd.send(:build, :authored_article_comments)
21
- @jd.join_associations.should have(1).association
22
- @jd.join_associations.first.table_name.should eq 'comments'
29
+ @jd = new_join_dependency(Person, :authored_article_comments, [])
30
+
31
+ if activerecord_version_at_least('4.2.0')
32
+ @jd.join_constraints([]).should have(1).join_info
33
+ @jd.join_root.children.first.table_name.should eq 'comments'
34
+ elsif activerecord_version_at_least('4.1.0')
35
+ @jd.join_constraints([]).should have(2).joins
36
+ @jd.join_root.children.first.table_name.should eq 'comments'
37
+ else
38
+ @jd.join_associations.should have(1).association
39
+ @jd.join_associations.first.table_name.should eq 'comments'
40
+ end
23
41
  end
24
42
 
25
43
  it 'joins with stubs' do
26
- @jd.send(:build, Nodes::Stub.new(:articles) => Nodes::Stub.new(:comments))
27
- @jd.join_associations.should have(2).associations
28
- @jd.join_associations.each do |association|
29
- association.join_type.should eq Arel::InnerJoin
44
+ @jd = new_join_dependency(Person, { Squeel::Nodes::Stub.new(:articles) => Squeel::Nodes::Stub.new(:comments) }, [])
45
+
46
+ if activerecord_version_at_least('4.2.0')
47
+ @jd.join_constraints([]).should have(2).join_info
48
+ @jd.join_constraints([]).map(&:joins).flatten.each do |join|
49
+ join.class.should eq Squeel::InnerJoin
50
+ end
51
+ @jd.join_root.children.first.table_name.should eq 'articles'
52
+ @jd.join_root.children.first.children.first.table_name.should eq 'comments'
53
+ elsif activerecord_version_at_least('4.1.0')
54
+ @jd.join_constraints([]).should have(2).joins
55
+ @jd.join_constraints([]).each do |join|
56
+ join.class.should eq Squeel::InnerJoin
57
+ end
58
+ @jd.join_root.children.first.table_name.should eq 'articles'
59
+ @jd.join_root.children.first.children.first.table_name.should eq 'comments'
60
+ else
61
+ @jd.join_associations.should have(2).associations
62
+ @jd.join_associations.each do |association|
63
+ association.join_type.should eq Squeel::InnerJoin
64
+ end
65
+ @jd.join_associations[0].table_name.should eq 'articles'
66
+ @jd.join_associations[1].table_name.should eq 'comments'
30
67
  end
31
- @jd.join_associations[0].table_name.should eq 'articles'
32
- @jd.join_associations[1].table_name.should eq 'comments'
33
68
  end
34
69
 
35
70
  it 'joins with key paths' do
36
- @jd.send(:build, dsl{children.children.parent})
37
- @jd.join_associations.should have(3).associations
38
- @jd.join_associations.each do |association|
39
- association.join_type.should eq Arel::InnerJoin
71
+ @jd = new_join_dependency(Person, dsl{ children.children.parent }, [])
72
+
73
+ if activerecord_version_at_least('4.2.0')
74
+ @jd.join_constraints([]).should have(3).join_info
75
+ @jd.join_constraints([]).map(&:joins).flatten.each do |join|
76
+ join.class.should eq Squeel::InnerJoin
77
+ end
78
+ (children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
79
+ (children_people2 = children_people.children.first).aliased_table_name.should eq 'children_people_2'
80
+ children_people2.children.first.aliased_table_name.should eq 'parents_people'
81
+ elsif activerecord_version_at_least('4.1.0')
82
+ @jd.join_constraints([]).should have(3).joins
83
+ @jd.join_constraints([]).each do |join|
84
+ join.class.should eq Squeel::InnerJoin
85
+ end
86
+ (children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
87
+ (children_people2 = children_people.children.first).aliased_table_name.should eq 'children_people_2'
88
+ children_people2.children.first.aliased_table_name.should eq 'parents_people'
89
+ else
90
+ @jd.join_associations.should have(3).associations
91
+ @jd.join_associations.each do |association|
92
+ association.join_type.should eq Squeel::InnerJoin
93
+ end
94
+ @jd.join_associations[0].aliased_table_name.should eq 'children_people'
95
+ @jd.join_associations[1].aliased_table_name.should eq 'children_people_2'
96
+ @jd.join_associations[2].aliased_table_name.should eq 'parents_people'
40
97
  end
41
- @jd.join_associations[0].aliased_table_name.should eq 'children_people'
42
- @jd.join_associations[1].aliased_table_name.should eq 'children_people_2'
43
- @jd.join_associations[2].aliased_table_name.should eq 'parents_people'
44
98
  end
45
99
 
46
100
  it 'joins with key paths as keys' do
47
- @jd.send(:build, dsl{{children.parent => parent}})
48
- @jd.join_associations.should have(3).associations
49
- @jd.join_associations.each do |association|
50
- association.join_type.should eq Arel::InnerJoin
101
+ @jd = new_join_dependency(Person, dsl{ { children.parent => parent } }, [])
102
+
103
+ if activerecord_version_at_least('4.2.0')
104
+ @jd.join_constraints([]).should have(3).join_info
105
+ @jd.join_constraints([]).map(&:joins).flatten.each do |join|
106
+ join.class.should eq Squeel::InnerJoin
107
+ end
108
+ (children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
109
+ (parents_people = children_people.children.first).aliased_table_name.should eq 'parents_people'
110
+ parents_people.children.first.aliased_table_name.should eq 'parents_people_2'
111
+ elsif activerecord_version_at_least('4.1.0')
112
+ @jd.join_constraints([]).should have(3).joins
113
+ @jd.join_constraints([]).each do |join|
114
+ join.class.should eq Squeel::InnerJoin
115
+ end
116
+ (children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
117
+ (parents_people = children_people.children.first).aliased_table_name.should eq 'parents_people'
118
+ parents_people.children.first.aliased_table_name.should eq 'parents_people_2'
119
+ else
120
+ @jd.join_associations.should have(3).associations
121
+ @jd.join_associations.each do |association|
122
+ association.join_type.should eq Squeel::InnerJoin
123
+ end
124
+ @jd.join_associations[0].aliased_table_name.should eq 'children_people'
125
+ @jd.join_associations[1].aliased_table_name.should eq 'parents_people'
126
+ @jd.join_associations[2].aliased_table_name.should eq 'parents_people_2'
51
127
  end
52
- @jd.join_associations[0].aliased_table_name.should eq 'children_people'
53
- @jd.join_associations[1].aliased_table_name.should eq 'parents_people'
54
- @jd.join_associations[2].aliased_table_name.should eq 'parents_people_2'
55
128
  end
56
129
 
57
130
  it 'joins using outer joins' do
58
- @jd.send(:build, :articles.outer => :comments.outer)
59
- @jd.join_associations.should have(2).associations
60
- @jd.join_associations.each do |association|
61
- association.join_type.should eq Arel::OuterJoin
131
+ @jd = new_join_dependency(Person, { :articles.outer => :comments.outer }, [])
132
+
133
+ if activerecord_version_at_least('4.2.0')
134
+ @jd.join_constraints([]).should have(2).join_info
135
+ @jd.join_constraints([]).map(&:joins).flatten.each do |join|
136
+ join.class.should eq Squeel::OuterJoin
137
+ end
138
+ elsif activerecord_version_at_least('4.1.0')
139
+ @jd.join_constraints([]).should have(2).joins
140
+ @jd.join_constraints([]).each do |join|
141
+ join.class.should eq Squeel::OuterJoin
142
+ end
143
+ else
144
+ @jd.join_associations.should have(2).associations
145
+ @jd.join_associations.each do |association|
146
+ association.join_type.should eq Squeel::OuterJoin
147
+ end
62
148
  end
63
149
  end
64
-
65
150
  end
66
151
  end
67
152
  end
68
- end
153
+ end
@@ -15,8 +15,21 @@ module Squeel
15
15
  queries = queries_for do
16
16
  Person.find_by_id('')
17
17
  end
18
- queries.should have(1).query
19
- queries.first.should match /"people"."id" = 0/
18
+ if activerecord_version_at_least('3.1.0')
19
+ queries.should have(1).query
20
+ else
21
+ puts 'skips count of queries expectation'
22
+ end
23
+
24
+ if activerecord_version_at_least('4.2.0')
25
+ if PG_ENV
26
+ queries.last.should match /#{Q}people#{Q}.#{Q}id#{Q} = \$1/
27
+ else
28
+ queries.last.should match /#{Q}people#{Q}.#{Q}id#{Q} = ?/
29
+ end
30
+ else
31
+ queries.last.should match /#{Q}people#{Q}.#{Q}id#{Q} = 0/
32
+ end
20
33
  end
21
34
 
22
35
  end
@@ -34,8 +47,12 @@ module Squeel
34
47
 
35
48
  arel = relation.build_arel
36
49
 
37
- relation.join_dependency.join_associations.should have(4).items
38
- arel.to_sql.should match /INNER JOIN "people" "parents_people_2" ON "parents_people_2"."id" = "parents_people"."parent_id"/
50
+ if activerecord_version_at_least('4.1.0')
51
+ relation.join_dependency.join_constraints([]).should have(4).items
52
+ else
53
+ relation.join_dependency.join_associations.should have(4).items
54
+ end
55
+ arel.to_sql.should match /INNER JOIN #{Q}people#{Q} #{Q}parents_people_2#{Q} ON #{Q}parents_people_2#{Q}.#{Q}id#{Q} = #{Q}parents_people#{Q}.#{Q}parent_id#{Q}/
39
56
  end
40
57
 
41
58
  it 'joins associations with custom join types' do
@@ -49,9 +66,13 @@ module Squeel
49
66
 
50
67
  arel = relation.build_arel
51
68
 
52
- relation.join_dependency.join_associations.should have(4).items
53
- arel.to_sql.should match /LEFT OUTER JOIN "people" "children_people"/
54
- arel.to_sql.should match /LEFT OUTER JOIN "people" "parents_people_2" ON "parents_people_2"."id" = "parents_people"."parent_id"/
69
+ if activerecord_version_at_least('4.1.0')
70
+ relation.join_dependency.join_constraints([]).should have(4).items
71
+ else
72
+ relation.join_dependency.join_associations.should have(4).items
73
+ end
74
+ arel.to_sql.should match /LEFT OUTER JOIN #{Q}people#{Q} #{Q}children_people#{Q}/
75
+ arel.to_sql.should match /LEFT OUTER JOIN #{Q}people#{Q} #{Q}parents_people_2#{Q} ON #{Q}parents_people_2#{Q}.#{Q}id#{Q} = #{Q}parents_people#{Q}.#{Q}parent_id#{Q}/
55
76
  end
56
77
 
57
78
  it 'only joins an association once, even if two overlapping joins_values hashes are given' do
@@ -70,8 +91,12 @@ module Squeel
70
91
  })
71
92
 
72
93
  arel = relation.build_arel
73
- relation.join_dependency.join_associations.should have(6).items
74
- arel.to_sql.should match /INNER JOIN "people" "parents_people_3" ON "parents_people_3"."id" = "children_people_3"."parent_id"/
94
+ if activerecord_version_at_least('4.1.0')
95
+ relation.join_dependency.join_constraints([]).should have(6).items
96
+ else
97
+ relation.join_dependency.join_associations.should have(6).items
98
+ end
99
+ arel.to_sql.should match /INNER JOIN #{Q}people#{Q} #{Q}parents_people_3#{Q} ON #{Q}parents_people_3#{Q}.#{Q}id#{Q} = #{Q}children_people_3#{Q}.#{Q}parent_id#{Q}/
75
100
  end
76
101
 
77
102
  it 'respects :uniq option on associations' do
@@ -81,15 +106,15 @@ module Squeel
81
106
  it 'visits wheres with a PredicateVisitor, converting them to Arel nodes' do
82
107
  relation = Person.where(:name.matches => '%bob%')
83
108
  arel = relation.build_arel
84
- arel.to_sql.should match /"people"."name" LIKE '%bob%'/
109
+ arel.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%bob%'/
85
110
  end
86
111
 
87
- it 'handles multiple wheres using a keypath' do
88
- relation = Person.joins{articles}.where{articles.title == 'Hello'}.
89
- where{articles.body == 'World'}
90
- arel = relation.build_arel
91
- arel.to_sql.should match /articles/
92
- end
112
+ it 'handles multiple wheres using a keypath' do
113
+ relation = Person.joins{articles}.where{articles.title == 'Hello'}.
114
+ where{articles.body == 'World'}
115
+ arel = relation.build_arel
116
+ arel.to_sql.should match /articles/
117
+ end
93
118
 
94
119
  it 'maps wheres inside a hash to their appropriate association table' do
95
120
  relation = Person.joins({
@@ -109,15 +134,14 @@ module Squeel
109
134
  })
110
135
 
111
136
  arel = relation.build_arel
112
-
113
- arel.to_sql.should match /"parents_people_2"."name" = 'bob'/
137
+ arel.to_sql.should match /#{Q}parents_people_2#{Q}.#{Q}name#{Q} = 'bob'/
114
138
  end
115
139
 
116
140
  it 'combines multiple conditions of the same type against the same column with AND' do
117
141
  relation = Person.where(:name.matches => '%bob%')
118
142
  relation = relation.where(:name.matches => '%joe%')
119
143
  arel = relation.build_arel
120
- arel.to_sql.should match /"people"."name" LIKE '%bob%' AND "people"."name" LIKE '%joe%'/
144
+ arel.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%bob%' AND #{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%joe%'/
121
145
  end
122
146
 
123
147
  it 'handles ORs between predicates' do
@@ -129,7 +153,7 @@ module Squeel
129
153
  it 'maintains groupings as given' do
130
154
  relation = Person.where(dsl{(name == 'Ernie') | ((name =~ 'Bob%') & (name =~ '%by'))})
131
155
  arel = relation.build_arel
132
- arel.to_sql.should match /"people"."name" = 'Ernie' OR \("people"."name" LIKE 'Bob%' AND "people"."name" LIKE '%by'\)/
156
+ arel.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'Ernie' OR \(#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE 'Bob%' AND #{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%by'\)/
133
157
  end
134
158
 
135
159
  it 'maps havings inside a hash to their appropriate association table' do
@@ -150,8 +174,7 @@ module Squeel
150
174
  })
151
175
 
152
176
  arel = relation.build_arel
153
-
154
- arel.to_sql.should match /HAVING "parents_people_2"."name" = 'joe'/
177
+ arel.to_sql.should match /HAVING #{Q}parents_people_2#{Q}.#{Q}name#{Q} = 'joe'/
155
178
  end
156
179
 
157
180
  it 'maps orders inside a hash to their appropriate association table' do
@@ -173,8 +196,7 @@ module Squeel
173
196
  })
174
197
 
175
198
  arel = relation.build_arel
176
-
177
- arel.to_sql.should match /ORDER BY "parents_people_2"."id" ASC/
199
+ arel.to_sql.should match /ORDER BY #{Q}parents_people_2#{Q}.#{Q}id#{Q} ASC/
178
200
  else
179
201
  pending 'Unsupported in ActiveRecord 4.0.0+'
180
202
  end
@@ -191,7 +213,6 @@ module Squeel
191
213
 
192
214
  it 'reverses order of Arel::Attributes when #last is called' do
193
215
  sorted_people = Person.all.to_a.sort {|a, b| a.name.downcase <=> b.name.downcase}
194
-
195
216
  Person.order{name}.last.should eq sorted_people.last
196
217
  end
197
218
 
@@ -245,46 +266,77 @@ module Squeel
245
266
 
246
267
  it 'eager loads belongs_to associations' do
247
268
  queries = queries_for do
248
- Article.includes(:person).
249
- where{person.name == 'Ernie'}.to_a
269
+ if activerecord_version_at_least('4.1.0')
270
+ Article.includes(:person).references(:person).
271
+ where{person.name == 'Ernie'}.to_a
272
+ else
273
+ Article.includes(:person).
274
+ where{person.name == 'Ernie'}.to_a
275
+ end
250
276
  end
251
- queries.should have(1).item
252
- queries.first.should match /LEFT OUTER JOIN "people"/
253
- queries.first.should match /"people"."name" = 'Ernie'/
277
+
278
+ if activerecord_version_at_least('4.1.0')
279
+ queries.should have(1).item
280
+ else
281
+ puts 'skips count of queries expectation.'
282
+ end
283
+
284
+ queries.last.should match /LEFT OUTER JOIN #{Q}people#{Q}/
285
+ queries.last.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'Ernie'/
254
286
  end
255
287
 
256
288
  it 'eager loads belongs_to associations on models with default_scopes' do
257
289
  queries = queries_for do
258
- PersonNamedBill.includes(:parent).
259
- where{parent.name == 'Ernie'}.to_a
290
+ if activerecord_version_at_least('4.1.0')
291
+ PersonNamedBill.includes(:parent).references(:parent).
292
+ where{parent.name == 'Ernie'}.to_a
293
+ else
294
+ PersonNamedBill.includes(:parent).
295
+ where{parent.name == 'Ernie'}.to_a
296
+ end
297
+
298
+ end
299
+
300
+ if activerecord_version_at_least('4.1.0')
301
+ queries.should have(1).item
302
+ else
303
+ puts 'skips count of queries expectation.'
260
304
  end
261
- queries.should have(1).item
262
- queries.first.should match /LEFT OUTER JOIN "people"/
263
- queries.first.should match /"people"."name" = 'Bill'/
264
- queries.first.should match /"parents_people"."name" = 'Ernie'/
305
+
306
+ queries.last.should match /LEFT OUTER JOIN #{Q}people#{Q}/
307
+ queries.last.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'Bill'/
308
+ queries.last.should match /#{Q}parents_people#{Q}.#{Q}name#{Q} = 'Ernie'/
265
309
  end
266
310
 
267
311
  it 'eager loads polymorphic belongs_to associations' do
268
312
  relation = Note.includes{notable(Article)}.where{{notable(Article) => {title => 'hey'}}}
269
- relation.debug_sql.should match /"notes"."notable_type" = 'Article'/
313
+ relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
270
314
  end
271
315
 
272
316
  it 'eager loads multiple polymorphic belongs_to associations' do
273
317
  relation = Note.includes{[notable(Article), notable(Person)]}.
274
318
  where{{notable(Article) => {title => 'hey'}}}.
275
319
  where{{notable(Person) => {name => 'joe'}}}
276
- relation.debug_sql.should match /"notes"."notable_type" = 'Article'/
277
- relation.debug_sql.should match /"notes"."notable_type" = 'Person'/
320
+ relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
321
+ relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
278
322
  end
279
323
 
280
- it "only includes once, even if two join types are used" do
324
+ it 'only includes once, even if two join types are used' do
281
325
  relation = Person.includes(:articles.inner, :articles.outer).where(:articles => {:title => 'hey'})
282
- relation.debug_sql.scan("JOIN").size.should eq 1
326
+ if activerecord_version_at_least('4.1.0')
327
+ pending ":article != :article.inner != :article.outer, you shouldn't pass two same tables into includes again"
328
+ else
329
+ relation.debug_sql.scan("JOIN").size.should eq 1
330
+ end
283
331
  end
284
332
 
285
333
  it 'includes a keypath' do
286
- relation = Note.includes{notable(Article).person.children}.where{notable(Article).person.children.name == 'Ernie'}
287
- relation.debug_sql.should match /SELECT "notes".* FROM "notes" LEFT OUTER JOIN "articles" ON "articles"."id" = "notes"."notable_id" AND "notes"."notable_type" = 'Article' LEFT OUTER JOIN "people" ON "people"."id" = "articles"."person_id" LEFT OUTER JOIN "people" "children_people" ON "children_people"."parent_id" = "people"."id"/
334
+ if activerecord_version_at_least('4.1.0')
335
+ relation = Note.includes{notable(Article).person.children}.references(:all).where{notable(Article).person.children.name == 'Ernie'}
336
+ else
337
+ relation = Note.includes{notable(Article).person.children}.where{notable(Article).person.children.name == 'Ernie'}
338
+ end
339
+ relation.debug_sql.should match /SELECT #{Q}notes#{Q}.* FROM #{Q}notes#{Q} LEFT OUTER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}id#{Q} = #{Q}notes#{Q}.#{Q}notable_id#{Q} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article' LEFT OUTER JOIN #{Q}people#{Q} ON #{Q}people#{Q}.#{Q}id#{Q} = #{Q}articles#{Q}.#{Q}person_id#{Q} LEFT OUTER JOIN #{Q}people#{Q} #{Q}children_people#{Q} ON #{Q}children_people#{Q}.#{Q}parent_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q} WHERE #{Q}children_people#{Q}.#{Q}name#{Q} = 'Ernie'/
288
340
  end
289
341
 
290
342
  end
@@ -311,7 +363,6 @@ module Squeel
311
363
  }}
312
364
 
313
365
  queries_for {relation.to_a}.should have(4).items
314
-
315
366
  queries_for {
316
367
  relation.first.articles
317
368
  relation.first.articles.first.comments
@@ -327,8 +378,12 @@ module Squeel
327
378
  standard = Person.eager_load(:children => :children)
328
379
  block = Person.eager_load{{children => children}}
329
380
  block.debug_sql.should eq standard.debug_sql
330
- queries_for {block.to_a}.should have(1).item
331
- queries_for {block.first.children}.should have(0).items
381
+ if activerecord_version_at_least('3.2.0')
382
+ queries_for {block.to_a}.should have(1).item
383
+ queries_for {block.first.children}.should have(0).items
384
+ else
385
+ puts 'skips count of queries expectation.'
386
+ end
332
387
  end
333
388
 
334
389
  it 'eager loads multiple top-level associations with a block' do
@@ -339,23 +394,27 @@ module Squeel
339
394
 
340
395
  it 'eager loads polymorphic belongs_to associations' do
341
396
  relation = Note.eager_load{notable(Article)}
342
- relation.debug_sql.should match /"notes"."notable_type" = 'Article'/
397
+ relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
343
398
  end
344
399
 
345
400
  it 'eager loads multiple polymorphic belongs_to associations' do
346
401
  relation = Note.eager_load{[notable(Article), notable(Person)]}
347
- relation.debug_sql.should match /"notes"."notable_type" = 'Article'/
348
- relation.debug_sql.should match /"notes"."notable_type" = 'Person'/
402
+ relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
403
+ relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
349
404
  end
350
405
 
351
406
  it "only eager_load once, even if two join types are used" do
352
407
  relation = Person.eager_load(:articles.inner, :articles.outer)
353
- relation.debug_sql.scan("JOIN").size.should eq 1
408
+ if activerecord_version_at_least('4.1.0')
409
+ pending ":article != :article.inner != :article.outer, you shouldn't pass two same tables into eager_load again"
410
+ else
411
+ relation.debug_sql.scan("JOIN").size.should eq 1
412
+ end
354
413
  end
355
414
 
356
415
  it 'eager_load a keypath' do
357
416
  relation = Note.eager_load{notable(Article).person.children}
358
- relation.debug_sql.should match /SELECT "notes".* FROM "notes" LEFT OUTER JOIN "articles" ON "articles"."id" = "notes"."notable_id" AND "notes"."notable_type" = 'Article' LEFT OUTER JOIN "people" ON "people"."id" = "articles"."person_id" LEFT OUTER JOIN "people" "children_people" ON "children_people"."parent_id" = "people"."id"/
417
+ relation.debug_sql.should match /SELECT #{Q}notes#{Q}.* FROM #{Q}notes#{Q} LEFT OUTER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}id#{Q} = #{Q}notes#{Q}.#{Q}notable_id#{Q} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article' LEFT OUTER JOIN #{Q}people#{Q} ON #{Q}people#{Q}.#{Q}id#{Q} = #{Q}articles#{Q}.#{Q}person_id#{Q} LEFT OUTER JOIN #{Q}people#{Q} #{Q}children_people#{Q} ON #{Q}children_people#{Q}.#{Q}parent_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q}/
359
418
  end
360
419
 
361
420
  end
@@ -385,39 +444,52 @@ module Squeel
385
444
 
386
445
  it 'behaves as normal with standard parameters' do
387
446
  people = Person.select(:id)
388
- people.should have(332).people
389
- expect { people.first.name }.to raise_error ActiveModel::MissingAttributeError
447
+ people.should have(10).people
448
+ if ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR == 0 && RUBY_VERSION >= '2.0.0'
449
+ people.first.name.should be_nil
450
+ else
451
+ expect { people.first.name }.to raise_error ActiveModel::MissingAttributeError
452
+ end
390
453
  end
391
454
 
392
455
  it 'works with multiple fields in select' do
393
- Article.select("title, body").count.should eq 51
456
+ Article.select("title, body").size.should eq 31
394
457
  end
395
458
 
396
459
  it 'allows a function in the select values via Symbol#func' do
397
- relation = Person.select(:max.func(:id).as('max_id'))
398
- relation.first.max_id.should eq 332
460
+ relation = Person.select(:max.func(:id).as('max_id')).order('max_id')
461
+ relation.first.max_id.to_i.should eq 10
399
462
  end
400
463
 
401
464
  it 'allows a function in the select values via block' do
402
- relation = Person.select{max(id).as(max_id)}
403
- relation.first.max_id.should eq 332
465
+ relation = Person.select{max(id).as(max_id)}.order('max_id')
466
+ relation.first.max_id.to_i.should eq 10
404
467
  end
405
468
 
406
469
  it 'allows an operation in the select values via block' do
407
- relation = Person.select{[id, (id + 1).as('id_plus_one')]}.where('id_plus_one = 2')
408
- relation.first.id.should eq 1
470
+ relation =
471
+ if SQLITE_ENV
472
+ Person.select{[id, (id + 1).as('id_plus_one')]}.where('id_plus_one = 2')
473
+ else
474
+ Person.select{[id, (id + 1).as('id_plus_one')]}.where{(id + 1) == 2}
475
+ end
476
+ relation.first.id.should eq 1
409
477
  end
410
478
 
411
479
  it 'allows custom operators in the select values via block' do
412
- relation = Person.select{name.op('||', '-diddly').as(flanderized_name)}
413
- relation.first.flanderized_name.should eq Person.first.name + '-diddly'
480
+ if MYSQL_ENV
481
+ pending "MySQL doesn't support concating string with ||."
482
+ else
483
+ relation = Person.select{name.op('||', '-diddly').as(flanderized_name)}
484
+ relation.first.flanderized_name.should eq Person.first.name + '-diddly'
485
+ end
414
486
  end
415
487
 
416
488
  it 'allows a subquery in the select values' do
417
489
  subquery = Article.where(:person_id => 1).select(:id).order{id.desc}.limit(1)
418
490
  relation = Person.where(:id => 1).select{[id, name, subquery.as('last_article_id')]}
419
491
  aric = relation.first
420
- aric.last_article_id.should eq Article.where(:person_id => 1).last.id
492
+ aric.last_article_id.to_i.should eq Article.where(:person_id => 1).last.id
421
493
  end
422
494
 
423
495
  end
@@ -425,7 +497,7 @@ module Squeel
425
497
  describe '#count' do
426
498
 
427
499
  it 'works with non-strings in select' do
428
- Article.select{distinct(title)}.count.should eq 51
500
+ Article.select{distinct(title)}.count.should eq 31
429
501
  end
430
502
 
431
503
  it 'works with non-strings in wheres' do
@@ -463,17 +535,17 @@ module Squeel
463
535
 
464
536
  it 'builds compound conditions with a block' do
465
537
  block = Person.where{(name == 'bob') & (salary == 100000)}
466
- block.to_sql.should match /"people"."name" = 'bob'/
538
+ block.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
467
539
  block.to_sql.should match /AND/
468
- block.to_sql.should match /"people"."salary" = 100000/
540
+ block.to_sql.should match /#{Q}people#{Q}.#{Q}salary#{Q} = 100000/
469
541
  end
470
542
 
471
543
  it 'allows mixing hash and operator syntax inside a block' do
472
544
  block = Person.joins(:comments).
473
545
  where{(name == 'bob') & {comments => (body == 'First post!')}}
474
- block.to_sql.should match /"people"."name" = 'bob'/
546
+ block.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
475
547
  block.to_sql.should match /AND/
476
- block.to_sql.should match /"comments"."body" = 'First post!'/
548
+ block.to_sql.should match /#{Q}comments#{Q}.#{Q}body#{Q} = 'First post!'/
477
549
  end
478
550
 
479
551
  it 'allows a condition on a function via block' do
@@ -496,6 +568,12 @@ module Squeel
496
568
  people_and_article_notes.should have(40).items
497
569
  end
498
570
 
571
+ it 'maps conditions onto their proper table with a polymorphic belongs_to join followed by a polymorphic has_many join' do
572
+ relation = Note.joins{notable(Article).notes}.
573
+ where{notable(Article).notes.note.eq('zomg')}
574
+ relation.to_sql.should match /#{Q}notes_articles#{Q}\.#{Q}note#{Q} = 'zomg'/
575
+ end
576
+
499
577
  it 'allows a subquery on the value side of a predicate' do
500
578
  names = [Person.first.name, Person.last.name]
501
579
  old_and_busted = Person.where(:name => names)
@@ -504,6 +582,21 @@ module Squeel
504
582
  old_and_busted.to_a.should eq new_hotness.to_a
505
583
  end
506
584
 
585
+ it 'allows a subquery from an association in a hash' do
586
+ scope = Person.first.articles
587
+ articles = scope.where(:id => scope)
588
+ articles.should have(3).articles
589
+
590
+ articles = Tag.all.second.articles.where(:id => scope)
591
+ articles.should have(1).articles
592
+ end
593
+
594
+ it 'allows a subquery from an association in a Squeel node' do
595
+ scope = Person.first.articles
596
+ articles = scope.where{id.in scope}
597
+ articles.should have(3).articles
598
+ end
599
+
507
600
  it 'is backwards-compatible with "where.not"' do
508
601
  if activerecord_version_at_least '4.0.0'
509
602
  name = Person.first.name
@@ -517,37 +610,61 @@ module Squeel
517
610
  it 'allows equality conditions against a belongs_to with an AR::Base value' do
518
611
  first_person = Person.first
519
612
  relation = Article.where { person.eq first_person }
520
- relation.to_sql.should match /"articles"."person_id" = #{first_person.id}/
613
+ relation.to_sql.should match /#{Q}articles#{Q}.#{Q}person_id#{Q} = #{first_person.id}/
521
614
  end
522
615
 
523
616
  it 'allows equality conditions against a polymorphic belongs_to with an AR::Base value' do
524
617
  first_person = Person.first
525
618
  relation = Note.where { notable.eq first_person }
526
- relation.to_sql.should match /"notes"."notable_id" = #{first_person.id} AND "notes"."notable_type" = 'Person'/
619
+ relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_id#{Q} = #{first_person.id} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
527
620
  end
528
621
 
529
622
  it 'allows inequality conditions against a belongs_to with an AR::Base value' do
530
623
  first_person = Person.first
531
624
  relation = Article.where { person.not_eq first_person }
532
- relation.to_sql.should match /"articles"."person_id" != #{first_person.id}/
625
+ relation.to_sql.should match /#{Q}articles#{Q}.#{Q}person_id#{Q} != #{first_person.id}/
533
626
  end
534
627
 
535
628
  it 'allows inequality conditions against a polymorphic belongs_to with an AR::Base value' do
536
629
  first_person = Person.first
537
630
  relation = Note.where { notable.not_eq first_person }
538
- relation.to_sql.should match /\("notes"."notable_id" != #{first_person.id} OR "notes"."notable_type" != 'Person'\)/
631
+ relation.to_sql.should match /\(#{Q}notes#{Q}.#{Q}notable_id#{Q} != #{first_person.id} OR #{Q}notes#{Q}.#{Q}notable_type#{Q} != 'Person'\)/
539
632
  end
540
633
 
541
634
  it 'allows hash equality conditions against a belongs_to with an AR::Base value' do
542
635
  first_person = Person.first
543
636
  relation = Article.where(:person => first_person)
544
- relation.to_sql.should match /"articles"."person_id" = #{first_person.id}/
637
+ relation.to_sql.should match /#{Q}articles#{Q}.#{Q}person_id#{Q} = #{first_person.id}/
545
638
  end
546
639
 
547
640
  it 'allows hash equality conditions against a polymorphic belongs_to with an AR::Base value' do
548
641
  first_person = Person.first
549
642
  relation = Note.where(:notable => first_person)
550
- relation.to_sql.should match /"notes"."notable_id" = #{first_person.id} AND "notes"."notable_type" = 'Person'/
643
+ relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
644
+ relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_id#{Q} = #{first_person.id}/
645
+ end
646
+
647
+ it 'keeps original AR hashes behavior' do
648
+ relation = Person.joins(:articles).where(articles: { person_id: Person.first })
649
+ relation.to_sql.should match /SELECT #{Q}people#{Q}.* FROM #{Q}people#{Q} INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}person_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q} WHERE #{Q}articles#{Q}.#{Q}person_id#{Q} = 1/
650
+
651
+ relation = Person.joins(:articles).where(articles: { person_id: Person.all.to_a })
652
+ relation.to_sql.should match /SELECT #{Q}people#{Q}.\* FROM #{Q}people#{Q} INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}person_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q} WHERE #{Q}articles#{Q}.#{Q}person_id#{Q} IN \(1, 2, 3, 4, 5, 6, 7, 8, 9, 10\)/
653
+ end
654
+
655
+ it 'returns ActiveRecord::Relation after complex associations, joins and wheres' do
656
+ relation = Note.first.notable.articles.joins(:comments).where{comments.article_id != nil}
657
+
658
+ relation.should be_kind_of(::ActiveRecord::Relation)
659
+ relation.first.should be_kind_of(Article)
660
+ end
661
+
662
+ it 'uses Squeel and Arel at the same time' do
663
+ relation = User.where{id.in([1,2,3]) & User.arel_table[:id].not_eq(nil) }
664
+ relation.to_sql.should match /SELECT #{Q}users#{Q}.\* FROM #{Q}users#{Q}\s+WHERE \(\(#{Q}users#{Q}.#{Q}id#{Q} IN \(1, 2, 3\) AND #{Q}users#{Q}.#{Q}id#{Q} IS NOT NULL\)\)/
665
+ relation = User.where{
666
+ (id.in([1,2,3]) | User.arel_table[:id].eq(1)) & ((id == 1) | User.arel_table[:id].not_eq(nil)) }
667
+ relation.to_sql.should match /SELECT #{Q}users#{Q}.\* FROM #{Q}users#{Q}\s+WHERE \(\(\(#{Q}users#{Q}.#{Q}id#{Q} IN \(1, 2, 3\) OR #{Q}users#{Q}.#{Q}id#{Q} = 1\) AND \(#{Q}users#{Q}.#{Q}id#{Q} = 1 OR #{Q}users#{Q}.#{Q}id#{Q} IS NOT NULL\)\)\)/
551
668
  end
552
669
 
553
670
  end
@@ -577,25 +694,66 @@ module Squeel
577
694
 
578
695
  it 'joins polymorphic belongs_to associations' do
579
696
  relation = Note.joins{notable(Article)}
580
- relation.to_sql.should match /"notes"."notable_type" = 'Article'/
697
+ relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
581
698
  end
582
699
 
583
700
  it 'joins multiple polymorphic belongs_to associations' do
584
701
  relation = Note.joins{[notable(Article), notable(Person)]}
585
- relation.to_sql.should match /"notes"."notable_type" = 'Article'/
586
- relation.to_sql.should match /"notes"."notable_type" = 'Person'/
702
+ relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
703
+ relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
587
704
  end
588
705
 
589
706
  it "only joins once, even if two join types are used" do
590
- relation = Person.joins(:articles.inner, :articles.outer)
591
- relation.to_sql.scan("JOIN").size.should eq 1
707
+ if activerecord_version_at_least('4.1.0')
708
+ pending "It's unreasonable to join once only, in some cases, we need twice."
709
+ else
710
+ relation = Person.joins(:articles.inner, :articles.outer)
711
+ relation.to_sql.scan("JOIN").size.should eq 1
712
+ end
592
713
  end
593
714
 
594
715
  it 'joins a keypath' do
595
716
  relation = Note.joins{notable(Article).person.children}
596
- relation.to_sql.should match /SELECT "notes".* FROM "notes" INNER JOIN "articles" ON "articles"."id" = "notes"."notable_id" AND "notes"."notable_type" = 'Article' INNER JOIN "people" ON "people"."id" = "articles"."person_id" INNER JOIN "people" "children_people" ON "children_people"."parent_id" = "people"."id"/
717
+ relation.to_sql.should match /SELECT #{Q}notes#{Q}.* FROM #{Q}notes#{Q} INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}id#{Q} = #{Q}notes#{Q}.#{Q}notable_id#{Q} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article' INNER JOIN #{Q}people#{Q} ON #{Q}people#{Q}.#{Q}id#{Q} = #{Q}articles#{Q}.#{Q}person_id#{Q} INNER JOIN #{Q}people#{Q} #{Q}children_people#{Q} ON #{Q}children_people#{Q}.#{Q}parent_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q}/
597
718
  end
598
719
 
720
+ it 'validates polymorphic relationship with source type' do
721
+ if activerecord_version_at_least '3.2.7'
722
+ Group.first.users.to_sql.should match /#{Q}memberships#{Q}.#{Q}member_type#{Q} = 'User'/
723
+ else
724
+ Group.first.users.size.should eq 1
725
+ end
726
+ end
727
+
728
+ it 'joins an ActiveRecord::Relation subquery' do
729
+ subquery = OrderItem.
730
+ group(:orderable_id).
731
+ select { [orderable_id, sum(quantity * unit_price).as(amount)] }
732
+
733
+ relation = Seat.
734
+ joins { [payment.outer,
735
+ subquery.as('seat_order_items').on { id == seat_order_items.orderable_id}.outer] }.
736
+ select { [seat_order_items.amount, "seats.*"] }.
737
+ where { seat_order_items.amount > 0 }
738
+
739
+ relation.debug_sql.should match /SELECT #{Q}seat_order_items#{Q}.#{Q}amount#{Q}, seats.\* FROM #{Q}seats#{Q} LEFT OUTER JOIN #{Q}payments#{Q} ON #{Q}payments#{Q}.#{Q}id#{Q} = #{Q}seats#{Q}.#{Q}payment_id#{Q} LEFT OUTER JOIN \(SELECT #{Q}order_items#{Q}.#{Q}orderable_id#{Q}, sum\(#{Q}order_items#{Q}.#{Q}quantity#{Q} \* #{Q}order_items#{Q}.#{Q}unit_price#{Q}\) AS amount FROM #{Q}order_items#{Q}\s+GROUP BY #{Q}order_items#{Q}.#{Q}orderable_id#{Q}\) seat_order_items ON #{Q}seats#{Q}.#{Q}id#{Q} = #{Q}seat_order_items#{Q}.#{Q}orderable_id#{Q} WHERE #{Q}seat_order_items#{Q}.#{Q}amount#{Q} > 0/
740
+ relation.to_a.should have(10).seats
741
+ relation.to_a.second.amount.to_i.should eq(10)
742
+ end
743
+
744
+ it 'joins from an association with default scopes' do
745
+ if activerecord_version_at_least('3.1.0')
746
+ if MYSQL_ENV
747
+ User.first.groups.to_sql.should match /#{Q}memberships#{Q}.#{Q}active#{Q} = 1/
748
+ else
749
+ puts User.first.groups.to_sql
750
+ User.first.groups.to_sql.should match /#{Q}memberships#{Q}.#{Q}active#{Q} = 't'/
751
+ end
752
+
753
+ else
754
+ pending "Rails 3.0.x doesn't support to_sql in an association."
755
+ end
756
+ end
599
757
  end
600
758
 
601
759
  describe '#having' do
@@ -607,8 +765,12 @@ module Squeel
607
765
  end
608
766
 
609
767
  it 'allows complex conditions on aggregate columns' do
610
- relation = Person.group(:parent_id).having{salary == max(salary)}
611
- relation.first.name.should eq Person.last.name
768
+ if SQLITE_ENV
769
+ relation = Person.group(:parent_id).having{salary == max(salary)}
770
+ relation.first.name.should eq Person.last.name
771
+ else
772
+ pending "MySQL & PG don't support this type of group & having clauses, don't use it."
773
+ end
612
774
  end
613
775
 
614
776
  it 'allows a condition on a function via block' do
@@ -627,13 +789,13 @@ module Squeel
627
789
 
628
790
  it 'builds options with a block' do
629
791
  block = Person.order{name}
630
- block.to_sql.should match /ORDER BY "people"\."name"/
792
+ block.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}name#{Q}/
631
793
  end
632
794
 
633
795
  it 'allows AR 4.0-style hash options' do
634
796
  if activerecord_version_at_least '4.0.0'
635
797
  block = Person.order(:name => :desc)
636
- block.to_sql.should match /ORDER BY "people"\."name" DESC/
798
+ block.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}name#{Q} DESC/
637
799
  else
638
800
  pending 'Not required in AR versions < 4.0.0'
639
801
  end
@@ -641,7 +803,7 @@ module Squeel
641
803
 
642
804
  it 'allows ordering by an attributes of a joined table' do
643
805
  relation = Article.joins(:person).order { person.id.asc }
644
- relation.to_sql.should match /ORDER BY "people"\."id" ASC/
806
+ relation.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}id#{Q} ASC/
645
807
  end
646
808
 
647
809
  end
@@ -654,7 +816,7 @@ module Squeel
654
816
  it 'builds options with a block' do
655
817
  block = @standard.reorder{id}
656
818
  block.to_sql.should_not eq @standard.to_sql
657
- block.to_sql.should match /ORDER BY "people"."id"/
819
+ block.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}id#{Q}/
658
820
  end
659
821
 
660
822
  it 'drops order by clause when passed nil' do
@@ -673,7 +835,7 @@ module Squeel
673
835
 
674
836
  describe '#from' do
675
837
  it 'creates froms with a block' do
676
- expected = /SELECT "sub"."name" AS aliased_name FROM \(SELECT "people"."name" FROM "people"\s*\) sub/
838
+ expected = /SELECT #{Q}sub#{Q}.#{Q}name#{Q} AS aliased_name FROM \(SELECT #{Q}people#{Q}.#{Q}name#{Q} FROM #{Q}people#{Q}\s*\) sub/
677
839
  block = Person.from{Person.select{name}.as('sub')}.
678
840
  select{sub.name.as('aliased_name')}
679
841
  sql = block.to_sql
@@ -681,7 +843,7 @@ module Squeel
681
843
  end
682
844
 
683
845
  it 'creates froms from literals' do
684
- expected = /SELECT "people".* FROM sub/
846
+ expected = /SELECT #{Q}people#{Q}.* FROM sub/
685
847
  relation = Person.from('sub')
686
848
  sql = relation.to_sql
687
849
  sql.should match expected
@@ -689,7 +851,7 @@ module Squeel
689
851
 
690
852
  it 'creates froms from relations' do
691
853
  if activerecord_version_at_least '4.0.0'
692
- expected = "SELECT \"people\".* FROM (SELECT \"people\".* FROM \"people\") alias"
854
+ expected = "SELECT #{Q}people#{Q}.* FROM (SELECT #{Q}people#{Q}.* FROM #{Q}people#{Q}) alias"
693
855
  relation = Person.from(Person.all, 'alias')
694
856
  sql = relation.to_sql
695
857
  sql.should == expected
@@ -697,6 +859,17 @@ module Squeel
697
859
  pending 'Unsupported before ActiveRecord 4.0'
698
860
  end
699
861
  end
862
+
863
+ it 'binds params from CollectionProxy subquery' do
864
+ if activerecord_version_at_least('3.1.0')
865
+ first_article = Article.first
866
+ expected_tags = Tag.where(id: [1,2,3]).order{name}.to_a
867
+
868
+ expected_tags.should == Tag.from{first_article.tags.as(Tag.table_name)}.order{tags.name}.to_a
869
+ else
870
+ pending "ActiveRecord 3.0.x doesn't support CollectionProxy chain."
871
+ end
872
+ end
700
873
  end
701
874
 
702
875
  describe '#build_where' do
@@ -718,7 +891,12 @@ module Squeel
718
891
 
719
892
  it 'adds hash where values without converting to Arel predicates' do
720
893
  wheres = Person.where({:name => 'bob'}).where_values
721
- wheres.should eq [{:name => 'bob'}]
894
+ if activerecord_version_at_least('4.0.0')
895
+ wheres.flatten.should have(1).equality
896
+ wheres.flatten.last.should be_kind_of(Arel::Nodes::Equality)
897
+ else
898
+ wheres.should eq [{:name => 'bob'}]
899
+ end
722
900
  end
723
901
 
724
902
  end
@@ -729,8 +907,12 @@ module Squeel
729
907
  relation = Person.includes(:comments, :articles).
730
908
  where(:comments => {:body => 'First post!'}).
731
909
  where(:articles => {:title => 'Hello, world!'})
732
- relation.debug_sql.should_not eq relation.to_sql
733
- relation.debug_sql.should match /SELECT "people"."id" AS t0_r0/
910
+ if activerecord_version_at_least('4.1.0')
911
+ relation.debug_sql.should_not eq relation.arel.to_sql
912
+ else
913
+ relation.debug_sql.should_not eq relation.to_sql
914
+ end
915
+ relation.debug_sql.should match /SELECT #{Q}people#{Q}.#{Q}id#{Q} AS t0_r0/
734
916
  end
735
917
 
736
918
  end
@@ -802,22 +984,27 @@ module Squeel
802
984
  it 'merges relations with the same base' do
803
985
  relation = Person.where{name == 'bob'}.merge(Person.where{salary == 100000})
804
986
  sql = relation.to_sql
805
- sql.should match /"people"."name" = 'bob'/
806
- sql.should match /"people"."salary" = 100000/
987
+ sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
988
+ sql.should match /#{Q}people#{Q}.#{Q}salary#{Q} = 100000/
807
989
  end
808
990
 
809
991
  it 'merges relations with a different base' do
810
992
  relation = Person.where{name == 'bob'}.joins(:articles).merge(Article.where{title == 'Hello world!'})
811
993
  sql = relation.to_sql
812
- sql.should match /INNER JOIN "articles" ON "articles"."person_id" = "people"."id"/
813
- sql.should match /"people"."name" = 'bob'/
814
- sql.should match /"articles"."title" = 'Hello world!'/
994
+ sql.should match /INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}person_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q}/
995
+ sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
996
+ sql.should match /#{Q}articles#{Q}.#{Q}title#{Q} = 'Hello world!'/
815
997
  end
816
998
 
817
999
  it 'does not break hm:t with conditions' do
818
1000
  relation = Person.first.condition_article_comments
819
- sql = relation.scoped.to_sql
820
- sql.should match /"articles"."title" = 'Condition'/
1001
+ sql =
1002
+ if activerecord_version_at_least('4.1.0')
1003
+ relation.to_sql
1004
+ else
1005
+ relation.scoped.to_sql
1006
+ end
1007
+ sql.should match /#{Q}articles#{Q}.#{Q}title#{Q} = 'Condition'/
821
1008
  end
822
1009
 
823
1010
  it 'uses the last condition in the case of a conflicting where' do
@@ -831,7 +1018,13 @@ module Squeel
831
1018
 
832
1019
  it 'uses the given equality condition in the case of a conflicting where from a default scope' do
833
1020
  if activerecord_version_at_least '3.1'
834
- relation = PersonNamedBill.where{name == 'Ernie'}
1021
+ relation =
1022
+ if activerecord_version_at_least('4.1.0')
1023
+ PersonNamedBill.rewhere(name: 'Ernie')
1024
+ # Or PersonNamedBill.unscope(where: :name).where { name == 'Ernie' }
1025
+ else
1026
+ PersonNamedBill.where{name == 'Ernie'}
1027
+ end
835
1028
  sql = relation.to_sql
836
1029
  sql.should_not match /Bill/
837
1030
  sql.should match /Ernie/
@@ -844,53 +1037,82 @@ module Squeel
844
1037
  relation = Person.with_article_title('hi').
845
1038
  with_article_condition_title('yo')
846
1039
  sql = relation.to_sql
847
- sql.should match /"articles"."title" = 'hi'/
848
- sql.should match /"articles_with_conditions_people"."title" = 'yo'/
1040
+ sql.should match /#{Q}articles#{Q}.#{Q}title#{Q} = 'hi'/
1041
+ sql.should match /#{Q}articles_with_conditions_people#{Q}.#{Q}title#{Q} = 'yo'/
849
1042
  end
850
1043
 
851
1044
  it "doesn't ruin everything when a scope returns nil" do
852
1045
  relation = Person.nil_scope
853
- relation.should eq Person.scoped
1046
+ if activerecord_version_at_least('4.1.0')
1047
+ relation.should eq Person.all
1048
+ else
1049
+ relation.should eq Person.scoped
1050
+ end
854
1051
  end
855
1052
 
856
1053
  it "doesn't ruin everything when a group exists" do
857
- relation = Person.scoped.merge(Person.group{name})
858
1054
  count_hash = {}
859
- expect { count_hash = relation.count }.should_not raise_error
860
- count_hash.size.should eq Person.scoped.size
1055
+ if activerecord_version_at_least('4.1.0')
1056
+ relation = Person.all.merge(Person.group{name})
1057
+ expect { count_hash = relation.count }.should_not raise_error
1058
+ count_hash.size.should eq Person.all.size
1059
+ else
1060
+ relation = Person.scoped.merge(Person.group{name})
1061
+ expect { count_hash = relation.count }.should_not raise_error
1062
+ count_hash.size.should eq Person.scoped.size
1063
+ end
1064
+
861
1065
  count_hash.values.all? {|v| v == 1}.should be_true
862
1066
  count_hash.keys.should =~ Person.select{name}.map(&:name)
863
1067
  end
864
1068
 
865
1069
  it "doesn't merge the default scope more than once" do
866
- relation = PersonNamedBill.scoped.highly_compensated.ending_with_ill
1070
+ relation =
1071
+ if activerecord_version_at_least('4.1.0')
1072
+ PersonNamedBill.all.highly_compensated.ending_with_ill
1073
+ else
1074
+ PersonNamedBill.scoped.highly_compensated.ending_with_ill
1075
+ end
867
1076
  sql = relation.to_sql
868
- sql.scan(/"people"."name" = 'Bill'/).should have(1).item
869
- sql.scan(/"people"."name" LIKE '%ill'/).should have(1).item
870
- sql.scan(/"people"."salary" > 200000/).should have(1).item
871
- sql.scan(/"people"."id"/).should have(1).item
1077
+ sql.scan(/#{Q}people#{Q}.#{Q}name#{Q} = 'Bill'/).should have(1).item
1078
+ sql.scan(/#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%ill'/).should have(1).item
1079
+ sql.scan(/#{Q}people#{Q}.#{Q}salary#{Q} > 200000/).should have(1).item
1080
+ sql.scan(/#{Q}people#{Q}.#{Q}id#{Q}/).should have(1).item
872
1081
  end
873
1082
 
874
1083
  it "doesn't hijack the table name when merging a relation with different base and default_scope" do
875
- relation = Article.joins(:person).merge(PersonNamedBill.scoped)
1084
+ relation =
1085
+ if activerecord_version_at_least('4.1.0')
1086
+ Article.joins(:person).merge(PersonNamedBill.all)
1087
+ else
1088
+ Article.joins(:person).merge(PersonNamedBill.scoped)
1089
+ end
876
1090
  sql = relation.to_sql
877
- sql.scan(/"people"."name" = 'Bill'/).should have(1).item
1091
+ sql.scan(/#{Q}people#{Q}.#{Q}name#{Q} = 'Bill'/).should have(1).item
878
1092
  end
879
1093
 
880
1094
  it 'merges scopes that contain functions' do
881
- relation = PersonNamedBill.scoped.with_salary_equal_to(100)
1095
+ relation =
1096
+ if activerecord_version_at_least('4.1.0')
1097
+ PersonNamedBill.all.with_salary_equal_to(100)
1098
+ else
1099
+ PersonNamedBill.scoped.with_salary_equal_to(100)
1100
+ end
882
1101
  sql = relation.to_sql
883
- sql.should match /abs\("people"."salary"\) = 100/
1102
+ sql.should match /abs\(#{Q}people#{Q}.#{Q}salary#{Q}\) = 100/
884
1103
  end
885
1104
 
886
1105
  it 'uses last equality when merging two scopes with identical function equalities' do
887
- relation = PersonNamedBill.scoped.with_salary_equal_to(100).
888
- with_salary_equal_to(200)
889
- sql = relation.to_sql
890
- sql.should_not match /abs\("people"."salary"\) = 100/
891
- sql.should match /abs\("people"."salary"\) = 200/
1106
+ if activerecord_version_at_least('4.1.0')
1107
+ pending "Named Functions can't be unscoped"
1108
+ else
1109
+ relation = PersonNamedBill.scoped.with_salary_equal_to(100).
1110
+ with_salary_equal_to(200)
1111
+ sql = relation.to_sql
1112
+ sql.should_not match /abs\(#{Q}people#{Q}.#{Q}salary#{Q}\) = 100/
1113
+ sql.should match /abs\(#{Q}people#{Q}.#{Q}salary#{Q}\) = 200/
1114
+ end
892
1115
  end
893
-
894
1116
  end
895
1117
 
896
1118
  describe '#to_a' do
@@ -908,14 +1130,18 @@ module Squeel
908
1130
  relation = UnidentifiedObject.where{person_id < 120}.includes(:person)
909
1131
  queries = queries_for do
910
1132
  vals = relation.to_a
911
- vals.should have(8).items
1133
+ vals.should have(20).items
912
1134
  end
913
1135
 
914
- queries.should have(2).queries
1136
+ if activerecord_version_at_least('3.1.0')
1137
+ queries.should have(2).queries
1138
+ else
1139
+ puts 'skips count of queries expectation'
1140
+ end
915
1141
 
916
1142
  matched_ids = queries.last.match(/IN \(([^)]*)/).captures.first
917
1143
  matched_ids = matched_ids.split(/,\s*/).map(&:to_i)
918
- matched_ids.should =~ [1, 34, 67, 100]
1144
+ matched_ids.should =~ [1, 2, 3, 4, 5 ,6 ,7 ,8 ,9 ,10]
919
1145
  end
920
1146
 
921
1147
  end