erichummel-sunspot_rails 1.2.1a

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.
Files changed (45) hide show
  1. data/History.txt +51 -0
  2. data/LICENSE +18 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +258 -0
  5. data/Rakefile +18 -0
  6. data/TESTING.md +35 -0
  7. data/TODO +8 -0
  8. data/VERSION.yml +4 -0
  9. data/dev_tasks/rdoc.rake +24 -0
  10. data/dev_tasks/release.rake +4 -0
  11. data/dev_tasks/spec.rake +22 -0
  12. data/dev_tasks/todo.rake +4 -0
  13. data/generators/sunspot/sunspot_generator.rb +9 -0
  14. data/generators/sunspot/templates/sunspot.yml +18 -0
  15. data/install.rb +1 -0
  16. data/lib/generators/sunspot_rails/install/install_generator.rb +13 -0
  17. data/lib/generators/sunspot_rails/install/templates/config/sunspot.yml +17 -0
  18. data/lib/generators/sunspot_rails.rb +9 -0
  19. data/lib/sunspot/rails/adapters.rb +83 -0
  20. data/lib/sunspot/rails/configuration.rb +323 -0
  21. data/lib/sunspot/rails/init.rb +5 -0
  22. data/lib/sunspot/rails/log_subscriber.rb +33 -0
  23. data/lib/sunspot/rails/railtie.rb +36 -0
  24. data/lib/sunspot/rails/railties/controller_runtime.rb +36 -0
  25. data/lib/sunspot/rails/request_lifecycle.rb +36 -0
  26. data/lib/sunspot/rails/searchable.rb +412 -0
  27. data/lib/sunspot/rails/server.rb +173 -0
  28. data/lib/sunspot/rails/solr_instrumentation.rb +21 -0
  29. data/lib/sunspot/rails/solr_logging.rb +63 -0
  30. data/lib/sunspot/rails/spec_helper.rb +26 -0
  31. data/lib/sunspot/rails/stub_session_proxy.rb +88 -0
  32. data/lib/sunspot/rails/tasks.rb +62 -0
  33. data/lib/sunspot/rails/version.rb +5 -0
  34. data/lib/sunspot/rails.rb +59 -0
  35. data/lib/sunspot_rails.rb +12 -0
  36. data/spec/configuration_spec.rb +173 -0
  37. data/spec/model_lifecycle_spec.rb +63 -0
  38. data/spec/model_spec.rb +356 -0
  39. data/spec/request_lifecycle_spec.rb +61 -0
  40. data/spec/schema.rb +27 -0
  41. data/spec/server_spec.rb +37 -0
  42. data/spec/session_spec.rb +24 -0
  43. data/spec/spec_helper.rb +46 -0
  44. data/spec/stub_session_proxy_spec.rb +122 -0
  45. metadata +155 -0
