oedipus 0.0.1.pre1 → 0.0.1.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/.gitignore +2 -0
  2. data/README.md +235 -44
  3. data/Rakefile +25 -0
  4. data/ext/oedipus/extconf.rb +72 -0
  5. data/ext/oedipus/oedipus.c +239 -0
  6. data/ext/oedipus/oedipus.h +50 -0
  7. data/lib/oedipus/comparison/between.rb +26 -0
  8. data/lib/oedipus/comparison/equal.rb +21 -0
  9. data/lib/oedipus/comparison/gt.rb +21 -0
  10. data/lib/oedipus/comparison/gte.rb +21 -0
  11. data/lib/oedipus/comparison/in.rb +21 -0
  12. data/lib/oedipus/comparison/lt.rb +21 -0
  13. data/lib/oedipus/comparison/lte.rb +21 -0
  14. data/lib/oedipus/comparison/not.rb +25 -0
  15. data/lib/oedipus/comparison/not_equal.rb +21 -0
  16. data/lib/oedipus/comparison/not_in.rb +21 -0
  17. data/lib/oedipus/comparison/outside.rb +26 -0
  18. data/lib/oedipus/comparison/shortcuts.rb +144 -0
  19. data/lib/oedipus/comparison.rb +88 -0
  20. data/lib/oedipus/connection.rb +91 -13
  21. data/lib/oedipus/connection_error.rb +14 -0
  22. data/lib/oedipus/index.rb +189 -46
  23. data/lib/oedipus/query_builder.rb +97 -4
  24. data/lib/oedipus/version.rb +1 -1
  25. data/lib/oedipus.rb +24 -7
  26. data/oedipus.gemspec +4 -5
  27. data/spec/integration/connection_spec.rb +58 -0
  28. data/spec/integration/index_spec.rb +353 -0
  29. data/spec/spec_helper.rb +2 -23
  30. data/spec/support/test_harness.rb +30 -9
  31. data/spec/unit/comparison/between_spec.rb +36 -0
  32. data/spec/unit/comparison/equal_spec.rb +22 -0
  33. data/spec/unit/comparison/gt_spec.rb +22 -0
  34. data/spec/unit/comparison/gte_spec.rb +22 -0
  35. data/spec/unit/comparison/in_spec.rb +22 -0
  36. data/spec/unit/comparison/lt_spec.rb +22 -0
  37. data/spec/unit/comparison/lte_spec.rb +22 -0
  38. data/spec/unit/comparison/not_equal_spec.rb +22 -0
  39. data/spec/unit/comparison/not_in_spec.rb +22 -0
  40. data/spec/unit/comparison/not_spec.rb +37 -0
  41. data/spec/unit/comparison/outside_spec.rb +36 -0
  42. data/spec/unit/comparison/shortcuts_spec.rb +125 -0
  43. data/spec/unit/comparison_spec.rb +109 -0
  44. data/spec/unit/query_builder_spec.rb +150 -0
  45. metadata +68 -19
  46. data/lib/oedipus/mysql/client.rb +0 -136
  47. data/spec/unit/connection_spec.rb +0 -36
  48. data/spec/unit/index_spec.rb +0 -85
