gojee-sunspot-rails 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +1 -0
  3. data/History.txt +74 -0
  4. data/LICENSE +18 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +265 -0
  7. data/Rakefile +17 -0
  8. data/TODO +8 -0
  9. data/dev_tasks/rdoc.rake +24 -0
  10. data/dev_tasks/release.rake +4 -0
  11. data/dev_tasks/spec.rake +107 -0
  12. data/dev_tasks/todo.rake +4 -0
  13. data/gemfiles/rails-2.3.14 +12 -0
  14. data/gemfiles/rails-3.0.11 +12 -0
  15. data/gemfiles/rails-3.1.3 +12 -0
  16. data/gemfiles/rails-3.2.1 +12 -0
  17. data/generators/sunspot/sunspot_generator.rb +9 -0
  18. data/generators/sunspot/templates/sunspot.yml +20 -0
  19. data/install.rb +1 -0
  20. data/lib/generators/sunspot_rails/install/install_generator.rb +13 -0
  21. data/lib/generators/sunspot_rails/install/templates/config/sunspot.yml +19 -0
  22. data/lib/generators/sunspot_rails.rb +9 -0
  23. data/lib/sunspot/rails/adapters.rb +83 -0
  24. data/lib/sunspot/rails/configuration.rb +376 -0
  25. data/lib/sunspot/rails/init.rb +5 -0
  26. data/lib/sunspot/rails/log_subscriber.rb +33 -0
  27. data/lib/sunspot/rails/railtie.rb +36 -0
  28. data/lib/sunspot/rails/railties/controller_runtime.rb +36 -0
  29. data/lib/sunspot/rails/request_lifecycle.rb +36 -0
  30. data/lib/sunspot/rails/searchable.rb +491 -0
  31. data/lib/sunspot/rails/server.rb +114 -0
  32. data/lib/sunspot/rails/solr_instrumentation.rb +19 -0
  33. data/lib/sunspot/rails/solr_logging.rb +62 -0
  34. data/lib/sunspot/rails/spec_helper.rb +26 -0
  35. data/lib/sunspot/rails/stub_session_proxy.rb +142 -0
  36. data/lib/sunspot/rails/tasks.rb +84 -0
  37. data/lib/sunspot/rails.rb +69 -0
  38. data/lib/sunspot_rails.rb +12 -0
  39. data/spec/configuration_spec.rb +209 -0
  40. data/spec/model_lifecycle_spec.rb +63 -0
  41. data/spec/model_spec.rb +601 -0
  42. data/spec/rails_template/app/controllers/application_controller.rb +10 -0
  43. data/spec/rails_template/app/controllers/posts_controller.rb +6 -0
  44. data/spec/rails_template/app/models/author.rb +8 -0
  45. data/spec/rails_template/app/models/blog.rb +12 -0
  46. data/spec/rails_template/app/models/location.rb +2 -0
  47. data/spec/rails_template/app/models/photo_post.rb +2 -0
  48. data/spec/rails_template/app/models/post.rb +11 -0
  49. data/spec/rails_template/app/models/post_with_auto.rb +10 -0
  50. data/spec/rails_template/app/models/post_with_default_scope.rb +11 -0
  51. data/spec/rails_template/config/boot.rb +127 -0
  52. data/spec/rails_template/config/preinitializer.rb +22 -0
  53. data/spec/rails_template/config/routes.rb +9 -0
  54. data/spec/rails_template/config/sunspot.yml +24 -0
  55. data/spec/rails_template/db/schema.rb +27 -0
  56. data/spec/request_lifecycle_spec.rb +61 -0
  57. data/spec/schema.rb +27 -0
  58. data/spec/searchable_spec.rb +12 -0
  59. data/spec/server_spec.rb +33 -0
  60. data/spec/session_spec.rb +57 -0
  61. data/spec/shared_examples/indexed_after_save.rb +8 -0
  62. data/spec/shared_examples/not_indexed_after_save.rb +8 -0
  63. data/spec/spec_helper.rb +48 -0
  64. data/spec/stub_session_proxy_spec.rb +122 -0
  65. data/sunspot_rails.gemspec +43 -0
  66. data/tmp/.gitkeep +0 -0
  67. metadata +97 -5
