kuahyeow-sunspot 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +83 -0
- data/LICENSE +18 -0
- data/README.rdoc +154 -0
- data/Rakefile +9 -0
- data/TODO +9 -0
- data/VERSION.yml +4 -0
- data/bin/sunspot-configure-solr +46 -0
- data/bin/sunspot-solr +62 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/composite_setup.rb +186 -0
- data/lib/sunspot/configuration.rb +38 -0
- data/lib/sunspot/data_extractor.rb +47 -0
- data/lib/sunspot/date_facet.rb +36 -0
- data/lib/sunspot/date_facet_row.rb +17 -0
- data/lib/sunspot/dsl/field_query.rb +72 -0
- data/lib/sunspot/dsl/fields.rb +86 -0
- data/lib/sunspot/dsl/query.rb +59 -0
- data/lib/sunspot/dsl/query_facet.rb +31 -0
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/scope.rb +193 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/dsl.rb +3 -0
- data/lib/sunspot/facet.rb +51 -0
- data/lib/sunspot/facet_row.rb +34 -0
- data/lib/sunspot/field.rb +157 -0
- data/lib/sunspot/field_factory.rb +126 -0
- data/lib/sunspot/indexer.rb +123 -0
- data/lib/sunspot/instantiated_facet.rb +38 -0
- data/lib/sunspot/instantiated_facet_row.rb +12 -0
- data/lib/sunspot/query/base_query.rb +94 -0
- data/lib/sunspot/query/connective.rb +126 -0
- data/lib/sunspot/query/dynamic_query.rb +69 -0
- data/lib/sunspot/query/field_facet.rb +149 -0
- data/lib/sunspot/query/field_query.rb +57 -0
- data/lib/sunspot/query/pagination.rb +39 -0
- data/lib/sunspot/query/query_facet.rb +72 -0
- data/lib/sunspot/query/query_facet_row.rb +19 -0
- data/lib/sunspot/query/restriction.rb +233 -0
- data/lib/sunspot/query/scope.rb +165 -0
- data/lib/sunspot/query/sort.rb +36 -0
- data/lib/sunspot/query/sort_composite.rb +33 -0
- data/lib/sunspot/query.rb +190 -0
- data/lib/sunspot/query_facet.rb +33 -0
- data/lib/sunspot/query_facet_row.rb +21 -0
- data/lib/sunspot/schema.rb +165 -0
- data/lib/sunspot/search/hit.rb +66 -0
- data/lib/sunspot/search.rb +220 -0
- data/lib/sunspot/session.rb +201 -0
- data/lib/sunspot/setup.rb +271 -0
- data/lib/sunspot/type.rb +200 -0
- data/lib/sunspot/util.rb +164 -0
- data/lib/sunspot.rb +470 -0
- data/solr/etc/jetty.xml +212 -0
- data/solr/etc/webdefault.xml +379 -0
- data/solr/lib/jetty-6.1.3.jar +0 -0
- data/solr/lib/jetty-util-6.1.3.jar +0 -0
- data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr/solr/conf/elevate.xml +36 -0
- data/solr/solr/conf/protwords.txt +21 -0
- data/solr/solr/conf/schema.xml +50 -0
- data/solr/solr/conf/solrconfig.xml +696 -0
- data/solr/solr/conf/stopwords.txt +57 -0
- data/solr/solr/conf/synonyms.txt +31 -0
- data/solr/start.jar +0 -0
- data/solr/webapps/solr.war +0 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/build_search_spec.rb +1018 -0
- data/spec/api/indexer_spec.rb +311 -0
- data/spec/api/query_spec.rb +153 -0
- data/spec/api/search_retrieval_spec.rb +335 -0
- data/spec/api/session_spec.rb +157 -0
- data/spec/api/spec_helper.rb +1 -0
- data/spec/api/sunspot_spec.rb +18 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/integration/faceting_spec.rb +169 -0
- data/spec/integration/keyword_search_spec.rb +83 -0
- data/spec/integration/scoped_search_spec.rb +289 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/stored_fields_spec.rb +10 -0
- data/spec/integration/test_pagination.rb +32 -0
- data/spec/mocks/adapters.rb +32 -0
- data/spec/mocks/blog.rb +3 -0
- data/spec/mocks/comment.rb +19 -0
- data/spec/mocks/connection.rb +84 -0
- data/spec/mocks/mock_adapter.rb +30 -0
- data/spec/mocks/mock_record.rb +48 -0
- data/spec/mocks/photo.rb +8 -0
- data/spec/mocks/post.rb +73 -0
- data/spec/mocks/user.rb +8 -0
- data/spec/spec_helper.rb +47 -0
- data/tasks/gemspec.rake +24 -0
- data/tasks/rcov.rake +28 -0
- data/tasks/rdoc.rake +22 -0
- data/tasks/schema.rake +19 -0
- data/tasks/spec.rake +24 -0
- data/tasks/todo.rake +4 -0
- data/templates/schema.xml.haml +24 -0
- metadata +247 -0
data/History.txt
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
== 0.9.0 2009-07-21
|
2
|
+
* Use Dismax parser for keyword search
|
3
|
+
* Field and document boosting
|
4
|
+
* Specify which fields to search in keyword search
|
5
|
+
* Allow indexing of multiple values in text fields
|
6
|
+
* Access keyword relevance score in Hit objects
|
7
|
+
* Allow stored fields, retrieve stored values from Hit objects
|
8
|
+
* Support more values in shorthand restrictions
|
9
|
+
* Disjunctions and conjunctions
|
10
|
+
* Random ordering
|
11
|
+
* Control all options for field facets
|
12
|
+
* Time range facets
|
13
|
+
* Get referenced objects from facets on foreign keys
|
14
|
+
* Facet by class
|
15
|
+
* Batch indexing
|
16
|
+
* New Date field type
|
17
|
+
* Direct access to data accessors
|
18
|
+
* Executable to configure production Solr instances
|
19
|
+
* Replace solr-ruby with RSolr
|
20
|
+
* Remove accidental ActiveSupport dependency
|
21
|
+
|
22
|
+
== 0.8.9 2009-06-23
|
23
|
+
* Fix OrderedHash bug in older versions of ActiveSupport
|
24
|
+
|
25
|
+
== 0.8.8 2009-06-15
|
26
|
+
* Escape type names to support namespaced classes
|
27
|
+
* Fix bug with anonymous modules in Ruby 1.9
|
28
|
+
|
29
|
+
== 0.8.7 2009-06-10
|
30
|
+
* Add --pid-dir option for sunspot-solr executable
|
31
|
+
|
32
|
+
== 0.8.5 2009-06-09
|
33
|
+
* Added dependencies for sunspot-solr executable to gem dependencies
|
34
|
+
* Search for adapters using class ancestors rather than superclasses
|
35
|
+
|
36
|
+
== 0.8.3 2009-06-03
|
37
|
+
* Index objects passed as a collection in a single HTTP request
|
38
|
+
|
39
|
+
== 0.8.2 2009-05-27
|
40
|
+
* Allow specification of Solr home when using sunspot-solr
|
41
|
+
|
42
|
+
== 0.8.1 2009-05-26
|
43
|
+
* Add Search#execute! to public API
|
44
|
+
|
45
|
+
== 0.8.0 2009-05-22
|
46
|
+
* Access query API directly; instantiate search without running it
|
47
|
+
* Dynamic fields
|
48
|
+
* Search blocks can be evaluated in calling context
|
49
|
+
|
50
|
+
== 0.7.3 2009-05-06
|
51
|
+
* Better exception handling when class doesn't have adapter/setup
|
52
|
+
|
53
|
+
== 0.7.2 2009-04-29
|
54
|
+
* Dirty sessions
|
55
|
+
|
56
|
+
== 0.7.1 2009-04-29
|
57
|
+
* Removed extlib dependency from gemspec
|
58
|
+
|
59
|
+
== 0.7.0 2009-04-28
|
60
|
+
* Less magic in the DSL
|
61
|
+
* Restrict by empty values
|
62
|
+
* Negative scoping using without() method
|
63
|
+
* Exclusion by object identity using without(instance)
|
64
|
+
* Support for faceting
|
65
|
+
* Explicit commits
|
66
|
+
* Boolean field type
|
67
|
+
* Attribute field flexibility
|
68
|
+
* Virtual field blocks can be evaluated in calling context
|
69
|
+
* Order available by multiple fields
|
70
|
+
* New adapter API
|
71
|
+
* Got rid of builder API
|
72
|
+
* Full documentation
|
73
|
+
|
74
|
+
== 0.0.2 2009-02-14
|
75
|
+
* Run sunspot's built-in Solr instance using
|
76
|
+
sunspot-solr executable
|
77
|
+
* Search hash interpretation delegated to
|
78
|
+
Builder object
|
79
|
+
|
80
|
+
== 0.0.1 2008-12-11
|
81
|
+
* Initial release
|
82
|
+
* Define indexing for any class using DSL
|
83
|
+
* Search indexed classes using DSL
|
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
2
|
+
a copy of this software and associated documentation files (the
|
3
|
+
'Software'), to deal in the Software without restriction, including
|
4
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
5
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
6
|
+
permit persons to whom the Software is furnished to do so, subject to
|
7
|
+
the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be
|
10
|
+
included in all copies or substantial portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
13
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
14
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
15
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
16
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
17
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
18
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
= Sunspot
|
2
|
+
|
3
|
+
http://outoftime.github.com/sunspot
|
4
|
+
|
5
|
+
Sunspot is a Ruby library for expressive, powerful interaction with the Solr search engine.
|
6
|
+
Sunspot is built on top of the RSolr gem, which provides a low-level interface for Solr
|
7
|
+
interaction; Sunspot provides a simple, intuitive, expressive DSL backed by powerful
|
8
|
+
features for indexing objects and searching for them.
|
9
|
+
|
10
|
+
Sunspot is designed to be easily plugged in to any ORM, or even non-database-backed
|
11
|
+
objects such as the filesystem.
|
12
|
+
|
13
|
+
=== Features:
|
14
|
+
|
15
|
+
* Define indexing strategy for each searchable class using intuitive block-based API
|
16
|
+
* Clean separation between keyword-searchable fields and fields for scoping/ordering
|
17
|
+
* Define fields based on existing attributes or "virtual fields" for custom indexing
|
18
|
+
* Indexes each object's entire superclass hierarchy, for easy searching for all objects inheriting from a parent class
|
19
|
+
* Intuitive DSL for scoping searches, with all the usual boolean operators available
|
20
|
+
* Intuitive interface for requesting facets on indexed fields
|
21
|
+
* Extensible adapter architecture for easy integration of other ORMs or non-model classes
|
22
|
+
* Refine search using field facets, date range facets, or ultra-powerful query facets
|
23
|
+
* Full compatibility with will_paginate
|
24
|
+
* Ordering
|
25
|
+
|
26
|
+
== Installation
|
27
|
+
|
28
|
+
gem sources -a http://gems.github.com
|
29
|
+
gem install outoftime-sunspot
|
30
|
+
|
31
|
+
In order to start the packaged Solr installation, run:
|
32
|
+
|
33
|
+
sunspot-solr start -- [-d /path/to/data/directory] [-p port] [-s path/to/solr/home] [--pid-dir=path/to/pid/dir]
|
34
|
+
|
35
|
+
If you don't specify a data directory, your Solr index will be stored in your operating system's temporary directory.
|
36
|
+
|
37
|
+
If you specify a solr home, the directory must contain a <code>conf</code>
|
38
|
+
directory, which should contain at least <code>schema.xml</code> and
|
39
|
+
<code>solrconfig.xml</code>. Be sure to copy the <code>schema.xml</code> out of
|
40
|
+
the Sunspot gem's <code>solr/solr/conf</code> directory. Sunspot relies on the
|
41
|
+
field name patterns defined in the packaged <code>schema.xml</code>, so those
|
42
|
+
cannot be modified.
|
43
|
+
|
44
|
+
You can also run your own instance of Solr wherever you'd like; just copy the solr/config/schema.xml file out of the gem's solr into your installation.
|
45
|
+
You can change the URL at which Sunspot accesses Solr with:
|
46
|
+
|
47
|
+
Sunspot.config.solr.url = 'http://solr.my.host:9818/solr'
|
48
|
+
|
49
|
+
== Rails Integration
|
50
|
+
|
51
|
+
The {Sunspot::Rails}[http://github.com/outoftime/sunspot_rails] plugin makes
|
52
|
+
integrating Sunspot into Rails drop-in easy.
|
53
|
+
|
54
|
+
== Using Sunspot
|
55
|
+
|
56
|
+
=== Define an index:
|
57
|
+
|
58
|
+
class Post
|
59
|
+
#...
|
60
|
+
end
|
61
|
+
|
62
|
+
Sunspot.setup(Post) do
|
63
|
+
text :title, :body
|
64
|
+
string :author_name
|
65
|
+
integer :blog_id
|
66
|
+
integer :category_ids
|
67
|
+
float :average_rating, :using => :ratings_average
|
68
|
+
time :published_at
|
69
|
+
string :sort_title do
|
70
|
+
title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
See Sunspot.setup for more information.
|
75
|
+
|
76
|
+
Note that in order for a class to be searchable, it must have an adapter
|
77
|
+
registered for itself or one of its subclasses. Adapters allow Sunspot to load
|
78
|
+
objects out of persistent storage, and to determine their primary key for
|
79
|
+
indexing. {Sunspot::Rails}[http://github.com/outoftime/sunspot_rails] comes with
|
80
|
+
an adapter for ActiveRecord objects, but for other types of models you will need
|
81
|
+
to define your own. See Sunspot::Adapters for more information.
|
82
|
+
|
83
|
+
=== Search for objects:
|
84
|
+
|
85
|
+
search = Sunspot.search Post do
|
86
|
+
keywords 'great pizza'
|
87
|
+
with :author_name, 'Mark Twain'
|
88
|
+
with(:blog_id).any_of [2, 14]
|
89
|
+
with(:category_ids).all_of [4, 10]
|
90
|
+
with(:published_at).less_than Time.now
|
91
|
+
any_of do
|
92
|
+
with(:expired_at).greater_than(Time.now)
|
93
|
+
with(:expired_at, nil)
|
94
|
+
end
|
95
|
+
without :title, 'Bad Title'
|
96
|
+
without bad_instance # specifically exclude this instance from results
|
97
|
+
|
98
|
+
paginate :page => 3, :per_page => 15
|
99
|
+
order_by :average_rating, :desc
|
100
|
+
|
101
|
+
facet :blog_id
|
102
|
+
end
|
103
|
+
|
104
|
+
See Sunspot.search for more information.
|
105
|
+
|
106
|
+
=== Get data from search:
|
107
|
+
|
108
|
+
search.results
|
109
|
+
search.total
|
110
|
+
search.page
|
111
|
+
search.per_page
|
112
|
+
search.facet(:blog_id)
|
113
|
+
|
114
|
+
== About the API documentation
|
115
|
+
|
116
|
+
All of the methods documented in the RDoc are considered part of Sunspot's
|
117
|
+
public API. Methods that are not part of the public API are documented in the
|
118
|
+
code, but excluded from the RDoc. If you find yourself needing to access methods
|
119
|
+
that are not part of the public API in order to do what you need, please contact
|
120
|
+
me so I can rectify the situation!
|
121
|
+
|
122
|
+
== Dependencies
|
123
|
+
|
124
|
+
1. RSolr
|
125
|
+
2. Daemons
|
126
|
+
3. OptiFlag
|
127
|
+
4. Haml
|
128
|
+
5. Java
|
129
|
+
|
130
|
+
Sunspot has been tested with MRI 1.8.6 and 1.8.7, REE 1.8.6, YARV 1.9.1, and
|
131
|
+
JRuby 1.2.0
|
132
|
+
|
133
|
+
== Bugs
|
134
|
+
|
135
|
+
Please submit bug reports to
|
136
|
+
http://outoftime.lighthouseapp.com/projects/20339-sunspot
|
137
|
+
|
138
|
+
== Further Reading
|
139
|
+
|
140
|
+
* Sunspot Discussion: http://groups.google.com/group/ruby-sunspot
|
141
|
+
* IRC: #sunspot-ruby @ Freenode
|
142
|
+
* Posts about Sunspot from my tumblog: http://outofti.me/tagged/sunspot
|
143
|
+
* Read about it on Linux Magazine: http://www.linux-mag.com/id/7341
|
144
|
+
|
145
|
+
== Contributors
|
146
|
+
|
147
|
+
* Mat Brown (mat@patch.com)
|
148
|
+
* Peer Allan (peer.allan@gmail.com)
|
149
|
+
* Dmitriy Dzema (dima@dzema.name)
|
150
|
+
* Benjamin Krause (bk@benjaminkrause.com)
|
151
|
+
|
152
|
+
== License
|
153
|
+
|
154
|
+
Sunspot is distributed under the MIT License, copyright (c) 2008-2009 Mat Brown
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
=== 0.9.X ===
|
2
|
+
* Deal with empty facet queries
|
3
|
+
* Passing an integer into the second argument of dynamic_facet() when multiple facets are requested gives the wrong value
|
4
|
+
=== 0.10 ===
|
5
|
+
* Highlighting
|
6
|
+
* LocalSolr
|
7
|
+
* Text field restrictions
|
8
|
+
* Prefixes
|
9
|
+
* Intelligently decide whether to instantiate all facet rows at once
|
data/VERSION.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
using_gems = false
|
4
|
+
begin
|
5
|
+
require 'fileutils'
|
6
|
+
require 'optiflag'
|
7
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'sunspot', 'schema')
|
8
|
+
rescue LoadError => e
|
9
|
+
if using_gems
|
10
|
+
raise(e)
|
11
|
+
else
|
12
|
+
using_gems = true
|
13
|
+
require 'rubygems'
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ConfigureSolrFlags extend OptiFlagSet
|
19
|
+
optional_flag 'tokenizer'
|
20
|
+
optional_flag 'extra_filters'
|
21
|
+
optional_flag 'dir'
|
22
|
+
and_process!
|
23
|
+
end
|
24
|
+
|
25
|
+
solr_directory = ARGV.flags.dir || FileUtils.pwd
|
26
|
+
conf_directory = File.join(solr_directory, 'conf')
|
27
|
+
schema_file = File.join(conf_directory, 'schema.xml')
|
28
|
+
FileUtils.mkdir_p(conf_directory)
|
29
|
+
|
30
|
+
schema = Sunspot::Schema.new
|
31
|
+
schema.tokenizer = ARGV.flags.tokenizer if ARGV.flags.tokenizer
|
32
|
+
if ARGV.flags.extra_filters
|
33
|
+
for filter in ARGV.flags.extra_filters.split(',')
|
34
|
+
schema.add_filter(filter)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if File.exist?(schema_file)
|
39
|
+
backup_file = File.join(conf_directory, "schema-#{File.mtime(schema_file).strftime('%Y%m%d%H%M%S')}.xml")
|
40
|
+
STDERR.puts("Backing up current schema file to #{File.expand_path(backup_file)}")
|
41
|
+
FileUtils.mv(schema_file, backup_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
File.open(File.join(conf_directory, 'schema.xml'), 'w') do |file|
|
45
|
+
file << schema.to_xml
|
46
|
+
end
|
data/bin/sunspot-solr
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
using_gems = false
|
3
|
+
begin
|
4
|
+
require 'fileutils'
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'daemons'
|
7
|
+
require 'optiflag'
|
8
|
+
rescue LoadError => e
|
9
|
+
if using_gems
|
10
|
+
raise(e)
|
11
|
+
else
|
12
|
+
using_gems = true
|
13
|
+
require 'rubygems'
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
working_directory = FileUtils.pwd
|
19
|
+
solr_home = File.join(File.dirname(__FILE__), '..', 'solr')
|
20
|
+
|
21
|
+
module SolrFlags extend OptiFlagSet
|
22
|
+
optional_flag 'p' do
|
23
|
+
description 'Port on which to run Solr (default 8983)'
|
24
|
+
long_form 'port'
|
25
|
+
end
|
26
|
+
|
27
|
+
optional_flag 'd' do
|
28
|
+
description 'Solr data directory'
|
29
|
+
end
|
30
|
+
|
31
|
+
optional_flag 's' do
|
32
|
+
description 'Solr home (should contain conf/ directory)'
|
33
|
+
end
|
34
|
+
|
35
|
+
optional_flag 'pd' do
|
36
|
+
long_form 'pid-dir'
|
37
|
+
description 'Directory for pid files'
|
38
|
+
end
|
39
|
+
|
40
|
+
and_process!
|
41
|
+
end
|
42
|
+
|
43
|
+
port = ARGV.flags.p || '8983'
|
44
|
+
data_dir = File.expand_path(ARGV.flags.d || File.join(Dir.tmpdir, 'solr_data'))
|
45
|
+
home = File.expand_path(ARGV.flags.s) if ARGV.flags.s
|
46
|
+
pid_dir = File.expand_path(ARGV.flags.pd || working_directory)
|
47
|
+
|
48
|
+
options = { :dir_mode => :normal, :dir => pid_dir }
|
49
|
+
|
50
|
+
Daemons.run_proc('sunspot-solr', options) do
|
51
|
+
FileUtils.cd(working_directory) do
|
52
|
+
FileUtils.cd(solr_home) do
|
53
|
+
args = ['java']
|
54
|
+
args << "-Djetty.port=#{port}" if port
|
55
|
+
args << "-Dsolr.data.dir=#{data_dir}" if data_dir
|
56
|
+
args << "-Dsolr.solr.home=#{home}" if home
|
57
|
+
args << '-jar' << 'start.jar'
|
58
|
+
STDERR.puts(args * ' ')
|
59
|
+
Kernel.exec(*args)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/light_config.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module LightConfig
|
2
|
+
class Configuration
|
3
|
+
def initialize(&block)
|
4
|
+
@properties = {}
|
5
|
+
::LightConfig::Builder.new(self).instance_eval(&block)
|
6
|
+
singleton = (class <<self; self; end)
|
7
|
+
@properties.keys.each do |property|
|
8
|
+
singleton.module_eval do
|
9
|
+
define_method property do
|
10
|
+
@properties[property]
|
11
|
+
end
|
12
|
+
|
13
|
+
define_method "#{property}=" do |value|
|
14
|
+
@properties[property] = value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Builder
|
22
|
+
def initialize(configuration)
|
23
|
+
@configuration = configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(method, *args, &block)
|
27
|
+
raise ArgumentError("wrong number of arguments(#{args.length} for 1)") unless args.length < 2
|
28
|
+
value = if block then ::LightConfig::Configuration.new(&block)
|
29
|
+
else args.first
|
30
|
+
end
|
31
|
+
@configuration.instance_variable_get(:@properties)[method] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class <<self
|
36
|
+
def build(&block)
|
37
|
+
LightConfig::Configuration.new(&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
module Sunspot
|
2
|
+
#
|
3
|
+
# Sunspot works by saving references to the primary key (or natural ID) of
|
4
|
+
# each indexed object, and then retrieving the objects from persistent storage
|
5
|
+
# when their IDs are referenced in search results. In order for Sunspot to
|
6
|
+
# know what an object's primary key is, and how to retrieve objects from
|
7
|
+
# persistent storage given a primary key, an adapter must be registered for
|
8
|
+
# that object's class or one of its superclasses (for instance, an adapter
|
9
|
+
# registered for ActiveRecord::Base would be used for all ActiveRecord
|
10
|
+
# models).
|
11
|
+
#
|
12
|
+
# To provide Sunspot with this ability, adapters must have two roles:
|
13
|
+
#
|
14
|
+
# Data accessor::
|
15
|
+
# A subclass of Sunspot::Adapters::DataAccessor, this object is instantiated
|
16
|
+
# with a particular class and must respond to the #load() method, which
|
17
|
+
# returns an object from persistent storage given that object's primary key.
|
18
|
+
# It can also optionally implement the #load_all() method, which returns
|
19
|
+
# a collection of objects given a collection of primary keys, if that can be
|
20
|
+
# done more efficiently than calling #load() on each key.
|
21
|
+
# Instance adapter::
|
22
|
+
# A subclass of Sunspot::Adapters::InstanceAdapter, this object is
|
23
|
+
# instantiated with a particular instance. Its only job is to tell Sunspot
|
24
|
+
# what the object's primary key is, by implementing the #id() method.
|
25
|
+
#
|
26
|
+
# Adapters are registered by registering their two components, telling Sunspot
|
27
|
+
# that they are available for one or more classes, and all of their
|
28
|
+
# subclasses. See Sunspot::Adapters::DataAccessor.register and
|
29
|
+
# Sunspot::Adapters::InstanceAdapter.register for the details.
|
30
|
+
#
|
31
|
+
# See spec/mocks/mock_adapter.rb for an example of how adapter classes should
|
32
|
+
# be implemented.
|
33
|
+
#
|
34
|
+
module Adapters
|
35
|
+
# Subclasses of the InstanceAdapter class should implement the #id method,
|
36
|
+
# which returns the primary key of the instance stored in the @instance
|
37
|
+
# variable. The primary key must be unique within the scope of the
|
38
|
+
# instance's class.
|
39
|
+
#
|
40
|
+
# ==== Example:
|
41
|
+
#
|
42
|
+
# class FileAdapter < Sunspot::Adapters::InstanceAdapter
|
43
|
+
# def id
|
44
|
+
# File.expand_path(@instance.path)
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# # then in your initializer
|
49
|
+
# Sunspot::Adapters::InstanceAdapter.register(MyAdapter, File)
|
50
|
+
#
|
51
|
+
class InstanceAdapter
|
52
|
+
def initialize(instance) #:nodoc:
|
53
|
+
@instance = instance
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# The universally-unique ID for this instance that will be stored in solr
|
58
|
+
#
|
59
|
+
# ==== Returns
|
60
|
+
#
|
61
|
+
# String:: ID for use in Solr
|
62
|
+
#
|
63
|
+
def index_id #:nodoc:
|
64
|
+
InstanceAdapter.index_id_for(@instance.class.name, id)
|
65
|
+
end
|
66
|
+
|
67
|
+
class <<self
|
68
|
+
# Instantiate an InstanceAdapter for the given object, searching for
|
69
|
+
# registered adapters for the object's class.
|
70
|
+
#
|
71
|
+
# ==== Parameters
|
72
|
+
#
|
73
|
+
# instance<Object>:: The instance to adapt
|
74
|
+
#
|
75
|
+
# ==== Returns
|
76
|
+
#
|
77
|
+
# InstanceAdapter::
|
78
|
+
# An instance of an InstanceAdapter implementation that
|
79
|
+
# wraps the given instance
|
80
|
+
#
|
81
|
+
def adapt(instance) #:nodoc:
|
82
|
+
self.for(instance.class).new(instance)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Register an instance adapter for a set of classes. When searching for
|
86
|
+
# an adapter for a given instance, Sunspot starts with the instance's
|
87
|
+
# class, and then searches for registered adapters up the class's
|
88
|
+
# ancestor chain.
|
89
|
+
#
|
90
|
+
# ==== Parameters
|
91
|
+
#
|
92
|
+
# instance_adapter<Class>:: The instance adapter class to register
|
93
|
+
# classes...<Class>::
|
94
|
+
# One or more classes that this instance adapter adapts
|
95
|
+
#
|
96
|
+
def register(instance_adapter, *classes)
|
97
|
+
for clazz in classes
|
98
|
+
instance_adapters[clazz.name.to_sym] = instance_adapter
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Find the best InstanceAdapter implementation that adapts the given
|
103
|
+
# class. Starting with the class and then moving up the ancestor chain,
|
104
|
+
# looks for registered InstanceAdapter implementations.
|
105
|
+
#
|
106
|
+
# ==== Parameters
|
107
|
+
#
|
108
|
+
# clazz<Class>:: The class to find an InstanceAdapter for
|
109
|
+
#
|
110
|
+
# ==== Returns
|
111
|
+
#
|
112
|
+
# Class:: Subclass of InstanceAdapter, or nil if none found
|
113
|
+
#
|
114
|
+
# ==== Raises
|
115
|
+
#
|
116
|
+
# Sunspot::NoAdapterError:: If no adapter is registered for this class
|
117
|
+
#
|
118
|
+
def for(clazz) #:nodoc:
|
119
|
+
original_class_name = clazz.name
|
120
|
+
clazz.ancestors.each do |ancestor_class|
|
121
|
+
next if ancestor_class.name.nil? || ancestor_class.name.empty?
|
122
|
+
class_name = ancestor_class.name.to_sym
|
123
|
+
return instance_adapters[class_name] if instance_adapters[class_name]
|
124
|
+
end
|
125
|
+
|
126
|
+
raise(Sunspot::NoAdapterError,
|
127
|
+
"No adapter is configured for #{original_class_name} or its superclasses. See the documentation for Sunspot::Adapters")
|
128
|
+
end
|
129
|
+
|
130
|
+
def index_id_for(class_name, id)
|
131
|
+
"#{class_name} #{id}"
|
132
|
+
end
|
133
|
+
|
134
|
+
protected
|
135
|
+
|
136
|
+
# Lazy-initialize the hash of registered instance adapters
|
137
|
+
#
|
138
|
+
# ==== Returns
|
139
|
+
#
|
140
|
+
# Hash:: Hash containing class names keyed to instance adapter classes
|
141
|
+
#
|
142
|
+
def instance_adapters #:nodoc:
|
143
|
+
@instance_adapters ||= {}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Subclasses of the DataAccessor class take care of retreiving instances of
|
149
|
+
# the adapted class from (usually persistent) storage. Subclasses must
|
150
|
+
# implement the #load method, which takes an id (the value returned by
|
151
|
+
# InstanceAdapter#id, as a string), and returns the instance referenced by
|
152
|
+
# that ID. Optionally, it can also override the #load_all method, which
|
153
|
+
# takes an array of IDs and returns an array of instances in the order
|
154
|
+
# given. #load_all need only be implemented if it can be done more
|
155
|
+
# efficiently than simply iterating over the IDs and calling #load on each
|
156
|
+
# individually.
|
157
|
+
#
|
158
|
+
# ==== Example
|
159
|
+
#
|
160
|
+
# class FileAccessor < Sunspot::Adapters::InstanceAdapter
|
161
|
+
# def load(id)
|
162
|
+
# @clazz.open(id)
|
163
|
+
# end
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# Sunspot::Adapters::DataAccessor.register(FileAccessor, File)
|
167
|
+
#
|
168
|
+
class DataAccessor
|
169
|
+
def initialize(clazz) #:nodoc:
|
170
|
+
@clazz = clazz
|
171
|
+
end
|
172
|
+
|
173
|
+
# Subclasses can override this class to provide more efficient bulk
|
174
|
+
# loading of instances. Instances must be returned in the same order
|
175
|
+
# that the IDs were given.
|
176
|
+
#
|
177
|
+
# ==== Parameters
|
178
|
+
#
|
179
|
+
# ids<Array>:: collection of IDs
|
180
|
+
#
|
181
|
+
# ==== Returns
|
182
|
+
#
|
183
|
+
# Array:: collection of instances, in order of IDs given
|
184
|
+
#
|
185
|
+
def load_all(ids)
|
186
|
+
ids.map { |id| self.load(id) }
|
187
|
+
end
|
188
|
+
|
189
|
+
class <<self
|
190
|
+
# Create a DataAccessor for the given class, searching registered
|
191
|
+
# adapters for the best match. See InstanceAdapter#adapt for discussion
|
192
|
+
# of inheritence.
|
193
|
+
#
|
194
|
+
# ==== Parameters
|
195
|
+
#
|
196
|
+
# clazz<Class>:: Class to create DataAccessor for
|
197
|
+
#
|
198
|
+
# ==== Returns
|
199
|
+
#
|
200
|
+
# DataAccessor::
|
201
|
+
# DataAccessor implementation which provides access to given class
|
202
|
+
#
|
203
|
+
def create(clazz) #:nodoc:
|
204
|
+
self.for(clazz).new(clazz)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Register data accessor for a set of classes. When searching for
|
208
|
+
# an accessor for a given class, Sunspot starts with the class,
|
209
|
+
# and then searches for registered adapters up the class's ancestor
|
210
|
+
# chain.
|
211
|
+
#
|
212
|
+
# ==== Parameters
|
213
|
+
#
|
214
|
+
# data_accessor<Class>:: The data accessor class to register
|
215
|
+
# classes...<Class>::
|
216
|
+
# One or more classes that this data accessor providess access to
|
217
|
+
#
|
218
|
+
def register(data_accessor, *classes)
|
219
|
+
for clazz in classes
|
220
|
+
data_accessors[clazz.name.to_sym] = data_accessor
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Find the best DataAccessor implementation that adapts the given class.
|
225
|
+
# Starting with the class and then moving up the ancestor chain, looks
|
226
|
+
# for registered DataAccessor implementations.
|
227
|
+
#
|
228
|
+
# ==== Parameters
|
229
|
+
#
|
230
|
+
# clazz<Class>:: The class to find a DataAccessor for
|
231
|
+
#
|
232
|
+
# ==== Returns
|
233
|
+
#
|
234
|
+
# Class:: Implementation of DataAccessor
|
235
|
+
#
|
236
|
+
# ==== Raises
|
237
|
+
#
|
238
|
+
# Sunspot::NoAdapterError:: If no data accessor exists for the given class
|
239
|
+
#
|
240
|
+
def for(clazz) #:nodoc:
|
241
|
+
original_class_name = clazz.name
|
242
|
+
clazz.ancestors.each do |ancestor_class|
|
243
|
+
next if ancestor_class.name.nil? || ancestor_class.name.empty?
|
244
|
+
class_name = ancestor_class.name.to_sym
|
245
|
+
return data_accessors[class_name] if data_accessors[class_name]
|
246
|
+
end
|
247
|
+
raise(Sunspot::NoAdapterError,
|
248
|
+
"No data accessor is configured for #{original_class_name} or its superclasses. See the documentation for Sunspot::Adapters")
|
249
|
+
end
|
250
|
+
|
251
|
+
protected
|
252
|
+
|
253
|
+
# Lazy-initialize the hash of registered data accessors
|
254
|
+
#
|
255
|
+
# ==== Returns
|
256
|
+
#
|
257
|
+
# Hash:: Hash containing class names keyed to data accessor classes
|
258
|
+
#
|
259
|
+
def data_accessors #:nodoc:
|
260
|
+
@adapters ||= {}
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|