@@ -0,0 +1,353 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Index do
13
+ include Oedipus::TestHarness
14
+
15
+ let(:conn) { Oedipus::Connection.new(searchd_host) }
16
+ let(:index) { Oedipus::Index.new(:posts_rt, conn) }
17
+
18
+ describe "#insert" do
19
+ context "with valid data" do
20
+ it "returns the number of rows inserted" do
21
+ index.insert(
22
+ 10,
23
+ title: "Badgers",
24
+ body: "They live in setts, do badgers.",
25
+ views: 721,
26
+ user_id: 7
27
+ ).should == 1
28
+ end
29
+ end
30
+
31
+ context "with invalid data" do
32
+ it "raises an error" do
33
+ expect {
34
+ index.insert(
35
+ 10,
36
+ bad_field: "Invalid",
37
+ body: "They live in setts, do badgers.",
38
+ views: 721,
39
+ user_id: 7
40
+ )
41
+ }.to raise_error(Oedipus::ConnectionError)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#fetch" do
47
+ before(:each) do
48
+ index.insert(1, title: "Badgers and foxes", views: 150)
49
+ index.insert(2, title: "Rabbits and hares", views: 73)
50
+ index.insert(3, title: "Clowns and cannon girls", views: 1)
51
+ end
52
+
53
+ context "with a valid document ID" do
54
+ it "returns the matched document" do
55
+ index.fetch(2).should == { id: 2, views: 73, user_id: 0, status: "" }
56
+ end
57
+ end
58
+
59
+ context "with a bad document ID" do
60
+ it "returns nil" do
61
+ index.fetch(7).should be_nil
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#update" do
67
+ before(:each) do
68
+ index.insert(1, title: "Badgers and foxes", views: 150, user_id: 7)
69
+ end
70
+
71
+ context "with valid data" do
72
+ it "returns the number of rows modified" do
73
+ index.update(1, views: 721).should == 1
74
+ end
75
+
76
+ it "modifies the data" do
77
+ index.update(1, views: 721)
78
+ index.fetch(1).should == { id: 1, views: 721, user_id: 7, status: "" }
79
+ end
80
+ end
81
+
82
+ context "with unmatched data" do
83
+ it "returns 0" do
84
+ index.update(3, views: 721).should == 0
85
+ end
86
+ end
87
+
88
+ context "with invalid data" do
89
+ it "raises an error" do
90
+ expect {
91
+ index.update(1, bad_field: "Invalid", views: 721)
92
+ }.to raise_error(Oedipus::ConnectionError)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "#replace" do
98
+ before(:each) do
99
+ index.insert(
100
+ 1,
101
+ title: "Badgers",
102
+ body: "They live in setts, do badgers.",
103
+ views: 721,
104
+ user_id: 7
105
+ )
106
+ end
107
+
108
+ context "with valid existing data" do
109
+ it "returns the number of rows inserted" do
110
+ index.replace(1, title: "Badgers and foxes", views: 150).should == 1
111
+ end
112
+
113
+ it "entirely replaces the record" do
114
+ index.replace(1, title: "Badgers and foxes", views: 150)
115
+ index.fetch(1).should == { id: 1, views: 150, user_id: 0, status: "" }
116
+ end
117
+ end
118
+
119
+ context "with valid new data" do
120
+ it "returns the number of rows inserted" do
121
+ index.replace(2, title: "Beer and wine", views: 15).should == 1
122
+ end
123
+
124
+ it "entirely replaces the record" do
125
+ index.replace(2, title: "Beer and wine", views: 15)
126
+ index.fetch(2).should == { id: 2, views: 15, user_id: 0, status: "" }
127
+ end
128
+ end
129
+
130
+ context "with invalid data" do
131
+ it "raises an error" do
132
+ expect {
133
+ index.replace(1, bad_field: "Badgers and foxes", views: 150)
134
+ }.to raise_error(Oedipus::ConnectionError)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "#search" do
140
+ before(:each) do
141
+ index.insert(1, title: "Badgers and foxes", views: 150)
142
+ index.insert(2, title: "Rabbits and hares", views: 87)
143
+ index.insert(3, title: "Badgers in the wild", views: 41)
144
+ index.insert(4, title: "Badgers for all!", views: 3003)
145
+ end
146
+
147
+ context "by fulltext matching" do
148
+ it "indicates the number of records found" do
149
+ index.search("badgers")[:total_found].should == 3
150
+ end
151
+
152
+ it "includes the matches records" do
153
+ index.search("badgers")[:records].should == [
154
+ { id: 1, views: 150, user_id: 0, status: "" },
155
+ { id: 3, views: 41, user_id: 0, status: "" },
156
+ { id: 4, views: 3003, user_id: 0, status: "" }
157
+ ]
158
+ end
159
+ end
160
+
161
+ context "by attribute filtering" do
162
+ it "indicates the number of records found" do
163
+ index.search(views: 40..90)[:total_found].should == 2
164
+ end
165
+
166
+ it "includes the matches records" do
167
+ index.search(views: 40..90)[:records].should == [
168
+ { id: 2, views: 87, user_id: 0, status: "" },
169
+ { id: 3, views: 41, user_id: 0, status: "" }
170
+ ]
171
+ end
172
+ end
173
+
174
+ context "by fulltext with attribute filtering" do
175
+ it "indicates the number of records found" do
176
+ index.search("badgers", views: Oedipus.gt(100))[:total_found].should == 2
177
+ end
178
+
179
+ it "includes the matches records" do
180
+ index.search("badgers", views: Oedipus.gt(100))[:records].should == [
181
+ { id: 1, views: 150, user_id: 0, status: "" },
182
+ { id: 4, views: 3003, user_id: 0, status: "" }
183
+ ]
184
+ end
185
+ end
186
+
187
+ context "with limits" do
188
+ it "still indicates the number of records found" do
189
+ index.search("badgers", limit: 2)[:total_found].should == 3
190
+ end
191
+
192
+ it "returns the limited subset of the results" do
193
+ index.search("badgers", limit: 2)[:records].should == [
194
+ { id: 1, views: 150, user_id: 0, status: "" },
195
+ { id: 3, views: 41, user_id: 0, status: "" }
196
+ ]
197
+ end
198
+
199
+ it "can use an offset" do
200
+ index.search("badgers", limit: 1, offset: 1)[:records].should == [
201
+ { id: 3, views: 41, user_id: 0, status: "" }
202
+ ]
203
+ end
204
+ end
205
+
206
+ context "with ordering" do
207
+ it "returns the results ordered accordingly" do
208
+ index.search("badgers", order: {views: :desc})[:records].should == [
209
+ { id: 4, views: 3003, user_id: 0, status: "" },
210
+ { id: 1, views: 150, user_id: 0, status: "" },
211
+ { id: 3, views: 41, user_id: 0, status: "" },
212
+ ]
213
+ end
214
+ end
215
+ end
216
+
217
+ describe "#multi_search" do
218
+ before(:each) do
219
+ index.insert(1, title: "Badgers and foxes", views: 150, user_id: 1)
220
+ index.insert(2, title: "Rabbits and hares", views: 87, user_id: 1)
221
+ index.insert(3, title: "Badgers in the wild", views: 41, user_id: 2)
222
+ index.insert(4, title: "Badgers for all!", views: 3003, user_id: 1)
223
+ end
224
+
225
+ context "by fulltext querying" do
226
+ it "indicates the number of results for each query" do
227
+ results = index.multi_search(
228
+ badgers: "badgers",
229
+ rabbits: "rabbits"
230
+ )
231
+ results[:badgers][:total_found].should == 3
232
+ results[:rabbits][:total_found].should == 1
233
+ end
234
+
235
+ it "returns the records for each search" do
236
+ results = index.multi_search(
237
+ badgers: "badgers",
238
+ rabbits: "rabbits"
239
+ )
240
+ results[:badgers][:records].should == [
241
+ { id: 1, views: 150, user_id: 1, status: "" },
242
+ { id: 3, views: 41, user_id: 2, status: "" },
243
+ { id: 4, views: 3003, user_id: 1, status: "" }
244
+ ]
245
+ results[:rabbits][:records].should == [
246
+ { id: 2, views: 87, user_id: 1, status: "" }
247
+ ]
248
+ end
249
+ end
250
+
251
+ context "by attribute filtering" do
252
+ it "indicates the number of results for each query" do
253
+ results = index.multi_search(
254
+ shiela: {user_id: 1},
255
+ barry: {user_id: 2}
256
+ )
257
+ results[:shiela][:total_found].should == 3
258
+ results[:barry][:total_found].should == 1
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "#faceted_search" do
264
+ before(:each) do
265
+ index.insert(1, title: "Badgers and foxes", body: "Badgers", views: 150, user_id: 1)
266
+ index.insert(2, title: "Rabbits and hares", body: "Rabbits", views: 87, user_id: 1)
267
+ index.insert(3, title: "Badgers in the wild", body: "Test", views: 41, user_id: 2)
268
+ index.insert(4, title: "Badgers for all!", body: "For all", views: 3003, user_id: 1)
269
+ end
270
+
271
+ context "with additional attribute filters" do
272
+ let(:results) do
273
+ index.faceted_search(
274
+ "badgers",
275
+ facets: {
276
+ popular: {views: Oedipus.gte(50)},
277
+ di_carla: {user_id: 2}
278
+ }
279
+ )
280
+ end
281
+
282
+ it "returns the main results in the top-level" do
283
+ results[:records].should == [
284
+ { id: 1, views: 150, user_id: 1, status: "" },
285
+ { id: 3, views: 41, user_id: 2, status: "" },
286
+ { id: 4, views: 3003, user_id: 1, status: "" }
287
+ ]
288
+ end
289
+
290
+ it "applies the filters on top of the base query" do
291
+ results[:facets][:popular][:records].should == [
292
+ { id: 1, views: 150, user_id: 1, status: "" },
293
+ { id: 4, views: 3003, user_id: 1, status: "" }
294
+ ]
295
+ results[:facets][:di_carla][:records].should == [
296
+ { id: 3, views: 41, user_id: 2, status: "" }
297
+ ]
298
+ end
299
+ end
300
+
301
+ context "with overriding attribute filters" do
302
+ let(:results) do
303
+ index.faceted_search(
304
+ "badgers",
305
+ user_id: 1,
306
+ facets: {
307
+ di_carla: {user_id: 2}
308
+ }
309
+ )
310
+ end
311
+
312
+ it "applies the filters on top of the base query" do
313
+ results[:facets][:di_carla][:records].should == [
314
+ { id: 3, views: 41, user_id: 2, status: "" }
315
+ ]
316
+ end
317
+ end
318
+
319
+ context "with overriding overriding fulltext queries" do
320
+ let(:results) do
321
+ index.faceted_search(
322
+ "badgers",
323
+ facets: {
324
+ rabbits: "rabbits"
325
+ }
326
+ )
327
+ end
328
+
329
+ it "entirely replaces the base query" do
330
+ results[:facets][:rabbits][:records].should == [
331
+ { id: 2, views: 87, user_id: 1, status: "" }
332
+ ]
333
+ end
334
+ end
335
+
336
+ context "with overriding refined fulltext queries" do
337
+ let(:results) do
338
+ index.faceted_search(
339
+ "badgers",
340
+ facets: {
341
+ in_body: "@body (%{query})"
342
+ }
343
+ )
344
+ end
345
+
346
+ it "merges the queries" do
347
+ results[:facets][:in_body][:records].should == [
348
+ { id: 1, views: 150, user_id: 1, status: "" },
349
+ ]
350
+ end
351
+ end
352
+ end
353
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,33 +7,12 @@
7
7
  # See LICENSE file for details.
