outoftime-sunspot_rails 0.9.4

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 (36) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +246 -0
  3. data/Rakefile +19 -0
  4. data/VERSION.yml +4 -0
  5. data/dev_tasks/gemspec.rake +20 -0
  6. data/dev_tasks/rdoc.rake +7 -0
  7. data/dev_tasks/todo.rake +4 -0
  8. data/install.rb +1 -0
  9. data/lib/sunspot/rails/adapters.rb +54 -0
  10. data/lib/sunspot/rails/configuration.rb +73 -0
  11. data/lib/sunspot/rails/request_lifecycle.rb +18 -0
  12. data/lib/sunspot/rails/searchable.rb +256 -0
  13. data/lib/sunspot/rails.rb +11 -0
  14. data/spec/mock_app/app/controllers/application.rb +10 -0
  15. data/spec/mock_app/app/controllers/application_controller.rb +10 -0
  16. data/spec/mock_app/app/controllers/posts_controller.rb +6 -0
  17. data/spec/mock_app/app/models/blog.rb +2 -0
  18. data/spec/mock_app/app/models/post.rb +5 -0
  19. data/spec/mock_app/app/models/post_with_auto.rb +9 -0
  20. data/spec/mock_app/config/boot.rb +110 -0
  21. data/spec/mock_app/config/database.yml +4 -0
  22. data/spec/mock_app/config/environment.rb +41 -0
  23. data/spec/mock_app/config/environments/development.rb +27 -0
  24. data/spec/mock_app/config/environments/test.rb +27 -0
  25. data/spec/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  26. data/spec/mock_app/config/initializers/session_store.rb +15 -0
  27. data/spec/mock_app/config/routes.rb +43 -0
  28. data/spec/mock_app/config/sunspot.yml +8 -0
  29. data/spec/model_lifecycle_spec.rb +38 -0
  30. data/spec/model_spec.rb +181 -0
  31. data/spec/request_lifecycle_spec.rb +10 -0
  32. data/spec/schema.rb +14 -0
  33. data/spec/spec_helper.rb +23 -0
  34. data/tasks/sunspot.rake +23 -0
  35. data/tasks/sunspot_rails_tasks.rake +4 -0
  36. metadata +173 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mat Brown
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,246 @@
1
+ = Sunspot::Rails
2
+
3
+ Sunspot::Rails is a Rails plugin that provides drop-in integration of the
4
+ Sunspot[http://outoftime.github.com/sunspot] Solr search library with Rails. It
5
+ provides the following features:
6
+
7
+ * Configure Sunspot using config/sunspot.yml
8
+ * Extend ActiveRecord for easy index configuration, search, and indexing
9
+ * Automatically index ActiveRecord objects when they are saved, and remove them
10
+ from the index when they are destroyed (can be disabled)
11
+ * Automatically commit Solr changes at the end of each request
12
+ * Provide utility methods to find and fix orphaned documents and rebuild the
13
+ Solr index for a given class
14
+ * Provide rake tasks for starting and stopping the development Solr instance,
15
+ using the configuration in sunspot.yml
16
+
17
+ Sunspot::Rails has been tested with Rails versions 2.1, 2.2, and 2.3
18
+
19
+ == Installation
20
+
21
+ First, install the Sunspot and Sunspot::Rails gems:
22
+
23
+ sudo gem install outoftime-sunspot outoftime-sunspot_rails --source=http://gems.github.com
24
+
25
+ In your project's <code>config/environment.rb</code>, add the following gem dependencies:
26
+
27
+ config.gem 'outoftime-sunspot', :lib => 'sunspot'
28
+ config.gem 'outoftime-sunspot_rails', :lib => 'sunspot/rails'
29
+
30
+ If you are using an older version of Rails that doesn't support plugins-as-gems,
31
+ use:
32
+
33
+ script/plugin install git://github.com/outoftime/sunspot_rails.git
34
+
35
+ Create the file <code>config/sunspot.yml</code> and set it up for your environments. Here is a sample:
36
+
37
+ common: &common
38
+ solr:
39
+ hostname: localhost
40
+ port: 8983
41
+
42
+ production:
43
+ <<: *common
44
+
45
+ development:
46
+ <<: *common
47
+ solr:
48
+ port: 8982
49
+
50
+ test:
51
+ <<: *common
52
+ solr:
53
+ port: 8981
54
+
55
+ To start up a Solr instance, issue the following:
56
+
57
+ rake sunspot:solr:start
58
+
59
+ Note that using the built-in Solr instance packaged with Sunspot is great for
60
+ development, but is not recommended for production. See the Sunspot
61
+ documentation for more information.
62
+
63
+ == Usage
64
+
65
+ === Setup
66
+
67
+ In order for an ActiveRecord model to be indexable and searchable, it must be
68
+ configured for search. For example:
69
+
70
+ class Post < ActiveRecord::Base
71
+ searchable do
72
+ text :title, :body
73
+ integer :blog_id
74
+ time :updated_at
75
+ string :sort_title do
76
+ title.downcase.sub(/^(an?|the) /, '')
77
+ end
78
+ end
79
+ end
80
+
81
+ See the documentation for Sunspot.setup for full details on what can go in the
82
+ configuration block.
83
+
84
+ === Indexing
85
+
86
+ By default, models are indexed whenever they are saved, and removed from the
87
+ index whenever they are destroyed. This behavior can be disabled:
88
+
89
+ class Post < ActiveRecord::Base
90
+ searchable :auto_index => false, :auto_remove => false do
91
+ # setup...
92
+ end
93
+ end
94
+
95
+ Note that <b>using the <code>:auto_remove</code> option is not recommended
96
+ </b>, as destroying an object without removing it from the index will
97
+ create an orphaned document in the index, which is a Bad Thing. Turning off
98
+ <code>:auto_index</code> is perfectly safe if you prefer to manage indexing manually
99
+ (perhaps using a background job).
100
+
101
+ If you have disabled lifecycle indexing hooks, you can invoke indexing
102
+ operations directly on your model:
103
+
104
+ post = Post.create
105
+ post.index
106
+ post.remove_from_index
107
+
108
+ === Committing
109
+
110
+ When data is changed in Solr, it is initially stored in memory and not made
111
+ available to the currently running searcher instance. Issuing a +commit+ to Solr
112
+ will cause it to write the changes to disk, and instantiate a new searcher
113
+ instance. This operation is fairly expensive, so rather than issuing a commit
114
+ every time a document is added or removed, Sunspot::Rails issues a commit at
115
+ the end of any request where data has been added to or removed from Solr. If
116
+ you need to immediately issue a commit, bang!-versions of the methods are
117
+ available:
118
+
119
+ post = Post.create
120
+ post.index!
121
+ # this is the same as...
122
+ post.index
123
+ Sunspot.commit
124
+
125
+ When writing tests outside of the context of a controller request, you will want
126
+ to use one of these two approaches.
127
+
128
+ === Searching
129
+
130
+ Do it like this:
131
+
132
+ Post.search do
133
+ with :blog_id, 1
134
+ with(:updated_at).greater_than(Time.now - 2.weeks)
135
+ order :sort_title, :asc
136
+ paginate :page => 1, :per_page => 15
137
+ end
138
+
139
+ See the documentation for <code>Sunspot.search</code> for all the options
140
+ available in the search block, and the information available in the result
141
+ block.
142
+
143
+ === Searching for IDs
144
+
145
+ In some situations, you may want to get the IDs for models returned by a search
146
+ without actually loading the models out of the database. For that, you can
147
+ call +search_ids+, using the same block format as #search. This will return an
148
+ array of IDs.
149
+
150
+
151
+ === Searching for multiple types
152
+
153
+ Sunspot is entirely agnostic about whether searches are for one or more types;
154
+ the only restriction is that columns used for restriction, ordering, etc. are
155
+ defined in the same way for all types being searched. Sunspot::Rails does not
156
+ provide any additional support for this, since there is not anything useful to
157
+ be added, so just use the interface provided by Sunspot:
158
+
159
+ Sunspot.search(Post, Comment) do
160
+ with :blog_id, 1
161
+ order :created_at, :asc
162
+ end
163
+
164
+ Be sure to check out the Sunspot documentation for all the details.
165
+
166
+ === Adding search functionality in mixins
167
+
168
+ Sunspot does not require that search setup for a given class happen all in one
169
+ place; it is perfectly acceptable to call the <code>Sunspot.setup</code> method
170
+ more than once. This capability is particularly useful for adding search
171
+ functionality in mixins. For instance, if you have a +Ratable+ module, you may
172
+ wish to add additional search fields for searchable classes that mix in that
173
+ module. For example:
174
+
175
+ module Ratable
176
+ def self.included(base)
177
+ if base.searchable?
178
+ base.searchable do
179
+ float :average_rating do
180
+ ratings.average(:value)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ Note the use of <code>base.searchable?</code> - this ensures that only classes
188
+ that already have search enabled will have the additional configuration added.
189
+ The above pattern requires that the class be declared searchable before the
190
+ module is mixed in; other patterns (such as passing a :searchable option to an
191
+ acts_as_-style declaration) may be more flexible.
192
+
193
+ === Utility methods
194
+
195
+ If you need to completely reindex all of the instances of a given model class,
196
+ you can issue:
197
+
198
+ Post.reindex
199
+
200
+ If for some reason models get deleted from the database, but not from the index,
201
+ they will become index orphans - not a good situation. To get IDs that exist in
202
+ the index but not the database, you can use the +index_orphans+ method; to
203
+ remove those documents from the index, use +clean_index_orphans+. Note that
204
+ neither of these operations should be needed if Sunspot and Sunspot::Rails are
205
+ used as intended.
206
+
207
+ == Further Reading
208
+
209
+ Reading the {Sunspot documentation}[http://outoftime.github.com/sunspot/docs] is
210
+ highly recommended. Sunspot::Rails exists to wrap Sunspot with a Rails-friendly
211
+ API, but almost all of the functionality you use in Sunspot::Rails is
212
+ implemented in Sunspot.
213
+
214
+ Posts about Sunspot on my blog are available at http://outofti.me/tagged/sunspot
215
+
216
+ == Bugs
217
+
218
+ Please submit bug reports to
219
+ http://outoftime.lighthouseapp.com/projects/20339-sunspot
220
+
221
+ == Contributors
222
+
223
+ Mat Brown <mat@patch.com>
224
+
225
+ == MIT License
226
+
227
+ Copyright (c) 2009 Mat Brown
228
+
229
+ Permission is hereby granted, free of charge, to any person obtaining
230
+ a copy of this software and associated documentation files (the
231
+ "Software"), to deal in the Software without restriction, including
232
+ without limitation the rights to use, copy, modify, merge, publish,
233
+ distribute, sublicense, and/or sell copies of the Software, and to
234
+ permit persons to whom the Software is furnished to do so, subject to
235
+ the following conditions:
236
+
237
+ The above copyright notice and this permission notice shall be
238
+ included in all copies or substantial portions of the Software.
239
+
240
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
241
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
242
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
243
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
244
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
245
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
246
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+ require 'rake/rdoctask'
4
+
5
+ task :default => :spec
6
+
7
+ desc 'Run all specs'
8
+ Spec::Rake::SpecTask.new(:spec) do |t|
9
+ t.spec_files = FileList['spec/**/*_spec.rb']
10
+ t.spec_opts << '--color'
11
+ end
12
+
13
+ task :environment do
14
+ ENV['RAILS_ROOT'] ||= File.join(File.dirname(__FILE__), 'spec', 'mock_app')
15
+ ENV['RAILS_ENV'] ||= 'test'
16
+ require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config', 'environment.rb'))
17
+ end
18
+
19
+ FileList['dev_tasks/*.rake', 'tasks/*.rake'].each { |file| load(file) }
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 4
3
+ :major: 0
4
+ :minor: 9
@@ -0,0 +1,20 @@
1
+ begin
2
+ gem 'technicalpickles-jeweler', '~> 1.0'
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |s|
5
+ s.name = 'sunspot_rails'
6
+ s.summary = 'Rails integration for the Sunspot Solr search library'
7
+ s.email = 'mat@patch.com'
8
+ s.homepage = 'http://github.com/outoftime/sunspot_rails'
9
+ s.description = 'Rails integration for the Sunspot Solr search library'
10
+ s.authors = ['Mat Brown']
11
+ s.files = FileList['[A-Z]*', '{lib,spec,tasks,dev_tasks}/**/*', 'install.rb', 'MIT-LICENSE', 'rails']
12
+ s.add_dependency 'rails', '~> 2.1'
13
+ s.add_dependency 'escape', '>= 0.0.4'
14
+ s.add_dependency 'outoftime-sunspot', '>= 0.7.2'
15
+ s.add_development_dependency 'rspec', '~> 1.2'
16
+ s.add_development_dependency 'rspec-rails', '~> 1.2'
17
+ s.add_development_dependency 'ruby-debug', '~> 0.10'
18
+ s.add_development_dependency 'technicalpickles-jeweler', '~> 1.0'
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ require 'rake/rdoctask'
2
+
3
+ Rake::RDocTask.new(:doc) do |rdoc|
4
+ rdoc.main = 'README.rdoc'
5
+ rdoc.rdoc_files.include('README.rdoc', 'lib/sunspot/rails/**/*.rb')
6
+ rdoc.rdoc_dir = 'doc'
7
+ end
@@ -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
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,54 @@
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
+ #
23
+ # Get one ActiveRecord instance out of the database by ID
24
+ #
25
+ # ==== Parameters
26
+ #
27
+ # id<String>:: Database ID of model to retreive
28
+ #
29
+ # ==== Returns
30
+ #
31
+ # ActiveRecord::Base:: ActiveRecord model
32
+ #
33
+ def load(id)
34
+ @clazz.find(id)
35
+ end
36
+
37
+ #
38
+ # Get a collection of ActiveRecord instances out of the database by ID
39
+ #
40
+ # ==== Parameters
41
+ #
42
+ # ids<Array>:: Database IDs of models to retrieve
43
+ #
44
+ # ==== Returns
45
+ #
46
+ # Array:: Collection of ActiveRecord models
47
+ #
48
+ def load(ids)
49
+ @clazz.find(ids)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,73 @@
1
+ module Sunspot #:nodoc:
2
+ module Rails #:nodoc:
3
+ #
4
+ # Sunspot::Rails is configured via the config/sunspot.yml file, which
5
+ # contains properties keyed by environment name. A sample sunspot.yml file
6
+ # would look like:
7
+ #
8
+ # development:
9
+ # solr:
10
+ # hostname: localhost
11
+ # port: 8982
12
+ # test:
13
+ # solr:
14
+ # hostname: localhost
15
+ # port: 8983
16
+ #
17
+ # Sunspot::Rails uses the configuration to set up the Solr connection, as
18
+ # well as for starting Solr with the appropriate port using the
19
+ # <code>rake sunspot:solr:start</code> task.
20
+ #
21
+ class Configuration
22
+ #
23
+ # The host name at which to connect to Solr. Default 'localhost'.
24
+ #
25
+ # ==== Returns
26
+ #
27
+ # String:: host name
28
+ #
29
+ def hostname
30
+ @hostname ||=
31
+ if user_configuration.has_key?('solr')
32
+ user_configuration['solr']['hostname']
33
+ end || 'localhost'
34
+ end
35
+
36
+ #
37
+ # The port at which to connect to Solr. Default 8983.
38
+ #
39
+ # ==== Returns
40
+ #
41
+ # Integer:: port
42
+ #
43
+ def port
44
+ @port ||=
45
+ if user_configuration.has_key?('solr')
46
+ user_configuration['solr']['port']
47
+ end || 8983
48
+ end
49
+
50
+ private
51
+
52
+ #
53
+ # Memoized hash of configuration options for the current Rails environment
54
+ # as specified in config/sunspot.yml
55
+ #
56
+ # ==== Returns
57
+ #
58
+ # Hash:: configuration options for current environment
59
+ #
60
+ def user_configuration
61
+ @user_configuration ||=
62
+ begin
63
+ path = File.join(::Rails.root, 'config', 'sunspot.yml')
64
+ if File.exist?(path)
65
+ File.open(path) do |file|
66
+ YAML.load(file)[::Rails.env]
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,18 @@
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
+ base.after_filter do
12
+ Sunspot.commit_if_dirty
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end