bento_search 1.5.0 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +27 -24
- data/Rakefile +30 -11
- data/app/assets/javascripts/bento_search/ajax_load.js +54 -22
- data/app/controllers/bento_search/search_controller.rb +31 -30
- data/app/helpers/bento_search_helper.rb +72 -74
- data/app/models/bento_search/concurrent_searcher.rb +136 -0
- data/app/models/bento_search/result_item.rb +15 -12
- data/app/models/bento_search/results/serialization.rb +22 -13
- data/app/models/bento_search/search_engine.rb +170 -140
- data/app/search_engines/bento_search/doaj_articles_engine.rb +20 -20
- data/app/search_engines/bento_search/ebsco_host_engine.rb +3 -3
- data/app/search_engines/bento_search/eds_engine.rb +326 -206
- data/app/search_engines/bento_search/google_books_engine.rb +2 -2
- data/app/search_engines/bento_search/scopus_engine.rb +87 -87
- data/app/search_engines/bento_search/summon_engine.rb +1 -1
- data/app/views/bento_search/_ajax_loading.html.erb +17 -0
- data/app/views/bento_search/_item_title.html.erb +2 -4
- data/app/views/bento_search/_link.html.erb +3 -3
- data/lib/bento_search.rb +24 -9
- data/lib/bento_search/engine.rb +2 -0
- data/lib/bento_search/version.rb +1 -1
- data/lib/generators/bento_search/install/ajax_load_js_generator.rb +15 -0
- data/test/decorator/standard_decorator_test.rb +30 -30
- data/test/dummy/app/assets/config/manifest.js +4 -0
- data/test/dummy/config/application.rb +7 -0
- data/test/dummy/config/boot.rb +4 -9
- data/test/dummy/config/environments/development.rb +2 -0
- data/test/dummy/config/environments/production.rb +7 -1
- data/test/dummy/config/environments/test.rb +10 -3
- data/test/functional/bento_search/search_controller_test.rb +68 -58
- data/test/helper/bento_search_helper_test.rb +103 -103
- data/test/search_engines/doaj_articles_engine_test.rb +9 -9
- data/test/search_engines/eds_engine_test.rb +91 -59
- data/test/search_engines/google_site_search_test.rb +48 -48
- data/test/search_engines/scopus_engine_test.rb +51 -51
- data/test/search_engines/search_engine_base_test.rb +108 -86
- data/test/search_engines/search_engine_test.rb +68 -56
- data/test/support/atom.xsd.xml +3 -3
- data/test/support/xml.xsd +117 -0
- data/test/test_helper.rb +23 -12
- data/test/unit/concurrent_searcher_test.rb +75 -0
- data/test/unit/pagination_test.rb +12 -12
- data/test/vcr_cassettes/eds/FullText_CustomLink.yml +198 -0
- data/test/vcr_cassettes/eds/basic_search_smoke_test.yml +1036 -1729
- data/test/vcr_cassettes/eds/catalog_ebook_query.yml +218 -0
- data/test/vcr_cassettes/eds/catalog_query.yml +255 -0
- data/test/vcr_cassettes/eds/get_auth_token.yml +11 -44
- data/test/vcr_cassettes/eds/get_auth_token_failure.yml +10 -7
- data/test/vcr_cassettes/eds/get_with_auth.yml +144 -153
- data/test/vcr_cassettes/eds/get_with_auth_recovers_from_bad_auth.yml +167 -223
- data/test/view/atom_results_test.rb +94 -94
- metadata +36 -46
- data/app/assets/javascripts/bento_search.js +0 -3
- data/app/item_decorators/bento_search/ebscohost/conditional_openurl_main_link.rb +0 -36
- data/app/item_decorators/bento_search/only_premade_openurl.rb +0 -20
- data/app/item_decorators/bento_search/openurl_add_other_link.rb +0 -39
- data/app/item_decorators/bento_search/openurl_main_link.rb +0 -34
- data/app/models/bento_search/multi_searcher.rb +0 -131
- data/test/dummy/config/initializers/secret_token.rb +0 -8
- data/test/unit/multi_searcher_test.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ed755cb880b6d45bd02d53373d80c25161c6689ef59b7108106896c0a392fc4c
|
4
|
+
data.tar.gz: 4c9009a41299bc44b84274e93c5f0e7a9a5fda599e0de97f6e8972cd3519faf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07e2ba15ccd24b1dc1d1cfac642dd01d60700de90dcd2b63ce164830a4619e30203c728077461123ab39ce7e8da6390d3f5009f6afaba637ebc275f5977fd23e
|
7
|
+
data.tar.gz: 755df58770fa0f91fa9cd0fa8ef507815f762889203f4c63edd9931bd4f15270027a033213fca650334749add1be5d9ef0dc15a64b6b9bf92d75838137db01bc
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# BentoSearch
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![CI Build Status](https://github.com/jrochkind/bento_search/actions/workflows/ruby.yml/badge.svg)](https://github.com/jrochkind/bento_search/actions/workflows/ruby.yml)
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/bento_search.png)](http://badge.fury.io/rb/bento_search)
|
5
5
|
|
6
6
|
bento_search provides an abstraction/normalization layer for querying and
|
7
7
|
displaying results from external search engines, in Ruby on Rails. Works with
|
8
|
-
Rails
|
8
|
+
Rails 5.2 - 6.1, ruby 2.5 through 3.0.
|
9
9
|
|
10
10
|
### Goals: To help you
|
11
11
|
|
@@ -35,6 +35,7 @@ Adapters currently included in bento_search
|
|
35
35
|
* [WorldCat Search](https://www.worldcat.org/) (requires OCLC membership to get api key)
|
36
36
|
* [Google Site Search](https://www.google.com/work/search/products/gss.html) (requires sign-up for more than 100 searches/day)
|
37
37
|
* [JournalTOCs](http://www.journaltocs.hw.ac.uk/) (limited support for fetching current articles by ISSN, free but requires registration)
|
38
|
+
* [Directory of Open Access Journals (DOAJ)](https://doaj.org/) article search. (free, no registration required)
|
38
39
|
|
39
40
|
|
40
41
|
|
@@ -46,12 +47,11 @@ search' functionality, but it does not and will never support merging results
|
|
46
47
|
from multiple engines into one result set. It is meant to support displaying the
|
47
48
|
first few results from multiple engines on one page, "bento box" style (as
|
48
49
|
named by Tito Sierra@NCSU), as well as more expanded single-search-on-a-page
|
49
|
-
uses.
|
50
|
+
uses -- or back-end functionality supporting features that are not straight discovery.
|
50
51
|
|
51
52
|
* bento_search provides abstract functionality for pagination, sorting,
|
52
|
-
and single-field-specified queries. Faceting
|
53
|
-
|
54
|
-
out in the future.
|
53
|
+
and single-field-specified queries. Faceting and generalized limiting are
|
54
|
+
not yet supported, but possibly will be built out in the future.
|
55
55
|
|
56
56
|
Not all search engine adapters support all features. Some engines offer
|
57
57
|
engine-specific features, such as limiting. Search engine adapters can
|
@@ -86,7 +86,7 @@ may be required for certain engines.
|
|
86
86
|
`results` are a [BentoSearch::Results](./app/models/bento_search/results.rb) object, which acts like an array of
|
87
87
|
[BentoSearch::ResultItem](./app/models/bento_search/result_item.rb) objects, along with some meta-information about the
|
88
88
|
search itself (pagination keys, etc). BentoSearch::Results and Item fields
|
89
|
-
are standardized
|
89
|
+
are standardized across engines. BentoSearch::Items provide semantic
|
90
90
|
values (title, author, etc.), as available from the particular engine.
|
91
91
|
|
92
92
|
To see which engines come bundled with BentoSearch, and any special
|
@@ -238,7 +238,7 @@ declared for the engine, that will be preferred.
|
|
238
238
|
This can be used to expose a multi-field search to users, and the `bento_field_hash_for`
|
239
239
|
helper method might be helpful in creating your UI. But this is also useful for looking
|
240
240
|
up known-item citations -- either by author/title, or issn/volume/issue/page, or doi, or
|
241
|
-
anything else -- as back-end support for various possible functions.
|
241
|
+
anything else -- as back-end support for various possible functions.
|
242
242
|
|
243
243
|
### Concurrent searching
|
244
244
|
|
@@ -250,12 +250,12 @@ to execute concurrently in seperate threads, so the total wait time is the slowe
|
|
250
250
|
engine, not the sum of the engines.
|
251
251
|
|
252
252
|
You can write your own logic using ruby threads to do this, but
|
253
|
-
BentoSearch provides a multi-searching helper using [
|
253
|
+
BentoSearch provides a multi-searching helper using [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby)
|
254
254
|
to help you do this easily. Say, in a controller:
|
255
255
|
|
256
256
|
~~~~ruby
|
257
257
|
# constructor takes id's registered with BentoSearch.register_engine
|
258
|
-
searcher = BentoSearch::
|
258
|
+
searcher = BentoSearch::ConcurentSearcher.new(:gbs, :scopus, :summon)
|
259
259
|
|
260
260
|
# Call 'search' with any parameters you would give to an_engine.search
|
261
261
|
searcher.search("my query", :semantic_search_field => :author, :sort => "title")
|
@@ -273,13 +273,18 @@ search execute in a seperate thread, so you can continue doing other work
|
|
273
273
|
in the main thread (like search a local store of some kind outside of
|
274
274
|
bento_search)
|
275
275
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
276
|
+
If you are using a Rails previous to 5.x, you will have to add the
|
277
|
+
`concurrent-ruby` gem to your `Gemfile` (It's already a dependency of
|
278
|
+
Rails5).
|
279
|
+
|
280
|
+
If you are using Rails5, ConcurrentSearcher uses new Rails API that
|
281
|
+
should make development-mode class reloading work fine even with
|
282
|
+
the ConcurrentSearcher's concurrency.
|
283
|
+
|
284
|
+
For more info, see [BentoSearch::ConcurrentSearcher](./app/models/bento_search/concurrent_searcher.rb).
|
285
|
+
|
280
286
|
|
281
287
|
|
282
|
-
For more info, see [BentoSearch::MultiSearcher](./app/models/bento_search/multi_searcher.rb).
|
283
288
|
|
284
289
|
### Delayed results loading via AJAX (actually more like AJAHtml)
|
285
290
|
|
@@ -375,7 +380,7 @@ There are additional details that might matter to you, for more info see the
|
|
375
380
|
### Round-Trip Serialization to JSON
|
376
381
|
|
377
382
|
You can serialize BentoSearch::Results to a simple straightforward JSON structure, and de-serialize
|
378
|
-
them back into BentoSearch::Results.
|
383
|
+
them back into BentoSearch::Results.
|
379
384
|
|
380
385
|
~~~ruby
|
381
386
|
json_str = results.dump_to_json
|
@@ -383,19 +388,19 @@ copy_of_results = BentoSearch::Results.load_json(json_str)
|
|
383
388
|
~~~
|
384
389
|
|
385
390
|
Search context (query, start, per_page) are not serialized, and will be lost
|
386
|
-
on de-serialization.
|
391
|
+
on de-serialization.
|
387
392
|
|
388
393
|
Unlike the Atom serialization, **the JSON serialization is of internal data
|
389
|
-
state, without decoration.** Configuration context is not serialized.
|
394
|
+
state, without decoration.** Configuration context is not serialized.
|
390
395
|
|
391
|
-
However, the engine_id is included in serialization if present,
|
396
|
+
However, the engine_id is included in serialization if present,
|
392
397
|
and configuration from the specified engine
|
393
398
|
will be re-assigned on de-serialization. This means if the configuration
|
394
399
|
changed between serialization and de-serialization, you get the new stuff
|
395
|
-
assigned on de-serialization.
|
400
|
+
assigned on de-serialization.
|
396
401
|
|
397
402
|
The use case guiding JSON serialization is storage somewhere, and
|
398
|
-
round-trip de-serialization in the current app context.
|
403
|
+
round-trip de-serialization in the current app context.
|
399
404
|
|
400
405
|
If you want to take de-serialized results that did not have an engine_id,
|
401
406
|
or set configuration on them to a different engine (registered or not) you can:
|
@@ -410,7 +415,7 @@ or set configuration on them to a different engine (registered or not) you can:
|
|
410
415
|
|
411
416
|
If you want a serialization to be consumed by something other than an
|
412
417
|
app using the bento_search gem, as an API, we recommend the [Atom serialization](https://github.com/jrochkind/bento_search/wiki/Machine-Readable-Serialization-With-Atom)
|
413
|
-
instead.
|
418
|
+
instead.
|
414
419
|
|
415
420
|
## Planned Features
|
416
421
|
|
@@ -423,8 +428,6 @@ Probably:
|
|
423
428
|
* Support for display facets for engines that support such, as well as
|
424
429
|
search with limits from controlled vocabulary (ie, selected facet, but
|
425
430
|
also may be supported by some engines that do not support facetting).
|
426
|
-
* Support for multi-field, multi-entry-box 'advanced search' UI's, in
|
427
|
-
a normalized cross-engine way.
|
428
431
|
|
429
432
|
Other needs or suggestions?
|
430
433
|
|
data/Rakefile
CHANGED
@@ -23,17 +23,36 @@ end
|
|
23
23
|
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
24
24
|
load 'rails/tasks/engine.rake'
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
load 'rails/tasks/statistics.rake'
|
27
|
+
|
28
|
+
require 'bundler/gem_tasks'
|
29
|
+
|
30
|
+
|
31
|
+
if Gem::Version.new(Rails.version) > Gem::Version.new('4.2.99999')
|
32
|
+
desc "Run tests"
|
33
|
+
task :test do
|
34
|
+
Rake::Task["app:test"].invoke
|
35
|
+
end
|
36
|
+
# use built-in Rails test command
|
37
|
+
# task :test do
|
38
|
+
# require "rails/test_unit/minitest_plugin"
|
39
|
+
# #$: << File.expand_path('test', ENGINE_ROOT)
|
40
|
+
# Minitest.rake_run([])
|
41
|
+
|
42
|
+
# # require 'rails/engine/commands_tasks'
|
43
|
+
# # Rails::Engine::CommandsTasks.new("").run_command!('test')
|
44
|
+
# end
|
45
|
+
else
|
46
|
+
# old rails4 style
|
47
|
+
require 'rake/testtask'
|
48
|
+
|
49
|
+
Rake::TestTask.new(:test) do |t|
|
50
|
+
t.libs << 'lib'
|
51
|
+
t.libs << 'test'
|
52
|
+
t.pattern = 'test/**/*_test.rb'
|
53
|
+
t.verbose = false
|
54
|
+
t.warning = false
|
55
|
+
end
|
37
56
|
end
|
38
57
|
|
39
58
|
|
@@ -1,56 +1,88 @@
|
|
1
1
|
var BentoSearch = BentoSearch || {}
|
2
2
|
|
3
|
-
// Pass in a DOM node that has a data-ajax-url attribute.
|
3
|
+
// Pass in a DOM node that has a data-ajax-url attribute.
|
4
4
|
// Will AJAX load bento search results inside that node.
|
5
|
-
//
|
6
|
-
|
5
|
+
//
|
6
|
+
// optional success_callback function.
|
7
|
+
//
|
8
|
+
// success_callback: will have the container div as 'this', and new content
|
9
|
+
// as first argument (in JQuery wrapper). Called before DOM update happens, so
|
10
|
+
// first arg div is not yet placed in the DOM. Callback can
|
11
|
+
// return `false` to prevent automatic placement in the DOM, if you want
|
12
|
+
// to handle it yourself.
|
13
|
+
//
|
14
|
+
// You can set default success_callback function for all calls with:
|
15
|
+
//
|
16
|
+
// BentoSearch.ajax_load.default_success_callback = function(div) { ...
|
17
|
+
//
|
18
|
+
// optional beforeSend function.
|
19
|
+
//
|
20
|
+
// beforeSend: to be used by jQuery.ajax as the settings.beforeSend function.
|
21
|
+
// this allows the outgoing request to modified, e.g. to add auth params
|
22
|
+
// in the query param or in a header.
|
23
|
+
// See https://api.jquery.com/jquery.ajax/#jQuery-ajax-settings
|
24
|
+
//
|
25
|
+
// You can set default beforeSend function for all calls with:
|
26
|
+
//
|
27
|
+
// BentoSearch.ajax_load.default_beforeSend = function(xhr, settings) { ...
|
28
|
+
BentoSearch.ajax_load = function(node, success_callback, beforeSend) {
|
29
|
+
// default success_callback
|
30
|
+
if (success_callback === undefined) {
|
31
|
+
success_callback = BentoSearch.ajax_load.default_success_callback;
|
32
|
+
}
|
33
|
+
// default beforeSend
|
34
|
+
if (beforeSend === undefined) {
|
35
|
+
beforeSend = BentoSearch.ajax_load.default_beforeSend;
|
36
|
+
}
|
37
|
+
|
7
38
|
var div = $(node);
|
8
|
-
|
39
|
+
|
9
40
|
if (div.length == 0) {
|
10
41
|
//we've got nothing
|
11
42
|
return
|
12
43
|
}
|
13
|
-
|
44
|
+
|
14
45
|
|
15
46
|
// We find the "waiting"/spinner section already rendered,
|
16
47
|
// and show it. We experimented with generating the spinner/waiting
|
17
48
|
// purely in JS, instead of rendering a hidden one server-side. But
|
18
|
-
// it was too weird and unreliable to do that, sorry.
|
49
|
+
// it was too weird and unreliable to do that, sorry.
|
19
50
|
div.find(".bento_search_ajax_loading").show();
|
20
|
-
|
21
|
-
|
51
|
+
|
52
|
+
|
22
53
|
// Now load the actual external content from html5 data-bento-ajax-url
|
23
54
|
$.ajax({
|
24
|
-
url: div.data("bentoAjaxUrl"),
|
55
|
+
url: div.data("bentoAjaxUrl"),
|
25
56
|
success: function(response, status, xhr) {
|
26
|
-
var do_replace = true;
|
57
|
+
var do_replace = true;
|
27
58
|
if (success_callback) {
|
28
59
|
// We need to make the response into a DOM so the callback
|
29
60
|
// can deal with it better. Wrapped in a div, so it makes
|
30
|
-
// jquery happy even if there isn't a single parent element.
|
61
|
+
// jquery happy even if there isn't a single parent element.
|
31
62
|
response = $("<div>" + response + "</div>");
|
32
|
-
|
33
|
-
do_replace = success_callback.apply(div, [response]);
|
63
|
+
|
64
|
+
do_replace = success_callback.apply(div, [response]);
|
34
65
|
}
|
35
|
-
if (do_replace != false) {
|
66
|
+
if (do_replace != false) {
|
36
67
|
div.replaceWith(response);
|
37
|
-
}
|
68
|
+
}
|
38
69
|
},
|
70
|
+
beforeSend: beforeSend,
|
39
71
|
error: function(xhr, status, errorThrown) {
|
40
72
|
var msg = "Sorry but there was an error: ";
|
41
73
|
div.html(msg + xhr.status + " " + xhr.statusText + ", " + status);
|
42
74
|
}
|
43
75
|
});
|
44
|
-
|
45
|
-
|
76
|
+
|
77
|
+
|
46
78
|
}
|
47
79
|
|
48
80
|
jQuery(document).ready(function($) {
|
49
81
|
//Intentionally wait for window.load, not just onready, to
|
50
|
-
//prevent interfering with rest of page load.
|
51
|
-
$(window).bind("load", function() {
|
82
|
+
//prevent interfering with rest of page load.
|
83
|
+
$(window).bind("load", function() {
|
52
84
|
$("*[data-bento-search-load=ajax_auto]").each(function(i, div) {
|
53
|
-
BentoSearch.ajax_load(div);
|
85
|
+
BentoSearch.ajax_load(div);
|
54
86
|
});
|
55
|
-
});
|
56
|
-
});
|
87
|
+
});
|
88
|
+
});
|
@@ -2,51 +2,51 @@ module BentoSearch
|
|
2
2
|
# This is a controller that provides stand-alone search results
|
3
3
|
# for registered engines. Right now, this is only for automatic
|
4
4
|
# AJAX delayed loading. In the future it may be used for atom results,
|
5
|
-
# or other such.
|
5
|
+
# or other such.
|
6
6
|
#
|
7
7
|
# You need to make sure to include routing for this controller in your
|
8
8
|
# app to use it, for instance with `BentoSearch::Routes.new(self).draw`
|
9
9
|
# in your ./config/routes.rb
|
10
10
|
#
|
11
11
|
# # Authorization Issues
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# You may have some engines which should not be publically searchable,
|
14
14
|
# they should only be searchable by certain auth'd users. This controller
|
15
15
|
# could accidentally provide a non-protected endpoint to get results if
|
16
|
-
# nothing were done to prevent it.
|
16
|
+
# nothing were done to prevent it.
|
17
17
|
#
|
18
18
|
# Only engines which have a :allow_routable_results => true key
|
19
|
-
# in their config will be served by this controller.
|
19
|
+
# in their config will be served by this controller.
|
20
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
|
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_action
|
23
23
|
# to provide auth. Say, in an initializer in your app:
|
24
24
|
#
|
25
|
-
# SearchController.
|
25
|
+
# SearchController.before_action do |controller|
|
26
26
|
# unless controller.current_user
|
27
27
|
# raise BentoSearch::SearchController::AccessDenied
|
28
28
|
# end
|
29
29
|
# end
|
30
|
-
#
|
31
|
-
#
|
30
|
+
#
|
31
|
+
#
|
32
32
|
# We may provide fancier/nicer API for this in the future, if there's
|
33
|
-
# demand.
|
33
|
+
# demand.
|
34
34
|
class SearchController < BentoSearchController
|
35
35
|
class AccessDenied < BentoSearch::Error ; end
|
36
36
|
|
37
|
-
|
37
|
+
|
38
38
|
rescue_from AccessDenied, :with => :deny_access
|
39
39
|
rescue_from NoSuchEngine, :with => :render_404
|
40
|
-
|
40
|
+
|
41
41
|
# returns partial HTML results, suitable for
|
42
|
-
# AJAX to insert into DOM.
|
42
|
+
# AJAX to insert into DOM.
|
43
43
|
# arguments for engine.search are taken from URI request params, whitelisted
|
44
|
-
def search
|
44
|
+
def search
|
45
45
|
engine = BentoSearch.get_engine(params[:engine_id])
|
46
|
-
# put it in an iVar mainly for testing purposes.
|
46
|
+
# put it in an iVar mainly for testing purposes.
|
47
47
|
@engine = engine
|
48
48
|
|
49
|
-
|
49
|
+
|
50
50
|
unless engine.configuration.allow_routable_results == true
|
51
51
|
raise AccessDenied.new("engine needs to be registered with :allow_routable_results => true")
|
52
52
|
end
|
@@ -54,28 +54,29 @@ module BentoSearch
|
|
54
54
|
@results = engine.search safe_search_args(engine, params)
|
55
55
|
# template name of a partial with 'yield' to use to wrap the results
|
56
56
|
@partial_wrapper = @results.display_configuration.lookup!("ajax.wrapper_template")
|
57
|
-
|
57
|
+
|
58
58
|
# partial HTML results
|
59
|
-
render "bento_search/search/search", :layout => false
|
59
|
+
render "bento_search/search/search", :layout => false
|
60
60
|
|
61
61
|
end
|
62
|
-
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
67
|
def safe_search_args(engine, params)
|
68
|
-
params.
|
68
|
+
all_hash = params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params.to_hash
|
69
|
+
all_hash.symbolize_keys.slice( *engine.public_settable_search_args )
|
69
70
|
end
|
70
|
-
|
71
|
+
|
71
72
|
def deny_access(exception)
|
72
|
-
render :
|
73
|
+
render :plain => exception.message, :status => 403
|
73
74
|
end
|
74
|
-
|
75
|
-
def render_404(exception)
|
76
|
-
render :
|
75
|
+
|
76
|
+
def render_404(exception)
|
77
|
+
render :plain => exception.message, :status => 404
|
77
78
|
end
|
78
|
-
|
79
|
-
|
79
|
+
|
80
|
+
|
80
81
|
end
|
81
82
|
end
|
@@ -3,112 +3,110 @@
|
|
3
3
|
require 'nokogiri'
|
4
4
|
|
5
5
|
# Rails helper module provided by BentoSearch meant to be included
|
6
|
-
# in host app's helpers.
|
6
|
+
# in host app's helpers.
|
7
7
|
module BentoSearchHelper
|
8
|
-
|
8
|
+
|
9
9
|
# Renders bento search results on page, or an AJAX loader, etc, as appropriate.
|
10
10
|
# Pass in:
|
11
11
|
# * BentoSearch::SearchResults => will render
|
12
12
|
# * an instantiated BentoSearch::SearchEngine => Will do search and render
|
13
|
-
# * an id that a search engine was registered under with
|
14
|
-
# BentoSearch.register_engine => will do search and render.
|
13
|
+
# * an id that a search engine was registered under with
|
14
|
+
# BentoSearch.register_engine => will do search and render.
|
15
15
|
#
|
16
16
|
# Second arg options hash includes options for bento_search helper,
|
17
17
|
# as well as other options pased on to BentoSearch::Engine.search(options)
|
18
18
|
#
|
19
|
+
# Partial used for display can be configured on engine with
|
20
|
+
# * for_display.error_partial => gets `results` local
|
21
|
+
# * for_display.no_results_partial => gets `results` local
|
22
|
+
# * for_display.item_partial => `:collection => results, :as => :item, :locals => {:results => results}`
|
23
|
+
# * for_display.ajax_loading_partial => local `engine`
|
24
|
+
#
|
25
|
+
# If not specified for a particular engine, the partials listed in BentoSearch.defaults will be used.
|
26
|
+
#
|
19
27
|
# == Options
|
20
28
|
#
|
21
29
|
# load: :ajax_auto, :immediate. :ajax_auto will put a spinner up there,
|
22
30
|
# and load actual results via AJAX request. :immediate preloads
|
23
|
-
# results.
|
31
|
+
# results.
|
24
32
|
#
|
25
33
|
# == Examples
|
26
34
|
#
|
27
35
|
# bento_results( results_obj )
|
28
36
|
# bento_results( engine_obj, :query => "cancer")
|
29
37
|
# bento_results("google_books", :query => "cancer", :load => :ajax_auto)
|
30
|
-
#
|
38
|
+
#
|
31
39
|
def bento_search(search_arg, options = {})
|
32
|
-
|
40
|
+
|
33
41
|
results = search_arg if search_arg.kind_of? BentoSearch::Results
|
34
|
-
|
35
|
-
load_mode = options.delete(:load)
|
36
|
-
|
42
|
+
|
43
|
+
load_mode = options.delete(:load)
|
44
|
+
|
37
45
|
engine = nil
|
38
46
|
unless results
|
39
|
-
# need to load an engine and do a search, or ajax, etc.
|
47
|
+
# need to load an engine and do a search, or ajax, etc.
|
40
48
|
engine = (if search_arg.kind_of? BentoSearch::SearchEngine
|
41
49
|
search_arg
|
42
50
|
else
|
43
51
|
raise ArgumentError.new("Need Results, engine, or registered engine_id as first argument to #bento_search") unless search_arg
|
44
52
|
BentoSearch.get_engine(search_arg.to_s)
|
45
53
|
end)
|
46
|
-
|
54
|
+
|
47
55
|
end
|
48
56
|
|
49
57
|
if (!results && [:ajax_auto, :ajax_triggered].include?(load_mode))
|
50
58
|
raise ArgumentError.new("`:load => :ajax` requires a registered engine with an id") unless engine.configuration.id
|
51
59
|
content_tag(:div,
|
52
60
|
:class => "bento_search_ajax_wait",
|
53
|
-
:"data-bento-search-load" => load_mode.to_s,
|
61
|
+
:"data-bento-search-load" => load_mode.to_s,
|
54
62
|
:"data-bento-ajax-url" => to_bento_search_url( {:engine_id => engine.configuration.id}.merge(options) )) do
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
content_tag("noscript") do
|
59
|
-
I18n.t("bento_search.ajax_noscript")
|
60
|
-
end +
|
61
|
-
content_tag(:div,
|
62
|
-
:class => "bento_search_ajax_loading",
|
63
|
-
:style => "display:none") do
|
64
|
-
image_tag("bento_search/large_loader.gif",
|
65
|
-
:alt => I18n.translate("bento_search.ajax_loading")
|
66
|
-
)
|
63
|
+
|
64
|
+
partial = (engine.configuration.for_display.ajax_loading_partial if engine.configuration.for_display) || BentoSearch.defaults.ajax_loading_partial
|
65
|
+
render :partial => partial, locals: { engine: engine }
|
67
66
|
end
|
68
|
-
end
|
69
67
|
else
|
70
68
|
results = engine.search(options) unless results
|
71
69
|
|
72
70
|
if results.failed?
|
73
|
-
partial = (results.display_configuration.error_partial if results.display_configuration) ||
|
71
|
+
partial = (results.display_configuration.error_partial if results.display_configuration) || BentoSearch.defaults.error_partial
|
74
72
|
render :partial => partial, :locals => {:results => results}
|
75
|
-
elsif results.length > 0
|
76
|
-
partial = (results.display_configuration.item_partial if results.display_configuration) ||
|
73
|
+
elsif results.length > 0
|
74
|
+
partial = (results.display_configuration.item_partial if results.display_configuration) || BentoSearch.defaults.item_partial
|
77
75
|
render :partial => partial, :collection => results, :as => :item, :locals => {:results => results}
|
78
76
|
else
|
79
77
|
content_tag(:div, :class=> "bento_search_no_results") do
|
80
|
-
partial = (results.display_configuration.no_results_partial if results.display_configuration) ||
|
78
|
+
partial = (results.display_configuration.no_results_partial if results.display_configuration) || BentoSearch.defaults.no_results_partial
|
81
79
|
render :partial => partial, :locals => {:results => results}
|
82
80
|
end
|
83
81
|
end
|
84
|
-
end
|
82
|
+
end
|
85
83
|
end
|
86
|
-
|
84
|
+
|
87
85
|
# Wrap a ResultItem in a decorator! For now hard-coded to
|
88
86
|
# BentoSearch::StandardDecorator
|
89
|
-
def bento_decorate(result_item)
|
87
|
+
def bento_decorate(result_item)
|
90
88
|
# in a helper method, 'self' is a view_context already I think?
|
91
|
-
decorated = BentoSearch::DecoratorBase.decorate(result_item, self)
|
92
|
-
yield(decorated) if block_given?
|
93
|
-
return decorated
|
89
|
+
decorated = BentoSearch::DecoratorBase.decorate(result_item, self)
|
90
|
+
yield(decorated) if block_given?
|
91
|
+
return decorated
|
94
92
|
end
|
95
|
-
|
96
|
-
|
93
|
+
|
94
|
+
|
97
95
|
##
|
98
96
|
# More methods used by bento standard views, namespaced with bento_, sorry
|
99
97
|
# no great way to take logic out of views into helper methods without
|
100
|
-
# namespacey hack.
|
98
|
+
# namespacey hack.
|
101
99
|
#
|
102
100
|
# You can use these methods in your own custom views, you also should be
|
103
101
|
# able to over-ride them (including calling super) in local helpers to
|
104
|
-
# change behavior in standard views.
|
102
|
+
# change behavior in standard views.
|
105
103
|
#
|
106
104
|
##
|
107
|
-
|
105
|
+
|
108
106
|
# Like rails truncate helper, and taking the same options, but html_safe.
|
109
|
-
#
|
110
|
-
# If input string is NOT marked html_safe?, simply passes to rails truncate helper.
|
111
|
-
# If a string IS marked html_safe?, uses nokogiri to parse it, and truncate
|
107
|
+
#
|
108
|
+
# If input string is NOT marked html_safe?, simply passes to rails truncate helper.
|
109
|
+
# If a string IS marked html_safe?, uses nokogiri to parse it, and truncate
|
112
110
|
# actual displayed text to max_length, while keeping html structure valid.
|
113
111
|
#
|
114
112
|
# Default omission marker is unicode elipsis
|
@@ -118,23 +116,23 @@ module BentoSearchHelper
|
|
118
116
|
def bento_truncate(str, options = {})
|
119
117
|
return str if str.nil? || str.empty?
|
120
118
|
|
121
|
-
options.reverse_merge!(:omission => "…", :length => 280, :separator => ' ')
|
122
|
-
|
119
|
+
options.reverse_merge!(:omission => "…", :length => 280, :separator => ' ')
|
120
|
+
|
123
121
|
# works for non-html of course, but for html a quick check
|
124
122
|
# to avoid expensive nokogiri parse if the whole string, even
|
125
|
-
# with tags, is still less than max length.
|
123
|
+
# with tags, is still less than max length.
|
126
124
|
return str if str.length < options[:length]
|
127
|
-
|
128
|
-
if str.html_safe?
|
125
|
+
|
126
|
+
if str.html_safe?
|
129
127
|
noko = Nokogiri::HTML::DocumentFragment.parse(str)
|
130
128
|
BentoSearch::Util.nokogiri_truncate(noko, options[:length], options[:omission], options[:separator]).inner_html.html_safe
|
131
129
|
else
|
132
130
|
return truncate(str, options)
|
133
131
|
end
|
134
132
|
end
|
135
|
-
|
136
133
|
|
137
|
-
|
134
|
+
|
135
|
+
|
138
136
|
# Deprecated, made more sense to put this in a partial. Just
|
139
137
|
# call partial directly:
|
140
138
|
# <%= render :partial => "bento_search/item_title", :object => item, :as => 'item' %>
|
@@ -142,41 +140,41 @@ module BentoSearchHelper
|
|
142
140
|
render :partial => "bento_search/item_title", :object => item, :as => 'item'
|
143
141
|
end
|
144
142
|
deprecate :bento_item_title
|
145
|
-
|
143
|
+
|
146
144
|
# pass in 0-based rails current collection counter and a BentoSearch::Results,
|
147
|
-
# calculates a user-displayable result set index label.
|
145
|
+
# calculates a user-displayable result set index label.
|
148
146
|
#
|
149
147
|
# Only non-trivial thing is both inputs are allowed to be nil; if either
|
150
|
-
# is nil, nil is returned.
|
148
|
+
# is nil, nil is returned.
|
151
149
|
def bento_item_counter(counter, results)
|
152
150
|
return nil if counter.nil? || results.nil? || results.start.nil?
|
153
|
-
|
151
|
+
|
154
152
|
return counter + results.start + 1
|
155
153
|
end
|
156
|
-
|
157
|
-
|
154
|
+
|
155
|
+
|
158
156
|
# returns a hash of label => key suitable for passing to rails
|
159
|
-
# options_for_select. (Yes, it works backwards from how you'd expect).
|
157
|
+
# options_for_select. (Yes, it works backwards from how you'd expect).
|
160
158
|
# Label is looked up using I18n, at bento_search.sort_keys.*
|
161
159
|
#
|
162
160
|
# If no i18n is found, titleized version of key itself is used as somewhat
|
163
|
-
# reasonable default.
|
161
|
+
# reasonable default.
|
164
162
|
def bento_sort_hash_for(engine)
|
165
|
-
Hash[
|
163
|
+
Hash[
|
166
164
|
engine.sort_definitions.keys.collect do |key|
|
167
165
|
[I18n.translate(key.to_s, :scope => "bento_search.sort_keys", :default => key.to_s.titleize), key.to_s]
|
168
|
-
end
|
169
|
-
]
|
166
|
+
end
|
167
|
+
]
|
170
168
|
end
|
171
|
-
|
169
|
+
|
172
170
|
# Returns a hash of label => key suitable for passing to rails
|
173
171
|
# options_for_select. ONLY includes fields with :semantics set at
|
174
|
-
# present. Key will be _semantic_ key name.
|
172
|
+
# present. Key will be _semantic_ key name.
|
175
173
|
# For engine-specific fields, you're on you're own, sorry!
|
176
174
|
#
|
177
175
|
# If first arg is an engine instance, will
|
178
176
|
# be search fields supported by that engine. If first arg is nil,
|
179
|
-
# will be any field in our i18n lists for search fields.
|
177
|
+
# will be any field in our i18n lists for search fields.
|
180
178
|
#
|
181
179
|
# Can pass in options :only or :except to customize list. Values
|
182
180
|
# in :only and :except will match on internal field names OR
|
@@ -190,25 +188,25 @@ module BentoSearchHelper
|
|
190
188
|
hash = I18n.t("bento_search.search_fields").invert
|
191
189
|
else
|
192
190
|
hash = Hash[ engine.search_field_definitions.collect do |k, defn|
|
193
|
-
if defn[:semantic] && (label = I18n.t(defn[:semantic], :scope => "bento_search.search_fields", :default => defn[:semantic].to_s.titlecase ))
|
191
|
+
if defn[:semantic] && (label = I18n.t(defn[:semantic], :scope => "bento_search.search_fields", :default => defn[:semantic].to_s.titlecase ))
|
194
192
|
[label, defn[:semantic].to_s]
|
195
193
|
end
|
196
194
|
end.compact]
|
197
195
|
end
|
198
|
-
|
199
|
-
# :only/:except
|
200
|
-
if options[:only]
|
196
|
+
|
197
|
+
# :only/:except
|
198
|
+
if options[:only]
|
201
199
|
keys = [options[:only]].flatten.collect(&:to_s)
|
202
|
-
hash.delete_if {|key, value| ! keys.include?(value) }
|
200
|
+
hash.delete_if {|key, value| ! keys.include?(value) }
|
203
201
|
end
|
204
|
-
|
202
|
+
|
205
203
|
if options[:except]
|
206
204
|
keys = [options[:except]].flatten.collect(&:to_s)
|
207
205
|
hash.delete_if {|key, value| keys.include?(value) }
|
208
206
|
end
|
209
|
-
|
207
|
+
|
210
208
|
return hash
|
211
209
|
end
|
212
|
-
|
213
|
-
|
210
|
+
|
211
|
+
|
214
212
|
end
|