8
8
  ##
9
9
 
10
- require "rspec"
11
10
  require "bundler/setup"
11
+
12
+ require "rspec"
12
13
  require "oedipus"
13
14
 
14
15
  Dir[File.expand_path("../support/**/*rb", __FILE__)].each { |f| require f }
15
16
 
16
17
  RSpec.configure do |config|
17
- include Oedipus::TestHarness
18
-
19
- config.before(:suite) do
20
- unless ENV.key?("SEARCHD")
21
- raise "You must specify a path to the Sphinx 'searchd' executable (>= 2.0.2)"
22
- end
23
-
24
- set_data_dir File.expand_path("../data", __FILE__)
25
- set_searchd ENV["SEARCHD"]
26
-
27
- prepare_data_dirs
28
- write_sphinx_conf
29
- start_searchd
30
- end
31
-
32
- config.before(:each) do
33
- empty_indexes
34
- end
35
-
36
- config.after(:suite) do
37
- stop_searchd
38
- end
39
18
  end
@@ -8,8 +8,33 @@
8
8
  ##
9
9
 
10
10
  module Oedipus
11
- # Mixed into RSpec suites to manage starting/stopping Sphinx.
11
+ # Mixed into RSpec suites to manage starting/stopping Sphinx and writing indexes.
12
12
  module TestHarness
