sqldsl 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -21,6 +21,11 @@ See the tests for more examples
21
21
  === Test Helper
22
22
 
23
23
  require 'test/unit'
24
+ unless File.directory? File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/'
25
+ raise "mocha 4.0 is required to run the test suite. create the 'vendor' directory as a sibling of test and 'gem unpack mocha' in 'vendor'"
26
+ end
27
+ $:.unshift File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/lib/'
28
+ require File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/lib/mocha'
24
29
  require File.dirname(__FILE__) + '/../lib/sqldsl'
25
30
 
26
31
  === Insert Example
@@ -72,22 +77,102 @@ See the tests for more examples
72
77
  require File.dirname(__FILE__) + '/test_helper'
73
78
 
74
79
  class SelectAcceptanceTest < Test::Unit::TestCase
75
- def test_simple_select
76
- statement = Select[:column1, 'book', 10].from[:table1].where do
80
+ def test_select_with_where_methods
81
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
77
82
  equal :column1, 99
78
- equal :column2, 'star'
83
+ not_equal :column1, 100
84
+ less_than :column2, 'foo'
85
+ less_than_or_equal :column3, :column4
86
+ greater_than :column1, 0
87
+ greater_than_or_equal :column2, 'bar'
88
+ is_not_null :column1
89
+ is_in :column1, [1,2]
90
+ is_not_in :column2, [3, 4]
91
+ exists 0
92
+ not_exists 0
79
93
  end
80
- expected = "select column1, 'book', 10 from table1 where column1 = 99 and column2 = 'star'"
81
- assert_equal expected, statement.to_sql
94
+ expected = "select column1, 'book', 10 from table1, table2
95
+ where column1 = 99 and column1 <> 100 and column2 < 'foo'
96
+ and column3 <= column4 and column1 > 0 and column2 >= 'bar'
97
+ and column1 is not null and column1 in (1, 2) and column2 not in (3, 4)
98
+ and exists (0) and not exists (0)"
99
+ assert_equal expected.delete("\n").squeeze(" "), statement.to_sql
100
+ end
101
+
102
+ def test_select_with_receive_any_objects_and_operators
103
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
104
+ table1.column1 = 99
105
+ column1 <=> 100
106
+ column2 < 'foo'
107
+ column3 <= column4
108
+ column1 > 0
109
+ column2 >= 'bar'
110
+ column1 ^ nil
111
+ column1 >> [1,2]
112
+ column2 << [3, 4]
113
+ end
114
+ expected = "select column1, 'book', 10 from table1, table2
115
+ where table1.column1 = 99 and column1 <> 100 and column2 < 'foo'
116
+ and column3 <= column4 and column1 > 0 and column2 >= 'bar'
117
+ and column1 is not null and column1 in (1, 2) and column2 not in (3, 4)"
118
+ assert_equal expected.delete("\n").squeeze(" "), statement.to_sql
82
119
  end
83
120
 
84
- def test_select_with_join
85
- statement = Select[:column1, :column2].from[:table1, :table2].where do
86
- equal :'table1.id', :'table2.table1_id'
121
+ def test_select_with_receive_any_objects_and_method_calls
122
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
123
+ column1.equal 99
124
+ column1.not_equal 100
125
+ column2.less_than 'foo'
126
+ column3.less_than_or_equal column4
127
+ column1.greater_than 0
128
+ column2.greater_than_or_equal 'bar'
129
+ column1.is_not_null
130
+ column1.is_in [1,2]
131
+ column2.is_not_in [3, 4]
87
132
  end
88
- expected = "select column1, column2 from table1, table2 where table1.id = table2.table1_id"
133
+ expected = "select column1, 'book', 10 from table1, table2
134
+ where column1 = 99 and column1 <> 100 and column2 < 'foo'
135
+ and column3 <= column4 and column1 > 0 and column2 >= 'bar'
136
+ and column1 is not null and column1 in (1, 2) and column2 not in (3, 4)"
137
+ assert_equal expected.delete("\n").squeeze(" "), statement.to_sql
138
+ end
139
+
140
+ def test_select_with_receive_any_objects_and_method_calls
141
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
142
+ column1.equal 0
143
+ end.or do
144
+ column1 > 100
145
+ end
146
+ expected = "select column1, 'book', 10 from table1, table2 where column1 = 0 or column1 > 100"
89
147
  assert_equal expected, statement.to_sql
90
148
  end
149
+
150
+ def test_columns_in_inner_where_are_validated_against_outer_tables
151
+ statement = Select.all.from[:table].where do
152
+ exists(Select.all.from[:inner_table => :aliased].where do
153
+ table.column1 = aliased.column1
154
+ end)
155
+ end
156
+ assert_equal 'select * from table where exists (select * from inner_table aliased where table.column1 = aliased.column1)', statement.to_sql
157
+ end
158
+
159
+ def test_columns_in_where_are_validated_against_tables
160
+ assert_raises ArgumentError do
161
+ Select.all.from[:table].where do
162
+ not_table.cat = 12
163
+ end
164
+ end
165
+ end
166
+
167
+ def test_columns_in_inner_where_are_validated_against_outer_and_inner_tables
168
+ assert_raises ArgumentError do
169
+ Select.all.from[:table].where do
170
+ exists(Select.all.from[:inner_table].where do
171
+ table.column1 = not_table.cat
172
+ end)
173
+ end
174
+ end
175
+ end
91
176
  end
92
177
 
93
178
  === Contributors
data/lib/array.rb CHANGED
@@ -8,13 +8,4 @@ class Array
8
8
  def to_sql
9
9
  self.collect { |element| element.to_sql }.join(', ')
10
10
  end
11
-
12
- # call-seq: array.to_sql_equal(any) -> a_string
13
- #
14
- # Returns a string containing the sql in expression
15
- #
16
- # [10, :column2, 'book'].to_sql_equal(:column1) #=> "column1 in (10, column2, 'book')"
17
- def to_sql_equal(lval)
18
- "#{lval.to_sql } in (#{ self.to_sql })"
19
- end
20
11
  end
