bento_search 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +299 -0
- data/Rakefile +40 -0
- data/app/assets/images/bento_search/large_loader.gif +0 -0
- data/app/assets/javascripts/bento_search.js +3 -0
- data/app/assets/javascripts/bento_search/ajax_load.js +22 -0
- data/app/assets/stylesheets/bento_search/bento.css +4 -0
- data/app/controllers/bento_search/bento_search_controller.rb +7 -0
- data/app/controllers/bento_search/search_controller.rb +72 -0
- data/app/helpers/bento_search_helper.rb +138 -0
- data/app/item_decorators/bento_search/only_premade_openurl.rb +16 -0
- data/app/item_decorators/bento_search/openurl_add_other_link.rb +35 -0
- data/app/item_decorators/bento_search/openurl_main_link.rb +30 -0
- data/app/models/bento_search/author.rb +25 -0
- data/app/models/bento_search/link.rb +30 -0
- data/app/models/bento_search/multi_searcher.rb +109 -0
- data/app/models/bento_search/openurl_creator.rb +128 -0
- data/app/models/bento_search/registrar.rb +70 -0
- data/app/models/bento_search/result_item.rb +203 -0
- data/app/models/bento_search/results.rb +54 -0
- data/app/models/bento_search/results/pagination.rb +67 -0
- data/app/models/bento_search/search_engine.rb +219 -0
- data/app/models/bento_search/search_engine/capabilities.rb +65 -0
- data/app/search_engines/bento_search/#Untitled-1# +11 -0
- data/app/search_engines/bento_search/ebsco_host_engine.rb +356 -0
- data/app/search_engines/bento_search/eds_engine.rb +557 -0
- data/app/search_engines/bento_search/google_books_engine.rb +184 -0
- data/app/search_engines/bento_search/primo_engine.rb +231 -0
- data/app/search_engines/bento_search/scopus_engine.rb +295 -0
- data/app/search_engines/bento_search/summon_engine.rb +398 -0
- data/app/search_engines/bento_search/xerxes_engine.rb +168 -0
- data/app/views/bento_search/_link.html.erb +4 -0
- data/app/views/bento_search/_search_error.html.erb +22 -0
- data/app/views/bento_search/_std_item.html.erb +39 -0
- data/app/views/bento_search/search/search.html.erb +1 -0
- data/config/locales/en.yml +25 -0
- data/lib/bento_search.rb +29 -0
- data/lib/bento_search/engine.rb +5 -0
- data/lib/bento_search/routes.rb +45 -0
- data/lib/bento_search/version.rb +3 -0
- data/lib/generators/bento_search/pull_ebsco_dbs_generator.rb +24 -0
- data/lib/generators/bento_search/templates/ebsco_global_var.erb +6 -0
- data/lib/http_client_patch/include_client.rb +86 -0
- data/lib/tasks/bento_search_tasks.rake +4 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +56 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +3100 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/functional/bento_search/search_controller_test.rb +81 -0
- data/test/helper/bento_search_helper_test.rb +125 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/support/mock_engine.rb +23 -0
- data/test/support/test_with_cassette.rb +38 -0
- data/test/test_helper.rb +52 -0
- data/test/unit/#vcr_test.rb# +68 -0
- data/test/unit/ebsco_host_engine_test.rb +134 -0
- data/test/unit/eds_engine_test.rb +105 -0
- data/test/unit/google_books_engine_test.rb +93 -0
- data/test/unit/item_decorators_test.rb +66 -0
- data/test/unit/multi_searcher_test.rb +49 -0
- data/test/unit/openurl_creator_test.rb +111 -0
- data/test/unit/pagination_test.rb +59 -0
- data/test/unit/primo_engine_test.rb +37 -0
- data/test/unit/register_engine_test.rb +50 -0
- data/test/unit/result_item_display_test.rb +39 -0
- data/test/unit/result_item_test.rb +36 -0
- data/test/unit/scopus_engine_test.rb +130 -0
- data/test/unit/search_engine_base_test.rb +178 -0
- data/test/unit/search_engine_test.rb +95 -0
- data/test/unit/summon_engine_test.rb +161 -0
- data/test/unit/xerxes_engine_test.rb +70 -0
- data/test/vcr_cassettes/ebscohost/error_bad_db.yml +45 -0
- data/test/vcr_cassettes/ebscohost/error_bad_password.yml +45 -0
- data/test/vcr_cassettes/ebscohost/get_info.yml +3626 -0
- data/test/vcr_cassettes/ebscohost/live_search.yml +45 -0
- data/test/vcr_cassettes/ebscohost/live_search_smoke_test.yml +1311 -0
- data/test/vcr_cassettes/eds/basic_search_smoke_test.yml +1811 -0
- data/test/vcr_cassettes/eds/get_auth_token.yml +75 -0
- data/test/vcr_cassettes/eds/get_auth_token_failure.yml +39 -0
- data/test/vcr_cassettes/eds/get_with_auth.yml +243 -0
- data/test/vcr_cassettes/eds/get_with_auth_recovers_from_bad_auth.yml +368 -0
- data/test/vcr_cassettes/gbs/error_condition.yml +40 -0
- data/test/vcr_cassettes/gbs/pagination.yml +702 -0
- data/test/vcr_cassettes/gbs/search.yml +340 -0
- data/test/vcr_cassettes/primo/search_smoke_test.yml +1112 -0
- data/test/vcr_cassettes/scopus/bad_api_key_should_return_error_response.yml +60 -0
- data/test/vcr_cassettes/scopus/escaped_chars.yml +187 -0
- data/test/vcr_cassettes/scopus/fielded_search.yml +176 -0
- data/test/vcr_cassettes/scopus/simple_search.yml +227 -0
- data/test/vcr_cassettes/scopus/zero_results_search.yml +67 -0
- data/test/vcr_cassettes/summon/bad_auth.yml +54 -0
- data/test/vcr_cassettes/summon/proper_tags_for_snippets.yml +216 -0
- data/test/vcr_cassettes/summon/search.yml +242 -0
- data/test/vcr_cassettes/xerxes/live_search.yml +2580 -0
- data/test/view/std_item_test.rb +98 -0
- metadata +421 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 Jonathan Rochkind/Johns Hopkins Universities
|
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.md
ADDED
@@ -0,0 +1,299 @@
|
|
1
|
+
# BentoSearch
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/jrochkind/bento_search.png)](http://travis-ci.org/jrochkind/bento_search)
|
4
|
+
|
5
|
+
|
6
|
+
(**in progress*, not yet ready for use, mainly because we need more
|
7
|
+
out of the box search engines supported).
|
8
|
+
|
9
|
+
bento_search provides an abstraction/normalization layer for querying and
|
10
|
+
displaying results for external search engines, in Ruby on Rails. Requires
|
11
|
+
Rails3 and tested only under ruby 1.9.3.
|
12
|
+
|
13
|
+
* It is focused on use cases for academic libraries, but may be useful in generic
|
14
|
+
cases too. Initially, engine adapters are planned to be provided for:
|
15
|
+
Google Books, Scopus, SerialSolutions Summon, Ex Libris Primo,
|
16
|
+
EBSCO Discovery Service, and EBSCO traditional 'EIT' api. Most
|
17
|
+
of these search engines require a vendor license to use.
|
18
|
+
|
19
|
+
* bento_search could be considered building blocks for a type of 'federated
|
20
|
+
search' functionality, but it does not and will never support merging results
|
21
|
+
from multiple engines into one result set. It is meant to support displaying the
|
22
|
+
first few results from multiple engines on one page, "bento box" style (as
|
23
|
+
named by Tito Sierra@NCSU), as well as more expanded single-search-on-a-page
|
24
|
+
uses.
|
25
|
+
|
26
|
+
* bento_search provides abstract functionality for pagination, sorting,
|
27
|
+
and single-field-specified queries. Faceting, limiting, and 'advanced'
|
28
|
+
multi-field searches are not yet supported, but planned. Not all
|
29
|
+
search engine adapters support all features. Search engine adapters can
|
30
|
+
declare search fields and sort options with 'semantics', so you can for
|
31
|
+
instance search or sort by 'title' across search engines without regard
|
32
|
+
to internal engine-specific field names.
|
33
|
+
|
34
|
+
bento_search is designed to allow code to be written agnostic of the search
|
35
|
+
provider, so you can switch out the search provider, minimizing dependent
|
36
|
+
code in your app that needs to be rewritten. As well as letting you get
|
37
|
+
started quick without reinventing the wheel and figuring out poorly
|
38
|
+
documented vendor API's yourself.
|
39
|
+
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
### Instantiate an engine, and search
|
44
|
+
|
45
|
+
When you instantiate an engine, you can provide configuration keys. There
|
46
|
+
are a few standard keys (see BentoSearch::SearchEngine), and others that
|
47
|
+
may be engine-specific. Some engine-specific keys (such as api auth keys)
|
48
|
+
may be required for certain engines.
|
49
|
+
|
50
|
+
engine = BentoSearch::GoogleBooksEngine.new(:api_key => "my_gbs_api_key")
|
51
|
+
results = engine.search("a query")
|
52
|
+
|
53
|
+
`results` are a BentoSearch::Results object, which acts like an array of
|
54
|
+
BentoSearch::Item objects, along with some meta-information about the
|
55
|
+
search itself (pagination keys, etc). BentoSearch::Results and Item fields
|
56
|
+
are standardized accross engines. BentoSearch::Items provide semantic
|
57
|
+
values (title, author, etc.), as available from the particular engine.
|
58
|
+
|
59
|
+
To see which engines come bundled with BentoSearch, and any special
|
60
|
+
engine-specific instructions, look at BentoSearch source in `./app/search_engines`
|
61
|
+
|
62
|
+
### Register engines in global configuration
|
63
|
+
|
64
|
+
It can be convenient to register an engine in global configuration, and is
|
65
|
+
required for certain functionality (like out-of-the-box AJAX loading).
|
66
|
+
|
67
|
+
In an initializer in your app, like say `./config/initializers/bento_search.rb`:
|
68
|
+
|
69
|
+
BentoSearch.register_engine("gbs") do |conf|
|
70
|
+
conf.engine = "BentoSearch::GoogleBooksEngine"
|
71
|
+
conf.api_key = "my_google_api_key"
|
72
|
+
# any other configuration
|
73
|
+
end
|
74
|
+
|
75
|
+
Then you can refer to it, for instance in a controller, by the id you registered:
|
76
|
+
|
77
|
+
@results = BentoSearch.get_engine("gbs").search("my query")
|
78
|
+
|
79
|
+
### Display results
|
80
|
+
|
81
|
+
You can of course write your own code to display a BentoSearch::Results object
|
82
|
+
however you like. But BentoSearch comes with a helper method for displaying
|
83
|
+
a list of BentoSearch::Results in a standard way, using the bento_search
|
84
|
+
helper method.
|
85
|
+
|
86
|
+
<%= bento_search(@results) %>
|
87
|
+
|
88
|
+
### Fielded searching.
|
89
|
+
|
90
|
+
You can search by an internal engine-specific field name:
|
91
|
+
|
92
|
+
google_books_engine.search("smith", :search_field => "inauthor")
|
93
|
+
|
94
|
+
Or, if the engine provides it, you can search by normalized semantic search
|
95
|
+
field type names:
|
96
|
+
|
97
|
+
google_books_engine.search("smith", :semantic_earch_field => :title)
|
98
|
+
|
99
|
+
This will raise if an engine doesn't support that semantic search field.
|
100
|
+
You can find out what fields a particular engine supports.
|
101
|
+
|
102
|
+
BentoSearch::GoogleBooksEngine.search_keys # => internal keys
|
103
|
+
BentoSearch::GoogleBooksEngine.semantic_search_keys
|
104
|
+
|
105
|
+
You can also provide all arguments in a single hash when it's convenient
|
106
|
+
to do so:
|
107
|
+
|
108
|
+
google_books_engine.search(:query => "smith", :search_field => "inauthor")
|
109
|
+
|
110
|
+
### Sorting
|
111
|
+
|
112
|
+
An engine advertises what sort types it supports:
|
113
|
+
|
114
|
+
BentoSearch::GoogleBooksEngine.sort_definitions
|
115
|
+
|
116
|
+
That returns a hash, where the keys are sort identifiers, where possible
|
117
|
+
chosen from a standard list of semantics. (See list in config/i18n/en.yml,
|
118
|
+
bento_search.sort_keys).
|
119
|
+
|
120
|
+
google_books_engine.search("my query", :sort => "date_desc")
|
121
|
+
|
122
|
+
For help creating your UI, you can use built-in helper method:
|
123
|
+
|
124
|
+
bento_sort_hash_for(engine)
|
125
|
+
#=> returns a Hash suitable as first argument for rails
|
126
|
+
# options_for_select helper, with sort options and labels from I18n.
|
127
|
+
|
128
|
+
|
129
|
+
### Pagination
|
130
|
+
|
131
|
+
You can tell the search engine how many items you want per-page, and
|
132
|
+
use _either_ `:start` (0-based item offset) or `:page` (1-based page
|
133
|
+
offset) keys to paginate into the results.
|
134
|
+
|
135
|
+
results = google_books_engine.search("my query", :per_page => 20, :start => 40)
|
136
|
+
results = google_books_engine.search("my query", :per_page => 20, :page => 2) # means same as above
|
137
|
+
|
138
|
+
An engine instance advertises it's maximum per-page values.
|
139
|
+
|
140
|
+
google_books_engine.max_per_page
|
141
|
+
|
142
|
+
bento_search fixes the default per_page at 10.
|
143
|
+
|
144
|
+
For help creating your UI, you can ask a BentoSearch::Results for
|
145
|
+
`results.pagination`, which returns a BentoSearch::Results::Pagination
|
146
|
+
object which should be suitable for passing to [kaminari](https://github.com/amatsuda/kaminari)
|
147
|
+
`paginate`, or else have convenient methods for roll your own pagination UI.
|
148
|
+
Kaminari's paginate method:
|
149
|
+
|
150
|
+
<%= paginate results.pagination %>
|
151
|
+
|
152
|
+
### Concurrent searching
|
153
|
+
|
154
|
+
If you're going to search 2 or more search engines at once, you'll want to execute
|
155
|
+
those searches concurrently. For instance, if GoogleBooks results take 2 second
|
156
|
+
to come in, and Scopus results take 3 seconds -- you don't want to first wait
|
157
|
+
the 2 second then wait the 3 seconds for a total of 5 -- you instead want
|
158
|
+
to execute concurrently in seperate threads, so the total wait time is the slowest
|
159
|
+
engine, not the sum of the engines.
|
160
|
+
|
161
|
+
You can write your own logic using ruby threads to do this, but
|
162
|
+
BentoSearch provides a multi-searching helper using [Celluloid](https://github.com/celluloid/celluloid)
|
163
|
+
to help you do this easily. Say, in a controller:
|
164
|
+
|
165
|
+
~~~~ruby
|
166
|
+
# constructor takes id's registered with BentoSearch.register_engine
|
167
|
+
searcher = BentoSearch::MultiSearcher.new(:gbs, :scopus, :summon)
|
168
|
+
|
169
|
+
# Call 'start' with any parameters you would give to an_engine.search
|
170
|
+
searcher.start("my query", :semantic_search_field => :author, :sort => "title")
|
171
|
+
|
172
|
+
# At this point, all searches are executing asynchronously in seperate threads.
|
173
|
+
# To get the results, blocking until all complete:
|
174
|
+
@results = searcher.results
|
175
|
+
|
176
|
+
# @results will be a hash, keyed by registered engine id, values
|
177
|
+
# are BentoSearch::Results
|
178
|
+
~~~~
|
179
|
+
|
180
|
+
Even if you are only searching one engine, this may be useful to have the
|
181
|
+
search execute in a seperate thread, so you can continue doing other work
|
182
|
+
in the main thread (like search a local store of some kind outside of
|
183
|
+
bento_search)
|
184
|
+
|
185
|
+
You will need to add the 'celluloid' gem to your app to use this feature,
|
186
|
+
BentoSearch doesn't automatically include the celluloid dependency right now
|
187
|
+
(should it?).
|
188
|
+
|
189
|
+
For more info, see BentoSearch::MultiSearcher.
|
190
|
+
|
191
|
+
### Delayed results loading via AJAX (actually more like AJAHtml)
|
192
|
+
|
193
|
+
BentoSearch provides some basic support for initially displaying a placeholder
|
194
|
+
progress spinner, and having Javascript call back to get the actual results.
|
195
|
+
|
196
|
+
* **Setup Pre-requisites**
|
197
|
+
* In your `./config/routes.rb`, you need `BentoSearch::Routes.new(self).draw` in order
|
198
|
+
to route to the ajax loader.
|
199
|
+
* In your asset pipeline, you must have `//= require 'bento_search/ajax_load`
|
200
|
+
to get JS for ajax loading. (or require 'bento_search' to get all bento_search JS)
|
201
|
+
* **Note** that this is not a panacea for a very slow search engine -- if the
|
202
|
+
search results take 20 seconds to come in, when the AJAX call back happens,
|
203
|
+
your Rails process _will_ be blocked from serving any other requests for that 20
|
204
|
+
seconds. In fact, that makes this feature of very limited applicability in general,
|
205
|
+
think carefully about what this will do for you.
|
206
|
+
* **Beware** that there are some authorization considerations if your search
|
207
|
+
engine is not publically configurable, see BentoSearch::SearchController
|
208
|
+
for more details.
|
209
|
+
|
210
|
+
You have have registered a configured engine globally, and given it the special
|
211
|
+
`:allow_routable_results` key.
|
212
|
+
|
213
|
+
BentoSearch.register_engine("gbs") do |conf|
|
214
|
+
conf.api_key = "x"
|
215
|
+
conf.allow_routable_results = true
|
216
|
+
end
|
217
|
+
|
218
|
+
Now you can use the `bento_search` helper method with the registered id
|
219
|
+
and query, instead of with results as before, and with an option for
|
220
|
+
ajax auto-load.
|
221
|
+
|
222
|
+
<%= bento_search("gbs", :query => "my query",
|
223
|
+
:semantic_search_field => :title,
|
224
|
+
:load => :ajax_auto) %>
|
225
|
+
|
226
|
+
|
227
|
+
|
228
|
+
### Item Decorators, and Links
|
229
|
+
|
230
|
+
You can configure Decorators, in the form of plain old ruby modules, to be
|
231
|
+
applied to BentoSearch::Items, on an engine-by-engine basis. These can modify,
|
232
|
+
add, or remove Item data, as well as over-ride some presentational methods.
|
233
|
+
|
234
|
+
One common use for these Decorators is changing, adding, or removing links
|
235
|
+
associated with an item. For instance, to link to your local OpenURL
|
236
|
+
link resolver.
|
237
|
+
|
238
|
+
BentoSearch::Items can have a main link associated with them (generally
|
239
|
+
hyperlinked from title), as well as a list of additional links. Most engines
|
240
|
+
do not provide additional links by default, custom local Decorators would
|
241
|
+
be used to add them.
|
242
|
+
|
243
|
+
BentoSearch.register_engine("something") do |conf|
|
244
|
+
conf.engine = SomeEngine
|
245
|
+
conf.item_decorators = [ SomeModule, OtherModule]
|
246
|
+
end
|
247
|
+
|
248
|
+
See BentoSearch::Item for more information on decorators, and BentoSearch::Link
|
249
|
+
on links.
|
250
|
+
|
251
|
+
## Planned Features
|
252
|
+
|
253
|
+
I am trying to keep BentoSearch as simple as it can be to conveniently meet
|
254
|
+
actual use cases. Trying to avoid premature over-engineering, and pave
|
255
|
+
the cowpaths as needed.
|
256
|
+
|
257
|
+
Probably:
|
258
|
+
|
259
|
+
* Support for display facets for engines that support such, as well as
|
260
|
+
search with limits from controlled vocabulary (ie, selected facet, but
|
261
|
+
also may be supported by some engines that do not support facetting).
|
262
|
+
* Support for multi-field, multi-entry-box 'advanced search' UI's, in
|
263
|
+
a normalized cross-engine way.
|
264
|
+
* More mindless support for displaying pagination UI with kaminari.
|
265
|
+
|
266
|
+
Other needs or suggestions?
|
267
|
+
|
268
|
+
|
269
|
+
## Developing
|
270
|
+
|
271
|
+
BentoSearch is fairly well covered by automated tests. We simply use Test::Unit.
|
272
|
+
Run tests with `rake test`.
|
273
|
+
|
274
|
+
The testing environment was generated with `rails plugin new`, and includes
|
275
|
+
a dummy app used when testing at `./test/dummy`.
|
276
|
+
|
277
|
+
For integration tests against live external search API's, we use the awesome
|
278
|
+
[VCR](https://github.com/myronmarston/vcr) gem to cache responses.
|
279
|
+
To write your own Test::Unit tests using VCR, take note of the
|
280
|
+
`test_with_cassette` method provided in `./test/support/test_with_cassette.rb`.
|
281
|
+
|
282
|
+
Also note use of VCR.filter_sensitive_data to make sure your API keys
|
283
|
+
do not get saved in cached response in the repo, while still allowing
|
284
|
+
tests to be run against cached responses even for engines that require
|
285
|
+
auth.
|
286
|
+
|
287
|
+
To re-generate cached responses, delete the relevant files in
|
288
|
+
`./test/vcr_cassettes` and re-run tests. You may have to set an ENV
|
289
|
+
variable with your own API keys to re-run tests without cached response
|
290
|
+
like this.
|
291
|
+
|
292
|
+
Also note `./test/support/mock_engine.rb`, a simple mock/dummy SearchEngine
|
293
|
+
implementation that can be used in other tests.
|
294
|
+
|
295
|
+
Pull requests welcome. Pull requests with additional search engine implementations
|
296
|
+
welcome. See more info on writing a BentoSearch::SearchEngine in the inline
|
297
|
+
docs in that file.
|
298
|
+
|
299
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'BentoSearch'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
Bundler::GemHelper.install_tasks
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
|
32
|
+
Rake::TestTask.new(:test) do |t|
|
33
|
+
t.libs << 'lib'
|
34
|
+
t.libs << 'test'
|
35
|
+
t.pattern = 'test/**/*_test.rb'
|
36
|
+
t.verbose = false
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
Binary file
|
@@ -0,0 +1,22 @@
|
|
1
|
+
jQuery(document).ready(function($) {
|
2
|
+
//Intentionally wait for window.load, not just onready, to
|
3
|
+
//prevent interfering with rest of page load.
|
4
|
+
$(window).bind("load", function() {
|
5
|
+
$(".bento_search_ajax_wait").each(function(i, div) {
|
6
|
+
div = $(div);
|
7
|
+
// from html5 data-bento-ajax-url
|
8
|
+
$.ajax({
|
9
|
+
url: div.data("bentoAjaxUrl"),
|
10
|
+
success: function(response, status, xhr) {
|
11
|
+
div.replaceWith(response);
|
12
|
+
},
|
13
|
+
error: function(xhr, status, errorThrown) {
|
14
|
+
var msg = "Sorry but there was an error: ";
|
15
|
+
div.html(msg + xhr.status + " " + xhr.statusText + ", " + status);
|
16
|
+
}
|
17
|
+
});
|
18
|
+
|
19
|
+
});
|
20
|
+
});
|
21
|
+
|
22
|
+
});
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module BentoSearch
|
2
|
+
# This is a controller that provides stand-alone search results
|
3
|
+
# for registered engines. Right now, this is only for automatic
|
4
|
+
# AJAX delayed loading. In the future it may be used for atom results,
|
5
|
+
# or other such.
|
6
|
+
#
|
7
|
+
# You need to make sure to include routing for this controller in your
|
8
|
+
# app to use it, for instance with `BentoSearch::Routes.new(self).draw`
|
9
|
+
# in your ./config/routes.rb
|
10
|
+
#
|
11
|
+
# # Authorization Issues
|
12
|
+
#
|
13
|
+
# You may have some engines which should not be publically searchable,
|
14
|
+
# they should only be searchable by certain auth'd users. This controller
|
15
|
+
# could accidentally provide a non-protected endpoint to get results if
|
16
|
+
# nothing were done to prevent it.
|
17
|
+
#
|
18
|
+
# Only engines which have a :allow_routable_results => true key
|
19
|
+
# in their config will be served by this controller.
|
20
|
+
#
|
21
|
+
# If you need routable results on an engine which ALSO needs to
|
22
|
+
# be protected by auth, you can add your own Rails before_filter
|
23
|
+
# to provide auth. Say, in an initializer in your app:
|
24
|
+
#
|
25
|
+
# SearchController.before_filter do |controller|
|
26
|
+
# unless controller.current_user
|
27
|
+
# raise BentoSearch::SearchController::AccessDenied
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# We may provide fancier/nicer API for this in the future, if there's
|
33
|
+
# demand.
|
34
|
+
class SearchController < BentoSearchController
|
35
|
+
class AccessDenied < Exception ; end
|
36
|
+
|
37
|
+
|
38
|
+
rescue_from AccessDenied, :with => :deny_access
|
39
|
+
rescue_from NoSuchEngine, :with => :render_404
|
40
|
+
|
41
|
+
# returns partial HTML results, suitable for
|
42
|
+
# AJAX to insert into DOM.
|
43
|
+
# arguments for engine.search are taken from URI request params.
|
44
|
+
# (TODO: Is this a security issue, do we need to whitelist em? )
|
45
|
+
def search
|
46
|
+
engine = BentoSearch.get_engine(params[:engine_id])
|
47
|
+
|
48
|
+
|
49
|
+
unless engine.configuration.allow_routable_results == true
|
50
|
+
raise AccessDenied.new("engine needs to be registered with :allow_routable_results => true")
|
51
|
+
end
|
52
|
+
|
53
|
+
@results = engine.search(params.to_hash.symbolize_keys)
|
54
|
+
|
55
|
+
render :layout => false # partial HTML results
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def deny_access(exception)
|
63
|
+
render :text => exception.message, :status => 403
|
64
|
+
end
|
65
|
+
|
66
|
+
def render_404(exception)
|
67
|
+
render :text => exception.message, :status => 404
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|