13
+ class << self
14
+ def included(base)
15
+ base.before(:all) do
16
+ unless ENV.key?("SEARCHD")
17
+ raise "You must specify a path to the Sphinx 'searchd' executable (>= 2.0.2)"
18
+ end
19
+
20
+ set_data_dir File.expand_path("../../data", __FILE__)
21
+ set_searchd ENV["SEARCHD"]
22
+
23
+ prepare_data_dirs
24
+ write_sphinx_conf
25
+ start_searchd
26
+ end
27
+
28
+ base.before(:each) do
29
+ empty_indexes
30
+ end
31
+
32
+ base.after(:all) do
33
+ stop_searchd
34
+ end
35
+ end
36
+ end
37
+
13
38
  # Set the path to the searchd executable.
14
39
  #
15
40
  # The version of Sphinx must be >= 2.0.2.
@@ -37,15 +62,11 @@ module Oedipus
37
62
  end
38
63
 
39
64
  def empty_indexes
40
- require "mysql"
41
- @conn ||= ::Mysql.new(searchd_host[:host], nil, nil, nil, searchd_host[:port])
42
-
43
- idxs = @conn.query("SHOW TABLES")
65
+ @conn ||= Oedipus::Connection.new(host: searchd_host[:host], port: searchd_host[:port])
44
66
 