data/lib/delete.rb CHANGED
@@ -17,6 +17,7 @@ class Delete < SqlStatement
17
17
  # Delete.from[:table1].to_sql #=> "delete from table1"
18
18
  def [](table)
19
19
  @to_sql += table.to_sql
20
+ @tables = [table]
20
21
  self
21
22
  end
22
23
  end
data/lib/numeric.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  class Numeric
2
- include WhereValue
3
2
  # call-seq: numeric.to_sql -> a_numeric
4
3
  #
5
4
  # Returns self
@@ -0,0 +1,15 @@
1
+ class OrWhereBuilder < WhereBuilder
2
+
3
+ # call-seq: or_where.to_sql -> a_string
4
+ #
5
+ # Returns a string by collecting all the conditions and joins them with ' and '.
6
+ #
7
+ # OrWhereBuilder.new [] do
8
+ # equal :column1, 10
9
+ # equal :column2, 'book'
10
+ # end.to_sql #=> " or column1 = 10 and column2 = 'book'"
11
+ def to_sql
12
+ " or #{sql_parts.join(' and ')}"
13
+ end
14
+
15
+ end
data/lib/receive_any.rb CHANGED
@@ -1,26 +1,59 @@
1
1
  class ReceiveAny #:nodoc:
2
- attr_reader :to_sql
2
+ attr_reader :to_sql, :builder
3
3
 
4
4
  def initialize(identifier, builder)
5
5
  @to_sql = identifier.to_s
6
6
  @builder = builder
7
7
  end
8
8
 
9
- def <=(arg)
10
- @builder.sql_parts << "#{self.to_sql} <= #{arg.to_sql}"
9
+ def equal(arg)
10
+ builder.equal self, arg
11
11
  end
12
12
 
13
- def >=(arg)
14
- @builder.sql_parts << "#{self.to_sql} >= #{arg.to_sql}"
13
+ def not_equal(arg)
14
+ builder.not_equal self, arg
15
15
  end
16
+ alias <=> not_equal
16
17
 
17
- def append_on_setter(lval, rval)
18
- @builder.equal(lval.to_sym, rval)
18
+ def less_than(arg)
19
+ builder.less_than self, arg
19
20
  end
21
+ alias < less_than
22
+
23
+ def less_than_or_equal(arg)
24
+ builder.less_than_or_equal self, arg
25
+ end
26
+ alias <= less_than_or_equal
27
+
28
+ def greater_than(arg)
29
+ builder.greater_than self, arg
30
+ end
31
+ alias > greater_than
32
+
33
+ def greater_than_or_equal(arg)
34
+ builder.greater_than_or_equal self, arg
35
+ end
36
+ alias >= greater_than_or_equal
37
+
38
+ def is_in(arg)
39
+ builder.is_in self, arg
40
+ end
41
+ alias >> is_in
42
+
43
+ def is_not_in(arg)
44
+ builder.is_not_in self, arg
45
+ end
46
+ alias << is_not_in
47
+
48
+ def is_not_null(arg=nil)
49
+ builder.is_not_null self
50
+ end
51
+ alias ^ is_not_null
20
52
 
21
53
  def method_missing(sym, *args)
54
+ raise ArgumentError.new("#{self.to_sql} is not specified as a table in your from statement") unless @builder.tables.include?(self.to_sql.to_sym)
22
55
  @to_sql << ".#{sym.to_s}".chomp("=")
23
- append_on_setter(self.to_sql, args.first) if sym.to_s =~ /=$/
56
+ self.equal args.first if sym.to_s =~ /=$/
24
57
  self
25
58
  end
26
59
  end
data/lib/select.rb CHANGED
@@ -45,7 +45,11 @@ class Select < SqlStatement
45
45
  #
46
46
  # Select[1, :column1, 'book'].from[:table1, :table2].to_sql #=> "select 1, column1, 'book' from table1, table2"
47
47
  def [](*table_names)
48
- @to_sql += table_names.sort{ |x,y| x.to_s <=> y.to_s }.to_sql
48
+ @tables = table_names.inject([]) { |result, element| result + (element.is_a?(Hash) ? element.values : [element]) }
49
+
50
+ @to_sql += table_names.inject([]) do |result, element|
51
+ result + (element.is_a?(Symbol) ? [element] : element.to_a.inject([]) { |result, pair| result << :"#{pair.first} #{pair.last}" })
52
+ end.sort{ |x,y| x.to_s <=> y.to_s }.to_sql
49
53
  self
50
54
  end
51
55
 
data/lib/sql_statement.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  class SqlStatement
2
+ attr_accessor :tables
2
3
  attr_reader :to_sql
3
4
 
4
5
  def initialize(sql) #:nodoc:
@@ -13,7 +14,19 @@ class SqlStatement
13
14
  #
14
15
  # Select[1].where { equal :column1, 1 }.to_sql #=> "select 1 where column1 = 1"
15
16
  def where(&block)
16
- @to_sql += WhereBuilder.new(&block).to_sql
17
+ @to_sql += WhereBuilder.new(self.tables, &block).to_sql
18
+ self
19
+ end
20
+
21
+ # call-seq: sql_statement.or { block } -> a_sql_statement
22
+ #
23
+ # Creates a new OrWhereBuilder instance, passing the block as a parameter, then executes to_sql on the OrWhereBuilder instance.
24
+ # The resulting string from the OrWhereBuilder instance is appended to the SQL statement.
25
+ # Returns self.
26
+ #
27
+ # Select[1].where { equal :column1, 1 }.or { equal :column1, 100 }.to_sql #=> "select 1 where column1 = 1 or column1 = 100"
28
+ def or(&block)
29
+ @to_sql += OrWhereBuilder.new(self.tables, &block).to_sql
17
30
  self
18
31
  end
19
32
  end
data/lib/sqldsl.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  require File.dirname(__FILE__) + '/receive_any.rb'
2
- require File.dirname(__FILE__) + '/where_value.rb'
3
- require File.dirname(__FILE__) + '/object.rb'
4
2
  require File.dirname(__FILE__) + '/symbol.rb'
5
3
  require File.dirname(__FILE__) + '/array.rb'
6
4
  require File.dirname(__FILE__) + '/string.rb'
