watson-acts_as_ferret 0.4.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. data/LICENSE +20 -0
  2. data/README +104 -0
  3. data/acts_as_ferret.gemspec +58 -0
  4. data/bin/aaf_install +29 -0
  5. data/config/ferret_server.yml +24 -0
  6. data/doc/README.win32 +23 -0
  7. data/doc/demo/README +154 -0
  8. data/doc/demo/README_DEMO +23 -0
  9. data/doc/demo/Rakefile +10 -0
  10. data/doc/demo/app/controllers/admin/backend_controller.rb +14 -0
  11. data/doc/demo/app/controllers/admin_area_controller.rb +4 -0
  12. data/doc/demo/app/controllers/application.rb +5 -0
  13. data/doc/demo/app/controllers/contents_controller.rb +49 -0
  14. data/doc/demo/app/controllers/searches_controller.rb +8 -0
  15. data/doc/demo/app/helpers/admin/backend_helper.rb +2 -0
  16. data/doc/demo/app/helpers/application_helper.rb +3 -0
  17. data/doc/demo/app/helpers/content_helper.rb +2 -0
  18. data/doc/demo/app/helpers/search_helper.rb +2 -0
  19. data/doc/demo/app/models/comment.rb +48 -0
  20. data/doc/demo/app/models/content.rb +12 -0
  21. data/doc/demo/app/models/content_base.rb +28 -0
  22. data/doc/demo/app/models/search.rb +19 -0
  23. data/doc/demo/app/models/shared_index1.rb +3 -0
  24. data/doc/demo/app/models/shared_index2.rb +3 -0
  25. data/doc/demo/app/models/special_content.rb +3 -0
  26. data/doc/demo/app/models/stats.rb +20 -0
  27. data/doc/demo/app/views/admin/backend/search.rhtml +18 -0
  28. data/doc/demo/app/views/contents/_form.rhtml +10 -0
  29. data/doc/demo/app/views/contents/edit.rhtml +9 -0
  30. data/doc/demo/app/views/contents/index.rhtml +24 -0
  31. data/doc/demo/app/views/contents/new.rhtml +8 -0
  32. data/doc/demo/app/views/contents/show.rhtml +8 -0
  33. data/doc/demo/app/views/layouts/application.html.erb +17 -0
  34. data/doc/demo/app/views/searches/_content.html.erb +2 -0
  35. data/doc/demo/app/views/searches/search.html.erb +20 -0
  36. data/doc/demo/config/boot.rb +109 -0
  37. data/doc/demo/config/database.yml +38 -0
  38. data/doc/demo/config/environment.rb +69 -0
  39. data/doc/demo/config/environments/development.rb +16 -0
  40. data/doc/demo/config/environments/production.rb +19 -0
  41. data/doc/demo/config/environments/test.rb +21 -0
  42. data/doc/demo/config/ferret_server.yml +18 -0
  43. data/doc/demo/config/lighttpd.conf +40 -0
  44. data/doc/demo/config/routes.rb +9 -0
  45. data/doc/demo/db/development_structure.sql +15 -0
  46. data/doc/demo/db/migrate/001_initial_migration.rb +18 -0
  47. data/doc/demo/db/migrate/002_add_type_to_contents.rb +9 -0
  48. data/doc/demo/db/migrate/003_create_shared_index1s.rb +11 -0
  49. data/doc/demo/db/migrate/004_create_shared_index2s.rb +11 -0
  50. data/doc/demo/db/migrate/005_special_field.rb +9 -0
  51. data/doc/demo/db/migrate/006_create_stats.rb +15 -0
  52. data/doc/demo/db/schema.sql +18 -0
  53. data/doc/demo/db/schema.sqlite +14 -0
  54. data/doc/demo/doc/README_FOR_APP +2 -0
  55. data/doc/demo/doc/howto.txt +70 -0
  56. data/doc/demo/public/404.html +8 -0
  57. data/doc/demo/public/500.html +8 -0
  58. data/doc/demo/public/dispatch.cgi +10 -0
  59. data/doc/demo/public/dispatch.fcgi +24 -0
  60. data/doc/demo/public/dispatch.rb +10 -0
  61. data/doc/demo/public/favicon.ico +0 -0
  62. data/doc/demo/public/images/rails.png +0 -0
  63. data/doc/demo/public/index.html +277 -0
  64. data/doc/demo/public/robots.txt +1 -0
  65. data/doc/demo/public/stylesheets/scaffold.css +74 -0
  66. data/doc/demo/script/about +3 -0
  67. data/doc/demo/script/breakpointer +3 -0
  68. data/doc/demo/script/console +3 -0
  69. data/doc/demo/script/destroy +3 -0
  70. data/doc/demo/script/ferret_server +10 -0
  71. data/doc/demo/script/generate +3 -0
  72. data/doc/demo/script/performance/benchmarker +3 -0
  73. data/doc/demo/script/performance/profiler +3 -0
  74. data/doc/demo/script/plugin +3 -0
  75. data/doc/demo/script/process/inspector +3 -0
  76. data/doc/demo/script/process/reaper +3 -0
  77. data/doc/demo/script/process/spawner +3 -0
  78. data/doc/demo/script/process/spinner +3 -0
  79. data/doc/demo/script/runner +3 -0
  80. data/doc/demo/script/server +3 -0
  81. data/doc/demo/test/fixtures/comments.yml +12 -0
  82. data/doc/demo/test/fixtures/contents.yml +13 -0
  83. data/doc/demo/test/fixtures/remote_contents.yml +9 -0
  84. data/doc/demo/test/fixtures/shared_index1s.yml +7 -0
  85. data/doc/demo/test/fixtures/shared_index2s.yml +7 -0
  86. data/doc/demo/test/functional/admin/backend_controller_test.rb +35 -0
  87. data/doc/demo/test/functional/contents_controller_test.rb +81 -0
  88. data/doc/demo/test/functional/searches_controller_test.rb +71 -0
  89. data/doc/demo/test/smoke/drb_smoke_test.rb +321 -0
  90. data/doc/demo/test/smoke/process_stats.rb +21 -0
  91. data/doc/demo/test/test_helper.rb +30 -0
  92. data/doc/demo/test/unit/comment_test.rb +217 -0
  93. data/doc/demo/test/unit/content_test.rb +705 -0
  94. data/doc/demo/test/unit/ferret_result_test.rb +24 -0
  95. data/doc/demo/test/unit/multi_index_test.rb +329 -0
  96. data/doc/demo/test/unit/remote_index_test.rb +23 -0
  97. data/doc/demo/test/unit/shared_index1_test.rb +108 -0
  98. data/doc/demo/test/unit/shared_index2_test.rb +13 -0
  99. data/doc/demo/test/unit/sort_test.rb +21 -0
  100. data/doc/demo/test/unit/special_content_test.rb +25 -0
  101. data/doc/demo/vendor/plugins/will_paginate/LICENSE +18 -0
  102. data/doc/demo/vendor/plugins/will_paginate/README +108 -0
  103. data/doc/demo/vendor/plugins/will_paginate/Rakefile +23 -0
  104. data/doc/demo/vendor/plugins/will_paginate/init.rb +21 -0
  105. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/collection.rb +45 -0
  106. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb +44 -0
  107. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/finder.rb +159 -0
  108. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb +95 -0
  109. data/doc/demo/vendor/plugins/will_paginate/test/array_pagination_test.rb +23 -0
  110. data/doc/demo/vendor/plugins/will_paginate/test/boot.rb +27 -0
  111. data/doc/demo/vendor/plugins/will_paginate/test/console +10 -0
  112. data/doc/demo/vendor/plugins/will_paginate/test/finder_test.rb +219 -0
  113. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/admin.rb +3 -0
  114. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/companies.yml +24 -0
  115. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/company.rb +23 -0
  116. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developer.rb +11 -0
  117. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml +13 -0
  118. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/project.rb +4 -0
  119. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/projects.yml +7 -0
  120. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/replies.yml +20 -0
  121. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/reply.rb +5 -0
  122. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/schema.sql +44 -0
  123. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topic.rb +19 -0
  124. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topics.yml +30 -0
  125. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/user.rb +2 -0
  126. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/users.yml +35 -0
  127. data/doc/demo/vendor/plugins/will_paginate/test/helper.rb +42 -0
  128. data/doc/demo/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb +64 -0
  129. data/doc/demo/vendor/plugins/will_paginate/test/lib/load_fixtures.rb +10 -0
  130. data/doc/demo/vendor/plugins/will_paginate/test/pagination_test.rb +136 -0
  131. data/doc/monit-example +22 -0
  132. data/init.rb +24 -0
  133. data/install.rb +18 -0
  134. data/lib/act_methods.rb +147 -0
  135. data/lib/acts_as_ferret.rb +593 -0
  136. data/lib/ar_mysql_auto_reconnect_patch.rb +41 -0
  137. data/lib/blank_slate.rb +54 -0
  138. data/lib/bulk_indexer.rb +56 -0
  139. data/lib/class_methods.rb +279 -0
  140. data/lib/ferret_extensions.rb +192 -0
  141. data/lib/ferret_find_methods.rb +142 -0
  142. data/lib/ferret_result.rb +58 -0
  143. data/lib/ferret_server.rb +238 -0
  144. data/lib/index.rb +99 -0
  145. data/lib/instance_methods.rb +172 -0
  146. data/lib/local_index.rb +202 -0
  147. data/lib/more_like_this.rb +217 -0
  148. data/lib/multi_index.rb +133 -0
  149. data/lib/rdig_adapter.rb +149 -0
  150. data/lib/remote_functions.rb +43 -0
  151. data/lib/remote_index.rb +54 -0
  152. data/lib/remote_multi_index.rb +20 -0
  153. data/lib/search_results.rb +50 -0
  154. data/lib/server_manager.rb +71 -0
  155. data/lib/unix_daemon.rb +86 -0
  156. data/lib/without_ar.rb +52 -0
  157. data/recipes/aaf_recipes.rb +116 -0
  158. data/script/ferret_daemon +94 -0
  159. data/script/ferret_server +12 -0
  160. data/script/ferret_service +178 -0
  161. data/tasks/ferret.rake +39 -0
  162. metadata +246 -0
