Commit 20d8de0922e6d1b896e27800a1b275cfdb184c3d

Authored by Braulio Bhavamitra
2 parents 4c3e514f 0498b3b8

Merge branch 'master' of ../brauliobos-noosfero

Showing 224 changed files with 9052 additions and 3861 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 224 files displayed.

HACKING
... ... @@ -38,6 +38,9 @@ commands and make sure you understand what you are doing):
38 38 cp config/database.yml.sqlite3 config/database.yml
39 39 # create tmp directory if it doesn't exist
40 40 mkdir tmp
  41 + # download and start Solr
  42 + rake solr:download
  43 + rake solr:start
41 44 # create the development database
42 45 rake db:schema:load
43 46 # run pending migrations
... ...
INSTALL
... ... @@ -13,7 +13,7 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or
13 13 Debian-based systems, all of these packages are available through the Debian
14 14 archive. You can install them with the following command:
15 15  
16   - # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme libhpricot-ruby
  16 + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby openjdk-6-jre libdaemons-ruby mongrel mongrel-cluster tango-icon-theme libhpricot-ruby
17 17  
18 18 On other systems, they may or may not be available through your regular package
19 19 management system. Below are the links to their homepages.
... ... @@ -24,7 +24,7 @@ management system. Below are the links to their homepages.
24 24 * Ruby-GetText: http://www.yotabanana.com/hiki/ruby-gettext.html?ruby-gettext (at least version 1.9.0)
25 25 * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby
26 26 * rcov: http://eigenclass.org/hiki/rcov
27   -* Ferret: http://ferret.davebalmain.com/trac
  27 +* Solr: http://lucene.apache.org/solr
28 28 * RMagick: http://rmagick.rubyforge.org/
29 29 * RedCloth: http://redcloth.org/
30 30 * will_paginate: http://github.com/mislav/will_paginate/wikis
... ... @@ -115,8 +115,11 @@ $ tar -zxvf noosfero-0.27.1.tar.gz
115 115 $ ln -s noosfero-0.27.1 current
116 116 $ cd current
117 117  
118   -Copy config/ferret_server.yml.dist to config/ferret_server.yml. You will
  118 +Copy config/solr.yml.dist to config/solr.yml. You will
119 119 probably not need to customize this configuration, but have a look at it.
  120 +Then you'll need to download Solr into noosfero:
  121 +
  122 +$ rake solr:download
120 123  
121 124 Create the mongrel configuration file:
122 125  
... ... @@ -237,6 +240,10 @@ Create the database structure:
237 240  
238 241 $ RAILS_ENV=production rake db:schema:load
239 242  
  243 +Run Solr:
  244 +
  245 +$ rake solr:start
  246 +
240 247 Now we have to create some initial data. To create your default environment
241 248 (the first one), run the command below:
242 249  
... ...
Rakefile
... ... @@ -7,4 +7,6 @@ require 'rake'
7 7 require 'rake/testtask'
8 8 require 'rake/rdoctask'
9 9  
  10 +ACTS_AS_SEARCHABLE_ENABLED = false if Rake.application.top_level_tasks.detect{|t| t == 'db:data:minimal'}
  11 +
10 12 require 'tasks/rails'
... ...
app/controllers/my_profile/profile_members_controller.rb
... ... @@ -126,11 +126,11 @@ class ProfileMembersController < MyProfileController
126 126 if !params[:query] || params[:query].length <= 2
127 127 @users_found = []
128 128 elsif params[:scope] == 'all_users'
129   - @users_found = Person.find_by_contents(params[:query] + '*').select {|user| !profile.members.include?(user)}
  129 + @users_found = Person.find_by_contents(params[:query] + '*')[:results].select {|user| !profile.members.include?(user)}
130 130 @button_alt = _('Add member')
131 131 @add_action = {:action => 'add_member'}
132 132 elsif params[:scope] == 'new_admins'
133   - @users_found = Person.find_by_contents(params[:query] + '*').select {|user| profile.members.include?(user) && !profile.admins.include?(user)}
  133 + @users_found = Person.find_by_contents(params[:query] + '*')[:results].select {|user| profile.members.include?(user) && !profile.admins.include?(user)}
134 134 @button_alt = _('Add member')
135 135 @add_action = {:action => 'add_admin'}
136 136 end
... ...
app/controllers/public/browse_controller.rb
... ... @@ -19,7 +19,7 @@ class BrowseController &lt; PublicController
19 19 @results = @environment.people.visible.send(@filter)
20 20  
21 21 if !params[:query].blank?
22   - @results = @results.find_by_contents(params[:query])
  22 + @results = @results.find_by_contents(params[:query])[:results]
23 23 end
24 24 @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
25 25 end
... ... @@ -31,7 +31,7 @@ class BrowseController &lt; PublicController
31 31 @results = @environment.communities.visible.send(@filter)
32 32  
33 33 if !params[:query].blank?
34   - @results = @results.find_by_contents(params[:query])
  34 + @results = @results.find_by_contents(params[:query])[:results]
35 35 end
36 36 @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
37 37 end
... ...
app/controllers/public/profile_search_controller.rb
... ... @@ -8,11 +8,10 @@ class ProfileSearchController &lt; PublicController
8 8 def index
9 9 @q = params[:q]
10 10 unless @q.blank?
11   - @filtered_query = remove_stop_words(@q)
12 11 if params[:where] == 'environment'
13 12 redirect_to :controller => 'search', :query => @q
14 13 else
15   - @results = profile.articles.published.find_by_contents(@filtered_query).paginate(:per_page => 10, :page => params[:page])
  14 + @results = profile.articles.published.find_by_contents(@q)[:results].paginate(:per_page => 10, :page => params[:page])
16 15 end
17 16 end
18 17 end
... ...
app/controllers/public/search_controller.rb
... ... @@ -89,7 +89,8 @@ class SearchController &lt; PublicController
89 89 # REFACTOR DUPLICATED CODE inner loop doing the same thing that outter loop
90 90  
91 91 if !@query.blank? || @region && !params[:radius].blank?
92   - @result_ids = @noosfero_finder.find(asset, @filtered_query, calculate_find_options(asset, nil, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]).merge({:limit => :all}))
  92 + ret = @noosfero_finder.find(asset, @query, calculate_find_options(asset, nil, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]).merge({:limit => :all}))
  93 + @result_ids = ret.is_a?(Hash) ? ret[:results] : ret
93 94 end
94 95  
95 96 end
... ... @@ -149,7 +150,6 @@ class SearchController &lt; PublicController
149 150  
150 151 def index
151 152 @query = params[:query] || ''
152   - @filtered_query = remove_stop_words(@query)
153 153 @product_category = ProductCategory.find(params[:product_category]) if params[:product_category]
154 154  
155 155 @region = City.find_by_id(params[:city]) if !params[:city].blank? && params[:city] =~ /^\d+$/
... ... @@ -158,12 +158,16 @@ class SearchController &lt; PublicController
158 158 number_of_result_assets = @searching.values.select{|v| v}.size
159 159  
160 160 @results = {}
  161 + @facets = {}
161 162 @order = []
162 163 @names = {}
163 164  
164 165 where_to_search.select { |key,description| @searching[key] }.each do |key, description|
165 166 @order << key
166   - @results[key] = @noosfero_finder.find(key, @filtered_query, calculate_find_options(key, limit, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]))
  167 + find_options = calculate_find_options(key, limit, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]);
  168 + ret = @noosfero_finder.find(key, @query, find_options)
  169 + @results[key] = ret.is_a?(Hash) ? ret[:results] : ret
  170 + @facets[key] = ret.is_a?(Hash) ? ret[:facets] : {}
167 171 @names[key] = getterm(description)
168 172 end
169 173  
... ...
app/helpers/search_helper.rb
... ... @@ -3,21 +3,12 @@ module SearchHelper
3 3 # FIXME remove it after search_controler refactored
4 4 include EventsHelper
5 5  
6   - STOP_WORDS = {
7   - 'pt_BR' => Ferret::Analysis::FULL_PORTUGUESE_STOP_WORDS,
8   - 'en' => Ferret::Analysis::FULL_ENGLISH_STOP_WORDS,
9   - }
10   -
11 6 def relevance_for(hit)
12 7 n = (hit.ferret_score if hit.respond_to?(:ferret_score))
13 8 n ||= 1.0
14 9 (n * 100.0).round
15 10 end
16 11  
17   - def remove_stop_words(query)
18   - (query.downcase.scan(/"[^"]*"?|'[^']*'?|[^'"\s]+/) - (STOP_WORDS[locale] || [])).join(' ')
19   - end
20   -
21 12 def display_results(use_map = true)
22 13  
23 14 unless use_map && GoogleMaps.enabled?(environment.default_hostname)
... ...
app/models/article.rb
... ... @@ -388,7 +388,7 @@ class Article &lt; ActiveRecord::Base
388 388 end
389 389  
390 390 def comments_updated
391   - ferret_update
  391 + solr_save
392 392 end
393 393  
394 394 def accept_category?(cat)
... ...
app/models/category_finder.rb
... ... @@ -28,10 +28,12 @@ class CategoryFinder
28 28 end
29 29  
30 30 if query.blank?
  31 + options.delete(:facets)
31 32 asset_class(asset).send(finder_method, :all, options_for_find(asset_class(asset), {:order => "#{asset_table(asset)}.name"}.merge(options), date_range))
32 33 else
33   - ferret_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}
34   - asset_class(asset).find_by_contents(query, ferret_options, options_for_find(asset_class(asset), options, date_range))
  34 + pg_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}
  35 + solr_options = {:facets => options.delete(:facets)}
  36 + asset_class(asset).find_by_contents(query, pg_options, solr_options, options_for_find(asset_class(asset), options, date_range))[:results]
35 37 end
36 38 end
37 39  
... ...
app/models/enterprise.rb
... ... @@ -71,7 +71,7 @@ class Enterprise &lt; Organization
71 71 end
72 72  
73 73 def product_updated
74   - ferret_update
  74 + solr_save
75 75 end
76 76  
77 77 after_save do |e|
... ...
app/models/environment_finder.rb
... ... @@ -22,6 +22,8 @@ class EnvironmentFinder
22 22 end
23 23  
24 24 if query.blank?
  25 + options.delete(:facets)
  26 +
25 27 # FIXME this test is in more than one place
26 28 if finder_method == 'paginate'
27 29 options = {:order => "#{asset_table(asset)}.name"}.merge(options)
... ... @@ -48,14 +50,20 @@ class EnvironmentFinder
48 50 end
49 51 end
50 52 else
51   - ferret_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}
  53 + pg_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}
  54 + solr_options = {:facets => options.delete(:facets)}
52 55 if product_category && asset == :products
53 56 # SECURITY no risk of SQL injection, since product_category_ids comes from trusted source
54   - @environment.send(asset).find_by_contents(query, ferret_options, options.merge({:include => 'product_categorizations', :conditions => 'product_categorizations.category_id = (%s)' % product_category.id }))
  57 + ret = @environment.send(asset).find_by_contents(query, pg_options, solr_options, options.merge({:include => 'product_categorizations', :conditions => 'product_categorizations.category_id = (%s)' % product_category.id }))
55 58 elsif product_category && asset == :enterprises
56   - @environment.send(asset).find_by_contents(query, ferret_options, options.merge(:joins => 'inner join product_categorizations on (product_categorizations.product_id = products.id)', :include => 'products', :conditions => "product_categorizations.category_id = (#{product_category.id})"))
  59 + ret = @environment.send(asset).find_by_contents(query, pg_options, solr_options, options.merge(:joins => 'inner join product_categorizations on (product_categorizations.product_id = products.id)', :include => 'products', :conditions => "product_categorizations.category_id = (#{product_category.id})"))
  60 + else
  61 + ret = @environment.send(asset).find_by_contents(query, pg_options, solr_options, options)
  62 + end
  63 + if solr_options[:facets].nil?
  64 + ret[:results]
57 65 else
58   - @environment.send(asset).find_by_contents(query, ferret_options, options)
  66 + ret
59 67 end
60 68 end
61 69 end
... ...
app/models/region.rb
... ... @@ -5,10 +5,9 @@ class Region &lt; Category
5 5 require_dependency 'enterprise' # enterprises can also be validators
6 6  
7 7 # searches for organizations that could become validators for this region.
8   - # <tt>search</tt> is passed as is to ferret's find_by_contents on Organizatio
9   - # find_by_contents on Organization class.
  8 + # <tt>search</tt> is passed as is to find_by_contents on Organization.
10 9 def search_possible_validators(search)
11   - Organization.find_by_contents(search).reject {|item| self.validator_ids.include?(item.id) }
  10 + Organization.find_by_contents(search)[:results].reject {|item| self.validator_ids.include?(item.id) }
12 11 end
13 12  
14 13 def has_validator?
... ...
config/solr.yml.dist 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +# Config file for the acts_as_solr plugin.
  2 +#
  3 +# If you change the host or port number here, make sure you update
  4 +# them in your Solr config file
  5 +
  6 +development:
  7 + url: http://0.0.0.0:8982/solr
  8 + jvm_options: -server -Xmx64M -Xms16M
  9 +
  10 +production:
  11 + url: http://127.0.0.1:8983/solr
  12 + jvm_options: -server -Xmx512M -Xms64M
  13 +
  14 +test:
  15 + url: http://0.0.0.0:8981/solr
  16 + jvm_options: -server -Xmx32M -Xms16M
  17 +
... ...
debian/control
... ... @@ -11,7 +11,7 @@ Vcs-Browser: http://git.colivre.coop.br/?p=noosfero.git
11 11  
12 12 Package: noosfero
13 13 Architecture: all
14   -Depends: rails, ruby1.8, ruby, rake, libgettext-ruby-data, libsqlite3-ruby, libpgsql-ruby, libmysql-ruby, librmagick-ruby, libredcloth-ruby, libwill-paginate-ruby, iso-codes, libfeedparser-ruby, libferret-ruby, libdaemons-ruby, rcov, mongrel, mongrel-cluster, tango-icon-theme, libhpricot-ruby, iso-codes, memcached, debconf, dbconfig-common, postgresql, adduser, ${misc:Depends}
  14 +Depends: rails, ruby1.8, ruby, rake, libgettext-ruby-data, libsqlite3-ruby, libpgsql-ruby, libmysql-ruby, librmagick-ruby, libredcloth-ruby, libwill-paginate-ruby, iso-codes, libfeedparser-ruby, openjdk-6-jre, libdaemons-ruby, rcov, mongrel, mongrel-cluster, tango-icon-theme, libhpricot-ruby, iso-codes, memcached, debconf, dbconfig-common, postgresql, adduser, ${misc:Depends}
15 15 Recommends: postgresql-client
16 16 Description: free web-based platform for social networks
17 17 Noosfero is a web platform for social and solidarity economy networks with
... ...
debian/ferret_server.yml
... ... @@ -1,4 +0,0 @@
1   -production:
2   - host: localhost
3   - port: 51000
4   - pid_file: tmp/pids/ferret.production.pid
debian/noosfero.install
... ... @@ -28,7 +28,7 @@ public usr/share/noosfero
28 28 debian/default/noosfero etc/default
29 29 etc/init.d/noosfero etc/init.d
30 30 debian/mongrel_cluster.yml etc/noosfero
31   -debian/ferret_server.yml etc/noosfero
  31 +debian/solr.yml etc/noosfero
32 32 etc/logrotate.d/noosfero etc/logrotate.d
33 33 debian/noosfero.yml etc/noosfero
34 34  
... ...
debian/noosfero.links
... ... @@ -3,7 +3,7 @@ var/tmp/noosfero usr/share/noosfero/tmp
3 3 var/log/noosfero usr/share/noosfero/log
4 4 etc/noosfero/database.yml usr/share/noosfero/config/database.yml
5 5 etc/noosfero/mongrel_cluster.yml usr/share/noosfero/config/mongrel_cluster.yml
6   -etc/noosfero/ferret_server.yml usr/share/noosfero/config/ferret_server.yml
  6 +etc/noosfero/solr.yml usr/share/noosfero/config/solr.yml
7 7 etc/noosfero/plugins usr/share/noosfero/config/plugins
8 8 etc/noosfero/noosfero.yml usr/share/noosfero/config/noosfero.yml
9 9 etc/noosfero/local.rb usr/share/noosfero/config/local.rb
... ...
debian/solr.yml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +# Config file for the acts_as_solr plugin.
  2 +#
  3 +# If you change the host or port number here, make sure you update
  4 +# them in your Solr config file
  5 +
  6 +production:
  7 + url: http://127.0.0.1:8983/solr
  8 + jvm_options: -server -Xmx512M -Xms64M
  9 +
... ...
etc/init.d/noosfero
... ... @@ -43,14 +43,14 @@ if [ -z &quot;$NOOSFERO_DIR&quot; ] || [ -z &quot;$NOOSFERO_USER&quot; ]; then
43 43 fi
44 44  
45 45 ######################
46   -FERRET_PID_FILE=$NOOSFERO_DIR/tmp/pids/ferret.production.pid
  46 +SOLR_PID_FILE=$NOOSFERO_DIR/tmp/pids/solr.production.pid