@@ -9,6 +7,7 @@ require File.dirname(__FILE__) + '/numeric.rb'
9
7
  require File.dirname(__FILE__) + '/time.rb'
10
8
  require File.dirname(__FILE__) + '/sql_statement.rb'
11
9
  require File.dirname(__FILE__) + '/where_builder.rb'
10
+ require File.dirname(__FILE__) + '/or_where_builder.rb'
12
11
  require File.dirname(__FILE__) + '/select.rb'
13
12
  require File.dirname(__FILE__) + '/distinct_select.rb'
14
13
  require File.dirname(__FILE__) + '/insert.rb'
data/lib/string.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  class String
2
- include WhereValue
3
-
4
2
  # call-seq: string.to_sql -> a_string
5
3
  #
6
4
  # Returns a string with single quotes escaped.
data/lib/symbol.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  class Symbol
2
- include WhereValue
3
2
  # call-seq: symbol.to_sql -> a_string
4
3
  #
5
4
  # Returns a string with single quotes escaped.
data/lib/update.rb CHANGED
@@ -6,7 +6,9 @@ class Update < SqlStatement
6
6
  #
7
7
  # Update[:table1].to_sql #=> "update table1"
8
8
  def [](table)
9
- self.new("update #{table.to_sql}")
9
+ update = self.new("update #{table.to_sql}")
10
+ update.tables = [table]
11
+ update
10
12
  end
11
13
  end
12
14
 
data/lib/where_builder.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  class WhereBuilder
2
-
3
- # call-seq: WhereBuilder.new(&block) -> a_where_builder
2
+ attr_reader :tables
3
+
4
+ # call-seq: WhereBuilder.new(tables, &block) -> a_where_builder
4
5
  #
5
6
  # Returns a new WhereBuilder. At initialization time the block is instance evaled on the
6
7
  # new WhereBuilder instance.
7
8
  #
8
- # WhereBuilder.new { equal :column1, 10 }.to_sql #=> " where column1 = 10"
9
- def initialize(&block)
9
+ # WhereBuilder.new [:table1] { equal :column1, 10 }.to_sql #=> " where column1 = 10"
10
+ def initialize(tables, &block)
11
+ raise ArgumentError.new("no block given to where, check for parenthesis usage") unless block_given?
12
+ @tables = tables.concat(eval("respond_to?(:tables) ? tables : []", block.binding))
10
13
  instance_eval(&block)
11
14
  end
12
15
 
@@ -14,25 +17,88 @@ class WhereBuilder
14
17
  #
15
18
  # Appends an equality condition to the where SQL clause.
16
19
  #
17
- # WhereBuilder.new { equal :column1, 10 }.to_sql #=> " where column1 = 10"
20
+ # where { equal :column1, 10 }.to_sql #=> " where column1 = 10"
18
21
  def equal(lval, rval)
19
- sql_parts << rval.to_sql_equal(lval)
22
+ add_condition(lval, "=", rval)
23
+ end
24
+
25
+ # call-seq: where.not_equal(arg1, arg2)
26
+ #
27
+ # Appends a not equal condition to the where SQL clause.
28
+ #
29
+ # where { not_equal :column1, 10 }.to_sql #=> " where column1 <> 10"
30
+ def not_equal(lval, rval)
31
+ add_condition(lval, "<>", rval)
32
+ end
33
+
34
+ # call-seq: where.less_than(arg1, arg2)
35
+ #
36
+ # Appends a less than condition to the where SQL clause.
37
+ #
38
+ # where { less_than :column1, 10 }.to_sql #=> " where column1 < 10"
39
+ def less_than(lval, rval)
40
+ add_condition(lval, "<", rval)
41
+ end
42
+
43
+ # call-seq: where.less_than_or_equal(arg1, arg2)
44
+ #
45
+ # Appends a less than or equal condition to the where SQL clause.
46
+ #
47
+ # where { less_than_or_equal :column1, 10 }.to_sql #=> " where column1 <= 10"
48
+ def less_than_or_equal(lval, rval)
49
+ add_condition(lval, "<=", rval)
50
+ end
51
+
52
+ # call-seq: where.greater_than(arg1, arg2)
53
+ #
54
+ # Appends a greater than condition to the where SQL clause.
55
+ #
56
+ # where { greater_than :column1, 10 }.to_sql #=> " where column1 > 10"
57
+ def greater_than(lval, rval)
58
+ add_condition(lval, ">", rval)
59
+ end
60
+
61
+ # call-seq: where.greater_than_or_equal(arg1, arg2)
62
+ #
63
+ # Appends a greater than or equal condition to the where SQL clause.
64
+ #
65
+ # where { greater_than_or_equal :column1, 10 }.to_sql #=> " where column1 >= 10"
66
+ def greater_than_or_equal(lval, rval)
67
+ add_condition(lval, ">=", rval)
68
+ end
69
+
70
+ # call-seq: where.is_in(arg1, arg2)
71
+ #
72
+ # Appends an in condition to the where SQL clause.
73
+ #
74
+ # where { is_in :column1, [10, 20] }.to_sql #=> " where column1 in (10, 20)"
75
+ def is_in(lval, rval)
76
+ add_parenthesis_condition(lval, "in", rval)
77
+ end
78
+
79
+ # call-seq: where.is_not_in(arg1, arg2)
80
+ #
81
+ # Appends a not in condition to the where SQL clause.
82
+ #
83
+ # where { is_not_in :column1, [10, 20] }.to_sql #=> " where column1 not in (10, 20)"
84
+ def is_not_in(lval, rval)
85
+ add_parenthesis_condition(lval, "not in", rval)
20
86
  end
21
87
 
22
88
  # call-seq: where.not_null(arg1)
23
89
  #
24
90
  # Appends a not null condition to the where SQL clause.
25
91
  #
26
- # WhereBuilder.new { not_null :column1 }.to_sql #=> " where column1 is not null"
27
- def not_null(column)
28
- sql_parts << "#{column} is not null"
92
+ # where { is_not_null :column1 }.to_sql #=> " where column1 is not null"
93
+ def is_not_null(column)
94
+ sql_parts << "#{column.to_sql} is not null"
29
95
  end
30
96
 
31
97
  # call-seq: where.exists(clause)
32
98
  #
33
99
  # Appends an exists condition to the where SQL clause.
34
100
  #
35
- # WhereBuilder.new { exists 'select id from table1' }.to_sql #=> " where exists (select id from table1)"
101
+ # where { exists 'select id from table1' }.to_sql #=> " where exists (select id from table1)"
36
102
  def exists(clause)
37
103
  sql_parts << "exists (#{clause.to_sql})"
38
104
  end
@@ -41,7 +107,7 @@ class WhereBuilder
41
107
  #
42
108
  # Appends an exists condition to the where SQL clause.
43
109
  #
44
- # WhereBuilder.new { not_exists 'select id from table1' }.to_sql #=> " where not exists (select id from table1)"
110
+ # where { not_exists 'select id from table1' }.to_sql #=> " where not exists (select id from table1)"
45
111
  def not_exists(clause)
46
112
  sql_parts << "not exists (#{clause.to_sql})"
47
113
  end
@@ -50,7 +116,7 @@ class WhereBuilder
50
116
  #
51
117
  # Returns a string by collecting all the conditions and joins them with ' and '.
52
118
  #
53
- # WhereBuilder.new do
119
+ # WhereBuilder.new [] do
54
120
  # equal :column1, 10
55
121
  # equal :column2, 'book'
56
122
  # end.to_sql #=> " where column1 = 10 and column2 = 'book'"
@@ -58,12 +124,21 @@ class WhereBuilder
58
124
  " where #{sql_parts.join(' and ')}"
59
125
  end
60
126
 
127
+ def add_condition(lval, operator, rval) #:nodoc:
128
+ sql_parts << "#{lval.to_sql} #{operator} #{rval.to_sql}"
129
+ end
130
+
131
+ def add_parenthesis_condition(lval, operator, rval) #:nodoc:
132
+ sql_parts << "#{lval.to_sql} #{operator} (#{rval.to_sql})"
133
+ end
134
+
135
+ protected
61
136
  def sql_parts #:nodoc:
62
137
  @sql_parts ||= []
63
138
  end
64
139
 
65
140
  def method_missing(sym, *args) #:nodoc:
141
+ super unless args.empty?
66
142
  ReceiveAny.new(sym, self)
67
143
  end
68
-
69
144
  end
data/rakefile.rb CHANGED
@@ -27,7 +27,7 @@ Gem::manage_gems
27
27
  specification = Gem::Specification.new do |s|
28
28
  s.name = "sqldsl"
29
29
  s.summary = "A DSL for creating SQL Statements"
30
- s.version = "1.2.2"
30
+ s.version = "1.3.0"
31
31
  s.author = 'Jay Fields'
32
32
  s.description = "A DSL for creating SQL Statements"
33
33
  s.email = 'sqldsl-developer@rubyforge.org'
data/test/all_tests.rb CHANGED
@@ -1 +1 @@
1
- Dir['**/*_test.rb'].each { |testCase| require testCase }
1
+ Dir["#{File.dirname __FILE__}/*_test.rb"].each { |test_case| require test_case }
data/test/array_test.rb CHANGED
@@ -4,8 +4,4 @@ class ArrayTest < Test::Unit::TestCase
4
4
  def test_to_sql_gives_of_to_sql_objects_comma_delimited
5
5
  assert_equal "too, 'the', 2", [:too, 'the', 2].to_sql
6
6
  end
7
-
8
- def test_sql_equal_uses_in
9
- assert_equal "column1 in (too, 'the', 2)", [:too, 'the', 2].to_sql_equal(:column1)
10
- end
11
7
  end
data/test/delete_test.rb CHANGED
@@ -23,7 +23,7 @@ class DeleteTest < Test::Unit::TestCase
23
23
  assert_equal expected, statement.to_sql
24
24
  end
25
25
 
26
- def test_if_where_is_called_with_no_block_gives_reminder_to_use_parents
26
+ def test_if_where_is_called_with_no_block_gives_reminder_to_use_parens
27
27
  assert_raise(ArgumentError) do
28
28
  statement = Delete.from[:potential_account_offers].where do
29
29
  exists Select[:'potential_account_offers.id'].from[:foo].where do
data/test/numeric_test.rb CHANGED
@@ -4,9 +4,4 @@ class NumbericTest < Test::Unit::TestCase
4
4
  def test_to_sql_gives_self
5
5
  assert_equal 123, 123.to_sql
6
6
  end
7
-
8
- def test_is_a_where_value_duck
9
- assert_equal true, 123.respond_to?(:is_not_in)
10
- end
11
-
12
7
  end
@@ -0,0 +1,161 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ReceiveAnyTest < Test::Unit::TestCase
4
+
5
+ def test_method_calls_to_valid_table_names_are_okay
6
+ builder = stub(:tables => [:foo])
7
+ ReceiveAny.new(:foo, builder).column1
8
+ end
9
+
10
+ def test_method_calls_to_invalid_table_names_raise_Argument_Error
11
+ assert_raises ArgumentError do
12
+ builder = stub(:tables => [])
13
+ ReceiveAny.new(:foo, builder).column1
14
+ end
15
+ end
16
+
17
+ def test_equal
18
+ builder = mock
19
+ builder.expects(:equal).with do |lval, rval|
20
+ lval.class == ReceiveAny && rval == 1
21
+ end
22
+ ReceiveAny.new(:foo, builder).equal 1
23
+ end
24
+
25
+ def test_equal_operator
26
+ builder = mock
27
+ builder.expects(:equal).with do |lval, rval|
28
+ lval.class == ReceiveAny && rval == 1
29
+ end
30
+ builder.expects(:tables).returns [:foo]
31
+ ReceiveAny.new(:foo, builder).bar = 1
32
+ end
33
+
34
+ def test_not_equal
35
+ builder = mock
36
+ builder.expects(:not_equal).with do |lval, rval|
37
+ lval.class == ReceiveAny && rval == 1
38
+ end
39
+ ReceiveAny.new(:foo, builder).not_equal 1
40
+ end
41
+
42
+ def test_not_equal_operator
43
+ builder = mock
44
+ builder.expects(:equal).with do |lval, rval|
45
+ lval.class == ReceiveAny && rval == 1
46
+ end
47
+ ReceiveAny.new(:foo, builder) <=> 1
48
+ end
49
+
50
+ def test_less_than
51
+ builder = mock
52
+ builder.expects(:less_than).with do |lval, rval|
53
+ lval.class == ReceiveAny && rval == 1
54
+ end
55
+ ReceiveAny.new(:foo, builder).less_than 1
56
+ end
57
+
58
+ def test_less_than_operator
59
+ builder = mock
60
+ builder.expects(:less_than).with do |lval, rval|
61
+ lval.class == ReceiveAny && rval == 1
62
+ end
63
+ ReceiveAny.new(:foo, builder) < 1
64
+ end
65
+
66
+ def test_less_than_or_equal
67
+ builder = mock
68
+ builder.expects(:less_than_or_equal).with do |lval, rval|
69
+ lval.class == ReceiveAny && rval == 1
70
+ end
71
+ ReceiveAny.new(:foo, builder).less_than_or_equal 1
72
+ end
73
+
74
+ def test_less_than_or_equal_operator
75
+ builder = mock
76
+ builder.expects(:less_than_or_equal).with do |lval, rval|
77
+ lval.class == ReceiveAny && rval == 1
78
+ end
79
+ ReceiveAny.new(:foo, builder) <= 1
80
+ end
81
+
82
+ def test_greater_than
83
+ builder = mock
84
+ builder.expects(:greater_than).with do |lval, rval|
85
+ lval.class == ReceiveAny && rval == 1
86
+ end
87
+ ReceiveAny.new(:foo, builder).greater_than 1
88
+ end
89
+
90
+ def test_greater_than_operator
91
+ builder = mock
92
+ builder.expects(:greater_than).with do |lval, rval|
93
+ lval.class == ReceiveAny && rval == 1
94
+ end
95
+ ReceiveAny.new(:foo, builder) > 1
96
+ end
97
+
98
+ def test_greater_than_or_equal
99
+ builder = mock
100
+ builder.expects(:greater_than_or_equal).with do |lval, rval|
101
+ lval.class == ReceiveAny && rval == 1
102
+ end
103
+ ReceiveAny.new(:foo, builder).greater_than_or_equal 1
104
+ end
105
+
106
+ def test_greater_than_or_equal_operator
107
+ builder = mock
108
+ builder.expects(:greater_than_or_equal).with do |lval, rval|
109
+ lval.class == ReceiveAny && rval == 1
110
+ end
111
+ ReceiveAny.new(:foo, builder) >= 1
112
+ end
113
+
114
+ def test_is_in
115
+ builder = mock
116
+ builder.expects(:is_in).with do |lval, rval|
117
+ lval.class == ReceiveAny && rval == [1,2]
118
+ end
119
+ ReceiveAny.new(:foo, builder).is_in [1,2]
120
+ end
121
+
122
+ def test_is_in_operator
123
+ builder = mock
124
+ builder.expects(:is_in).with do |lval, rval|
125
+ lval.class == ReceiveAny && rval == [1,2]
126
+ end
127
+ ReceiveAny.new(:foo, builder) >> [1,2]
128
+ end
129
+
130
+ def test_is_not_in
131
+ builder = mock
132
+ builder.expects(:is_not_in).with do |lval, rval|
133
+ lval.class == ReceiveAny && rval == [1,2]
134
+ end
135
+ ReceiveAny.new(:foo, builder).is_not_in [1,2]
136
+ end
137
+
138
+ def test_is_not_in_operator
139
+ builder = mock
140
+ builder.expects(:is_not_in).with do |lval, rval|
141
+ lval.class == ReceiveAny && rval == [1,2]
142
+ end
143
+ ReceiveAny.new(:foo, builder) << [1,2]
144
+ end
145
+
146
+ def test_is_not_null
147
+ builder = mock
148
+ builder.expects(:is_not_null).with do |lval|
149
+ lval.class == ReceiveAny
150
+ end
151
+ ReceiveAny.new(:foo, builder).is_not_null
152
+ end
153
+
154
+ def test_is_not_null_operator
155
+ builder = mock
156
+ builder.expects(:is_not_in).with do |lval|
157
+ lval.class == ReceiveAny
158
+ end
159
+ ReceiveAny.new(:foo, builder) ^ nil
160
+ end
161
+ end
@@ -1,20 +1,100 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
3
  class SelectAcceptanceTest < Test::Unit::TestCase
4
- def test_simple_select
5
- statement = Select[:column1, 'book', 10].from[:table1].where do
4
+ def test_select_with_where_methods
5
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
6
6
  equal :column1, 99
7
- equal :column2, 'star'
7
+ not_equal :column1, 100
8
+ less_than :column2, 'foo'
9
+ less_than_or_equal :column3, :column4
10
+ greater_than :column1, 0
11
+ greater_than_or_equal :column2, 'bar'
12
+ is_not_null :column1
13
+ is_in :column1, [1,2]
14
+ is_not_in :column2, [3, 4]
15
+ exists 0
16
+ not_exists 0
8
17
  end
9
- expected = "select column1, 'book', 10 from table1 where column1 = 99 and column2 = 'star'"
10
- assert_equal expected, statement.to_sql
18
+ expected = "select column1, 'book', 10 from table1, table2
19
+ where column1 = 99 and column1 <> 100 and column2 < 'foo'
20
+ and column3 <= column4 and column1 > 0 and column2 >= 'bar'
21
+ and column1 is not null and column1 in (1, 2) and column2 not in (3, 4)
22
+ and exists (0) and not exists (0)"
23
+ assert_equal expected.delete("\n").squeeze(" "), statement.to_sql
24
+ end
25
+
26
+ def test_select_with_receive_any_objects_and_operators
27
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
28
+ table1.column1 = 99
29
+ column1 <=> 100
30
+ column2 < 'foo'
31
+ column3 <= column4
32
+ column1 > 0
33
+ column2 >= 'bar'
34
+ column1 ^ nil
35
+ column1 >> [1,2]
36
+ column2 << [3, 4]
37
+ end
38
+ expected = "select column1, 'book', 10 from table1, table2
39
+ where table1.column1 = 99 and column1 <> 100 and column2 < 'foo'
40
+ and column3 <= column4 and column1 > 0 and column2 >= 'bar'
41
+ and column1 is not null and column1 in (1, 2) and column2 not in (3, 4)"
42
+ assert_equal expected.delete("\n").squeeze(" "), statement.to_sql
11
43
  end