45
- while idx = idxs.fetch_hash
46
- docs = @conn.query("SELECT id FROM #{idx['Index']}")
47
- while hash = docs.fetch_hash
48
- @conn.query("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
67
+ @conn.query("SHOW TABLES").each do |idx|
68
+ @conn.query("SELECT id FROM #{idx['Index']}").each do |hash|
69
+ @conn.execute("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
49
70
  end
50
71
  end
51
72
  end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::Between do
13
+ context "with an inclusive range" do
14
+ let(:comparison) { Oedipus::Comparison::Between.new(42..100) }
15
+
16
+ it "draws as BETWEEN x AND y" do
17
+ comparison.to_s.should == "BETWEEN 42 AND 100"
18
+ end
19
+
20
+ it "inverses as NOT BETWEEN x AND y" do
21
+ comparison.inverse.to_s.should == "NOT BETWEEN 42 AND 100"
22
+ end
23
+ end
24
+
25
+ context "with an exclusive range" do
26
+ let(:comparison) { Oedipus::Comparison::Between.new(42...100) }
27
+
28
+ it "draws as BETWEEN x AND y-1" do
29
+ comparison.to_s.should == "BETWEEN 42 AND 99"
30
+ end
31
+
32
+ it "inverses as NOT BETWEEN x AND y-1" do
33
+ comparison.inverse.to_s.should == "NOT BETWEEN 42 AND 99"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::Equal do
13
+ let(:comparison) { Oedipus::Comparison::Equal.new('test') }
14
+
15
+ it "draws as = v" do
16
+ comparison.to_s.should == "= 'test'"
17
+ end
18
+
19
+ it "inverses as != v" do
20
+ comparison.inverse.to_s.should == "!= 'test'"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::GT do
13
+ let(:comparison) { Oedipus::Comparison::GT.new(42) }
14
+
15
+ it "draws as > v" do
16
+ comparison.to_s.should == "> 42"
17
+ end
18
+
19
+ it "inverses as <= v" do
20
+ comparison.inverse.to_s.should == "<= 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::GTE do
13
+ let(:comparison) { Oedipus::Comparison::GTE.new(42) }
14
+
15
+ it "draws as >= v" do
16
+ comparison.to_s.should == ">= 42"
17
+ end
18
+
19
+ it "inverses as < v" do
20
+ comparison.inverse.to_s.should == "< 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::In do
13
+ let(:comparison) { Oedipus::Comparison::In.new([1, 2, 3]) }
14
+
15
+ it "draws as IN (x, y, z)" do
16
+ comparison.to_s.should == "IN (1, 2, 3)"
17
+ end
18
+
19
+ it "inverses as NOT IN (x, y, z)" do
20
+ comparison.inverse.to_s.should == "NOT IN (1, 2, 3)"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::LT do
13
+ let(:comparison) { Oedipus::Comparison::LT.new(42) }
14
+
15
+ it "draws as < v" do
16
+ comparison.to_s.should == "< 42"
17
+ end
18
+
19
+ it "inverses as >= v" do
20
+ comparison.inverse.to_s.should == ">= 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::LTE do
13
+ let(:comparison) { Oedipus::Comparison::LTE.new(42) }
14
+
15
+ it "draws as <= v" do
16
+ comparison.to_s.should == "<= 42"
17
+ end
18
+
19
+ it "inverses as > v" do
20
+ comparison.inverse.to_s.should == "> 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::NotEqual do
13
+ let(:comparison) { Oedipus::Comparison::NotEqual.new('test') }
14
+
15
+ it "draws as != v" do
16
+ comparison.to_s.should == "!= 'test'"
17
+ end
18
+
19
+ it "inverses as = v" do
20
+ comparison.inverse.to_s.should == "= 'test'"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::NotIn do
13
+ let(:comparison) { Oedipus::Comparison::NotIn.new([1, 2, 3]) }
14
+
15
+ it "draws as NOT IN (x, y, z)" do
16
+ comparison.to_s.should == "NOT IN (1, 2, 3)"
17
+ end
18
+
19
+ it "inverses as IN (x, y, z)" do
20
+ comparison.inverse.to_s.should == "IN (1, 2, 3)"
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::In do
13
+ context "with a non-comparison" do
14
+ let(:comparison) { Oedipus::Comparison::Not.new(0..10) }
15
+
16
+ it "converts to a comparison" do
17
+ comparison.v.should be_a_kind_of(Oedipus::Comparison)
18
+ end
19
+
20
+ it "returns the comparison as its inverse" do
21
+ comparison.inverse.should == comparison.v
22
+ end
23
+ end
24
+
25
+ context "with a comparison" do
26
+ let(:original) { Oedipus::Comparison::GTE.new(7) }
27
+ let(:comparison) { Oedipus::Comparison::Not.new(original) }
28
+
29
+ it "draws as the inverse of the comparison" do
30
+ comparison.to_s.should == original.inverse.to_s
31
+ end
32
+
33
+ it "inverses as the original" do
34
+ comparison.inverse.to_s == original.to_s
35
+ end
36
+ end
37
+ end