substantial-sunspot_rails 2.0.0.pre.111215

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +1 -0
  3. data/History.txt +66 -0
  4. data/LICENSE +18 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +265 -0
  7. data/Rakefile +12 -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 +15 -0
  14. data/gemfiles/rails-3.0.11 +15 -0
  15. data/gemfiles/rails-3.1.3 +15 -0
  16. data/generators/sunspot/sunspot_generator.rb +9 -0
  17. data/generators/sunspot/templates/sunspot.yml +18 -0
  18. data/install.rb +1 -0
  19. data/lib/generators/sunspot_rails.rb +9 -0
  20. data/lib/generators/sunspot_rails/install/install_generator.rb +13 -0
  21. data/lib/generators/sunspot_rails/install/templates/config/sunspot.yml +17 -0
  22. data/lib/substantial-sunspot_rails.rb +1 -0
  23. data/lib/sunspot/rails.rb +65 -0
  24. data/lib/sunspot/rails/adapters.rb +83 -0
  25. data/lib/sunspot/rails/configuration.rb +340 -0
  26. data/lib/sunspot/rails/init.rb +5 -0
  27. data/lib/sunspot/rails/log_subscriber.rb +33 -0
  28. data/lib/sunspot/rails/railtie.rb +36 -0
  29. data/lib/sunspot/rails/railties/controller_runtime.rb +36 -0
  30. data/lib/sunspot/rails/request_lifecycle.rb +36 -0
  31. data/lib/sunspot/rails/searchable.rb +480 -0
  32. data/lib/sunspot/rails/server.rb +106 -0
  33. data/lib/sunspot/rails/solr_instrumentation.rb +18 -0
  34. data/lib/sunspot/rails/solr_logging.rb +62 -0
  35. data/lib/sunspot/rails/spec_helper.rb +26 -0
  36. data/lib/sunspot/rails/stub_session_proxy.rb +142 -0
  37. data/lib/sunspot/rails/tasks.rb +84 -0
  38. data/lib/sunspot_rails.rb +12 -0
  39. data/spec/configuration_spec.rb +195 -0
  40. data/spec/model_lifecycle_spec.rb +63 -0
  41. data/spec/model_spec.rb +595 -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 +22 -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/substantial-sunspot_rails.gemspec +43 -0
  66. metadata +228 -0