12
44
 
13
- def test_select_with_join
14
- statement = Select[:column1, :column2].from[:table1, :table2].where do
15
- equal :'table1.id', :'table2.table1_id'
45
+ def test_select_with_receive_any_objects_and_method_calls
46
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
47
+ column1.equal 99
48
+ column1.not_equal 100
49
+ column2.less_than 'foo'
50
+ column3.less_than_or_equal column4
51
+ column1.greater_than 0
52
+ column2.greater_than_or_equal 'bar'
53
+ column1.is_not_null
54
+ column1.is_in [1,2]
55
+ column2.is_not_in [3, 4]
16
56
  end
17
- expected = "select column1, column2 from table1, table2 where table1.id = table2.table1_id"
57
+ expected = "select column1, 'book', 10 from table1, table2
58
+ where column1 = 99 and column1 <> 100 and column2 < 'foo'
59
+ and column3 <= column4 and column1 > 0 and column2 >= 'bar'
60
+ and column1 is not null and column1 in (1, 2) and column2 not in (3, 4)"
61
+ assert_equal expected.delete("\n").squeeze(" "), statement.to_sql
62
+ end
63
+
64
+ def test_select_with_receive_any_objects_and_method_calls
65
+ statement = Select[:column1, 'book', 10].from[:table1, :table2].where do
66
+ column1.equal 0
67
+ end.or do
68
+ column1 > 100
69
+ end
70
+ expected = "select column1, 'book', 10 from table1, table2 where column1 = 0 or column1 > 100"
18
71
  assert_equal expected, statement.to_sql
19
72
  end
73
+
74
+ def test_columns_in_inner_where_are_validated_against_outer_tables
75
+ statement = Select.all.from[:table].where do
76
+ exists(Select.all.from[:inner_table => :aliased].where do
77
+ table.column1 = aliased.column1
78
+ end)
79
+ end
80
+ assert_equal 'select * from table where exists (select * from inner_table aliased where table.column1 = aliased.column1)', statement.to_sql
81
+ end
82
+
83
+ def test_columns_in_where_are_validated_against_tables
84
+ assert_raises ArgumentError do
85
+ Select.all.from[:table].where do
86
+ not_table.cat = 12
87
+ end
88
+ end
89
+ end
90
+
91
+ def test_columns_in_inner_where_are_validated_against_outer_and_inner_tables
92
+ assert_raises ArgumentError do
93
+ Select.all.from[:table].where do
94
+ exists(Select.all.from[:inner_table].where do
95
+ table.column1 = not_table.cat
96
+ end)
97
+ end
98
+ end
99
+ end
20
100
  end
data/test/select_test.rb CHANGED
@@ -23,8 +23,7 @@ class SelectTest < Test::Unit::TestCase
23
23
  end
24
24
 
25
25
  def test_select_with_multiple_tables
26
- assert_equal 'select column from bar, foo',
27
- Select[:column].from[:foo, :bar].to_sql
26
+ assert_equal 'select column from bar, foo', Select[:column].from[:foo, :bar].to_sql
28
27
  end
29
28
 
30
29
  def test_order_by
@@ -44,11 +43,19 @@ class SelectTest < Test::Unit::TestCase
44
43
  end
45
44
 
46
45
  def test_table_aliasing
47
- assert_equal 'select * from table1 as foo, table2 as bar', Select.all.from[:table1 => :foo, :table2 => :bar].to_sql
46
+ assert_equal 'select * from table1 foo, table2 bar', Select.all.from[:table1 => :foo, :table2 => :bar].to_sql
47
+ end
48
+
49
+ def test_tables_no_aliasing
50
+ assert_equal 'select * from table1, table2', Select.all.from[:table1, :table2].to_sql
48
51
  end
49
52
 
50
53
  def test_select_all
51
54
  assert_equal 'select *', Select.all.to_sql
52
55
  end
53
56
 
57
+ def test_table_array
58
+ assert_equal [:table1, :table2], Select.all.from[:table1, :table2].instance_variable_get("@tables")
59
+ end
60
+
54
61
  end
data/test/string_test.rb CHANGED
@@ -8,9 +8,4 @@ class StringTest < Test::Unit::TestCase
8
8
  def test_to_sql_gives_quoted_and_escapes_single_quotes
9
9
  assert_equal "'it''s'", "it's".to_sql
10
10
  end
11
-
12
- def test_is_a_where_value_duck
13
- assert_equal true, "foo".respond_to?(:is_not_in)
14
- end
15
-
16
11
  end
data/test/symbol_test.rb CHANGED
@@ -4,9 +4,4 @@ class SymbolTest < Test::Unit::TestCase
4
4
  def test_to_sql_returns_to_s
5
5
  assert_equal 'asdf', :asdf.to_sql
6
6
  end
7
-
8
- def test_is_a_where_value_duck
9
- assert_equal true, :sym.respond_to?(:is_not_in)
10
- end
11
-
12
7
  end
data/test/test_helper.rb CHANGED
@@ -1,2 +1,7 @@
1
1
  require 'test/unit'
2
+ unless File.directory? File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/'
3
+ raise "mocha 4.0 is required to run the test suite. create the 'vendor' directory as a sibling of test and 'gem unpack mocha' in 'vendor'"
4
+ end
5
+ $:.unshift File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/lib/'
6
+ require File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/lib/mocha'
2
7
  require File.dirname(__FILE__) + '/../lib/sqldsl'
@@ -2,108 +2,117 @@ require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
3
  class WhereBuilderTest < Test::Unit::TestCase
4
4
 