47 47  
48 48 main_script() {
49 49 cd $NOOSFERO_DIR
50 50 if [ "$NOOSFERO_USER" != "$USER" ]; then
51   - su $NOOSFERO_USER -l -c "./script/production $1"
  51 + su $NOOSFERO_USER -l -c "SOLR_DATA_PATH=/var/lib/noosfero-data/index ./script/production $1"
52 52 else
53   - ./script/production $1
  53 + SOLR_DATA_PATH=/var/lib/noosfero-data/index ./script/production $1
54 54 fi
55 55 }
56 56  
... ... @@ -76,6 +76,13 @@ do_setup() {
76 76 chmod 750 /var/tmp/noosfero
77 77 fi
78 78  
  79 + # Solr directory
  80 + if [ ! -d /var/tmp/noosfero/solr ]; then
  81 + mkdir -p /var/tmp/noosfero/solr
  82 + chown $NOOSFERO_USER:root /var/tmp/noosfero/solr
  83 + chmod 750 /var/tmp/noosfero/solr
  84 + fi
  85 +
79 86 # symlink the directories into Noosfero directory
80 87 if [ ! -e $NOOSFERO_DIR/tmp ]; then
81 88 ln -s /var/tmp/noosfero $NOOSFERO_DIR/tmp
... ... @@ -90,8 +97,8 @@ do_setup() {
90 97  
91 98 do_start() {
92 99  
93   - # FIXME should not test for ferret only
94   - if [ -e $FERRET_PID_FILE ]; then
  100 + # FIXME should not test for solr only
  101 + if [ -e $SOLR_PID_FILE ]; then
95 102 echo 'noosfero already running, cannot start.'
96 103 exit 2
97 104 fi
... ... @@ -104,8 +111,8 @@ do_start() {
104 111  
105 112 do_stop() {
106 113  
107   - # FIXME should not test for ferret only
108   - if [ ! -e $FERRET_PID_FILE ]; then
  114 + # FIXME should not test for solr only
  115 + if [ ! -e $SOLR_PID_FILE ]; then
109 116 echo 'noosfero not running, cannot stop'
110 117 exit 2
111 118 fi
... ...
lib/acts_as_searchable.rb
1 1 module ActsAsSearchable
2 2  
3 3 module ClassMethods
  4 + ACTS_AS_SEARCHABLE_ENABLED = true unless defined? ACTS_AS_SEARCHABLE_ENABLED
  5 +
4 6 def acts_as_searchable(options = {})
5   - if Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?
6   - options[:additional_fields] ||= {}
7   - options[:additional_fields] = Hash[*options[:additional_fields].collect{ |v| [v, {}] }.flatten] if options[:additional_fields].is_a?(Array)
8   - options[:additional_fields].merge!(:schema_name => { :index => :untokenized })
  7 + if ACTS_AS_SEARCHABLE_ENABLED
  8 + if (!options[:fields])
  9 + options[:additional_fields] |= [{:schema_name => :string}]
  10 + else
  11 + options[:fields] << {:schema_name => :string}
  12 + end
  13 + acts_as_solr options
  14 + extend FindByContents
  15 + send :include, InstanceMethods
9 16 end
10   - acts_as_ferret({ :remote => true }.merge(options))
11   - extend FindByContents
12   - send :include, InstanceMethods
13 17 end
14 18  
15 19 module InstanceMethods
16 20 def schema_name
17   - ActiveRecord::Base.connection.schema_search_path
  21 + (Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?) ? ActiveRecord::Base.connection.schema_search_path : ''
18 22 end
19 23 end
20 24  
21 25 module FindByContents
22 26  
23 27 def schema_name
24   - ActiveRecord::Base.connection.schema_search_path
  28 + (Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?) ? ActiveRecord::Base.connection.schema_search_path : ''
25 29 end
26 30  
27   - def find_by_contents(query, ferret_options = {}, db_options = {})
28   - pg_options = {}
29   - if ferret_options[:page]
30   - pg_options[:page] = ferret_options.delete(:page)
31   - end
32   - if ferret_options[:per_page]
33   - pg_options[:per_page] = ferret_options.delete(:per_page)
34   - end
  31 + def find_by_contents(query, pg_options = {}, options = {}, db_options = {})
  32 + pg_options[:page] ||= 1
  33 + pg_options[:per_page] ||= 20
  34 + options[:limit] = pg_options[:per_page].to_i*pg_options[:page].to_i
  35 + options[:scores] = true;
35 36  
36   - ferret_options[:limit] = :all
  37 + query = !schema_name.empty? ? "+schema_name:\"#{schema_name}\" AND #{query}" : query
  38 + solr_result = find_by_solr(query, options)
  39 + if solr_result.nil?
  40 + results = facets = []
  41 + else
  42 + facets = options.include?(:facets) ? solr_result.facets : []
37 43  
38   - ferret_query = (Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?) ? "+schema_name:\"#{schema_name}\" AND #{query}" : query
39   - # FIXME this is a HORRIBLE HACK
40   - ids = find_ids_with_ferret(ferret_query, ferret_options)[1][0..8000].map{|r|r[:id].to_i}
  44 + if db_options.empty?
  45 + results = solr_result.results
  46 + else
  47 + ids = solr_result.results.map{|r|r[:id].to_i}
  48 + if ids.empty?
  49 + ids << -1
  50 + end
41 51  
42   - if ids.empty?
43   - ids << -1
44   - end
  52 + if db_options[:conditions]
  53 + db_options[:conditions] = sanitize_sql_for_conditions(db_options[:conditions]) + " and #{table_name}.id in (#{ids.join(', ')})"
  54 + else
  55 + db_options[:conditions] = "#{table_name}.id in (#{ids.join(', ')})"
  56 + end
45 57  
46   - if db_options[:conditions]
47   - db_options[:conditions] = sanitize_sql_for_conditions(db_options[:conditions]) + " and #{table_name}.id in (#{ids.join(', ')})"
48   - else
49   - db_options[:conditions] = "#{table_name}.id in (#{ids.join(', ')})"
  58 + results = find(:all, db_options)
  59 + end
  60 +
  61 + results = results.paginate(pg_options.merge(:total_entries => solr_result.total))
50 62 end
51 63  
52   - pg_options[:page] ||= 1
53   - result = find(:all, db_options)
54   - result.paginate(pg_options)
  64 + {:results => results, :facets => facets}
55 65 end
56 66 end
57 67 end
... ...
lib/create_thumbnails_job.rb
1 1 class CreateThumbnailsJob < Struct.new(:class_name, :file_id)
2 2 def perform
3 3 return unless class_name.constantize.exists?(file_id)
4   - Article.disable_ferret # acts_as_ferret sucks
5 4 file = class_name.constantize.find(file_id)
6 5 file.create_thumbnails
7   - Article.enable_ferret # acts_as_ferret sucks
8 6 end
9 7 end
... ...
lib/tasks/package.rake
... ... @@ -8,10 +8,22 @@ task :package =&gt; &#39;package:clobber&#39; do
8 8 puts "** The `package` task only works from within #{Noosfero::PROJECT}'s git repository."
9 9 fail
10 10 end
  11 + begin
  12 + sh 'test -f vendor/plugins/acts_as_solr/solr/start.jar'
  13 + rescue
  14 + puts "** The `package` task needs Solr installed within #{Noosfero::PROJECT}. Run 'rake solr:download'."
  15 + fail
  16 + end
11 17 release = "#{Noosfero::PROJECT}-#{Noosfero::VERSION}"
12 18 target = "pkg/#{release}"
13 19 mkdir_p target
14 20 sh "git archive HEAD | (cd #{target} && tar x)"
  21 +
  22 + #solr inclusion
  23 + cp_r "vendor/plugins/acts_as_solr/solr", "#{target}/vendor/plugins/acts_as_solr", :verbose => true
  24 + rm_r "#{target}/vendor/plugins/acts_as_solr/solr/work"
  25 + mkdir_p "#{target}/vendor/plugins/acts_as_solr/solr/work"
  26 +
15 27 sh "cd pkg && tar czf #{release}.tar.gz #{release}"
16 28 end
17 29  
... ...
lib/tasks/test.rake
... ... @@ -7,6 +7,10 @@ else
7 7 end
8 8  
9 9 task :test do
  10 + ENV['RAILS_ENV'] = 'test'
  11 + Rake::Task['solr:stop'].invoke
  12 + Rake::Task['solr:download'].invoke
  13 + Rake::Task['solr:start'].invoke
10 14 errors = %w(test:units test:functionals test:integration cucumber selenium).collect do |task|
11 15 begin
12 16 Rake::Task[task].invoke
... ... @@ -15,6 +19,7 @@ task :test do
15 19 task
16 20 end
17 21 end.compact
  22 + Rake::Task['solr:stop'].invoke
18 23 abort "Errors running #{errors.to_sentence}!" if errors.any?
19 24 end
20 25  
... ...
script/development
1 1 #!/bin/sh
2 2  
  3 +export RAILS_ENV=development
  4 +
3 5 stop() {
4 6 ./script/delayed_job stop
5 7 ./script/feed-updater stop
  8 + rake -s solr:stop
6 9 }
7 10  
8 11 start() {
9 12 ./script/feed-updater start
10 13 ./script/delayed_job start
  14 + rake -s solr:download
  15 + rake -s solr:start
11 16 trap stop INT TERM
12 17 ./script/server $@
13 18 }
... ...
script/ferret_server
... ... @@ -1,10 +0,0 @@
1   -#!/usr/bin/env ruby
2   -
3   -begin
4   - require File.join(File.dirname(__FILE__), '../vendor/plugins/acts_as_ferret/lib/server_manager')
5   -rescue LoadError
6   - # try the gem
7   - require 'rubygems'
8   - gem 'acts_as_ferret'
9   - require 'server_manager'
10   -end
script/production
... ... @@ -22,7 +22,7 @@ do_start() {
22 22 fi
23 23  
24 24 clear_cache
25   - ./script/ferret_server -e $RAILS_ENV start
  25 + rake -s solr:start
26 26 environments_loop
27 27 mongrel_rails cluster::start
28 28 }
... ... @@ -31,7 +31,7 @@ do_stop() {
31 31 mongrel_rails cluster::stop
32 32 ./script/delayed_job stop
33 33 ./script/feed-updater stop
34   - ./script/ferret_server -e $RAILS_ENV stop
  34 + rake -s solr:stop
35 35 }
36 36  
37 37 environments_loop() {
... ...
test/factories.rb
... ... @@ -22,7 +22,7 @@ module Noosfero::Factory
22 22 end
23 23 end
24 24 if options[:search]
25   - obj.ferret_create
  25 + obj.solr_save
26 26 end
27 27 obj
28 28 end
... ...
test/functional/profile_search_controller_test.rb
... ... @@ -6,6 +6,7 @@ class ProfileSearchController; def rescue_action(e) raise e end; end
6 6  
7 7 class ProfileSearchControllerTest < Test::Unit::TestCase
8 8 def setup
  9 + Test::Unit::TestCase::setup
9 10 @controller = ProfileSearchController.new
10 11 @request = ActionController::TestRequest.new
11 12 @response = ActionController::TestResponse.new
... ... @@ -14,14 +15,6 @@ class ProfileSearchControllerTest &lt; Test::Unit::TestCase
14 15 end
15 16 attr_reader :person
16 17  
17   - should 'filter stop words' do
18   - @controller.expects(:locale).returns('en').at_least_once
19   - get 'index', :profile => person.identifier, :q => 'an article about something'
20   - assert_response :success
21   - assert_template 'index'
22   - assert_equal 'article something', assigns('filtered_query')
23   - end
24   -
25 18 should 'espape xss attack' do
26 19 @controller.expects(:profile).returns(person).at_least_once
27 20 get 'index', :profile => person.identifier, :q => '<wslite>'
... ... @@ -41,8 +34,8 @@ class ProfileSearchControllerTest &lt; Test::Unit::TestCase
41 34 end
42 35  
43 36 should 'display search results' do
44   - article1 = fast_create(Article, :body => '<p>Article to test profile search</p>', :profile_id => person.id)
45   - article2 = fast_create(Article, :body => '<p>Another article to test profile search</p>', :profile_id => person.id)
  37 + article1 = fast_create(Article, {:body => '<p>Article to test profile search</p>', :profile_id => person.id}, :search => true)
  38 + article2 = fast_create(Article, {:body => '<p>Another article to test profile search</p>', :profile_id => person.id}, :search => true)
46 39  
47 40 get 'index', :profile => person.identifier, :q => 'article'
48 41  
... ...
test/functional/search_controller_test.rb
... ... @@ -6,6 +6,7 @@ class SearchController; def rescue_action(e) raise e end; end
6 6  
7 7 class SearchControllerTest < Test::Unit::TestCase
8 8 def setup
  9 + Test::Unit::TestCase::setup
9 10 @controller = SearchController.new
10 11 @request = ActionController::TestRequest.new
11 12 @response = ActionController::TestResponse.new
... ... @@ -35,21 +36,6 @@ class SearchControllerTest &lt; Test::Unit::TestCase
35 36 assert_valid_xhtml
36 37 end
37 38  
38   - should 'filter stop words' do
39   - @controller.expects(:locale).returns('pt_BR').at_least_once
40   - get 'index', :query => 'a carne da vaca'
41   - assert_response :success
42   - assert_template 'index'
43   - assert_equal 'carne vaca', assigns('filtered_query')
44   - end
45   -
46   - should 'search with filtered query' do
47   - @controller.expects(:locale).returns('pt_BR').at_least_once
48   - get 'index', :query => 'a carne da vaca'
49   -
50   - assert_equal 'carne vaca', assigns('filtered_query')
51   - end
52   -
53 39 should 'espape xss attack' do
54 40 get 'index', :query => '<wslite>'
55 41 assert_no_tag :tag => 'wslite'
... ...
test/test_helper.rb
... ... @@ -47,6 +47,11 @@ class Test::Unit::TestCase
47 47 include AuthenticatedTestHelper
48 48  
49 49 fixtures :environments, :roles
  50 +
  51 + def self.setup
  52 + # clean up index db before each test
  53 + ActsAsSolr::Post.execute(Solr::Request::Delete.new(:query => '*:*'))
  54 + end
50 55  
51 56 def self.all_fixtures
52 57 Dir.glob(File.join(RAILS_ROOT, 'test', 'fixtures', '*.yml')).each do |item|
... ... @@ -190,7 +195,6 @@ class Test::Unit::TestCase
190 195 adapter.any_instance.stubs(:adapter_name).returns('PostgreSQL')
191 196 adapter.any_instance.stubs(:schema_search_path).returns(schema_name)
192 197 Noosfero::MultiTenancy.stubs(:on?).returns(true)
193   - reload_for_ferret
194 198 end
195 199  
196 200 def uses_sqlite
... ... @@ -199,20 +203,6 @@ class Test::Unit::TestCase
199 203 Noosfero::MultiTenancy.stubs(:on?).returns(false)
200 204 end
201 205  
202   - def reload_for_ferret
203   - ActsAsFerret.send(:remove_const, :DEFAULT_FIELD_OPTIONS)
204   - load File.join(RAILS_ROOT, 'lib', 'acts_as_searchable.rb')
205   - load File.join(RAILS_ROOT, 'vendor', 'plugins', 'acts_as_ferret', 'lib', 'acts_as_ferret.rb')
206   - [Article, Profile, Product].each do |clazz|
207   - inst_meth = clazz.instance_methods.reject{ |m| m =~ /_to_ferret$/ }
208   - clazz.stubs(:instance_methods).returns(inst_meth)
209   - end
210   - #FIXME Is there a way to avoid this replication from model code?
211   - Article.acts_as_searchable :additional_fields => [ :comment_data ]
212   - Profile.acts_as_searchable :additional_fields => [ :extra_data_for_index ]
213   - Product.acts_as_searchable :fields => [ :name, :description, :category_full_name ]
214   - end
215   -
216 206 end
217 207  
218 208 module NoosferoTestHelper
... ...
test/unit/article_test.rb
... ... @@ -5,6 +5,7 @@ class ArticleTest &lt; Test::Unit::TestCase
5 5 fixtures :environments
6 6  
7 7 def setup
  8 + Test::Unit::TestCase::setup
8 9 @profile = create_user('testing').person
9 10 end
10 11 attr_reader :profile
... ... @@ -356,7 +357,7 @@ class ArticleTest &lt; Test::Unit::TestCase
356 357  
357 358 should 'reindex when comments are changed' do
358 359 a = Article.new
359   - a.expects(:ferret_update)
  360 + a.expects(:solr_save)
360 361 a.comments_updated
361 362 end
362 363  
... ... @@ -365,7 +366,7 @@ class ArticleTest &lt; Test::Unit::TestCase
365 366 art = owner.articles.build(:name => 'ytest'); art.save!
366 367 c1 = art.comments.build(:title => 'a nice comment', :body => 'anything', :author => owner); c1.save!
367 368  
368   - assert_includes Article.find_by_contents('nice'), art
  369 + assert_includes Article.find_by_contents('nice')[:results], art
369 370 end
370 371  
371 372 should 'index comments body together with article' do
... ... @@ -373,7 +374,7 @@ class ArticleTest &lt; Test::Unit::TestCase
373 374 art = owner.articles.build(:name => 'ytest'); art.save!
374 375 c1 = art.comments.build(:title => 'test comment', :body => 'anything', :author => owner); c1.save!
375 376  
376   - assert_includes Article.find_by_contents('anything'), art
  377 + assert_includes Article.find_by_contents('anything')[:results], art
377 378 end
378 379  
379 380 should 'cache children count' do
... ... @@ -1506,24 +1507,24 @@ class ArticleTest &lt; Test::Unit::TestCase
1506 1507 should 'index by schema name when database is postgresql' do
1507 1508 uses_postgresql 'schema_one'
1508 1509 art1 = Article.create!(:name => 'some thing', :profile_id => @profile.id)
1509   - assert_equal Article.find_by_contents('thing'), [art1]
  1510 + assert_equal Article.find_by_contents('thing')[:results], [art1]
1510 1511 uses_postgresql 'schema_two'
1511 1512 art2 = Article.create!(:name => 'another thing', :profile_id => @profile.id)
1512   - assert_not_includes Article.find_by_contents('thing'), art1
1513   - assert_includes Article.find_by_contents('thing'), art2
  1513 + assert_not_includes Article.find_by_contents('thing')[:results], art1
  1514 + assert_includes Article.find_by_contents('thing')[:results], art2
1514 1515 uses_postgresql 'schema_one'
1515   - assert_includes Article.find_by_contents('thing'), art1
1516   - assert_not_includes Article.find_by_contents('thing'), art2
  1516 + assert_includes Article.find_by_contents('thing')[:results], art1
  1517 + assert_not_includes Article.find_by_contents('thing')[:results], art2
1517 1518 uses_sqlite
1518 1519 end
1519 1520  
1520 1521 should 'not index by schema name when database is not postgresql' do
1521 1522 uses_sqlite
1522 1523 art1 = Article.create!(:name => 'some thing', :profile_id => @profile.id)
1523   - assert_equal Article.find_by_contents('thing'), [art1]
  1524 + assert_equal Article.find_by_contents('thing')[:results], [art1]
1524 1525 art2 = Article.create!(:name => 'another thing', :profile_id => @profile.id)
1525   - assert_includes Article.find_by_contents('thing'), art1
1526   - assert_includes Article.find_by_contents('thing'), art2
  1526 + assert_includes Article.find_by_contents('thing')[:results], art1
  1527 + assert_includes Article.find_by_contents('thing')[:results], art2
1527 1528 end
1528 1529  
1529 1530 should 'get images paths in article body' do
... ...
test/unit/category_finder_test.rb
... ... @@ -3,11 +3,12 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class CategoryFinderTest < ActiveSupport::TestCase
4 4  
5 5 def setup
  6 + Test::Unit::TestCase::setup
6 7 @category = Category.create!(:name => 'my category', :environment => Environment.default)
7 8 @finder = CategoryFinder.new(@category)
8 9 @product_category = fast_create(ProductCategory, :name => 'Products')
9 10  
10   - Profile.rebuild_index
  11 + Profile.rebuild_solr_index
11 12 end
12 13  
13 14 should 'search for articles in a specific category' do
... ...
test/unit/enterprise_test.rb
... ... @@ -4,6 +4,7 @@ class EnterpriseTest &lt; Test::Unit::TestCase
4 4 fixtures :profiles, :environments, :users
5 5  
6 6 def setup
  7 + Test::Unit::TestCase::setup
7 8 @product_category = fast_create(ProductCategory, :name => 'Products')
8 9 end
9 10  
... ... @@ -91,7 +92,7 @@ class EnterpriseTest &lt; Test::Unit::TestCase
91 92  
92 93 ent2 = fast_create(Enterprise, :name => 'test2', :identifier => 'test2')
93 94  
94   - result = Enterprise.find_by_contents(prod_cat.name)
  95 + result = Enterprise.find_by_contents(prod_cat.name)[:results]
95 96  
96 97 assert_includes result, ent1
97 98 assert_not_includes result, ent2
... ... @@ -105,7 +106,7 @@ class EnterpriseTest &lt; Test::Unit::TestCase
105 106  
106 107 ent2 = fast_create(Enterprise, :name => 'test2', :identifier => 'test2')
107 108  
108   - result = Enterprise.find_by_contents(prod_cat.name)
  109 + result = Enterprise.find_by_contents(prod_cat.name)[:results]
109 110  
110 111 assert_includes result, ent1
111 112 assert_not_includes result, ent2
... ... @@ -406,6 +407,12 @@ class EnterpriseTest &lt; Test::Unit::TestCase
406 407  
407 408 assert_equal product.inputs, enterprise.inputs
408 409 end
  410 +
  411 + should 'reindex when products are changed' do
  412 + a = Enterprise.new
  413 + a.expects(:solr_save)
  414 + a.product_updated
  415 + end
409 416  
410 417 should "the followed_by? be true only to members" do
411 418 e = fast_create(Enterprise)
... ...
test/unit/environment_finder_test.rb
... ... @@ -3,6 +3,7 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class EnvironmentFinderTest < ActiveSupport::TestCase
4 4  
5 5 def setup
  6 + Test::Unit::TestCase::setup
6 7 @product_category = fast_create(ProductCategory, :name => 'Products')
7 8 end
8 9  
... ... @@ -77,10 +78,10 @@ class EnvironmentFinderTest &lt; ActiveSupport::TestCase
77 78 finder = EnvironmentFinder.new(Environment.default)
78 79  
79 80 region = fast_create(Region, :name => 'r-test', :environment_id => Environment.default.id, :lat => 45.0, :lng => 45.0)
80   - ent1 = fast_create(Enterprise, :name => 'test 1', :identifier => 'test1', :lat => 45.0, :lng => 45.0)
  81 + ent1 = fast_create(Enterprise, {:name => 'test 1', :identifier => 'test1', :lat => 45.0, :lng => 45.0}, :search => true)
81 82 p1 = create_user('test2').person
82 83 p1.name = 'test 2'; p1.lat = 45.0; p1.lng = 45.0; p1.save!
83   - ent2 = fast_create(Enterprise, :name => 'test 3', :identifier => 'test3', :lat => 30.0, :lng => 30.0)
  84 + ent2 = fast_create(Enterprise, {:name => 'test 3', :identifier => 'test3', :lat => 30.0, :lng => 30.0}, :search => true)
84 85 p2 = create_user('test4').person
85 86 p2.name = 'test 4'; p2.lat = 30.0; p2.lng = 30.0; p2.save!
86 87  
... ...
test/unit/environment_test.rb
... ... @@ -3,6 +3,10 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class EnvironmentTest < Test::Unit::TestCase
4 4 fixtures :environments
5 5  
  6 + def setup
  7 + Test::Unit::TestCase::setup
  8 + end
  9 +
6 10 def test_exists_default_and_it_is_unique
7 11 Environment.delete_all
8 12 vc = Environment.new(:name => 'Test Community')
... ... @@ -442,7 +446,7 @@ class EnvironmentTest &lt; Test::Unit::TestCase
442 446 should 'find by contents from articles' do
443 447 environment = fast_create(Environment)
444 448 assert_nothing_raised do
445   - environment.articles.find_by_contents('')
  449 + environment.articles.find_by_contents('')[:results]
446 450 end
447 451 end
448 452  
... ... @@ -559,7 +563,7 @@ class EnvironmentTest &lt; Test::Unit::TestCase
559 563 Enterprise.create!(:name => 'test ' + n, :identifier => 'test_' + n)
560 564 end
561 565  
562   - assert_equal 20, env.enterprises.find_by_contents('test').total_entries
  566 + assert_equal 20, env.enterprises.find_by_contents('test')[:results].total_entries
563 567 end
564 568  
565 569 should 'set replace_enterprise_template_when_enable on environment' do
... ...
test/unit/event_test.rb
... ... @@ -2,6 +2,10 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
2 2  
3 3 class EventTest < ActiveSupport::TestCase
4 4  
  5 + def setup
  6 + Test::Unit::TestCase::setup
  7 + end
  8 +
5 9 should 'be an article' do
6 10 assert_kind_of Article, Event.new
7 11 end
... ... @@ -59,13 +63,13 @@ class EventTest &lt; ActiveSupport::TestCase
59 63 should 'be indexed by title' do
60 64 profile = create_user('testuser').person
61 65 e = Event.create!(:name => 'my surprisingly nice event', :start_date => Date.new(2008, 06, 06), :profile => profile)
62   - assert_includes Event.find_by_contents('surprisingly'), e
  66 + assert_includes Event.find_by_contents('surprisingly')[:results], e
63 67 end
64 68  
65 69 should 'be indexed by body' do
66 70 profile = create_user('testuser').person
67 71 e = Event.create!(:name => 'bli', :start_date => Date.new(2008, 06, 06), :profile => profile, :body => 'my surprisingly long description about my freaking nice event')
68   - assert_includes Event.find_by_contents('surprisingly'), e
  72 + assert_includes Event.find_by_contents('surprisingly')[:results], e
69 73 end
70 74  
71 75 should 'use its own icon' do
... ...
test/unit/forum_helper_test.rb
... ... @@ -50,14 +50,14 @@ class ForumHelperTest &lt; Test::Unit::TestCase
50 50 some_post.comments << Comment.new(:title => 'test', :body => 'test', :author => a2)
51 51 c = Comment.last
52 52 assert_equal 2, some_post.comments.count
53   - assert_match /#{c.created_at.to_s} ago by <a href='[^']+'>a2<\/a>/, last_topic_update(some_post)
  53 + assert_match(/#{Regexp.escape(c.created_at.to_s)} ago by <a href='[^']+'>a2<\/a>/, last_topic_update(some_post))
54 54 end
55 55  
56 56 should "return last comment author's name from unauthenticated user" do
57 57 some_post = TextileArticle.create!(:name => 'First post', :profile => profile, :parent => forum, :published => true)
58 58 some_post.comments << Comment.new(:name => 'John', :email => 'lenon@example.com', :title => 'test', :body => 'test')
59 59 c = Comment.last
60   - assert_match /#{c.created_at.to_s} ago by John/m, last_topic_update(some_post)
  60 + assert_match(/#{Regexp.escape(c.created_at.to_s)} ago by John/m, last_topic_update(some_post))
61 61 end
62 62  
63 63 protected
... ...
test/unit/product_test.rb
... ... @@ -3,6 +3,7 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class ProductTest < Test::Unit::TestCase
4 4  
5 5 def setup
  6 + Test::Unit::TestCase::setup
6 7 @product_category = fast_create(ProductCategory, :name => 'Products')
7 8 end
8 9  
... ... @@ -92,7 +93,7 @@ class ProductTest &lt; Test::Unit::TestCase
92 93 p.stubs(:category_full_name).returns('interesting category')
93 94 p.save!
94 95  
95   - assert_includes Product.find_by_contents('interesting'), p
  96 + assert_includes Product.find_by_contents('interesting')[:results], p
96 97 end
97 98  
98 99 should 'have same lat and lng of its enterprise' do
... ... @@ -362,24 +363,24 @@ class ProductTest &lt; Test::Unit::TestCase
362 363 should 'index by schema name when database is postgresql' do
363 364 uses_postgresql 'schema_one'
364 365 p1 = Product.create!(:name => 'some thing', :product_category => @product_category)
365   - assert_equal Product.find_by_contents('thing'), [p1]
  366 + assert_equal Product.find_by_contents('thing')[:results], [p1]
366 367 uses_postgresql 'schema_two'
367 368 p2 = Product.create!(:name => 'another thing', :product_category => @product_category)
368   - assert_not_includes Product.find_by_contents('thing'), p1
369   - assert_includes Product.find_by_contents('thing'), p2
  369 + assert_not_includes Product.find_by_contents('thing')[:results], p1
  370 + assert_includes Product.find_by_contents('thing')[:results], p2
370 371 uses_postgresql 'schema_one'
371   - assert_includes Product.find_by_contents('thing'), p1
372   - assert_not_includes Product.find_by_contents('thing'), p2
  372 + assert_includes Product.find_by_contents('thing')[:results], p1
  373 + assert_not_includes Product.find_by_contents('thing')[:results], p2
373 374 uses_sqlite
374 375 end
375 376  
376 377 should 'not index by schema name when database is not postgresql' do
377 378 uses_sqlite
378 379 p1 = Product.create!(:name => 'some thing', :product_category => @product_category)
379   - assert_equal Product.find_by_contents('thing'), [p1]
  380 + assert_equal Product.find_by_contents('thing')[:results], [p1]
380 381 p2 = Product.create!(:name => 'another thing', :product_category => @product_category)
381   - assert_includes Product.find_by_contents('thing'), p1
382   - assert_includes Product.find_by_contents('thing'), p2
  382 + assert_includes Product.find_by_contents('thing')[:results], p1
  383 + assert_includes Product.find_by_contents('thing')[:results], p2
383 384 end
384 385  
385 386 end
... ...
test/unit/profile_test.rb
... ... @@ -3,6 +3,10 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class ProfileTest < Test::Unit::TestCase
4 4 fixtures :profiles, :environments, :users, :roles, :domains
5 5  
  6 + def setup
  7 + Test::Unit::TestCase::setup
  8 + end
  9 +
6 10 def test_identifier_validation
7 11 p = Profile.new
8 12 p.valid?
... ... @@ -100,8 +104,8 @@ class ProfileTest &lt; Test::Unit::TestCase
100 104 def test_find_by_contents
101 105 p = create(Profile, :name => 'wanted')
102 106  
103   - assert Profile.find_by_contents('wanted').include?(p)
104   - assert ! Profile.find_by_contents('not_wanted').include?(p)
  107 + assert Profile.find_by_contents('wanted')[:results].include?(p)
  108 + assert ! Profile.find_by_contents('not_wanted')[:results].include?(p)
105 109 end
106 110  
107 111 should 'remove pages when removing profile' do
... ... @@ -192,10 +196,10 @@ class ProfileTest &lt; Test::Unit::TestCase
192 196 small = create(Profile, :name => 'A small profile for testing')
193 197 big = create(Profile, :name => 'A big profile for testing')
194 198  
195   - assert Profile.find_by_contents('small').include?(small)
196   - assert Profile.find_by_contents('big').include?(big)
  199 + assert Profile.find_by_contents('small')[:results].include?(small)
  200 + assert Profile.find_by_contents('big')[:results].include?(big)
197 201  
198   - both = Profile.find_by_contents('profile testing')
  202 + both = Profile.find_by_contents('profile testing')[:results]
199 203 assert both.include?(small)
200 204 assert both.include?(big)
201 205 end
... ... @@ -517,18 +521,18 @@ class ProfileTest &lt; Test::Unit::TestCase
517 521 should 'actually index by results of extra_data_for_index' do
518 522 profile = TestingExtraDataForIndex.create!(:name => 'testprofile', :identifier => 'testprofile')
519 523  
520   - assert_includes TestingExtraDataForIndex.find_by_contents('sample'), profile
  524 + assert_includes TestingExtraDataForIndex.find_by_contents('sample')[:results], profile
521 525 end
522 526  
523 527 should 'index profile identifier for searching' do
524 528 Profile.destroy_all
525 529 p = create(Profile, :identifier => 'lalala')
526   - assert_includes Profile.find_by_contents('lalala'), p
  530 + assert_includes Profile.find_by_contents('lalala')[:results], p
527 531 end
528 532  
529 533 should 'index profile name for searching' do
530 534 p = create(Profile, :name => 'Interesting Profile')
531   - assert_includes Profile.find_by_contents('interesting'), p
  535 + assert_includes Profile.find_by_contents('interesting')[:results], p
532 536 end
533 537  
534 538 should 'enabled by default on creation' do
... ... @@ -1665,24 +1669,24 @@ class ProfileTest &lt; Test::Unit::TestCase
1665 1669 should 'index by schema name when database is postgresql' do
1666 1670 uses_postgresql 'schema_one'
1667 1671 p1 = Profile.create!(:name => 'some thing', :identifier => 'some-thing')
1668   - assert_equal Profile.find_by_contents('thing'), [p1]
  1672 + assert_equal Profile.find_by_contents('thing')[:results], [p1]
1669 1673 uses_postgresql 'schema_two'
1670 1674 p2 = Profile.create!(:name => 'another thing', :identifier => 'another-thing')
1671   - assert_not_includes Profile.find_by_contents('thing'), p1
1672   - assert_includes Profile.find_by_contents('thing'), p2
  1675 + assert_not_includes Profile.find_by_contents('thing')[:results], p1
  1676 + assert_includes Profile.find_by_contents('thing')[:results], p2
1673 1677 uses_postgresql 'schema_one'
1674   - assert_includes Profile.find_by_contents('thing'), p1
1675   - assert_not_includes Profile.find_by_contents('thing'), p2
  1678 + assert_includes Profile.find_by_contents('thing')[:results], p1
  1679 + assert_not_includes Profile.find_by_contents('thing')[:results], p2
1676 1680 uses_sqlite
1677 1681 end
1678 1682  
1679 1683 should 'not index by schema name when database is not postgresql' do
1680 1684 uses_sqlite
1681 1685 p1 = Profile.create!(:name => 'some thing', :identifier => 'some-thing')
1682   - assert_equal Profile.find_by_contents('thing'), [p1]
  1686 + assert_equal Profile.find_by_contents('thing')[:results], [p1]
1683 1687 p2 = Profile.create!(:name => 'another thing', :identifier => 'another-thing')
1684   - assert_includes Profile.find_by_contents('thing'), p1
1685   - assert_includes Profile.find_by_contents('thing'), p2
  1688 + assert_includes Profile.find_by_contents('thing')[:results], p1
  1689 + assert_includes Profile.find_by_contents('thing')[:results], p2
1686 1690 end
1687 1691  
1688 1692 should 'know if url is the profile homepage' do
... ...
test/unit/tiny_mce_article_test.rb
... ... @@ -3,7 +3,8 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class TinyMceArticleTest < Test::Unit::TestCase
4 4  
5 5 def setup
6   - Article.rebuild_index
  6 + Test::Unit::TestCase::setup
  7 + Article.rebuild_solr_index
7 8 @profile = create_user('zezinho').person
8 9 end
9 10 attr_reader :profile
... ... @@ -23,8 +24,8 @@ class TinyMceArticleTest &lt; Test::Unit::TestCase
23 24  
24 25 should 'be found when searching for articles by query' do
25 26 tma = TinyMceArticle.create!(:name => 'test tinymce article', :body => '---', :profile => profile)
26   - assert_includes TinyMceArticle.find_by_contents('article'), tma
27   - assert_includes Article.find_by_contents('article'), tma
  27 + assert_includes TinyMceArticle.find_by_contents('article')[:results], tma
  28 + assert_includes Article.find_by_contents('article')[:results], tma
28 29 end
29 30  
30 31 should 'not sanitize target attribute' do
... ...
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,53 +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   -Aaf is is also available via git from Rubyforge:
16   -git clone git://rubyforge.org/actsasferret.git
17   -
18   -=== System-wide installation with Rubygems
19   -
20   -<tt>sudo gem install acts_as_ferret</tt>
21   -
22   -To use acts_as_ferret in your project, add the following line to your
23   -project's config/environment.rb:
24   -
25   -<tt>require 'acts_as_ferret'</tt>
26   -
27   -Call the aaf_install script inside your project directory to install the sample
28   -config file and the drb server start/stop script.
29   -
30   -
31   -== Usage
32   -
33   -include the following in your model class (specifiying the fields you want to get indexed):
34   -
35   -<tt>acts_as_ferret :fields => [ :title, :description ]</tt>
36   -
37   -now you can use ModelClass.find_by_contents(query) to find instances of your model
38   -whose indexed fields match a given query. All query terms are required by default,
39   -but explicit OR queries are possible. This differs from the ferret default, but
40   -imho is the more often needed/expected behaviour (more query terms result in
41   -less results).
42   -
43   -Please see ActsAsFerret::ActMethods#acts_as_ferret for more information.
44   -
45   -== License
46   -
47   -Released under the MIT license.
48   -
49   -== Authors
50   -
51   -* Kasper Weibel Nielsen-Refs (original author)
52   -* Jens Kraemer <jk@jkraemer.net> (current maintainer)
53   -
vendor/plugins/acts_as_ferret/bin/aaf_install
... ... @@ -1,23 +0,0 @@
1   -# acts_as_ferret gem install script
2   -# Use inside the root of your Rails project
3   -require 'fileutils'
4   -
5   -@basedir = File.join(File.dirname(__FILE__), '..')
6   -
7   -def install(dir, file, executable=false)
8   - puts "Installing: #{file}"
9   - target = File.join('.', dir, file)
10   - if File.exists?(target)
11   - puts "#{target} already exists, skipping"
12   - else
13   - FileUtils.cp File.join(@basedir, dir, file), target
14   - FileUtils.chmod 0755, target if executable
15   - end
16   -end
17   -
18   -
19   -install 'script', 'ferret_server', true
20   -install 'config', 'ferret_server.yml'
21   -
22   -puts IO.read(File.join(@basedir, 'README'))
23   -
vendor/plugins/acts_as_ferret/config/ferret_server.yml
... ... @@ -1,24 +0,0 @@
1   -# configuration for the acts_as_ferret DRb server
2   -# host: where to reach the DRb server (used by application processes to contact the server)
3   -# port: which port the server should listen on
4   -# socket: where the DRb server should create the socket (absolute path), this setting overrides host:port configuration
5   -# pid_file: location of the server's pid file (relative to RAILS_ROOT)
6   -# log_file: log file (default: RAILS_ROOT/log/ferret_server.log
7   -# log_level: log level for the server's logger
8   -production:
9   - host: localhost
10   - port: 9010
11   - pid_file: log/ferret.pid
12   - log_file: log/ferret_server.log
13   - log_level: warn
14   -
15   -# aaf won't try to use the DRb server in environments that are not
16   -# configured here.
17   -#development:
18   -# host: localhost
19   -# port: 9010
20   -# pid_file: log/ferret.pid
21   -#test:
22   -# host: localhost
23   -# port: 9009
24   -# pid_file: log/ferret.pid
vendor/plugins/acts_as_ferret/doc/README.win32
... ... @@ -1,23 +0,0 @@
1   -Credits
2   -=======
3   -
4   -The Win32 service support scripts have been written by
5   -Herryanto Siatono <herryanto@pluitsolutions.com>.
6   -
7   -See his accompanying blog posting at
8   -http://www.pluitsolutions.com/2007/07/30/acts-as-ferret-drbserver-win32-service/
9   -
10   -
11   -Usage
12   -=====
13   -
14   -There are two scripts:
15   -
16   -script/ferret_service is used to install/remove/start/stop the win32 service.
17   -
18   -script/ferret_daemon is to be called by Win32 service to start/stop the
19   -DRbServer.
20   -
21   -Run 'ruby script/ferret_service -h' for more info.
22   -
23   -
vendor/plugins/acts_as_ferret/doc/monit-example
... ... @@ -1,22 +0,0 @@
1   -# monit configuration snippet to watch the Ferret DRb server shipped with
2   -# acts_as_ferret
3   -check process ferret with pidfile /path/to/ferret.pid
4   -
5   - # username is the user the drb server should be running as (It's good practice
6   - # to run such services as a non-privileged user)
7   - start program = "/bin/su -c 'cd /path/to/your/app/current/ && script/ferret_server -e production start' username"
8   - stop program = "/bin/su -c 'cd /path/to/your/app/current/ && script/ferret_server -e production stop' username"
9   -
10   - # cpu usage boundaries
11   - if cpu > 60% for 2 cycles then alert
12   - if cpu > 90% for 5 cycles then restart
13   -
14   - # memory usage varies with index size and usage scenarios, so check how
15   - # much memory your DRb server uses up usually and add some spare to that
16   - # before enabling this rule:
17   - # if totalmem > 50.0 MB for 5 cycles then restart
18   -
19   - # adjust port numbers according to your setup:
20   - if failed port 9010 then alert
21   - if failed port 9010 for 2 cycles then restart
22   - group ferret
vendor/plugins/acts_as_ferret/init.rb
... ... @@ -1,24 +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   -
23   -config.after_initialize { ActsAsFerret::load_config }
24   -config.to_prepare { ActsAsFerret::load_config }
vendor/plugins/acts_as_ferret/install.rb
... ... @@ -1,18 +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_server' )
15   -install File.join( 'config', 'ferret_server.yml' )
16   -
17   -puts IO.read(File.join(File.dirname(__FILE__), 'README'))
18   -
vendor/plugins/acts_as_ferret/lib/act_methods.rb
... ... @@ -1,155 +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   - # if:: Can be set to a block that will be called with the record in question
26   - # to determine if it should be indexed or not.
27   - #
28   - # index_dir:: declares the directory where to put the index for this class.
29   - # The default is RAILS_ROOT/index/RAILS_ENV/CLASSNAME.
30   - # The index directory will be created if it doesn't exist.
31   - #
32   - # reindex_batch_size:: reindexing is done in batches of this size, default is 1000
33   - # mysql_fast_batches:: set this to false to disable the faster mysql batching
34   - # algorithm if this model uses a non-integer primary key named
35   - # 'id' on MySQL.
36   - #
37   - # ferret:: Hash of Options that directly influence the way the Ferret engine works. You
38   - # can use most of the options the Ferret::I class accepts here, too. Among the
39   - # more useful are:
40   - #
41   - # or_default:: whether query terms are required by
42   - # default (the default, false), or not (true)
43   - #
44   - # analyzer:: the analyzer to use for query parsing (default: nil,
45   - # which means the ferret StandardAnalyzer gets used)
46   - #
47   - # default_field:: use to set one or more fields that are searched for query terms
48   - # that don't have an explicit field list. This list should *not*
49   - # contain any untokenized fields. If it does, you're asking
50   - # for trouble (i.e. not getting results for queries having
51   - # stop words in them). Aaf by default initializes the default field
52   - # list to contain all tokenized fields. If you use :single_index => true,
53   - # you really should set this option specifying your default field
54   - # list (which should be equal in all your classes sharing the index).
55   - # Otherwise you might get incorrect search results and you won't get
56   - # any lazy loading of stored field data.
57   - #
58   - # For downwards compatibility reasons you can also specify the Ferret options in the
59   - # last Hash argument.
60   - def acts_as_ferret(options={})
61   -
62   - extend ClassMethods
63   -
64   - include InstanceMethods
65   - include MoreLikeThis::InstanceMethods
66   -
67   - if options[:rdig]
68   - cattr_accessor :rdig_configuration
69   - self.rdig_configuration = options[:rdig]
70   - require 'rdig_adapter'
71   - include ActsAsFerret::RdigAdapter
72   - end
73   -
74   - unless included_modules.include?(ActsAsFerret::WithoutAR)
75   - # set up AR hooks
76   - after_create :ferret_create
77   - after_update :ferret_update
78   - after_destroy :ferret_destroy
79   - end
80   -
81   - cattr_accessor :aaf_configuration
82   -
83   - # apply default config for rdig based models
84   - if options[:rdig]
85   - options[:fields] ||= { :title => { :boost => 3, :store => :yes },
86   - :content => { :store => :yes } }
87   - end
88   -
89   - # name of this index
90   - index_name = options.delete(:index) || self.name.underscore
91   -
92   - index = ActsAsFerret::register_class_with_index(self, index_name, options)
93   - self.aaf_configuration = index.index_definition.dup
94   - logger.debug "configured index for class #{self.name}:\n#{aaf_configuration.inspect}"
95   -
96   - # update our copy of the global index config with options local to this class
97   - aaf_configuration[:class_name] ||= self.name
98   - aaf_configuration[:if] ||= options[:if]
99   -
100   - # add methods for retrieving field values
101   - add_fields options[:fields]
102   - add_fields options[:additional_fields]
103   - add_fields aaf_configuration[:fields]
104   - add_fields aaf_configuration[:additional_fields]
105   -
106   - # not good at class level, index might get initialized too early
107   - #if options[:remote]
108   - # aaf_index.ensure_index_exists
109   - #end
110   - end
111   -
112   -
113   - protected
114   -
115   -
116   - # helper to defines a method which adds the given field to a ferret
117   - # document instance
118   - def define_to_field_method(field, options = {})
119   - method_name = "#{field}_to_ferret"
120   - return if instance_methods.include?(method_name) # already defined
121   - aaf_configuration[:defined_fields] ||= {}
122   - aaf_configuration[:defined_fields][field] = options
123   - dynamic_boost = options[:boost] if options[:boost].is_a?(Symbol)
124   - via = options[:via] || field
125   - define_method(method_name.to_sym) do
126   - val = begin
127   - content_for_field_name(field, via, dynamic_boost)
128   - rescue
129   - logger.warn("Error retrieving value for field #{field}: #{$!}")
130   - ''
131   - end
132   - logger.debug("Adding field #{field} with value '#{val}' to index")
133   - val
134   - end
135   - end
136   -
137   - def add_fields(field_config)
138   - # TODO
139   - #field_config.each do |*args|
140   - # define_to_field_method *args
141   - #end
142   - if field_config.is_a? Hash
143   - field_config.each_pair do |field, options|
144   - define_to_field_method field, options
145   - end
146   - elsif field_config.respond_to?(:each)
147   - field_config.each do |field|
148   - define_to_field_method field
149   - end
150   - end
151   - end
152   -
153   - end
154   -
155   -end
vendor/plugins/acts_as_ferret/lib/acts_as_ferret.rb
... ... @@ -1,567 +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 'enumerator'
25   -require 'ferret'
26   -
27   -require 'ferret_find_methods'
28   -require 'remote_functions'
29   -require 'blank_slate'
30   -require 'bulk_indexer'
31   -require 'ferret_extensions'
32   -require 'act_methods'
33   -require 'search_results'
34   -require 'class_methods'
35   -require 'ferret_result'
36   -require 'instance_methods'
37   -require 'without_ar'
38   -
39   -require 'multi_index'
40   -require 'remote_multi_index'
41   -require 'more_like_this'
42   -
43   -require 'index'
44   -require 'local_index'
45   -require 'remote_index'
46   -
47   -require 'ferret_server'
48   -
49   -require 'rdig_adapter'
50   -
51   -# The Rails ActiveRecord Ferret Mixin.
52   -#
53   -# This mixin adds full text search capabilities to any Rails model.
54   -#
55   -# The current version emerged from on the original acts_as_ferret plugin done by
56   -# Kasper Weibel and a modified version done by Thomas Lockney, which both can be
57   -# found on the Ferret Wiki: http://ferret.davebalmain.com/trac/wiki/FerretOnRails.
58   -#
59   -# basic usage:
60   -# include the following in your model class (specifiying the fields you want to get indexed):
61   -# acts_as_ferret :fields => [ :title, :description ]
62   -#
63   -# now you can use ModelClass.find_by_contents(query) to find instances of your model
64   -# whose indexed fields match a given query. All query terms are required by default, but
65   -# explicit OR queries are possible. This differs from the ferret default, but imho is the more
66   -# often needed/expected behaviour (more query terms result in less results).
67   -#
68   -# Released under the MIT license.
69   -#
70   -# Authors:
71   -# Kasper Weibel Nielsen-Refs (original author)
72   -# Jens Kraemer <jk@jkraemer.net> (active maintainer since 2006)
73   -#
74   -#
75   -# == Global properties
76   -#
77   -# raise_drb_errors:: Set this to true if you want aaf to raise Exceptions
78   -# in case the DRb server cannot be reached (in other word - behave like
79   -# versions up to 0.4.3). Defaults to false so DRb exceptions
80   -# are logged but not raised. Be sure to set up some
81   -# monitoring so you still detect when your DRb server died for
82   -# whatever reason.
83   -#
84   -# remote:: Set this to false to force acts_as_ferret into local (non-DRb) mode even if
85   -# config/ferret_server.yml contains a section for the current RAILS_ENV
86   -# Usually you won't need to touch this option - just configure DRb for
87   -# production mode in ferret_server.yml.
88   -#
89   -module ActsAsFerret
90   -
91   - class ActsAsFerretError < StandardError; end
92   - class IndexNotDefined < ActsAsFerretError; end
93   - class IndexAlreadyDefined < ActsAsFerretError; end
94   -
95   - # global Hash containing all multi indexes created by all classes using the plugin
96   - # key is the concatenation of alphabetically sorted names of the classes the
97   - # searcher searches.
98   - @@multi_indexes = Hash.new
99   - def self.multi_indexes; @@multi_indexes end
100   -
101   - # global Hash containing the ferret indexes of all classes using the plugin
102   - # key is the index name.
103   - @@ferret_indexes = Hash.new
104   - def self.ferret_indexes; @@ferret_indexes end
105   -
106   - # mapping from class name to index name
107   - @@index_using_classes = {}
108   -
109   - @@logger = Logger.new "#{RAILS_ROOT}/log/acts_as_ferret.log"
110   - @@logger.level = ActiveRecord::Base.logger.level rescue Logger::DEBUG
111   - mattr_accessor :logger
112   -
113   -
114   - # Default ferret configuration for index fields
115   - DEFAULT_FIELD_OPTIONS = {
116   - :store => :no,
117   - :highlight => :yes,
118   - :index => :yes,
119   - :term_vector => :with_positions_offsets,
120   - :boost => 1.0
121   - }
122   -
123   - @@raise_drb_errors = false
124   - mattr_writer :raise_drb_errors
125   - def self.raise_drb_errors?; @@raise_drb_errors end
126   -
127   - @@remote = nil
128   - mattr_accessor :remote
129   - def self.remote?
130   - if @@remote.nil?
131   - if ENV["FERRET_USE_LOCAL_INDEX"] || ActsAsFerret::Remote::Server.running
132   - @@remote = false
133   - else
134   - @@remote = ActsAsFerret::Remote::Config.new.uri rescue false
135   - end
136   - if @@remote
137   - logger.info "Will use remote index server which should be available at #{@@remote}"
138   - else
139   - logger.info "Will use local index."
140   - end
141   - end
142   - @@remote
143   - end
144   - remote?
145   -
146   -
147   - # Globally declares an index.
148   - #
149   - # This method is also used to implicitly declare an index when you use the
150   - # acts_as_ferret call in your class. Returns the created index instance.
151   - #
152   - # === Options are:
153   - #
154   - # +models+:: Hash of model classes and their per-class option hashes which should
155   - # use this index. Any models mentioned here will automatically use
156   - # the index, there is no need to explicitly call +acts_as_ferret+ in the
157   - # model class definition.
158   - def self.define_index(name, options = {})
159   - name = name.to_sym
160   - pending_classes = nil
161   - if ferret_indexes.has_key?(name)
162   - # seems models have been already loaded. remove that index for now,
163   - # re-register any already loaded classes later on.
164   - idx = get_index(name)
165   - pending_classes = idx.index_definition[:registered_models]
166   - pending_classes_configs = idx.registered_models_config
167   - idx.close
168   - ferret_indexes.delete(name)
169   - end
170   -
171   - index_definition = {
172   - :index_dir => "#{ActsAsFerret::index_dir}/#{name}",
173   - :name => name,
174   - :single_index => false,
175   - :reindex_batch_size => 1000,
176   - :ferret => {},
177   - :ferret_fields => {}, # list of indexed fields that will be filled later
178   - :enabled => true, # used for class-wide disabling of Ferret
179   - :mysql_fast_batches => true, # turn off to disable the faster, id based batching mechanism for MySQL
180   - :raise_drb_errors => false # handle DRb connection errors by default
181   - }.update( options )
182   -
183   - index_definition[:registered_models] = []
184   -
185   - # build ferret configuration
186   - index_definition[:ferret] = {
187   - :or_default => false,
188   - :handle_parse_errors => true,
189   - :default_field => nil, # will be set later on
190   - #:max_clauses => 512,
191   - #:analyzer => Ferret::Analysis::StandardAnalyzer.new,
192   - # :wild_card_downcase => true
193   - }.update( options[:ferret] || {} )
194   -
195   - index_definition[:user_default_field] = index_definition[:ferret][:default_field]
196   -
197   - unless remote?
198   - ActsAsFerret::ensure_directory index_definition[:index_dir]
199   - index_definition[:index_base_dir] = index_definition[:index_dir]
200   - index_definition[:index_dir] = find_last_index_version(index_definition[:index_dir])
201   - logger.debug "using index in #{index_definition[:index_dir]}"
202   - end
203   -
204   - # these properties are somewhat vital to the plugin and shouldn't
205   - # be overwritten by the user:
206   - index_definition[:ferret].update(
207   - :key => ((Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?) ? [:id, :class_name, :schema_name] : [:id, :class_name]),
208   - :path => index_definition[:index_dir],
209   - :auto_flush => true, # slower but more secure in terms of locking problems TODO disable when running in drb mode?
210   - :create_if_missing => true
211   - )
212   -
213   - # field config
214   - index_definition[:ferret_fields] = build_field_config( options[:fields] )
215   - index_definition[:ferret_fields].update build_field_config( options[:additional_fields] )
216   -
217   - idx = ferret_indexes[name] = create_index_instance( index_definition )
218   -
219   - # re-register early loaded classes
220   - if pending_classes
221   - pending_classes.each { |clazz| idx.register_class clazz, { :force_re_registration => true }.merge(pending_classes_configs[clazz]) }
222   - end
223   -
224   - if models = options[:models]
225   - models.each do |clazz, config|
226   - clazz.send :include, ActsAsFerret::WithoutAR unless clazz.respond_to?(:acts_as_ferret)
227   - clazz.acts_as_ferret config.merge(:index => name)
228   - end
229   - end
230   -
231   - return idx
232   - end
233   -
234   - # called internally by the acts_as_ferret method
235   - #
236   - # returns the index
237   - def self.register_class_with_index(clazz, index_name, options = {})
238   - index_name = index_name.to_sym
239   - @@index_using_classes[clazz.name] = index_name
240   - unless index = ferret_indexes[index_name]
241   - # index definition on the fly
242   - # default to all attributes of this class
243   - options[:fields] ||= clazz.new.attributes.keys.map { |k| k.to_sym }
244   - index = define_index index_name, options
245   - end
246   - index.register_class(clazz, options)
247   - return index
248   - end
249   -
250   - def self.load_config
251   - # using require_dependency to make the reloading in dev mode work.
252   - require_dependency "#{RAILS_ROOT}/config/aaf.rb"
253   - ActsAsFerret::logger.info "loaded configuration file aaf.rb"
254   - rescue LoadError
255   - ensure
256   - @aaf_config_loaded = true
257   - end
258   -
259   - # returns the index with the given name.
260   - def self.get_index(name)
261   - name = name.to_sym rescue nil
262   - unless ferret_indexes.has_key?(name)
263   - if @aaf_config_loaded
264   - raise IndexNotDefined.new(name.to_s)
265   - else
266   - load_config and return get_index name
267   - end
268   - end
269   - ferret_indexes[name]
270   - end
271   -
272   - # count hits for a query with multiple models
273   - def self.total_hits(query, models, options = {})
274   - find_index(models).total_hits query, options.merge( :models => models )
275   - end
276   -
277   - # find ids of records with multiple models
278   - # TODO pagination logic?
279   - def self.find_ids(query, models, options = {}, &block)
280   - find_index(models).find_ids query, options.merge( :models => models ), &block
281   - end
282   -
283   - def self.find_index(models_or_index_name)
284   - case models_or_index_name
285   - when Symbol
286   - get_index models_or_index_name
287   - when String
288   - get_index models_or_index_name.to_sym
289   - #when Array
290   - # get_index_for models_or_index_name
291   - else
292   - get_index_for models_or_index_name
293   - end
294   - end
295   -
296   - def self.find(query, models_or_index_name, options = {}, ar_options = {})
297   - # TODO generalize local/remote index so we can remove the workaround below
298   - # (replace logic in class_methods#find_with_ferret)
299   - # maybe put pagination stuff in a module to be included by all index
300   - # implementations
301   - models = [ models_or_index_name ] if Class === models_or_index_name
302   - if models && models.size == 1
303   - return models.shift.find_with_ferret query, options, ar_options
304   - end
305   - index = find_index(models_or_index_name)
306   - multi = (MultiIndex === index or index.shared?)
307   - if options[:per_page]
308   - options[:page] = options[:page] ? options[:page].to_i : 1
309   - limit = options[:per_page]
310   - offset = (options[:page] - 1) * limit
311   - if ar_options[:conditions] && !multi
312   - ar_options[:limit] = limit
313   - ar_options[:offset] = offset
314   - options[:limit] = :all
315   - options.delete :offset
316   - else
317   - # do pagination with ferret (or after everything is done in the case
318   - # of multi_search)
319   - options[:limit] = limit
320   - options[:offset] = offset
321   - end
322   - elsif ar_options[:conditions]
323   - if multi
324   - # multisearch ignores find_options limit and offset
325   - options[:limit] ||= ar_options.delete(:limit)
326   - options[:offset] ||= ar_options.delete(:offset)
327   - else
328   - # let the db do the limiting and offsetting for single-table searches
329   - unless options[:limit] == :all
330   - ar_options[:limit] ||= options.delete(:limit)
331   - end
332   - ar_options[:offset] ||= options.delete(:offset)
333   - options[:limit] = :all
334   - end
335   - end
336   -
337   - total_hits, result = index.find_records query, options.merge(:models => models), ar_options
338   - logger.debug "Query: #{query}\ntotal hits: #{total_hits}, results delivered: #{result.size}"
339   - SearchResults.new(result, total_hits, options[:page], options[:per_page])
340   - end
341   -
342   - # returns the index used by the given class.
343   - #
344   - # If multiple classes are given, either the single index shared by these
345   - # classes, or a multi index (to be used for search only) across the indexes
346   - # of all models, is returned.
347   - def self.get_index_for(*classes)
348   - classes.flatten!
349   - raise ArgumentError.new("no class specified") unless classes.any?
350   - classes.map!(&:constantize) unless Class === classes.first
351   - logger.debug "index_for #{classes.inspect}"
352   - index = if classes.size > 1
353   - indexes = classes.map { |c| get_index_for c }.uniq
354   - indexes.size > 1 ? multi_index(indexes) : indexes.first
355   - else
356   - clazz = classes.first
357   - clazz = clazz.superclass while clazz && !@@index_using_classes.has_key?(clazz.name)
358   - get_index @@index_using_classes[clazz.name]
359   - end
360   - raise IndexNotDefined.new("no index found for class: #{classes.map(&:name).join(',')}") if index.nil?
361   - return index
362   - end
363   -
364   -
365   - # creates a new Index instance.
366   - def self.create_index_instance(definition)
367   - (remote? ? RemoteIndex : LocalIndex).new(definition)
368   - end
369   -
370   - def self.rebuild_index(name)
371   - get_index(name).rebuild_index
372   - end
373   -
374   - def self.change_index_dir(name, new_dir)
375   - get_index(name).change_index_dir new_dir
376   - end
377   -
378   - # find the most recent version of an index
379   - def self.find_last_index_version(basedir)
380   - # check for versioned index
381   - versions = Dir.entries(basedir).select do |f|
382   - dir = File.join(basedir, f)
383   - File.directory?(dir) && File.file?(File.join(dir, 'segments')) && f =~ /^\d+(_\d+)?$/
384   - end
385   - if versions.any?
386   - # select latest version
387   - versions.sort!
388   - File.join basedir, versions.last
389   - else
390   - basedir
391   - end
392   - end
393   -
394   - # returns a MultiIndex instance operating on a MultiReader
395   - def self.multi_index(indexes)
396   - index_names = indexes.dup
397   - index_names = index_names.map(&:to_s) if Symbol === index_names.first
398   - if String === index_names.first
399   - indexes = index_names.map{ |name| get_index name }
400   - else
401   - index_names = index_names.map{ |i| i.index_name.to_s }
402   - end
403   - key = index_names.sort.join(",")
404   - ActsAsFerret::multi_indexes[key] ||= (remote? ? ActsAsFerret::RemoteMultiIndex : ActsAsFerret::MultiIndex).new(indexes)
405   - end
406   -
407   - # check for per-model conditions and return these if provided
408   - def self.conditions_for_model(model, conditions = {})
409   - if Hash === conditions
410   - key = model.name.underscore.to_sym
411   - conditions = conditions[key]
412   - end
413   - return conditions
414   - end
415   -
416   - # retrieves search result records from a data structure like this:
417   - # { 'Model1' => { '1' => [ rank, score ], '2' => [ rank, score ] }
418   - #
419   - # TODO: in case of STI AR will filter out hits from other
420   - # classes for us, but this
421   - # will lead to less results retrieved --> scoping of ferret query
422   - # to self.class is still needed.
423   - # from the ferret ML (thanks Curtis Hatter)
424   - # > I created a method in my base STI class so I can scope my query. For scoping
425   - # > I used something like the following line:
426   - # >
427   - # > query << " role:#{self.class.eql?(Contents) '*' : self.class}"
428   - # >
429   - # > Though you could make it more generic by simply asking
430   - # > "self.descends_from_active_record?" which is how rails decides if it should
431   - # > scope your "find" query for STI models. You can check out "base.rb" in
432   - # > activerecord to see that.
433   - # but maybe better do the scoping in find_ids_with_ferret...
434   - def self.retrieve_records(id_arrays, find_options = {})
435   - result = []
436   - # get objects for each model
437   - id_arrays.each do |model, id_array|
438   - next if id_array.empty?
439   - model_class = model.constantize
440   -
441   - # merge conditions
442   - conditions = conditions_for_model model_class, find_options[:conditions]
443   - conditions = combine_conditions([ "#{model_class.table_name}.#{model_class.primary_key} in (?)",
444   - id_array.keys ],
445   - conditions)
446   -
447   - # check for include association that might only exist on some models in case of multi_search
448   - filtered_include_options = []
449   - if include_options = find_options[:include]
450   - include_options = [ include_options ] unless include_options.respond_to?(:each)
451   - include_options.each do |include_option|
452   - filtered_include_options << include_option if model_class.reflections.has_key?(include_option.is_a?(Hash) ? include_option.keys[0].to_sym : include_option.to_sym)
453   - end
454   - end
455   - filtered_include_options = nil if filtered_include_options.empty?
456   -
457   - # fetch
458   - tmp_result = model_class.find(:all, find_options.merge(:conditions => conditions,
459   - :include => filtered_include_options))
460   -
461   - # set scores and rank
462   - tmp_result.each do |record|
463   - record.ferret_rank, record.ferret_score = id_array[record.id.to_s]
464   - end
465   - # merge with result array
466   - result += tmp_result
467   - end
468   -
469   - # order results as they were found by ferret, unless an AR :order
470   - # option was given
471   - result.sort! { |a, b| a.ferret_rank <=> b.ferret_rank } unless find_options[:order]
472   - return result
473   - end
474   -
475   - # combine our conditions with those given by user, if any
476   - def self.combine_conditions(conditions, additional_conditions = [])
477   - returning conditions do
478   - if additional_conditions && additional_conditions.any?
479   - cust_opts = (Array === additional_conditions) ? additional_conditions.dup : [ additional_conditions ]
480   - logger.debug "cust_opts: #{cust_opts.inspect}"
481   - conditions.first << " and " << cust_opts.shift
482   - conditions.concat(cust_opts)
483   - end
484   - end
485   - end
486   -
487   - def self.build_field_config(fields)
488   - field_config = {}
489   - case fields
490   - when Array
491   - fields.each { |name| field_config[name] = field_config_for name }
492   - when Hash
493   - fields.each { |name, options| field_config[name] = field_config_for name, options }
494   - else raise InvalidArgumentError.new(":fields option must be Hash or Array")
495   - end if fields
496   - return field_config
497   - end
498   -
499   - def self.ensure_directory(dir)
500   - FileUtils.mkdir_p dir unless (File.directory?(dir) || File.symlink?(dir))
501   - end
502   -
503   -
504   - # make sure the default index base dir exists. by default, all indexes are created
505   - # under RAILS_ROOT/index/RAILS_ENV
506   - def self.init_index_basedir
507   - index_base = "#{RAILS_ROOT}/index"
508   - @@index_dir = "#{index_base}/#{RAILS_ENV}"
509   - end
510   -
511   - mattr_accessor :index_dir
512   - init_index_basedir
513   -
514   - def self.append_features(base)
515   - super
516   - base.extend(ClassMethods)
517   - end
518   -
519   - # builds a FieldInfos instance for creation of an index
520   - def self.field_infos(index_definition)
521   - # default attributes for fields
522   - fi = Ferret::Index::FieldInfos.new(:store => :no,
523   - :index => :yes,
524   - :term_vector => :no,
525   - :boost => 1.0)
526   - # primary key
527   - fi.add_field(:id, :store => :yes, :index => :untokenized)
528   - # class_name
529   - fi.add_field(:class_name, :store => :yes, :index => :untokenized)
530   -
531   - # other fields
532   - index_definition[:ferret_fields].each_pair do |field, options|
533   - options = options.dup
534   - options.delete :via
535   - options.delete :boost if options[:boost].is_a?(Symbol) # dynamic boost
536   - fi.add_field(field, options)
537   - end
538   - return fi
539   - end
540   -
541   - def self.close_multi_indexes
542   - # close combined index readers, just in case
543   - # this seems to fix a strange test failure that seems to relate to a
544   - # multi_index looking at an old version of the content_base index.
545   - multi_indexes.each_pair do |key, index|
546   - # puts "#{key} -- #{self.name}"
547   - # TODO only close those where necessary (watch inheritance, where
548   - # self.name is base class of a class where key is made from)
549   - index.close #if key =~ /#{self.name}/
550   - end
551   - multi_indexes.clear
552   - end
553   -
554   - protected
555   -
556   - def self.field_config_for(fieldname, options = {})
557   - config = DEFAULT_FIELD_OPTIONS.merge options
558   - config[:via] ||= fieldname
559   - config[:term_vector] = :no if config[:index] == :no
560   - return config
561   - end
562   -
563   -end
564   -
565   -# include acts_as_ferret method into ActiveRecord::Base
566   -ActiveRecord::Base.extend ActsAsFerret::ActMethods
567   -
vendor/plugins/acts_as_ferret/lib/ar_mysql_auto_reconnect_patch.rb
... ... @@ -1,41 +0,0 @@
1   -# Source: http://pastie.caboo.se/154842
2   -#
3   -# in /etc/my.cnf on the MySQL server, you can set the interactive-timeout parameter,
4   -# for example, 12 hours = 28800 sec
5   -# interactive-timeout=28800
6   -
7   -# in ActiveRecord, setting the verification_timeout to something less than
8   -# the interactive-timeout parameter; 14400 sec = 6 hours
9   -ActiveRecord::Base.verification_timeout = 14400
10   -ActiveRecord::Base.establish_connection
11   -
12   -# Below is a monkey patch for keeping ActiveRecord connections alive.
13   -# http://www.sparecycles.org/2007/7/2/saying-goodbye-to-lost-connections-in-rails
14   -
15   -module ActiveRecord
16   - module ConnectionAdapters
17   - class MysqlAdapter
18   - def execute(sql, name = nil) #:nodoc:
19   - reconnect_lost_connections = true
20   - begin
21   - log(sql, name) { @connection.query(sql) }
22   - rescue ActiveRecord::StatementInvalid => exception
23   - if reconnect_lost_connections and exception.message =~ /(Lost connection to MySQL server during query
24   -|MySQL server has gone away)/
25   - reconnect_lost_connections = false
26   - reconnect!
27   - retry
28   - elsif exception.message.split(":").first =~ /Packets out of order/
29   - raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database.
30   - Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hash
31   -ing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql
32   -bindings."
33   - else
34   - raise
35   - end
36   - end
37   - end
38   - end
39   - end
40   -end
41   -
vendor/plugins/acts_as_ferret/lib/blank_slate.rb
... ... @@ -1,53 +0,0 @@
1   -if defined?(BlankSlate)
2   - # Rails 2.x has it already
3   - module ActsAsFerret
4   - class BlankSlate < ::BlankSlate
5   - end
6   - end
7   -else
8   - module ActsAsFerret
9   - # 'backported' for Rails pre 2.0
10   - #
11   - #--
12   - # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
13   - # All rights reserved.
14   -
15   - # Permission is granted for use, copying, modification, distribution,
16   - # and distribution of modified versions of this work as long as the
17   - # above copyright notice is included.
18   - #++
19   -
20   - ######################################################################
21   - # BlankSlate provides an abstract base class with no predefined
22   - # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
23   - # BlankSlate is useful as a base class when writing classes that
24   - # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
25   - #
26   - class BlankSlate
27   - class << self
28   - # Hide the method named +name+ in the BlankSlate class. Don't
29   - # hide +instance_eval+ or any method beginning with "__".
30   - def hide(name)
31   - if instance_methods.include?(name.to_s) and name !~ /^(__|instance_eval|methods)/
32   - @hidden_methods ||= {}
33   - @hidden_methods[name.to_sym] = instance_method(name)
34   - undef_method name
35   - end
36   - end
37   -
38   - # Redefine a previously hidden method so that it may be called on a blank
39   - # slate object.
40   - #
41   - # no-op here since we don't hide the methods we reveal where this is
42   - # used in this implementation
43   - def reveal(name)
44   - end
45   - end
46   -
47   - instance_methods.each { |m| hide(m) }
48   -
49   - end
50   - end
51   -
52   -end
53   -
vendor/plugins/acts_as_ferret/lib/bulk_indexer.rb
... ... @@ -1,35 +0,0 @@
1   -module ActsAsFerret
2   - class BulkIndexer
3   - def initialize(args = {})
4   - @batch_size = args[:batch_size] || 1000
5   - @logger = args[:logger]
6   - @model = args[:model]
7   - @work_done = 0
8   - @index = args[:index]
9   - if args[:reindex]
10   - @reindex = true
11   - @model_count = @model.count.to_f
12   - else
13   - @model_count = args[:total]
14   - end
15   - end
16   -
17   - def index_records(records, offset)
18   - batch_time = measure_time {
19   - records.each { |rec| @index.add_document(rec.to_doc, rec.ferret_analyzer) if rec.ferret_enabled?(true) }
20   - }.to_f
21   - @work_done = offset.to_f / @model_count * 100.0 if @model_count > 0
22   - remaining_time = ( batch_time / @batch_size ) * ( @model_count - offset + @batch_size )
23   - @logger.info "#{@reindex ? 're' : 'bulk '}index model #{@model.name} : #{'%.2f' % @work_done}% complete : #{'%.2f' % remaining_time} secs to finish"
24   -
25   - end
26   -
27   - def measure_time
28   - t1 = Time.now
29   - yield
30   - Time.now - t1
31   - end
32   -
33   - end
34   -
35   -end
vendor/plugins/acts_as_ferret/lib/class_methods.rb
... ... @@ -1,295 +0,0 @@
1   -module ActsAsFerret
2   -
3   - module ClassMethods
4   -
5   - # Disables ferret index updates for this model. When a block is given,
6   - # Ferret will be re-enabled again after executing the block.
7   - def disable_ferret
8   - aaf_configuration[:enabled] = false
9   - if block_given?
10   - yield
11   - enable_ferret
12   - end
13   - end
14   -
15   - def enable_ferret
16   - aaf_configuration[:enabled] = true
17   - end
18   -
19   - def ferret_enabled?
20   - aaf_configuration[:enabled]
21   - end
22   -
23   - # rebuild the index from all data stored for this model, and any other
24   - # model classes associated with the same index.
25   - # This is called automatically when no index exists yet.
26   - #
27   - def rebuild_index
28   - aaf_index.rebuild_index
29   - end
30   -
31   - # re-index a number records specified by the given ids. Use for large
32   - # indexing jobs i.e. after modifying a lot of records with Ferret disabled.
33   - # Please note that the state of Ferret (enabled or disabled at class or
34   - # record level) is not checked by this method, so if you need to do so
35   - # (e.g. because of a custom ferret_enabled? implementation), you have to do
36   - # so yourself.
37   - def bulk_index(*ids)
38   - options = Hash === ids.last ? ids.pop : {}
39   - ids = ids.first if ids.size == 1 && ids.first.is_a?(Enumerable)
40   - aaf_index.bulk_index(self.name, ids, options)
41   - end
42   -
43   - # true if our db and table appear to be suitable for the mysql fast batch
44   - # hack (see
45   - # http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord)
46   - def use_fast_batches?
47   - if connection.class.name =~ /Mysql/ && primary_key == 'id' && aaf_configuration[:mysql_fast_batches]
48   - logger.info "using mysql specific batched find :all. Turn off with :mysql_fast_batches => false if you encounter problems (i.e. because of non-integer UUIDs in the id column)"
49   - true
50   - end
51   - end
52   -
53   - # Returns all records modified or created after the specified time.
54   - # Used by the rake rebuild task to find models that need to be updated in
55   - # the index after the rebuild finished because they changed while the
56   - # rebuild was running.
57   - # Override if your models don't stick to the created_at/updated_at
58   - # convention.
59   - def records_modified_since(time)
60   - condition = []
61   - %w(updated_at created_at).each do |col|
62   - condition << "#{col} >= ?" if column_names.include? col
63   - end
64   - if condition.empty?
65   - logger.warn "#{self.name}: Override records_modified_since(time) to keep the index up to date with records changed during rebuild."
66   - []
67   - else
68   - find :all, :conditions => [ condition.join(' AND '), *([time]*condition.size) ]
69   - end
70   - end
71   -
72   - # runs across all records yielding those to be indexed when the index is rebuilt
73   - def records_for_rebuild(batch_size = 1000)
74   - transaction do
75   - if use_fast_batches?
76   - offset = 0
77   - while (rows = find :all, :conditions => [ "#{table_name}.id > ?", offset ], :limit => batch_size).any?
78   - offset = rows.last.id
79   - yield rows, offset
80   - end
81   - else
82   - order = "#{primary_key} ASC" # fixes #212
83   - 0.step(self.count, batch_size) do |offset|
84   - yield find( :all, :limit => batch_size, :offset => offset, :order => order ), offset
85   - end
86   - end
87   - end
88   - end
89   -
90   - # yields the records with the given ids, in batches of batch_size
91   - def records_for_bulk_index(ids, batch_size = 1000)
92   - transaction do
93   - offset = 0
94   - ids.each_slice(batch_size) do |id_slice|
95   - records = find( :all, :conditions => ["id in (?)", id_slice] )
96   - #yield records, offset
97   - yield find( :all, :conditions => ["id in (?)", id_slice] ), offset
98   - offset += batch_size
99   - end
100   - end
101   - end
102   -
103   - # Retrieve the index instance for this model class. This can either be a
104   - # LocalIndex, or a RemoteIndex instance.
105   - #
106   - def aaf_index
107   - @index ||= ActsAsFerret::get_index(aaf_configuration[:name])
108   - end
109   -
110   - # Finds instances by searching the Ferret index. Terms are ANDed by default, use
111   - # OR between terms for ORed queries. Or specify +:or_default => true+ in the
112   - # +:ferret+ options hash of acts_as_ferret.
113   - #
114   - # You may either use the +offset+ and +limit+ options to implement your own
115   - # pagination logic, or use the +page+ and +per_page+ options to use the
116   - # built in pagination support which is compatible with will_paginate's view
117   - # helpers. If +page+ and +per_page+ are given, +offset+ and +limit+ will be
118   - # ignored.
119   - #
120   - # == options:
121   - # page:: page of search results to retrieve
122   - # per_page:: number of search results that are displayed per page
123   - # offset:: first hit to retrieve (useful for paging)
124   - # limit:: number of hits to retrieve, or :all to retrieve
125   - # all results
126   - # lazy:: Array of field names whose contents should be read directly
127   - # from the index. Those fields have to be marked
128   - # +:store => :yes+ in their field options. Give true to get all
129   - # stored fields. Note that if you have a shared index, you have
130   - # to explicitly state the fields you want to fetch, true won't
131   - # work here)
132   - #
133   - # +find_options+ is a hash passed on to active_record's find when
134   - # retrieving the data from db, useful to i.e. prefetch relationships with
135   - # :include or to specify additional filter criteria with :conditions.
136   - #
137   - # This method returns a +SearchResults+ instance, which really is an Array that has
138   - # been decorated with a total_hits attribute holding the total number of hits.
139   - # Additionally, SearchResults is compatible with the pagination helper
140   - # methods of the will_paginate plugin.
141   - #
142   - # Please keep in mind that the number of results delivered might be less than
143   - # +limit+ if you specify any active record conditions that further limit
144   - # the result. Use +limit+ and +offset+ as AR find_options instead.
145   - # +page+ and +per_page+ are supposed to work regardless of any
146   - # +conitions+ present in +find_options+.
147   - def find_with_ferret(q, options = {}, find_options = {})
148   - aaf_index.register_class(self, {})
149   -
150   - if respond_to?(:scope) && scope(:find, :conditions)
151   - if find_options[:conditions]
152   - find_options[:conditions] = "(#{find_options[:conditions]}) AND (#{scope(:find, :conditions)})"
153   - else
154   - find_options[:conditions] = scope(:find, :conditions)
155   - end
156   - end
157   -
158   - if options[:per_page]
159   - options[:page] = options[:page] ? options[:page].to_i : 1
160   - limit = options[:per_page]
161   - offset = (options[:page] - 1) * limit
162   - if find_options[:conditions]
163   - find_options[:limit] = limit
164   - find_options[:offset] = offset
165   - options[:limit] = :all
166   - options.delete :offset
167   - else
168   - # do pagination with ferret
169   - options[:limit] = limit
170   - options[:offset] = offset
171   - end
172   - elsif find_options[:conditions]
173   - find_options[:limit] ||= options.delete(:limit) unless options[:limit] == :all
174   - find_options[:offset] ||= options.delete(:offset)
175   - options[:limit] = :all
176   - end
177   -
178   - total_hits, result = aaf_index.find_records q, options.merge(:models => [self]), find_options
179   - logger.debug "Query: #{q}\ntotal hits: #{total_hits}, results delivered: #{result.size}"
180   - SearchResults.new(result, total_hits, options[:page], options[:per_page])
181   - end
182   -
183   -
184   - # Returns the total number of hits for the given query
185   - #
186   - # Note that since we don't query the database here, this method won't deliver
187   - # the expected results when used on an AR association.
188   - #
189   - def total_hits(q, options={})
190   - aaf_index.total_hits(q, options)
191   - end
192   -
193   - # Finds instance model name, ids and scores by contents.
194   - # Useful e.g. if you want to search across models or do not want to fetch
195   - # all result records (yet).
196   - #
197   - # Options are the same as for find_by_contents
198   - #
199   - # A block can be given too, it will be executed with every result:
200   - # find_ids_with_ferret(q, options) do |model, id, score|
201   - # id_array << id
202   - # scores_by_id[id] = score
203   - # end
204   - # NOTE: in case a block is given, only the total_hits value will be returned
205   - # instead of the [total_hits, results] array!
206   - #
207   - def find_ids_with_ferret(q, options = {}, &block)
208   - aaf_index.find_ids(q, options, &block)
209   - end
210   -
211   -
212   - protected
213   -
214   -# def find_records_lazy_or_not(q, options = {}, find_options = {})
215   -# if options[:lazy]
216   -# logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?
217   -# lazy_find_by_contents q, options
218   -# else
219   -# ar_find_by_contents q, options, find_options
220   -# end
221   -# end
222   -#
223   -# def ar_find_by_contents(q, options = {}, find_options = {})
224   -# result_ids = {}
225   -# total_hits = find_ids_with_ferret(q, options) do |model, id, score, data|
226   -# # stores ids, index and score of each hit for later ordering of
227   -# # results
228   -# result_ids[id] = [ result_ids.size + 1, score ]
229   -# end
230   -#
231   -# result = ActsAsFerret::retrieve_records( { self.name => result_ids }, find_options )
232   -#
233   -# # count total_hits via sql when using conditions or when we're called
234   -# # from an ActiveRecord association.
235   -# if find_options[:conditions] or caller.find{ |call| call =~ %r{active_record/associations} }
236   -# # chances are the ferret result count is not our total_hits value, so
237   -# # we correct this here.
238   -# if options[:limit] != :all || options[:page] || options[:offset] || find_options[:limit] || find_options[:offset]
239   -# # our ferret result has been limited, so we need to re-run that
240   -# # search to get the full result set from ferret.
241   -# result_ids = {}
242   -# find_ids_with_ferret(q, options.update(:limit => :all, :offset => 0)) do |model, id, score, data|
243   -# result_ids[id] = [ result_ids.size + 1, score ]
244   -# end
245   -# # Now ask the database for the total size of the final result set.
246   -# total_hits = count_records( { self.name => result_ids }, find_options )
247   -# else
248   -# # what we got from the database is our full result set, so take
249   -# # it's size
250   -# total_hits = result.length
251   -# end
252   -# end
253   -#
254   -# [ total_hits, result ]
255   -# end
256   -#
257   -# def lazy_find_by_contents(q, options = {})
258   -# logger.debug "lazy_find_by_contents: #{q}"
259   -# result = []
260   -# rank = 0
261   -# total_hits = find_ids_with_ferret(q, options) do |model, id, score, data|
262   -# logger.debug "model: #{model}, id: #{id}, data: #{data}"
263   -# result << FerretResult.new(model, id, score, rank += 1, data)
264   -# end
265   -# [ total_hits, result ]
266   -# end
267   -
268   -
269   - def model_find(model, id, find_options = {})
270   - model.constantize.find(id, find_options)
271   - end
272   -
273   -
274   -# def count_records(id_arrays, find_options = {})
275   -# count_options = find_options.dup
276   -# count_options.delete :limit
277   -# count_options.delete :offset
278   -# count = 0
279   -# id_arrays.each do |model, id_array|
280   -# next if id_array.empty?
281   -# model = model.constantize
282   -# # merge conditions
283   -# conditions = ActsAsFerret::combine_conditions([ "#{model.table_name}.#{model.primary_key} in (?)", id_array.keys ],
284   -# find_options[:conditions])
285   -# opts = find_options.merge :conditions => conditions
286   -# opts.delete :limit; opts.delete :offset
287   -# count += model.count opts
288   -# end
289   -# count
290   -# end
291   -
292   - end
293   -
294   -end
295   -
vendor/plugins/acts_as_ferret/lib/ferret_extensions.rb
... ... @@ -1,115 +0,0 @@
1   -module Ferret
2   -
3   - module Analysis
4   -
5   - # = PerFieldAnalyzer
6   - #
7   - # This PerFieldAnalyzer is a workaround to a memory leak in
8   - # ferret 0.11.4. It does basically do the same as the original
9   - # Ferret::Analysis::PerFieldAnalyzer, but without the leak :)
10   - #
11   - # http://ferret.davebalmain.com/api/classes/Ferret/Analysis/PerFieldAnalyzer.html
12   - #
13   - # Thanks to Ben from omdb.org for tracking this down and creating this
14   - # workaround.
15   - # You can read more about the issue there:
16   - # http://blog.omdb-beta.org/2007/7/29/tracking-down-a-memory-leak-in-ferret-0-11-4
17   - class PerFieldAnalyzer < ::Ferret::Analysis::Analyzer
18   - def initialize( default_analyzer = StandardAnalyzer.new )
19   - @analyzers = {}
20   - @default_analyzer = default_analyzer
21   - end
22   -
23   - def add_field( field, analyzer )
24   - @analyzers[field] = analyzer
25   - end
26   - alias []= add_field
27   -
28   - def token_stream(field, string)
29   - @analyzers.has_key?(field) ? @analyzers[field].token_stream(field, string) :
30   - @default_analyzer.token_stream(field, string)
31   - end
32   - end
33   - end
34   -
35   - class Index::Index
36   - attr_accessor :batch_size, :logger
37   -
38   - def index_models(models)
39   - models.each { |model| index_model model }
40   - flush
41   - optimize
42   - close
43   - ActsAsFerret::close_multi_indexes
44   - end
45   -
46   - def index_model(model)
47   - bulk_indexer = ActsAsFerret::BulkIndexer.new(:batch_size => @batch_size, :logger => logger,
48   - :model => model, :index => self, :reindex => true)
49   - logger.info "reindexing model #{model.name}"
50   -
51   - model.records_for_rebuild(@batch_size) do |records, offset|
52   - bulk_indexer.index_records(records, offset)
53   - end
54   - end
55   -
56   - def bulk_index(model, ids, options = {})
57   - options.reverse_merge! :optimize => true
58   - orig_flush = @auto_flush
59   - @auto_flush = false
60   - bulk_indexer = ActsAsFerret::BulkIndexer.new(:batch_size => @batch_size, :logger => logger,
61   - :model => model, :index => self, :total => ids.size)
62   - model.records_for_bulk_index(ids, @batch_size) do |records, offset|
63   - logger.debug "#{model} bulk indexing #{records.size} at #{offset}"
64   - bulk_indexer.index_records(records, offset)
65   - end
66   - logger.info 'finishing bulk index...'
67   - flush
68   - if options[:optimize]
69   - logger.info 'optimizing...'
70   - optimize
71   - end
72   - @auto_flush = orig_flush
73   - end
74   -
75   - end
76   -
77   - # add marshalling support to SortFields
78   - class Search::SortField
79   - def _dump(depth)
80   - to_s
81   - end
82   -
83   - def self._load(string)
84   - case string
85   - when /<DOC(_ID)?>!/ : Ferret::Search::SortField::DOC_ID_REV
86   - when /<DOC(_ID)?>/ : Ferret::Search::SortField::DOC_ID
87   - when '<SCORE>!' : Ferret::Search::SortField::SCORE_REV
88   - when '<SCORE>' : Ferret::Search::SortField::SCORE
89   - when /^(\w+):<(\w+)>(!)?$/ : new($1.to_sym, :type => $2.to_sym, :reverse => !$3.nil?)
90   - else raise "invalid value: #{string}"
91   - end
92   - end
93   - end
94   -
95   - # add marshalling support to Sort
96   - class Search::Sort
97   - def _dump(depth)
98   - to_s
99   - end
100   -
101   - def self._load(string)
102   - # we exclude the last <DOC> sorting as it is appended by new anyway
103   - if string =~ /^Sort\[(.*?)(<DOC>(!)?)?\]$/
104   - sort_fields = $1.split(',').map do |value|
105   - value.strip!
106   - Ferret::Search::SortField._load value unless value.blank?
107   - end
108   - new sort_fields.compact
109   - else
110   - raise "invalid value: #{string}"
111   - end
112   - end
113   - end
114   -
115   -end
vendor/plugins/acts_as_ferret/lib/ferret_find_methods.rb
... ... @@ -1,118 +0,0 @@
1   -module ActsAsFerret
2   - # Ferret search logic common to single-class indexes, shared indexes and
3   - # multi indexes.
4   - module FerretFindMethods
5   -
6   - def find_records(q, options = {}, ar_options = {})
7   - if options[:lazy]
8   - logger.warn "find_options #{ar_options} are ignored because :lazy => true" unless ar_options.empty?
9   - lazy_find q, options
10   - else
11   - ar_find q, options, ar_options
12   - end
13   - end
14   -
15   - def lazy_find(q, options = {})
16   - logger.debug "lazy_find: #{q}"
17   - result = []
18   - rank = 0
19   - total_hits = find_ids(q, options) do |model, id, score, data|
20   - logger.debug "model: #{model}, id: #{id}, data: #{data}"
21   - result << FerretResult.new(model, id, score, rank += 1, data)
22   - end
23   - [ total_hits, result ]
24   - end
25   -
26   - def ar_find(q, options = {}, ar_options = {})
27   - total_hits, id_arrays = find_id_model_arrays q, options
28   - result = ActsAsFerret::retrieve_records(id_arrays, ar_options)
29   -
30   - # count total_hits via sql when using conditions, multiple models, or when we're called
31   - # from an ActiveRecord association.
32   - if id_arrays.size > 1 or ar_options[:conditions]
33   - # chances are the ferret result count is not our total_hits value, so
34   - # we correct this here.
35   - if options[:limit] != :all || options[:page] || options[:offset] || ar_options[:limit] || ar_options[:offset]
36   - # our ferret result has been limited, so we need to re-run that
37   - # search to get the full result set from ferret.
38   - new_th, id_arrays = find_id_model_arrays( q, options.merge(:limit => :all, :offset => 0) )
39   - # Now ask the database for the total size of the final result set.
40   - total_hits = count_records( id_arrays, ar_options )
41   - else
42   - # what we got from the database is our full result set, so take
43   - # it's size
44   - total_hits = result.length
45   - end
46   - end
47   - [ total_hits, result ]
48   - end
49   -
50   - def count_records(id_arrays, ar_options = {})
51   - count_options = ar_options.dup
52   - count_options.delete :limit
53   - count_options.delete :offset
54   - count = 0
55   - id_arrays.each do |model, id_array|
56   - next if id_array.empty?
57   - model = model.constantize
58   - # merge conditions
59   - conditions = ActsAsFerret::conditions_for_model model, ar_options[:conditions]
60   - count_options[:conditions] = ActsAsFerret::combine_conditions([ "#{model.table_name}.#{model.primary_key} in (?)", id_array.keys ], conditions)
61   - count += model.count count_options
62   - end
63   - count
64   - end
65   -
66   - def find_id_model_arrays(q, options)
67   - id_arrays = {}
68   - rank = 0
69   - total_hits = find_ids(q, options) do |model, id, score, data|
70   - id_arrays[model] ||= {}
71   - id_arrays[model][id] = [ rank += 1, score ]
72   - end
73   - [total_hits, id_arrays]
74   - end
75   -
76   - # Queries the Ferret index to retrieve model class, id, score and the
77   - # values of any fields stored in the index for each hit.
78   - # If a block is given, these are yielded and the number of total hits is
79   - # returned. Otherwise [total_hits, result_array] is returned.
80   - def find_ids(query, options = {})
81   -
82   - result = []
83   - stored_fields = determine_stored_fields options
84   -
85   - q = process_query(query, options)
86   - q = scope_query_to_models q, options[:models] #if shared?
87   - logger.debug "query: #{query}\n-->#{q}"
88   - s = searcher
89   - total_hits = s.search_each(q, options) do |hit, score|
90   - doc = s[hit]
91   - model = doc[:class_name]
92   - # fetch stored fields if lazy loading
93   - data = extract_stored_fields(doc, stored_fields)
94   - if block_given?
95   - yield model, doc[:id], score, data
96   - else
97   - result << { :model => model, :id => doc[:id], :score => score, :data => data }
98   - end
99   - end
100   - #logger.debug "id_score_model array: #{result.inspect}"
101   - return block_given? ? total_hits : [total_hits, result]
102   - end
103   -
104   - def scope_query_to_models(query, models)
105   - return query if models.nil? or models == :all
106   - models = [ models ] if Class === models
107   - q = Ferret::Search::BooleanQuery.new
108   - q.add_query(query, :must)
109   - model_query = Ferret::Search::BooleanQuery.new
110   - models.each do |model|
111   - model_query.add_query(Ferret::Search::TermQuery.new(:class_name, model.name), :should)
112   - end
113   - q.add_query(model_query, :must)
114   - return q
115   - end
116   -
117   - end
118   -end
vendor/plugins/acts_as_ferret/lib/ferret_result.rb
... ... @@ -1,53 +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 < ActsAsFerret::BlankSlate
13   - include ResultAttributes
14   - attr_accessor :id
15   - reveal :methods
16   -
17   - def initialize(model, id, score, rank, data = {})
18   - @model = model.constantize
19   - @id = id
20   - @ferret_score = score
21   - @ferret_rank = rank
22   - @data = data
23   - @use_record = false
24   - end
25   -
26   - def inspect
27   - "#<FerretResult wrapper for #{@model} with id #{@id}"
28   - end
29   -
30   - def method_missing(method, *args, &block)
31   - if (@ar_record && @use_record) || !@data.has_key?(method)
32   - to_record.send method, *args, &block
33   - else
34   - @data[method]
35   - end
36   - end
37   -
38   - def respond_to?(name)
39   - methods.include?(name.to_s) || @data.has_key?(name.to_sym) || to_record.respond_to?(name)
40   - end
41   -
42   - def to_record
43   - unless @ar_record
44   - @ar_record = @model.find(id)
45   - @ar_record.ferret_rank = ferret_rank
46   - @ar_record.ferret_score = ferret_score
47   - # don't try to fetch attributes from RDig based records
48   - @use_record = !@ar_record.class.included_modules.include?(ActsAsFerret::RdigAdapter)
49   - end
50   - @ar_record
51   - end
52   - end
53   -end
vendor/plugins/acts_as_ferret/lib/ferret_server.rb
... ... @@ -1,238 +0,0 @@
1   -require 'drb'
2   -require 'thread'
3   -require 'yaml'
4   -require 'erb'
5   -
6   -################################################################################
7   -module ActsAsFerret
8   - module Remote
9   -
10   - ################################################################################
11   - class Config
12   -
13   - ################################################################################
14   - DEFAULTS = {
15   - 'host' => 'localhost',
16   - 'port' => '9009',
17   - 'cf' => "#{RAILS_ROOT}/config/ferret_server.yml",
18   - 'pid_file' => "#{RAILS_ROOT}/log/ferret_server.pid",
19   - 'log_file' => "#{RAILS_ROOT}/log/ferret_server.log",
20   - 'log_level' => 'debug',
21   - 'socket' => nil,
22   - 'script' => nil
23   - }
24   -
25   - ################################################################################
26   - # load the configuration file and apply default settings
27   - def initialize (file=DEFAULTS['cf'])
28   - @everything = YAML.load(ERB.new(IO.read(file)).result)
29   - raise "malformed ferret server config" unless @everything.is_a?(Hash)
30   - @config = DEFAULTS.merge(@everything[RAILS_ENV] || {})
31   - if @everything[RAILS_ENV]
32   - @config['uri'] = socket.nil? ? "druby://#{host}:#{port}" : "drbunix:#{socket}"
33   - end
34   - end
35   -
36   - ################################################################################
37   - # treat the keys of the config data as methods
38   - def method_missing (name, *args)
39   - @config.has_key?(name.to_s) ? @config[name.to_s] : super
40   - end
41   -
42   - end
43   -
44   - #################################################################################
45   - # This class acts as a drb server listening for indexing and
46   - # search requests from models declared to 'acts_as_ferret :remote => true'
47   - #
48   - # Usage:
49   - # - modify RAILS_ROOT/config/ferret_server.yml to suit your needs.
50   - # - environments for which no section in the config file exists will use
51   - # the index locally (good for unit tests/development mode)
52   - # - run script/ferret_server to start the server:
53   - # script/ferret_server -e production start
54   - # - to stop the server run
55   - # script/ferret_server -e production stop
56   - #
57   - class Server
58   -
59   - #################################################################################
60   - # FIXME include detection of OS and include the correct file
61   - require 'unix_daemon'
62   - include(ActsAsFerret::Remote::UnixDaemon)
63   -
64   -
65   - ################################################################################
66   - cattr_accessor :running
67   -
68   - ################################################################################
69   - def initialize
70   - ActiveRecord::Base.allow_concurrency = true
71   - require 'ar_mysql_auto_reconnect_patch'
72   - @cfg = ActsAsFerret::Remote::Config.new
73   - ActiveRecord::Base.logger = @logger = Logger.new(@cfg.log_file)
74   - ActiveRecord::Base.logger.level = Logger.const_get(@cfg.log_level.upcase) rescue Logger::DEBUG
75   - if @cfg.script
76   - path = File.join(RAILS_ROOT, @cfg.script)
77   - load path
78   - @logger.info "loaded custom startup script from #{path}"
79   - end
80   - end
81   -
82   - ################################################################################
83   - # start the server as a daemon process
84   - def start
85   - raise "ferret_server not configured for #{RAILS_ENV}" unless (@cfg.uri rescue nil)
86   - platform_daemon { run_drb_service }
87   - end
88   -
89   - ################################################################################
90   - # run the server and block until it exits
91   - def run
92   - raise "ferret_server not configured for #{RAILS_ENV}" unless (@cfg.uri rescue nil)
93   - run_drb_service
94   - end
95   -
96   - def run_drb_service
97   - $stdout.puts("starting ferret server...")
98   - self.class.running = true
99   - DRb.start_service(@cfg.uri, self)
100   - DRb.thread.join
101   - rescue Exception => e
102   - @logger.error(e.to_s)
103   - raise
104   - end
105   -
106   - #################################################################################
107   - # handles all incoming method calls, and sends them on to the correct local index
108   - # instance.
109   - #
110   - # Calls are not queued, so this will block until the call returned.
111   - #
112   - def method_missing(name, *args)
113   - @logger.debug "\#method_missing(#{name.inspect}, #{args.inspect})"
114   -
115   -
116   - index_name = args.shift
117   - index = if name.to_s =~ /^multi_(.+)/
118   - name = $1
119   - ActsAsFerret::multi_index(index_name)
120   - else
121   - ActsAsFerret::get_index(index_name)
122   - end
123   -
124   - if index.nil?
125   - @logger.error "\#index with name #{index_name} not found in call to #{name} with args #{args.inspect}"
126   - raise ActsAsFerret::IndexNotDefined.new(index_name)
127   - end
128   -
129   -
130   - # TODO find another way to implement the reconnection logic (maybe in
131   - # local_index or class_methods)
132   - # reconnect_when_needed(clazz) do
133   -
134   - # using respond_to? here so we not have to catch NoMethodError
135   - # which would silently catch those from deep inside the indexing
136   - # code, too...
137   -
138   - if index.respond_to?(name)
139   - index.send name, *args
140   - # TODO check where we need this:
141   - #elsif clazz.respond_to?(name)
142   - # @logger.debug "no luck, trying to call class method instead"
143   - # clazz.send name, *args
144   - else
145   - raise NoMethodError.new("method #{name} not supported by DRb server")
146   - end
147   - rescue => e
148   - @logger.error "ferret server error #{$!}\n#{$!.backtrace.join "\n"}"
149   - raise e
150   - end
151   -
152   - def register_class(class_name)
153   - @logger.debug "############ registerclass #{class_name}"
154   - class_name.constantize
155   - @logger.debug "index for class #{class_name}: #{ActsAsFerret::ferret_indexes[class_name.underscore.to_sym]}"
156   -
157   - end
158   -
159   - # make sure we have a versioned index in place, building one if necessary
160   - def ensure_index_exists(index_name)
161   - @logger.debug "DRb server: ensure_index_exists for index #{index_name}"
162   - definition = ActsAsFerret::get_index(index_name).index_definition
163   - dir = definition[:index_dir]
164   - unless File.directory?(dir) && File.file?(File.join(dir, 'segments')) && dir =~ %r{/\d+(_\d+)?$}
165   - rebuild_index(index_name)
166   - end
167   - end
168   -
169   - # disconnects the db connection for the class specified by class_name
170   - # used only in unit tests to check the automatic reconnection feature
171   - def db_disconnect!(class_name)
172   - with_class class_name do |clazz|
173   - clazz.connection.disconnect!
174   - end
175   - end
176   -
177   - # hides LocalIndex#rebuild_index to implement index versioning
178   - def rebuild_index(index_name)
179   - definition = ActsAsFerret::get_index(index_name).index_definition.dup
180   - models = definition[:registered_models]
181   - index = new_index_for(definition)
182   - # TODO fix reconnection stuff
183   - # reconnect_when_needed(clazz) do
184   - # @logger.debug "DRb server: rebuild index for class(es) #{models.inspect} in #{index.options[:path]}"
185   - index.index_models models
186   - # end
187   - new_version = File.join definition[:index_base_dir], Time.now.utc.strftime('%Y%m%d%H%M%S')
188   - # create a unique directory name (needed for unit tests where
189   - # multiple rebuilds per second may occur)
190   - if File.exists?(new_version)
191   - i = 0
192   - i+=1 while File.exists?("#{new_version}_#{i}")
193   - new_version << "_#{i}"
194   - end
195   -
196   - File.rename index.options[:path], new_version
197   - ActsAsFerret::change_index_dir index_name, new_version
198   - end
199   -
200   -
201   - protected
202   -
203   - def reconnect_when_needed(clazz)
204   - retried = false
205   - begin
206   - yield
207   - rescue ActiveRecord::StatementInvalid => e
208   - if e.message =~ /MySQL server has gone away/
209   - if retried
210   - raise e
211   - else
212   - @logger.info "StatementInvalid caught, trying to reconnect..."
213   - clazz.connection.reconnect!
214   - retried = true
215   - retry
216   - end
217   - else
218   - @logger.error "StatementInvalid caught, but unsure what to do with it: #{e}"
219   - raise e
220   - end
221   - end
222   - end
223   -
224   - def new_index_for(index_definition)
225   - ferret_cfg = index_definition[:ferret].dup
226   - ferret_cfg.update :auto_flush => false,
227   - :create => true,
228   - :field_infos => ActsAsFerret::field_infos(index_definition),
229   - :path => File.join(index_definition[:index_base_dir], 'rebuild')
230   - returning Ferret::Index::Index.new(ferret_cfg) do |i|
231   - i.batch_size = index_definition[:reindex_batch_size]
232   - i.logger = @logger
233   - end
234   - end
235   -
236   - end
237   - end
238   -end
vendor/plugins/acts_as_ferret/lib/index.rb
... ... @@ -1,99 +0,0 @@
1   -module ActsAsFerret
2   -
3   - class IndexLogger
4   - def initialize(logger, name)
5   - @logger = logger
6   - @index_name = name
7   - end
8   - %w(debug info warn error).each do |m|
9   - define_method(m) do |message|
10   - @logger.send m, "[#{@index_name}] #{message}"
11   - end
12   - question = :"#{m}?"
13   - define_method(question) do
14   - @logger.send question
15   - end
16   - end
17   - end
18   -
19   - # base class for local and remote indexes
20   - class AbstractIndex
21   - include FerretFindMethods
22   -
23   - attr_reader :logger, :index_name, :index_definition, :registered_models_config
24   - def initialize(index_definition)
25   - @index_definition = index_definition
26   - @registered_models_config = {}
27   - @index_name = index_definition[:name]
28   - @logger = IndexLogger.new(ActsAsFerret::logger, @index_name)
29   - end
30   -
31   - # TODO allow for per-class field configuration (i.e. different via, boosts
32   - # for the same field among different models)
33   - def register_class(clazz, options = {})
34   - logger.info "register class #{clazz} with index #{index_name}"
35   -
36   - if force = options.delete(:force_re_registration)
37   - index_definition[:registered_models].delete(clazz)
38   - end
39   -
40   - if index_definition[:registered_models].map(&:name).include?(clazz.name)
41   - logger.info("refusing re-registration of class #{clazz}")
42   - else
43   - index_definition[:registered_models] << clazz
44   - @registered_models_config[clazz] = options
45   -
46   - # merge fields from this acts_as_ferret call with predefined fields
47   - already_defined_fields = index_definition[:ferret_fields]
48   - field_config = ActsAsFerret::build_field_config options[:fields]
49   - field_config.update ActsAsFerret::build_field_config( options[:additional_fields] )
50   - field_config.each do |field, config|
51   - if already_defined_fields.has_key?(field)
52   - logger.info "ignoring redefinition of ferret field #{field}" if shared?
53   - else
54   - already_defined_fields[field] = config
55   - logger.info "adding new field #{field} from class #{clazz.name} to index #{index_name}"
56   - end
57   - end
58   -
59   - # update default field list to be used by the query parser, unless it
60   - # was explicitly given by user.
61   - #
62   - # It will include all content fields *not* marked as :untokenized.
63   - # This fixes the otherwise failing CommentTest#test_stopwords. Basically
64   - # this means that by default only tokenized fields (which all fields are
65   - # by default) will be searched. If you want to search inside the contents
66   - # of an untokenized field, you'll have to explicitly specify it in your
67   - # query.
68   - unless index_definition[:user_default_field]
69   - # grab all tokenized fields
70   - ferret_fields = index_definition[:ferret_fields]
71   - index_definition[:ferret][:default_field] = ferret_fields.keys.select do |field|
72   - ferret_fields[field][:index] != :untokenized
73   - end
74   - logger.info "default field list for index #{index_name}: #{index_definition[:ferret][:default_field].inspect}"
75   - end
76   - end
77   -
78   - return index_definition
79   - end
80   -
81   - # true if this index is used by more than one model class
82   - def shared?
83   - index_definition[:registered_models].size > 1
84   - end
85   -
86   - # Switches the index to a new index directory.
87   - # Used by the DRb server when switching to a new index version.
88   - def change_index_dir(new_dir)
89   - logger.debug "[#{index_name}] changing index dir to #{new_dir}"
90   - index_definition[:index_dir] = index_definition[:ferret][:path] = new_dir
91   - reopen!
92   - logger.debug "[#{index_name}] index dir is now #{new_dir}"
93   - end
94   -
95   - protected
96   -
97   - end
98   -
99   -end
vendor/plugins/acts_as_ferret/lib/instance_methods.rb
... ... @@ -1,165 +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 to a Unicode elipsis
29   - # character.
30   - def highlight(query, options = {})
31   - self.class.aaf_index.highlight(self.send(self.class.primary_key), self.class.name, query, options)
32   - end
33   -
34   - # re-eneable ferret indexing for this instance after a call to #disable_ferret
35   - def enable_ferret
36   - @ferret_disabled = nil
37   - end
38   - alias ferret_enable enable_ferret # compatibility
39   -
40   - # returns true if ferret indexing is enabled for this record.
41   - #
42   - # The optional is_bulk_index parameter will be true if the method is called
43   - # by rebuild_index or bulk_index, and false otherwise.
44   - #
45   - # If is_bulk_index is true, the class level ferret_enabled state will be
46   - # ignored by this method (per-instance ferret_enabled checks however will
47   - # take place, so if you override this method to forbid indexing of certain
48   - # records you're still safe).
49   - def ferret_enabled?(is_bulk_index = false)
50   - @ferret_disabled.nil? && (is_bulk_index || self.class.ferret_enabled?) && (aaf_configuration[:if].nil? || aaf_configuration[:if].call(self))
51   - end
52   -
53   - # Returns the analyzer to use when adding this record to the index.
54   - #
55   - # Override to return a specific analyzer for any record that is to be
56   - # indexed, i.e. specify a different analyzer based on language. Returns nil
57   - # by default so the global analyzer (specified with the acts_as_ferret
58   - # call) is used.
59   - def ferret_analyzer
60   - nil
61   - end
62   -
63   - # Disable Ferret for this record for a specified amount of time. ::once will
64   - # disable Ferret for the next call to #save (this is the default), ::always
65   - # will do so for all subsequent calls.
66   - #
67   - # Note that this will turn off only the create and update hooks, but not the
68   - # destroy hook. I think that's reasonable, if you think the opposite, please
69   - # tell me.
70   - #
71   - # To manually trigger reindexing of a record after you're finished modifying
72   - # it, you can call #ferret_update directly instead of #save (remember to
73   - # enable ferret again before).
74   - #
75   - # When given a block, this will be executed without any ferret indexing of
76   - # this object taking place. The optional argument in this case can be used
77   - # to indicate if the object should be indexed after executing the block
78   - # (::index_when_finished). Automatic Ferret indexing of this object will be
79   - # turned on after the block has been executed. If passed ::index_when_true,
80   - # the index will only be updated if the block evaluated not to false or nil.
81   - #
82   - def disable_ferret(option = :once)
83   - if block_given?
84   - @ferret_disabled = :always
85   - result = yield
86   - ferret_enable
87   - ferret_update if option == :index_when_finished || (option == :index_when_true && result)
88   - result
89   - elsif [:once, :always].include?(option)
90   - @ferret_disabled = option
91   - else
92   - raise ArgumentError.new("Invalid Argument #{option}")
93   - end
94   - end
95   -
96   - # add to index
97   - def ferret_create
98   - if ferret_enabled?
99   - logger.debug "ferret_create/update: #{self.class.name} : #{self.id}"
100   - self.class.aaf_index << self
101   - else
102   - ferret_enable if @ferret_disabled == :once
103   - end
104   - true # signal success to AR
105   - end
106   - alias :ferret_update :ferret_create
107   -
108   -
109   - # remove from index
110   - def ferret_destroy
111   - logger.debug "ferret_destroy: #{self.class.name} : #{self.id}"
112   - begin
113   - self.class.aaf_index.remove self.id, self.class.name
114   - rescue
115   - logger.warn("Could not find indexed value for this object: #{$!}\n#{$!.backtrace}")
116   - end
117   - true # signal success to AR
118   - end
119   -
120   - # turn this instance into a ferret document (which basically is a hash of
121   - # fieldname => value pairs)
122   - def to_doc
123   - logger.debug "creating doc for class: #{self.class.name}, id: #{self.id}"
124   - returning Ferret::Document.new do |doc|
125   - # store the id and class name of each item
126   - doc[:id] = self.id
127   - doc[:class_name] = self.class.name
128   -
129   - # iterate through the fields and add them to the document
130   - aaf_configuration[:defined_fields].each_pair do |field, config|
131   - doc[field] = self.send("#{field}_to_ferret") unless config[:ignore]
132   - end
133   - if aaf_configuration[:boost]
134   - if self.respond_to?(aaf_configuration[:boost])
135   - boost = self.send aaf_configuration[:boost]
136   - doc.boost = boost.to_i if boost
137   - else
138   - logger.error "boost option should point to an instance method: #{aaf_configuration[:boost]}"
139   - end
140   - end
141   - end
142   - end
143   -
144   - def document_number
145   - self.class.aaf_index.document_number(id, self.class.name)
146   - end
147   -
148   - def query_for_record
149   - self.class.aaf_index.query_for_record(id, self.class.name)
150   - end
151   -
152   - def content_for_field_name(field, via = field, dynamic_boost = nil)
153   - return nil if field.to_sym == :type
154   - field_data = self.send(via) || self.instance_variable_get("@#{via}")
155   - if (dynamic_boost && boost_value = self.send(dynamic_boost))
156   - field_data = Ferret::Field.new(field_data)
157   - field_data.boost = boost_value.to_i
158   - end
159   - field_data
160   - end
161   -
162   -
163   - end
164   -
165   -end
vendor/plugins/acts_as_ferret/lib/local_index.rb
... ... @@ -1,199 +0,0 @@
1   -module ActsAsFerret
2   - class LocalIndex < AbstractIndex
3   - include MoreLikeThis::IndexMethods
4   -
5   - def initialize(index_name)
6   - super
7   - ensure_index_exists
8   - end
9   -
10   - def reopen!
11   - logger.debug "reopening index at #{index_definition[:ferret][:path]}"
12   - close
13   - ferret_index
14   - end
15   -
16   - # The 'real' Ferret Index instance
17   - def ferret_index
18   - ensure_index_exists
19   - returning @ferret_index ||= Ferret::Index::Index.new(index_definition[:ferret]) do
20   - @ferret_index.batch_size = index_definition[:reindex_batch_size]
21   - @ferret_index.logger = logger
22   - end
23   - end
24   -
25   - # Checks for the presence of a segments file in the index directory
26   - # Rebuilds the index if none exists.
27   - def ensure_index_exists
28   - #logger.debug "LocalIndex: ensure_index_exists at #{index_definition[:index_dir]}"
29   - unless File.file? "#{index_definition[:index_dir]}/segments"
30   - ActsAsFerret::ensure_directory(index_definition[:index_dir])
31   - rebuild_index
32   - end
33   - end
34   -
35   - # Closes the underlying index instance
36   - def close
37   - @ferret_index.close if @ferret_index
38   - rescue StandardError
39   - # is raised when index already closed
40   - ensure
41   - @ferret_index = nil
42   - end
43   -
44   - # rebuilds the index from all records of the model classes associated with this index
45   - def rebuild_index
46   - models = index_definition[:registered_models]
47   - logger.debug "rebuild index with models: #{models.inspect}"
48   - close
49   - index = Ferret::Index::Index.new(index_definition[:ferret].dup.update(:auto_flush => false,
50   - :field_infos => ActsAsFerret::field_infos(index_definition),
51   - :create => true))
52   - index.batch_size = index_definition[:reindex_batch_size]
53   - index.logger = logger
54   - index.index_models models
55   - reopen!
56   - end
57   -
58   - def bulk_index(class_name, ids, options)
59   - ferret_index.bulk_index(class_name.constantize, ids, options)
60   - end
61   -
62   - # Parses the given query string into a Ferret Query object.
63   - def process_query(query, options = {})
64   - return query unless String === query
65   - ferret_index.synchronize do
66   - if options[:analyzer]
67   - # use per-query analyzer if present
68   - qp = Ferret::QueryParser.new ferret_index.instance_variable_get('@options').merge(options)
69   - reader = ferret_index.reader
70   - qp.fields =
71   - reader.fields unless options[:all_fields] || options[:fields]
72   - qp.tokenized_fields =
73   - reader.tokenized_fields unless options[:tokenized_fields]
74   - return qp.parse query
75   - else
76   - # work around ferret bug in #process_query (doesn't ensure the
77   - # reader is open)
78   - ferret_index.send(:ensure_reader_open)
79   - return ferret_index.process_query(query)
80   - end
81   - end
82   - end
83   -
84   - # Total number of hits for the given query.
85   - def total_hits(query, options = {})
86   - ferret_index.search(query, options).total_hits
87   - end
88   -
89   - def searcher
90   - ferret_index
91   - end
92   -
93   -
94   - ######################################
95   - # methods working on a single record
96   - # called from instance_methods, here to simplify interfacing with the
97   - # remote ferret server
98   - # TODO having to pass id and class_name around like this isn't nice
99   - ######################################
100   -
101   - # add record to index
102   - # record may be the full AR object, a Ferret document instance or a Hash
103   - def add(record, analyzer = nil)
104   - unless Hash === record || Ferret::Document === record
105   - analyzer = record.ferret_analyzer
106   - record = record.to_doc
107   - end
108   - ferret_index.add_document(record, analyzer)
109   - end
110   - alias << add
111   -
112   - # delete record from index
113   - def remove(id, class_name)
114   - ferret_index.query_delete query_for_record(id, class_name)
115   - end
116   -
117   - # highlight search terms for the record with the given id.
118   - def highlight(id, class_name, query, options = {})
119   - logger.debug("highlight: #{class_name} / #{id} query: #{query}")
120   - options.reverse_merge! :num_excerpts => 2, :pre_tag => '<em>', :post_tag => '</em>'
121   - highlights = []
122   - ferret_index.synchronize do
123   - doc_num = document_number(id, class_name)
124   -
125   - if options[:field]
126   - highlights << ferret_index.highlight(query, doc_num, options)
127   - else
128   - query = process_query(query) # process only once
129   - index_definition[:ferret_fields].each_pair do |field, config|
130   - next if config[:store] == :no || config[:highlight] == :no
131   - options[:field] = field
132   - highlights << ferret_index.highlight(query, doc_num, options)
133   - end
134   - end
135   - end
136   - return highlights.compact.flatten[0..options[:num_excerpts]-1]
137   - end
138   -
139   - # retrieves the ferret document number of the record with the given id.
140   - def document_number(id, class_name)
141   - hits = ferret_index.search(query_for_record(id, class_name))
142   - return hits.hits.first.doc if hits.total_hits == 1
143   - raise "cannot determine document number for class #{class_name} / primary key: #{id}\nresult was: #{hits.inspect}"
144   - end
145   -
146   - # build a ferret query matching only the record with the given id
147   - # the class name only needs to be given in case of a shared index configuration
148   - def query_for_record(id, class_name = nil)
149   - if shared?
150   - raise InvalidArgumentError.new("shared index needs class_name argument") if class_name.nil?
151   - returning bq = Ferret::Search::BooleanQuery.new do
152   - bq.add_query(Ferret::Search::TermQuery.new(:id, id.to_s), :must)
153   - bq.add_query(Ferret::Search::TermQuery.new(:class_name, class_name), :must)
154   - end
155   - else
156   - Ferret::Search::TermQuery.new(:id, id.to_s)
157   - end
158   - end
159   -
160   -
161   -
162   - def determine_stored_fields(options = {})
163   - stored_fields = options[:lazy]
164   - if stored_fields && !(Array === stored_fields)
165   - stored_fields = index_definition[:ferret_fields].select { |field, config| config[:store] == :yes }.map(&:first)
166   - end
167   - logger.debug "stored_fields: #{stored_fields.inspect}"
168   - return stored_fields
169   - end
170   -
171   - # loads data for fields declared as :lazy from the Ferret document
172   - def extract_stored_fields(doc, stored_fields)
173   - fields = index_definition[:ferret_fields]
174   - data = {}
175   - logger.debug "extracting stored fields #{stored_fields.inspect} from document #{doc[:class_name]} / #{doc[:id]}"
176   - stored_fields.each do |field|
177   - if field_cfg = fields[field]
178   - data[field_cfg[:via]] = doc[field]
179   - end
180   - end if stored_fields
181   - logger.debug "done: #{data.inspect}"
182   - return data
183   - end
184   -
185   - protected
186   -
187   - # returns a MultiIndex instance operating on a MultiReader
188   - #def multi_index(model_classes)
189   - # model_classes.map!(&:constantize) if String === model_classes.first
190   - # model_classes.sort! { |a, b| a.name <=> b.name }
191   - # key = model_classes.inject("") { |s, clazz| s + clazz.name }
192   - # multi_config = index_definition[:ferret].dup
193   - # multi_config.delete :default_field # we don't want the default field list of *this* class for multi_searching
194   - # ActsAsFerret::multi_indexes[key] ||= MultiIndex.new(model_classes, multi_config)
195   - #end
196   -
197   - end
198   -
199   -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_with_ferret(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,126 +0,0 @@
1   -module ActsAsFerret #:nodoc:
2   -
3   - # Base class for remote and local multi-indexes
4   - class MultiIndexBase
5   - include FerretFindMethods
6   - attr_accessor :logger
7   -
8   - def initialize(indexes, options = {})
9   - # ensure all models indexes exist
10   - @indexes = indexes
11   - indexes.each { |i| i.ensure_index_exists }
12   - default_fields = indexes.inject([]) do |fields, idx|
13   - fields + [ idx.index_definition[:ferret][:default_field] ]
14   - end.flatten.uniq
15   - @options = {
16   - :default_field => default_fields
17   - }.update(options)
18   - @logger = IndexLogger.new(ActsAsFerret::logger, "multi: #{indexes.map(&:index_name).join(',')}")
19   - end
20   -
21   - def ar_find(query, options = {}, ar_options = {})
22   - limit = options.delete(:limit)
23   - offset = options.delete(:offset) || 0
24   - options[:limit] = :all
25   - total_hits, result = super query, options, ar_options
26   - total_hits = result.size if ar_options[:conditions]
27   - if limit && limit != :all
28   - result = result[offset..limit+offset-1]
29   - end
30   - [total_hits, result]
31   - end
32   -
33   - def determine_stored_fields(options)
34   - return nil unless options.has_key?(:lazy)
35   - stored_fields = []
36   - @indexes.each do |index|
37   - stored_fields += index.determine_stored_fields(options)
38   - end
39   - return stored_fields.uniq
40   - end
41   -
42   - def shared?
43   - false
44   - end
45   -
46   - end
47   -
48   - # This class can be used to search multiple physical indexes at once.
49   - class MultiIndex < MultiIndexBase
50   -
51   - def extract_stored_fields(doc, stored_fields)
52   - ActsAsFerret::get_index_for(doc[:class_name]).extract_stored_fields(doc, stored_fields) unless stored_fields.blank?
53   - end
54   -
55   - def total_hits(q, options = {})
56   - search(q, options).total_hits
57   - end
58   -
59   - def search(query, options={})
60   - query = process_query(query, options)
61   - logger.debug "parsed query: #{query.to_s}"
62   - searcher.search(query, options)
63   - end
64   -
65   - def search_each(query, options = {}, &block)
66   - query = process_query(query, options)
67   - searcher.search_each(query, options, &block)
68   - end
69   -
70   - # checks if all our sub-searchers still are up to date
71   - def latest?
72   - #return false unless @reader
73   - # segfaults with 0.10.4 --> TODO report as bug @reader.latest?
74   - @reader and @reader.latest?
75   - #@sub_readers.each do |r|
76   - # return false unless r.latest?
77   - #end
78   - #true
79   - end
80   -
81   - def searcher
82   - ensure_searcher
83   - @searcher
84   - end
85   -
86   - def doc(i)
87   - searcher[i]
88   - end
89   - alias :[] :doc
90   -
91   - def query_parser
92   - @query_parser ||= Ferret::QueryParser.new(@options)
93   - end
94   -
95   - def process_query(query, options = {})
96   - query = query_parser.parse(query) if query.is_a?(String)
97   - return query
98   - end
99   -
100   - def close
101   - @searcher.close if @searcher
102   - @reader.close if @reader
103   - end
104   -
105   - protected
106   -
107   - def ensure_searcher
108   - unless latest?
109   - @sub_readers = @indexes.map { |idx|
110   - begin
111   - reader = Ferret::Index::IndexReader.new(idx.index_definition[:index_dir])
112   - logger.debug "sub-reader opened: #{reader}"
113   - reader
114   - rescue Exception
115   - raise "error opening reader on index for class #{clazz.inspect}: #{$!}"
116   - end
117   - }
118   - close
119   - @reader = Ferret::Index::IndexReader.new(@sub_readers)
120   - @searcher = Ferret::Search::Searcher.new(@reader)
121   - end
122   - end
123   -
124   - end # of class MultiIndex
125   -
126   -end
vendor/plugins/acts_as_ferret/lib/rdig_adapter.rb
... ... @@ -1,141 +0,0 @@
1   -begin
2   - require 'rdig'
3   -rescue LoadError
4   -end
5   -module ActsAsFerret
6   -
7   - # The RdigAdapter is automatically included into your model if you specify
8   - # the +:rdig+ options hash in your call to acts_as_ferret. It overrides
9   - # several methods declared by aaf to retrieve documents with the help of
10   - # RDig's http crawler when you call rebuild_index.
11   - module RdigAdapter
12   -
13   - if defined?(RDig)
14   -
15   - def self.included(target)
16   - target.extend ClassMethods
17   - target.send :include, InstanceMethods
18   - end
19   -
20   - # Indexer class to replace RDig's original indexer
21   - class Indexer
22   - include MonitorMixin
23   - def initialize(batch_size, model_class, &block)
24   - @batch_size = batch_size
25   - @model_class = model_class
26   - @documents = []
27   - @offset = 0
28   - @block = block
29   - super()
30   - end
31   -
32   - def add(doc)
33   - synchronize do
34   - @documents << @model_class.new(doc.uri.to_s, doc)
35   - process_batch if @documents.size >= @batch_size
36   - end
37   - end
38   - alias << add
39   -
40   - def close
41   - synchronize do
42   - process_batch
43   - end
44   - end
45   -
46   - protected
47   - def process_batch
48   - ActsAsFerret::logger.info "RdigAdapter::Indexer#process_batch: #{@documents.size} docs in queue, offset #{@offset}"
49   - @block.call @documents, @offset
50   - @offset += @documents.size
51   - @documents = []
52   - end
53   - end
54   -
55   - module ClassMethods
56   - # overriding aaf to return the documents fetched via RDig
57   - def records_for_rebuild(batch_size = 1000, &block)
58   - indexer = Indexer.new(batch_size, self, &block)
59   - configure_rdig do
60   - crawler = RDig::Crawler.new RDig.configuration, ActsAsFerret::logger
61   - crawler.instance_variable_set '@indexer', indexer
62   - ActsAsFerret::logger.debug "now crawling..."
63   - crawler.crawl
64   - end
65   - rescue => e
66   - ActsAsFerret::logger.error e
67   - ActsAsFerret::logger.debug e.backtrace.join("\n")
68   - ensure
69   - indexer.close if indexer
70   - end
71   -
72   - # overriding aaf to skip reindexing records changed during the rebuild
73   - # when rebuilding with the rake task
74   - def records_modified_since(time)
75   - []
76   - end
77   -
78   - # unfortunately need to modify global RDig.configuration because it's
79   - # used everywhere in RDig
80   - def configure_rdig
81   - # back up original config
82   - old_logger = RDig.logger
83   - old_cfg = RDig.configuration.dup
84   - RDig.logger = ActsAsFerret.logger
85   - rdig_configuration[:crawler].each { |k,v| RDig.configuration.crawler.send :"#{k}=", v } if rdig_configuration[:crawler]
86   - if ce_config = rdig_configuration[:content_extraction]
87   - RDig.configuration.content_extraction = OpenStruct.new( :hpricot => OpenStruct.new( ce_config ) )
88   - end
89   - yield
90   - ensure
91   - # restore original config
92   - RDig.configuration.crawler = old_cfg.crawler
93   - RDig.configuration.content_extraction = old_cfg.content_extraction
94   - RDig.logger = old_logger
95   - end
96   -
97   - # overriding aaf to enforce loading page title and content from the
98   - # ferret index
99   - def find_with_ferret(q, options = {}, find_options = {})
100   - options[:lazy] = true
101   - super
102   - end
103   -
104   - def find_for_id(id)
105   - new id
106   - end
107   - end
108   -
109   - module InstanceMethods
110   - def initialize(uri, rdig_document = nil)
111   - @id = uri
112   - @rdig_document = rdig_document
113   - end
114   -
115   - # Title of the document.
116   - # Use the +:title_tag_selector+ option to declare the hpricot expression
117   - # that should be used for selecting the content for this field.
118   - def title
119   - @rdig_document.title
120   - end
121   -
122   - # Content of the document.
123   - # Use the +:content_tag_selector+ option to declare the hpricot expression
124   - # that should be used for selecting the content for this field.
125   - def content
126   - @rdig_document.body
127   - end
128   -
129   - # Url of this document.
130   - def id
131   - @id
132   - end
133   -
134   - def to_s
135   - "Page at #{id}, title: #{title}"
136   - end
137   - end
138   - end
139   - end
140   -
141   -end
vendor/plugins/acts_as_ferret/lib/remote_functions.rb
... ... @@ -1,23 +0,0 @@
1   -module ActsAsFerret
2   - module RemoteFunctions
3   -
4   - private
5   -
6   - def yield_results(total_hits, results)
7   - results.each do |result|
8   - yield result[:model], result[:id], result[:score], result[:data]
9   - end
10   - total_hits
11   - end
12   -
13   -
14   - def handle_drb_error(return_value_in_case_of_error = false)
15   - yield
16   - rescue DRb::DRbConnError => e
17   - logger.error "DRb connection error: #{e}"
18   - logger.warn e.backtrace.join("\n")
19   - raise e if ActsAsFerret::raise_drb_errors?
20   - return_value_in_case_of_error
21   - end
22   - end
23   -end
vendor/plugins/acts_as_ferret/lib/remote_index.rb
... ... @@ -1,54 +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   - include RemoteFunctions
8   -
9   - def initialize(config)
10   - super
11   - @server = DRbObject.new(nil, ActsAsFerret::remote)
12   - end
13   -
14   - # Cause model classes to be loaded (and indexes get declared) on the DRb
15   - # side of things.
16   - def register_class(clazz, options)
17   - handle_drb_error { @server.register_class clazz.name }
18   - end
19   -
20   - def method_missing(method_name, *args)
21   - args.unshift index_name
22   - handle_drb_error { @server.send(method_name, *args) }
23   - end
24   -
25   - # Proxy any methods that require special return values in case of errors
26   - {
27   - :highlight => []
28   - }.each do |method_name, default_result|
29   - define_method method_name do |*args|
30   - args.unshift index_name
31   - handle_drb_error(default_result) { @server.send method_name, *args }
32   - end
33   - end
34   -
35   - def find_ids(q, options = {}, &proc)
36   - total_hits, results = handle_drb_error([0, []]) { @server.find_ids(index_name, q, options) }
37   - block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ]
38   - end
39   -
40   - # add record to index
41   - def add(record)
42   - handle_drb_error { @server.add index_name, record.to_doc }
43   - end
44   - alias << add
45   -
46   - private
47   -
48   - #def model_class_name
49   - # index_definition[:class_name]
50   - #end
51   -
52   - end
53   -
54   -end
vendor/plugins/acts_as_ferret/lib/remote_multi_index.rb
... ... @@ -1,20 +0,0 @@
1   -module ActsAsFerret
2   - class RemoteMultiIndex < MultiIndexBase
3   - include RemoteFunctions
4   -
5   - def initialize(indexes, options = {})
6   - @index_names = indexes.map(&:index_name)
7   - @server = DRbObject.new(nil, ActsAsFerret::remote)
8   - super
9   - end
10   -
11   - def find_ids(query, options, &proc)
12   - total_hits, results = handle_drb_error([0, []]) { @server.multi_find_ids(@index_names, query, options) }
13   - block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ]
14   - end
15   -
16   - def method_missing(name, *args)
17   - handle_drb_error { @server.send(:"multi_#{name}", @index_names, *args) }
18   - end
19   - end
20   -end
vendor/plugins/acts_as_ferret/lib/search_results.rb
... ... @@ -1,50 +0,0 @@
1   -module ActsAsFerret
2   -
3   - # decorator that adds a total_hits accessor and will_paginate compatible
4   - # paging support to search result arrays
5   - class SearchResults < ActsAsFerret::BlankSlate
6   - reveal :methods
7   - attr_reader :current_page, :per_page, :total_hits, :total_pages
8   - alias total_entries total_hits # will_paginate compatibility
9   - alias page_count total_pages # will_paginate backwards compatibility
10   -
11   - def initialize(results, total_hits, current_page = 1, per_page = nil)
12   - @results = results
13   - @total_hits = total_hits
14   - @current_page = current_page
15   - @per_page = (per_page || total_hits)
16   - @total_pages = @per_page > 0 ? (@total_hits / @per_page.to_f).ceil : 0
17   - end
18   -
19   - def method_missing(symbol, *args, &block)
20   - @results.send(symbol, *args, &block)
21   - end
22   -
23   - def respond_to?(name)
24   - methods.include?(name.to_s) || @results.respond_to?(name)
25   - end
26   -
27   -
28   - # code from here on was directly taken from will_paginate's collection.rb
29   -
30   - # Current offset of the paginated collection. If we're on the first page,
31   - # it is always 0. If we're on the 2nd page and there are 30 entries per page,
32   - # the offset is 30. This property is useful if you want to render ordinals
33   - # besides your records: simply start with offset + 1.
34   - #
35   - def offset
36   - (current_page - 1) * per_page
37   - end
38   -
39   - # current_page - 1 or nil if there is no previous page
40   - def previous_page
41   - current_page > 1 ? (current_page - 1) : nil
42   - end
43   -
44   - # current_page + 1 or nil if there is no next page
45   - def next_page
46   - current_page < total_pages ? (current_page + 1) : nil
47   - end
48   - end
49   -
50   -end
vendor/plugins/acts_as_ferret/lib/server_manager.rb
... ... @@ -1,58 +0,0 @@
1   -################################################################################
2   -require 'optparse'
3   -
4   -################################################################################
5   -$ferret_server_options = {
6   - 'environment' => nil,
7   - 'debug' => nil,
8   - 'root' => nil
9   -}
10   -
11   -################################################################################
12   -OptionParser.new do |optparser|
13   - optparser.banner = "Usage: #{File.basename($0)} [options] {start|stop|run}"
14   -
15   - optparser.on('-h', '--help', "This message") do
16   - puts optparser
17   - exit
18   - end
19   -
20   - optparser.on('-R', '--root=PATH', 'Set RAILS_ROOT to the given string') do |r|
21   - $ferret_server_options['root'] = r
22   - end
23   -
24   - optparser.on('-e', '--environment=NAME', 'Set RAILS_ENV to the given string') do |e|
25   - $ferret_server_options['environment'] = e
26   - end
27   -
28   - optparser.on('--debug', 'Include full stack traces on exceptions') do
29   - $ferret_server_options['debug'] = true
30   - end
31   -
32   - $ferret_server_action = optparser.permute!(ARGV)
33   - (puts optparser; exit(1)) unless $ferret_server_action.size == 1
34   -
35   - $ferret_server_action = $ferret_server_action.first
36   - (puts optparser; exit(1)) unless %w(start stop run).include?($ferret_server_action)
37   -end
38   -
39   -################################################################################
40   -begin
41   - ENV['FERRET_USE_LOCAL_INDEX'] = 'true'
42   - ENV['RAILS_ENV'] = $ferret_server_options['environment']
43   -
44   - # determine RAILS_ROOT unless already set
45   - RAILS_ROOT = $ferret_server_options['root'] || File.join(File.dirname(__FILE__), *(['..']*4)) unless defined? RAILS_ROOT
46   - # check if environment.rb is present
47   - rails_env_file = File.join(RAILS_ROOT, 'config', 'environment')
48   - raise "Unable to find Rails environment.rb at \n#{rails_env_file}.rb\nPlease use the --root option of ferret_server to point it to your RAILS_ROOT." unless File.exists?(rails_env_file+'.rb')
49   - # load it
50   - require rails_env_file
51   -
52   - require 'acts_as_ferret'
53   - ActsAsFerret::Remote::Server.new.send($ferret_server_action)
54   -rescue Exception => e
55   - $stderr.puts(e.message)
56   - $stderr.puts(e.backtrace.join("\n")) if $ferret_server_options['debug']
57   - exit(1)
58   -end
vendor/plugins/acts_as_ferret/lib/unix_daemon.rb
... ... @@ -1,64 +0,0 @@
1   -################################################################################
2   -module ActsAsFerret
3   - module Remote
4   -
5   - ################################################################################
6   - # methods for becoming a daemon on Unix-like operating systems
7   - module UnixDaemon
8   -
9   - ################################################################################
10   - def platform_daemon (&block)
11   - safefork do
12   - write_pid_file
13   - trap("TERM") { exit(0) }
14   - sess_id = Process.setsid
15   - STDIN.reopen("/dev/null")
16   - STDOUT.reopen("#{RAILS_ROOT}/log/ferret_server.out", "a")
17   - STDERR.reopen(STDOUT)
18   - block.call
19   - end
20   - end
21   -
22   - ################################################################################
23   - # stop the daemon, nicely at first, and then forcefully if necessary
24   - def stop
25   - pid = read_pid_file
26   - raise "ferret_server doesn't appear to be running" unless pid
27   - $stdout.puts("stopping ferret server...")
28   - Process.kill("TERM", pid)
29   - 30.times { Process.kill(0, pid); sleep(0.5) }
30   - $stdout.puts("using kill -9 #{pid}")
31   - Process.kill(9, pid)
32   - rescue Errno::ESRCH => e
33   - $stdout.puts("process #{pid} has stopped")
34   - ensure
35   - File.unlink(@cfg.pid_file) if File.exist?(@cfg.pid_file)
36   - end
37   -
38   - ################################################################################
39   - def safefork (&block)
40   - @fork_tries ||= 0
41   - fork(&block)
42   - rescue Errno::EWOULDBLOCK
43   - raise if @fork_tries >= 20
44   - @fork_tries += 1
45   - sleep 5
46   - retry
47   - end
48   -
49   - #################################################################################
50   - # create the PID file and install an at_exit handler
51   - def write_pid_file
52   - raise "ferret_server may already be running, a pid file exists: #{@cfg.pid_file}" if read_pid_file
53   - open(@cfg.pid_file, "w") {|f| f << Process.pid << "\n"}
54   - at_exit { File.unlink(@cfg.pid_file) if read_pid_file == Process.pid }
55   - end
56   -
57   - #################################################################################
58   - def read_pid_file
59   - File.read(@cfg.pid_file).to_i if File.exist?(@cfg.pid_file)
60   - end
61   -
62   - end
63   - end
64   -end
vendor/plugins/acts_as_ferret/lib/without_ar.rb
... ... @@ -1,49 +0,0 @@
1   -module ActsAsFerret
2   -
3   - # Include this module to use acts_as_ferret with model classes
4   - # not based on ActiveRecord.
5   - #
6   - # Implement the find_for_id(id) class method in your model class in
7   - # order to make search work.
8   - module WithoutAR
9   - def self.included(target)
10   - target.extend ClassMethods
11   - target.extend ActsAsFerret::ActMethods
12   - target.send :include, InstanceMethods
13   - end
14   -
15   - module ClassMethods
16   - def logger
17   - RAILS_DEFAULT_LOGGER
18   - end
19   - def table_name
20   - self.name.underscore
21   - end
22   - def primary_key
23   - 'id'
24   - end
25   - def find(what, args = {})
26   - case what
27   - when :all
28   - ids = args[:conditions][1]
29   - ids.map { |id| find id }
30   - else
31   - find_for_id what
32   - end
33   - end
34   - def find_for_id(id)
35   - raise NotImplementedError.new("implement find_for_id in class #{self.name}")
36   - end
37   - def count
38   - 0
39   - end
40   - end
41   -
42   - module InstanceMethods
43   - def logger
44   - self.class.logger
45   - end
46   - end
47   - end
48   -
49   -end
vendor/plugins/acts_as_ferret/rakefile
... ... @@ -1,134 +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.bindir = "bin"
65   - s.executables = ["aaf_install"]
66   - s.default_executable = "aaf_install"
67   - s.autorequire = 'acts_as_ferret'
68   - s.has_rdoc = true
69   - # s.test_files = Dir['test/**/*_test.rb']
70   - s.author = "Jens Kraemer"
71   - s.email = "jk@jkraemer.net"
72   - s.homepage = "http://projects.jkraemer.net/acts_as_ferret"
73   - end
74   -
75   - package_task = Rake::GemPackageTask.new(spec) do |pkg|
76   - pkg.need_tar = true
77   - end
78   -
79   - # Validate that everything is ready to go for a release.
80   - task :prerelease do
81   - announce
82   - announce "**************************************************************"
83   - announce "* Making RubyGem Release #{PKG_VERSION}"
84   - announce "**************************************************************"
85   - announce
86   - # Are all source files checked in?
87   - if ENV['RELTEST']
88   - announce "Release Task Testing, skipping checked-in file test"
89   - else
90   - announce "Pulling in svn..."
91   - `svk pull .`
92   - announce "Checking for unchecked-in files..."
93   - data = `svk st`
94   - unless data =~ /^$/
95   - fail "SVK status is not clean ... do you have unchecked-in files?"
96   - end
97   - announce "No outstanding checkins found ... OK"
98   -# announce "Pushing to svn..."
99   -# `svk push .`
100   - end
101   - end
102   -
103   -
104   - desc "tag the new release"
105   - task :tag => [ :prerelease ] do
106   - reltag = "REL_#{PKG_VERSION.gsub(/\./, '_')}"
107   - reltag << ENV['REUSE'].gsub(/\./, '_') if ENV['REUSE']
108   - announce "Tagging with [#{PKG_VERSION}]"
109   - if ENV['RELTEST']
110   - announce "Release Task Testing, skipping tagging"
111   - else
112   - `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}`
113   - `svn del -m 'remove old stable' svn://projects.jkraemer.net/acts_as_ferret/tags/stable`
114   - `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`
115   - end
116   - end
117   -
118   - # Upload release to rubyforge
119   - desc "Upload release to rubyforge"
120   - task :prel => [ :tag, :prerelease, :package ] do
121   - `rubyforge login`
122   - release_command = "rubyforge add_release #{RUBYFORGE_PROJECT} #{PKG_NAME} '#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"
123   - puts release_command
124   - system(release_command)
125   - `rubyforge config #{RUBYFORGE_PROJECT}`
126   - release_command = "rubyforge add_file #{RUBYFORGE_PROJECT} #{PKG_NAME} '#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.tgz"
127   - puts release_command
128   - system(release_command)
129   - end
130   -
131   - desc 'Publish the gem and API docs'
132   - task :release => [:pdoc, :prel ]
133   -
134   -end
vendor/plugins/acts_as_ferret/recipes/aaf_recipes.rb
... ... @@ -1,97 +0,0 @@
1   -# Ferret DRb server Capistrano tasks
2   -#
3   -# Usage:
4   -# in your Capfile, add acts_as_ferret's recipes directory to your load path and
5   -# load the ferret tasks:
6   -#
7   -# load_paths << 'vendor/plugins/acts_as_ferret/recipes'
8   -# load 'aaf_recipes'
9   -#
10   -# This will hook aaf's DRb start/stop tasks into the standard
11   -# deploy:{start|restart|stop} tasks so the server will be restarted along with
12   -# the rest of your application.
13   -# Also an index directory in the shared folder will be created and symlinked
14   -# into current/ when you deploy.
15   -#
16   -# In order to use the ferret:index:rebuild task, declare the indexes you intend to
17   -# rebuild remotely in config/deploy.rb:
18   -#
19   -# set :ferret_indexes, %w( model another_model shared )
20   -#
21   -# HINT: To be very sure that your DRb server and application are always using
22   -# the same model and schema versions, and you never lose any index updates because
23   -# of the DRb server being restarted in that moment, use the following sequence
24   -# to update your application:
25   -#
26   -# cap deploy:stop deploy:update deploy:migrate deploy:start
27   -#
28   -# That will stop the DRb server after stopping your application, and bring it
29   -# up before starting the application again. Plus they'll never use different
30   -# versions of model classes (which might happen otherwise)
31   -# Downside: Your downtime is a bit longer than with the usual deploy, so be sure to
32   -# put up some maintenance page for the meantime. Obviously this won't work if
33   -# your migrations need acts_as_ferret (i.e. if you update model instances which
34   -# would lead to index updates). In this case bring up the DRb server before
35   -# running your migrations:
36   -#
37   -# cap deploy:stop deploy:update ferret:start deploy:migrate ferret:stop deploy:start
38   -#
39   -# Chances are that you're still not safe if your migrations not only modify the index,
40   -# but also change the structure of your models. So just don't do both things in
41   -# one go - I can't think of an easy way to handle this case automatically.
42   -# Suggestions and patches are of course very welcome :-)
43   -
44   -namespace :ferret do
45   -
46   - desc "Stop the Ferret DRb server"
47   - task :stop, :roles => :app do
48   - rails_env = fetch(:rails_env, 'production')
49   - run "cd #{current_path}; script/ferret_server -e #{rails_env} stop || true"
50   - end
51   -
52   - desc "Start the Ferret DRb server"
53   - task :start, :roles => :app do
54   - rails_env = fetch(:rails_env, 'production')
55   - run "cd #{current_path}; script/ferret_server -e #{rails_env} start"
56   - end
57   -
58   - desc "Restart the Ferret DRb server"
59   - task :restart, :roles => :app do
60   - top.ferret.stop
61   - sleep 1
62   - top.ferret.start
63   - end
64   -
65   - namespace :index do
66   -
67   - desc "Rebuild the Ferret index. See aaf_recipes.rb for instructions."
68   - task :rebuild => :environment, :roles => :app do
69   - rake = fetch(:rake, 'rake')
70   - rails_env = fetch(:rails_env, 'production')
71   - indexes = fetch(:ferret_indexes, nil)
72   - if indexes and indexes.any?
73   - run "cd #{current_path}; RAILS_ENV=#{rails_env} INDEXES='#{indexes.join(' ')}' #{rake} ferret:rebuild"
74   - end
75   - end
76   -
77   - desc "purges all indexes for the current environment"
78   - task :purge, :roles => :app do
79   - run "rm -fr #{shared_path}/index/#{rails_env}"
80   - end
81   -
82   - desc "symlinks index folder"
83   - task :symlink, :roles => :app do
84   - run "mkdir -p #{shared_path}/index && rm -rf #{release_path}/index && ln -nfs #{shared_path}/index #{release_path}/index"
85   - end
86   -
87   - end
88   -
89   -end
90   -
91   -after "deploy:stop", "ferret:stop"
92   -before "deploy:start", "ferret:start"
93   -
94   -before "deploy:restart", "ferret:stop"
95   -after "deploy:restart", "ferret:start"
96   -after "deploy:symlink", "ferret:index:symlink"
97   -
vendor/plugins/acts_as_ferret/script/ferret_daemon
... ... @@ -1,94 +0,0 @@
1   -# Ferret Win32 Service Daemon, called by Win 32 service,
2   -# created by Herryanto Siatono <herryanto@pluitsolutions.com>
3   -#
4   -# see doc/README.win32 for usage instructions
5   -#
6   -require 'optparse'
7   -require 'win32/service'
8   -include Win32
9   -
10   -# Read options
11   -options = {}
12   -ARGV.options do |opts|
13   - opts.banner = 'Usage: ferret_daemon [options]'
14   - opts.on("-l", "--log FILE", "Daemon log file") {|file| options[:log] = file }
15   - opts.on("-c","--console","Run Ferret server on console.") {options[:console] = true}
16   - opts.on_tail("-h","--help", "Show this help message") {puts opts; exit}
17   - opts.on("-e", "--environment ENV ", "Rails environment") {|env|
18   - options[:environment] = env
19   - ENV['RAILS_ENV'] = env
20   - }
21   - opts.parse!
22   -end
23   -
24   -require File.dirname(__FILE__) + '/../config/environment'
25   -
26   -# Ferret Win32 Service Daemon, called by Win 32 service,
27   -# to run on the console, use -c or --console option.
28   -module Ferret
29   - class FerretDaemon < Daemon
30   - # Standard logger to redirect STDOUT and STDERR to a log file
31   - class FerretStandardLogger
32   - def initialize(logger)
33   - @logger = logger
34   - end
35   -
36   - def write(s)
37   - @logger.info s
38   - end
39   - end
40   -
41   - def initialize(options={})
42   - @options = options
43   -
44   - # initialize logger
45   - if options[:log]
46   - @logger = Logger.new @options[:log]
47   - else
48   - @logger = Logger.new RAILS_ROOT + "/log/ferret_service_#{RAILS_ENV}.log"
49   - end
50   -
51   - # redirect stout and stderr to Ferret logger if running as windows service
52   - $stdout = $stderr = FerretStandardLogger.new(@logger) unless @options[:console]
53   -
54   - log "Initializing FerretDaemon..."
55   - if @options[:console]
56   - self.service_init
57   - self.service_main
58   - end
59   - end
60   -
61   - def service_main
62   - log "Service main enterred..."
63   -
64   - while running?
65   - log "Listening..."
66   - sleep
67   - end
68   -
69   - log "Service main exit..."
70   - end
71   -
72   - def service_init
73   - log "Starting Ferret DRb server..."
74   - ActsAsFerret::Remote::Server.start
75   - log "FerretDaemon started."
76   - end
77   -
78   - def service_stop
79   - log "Stopping service..."
80   - DRb.stop_service
81   - log "FerretDaemon stopped."
82   - end
83   -
84   - def log(msg)
85   - @logger.info msg
86   - puts msg if @options[:console]
87   - end
88   - end
89   -end
90   -
91   -if __FILE__ == $0
92   - d = Ferret::FerretDaemon.new(options)
93   - d.mainloop
94   -end
vendor/plugins/acts_as_ferret/script/ferret_server
... ... @@ -1,10 +0,0 @@
1   -#!/usr/bin/env ruby
2   -
3   -begin
4   - require File.join(File.dirname(__FILE__), '../vendor/plugins/acts_as_ferret/lib/server_manager')
5   -rescue LoadError
6   - # try the gem
7   - require 'rubygems'
8   - gem 'acts_as_ferret'
9   - require 'server_manager'
10   -end
vendor/plugins/acts_as_ferret/script/ferret_service
... ... @@ -1,178 +0,0 @@
1   -# Ferret Win32 Service Daemon install script
2   -# created by Herryanto Siatono <herryanto@pluitsolutions.com>
3   -#
4   -# see doc/README.win32 for usage instructions
5   -#
6   -require 'optparse'
7   -require 'win32/service'
8   -include Win32
9   -
10   -module Ferret
11   - # Parse and validate service command and options
12   - class FerretServiceCommand
13   - COMMANDS = ['install', 'remove', 'start', 'stop', 'help']
14   - BANNER = "Usage: ruby script/ferret_service <command> [options]"
15   -
16   - attr_reader :options, :command
17   -
18   - def initialize
19   - @options = {}
20   - end
21   -
22   - def valid_command?
23   - COMMANDS.include?@command
24   - end
25   -
26   - def valid_options?
27   - @options[:name] and !@options[:name].empty?
28   - end
29   -
30   - def print_command_list
31   - puts BANNER
32   - puts "\nAvailable commands:\n"
33   - puts COMMANDS.map {|cmd| " - #{cmd}\n"}
34   - puts "\nUse option -h for each command to help."
35   - exit
36   - end
37   -
38   - def validate_options
39   - errors = []
40   - errors << "Service name is required." unless @options[:name]
41   -
42   - if (errors.size > 0)
43   - errors << "Error found. Use: 'ruby script/ferret_service #{@command} -h' for to get help."
44   - puts errors.join("\n")
45   - exit
46   - end
47   - end
48   -
49   - def run(args)
50   - @command = args.shift
51   - @command = @command.dup.downcase if @command
52   -
53   - # validate command and options
54   - print_command_list unless valid_command? or @command == 'help'
55   -
56   - opts_parser = create_options_parser
57   - begin
58   - opts_parser.parse!(args)
59   - rescue OptionParser::ParseError => e
60   - puts e
61   - puts opts_parser
62   - end
63   -
64   - # validate required options
65   - validate_options
66   - end
67   -
68   - def create_options_parser
69   - opts_parser = OptionParser.new
70   - opts_parser.banner = BANNER
71   - opts_parser.on("-n", "--name=NAME", "Service name") {|name| @options[:name] = name }
72   - opts_parser.on_tail("-t", "--trace", "Display stack trace when exception thrown") { @options[:trace] = true }
73   - opts_parser.on_tail("-h", "--help", "Show this help message") { puts opts_parser; exit }
74   -
75   - if ['install'].include?@command
76   - opts_parser.on("-d", "--display=NAME", "Service display name") {|name| @options[:display] = name }
77   -
78   - opts_parser.on("-l", "--log FILE", "Service log file") {|file| @options[:log] = file }
79   - opts_parser.on("-e", "--environment ENV ", "Rails environment") { |env|
80   - @options[:environment] = env
81   - ENV['RAILS_ENV'] = env
82   - }
83   - end
84   - opts_parser
85   - end
86   - end
87   -
88   - # Install, Remove, Start and Stop Ferret DRb server Win32 service
89   - class FerretService
90   - FERRET_DAEMON = 'ferret_daemon'
91   -
92   - def initialize
93   - end
94   -
95   - def install
96   - svc = Service.new
97   -
98   - begin
99   - if Service.exists?(@options[:name])
100   - puts "Service name '#{@options[:name]}' already exists."
101   - return
102   - end
103   -
104   - svc.create_service do |s|
105   - s.service_name = @options[:name]
106   - s.display_name = @options[:display]
107   - s.binary_path_name = binary_path_name
108   - s.dependencies = []
109   - end
110   -
111   - svc.close
112   - puts "'#{@options[:name]}' service installed."
113   - rescue => e
114   - handle_error(e)
115   - end
116   - end
117   -
118   - def remove
119   - begin
120   - Service.stop(@options[:name])
121   - rescue
122   - end
123   -
124   - begin
125   - Service.delete(@options[:name])
126   - puts "'#{@options[:name]}' service removed."
127   - rescue => e
128   - handle_error(e)
129   - end
130   - end
131   -
132   - def start
133   - begin
134   - Service.start(@options[:name])
135   - puts "'#{@options[:name]}' successfully started."
136   - rescue => e
137   - handle_error(e)
138   - end
139   - end
140   -
141   - def stop
142   - begin
143   - Service.stop(@options[:name])
144   - puts "'#{@options[:name]}' successfully stopped.\n"
145   - rescue => e
146   - handle_error(e)
147   - end
148   - end
149   -
150   - def run(args)
151   - svc_cmd = FerretServiceCommand.new
152   - svc_cmd.run(args)
153   - @options = svc_cmd.options
154   - self.send(svc_cmd.command.to_sym)
155   - end
156   -
157   - protected
158   - def handle_error(e)
159   - if @options[:trace]
160   - raise e
161   - else
162   - puts e
163   - end
164   - end
165   -
166   - def binary_path_name
167   - path = ""
168   - path << "#{ENV['RUBY_HOME']}/bin/" if ENV['RUBY_HOME']
169   - path << "ruby.exe "
170   - path << File.expand_path("script/" + FERRET_DAEMON)
171   - path << " -e #{@options[:environment]} " if @options[:environment]
172   - path << " -l #{@options[:log]} " if @options[:log]
173   - path
174   - end
175   - end
176   -end
177   -
178   -Ferret::FerretService.new.run(ARGV)
vendor/plugins/acts_as_ferret/tasks/ferret.rake
... ... @@ -1,24 +0,0 @@
1   -namespace :ferret do
2   -
3   - # Rebuild index task. Declare the indexes to be rebuilt with the INDEXES
4   - # environment variable:
5   - #
6   - # INDEXES="my_model shared" rake ferret:rebuild
7   - desc "Rebuild a Ferret index. Specify what model to rebuild with the MODEL environment variable."
8   - task :rebuild do
9   - require File.join(RAILS_ROOT, 'config', 'environment')
10   -
11   - indexes = ENV['INDEXES'].split
12   - indexes.each do |index_name|
13   - start = 1.minute.ago
14   - ActsAsFerret::rebuild_index index_name
15   - idx = ActsAsFerret::get_index index_name
16   - # update records that have changed since the rebuild started
17   - idx.index_definition[:registered_models].each do |m|
18   - m.records_modified_since(start).each do |object|
19   - object.ferret_update
20   - end
21   - end
22   - end
23   - end
24   -end
vendor/plugins/acts_as_solr_reloaded/.gitignore 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +*.log
  2 +*.log
  3 +*_pid
  4 +coverage/*
  5 +coverage.data
  6 +solr/solr/data/*
  7 +.svn
  8 +test/db/*.db
  9 +test/log/*.log
  10 +pkg/*
  11 +*.sw?
  12 +.DS_Store
  13 +coverage
  14 +rdoc
  15 +pkg
  16 +acts_as_solr_reloaded-*.gem
  17 +tags
... ...
vendor/plugins/acts_as_solr_reloaded/LICENSE 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +(The MIT License)
  2 +
  3 +Copyright © 2010
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining
  6 +a copy of this software and associated documentation files (the
  7 +‘Software’), to deal in the Software without restriction, including
  8 +without limitation the rights to use, copy, modify, merge, publish,
  9 +distribute, sublicense, and/or sell copies of the Software, and to
  10 +permit persons to whom the Software is furnished to do so, subject to
  11 +the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be
  14 +included in all copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
  17 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  19 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  20 +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  21 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  22 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
vendor/plugins/acts_as_solr_reloaded/README.markdown 0 → 100644
... ... @@ -0,0 +1,64 @@
  1 +Description
  2 +======
  3 +This plugin adds full text search capabilities and many other nifty features from Apache's [Solr](http://lucene.apache.org/solr/) to any Rails model.
  4 +It was based on the first draft by Erik Hatcher.
  5 +
  6 +This plugin is intended for use in old versions of Rails. For newer versions, I strongly advice using Sunspot!
  7 +Nevertheless, this plugin is used for Noosfero project in production. Any problem please open an issue.
  8 +
  9 +Installation
  10 +======
  11 +
  12 +Install as a plugin
  13 +
  14 + script/plugin install git://github.com/brauliobo/acts_as_solr_reloaded.git
  15 +
  16 +Download Solr 3.1
  17 +
  18 + rake solr:download
  19 +
  20 +Requirements
  21 +------
  22 +* Java Runtime Environment(JRE) 1.6 aka 6.0 or newer [http://www.java.com/en/download/index.jsp](http://www.java.com/en/download/index.jsp) (use default-jre for Debian like distribution)
  23 +* (Recommended) If you have libxml-ruby installed, make sure it's at least version 0.7
  24 +
  25 +Configuration
  26 +======
  27 +See config/solr.yml file.
  28 +
  29 +Basic Usage
  30 +======
  31 +<pre><code>
  32 +# Just include the line below to any of your ActiveRecord models:
  33 + acts_as_solr
  34 +
  35 +# Or if you want, you can specify only the fields that should be indexed:
  36 + acts_as_solr :fields => [:name, :author]
  37 +
  38 +# Then to find instances of your model, just do:
  39 + Model.search(query) #query is a string representing your query
  40 +
  41 +# Please see ActsAsSolr::ActsMethods for a complete info
  42 +
  43 +</code></pre>
  44 +
  45 +
  46 +`acts_as_solr` in your tests
  47 +======
  48 +To test code that uses `acts_as_solr` you must start a Solr server for the test environment. You can do that with `rake solr:start RAILS_ENV=test`
  49 +
  50 +However, if you would like to mock out Solr calls so that a Solr server is not needed (and your tests will run much faster), just add this to your `test_helper.rb` or similar:
  51 +
  52 +<pre><code>
  53 +class ActsAsSolr::Post
  54 + def self.execute(request)
  55 + true
  56 + end
  57 +end
  58 +</pre></code>
  59 +
  60 +([via](http://www.subelsky.com/2007/10/actsassolr-capistranhttpwwwbloggercomim.html#c1646308013209805416))
  61 +
  62 +Release Information
  63 +======
  64 +Released under the MIT license.
... ...
vendor/plugins/acts_as_solr_reloaded/README.rdoc 0 → 100644
... ... @@ -0,0 +1,96 @@
  1 += DESCRIPTION
  2 +
  3 +This plugin adds full text search capabilities and many other nifty features from Apache's Solr[http://lucene.apache.org/solr/] to any Rails model, like:
  4 +
  5 +* faceting
  6 +* dynamic attributes
  7 +* integration with acts_as_taggable_on
  8 +* integration with will_paginate
  9 +* highlighting
  10 +* geolocation
  11 +* relevance
  12 +* suggest
  13 +
  14 +Watch this screencast for a short demo of the latests features:
  15 +
  16 +http://www.vimeo.com/8728276
  17 +
  18 +== INSTALLATION
  19 +
  20 + script/plugin install git://github.com/brauliobo/acts_as_solr_reloaded.git
  21 + rake solr:download
  22 +
  23 +== REQUIREMENTS
  24 +
  25 +* Java Runtime Environment(JRE) 1.5 aka 5.0 [http://www.java.com/en/download/index.jsp](http://www.java.com/en/download/index.jsp) (use default-jre for Debian like distribution)
  26 +* (Recommended) If you have libxml-ruby installed, make sure it's at least version 0.7
  27 +
  28 +== CONFIGURATION
  29 +
  30 +See config/solr.yml file.
  31 +
  32 +== USAGE
  33 +
  34 +Just include the line below to any of your ActiveRecord models:
  35 + acts_as_solr
  36 +
  37 +Or if you want, you can specify only the fields that should be indexed:
  38 + acts_as_solr :fields => [:name, :author]
  39 +
  40 +Then to find instances of your model, just do:
  41 + Model.search(query) #query is a string representing your query
  42 +
  43 +Case you want to use dynamic attributes or geolocalization, you can use this generators that setup the database:
  44 +
  45 + script/generate dynamic_attributes_migration
  46 + script/generate local_migration
  47 +
  48 +and then configure your model like this:
  49 +
  50 + acts_as_solr :dynamic_attributes => true,
  51 + :spatial => true
  52 +
  53 +If you want to integrate the model with acts_as_taggable_on, just add the option :taggable => true :
  54 +
  55 + acts_as_solr :taggable => true
  56 +
  57 +Please see ActsAsSolr::ActsMethods for a complete info
  58 +
  59 +== PAGINATION
  60 +
  61 +In your controller:
  62 +
  63 + @search = Product.search "beer", :page => 2, :per_page => 20
  64 +
  65 +And in your view:
  66 +
  67 + will_paginate @search
  68 +
  69 +== TESTING
  70 +
  71 +To test code that uses acts_as_solr_reloaded you must start a Solr server for the test environment and also start MongoDB.
  72 +
  73 +== LICENSE
  74 +
  75 +(The MIT License)
  76 +
  77 +Copyright © 2010
  78 +
  79 +Permission is hereby granted, free of charge, to any person obtaining
  80 +a copy of this software and associated documentation files (the
  81 +‘Software’), to deal in the Software without restriction, including
  82 +without limitation the rights to use, copy, modify, merge, publish,
  83 +distribute, sublicense, and/or sell copies of the Software, and to
  84 +permit persons to whom the Software is furnished to do so, subject to
  85 +the following conditions:
  86 +
  87 +The above copyright notice and this permission notice shall be
  88 +included in all copies or substantial portions of the Software.
  89 +
  90 +THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
  91 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  92 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  93 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  94 +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  95 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  96 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
vendor/plugins/acts_as_solr_reloaded/Rakefile 0 → 100644
... ... @@ -0,0 +1,71 @@
  1 +require 'rubygems'
  2 +require 'rake'
  3 +require 'rake/testtask'
  4 +require 'rake/rdoctask'
  5 +
  6 +Dir["#{File.dirname(__FILE__)}/lib/tasks/*.rake"].sort.each { |ext| load ext }
  7 +
  8 +desc "Default Task"
  9 +task :default => [:test]
  10 +
  11 +desc "Runs the unit tests"
  12 +task :test => "test:unit"
  13 +
  14 +namespace :test do
  15 + task :setup do
  16 + RAILS_ROOT = File.expand_path("#{File.dirname(__FILE__)}/test") unless defined? RAILS_ROOT
  17 + ENV['RAILS_ENV'] = "test"
  18 + ENV["ACTS_AS_SOLR_TEST"] = "true"
  19 + require File.expand_path("#{File.dirname(__FILE__)}/config/solr_environment")
  20 + puts "Using " + DB
  21 + %x(mysql -u#{MYSQL_USER} < #{File.dirname(__FILE__) + "/test/fixtures/db_definitions/mysql.sql"}) if DB == 'mysql'
  22 +
  23 + Rake::Task["test:migrate"].invoke
  24 + end
  25 +
  26 + desc 'Measures test coverage using rcov'
  27 + task :rcov => :setup do
  28 + rm_f "coverage"
  29 + rm_f "coverage.data"
  30 + rcov = "rcov --rails --aggregate coverage.data --text-summary -Ilib"
  31 +
  32 + system("#{rcov} --html #{Dir.glob('test/**/*_shoulda.rb').join(' ')}")
  33 + system("open coverage/index.html") if PLATFORM['darwin']
  34 + end
  35 +
  36 + desc 'Runs the functional tests, testing integration with Solr'
  37 + Rake::TestTask.new('functional' => :setup) do |t|
  38 + t.pattern = "test/functional/*_test.rb"
  39 + t.verbose = true
  40 + end
  41 +
  42 + desc "Unit tests"
  43 + Rake::TestTask.new(:unit) do |t|
  44 + t.libs << 'test/unit'
  45 + t.pattern = "test/unit/*_shoulda.rb"
  46 + t.verbose = true
  47 + end
  48 +end
  49 +
  50 +Rake::RDocTask.new do |rd|
  51 + rd.main = "README.rdoc"
  52 + rd.rdoc_dir = "rdoc"
  53 + rd.rdoc_files.exclude("lib/solr/**/*.rb", "lib/solr.rb")
  54 + rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
  55 +end
  56 +
  57 +begin
  58 + require 'jeweler'
  59 + Jeweler::Tasks.new do |s|
  60 + s.name = "acts_as_solr_reloaded"
  61 + s.summary = "This gem adds full text search capabilities and many other nifty features from Apache Solr to any Rails model."
  62 + s.email = "dc.rec1@gmail.com"
  63 + s.homepage = "http://github.com/dcrec1/acts_as_solr_reloaded"
  64 + s.description = "This gem adds full text search capabilities and many other nifty features from Apache Solr to any Rails model."
  65 + s.authors = ["Diego Carrion"]
  66 + s.files = FileList["[A-Z]*", "{bin,generators,config,lib,solr}/**/*"] +
  67 + FileList["test/**/*"].reject {|f| f.include?("test/log")}.reject {|f| f.include?("test/tmp")}
  68 + end
  69 +rescue LoadError
  70 + puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
  71 +end
... ...
vendor/plugins/acts_as_solr_reloaded/TESTING_THE_PLUGIN 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +acts_as_solr comes with a quick and fast unit test suite, and with a longer-running
  2 +functional test suite, the latter testing the actual integration with Solr.
  3 +
  4 +The unit test suite is written using Shoulda, so make sure you have a recent version
  5 +installed.
  6 +
  7 +Running `rake test` or just `rake` will run both test suites. Use `rake test:unit` to
  8 +just run the unit test suite.
  9 +
  10 +== How to run functional tests for this plugin:
  11 +To run the acts_as_solr's plugin tests run the following steps:
  12 +
  13 +- create a MySQL database called "actsassolr_test" (if you want to use MySQL)
  14 +
  15 +- create a new Rails project, if needed (the plugin can only be tested from within a Rails project); move/checkout acts_as_solr into its vendor/plugins/, as usual
  16 +
  17 +- copy vendor/plugins/acts_as_solr/config/solr.yml to config/ (the Rails config folder)
  18 +
  19 +- rake solr:start RAILS_ENV=test
  20 +
  21 +- rake test:functional (Accepts the following arguments: DB=sqlite|mysql and MYSQL_USER=user)
  22 +
  23 +== Troubleshooting:
  24 +If for some reason the tests don't run and you get MySQL errors, make sure you edit the MYSQL_USER entry under
  25 +config/environment.rb. It's recommended to create or use a MySQL user with no password.
... ...
vendor/plugins/acts_as_solr_reloaded/VERSION 0 → 100644
... ... @@ -0,0 +1 @@
  1 +1.6.0
0 2 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/acts_as_solr_reloaded.gemspec 0 → 100644
... ... @@ -0,0 +1,205 @@
  1 +# Generated by jeweler
  2 +# DO NOT EDIT THIS FILE DIRECTLY
  3 +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
  4 +# -*- encoding: utf-8 -*-
  5 +
  6 +Gem::Specification.new do |s|
  7 + s.name = %q{acts_as_solr_reloaded}
  8 + s.version = "1.6.0"
  9 +
  10 + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
  11 + s.authors = ["Diego Carrion"]
  12 + s.date = %q{2011-03-20}
  13 + s.description = %q{This gem adds full text search capabilities and many other nifty features from Apache Solr to any Rails model.}
  14 + s.email = %q{dc.rec1@gmail.com}
  15 + s.extra_rdoc_files = [
  16 + "LICENSE",
  17 + "README.markdown",
  18 + "README.rdoc"
  19 + ]
  20 + s.files = [
  21 + "LICENSE",
  22 + "README.markdown",
  23 + "README.rdoc",
  24 + "Rakefile",
  25 + "TESTING_THE_PLUGIN",
  26 + "VERSION",
  27 + "config/solr.yml",
  28 + "config/solr_environment.rb",
  29 + "generators/dynamic_attributes_migration/dynamic_attributes_migration_generator.rb",
  30 + "generators/dynamic_attributes_migration/templates/migration.rb",
  31 + "generators/local_migration/local_migration_generator.rb",
  32 + "generators/local_migration/templates/migration.rb",
  33 + "lib/acts_as_solr.rb",
  34 + "lib/acts_as_solr/acts_methods.rb",
  35 + "lib/acts_as_solr/class_methods.rb",
  36 + "lib/acts_as_solr/common_methods.rb",
  37 + "lib/acts_as_solr/deprecation.rb",
  38 + "lib/acts_as_solr/dynamic_attribute.rb",
  39 + "lib/acts_as_solr/instance_methods.rb",
  40 + "lib/acts_as_solr/lazy_document.rb",
  41 + "lib/acts_as_solr/local.rb",
  42 + "lib/acts_as_solr/mongo_mapper.rb",
  43 + "lib/acts_as_solr/parser_methods.rb",
  44 + "lib/acts_as_solr/search_results.rb",
  45 + "lib/acts_as_solr/solr_fixtures.rb",
  46 + "lib/acts_as_solr/tasks.rb",
  47 + "lib/acts_as_solr_reloaded.rb",
  48 + "lib/solr.rb",
  49 + "lib/solr/connection.rb",
  50 + "lib/solr/document.rb",
  51 + "lib/solr/exception.rb",
  52 + "lib/solr/field.rb",
  53 + "lib/solr/importer.rb",
  54 + "lib/solr/importer/array_mapper.rb",
  55 + "lib/solr/importer/delimited_file_source.rb",
  56 + "lib/solr/importer/hpricot_mapper.rb",
  57 + "lib/solr/importer/mapper.rb",
  58 + "lib/solr/importer/solr_source.rb",
  59 + "lib/solr/importer/xpath_mapper.rb",
  60 + "lib/solr/indexer.rb",
  61 + "lib/solr/request.rb",
  62 + "lib/solr/request/add_document.rb",
  63 + "lib/solr/request/base.rb",
  64 + "lib/solr/request/commit.rb",
  65 + "lib/solr/request/delete.rb",
  66 + "lib/solr/request/dismax.rb",
  67 + "lib/solr/request/index_info.rb",
  68 + "lib/solr/request/modify_document.rb",
  69 + "lib/solr/request/optimize.rb",
  70 + "lib/solr/request/ping.rb",
  71 + "lib/solr/request/select.rb",
  72 + "lib/solr/request/spellcheck.rb",
  73 + "lib/solr/request/standard.rb",
  74 + "lib/solr/request/update.rb",
  75 + "lib/solr/response.rb",
  76 + "lib/solr/response/add_document.rb",
  77 + "lib/solr/response/base.rb",
  78 + "lib/solr/response/commit.rb",
  79 + "lib/solr/response/delete.rb",
  80 + "lib/solr/response/dismax.rb",
  81 + "lib/solr/response/index_info.rb",
  82 + "lib/solr/response/modify_document.rb",
  83 + "lib/solr/response/optimize.rb",
  84 + "lib/solr/response/ping.rb",
  85 + "lib/solr/response/ruby.rb",
  86 + "lib/solr/response/select.rb",
  87 + "lib/solr/response/spellcheck.rb",
  88 + "lib/solr/response/standard.rb",
  89 + "lib/solr/response/xml.rb",
  90 + "lib/solr/solrtasks.rb",
  91 + "lib/solr/util.rb",
  92 + "lib/solr/xml.rb",
  93 + "lib/tasks/database.rake",
  94 + "lib/tasks/solr.rake",
  95 + "lib/tasks/test.rake",
  96 + "test/config/solr.yml",
  97 + "test/db/connections/mysql/connection.rb",
  98 + "test/db/connections/sqlite/connection.rb",
  99 + "test/db/migrate/001_create_books.rb",
  100 + "test/db/migrate/002_create_movies.rb",
  101 + "test/db/migrate/003_create_categories.rb",
  102 + "test/db/migrate/004_create_electronics.rb",
  103 + "test/db/migrate/005_create_authors.rb",
  104 + "test/db/migrate/006_create_postings.rb",
  105 + "test/db/migrate/007_create_posts.rb",
  106 + "test/db/migrate/008_create_gadgets.rb",
  107 + "test/db/migrate/009_create_dynamic_attributes.rb",
  108 + "test/db/migrate/010_create_advertises.rb",
  109 + "test/db/migrate/011_create_locals.rb",
  110 + "test/db/test.db",
  111 + "test/fixtures/advertises.yml",
  112 + "test/fixtures/authors.yml",
  113 + "test/fixtures/books.yml",
  114 + "test/fixtures/categories.yml",
  115 + "test/fixtures/db_definitions/mysql.sql",
  116 + "test/fixtures/dynamic_attributes.yml",
  117 + "test/fixtures/electronics.yml",
  118 + "test/fixtures/locals.yml",
  119 + "test/fixtures/movies.yml",
  120 + "test/fixtures/postings.yml",
  121 + "test/functional/acts_as_solr_test.rb",
  122 + "test/functional/association_indexing_test.rb",
  123 + "test/functional/faceted_search_test.rb",
  124 + "test/functional/multi_solr_search_test.rb",
  125 + "test/models/advertise.rb",
  126 + "test/models/author.rb",
  127 + "test/models/book.rb",
  128 + "test/models/category.rb",
  129 + "test/models/document.rb",
  130 + "test/models/dynamic_attribute.rb",
  131 + "test/models/electronic.rb",
  132 + "test/models/gadget.rb",
  133 + "test/models/local.rb",
  134 + "test/models/movie.rb",
  135 + "test/models/novel.rb",
  136 + "test/models/post.rb",
  137 + "test/models/posting.rb",
  138 + "test/test_helper.rb",
  139 + "test/unit/acts_methods_shoulda.rb",
  140 + "test/unit/class_methods_shoulda.rb",
  141 + "test/unit/common_methods_shoulda.rb",
  142 + "test/unit/instance_methods_shoulda.rb",
  143 + "test/unit/lazy_document_shoulda.rb",
  144 + "test/unit/parser_instance.rb",
  145 + "test/unit/parser_methods_shoulda.rb",
  146 + "test/unit/solr_instance.rb",
  147 + "test/unit/test_helper.rb"
  148 + ]
  149 + s.homepage = %q{http://github.com/dcrec1/acts_as_solr_reloaded}
  150 + s.require_paths = ["lib"]
  151 + s.rubygems_version = %q{1.5.0}
  152 + s.summary = %q{This gem adds full text search capabilities and many other nifty features from Apache Solr to any Rails model.}
  153 + s.test_files = [
  154 + "test/db/connections/mysql/connection.rb",
  155 + "test/db/connections/sqlite/connection.rb",
  156 + "test/db/migrate/001_create_books.rb",
  157 + "test/db/migrate/002_create_movies.rb",
  158 + "test/db/migrate/003_create_categories.rb",
  159 + "test/db/migrate/004_create_electronics.rb",
  160 + "test/db/migrate/005_create_authors.rb",
  161 + "test/db/migrate/006_create_postings.rb",
  162 + "test/db/migrate/007_create_posts.rb",
  163 + "test/db/migrate/008_create_gadgets.rb",
  164 + "test/db/migrate/009_create_dynamic_attributes.rb",
  165 + "test/db/migrate/010_create_advertises.rb",
  166 + "test/db/migrate/011_create_locals.rb",
  167 + "test/functional/acts_as_solr_test.rb",
  168 + "test/functional/association_indexing_test.rb",
  169 + "test/functional/faceted_search_test.rb",
  170 + "test/functional/multi_solr_search_test.rb",
  171 + "test/models/advertise.rb",
  172 + "test/models/author.rb",
  173 + "test/models/book.rb",
  174 + "test/models/category.rb",
  175 + "test/models/document.rb",
  176 + "test/models/dynamic_attribute.rb",
  177 + "test/models/electronic.rb",
  178 + "test/models/gadget.rb",
  179 + "test/models/local.rb",
  180 + "test/models/movie.rb",
  181 + "test/models/novel.rb",
  182 + "test/models/post.rb",
  183 + "test/models/posting.rb",
  184 + "test/test_helper.rb",
  185 + "test/unit/acts_methods_shoulda.rb",
  186 + "test/unit/class_methods_shoulda.rb",
  187 + "test/unit/common_methods_shoulda.rb",
  188 + "test/unit/instance_methods_shoulda.rb",
  189 + "test/unit/lazy_document_shoulda.rb",
  190 + "test/unit/parser_instance.rb",
  191 + "test/unit/parser_methods_shoulda.rb",
  192 + "test/unit/solr_instance.rb",
  193 + "test/unit/test_helper.rb"
  194 + ]
  195 +
  196 + if s.respond_to? :specification_version then
  197 + s.specification_version = 3
  198 +
  199 + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
  200 + else
  201 + end
  202 + else
  203 + end
  204 +end
  205 +
... ...
vendor/plugins/acts_as_solr_reloaded/config/solr.yml 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +# Config file for the acts_as_solr_reloaded plugin.
  2 +#
  3 +# If you change the host or port number here, make sure you update
  4 +# them in your Solr config file
  5 +
  6 +development:
  7 + url: http://127.0.0.1:8982/solr
  8 +
  9 +test:
  10 + url: http://127.0.0.1:8981/solr
  11 +
  12 +production:
  13 + url: http://127.0.0.1:8983/solr
  14 + jvm_options: -server -d64 -Xmx1024M -Xms64M
0 15 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/config/solr_environment.rb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +ENV['RAILS_ENV'] = (ENV['RAILS_ENV'] || 'development').dup
  2 +require "uri"
  3 +require "fileutils"
  4 +require "yaml"
  5 +dir = File.dirname(__FILE__)
  6 +SOLR_PATH = File.expand_path("#{dir}/../solr") unless defined? SOLR_PATH
  7 +
  8 +# RAILS_ROOT isn't defined yet, so figure it out.
  9 +unless defined? RAILS_ROOT
  10 + RAILS_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../test")
  11 +end
  12 +unless defined? SOLR_LOGS_PATH
  13 + SOLR_LOGS_PATH = ENV["SOLR_LOGS_PATH"] || "#{RAILS_ROOT}/log"
  14 +end
  15 +unless defined? SOLR_PIDS_PATH
  16 + SOLR_PIDS_PATH = ENV["SOLR_PIDS_PATH"] || "#{RAILS_ROOT}/tmp/pids"
  17 +end
  18 +unless defined? SOLR_DATA_PATH
  19 + SOLR_DATA_PATH = ENV["SOLR_DATA_PATH"] || "#{RAILS_ROOT}/solr/#{ENV['RAILS_ENV']}"
  20 +end
  21 +unless defined? SOLR_CONFIG_PATH
  22 + SOLR_CONFIG_PATH = ENV["SOLR_CONFIG_PATH"] || "#{SOLR_PATH}/solr"
  23 +end
  24 +unless defined? SOLR_PID_FILE
  25 + SOLR_PID_FILE="#{SOLR_PIDS_PATH}/solr.#{ENV['RAILS_ENV']}.pid"
  26 +end
  27 +
  28 +unless defined? SOLR_PORT
  29 + config = YAML::load_file(RAILS_ROOT+'/config/solr.yml')
  30 + raise("No solr environment defined for RAILS_ENV the #{ENV['RAILS_ENV'].inspect}") unless config[ENV['RAILS_ENV']]
  31 +
  32 + SOLR_HOST = ENV['HOST'] || URI.parse(config[ENV['RAILS_ENV']]['url']).host
  33 + SOLR_PORT = ENV['PORT'] || URI.parse(config[ENV['RAILS_ENV']]['url']).port
  34 +end
  35 +
  36 +SOLR_JVM_OPTIONS = config[ENV['RAILS_ENV']]['jvm_options'] unless defined? SOLR_JVM_OPTIONS
  37 +
  38 +if ENV["ACTS_AS_SOLR_TEST"]
  39 + require "activerecord"
  40 + DB = (ENV['DB'] ? ENV['DB'] : 'sqlite') unless defined?(DB)
  41 + MYSQL_USER = (ENV['MYSQL_USER'].nil? ? 'root' : ENV['MYSQL_USER']) unless defined? MYSQL_USER
  42 + require File.join(File.dirname(File.expand_path(__FILE__)), '..', 'test', 'db', 'connections', DB, 'connection.rb')
  43 +end
... ...
vendor/plugins/acts_as_solr_reloaded/generators/dynamic_attributes_migration/dynamic_attributes_migration_generator.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +class DynamicAttributesMigrationGenerator < Rails::Generator::Base
  2 + def manifest
  3 + record do |m|
  4 + m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => "dynamic_attributes_migration"
  5 + end
  6 + end
  7 +end
0 8 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/generators/dynamic_attributes_migration/templates/migration.rb 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +class DynamicAttributesMigration < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :dynamic_attributes do |t|
  4 + t.integer :dynamicable_id
  5 + t.string :dynamicable_type
  6 + t.string :name
  7 + t.text :value
  8 + t.timestamps
  9 + end
  10 + add_index :dynamic_attributes, [:dynamicable_id, :dynamicable_type, :name], :unique => true, :name => 'da_pk'
  11 + end
  12 +
  13 + def self.down
  14 + remove_index 'da_pk'
  15 + drop_table :dynamic_attributes
  16 + end
  17 +end
... ...
vendor/plugins/acts_as_solr_reloaded/generators/local_migration/local_migration_generator.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +class LocalMigrationGenerator < Rails::Generator::Base
  2 + def manifest
  3 + record do |m|
  4 + m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => "local_migration"
  5 + end
  6 + end
  7 +end
0 8 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/generators/local_migration/templates/migration.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class LocalMigration < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :locals do |t|
  4 + t.integer :localizable_id
  5 + t.string :localizable_type
  6 + t.string :latitude
  7 + t.string :longitude
  8 +
  9 + t.timestamps
  10 + end
  11 + end
  12 +
  13 + def self.down
  14 + drop_table :locals
  15 + end
  16 +end
0 17 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/init.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require 'acts_as_solr'
0 2 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/install.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +require 'fileutils'
  2 +
  3 +src = File.join(File.dirname(__FILE__), 'config', 'solr.yml')
  4 +target = File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'solr.yml')
  5 +FileUtils.cp src, target
0 6 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/lib/acts_as_solr.rb 0 → 100644
... ... @@ -0,0 +1,47 @@
  1 +require 'active_record'
  2 +require 'rexml/document'
  3 +require 'net/http'
  4 +require 'yaml'
  5 +require 'time'
  6 +require 'erb'
  7 +require 'rexml/xpath'
  8 +
  9 +require File.dirname(__FILE__) + '/solr'
  10 +require File.dirname(__FILE__) + '/acts_as_solr/acts_methods'
  11 +require File.dirname(__FILE__) + '/acts_as_solr/common_methods'
  12 +require File.dirname(__FILE__) + '/acts_as_solr/parser_methods'
  13 +require File.dirname(__FILE__) + '/acts_as_solr/class_methods'
  14 +require File.dirname(__FILE__) + '/acts_as_solr/dynamic_attribute'
  15 +require File.dirname(__FILE__) + '/acts_as_solr/local'
  16 +require File.dirname(__FILE__) + '/acts_as_solr/instance_methods'
  17 +require File.dirname(__FILE__) + '/acts_as_solr/common_methods'
  18 +require File.dirname(__FILE__) + '/acts_as_solr/deprecation'
  19 +require File.dirname(__FILE__) + '/acts_as_solr/search_results'
  20 +require File.dirname(__FILE__) + '/acts_as_solr/lazy_document'
  21 +require File.dirname(__FILE__) + '/acts_as_solr/mongo_mapper'
  22 +
  23 +module ActsAsSolr
  24 + class Post
  25 + class << self
  26 + def config
  27 + @config ||= YAML::load_file("#{Rails.root}/config/solr.yml")[Rails.env]
  28 + end
  29 +
  30 + def credentials
  31 + @credentials ||= {:username => config['username'], :password => config['password']}
  32 + end
  33 +
  34 + def url(core)
  35 + core.nil? ? config['url'] : "#{config['url']}/#{core}"
  36 + end
  37 +
  38 + def execute(request, core = nil)
  39 + connection = Solr::Connection.new(url(core), credentials)
  40 + connection.send request
  41 + end
  42 + end
  43 + end
  44 +end
  45 +
  46 +# reopen ActiveRecord and include the acts_as_solr method
  47 +ActiveRecord::Base.extend ActsAsSolr::ActsMethods
... ...
vendor/plugins/acts_as_solr_reloaded/lib/acts_as_solr/acts_methods.rb 0 → 100644
... ... @@ -0,0 +1,373 @@
  1 +module ActsAsSolr #:nodoc:
  2 +
  3 + module ActsMethods
  4 +
  5 + # declares a class as solr-searchable
  6 + #
  7 + # ==== options:
  8 + # fields:: This option can be used to specify only the fields you'd
  9 + # like to index. If not given, all the attributes from the
  10 + # class will be indexed. You can also use this option to
  11 + # include methods that should be indexed as fields
  12 + #
  13 + # class Movie < ActiveRecord::Base
  14 + # acts_as_solr :fields => [:name, :description, :current_time]
  15 + # def current_time
  16 + # Time.now.to_s
  17 + # end
  18 + # end
  19 + #
  20 + # Each field passed can also be a hash with the value being a field type
  21 + #
  22 + # class Electronic < ActiveRecord::Base
  23 + # acts_as_solr :fields => [{:price => :range_float}]
  24 + # def current_time
  25 + # Time.now
  26 + # end
  27 + # end
  28 + #
  29 + # The field types accepted are:
  30 + #
  31 + # :float:: Index the field value as a float (ie.: 12.87)
  32 + # :integer:: Index the field value as an integer (ie.: 31)
  33 + # :boolean:: Index the field value as a boolean (ie.: true/false)
  34 + # :date:: Index the field value as a date (ie.: Wed Nov 15 23:13:03 PST 2006)
  35 + # :string:: Index the field value as a text string, not applying the same indexing
  36 + # filters as a regular text field
  37 + # :range_integer:: Index the field value for integer range queries (ie.:[5 TO 20])
  38 + # :range_float:: Index the field value for float range queries (ie.:[14.56 TO 19.99])
  39 + #
  40 + # Setting the field type preserves its original type when indexed
  41 + #
  42 + # The field may also be passed with a hash value containing options
  43 + #
  44 + # class Author < ActiveRecord::Base
  45 + # acts_as_solr :fields => [{:full_name => {:type => :text, :as => :name}}]
  46 + # def full_name
  47 + # self.first_name + ' ' + self.last_name
  48 + # end
  49 + # end
  50 + #
  51 + # The options accepted are:
  52 + #
  53 + # :type:: Index the field using the specified type
  54 + # :as:: Index the field using the specified field name
  55 + #
  56 + # additional_fields:: This option takes fields to be include in the index
  57 + # in addition to those derived from the database. You
  58 + # can also use this option to include custom fields
  59 + # derived from methods you define. This option will be
  60 + # ignored if the :fields option is given. It also accepts
  61 + # the same field types as the option above
  62 + #
  63 + # class Movie < ActiveRecord::Base
  64 + # acts_as_solr :additional_fields => [:current_time]
  65 + # def current_time
  66 + # Time.now.to_s
  67 + # end
  68 + # end
  69 + #
  70 + # exclude_fields:: This option taks an array of fields that should be ignored from indexing:
  71 + #
  72 + # class User < ActiveRecord::Base
  73 + # acts_as_solr :exclude_fields => [:password, :login, :credit_card_number]
  74 + # end
  75 + #
  76 + # include:: This option can be used for association indexing, which
  77 + # means you can include any :has_one, :has_many, :belongs_to
  78 + # and :has_and_belongs_to_many association to be indexed:
  79 + #
  80 + # class Category < ActiveRecord::Base
  81 + # has_many :books
  82 + # acts_as_solr :include => [:books]
  83 + # end
  84 + #
  85 + # Each association may also be specified as a hash with an option hash as a value
  86 + #
  87 + # class Book < ActiveRecord::Base
  88 + # belongs_to :author
  89 + # has_many :distribution_companies
  90 + # has_many :copyright_dates
  91 + # has_many :media_types
  92 + # acts_as_solr(
  93 + # :fields => [:name, :description],
  94 + # :include => [
  95 + # {:author => {:using => :fullname, :as => :name}},
  96 + # {:media_types => {:using => lambda{|media| type_lookup(media.id)}}}
  97 + # {:distribution_companies => {:as => :distributor, :multivalued => true}},
  98 + # {:copyright_dates => {:as => :copyright, :type => :date}}
  99 + # ]
  100 + # ]
  101 + #
  102 + # The options accepted are:
  103 + #
  104 + # :type:: Index the associated objects using the specified type
  105 + # :as:: Index the associated objects using the specified field name
  106 + # :using:: Index the associated objects using the value returned by the specified method or proc. If a method
  107 + # symbol is supplied, it will be sent to each object to look up the value to index; if a proc is
  108 + # supplied, it will be called once for each object with the object as the only argument
  109 + # :multivalued:: Index the associated objects using one field for each object rather than joining them
  110 + # all into a single field
  111 + #
  112 + # facets:: This option can be used to specify the fields you'd like to
  113 + # index as facet fields
  114 + #
  115 + # class Electronic < ActiveRecord::Base
  116 + # acts_as_solr :facets => [:category, :manufacturer]
  117 + # end
  118 + #
  119 + # boost:: You can pass a boost (float) value that will be used to boost the document and/or a field. To specify a more
  120 + # boost for the document, you can either pass a block or a symbol. The block will be called with the record
  121 + # as an argument, a symbol will result in the according method being called:
  122 + #
  123 + # class Electronic < ActiveRecord::Base
  124 + # acts_as_solr :fields => [{:price => {:boost => 5.0}}], :boost => 10.0
  125 + # end
  126 + #
  127 + # class Electronic < ActiveRecord::Base
  128 + # acts_as_solr :fields => [{:price => {:boost => 5.0}}], :boost => proc {|record| record.id + 120*37}
  129 + # end
  130 + #
  131 + # class Electronic < ActiveRecord::Base
  132 + # acts_as_solr :fields => [{:price => {:boost => :price_rating}}], :boost => 10.0
  133 + # end
  134 + #
  135 + # if:: Only indexes the record if the condition evaluated is true. The argument has to be
  136 + # either a symbol, string (to be eval'ed), proc/method, or class implementing a static
  137 + # validation method. It behaves the same way as ActiveRecord's :if option.
  138 + #
  139 + # class Electronic < ActiveRecord::Base
  140 + # acts_as_solr :if => proc{|record| record.is_active?}
  141 + # end
  142 + #
  143 + # offline:: Assumes that your using an outside mechanism to explicitly trigger indexing records, e.g. you only
  144 + # want to update your index through some asynchronous mechanism. Will accept either a boolean or a block
  145 + # that will be evaluated before actually contacting the index for saving or destroying a document. Defaults
  146 + # to false. It doesn't refer to the mechanism of an offline index in general, but just to get a centralized point
  147 + # where you can control indexing. Note: This is only enabled for saving records. acts_as_solr doesn't always like
  148 + # it, if you have a different number of results coming from the database and the index. This might be rectified in
  149 + # another patch to support lazy loading.
  150 + #
  151 + # class Electronic < ActiveRecord::Base
  152 + # acts_as_solr :offline => proc {|record| record.automatic_indexing_disabled?}
  153 + # end
  154 + #
  155 + # auto_commit:: The commit command will be sent to Solr only if its value is set to true:
  156 + #
  157 + # class Author < ActiveRecord::Base
  158 + # acts_as_solr :auto_commit => false
  159 + # end
  160 + #
  161 + # dynamic_attributes: Default false. When true, requires a has_many relationship to a DynamicAttribute
  162 + # (:name, :value) model. Then, all dynamic attributes will be mapped as normal attributes
  163 + # in Solr, so you can filter like this: Model.find_by_solr "#{dynamic_attribute.name}:Lorem"
  164 + # taggable: Default false. When true, indexes tags with field name tag. Tags are taken from taggings.tag
  165 + # spatial: Default false. When true, indexes model.local.latitude and model.local.longitude as coordinates.
  166 + def acts_as_solr(options={}, solr_options={}, &deferred_solr_configuration)
  167 +
  168 + extend ClassMethods
  169 + include InstanceMethods
  170 + include CommonMethods
  171 + include ParserMethods
  172 +
  173 + define_solr_configuration_methods
  174 +
  175 + acts_as_taggable_on :tags if options[:taggable]
  176 + has_many :dynamic_attributes, :as => "dynamicable" if options[:dynamic_attributes]
  177 + has_one :local, :as => "localizable" if options[:spatial]
  178 +
  179 + after_save :solr_save
  180 + after_destroy :solr_destroy
  181 +
  182 + if deferred_solr_configuration
  183 + self.deferred_solr_configuration = deferred_solr_configuration
  184 + else
  185 + process_acts_as_solr(options, solr_options)
  186 + end
  187 + end
  188 +
  189 + def process_acts_as_solr(options, solr_options)
  190 + process_solr_options(options, solr_options)
  191 + end
  192 +
  193 + def define_solr_configuration_methods
  194 + # I'd like to use cattr_accessor, but it does not support lazy loaders and delegation to the class in the instance methods.
  195 + # TODO: Reconcile with cattr_accessor, or a more appropriate method.
  196 + class_eval(<<-EOS, __FILE__, __LINE__)
  197 + @@configuration = nil unless defined?(@@configuration)
  198 + @@solr_configuration = nil unless defined?(@@solr_configuration)
  199 + @@deferred_solr_configuration = nil unless defined?(@@deferred_solr_configuration)
  200 +
  201 + def self.configuration
  202 + return @@configuration if @@configuration
  203 + process_deferred_solr_configuration
  204 + @@configuration
  205 + end
  206 + def configuration
  207 + self.class.configuration
  208 + end
  209 + def self.configuration=(value)
  210 + @@configuration = value
  211 + end
  212 + def configuration=(value)
  213 + self.class.configuration = value
  214 + end
  215 +
  216 + def self.solr_configuration
  217 + return @@solr_configuration if @@solr_configuration
  218 + process_deferred_solr_configuration
  219 + @@solr_configuration
  220 + end
  221 + def solr_configuration
  222 + self.class.solr_configuration
  223 + end
  224 + def self.solr_configuration=(value)
  225 + @@solr_configuration = value
  226 + end
  227 + def solr_configuration=(value)
  228 + self.class.solr_configuration = value
  229 + end
  230 +
  231 + def self.deferred_solr_configuration
  232 + return @@deferred_solr_configuration if @@deferred_solr_configuration
  233 + @@deferred_solr_configuration
  234 + end
  235 + def deferred_solr_configuration
  236 + self.class.deferred_solr_configuration
  237 + end
  238 + def self.deferred_solr_configuration=(value)
  239 + @@deferred_solr_configuration = value
  240 + end
  241 + def deferred_solr_configuration=(value)
  242 + self.class.deferred_solr_configuration = value
  243 + end
  244 + EOS
  245 + end
  246 +
  247 + def process_deferred_solr_configuration
  248 + return unless deferred_solr_configuration
  249 + options, solr_options = deferred_solr_configuration.call
  250 + self.deferred_solr_configuration = nil
  251 + self.process_solr_options(options, solr_options)
  252 + end
  253 +
  254 + def process_solr_options(options={}, solr_options={})
  255 + self.configuration = {
  256 + :fields => nil,
  257 + :additional_fields => nil,
  258 + :dynamic_attributes => false,
  259 + :exclude_fields => [],
  260 + :auto_commit => true,
  261 + :include => nil,
  262 + :facets => nil,
  263 + :boost => nil,
  264 + :if => "true",
  265 + :offline => false,
  266 + :spatial => false
  267 + }
  268 + self.solr_configuration = {
  269 + :type_field => "type_s",
  270 + :primary_key_field => "pk_s",
  271 + :default_boost => 1.0
  272 + }
  273 +
  274 + configuration.update(options) if options.is_a?(Hash)
  275 + solr_configuration.update(solr_options) if solr_options.is_a?(Hash)
  276 + Deprecation.validate_index(configuration)
  277 +
  278 + configuration[:solr_fields] = {}
  279 + configuration[:solr_includes] = {}
  280 +
  281 + if configuration[:fields].respond_to?(:each)
  282 + process_fields(configuration[:fields])
  283 + else
  284 + process_fields(self.new.attributes.keys.map { |k| k.to_sym })
  285 + process_fields(configuration[:additional_fields])
  286 + end
  287 +
  288 + if configuration[:include].respond_to?(:each)
  289 + process_includes(configuration[:include])
  290 + end
  291 + end
  292 +
  293 + private
  294 +
  295 + def get_field_value(field)
  296 + field_name, options = determine_field_name_and_options(field)
  297 + configuration[:solr_fields][field_name] = options
  298 +
  299 + define_method("#{field_name}_for_solr".to_sym) do
  300 + begin
  301 + value = self[field_name] || self.instance_variable_get("@#{field_name.to_s}".to_sym) || self.send(field_name.to_sym)
  302 + case options[:type]
  303 + # format dates properly; return nil for nil dates
  304 + when :date
  305 + value ? (value.respond_to?(:utc) ? value.utc : value).strftime("%Y-%m-%dT%H:%M:%SZ") : nil
  306 + else value
  307 + end
  308 + rescue
  309 + puts $!
  310 + logger.debug "There was a problem getting the value for the field '#{field_name}': #{$!}"
  311 + value = ''
  312 + end
  313 + end
  314 + end
  315 +
  316 + def process_fields(raw_field)
  317 + if raw_field.respond_to?(:each)
  318 + raw_field.each do |field|
  319 + next if configuration[:exclude_fields].include?(field)
  320 + get_field_value(field)
  321 + end
  322 + end
  323 + end
  324 +
  325 + def process_includes(includes)
  326 + if includes.respond_to?(:each)
  327 + includes.each do |assoc|
  328 + field_name, options = determine_field_name_and_options(assoc)
  329 + configuration[:solr_includes][field_name] = options
  330 + end
  331 + end
  332 + end
  333 +
  334 + def determine_field_name_and_options(field)
  335 + if field.is_a?(Hash)
  336 + name = field.keys.first
  337 + options = field.values.first
  338 + if options.is_a?(Hash)
  339 + [name, {:type => type_for_field(field)}.merge(options)]
  340 + else
  341 + [name, {:type => options}]
  342 + end
  343 + else
  344 + [field, {:type => type_for_field(field)}]
  345 + end
  346 + end
  347 +
  348 + def type_for_field(field)
  349 + if configuration[:facets] && configuration[:facets].include?(field)
  350 + :facet
  351 + elsif column = columns_hash[field.to_s]
  352 + column_type = format_column_type(column.type)
  353 +
  354 + case column_type
  355 + when :string then :text
  356 + when :datetime then :date
  357 + when :time then :date
  358 + else column_type
  359 + end
  360 + else
  361 + :text
  362 + end
  363 + end
  364 +
  365 + def format_column_type(type)
  366 + if type.class.eql? Symbol
  367 + type
  368 + else
  369 + type.to_s.eql?("ObjectId") ? :string : type.to_s.downcase.to_sym
  370 + end
  371 + end
  372 + end
  373 +end
0 374 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/lib/acts_as_solr/class_methods.rb 0 → 100644
... ... @@ -0,0 +1,242 @@
  1 +module ActsAsSolr #:nodoc:
  2 +
  3 + module ClassMethods
  4 + include CommonMethods
  5 + include ParserMethods
  6 +
  7 + # Finds instances of a model. Terms are ANDed by default, can be overwritten
  8 + # by using OR between terms
  9 + #
  10 + # Here's a sample (untested) code for your controller:
  11 + #
  12 + # def search
  13 + # results = Book.find_by_solr params[:query]
  14 + # end
  15 + #
  16 + # You can also search for specific fields by searching for 'field:value'
  17 + #
  18 + # ====options:
  19 + # offset:: - The first document to be retrieved (offset)
  20 + # page:: - The page to be retrieved
  21 + # limit:: - The number of rows per page
  22 + # per_page:: - Alias for limit
  23 + # order:: - Orders (sort by) the result set using a given criteria:
  24 + #
  25 + # Book.find_by_solr 'ruby', :order => 'description asc'
  26 + #
  27 + # field_types:: This option is deprecated and will be obsolete by version 1.0.
  28 + # There's no need to specify the :field_types anymore when doing a
  29 + # search in a model that specifies a field type for a field. The field
  30 + # types are automatically traced back when they're included.
  31 + #
  32 + # class Electronic < ActiveRecord::Base
  33 + # acts_as_solr :fields => [{:price => :range_float}]
  34 + # end
  35 + #
  36 + # facets:: This option argument accepts the following arguments:
  37 + # fields:: The fields to be included in the faceted search (Solr's facet.field)
  38 + # query:: The queries to be included in the faceted search (Solr's facet.query)
  39 + # zeros:: Display facets with count of zero. (true|false)
  40 + # sort:: Sorts the faceted resuls by highest to lowest count. (true|false)
  41 + # browse:: This is where the 'drill-down' of the facets work. Accepts an array of
  42 + # fields in the format "facet_field:term"
  43 + # mincount:: Replacement for zeros (it has been deprecated in Solr). Specifies the
  44 + # minimum count necessary for a facet field to be returned. (Solr's
  45 + # facet.mincount) Overrides :zeros if it is specified. Default is 0.
  46 + #
  47 + # dates:: Run date faceted queries using the following arguments:
  48 + # fields:: The fields to be included in the faceted date search (Solr's facet.date).
  49 + # It may be either a String/Symbol or Hash. If it's a hash the options are the
  50 + # same as date_facets minus the fields option (i.e., :start:, :end, :gap, :other,
  51 + # :between). These options if provided will override the base options.
  52 + # (Solr's f.<field_name>.date.<key>=<value>).
  53 + # start:: The lower bound for the first date range for all Date Faceting. Required if
  54 + # :fields is present
  55 + # end:: The upper bound for the last date range for all Date Faceting. Required if
  56 + # :fields is prsent
  57 + # gap:: The size of each date range expressed as an interval to be added to the lower
  58 + # bound using the DateMathParser syntax. Required if :fields is prsent
  59 + # hardend:: A Boolean parameter instructing Solr what do do in the event that
  60 + # facet.date.gap does not divide evenly between facet.date.start and facet.date.end.
  61 + # other:: This param indicates that in addition to the counts for each date range
  62 + # constraint between facet.date.start and facet.date.end, other counds should be
  63 + # calculated. May specify more then one in an Array. The possible options are:
  64 + # before:: - all records with lower bound less than start
  65 + # after:: - all records with upper bound greater than end
  66 + # between:: - all records with field values between start and end
  67 + # none:: - compute no other bounds (useful in per field assignment)
  68 + # all:: - shortcut for before, after, and between
  69 + # filter:: Similar to :query option provided by :facets, in that accepts an array of
  70 + # of date queries to limit results. Can not be used as a part of a :field hash.
  71 + # This is the only option that can be used if :fields is not present.
  72 + #
  73 + # Example:
  74 + #
  75 + # Electronic.find_by_solr "memory", :facets => {:zeros => false, :sort => true,
  76 + # :query => ["price:[* TO 200]",
  77 + # "price:[200 TO 500]",
  78 + # "price:[500 TO *]"],
  79 + # :fields => [:category, :manufacturer],
  80 + # :browse => ["category:Memory","manufacturer:Someone"]}
  81 + #
  82 + #
  83 + # Examples of date faceting:
  84 + #
  85 + # basic:
  86 + # Electronic.find_by_solr "memory", :facets => {:dates => {:fields => [:updated_at, :created_at],
  87 + # :start => 'NOW-10YEARS/DAY', :end => 'NOW/DAY', :gap => '+2YEARS', :other => :before}}
  88 + #
  89 + # advanced:
  90 + # Electronic.find_by_solr "memory", :facets => {:dates => {:fields => [:updated_at,
  91 + # {:created_at => {:start => 'NOW-20YEARS/DAY', :end => 'NOW-10YEARS/DAY', :other => [:before, :after]}
  92 + # }], :start => 'NOW-10YEARS/DAY', :end => 'NOW/DAY', :other => :before, :filter =>
  93 + # ["created_at:[NOW-10YEARS/DAY TO NOW/DAY]", "updated_at:[NOW-1YEAR/DAY TO NOW/DAY]"]}}
  94 + #
  95 + # filter only:
  96 + # Electronic.find_by_solr "memory", :facets => {:dates => {:filter => "updated_at:[NOW-1YEAR/DAY TO NOW/DAY]"}}
  97 + #
  98 + #
  99 + #
  100 + # scores:: If set to true this will return the score as a 'solr_score' attribute
  101 + # for each one of the instances found. Does not currently work with find_id_by_solr
  102 + #
  103 + # books = Book.find_by_solr 'ruby OR splinter', :scores => true
  104 + # books.records.first.solr_score
  105 + # => 1.21321397
  106 + # books.records.last.solr_score
  107 + # => 0.12321548
  108 + #
  109 + # lazy:: If set to true the search will return objects that will touch the database when you ask for one
  110 + # of their attributes for the first time. Useful when you're using fragment caching based solely on
  111 + # types and ids.
  112 + #
  113 + # relevance:: Sets fields relevance
  114 + #
  115 + # Book.find_by_solr "zidane", :relevance => {:title => 5, :author => 2}
  116 + #
  117 + def find_by_solr(query, options={})
  118 + data = parse_query(query, options)
  119 + return parse_results(data, options)
  120 + end
  121 + alias :search :find_by_solr
  122 +
  123 + # Finds instances of a model and returns an array with the ids:
  124 + # Book.find_id_by_solr "rails" => [1,4,7]
  125 + # The options accepted are the same as find_by_solr
  126 + #
  127 + def find_id_by_solr(query, options={})
  128 + data = parse_query(query, options)
  129 + return parse_results(data, {:format => :ids})
  130 + end
  131 +
  132 + # This method can be used to execute a search across multiple models:
  133 + # Book.multi_solr_search "Napoleon OR Tom", :models => [Movie]
  134 + #
  135 + # ====options:
  136 + # Accepts the same options as find_by_solr plus:
  137 + # models:: The additional models you'd like to include in the search
  138 + # results_format:: Specify the format of the results found
  139 + # :objects :: Will return an array with the results being objects (default). Example:
  140 + # Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :objects
  141 + # :ids :: Will return an array with the ids of each entry found. Example:
  142 + # Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :ids
  143 + # => [{"id" => "Movie:1"},{"id" => Book:1}]
  144 + # Where the value of each array is as Model:instance_id
  145 + # scores:: If set to true this will return the score as a 'solr_score' attribute
  146 + # for each one of the instances found. Does not currently work with find_id_by_solr
  147 + #
  148 + # books = Book.multi_solr_search 'ruby OR splinter', :scores => true
  149 + # books.records.first.solr_score
  150 + # => 1.21321397
  151 + # books.records.last.solr_score
  152 + # => 0.12321548
  153 + #
  154 + def multi_solr_search(query, options = {})
  155 + models = multi_model_suffix(options)
  156 + options.update(:results_format => :objects) unless options[:results_format]
  157 + data = parse_query(query, options, models)
  158 +
  159 + if data.nil? or data.total_hits == 0
  160 + return SearchResults.new(:docs => [], :total => 0)
  161 + end
  162 +
  163 + result = find_multi_search_objects(data, options)
  164 + if options[:scores] and options[:results_format] == :objects
  165 + add_scores(result, data)
  166 + end
  167 + SearchResults.new :docs => result, :total => data.total_hits
  168 + end
  169 +
  170 + def find_multi_search_objects(data, options)
  171 + result = []
  172 + if options[:results_format] == :objects
  173 + data.hits.each do |doc|
  174 + k = doc.fetch('id').first.to_s.split(':')
  175 + result << k[0].constantize.find_by_id(k[1])
  176 + end
  177 + elsif options[:results_format] == :ids
  178 + data.hits.each{|doc| result << {"id" => doc["id"].to_s}}
  179 + end
  180 + result
  181 + end
  182 +
  183 + def multi_model_suffix(options)
  184 + models = "AND (#{solr_configuration[:type_field]}:#{self.name}"
  185 + models << " OR " + options[:models].collect {|m| "#{solr_configuration[:type_field]}:" + m.to_s}.join(" OR ") if options[:models].is_a?(Array)
  186 + models << ")"
  187 + end
  188 +
  189 + # returns the total number of documents found in the query specified:
  190 + # Book.count_by_solr 'rails' => 3
  191 + #
  192 + def count_by_solr(query, options = {})
  193 + data = parse_query(query, options)
  194 + data.total_hits
  195 + end
  196 +
  197 + # It's used to rebuild the Solr index for a specific model.
  198 + # Book.rebuild_solr_index
  199 + #
  200 + # If batch_size is greater than 0, adds will be done in batches.
  201 + # NOTE: If using sqlserver, be sure to use a finder with an explicit order.
  202 + # Non-edge versions of rails do not handle pagination correctly for sqlserver
  203 + # without an order clause.
  204 + #
  205 + # If a finder block is given, it will be called to retrieve the items to index.
  206 + # This can be very useful for things such as updating based on conditions or
  207 + # using eager loading for indexed associations.
  208 + def rebuild_solr_index(batch_size=100, &finder)
  209 + finder ||= lambda { |ar, options| ar.find(:all, options.merge({:order => self.primary_key})) }
  210 + start_time = Time.now
  211 +
  212 + if batch_size > 0
  213 + items_processed = 0
  214 + limit = batch_size
  215 + offset = 0
  216 + begin
  217 + iteration_start = Time.now
  218 + items = finder.call(self, {:limit => limit, :offset => offset})
  219 + add_batch = items.collect { |content| content.to_solr_doc }
  220 +
  221 + if items.size > 0
  222 + solr_add add_batch
  223 + solr_commit
  224 + end
  225 +
  226 + items_processed += items.size
  227 + last_id = items.last.id if items.last
  228 + time_so_far = Time.now - start_time
  229 + iteration_time = Time.now - iteration_start
  230 + logger.info "#{Process.pid}: #{items_processed} items for #{self.name} have been batch added to index in #{'%.3f' % time_so_far}s at #{'%.3f' % (items_processed / time_so_far)} items/sec (#{'%.3f' % (items.size / iteration_time)} items/sec for the last batch). Last id: #{last_id}"
  231 + offset += items.size
  232 + end while items.nil? || items.size > 0
  233 + else
  234 + items = finder.call(self, {})
  235 + items.each { |content| content.solr_save }
  236 + items_processed = items.size
  237 + end
  238 + solr_optimize
  239 + logger.info items_processed > 0 ? "Index for #{self.name} has been rebuilt" : "Nothing to index for #{self.name}"
  240 + end
  241 + end
  242 +end
... ...
vendor/plugins/acts_as_solr_reloaded/lib/acts_as_solr/common_methods.rb 0 → 100644
... ... @@ -0,0 +1,87 @@
  1 +module ActsAsSolr #:nodoc:
  2 +
  3 + module CommonMethods
  4 +
  5 + # Converts field types into Solr types
  6 + def get_solr_field_type(field_type)
  7 + if field_type.is_a?(Symbol)
  8 + case field_type
  9 + when :float
  10 + return "f"
  11 + when :integer
  12 + return "i"
  13 + when :boolean
  14 + return "b"
  15 + when :string
  16 + return "s"
  17 + when :date
  18 + return "d"
  19 + when :range_float
  20 + return "rf"
  21 + when :range_integer
  22 + return "ri"
  23 + when :facet
  24 + return "facet"
  25 + when :text
  26 + return "t"
  27 + else
  28 + raise "Unknown field_type symbol: #{field_type}"
  29 + end
  30 + elsif field_type.is_a?(String)
  31 + return field_type
  32 + else
  33 + raise "Unknown field_type class: #{field_type.class}: #{field_type}"
  34 + end
  35 + end
  36 +
  37 + # Sets a default value when value being set is nil.
  38 + def set_value_if_nil(field_type)
  39 + case field_type
  40 + when "b", :boolean
  41 + return "false"
  42 + when "s", "t", "d", :date, :string, :text
  43 + return ""
  44 + when "f", "rf", :float, :range_float
  45 + return 0.00
  46 + when "i", "ri", :integer, :range_integer
  47 + return 0
  48 + else
  49 + return ""
  50 + end
  51 + end
  52 +
  53 + # Sends an add command to Solr
  54 + def solr_add(add_xml)
  55 + ActsAsSolr::Post.execute(Solr::Request::AddDocument.new(add_xml))
  56 + end
  57 +
  58 + # Sends the delete command to Solr
  59 + def solr_delete(solr_ids)
  60 + ActsAsSolr::Post.execute(Solr::Request::Delete.new(:id => solr_ids))
  61 + end
  62 +
  63 + # Sends the commit command to Solr
  64 + def solr_commit
  65 + ActsAsSolr::Post.execute(Solr::Request::Commit.new)
  66 + end
  67 +
  68 + # Optimizes the Solr index. Solr says:
  69 + #
  70 + # Optimizations can take nearly ten minutes to run.
  71 + # We are presuming optimizations should be run once following large
  72 + # batch-like updates to the collection and/or once a day.
  73 + #
  74 + # One of the solutions for this would be to create a cron job that
  75 + # runs every day at midnight and optmizes the index:
  76 + # 0 0 * * * /your_rails_dir/script/runner -e production "Model.solr_optimize"
  77 + #
  78 + def solr_optimize
  79 + ActsAsSolr::Post.execute(Solr::Request::Optimize.new)
  80 + end
  81 +
  82 + # Returns the id for the given instance
  83 + def record_id(object)
  84 + eval "object.#{object.class.primary_key}"
  85 + end
  86 + end
  87 +end
0 88 \ No newline at end of file
... ...
vendor/plugins/acts_as_solr_reloaded/lib/acts_as_solr/deprecation.rb 0 → 100644
... ... @@ -0,0 +1,61 @@
  1 +module ActsAsSolr #:nodoc:
  2 +
  3 + class Post
  4 + def initialize(body, mode = :search)
  5 + @body = body
  6 + @mode = mode
  7 + puts "The method ActsAsSolr::Post.new(body, mode).execute_post is depracated. " +
  8 + "Use ActsAsSolr::Post.execute(body, mode) instead!"
  9 + end
  10 +
  11 + def execute_post
  12 + ActsAsSolr::Post.execute(@body, @mode)
  13 + end
  14 + end
  15 +
  16 + module ClassMethods
  17 + def find_with_facet(query, options={})
  18 + Deprecation.plog "The method find_with_facet is deprecated. Use find_by_solr instead, passing the " +
  19 + "arguments the same way you used to do with find_with_facet."
  20 + find_by_solr(query, options)
  21 + end
  22 + end
  23 +
  24 + class Deprecation
  25 + # Validates the options passed during query
  26 + def self.validate_query options={}
  27 + if options[:field_types]
  28 + plog "The option :field_types for searching is deprecated. " +
  29 + "The field types are automatically traced back when you specify a field type in your model."
  30 + end
  31 + if options[:sort_by]
  32 + plog "The option :sort_by is deprecated, use :order instead!"
  33 + options[:order] ||= options[:sort_by]
  34 + end
  35 + if options[:start]
  36 + plog "The option :start is deprecated, use :offset instead!"
  37 + options[:offset] ||= options[:start]
  38 + end
  39 + if options[:rows]
  40 + plog "The option :rows is deprecated, use :limit instead!"
  41 + options[:limit] ||= options[:rows]
  42 + end
  43 + end
  44 +
  45 + # Validates the options passed during indexing
  46 + def self.validate_index options={}
  47 + if options[:background]
  48 + plog "The :background option is being deprecated. There are better and more efficient " +
  49 + "ways to handle delayed saving of your records."
  50 + end
  51 + end
  52 +
  53 + # This will print the text to stdout and log the text
  54 + # if rails logger is available
  55 + def self.plog text
  56 + puts text
  57 + RAILS_DEFAULT_LOGGER.warn text if defined? RAILS_DEFAULT_LOGGER
  58 + end
  59 + end
  60 +
  61 +end
... ...
vendor/plugins/acts_as_solr_reloaded/lib/acts_as_solr/dynamic_attribute.rb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +class DynamicAttribute < ActiveRecord::Base
  2 + belongs_to :dynamicable, :polymorphic => true
  3 +end
... ...