minisphinx 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/.gitignore +3 -0
- data/.rbenv-gemsets +1 -0
- data/.rbenv-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README +9 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/examples/default.conf +14 -0
- data/lib/minisphinx/charset.rb +40 -0
- data/lib/minisphinx.rb +243 -0
- data/lib/mysqlcompat/concat_ws.sql +36 -0
- data/lib/mysqlcompat/crc32.sql +16 -0
- data/lib/mysqlcompat/group_concat.sql +24 -0
- data/lib/mysqlcompat/hex_to_int.sql +16 -0
- data/lib/mysqlcompat/unix_timestamp.sql +13 -0
- data/minisphinx.gemspec +25 -0
- data/test/minisphinx_test.rb +60 -0
- data/test/test_helper.rb +23 -0
- metadata +176 -0
data/.gitignore
ADDED
data/.rbenv-gemsets
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
minisphinx
|
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ree-1.8.7-2012.02
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Justin Balthrop
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
indexer {
|
2
|
+
mem_limit = 1024M
|
3
|
+
}
|
4
|
+
|
5
|
+
searchd {
|
6
|
+
read_timeout = 5
|
7
|
+
max_children = 300
|
8
|
+
log = <%= RAILS_ROOT %>/log/searchd.log
|
9
|
+
max_matches = 100000
|
10
|
+
query_log = <%= RAILS_ROOT %>/log/query.log
|
11
|
+
seamless_rotate = 1
|
12
|
+
listen = 0.0.0.0:3313
|
13
|
+
pid_file = <%= RAILS_ROOT %>/log/searchd.pid
|
14
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'deep_hash'
|
2
|
+
|
3
|
+
module Minisphinx
|
4
|
+
class Charset
|
5
|
+
attr_reader :name, :only, :except
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
opts = {:name => opts} if opts.kind_of?(Symbol)
|
9
|
+
@name = opts[:name]
|
10
|
+
@only = Array(opts[:only]).to_set if opts[:only]
|
11
|
+
@except = Array(opts[:except]).to_set if opts[:except]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.charset
|
15
|
+
@charset ||= YAML.load_file(RAILS_ROOT + '/config/sphinx/charset.yml')
|
16
|
+
end
|
17
|
+
|
18
|
+
MAX_PER_LINE = 50
|
19
|
+
def to_s
|
20
|
+
chars = {}
|
21
|
+
|
22
|
+
self.class.charset[name.to_s].each do |charset|
|
23
|
+
charset.each do |group, charset|
|
24
|
+
next if except and except.include?(group.to_sym)
|
25
|
+
next if only and not only.include?(group.to_sym)
|
26
|
+
charset.split(',').each do |char|
|
27
|
+
key = char.strip.split('->').first
|
28
|
+
chars[key] ||= char
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
lines = []
|
34
|
+
chars.values.flatten.sort.each_slice(MAX_PER_LINE) do |line_chars|
|
35
|
+
lines << line_chars.join(', ')
|
36
|
+
end
|
37
|
+
lines.join(", \\\n")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/minisphinx.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
module Minisphinx
|
2
|
+
def sphinx_source(name, opts)
|
3
|
+
opts[:model_class] ||= self
|
4
|
+
Minisphinx.sources << Source.new(name, opts)
|
5
|
+
end
|
6
|
+
|
7
|
+
def sphinx_index(name, opts = {})
|
8
|
+
Minisphinx.indexes << Index.new(name, opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.indexes; @indexes ||= []; end
|
12
|
+
def self.sources; @sources ||= []; end
|
13
|
+
|
14
|
+
def self.configure(opts)
|
15
|
+
template = ['default', RAILS_ENV].collect {|base| RAILS_ROOT + "/config/sphinx/#{base}.conf"}.detect {|file| File.exists?(file)}
|
16
|
+
|
17
|
+
File.open(opts[:path] + '/sphinx.conf', 'w') do |file|
|
18
|
+
file << "# Autogenerated by minisphinx at #{Time.now}\n"
|
19
|
+
file << ERB.new(IO.read(template)).result(binding) + "\n" if template
|
20
|
+
file << "# Sources\n\n"
|
21
|
+
sources.each do |source|
|
22
|
+
file << source.configuration(opts)
|
23
|
+
end
|
24
|
+
file << "# Indexes\n\n"
|
25
|
+
indexes.each do |index|
|
26
|
+
file << index.configuration(opts)
|
27
|
+
end
|
28
|
+
end unless opts[:write] == false
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.config_block(head, lines)
|
32
|
+
"#{head}\n{\n #{lines.flatten.compact.join("\n ")}\n}\n\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
class Source
|
36
|
+
attr_reader :name, :model_class, :fetch_key, :db, :fields, :joins, :config, :delta_field
|
37
|
+
|
38
|
+
def initialize(name, opts)
|
39
|
+
@name = name
|
40
|
+
@delta_field = opts.delete(:delta_field)
|
41
|
+
@delta_min = opts.delete(:delta_min)
|
42
|
+
@fetch_key = opts.delete(:fetch_key) || 'id'
|
43
|
+
|
44
|
+
@fields = initialize_fields(opts)
|
45
|
+
@joins = Array(opts.delete(:joins)) + Array(opts.delete(:join))
|
46
|
+
(opts.delete(:include) || []).each do |include_opts|
|
47
|
+
@fields.concat initialize_fields(include_opts)
|
48
|
+
@joins.concat Array(include_opts.delete(:joins)) + Array(include_opts.delete(:join))
|
49
|
+
end
|
50
|
+
raise 'at least one field required' if @fields.empty?
|
51
|
+
@fields.sort!
|
52
|
+
|
53
|
+
@model_class = opts.delete(:model_class)
|
54
|
+
@table_name = opts.delete(:table_name)
|
55
|
+
@db = opts.delete(:db) || model_class.connection.config
|
56
|
+
@db = ActiveRecord::Base.configurations["#{db}_#{RAILS_ENV}"] unless db.kind_of?(Hash)
|
57
|
+
|
58
|
+
@config = self.class.config.merge(opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
def table_name
|
62
|
+
@table_name ||= model_class.table_name
|
63
|
+
end
|
64
|
+
|
65
|
+
def type
|
66
|
+
db[:adapter] == 'postgresql' ? 'pgsql' : db[:adapter]
|
67
|
+
end
|
68
|
+
|
69
|
+
def configuration(opts)
|
70
|
+
str = Minisphinx.config_block("source #{name}_base", [
|
71
|
+
"type = #{type}",
|
72
|
+
config.collect do |key, value|
|
73
|
+
"sql_#{key} = #{value}"
|
74
|
+
end,
|
75
|
+
"sql_db = #{db[:database]}",
|
76
|
+
"sql_host = #{db[:host]}",
|
77
|
+
"sql_pass = #{db[:password]}",
|
78
|
+
"sql_user = #{db[:username]}",
|
79
|
+
"sql_query_info = #{sql_query_info}",
|
80
|
+
fields.collect do |field|
|
81
|
+
"sql_attr_#{field.type} = #{field.name}" if field.type != :text
|
82
|
+
end,
|
83
|
+
])
|
84
|
+
str << Minisphinx.config_block("source #{name} : #{name}_base", [
|
85
|
+
"sql_query_range = #{sql_query_range}",
|
86
|
+
"sql_query = #{sql_query}",
|
87
|
+
])
|
88
|
+
if delta_field
|
89
|
+
str << Minisphinx.config_block("source #{name}_delta : #{name}_base", [
|
90
|
+
"sql_query = #{sql_query_delta(opts[:delta_min])}",
|
91
|
+
])
|
92
|
+
end
|
93
|
+
str
|
94
|
+
end
|
95
|
+
|
96
|
+
def sql_query_range
|
97
|
+
"SELECT coalesce(MIN(#{fetch_key}),1)::bigint, coalesce(MAX(#{fetch_key}),1)::bigint FROM #{table_name}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def sql_query
|
101
|
+
"SELECT #{table_name}.id AS doc_id, #{fields.join(', ')} " <<
|
102
|
+
"FROM #{table_name} #{joins.join(' ')} WHERE #{fetch_key} >= $start AND #{fetch_key} <= $end"
|
103
|
+
end
|
104
|
+
|
105
|
+
def sql_query_delta(delta_min)
|
106
|
+
delta_min = "'#{delta_min.to_s(:db)}'" if delta_min.kind_of?(Time) or delta_min.kind_of?(Date)
|
107
|
+
"SELECT #{table_name}.id AS doc_id, #{fields.join(', ')} " <<
|
108
|
+
"FROM #{table_name} #{joins.join(' ')} WHERE #{delta_field} >= #{delta_min}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def sql_query_info
|
112
|
+
"SELECT * FROM #{table_name} WHERE id = $id"
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.config
|
116
|
+
@config ||= {
|
117
|
+
:range_step => 5000,
|
118
|
+
:ranged_throttle => 0,
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def initialize_fields(opts)
|
125
|
+
(opts.delete(:fields) || []).collect do |field_opts|
|
126
|
+
field_opts = {:field => field_opts} unless field_opts.kind_of?(Hash)
|
127
|
+
field_opts[:table_name] = opts[:table_name]
|
128
|
+
field_opts[:model_class] = opts[:model_class]
|
129
|
+
|
130
|
+
if field_opts[:sortable] and not [nil, :text].include?(field_opts[:type])
|
131
|
+
raise ":sortable only permitted for :text fields, #{field_opts[:field]} is of type #{field_opts[:type].inspect}"
|
132
|
+
end
|
133
|
+
|
134
|
+
[Field.new(field_opts), field_opts[:sortable] && Field.new(field_opts.merge(:type => :sortable, :suffix => 'sortable'))]
|
135
|
+
end.flatten.compact
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class Field
|
140
|
+
attr_reader :model_class, :field, :name, :type
|
141
|
+
|
142
|
+
TYPE_MAP = {
|
143
|
+
:integer => :uint,
|
144
|
+
:decimal => :float,
|
145
|
+
:boolean => :bool,
|
146
|
+
:date => :timestamp,
|
147
|
+
:datetime => :timestamp,
|
148
|
+
:timestamp => :timestamp,
|
149
|
+
:string => :text,
|
150
|
+
:sortable => :string,
|
151
|
+
}
|
152
|
+
|
153
|
+
def initialize(opts)
|
154
|
+
@model_class = opts[:model_class]
|
155
|
+
@table_name = opts[:table_name]
|
156
|
+
|
157
|
+
@type = opts[:type]
|
158
|
+
@name = opts[:as] || opts[:field]
|
159
|
+
@name = "#{name}_#{opts[:suffix]}" if opts[:suffix]
|
160
|
+
@field = opts[:field]
|
161
|
+
@field = "#{table_name}.#{field}" if not field.index(/[\(.\s]/)
|
162
|
+
@field = opts[:transform] % field if opts[:transform]
|
163
|
+
@field = "UNIX_TIMESTAMP(#{field})" if type == :timestamp
|
164
|
+
end
|
165
|
+
|
166
|
+
def table_name
|
167
|
+
@table_name ||= model_class ? model_class.table_name : nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def <=>(other)
|
171
|
+
# Sphinx has a bug that messes up your index unless str2ordinal fields come first.
|
172
|
+
if type != other.type
|
173
|
+
(type == :str2ordinal && -1) || (other.type == :str2ordinal && 1) || (type.to_s <=> other.type.to_s)
|
174
|
+
else
|
175
|
+
name <=> other.name
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def type
|
180
|
+
if @type.nil? and model_class
|
181
|
+
column = model_class.columns_hash[name]
|
182
|
+
@type = column.sql_type == 'bigint' ? :bigint : column.type.to_sym
|
183
|
+
end
|
184
|
+
TYPE_MAP[@type] || @type || :text
|
185
|
+
end
|
186
|
+
|
187
|
+
def to_s
|
188
|
+
"#{field} AS #{name}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Index
|
193
|
+
attr_reader :name, :sources, :charset, :ngram, :config, :delta
|
194
|
+
|
195
|
+
def initialize(name, opts)
|
196
|
+
@name = name
|
197
|
+
@config = self.class.config.merge(opts)
|
198
|
+
@delta = config.delete(:delta)
|
199
|
+
@delta = "#{name}_delta" if @delta == true
|
200
|
+
@sources = Array(config.delete(:source)) + Array(config.delete(:sources))
|
201
|
+
@charset = Minisphinx::Charset.new(config.delete(:charset)) if config[:charset]
|
202
|
+
@ngram = Minisphinx::Charset.new(config.delete(:ngram)) if config[:ngram]
|
203
|
+
end
|
204
|
+
|
205
|
+
def configuration(opts)
|
206
|
+
path = opts[:path]
|
207
|
+
str = Minisphinx.config_block("index #{name}", [
|
208
|
+
sources.collect do |source|
|
209
|
+
"source = #{source}"
|
210
|
+
end,
|
211
|
+
"path = #{path}/#{name}",
|
212
|
+
config.collect do |key, value|
|
213
|
+
"#{key} = #{value}"
|
214
|
+
end,
|
215
|
+
charset && "charset_table = #{charset}",
|
216
|
+
ngram && "ngram_chars = #{ngram}",
|
217
|
+
ngram && "ngram_len = 1",
|
218
|
+
])
|
219
|
+
if delta
|
220
|
+
str << Minisphinx.config_block("index #{delta} : #{name}", [
|
221
|
+
sources.collect do |source|
|
222
|
+
"source = #{source}_delta"
|
223
|
+
end,
|
224
|
+
"path = #{path}/#{delta}",
|
225
|
+
])
|
226
|
+
end
|
227
|
+
str
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.config
|
231
|
+
@config ||= {
|
232
|
+
:charset_type => 'utf-8',
|
233
|
+
:min_word_len => 1,
|
234
|
+
:html_strip => 0,
|
235
|
+
:docinfo => 'extern',
|
236
|
+
}
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class ActiveRecord::ConnectionAdapters::AbstractAdapter
|
242
|
+
attr_reader :config
|
243
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/* Taken from ultrasphinx */
|
2
|
+
|
3
|
+
/* http://osdir.com/ml/db.postgresql.admIN/2003-08/msg00057.html */
|
4
|
+
|
5
|
+
CREATE FUNCTION MAKE_CONCAT_WS() RETURNS text AS '
|
6
|
+
declare
|
7
|
+
v_args int := 32;
|
8
|
+
v_first text := ''CREATE FUNCTION CONCAT_WS(text,text,text) RETURNS text AS ''''SELECT CASE WHEN $1 IS NULL THEN NULL WHEN $3 IS NULL THEN $2 ELSE $2 || $1 || $3 END'''' LANGUAGE sql IMMUTABLE'';
|
9
|
+
v_part1 text := ''CREATE FUNCTION CONCAT_WS(text,text'';
|
10
|
+
v_part2 text := '') RETURNS text AS ''''SELECT CONCAT_WS($1,CONCAT_WS($1,$2'';
|
11
|
+
v_part3 text := '')'''' LANGUAGE sql IMMUTABLE'';
|
12
|
+
v_sql text;
|
13
|
+
|
14
|
+
BEGIN
|
15
|
+
EXECUTE v_first;
|
16
|
+
FOR i IN 4 .. v_args loop
|
17
|
+
v_sql := v_part1;
|
18
|
+
FOR j IN 3 .. i loop
|
19
|
+
v_sql := v_sql || '',text'';
|
20
|
+
END loop;
|
21
|
+
|
22
|
+
v_sql := v_sql || v_part2;
|
23
|
+
|
24
|
+
FOR j IN 3 .. i - 1 loop
|
25
|
+
v_sql := v_sql || '',$'' || j::text;
|
26
|
+
END loop;
|
27
|
+
v_sql := v_sql || ''),$'' || i::text;
|
28
|
+
|
29
|
+
v_sql := v_sql || v_part3;
|
30
|
+
EXECUTE v_sql;
|
31
|
+
END loop;
|
32
|
+
RETURN ''OK'';
|
33
|
+
END;
|
34
|
+
' LANGUAGE 'plpgsql';
|
35
|
+
|
36
|
+
SELECT MAKE_CONCAT_WS();
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/* Taken from ultrasphinx */
|
2
|
+
|
3
|
+
/* Fake CRC32 */
|
4
|
+
|
5
|
+
CREATE FUNCTION crc32(text)
|
6
|
+
RETURNS bigint AS $$
|
7
|
+
DECLARE
|
8
|
+
tmp bigint;
|
9
|
+
BEGIN
|
10
|
+
tmp = (hex_to_int(SUBSTRING(MD5($1) FROM 1 FOR 8))::bigint);
|
11
|
+
IF tmp < 0 THEN
|
12
|
+
tmp = 4294967296 + tmp;
|
13
|
+
END IF;
|
14
|
+
return tmp;
|
15
|
+
END
|
16
|
+
$$ IMMUTABLE STRICT LANGUAGE plpgsql;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
/* Taken from ultrasphinx */
|
2
|
+
|
3
|
+
/*
|
4
|
+
mysqlcompat-1.0b3
|
5
|
+
public domain
|
6
|
+
GROUP_CONCAT()
|
7
|
+
Note: For DISTINCT and ORDER BY a subquery is required
|
8
|
+
*/
|
9
|
+
|
10
|
+
CREATE FUNCTION _group_concat(text, text)
|
11
|
+
RETURNS text AS $$
|
12
|
+
SELECT CASE
|
13
|
+
WHEN $2 IS NULL THEN COALESCE($1, ' ')
|
14
|
+
WHEN $1 IS NULL THEN $2
|
15
|
+
ELSE $1 operator(pg_catalog.||) ' ' operator(pg_catalog.||) $2
|
16
|
+
END
|
17
|
+
$$ IMMUTABLE LANGUAGE SQL;
|
18
|
+
|
19
|
+
CREATE AGGREGATE group_concat (
|
20
|
+
BASETYPE = text,
|
21
|
+
SFUNC = _group_concat,
|
22
|
+
STYPE = text
|
23
|
+
);
|
24
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/* Taken from ultrasphinx */
|
2
|
+
|
3
|
+
CREATE FUNCTION hex_to_int(varchar) RETURNS int4 AS '
|
4
|
+
DECLARE
|
5
|
+
h alias for $1;
|
6
|
+
exec varchar;
|
7
|
+
curs refcursor;
|
8
|
+
res int;
|
9
|
+
BEGIN
|
10
|
+
exec := ''SELECT x'''''' || h || ''''''::int4'';
|
11
|
+
OPEN curs FOR EXECUTE exec;
|
12
|
+
FETCH curs INTO res;
|
13
|
+
CLOSE curs;
|
14
|
+
return res;
|
15
|
+
END;'
|
16
|
+
LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/* Taken from ultrasphinx */
|
2
|
+
|
3
|
+
/*
|
4
|
+
mysqlcompat-1.0b3
|
5
|
+
public domain
|
6
|
+
modified
|
7
|
+
UNIX_TIMESTAMP(date)
|
8
|
+
*/
|
9
|
+
|
10
|
+
CREATE FUNCTION unix_timestamp(timestamp without time zone)
|
11
|
+
RETURNS bigint AS $$
|
12
|
+
SELECT EXTRACT(EPOCH FROM $1)::bigint
|
13
|
+
$$ VOLATILE LANGUAGE SQL;
|
data/minisphinx.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "minisphinx"
|
6
|
+
gem.version = IO.read('VERSION')
|
7
|
+
gem.authors = ["Justin Balthrop"]
|
8
|
+
gem.email = ["git@justinbalthrop.com"]
|
9
|
+
gem.description = %q{A simple Sphinx indexing library for Ruby}
|
10
|
+
gem.summary = gem.description
|
11
|
+
gem.homepage = "https://github.com/ninjudd/minisphinx"
|
12
|
+
|
13
|
+
gem.add_development_dependency 'shoulda', '3.0.1'
|
14
|
+
gem.add_development_dependency 'mocha'
|
15
|
+
gem.add_development_dependency 'rake'
|
16
|
+
gem.add_development_dependency 'activerecord-postgresql-adapter'
|
17
|
+
|
18
|
+
gem.add_dependency 'activerecord', '~> 2.3.9'
|
19
|
+
gem.add_dependency 'deep_hash', '0.0.2'
|
20
|
+
|
21
|
+
gem.files = `git ls-files`.split($/)
|
22
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
23
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
24
|
+
gem.require_paths = ["lib"]
|
25
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class MinisphinxTest < Test::Unit::TestCase
|
4
|
+
class CreateTables < ActiveRecord::Migration
|
5
|
+
def self.up
|
6
|
+
create_table :pets do |t|
|
7
|
+
t.column :name, :string
|
8
|
+
t.column :species, :string
|
9
|
+
t.column :breed, :string
|
10
|
+
t.column :color, :string
|
11
|
+
t.column :gender, :string
|
12
|
+
t.column :adopted, :boolean
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :pets
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Pet < ActiveRecord::Base
|
22
|
+
extend Minisphinx
|
23
|
+
|
24
|
+
def self.initialize_sphinx
|
25
|
+
sphinx_source :pets,
|
26
|
+
:fetch_key => 'public_id',
|
27
|
+
:delta_field => 'CASE WHEN master_profile THEN now() ELSE updated_at END',
|
28
|
+
:fields => [
|
29
|
+
{:field => 'name', :sortable => true},
|
30
|
+
{:field => 'species', :sortable => true},
|
31
|
+
{:field => 'breed', :sortable => true},
|
32
|
+
{:field => 'gender', :sortable => true},
|
33
|
+
{:field => 'adopted', :type => :boolean},
|
34
|
+
]
|
35
|
+
|
36
|
+
Minisphinx::Index.config[:source ] = :profiles
|
37
|
+
Minisphinx::Index.config[:delta ] = true
|
38
|
+
|
39
|
+
sphinx_index :full
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with a db connection' do
|
44
|
+
setup do
|
45
|
+
Dir.mkdir('/tmp/minisphinx-test')
|
46
|
+
CreateTables.verbose = false
|
47
|
+
CreateTables.up
|
48
|
+
end
|
49
|
+
|
50
|
+
teardown do
|
51
|
+
CreateTables.down
|
52
|
+
end
|
53
|
+
|
54
|
+
should "write config" do
|
55
|
+
Pet.initialize_sphinx
|
56
|
+
Minisphinx.configure(:path => '/tmp/minisphinx-test')
|
57
|
+
assert File.exists('/tmp/minisphinx-test/sphinx.conf')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha/setup'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
require 'active_record'
|
8
|
+
require 'minisphinx'
|
9
|
+
require 'minisphinx/charset'
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
end
|
13
|
+
|
14
|
+
RAILS_ENV = 'test'
|
15
|
+
RAILS_ROOT = '/tmp/minisphinx-test'
|
16
|
+
|
17
|
+
ActiveRecord::Base.establish_connection(
|
18
|
+
:adapter => "postgresql",
|
19
|
+
:host => "localhost",
|
20
|
+
:database => "minisphinx_test"
|
21
|
+
)
|
22
|
+
ActiveRecord::Migration.verbose = false
|
23
|
+
ActiveRecord::Base.connection.client_min_messages = 'panic'
|
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: minisphinx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Justin Balthrop
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-06-03 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - "="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 5
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 1
|
34
|
+
version: 3.0.1
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: mocha
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: rake
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: activerecord-postgresql-adapter
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
type: :development
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: activerecord
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 17
|
88
|
+
segments:
|
89
|
+
- 2
|
90
|
+
- 3
|
91
|
+
- 9
|
92
|
+
version: 2.3.9
|
93
|
+
type: :runtime
|
94
|
+
version_requirements: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: deep_hash
|
97
|
+
prerelease: false
|
98
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - "="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 27
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
- 0
|
107
|
+
- 2
|
108
|
+
version: 0.0.2
|
109
|
+
type: :runtime
|
110
|
+
version_requirements: *id006
|
111
|
+
description: A simple Sphinx indexing library for Ruby
|
112
|
+
email:
|
113
|
+
- git@justinbalthrop.com
|
114
|
+
executables: []
|
115
|
+
|
116
|
+
extensions: []
|
117
|
+
|
118
|
+
extra_rdoc_files: []
|
119
|
+
|
120
|
+
files:
|
121
|
+
- .gitignore
|
122
|
+
- .rbenv-gemsets
|
123
|
+
- .rbenv-version
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE
|
126
|
+
- README
|
127
|
+
- Rakefile
|
128
|
+
- VERSION
|
129
|
+
- examples/default.conf
|
130
|
+
- lib/minisphinx.rb
|
131
|
+
- lib/minisphinx/charset.rb
|
132
|
+
- lib/mysqlcompat/concat_ws.sql
|
133
|
+
- lib/mysqlcompat/crc32.sql
|
134
|
+
- lib/mysqlcompat/group_concat.sql
|
135
|
+
- lib/mysqlcompat/hex_to_int.sql
|
136
|
+
- lib/mysqlcompat/unix_timestamp.sql
|
137
|
+
- minisphinx.gemspec
|
138
|
+
- test/minisphinx_test.rb
|
139
|
+
- test/test_helper.rb
|
140
|
+
has_rdoc: true
|
141
|
+
homepage: https://github.com/ninjudd/minisphinx
|
142
|
+
licenses: []
|
143
|
+
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
none: false
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
hash: 3
|
155
|
+
segments:
|
156
|
+
- 0
|
157
|
+
version: "0"
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
none: false
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
hash: 3
|
164
|
+
segments:
|
165
|
+
- 0
|
166
|
+
version: "0"
|
167
|
+
requirements: []
|
168
|
+
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 1.5.2
|
171
|
+
signing_key:
|
172
|
+
specification_version: 3
|
173
|
+
summary: A simple Sphinx indexing library for Ruby
|
174
|
+
test_files:
|
175
|
+
- test/minisphinx_test.rb
|
176
|
+
- test/test_helper.rb
|