5
- def test_single_equal_where_criteria
6
- statement = WhereBuilder.new do
7
- equal :'foo.column1', :'bar.column2'
8
- end
9
- assert_equal ' where foo.column1 = bar.column2', statement.to_sql
5
+ def test_condition_calls_to_sql
6
+ lval, rval = mock, mock
7
+ lval.expects(:to_sql).returns "lval"
8
+ rval.expects(:to_sql).returns "rval"
9
+ where = WhereBuilder.new([], &lambda {})
10
+ where.add_condition(lval, "op", rval)
11
+ assert_equal "lval op rval", where.send(:sql_parts).first
10
12
  end
11
-
12
- def test_less_then_equal_where_criteria
13
- statement = WhereBuilder.new do
14
- column1 <= column2
15
- end
16
- assert_equal " where column1 <= column2", statement.to_sql
13
+
14
+ def test_parenthesis_condition_calls_to_sql
15
+ lval, rval = mock, mock
16
+ lval.expects(:to_sql).returns "lval"
17
+ rval.expects(:to_sql).returns "rval"
18
+ where = WhereBuilder.new([], &lambda {})
19
+ where.add_parenthesis_condition(lval, "op", rval)
20
+ assert_equal "lval op (rval)", where.send(:sql_parts).first
17
21
  end
18
-
19
- def test_less_then_equal_with_table_aliasing_where_criteria
20
- statement = WhereBuilder.new do
21
- table1.column1 <= table2.column2
22
+
23
+ def test_method_missing_for_receive_any_creation
24
+ assert_equal ReceiveAny, WhereBuilder.new([], &lambda {}).missing_method.class
25
+ end
26
+
27
+ def test_method_missing_when_method_call_is_likely_a_mistake
28
+ assert_raises NoMethodError do
29
+ WhereBuilder.new([], &lambda {}).missing_method(:with_args)
22
30
  end
23
- assert_equal " where table1.column1 <= table2.column2", statement.to_sql
24
31
  end
25
32
 
26
- def test_less_then_equal_with_table_aliasing_where_criteria
27
- statement = WhereBuilder.new do
28
- table1.column1 >= table2.column2
33
+ def test_equal
34
+ statement = WhereBuilder.new [] do
35
+ equal :column1, :column2
29
36
  end
30
- assert_equal " where table1.column1 >= table2.column2", statement.to_sql
37
+ assert_equal ' where column1 = column2', statement.to_sql
31
38
  end
32
39
 
33
- def test_equal_with_table_aliasing_where_criteria
34
- statement = WhereBuilder.new do
35
- table1.Column1 = table2.Folumn2
40
+ def test_not_equal
41
+ statement = WhereBuilder.new [] do
42
+ not_equal :column1, :column2
36
43
  end
37
- assert_equal " where table1.Column1 = table2.Folumn2", statement.to_sql
44
+ assert_equal " where column1 <> column2", statement.to_sql
38
45
  end
39
46
 
40
- def test_equal_with_a_numeric
41
- statement = WhereBuilder.new do
42
- table1.Column1 = 1
47
+ def test_is_in_array
48
+ statement = WhereBuilder.new [] do
49
+ is_in :column1, [1,2]
43
50
  end
44
- assert_equal " where table1.Column1 = 1", statement.to_sql
51
+ assert_equal " where column1 in (1, 2)", statement.to_sql
45
52
  end
46
53
 
47
- def test_equal_with_a_string
48
- statement = WhereBuilder.new do
49
- table1.Column1 = "foo"
54
+ def test_is_not_in_where_criteria
55
+ statement = WhereBuilder.new [] do
56
+ is_not_in :column1, [1,2]
50
57
  end
51
- assert_equal " where table1.Column1 = 'foo'", statement.to_sql
58
+ assert_equal ' where column1 not in (1, 2)', statement.to_sql
52
59
  end
53
60
 
54
- def test_single_equal_with_array_where_criteria
55
- statement = WhereBuilder.new do
56
- equal :'foo.column1', ['foo','bar']
61
+ def test_less_than
62
+ statement = WhereBuilder.new [] do
63
+ less_than :column1, :column2
57
64
  end
58
- assert_equal " where foo.column1 in ('foo', 'bar')", statement.to_sql
65
+ assert_equal " where column1 < column2", statement.to_sql
66
+ end
67
+
68
+ def test_less_than_or_equal
69
+ statement = WhereBuilder.new [] do
70
+ less_than_or_equal :column1, :column2
71
+ end
72
+ assert_equal " where column1 <= column2", statement.to_sql
59
73
  end
60
74
 
61
- def test_multiple_equal_with_more_than_one_where_criteria
62
- statement = WhereBuilder.new do
63
- equal :'foo.column1', :'bar.column2'
64
- equal :'foo.column3', :'bar.column5'
75
+ def test_greater_than
76
+ statement = WhereBuilder.new [] do
77
+ greater_than :column1, :column2
65
78
  end
66
- expected = " where foo.column1 = bar.column2 and foo.column3 = bar.column5"
67
- assert_equal expected, statement.to_sql
79
+ assert_equal " where column1 > column2", statement.to_sql
80
+ end
81
+
82
+ def test_greater_than_or_equal
83
+ statement = WhereBuilder.new [] do
84
+ greater_than_or_equal :column1, :column2
85
+ end
86
+ assert_equal " where column1 >= column2", statement.to_sql
68
87
  end
69
88
 
70
- def test_with_literal_where_criteria
71
- statement = WhereBuilder.new do
72
- equal 'foo.column1', :'bar.column2'
73
- equal :'foo.column3', 'bar.column5'
89
+ def test_where_clause_is_built_with_multiple_conditions
90
+ statement = WhereBuilder.new [] do
91
+ equal :column1, :column2
92
+ equal :column3, :column5
74
93
  end
75
- expected = " where 'foo.column1' = bar.column2 and foo.column3 = 'bar.column5'"
94
+ expected = " where column1 = column2 and column3 = column5"
76
95
  assert_equal expected, statement.to_sql
77
96
  end
78
97
 
79
- def test_exists_evaluates_sql_statement_followed
80
- statement = WhereBuilder.new do
98
+ def test_exists_evaluates_sql_statement_argument
99
+ statement = WhereBuilder.new [] do
81
100
  exists Struct.new(:to_sql).new('select foo')
82
101
  end
83
102
  assert_equal ' where exists (select foo)', statement.to_sql
84
103
  end
85
104
 
86
- def test_not_exists_evaluates_sql_statement_followed
87
- statement = WhereBuilder.new do
105
+ def test_not_exists_evaluates_sql_statement_argument
106
+ statement = WhereBuilder.new [] do
88
107
  not_exists Struct.new(:to_sql).new('select foo')
89
108
  end
90
109
  assert_equal ' where not exists (select foo)', statement.to_sql
91
110
  end
92
111
 
93
112
  def test_is_not_null_where_criteria
94
- statement = WhereBuilder.new do
95
- not_null :something
113
+ statement = WhereBuilder.new [] do
114
+ is_not_null Struct.new(:to_sql).new('something')
96
115
  end
97
116
  assert_equal ' where something is not null', statement.to_sql
98
117
  end
99
-
100
- def test_is_not_in_where_criteria
101
- statement = WhereBuilder.new do
102
- :column1.is_not_in do
103
- Select[:shipment_option_id]
104
- end
105
- end
106
- assert_equal ' where column1 not in (select shipment_option_id)', statement.to_sql
107
- end
108
-
109
- end
118
+ end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: sqldsl
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.2.2
7
- date: 2007-03-01 00:00:00 -05:00
6
+ version: 1.3.0
7
+ date: 2007-03-11 00:00:00 -05:00
8
8
  summary: A DSL for creating SQL Statements
9
9
  require_paths:
10
10
  - lib
@@ -25,7 +25,6 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
- post_install_message:
29
28
  authors:
30
29
  - Jay Fields
31
30
  files:
@@ -35,7 +34,7 @@ files:
35
34
  - lib/hash.rb
36
35
  - lib/insert.rb
37
36
  - lib/numeric.rb
38
- - lib/object.rb
37
+ - lib/or_where_builder.rb
39
38
  - lib/receive_any.rb
40
39
  - lib/select.rb
41
40
  - lib/sql_statement.rb
@@ -45,7 +44,6 @@ files:
45
44
  - lib/time.rb
46
45
  - lib/update.rb
47
46
  - lib/where_builder.rb
48
- - lib/where_value.rb
49
47
  - test/all_tests.rb
50
48
  - test/array_test.rb
51
49
  - test/delete_acceptance_test.rb
@@ -54,7 +52,7 @@ files:
54
52
  - test/insert_acceptance_test.rb
55
53
  - test/insert_test.rb
56
54
  - test/numeric_test.rb
57
- - test/object_test.rb
55
+ - test/receive_any_test.rb
58
56
  - test/select_acceptance_test.rb
59
57
  - test/select_test.rb
60
58
  - test/string_test.rb
@@ -64,7 +62,6 @@ files:
64
62
  - test/update_acceptance_test.rb
65
63
  - test/update_test.rb
66
64
  - test/where_builder_test.rb
67
- - test/where_value_test.rb
68
65
  - rakefile.rb
69
66
  - README
70
67
  test_files:
data/lib/object.rb DELETED
@@ -1,34 +0,0 @@
1
- class Object
2
-
3
- # call-seq: obj.to_sql_equal(any) -> a_string
4
- #
5
- # Returns a string containing the sql equality expression
6
- #
7
- # 10.to_sql_equal(:column1) #=> "column1 = 10"
8
- def to_sql_equal(lval)
9
- sql_expression(lval, "=")
10
- end
11
-
12
- def to_sql_more_than(lval) #:nodoc:
13
- sql_expression(lval, ">")
14
- end
15
-
16
- def to_sql_less_than(lval) #:nodoc:
17
- sql_expression(lval, "<")
18
- end
19
-
20
- def to_sql_less_than_or_equal_to(lval) #:nodoc:
21
- sql_expression(lval, "<=")
22
- end
23
-
24
- def to_sql_more_than_or_equal_to(lval) #:nodoc:
25
- sql_expression(lval, ">=")
26
- end
27
-
28
- protected
29
-
30
- def sql_expression(lval, operator) #:nodoc:
31
- "#{ lval.to_sql } #{operator} #{ self.to_sql }"
32
- end
33
-
34
- end
data/lib/where_value.rb DELETED
@@ -1,8 +0,0 @@
1
- module WhereValue
2
-
3
- def is_not_in &block
4
- builder = eval "self", block.binding
5
- builder.sql_parts << "#{self.to_sql} not in (#{block.call.to_sql})"
6
- end
7
-
8
- end
data/test/object_test.rb DELETED
@@ -1,29 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper'
2
-
3
- class ObjectTest < Test::Unit::TestCase
4
-
5
- def test_to_sql_equal
6
- assert_equal "column1 = foo", foo.to_sql_equal(:column1)
7
- end
8
-
9
- def test_to_sql_more_than
10
- assert_equal "column1 > foo", foo.to_sql_more_than(:column1)
11
- end
12
-
13
- def test_to_sql_less_than
14
- assert_equal "column1 < foo", foo.to_sql_less_than(:column1)
15
- end
16
-
17
- def test_to_sql_less_than_equal
18
- assert_equal "column1 <= foo", foo.to_sql_less_than_or_equal_to(:column1)
19
- end
20
-
21
- def test_to_sql_more_than_equal
22
- assert_equal "column1 >= foo", foo.to_sql_more_than_or_equal_to(:column1)
23
- end
24
-
25
- def foo
26
- Struct.new(:to_sql).new(:foo)
27
- end
28
-
29
- end
@@ -1,22 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper'
2
-
3
- class WhereValueTest < Test::Unit::TestCase
4
-
5
- def test_not_in_sql_statement_with_value
6
- klass = Class.new() do
7
- include WhereValue
8
-
9
- def to_sql
10
- "klass"
11
- end
12
- end
13
-
14
- statement = Select[:foo].where do
15
- klass.new.is_not_in do
16
- "anything"
17
- end
18
- end
19
- assert_equal "select foo where klass not in ('anything')", statement.to_sql
20
- end
21
-
22
- end