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.
- data/.gitignore +2 -0
- data/README.md +235 -44
- data/Rakefile +25 -0
- data/ext/oedipus/extconf.rb +72 -0
- data/ext/oedipus/oedipus.c +239 -0
- data/ext/oedipus/oedipus.h +50 -0
- data/lib/oedipus/comparison/between.rb +26 -0
- data/lib/oedipus/comparison/equal.rb +21 -0
- data/lib/oedipus/comparison/gt.rb +21 -0
- data/lib/oedipus/comparison/gte.rb +21 -0
- data/lib/oedipus/comparison/in.rb +21 -0
- data/lib/oedipus/comparison/lt.rb +21 -0
- data/lib/oedipus/comparison/lte.rb +21 -0
- data/lib/oedipus/comparison/not.rb +25 -0
- data/lib/oedipus/comparison/not_equal.rb +21 -0
- data/lib/oedipus/comparison/not_in.rb +21 -0
- data/lib/oedipus/comparison/outside.rb +26 -0
- data/lib/oedipus/comparison/shortcuts.rb +144 -0
- data/lib/oedipus/comparison.rb +88 -0
- data/lib/oedipus/connection.rb +91 -13
- data/lib/oedipus/connection_error.rb +14 -0
- data/lib/oedipus/index.rb +189 -46
- data/lib/oedipus/query_builder.rb +97 -4
- data/lib/oedipus/version.rb +1 -1
- data/lib/oedipus.rb +24 -7
- data/oedipus.gemspec +4 -5
- data/spec/integration/connection_spec.rb +58 -0
- data/spec/integration/index_spec.rb +353 -0
- data/spec/spec_helper.rb +2 -23
- data/spec/support/test_harness.rb +30 -9
- data/spec/unit/comparison/between_spec.rb +36 -0
- data/spec/unit/comparison/equal_spec.rb +22 -0
- data/spec/unit/comparison/gt_spec.rb +22 -0
- data/spec/unit/comparison/gte_spec.rb +22 -0
- data/spec/unit/comparison/in_spec.rb +22 -0
- data/spec/unit/comparison/lt_spec.rb +22 -0
- data/spec/unit/comparison/lte_spec.rb +22 -0
- data/spec/unit/comparison/not_equal_spec.rb +22 -0
- data/spec/unit/comparison/not_in_spec.rb +22 -0
- data/spec/unit/comparison/not_spec.rb +37 -0
- data/spec/unit/comparison/outside_spec.rb +36 -0
- data/spec/unit/comparison/shortcuts_spec.rb +125 -0
- data/spec/unit/comparison_spec.rb +109 -0
- data/spec/unit/query_builder_spec.rb +150 -0
- metadata +68 -19
- data/lib/oedipus/mysql/client.rb +0 -136
- data/spec/unit/connection_spec.rb +0 -36
- data/spec/unit/index_spec.rb +0 -85
data/lib/oedipus/index.rb
CHANGED
@@ -11,7 +11,6 @@ module Oedipus
|
|
11
11
|
# Representation of a search index for querying.
|
12
12
|
class Index
|
13
13
|
attr_reader :name
|
14
|
-
attr_reader :attributes
|
15
14
|
|
16
15
|
# Initialize the index named +name+ on the connection +conn+.
|
17
16
|
#
|
@@ -21,16 +20,15 @@ module Oedipus
|
|
21
20
|
# @param [Connection] conn
|
22
21
|
# an instance of Oedipus::Connection for querying
|
23
22
|
def initialize(name, conn)
|
24
|
-
@name
|
25
|
-
@conn
|
26
|
-
@
|
27
|
-
@builder = QueryBuilder.new(name, conn)
|
23
|
+
@name = name.to_sym
|
24
|
+
@conn = conn
|
25
|
+
@builder = QueryBuilder.new(name)
|
28
26
|
end
|
29
27
|
|
30
28
|
# Insert the record with the ID +id+.
|
31
29
|
#
|
32
30
|
# @example
|
33
|
-
# index.insert(42, :
|
31
|
+
# index.insert(42, title: "example", views: 22)
|
34
32
|
#
|
35
33
|
# @param [Integer] id
|
36
34
|
# the unique ID of the document in the index
|
@@ -38,66 +36,211 @@ module Oedipus
|
|
38
36
|
# @param [Hash] hash
|
39
37
|
# a symbol-keyed hash of data to insert
|
40
38
|
#
|
41
|
-
# @return [
|
42
|
-
#
|
39
|
+
# @return [Fixnum]
|
40
|
+
# the number of rows inserted (currently always 1)
|
43
41
|
def insert(id, hash)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
@conn.execute(@builder.insert(id, hash))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Update the record with the ID +id+.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# index.update(42, views: 25)
|
49
|
+
#
|
50
|
+
# @param [Integer] id
|
51
|
+
# the unique ID of the document in the index
|
52
|
+
#
|
53
|
+
# @param [Hash] hash
|
54
|
+
# a symbol-keyed hash of data to set
|
55
|
+
#
|
56
|
+
# @return [Fixnum]
|
57
|
+
# the number of rows updated (1 or 0)
|
58
|
+
def update(id, hash)
|
59
|
+
@conn.execute(@builder.update(id, hash))
|
50
60
|
end
|
51
61
|
|
62
|
+
# Completely replace the record with the ID +id+.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# index.replace(42, title: "New title", views: 25)
|
66
|
+
#
|
67
|
+
# @param [Integer] id
|
68
|
+
# the unique ID of the document in the index
|
69
|
+
#
|
70
|
+
# @param [Hash] hash
|
71
|
+
# a symbol-keyed hash of data to insert
|
72
|
+
#
|
73
|
+
# @return [Fixnum]
|
74
|
+
# the number of rows inserted (currentl always 1)
|
75
|
+
def replace(id, hash)
|
76
|
+
@conn.execute(@builder.replace(id, hash))
|
77
|
+
end
|
78
|
+
|
79
|
+
# Fetch a single document by its ID.
|
80
|
+
#
|
81
|
+
# Returns the Hash of attributes if found, otherwise nil.
|
82
|
+
#
|
83
|
+
# @param [Fixnum] id
|
84
|
+
# the ID of the document
|
85
|
+
#
|
86
|
+
# @return [Hash]
|
87
|
+
# the attributes of the record
|
88
|
+
def fetch(id)
|
89
|
+
search(id: id)[:records].first
|
90
|
+
end
|
91
|
+
|
92
|
+
# Perform a search on the index.
|
93
|
+
#
|
94
|
+
# Either one or two arguments may be passed, with either one being mutually
|
95
|
+
# optional.
|
96
|
+
#
|
97
|
+
# @example Fulltext search
|
98
|
+
# index.search("cats AND dogs")
|
99
|
+
#
|
100
|
+
# @example Fulltext search with attribute filters
|
101
|
+
# index.search("cats AND dogs", author_id: 57)
|
102
|
+
#
|
103
|
+
# @example Attribute search only
|
104
|
+
# index.search(author_id: 57)
|
105
|
+
#
|
106
|
+
# @param [String] query
|
107
|
+
# a fulltext query
|
108
|
+
#
|
109
|
+
# @param [Hash] filters
|
110
|
+
# attribute filters, limits, sorting and other options
|
111
|
+
#
|
112
|
+
# @return [Hash]
|
113
|
+
# a Hash containing meta data, with the records in :records
|
52
114
|
def search(*args)
|
53
|
-
|
115
|
+
multi_search(_main_: args)[:_main_]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Perform a faceted search on the index, using a base query and one or more facets.
|
119
|
+
#
|
120
|
+
# The base query is inherited by each facet, which may override (or refine) the
|
121
|
+
# query.
|
122
|
+
#
|
123
|
+
# The results returned include a :facets key, containing the results for each facet.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# index.faceted_search(
|
127
|
+
# "cats | dogs",
|
128
|
+
# category_id: 7,
|
129
|
+
# facets: {
|
130
|
+
# popular: {views: Oedipus.gt(150)},
|
131
|
+
# recent: {published_at: Oedipus.gt(Time.now.to_i - 7 * 86400)}
|
132
|
+
# }
|
133
|
+
# )
|
134
|
+
#
|
135
|
+
# @param [String] fulltext_query
|
136
|
+
# a fulltext query to search on, optional
|
137
|
+
#
|
138
|
+
# @param [Hash] options
|
139
|
+
# attribute filters and facets in a sub-hash
|
140
|
+
#
|
141
|
+
# @return [Hash]
|
142
|
+
# a Hash whose top-level result set is for the main query and which
|
143
|
+
# contains a :facets element with keys mapping 1:1 for all facets
|
144
|
+
def faceted_search(*args)
|
145
|
+
query, options = extract_query_data(args)
|
146
|
+
main_query = [query, options.reject { |k, _| k == :facets }]
|
147
|
+
facets = merge_queries(main_query, options.fetch(:facets, {}))
|
54
148
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
149
|
+
{ facets: {} }.tap do |results|
|
150
|
+
multi_search({ _main_: main_query }.merge(facets)).each do |k, v|
|
151
|
+
k == :_main_ ? results.merge!(v) : results[:facets].merge!(k => v)
|
152
|
+
end
|
59
153
|
end
|
154
|
+
end
|
60
155
|
|
61
|
-
|
62
|
-
|
156
|
+
# Perform a a batch search on the index.
|
157
|
+
#
|
158
|
+
# A Hash of queries is passed, whose keys are used to collate the results in
|
159
|
+
# the return value.
|
160
|
+
#
|
161
|
+
# Each query may either by a string (fulltext search), a Hash (attribute search)
|
162
|
+
# or an array containing both. In other words, the same arguments accepted by
|
163
|
+
# the #search method.
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
# index.multi_search(
|
167
|
+
# cat_results: ["cats", { author_id: 57 }],
|
168
|
+
# dog_results: ["dogs", { author_id: 57 }]
|
169
|
+
# )
|
170
|
+
#
|
171
|
+
# @param [Hash] queries
|
172
|
+
# a hash whose keys map to queries
|
173
|
+
#
|
174
|
+
# @return [Hash]
|
175
|
+
# a Hash whose keys map 1:1 with the input Hash, each element containing the
|
176
|
+
# same results as those returned by the #search method.
|
177
|
+
def multi_search(queries)
|
178
|
+
unless queries.kind_of?(Hash)
|
179
|
+
raise ArgumentError, "Argument must be a Hash of named queries (#{queries.class} given)"
|
180
|
+
end
|
63
181
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
182
|
+
rs = @conn.multi_query(
|
183
|
+
queries.map { |key, args|
|
184
|
+
[@builder.select(*extract_query_data(args)), "SHOW META"]
|
185
|
+
}.flatten.join(";\n")
|
186
|
+
)
|
187
|
+
|
188
|
+
Hash[].tap do |result|
|
189
|
+
queries.keys.each do |key|
|
190
|
+
records, meta = rs.shift, rs.shift
|
191
|
+
result[key] = meta_to_hash(meta).tap do |r|
|
192
|
+
r[:records] = records.map { |hash|
|
193
|
+
hash.inject({}) { |o, (k, v)| o.merge!(k.to_sym => v) }
|
194
|
+
}
|
71
195
|
end
|
72
196
|
end
|
197
|
+
end
|
198
|
+
end
|
73
199
|
|
74
|
-
|
200
|
+
private
|
75
201
|
|
76
|
-
|
77
|
-
|
202
|
+
def meta_to_hash(meta)
|
203
|
+
Hash[].tap do |hash|
|
204
|
+
meta.each do |m|
|
205
|
+
n, v = m.values
|
206
|
+
case n
|
207
|
+
when "total_found", "total" then hash[n.to_sym] = v.to_i
|
208
|
+
when "time" then hash[:time] = v.to_f
|
209
|
+
when /\Adocs\[\d+\]\Z/ then (hash[:docs] ||= []).tap { |a| a << v.to_i }
|
210
|
+
when /\Ahits\[\d+\]\Z/ then (hash[:hits] ||= []).tap { |a| a << v.to_i }
|
211
|
+
when /\Akeyword\[\d+\]\Z/ then (hash[:keywords] ||= []).tap { |a| a << v }
|
212
|
+
else hash[n.to_sym] = v
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if hash.key?(:docs) && hash.key?(:hits) && hash.key?(:keywords)
|
217
|
+
hash[:docs] = Hash[(hash[:keywords]).zip(hash[:docs])]
|
218
|
+
hash[:hits] = Hash[(hash[:keywords]).zip(hash[:hits])]
|
78
219
|
end
|
79
220
|
end
|
80
221
|
end
|
81
222
|
|
82
|
-
|
223
|
+
def extract_query_data(args, default_query = "")
|
224
|
+
args = [args] unless Array === args
|
83
225
|
|
84
|
-
|
85
|
-
|
86
|
-
when Fixnum then Integer(value)
|
87
|
-
else value
|
226
|
+
unless (1..2) === args.size
|
227
|
+
raise ArgumentError, "Wrong number of query arguments (#{args.size} for 1..2)"
|
88
228
|
end
|
89
|
-
end
|
90
229
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
case row['Type']
|
96
|
-
when 'uint', 'integer' then attrs[row['Field'].to_sym] = 0
|
97
|
-
when 'string' then attrs[row['Field'].to_sym] = ""
|
98
|
-
end
|
99
|
-
end
|
230
|
+
case args[0]
|
231
|
+
when String then [args[0], args.fetch(1, {})]
|
232
|
+
when Hash then [default_query, args[0] ]
|
233
|
+
else raise ArgumentError, "Invalid query argument type #{args.first.class}"
|
100
234
|
end
|
101
235
|
end
|
236
|
+
|
237
|
+
def merge_queries(base, others)
|
238
|
+
base_query, base_filters = base
|
239
|
+
|
240
|
+
Hash[others.map { |k, q|
|
241
|
+
query, filters = extract_query_data(q, base_query)
|
242
|
+
[k, [query.gsub("%{query}", base_query), base_filters.merge(filters)]]
|
243
|
+
}]
|
244
|
+
end
|
102
245
|
end
|
103
246
|
end
|
@@ -8,32 +8,125 @@
|
|
8
8
|
##
|
9
9
|
|
10
10
|
module Oedipus
|
11
|
+
# Constructs SphinxQL queries from the internal Hash format.
|
11
12
|
class QueryBuilder
|
12
|
-
|
13
|
+
# Initialize a new QueryBuilder for +index_name+.
|
14
|
+
#
|
15
|
+
# @param [Symbol] index_name
|
16
|
+
# the name of the index being queried
|
17
|
+
def initialize(index_name)
|
13
18
|
@index_name = index_name
|
14
|
-
@conn = conn
|
15
19
|
end
|
16
20
|
|
17
|
-
|
21
|
+
# Build a SphinxQL query for the fulltext search +query+ and filters in +filters+.
|
22
|
+
#
|
23
|
+
# @param [String] query
|
24
|
+
# the fulltext query to execute (may be empty)
|
25
|
+
#
|
26
|
+
# @param [Hash] filters
|
27
|
+
# additional attribute filters and other options
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
# a SphinxQL query
|
31
|
+
def select(query, filters)
|
18
32
|
[
|
19
33
|
from,
|
20
34
|
conditions(query, filters),
|
35
|
+
order_by(filters),
|
21
36
|
limits(filters)
|
22
37
|
].join(" ")
|
23
38
|
end
|
24
39
|
|
40
|
+
# Build a SphinxQL query to insert the record identified by +id+ with the given attributes.
|
41
|
+
#
|
42
|
+
# @param [Fixnum] id
|
43
|
+
# the unique ID of the document to insert
|
44
|
+
#
|
45
|
+
# @param [Hash] attributes
|
46
|
+
# a Hash of attributes
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
# the SphinxQL to insert the record
|
50
|
+
def insert(id, attributes)
|
51
|
+
into("INSERT", id, attributes)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Build a SphinxQL query to update the record identified by +id+ with the given attributes.
|
55
|
+
#
|
56
|
+
# @param [Fixnum] id
|
57
|
+
# the unique ID of the document to update
|
58
|
+
#
|
59
|
+
# @param [Hash] attributes
|
60
|
+
# a Hash of attributes
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
# the SphinxQL to update the record
|
64
|
+
def update(id, attributes)
|
65
|
+
[
|
66
|
+
"UPDATE #{@index_name} SET",
|
67
|
+
update_attributes(attributes),
|
68
|
+
"WHERE id = #{Connection.quote(id)}"
|
69
|
+
].join(" ")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Build a SphinxQL query to replace the record identified by +id+ with the given attributes.
|
73
|
+
#
|
74
|
+
# @param [Fixnum] id
|
75
|
+
# the unique ID of the document to replace
|
76
|
+
#
|
77
|
+
# @param [Hash] attributes
|
78
|
+
# a Hash of attributes
|
79
|
+
#
|
80
|
+
# @return [String]
|
81
|
+
# the SphinxQL to replace the record
|
82
|
+
def replace(id, attributes)
|
83
|
+
into("REPLACE", id, attributes)
|
84
|
+
end
|
85
|
+
|
25
86
|
private
|
26
87
|
|
27
88
|
def from
|
28
89
|
"SELECT * FROM #{@index_name}"
|
29
90
|
end
|
30
91
|
|
92
|
+
def into(type, id, attributes)
|
93
|
+
[
|
94
|
+
type,
|
95
|
+
"INTO #{@index_name}",
|
96
|
+
"(#{([:id] + attributes.keys).join(', ')})",
|
97
|
+
"VALUES",
|
98
|
+
"(#{([id] + attributes.values).map { |v| Connection.quote(v) }.join(', ')})"
|
99
|
+
].join(" ")
|
100
|
+
end
|
101
|
+
|
31
102
|
def conditions(query, filters)
|
32
103
|
exprs = []
|
33
|
-
exprs << "MATCH(#{
|
104
|
+
exprs << "MATCH(#{Connection.quote(query)})" unless query.empty?
|
105
|
+
exprs += attribute_conditions(filters)
|
34
106
|
"WHERE " << exprs.join(" AND ") if exprs.any?
|
35
107
|
end
|
36
108
|
|
109
|
+
def attribute_conditions(filters)
|
110
|
+
filters \
|
111
|
+
.reject { |k, v| [:limit, :offset, :order].include?(k.to_sym) } \
|
112
|
+
.map { |k, v| "#{k} #{Comparison.of(v)}" }
|
113
|
+
end
|
114
|
+
|
115
|
+
def update_attributes(attributes)
|
116
|
+
attributes \
|
117
|
+
.map { |k, v| "#{k} = #{Connection.quote(v)}" } \
|
118
|
+
.join(", ")
|
119
|
+
end
|
120
|
+
|
121
|
+
def order_by(filters)
|
122
|
+
return unless filters.key?(:order)
|
123
|
+
|
124
|
+
[
|
125
|
+
"ORDER BY",
|
126
|
+
Array(filters[:order]).map { |k, dir| "#{k} #{dir ? dir.to_s.upcase : 'ASC'}" }.join(", ")
|
127
|
+
].join(" ")
|
128
|
+
end
|
129
|
+
|
37
130
|
def limits(filters)
|
38
131
|
"LIMIT #{filters[:offset].to_i}, #{filters[:limit].to_i}" if filters.key?(:limit)
|
39
132
|
end
|
data/lib/oedipus/version.rb
CHANGED
data/lib/oedipus.rb
CHANGED
@@ -8,18 +8,39 @@
|
|
8
8
|
##
|
9
9
|
|
10
10
|
require "oedipus/version"
|
11
|
+
|
12
|
+
require "oedipus/oedipus"
|
13
|
+
|
14
|
+
require "oedipus/comparison"
|
15
|
+
require "oedipus/comparison/equal"
|
16
|
+
require "oedipus/comparison/not_equal"
|
17
|
+
require "oedipus/comparison/between"
|
18
|
+
require "oedipus/comparison/outside"
|
19
|
+
require "oedipus/comparison/in"
|
20
|
+
require "oedipus/comparison/not_in"
|
21
|
+
require "oedipus/comparison/gte"
|
22
|
+
require "oedipus/comparison/gt"
|
23
|
+
require "oedipus/comparison/lte"
|
24
|
+
require "oedipus/comparison/lt"
|
25
|
+
require "oedipus/comparison/not"
|
26
|
+
require "oedipus/comparison/shortcuts"
|
27
|
+
|
11
28
|
require "oedipus/query_builder"
|
29
|
+
|
30
|
+
require "oedipus/connection_error"
|
12
31
|
require "oedipus/connection"
|
32
|
+
|
13
33
|
require "oedipus/index"
|
14
|
-
require "oedipus/mysql/client"
|
15
34
|
|
16
35
|
module Oedipus
|
36
|
+
extend Comparison::Shortcuts
|
37
|
+
|
17
38
|
class << self
|
18
39
|
# Connect to Sphinx running SphinxQL.
|
19
40
|
#
|
20
41
|
# @example
|
21
42
|
# c = Oedipus.connect("localhost:9306")
|
22
|
-
# c = Oedipus.connect(:
|
43
|
+
# c = Oedipus.connect(host: "localhost", port: 9306)
|
23
44
|
#
|
24
45
|
# @param [String] server
|
25
46
|
# a 'hostname:port' string
|
@@ -31,11 +52,7 @@ module Oedipus
|
|
31
52
|
# a client connected to SphinxQL
|
32
53
|
def connect(options)
|
33
54
|
# TODO: Add pooling
|
34
|
-
Connection.new(
|
35
|
-
options.kind_of?(String) ?
|
36
|
-
Hash[ [:host, :port].zip(options.split(":")) ] :
|
37
|
-
options
|
38
|
-
)
|
55
|
+
Connection.new(options)
|
39
56
|
end
|
40
57
|
end
|
41
58
|
end
|
data/oedipus.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["chris@w3style.co.uk"]
|
10
10
|
s.homepage = "https://github.com/d11wtq/oedipus"
|
11
11
|
s.summary = "Sphinx 2 Search Client for Ruby"
|
12
|
-
s.description = <<-DESC.
|
12
|
+
s.description = <<-DESC.gsub(/^ {4}/m, "")
|
13
13
|
Oedipus brings full support for Sphinx 2 to Ruby:
|
14
14
|
|
15
15
|
* real-time indexes
|
@@ -25,11 +25,10 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.rubyforge_project = "oedipus"
|
26
26
|
|
27
27
|
s.files = `git ls-files`.split("\n")
|
28
|
-
s.test_files = `git ls-files --
|
29
|
-
s.
|
28
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
29
|
+
s.extensions = ["ext/oedipus/extconf.rb"]
|
30
30
|
s.require_paths = ["lib"]
|
31
31
|
|
32
|
-
s.add_runtime_dependency "ruby-mysql"
|
33
|
-
|
34
32
|
s.add_development_dependency "rspec"
|
33
|
+
s.add_development_dependency "rake-compiler"
|
35
34
|
end
|
@@ -0,0 +1,58 @@
|
|
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::Connection do
|
13
|
+
include Oedipus::TestHarness
|
14
|
+
|
15
|
+
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
16
|
+
|
17
|
+
describe "#initialize" do
|
18
|
+
context "with a hosname:port string" do
|
19
|
+
context "on successful connection" do
|
20
|
+
it "returns the connection" do
|
21
|
+
Oedipus::Connection.new(searchd_host.values.join(":")).should be_a_kind_of(Oedipus::Connection)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "on failed connection" do
|
26
|
+
it "raises an error" do
|
27
|
+
expect {
|
28
|
+
Oedipus::Connection.new("127.0.0.1:45346138")
|
29
|
+
}.to raise_error(Oedipus::ConnectionError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with an options Hash" do
|
35
|
+
context "on successful connection" do
|
36
|
+
it "returns the connection" do
|
37
|
+
Oedipus::Connection.new(searchd_host).should be_a_kind_of(Oedipus::Connection)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "on failed connection" do
|
42
|
+
it "raises an error" do
|
43
|
+
expect {
|
44
|
+
Oedipus::Connection.new(:host => "127.0.0.1", :port => 45346138)
|
45
|
+
}.to raise_error(Oedipus::ConnectionError)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#[]" do
|
52
|
+
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
53
|
+
|
54
|
+
it "returns an index" do
|
55
|
+
conn[:posts_rt].should be_a_kind_of(Oedipus::Index)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|