@@ -0,0 +1,491 @@
1
+ module Sunspot #:nodoc:
2
+ module Rails #:nodoc:
3
+ #
4
+ # This module adds Sunspot functionality to ActiveRecord models. As well as
5
+ # providing class and instance methods, it optionally adds lifecycle hooks
6
+ # to automatically add and remove models from the Solr index as they are
7
+ # created and destroyed.
8
+ #
9
+ module Searchable
10
+ class <<self
11
+ def included(base) #:nodoc:
12
+ base.module_eval do
13
+ extend(ActsAsMethods)
14
+ end
15
+ end
16
+ end
17
+
18
+ module ActsAsMethods
19
+ #
20
+ # Makes a class searchable if it is not already, or adds search
21
+ # configuration if it is. Note that the options passed in are only used
22
+ # the first time this method is called for a particular class; so,
23
+ # search should be defined before activating any mixins that extend
24
+ # search configuration.
25
+ #
26
+ # The block passed into this method is evaluated by the
27
+ # <code>Sunspot.setup</code> method. See the Sunspot documentation for
28
+ # complete information on the functionality provided by that method.
29
+ #
30
+ # ==== Options (+options+)
31
+ #
32
+ # :auto_index<Boolean>::
33
+ # Automatically index models in Solr when they are saved.
34
+ # Default: true
35
+ # :auto_remove<Boolean>::
36
+ # Automatically remove models from the Solr index when they are
37
+ # destroyed. <b>Setting this option to +false+ is not recommended
38
+ # </b>(see the README).
39
+ # :if<Mixed>::
40
+ # Only index models in Solr if the method, proc or string evaluates
41
+ # to true (e.g. <code>:if => :should_index?</code> or <code>:if =>
42
+ # proc { |model| model.foo > 2 }</code>). Models that do not match
43
+ # the constraint will be removed from the index upon save. Multiple
44
+ # constraints can be specified by passing an array (e.g. <code>:if =>
45
+ # [:method1, :method2]</code>).
46
+ # :ignore_attribute_changes_of<Array>::
47
+ # Define attributes, that should not trigger a reindex of that
48
+ # object. Usual suspects are updated_at or counters.
49
+ # :include<Mixed>::
50
+ # Define default ActiveRecord includes, set this to allow ActiveRecord
51
+ # to load required associations when indexing. See ActiveRecord's
52
+ # documentation on eager-loading for examples on how to set this
53
+ # Default: []
54
+ # :unless<Mixed>::
55
+ # Only index models in Solr if the method, proc or string evaluates
56
+ # to false (e.g. <code>:unless => :should_not_index?</code> or <code>
57
+ # :unless => proc { |model| model.foo <= 2 }</code>). Models that do
58
+ # not match the constraint will be removed from the index upon save.
59
+ # Multiple constraints can be specified by passing an array (e.g.
60
+ # <code>:unless => [:method1, :method2]</code>).
61
+ #
62
+ # ==== Example
63
+ #
64
+ # class Post < ActiveRecord::Base
65
+ # searchable do
66
+ # text :title, :body
67
+ # string :sort_title do
68
+ # title.downcase.sub(/^(an?|the)/, '')
69
+ # end
70
+ # integer :blog_id
71
+ # time :updated_at
72
+ # end
73
+ # end
74
+ #
75
+ def searchable(options = {}, &block)
76
+ Sunspot.setup(self, &block)
77
+
78
+ if searchable?
79
+ sunspot_options[:include].concat(Util::Array(options[:include]))
80
+ else
81
+ extend ClassMethods
82
+ include InstanceMethods
83
+
84
+ class_attribute :sunspot_options
85
+
86
+ unless options[:auto_index] == false
87
+ before_save :mark_for_auto_indexing_or_removal
88
+ after_save :perform_index_tasks
89
+ end
90
+
91
+ unless options[:auto_remove] == false
92
+ after_destroy do |searchable|
93
+ searchable.remove_from_index
94
+ end
95
+ end
96
+ options[:include] = Util::Array(options[:include])
97
+
98
+ self.sunspot_options = options
99
+ end
100
+ end
101
+
102
+ #
103
+ # This method is defined on all ActiveRecord::Base subclasses. It
104
+ # is false for classes on which #searchable has not been called, and
105
+ # true for classes on which #searchable has been called.
106
+ #
107
+ # ==== Returns
108
+ #
109
+ # +false+
110
+ #
111
+ def searchable?
112
+ false
113
+ end
114
+ end
115
+
116
+ module ClassMethods
117
+ def self.extended(base) #:nodoc:
118
+ class <<base
119
+ alias_method :search, :solr_search unless method_defined? :search
120
+ alias_method :search_ids, :solr_search_ids unless method_defined? :search_ids
121
+ alias_method :remove_all_from_index, :solr_remove_all_from_index unless method_defined? :remove_all_from_index
122
+ alias_method :remove_all_from_index!, :solr_remove_all_from_index! unless method_defined? :remove_all_from_index!
123
+ alias_method :reindex, :solr_reindex unless method_defined? :reindex
124
+ alias_method :index, :solr_index unless method_defined? :index
125
+ alias_method :index_orphans, :solr_index_orphans unless method_defined? :index_orphans
126
+ alias_method :clean_index_orphans, :solr_clean_index_orphans unless method_defined? :clean_index_orphans
127
+ end
128
+ end
129
+ #
130
+ # Search for instances of this class in Solr. The block is delegated to
131
+ # the Sunspot.search method - see the Sunspot documentation for the full
132
+ # API.
133
+ #
134
+ # ==== Example
135
+ #
136
+ # Post.search(:include => [:blog]) do
137
+ # keywords 'best pizza'
138
+ # with :blog_id, 1
139
+ # order :updated_at, :desc
140
+ # facet :category_ids
141
+ # end
142
+ #
143
+ # ==== Options
144
+ #
145
+ # :include:: Specify associations to eager load
146
+ # :select:: Specify columns to select from database when loading results
147
+ #
148
+ # ==== Returns
149
+ #
150
+ # Sunspot::Search:: Object containing results, totals, facets, etc.
151
+ #
152
+ def solr_search(options = {}, &block)
153
+ solr_execute_search(options) do
154
+ Sunspot.new_search(self, &block)
155
+ end
156
+ end
157
+
158
+ #
159
+ # Get IDs of matching results without loading the result objects from
160
+ # the database. This method may be useful if search is used as an
161
+ # intermediate step in a larger find operation. The block is the same
162
+ # as the block provided to the #search method.
163
+ #
164
+ # ==== Returns
165
+ #
166
+ # Array:: Array of IDs, in the order returned by the search
167
+ #
168
+ def solr_search_ids(&block)
169
+ solr_execute_search_ids do
170
+ solr_search(&block)
171
+ end
172
+ end
173
+
174
+ #
175
+ # Remove instances of this class from the Solr index.
176
+ #
177
+ def solr_remove_all_from_index
178
+ Sunspot.remove_all(self)
179
+ end
180
+
181
+ #
182
+ # Remove all instances of this class from the Solr index and immediately
183
+ # commit.
184
+ #
185
+ #
186
+ def solr_remove_all_from_index!
187
+ Sunspot.remove_all!(self)
188
+ end
189
+
190
+ #
191
+ # Completely rebuild the index for this class. First removes all
192
+ # instances from the index, then loads records and indexes them.
193
+ #
194
+ # See #index for information on options, etc.
195
+ #
196
+ def solr_reindex(options = {})
197
+ solr_remove_all_from_index
198
+ solr_index(options)
199
+ end
200
+
201
+ #
202
+ # Add/update all existing records in the Solr index. The
203
+ # +batch_size+ argument specifies how many records to load out of the
204
+ # database at a time. The default batch size is 50; if nil is passed,
205
+ # records will not be indexed in batches. By default, a commit is issued
206
+ # after each batch; passing +false+ for +batch_commit+ will disable
207
+ # this, and only issue a commit at the end of the process. If associated
208
+ # objects need to indexed also, you can specify +include+ in format
209
+ # accepted by ActiveRecord to improve your sql select performance
210
+ #
211
+ # ==== Options (passed as a hash)
212
+ #
213
+ # batch_size<Integer>:: Batch size with which to load records. Passing
214
+ # 'nil' will skip batches. Default is 50.
215
+ # batch_commit<Boolean>:: Flag signalling if a commit should be done after
216
+ # after each batch is indexed, default is 'true'
217
+ # include<Mixed>:: include option to be passed to the ActiveRecord find,
218
+ # used for including associated objects that need to be
219
+ # indexed with the parent object, accepts all formats
220
+ # ActiveRecord::Base.find does
221
+ # first_id:: The lowest possible ID for this class. Defaults to 0, which
222
+ # is fine for integer IDs; string primary keys will need to
223
+ # specify something reasonable here.
224
+ #
225
+ # ==== Examples
226
+ #
227
+ # # index in batches of 50, commit after each
228
+ # Post.index
229
+ #
230
+ # # index all rows at once, then commit
231
+ # Post.index(:batch_size => nil)
232
+ #
233
+ # # index in batches of 50, commit when all batches complete
234
+ # Post.index(:batch_commit => false)
235
+ #
236
+ # # include the associated +author+ object when loading to index
237
+ # Post.index(:include => :author)
238
+ #
239
+ def solr_index(opts={})
240
+ options = {
241
+ :batch_size => Sunspot.config.indexing.default_batch_size,
242
+ :batch_commit => true,
243
+ :include => self.sunspot_options[:include],
244
+ :start => opts.delete(:first_id) || 0
245
+ }.merge(opts)
246
+ find_in_batch_options = {
247
+ :include => options[:include],
248
+ :batch_size => options[:batch_size],
249
+ :start => options[:first_id]
250
+ }
251
+ progress_bar = options[:progress_bar]
252
+ if options[:batch_size]
253
+ batch_counter = 0
254
+ find_in_batches(find_in_batch_options) do |records|
255
+ solr_benchmark options[:batch_size], batch_counter do
256
+ Sunspot.index(records.select { |model| model.indexable? })
257
+ Sunspot.commit if options[:batch_commit]
258
+ end
259
+ # track progress
260
+ progress_bar.increment!(records.length) if progress_bar
261
+ batch_counter += 1
262
+ end
263
+ else
264
+ records = all(:include => options[:include]).select { |model| model.indexable? }
265
+ Sunspot.index!(records)
266
+ end
267
+ # perform a final commit if not committing in batches
268
+ Sunspot.commit unless options[:batch_commit]
269
+ end
270
+
271
+ #
272
+ # Return the IDs of records of this class that are indexed in Solr but
273
+ # do not exist in the database. Under normal circumstances, this should
274
+ # never happen, but this method is provided in case something goes
275
+ # wrong. Usually you will want to rectify the situation by calling
276
+ # #clean_index_orphans or #reindex
277
+ #
278
+ # ==== Options (passed as a hash)
279
+ #
280
+ # batch_size<Integer>:: Batch size with which to load records. Passing
281
+ # Default is 1000 (from ActiveRecord).
282
+ #
283
+ # ==== Returns
284
+ #
285
+ # Array:: Collection of IDs that exist in Solr but not in the database
286
+ def solr_index_orphans(opts={})
287
+ batch_size = opts[:batch_size] || Sunspot.config.indexing.default_batch_size
288
+ count = self.count
289
+ indexed_ids = solr_search_ids { paginate(:page => 1, :per_page => count) }.to_set
290
+ find_each(:select => 'id', :batch_size => batch_size) do |object|
291
+ indexed_ids.delete(object.id)
292
+ end
293
+ indexed_ids.to_a
294
+ end
295
+
296
+ #
297
+ # Find IDs of records of this class that are indexed in Solr but do not
298
+ # exist in the database, and remove them from Solr. Under normal
299
+ # circumstances, this should not be necessary; this method is provided
300
+ # in case something goes wrong.
301
+ #
302
+ # ==== Options (passed as a hash)
303
+ #
304
+ # batch_size<Integer>:: Batch size with which to load records
305
+ # Default is 50
306
+ #
307
+ def solr_clean_index_orphans(opts={})
308
+ solr_index_orphans(opts).each do |id|
309
+ new do |fake_instance|
310
+ fake_instance.id = id
311
+ end.solr_remove_from_index
312
+ end
313
+ end
314
+
315
+ #
316
+ # Classes that have been defined as searchable return +true+ for this
317
+ # method.
318
+ #
319
+ # ==== Returns
320
+ #
321
+ # +true+
322
+ #
323
+ def searchable?
324
+ true
325
+ end
326
+
327
+ def solr_execute_search(options = {})
328
+ options.assert_valid_keys(:include, :select)
329
+ search = yield
330
+ unless options.empty?
331
+ search.build do |query|
332
+ if options[:include]
333
+ query.data_accessor_for(self).include = options[:include]
334
+ end
335
+ if options[:select]
336
+ query.data_accessor_for(self).select = options[:select]
337
+ end
338
+ end
339
+ end
340
+ search.execute
341
+ end
342
+
343
+ def solr_execute_search_ids(options = {})
344
+ search = yield
345
+ search.raw_results.map { |raw_result| raw_result.primary_key.to_i }
346
+ end
347
+
348
+ protected
349
+
350
+ #
351
+ # Does some logging for benchmarking indexing performance
352
+ #
353
+ def solr_benchmark(batch_size, counter, &block)
354
+ start = Time.now
355
+ logger.info("[#{Time.now}] Start Indexing")
356
+ yield
357
+ elapsed = Time.now-start
358
+ logger.info("[#{Time.now}] Completed Indexing. Rows indexed #{counter * batch_size}. Rows/sec: #{batch_size/elapsed.to_f} (Elapsed: #{elapsed} sec.)")
359
+ end
360
+
361
+ end
362
+
363
+ module InstanceMethods
364
+ def self.included(base) #:nodoc:
365
+ base.module_eval do
366
+ alias_method :index, :solr_index unless method_defined? :index
367
+ alias_method :index!, :solr_index! unless method_defined? :index!
368
+ alias_method :remove_from_index, :solr_remove_from_index unless method_defined? :remove_from_index
369
+ alias_method :remove_from_index!, :solr_remove_from_index! unless method_defined? :remove_from_index!
370
+ alias_method :more_like_this, :solr_more_like_this unless method_defined? :more_like_this
371
+ alias_method :more_like_this_ids, :solr_more_like_this_ids unless method_defined? :more_like_this_ids
372
+ end
373
+ end
374
+ #
375
+ # Index the model in Solr. If the model is already indexed, it will be
376
+ # updated. Using the defaults, you will usually not need to call this
377
+ # method, as models are indexed automatically when they are created or
378
+ # updated. If you have disabled automatic indexing (see
379
+ # ClassMethods#searchable), this method allows you to manage indexing
380
+ # manually.
381
+ #
382
+ def solr_index
383
+ Sunspot.index(self)
384
+ end
385
+
386
+ #
387
+ # Index the model in Solr and immediately commit. See #index
388
+ #
389
+ def solr_index!
390
+ Sunspot.index!(self)
391
+ end
392
+
393
+ #
394
+ # Remove the model from the Solr index. Using the defaults, this should
395
+ # not be necessary, as models will automatically be removed from the
396
+ # index when they are destroyed. If you disable automatic removal
397
+ # (which is not recommended!), you can use this method to manage removal
398
+ # manually.
399
+ #
400
+ def solr_remove_from_index
401
+ Sunspot.remove(self)
402
+ end
403
+
404
+ #
405
+ # Remove the model from the Solr index and commit immediately. See
406
+ # #remove_from_index
407
+ #
408
+ def solr_remove_from_index!
409
+ Sunspot.remove!(self)
410
+ end
411
+
412
+ def solr_more_like_this(*args, &block)
413
+ options = args.extract_options!
414
+ self.class.solr_execute_search(options) do
415
+ Sunspot.new_more_like_this(self, *args, &block)
416
+ end
417
+ end
418
+
419
+ def solr_more_like_this_ids(&block)
420
+ self.class.solr_execute_search_ids do
421
+ solr_more_like_this(&block)
422
+ end
423
+ end
424
+
425
+ def indexable?
426
+ # options[:if] is not specified or they successfully pass
427
+ if_passes = self.class.sunspot_options[:if].nil? ||
428
+ constraint_passes?(self.class.sunspot_options[:if])
429
+
430
+ # options[:unless] is not specified or they successfully pass
431
+ unless_passes = self.class.sunspot_options[:unless].nil? ||
432
+ !constraint_passes?(self.class.sunspot_options[:unless])
433
+
434
+ if_passes and unless_passes
435
+ end
436
+
437
+ private
438
+
439
+ def constraint_passes?(constraint)
440
+ case constraint
441
+ when Symbol
442
+ self.__send__(constraint)
443
+ when String
444
+ self.__send__(constraint.to_sym)
445
+ when Enumerable
446
+ # All constraints must pass
447
+ constraint.all? { |inner_constraint| constraint_passes?(inner_constraint) }
448
+ else
449
+ if constraint.respond_to?(:call) # could be a Proc or anything else that responds to call
450
+ constraint.call(self)
451
+ else
452
+ raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
453
+ end
454
+ end
455
+ end
456
+
457
+ def mark_for_auto_indexing_or_removal
458
+ if indexable?
459
+ # :if/:unless constraints pass or were not present
460
+
461
+ @marked_for_auto_indexing =
462
+ if !new_record? && ignore_attributes = self.class.sunspot_options[:ignore_attribute_changes_of]
463
+ !(changed.map { |attr| attr.to_sym } - ignore_attributes).blank?
464
+ else
465
+ true
466
+ end
467
+
468
+ @marked_for_auto_removal = false
469
+ else
470
+ # :if/:unless constraints did not pass; do not auto index and
471
+ # actually go one step further by removing it from the index
472
+ @marked_for_auto_indexing = false
473
+ @marked_for_auto_removal = true
474
+ end
475
+
476
+ true
477
+ end
478
+
479
+ def perform_index_tasks
480
+ if @marked_for_auto_indexing
481
+ solr_index
482
+ remove_instance_variable(:@marked_for_auto_indexing)
483
+ elsif @marked_for_auto_removal
484
+ solr_remove_from_index
485
+ remove_instance_variable(:@marked_for_auto_removal)
486
+ end
487
+ end
488
+ end
489
+ end
490
+ end
491
+ end
@@ -0,0 +1,114 @@
1
+ module Sunspot
2
+ module Rails
3
+ class Server < Sunspot::Solr::Server
4
+ # ActiveSupport log levels are integers; this array maps them to the
5
+ # appropriate java.util.logging.Level constant
6
+ LOG_LEVELS = %w(FINE INFO WARNING SEVERE SEVERE INFO)
7
+
8
+ #
9
+ # Directory in which to store PID files
10
+ #
11
+ def pid_dir
12
+ configuration.pid_dir || File.join(::Rails.root, 'tmp', 'pids')
13
+ end
14
+
15
+ #
16
+ # Name of the PID file
17
+ #
18
+ def pid_file
19
+ "sunspot-solr-#{::Rails.env}.pid"
20
+ end
21
+
22
+ #
23
+ # Directory to store lucene index data files
24
+ #
25
+ # ==== Returns
26
+ #
27
+ # String:: data_path
28
+ #
29
+ def solr_data_dir
30
+ configuration.data_path
31
+ end
32
+
33
+ #
34
+ # Directory to use for Solr home.
35
+ #
36
+ def solr_home
37
+ File.join(configuration.solr_home)
38
+ end
39
+
40
+ #
41
+ # Solr start jar
42
+ #
43
+ def solr_jar
44
+ configuration.solr_jar || super
45
+ end
46
+
47
+ #
48
+ # Address on which to run Solr
49
+ #
50
+ def bind_address
51
+ configuration.bind_address
52
+ end
53
+
54
+ #
55
+ # Port on which to run Solr
56
+ #
57
+ def port
58
+ configuration.port
59
+ end
60
+
61
+ #
62
+ # Severity level for logging. This is based on the severity level for the
63
+ # Rails logger.
64
+ #
65
+ def log_level
66
+ LOG_LEVELS[::Rails.logger.level]
67
+ end
68
+
69
+ #
70
+ # Log file for Solr. File is in the rails log/ directory.
71
+ #
72
+ def log_file
73
+ File.join(::Rails.root, 'log', "sunspot-solr-#{::Rails.env}.log")
74
+ end
75
+
76
+ #
77
+ # Minimum Java heap size for Solr
78
+ #
79
+ def min_memory
80
+ configuration.min_memory
81
+ end
82
+
83
+ #
84
+ # Maximum Java heap size for Solr
85
+ #
86
+ def max_memory
87
+ configuration.max_memory
88
+ end
89
+
90
+ #
91
+ # A boolean value indicating whether or not solr is being run with multiple cores
92
+ #
93
+ def multicore
94
+ !!configuration.multicore
95
+ end
96
+
97
+ private
98
+
99
+ #
100
+ # access to the Sunspot::Rails::Configuration, defined in
101
+ # sunspot.yml. Use Sunspot::Rails.configuration if you want
102
+ # to access the configuration directly.
103
+ #
104
+ # ==== returns
105
+ #
106
+ # Sunspot::Rails::Configuration:: configuration
107
+ #
108
+ def configuration
109
+ Sunspot::Rails.configuration
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,19 @@
1
+ module Sunspot
2
+ module Rails
3
+ module SolrInstrumentation
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ alias_method_chain :execute, :as_instrumentation
8
+ end
9
+
10
+
11
+ def execute_with_as_instrumentation(path, params={}, *extra)
12
+ ActiveSupport::Notifications.instrument("request.rsolr",
13
+ {:path => path, :parameters => params}) do
14
+ execute_without_as_instrumentation(path, params, *extra)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ module Sunspot
2
+ module Rails
3
+ module SolrLogging
4
+
5
+ class <<self
6
+ def included(base)
7
+ base.alias_method_chain :execute, :rails_logging
8
+ end
9
+ end
10
+
11
+ COMMIT = %r{<commit/>}
12
+
13
+ def execute_with_rails_logging(client, request_context)
14
+ body = (request_context[:data]||"").dup
15
+ action = request_context[:path].capitalize
16
+ if body =~ COMMIT
17
+ action = "Commit"
18
+ body = ""
19
+ end
20
+ body = body[0, 800] + '...' if body.length > 800
21
+
22
+ # Make request and log.
23
+ response = nil
24
+ begin
25
+ ms = Benchmark.ms do
26
+ response = execute_without_rails_logging(client, request_context)
27
+ end
28
+ log_name = 'Solr %s (%.1fms)' % [action, ms]
29
+ ::Rails.logger.debug(format_log_entry(log_name, body))
30
+ rescue Exception => e
31
+ log_name = 'Solr %s (Error)' % [action]
32
+ ::Rails.logger.error(format_log_entry(log_name, body))
33
+ raise e
34
+ end
35
+
36
+ response
37
+ end
38
+
39
+ private
40
+
41
+ def format_log_entry(message, dump = nil)
42
+ @colorize_logging ||= begin
43
+ ::Rails.application.config.colorize_logging # Rails 3
44
+ rescue NoMethodError
45
+ ActiveRecord::Base.colorize_logging # Rails 2
46
+ end
47
+ if @colorize_logging
48
+ message_color, dump_color = "4;32;1", "0;1"
49
+ log_entry = " \e[#{message_color}m#{message}\e[0m "
50
+ log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump
51
+ log_entry
52
+ else
53
+ "%s %s" % [message, dump]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ RSolr::Connection.module_eval do
61
+ include Sunspot::Rails::SolrLogging
62
+ end