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 ADDED
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-07-26
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/LICENSE.txt ADDED
@@ -0,0 +1,27 @@
1
+ Copyright 2007 Eric Hodel. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+ 3. Neither the names of the authors nor the names of their contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
17
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
20
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
data/Manifest.txt ADDED
@@ -0,0 +1,16 @@
1
+ History.txt
2
+ LICENSE.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/sphincter.rb
7
+ lib/sphincter/association_searcher.rb
8
+ lib/sphincter/configure.rb
9
+ lib/sphincter/search.rb
10
+ lib/sphincter/search_stub.rb
11
+ lib/sphincter/tasks.rb
12
+ test/sphincter_test_case.rb
13
+ test/test_sphincter_association_searcher.rb
14
+ test/test_sphincter_configure.rb
15
+ test/test_sphincter_search.rb
16
+ test/test_sphincter_search_stub.rb
data/README.txt ADDED
@@ -0,0 +1,132 @@
1
+ Sphincter
2
+
3
+ Eric Hodel <drbrain@segment7.net>
4
+
5
+ http://seattlerb.org/Sphincter
6
+
7
+ Sphincter was named by David Yeu.
8
+
9
+ == DESCRIPTION:
10
+
11
+ Sphincter is an ActiveRecord extension for full-text searching with Sphinx.
12
+
13
+ Sphincter uses Dmytro Shteflyuk's sphinx Ruby API and automatic
14
+ configuration to make totally rad ActiveRecord searching. Well, you
15
+ still have to tell Sphincter what models you want to search. It
16
+ doesn't read your mind.
17
+
18
+ For complete documentation:
19
+
20
+ ri Sphincter
21
+
22
+ == FEATURES:
23
+
24
+ * Automatically configures itself.
25
+ * Handy set of rake tasks for easy, automatic management.
26
+ * Automatically adds has_many metadata for searching across the
27
+ association.
28
+ * Stub for testing without connecting to searchd, Sphincter::SearchStub.
29
+ * Easy pagination support.
30
+ * Filtering by index metadata and ranges, including dates.
31
+
32
+ == PROBLEMS:
33
+
34
+ * Setting match mode not supported.
35
+ * Setting sort mode not supported.
36
+ * Setting per-field weights not supported.
37
+ * Setting id range not supported.
38
+ * Setting group-by not supported.
39
+
40
+ == QUICK-START:
41
+
42
+ Download and install Sphinx from http://www.sphinxsearch.com/downloads.html
43
+
44
+ Download Sphinx Ruby API from http://rubyforge.org/frs/?group_id=2604&release_id=11049
45
+
46
+ Unpack Sphinx Ruby API into vendor/plugins/.
47
+
48
+ Install Sphincter:
49
+
50
+ $ gem install Sphincter
51
+
52
+ Load Sphincter in config/environment.rb:
53
+
54
+ require 'sphincter'
55
+
56
+ By default, Sphincter will run searchd on the same port for all
57
+ environments. See Sphincter::Configure for how to configure different
58
+ environments to use different ports.
59
+
60
+ Add indexes to models:
61
+
62
+ class Post < ActiveRecord::Base
63
+ belongs_to :blog
64
+ add_index :fields => %w[title body published]
65
+ end
66
+
67
+ Add searching UI:
68
+
69
+ class BlogController < ApplicationController
70
+ def search
71
+ @blog = Blog.find params[:id]
72
+
73
+ @results = @blog.posts.search params[:q]
74
+ end
75
+ end
76
+
77
+ Start searchd:
78
+
79
+ $ rake sphincter:start_searchd
80
+
81
+ Then test it out in your browser.
82
+
83
+ == TESTING QUICK-START:
84
+
85
+ See Sphinx::SearchStub.
86
+
87
+ == EXAMPLES:
88
+
89
+ See Sphincter::Search#search for full documentation.
90
+
91
+ Example ActiveRecord model:
92
+
93
+ class Post < ActiveRecord::Base
94
+ belongs_to :blog
95
+
96
+ # published is a boolean and title and body are string or text fields
97
+ add_index :fields => %w[title body published]
98
+ end
99
+
100
+ Simple search:
101
+
102
+ Post.search 'words'
103
+
104
+ Only search published posts:
105
+
106
+ Post.search 'words', :conditions => { :published => 1 }
107
+
108
+ Only search posts created in the last week:
109
+
110
+ now = Time.now
111
+ ago = now - 1.weeks
112
+ Post.search 'words', :between => { :created_on => [ago, now] }
113
+
114
+ Pagination (defaults to ten records/page):
115
+
116
+ Post.search 'words', :page => 2
117
+
118
+ Pagination with custom page size:
119
+
120
+ Post.search 'words', :page => 2, :per_page => 20
121
+
122
+ Pagination with custom page size (better):
123
+
124
+ Add to config/sphincter.yml:
125
+
126
+ sphincter:
127
+ per_page: 20
128
+
129
+ Then search:
130
+
131
+ Post.search 'words', :page => 2
132
+
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ $:.unshift 'lib'
6
+ require 'sphincter'
7
+
8
+ Hoe.new('Sphincter', Sphincter::VERSION) do |p|
9
+ p.rubyforge_name = 'seattlerb'
10
+ p.author = 'Eric Hodel'
11
+ p.email = 'drbrain@segment7.net'
12
+ p.summary = p.paragraphs_of('README.txt', 5).first
13
+ p.description = p.paragraphs_of('README.txt', 6).first
14
+ p.url = p.paragraphs_of('README.txt', 2).first
15
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
+
17
+ p.extra_deps << ['rake', '>= 0.7.3']
18
+ p.extra_deps << ['rails', '>= 1.2.3']
19
+ end
20
+
21
+ # vim: syntax=Ruby
data/lib/sphincter.rb ADDED
@@ -0,0 +1,102 @@
1
+ $TESTING = defined?($TESTING) && $TESTING
2
+
3
+ ##
4
+ # Sphincter is a ActiveRecord extension for full-text searching using the
5
+ # Sphinx library.
6
+ #
7
+ # For the quick-start guide and some examples, see README.txt.
8
+ #
9
+ # == Installing
10
+ #
11
+ # Download and install Sphinx from http://www.sphinxsearch.com/downloads.html
12
+ #
13
+ # Download Sphinx Ruby API from
14
+ # http://rubyforge.org/frs/?group_id=2604&release_id=11049
15
+ #
16
+ # Unpack Sphinx Ruby API into vendor/plugins/.
17
+ #
18
+ # Install the gem:
19
+ #
20
+ # gem install Sphincter
21
+ #
22
+ # Require Sphincter in config/environment.rb:
23
+ #
24
+ # require 'sphincter'
25
+ #
26
+ # Require the Sphincter rake tasks in Rakefile:
27
+ #
28
+ # require 'sphincter/tasks'
29
+ #
30
+ # == Setup
31
+ #
32
+ # At best, you don't do anything to setup Sphincter. It has sensible built-in
33
+ # defaults.
34
+ #
35
+ # If you're running Sphinx's searchd for multiple environments on the same
36
+ # machine, you'll want to add a config file to change the port that searchd
37
+ # and the RAILS_ENV will comminicate across. Do that in a per-environment
38
+ # configuration file.
39
+ #
40
+ # If you have multiple machines, you'll want to change which address searchd
41
+ # will run on. Do that in the global configuration file.
42
+ #
43
+ # See Sphincter::Configure for full information on how to setup these and
44
+ # other options for Sphincter.
45
+ #
46
+ # When you're done, run:
47
+ #
48
+ # $ rake sphincter:configure
49
+ #
50
+ # == Indexing
51
+ #
52
+ # Sphincter automatically extends ActiveRecord::Base with Sphincter::Search, so
53
+ # you only have to call add_index in the models you want indexed:
54
+ #
55
+ # class Model < ActiveRecord::Base
56
+ # belongs_to :other
57
+ #
58
+ # add_index :fields => %w[title body]
59
+ # end
60
+ #
61
+ # class Other < ActiveRecord::Base
62
+ # has_many :models
63
+ # end
64
+ #
65
+ # add_index automatically adds a #search method to has_many associations
66
+ # referencing this model, so you could:
67
+ #
68
+ # Other.find(id).models.search 'some query'
69
+ #
70
+ # See Sphincter::Search for details.
71
+ #
72
+ # When you're done, run:
73
+ #
74
+ # rake sphincter:index
75
+ #
76
+ # == Tasks
77
+ #
78
+ # You can get a set of Sphincter tasks by requiring 'sphincter/tasks' in your
79
+ # Rakefile. These tasks are all in the 'sphincter' namespace:
80
+ #
81
+ # configure:: Creates sphinx.conf if it doesn't exist
82
+ # reconfigure:: Creates sphinx.conf, replacing the existing one.
83
+ # index:: Runs the sphinx indexer if the index doesn't exist.
84
+ # reindex:: Runs the sphinx indexer. Rotates the index if searchd is running.
85
+ # reset:: Stops searchd, reconfigures and reindexes
86
+ # restart_searchd:: Restarts the searchd sphinx daemon
87
+ # start_searchd:: Starts the searchd sphinx daemon
88
+ # stop_searchd:: Stops the searchd daemon
89
+
90
+ module Sphincter
91
+
92
+ ##
93
+ # This is the version of Sphincter you are using.
94
+
95
+ VERSION = '1.0.0'
96
+
97
+ end
98
+
99
+ require 'sphincter/configure'
100
+ require 'sphincter/association_searcher'
101
+ require 'sphincter/search'
102
+
@@ -0,0 +1,22 @@
1
+ require 'sphincter'
2
+
3
+ ##
4
+ # ActiveRecord::Associations::ClassMethods#has_many extension for searching
5
+ # the items of an ActiveRecord::Associations::AssociationProxy.
6
+
7
+ module Sphincter::AssociationSearcher
8
+
9
+ ##
10
+ # Searches for +query+ with +options+. Adds a condition so only the
11
+ # proxy_owner's records are matched.
12
+
13
+ def search(query, options = {})
14
+ pkey = proxy_reflection.primary_key_name
15
+ options[:conditions] ||= {}
16
+ options[:conditions][pkey] = proxy_owner.id
17
+
18
+ proxy_reflection.klass.search query, options
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,380 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+
4
+ require 'sphincter'
5
+
6
+ ##
7
+ # Configuration module for Sphincter.
8
+ #
9
+ # DEFAULT_CONF contains the default options. They can be overridden in both a
10
+ # global config/sphincter.yml and in a per-environment
11
+ # config/environments/sphincter.RAILS_ENV.yml.
12
+ #
13
+ # The only option you should need to override is the port option of
14
+ # sphincter, so a config file for separate test and development indexes would
15
+ # look like:
16
+ #
17
+ # config/environments/sphincter.development.yml:
18
+ #
19
+ # sphincter:
20
+ # port: 3313
21
+ #
22
+ # config/environments/sphincter.test.yml:
23
+ #
24
+ # sphincter:
25
+ # port: 3314
26
+ #
27
+ # Configuration options:
28
+ #
29
+ # sphincter:: Options for serachd's and Sphinx's port and address, and
30
+ # paths for index files.
31
+ # index:: Options for a sphinx index conf section
32
+ # indexer:: Options for the sphinx indexer
33
+ # mysql:: Options for the sphinx indexer's mysql database connection. The
34
+ # important ones are filled from config/database.yml
35
+ # searchd:: Options for a sphinx searchd conf section
36
+ # source:: Options for a sphinx source conf section
37
+ #
38
+ # The sphincter entry contains:
39
+ #
40
+ # address:: Which host searchd will run on, and which host Sphincter will
41
+ # connect to.
42
+ # port:: Which port searchd and Sphincter will connect to.
43
+ # path:: Location of searchd indexes, relative to RAILS_ROOT.
44
+ # per_page:: How many items to include in a search by default.
45
+ #
46
+ # All other entries are from Sphinx.
47
+ #
48
+ # See http://www.sphinxsearch.com/doc.html#reference for details on sphinx
49
+ # conf file settings.
50
+
51
+ module Sphincter::Configure
52
+
53
+ @env_conf = nil
54
+ @index_count = nil
55
+
56
+ rails_env = defined?(RAILS_ENV) ? RAILS_ENV : 'RAILS_ENV'
57
+
58
+ ##
59
+ # Default Sphincter configuration.
60
+
61
+ DEFAULT_CONF = {
62
+ 'sphincter' => {
63
+ 'address' => '127.0.0.1',
64
+ 'path' => "sphinx/#{rails_env}",
65
+ 'per_page' => 10,
66
+ 'port' => 3312,
67
+ },
68
+
69
+ 'index' => {
70
+ 'charset_type' => 'utf-8',
71
+ 'docinfo' => 'extern',
72
+ 'min_word_len' => 1,
73
+ 'morphology' => 'stem_en',
74
+ 'stopwords' => '',
75
+ },
76
+
77
+ 'indexer' => {
78
+ 'mem_limit' => '32M',
79
+ },
80
+
81
+ 'mysql' => {
82
+ 'sql_query_pre' => [
83
+ 'SET SESSION group_concat_max_len = 65535',
84
+ 'SET NAMES utf8',
85
+ ],
86
+ },
87
+
88
+ 'searchd' => {
89
+ 'log' => "log/sphinx/searchd.#{rails_env}.log",
90
+ 'max_children' => 30,
91
+ 'max_matches' => 1000,
92
+ 'query_log' => "log/sphinx/query.#{rails_env}.log",
93
+ 'read_timeout' => 5,
94
+ },
95
+
96
+ 'source' => {
97
+ 'index_html_attrs' => '',
98
+ 'sql_query_post' => '',
99
+ 'sql_range_step' => 20000,
100
+ 'strip_html' => 0,
101
+ },
102
+ }
103
+
104
+ ##
105
+ # Builds and writes out a sphinx.conf file.
106
+
107
+ def self.configure
108
+ conf = get_conf
109
+ db_conf = get_db_conf
110
+
111
+ db_conf = conf[db_conf['type']].merge db_conf
112
+
113
+ sources = get_sources
114
+
115
+ sources.each do |name, source_conf|
116
+ sources[name] = db_conf.merge source_conf
117
+ end
118
+
119
+ write_configuration conf, sources
120
+ end
121
+
122
+ ##
123
+ # Merges Hashes of Hashes +mergee+ and +hash+.
124
+
125
+ def self.deep_merge(mergee, hash)
126
+ mergee = mergee.dup
127
+ hash.keys.each do |key| mergee[key] ||= hash[key] end
128
+ mergee.each do |key, value|
129
+ next unless hash[key]
130
+ mergee[key] = value.merge hash[key]
131
+ end
132
+ end
133
+
134
+ ##
135
+ # Builds the Sphincter configuration.
136
+ #
137
+ # Automatically fills in searchd address, port and pid_file from 'sphincter'
138
+ # section.
139
+
140
+ def self.get_conf
141
+ return @env_conf unless @env_conf.nil?
142
+
143
+ base_file = File.expand_path File.join(RAILS_ROOT, 'config', 'sphincter.yml')
144
+ base_conf = deep_merge DEFAULT_CONF, get_conf_from(base_file)
145
+
146
+ env_file = File.expand_path File.join(RAILS_ROOT, 'config', 'environments',
147
+ "sphincter.#{RAILS_ENV}.yml")
148
+ env_conf = deep_merge base_conf, get_conf_from(env_file)
149
+
150
+ env_conf['searchd']['address'] = env_conf['sphincter']['address']
151
+ env_conf['searchd']['port'] = env_conf['sphincter']['port']
152
+ env_conf['searchd']['pid_file'] = File.join(env_conf['sphincter']['path'],
153
+ 'searchd.pid')
154
+
155
+ @env_conf = env_conf
156
+ end
157
+
158
+ ##
159
+ # Reads configuration file +file+. Returns {} if the file does not exist.
160
+
161
+ def self.get_conf_from(file)
162
+ if File.exist? file then
163
+ YAML.load File.read(file)
164
+ else
165
+ {}
166
+ end
167
+ end
168
+
169
+ ##
170
+ # Builds a sphinx.conf source configuration for each index.
171
+
172
+ def self.get_sources
173
+ load_models
174
+
175
+ indexes = Sphincter::Search.indexes
176
+ index_count # HACK necessary to set options[:index_id] per-index
177
+
178
+ sources = {}
179
+
180
+ indexes.each do |klass, model_indexes|
181
+ model_indexes.each do |options|
182
+ conn = klass.connection
183
+ table = klass.table_name
184
+ pk = conn.quote_column_name klass.primary_key
185
+ index_id = options[:index_id]
186
+
187
+ source_conf = {}
188
+ source_conf['sql_date_column'] = []
189
+ source_conf['sql_group_column'] = %w[sphincter_index_id]
190
+
191
+ fields = []
192
+ fields << "(#{table}.#{pk} * #{index_count} + #{index_id}) AS #{pk}"
193
+ fields << "#{index_id} AS sphincter_index_id"
194
+ fields << "#{conn.quote table} AS klass"
195
+
196
+ options[:fields].each do |field|
197
+ fields << get_sources_field(source_conf, klass, field)
198
+ end
199
+
200
+ fields = fields.join ', '
201
+
202
+ where = []
203
+ where << "#{table}.#{pk} >= $start AND #{table}.#{pk} <= $end"
204
+ where << options[:conditions]
205
+ where = where.compact.join ' AND '
206
+
207
+ source_conf['sql_query'] =
208
+ "SELECT #{fields} FROM #{table} WHERE #{where}"
209
+ source_conf['sql_query_info'] =
210
+ "SELECT * FROM #{table} " \
211
+ "WHERE #{table}.#{pk} = (($id - #{index_id}) / #{index_count})"
212
+ source_conf['sql_query_range'] =
213
+ "SELECT MIN(#{pk}), MAX(#{pk}) FROM #{table}"
214
+ source_conf['strip_html'] = options[:strip_html] ? 1 : 0
215
+
216
+ name = options[:name] || table
217
+ sources[name] = source_conf
218
+ end
219
+ end
220
+
221
+ sources
222
+ end
223
+
224
+ ##
225
+ # Builds a field for a source's sql_query sphinx.conf setting.
226
+ #
227
+ # get_sources_field only understands :datetime, :boolean, :integer, :string
228
+ # and :text column types.
229
+
230
+ def self.get_sources_field(source_conf, klass, field)
231
+ conn = klass.connection
232
+ table = klass.table_name
233
+
234
+ quoted_field = conn.quote_column_name field
235
+ case klass.columns_hash[field].type
236
+ when :date, :datetime, :time, :timestamp then
237
+ source_conf['sql_date_column'] << field
238
+ "UNIX_TIMESTAMP(#{table}.#{quoted_field}) AS #{quoted_field}"
239
+ when :boolean, :integer then
240
+ source_conf['sql_group_column'] << field
241
+ "#{table}.#{quoted_field} AS #{quoted_field}"
242
+ when :string, :text then
243
+ "#{table}.#{quoted_field} AS #{quoted_field}"
244
+ end
245
+ end
246
+
247
+ ##
248
+ # Retrieves the database configuration for ActiveRecord::Base and adapts it
249
+ # for a sphinx.conf file.
250
+
251
+ def self.get_db_conf
252
+ conf = {}
253
+ ar_conf = ActiveRecord::Base.configurations[::RAILS_ENV]
254
+
255
+ conf['type'] = ar_conf['adapter']
256
+ conf['sql_host'] = ar_conf['host'] if ar_conf.include? 'host'
257
+ conf['sql_user'] = ar_conf['username'] if ar_conf.include? 'username'
258
+ conf['sql_pass'] = ar_conf['password'] if ar_conf.include? 'password'
259
+ conf['sql_db'] = ar_conf['database'] if ar_conf.include? 'database'
260
+ conf['sql_sock'] = ar_conf['socket'] if ar_conf.include? 'socket'
261
+
262
+ conf
263
+ end
264
+
265
+ ##
266
+ # Iterates over the searchable ActiveRecord::Base classes and assigns an
267
+ # index to each one. Returns the total number of indexes found.
268
+
269
+ def self.index_count
270
+ return @index_count unless @index_count.nil?
271
+
272
+ @index_count = 0
273
+
274
+ load_models
275
+
276
+ Sphincter::Search.indexes.each do |model, model_indexes|
277
+ model_indexes.each do |options|
278
+ options[:index_id] = @index_count
279
+ @index_count += 1
280
+ end
281
+ end
282
+ @index_count
283
+ end
284
+
285
+ ##
286
+ # Loads ActiveRecord::Base models from app/models.
287
+
288
+ def self.load_models
289
+ model_files = Dir[File.join(RAILS_ROOT, 'app', 'models', '*.rb')]
290
+ model_names = model_files.map { |name| File.basename name, '.rb' }
291
+ model_names.each { |name| name.camelize.constantize }
292
+ end
293
+
294
+ ##
295
+ # Returns the pid of searchd if searchd is running, otherwise false.
296
+
297
+ def self.searchd_running?
298
+ pid_file = Sphincter::Configure.get_conf['searchd']['pid_file']
299
+ return false unless File.exist? pid_file
300
+
301
+ pid = File.read pid_file
302
+ return false if pid.empty?
303
+
304
+ running = `ps -p #{pid}` =~ /#{pid}.*searchd/
305
+ running ? pid : false
306
+ end
307
+
308
+ ##
309
+ # Outputs a sphinx.conf configuration section titled +heading+ using the
310
+ # Hash +data+. Values in +data+ may be a String or Array. For an Array,
311
+ # the Hash key is printed multiple times.
312
+
313
+ def self.section(heading, data)
314
+ section = []
315
+ section << heading
316
+ section << '{'
317
+ data.sort_by { |k,| k }.each do |key, value|
318
+ case value
319
+ when Array then
320
+ next if value.empty?
321
+ value.each do |v|
322
+ section << " #{key} = #{v}"
323
+ end
324
+ else
325
+ section << " #{key} = #{value}"
326
+ end
327
+ end
328
+ section << '}'
329
+ section.join "\n"
330
+ end
331
+
332
+ ##
333
+ # The path to sphinx.conf.
334
+
335
+ def self.sphinx_conf
336
+ @sphinx_conf ||= File.join sphinx_dir, 'sphinx.conf'
337
+ end
338
+
339
+ ##
340
+ # The directory where sphinx's files live.
341
+
342
+ def self.sphinx_dir
343
+ @sphinx_dir ||= File.join(RAILS_ROOT,
344
+ Sphincter::Configure.get_conf['sphincter']['path'])
345
+ end
346
+
347
+ ##
348
+ # Writes out a sphinx.conf configuration using +conf+ and +sources+.
349
+
350
+ def self.write_configuration(conf, sources)
351
+ FileUtils.mkdir_p sphinx_dir
352
+
353
+ out = []
354
+
355
+ out << section('indexer', conf['indexer'])
356
+ out << nil
357
+
358
+ out << section('searchd', conf['searchd'])
359
+ out << nil
360
+
361
+ sources.each do |index_name, values|
362
+ source_data = conf['source'].merge values
363
+ out << section("source #{index_name}", source_data)
364
+ out << nil
365
+
366
+ index_path = File.join sphinx_dir, index_name
367
+ index_data = conf['index'].merge 'source' => index_name,
368
+ 'path' => index_path
369
+
370
+ out << section("index #{index_name}", index_data)
371
+ out << nil
372
+ end
373
+
374
+ File.open sphinx_conf, 'w' do |fp|
375
+ fp.write out.join("\n")
376
+ end
377
+ end
378
+
379
+ end
380
+