Commit ad8ab8dfc2ee6fb5e1316500cb357cf971e3f56e

Authored by MoisesMachado
1 parent badd8898

ActionItem129: acts_as_ferret removed


git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1831 3f533792-8f58-4932-b0fe-aaf55b0a4547
vendor/plugins/acts_as_ferret/LICENSE
@@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
1 -Copyright (c) 2006 Kasper Weibel, Jens Kraemer  
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.  
vendor/plugins/acts_as_ferret/README
@@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
1 -= acts_as_ferret  
2 -  
3 -This ActiveRecord mixin adds full text search capabilities to any Rails model.  
4 -  
5 -It is heavily based on the original acts_as_ferret plugin done by  
6 -Kasper Weibel and a modified version done by Thomas Lockney, which  
7 -both can be found on http://ferret.davebalmain.com/trac/wiki/FerretOnRails  
8 -  
9 -== Installation  
10 -  
11 -=== Installation inside your Rails project via script/plugin  
12 -  
13 -script/plugin install svn://projects.jkraemer.net/acts_as_ferret/trunk/plugin/acts_as_ferret  
14 -  
15 -  
16 -=== System-wide installation with Rubygems  
17 -  
18 -<tt>sudo gem install acts_as_ferret</tt>  
19 -  
20 -To use acts_as_ferret in your project, add the following line to your  
21 -project's config/environment.rb:  
22 -  
23 -<tt>require 'acts_as_ferret'</tt>  
24 -  
25 -  
26 -== Usage  
27 -  
28 -include the following in your model class (specifiying the fields you want to get indexed):  
29 -  
30 -<tt>acts_as_ferret :fields => [ :title, :description ]</tt>  
31 -  
32 -now you can use ModelClass.find_by_contents(query) to find instances of your model  
33 -whose indexed fields match a given query. All query terms are required by default,  
34 -but explicit OR queries are possible. This differs from the ferret default, but  
35 -imho is the more often needed/expected behaviour (more query terms result in  
36 -less results).  
37 -  
38 -Please see ActsAsFerret::ActMethods#acts_as_ferret for more information.  
39 -  
40 -== License  
41 -  
42 -Released under the MIT license.  
43 -  
44 -== Authors  
45 -  
46 -* Kasper Weibel Nielsen-Refs (original author)  
47 -* Jens Kraemer <jk@jkraemer.net> (current maintainer)  
48 -  
49 -  
vendor/plugins/acts_as_ferret/config/ferret_server.yml
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -production:  
2 - host: ferret.yourdomain.com  
3 - port: 9010  
4 - pid_file: log/ferret.pid  
5 -development:  
6 - host: localhost  
7 - port: 9010  
8 - pid_file: log/ferret.pid  
9 -test:  
10 - host: localhost  
11 - port: 9009  
12 - pid_file: log/ferret.pid  
vendor/plugins/acts_as_ferret/init.rb
@@ -1,22 +0,0 @@ @@ -1,22 +0,0 @@
1 -# Copyright (c) 2006 Kasper Weibel Nielsen-Refs, Thomas Lockney, Jens Krämer  
2 -#  
3 -# Permission is hereby granted, free of charge, to any person obtaining a copy  
4 -# of this software and associated documentation files (the "Software"), to deal  
5 -# in the Software without restriction, including without limitation the rights  
6 -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
7 -# copies of the Software, and to permit persons to whom the Software is  
8 -# furnished to do so, subject to the following conditions:  
9 -#  
10 -# The above copyright notice and this permission notice shall be included in all  
11 -# copies or substantial portions of the Software.  
12 -#  
13 -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
14 -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
15 -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
16 -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
17 -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  
18 -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  
19 -# SOFTWARE.  
20 -  
21 -require 'acts_as_ferret'  
22 -  
vendor/plugins/acts_as_ferret/install.rb
@@ -1,19 +0,0 @@ @@ -1,19 +0,0 @@
1 -# acts_as_ferret install script  
2 -require 'fileutils'  
3 -  
4 -def install(file)  
5 - puts "Installing: #{file}"  
6 - target = File.join(File.dirname(__FILE__), '..', '..', '..', file)  
7 - if File.exists?(target)  
8 - puts "target #{target} already exists, skipping"  
9 - else  
10 - FileUtils.cp File.join(File.dirname(__FILE__), file), target  
11 - end  
12 -end  
13 -  
14 -install File.join( 'script', 'ferret_start' )  
15 -install File.join( 'script', 'ferret_stop' )  
16 -install File.join( 'config', 'ferret_server.yml' )  
17 -  
18 -puts IO.read(File.join(File.dirname(__FILE__), 'README'))  
19 -  
vendor/plugins/acts_as_ferret/lib/act_methods.rb
@@ -1,242 +0,0 @@ @@ -1,242 +0,0 @@
1 -module ActsAsFerret #:nodoc:  
2 -  
3 - # This module defines the acts_as_ferret method and is included into  
4 - # ActiveRecord::Base  
5 - module ActMethods  
6 -  
7 -  
8 - def reloadable?; false end  
9 -  
10 - # declares a class as ferret-searchable.  
11 - #  
12 - # ====options:  
13 - # fields:: names all fields to include in the index. If not given,  
14 - # all attributes of the class will be indexed. You may also give  
15 - # symbols pointing to instance methods of your model here, i.e.  
16 - # to retrieve and index data from a related model.  
17 - #  
18 - # additional_fields:: names fields to include in the index, in addition  
19 - # to those derived from the db scheme. use if you want  
20 - # to add custom fields derived from methods to the db  
21 - # fields (which will be picked by aaf). This option will  
22 - # be ignored when the fields option is given, in that  
23 - # case additional fields get specified there.  
24 - #  
25 - # index_dir:: declares the directory where to put the index for this class.  
26 - # The default is RAILS_ROOT/index/RAILS_ENV/CLASSNAME.  
27 - # The index directory will be created if it doesn't exist.  
28 - #  
29 - # single_index:: set this to true to let this class use a Ferret  
30 - # index that is shared by all classes having :single_index set to true.  
31 - # :store_class_name is set to true implicitly, as well as index_dir, so  
32 - # don't bother setting these when using this option. the shared index  
33 - # will be located in index/<RAILS_ENV>/shared .  
34 - #  
35 - # store_class_name:: to make search across multiple models (with either  
36 - # single_index or the multi_search method) useful, set  
37 - # this to true. the model class name will be stored in a keyword field  
38 - # named class_name  
39 - #  
40 - # reindex_batch_size:: reindexing is done in batches of this size, default is 1000  
41 - #  
42 - # ferret:: Hash of Options that directly influence the way the Ferret engine works. You  
43 - # can use most of the options the Ferret::I class accepts here, too. Among the  
44 - # more useful are:  
45 - #  
46 - # or_default:: whether query terms are required by  
47 - # default (the default, false), or not (true)  
48 - #  
49 - # analyzer:: the analyzer to use for query parsing (default: nil,  
50 - # which means the ferret StandardAnalyzer gets used)  
51 - #  
52 - # default_field:: use to set one or more fields that are searched for query terms  
53 - # that don't have an explicit field list. This list should *not*  
54 - # contain any untokenized fields. If it does, you're asking  
55 - # for trouble (i.e. not getting results for queries having  
56 - # stop words in them). Aaf by default initializes the default field  
57 - # list to contain all tokenized fields. If you use :single_index => true,  
58 - # you really should set this option specifying your default field  
59 - # list (which should be equal in all your classes sharing the index).  
60 - # Otherwise you might get incorrect search results and you won't get  
61 - # any lazy loading of stored field data.  
62 - #  
63 - # For downwards compatibility reasons you can also specify the Ferret options in the  
64 - # last Hash argument.  
65 - def acts_as_ferret(options={}, ferret_options={})  
66 -  
67 - # force local mode if running *inside* the Ferret server - somewhere the  
68 - # real indexing has to be done after all :-)  
69 - # Usually the automatic detection of server mode works fine, however if you  
70 - # require your model classes in environment.rb they will get loaded before the  
71 - # DRb server is started, so this code is executed too early and detection won't  
72 - # work. In this case you'll get endless loops resulting in "stack level too deep"  
73 - # errors.  
74 - # To get around this, start the server with the environment variable  
75 - # FERRET_USE_LOCAL_INDEX set to '1'.  
76 - logger.debug "Asked for a remote server ? #{options[:remote].inspect}, ENV[\"FERRET_USE_LOCAL_INDEX\"] is #{ENV["FERRET_USE_LOCAL_INDEX"].inspect}, looks like we are#{ActsAsFerret::Remote::Server.running || ENV['FERRET_USE_LOCAL_INDEX'] ? '' : ' not'} the server"  
77 - options.delete(:remote) if ENV["FERRET_USE_LOCAL_INDEX"] || ActsAsFerret::Remote::Server.running  
78 -  
79 - if options[:remote] && options[:remote] !~ /^druby/  
80 - # read server location from config/ferret_server.yml  
81 - options[:remote] = ActsAsFerret::Remote::Config.load("#{RAILS_ROOT}/config/ferret_server.yml")[:uri] rescue nil  
82 - end  
83 -  
84 - if options[:remote]  
85 - logger.debug "Will use remote index server which should be available at #{options[:remote]}"  
86 - else  
87 - logger.debug "Will use local index."  
88 - end  
89 -  
90 -  
91 - extend ClassMethods  
92 - extend SharedIndexClassMethods if options[:single_index]  
93 -  
94 - include InstanceMethods  
95 - include MoreLikeThis::InstanceMethods  
96 -  
97 - # AR hooks  
98 - after_create :ferret_create  
99 - after_update :ferret_update  
100 - after_destroy :ferret_destroy  
101 -  
102 - cattr_accessor :aaf_configuration  
103 -  
104 - # default config  
105 - self.aaf_configuration = {  
106 - :index_dir => "#{ActsAsFerret::index_dir}/#{self.name.underscore}",  
107 - :store_class_name => false,  
108 - :name => self.table_name,  
109 - :class_name => self.name,  
110 - :single_index => false,  
111 - :reindex_batch_size => 1000,  
112 - :ferret => {}, # Ferret config Hash  
113 - :ferret_fields => {} # list of indexed fields that will be filled later  
114 - }  
115 -  
116 - # merge aaf options with args  
117 - aaf_configuration.update(options) if options.is_a?(Hash)  
118 - # apply appropriate settings for shared index  
119 - if aaf_configuration[:single_index]  
120 - aaf_configuration[:index_dir] = "#{ActsAsFerret::index_dir}/shared"  
121 - aaf_configuration[:store_class_name] = true  
122 - end  
123 -  
124 - # set ferret default options  
125 - aaf_configuration[:ferret].reverse_merge!( :or_default => false,  
126 - :handle_parse_errors => true,  
127 - :default_field => nil # will be set later on  
128 - #:max_clauses => 512,  
129 - #:analyzer => Ferret::Analysis::StandardAnalyzer.new,  
130 - # :wild_card_downcase => true  
131 - )  
132 -  
133 - # merge ferret options with those from second parameter hash  
134 - aaf_configuration[:ferret].update(ferret_options) if ferret_options.is_a?(Hash)  
135 -  
136 - unless options[:remote]  
137 - ActsAsFerret::ensure_directory aaf_configuration[:index_dir]  
138 - aaf_configuration[:index_base_dir] = aaf_configuration[:index_dir]  
139 - aaf_configuration[:index_dir] = find_last_index_version(aaf_configuration[:index_dir])  
140 - logger.debug "using index in #{aaf_configuration[:index_dir]}"  
141 - end  
142 -  
143 - # these properties are somewhat vital to the plugin and shouldn't  
144 - # be overwritten by the user:  
145 - aaf_configuration[:ferret].update(  
146 - :key => (aaf_configuration[:single_index] ? [:id, :class_name] : :id),  
147 - :path => aaf_configuration[:index_dir],  
148 - :auto_flush => true, # slower but more secure in terms of locking problems TODO disable when running in drb mode?  
149 - :create_if_missing => true  
150 - )  
151 -  
152 - if aaf_configuration[:fields]  
153 - add_fields(aaf_configuration[:fields])  
154 - else  
155 - add_fields(self.new.attributes.keys.map { |k| k.to_sym })  
156 - add_fields(aaf_configuration[:additional_fields])  
157 - end  
158 -  
159 - # now that all fields have been added, we can initialize the default  
160 - # field list to be used by the query parser.  
161 - # It will include all content fields *not* marked as :untokenized.  
162 - # This fixes the otherwise failing CommentTest#test_stopwords. Basically  
163 - # this means that by default only tokenized fields (which is the default)  
164 - # will be searched. If you want to search inside the contents of an  
165 - # untokenized field, you'll have to explicitly specify it in your query.  
166 - #  
167 - # Unfortunately this is not very useful with a shared index (see  
168 - # http://projects.jkraemer.net/acts_as_ferret/ticket/85)  
169 - # You should consider specifying the default field list to search for as  
170 - # part of the ferret_options hash in your call to acts_as_ferret.  
171 - aaf_configuration[:ferret][:default_field] ||= if aaf_configuration[:single_index]  
172 - logger.warn "You really should set the acts_as_ferret :default_field option when using a shared index!"  
173 - '*'  
174 - else  
175 - aaf_configuration[:ferret_fields].keys.select do |f|  
176 - aaf_configuration[:ferret_fields][f][:index] != :untokenized  
177 - end  
178 - end  
179 - logger.info "default field list: #{aaf_configuration[:ferret][:default_field].inspect}"  
180 -  
181 - if options[:remote]  
182 - aaf_index.ensure_index_exists  
183 - end  
184 - end  
185 -  
186 -  
187 - protected  
188 -  
189 - # find the most recent version of an index  
190 - def find_last_index_version(basedir)  
191 - # check for versioned index  
192 - versions = Dir.entries(basedir).select do |f|  
193 - dir = File.join(basedir, f)  
194 - File.directory?(dir) && File.file?(File.join(dir, 'segments')) && f =~ /^\d+(_\d+)?$/  
195 - end  
196 - if versions.any?  
197 - # select latest version  
198 - versions.sort!  
199 - File.join basedir, versions.last  
200 - else  
201 - basedir  
202 - end  
203 - end  
204 -  
205 -  
206 - # helper that defines a method that adds the given field to a ferret  
207 - # document instance  
208 - def define_to_field_method(field, options = {})  
209 - options.reverse_merge!( :store => :no,  
210 - :highlight => :yes,  
211 - :index => :yes,  
212 - :term_vector => :with_positions_offsets,  
213 - :boost => 1.0 )  
214 - options[:term_vector] = :no if options[:index] == :no  
215 - aaf_configuration[:ferret_fields][field] = options  
216 - define_method("#{field}_to_ferret".to_sym) do  
217 - begin  
218 - val = content_for_field_name(field)  
219 - rescue  
220 - logger.warn("Error retrieving value for field #{field}: #{$!}")  
221 - val = ''  
222 - end  
223 - logger.debug("Adding field #{field} with value '#{val}' to index")  
224 - val  
225 - end  
226 - end  
227 -  
228 - def add_fields(field_config)  
229 - if field_config.is_a? Hash  
230 - field_config.each_pair do |key,val|  
231 - define_to_field_method(key,val)  
232 - end  
233 - elsif field_config.respond_to?(:each)  
234 - field_config.each do |field|  
235 - define_to_field_method(field)  
236 - end  
237 - end  
238 - end  
239 -  
240 - end  
241 -  
242 -end  
vendor/plugins/acts_as_ferret/lib/acts_as_ferret.rb
@@ -1,160 +0,0 @@ @@ -1,160 +0,0 @@
1 -# Copyright (c) 2006 Kasper Weibel Nielsen-Refs, Thomas Lockney, Jens Krämer  
2 -#  
3 -# Permission is hereby granted, free of charge, to any person obtaining a copy  
4 -# of this software and associated documentation files (the "Software"), to deal  
5 -# in the Software without restriction, including without limitation the rights  
6 -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
7 -# copies of the Software, and to permit persons to whom the Software is  
8 -# furnished to do so, subject to the following conditions:  
9 -#  
10 -# The above copyright notice and this permission notice shall be included in all  
11 -# copies or substantial portions of the Software.  
12 -#  
13 -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
14 -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
15 -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
16 -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
17 -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  
18 -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  
19 -# SOFTWARE.  
20 -  
21 -require 'active_support'  
22 -require 'active_record'  
23 -require 'set'  
24 -require 'ferret'  
25 -  
26 -require 'ferret_extensions'  
27 -require 'act_methods'  
28 -require 'class_methods'  
29 -require 'shared_index_class_methods'  
30 -require 'ferret_result'  
31 -require 'instance_methods'  
32 -  
33 -require 'multi_index'  
34 -require 'more_like_this'  
35 -  
36 -require 'index'  
37 -require 'local_index'  
38 -require 'shared_index'  
39 -require 'remote_index'  
40 -  
41 -require 'ferret_server'  
42 -  
43 -  
44 -# The Rails ActiveRecord Ferret Mixin.  
45 -#  
46 -# This mixin adds full text search capabilities to any Rails model.  
47 -#  
48 -# The current version emerged from on the original acts_as_ferret plugin done by  
49 -# Kasper Weibel and a modified version done by Thomas Lockney, which both can be  
50 -# found on the Ferret Wiki: http://ferret.davebalmain.com/trac/wiki/FerretOnRails.  
51 -#  
52 -# basic usage:  
53 -# include the following in your model class (specifiying the fields you want to get indexed):  
54 -# acts_as_ferret :fields => [ :title, :description ]  
55 -#  
56 -# now you can use ModelClass.find_by_contents(query) to find instances of your model  
57 -# whose indexed fields match a given query. All query terms are required by default, but  
58 -# explicit OR queries are possible. This differs from the ferret default, but imho is the more  
59 -# often needed/expected behaviour (more query terms result in less results).  
60 -#  
61 -# Released under the MIT license.  
62 -#  
63 -# Authors:  
64 -# Kasper Weibel Nielsen-Refs (original author)  
65 -# Jens Kraemer <jk@jkraemer.net> (active maintainer)  
66 -#  
67 -module ActsAsFerret  
68 -  
69 - # global Hash containing all multi indexes created by all classes using the plugin  
70 - # key is the concatenation of alphabetically sorted names of the classes the  
71 - # searcher searches.  
72 - @@multi_indexes = Hash.new  
73 - def self.multi_indexes; @@multi_indexes end  
74 -  
75 - # global Hash containing the ferret indexes of all classes using the plugin  
76 - # key is the index directory.  
77 - @@ferret_indexes = Hash.new  
78 - def self.ferret_indexes; @@ferret_indexes end  
79 -  
80 -  
81 - # decorator that adds a total_hits accessor to search result arrays  
82 - class SearchResults  
83 - attr_reader :total_hits  
84 - def initialize(results, total_hits)  
85 - @results = results  
86 - @total_hits = total_hits  
87 - end  
88 - def method_missing(symbol, *args, &block)  
89 - @results.send(symbol, *args, &block)  
90 - end  
91 - def respond_to?(name)  
92 - self.methods.include?(name) || @results.respond_to?(name)  
93 - end  
94 - end  
95 -  
96 - def self.ensure_directory(dir)  
97 - FileUtils.mkdir_p dir unless (File.directory?(dir) || File.symlink?(dir))  
98 - end  
99 -  
100 - # make sure the default index base dir exists. by default, all indexes are created  
101 - # under RAILS_ROOT/index/RAILS_ENV  
102 - def self.init_index_basedir  
103 - index_base = "#{RAILS_ROOT}/index"  
104 - @@index_dir = "#{index_base}/#{RAILS_ENV}"  
105 - end  
106 -  
107 - mattr_accessor :index_dir  
108 - init_index_basedir  
109 -  
110 - def self.append_features(base)  
111 - super  
112 - base.extend(ClassMethods)  
113 - end  
114 -  
115 - # builds a FieldInfos instance for creation of an index containing fields  
116 - # for the given model classes.  
117 - def self.field_infos(models)  
118 - # default attributes for fields  
119 - fi = Ferret::Index::FieldInfos.new(:store => :no,  
120 - :index => :yes,  
121 - :term_vector => :no,  
122 - :boost => 1.0)  
123 - # primary key  
124 - fi.add_field(:id, :store => :yes, :index => :untokenized)  
125 - fields = {}  
126 - have_class_name = false  
127 - models.each do |model|  
128 - fields.update(model.aaf_configuration[:ferret_fields])  
129 - # class_name  
130 - if !have_class_name && model.aaf_configuration[:store_class_name]  
131 - fi.add_field(:class_name, :store => :yes, :index => :untokenized)  
132 - have_class_name = true  
133 - end  
134 - end  
135 - fields.each_pair do |field, options|  
136 - fi.add_field(field, { :store => :no,  
137 - :index => :yes }.update(options))  
138 - end  
139 - return fi  
140 - end  
141 -  
142 - def self.close_multi_indexes  
143 - # close combined index readers, just in case  
144 - # this seems to fix a strange test failure that seems to relate to a  
145 - # multi_index looking at an old version of the content_base index.  
146 - multi_indexes.each_pair do |key, index|  
147 - # puts "#{key} -- #{self.name}"  
148 - # TODO only close those where necessary (watch inheritance, where  
149 - # self.name is base class of a class where key is made from)  
150 - index.close #if key =~ /#{self.name}/  
151 - end  
152 - multi_indexes.clear  
153 - end  
154 -  
155 -end  
156 -  
157 -# include acts_as_ferret method into ActiveRecord::Base  
158 -ActiveRecord::Base.extend ActsAsFerret::ActMethods  
159 -  
160 -  
vendor/plugins/acts_as_ferret/lib/class_methods.rb
@@ -1,316 +0,0 @@ @@ -1,316 +0,0 @@
1 -module ActsAsFerret  
2 -  
3 - module ClassMethods  
4 -  
5 - # rebuild the index from all data stored for this model.  
6 - # This is called automatically when no index exists yet.  
7 - #  
8 - # When calling this method manually, you can give any additional  
9 - # model classes that should also go into this index as parameters.  
10 - # Useful when using the :single_index option.  
11 - # Note that attributes named the same in different models will share  
12 - # the same field options in the shared index.  
13 - def rebuild_index(*models)  
14 - models << self unless models.include?(self)  
15 - aaf_index.rebuild_index models.map(&:to_s)  
16 - index_dir = find_last_index_version(aaf_configuration[:index_base_dir]) unless aaf_configuration[:remote]  
17 - end  
18 -  
19 - # runs across all records yielding those to be indexed when the index is rebuilt  
20 - def records_for_rebuild(batch_size = 1000)  
21 - transaction do  
22 - if connection.class.name =~ /Mysql/ && primary_key == 'id'  
23 - logger.info "using mysql specific batched find :all"  
24 - offset = 0  
25 - while (rows = find :all, :conditions => ["id > ?", offset ], :limit => batch_size).any?  
26 - offset = rows.last.id  
27 - yield rows, offset  
28 - end  
29 - else  
30 - # sql server adapter won't batch correctly without defined ordering  
31 - order = "#{primary_key} ASC" if connection.class.name =~ /SQLServer/  
32 - 0.step(self.count, batch_size) do |offset|  
33 - yield find( :all, :limit => batch_size, :offset => offset, :order => order ), offset  
34 - end  
35 - end  
36 - end  
37 - end  
38 -  
39 - # Switches this class to a new index located in dir.  
40 - # Used by the DRb server when switching to a new index version.  
41 - def index_dir=(dir)  
42 - logger.debug "changing index dir to #{dir}"  
43 - aaf_configuration[:index_dir] = aaf_configuration[:ferret][:path] = dir  
44 - aaf_index.reopen!  
45 - logger.debug "index dir is now #{dir}"  
46 - end  
47 -  
48 - # Retrieve the index instance for this model class. This can either be a  
49 - # LocalIndex, or a RemoteIndex instance.  
50 - #  
51 - # Index instances are stored in a hash, using the index directory  
52 - # as the key. So model classes sharing a single index will share their  
53 - # Index object, too.  
54 - def aaf_index  
55 - ActsAsFerret::ferret_indexes[aaf_configuration[:index_dir]] ||= create_index_instance  
56 - end  
57 -  
58 - # Finds instances by searching the Ferret index. Terms are ANDed by default, use  
59 - # OR between terms for ORed queries. Or specify +:or_default => true+ in the  
60 - # +:ferret+ options hash of acts_as_ferret.  
61 - #  
62 - # == options:  
63 - # offset:: first hit to retrieve (useful for paging)  
64 - # limit:: number of hits to retrieve, or :all to retrieve  
65 - # all results  
66 - # lazy:: Array of field names whose contents should be read directly  
67 - # from the index. Those fields have to be marked  
68 - # +:store => :yes+ in their field options. Give true to get all  
69 - # stored fields. Note that if you have a shared index, you have  
70 - # to explicitly state the fields you want to fetch, true won't  
71 - # work here)  
72 - # models:: only for single_index scenarios: an Array of other Model classes to  
73 - # include in this search. Use :all to query all models.  
74 - #  
75 - # +find_options+ is a hash passed on to active_record's find when  
76 - # retrieving the data from db, useful to i.e. prefetch relationships with  
77 - # :include or to specify additional filter criteria with :conditions.  
78 - #  
79 - # This method returns a +SearchResults+ instance, which really is an Array that has  
80 - # been decorated with a total_hits attribute holding the total number of hits.  
81 - #  
82 - # Please keep in mind that the number of total hits might be wrong if you specify  
83 - # both ferret options and active record find_options that somehow limit the result  
84 - # set (e.g. +:num_docs+ and some +:conditions+).  
85 - def find_with_ferret(q, options = {}, find_options = {})  
86 - total_hits, result = find_records_lazy_or_not q, options, find_options  
87 - logger.debug "Query: #{q}\ntotal hits: #{total_hits}, results delivered: #{result.size}"  
88 - return SearchResults.new(result, total_hits)  
89 - end  
90 - alias find_by_contents find_with_ferret  
91 -  
92 -  
93 -  
94 - # Returns the total number of hits for the given query  
95 - # To count the results of a multi_search query, specify an array of  
96 - # class names with the :models option.  
97 - def total_hits(q, options={})  
98 - if models = options[:models]  
99 - options[:models] = add_self_to_model_list_if_necessary(models).map(&:to_s)  
100 - end  
101 - aaf_index.total_hits(q, options)  
102 - end  
103 -  
104 - # Finds instance model name, ids and scores by contents.  
105 - # Useful e.g. if you want to search across models or do not want to fetch  
106 - # all result records (yet).  
107 - #  
108 - # Options are the same as for find_by_contents  
109 - #  
110 - # A block can be given too, it will be executed with every result:  
111 - # find_id_by_contents(q, options) do |model, id, score|  
112 - # id_array << id  
113 - # scores_by_id[id] = score  
114 - # end  
115 - # NOTE: in case a block is given, only the total_hits value will be returned  
116 - # instead of the [total_hits, results] array!  
117 - #  
118 - def find_id_by_contents(q, options = {}, &block)  
119 - deprecated_options_support(options)  
120 - aaf_index.find_id_by_contents(q, options, &block)  
121 - end  
122 -  
123 - # requires the store_class_name option of acts_as_ferret to be true  
124 - # for all models queried this way.  
125 - def multi_search(query, additional_models = [], options = {}, find_options = {})  
126 - result = []  
127 -  
128 - if options[:lazy]  
129 - logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?  
130 - total_hits = id_multi_search(query, additional_models, options) do |model, id, score, data|  
131 - result << FerretResult.new(model, id, score, data)  
132 - end  
133 - else  
134 - id_arrays = {}  
135 - rank = 0  
136 - total_hits = id_multi_search(query, additional_models, options) do |model, id, score, data|  
137 - id_arrays[model] ||= {}  
138 - id_arrays[model][id] = [ rank += 1, score ]  
139 - end  
140 - result = retrieve_records(id_arrays, find_options)  
141 - end  
142 -  
143 - SearchResults.new(result, total_hits)  
144 - end  
145 -  
146 - # returns an array of hashes, each containing :class_name,  
147 - # :id and :score for a hit.  
148 - #  
149 - # if a block is given, class_name, id and score of each hit will  
150 - # be yielded, and the total number of hits is returned.  
151 - def id_multi_search(query, additional_models = [], options = {}, &proc)  
152 - deprecated_options_support(options)  
153 - additional_models = add_self_to_model_list_if_necessary(additional_models)  
154 - aaf_index.id_multi_search(query, additional_models.map(&:to_s), options, &proc)  
155 - end  
156 -  
157 -  
158 - protected  
159 -  
160 - def add_self_to_model_list_if_necessary(models)  
161 - models = [ models ] unless models.is_a? Array  
162 - models << self unless models.include?(self)  
163 - end  
164 -  
165 - def find_records_lazy_or_not(q, options = {}, find_options = {})  
166 - if options[:lazy]  
167 - logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?  
168 - lazy_find_by_contents q, options  
169 - else  
170 - ar_find_by_contents q, options, find_options  
171 - end  
172 - end  
173 -  
174 - def ar_find_by_contents(q, options = {}, find_options = {})  
175 - result_ids = {}  
176 - total_hits = find_id_by_contents(q, options) do |model, id, score, data|  
177 - # stores ids, index of each id for later ordering of  
178 - # results, and score  
179 - result_ids[id] = [ result_ids.size + 1, score ]  
180 - end  
181 -  
182 - result = retrieve_records( { self.name => result_ids }, find_options )  
183 -  
184 - if find_options[:conditions]  
185 - if options[:limit] != :all  
186 - # correct result size if the user specified conditions  
187 - # wenn conditions: options[:limit] != :all --> ferret-query mit :all wiederholen und select count machen  
188 - result_ids = {}  
189 - find_id_by_contents(q, options.update(:limit => :all)) do |model, id, score, data|  
190 - result_ids[id] = [ result_ids.size + 1, score ]  
191 - end  
192 - total_hits = count_records( { self.name => result_ids }, find_options )  
193 - else  
194 - total_hits = result.length  
195 - end  
196 - end  
197 -  
198 - [ total_hits, result ]  
199 - end  
200 -  
201 - def lazy_find_by_contents(q, options = {})  
202 - result = []  
203 - total_hits = find_id_by_contents(q, options) do |model, id, score, data|  
204 - result << FerretResult.new(model, id, score, data)  
205 - end  
206 - [ total_hits, result ]  
207 - end  
208 -  
209 -  
210 - def model_find(model, id, find_options = {})  
211 - model.constantize.find(id, find_options)  
212 - end  
213 -  
214 - # retrieves search result records from a data structure like this:  
215 - # { 'Model1' => { '1' => [ rank, score ], '2' => [ rank, score ] }  
216 - #  
217 - # TODO: in case of STI AR will filter out hits from other  
218 - # classes for us, but this  
219 - # will lead to less results retrieved --> scoping of ferret query  
220 - # to self.class is still needed.  
221 - # from the ferret ML (thanks Curtis Hatter)  
222 - # > I created a method in my base STI class so I can scope my query. For scoping  
223 - # > I used something like the following line:  
224 - # >  
225 - # > query << " role:#{self.class.eql?(Contents) '*' : self.class}"  
226 - # >  
227 - # > Though you could make it more generic by simply asking  
228 - # > "self.descends_from_active_record?" which is how rails decides if it should  
229 - # > scope your "find" query for STI models. You can check out "base.rb" in  
230 - # > activerecord to see that.  
231 - # but maybe better do the scoping in find_id_by_contents...  
232 - def retrieve_records(id_arrays, find_options = {})  
233 - result = []  
234 - # get objects for each model  
235 - id_arrays.each do |model, id_array|  
236 - next if id_array.empty?  
237 - begin  
238 - model = model.constantize  
239 - rescue  
240 - raise "Please use ':store_class_name => true' if you want to use multi_search.\n#{$!}"  
241 - end  
242 -  
243 - # check for include association that might only exist on some models in case of multi_search  
244 - filtered_include_options = []  
245 - if include_options = find_options[:include]  
246 - include_options.each do |include_option|  
247 - filtered_include_options << include_option if model.reflections.has_key?(include_option.is_a?(Hash) ? include_option.keys[0].to_sym : include_option.to_sym)  
248 - end  
249 - end  
250 - filtered_include_options=nil if filtered_include_options.empty?  
251 -  
252 - # fetch  
253 - tmp_result = nil  
254 - model.send(:with_scope, :find => find_options) do  
255 - tmp_result = model.find( :all, :conditions => [  
256 - "#{model.table_name}.#{model.primary_key} in (?)", id_array.keys ],  
257 - :include => filtered_include_options )  
258 - end  
259 -  
260 - # set scores and rank  
261 - tmp_result.each do |record|  
262 - record.ferret_rank, record.ferret_score = id_array[record.id.to_s]  
263 - end  
264 - # merge with result array  
265 - result.concat tmp_result  
266 - end  
267 -  
268 - # order results as they were found by ferret, unless an AR :order  
269 - # option was given  
270 - result.sort! { |a, b| a.ferret_rank <=> b.ferret_rank } unless find_options[:order]  
271 - return result  
272 - end  
273 -  
274 - def count_records(id_arrays, find_options = {})  
275 - count = 0  
276 - id_arrays.each do |model, id_array|  
277 - next if id_array.empty?  
278 - begin  
279 - model = model.constantize  
280 - model.send(:with_scope, :find => find_options) do  
281 - count += model.count(:conditions => [ "#{model.table_name}.#{model.primary_key} in (?)",  
282 - id_array.keys ])  
283 - end  
284 - rescue TypeError  
285 - raise "#{model} must use :store_class_name option if you want to use multi_search against it.\n#{$!}"  
286 - end  
287 - end  
288 - count  
289 - end  
290 -  
291 - def deprecated_options_support(options)  
292 - if options[:num_docs]  
293 - logger.warn ":num_docs is deprecated, use :limit instead!"  
294 - options[:limit] ||= options[:num_docs]  
295 - end  
296 - if options[:first_doc]  
297 - logger.warn ":first_doc is deprecated, use :offset instead!"  
298 - options[:offset] ||= options[:first_doc]  
299 - end  
300 - end  
301 -  
302 - # creates a new Index instance.  
303 - def create_index_instance  
304 - if aaf_configuration[:remote]  
305 - RemoteIndex  
306 - elsif aaf_configuration[:single_index]  
307 - SharedIndex  
308 - else  
309 - LocalIndex  
310 - end.new(aaf_configuration)  
311 - end  
312 -  
313 - end  
314 -  
315 -end  
316 -  
vendor/plugins/acts_as_ferret/lib/ferret_cap_tasks.rb
@@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
1 -# Ferret DRb server Capistrano tasks  
2 -# Usage:  
3 -# Add require 'vendor/plugins/acts_as_ferret/lib/ferret_cap_tasks' to your  
4 -# config/deploy.rb  
5 -# call ferret.restart where you restart your Mongrels.  
6 -# ferret.stop and ferret.start are available, too.  
7 -module FerretCapTasks  
8 - def start  
9 - run "cd #{current_path}; RAILS_ENV=production script/ferret_start"  
10 - end  
11 -  
12 - def stop  
13 - run "cd #{current_path}; RAILS_ENV=production script/ferret_stop"  
14 - end  
15 -  
16 - def restart  
17 - stop  
18 - start  
19 - end  
20 -end  
21 -Capistrano.plugin :ferret, FerretCapTasks  
vendor/plugins/acts_as_ferret/lib/ferret_extensions.rb
@@ -1,81 +0,0 @@ @@ -1,81 +0,0 @@
1 -module Ferret  
2 -  
3 -  
4 - class Index::Index  
5 - attr_accessor :batch_size  
6 - attr_accessor :logger  
7 -  
8 - def index_models(models)  
9 - models.each { |model| index_model model }  
10 - flush  
11 - optimize  
12 - close  
13 - ActsAsFerret::close_multi_indexes  
14 - end  
15 -  
16 - def index_model(model)  
17 - @batch_size ||= 0  
18 - work_done = 0  
19 - batch_time = 0  
20 - logger.info "reindexing model #{model.name}"  
21 -  
22 - model_count = model.count.to_f  
23 - model.records_for_rebuild(@batch_size) do |records, offset|  
24 - #records = [ records ] unless records.is_a?(Array)  
25 - batch_time = measure_time {  
26 - records.each { |rec| self << rec.to_doc if rec.ferret_enabled?(true) }  
27 - }.to_f  
28 - work_done = offset.to_f / model_count * 100.0 if model_count > 0  
29 - remaining_time = ( batch_time / @batch_size ) * ( model_count - offset + @batch_size )  
30 - logger.info "reindex model #{model.name} : #{'%.2f' % work_done}% complete : #{'%.2f' % remaining_time} secs to finish"  
31 - end  
32 - end  
33 -  
34 - def measure_time  
35 - t1 = Time.now  
36 - yield  
37 - Time.now - t1  
38 - end  
39 -  
40 - end  
41 -  
42 -  
43 - # add marshalling support to SortFields  
44 - class Search::SortField  
45 - def _dump(depth)  
46 - to_s  
47 - end  
48 -  
49 - def self._load(string)  
50 - case string  
51 - when /<DOC(_ID)?>!/ : Ferret::Search::SortField::DOC_ID_REV  
52 - when /<DOC(_ID)?>/ : Ferret::Search::SortField::DOC_ID  
53 - when '<SCORE>!' : Ferret::Search::SortField::SCORE_REV  
54 - when '<SCORE>' : Ferret::Search::SortField::SCORE  
55 - when /^(\w+):<(\w+)>(!)?$/ : new($1.to_sym, :type => $2.to_sym, :reverse => !$3.nil?)  
56 - else raise "invalid value: #{string}"  
57 - end  
58 - end  
59 - end  
60 -  
61 - # add marshalling support to Sort  
62 - class Search::Sort  
63 - def _dump(depth)  
64 - to_s  
65 - end  
66 -  
67 - def self._load(string)  
68 - # we exclude the last <DOC> sorting as it is appended by new anyway  
69 - if string =~ /^Sort\[(.*?)(<DOC>(!)?)?\]$/  
70 - sort_fields = $1.split(',').map do |value|  
71 - value.strip!  
72 - Ferret::Search::SortField._load value unless value.blank?  
73 - end  
74 - new sort_fields.compact  
75 - else  
76 - raise "invalid value: #{string}"  
77 - end  
78 - end  
79 - end  
80 -  
81 -end  
vendor/plugins/acts_as_ferret/lib/ferret_result.rb
@@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
1 -module ActsAsFerret  
2 -  
3 - # mixed into the FerretResult and AR classes calling acts_as_ferret  
4 - module ResultAttributes  
5 - # holds the score this record had when it was found via  
6 - # acts_as_ferret  
7 - attr_accessor :ferret_score  
8 -  
9 - attr_accessor :ferret_rank  
10 - end  
11 -  
12 - class FerretResult  
13 - include ResultAttributes  
14 - attr_accessor :id  
15 -  
16 - def initialize(model, id, score, data = {})  
17 - @model = model.constantize  
18 - @id = id  
19 - @ferret_score = score  
20 - @data = data  
21 - end  
22 -  
23 - def method_missing(method, *args)  
24 - if @ar_record || @data[method].nil?  
25 - ferret_load_record unless @ar_record  
26 - @ar_record.send method, *args  
27 - else  
28 - @data[method]  
29 - end  
30 - end  
31 -  
32 - def ferret_load_record  
33 - @ar_record = @model.find(id)  
34 - end  
35 - end  
36 -end  
vendor/plugins/acts_as_ferret/lib/ferret_server.rb
@@ -1,131 +0,0 @@ @@ -1,131 +0,0 @@
1 -require 'drb'  
2 -require 'thread'  
3 -require 'yaml'  
4 -require 'erb'  
5 -  
6 -  
7 -module ActsAsFerret  
8 -  
9 - module Remote  
10 -  
11 - module Config  
12 - class << self  
13 - DEFAULTS = {  
14 - 'host' => 'localhost',  
15 - 'port' => '9009'  
16 - }  
17 - # read connection settings from config file  
18 - def load(file = "#{RAILS_ROOT}/config/ferret_server.yml")  
19 - config = DEFAULTS.merge(YAML.load(ERB.new(IO.read(file)).result))  
20 - if config = config[RAILS_ENV]  
21 - config[:uri] = "druby://#{config['host']}:#{config['port']}"  
22 - return config  
23 - end  
24 - {}  
25 - end  
26 - end  
27 - end  
28 -  
29 - # This class acts as a drb server listening for indexing and  
30 - # search requests from models declared to 'acts_as_ferret :remote => true'  
31 - #  
32 - # Usage:  
33 - # - modify RAILS_ROOT/config/ferret_server.yml to suit your needs.  
34 - # - environments for which no section in the config file exists will use  
35 - # the index locally (good for unit tests/development mode)  
36 - # - run script/ferret_start to start the server:  
37 - # RAILS_ENV=production script/ferret_start  
38 - #  
39 - class Server  
40 -  
41 - cattr_accessor :running  
42 -  
43 - def self.start(uri = nil)  
44 - ActiveRecord::Base.allow_concurrency = true  
45 - ActiveRecord::Base.logger = Logger.new("#{RAILS_ROOT}/log/ferret_server.log")  
46 - uri ||= ActsAsFerret::Remote::Config.load[:uri]  
47 - DRb.start_service(uri, ActsAsFerret::Remote::Server.new)  
48 - self.running = true  
49 - end  
50 -  
51 - def initialize  
52 - @logger = ActiveRecord::Base.logger  
53 - end  
54 -  
55 - # handles all incoming method calls, and sends them on to the LocalIndex  
56 - # instance of the correct model class.  
57 - #  
58 - # Calls are not queued atm, so this will block until the call returned.  
59 - #  
60 - def method_missing(name, *args)  
61 - @logger.debug "\#method_missing(#{name.inspect}, #{args.inspect})"  
62 - with_class args.shift do |clazz|  
63 - begin  
64 - clazz.aaf_index.send name, *args  
65 - rescue NoMethodError  
66 - @logger.debug "no luck, trying to call class method instead"  
67 - clazz.send name, *args  
68 - end  
69 - end  
70 - rescue  
71 - @logger.error "ferret server error #{$!}\n#{$!.backtrace.join '\n'}"  
72 - raise  
73 - end  
74 -  
75 - # make sure we have a versioned index in place, building one if necessary  
76 - def ensure_index_exists(class_name)  
77 - @logger.debug "DRb server: ensure_index_exists for class #{class_name}"  
78 - with_class class_name do |clazz|  
79 - dir = clazz.aaf_configuration[:index_dir]  
80 - unless File.directory?(dir) && File.file?(File.join(dir, 'segments')) && dir =~ %r{/\d+(_\d+)?$}  
81 - rebuild_index(clazz)  
82 - end  
83 - end  
84 - end  
85 -  
86 - # hides LocalIndex#rebuild_index to implement index versioning  
87 - def rebuild_index(clazz, *models)  
88 - with_class clazz do |clazz|  
89 - models = models.flatten.uniq.map(&:constantize)  
90 - models << clazz unless models.include?(clazz)  
91 - index = new_index_for(clazz, models)  
92 - @logger.debug "DRb server: rebuild index for class(es) #{models.inspect} in #{index.options[:path]}"  
93 - index.index_models models  
94 - new_version = File.join clazz.aaf_configuration[:index_base_dir], Time.now.utc.strftime('%Y%m%d%H%M%S')  
95 - # create a unique directory name (needed for unit tests where  
96 - # multiple rebuilds per second may occur)  
97 - if File.exists?(new_version)  
98 - i = 0  
99 - i+=1 while File.exists?("#{new_version}_#{i}")  
100 - new_version << "_#{i}"  
101 - end  
102 -  
103 - File.rename index.options[:path], new_version  
104 - clazz.index_dir = new_version  
105 - end  
106 - end  
107 -  
108 -  
109 - protected  
110 -  
111 - def with_class(clazz, *args)  
112 - clazz = clazz.constantize if String === clazz  
113 - yield clazz, *args  
114 - end  
115 -  
116 - def new_index_for(clazz, models)  
117 - aaf_configuration = clazz.aaf_configuration  
118 - ferret_cfg = aaf_configuration[:ferret].dup  
119 - ferret_cfg.update :auto_flush => false,  
120 - :create => true,  
121 - :field_infos => ActsAsFerret::field_infos(models),  
122 - :path => File.join(aaf_configuration[:index_base_dir], 'rebuild')  
123 - returning Ferret::Index::Index.new ferret_cfg do |i|  
124 - i.batch_size = aaf_configuration[:reindex_batch_size]  
125 - i.logger = @logger  
126 - end  
127 - end  
128 -  
129 - end  
130 - end  
131 -end  
vendor/plugins/acts_as_ferret/lib/index.rb
@@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
1 -module ActsAsFerret  
2 -  
3 - # base class for local and remote indexes  
4 - class AbstractIndex  
5 -  
6 - attr_reader :aaf_configuration  
7 - attr_accessor :logger  
8 - def initialize(aaf_configuration)  
9 - @aaf_configuration = aaf_configuration  
10 - @logger = Logger.new("#{RAILS_ROOT}/log/ferret_index.log")  
11 - end  
12 -  
13 - class << self  
14 - def proxy_method(name, *args)  
15 - define_method name do |*args|  
16 - @server.send name, model_class_name, *args  
17 - end  
18 - end  
19 -  
20 - def index_proxy_method(*names)  
21 - names.each do |name|  
22 - define_method name do |*args|  
23 - @server.send :"index_#{name}", model_class_name, *args  
24 - end  
25 - end  
26 - end  
27 -  
28 - end  
29 - end  
30 -  
31 -end  
vendor/plugins/acts_as_ferret/lib/instance_methods.rb
@@ -1,126 +0,0 @@ @@ -1,126 +0,0 @@
1 -module ActsAsFerret #:nodoc:  
2 -  
3 - module InstanceMethods  
4 - include ResultAttributes  
5 -  
6 - # Returns an array of strings with the matches highlighted. The +query+ can  
7 - # either be a String or a Ferret::Search::Query object.  
8 - #  
9 - # === Options  
10 - #  
11 - # field:: field to take the content from. This field has  
12 - # to have it's content stored in the index  
13 - # (:store => :yes in your call to aaf). If not  
14 - # given, all stored fields are searched, and the  
15 - # highlighted content found in all of them is returned.  
16 - # set :highlight => :no in the field options to  
17 - # avoid highlighting of contents from a :stored field.  
18 - # excerpt_length:: Default: 150. Length of excerpt to show. Highlighted  
19 - # terms will be in the centre of the excerpt.  
20 - # num_excerpts:: Default: 2. Number of excerpts to return.  
21 - # pre_tag:: Default: "<em>". Tag to place to the left of the  
22 - # match.  
23 - # post_tag:: Default: "</em>". This tag should close the  
24 - # +:pre_tag+.  
25 - # ellipsis:: Default: "...". This is the string that is appended  
26 - # at the beginning and end of excerpts (unless the  
27 - # excerpt hits the start or end of the field. You'll  
28 - # probably want to change this so a Unicode elipsis  
29 - # character.  
30 - def highlight(query, options = {})  
31 - self.class.aaf_index.highlight(id, self.class.name, query, options)  
32 - end  
33 -  
34 - # re-eneable ferret indexing after a call to #disable_ferret  
35 - def ferret_enable; @ferret_disabled = nil end  
36 -  
37 - # returns true if ferret indexing is enabled  
38 - # the optional parameter will be true if the method is called by rebuild_index,  
39 - # and false otherwise. I.e. useful to enable a model only for indexing during  
40 - # scheduled reindex runs.  
41 - def ferret_enabled?(is_rebuild = false); @ferret_disabled.nil? end  
42 -  
43 - # Disable Ferret for a specified amount of time. ::once will disable  
44 - # Ferret for the next call to #save (this is the default), ::always will  
45 - # do so for all subsequent calls.  
46 - # To manually trigger reindexing of a record, you can call #ferret_update  
47 - # directly.  
48 - #  
49 - # When given a block, this will be executed without any ferret indexing of  
50 - # this object taking place. The optional argument in this case can be used  
51 - # to indicate if the object should be indexed after executing the block  
52 - # (::index_when_finished). Automatic Ferret indexing of this object will be  
53 - # turned on after the block has been executed. If passed ::index_when_true,  
54 - # the index will only be updated if the block evaluated not to false or nil.  
55 - def disable_ferret(option = :once)  
56 - if block_given?  
57 - @ferret_disabled = :always  
58 - result = yield  
59 - ferret_enable  
60 - ferret_update if option == :index_when_finished || (option == :index_when_true && result)  
61 - result  
62 - elsif [:once, :always].include?(option)  
63 - @ferret_disabled = option  
64 - else  
65 - raise ArgumentError.new("Invalid Argument #{option}")  
66 - end  
67 - end  
68 -  
69 - # add to index  
70 - def ferret_create  
71 - if ferret_enabled?  
72 - logger.debug "ferret_create/update: #{self.class.name} : #{self.id}"  
73 - self.class.aaf_index << self  
74 - else  
75 - ferret_enable if @ferret_disabled == :once  
76 - end  
77 - true # signal success to AR  
78 - end  
79 - alias :ferret_update :ferret_create  
80 -  
81 -  
82 - # remove from index  
83 - def ferret_destroy  
84 - logger.debug "ferret_destroy: #{self.class.name} : #{self.id}"  
85 - begin  
86 - self.class.aaf_index.remove self.id, self.class.name  
87 - rescue  
88 - logger.warn("Could not find indexed value for this object: #{$!}\n#{$!.backtrace}")  
89 - end  
90 - true # signal success to AR  
91 - end  
92 -  
93 - # turn this instance into a ferret document (which basically is a hash of  
94 - # fieldname => value pairs)  
95 - def to_doc  
96 - logger.debug "creating doc for class: #{self.class.name}, id: #{self.id}"  
97 - returning doc = Ferret::Document.new do  
98 - # store the id of each item  
99 - doc[:id] = self.id  
100 -  
101 - # store the class name if configured to do so  
102 - doc[:class_name] = self.class.name if aaf_configuration[:store_class_name]  
103 -  
104 - # iterate through the fields and add them to the document  
105 - aaf_configuration[:ferret_fields].each_pair do |field, config|  
106 - doc[field] = self.send("#{field}_to_ferret") unless config[:ignore]  
107 - end  
108 - end  
109 - end  
110 -  
111 - def document_number  
112 - self.class.aaf_index.document_number(id, self.class.name)  
113 - end  
114 -  
115 - def query_for_record  
116 - self.class.aaf_index.query_for_record(id, self.class.name)  
117 - end  
118 -  
119 - def content_for_field_name(field)  
120 - self[field] || self.instance_variable_get("@#{field.to_s}".to_sym) || self.send(field.to_sym)  
121 - end  
122 -  
123 -  
124 - end  
125 -  
126 -end  
vendor/plugins/acts_as_ferret/lib/local_index.rb
@@ -1,209 +0,0 @@ @@ -1,209 +0,0 @@
1 -module ActsAsFerret  
2 -  
3 - class LocalIndex < AbstractIndex  
4 - include MoreLikeThis::IndexMethods  
5 -  
6 -  
7 - def initialize(aaf_configuration)  
8 - super  
9 - ensure_index_exists  
10 - end  
11 -  
12 - def reopen!  
13 - if @ferret_index  
14 - @ferret_index.close  
15 - @ferret_index = nil  
16 - end  
17 - logger.debug "reopening index at #{aaf_configuration[:ferret][:path]}"  
18 - ferret_index  
19 - end  
20 -  
21 - # The 'real' Ferret Index instance  
22 - def ferret_index  
23 - ensure_index_exists  
24 - returning @ferret_index ||= Ferret::Index::Index.new(aaf_configuration[:ferret]) do  
25 - @ferret_index.batch_size = aaf_configuration[:reindex_batch_size]  
26 - @ferret_index.logger = logger  
27 - end  
28 - end  
29 -  
30 - # Checks for the presence of a segments file in the index directory  
31 - # Rebuilds the index if none exists.  
32 - def ensure_index_exists  
33 - logger.debug "LocalIndex: ensure_index_exists at #{aaf_configuration[:index_dir]}"  
34 - unless File.file? "#{aaf_configuration[:index_dir]}/segments"  
35 - ActsAsFerret::ensure_directory(aaf_configuration[:index_dir])  
36 - close  
37 - rebuild_index  
38 - end  
39 - end  
40 -  
41 - # Closes the underlying index instance  
42 - def close  
43 - @ferret_index.close if @ferret_index  
44 - rescue StandardError  
45 - # is raised when index already closed  
46 - ensure  
47 - @ferret_index = nil  
48 - end  
49 -  
50 - # rebuilds the index from all records of the model class this index belongs  
51 - # to. Arguments can be given in shared index scenarios to name multiple  
52 - # model classes to include in the index  
53 - def rebuild_index(*models)  
54 - models << aaf_configuration[:class_name] unless models.include?(aaf_configuration[:class_name])  
55 - models = models.flatten.uniq.map(&:constantize)  
56 - logger.debug "rebuild index: #{models.inspect}"  
57 - index = Ferret::Index::Index.new(aaf_configuration[:ferret].dup.update(:auto_flush => false,  
58 - :field_infos => ActsAsFerret::field_infos(models),  
59 - :create => true))  
60 - index.batch_size = aaf_configuration[:reindex_batch_size]  
61 - index.logger = logger  
62 - index.index_models models  
63 - end  
64 -  
65 - # Parses the given query string into a Ferret Query object.  
66 - def process_query(query)  
67 - # work around ferret bug in #process_query (doesn't ensure the  
68 - # reader is open)  
69 - ferret_index.synchronize do  
70 - ferret_index.send(:ensure_reader_open)  
71 - original_query = ferret_index.process_query(query)  
72 - end  
73 - end  
74 -  
75 - # Total number of hits for the given query.  
76 - # To count the results of a multi_search query, specify an array of  
77 - # class names with the :models option.  
78 - def total_hits(query, options = {})  
79 - index = (models = options.delete(:models)) ? multi_index(models) : ferret_index  
80 - index.search(query, options).total_hits  
81 - end  
82 -  
83 - def determine_lazy_fields(options = {})  
84 - stored_fields = options[:lazy]  
85 - if stored_fields && !(Array === stored_fields)  
86 - stored_fields = aaf_configuration[:ferret_fields].select { |field, config| config[:store] == :yes }.map(&:first)  
87 - end  
88 - logger.debug "stored_fields: #{stored_fields}"  
89 - return stored_fields  
90 - end  
91 -  
92 - # Queries the Ferret index to retrieve model class, id, score and the  
93 - # values of any fields stored in the index for each hit.  
94 - # If a block is given, these are yielded and the number of total hits is  
95 - # returned. Otherwise [total_hits, result_array] is returned.  
96 - def find_id_by_contents(query, options = {})  
97 - result = []  
98 - index = ferret_index  
99 - logger.debug "query: #{ferret_index.process_query query}" # TODO only enable this for debugging purposes  
100 - lazy_fields = determine_lazy_fields options  
101 -  
102 - total_hits = index.search_each(query, options) do |hit, score|  
103 - doc = index[hit]  
104 - model = aaf_configuration[:store_class_name] ? doc[:class_name] : aaf_configuration[:class_name]  
105 - # fetch stored fields if lazy loading  
106 - data = {}  
107 - lazy_fields.each { |field| data[field] = doc[field] } if lazy_fields  
108 - if block_given?  
109 - yield model, doc[:id], score, data  
110 - else  
111 - result << { :model => model, :id => doc[:id], :score => score, :data => data }  
112 - end  
113 - end  
114 - #logger.debug "id_score_model array: #{result.inspect}"  
115 - return block_given? ? total_hits : [total_hits, result]  
116 - end  
117 -  
118 - # Queries multiple Ferret indexes to retrieve model class, id and score for  
119 - # each hit. Use the models parameter to give the list of models to search.  
120 - # If a block is given, model, id and score are yielded and the number of  
121 - # total hits is returned. Otherwise [total_hits, result_array] is returned.  
122 - def id_multi_search(query, models, options = {})  
123 - index = multi_index(models)  
124 - result = []  
125 - lazy_fields = determine_lazy_fields options  
126 - total_hits = index.search_each(query, options) do |hit, score|  
127 - doc = index[hit]  
128 - # fetch stored fields if lazy loading  
129 - data = {}  
130 - lazy_fields.each { |field| data[field] = doc[field] } if lazy_fields  
131 - raise "':store_class_name => true' required for multi_search to work" if doc[:class_name].blank?  
132 - if block_given?  
133 - yield doc[:class_name], doc[:id], score, doc, data  
134 - else  
135 - result << { :model => doc[:class_name], :id => doc[:id], :score => score, :data => data }  
136 - end  
137 - end  
138 - return block_given? ? total_hits : [ total_hits, result ]  
139 - end  
140 -  
141 - ######################################  
142 - # methods working on a single record  
143 - # called from instance_methods, here to simplify interfacing with the  
144 - # remote ferret server  
145 - # TODO having to pass id and class_name around like this isn't nice  
146 - ######################################  
147 -  
148 - # add record to index  
149 - # record may be the full AR object, a Ferret document instance or a Hash  
150 - def add(record)  
151 - record = record.to_doc unless Hash === record || Ferret::Document === record  
152 - ferret_index << record  
153 - end  
154 - alias << add  
155 -  
156 - # delete record from index  
157 - def remove(id, class_name)  
158 - ferret_index.query_delete query_for_record(id, class_name)  
159 - end  
160 -  
161 - # highlight search terms for the record with the given id.  
162 - def highlight(id, class_name, query, options = {})  
163 - options.reverse_merge! :num_excerpts => 2, :pre_tag => '<em>', :post_tag => '</em>'  
164 - highlights = []  
165 - ferret_index.synchronize do  
166 - doc_num = document_number(id, class_name)  
167 - if options[:field]  
168 - highlights << ferret_index.highlight(query, doc_num, options)  
169 - else  
170 - query = process_query(query) # process only once  
171 - aaf_configuration[:ferret_fields].each_pair do |field, config|  
172 - next if config[:store] == :no || config[:highlight] == :no  
173 - options[:field] = field  
174 - highlights << ferret_index.highlight(query, doc_num, options)  
175 - end  
176 - end  
177 - end  
178 - return highlights.compact.flatten[0..options[:num_excerpts]-1]  
179 - end  
180 -  
181 - # retrieves the ferret document number of the record with the given id.  
182 - def document_number(id, class_name)  
183 - hits = ferret_index.search(query_for_record(id, class_name))  
184 - return hits.hits.first.doc if hits.total_hits == 1  
185 - raise "cannot determine document number from primary key: #{id}"  
186 - end  
187 -  
188 - # build a ferret query matching only the record with the given id  
189 - # the class name only needs to be given in case of a shared index configuration  
190 - def query_for_record(id, class_name = nil)  
191 - Ferret::Search::TermQuery.new(:id, id.to_s)  
192 - end  
193 -  
194 -  
195 - protected  
196 -  
197 - # returns a MultiIndex instance operating on a MultiReader  
198 - def multi_index(model_classes)  
199 - model_classes.map!(&:constantize) if String === model_classes.first  
200 - model_classes.sort! { |a, b| a.name <=> b.name }  
201 - key = model_classes.inject("") { |s, clazz| s + clazz.name }  
202 - multi_config = aaf_configuration[:ferret].dup  
203 - multi_config.delete :default_field # we don't want the default field list of *this* class for multi_searching  
204 - ActsAsFerret::multi_indexes[key] ||= MultiIndex.new(model_classes, multi_config)  
205 - end  
206 -  
207 - end  
208 -  
209 -end  
vendor/plugins/acts_as_ferret/lib/more_like_this.rb
@@ -1,217 +0,0 @@ @@ -1,217 +0,0 @@
1 -module ActsAsFerret #:nodoc:  
2 -  
3 - module MoreLikeThis  
4 -  
5 - module InstanceMethods  
6 -  
7 - # returns other instances of this class, which have similar contents  
8 - # like this one. Basically works like this: find out n most interesting  
9 - # (i.e. characteristic) terms from this document, and then build a  
10 - # query from those which is run against the whole index. Which terms  
11 - # are interesting is decided on variour criteria which can be  
12 - # influenced by the given options.  
13 - #  
14 - # The algorithm used here is a quite straight port of the MoreLikeThis class  
15 - # from Apache Lucene.  
16 - #  
17 - # options are:  
18 - # :field_names : Array of field names to use for similarity search (mandatory)  
19 - # :min_term_freq => 2, # Ignore terms with less than this frequency in the source doc.  
20 - # :min_doc_freq => 5, # Ignore words which do not occur in at least this many docs  
21 - # :min_word_length => nil, # Ignore words shorter than this length (longer words tend to  
22 - # be more characteristic for the document they occur in).  
23 - # :max_word_length => nil, # Ignore words if greater than this len.  
24 - # :max_query_terms => 25, # maximum number of terms in the query built  
25 - # :max_num_tokens => 5000, # maximum number of tokens to examine in a single field  
26 - # :boost => false, # when true, a boost according to the relative score of  
27 - # a term is applied to this Term's TermQuery.  
28 - # :similarity => 'DefaultAAFSimilarity' # the similarity implementation to use (the default  
29 - # equals Ferret's internal similarity implementation)  
30 - # :analyzer => 'Ferret::Analysis::StandardAnalyzer' # class name of the analyzer to use  
31 - # :append_to_query => nil # proc taking a query object as argument, which will be called after generating the query. can be used to further manipulate the query used to find related documents, i.e. to constrain the search to a given class in single table inheritance scenarios  
32 - # ferret_options : Ferret options handed over to find_by_contents (i.e. for limits and sorting)  
33 - # ar_options : options handed over to find_by_contents for AR scoping  
34 - def more_like_this(options = {}, ferret_options = {}, ar_options = {})  
35 - options = {  
36 - :field_names => nil, # Default field names  
37 - :min_term_freq => 2, # Ignore terms with less than this frequency in the source doc.  
38 - :min_doc_freq => 5, # Ignore words which do not occur in at least this many docs  
39 - :min_word_length => 0, # Ignore words if less than this len. Default is not to ignore any words.  
40 - :max_word_length => 0, # Ignore words if greater than this len. Default is not to ignore any words.  
41 - :max_query_terms => 25, # maximum number of terms in the query built  
42 - :max_num_tokens => 5000, # maximum number of tokens to analyze when analyzing contents  
43 - :boost => false,  
44 - :similarity => 'ActsAsFerret::MoreLikeThis::DefaultAAFSimilarity', # class name of the similarity implementation to use  
45 - :analyzer => 'Ferret::Analysis::StandardAnalyzer', # class name of the analyzer to use  
46 - :append_to_query => nil,  
47 - :base_class => self.class # base class to use for querying, useful in STI scenarios where BaseClass.find_by_contents can be used to retrieve results from other classes, too  
48 - }.update(options)  
49 - #index.search_each('id:*') do |doc, score|  
50 - # puts "#{doc} == #{index[doc][:description]}"  
51 - #end  
52 - clazz = options[:base_class]  
53 - options[:base_class] = clazz.name  
54 - query = clazz.aaf_index.build_more_like_this_query(self.id, self.class.name, options)  
55 - options[:append_to_query].call(query) if options[:append_to_query]  
56 - clazz.find_by_contents(query, ferret_options, ar_options)  
57 - end  
58 -  
59 - end  
60 -  
61 - module IndexMethods  
62 -  
63 - # TODO to allow morelikethis for unsaved records, we have to give the  
64 - # unsaved record's data to this method. check how this will work out  
65 - # via drb...  
66 - def build_more_like_this_query(id, class_name, options)  
67 - [:similarity, :analyzer].each { |sym| options[sym] = options[sym].constantize.new }  
68 - ferret_index.synchronize do # avoid that concurrent writes close our reader  
69 - ferret_index.send(:ensure_reader_open)  
70 - reader = ferret_index.send(:reader)  
71 - term_freq_map = retrieve_terms(id, class_name, reader, options)  
72 - priority_queue = create_queue(term_freq_map, reader, options)  
73 - create_query(id, class_name, priority_queue, options)  
74 - end  
75 - end  
76 -  
77 - protected  
78 -  
79 - def create_query(id, class_name, priority_queue, options={})  
80 - query = Ferret::Search::BooleanQuery.new  
81 - qterms = 0  
82 - best_score = nil  
83 - while(cur = priority_queue.pop)  
84 - term_query = Ferret::Search::TermQuery.new(cur.field, cur.word)  
85 -  
86 - if options[:boost]  
87 - # boost term according to relative score  
88 - # TODO untested  
89 - best_score ||= cur.score  
90 - term_query.boost = cur.score / best_score  
91 - end  
92 - begin  
93 - query.add_query(term_query, :should)  
94 - rescue Ferret::Search::BooleanQuery::TooManyClauses  
95 - break  
96 - end  
97 - qterms += 1  
98 - break if options[:max_query_terms] > 0 && qterms >= options[:max_query_terms]  
99 - end  
100 - # exclude the original record  
101 - query.add_query(query_for_record(id, class_name), :must_not)  
102 - return query  
103 - end  
104 -  
105 -  
106 -  
107 - # creates a term/term_frequency map for terms from the fields  
108 - # given in options[:field_names]  
109 - def retrieve_terms(id, class_name, reader, options)  
110 - raise "more_like_this atm only works on saved records" if id.nil?  
111 - document_number = document_number(id, class_name) rescue nil  
112 - field_names = options[:field_names]  
113 - max_num_tokens = options[:max_num_tokens]  
114 - term_freq_map = Hash.new(0)  
115 - doc = nil  
116 - record = nil  
117 - field_names.each do |field|  
118 - #puts "field: #{field}"  
119 - term_freq_vector = reader.term_vector(document_number, field) if document_number  
120 - #if false  
121 - if term_freq_vector  
122 - # use stored term vector  
123 - # puts 'using stored term vector'  
124 - term_freq_vector.terms.each do |term|  
125 - term_freq_map[term.text] += term.positions.size unless noise_word?(term.text, options)  
126 - end  
127 - else  
128 - # puts 'no stored term vector'  
129 - # no term vector stored, but we have stored the contents in the index  
130 - # -> extract terms from there  
131 - content = nil  
132 - if document_number  
133 - doc = reader[document_number]  
134 - content = doc[field]  
135 - end  
136 - unless content  
137 - # no term vector, no stored content, so try content from this instance  
138 - record ||= options[:base_class].constantize.find(id)  
139 - content = record.content_for_field_name(field.to_s)  
140 - end  
141 - puts "have doc: #{doc[:id]} with #{field} == #{content}"  
142 - token_count = 0  
143 -  
144 - ts = options[:analyzer].token_stream(field, content)  
145 - while token = ts.next  
146 - break if (token_count+=1) > max_num_tokens  
147 - next if noise_word?(token.text, options)  
148 - term_freq_map[token.text] += 1  
149 - end  
150 - end  
151 - end  
152 - term_freq_map  
153 - end  
154 -  
155 - # create an ordered(by score) list of word,fieldname,score  
156 - # structures  
157 - def create_queue(term_freq_map, reader, options)  
158 - pq = Array.new(term_freq_map.size)  
159 -  
160 - similarity = options[:similarity]  
161 - num_docs = reader.num_docs  
162 - term_freq_map.each_pair do |word, tf|  
163 - # filter out words that don't occur enough times in the source  
164 - next if options[:min_term_freq] && tf < options[:min_term_freq]  
165 -  
166 - # go through all the fields and find the largest document frequency  
167 - top_field = options[:field_names].first  
168 - doc_freq = 0  
169 - options[:field_names].each do |field_name|  
170 - freq = reader.doc_freq(field_name, word)  
171 - if freq > doc_freq  
172 - top_field = field_name  
173 - doc_freq = freq  
174 - end  
175 - end  
176 - # filter out words that don't occur in enough docs  
177 - next if options[:min_doc_freq] && doc_freq < options[:min_doc_freq]  
178 - next if doc_freq == 0 # index update problem ?  
179 -  
180 - idf = similarity.idf(doc_freq, num_docs)  
181 - score = tf * idf  
182 - pq << FrequencyQueueItem.new(word, top_field, score)  
183 - end  
184 - pq.compact!  
185 - pq.sort! { |a,b| a.score<=>b.score }  
186 - return pq  
187 - end  
188 -  
189 - def noise_word?(text, options)  
190 - len = text.length  
191 - (  
192 - (options[:min_word_length] > 0 && len < options[:min_word_length]) ||  
193 - (options[:max_word_length] > 0 && len > options[:max_word_length]) ||  
194 - (options[:stop_words] && options.include?(text))  
195 - )  
196 - end  
197 -  
198 - end  
199 -  
200 - class DefaultAAFSimilarity  
201 - def idf(doc_freq, num_docs)  
202 - return 0.0 if num_docs == 0  
203 - return Math.log(num_docs.to_f/(doc_freq+1)) + 1.0  
204 - end  
205 - end  
206 -  
207 -  
208 - class FrequencyQueueItem  
209 - attr_reader :word, :field, :score  
210 - def initialize(word, field, score)  
211 - @word = word; @field = field; @score = score  
212 - end  
213 - end  
214 -  
215 - end  
216 -end  
217 -  
vendor/plugins/acts_as_ferret/lib/multi_index.rb
@@ -1,83 +0,0 @@ @@ -1,83 +0,0 @@
1 -module ActsAsFerret #:nodoc:  
2 -  
3 - # this class is not threadsafe  
4 - class MultiIndex  
5 -  
6 - def initialize(model_classes, options = {})  
7 - @model_classes = model_classes  
8 - # ensure all models indexes exist  
9 - @model_classes.each { |m| m.aaf_index.ensure_index_exists }  
10 - default_fields = @model_classes.inject([]) do |fields, c|  
11 - fields + [ c.aaf_configuration[:ferret][:default_field] ].flatten  
12 - end  
13 - @options = {  
14 - :default_field => default_fields  
15 - }.update(options)  
16 - end  
17 -  
18 - def search(query, options={})  
19 - #puts "querystring: #{query.to_s}"  
20 - query = process_query(query)  
21 - #puts "parsed query: #{query.to_s}"  
22 - searcher.search(query, options)  
23 - end  
24 -  
25 - def search_each(query, options = {}, &block)  
26 - query = process_query(query)  
27 - searcher.search_each(query, options, &block)  
28 - end  
29 -  
30 - # checks if all our sub-searchers still are up to date  
31 - def latest?  
32 - return false unless @reader  
33 - # segfaults with 0.10.4 --> TODO report as bug @reader.latest?  
34 - @sub_readers.each do |r|  
35 - return false unless r.latest?  
36 - end  
37 - true  
38 - end  
39 -  
40 - def searcher  
41 - ensure_searcher  
42 - @searcher  
43 - end  
44 -  
45 - def doc(i)  
46 - searcher[i]  
47 - end  
48 - alias :[] :doc  
49 -  
50 - def query_parser  
51 - @query_parser ||= Ferret::QueryParser.new(@options)  
52 - end  
53 -  
54 - def process_query(query)  
55 - query = query_parser.parse(query) if query.is_a?(String)  
56 - return query  
57 - end  
58 -  
59 - def close  
60 - @searcher.close if @searcher  
61 - @reader.close if @reader  
62 - end  
63 -  
64 - protected  
65 -  
66 - def ensure_searcher  
67 - unless latest?  
68 - @sub_readers = @model_classes.map { |clazz|  
69 - begin  
70 - reader = Ferret::Index::IndexReader.new(clazz.aaf_configuration[:index_dir])  
71 - rescue Exception  
72 - raise "error opening #{clazz.aaf_configuration[:index_dir]}: #{$!}"  
73 - end  
74 - }  
75 - close  
76 - @reader = Ferret::Index::IndexReader.new(@sub_readers)  
77 - @searcher = Ferret::Search::Searcher.new(@reader)  
78 - end  
79 - end  
80 -  
81 - end # of class MultiIndex  
82 -  
83 -end  
vendor/plugins/acts_as_ferret/lib/remote_index.rb
@@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
1 -require 'drb'  
2 -module ActsAsFerret  
3 -  
4 - # This index implementation connects to a remote ferret server instance. It  
5 - # basically forwards all calls to the remote server.  
6 - class RemoteIndex < AbstractIndex  
7 -  
8 - def initialize(config)  
9 - @config = config  
10 - @ferret_config = config[:ferret]  
11 - @server = DRbObject.new(nil, config[:remote])  
12 - end  
13 -  
14 - def method_missing(method_name, *args)  
15 - args.unshift model_class_name  
16 - @server.send(method_name, *args)  
17 - end  
18 -  
19 - def find_id_by_contents(q, options = {}, &proc)  
20 - total_hits, results = @server.find_id_by_contents(model_class_name, q, options)  
21 - block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ]  
22 - end  
23 -  
24 - def id_multi_search(query, models, options, &proc)  
25 - total_hits, results = @server.id_multi_search(model_class_name, query, models, options)  
26 - block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ]  
27 - end  
28 -  
29 - # add record to index  
30 - def add(record)  
31 - @server.add record.class.name, record.to_doc  
32 - end  
33 - alias << add  
34 -  
35 - private  
36 -  
37 - def yield_results(total_hits, results)  
38 - results.each do |result|  
39 - yield result[:model], result[:id], result[:score], result[:data]  
40 - end  
41 - total_hits  
42 - end  
43 -  
44 - def model_class_name  
45 - @config[:class_name]  
46 - end  
47 -  
48 - end  
49 -  
50 -end  
vendor/plugins/acts_as_ferret/lib/shared_index.rb
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -module ActsAsFerret  
2 -  
3 - class SharedIndex < LocalIndex  
4 -  
5 - # build a ferret query matching only the record with the given id and class  
6 - def query_for_record(id, class_name)  
7 - returning bq = Ferret::Search::BooleanQuery.new do  
8 - bq.add_query(Ferret::Search::TermQuery.new(:id, id.to_s), :must)  
9 - bq.add_query(Ferret::Search::TermQuery.new(:class_name, class_name), :must)  
10 - end  
11 - end  
12 -  
13 - end  
14 -end  
vendor/plugins/acts_as_ferret/lib/shared_index_class_methods.rb
@@ -1,90 +0,0 @@ @@ -1,90 +0,0 @@
1 -module ActsAsFerret  
2 -  
3 - # class methods for classes using acts_as_ferret :single_index => true  
4 - module SharedIndexClassMethods  
5 -  
6 - def find_id_by_contents(q, options = {}, &block)  
7 - # add class name scoping to query if necessary  
8 - unless options[:models] == :all # search needs to be restricted by one or more class names  
9 - options[:models] ||= []  
10 - # add this class to the list of given models  
11 - options[:models] << self unless options[:models].include?(self)  
12 - # keep original query  
13 - original_query = q  
14 -  
15 - if original_query.is_a? String  
16 - model_query = options[:models].map(&:name).join '|'  
17 - q << %{ +class_name:"#{model_query}"}  
18 - else  
19 - q = Ferret::Search::BooleanQuery.new  
20 - q.add_query(original_query, :must)  
21 - model_query = Ferret::Search::BooleanQuery.new  
22 - options[:models].each do |model|  
23 - model_query.add_query(Ferret::Search::TermQuery.new(:class_name, model.name), :should)  
24 - end  
25 - q.add_query(model_query, :must)  
26 - end  
27 - end  
28 - options.delete :models  
29 -  
30 - super(q, options, &block)  
31 - end  
32 -  
33 - # Overrides the standard find_by_contents for searching a shared index.  
34 - #  
35 - # please note that records from different models will be fetched in  
36 - # separate sql calls, so any sql order_by clause given with  
37 - # find_options[:order] will be ignored.  
38 - def find_by_contents(q, options = {}, find_options = {})  
39 - if order = find_options.delete(:order)  
40 - logger.warn "using a shared index, so ignoring order_by clause #{order}"  
41 - end  
42 - total_hits, result = find_records_lazy_or_not q, options, find_options  
43 - # sort so results have the same order they had when originally retrieved  
44 - # from ferret  
45 - return SearchResults.new(result, total_hits)  
46 - end  
47 -  
48 - protected  
49 -  
50 - def ar_find_by_contents(q, options = {}, find_options = {})  
51 - total_hits, id_arrays = collect_results(q, options)  
52 - result = retrieve_records(id_arrays, find_options)  
53 - result.sort! { |a, b| id_arrays[a.class.name][a.id.to_s].first <=> id_arrays[b.class.name][b.id.to_s].first }  
54 - [ total_hits, result ]  
55 - end  
56 -  
57 - def collect_results(q, options = {})  
58 - id_arrays = {}  
59 - # get object ids for index hits  
60 - rank = 0  
61 - total_hits = find_id_by_contents(q, options) do |model, id, score, data|  
62 - id_arrays[model] ||= {}  
63 - # store result rank and score  
64 - id_arrays[model][id] = [ rank += 1, score ]  
65 - end  
66 - [ total_hits, id_arrays ]  
67 - end  
68 -  
69 -  
70 - # determine all field names in the shared index  
71 - # TODO unused  
72 -# def single_index_field_names(models)  
73 -# @single_index_field_names ||= (  
74 -# searcher = Ferret::Search::Searcher.new(class_index_dir)  
75 -# if searcher.reader.respond_to?(:get_field_names)  
76 -# (searcher.reader.send(:get_field_names) - ['id', 'class_name']).to_a  
77 -# else  
78 -# puts <<-END  
79 -#unable to retrieve field names for class #{self.name}, please  
80 -#consider naming all indexed fields in your call to acts_as_ferret!  
81 -# END  
82 -# models.map { |m| m.content_columns.map { |col| col.name } }.flatten  
83 -# end  
84 -# )  
85 -#  
86 -# end  
87 -  
88 - end  
89 -end  
90 -  
vendor/plugins/acts_as_ferret/rakefile
@@ -1,131 +0,0 @@ @@ -1,131 +0,0 @@
1 -# rakefile for acts_as_ferret.  
2 -# use to create a gem or generate rdoc api documentation.  
3 -#  
4 -# RELEASE creation:  
5 -# rake release REL=x.y.z  
6 -  
7 -require 'rake'  
8 -require 'rake/rdoctask'  
9 -require 'rake/packagetask'  
10 -require 'rake/gempackagetask'  
11 -require 'rake/testtask'  
12 -require 'rake/contrib/rubyforgepublisher'  
13 -  
14 -def announce(msg='')  
15 - STDERR.puts msg  
16 -end  
17 -  
18 -  
19 -PKG_NAME = 'acts_as_ferret'  
20 -PKG_VERSION = ENV['REL']  
21 -PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"  
22 -RUBYFORGE_PROJECT = 'actsasferret'  
23 -RUBYFORGE_USER = 'jkraemer'  
24 -  
25 -desc 'Default: run unit tests.'  
26 -task :default => :test  
27 -  
28 -desc 'Test the acts_as_ferret plugin.'  
29 -Rake::TestTask.new(:test) do |t|  
30 - t.libs << 'lib'  
31 - t.pattern = 'test/**/*_test.rb'  
32 - t.verbose = true  
33 -end  
34 -  
35 -desc 'Generate documentation for the acts_as_ferret plugin.'  
36 -Rake::RDocTask.new(:rdoc) do |rdoc|  
37 - rdoc.rdoc_dir = 'html'  
38 - rdoc.title = "acts_as_ferret - Ferret based full text search for any ActiveRecord model"  
39 - rdoc.options << '--line-numbers' << '--inline-source'  
40 - rdoc.options << '--main' << 'README'  
41 - rdoc.rdoc_files.include('README', 'LICENSE')  
42 - rdoc.template = "#{ENV['template']}.rb" if ENV['template']  
43 - rdoc.rdoc_files.include('lib/**/*.rb')  
44 -end  
45 -  
46 -desc "Publish the API documentation"  
47 -task :pdoc => [:rdoc] do  
48 - Rake::RubyForgePublisher.new(RUBYFORGE_PROJECT, RUBYFORGE_USER).upload  
49 -end  
50 -  
51 -if PKG_VERSION  
52 - spec = Gem::Specification.new do |s|  
53 - s.name = PKG_NAME  
54 - s.version = PKG_VERSION  
55 - s.platform = Gem::Platform::RUBY  
56 - s.summary = "acts_as_ferret - Ferret based full text search for any ActiveRecord model"  
57 - s.files = Dir.glob('**/*', File::FNM_DOTMATCH).reject do |f|  
58 - [ /\.$/, /sqlite$/, /\.log$/, /^pkg/, /\.svn/, /\.\w+\.sw.$/,  
59 - /^html/, /\~$/, /\/\._/, /\/#/ ].any? {|regex| f =~ regex }  
60 - end  
61 - #s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG)  
62 - # s.files.delete ...  
63 - s.require_path = 'lib'  
64 - s.autorequire = 'acts_as_ferret'  
65 - s.has_rdoc = true  
66 - # s.test_files = Dir['test/**/*_test.rb']  
67 - s.author = "Jens Kraemer"  
68 - s.email = "jk@jkraemer.net"  
69 - s.homepage = "http://projects.jkraemer.net/acts_as_ferret"  
70 - end  
71 -  
72 - package_task = Rake::GemPackageTask.new(spec) do |pkg|  
73 - pkg.need_tar = true  
74 - end  
75 -  
76 - # Validate that everything is ready to go for a release.  
77 - task :prerelease do  
78 - announce  
79 - announce "**************************************************************"  
80 - announce "* Making RubyGem Release #{PKG_VERSION}"  
81 - announce "**************************************************************"  
82 - announce  
83 - # Are all source files checked in?  
84 - if ENV['RELTEST']  
85 - announce "Release Task Testing, skipping checked-in file test"  
86 - else  
87 - announce "Pulling in svn..."  
88 - `svk pull .`  
89 - announce "Checking for unchecked-in files..."  
90 - data = `svk st`  
91 - unless data =~ /^$/  
92 - fail "SVK status is not clean ... do you have unchecked-in files?"  
93 - end  
94 - announce "No outstanding checkins found ... OK"  
95 - announce "Pushing to svn..."  
96 - `svk push .`  
97 - end  
98 - end  
99 -  
100 -  
101 - desc "tag the new release"  
102 - task :tag => [ :prerelease ] do  
103 - reltag = "REL_#{PKG_VERSION.gsub(/\./, '_')}"  
104 - reltag << ENV['REUSE'].gsub(/\./, '_') if ENV['REUSE']  
105 - announce "Tagging with [#{PKG_VERSION}]"  
106 - if ENV['RELTEST']  
107 - announce "Release Task Testing, skipping tagging"  
108 - else  
109 - `svn copy -m 'tagging version #{PKG_VERSION}' svn://projects.jkraemer.net/acts_as_ferret/trunk/plugin svn://projects.jkraemer.net/acts_as_ferret/tags/#{PKG_VERSION}`  
110 - `svn del -m 'remove old stable' svn://projects.jkraemer.net/acts_as_ferret/tags/stable`  
111 - `svn copy -m 'tagging version #{PKG_VERSION} as stable' svn://projects.jkraemer.net/acts_as_ferret/tags/#{PKG_VERSION} svn://projects.jkraemer.net/acts_as_ferret/tags/stable`  
112 - end  
113 - end  
114 -  
115 - # Upload release to rubyforge  
116 - desc "Upload release to rubyforge"  
117 - task :prel => [ :tag, :prerelease, :package ] do  
118 - `rubyforge login`  
119 - release_command = "rubyforge add_release #{RUBYFORGE_PROJECT} #{PKG_NAME} '#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"  
120 - puts release_command  
121 - system(release_command)  
122 - `rubyforge config #{RUBYFORGE_PROJECT}`  
123 - release_command = "rubyforge add_file #{RUBYFORGE_PROJECT} #{PKG_NAME} '#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.tgz"  
124 - puts release_command  
125 - system(release_command)  
126 - end  
127 -  
128 - desc 'Publish the gem and API docs'  
129 - task :release => [:pdoc, :prel ]  
130 -  
131 -end  
vendor/plugins/acts_as_ferret/script/ferret_server
@@ -1,18 +0,0 @@ @@ -1,18 +0,0 @@
1 -#!/usr/bin/env ruby  
2 -  
3 -# Ferret DRb server launcher script  
4 -#  
5 -# Place doc/ferret_server.yml into RAILS_ROOT/config and fit to taste.  
6 -#  
7 -# Start this script with script/runner and RAILS_ENV set.  
8 -#  
9 -# to run the unit tests against the drb server, start it with  
10 -# RAILS_ENV=test script/runner script/ferret_server  
11 -# and run your tests with the AAF_REMOTE environment variable set to a  
12 -# non-empty value  
13 -  
14 -  
15 -ActsAsFerret::Remote::Server.start  
16 -DRb.thread.join  
17 -  
18 -  
vendor/plugins/acts_as_ferret/script/ferret_start
@@ -1,72 +0,0 @@ @@ -1,72 +0,0 @@
1 -#!/usr/bin/env ruby  
2 -# Ferret DRb server launcher script  
3 -#  
4 -# Place doc/ferret_server.yml into RAILS_ROOT/config and fit to taste. Start  
5 -# it with RAILS_ENV set to the desired environment.  
6 -#  
7 -#  
8 -# To run the demo project's unit tests against the drb server, start it with  
9 -#  
10 -# RAILS_ENV=test script/ferret_start  
11 -#  
12 -# and run your tests with the AAF_REMOTE environment variable set to a  
13 -# non-empty value:  
14 -#  
15 -# AAF_REMOTE=true rake  
16 -#  
17 -# The server writes a log file in log/ferret_server.log, it's  
18 -# STDOUT gets redirected to log/ferret_server.out  
19 -  
20 -ENV['FERRET_USE_LOCAL_INDEX'] = 'true'  
21 -require File.dirname(__FILE__) + '/../config/boot'  
22 -require RAILS_ROOT + '/config/environment'  
23 -  
24 -  
25 -config = ActsAsFerret::Remote::Config.load  
26 -@pid_file = config['pid_file']  
27 -  
28 -def write_pid_file  
29 - raise "No PID file defined" if @pid_file.blank?  
30 - open(@pid_file,"w") {|f| f.write(Process.pid) }  
31 -end  
32 -  
33 -def safefork  
34 - tryagain = true  
35 -  
36 - while tryagain  
37 - tryagain = false  
38 - begin  
39 - if pid = fork  
40 - return pid  
41 - end  
42 - rescue Errno::EWOULDBLOCK  
43 - sleep 5  
44 - tryagain = true  
45 - end  
46 - end  
47 -end  
48 -  
49 -safefork and exit  
50 -at_exit do  
51 - File.unlink(@pid_file) if @pid_file && File.exists?(@pid_file) && File.read(@pid_file).to_i == Process.pid  
52 -end  
53 -print "Starting ferret DRb server..."  
54 -trap("TERM") { exit(0) }  
55 -sess_id = Process.setsid  
56 -  
57 -  
58 -begin  
59 - ActsAsFerret::Remote::Server.start  
60 - write_pid_file  
61 - puts "Done."  
62 - STDIN.reopen "/dev/null" # Free file descriptors and  
63 - STDOUT.reopen "#{RAILS_ROOT}/log/ferret_server.out", "a" # point them somewhere sensible  
64 - STDERR.reopen STDOUT # STDOUT/STDERR should go to a logfile  
65 -rescue  
66 - $stderr.puts "Error starting ferret DRb server: #{$!}"  
67 - $stderr.puts $!.backtrace  
68 - exit(1)  
69 -end  
70 -DRb.thread.join  
71 -  
72 -# vim:set filetype=ruby:  
vendor/plugins/acts_as_ferret/script/ferret_stop
@@ -1,26 +0,0 @@ @@ -1,26 +0,0 @@
1 -#!/usr/bin/env script/runner  
2 -  
3 -config = ActsAsFerret::Remote::Config.load  
4 -  
5 -def send_signal(signal, pid_file)  
6 - pid = open(pid_file).read.to_i  
7 - print "Sending #{signal} to ferret_server with PID #{pid}..."  
8 - begin  
9 - Process.kill(signal, pid)  
10 - rescue Errno::ESRCH  
11 - puts "Process does not exist. Not running. Removing stale pid file anyway."  
12 - File.unlink(pid_file)  
13 - end  
14 -  
15 - puts "Done."  
16 -end  
17 -  
18 -pid_file = config['pid_file']  
19 -puts "Stopping ferret_server..."  
20 -if File.file?(pid_file)  
21 - send_signal("TERM", pid_file)  
22 -else  
23 - puts "no pid file found"  
24 -end  
25 -  
26 -# vim:set filetype=ruby: