robsharp-sunspot_rails 1.1.0.2
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/History.txt +40 -0
- data/LICENSE +18 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +256 -0
- data/Rakefile +27 -0
- data/TODO +8 -0
- data/VERSION.yml +4 -0
- data/dev_tasks/gemspec.rake +33 -0
- data/dev_tasks/rdoc.rake +24 -0
- data/dev_tasks/release.rake +4 -0
- data/dev_tasks/todo.rake +4 -0
- data/generators/sunspot/sunspot_generator.rb +9 -0
- data/generators/sunspot/templates/sunspot.yml +18 -0
- data/install.rb +1 -0
- data/lib/sunspot/rails.rb +58 -0
- data/lib/sunspot/rails/adapters.rb +160 -0
- data/lib/sunspot/rails/configuration.rb +272 -0
- data/lib/sunspot/rails/request_lifecycle.rb +31 -0
- data/lib/sunspot/rails/searchable.rb +464 -0
- data/lib/sunspot/rails/server.rb +173 -0
- data/lib/sunspot/rails/solr_logging.rb +58 -0
- data/lib/sunspot/rails/spec_helper.rb +19 -0
- data/lib/sunspot/rails/stub_session_proxy.rb +88 -0
- data/lib/sunspot/rails/tasks.rb +62 -0
- data/lib/sunspot/rails/version.rb +5 -0
- data/rails/init.rb +10 -0
- data/spec/configuration_spec.rb +102 -0
- data/spec/mock_app/app/controllers/application.rb +10 -0
- data/spec/mock_app/app/controllers/application_controller.rb +10 -0
- data/spec/mock_app/app/controllers/posts_controller.rb +6 -0
- data/spec/mock_app/app/models/author.rb +8 -0
- data/spec/mock_app/app/models/blog.rb +12 -0
- data/spec/mock_app/app/models/location.rb +2 -0
- data/spec/mock_app/app/models/photo_post.rb +2 -0
- data/spec/mock_app/app/models/post.rb +10 -0
- data/spec/mock_app/app/models/post_with_auto.rb +10 -0
- data/spec/mock_app/config/boot.rb +110 -0
- data/spec/mock_app/config/database.yml +4 -0
- data/spec/mock_app/config/environment.rb +42 -0
- data/spec/mock_app/config/environments/development.rb +27 -0
- data/spec/mock_app/config/environments/test.rb +27 -0
- data/spec/mock_app/config/initializers/new_rails_defaults.rb +19 -0
- data/spec/mock_app/config/initializers/session_store.rb +15 -0
- data/spec/mock_app/config/routes.rb +43 -0
- data/spec/mock_app/config/sunspot.yml +19 -0
- data/spec/mock_app/db/schema.rb +27 -0
- data/spec/model_lifecycle_spec.rb +63 -0
- data/spec/model_spec.rb +409 -0
- data/spec/request_lifecycle_spec.rb +52 -0
- data/spec/schema.rb +27 -0
- data/spec/server_spec.rb +36 -0
- data/spec/session_spec.rb +24 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/stub_session_proxy_spec.rb +122 -0
- metadata +170 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Rails
|
3
|
+
class Server < Sunspot::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
|
+
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
|
+
#
|
41
|
+
# Directory in which to store PID files
|
42
|
+
#
|
43
|
+
def pid_dir
|
44
|
+
File.join(::Rails.root, 'tmp', 'pids')
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Name of the PID file
|
49
|
+
#
|
50
|
+
def pid_file
|
51
|
+
"sunspot-solr-#{::Rails.env}.pid"
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Directory to store lucene index data files
|
56
|
+
#
|
57
|
+
# ==== Returns
|
58
|
+
#
|
59
|
+
# String:: data_path
|
60
|
+
#
|
61
|
+
def solr_data_dir
|
62
|
+
File.join(solr_home, 'data', ::Rails.env)
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Directory to use for Solr home.
|
67
|
+
#
|
68
|
+
def solr_home
|
69
|
+
File.join(::Rails.root, 'solr')
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Solr start jar
|
74
|
+
#
|
75
|
+
def solr_jar
|
76
|
+
configuration.solr_jar || super
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Port on which to run Solr
|
81
|
+
#
|
82
|
+
def port
|
83
|
+
configuration.port
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Severity level for logging. This is based on the severity level for the
|
88
|
+
# Rails logger.
|
89
|
+
#
|
90
|
+
def log_level
|
91
|
+
LOG_LEVELS[::Rails.logger.level]
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Log file for Solr. File is in the rails log/ directory.
|
96
|
+
#
|
97
|
+
def log_file
|
98
|
+
File.join(::Rails.root, 'log', "sunspot-solr-#{::Rails.env}.log")
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Minimum Java heap size for Solr
|
103
|
+
#
|
104
|
+
def min_memory
|
105
|
+
configuration.min_memory
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Maximum Java heap size for Solr
|
110
|
+
#
|
111
|
+
def max_memory
|
112
|
+
configuration.max_memory
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
#
|
118
|
+
# access to the Sunspot::Rails::Configuration, defined in
|
119
|
+
# sunspot.yml. Use Sunspot::Rails.configuration if you want
|
120
|
+
# to access the configuration directly.
|
121
|
+
#
|
122
|
+
# ==== returns
|
123
|
+
#
|
124
|
+
# Sunspot::Rails::Configuration:: configuration
|
125
|
+
#
|
126
|
+
def configuration
|
127
|
+
Sunspot::Rails.configuration
|
128
|
+
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
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Rails
|
3
|
+
module SolrLogging
|
4
|
+
class <<self
|
5
|
+
def included(base)
|
6
|
+
base.module_eval { alias_method_chain(:request, :rails_logging) }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def request_with_rails_logging(path, params={}, *extra)
|
11
|
+
|
12
|
+
# Set up logging text.
|
13
|
+
body = (params.nil? || params.empty?) ? extra.first : params.inspect
|
14
|
+
action = path[1..-1].capitalize
|
15
|
+
if body == "<commit/>"
|
16
|
+
action = 'Commit'
|
17
|
+
body = ''
|
18
|
+
end
|
19
|
+
body = body[0, 800] + '...' if body.length > 800
|
20
|
+
|
21
|
+
# Make request and log.
|
22
|
+
response = nil
|
23
|
+
begin
|
24
|
+
ms = Benchmark.ms do
|
25
|
+
response = request_without_rails_logging(path, params, *extra)
|
26
|
+
end
|
27
|
+
log_name = 'Solr %s (%.1fms)' % [action, ms]
|
28
|
+
::Rails.logger.debug(format_log_entry(log_name, body))
|
29
|
+
rescue Exception => e
|
30
|
+
log_name = 'Solr %s (Error)' % [action]
|
31
|
+
::Rails.logger.error(format_log_entry(log_name, body))
|
32
|
+
raise e
|
33
|
+
end
|
34
|
+
|
35
|
+
response
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def format_log_entry(message, dump = nil)
|
41
|
+
if ActiveRecord::Base.colorize_logging
|
42
|
+
message_color, dump_color = "4;32;1", "0;1"
|
43
|
+
log_entry = " \e[#{message_color}m#{message}\e[0m "
|
44
|
+
log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump
|
45
|
+
log_entry
|
46
|
+
else
|
47
|
+
"%s %s" % [message, dump]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
RSolr::Client.module_eval { include(Sunspot::Rails::SolrLogging) }
|
56
|
+
rescue NameError # RSolr 0.9.6
|
57
|
+
RSolr::Connection::Base.module_eval { include(Sunspot::Rails::SolrLogging) }
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Rails
|
3
|
+
module SpecHelper
|
4
|
+
def disconnect_sunspot
|
5
|
+
before(:each) do
|
6
|
+
Sunspot.session = StubSessionProxy.new(Sunspot.session)
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:each) do
|
10
|
+
Sunspot.session = Sunspot.session.original_session
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Spec::Runner.configure do |config|
|
18
|
+
config.extend(Sunspot::Rails::SpecHelper)
|
19
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Rails
|
3
|
+
class StubSessionProxy
|
4
|
+
attr_reader :original_session
|
5
|
+
|
6
|
+
def initialize(original_session)
|
7
|
+
@original_session = original_session
|
8
|
+
end
|
9
|
+
|
10
|
+
def index(*objects)
|
11
|
+
end
|
12
|
+
|
13
|
+
def index!(*objects)
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(*objects)
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove!(*objects)
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove_by_id(clazz, id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_by_id!(clazz, id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_all(clazz = nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_all!(clazz = nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
def dirty?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_dirty?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def commit_if_dirty
|
43
|
+
end
|
44
|
+
|
45
|
+
def commit_if_delete_dirty
|
46
|
+
end
|
47
|
+
|
48
|
+
def commit
|
49
|
+
end
|
50
|
+
|
51
|
+
def search(*types)
|
52
|
+
Search.new
|
53
|
+
end
|
54
|
+
|
55
|
+
def new_search(*types)
|
56
|
+
Search.new
|
57
|
+
end
|
58
|
+
|
59
|
+
class Search
|
60
|
+
def build
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def results
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
|
68
|
+
def hits(options = {})
|
69
|
+
[]
|
70
|
+
end
|
71
|
+
|
72
|
+
def total
|
73
|
+
0
|
74
|
+
end
|
75
|
+
|
76
|
+
def facet(name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def dynamic_facet(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def execute
|
83
|
+
self
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
namespace :sunspot do
|
2
|
+
namespace :solr do
|
3
|
+
desc 'Start the Solr instance'
|
4
|
+
task :start => :environment do
|
5
|
+
if RUBY_PLATFORM =~ /w(in)?32$/
|
6
|
+
abort('This command does not work on Windows. Please use rake sunspot:solr:run to run Solr in the foreground.')
|
7
|
+
end
|
8
|
+
Sunspot::Rails::Server.new.start
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Run the Solr instance in the foreground'
|
12
|
+
task :run => :environment do
|
13
|
+
Sunspot::Rails::Server.new.run
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Stop the Solr instance'
|
17
|
+
task :stop => :environment do
|
18
|
+
if RUBY_PLATFORM =~ /w(in)?32$/
|
19
|
+
abort('This command does not work on Windows.')
|
20
|
+
end
|
21
|
+
Sunspot::Rails::Server.new.stop
|
22
|
+
end
|
23
|
+
|
24
|
+
task :reindex => :"sunspot:reindex"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Reindex all solr models that are located in your application's models directory."
|
28
|
+
# This task depends on the standard Rails file naming \
|
29
|
+
# conventions, in that the file name matches the defined class name. \
|
30
|
+
# By default the indexing system works in batches of 500 records, you can \
|
31
|
+
# set your own value for this by using the batch_size argument. You can \
|
32
|
+
# also optionally define a list of models to separated by a forward slash '/'
|
33
|
+
#
|
34
|
+
# $ rake sunspot:reindex # reindex all models
|
35
|
+
# $ rake sunspot:reindex[1000] # reindex in batches of 1000
|
36
|
+
# $ rake sunspot:reindex[false] # reindex without batching
|
37
|
+
# $ rake sunspot:reindex[,Post] # reindex only the Post model
|
38
|
+
# $ rake sunspot:reindex[1000,Post] # reindex only the Post model in
|
39
|
+
# # batchs of 1000
|
40
|
+
# $ rake sunspot:reindex[,Post+Author] # reindex Post and Author model
|
41
|
+
task :reindex, :batch_size, :models, :needs => :environment do |t, args|
|
42
|
+
reindex_options = {:batch_commit => false}
|
43
|
+
case args[:batch_size]
|
44
|
+
when 'false'
|
45
|
+
reindex_options[:batch_size] = nil
|
46
|
+
when /^\d+$/
|
47
|
+
reindex_options[:batch_size] = args[:batch_size].to_i if args[:batch_size].to_i > 0
|
48
|
+
end
|
49
|
+
unless args[:models]
|
50
|
+
all_files = Dir.glob(File.join(RAILS_ROOT, 'app', 'models', '*.rb'))
|
51
|
+
all_models = all_files.map { |path| File.basename(path, '.rb').camelize.constantize }
|
52
|
+
# FIXME: Workaround the hack
|
53
|
+
sunspot_models = all_models #.select { |m| m < ActiveRecord::Base and m.searchable? }
|
54
|
+
else
|
55
|
+
sunspot_models = args[:models].split('+').map{|m| m.constantize}
|
56
|
+
end
|
57
|
+
sunspot_models.each do |model|
|
58
|
+
model.solr_reindex reindex_options
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'sunspot'
|
2
|
+
|
3
|
+
Sunspot.session = Sunspot::Rails.build_session
|
4
|
+
Sunspot::Adapters::InstanceAdapter.register(Sunspot::Rails::Adapters::ActiveRecordInstanceAdapter, ActiveRecord::Base)
|
5
|
+
Sunspot::Adapters::InstanceAdapter.register(Sunspot::Rails::Adapters::ActiveResourceInstanceAdapter, ActiveResource::Base)
|
6
|
+
Sunspot::Adapters::DataAccessor.register(Sunspot::Rails::Adapters::ActiveRecordDataAccessor, ActiveRecord::Base)
|
7
|
+
Sunspot::Adapters::DataAccessor.register(Sunspot::Rails::Adapters::ActiveResourceDataAccessor, ActiveResource::Base)
|
8
|
+
ActiveRecord::Base.module_eval { include(Sunspot::Rails::Searchable) }
|
9
|
+
ActiveResource::Base.module_eval { include(Sunspot::Rails::Searchable) }
|
10
|
+
ActionController::Base.module_eval { include(Sunspot::Rails::RequestLifecycle) }
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Sunspot::Rails::Configuration, "default values" do
|
4
|
+
before(:each) do
|
5
|
+
File.should_receive(:exist?).at_least(:once).and_return(false)
|
6
|
+
@config = Sunspot::Rails::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should handle the 'hostname' property when not set" do
|
10
|
+
@config.hostname.should == 'localhost'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should handle the 'path' property when not set" do
|
14
|
+
@config.path.should == '/solr'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should handle the 'port' property when not set" do
|
18
|
+
@config.port.should == 8983
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should handle the 'log_level' property when not set" do
|
22
|
+
@config.log_level.should == 'INFO'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should handle the 'log_file' property" do
|
26
|
+
@config.log_file.should =~ /log\/solr_test.log/
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should handle the 'solr_home' property when not set" do
|
30
|
+
Rails.should_receive(:root).at_least(1).and_return('/some/path')
|
31
|
+
@config.solr_home.should == '/some/path/solr'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should handle the 'data_path' property when not set" do
|
35
|
+
Rails.should_receive(:root).at_least(1).and_return('/some/path')
|
36
|
+
@config.data_path.should == '/some/path/solr/data/test'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should handle the 'pid_path' property when not set" do
|
40
|
+
Rails.should_receive(:root).at_least(1).and_return('/some/path')
|
41
|
+
@config.pid_path.should == '/some/path/solr/pids/test'
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should handle the 'solr_home' property when not set" do
|
45
|
+
@config.solr_home.should_not == nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should handle the 'auto_commit_after_request' propery when not set" do
|
49
|
+
@config.auto_commit_after_request?.should == true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should handle the 'auto_commit_after_delete_request' propery when not set" do
|
53
|
+
@config.auto_commit_after_delete_request?.should == false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe Sunspot::Rails::Configuration, "user settings" do
|
58
|
+
before(:each) do
|
59
|
+
::Rails.stub!(:env => 'config_test')
|
60
|
+
@config = Sunspot::Rails::Configuration.new
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should handle the 'hostname' property when set" do
|
64
|
+
@config.hostname.should == 'some.host'
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should handle the 'port' property when set" do
|
68
|
+
@config.port.should == 1234
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should handle the 'path' property when set" do
|
72
|
+
@config.path.should == '/solr/idx'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should handle the 'log_level' propery when set" do
|
76
|
+
@config.log_level.should == 'WARNING'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should handle the 'solr_home' propery when set" do
|
80
|
+
@config.solr_home.should == '/my_superior_path'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should handle the 'data_path' property when set" do
|
84
|
+
@config.data_path.should == '/my_superior_path/data'
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should handle the 'pid_path' property when set" do
|
88
|
+
@config.pid_path.should == '/my_superior_path/pids'
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should handle the 'solr_home' property when set" do
|
92
|
+
@config.solr_home.should == '/my_superior_path'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should handle the 'auto_commit_after_request' propery when set" do
|
96
|
+
@config.auto_commit_after_request?.should == false
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should handle the 'auto_commit_after_delete_request' propery when set" do
|
100
|
+
@config.auto_commit_after_delete_request?.should == true
|
101
|
+
end
|
102
|
+
end
|