@@ -0,0 +1,323 @@
1
+ require 'erb'
2
+
3
+ module Sunspot #:nodoc:
4
+ module Rails #:nodoc:
5
+ #
6
+ # Sunspot::Rails is configured via the config/sunspot.yml file, which
7
+ # contains properties keyed by environment name. A sample sunspot.yml file
8
+ # would look like:
9
+ #
10
+ # development:
11
+ # solr:
12
+ # hostname: localhost
13
+ # port: 8982
14
+ # min_memory: 512M
15
+ # max_memory: 1G
16
+ # solr_jar: /some/path/solr15/start.jar
17
+ # test:
18
+ # solr:
19
+ # hostname: localhost
20
+ # port: 8983
21
+ # log_level: OFF
22
+ # production:
23
+ # solr:
24
+ # hostname: localhost
25
+ # port: 8983
26
+ # path: /solr/myindex
27
+ # log_level: WARNING
28
+ # solr_home: /some/path
29
+ # master_solr:
30
+ # hostname: localhost
31
+ # port: 8982
32
+ # path: /solr
33
+ # auto_commit_after_request: true
34
+ #
35
+ # Sunspot::Rails uses the configuration to set up the Solr connection, as
36
+ # well as for starting Solr with the appropriate port using the
37
+ # <code>rake sunspot:solr:start</code> task.
38
+ #
39
+ # If the <code>master_solr</code> configuration is present, Sunspot will use
40
+ # the Solr instance specified here for all write operations, and the Solr
41
+ # configured under <code>solr</code> for all read operations.
42
+ #
43
+ class Configuration
44
+ attr_writer :user_configuration
45
+ #
46
+ # The host name at which to connect to Solr. Default 'localhost'.
47
+ #
48
+ # ==== Returns
49
+ #
50
+ # String:: host name
51
+ #
52
+ def hostname
53
+ unless defined?(@hostname)
54
+ @hostname = solr_url.host if solr_url
55
+ @hostname ||= user_configuration_from_key('solr', 'hostname')
56
+ @hostname ||= default_hostname
57
+ end
58
+ @hostname
59
+ end
60
+
61
+ #
62
+ # The port at which to connect to Solr.
63
+ # Defaults to 8981 in test, 8982 in development and 8983 in production.
64
+ #
65
+ # ==== Returns
66
+ #
67
+ # Integer:: port
68
+ #
69
+ def port
70
+ unless defined?(@port)
71
+ @port = solr_url.port if solr_url
72
+ @port ||= user_configuration_from_key('solr', 'port')
73
+ @port ||= default_port
74
+ @port = @port.to_i
75
+ end
76
+ @port
77
+ end
78
+
79
+ #
80
+ # The url path to the Solr servlet (useful if you are running multicore).
81
+ # Default '/solr'.
82
+ #
83
+ # ==== Returns
84
+ #
85
+ # String:: path
86
+ #
87
+ def path
88
+ unless defined?(@path)
89
+ @path = solr_url.path if solr_url
90
+ @path ||= user_configuration_from_key('solr', 'path')
91
+ @path ||= default_path
92
+ end
93
+ @path
94
+ end
95
+
96
+ #
97
+ # The host name at which to connect to the master Solr instance. Defaults
98
+ # to the 'hostname' configuration option.
99
+ #
100
+ # ==== Returns
101
+ #
102
+ # String:: host name
103
+ #
104
+ def master_hostname
105
+ @master_hostname ||= (user_configuration_from_key('master_solr', 'hostname') || hostname)
106
+ end
107
+
108
+ #
109
+ # The port at which to connect to the master Solr instance. Defaults to
110
+ # the 'port' configuration option.
111
+ #
112
+ # ==== Returns
113
+ #
114
+ # Integer:: port
115
+ #
116
+ def master_port
117
+ @master_port ||= (user_configuration_from_key('master_solr', 'port') || port).to_i
118
+ end
119
+
120
+ #
121
+ # The path to the master Solr servlet (useful if you are running multicore).
122
+ # Defaults to the value of the 'path' configuration option.
123
+ #
124
+ # ==== Returns
125
+ #
126
+ # String:: path
127
+ #
128
+ def master_path
129
+ @master_path ||= (user_configuration_from_key('master_solr', 'path') || path)
130
+ end
131
+
132
+ #
133
+ # True if there is a master Solr instance configured, otherwise false.
134
+ #
135
+ # ==== Returns
136
+ #
137
+ # Boolean:: bool
138
+ #
139
+ def has_master?
140
+ @has_master = !!user_configuration_from_key('master_solr')
141
+ end
142
+
143
+ #
144
+ # The default log_level that should be passed to solr. You can
145
+ # change the individual log_levels in the solr admin interface.
146
+ # Default 'INFO'.
147
+ #
148
+ # ==== Returns
149
+ #
150
+ # String:: log_level
151
+ #
152
+ def log_level
153
+ @log_level ||= (user_configuration_from_key('solr', 'log_level') || 'INFO')
154
+ end
155
+
156
+ #
157
+ # Should the solr index receive a commit after each http-request.
158
+ # Default true
159
+ #
160
+ # ==== Returns
161
+ #
162
+ # Boolean: auto_commit_after_request?
163
+ #
164
+ def auto_commit_after_request?
165
+ @auto_commit_after_request ||=
166
+ user_configuration_from_key('auto_commit_after_request') != false
167
+ end
168
+
169
+ #
170
+ # As for #auto_commit_after_request? but only for deletes
171
+ # Default false
172
+ #
173
+ # ==== Returns
174
+ #
175
+ # Boolean: auto_commit_after_delete_request?
176
+ #
177
+ def auto_commit_after_delete_request?
178
+ @auto_commit_after_delete_request ||=
179
+ (user_configuration_from_key('auto_commit_after_delete_request') || false)
180
+ end
181
+
182
+
183
+ #
184
+ # The log directory for solr logfiles
185
+ #
186
+ # ==== Returns
187
+ #
188
+ # String:: log_dir
189
+ #
190
+ def log_file
191
+ @log_file ||= (user_configuration_from_key('solr', 'log_file') || default_log_file_location )
192
+ end
193
+
194
+ def data_path
195
+ @data_path ||= user_configuration_from_key('solr', 'data_path') || File.join(::Rails.root, 'solr', 'data', ::Rails.env)
196
+ end
197
+
198
+ def pid_dir
199
+ @pid_dir ||= user_configuration_from_key('solr', 'pid_dir') || File.join(::Rails.root, 'solr', 'pids', ::Rails.env)
200
+ end
201
+
202
+
203
+ #
204
+ # The solr home directory. Sunspot::Rails expects this directory
205
+ # to contain a config, data and pids directory. See
206
+ # Sunspot::Rails::Server.bootstrap for more information.
207
+ #
208
+ # ==== Returns
209
+ #
210
+ # String:: solr_home
211
+ #
212
+ def solr_home
213
+ @solr_home ||=
214
+ if user_configuration_from_key('solr', 'solr_home')
215
+ user_configuration_from_key('solr', 'solr_home')
216
+ else
217
+ File.join(::Rails.root, 'solr')
218
+ end
219
+ end
220
+
221
+ #
222
+ # Solr start jar
223
+ #
224
+ def solr_jar
225
+ @solr_jar ||= user_configuration_from_key('solr', 'solr_jar')
226
+ end
227
+
228
+ #
229
+ # Minimum java heap size for Solr instance
230
+ #
231
+ def min_memory
232
+ @min_memory ||= user_configuration_from_key('solr', 'min_memory')
233
+ end
234
+
235
+ #
236
+ # Maximum java heap size for Solr instance
237
+ #
238
+ def max_memory
239
+ @max_memory ||= user_configuration_from_key('solr', 'max_memory')
240
+ end
241
+
242
+ private
243
+
244
+ #
245
+ # Logging in rails_root/log as solr_<environment>.log as a
246
+ # default.
247
+ #
248
+ # ===== Returns
249
+ #
250
+ # String:: default_log_file_location
251
+ #
252
+ def default_log_file_location
253
+ File.join(::Rails.root, 'log', "solr_" + ::Rails.env + ".log")
254
+ end
255
+
256
+ #
257
+ # return a specific key from the user configuration in config/sunspot.yml
258
+ #
259
+ # ==== Returns
260
+ #
261
+ # Mixed:: requested_key or nil
262
+ #
263
+ def user_configuration_from_key( *keys )
264
+ keys.inject(user_configuration) do |hash, key|
265
+ hash[key] if hash
266
+ end
267
+ end
268
+
269
+ #
270
+ # Memoized hash of configuration options for the current Rails environment
271
+ # as specified in config/sunspot.yml
272
+ #
273
+ # ==== Returns
274
+ #
275
+ # Hash:: configuration options for current environment
276
+ #
277
+ def user_configuration
278
+ @user_configuration ||=
279
+ begin
280
+ path = File.join(::Rails.root, 'config', 'sunspot.yml')
281
+ if File.exist?(path)
282
+ File.open(path) do |file|
283
+ processed = ERB.new(file.read).result
284
+ YAML.load(processed)[::Rails.env]
285
+ end
286
+ else
287
+ {}
288
+ end
289
+ end
290
+ end
291
+
292
+ protected
293
+
294
+ #
295
+ # When a specific hostname, port and path aren't provided in the
296
+ # sunspot.yml file, look for a key named 'url', then check the
297
+ # environment, then fall back to a sensible localhost default.
298
+ #
299
+
300
+ def solr_url
301
+ if ENV['SOLR_URL'] || ENV['WEBSOLR_URL']
302
+ URI.parse(ENV['SOLR_URL'] || ENV['WEBSOLR_URL'])
303
+ end
304
+ end
305
+
306
+ def default_hostname
307
+ 'localhost'
308
+ end
309
+
310
+ def default_port
311
+ { 'test' => 8981,
312
+ 'development' => 8982,
313
+ 'production' => 8983
314
+ }[::Rails.env] || 8983
315
+ end
316
+
317
+ def default_path
318
+ '/solr'
319
+ end
320
+
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,5 @@
1
+ Sunspot.session = Sunspot::Rails.build_session
2
+ Sunspot::Adapters::InstanceAdapter.register(Sunspot::Rails::Adapters::ActiveRecordInstanceAdapter, ActiveRecord::Base)
3
+ Sunspot::Adapters::DataAccessor.register(Sunspot::Rails::Adapters::ActiveRecordDataAccessor, ActiveRecord::Base)
4
+ ActiveRecord::Base.module_eval { include(Sunspot::Rails::Searchable) }
5
+ ActionController::Base.module_eval { include(Sunspot::Rails::RequestLifecycle) }
@@ -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
@@ -0,0 +1,36 @@
1
+ module Sunspot
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie
4
+ initializer 'sunspot_rails.init' do
5
+ Sunspot.session = Sunspot::Rails.build_session
6
+ ActiveSupport.on_load(:active_record) do
7
+ Sunspot::Adapters::InstanceAdapter.register(Sunspot::Rails::Adapters::ActiveRecordInstanceAdapter, ActiveRecord::Base)
8
+ Sunspot::Adapters::DataAccessor.register(Sunspot::Rails::Adapters::ActiveRecordDataAccessor, ActiveRecord::Base)
9
+ include(Sunspot::Rails::Searchable)
10
+ end
11
+ ActiveSupport.on_load(:action_controller) do
12
+ include(Sunspot::Rails::RequestLifecycle)
13
+ end
14
+ require 'sunspot/rails/log_subscriber'
15
+ RSolr::Client.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
24
+ end
25
+
26
+ rake_tasks do
27
+ load 'sunspot/rails/tasks.rb'
28
+ end
29
+
30
+ generators do
31
+ load "generators/sunspot_rails.rb"
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -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
@@ -0,0 +1,36 @@
1
+ module Sunspot #:nodoc:
2
+ module Rails #:nodoc:
3
+ #
4
+ # This module adds an after_filter to ActionController::Base that commits
5
+ # the Sunspot session if any documents have been added, changed, or removed
6
+ # in the course of the request.
7
+ #
8
+ module RequestLifecycle
9
+ class <<self
10
+ def included(base) #:nodoc:
11
+ subclasses = base.subclasses.map do |subclass|
12
+ begin
13
+ subclass.constantize
14
+ rescue NameError
15
+ end
16
+ end.compact
17
+ loaded_controllers = [base].concat(subclasses)
18
+ # Depending on how Sunspot::Rails is loaded, there may already be
19
+ # controllers loaded into memory that subclass this controller. In
20
+ # this case, since after_filter uses the inheritable_attribute
21
+ # structure, the already-loaded subclasses don't get the filters. So,
22
+ # the below ensures that all loaded controllers have the filter.
23
+ loaded_controllers.each do |controller|
24
+ controller.after_filter do
25
+ if Sunspot::Rails.configuration.auto_commit_after_request?
26
+ Sunspot.commit_if_dirty
27
+ elsif Sunspot::Rails.configuration.auto_commit_after_delete_request?
28
+ Sunspot.commit_if_delete_dirty
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end