sunspot_rails 1.2.1 → 1.3.0.rc6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/History.txt +15 -0
- data/README.rdoc +14 -7
- data/Rakefile +0 -6
- data/TESTING.md +32 -18
- data/dev_tasks/spec.rake +103 -18
- data/gemfiles/rails-2.3.14 +15 -0
- data/gemfiles/rails-3.0.10 +15 -0
- data/gemfiles/rails-3.1.1 +15 -0
- data/lib/sunspot/rails.rb +13 -6
- data/lib/sunspot/rails/configuration.rb +25 -4
- data/lib/sunspot/rails/log_subscriber.rb +33 -0
- data/lib/sunspot/rails/railtie.rb +10 -0
- data/lib/sunspot/rails/railties/controller_runtime.rb +36 -0
- data/lib/sunspot/rails/searchable.rb +88 -20
- data/lib/sunspot/rails/server.rb +10 -77
- data/lib/sunspot/rails/solr_instrumentation.rb +20 -0
- data/lib/sunspot/rails/solr_logging.rb +16 -17
- data/lib/sunspot/rails/stub_session_proxy.rb +56 -2
- data/lib/sunspot/rails/tasks.rb +56 -33
- data/lib/sunspot_rails.rb +5 -0
- data/spec/configuration_spec.rb +27 -5
- data/spec/model_lifecycle_spec.rb +1 -1
- data/spec/model_spec.rb +240 -1
- data/spec/rails_template/app/controllers/application_controller.rb +10 -0
- data/spec/rails_template/app/controllers/posts_controller.rb +6 -0
- data/spec/rails_template/app/models/author.rb +8 -0
- data/spec/rails_template/app/models/blog.rb +12 -0
- data/spec/rails_template/app/models/location.rb +2 -0
- data/spec/rails_template/app/models/photo_post.rb +2 -0
- data/spec/rails_template/app/models/post.rb +11 -0
- data/spec/rails_template/app/models/post_with_auto.rb +10 -0
- data/spec/rails_template/app/models/post_with_default_scope.rb +11 -0
- data/spec/rails_template/config/boot.rb +127 -0
- data/spec/rails_template/config/preinitializer.rb +22 -0
- data/spec/rails_template/config/routes.rb +9 -0
- data/spec/rails_template/config/sunspot.yml +22 -0
- data/spec/rails_template/db/schema.rb +27 -0
- data/spec/request_lifecycle_spec.rb +1 -1
- data/spec/searchable_spec.rb +12 -0
- data/spec/server_spec.rb +2 -6
- data/spec/session_spec.rb +35 -2
- data/spec/shared_examples/indexed_after_save.rb +8 -0
- data/spec/shared_examples/not_indexed_after_save.rb +8 -0
- data/spec/spec_helper.rb +4 -2
- data/spec/stub_session_proxy_spec.rb +2 -2
- data/sunspot_rails.gemspec +43 -0
- metadata +114 -44
- data/lib/sunspot/rails/version.rb +0 -5
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Rails
|
3
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
4
|
+
def self.runtime=(value)
|
5
|
+
Thread.current["sorl_runtime"] = value
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.runtime
|
9
|
+
Thread.current["sorl_runtime"] ||= 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.reset_runtime
|
13
|
+
rt, self.runtime = runtime, 0
|
14
|
+
rt
|
15
|
+
end
|
16
|
+
|
17
|
+
def request(event)
|
18
|
+
self.class.runtime += event.duration
|
19
|
+
return unless logger.debug?
|
20
|
+
|
21
|
+
name = '%s (%.1fms)' % ["SOLR Request", event.duration]
|
22
|
+
|
23
|
+
# produces: path=/select parameters={fq: ["type:Tag"], q: rossi, fl: * score, qf: tag_name_text, defType: dismax, start: 0, rows: 20}
|
24
|
+
parameters = event.payload[:parameters].map { |k, v| "#{k}: #{color(v, BOLD, true)}" }.join(', ')
|
25
|
+
request = "path=#{event.payload[:path]} parameters={#{parameters}}"
|
26
|
+
|
27
|
+
debug " #{color(name, GREEN, true)} [ #{request} ]"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Sunspot::Rails::LogSubscriber.attach_to :rsolr
|
@@ -11,6 +11,16 @@ module Sunspot
|
|
11
11
|
ActiveSupport.on_load(:action_controller) do
|
12
12
|
include(Sunspot::Rails::RequestLifecycle)
|
13
13
|
end
|
14
|
+
require 'sunspot/rails/log_subscriber'
|
15
|
+
RSolr::Connection.module_eval{ include Sunspot::Rails::SolrInstrumentation }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Expose database runtime to controller for logging.
|
19
|
+
initializer "sunspot_rails.log_runtime" do |app|
|
20
|
+
require "sunspot/rails/railties/controller_runtime"
|
21
|
+
ActiveSupport.on_load(:action_controller) do
|
22
|
+
include Sunspot::Rails::Railties::ControllerRuntime
|
23
|
+
end
|
14
24
|
end
|
15
25
|
|
16
26
|
rake_tasks do
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Rails
|
3
|
+
module Railties
|
4
|
+
module ControllerRuntime
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
attr_internal :solr_runtime
|
10
|
+
|
11
|
+
def cleanup_view_runtime
|
12
|
+
# TODO only if solr is connected? if not call to super
|
13
|
+
|
14
|
+
solr_rt_before_render = Sunspot::Rails::LogSubscriber.reset_runtime
|
15
|
+
runtime = super
|
16
|
+
solr_rt_after_render = Sunspot::Rails::LogSubscriber.reset_runtime
|
17
|
+
self.solr_runtime = solr_rt_before_render + solr_rt_after_render
|
18
|
+
runtime - solr_rt_after_render
|
19
|
+
end
|
20
|
+
|
21
|
+
def append_info_to_payload(payload)
|
22
|
+
super
|
23
|
+
payload[:solr_runtime] = solr_runtime
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def log_process_action(payload)
|
28
|
+
messages, solr_runtime = super, payload[:solr_runtime]
|
29
|
+
messages << ("Solr: %.1fms" % solr_runtime.to_f) if solr_runtime
|
30
|
+
messages
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -36,6 +36,13 @@ module Sunspot #:nodoc:
|
|
36
36
|
# Automatically remove models from the Solr index when they are
|
37
37
|
# destroyed. <b>Setting this option to +false+ is not recommended
|
38
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>).
|
39
46
|
# :ignore_attribute_changes_of<Array>::
|
40
47
|
# Define attributes, that should not trigger a reindex of that
|
41
48
|
# object. Usual suspects are updated_at or counters.
|
@@ -44,6 +51,13 @@ module Sunspot #:nodoc:
|
|
44
51
|
# to load required associations when indexing. See ActiveRecord's
|
45
52
|
# documentation on eager-loading for examples on how to set this
|
46
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>).
|
47
61
|
#
|
48
62
|
# ==== Example
|
49
63
|
#
|
@@ -67,11 +81,11 @@ module Sunspot #:nodoc:
|
|
67
81
|
extend ClassMethods
|
68
82
|
include InstanceMethods
|
69
83
|
|
70
|
-
|
71
|
-
|
84
|
+
class_attribute :sunspot_options
|
85
|
+
|
72
86
|
unless options[:auto_index] == false
|
73
|
-
before_save :
|
74
|
-
after_save :
|
87
|
+
before_save :mark_for_auto_indexing_or_removal
|
88
|
+
after_save :perform_index_tasks
|
75
89
|
end
|
76
90
|
|
77
91
|
unless options[:auto_remove] == false
|
@@ -227,22 +241,31 @@ module Sunspot #:nodoc:
|
|
227
241
|
:batch_size => 50,
|
228
242
|
:batch_commit => true,
|
229
243
|
:include => self.sunspot_options[:include],
|
230
|
-
:
|
244
|
+
:start => opts.delete(:first_id) || 0
|
231
245
|
}.merge(opts)
|
232
|
-
|
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]
|
233
252
|
if options[:batch_size]
|
234
|
-
|
235
|
-
find_in_batches(
|
236
|
-
solr_benchmark options[:batch_size],
|
237
|
-
Sunspot.index(records)
|
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]
|
238
258
|
end
|
239
|
-
|
240
|
-
|
259
|
+
# track progress
|
260
|
+
progress_bar.increment!(records.length) if progress_bar
|
261
|
+
batch_counter += 1
|
241
262
|
end
|
242
|
-
Sunspot.commit unless options[:batch_commit]
|
243
263
|
else
|
244
|
-
|
264
|
+
records = all(:include => options[:include]).select { |model| model.indexable? }
|
265
|
+
Sunspot.index!(records)
|
245
266
|
end
|
267
|
+
# perform a final commit if not committing in batches
|
268
|
+
Sunspot.commit unless options[:batch_commit]
|
246
269
|
end
|
247
270
|
|
248
271
|
#
|
@@ -388,22 +411,67 @@ module Sunspot #:nodoc:
|
|
388
411
|
end
|
389
412
|
end
|
390
413
|
|
414
|
+
def indexable?
|
415
|
+
# options[:if] is not specified or they successfully pass
|
416
|
+
if_passes = self.class.sunspot_options[:if].nil? ||
|
417
|
+
constraint_passes?(self.class.sunspot_options[:if])
|
418
|
+
|
419
|
+
# options[:unless] is not specified or they successfully pass
|
420
|
+
unless_passes = self.class.sunspot_options[:unless].nil? ||
|
421
|
+
!constraint_passes?(self.class.sunspot_options[:unless])
|
422
|
+
|
423
|
+
if_passes and unless_passes
|
424
|
+
end
|
425
|
+
|
391
426
|
private
|
392
427
|
|
393
|
-
def
|
394
|
-
|
395
|
-
|
396
|
-
|
428
|
+
def constraint_passes?(constraint)
|
429
|
+
case constraint
|
430
|
+
when Symbol
|
431
|
+
self.__send__(constraint)
|
432
|
+
when String
|
433
|
+
self.__send__(constraint.to_sym)
|
434
|
+
when Enumerable
|
435
|
+
# All constraints must pass
|
436
|
+
constraint.all? { |inner_constraint| constraint_passes?(inner_constraint) }
|
437
|
+
else
|
438
|
+
if constraint.respond_to?(:call) # could be a Proc or anything else that responds to call
|
439
|
+
constraint.call(self)
|
397
440
|
else
|
398
|
-
|
441
|
+
raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
|
399
442
|
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def mark_for_auto_indexing_or_removal
|
447
|
+
if indexable?
|
448
|
+
# :if/:unless constraints pass or were not present
|
449
|
+
|
450
|
+
@marked_for_auto_indexing =
|
451
|
+
if !new_record? && ignore_attributes = self.class.sunspot_options[:ignore_attribute_changes_of]
|
452
|
+
!(changed.map { |attr| attr.to_sym } - ignore_attributes).blank?
|
453
|
+
else
|
454
|
+
true
|
455
|
+
end
|
456
|
+
|
457
|
+
@marked_for_auto_removal = false
|
458
|
+
else
|
459
|
+
# :if/:unless constraints did not pass; do not auto index and
|
460
|
+
# actually go one step further by removing it from the index
|
461
|
+
@marked_for_auto_indexing = false
|
462
|
+
@marked_for_auto_removal = true
|
463
|
+
end
|
464
|
+
|
400
465
|
true
|
401
466
|
end
|
402
467
|
|
403
|
-
def
|
468
|
+
def perform_index_tasks
|
404
469
|
if @marked_for_auto_indexing
|
405
470
|
solr_index
|
406
471
|
remove_instance_variable(:@marked_for_auto_indexing)
|
472
|
+
elsif @marked_for_auto_removal
|
473
|
+
solr_remove_from_index
|
474
|
+
remove_instance_variable(:@marked_for_auto_removal)
|
407
475
|
end
|
408
476
|
end
|
409
477
|
end
|
data/lib/sunspot/rails/server.rb
CHANGED
@@ -1,47 +1,15 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module Rails
|
3
|
-
class Server < Sunspot::Server
|
3
|
+
class Server < Sunspot::Solr::Server
|
4
4
|
# ActiveSupport log levels are integers; this array maps them to the
|
5
5
|
# appropriate java.util.logging.Level constant
|
6
6
|
LOG_LEVELS = %w(FINE INFO WARNING SEVERE SEVERE INFO)
|
7
7
|
|
8
|
-
def start
|
9
|
-
bootstrap
|
10
|
-
super
|
11
|
-
end
|
12
|
-
|
13
|
-
def run
|
14
|
-
bootstrap
|
15
|
-
super
|
16
|
-
end
|
17
|
-
|
18
|
-
#
|
19
|
-
# Bootstrap a new solr_home by creating all required
|
20
|
-
# directories.
|
21
|
-
#
|
22
|
-
# ==== Returns
|
23
|
-
#
|
24
|
-
# Boolean:: success
|
25
|
-
#
|
26
|
-
def bootstrap
|
27
|
-
unless @bootstrapped
|
28
|
-
install_solr_home
|
29
|
-
@bootstrapped = true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
#
|
34
|
-
# Directory to store custom libraries for solr
|
35
|
-
#
|
36
|
-
def lib_path
|
37
|
-
File.join( solr_home, 'lib' )
|
38
|
-
end
|
39
|
-
|
40
8
|
#
|
41
9
|
# Directory in which to store PID files
|
42
10
|
#
|
43
11
|
def pid_dir
|
44
|
-
File.join(::Rails.root, 'tmp', 'pids')
|
12
|
+
configuration.pid_dir || File.join(::Rails.root, 'tmp', 'pids')
|
45
13
|
end
|
46
14
|
|
47
15
|
#
|
@@ -59,7 +27,7 @@ module Sunspot
|
|
59
27
|
# String:: data_path
|
60
28
|
#
|
61
29
|
def solr_data_dir
|
62
|
-
|
30
|
+
configuration.data_path
|
63
31
|
end
|
64
32
|
|
65
33
|
#
|
@@ -76,6 +44,13 @@ module Sunspot
|
|
76
44
|
configuration.solr_jar || super
|
77
45
|
end
|
78
46
|
|
47
|
+
#
|
48
|
+
# Address on which to run Solr
|
49
|
+
#
|
50
|
+
def bind_address
|
51
|
+
configuration.bind_address
|
52
|
+
end
|
53
|
+
|
79
54
|
#
|
80
55
|
# Port on which to run Solr
|
81
56
|
#
|
@@ -126,48 +101,6 @@ module Sunspot
|
|
126
101
|
def configuration
|
127
102
|
Sunspot::Rails.configuration
|
128
103
|
end
|
129
|
-
|
130
|
-
#
|
131
|
-
# Directory to store solr config files
|
132
|
-
#
|
133
|
-
# ==== Returns
|
134
|
-
#
|
135
|
-
# String:: config_path
|
136
|
-
#
|
137
|
-
def config_path
|
138
|
-
File.join(solr_home, 'conf')
|
139
|
-
end
|
140
|
-
|
141
|
-
#
|
142
|
-
# Copy default solr configuration files from sunspot
|
143
|
-
# gem to the new solr_home/config directory
|
144
|
-
#
|
145
|
-
# ==== Returns
|
146
|
-
#
|
147
|
-
# Boolean:: success
|
148
|
-
#
|
149
|
-
def install_solr_home
|
150
|
-
unless File.exists?(solr_home)
|
151
|
-
Sunspot::Installer.execute(
|
152
|
-
solr_home,
|
153
|
-
:force => true,
|
154
|
-
:verbose => true
|
155
|
-
)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
#
|
160
|
-
# Create new solr_home, config, log and pid directories
|
161
|
-
#
|
162
|
-
# ==== Returns
|
163
|
-
#
|
164
|
-
# Boolean:: success
|
165
|
-
#
|
166
|
-
def create_solr_directories
|
167
|
-
[solr_data_dir, pid_dir].each do |path|
|
168
|
-
FileUtils.mkdir_p( path )
|
169
|
-
end
|
170
|
-
end
|
171
104
|
end
|
172
105
|
end
|
173
106
|
end
|
@@ -0,0 +1,20 @@
|
|
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
|
+
module InstanceMethods
|
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
|
20
|
+
end
|
@@ -1,20 +1,21 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module Rails
|
3
3
|
module SolrLogging
|
4
|
+
|
4
5
|
class <<self
|
5
6
|
def included(base)
|
6
|
-
base.
|
7
|
+
base.alias_method_chain :execute, :rails_logging
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
10
|
-
|
11
|
+
COMMIT = %r{<commit/>}
|
11
12
|
|
12
|
-
|
13
|
-
body = (
|
14
|
-
action = path
|
15
|
-
if body
|
16
|
-
action =
|
17
|
-
body =
|
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 = ""
|
18
19
|
end
|
19
20
|
body = body[0, 800] + '...' if body.length > 800
|
20
21
|
|
@@ -22,7 +23,7 @@ module Sunspot
|
|
22
23
|
response = nil
|
23
24
|
begin
|
24
25
|
ms = Benchmark.ms do
|
25
|
-
response =
|
26
|
+
response = execute_without_rails_logging(client, request_context)
|
26
27
|
end
|
27
28
|
log_name = 'Solr %s (%.1fms)' % [action, ms]
|
28
29
|
::Rails.logger.debug(format_log_entry(log_name, body))
|
@@ -39,10 +40,10 @@ module Sunspot
|
|
39
40
|
|
40
41
|
def format_log_entry(message, dump = nil)
|
41
42
|
@colorize_logging ||= begin
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
::Rails.application.config.colorize_logging # Rails 3
|
44
|
+
rescue NoMethodError
|
45
|
+
ActiveRecord::Base.colorize_logging # Rails 2
|
46
|
+
end
|
46
47
|
if @colorize_logging
|
47
48
|
message_color, dump_color = "4;32;1", "0;1"
|
48
49
|
log_entry = " \e[#{message_color}m#{message}\e[0m "
|
@@ -56,8 +57,6 @@ module Sunspot
|
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
rescue NameError # RSolr 0.9.6
|
62
|
-
RSolr::Connection::Base.module_eval { include(Sunspot::Rails::SolrLogging) }
|
60
|
+
RSolr::Connection.module_eval do
|
61
|
+
include Sunspot::Rails::SolrLogging
|
63
62
|
end
|