@@ -0,0 +1,133 @@
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
+ # Patch to pass an or_default setting on to ferret.
16
+ # Without an or_default set, ferret will use OR queries by default.
17
+ # This implementation will use OR if any model asks for OR, otherwise
18
+ # AND. -- Paul Fitzpatrick, 11/1/2010 (contributed to public domain)
19
+ or_default =
20
+ indexes.select{|idx| idx.index_definition[:ferret][:or_default]}.size>0
21
+ @options = {
22
+ :default_field => default_fields,
23
+ :or_default => or_default
24
+ }.update(options)
25
+ @logger = IndexLogger.new(ActsAsFerret::logger, "multi: #{indexes.map(&:index_name).join(',')}")
26
+ end
27
+
28
+ def ar_find(query, options = {}, ar_options = {})
29
+ limit = options.delete(:limit)
30
+ offset = options.delete(:offset) || 0
31
+ options[:limit] = :all
32
+ total_hits, result = super query, options, ar_options
33
+ total_hits = result.size if ar_options[:conditions]
34
+ # if limit && limit != :all
35
+ # result = result[offset..limit+offset-1]
36
+ # end
37
+ [total_hits, result]
38
+ end
39
+
40
+ def determine_stored_fields(options)
41
+ return nil unless options.has_key?(:lazy)
42
+ stored_fields = []
43
+ @indexes.each do |index|
44
+ stored_fields += index.determine_stored_fields(options)
45
+ end
46
+ return stored_fields.uniq
47
+ end
48
+
49
+ def shared?
50
+ false
51
+ end
52
+
53
+ end
54
+
55
+ # This class can be used to search multiple physical indexes at once.
56
+ class MultiIndex < MultiIndexBase
57
+
58
+ def extract_stored_fields(doc, stored_fields)
59
+ ActsAsFerret::get_index_for(doc[:class_name]).extract_stored_fields(doc, stored_fields) unless stored_fields.blank?
60
+ end
61
+
62
+ def total_hits(q, options = {})
63
+ search(q, options).total_hits
64
+ end
65
+
66
+ def search(query, options={})
67
+ query = process_query(query, options)
68
+ logger.debug "parsed query: #{query.to_s}"
69
+ searcher.search(query, options)
70
+ end
71
+
72
+ def search_each(query, options = {}, &block)
73
+ query = process_query(query, options)
74
+ searcher.search_each(query, options, &block)
75
+ end
76
+
77
+ # checks if all our sub-searchers still are up to date
78
+ def latest?
79
+ #return false unless @reader
80
+ # segfaults with 0.10.4 --> TODO report as bug @reader.latest?
81
+ @reader and @reader.latest?
82
+ #@sub_readers.each do |r|
83
+ # return false unless r.latest?
84
+ #end
85
+ #true
86
+ end
87
+
88
+ def searcher
89
+ ensure_searcher
90
+ @searcher
91
+ end
92
+
93
+ def doc(i)
94
+ searcher[i]
95
+ end
96
+ alias :[] :doc
97
+
98
+ def query_parser
99
+ @query_parser ||= Ferret::QueryParser.new(@options)
100
+ end
101
+
102
+ def process_query(query, options = {})
103
+ query = query_parser.parse(query) if query.is_a?(String)
104
+ return query
105
+ end
106
+
107
+ def close
108
+ @searcher.close if @searcher
109
+ @reader.close if @reader
110
+ end
111
+
112
+ protected
113
+
114
+ def ensure_searcher
115
+ unless latest?
116
+ @sub_readers = @indexes.map { |idx|
117
+ begin
118
+ reader = Ferret::Index::IndexReader.new(idx.index_definition[:index_dir])
119
+ logger.debug "sub-reader opened: #{reader}"
120
+ reader
121
+ rescue Exception
122
+ raise "error opening reader on index for class #{clazz.inspect}: #{$!}"
123
+ end
124
+ }
125
+ close
126
+ @reader = Ferret::Index::IndexReader.new(@sub_readers)
127
+ @searcher = Ferret::Search::Searcher.new(@reader)
128
+ end
129
+ end
130
+
131
+ end # of class MultiIndex
132
+
133
+ end
@@ -0,0 +1,149 @@
1
+ begin
2
+ require 'rdig'
3
+ rescue LoadError
4
+ puts "RDig gem not found, searching and indexing static pages won't work."
5
+ end
6
+ require 'digest/md5'
7
+
8
+ module ActsAsFerret
9
+
10
+ # The RdigAdapter is automatically included into your model if you specify
11
+ # the +:rdig+ options hash in your call to acts_as_ferret. It overrides
12
+ # several methods declared by aaf to retrieve documents with the help of
13
+ # RDig's http crawler when you call rebuild_index.
14
+ module RdigAdapter
15
+
16
+ if defined?(RDig)
17
+
18
+ def self.included(target)
19
+ target.extend ClassMethods
20
+ target.send :include, InstanceMethods
21
+ target.alias_method_chain :ferret_key, :md5
22
+ end
23
+
24
+ # Indexer class to replace RDig's original indexer
25
+ class Indexer
26
+ include MonitorMixin
27
+ def initialize(batch_size, model_class, &block)
28
+ @batch_size = batch_size
29
+ @model_class = model_class
30
+ @documents = []
31
+ @offset = 0
32
+ @block = block
33
+ super()
34
+ end
35
+
36
+ def add(doc)
37
+ synchronize do
38
+ @documents << @model_class.new(doc.uri.to_s, doc)
39
+ process_batch if @documents.size >= @batch_size
40
+ end
41
+ end
42
+ alias << add
43
+
44
+ def close
45
+ synchronize do
46
+ process_batch
47
+ end
48
+ end
49
+
50
+ protected
51
+ def process_batch
52
+ ActsAsFerret::logger.info "RdigAdapter::Indexer#process_batch: #{@documents.size} docs in queue, offset #{@offset}"
53
+ @block.call @documents, @offset
54
+ @offset += @documents.size
55
+ @documents = []
56
+ end
57
+ end
58
+
59
+ module ClassMethods
60
+ # overriding aaf to return the documents fetched via RDig
61
+ def records_for_rebuild(batch_size = 1000, &block)
62
+ indexer = Indexer.new(batch_size, self, &block)
63
+ configure_rdig do
64
+ crawler = RDig::Crawler.new RDig.configuration, ActsAsFerret::logger
65
+ crawler.instance_variable_set '@indexer', indexer
66
+ ActsAsFerret::logger.debug "now crawling..."
67
+ crawler.crawl
68
+ end
69
+ rescue => e
70
+ ActsAsFerret::logger.error e
71
+ ActsAsFerret::logger.debug e.backtrace.join("\n")
72
+ ensure
73
+ indexer.close if indexer
74
+ end
75
+
76
+ # overriding aaf to skip reindexing records changed during the rebuild
77
+ # when rebuilding with the rake task
78
+ def records_modified_since(time)
79
+ []
80
+ end
81
+
82
+ # unfortunately need to modify global RDig.configuration because it's
83
+ # used everywhere in RDig
84
+ def configure_rdig
85
+ # back up original config
86
+ old_logger = RDig.logger
87
+ old_cfg = RDig.configuration.dup
88
+ RDig.logger = ActsAsFerret.logger
89
+ rdig_configuration[:crawler].each { |k,v| RDig.configuration.crawler.send :"#{k}=", v } if rdig_configuration[:crawler]
90
+ if ce_config = rdig_configuration[:content_extraction]
91
+ RDig.configuration.content_extraction = OpenStruct.new( :hpricot => OpenStruct.new( ce_config ) )
92
+ end
93
+ yield
94
+ ensure
95
+ # restore original config
96
+ RDig.configuration.crawler = old_cfg.crawler
97
+ RDig.configuration.content_extraction = old_cfg.content_extraction
98
+ RDig.logger = old_logger
99
+ end
100
+
101
+ # overriding aaf to enforce loading page title and content from the
102
+ # ferret index
103
+ def find_with_ferret(q, options = {}, find_options = {})
104
+ options[:lazy] = true
105
+ super
106
+ end
107
+
108
+ def find_for_id(id)
109
+ new id
110
+ end
111
+ end
112
+
113
+ module InstanceMethods
114
+ def initialize(uri, rdig_document = nil)
115
+ @id = uri
116
+ @rdig_document = rdig_document
117
+ end
118
+
119
+ # Title of the document.
120
+ # Use the +:title_tag_selector+ option to declare the hpricot expression
121
+ # that should be used for selecting the content for this field.
122
+ def title
123
+ @rdig_document.title
124
+ end
125
+
126
+ # Content of the document.
127
+ # Use the +:content_tag_selector+ option to declare the hpricot expression
128
+ # that should be used for selecting the content for this field.
129
+ def content
130
+ @rdig_document.body
131
+ end
132
+
133
+ # Url of this document.
134
+ def id
135
+ @id
136
+ end
137
+
138
+ def ferret_key_with_md5
139
+ Digest::MD5.hexdigest(ferret_key_without_md5)
140
+ end
141
+
142
+ def to_s
143
+ "Page at #{id}, title: #{title}"
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ end
@@ -0,0 +1,43 @@
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
+
23
+ alias :old_handle_drb_error :handle_drb_error
24
+ def handle_drb_error(return_value_in_case_of_error = false)
25
+ handle_drb_restart do
26
+ old_handle_drb_error(return_value_in_case_of_error) { yield }
27
+ end
28
+ end
29
+
30
+ def handle_drb_restart
31
+ trys = 1
32
+ begin
33
+ return yield
34
+ rescue ActsAsFerret::IndexNotDefined
35
+ logger.warn "Recovering from ActsAsFerret::IndexNotDefined exception"
36
+ ActsAsFerret::ferret_indexes[index_name] = ActsAsFerret::create_index_instance( index_definition )
37
+ ActsAsFerret::ferret_indexes[index_name].register_class ActsAsFerret::index_using_classes.index(index_name).constantize, {}
38
+ retry if (trys -= 1) > 0
39
+ end
40
+ yield
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,54 @@
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
@@ -0,0 +1,20 @@
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
@@ -0,0 +1,50 @@
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