@@ -0,0 +1,4 @@
1
+ desc 'Show all TODO and related tags'
2
+ task :todo do
3
+ FileList['lib/**/*.rb'].egrep(/#.*(TODO|FIXME|XXX)/)
4
+ end
@@ -0,0 +1,15 @@
1
+ source :rubygems
2
+
3
+ gem 'rails', '2.3.14'
4
+ gem 'sqlite3-ruby', '~> 1.3.1'
5
+
6
+ gem 'sunspot', :path => File.expand_path('../../../sunspot', __FILE__)
7
+ gem 'sunspot_solr', :path => File.expand_path('../../../sunspot_solr', __FILE__)
8
+ gem 'sunspot_rails', :path => File.expand_path('../..', __FILE__)
9
+
10
+ group :test do
11
+ gem 'rspec-rails', '~> 1.3.4'
12
+ gem 'ruby-debug19', :require => nil, :platforms => :mri_19
13
+ gem 'test-unit', '1.2.3', :platforms => :mri_19
14
+ gem 'ruby-debug', :require => nil, :platforms => :mri_18
15
+ end
@@ -0,0 +1,15 @@
1
+ source :rubygems
2
+
3
+ gem 'rails', '3.0.11'
4
+ gem 'sqlite3-ruby', '~> 1.3.1'
5
+
6
+ gem 'sunspot', :path => File.expand_path('../../../sunspot', __FILE__)
7
+ gem 'sunspot_solr', :path => File.expand_path('../../../sunspot_solr', __FILE__)
8
+ gem 'sunspot_rails', :path => File.expand_path('../..', __FILE__)
9
+
10
+ group :test do
11
+ gem 'rspec-rails', '~> 2.0.0'
12
+ gem 'ruby-debug19', :require => nil, :platforms => :mri_19
13
+ gem 'test-unit', '1.2.3', :platforms => :mri_19
14
+ gem 'ruby-debug', :require => nil, :platforms => :mri_18
15
+ end
@@ -0,0 +1,15 @@
1
+ source :rubygems
2
+
3
+ gem 'rails', '3.1.3'
4
+ gem 'sqlite3-ruby', '~> 1.3.1'
5
+
6
+ gem 'sunspot', :path => File.expand_path('../../../sunspot', __FILE__)
7
+ gem 'sunspot_solr', :path => File.expand_path('../../../sunspot_solr', __FILE__)
8
+ gem 'sunspot_rails', :path => File.expand_path('../..', __FILE__)
9
+
10
+ group :test do
11
+ gem 'rspec-rails', '~> 2.0.0'
12
+ gem 'ruby-debug19', :require => nil, :platforms => :mri_19
13
+ gem 'test-unit', '1.2.3', :platforms => :mri_19
14
+ gem 'ruby-debug', :require => nil, :platforms => :mri_18
15
+ end
@@ -0,0 +1,9 @@
1
+ class SunspotGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ m.template 'sunspot.yml', 'config/sunspot.yml'
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,18 @@
1
+ production:
2
+ solr:
3
+ hostname: localhost
4
+ port: 8983
5
+ log_level: WARNING
6
+
7
+ development:
8
+ solr:
9
+ hostname: localhost
10
+ port: 8982
11
+ log_level: INFO
12
+
13
+ test:
14
+ solr:
15
+ hostname: localhost
16
+ port: 8981
17
+ log_level: WARNING
18
+
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,9 @@
1
+ module SunspotRails
2
+ module Generators
3
+ class Base < Rails::Generators::NamedBase
4
+ def self.source_root
5
+ @_sunspot_rails_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'sunspot_rails', generator_name, 'templates'))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module SunspotRails
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ def self.source_root
5
+ @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
6
+ end
7
+ def copy_config_file
8
+ template 'config/sunspot.yml'
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,17 @@
1
+ production:
2
+ solr:
3
+ hostname: localhost
4
+ port: 8983
5
+ log_level: WARNING
6
+
7
+ development:
8
+ solr:
9
+ hostname: localhost
10
+ port: 8982
11
+ log_level: INFO
12
+
13
+ test:
14
+ solr:
15
+ hostname: localhost
16
+ port: 8981
17
+ log_level: WARNING
@@ -0,0 +1 @@
1
+ require 'sunspot_rails'
@@ -0,0 +1,65 @@
1
+ require 'sunspot'
2
+ require File.join(File.dirname(__FILE__), 'rails', 'configuration')
3
+ require File.join(File.dirname(__FILE__), 'rails', 'adapters')
4
+ require File.join(File.dirname(__FILE__), 'rails', 'request_lifecycle')
5
+ require File.join(File.dirname(__FILE__), 'rails', 'searchable')
6
+
7
+ module Sunspot #:nodoc:
8
+ module Rails #:nodoc:
9
+ autoload :SolrInstrumentation, File.join(File.dirname(__FILE__), 'rails', 'solr_instrumentation')
10
+ autoload :StubSessionProxy, File.join(File.dirname(__FILE__), 'rails', 'stub_session_proxy')
11
+ begin
12
+ require 'sunspot_solr'
13
+ autoload :Server, File.join(File.dirname(__FILE__), 'rails', 'server')
14
+ rescue LoadError => e
15
+ # We're fine
16
+ end
17
+
18
+ class <<self
19
+ attr_writer :configuration
20
+
21
+ def configuration
22
+ @configuration ||= Sunspot::Rails::Configuration.new
23
+ end
24
+
25
+ def reset
26
+ @configuration = nil
27
+ end
28
+
29
+ def build_session(configuration = self.configuration)
30
+ if configuration.disabled?
31
+ StubSessionProxy.new(Sunspot.session)
32
+ elsif configuration.has_master?
33
+ SessionProxy::MasterSlaveSessionProxy.new(
34
+ SessionProxy::ThreadLocalSessionProxy.new(master_config(configuration)),
35
+ SessionProxy::ThreadLocalSessionProxy.new(slave_config(configuration))
36
+ )
37
+ else
38
+ SessionProxy::ThreadLocalSessionProxy.new(slave_config(configuration))
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def master_config(sunspot_rails_configuration)
45
+ config = Sunspot::Configuration.build
46
+ config.solr.url = URI::HTTP.build(
47
+ :host => sunspot_rails_configuration.master_hostname,
48
+ :port => sunspot_rails_configuration.master_port,
49
+ :path => sunspot_rails_configuration.master_path
50
+ ).to_s
51
+ config
52
+ end
53
+
54
+ def slave_config(sunspot_rails_configuration)
55
+ config = Sunspot::Configuration.build
56
+ config.solr.url = URI::HTTP.build(
57
+ :host => sunspot_rails_configuration.hostname,
58
+ :port => sunspot_rails_configuration.port,
59
+ :path => sunspot_rails_configuration.path
60
+ ).to_s
61
+ config
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,83 @@
1
+ module Sunspot #:nodoc:
2
+ module Rails #:nodoc:
3
+ #
4
+ # This module provides Sunspot Adapter implementations for ActiveRecord
5
+ # models.
6
+ #
7
+ module Adapters
8
+ class ActiveRecordInstanceAdapter < Sunspot::Adapters::InstanceAdapter
9
+ #
10
+ # Return the primary key for the adapted instance
11
+ #
12
+ # ==== Returns
13
+ #
14
+ # Integer:: Database ID of model
15
+ #
16
+ def id
17
+ @instance.id
18
+ end
19
+ end
20
+
21
+ class ActiveRecordDataAccessor < Sunspot::Adapters::DataAccessor
22
+ # options for the find
23
+ attr_accessor :include, :select
24
+
25
+ #
26
+ # Set the fields to select from the database. This will be passed
27
+ # to ActiveRecord.
28
+ #
29
+ # ==== Parameters
30
+ #
31
+ # value<Mixed>:: String of comma-separated columns or array of columns
32
+ #
33
+ def select=(value)
34
+ value = value.join(', ') if value.respond_to?(:join)
35
+ @select = value
36
+ end
37
+
38
+ #
39
+ # Get one ActiveRecord instance out of the database by ID
40
+ #
41
+ # ==== Parameters
42
+ #
43
+ # id<String>:: Database ID of model to retreive
44
+ #
45
+ # ==== Returns
46
+ #
47
+ # ActiveRecord::Base:: ActiveRecord model
48
+ #
49
+ def load(id)
50
+ @clazz.first(options_for_find.merge(
51
+ :conditions => { @clazz.primary_key => id}
52
+ ))
53
+ end
54
+
55
+ #
56
+ # Get a collection of ActiveRecord instances out of the database by ID
57
+ #
58
+ # ==== Parameters
59
+ #
60
+ # ids<Array>:: Database IDs of models to retrieve
61
+ #
62
+ # ==== Returns
63
+ #
64
+ # Array:: Collection of ActiveRecord models
65
+ #
66
+ def load_all(ids)
67
+ @clazz.all(options_for_find.merge(
68
+ :conditions => { @clazz.primary_key => ids.map { |id| id }}
69
+ ))
70
+ end
71
+
72
+ private
73
+
74
+ def options_for_find
75
+ options = {}
76
+ options[:include] = @include unless @include.blank?
77
+ options[:select] = @select unless @select.blank?
78
+ options
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,340 @@
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
+ # bind_address: 0.0.0.0
18
+ # disabled: false
19
+ # test:
20
+ # solr:
21
+ # hostname: localhost
22
+ # port: 8983
23
+ # log_level: OFF
24
+ # production:
25
+ # solr:
26
+ # hostname: localhost
27
+ # port: 8983
28
+ # path: /solr/myindex
29
+ # log_level: WARNING
30
+ # solr_home: /some/path
31
+ # master_solr:
32
+ # hostname: localhost
33
+ # port: 8982
34
+ # path: /solr
35
+ # auto_commit_after_request: true
36
+ #
37
+ # Sunspot::Rails uses the configuration to set up the Solr connection, as
38
+ # well as for starting Solr with the appropriate port using the
39
+ # <code>rake sunspot:solr:start</code> task.
40
+ #
41
+ # If the <code>master_solr</code> configuration is present, Sunspot will use
42
+ # the Solr instance specified here for all write operations, and the Solr
43
+ # configured under <code>solr</code> for all read operations.
44
+ #
45
+ class Configuration
46
+ attr_writer :user_configuration
47
+ #
48
+ # The host name at which to connect to Solr. Default 'localhost'.
49
+ #
50
+ # ==== Returns
51
+ #
52
+ # String:: host name
53
+ #
54
+ def hostname
55
+ unless defined?(@hostname)
56
+ @hostname = solr_url.host if solr_url
57
+ @hostname ||= user_configuration_from_key('solr', 'hostname')
58
+ @hostname ||= default_hostname
59
+ end
60
+ @hostname
61
+ end
62
+
63
+ #
64
+ # The port at which to connect to Solr.
65
+ # Defaults to 8981 in test, 8982 in development and 8983 in production.
66
+ #
67
+ # ==== Returns
68
+ #
69
+ # Integer:: port
70
+ #
71
+ def port
72
+ unless defined?(@port)
73
+ @port = solr_url.port if solr_url
74
+ @port ||= user_configuration_from_key('solr', 'port')
75
+ @port ||= default_port
76
+ @port = @port.to_i
77
+ end
78
+ @port
79
+ end
80
+
81
+ #
82
+ # The url path to the Solr servlet (useful if you are running multicore).
83
+ # Default '/solr'.
84
+ #
85
+ # ==== Returns
86
+ #
87
+ # String:: path
88
+ #
89
+ def path
90
+ unless defined?(@path)
91
+ @path = solr_url.path if solr_url
92
+ @path ||= user_configuration_from_key('solr', 'path')
93
+ @path ||= default_path
94
+ end
95
+ @path
96
+ end
97
+
98
+ #
99
+ # The host name at which to connect to the master Solr instance. Defaults
100
+ # to the 'hostname' configuration option.
101
+ #
102
+ # ==== Returns
103
+ #
104
+ # String:: host name
105
+ #
106
+ def master_hostname
107
+ @master_hostname ||= (user_configuration_from_key('master_solr', 'hostname') || hostname)
108
+ end
109
+
110
+ #
111
+ # The port at which to connect to the master Solr instance. Defaults to
112
+ # the 'port' configuration option.
113
+ #
114
+ # ==== Returns
115
+ #
116
+ # Integer:: port
117
+ #
118
+ def master_port
119
+ @master_port ||= (user_configuration_from_key('master_solr', 'port') || port).to_i
120
+ end
121
+
122
+ #
123
+ # The path to the master Solr servlet (useful if you are running multicore).
124
+ # Defaults to the value of the 'path' configuration option.
125
+ #
126
+ # ==== Returns
127
+ #
128
+ # String:: path
129
+ #
130
+ def master_path
131
+ @master_path ||= (user_configuration_from_key('master_solr', 'path') || path)
132
+ end
133
+
134
+ #
135
+ # True if there is a master Solr instance configured, otherwise false.
136
+ #
137
+ # ==== Returns
138
+ #
139
+ # Boolean:: bool
140
+ #
141
+ def has_master?
142
+ @has_master = !!user_configuration_from_key('master_solr')
143
+ end
144
+
145
+ #
146
+ # The default log_level that should be passed to solr. You can
147
+ # change the individual log_levels in the solr admin interface.
148
+ # Default 'INFO'.
149
+ #
150
+ # ==== Returns
151
+ #
152
+ # String:: log_level
153
+ #
154
+ def log_level
155
+ @log_level ||= (user_configuration_from_key('solr', 'log_level') || 'INFO')
156
+ end
157
+
158
+ #
159
+ # Should the solr index receive a commit after each http-request.
160
+ # Default true
161
+ #
162
+ # ==== Returns
163
+ #
164
+ # Boolean: auto_commit_after_request?
165
+ #
166
+ def auto_commit_after_request?
167
+ @auto_commit_after_request ||=
168
+ user_configuration_from_key('auto_commit_after_request') != false
169
+ end
170
+
171
+ #
172
+ # As for #auto_commit_after_request? but only for deletes
173
+ # Default false
174
+ #
175
+ # ==== Returns
176
+ #
177
+ # Boolean: auto_commit_after_delete_request?
178
+ #
179
+ def auto_commit_after_delete_request?
180
+ @auto_commit_after_delete_request ||=
181
+ (user_configuration_from_key('auto_commit_after_delete_request') || false)
182
+ end
183
+
184
+
185
+ #
186
+ # The log directory for solr logfiles
187
+ #
188
+ # ==== Returns
189
+ #
190
+ # String:: log_dir
191
+ #
192
+ def log_file
193
+ @log_file ||= (user_configuration_from_key('solr', 'log_file') || default_log_file_location )
194
+ end
195
+
196
+ def data_path
197
+ @data_path ||= user_configuration_from_key('solr', 'data_path') || File.join(::Rails.root, 'solr', 'data', ::Rails.env)
198
+ end
199
+
200
+ def pid_dir
201
+ @pid_dir ||= user_configuration_from_key('solr', 'pid_dir') || File.join(::Rails.root, 'solr', 'pids', ::Rails.env)
202
+ end
203
+
204
+
205
+ #
206
+ # The solr home directory. Sunspot::Rails expects this directory
207
+ # to contain a config, data and pids directory. See
208
+ # Sunspot::Rails::Server.bootstrap for more information.
209
+ #
210
+ # ==== Returns
211
+ #
212
+ # String:: solr_home
213
+ #
214
+ def solr_home
215
+ @solr_home ||=
216
+ if user_configuration_from_key('solr', 'solr_home')
217
+ user_configuration_from_key('solr', 'solr_home')
218
+ else
219
+ File.join(::Rails.root, 'solr')
220
+ end
221
+ end
222
+
223
+ #
224
+ # Solr start jar
225
+ #
226
+ def solr_jar
227
+ @solr_jar ||= user_configuration_from_key('solr', 'solr_jar')
228
+ end
229
+
230
+ #
231
+ # Minimum java heap size for Solr instance
232
+ #
233
+ def min_memory
234
+ @min_memory ||= user_configuration_from_key('solr', 'min_memory')
235
+ end
236
+
237
+ #
238
+ # Maximum java heap size for Solr instance
239
+ #
240
+ def max_memory
241
+ @max_memory ||= user_configuration_from_key('solr', 'max_memory')
242
+ end
243
+
244
+ #
245
+ # Interface on which to run Solr
246
+ #
247
+ def bind_address
248
+ @bind_address ||= user_configuration_from_key('solr', 'bind_address')
249
+ end
250
+
251
+ #
252
+ # Whether or not to disable Solr.
253
+ # Defaults to false.
254
+ #
255
+ def disabled?
256
+ @disabled ||= (user_configuration_from_key('disabled') || false)
257
+ end
258
+
259
+ private
260
+
261
+ #
262
+ # Logging in rails_root/log as solr_<environment>.log as a
263
+ # default.
264
+ #
265
+ # ===== Returns
266
+ #
267
+ # String:: default_log_file_location
268
+ #
269
+ def default_log_file_location
270
+ File.join(::Rails.root, 'log', "solr_" + ::Rails.env + ".log")
271
+ end
272
+
273
+ #
274
+ # return a specific key from the user configuration in config/sunspot.yml
275
+ #
276
+ # ==== Returns
277
+ #
278
+ # Mixed:: requested_key or nil
279
+ #
280
+ def user_configuration_from_key( *keys )
281
+ keys.inject(user_configuration) do |hash, key|
282
+ hash[key] if hash
283
+ end
284
+ end
285
+
286
+ #
287
+ # Memoized hash of configuration options for the current Rails environment
288
+ # as specified in config/sunspot.yml
289
+ #
290
+ # ==== Returns
291
+ #
292
+ # Hash:: configuration options for current environment
293
+ #
294
+ def user_configuration
295
+ @user_configuration ||=
296
+ begin
297
+ path = File.join(::Rails.root, 'config', 'sunspot.yml')
298
+ if File.exist?(path)
299
+ File.open(path) do |file|
300
+ processed = ERB.new(file.read).result
301
+ YAML.load(processed)[::Rails.env]
302
+ end
303
+ else
304
+ {}
305
+ end
306
+ end
307
+ end
308
+
309
+ protected
310
+
311
+ #
312
+ # When a specific hostname, port and path aren't provided in the
313
+ # sunspot.yml file, look for a key named 'url', then check the
314
+ # environment, then fall back to a sensible localhost default.
315
+ #
316
+
317
+ def solr_url
318
+ if ENV['SOLR_URL'] || ENV['WEBSOLR_URL']
319
+ URI.parse(ENV['SOLR_URL'] || ENV['WEBSOLR_URL'])
320
+ end
321
+ end
322
+
323
+ def default_hostname
324
+ 'localhost'
325
+ end
326
+
327
+ def default_port
328
+ { 'test' => 8981,
329
+ 'development' => 8982,
330
+ 'production' => 8983
331
+ }[::Rails.env] || 8983
332
+ end
333
+
334
+ def default_path
335
+ '/solr'
336
+ end
337
+
338
+ end
339
+ end
340
+ end