pager-ultrasphinx 1.0.20080510

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,199 @@
1
+
2
+ module Ultrasphinx
3
+
4
+ class Error < ::StandardError #:nodoc:
5
+ end
6
+ class ConfigurationError < Error #:nodoc:
7
+ end
8
+ class DaemonError < Error #:nodoc:
9
+ end
10
+ class UsageError < Error #:nodoc:
11
+ end
12
+
13
+ # Internal file paths
14
+
15
+ SUBDIR = "config/ultrasphinx"
16
+
17
+ DIR = "#{RAILS_ROOT}/#{SUBDIR}"
18
+
19
+ THIS_DIR = File.expand_path(File.dirname(__FILE__))
20
+
21
+ CONF_PATH = "#{DIR}/#{RAILS_ENV}.conf"
22
+
23
+ ENV_BASE_PATH = "#{DIR}/#{RAILS_ENV}.base"
24
+
25
+ GENERIC_BASE_PATH = "#{DIR}/default.base"
26
+
27
+ BASE_PATH = (File.exist?(ENV_BASE_PATH) ? ENV_BASE_PATH : GENERIC_BASE_PATH)
28
+
29
+ raise ConfigurationError, "Please create a '#{SUBDIR}/#{RAILS_ENV}.base' or '#{SUBDIR}/default.base' file in order to use Ultrasphinx in your #{RAILS_ENV} environment." unless File.exist? BASE_PATH # XXX lame
30
+
31
+ # Some miscellaneous constants
32
+
33
+ MAX_INT = 2**32-1
34
+
35
+ MAX_WORDS = 2**16 # The maximum number of stopwords built
36
+
37
+ MAIN_INDEX = "main"
38
+
39
+ DELTA_INDEX = "delta"
40
+
41
+ INDEXES = [MAIN_INDEX, DELTA_INDEX]
42
+
43
+ CONFIG_MAP = {
44
+ # These must be symbols for key mapping against Rails itself.
45
+ :username => 'sql_user',
46
+ :password => 'sql_pass',
47
+ :host => 'sql_host',
48
+ :database => 'sql_db',
49
+ :port => 'sql_port',
50
+ :socket => 'sql_sock'
51
+ }
52
+
53
+ CONNECTION_DEFAULTS = {
54
+ :host => 'localhost',
55
+ :password => '',
56
+ :username => 'root'
57
+ }
58
+
59
+ mattr_accessor :with_rake
60
+
61
+ def self.load_stored_procedure(name)
62
+ open("#{THIS_DIR}/postgresql/#{name}.sql").read.gsub(/\s+/, ' ')
63
+ end
64
+
65
+ SQL_FUNCTIONS = {
66
+ 'mysql' => {
67
+ 'group_concat' => "CAST(GROUP_CONCAT(DISTINCT ? ? SEPARATOR ' ') AS CHAR)",
68
+ 'delta' => "DATE_SUB(NOW(), INTERVAL ? SECOND)",
69
+ 'hash' => "CAST(CRC32(?) AS unsigned)",
70
+ 'range_cast' => "?"
71
+ },
72
+ 'postgresql' => {
73
+ 'group_concat' => "GROUP_CONCAT(?)",
74
+ 'delta' => "(NOW() - '? SECOND'::interval)",
75
+ 'range_cast' => "cast(coalesce(?,1) AS integer)",
76
+ 'hash' => "CRC32(?)"
77
+ }
78
+ }
79
+
80
+ DEFAULTS = {
81
+ 'mysql' => %(
82
+ type = mysql
83
+ sql_query_pre = SET SESSION group_concat_max_len = 65535
84
+ sql_query_pre = SET NAMES utf8
85
+ ),
86
+ 'postgresql' => %(
87
+ type = pgsql
88
+ sql_query_pre =
89
+ )
90
+ }
91
+
92
+ ADAPTER = ActiveRecord::Base.connection.instance_variable_get("@config")[:adapter] rescue 'mysql'
93
+
94
+ # Warn-mode logger. Also called from rake tasks.
95
+ def self.say msg
96
+ # XXX Method name is stupid.
97
+ if with_rake
98
+ puts msg[0..0].upcase + msg[1..-1]
99
+ else
100
+ msg = "** ultrasphinx: #{msg}"
101
+ if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER
102
+ RAILS_DEFAULT_LOGGER.warn msg
103
+ else
104
+ STDERR.puts msg
105
+ end
106
+ end
107
+ nil # Explicitly return nil
108
+ end
109
+
110
+ # Debug-mode logger.
111
+ def self.log msg
112
+ # XXX Method name is stupid.
113
+ if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER
114
+ RAILS_DEFAULT_LOGGER.debug msg
115
+ else
116
+ STDERR.puts msg
117
+ end
118
+ end
119
+
120
+ # Configuration file parser.
121
+ def self.options_for(heading, path)
122
+ # Evaluate ERB
123
+ template = ERB.new(File.open(path) {|f| f.read})
124
+ contents = template.result(binding)
125
+
126
+ # Find the correct heading.
127
+ section = contents[/^#{heading.gsub('/', '__')}\s*?\{(.*?)\}/m, 1]
128
+
129
+ if section
130
+ # Convert to a hash
131
+ options = section.split("\n").map do |line|
132
+ line =~ /\s*(.*?)\s*=\s*([^\#]*)/
133
+ $1 ? [$1, $2.strip] : []
134
+ end
135
+ Hash[*options.flatten]
136
+ else
137
+ # XXX Is it safe to raise here?
138
+ Ultrasphinx.say "warning; heading #{heading} not found in #{path}; it may be corrupted. "
139
+ {}
140
+ end
141
+ end
142
+
143
+ def self.get_models_to_class_ids #:nodoc:
144
+ # Reading the conf file makes sure that we are in sync with the actual Sphinx index, not
145
+ # whatever you happened to change your models to most recently.
146
+ if File.exist? CONF_PATH
147
+ lines, hash = open(CONF_PATH).readlines, {}
148
+ msg = "#{CONF_PATH} file is corrupted. Please run 'rake ultrasphinx:configure'."
149
+
150
+ lines.each_with_index do |line, index|
151
+ # Find the main sources
152
+ if line =~ /^source ([\w\d_-]*)_#{MAIN_INDEX}/
153
+ # Derive the model name
154
+ model = $1.gsub('__', '/').classify
155
+
156
+ # Get the id modulus out of the adjacent sql_query
157
+ query = lines[index..-1].detect do |query_line|
158
+ query_line =~ /^sql_query /
159
+ end
160
+ raise ConfigurationError, msg unless query
161
+ hash[model] = query[/(\d*) AS class_id/, 1].to_i
162
+ end
163
+ end
164
+ raise ConfigurationError, msg unless hash.values.size == hash.values.uniq.size
165
+ hash
166
+ else
167
+ # We can't raise here because you may be generating the configuration for the first time
168
+ Ultrasphinx.say "configuration file not found for #{RAILS_ENV.inspect} environment"
169
+ Ultrasphinx.say "please run 'rake ultrasphinx:configure'"
170
+ end
171
+ end
172
+
173
+ # Introspect on the existing generated conf files.
174
+ INDEXER_SETTINGS = options_for('indexer', BASE_PATH)
175
+ CLIENT_SETTINGS = options_for('client', BASE_PATH)
176
+ DAEMON_SETTINGS = options_for('searchd', BASE_PATH)
177
+ SOURCE_SETTINGS = options_for('source', BASE_PATH)
178
+ INDEX_SETTINGS = options_for('index', BASE_PATH)
179
+
180
+ # Make sure there's a trailing slash.
181
+ INDEX_SETTINGS['path'] = INDEX_SETTINGS['path'].chomp("/") + "/"
182
+
183
+ DICTIONARY = CLIENT_SETTINGS['dictionary_name'] || 'ap'
184
+ raise ConfigurationError, "Aspell does not support dictionary names longer than two letters" if DICTIONARY.size > 2
185
+
186
+ STOPWORDS_PATH = "#{Ultrasphinx::INDEX_SETTINGS['path']}/#{DICTIONARY}-stopwords.txt"
187
+
188
+ MODEL_CONFIGURATION = {}
189
+
190
+ # See if a delta index was defined.
191
+ def self.delta_index_present?
192
+ if File.exist?(CONF_PATH)
193
+ File.open(CONF_PATH).readlines.detect do |line|
194
+ line =~ /^index delta/
195
+ end
196
+ end
197
+ end
198
+
199
+ end
@@ -0,0 +1,36 @@
1
+
2
+ require 'fileutils'
3
+ require 'chronic'
4
+ require 'singleton'
5
+
6
+ if defined? RAILS_ENV and RAILS_ENV == "development"
7
+ if ENV['USER'] == 'eweaver'
8
+ require 'ruby-debug'
9
+ Debugger.start
10
+ end
11
+ end
12
+
13
+ $LOAD_PATH << "#{File.dirname(__FILE__)}/../vendor/riddle/lib"
14
+ require 'riddle'
15
+ require 'ultrasphinx/ultrasphinx'
16
+ require 'ultrasphinx/associations'
17
+ require 'ultrasphinx/core_extensions'
18
+ require 'ultrasphinx/is_indexed'
19
+
20
+ if (ActiveRecord::Base.connection rescue nil) # XXX Not sure why this needed to be wrapped.
21
+ require 'ultrasphinx/configure'
22
+ require 'ultrasphinx/autoload'
23
+ require 'ultrasphinx/fields'
24
+
25
+ require 'ultrasphinx/search/internals'
26
+ require 'ultrasphinx/search/parser'
27
+ require 'ultrasphinx/search'
28
+
29
+ begin
30
+ require 'raspell'
31
+ rescue Object => e
32
+ end
33
+
34
+ require 'ultrasphinx/spell'
35
+ end
36
+
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+
2
+ require 'ultrasphinx'
@@ -0,0 +1,206 @@
1
+
2
+ ENV['RAILS_ENV'] ||= "development"
3
+
4
+ namespace :ultrasphinx do
5
+
6
+ task :_environment => [:environment] do
7
+ # We can't just chain :environment because we want to make
8
+ # sure it's set only for known Sphinx tasks
9
+ Ultrasphinx.with_rake = true
10
+ end
11
+
12
+ desc "Bootstrap a full Sphinx environment"
13
+ task :bootstrap => [:_environment, :configure, :index, :"daemon:restart"] do
14
+ say "done"
15
+ say "please restart your application containers"
16
+ end
17
+
18
+ desc "Rebuild the configuration file for this particular environment."
19
+ task :configure => [:_environment] do
20
+ Ultrasphinx::Configure.run
21
+ end
22
+
23
+ namespace :index do
24
+ desc "Reindex and rotate the main index."
25
+ task :main => [:_environment] do
26
+ ultrasphinx_index(Ultrasphinx::MAIN_INDEX)
27
+ end
28
+
29
+ desc "Reindex and rotate the delta index."
30
+ task :delta => [:_environment] do
31
+ ultrasphinx_index(Ultrasphinx::DELTA_INDEX)
32
+ end
33
+
34
+ desc "Merge the delta index into the main index."
35
+ task :merge => [:_environment] do
36
+ ultrasphinx_merge
37
+ end
38
+
39
+ end
40
+
41
+ desc "Reindex and rotate all indexes."
42
+ task :index => [:_environment] do
43
+ ultrasphinx_index("--all")
44
+ end
45
+
46
+ namespace :daemon do
47
+ desc "Start the search daemon"
48
+ task :start => [:_environment] do
49
+ FileUtils.mkdir_p File.dirname(Ultrasphinx::DAEMON_SETTINGS["log"]) rescue nil
50
+ raise Ultrasphinx::DaemonError, "Already running" if ultrasphinx_daemon_running?
51
+ system "searchd --config '#{Ultrasphinx::CONF_PATH}'"
52
+ sleep(4) # give daemon a chance to write the pid file
53
+ if ultrasphinx_daemon_running?
54
+ say "started successfully"
55
+ else
56
+ say "failed to start"
57
+ end
58
+ end
59
+
60
+ desc "Stop the search daemon"
61
+ task :stop => [:_environment] do
62
+ raise Ultrasphinx::DaemonError, "Doesn't seem to be running" unless ultrasphinx_daemon_running?
63
+ system "kill #{pid = ultrasphinx_daemon_pid}"
64
+ sleep(1)
65
+ if ultrasphinx_daemon_running?
66
+ system "kill -9 #{pid}"
67
+ sleep(1)
68
+ end
69
+ if ultrasphinx_daemon_running?
70
+ say "#{pid} could not be stopped"
71
+ else
72
+ say "stopped #{pid}"
73
+ end
74
+ end
75
+
76
+ desc "Restart the search daemon"
77
+ task :restart => [:_environment] do
78
+ Rake::Task["ultrasphinx:daemon:stop"].invoke if ultrasphinx_daemon_running?
79
+ sleep(3)
80
+ Rake::Task["ultrasphinx:daemon:start"].invoke
81
+ end
82
+
83
+ desc "Check if the search daemon is running"
84
+ task :status => [:_environment] do
85
+ if ultrasphinx_daemon_running?
86
+ say "daemon is running."
87
+ else
88
+ say "daemon is stopped."
89
+ end
90
+ end
91
+ end
92
+
93
+
94
+ namespace :spelling do
95
+ desc "Rebuild the custom spelling dictionary. You may need to use 'sudo' if your Aspell folder is not writable by the app user."
96
+ task :build => [:_environment] do
97
+ ENV['OPTS'] = "--buildstops #{Ultrasphinx::STOPWORDS_PATH} #{Ultrasphinx::MAX_WORDS} --buildfreqs"
98
+ Rake::Task["ultrasphinx:index"].invoke
99
+ tmpfile = "/tmp/ultrasphinx-stopwords.txt"
100
+ words = []
101
+ say "filtering"
102
+ File.open(Ultrasphinx::STOPWORDS_PATH).each do |line|
103
+ if line =~ /^([^\s\d_]{4,}) (\d+)/
104
+ # XXX should be configurable
105
+ words << $1 if $2.to_i > 40
106
+ # ideally we would also skip words within X edit distance of a correction
107
+ # by aspell-en, in order to not add typos to the dictionary
108
+ end
109
+ end
110
+ say "writing #{words.size} words"
111
+ File.open(tmpfile, 'w').write(words.join("\n"))
112
+ say "loading dictionary '#{Ultrasphinx::DICTIONARY}' into aspell"
113
+ system("aspell --lang=en create master #{Ultrasphinx::DICTIONARY}.rws < #{tmpfile}")
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ # task shortcuts
120
+ namespace :us do
121
+ task :start => ["ultrasphinx:daemon:start"]
122
+ task :restart => ["ultrasphinx:daemon:restart"]
123
+ task :stop => ["ultrasphinx:daemon:stop"]
124
+ task :stat => ["ultrasphinx:daemon:status"]
125
+ task :index => ["ultrasphinx:index"]
126
+ task :in => ["ultrasphinx:index"]
127
+ task :main => ["ultrasphinx:index:main"]
128
+ task :delta => ["ultrasphinx:index:delta"]
129
+ task :merge => ["ultrasphinx:index:merge"]
130
+ task :spell => ["ultrasphinx:spelling:build"]
131
+ task :conf => ["ultrasphinx:configure"]
132
+ task :boot => ["ultrasphinx:bootstrap"]
133
+ end
134
+
135
+ # Support methods
136
+
137
+ def ultrasphinx_daemon_pid
138
+ open(Ultrasphinx::DAEMON_SETTINGS['pid_file']).readline.chomp rescue nil
139
+ end
140
+
141
+ def ultrasphinx_daemon_running?
142
+ if ultrasphinx_daemon_pid and `ps -p#{ultrasphinx_daemon_pid} | wc`.to_i > 1
143
+ true
144
+ else
145
+ # Remove bogus lockfiles.
146
+ Dir[Ultrasphinx::INDEX_SETTINGS["path"] + "*spl"].each {|file| File.delete(file)}
147
+ false
148
+ end
149
+ end
150
+
151
+ def ultrasphinx_index(index)
152
+ rotate = ultrasphinx_daemon_running?
153
+ ultrasphinx_create_index_path
154
+
155
+ cmd = "indexer --config '#{Ultrasphinx::CONF_PATH}'"
156
+ cmd << " #{ENV['OPTS']} " if ENV['OPTS']
157
+ cmd << " --rotate" if rotate
158
+ cmd << " #{index}"
159
+
160
+ say "$ #{cmd}"
161
+ system cmd
162
+
163
+ ultrasphinx_check_rotate if rotate
164
+ end
165
+
166
+ def ultrasphinx_merge
167
+ rotate = ultrasphinx_daemon_running?
168
+
169
+ indexes = [Ultrasphinx::MAIN_INDEX, Ultrasphinx::DELTA_INDEX]
170
+ indexes.each do |index|
171
+ raise "#{index} index is missing" unless File.exist? "#{Ultrasphinx::INDEX_SETTINGS['path']}/sphinx_index_#{index}.spa"
172
+ end
173
+
174
+ cmd = "indexer --config '#{Ultrasphinx::CONF_PATH}'"
175
+ cmd << " #{ENV['OPTS']} " if ENV['OPTS']
176
+ cmd << " --rotate" if rotate
177
+ cmd << " --merge #{indexes.join(' ')}"
178
+
179
+ say "$ #{cmd}"
180
+ system cmd
181
+
182
+ ultrasphinx_check_rotate if rotate
183
+ end
184
+
185
+ def ultrasphinx_check_rotate
186
+ sleep(4)
187
+ failed = Dir[Ultrasphinx::INDEX_SETTINGS['path'] + "/*.new.*"]
188
+ if failed.any?
189
+ say "warning; index failed to rotate! Deleting new indexes"
190
+ say "try 'killall searchd' and then 'rake ultrasphinx:daemon:start'"
191
+ failed.each {|f| File.delete f }
192
+ else
193
+ say "index rotated ok"
194
+ end
195
+ end
196
+
197
+ def ultrasphinx_create_index_path
198
+ unless File.directory? Ultrasphinx::INDEX_SETTINGS['path']
199
+ mkdir_p Ultrasphinx::INDEX_SETTINGS['path']
200
+ end
201
+ end
202
+
203
+ def say msg
204
+ Ultrasphinx.say msg
205
+ end
206
+
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Pat Allan
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.
@@ -0,0 +1,74 @@
1
+ This client has been written to interface with Sphinx[http://sphinxsearch.com/]. It is written by
2
+ {Pat Allan}[http://freelancing-gods.com], and has been influenced by both Dmytro Shteflyuk's Ruby
3
+ client and the original PHP client - credit where credit's due, after all.
4
+
5
+ It does not follow the same syntax as those two, though (not much point writing this otherwise) -
6
+ opting for a more Ruby-like structure.
7
+
8
+ The easiest way to install is to grab the gem (available since 0.9.8r1112 only):
9
+
10
+ sudo gem install riddle
11
+
12
+ However, if you're so inclined, you can grab sourcecode via subversion. If you
13
+ are after a specific release, use the tag as follows:
14
+
15
+ svn co http://rails-oceania.googlecode.com/svn/patallan/riddle/tags/0.9.8-rc2 riddle
16
+
17
+ Or for the most current, just use trunk:
18
+
19
+ svn co http://rails-oceania.googlecode.com/svn/patallan/riddle/trunk riddle
20
+
21
+ Please note that at the time of writing, the following versions are supported (if you get the appropriate tag):
22
+
23
+ * 0.9.8-r871
24
+ * 0.9.8-r909
25
+ * 0.9.8-r985
26
+ * 0.9.8-r1065
27
+ * 0.9.8-r1112
28
+ * 0.9.8-rc1 (gem version: 0.9.8.1198)
29
+ * 0.9.8-rc2 (gem version: 0.9.8.1231)
30
+
31
+ To get started, just instantiate a Client object:
32
+
33
+ client = Riddle::Client.new # defaults to localhost and port 3312
34
+ client = Riddle::Client.new "sphinxserver.domain.tld", 3333 # custom settings
35
+
36
+ And then set the parameters to what you want, before running a query:
37
+
38
+ client.match_mode = :extended
39
+ client.query "Pat Allan @state Victoria"
40
+
41
+ The results from a query are similar to the other clients - but here's the details. It's a hash with
42
+ the following keys:
43
+
44
+ * :matches
45
+ * :fields
46
+ * :attributes
47
+ * :attribute_names
48
+ * :words
49
+ * :total
50
+ * :total_found
51
+ * :time
52
+ * :status
53
+ * :warning (if appropriate)
54
+ * :error (if appropriate)
55
+
56
+ The key <tt>:matches</tt> returns an array of hashes - the actual search results. Each hash has the
57
+ document id (<tt>:doc</tt>), the result weighting (<tt>:weight</tt>), and a hash of the attributes for
58
+ the document (<tt>:attributes</tt>).
59
+
60
+ The <tt>:fields</tt> and <tt>:attribute_names</tt> keys return list of fields and attributes for the
61
+ documents. The key <tt>:attributes</tt> will return a hash of attribute name and type pairs, and
62
+ <tt>:words</tt> returns a hash of hashes representing the words from the search, with the number of
63
+ documents and hits for each, along the lines of:
64
+
65
+ results[:words]["Pat"] #=> {:docs => 12, :hits => 15}
66
+
67
+ <tt>:total</tt>, <tt>:total_found</tt> and <tt>:time</tt> return the number of matches available, the
68
+ total number of matches (which may be greater than the maximum available), and the time in milliseconds
69
+ that the query took to run.
70
+
71
+ <tt>:status</tt> is the error code for the query - and if there was a related warning, it will be under
72
+ the <tt>:warning</tt> key. Fatal errors will be described under <tt>:error</tt>.
73
+
74
+ If you've installed the gem and wondering why there's no tests - check out the svn version. I've kept the specs out of the gem as I have a decent amount of test data in there, which really isn't needed unless you want to submit patches.
@@ -0,0 +1,117 @@
1
+ require 'rake'
2
+ require 'rake/packagetask'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+ require 'rdoc/rdoc'
6
+ require 'rdoc/generators/html_generator'
7
+ require 'rdoc/generators/template/html/html'
8
+
9
+ module Generators
10
+ class HtmlFile < ContextUser
11
+ alias_method :core_attribute_values, :file_attribute_values
12
+
13
+ def file_attribute_values
14
+ core_attribute_values
15
+
16
+ @values["analytics"] = @options.analytics if @options.analytics
17
+ end
18
+ end
19
+
20
+ class HtmlClass < ContextUser
21
+ alias_method :core_attribute_values, :class_attribute_values
22
+
23
+ def class_attribute_values
24
+ core_attribute_values
25
+
26
+ @values["analytics"] = @options.analytics if @options.analytics
27
+ end
28
+ end
29
+ end
30
+
31
+ class Options
32
+ attr_accessor :analytics
33
+
34
+ module OptionList
35
+ OPTION_LIST << [
36
+ "--analytics", "-y", "code", "Google Analytics Code"
37
+ ]
38
+ end
39
+
40
+ alias_method :core_parse, :parse
41
+
42
+ def parse(argv, generators)
43
+ core_parse(argv, generators)
44
+
45
+ old_args = ARGV.dup
46
+ ARGV.replace(argv)
47
+
48
+ go = GetoptLong.new(*OptionList.options)
49
+ go.quiet = true
50
+
51
+ go.each do |opt, arg|
52
+ case opt
53
+ when "--analytics"
54
+ @analytics = arg.strip
55
+ end
56
+ end
57
+
58
+ ARGV.replace(old_args)
59
+ end
60
+
61
+ end
62
+
63
+ module RDoc
64
+ module Page
65
+ remove_const :FOOTER
66
+ const_set :FOOTER, %{
67
+ <div id="validator-badges">
68
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
69
+ </div>
70
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
71
+ </script>
72
+ <script type="text/javascript">
73
+ _uacct = "%analytics%";
74
+ urchinTracker();
75
+ </script>
76
+
77
+ </body>
78
+ </html>
79
+ }
80
+
81
+ remove_const :BODY
82
+ const_set :BODY, HEADER + %{
83
+
84
+ !INCLUDE! <!-- banner header -->
85
+
86
+ <div id="bodyContent">
87
+
88
+ } + METHOD_LIST + %{
89
+
90
+ </div>
91
+
92
+ } + FOOTER
93
+ end
94
+ end
95
+
96
+ desc 'Generate documentation'
97
+ Rake::RDocTask.new(:rdoc) do |rdoc|
98
+ rdoc.rdoc_dir = 'rdoc'
99
+ rdoc.title = 'Riddle - Ruby Sphinx Client'
100
+ rdoc.options << '--line-numbers' << '--inline-source'
101
+ rdoc.options << '-y US-2475317-6'
102
+ rdoc.rdoc_files.include('README')
103
+ rdoc.rdoc_files.include('lib/**/*.rb')
104
+ end
105
+
106
+ desc "Run Riddle's specs"
107
+ Spec::Rake::SpecTask.new do |t|
108
+ t.libs << 'lib'
109
+ t.spec_files = FileList['spec/**/*_spec.rb']
110
+ end
111
+
112
+ Spec::Rake::SpecTask.new(:rcov) do |t|
113
+ t.libs << 'lib'
114
+ t.spec_files = FileList['spec/**/*_spec.rb']
115
+ t.rcov = true
116
+ t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
117
+ end
@@ -0,0 +1,44 @@
1
+ module Riddle
2
+ class Client
3
+ # Used for querying Sphinx.
4
+ class Filter
5
+ attr_accessor :attribute, :values, :exclude
6
+
7
+ # Attribute name, values (which can be an array or a range), and whether
8
+ # the filter should be exclusive.
9
+ def initialize(attribute, values, exclude=false)
10
+ @attribute, @values, @exclude = attribute, values, exclude
11
+ end
12
+
13
+ def exclude?
14
+ self.exclude
15
+ end
16
+
17
+ # Returns the message for this filter to send to the Sphinx service
18
+ def query_message
19
+ message = Message.new
20
+
21
+ message.append_string self.attribute
22
+ case self.values
23
+ when Range
24
+ if self.values.first.is_a?(Float) && self.values.last.is_a?(Float)
25
+ message.append_int FilterTypes[:float_range]
26
+ message.append_floats self.values.first, self.values.last
27
+ else
28
+ message.append_int FilterTypes[:range]
29
+ message.append_ints self.values.first, self.values.last
30
+ end
31
+ when Array
32
+ message.append_int FilterTypes[:values]
33
+ message.append_int self.values.length
34
+ # using to_f is a hack from the php client - to workaround 32bit
35
+ # signed ints on x32 platforms
36
+ message.append_ints *self.values.collect { |val| val.to_f }
37
+ end
38
+ message.append_int self.exclude? ? 1 : 0
39
+
40
+ message.to_s
41
+ end
42
+ end
43
+ end
44
+ end