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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +27 -24
  3. data/Rakefile +30 -11
  4. data/app/assets/javascripts/bento_search/ajax_load.js +54 -22
  5. data/app/controllers/bento_search/search_controller.rb +31 -30
  6. data/app/helpers/bento_search_helper.rb +72 -74
  7. data/app/models/bento_search/concurrent_searcher.rb +136 -0
  8. data/app/models/bento_search/result_item.rb +15 -12
  9. data/app/models/bento_search/results/serialization.rb +22 -13
  10. data/app/models/bento_search/search_engine.rb +170 -140
  11. data/app/search_engines/bento_search/doaj_articles_engine.rb +20 -20
  12. data/app/search_engines/bento_search/ebsco_host_engine.rb +3 -3
  13. data/app/search_engines/bento_search/eds_engine.rb +326 -206
  14. data/app/search_engines/bento_search/google_books_engine.rb +2 -2
  15. data/app/search_engines/bento_search/scopus_engine.rb +87 -87
  16. data/app/search_engines/bento_search/summon_engine.rb +1 -1
  17. data/app/views/bento_search/_ajax_loading.html.erb +17 -0
  18. data/app/views/bento_search/_item_title.html.erb +2 -4
  19. data/app/views/bento_search/_link.html.erb +3 -3
  20. data/lib/bento_search.rb +24 -9
  21. data/lib/bento_search/engine.rb +2 -0
  22. data/lib/bento_search/version.rb +1 -1
  23. data/lib/generators/bento_search/install/ajax_load_js_generator.rb +15 -0
  24. data/test/decorator/standard_decorator_test.rb +30 -30
  25. data/test/dummy/app/assets/config/manifest.js +4 -0
  26. data/test/dummy/config/application.rb +7 -0
  27. data/test/dummy/config/boot.rb +4 -9
  28. data/test/dummy/config/environments/development.rb +2 -0
  29. data/test/dummy/config/environments/production.rb +7 -1
  30. data/test/dummy/config/environments/test.rb +10 -3
  31. data/test/functional/bento_search/search_controller_test.rb +68 -58
  32. data/test/helper/bento_search_helper_test.rb +103 -103
  33. data/test/search_engines/doaj_articles_engine_test.rb +9 -9
  34. data/test/search_engines/eds_engine_test.rb +91 -59
  35. data/test/search_engines/google_site_search_test.rb +48 -48
  36. data/test/search_engines/scopus_engine_test.rb +51 -51
  37. data/test/search_engines/search_engine_base_test.rb +108 -86
  38. data/test/search_engines/search_engine_test.rb +68 -56
  39. data/test/support/atom.xsd.xml +3 -3
  40. data/test/support/xml.xsd +117 -0
  41. data/test/test_helper.rb +23 -12
  42. data/test/unit/concurrent_searcher_test.rb +75 -0
  43. data/test/unit/pagination_test.rb +12 -12
  44. data/test/vcr_cassettes/eds/FullText_CustomLink.yml +198 -0
  45. data/test/vcr_cassettes/eds/basic_search_smoke_test.yml +1036 -1729
  46. data/test/vcr_cassettes/eds/catalog_ebook_query.yml +218 -0
  47. data/test/vcr_cassettes/eds/catalog_query.yml +255 -0
  48. data/test/vcr_cassettes/eds/get_auth_token.yml +11 -44
  49. data/test/vcr_cassettes/eds/get_auth_token_failure.yml +10 -7
  50. data/test/vcr_cassettes/eds/get_with_auth.yml +144 -153
  51. data/test/vcr_cassettes/eds/get_with_auth_recovers_from_bad_auth.yml +167 -223
  52. data/test/view/atom_results_test.rb +94 -94
  53. metadata +36 -46
  54. data/app/assets/javascripts/bento_search.js +0 -3
  55. data/app/item_decorators/bento_search/ebscohost/conditional_openurl_main_link.rb +0 -36
  56. data/app/item_decorators/bento_search/only_premade_openurl.rb +0 -20
  57. data/app/item_decorators/bento_search/openurl_add_other_link.rb +0 -39
  58. data/app/item_decorators/bento_search/openurl_main_link.rb +0 -34
  59. data/app/models/bento_search/multi_searcher.rb +0 -131
  60. data/test/dummy/config/initializers/secret_token.rb +0 -8
  61. data/test/unit/multi_searcher_test.rb +0 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8824757e65434f6a003c9748cf68e51cddcc3795
4
- data.tar.gz: 081397571dbcb6307fdf9f162f5493e1d75d1e77
2
+ SHA256:
3
+ metadata.gz: ed755cb880b6d45bd02d53373d80c25161c6689ef59b7108106896c0a392fc4c
4
+ data.tar.gz: 4c9009a41299bc44b84274e93c5f0e7a9a5fda599e0de97f6e8972cd3519faf0
5
5
  SHA512:
6
- metadata.gz: b5f11bd2566934fc22409ec97c9aa89d91e10a497948839ce6708e9a7c90bbf1ee1d951d5d2585641aa1dc0afb0fb082ac4cd258d64b25f8589bfda112233fd5
7
- data.tar.gz: 32ac0a5ef5be6ec91d2b9bd034e7332d7ab52812743e56aeca97f59832c69fba45a20153102264730e6e18c9d9f97dfb26f4c0d51d1589eaf651fa9d2632823e
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://secure.travis-ci.org/jrochkind/bento_search.png)](http://travis-ci.org/jrochkind/bento_search)
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 3.x or 4.x. ruby 1.9.3+
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, generalized limiting, and 'advanced'
53
- multi-field searches are not yet supported, but possibly will be built
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 accross engines. BentoSearch::Items provide semantic
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 [Celluloid](https://github.com/celluloid/celluloid)
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::MultiSearcher.new(:gbs, :scopus, :summon)
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
- You will need to add the 'celluloid' gem to your app to use this feature,
277
- BentoSearch doesn't automatically include the celluloid dependency. Note
278
- that Celluloid uses multi-threading in such a way that you might need
279
- to turn Rails config.cache_classes=true even in development.
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
- 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
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
- // optional second arg success callback function.
6
- BentoSearch.ajax_load = function(node, success_callback) {
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 before_filter
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.before_filter do |controller|
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
- protected
66
-
63
+
64
+
65
+ protected
66
+
67
67
  def safe_search_args(engine, params)
68
- params.to_hash.symbolize_keys.slice( *engine.public_settable_search_args )
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 :text => exception.message, :status => 403
73
+ render :plain => exception.message, :status => 403
73
74
  end
74
-
75
- def render_404(exception)
76
- render :text => exception.message, :status => 404
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
- # An initially hidden div with loading msg/spinner that will be shown
57
- # by js on ajax load
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) || "bento_search/search_error"
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) || "bento_search/std_item"
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) || "bento_search/no_results"
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