Sphincter 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +5 -0
- data/LICENSE.txt +27 -0
- data/Manifest.txt +16 -0
- data/README.txt +132 -0
- data/Rakefile +21 -0
- data/lib/sphincter.rb +102 -0
- data/lib/sphincter/association_searcher.rb +22 -0
- data/lib/sphincter/configure.rb +380 -0
- data/lib/sphincter/search.rb +173 -0
- data/lib/sphincter/search_stub.rb +60 -0
- data/lib/sphincter/tasks.rb +64 -0
- data/test/sphincter_test_case.rb +190 -0
- data/test/test_sphincter_association_searcher.rb +39 -0
- data/test/test_sphincter_configure.rb +318 -0
- data/test/test_sphincter_search.rb +100 -0
- data/test/test_sphincter_search_stub.rb +50 -0
- metadata +100 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test/sphincter_test_case'
|
2
|
+
require 'sphincter/association_searcher'
|
3
|
+
|
4
|
+
class TestSphincterAssociationSearcher < SphincterTestCase
|
5
|
+
|
6
|
+
class Proxy
|
7
|
+
include Sphincter::AssociationSearcher
|
8
|
+
|
9
|
+
attr_accessor :reflection
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@reflection = SphincterTestCase::Reflection.new :has_many, 'other'
|
13
|
+
klass = Object.new
|
14
|
+
def klass.search_args() @search_args end
|
15
|
+
def klass.search(*args) @search_args = args; :searched end
|
16
|
+
@reflection.klass = klass
|
17
|
+
end
|
18
|
+
|
19
|
+
def proxy_reflection()
|
20
|
+
@reflection
|
21
|
+
end
|
22
|
+
|
23
|
+
def proxy_owner()
|
24
|
+
SphincterTestCase::Other.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_search
|
29
|
+
proxy = Proxy.new
|
30
|
+
|
31
|
+
results = proxy.search 'words'
|
32
|
+
|
33
|
+
assert_equal :searched, results
|
34
|
+
assert_equal ['words', { :conditions => { 'other_id' => 42 } } ],
|
35
|
+
proxy.proxy_reflection.klass.search_args
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'test/sphincter_test_case'
|
2
|
+
require 'sphincter/configure'
|
3
|
+
|
4
|
+
class TestSphincterConfigure < SphincterTestCase
|
5
|
+
|
6
|
+
DEFAULT_GET_CONF_EXPECTED = {
|
7
|
+
"mysql" => {
|
8
|
+
"sql_query_pre" => [
|
9
|
+
"SET SESSION group_concat_max_len = 65535",
|
10
|
+
"SET NAMES utf8",
|
11
|
+
]
|
12
|
+
},
|
13
|
+
"sphincter" => {
|
14
|
+
"address" => "127.0.0.1",
|
15
|
+
"path" => "sphinx/RAILS_ENV",
|
16
|
+
"per_page" => 10,
|
17
|
+
"port" => 3312,
|
18
|
+
},
|
19
|
+
"index" => {
|
20
|
+
"charset_type" => "utf-8",
|
21
|
+
"docinfo" => "extern",
|
22
|
+
"min_word_len" => 1,
|
23
|
+
"morphology" => "stem_en",
|
24
|
+
"stopwords" => "",
|
25
|
+
},
|
26
|
+
"source" => {
|
27
|
+
"index_html_attrs" => "",
|
28
|
+
"sql_query_post" => "",
|
29
|
+
"sql_range_step" => 20000,
|
30
|
+
"strip_html" => 0,
|
31
|
+
},
|
32
|
+
"indexer" => {
|
33
|
+
"mem_limit" => "32M",
|
34
|
+
},
|
35
|
+
"searchd" => {
|
36
|
+
"address" => "127.0.0.1",
|
37
|
+
"log" => "log/sphinx/searchd.RAILS_ENV.log",
|
38
|
+
"max_children" => 30,
|
39
|
+
"max_matches" => 1000,
|
40
|
+
"pid_file" => "sphinx/RAILS_ENV/searchd.pid",
|
41
|
+
"port" => 3312,
|
42
|
+
"query_log" => "log/sphinx/query.RAILS_ENV.log",
|
43
|
+
"read_timeout" => 5,
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
def test_self_configure
|
48
|
+
expected = <<-EOF
|
49
|
+
indexer
|
50
|
+
{
|
51
|
+
mem_limit = 32M
|
52
|
+
}
|
53
|
+
|
54
|
+
searchd
|
55
|
+
{
|
56
|
+
address = 127.0.0.1
|
57
|
+
log = log/sphinx/searchd.RAILS_ENV.log
|
58
|
+
max_children = 30
|
59
|
+
max_matches = 1000
|
60
|
+
pid_file = sphinx/RAILS_ENV/searchd.pid
|
61
|
+
port = 3312
|
62
|
+
query_log = log/sphinx/query.RAILS_ENV.log
|
63
|
+
read_timeout = 5
|
64
|
+
}
|
65
|
+
EOF
|
66
|
+
|
67
|
+
Sphincter::Configure.configure
|
68
|
+
|
69
|
+
assert_equal expected, File.read(Sphincter::Configure.sphinx_conf)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_self_deep_merge
|
73
|
+
h1 = { 'x' => { 'm' => 0 },
|
74
|
+
'y' => { 'a' => 1, 'b' => 2 } }
|
75
|
+
h2 = { 'y' => { 'b' => -2, 'c' => -3 },
|
76
|
+
'z' => { 'm' => 0 } }
|
77
|
+
result = Sphincter::Configure.deep_merge h1, h2
|
78
|
+
|
79
|
+
expected = {
|
80
|
+
'x' => { 'm' => 0 },
|
81
|
+
'y' => { 'a' => 1, 'b' => -2, 'c' => -3 },
|
82
|
+
'z' => { 'm' => 0 },
|
83
|
+
}
|
84
|
+
|
85
|
+
assert_equal expected, result
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_self_get_conf
|
89
|
+
expected = DEFAULT_GET_CONF_EXPECTED
|
90
|
+
|
91
|
+
assert_equal expected, Sphincter::Configure.get_conf
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_self_get_conf_app_conf
|
95
|
+
FileUtils.mkdir_p 'config'
|
96
|
+
File.open 'config/sphincter.yml', 'w' do |fp|
|
97
|
+
fp.puts "sphincter:\n port: 3313"
|
98
|
+
end
|
99
|
+
|
100
|
+
expected = util_deep_clone DEFAULT_GET_CONF_EXPECTED
|
101
|
+
expected['sphincter']['port'] = 3313
|
102
|
+
expected['searchd']['port'] = 3313
|
103
|
+
|
104
|
+
assert_equal expected, Sphincter::Configure.get_conf
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_self_get_conf_env_conf
|
108
|
+
FileUtils.mkdir_p 'config/environments'
|
109
|
+
File.open 'config/sphincter.yml', 'w' do |fp|
|
110
|
+
fp.puts "sphincter:\n port: 3313"
|
111
|
+
end
|
112
|
+
File.open "config/environments/sphincter.#{RAILS_ENV}.yml", 'w' do |fp|
|
113
|
+
fp.puts "sphincter:\n port: 3314"
|
114
|
+
end
|
115
|
+
|
116
|
+
expected = util_deep_clone DEFAULT_GET_CONF_EXPECTED
|
117
|
+
expected['sphincter']['port'] = 3314
|
118
|
+
expected['searchd']['port'] = 3314
|
119
|
+
|
120
|
+
assert_equal expected, Sphincter::Configure.get_conf
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_self_get_conf_from
|
124
|
+
assert_equal Hash.new, Sphincter::Configure.get_conf_from('/nonexistent')
|
125
|
+
|
126
|
+
File.open 'foo.yml', 'w' do |fp| fp.puts "foo:\n bar" end
|
127
|
+
|
128
|
+
assert_equal({'foo' => 'bar'}, Sphincter::Configure.get_conf_from('foo.yml'))
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_self_get_sources
|
132
|
+
Sphincter::Search.indexes[Model] << { :fields => %w[text] }
|
133
|
+
|
134
|
+
expected = {
|
135
|
+
"models" => {
|
136
|
+
"strip_html" => 0,
|
137
|
+
"sql_group_column" => ["sphincter_index_id"],
|
138
|
+
"sql_query_range" => "SELECT MIN(`id`), MAX(`id`) FROM models",
|
139
|
+
"sql_query_info" =>
|
140
|
+
"SELECT * FROM models WHERE models.`id` = (($id - 0) / 1)",
|
141
|
+
"sql_date_column" => [],
|
142
|
+
"sql_query" =>
|
143
|
+
"SELECT (models.`id` * 1 + 0) AS `id`, " \
|
144
|
+
"0 AS sphincter_index_id, " \
|
145
|
+
"`models` AS klass, "\
|
146
|
+
"models.`text` AS `text` " \
|
147
|
+
"FROM models WHERE models.`id` >= $start AND " \
|
148
|
+
"models.`id` <= $end"
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
assert_equal expected, Sphincter::Configure.get_sources
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_self_get_sources_field
|
156
|
+
source_conf = { 'sql_group_column' => [], 'sql_date_column' => [] }
|
157
|
+
klass = Model
|
158
|
+
|
159
|
+
fields = []
|
160
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
161
|
+
'date')
|
162
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
163
|
+
'datetime')
|
164
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
165
|
+
'boolean')
|
166
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
167
|
+
'integer')
|
168
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
169
|
+
'string')
|
170
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
171
|
+
'time')
|
172
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
173
|
+
'timestamp')
|
174
|
+
fields << Sphincter::Configure.get_sources_field(source_conf, klass,
|
175
|
+
'text')
|
176
|
+
|
177
|
+
expected_fields = [
|
178
|
+
"UNIX_TIMESTAMP(models.`date`) AS `date`",
|
179
|
+
"UNIX_TIMESTAMP(models.`datetime`) AS `datetime`",
|
180
|
+
"models.`boolean` AS `boolean`",
|
181
|
+
"models.`integer` AS `integer`",
|
182
|
+
"models.`string` AS `string`",
|
183
|
+
"UNIX_TIMESTAMP(models.`time`) AS `time`",
|
184
|
+
"UNIX_TIMESTAMP(models.`timestamp`) AS `timestamp`",
|
185
|
+
"models.`text` AS `text`"
|
186
|
+
]
|
187
|
+
|
188
|
+
assert_equal expected_fields, fields
|
189
|
+
|
190
|
+
assert_equal %w[boolean integer], source_conf['sql_group_column']
|
191
|
+
assert_equal %w[date datetime time timestamp],
|
192
|
+
source_conf['sql_date_column']
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_self_get_db_conf
|
196
|
+
expected = {
|
197
|
+
'type' => 'mysql',
|
198
|
+
'sql_host' => 'host',
|
199
|
+
'sql_pass' => 'password',
|
200
|
+
'sql_db' => 'database',
|
201
|
+
'sql_user' => 'username',
|
202
|
+
'sql_sock' => 'socket',
|
203
|
+
}
|
204
|
+
|
205
|
+
assert_equal expected, Sphincter::Configure.get_db_conf
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_self_index_count
|
209
|
+
Sphincter::Search.indexes[Object] << { :fields => %w[title body] }
|
210
|
+
Sphincter::Search.indexes[Object] << {
|
211
|
+
:fields => %w[title body], :name => 'foo'
|
212
|
+
}
|
213
|
+
|
214
|
+
assert_equal 2, Sphincter::Configure.index_count
|
215
|
+
|
216
|
+
expected = {
|
217
|
+
Object => [
|
218
|
+
{ :index_id => 0, :fields => %w[title body] },
|
219
|
+
{ :index_id => 1, :fields => %w[title body], :name => 'foo' },
|
220
|
+
],
|
221
|
+
}
|
222
|
+
|
223
|
+
assert_equal expected, Sphincter::Search.indexes
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_self_section
|
227
|
+
heading = 'searchd'
|
228
|
+
data = {
|
229
|
+
'array' => %w[value1 value2],
|
230
|
+
'empty' => '',
|
231
|
+
'nil' => nil,
|
232
|
+
'string' => 'value',
|
233
|
+
}
|
234
|
+
|
235
|
+
expected = <<-EOF.strip
|
236
|
+
searchd
|
237
|
+
{
|
238
|
+
array = value1
|
239
|
+
array = value2
|
240
|
+
empty =
|
241
|
+
nil =
|
242
|
+
string = value
|
243
|
+
}
|
244
|
+
EOF
|
245
|
+
|
246
|
+
assert_equal expected, Sphincter::Configure.section(heading, data)
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_self_sphinx_conf
|
250
|
+
assert_equal File.join(RAILS_ROOT, 'sphinx/RAILS_ENV/sphinx.conf'),
|
251
|
+
Sphincter::Configure.sphinx_conf
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_self_sphinx_dir
|
255
|
+
assert_equal File.join(RAILS_ROOT, 'sphinx/RAILS_ENV'),
|
256
|
+
Sphincter::Configure.sphinx_dir
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_self_write_configuration
|
260
|
+
conf = Hash.new { |h,k| h[k] = {} }
|
261
|
+
sources = Hash.new { |h,k| h[k] = {} }
|
262
|
+
|
263
|
+
conf['sphincter']['path'] = 'sphinx/development'
|
264
|
+
conf['source']['key1'] = 'value1'
|
265
|
+
conf['index']['key1'] = 'value1'
|
266
|
+
|
267
|
+
sources['source_1']['key2'] = 'value2'
|
268
|
+
|
269
|
+
sources['source_2']['key1'] = 'value3'
|
270
|
+
sources['source_2']['key2'] = 'value4'
|
271
|
+
|
272
|
+
expected = <<-EOF
|
273
|
+
indexer
|
274
|
+
{
|
275
|
+
}
|
276
|
+
|
277
|
+
searchd
|
278
|
+
{
|
279
|
+
}
|
280
|
+
|
281
|
+
source source_1
|
282
|
+
{
|
283
|
+
key1 = value1
|
284
|
+
key2 = value2
|
285
|
+
}
|
286
|
+
|
287
|
+
index source_1
|
288
|
+
{
|
289
|
+
key1 = value1
|
290
|
+
path = #{Sphincter::Configure.sphinx_dir}/source_1
|
291
|
+
source = source_1
|
292
|
+
}
|
293
|
+
|
294
|
+
source source_2
|
295
|
+
{
|
296
|
+
key1 = value3
|
297
|
+
key2 = value4
|
298
|
+
}
|
299
|
+
|
300
|
+
index source_2
|
301
|
+
{
|
302
|
+
key1 = value1
|
303
|
+
path = #{Sphincter::Configure.sphinx_dir}/source_2
|
304
|
+
source = source_2
|
305
|
+
}
|
306
|
+
EOF
|
307
|
+
|
308
|
+
Sphincter::Configure.write_configuration conf, sources
|
309
|
+
|
310
|
+
assert_equal expected, File.read(Sphincter::Configure.sphinx_conf)
|
311
|
+
end
|
312
|
+
|
313
|
+
def util_deep_clone(obj)
|
314
|
+
Marshal.load Marshal.dump(obj)
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'test/sphincter_test_case'
|
2
|
+
require 'sphincter/search'
|
3
|
+
|
4
|
+
SphincterTestCase::Model.extend Sphincter::Search
|
5
|
+
|
6
|
+
class TestSphincterSearch < SphincterTestCase
|
7
|
+
|
8
|
+
def test_self_indexes
|
9
|
+
assert_equal [], Sphincter::Search.indexes[Model]
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_add_index
|
13
|
+
Model.add_index :fields => %w[text]
|
14
|
+
|
15
|
+
assert_equal [{ :fields => %w[text other_id] }],
|
16
|
+
Sphincter::Search.indexes[Model]
|
17
|
+
|
18
|
+
other_belongs_to = Other.reflections.first
|
19
|
+
other_has_many = Other.reflections.last
|
20
|
+
|
21
|
+
assert_equal({}, other_belongs_to.options, 'Other belongs_to')
|
22
|
+
assert_equal({ :extend => [Sphincter::AssociationSearcher] },
|
23
|
+
other_has_many.options, 'Other has_many')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_sphincter_convert_values
|
27
|
+
assert_equal [0, 1], Model.sphincter_convert_values([false, true])
|
28
|
+
|
29
|
+
now = Time.at 999_932_400
|
30
|
+
expected = [999_932_400]
|
31
|
+
|
32
|
+
assert_equal expected,
|
33
|
+
Model.sphincter_convert_values([999_932_400])
|
34
|
+
assert_equal expected,
|
35
|
+
Model.sphincter_convert_values([now])
|
36
|
+
assert_equal expected,
|
37
|
+
Model.sphincter_convert_values([Date.parse(now.to_s)])
|
38
|
+
assert_equal expected,
|
39
|
+
Model.sphincter_convert_values([DateTime.parse(now.to_s)])
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_search
|
43
|
+
Model.add_index :fields => %w[text]
|
44
|
+
Sphincter::Configure.get_conf['sphincter']['host'] = 'localhost'
|
45
|
+
Sphincter::Configure.get_conf['sphincter']['port'] = 3312
|
46
|
+
|
47
|
+
results = Model.search 'words'
|
48
|
+
|
49
|
+
assert_equal [11, 13, 12], results.records
|
50
|
+
assert_equal 3, results.total
|
51
|
+
assert_equal 10, results.per_page
|
52
|
+
|
53
|
+
assert_equal 'localhost', Sphinx::Client.last_client.host
|
54
|
+
assert_equal 3312, Sphinx::Client.last_client.port
|
55
|
+
assert_equal 'words', Sphinx::Client.last_client.query
|
56
|
+
assert_equal 'models', Sphinx::Client.last_client.index
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_search_between
|
60
|
+
Model.add_index :fields => %w[text]
|
61
|
+
|
62
|
+
now = Time.now
|
63
|
+
ago = now - 3600
|
64
|
+
between = {
|
65
|
+
:created_at => [ago, now]
|
66
|
+
}
|
67
|
+
|
68
|
+
Model.search 'words', :between => between
|
69
|
+
|
70
|
+
expected = { 'created_at' => [:range, ago.to_i, now.to_i] }
|
71
|
+
|
72
|
+
assert_equal expected, Sphinx::Client.last_client.filters
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_search_conditions
|
76
|
+
Model.add_index :fields => %w[text]
|
77
|
+
|
78
|
+
now = Time.now
|
79
|
+
conditions = {
|
80
|
+
:some_id => 1,
|
81
|
+
:other_id => [1, 2],
|
82
|
+
:boolean => [true, false],
|
83
|
+
}
|
84
|
+
|
85
|
+
Model.search 'words', :conditions => conditions
|
86
|
+
|
87
|
+
expected = { 'some_id' => [1], 'other_id' => [1, 2], 'boolean' => [1, 0] }
|
88
|
+
|
89
|
+
assert_equal expected, Sphinx::Client.last_client.filters
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_search_index
|
93
|
+
Model.add_index :fields => %w[text]
|
94
|
+
|
95
|
+
Model.search 'words', :index => 'other'
|
96
|
+
|
97
|
+
assert_equal 'other', Sphinx::Client.last_client.index
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|