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   -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   -= 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   -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   -# 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   -# 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   -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   -# 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   -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   -# 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   -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   -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   -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   -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   -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   -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   -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   -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   -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   -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   -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   -# 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   -#!/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   -#!/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   -#!/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: