bento_search 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +6 -5
 - data/app/assets/javascripts/bento_search/ajax_load.js +42 -16
 - data/app/assets/stylesheets/bento_search/suggested_styles.css +9 -0
 - data/app/controllers/bento_search/search_controller.rb +15 -6
 - data/app/helpers/bento_search_helper.rb +24 -8
 - data/app/item_decorators/bento_search/no_links.rb +13 -0
 - data/app/models/bento_search/openurl_creator.rb +18 -8
 - data/app/models/bento_search/registrar.rb +2 -6
 - data/app/models/bento_search/result_item.rb +43 -3
 - data/app/models/bento_search/results.rb +4 -0
 - data/app/models/bento_search/search_engine.rb +25 -23
 - data/app/search_engines/bento_search/ebsco_host_engine.rb +42 -17
 - data/app/search_engines/bento_search/google_books_engine.rb +2 -0
 - data/app/search_engines/bento_search/google_site_search_engine.rb +177 -0
 - data/app/search_engines/bento_search/mock_engine.rb +5 -0
 - data/app/search_engines/bento_search/primo_engine.rb +23 -2
 - data/app/search_engines/bento_search/scopus_engine.rb +4 -1
 - data/app/search_engines/bento_search/summon_engine.rb +4 -14
 - data/app/search_engines/bento_search/worldcat_sru_dc_engine.rb +293 -0
 - data/app/views/bento_search/_std_item.html.erb +4 -5
 - data/app/views/bento_search/_wrap_with_count.html.erb +20 -0
 - data/app/views/bento_search/search/search.html.erb +15 -1
 - data/config/locales/en.yml +6 -4
 - data/lib/bento_search/util.rb +13 -0
 - data/lib/bento_search/version.rb +1 -1
 - data/test/dummy/log/development.log +1 -0
 - data/test/dummy/log/test.log +24357 -0
 - data/test/functional/bento_search/search_controller_test.rb +39 -0
 - data/test/helper/bento_search_helper_test.rb +47 -5
 - data/test/unit/ebsco_host_engine_test.rb +15 -0
 - data/test/unit/google_books_engine_test.rb +1 -0
 - data/test/unit/google_site_search_test.rb +122 -0
 - data/test/unit/item_decorators_test.rb +12 -1
 - data/test/unit/openurl_creator_test.rb +19 -3
 - data/test/unit/primo_engine_test.rb +5 -3
 - data/test/unit/result_item_test.rb +36 -1
 - data/test/unit/search_engine_test.rb +27 -4
 - data/test/unit/worldcat_sru_dc_engine_test.rb +120 -0
 - data/test/vcr_cassettes/google_site/basic_smoke_test.yml +254 -0
 - data/test/vcr_cassettes/google_site/empty_result_set.yml +53 -0
 - data/test/vcr_cassettes/google_site/pagination_object_is_correct_for_actual_page_when_you_ask_for_too_far.yml +260 -0
 - data/test/vcr_cassettes/google_site/with_highlighting.yml +265 -0
 - data/test/vcr_cassettes/google_site/without_highlighting.yml +267 -0
 - data/test/vcr_cassettes/primo/proper_tags_for_snippets.yml +517 -502
 - data/test/vcr_cassettes/primo/search_smoke_test.yml +1 -1
 - data/test/vcr_cassettes/worldcat_sru_dc/smoke_test.yml +628 -0
 - metadata +40 -4
 
    
        data/README.md
    CHANGED
    
    | 
         @@ -13,7 +13,7 @@ Rails3 and tested only under ruby 1.9.3. 
     | 
|
| 
       13 
13 
     | 
    
         
             
            * It is focused on use cases for academic libraries, but may be useful in generic
         
     | 
| 
       14 
14 
     | 
    
         
             
            cases too. Initially, engine adapters are planned to be provided for: 
         
     | 
| 
       15 
15 
     | 
    
         
             
            Google Books, Scopus, SerialSolutions Summon, Ex Libris Primo, 
         
     | 
| 
       16 
     | 
    
         
            -
            EBSCO Discovery Service,  
     | 
| 
      
 16 
     | 
    
         
            +
            EBSCO Discovery Service, EBSCO traditional 'EIT' api, Google Site Search.  Most
         
     | 
| 
       17 
17 
     | 
    
         
             
            of these search engines require a vendor license to use. 
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            * bento_search could be considered building blocks for a type of 'federated
         
     | 
| 
         @@ -42,6 +42,7 @@ BentoSearch::SearchEngine. http://rubydoc.info/gems/bento_search/frames/ 
     | 
|
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
            An example app using BentoSearch and showing it's features is
         
     | 
| 
       44 
44 
     | 
    
         
             
            available at http://github.com/jrochkind/sample_megasearch
         
     | 
| 
      
 45 
     | 
    
         
            +
            There is a short screencast showing that sample app in action here: http://screencast.com/t/JLS0lclrBZU
         
     | 
| 
       45 
46 
     | 
    
         | 
| 
       46 
47 
     | 
    
         
             
            ## Usage Examples
         
     | 
| 
       47 
48 
     | 
    
         | 
| 
         @@ -104,8 +105,8 @@ field type names: 
     | 
|
| 
       104 
105 
     | 
    
         
             
            This will raise if an engine doesn't support that semantic search field. 
         
     | 
| 
       105 
106 
     | 
    
         
             
            You can find out what fields a particular engine supports.
         
     | 
| 
       106 
107 
     | 
    
         | 
| 
       107 
     | 
    
         
            -
                 
     | 
| 
       108 
     | 
    
         
            -
                 
     | 
| 
      
 108 
     | 
    
         
            +
                google_books_engine.search_keys # => internal keys
         
     | 
| 
      
 109 
     | 
    
         
            +
                google_books_engine.semantic_search_keys 
         
     | 
| 
       109 
110 
     | 
    
         | 
| 
       110 
111 
     | 
    
         
             
            You can also provide all arguments in a single hash when it's convenient
         
     | 
| 
       111 
112 
     | 
    
         
             
            to do so:
         
     | 
| 
         @@ -116,9 +117,9 @@ to do so: 
     | 
|
| 
       116 
117 
     | 
    
         | 
| 
       117 
118 
     | 
    
         
             
            An engine advertises what sort types it supports:
         
     | 
| 
       118 
119 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                 
     | 
| 
      
 120 
     | 
    
         
            +
                google_books_engine.sort_keys
         
     | 
| 
       120 
121 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
      
 122 
     | 
    
         
            +
            An array of sort identifiers, where possible
         
     | 
| 
       122 
123 
     | 
    
         
             
            chosen from a standard list of semantics. (See list in config/i18n/en.yml,
         
     | 
| 
       123 
124 
     | 
    
         
             
            bento_search.sort_keys). 
         
     | 
| 
       124 
125 
     | 
    
         | 
| 
         @@ -1,22 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            var BentoSearch = BentoSearch || {}
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            // Pass in a DOM node that has a data-ajax-url attribute. 
         
     | 
| 
      
 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) {
         
     | 
| 
      
 7 
     | 
    
         
            +
              div = $(node);
         
     | 
| 
      
 8 
     | 
    
         
            +
              
         
     | 
| 
      
 9 
     | 
    
         
            +
              if (div.length == 0) {
         
     | 
| 
      
 10 
     | 
    
         
            +
                //we've got nothing
         
     | 
| 
      
 11 
     | 
    
         
            +
                return
         
     | 
| 
      
 12 
     | 
    
         
            +
              }
         
     | 
| 
      
 13 
     | 
    
         
            +
              
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              // We find the "waiting"/spinner section already rendered,
         
     | 
| 
      
 16 
     | 
    
         
            +
              // and show it. We experimented with generating the spinner/waiting
         
     | 
| 
      
 17 
     | 
    
         
            +
              // purely in JS, instead of rendering a hidden one server-side. But
         
     | 
| 
      
 18 
     | 
    
         
            +
              // it was too weird and unreliable to do that, sorry.   
         
     | 
| 
      
 19 
     | 
    
         
            +
              div.find(".bento_search_ajax_loading").show();
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
              
         
     | 
| 
      
 22 
     | 
    
         
            +
              // Now load the actual external content from html5 data-bento-ajax-url
         
     | 
| 
      
 23 
     | 
    
         
            +
              $.ajax({
         
     | 
| 
      
 24 
     | 
    
         
            +
                  url: div.data("bentoAjaxUrl"), 
         
     | 
| 
      
 25 
     | 
    
         
            +
                  success: function(response, status, xhr) {        
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if (success_callback) {
         
     | 
| 
      
 27 
     | 
    
         
            +
                       success_callback.apply(div, response);
         
     | 
| 
      
 28 
     | 
    
         
            +
                    }
         
     | 
| 
      
 29 
     | 
    
         
            +
                    div.replaceWith(response);          
         
     | 
| 
      
 30 
     | 
    
         
            +
                  },
         
     | 
| 
      
 31 
     | 
    
         
            +
                  error: function(xhr, status, errorThrown) {
         
     | 
| 
      
 32 
     | 
    
         
            +
                    var msg = "Sorry but there was an error: ";
         
     | 
| 
      
 33 
     | 
    
         
            +
                    div.html(msg + xhr.status + " " + xhr.statusText + ", " + status);
         
     | 
| 
      
 34 
     | 
    
         
            +
                  }
         
     | 
| 
      
 35 
     | 
    
         
            +
              });
         
     | 
| 
      
 36 
     | 
    
         
            +
             
         
     | 
| 
      
 37 
     | 
    
         
            +
              
         
     | 
| 
      
 38 
     | 
    
         
            +
            }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       1 
40 
     | 
    
         
             
            jQuery(document).ready(function($) {
         
     | 
| 
       2 
41 
     | 
    
         
             
                //Intentionally wait for window.load, not just onready, to
         
     | 
| 
       3 
42 
     | 
    
         
             
                //prevent interfering with rest of page load. 
         
     | 
| 
       4 
43 
     | 
    
         
             
                $(window).bind("load", function() {   
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       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 
     | 
    
         
            -
                                          
         
     | 
| 
      
 44 
     | 
    
         
            +
                    $("*[data-bento-search-load=ajax_auto]").each(function(i, div) {
         
     | 
| 
      
 45 
     | 
    
         
            +
                        BentoSearch.ajax_load(div);                              
         
     | 
| 
       19 
46 
     | 
    
         
             
                  });
         
     | 
| 
       20 
     | 
    
         
            -
                });
         
     | 
| 
       21 
     | 
    
         
            -
                
         
     | 
| 
      
 47 
     | 
    
         
            +
                });    
         
     | 
| 
       22 
48 
     | 
    
         
             
            });
         
     | 
| 
         @@ -20,3 +20,12 @@ 
     | 
|
| 
       20 
20 
     | 
    
         
             
              margin: 2em;
         
     | 
| 
       21 
21 
     | 
    
         
             
            }
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
            /* we put title in an h4, but if it's a link too, it
         
     | 
| 
      
 24 
     | 
    
         
            +
               doesn't really need to be bold, link style is already
         
     | 
| 
      
 25 
     | 
    
         
            +
               visible enough. */ 
         
     | 
| 
      
 26 
     | 
    
         
            +
            .bento_item_title a {
         
     | 
| 
      
 27 
     | 
    
         
            +
              font-weight: normal;
         
     | 
| 
      
 28 
     | 
    
         
            +
            }
         
     | 
| 
      
 29 
     | 
    
         
            +
            .bento_item_title a .bento_search_highlight {
         
     | 
| 
      
 30 
     | 
    
         
            +
                font-style: inherit;
         
     | 
| 
      
 31 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -40,25 +40,34 @@ module BentoSearch 
     | 
|
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
                # returns partial HTML results, suitable for
         
     | 
| 
       42 
42 
     | 
    
         
             
                # AJAX to insert into DOM. 
         
     | 
| 
       43 
     | 
    
         
            -
                # arguments for engine.search are taken from URI request params 
     | 
| 
       44 
     | 
    
         
            -
                 
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                   
     | 
| 
      
 43 
     | 
    
         
            +
                # arguments for engine.search are taken from URI request params, whitelisted
         
     | 
| 
      
 44 
     | 
    
         
            +
                def search           
         
     | 
| 
      
 45 
     | 
    
         
            +
                  engine  =  BentoSearch.get_engine(params[:engine_id])
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # put it in an iVar mainly for testing purposes. 
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @engine = engine
         
     | 
| 
       47 
48 
     | 
    
         | 
| 
       48 
49 
     | 
    
         | 
| 
       49 
50 
     | 
    
         
             
                  unless engine.configuration.allow_routable_results == true
         
     | 
| 
       50 
51 
     | 
    
         
             
                    raise AccessDenied.new("engine needs to be registered with :allow_routable_results => true")
         
     | 
| 
       51 
52 
     | 
    
         
             
                  end
         
     | 
| 
       52 
53 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                  @results 
     | 
| 
      
 54 
     | 
    
         
            +
                  @results         = engine.search safe_search_args(engine, params)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # template name of a partial with 'yield' to use to wrap the results
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @partial_wrapper = @results.display_configuration.lookup!("ajax.wrapper_template")
         
     | 
| 
       54 
57 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                   
     | 
| 
      
 58 
     | 
    
         
            +
                  # partial HTML results
         
     | 
| 
      
 59 
     | 
    
         
            +
                  render "bento_search/search/search", :layout => false 
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       56 
61 
     | 
    
         
             
                end
         
     | 
| 
       57 
62 
     | 
    
         | 
| 
       58 
63 
     | 
    
         | 
| 
       59 
64 
     | 
    
         | 
| 
       60 
65 
     | 
    
         
             
                protected 
         
     | 
| 
       61 
66 
     | 
    
         | 
| 
      
 67 
     | 
    
         
            +
                def safe_search_args(engine, params)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  params.to_hash.symbolize_keys.slice( *engine.public_settable_search_args )       
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
                
         
     | 
| 
       62 
71 
     | 
    
         
             
                def deny_access(exception)
         
     | 
| 
       63 
72 
     | 
    
         
             
                  render :text => exception.message, :status => 403
         
     | 
| 
       64 
73 
     | 
    
         
             
                end
         
     | 
| 
         @@ -40,13 +40,26 @@ module BentoSearchHelper 
     | 
|
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
                end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
                if (!results &&  
     | 
| 
      
 43 
     | 
    
         
            +
                if (!results && [:ajax_auto, :ajax_triggered].include?(load_mode))
         
     | 
| 
       44 
44 
     | 
    
         
             
                  raise ArgumentError.new("`:load => :ajax` requires a registered engine with an id") unless engine.configuration.id
         
     | 
| 
       45 
     | 
    
         
            -
                  content_tag(:div, 
     | 
| 
       46 
     | 
    
         
            -
                    : 
     | 
| 
       47 
     | 
    
         
            -
                     
     | 
| 
       48 
     | 
    
         
            -
                     
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 45 
     | 
    
         
            +
                  content_tag(:div,
         
     | 
| 
      
 46 
     | 
    
         
            +
                    :class => "bento_search_ajax_wait",
         
     | 
| 
      
 47 
     | 
    
         
            +
                    :"data-bento-search-load" => load_mode.to_s, 
         
     | 
| 
      
 48 
     | 
    
         
            +
                    :"data-bento-ajax-url"    => to_bento_search_url( {:engine_id => engine.configuration.id}.merge(options) )) do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # An initially hidden div with loading msg/spinner that will be shown
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # by js on ajax load
         
     | 
| 
      
 52 
     | 
    
         
            +
                  content_tag("noscript") do
         
     | 
| 
      
 53 
     | 
    
         
            +
                    I18n.t("bento_search.ajax_noscript")
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end +
         
     | 
| 
      
 55 
     | 
    
         
            +
                  content_tag(:div, 
         
     | 
| 
      
 56 
     | 
    
         
            +
                    :class => "bento_search_ajax_loading", 
         
     | 
| 
      
 57 
     | 
    
         
            +
                    :style => "display:none") do
         
     | 
| 
      
 58 
     | 
    
         
            +
                  
         
     | 
| 
      
 59 
     | 
    
         
            +
                      image_tag("bento_search/large_loader.gif", 
         
     | 
| 
      
 60 
     | 
    
         
            +
                        :alt => I18n.translate("bento_search.ajax_loading"),            
         
     | 
| 
      
 61 
     | 
    
         
            +
                      ) 
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
       50 
63 
     | 
    
         
             
                    end
         
     | 
| 
       51 
64 
     | 
    
         
             
                  end
         
     | 
| 
       52 
65 
     | 
    
         
             
                else
         
     | 
| 
         @@ -55,7 +68,7 @@ module BentoSearchHelper 
     | 
|
| 
       55 
68 
     | 
    
         
             
                  if results.failed?
         
     | 
| 
       56 
69 
     | 
    
         
             
                    render :partial => "bento_search/search_error", :locals => {:results => results}
         
     | 
| 
       57 
70 
     | 
    
         
             
                  elsif results.length > 0      
         
     | 
| 
       58 
     | 
    
         
            -
                    render :partial => "bento_search/std_item", :collection => results
         
     | 
| 
      
 71 
     | 
    
         
            +
                    render :partial => "bento_search/std_item", :collection => results, :as => :item
         
     | 
| 
       59 
72 
     | 
    
         
             
                  else
         
     | 
| 
       60 
73 
     | 
    
         
             
                    content_tag(:div, :class=> "bento_search_no_results") do
         
     | 
| 
       61 
74 
     | 
    
         
             
                      I18n.translate("bento_search.no_results")
         
     | 
| 
         @@ -126,10 +139,13 @@ module BentoSearchHelper 
     | 
|
| 
       126 
139 
     | 
    
         
             
              # returns a hash of label => key suitable for passing to rails
         
     | 
| 
       127 
140 
     | 
    
         
             
              # options_for_select. (Yes, it works backwards from how you'd expect). 
         
     | 
| 
       128 
141 
     | 
    
         
             
              # Label is looked up using I18n, at bento_search.sort_keys.*
         
     | 
| 
      
 142 
     | 
    
         
            +
              #
         
     | 
| 
      
 143 
     | 
    
         
            +
              # If no i18n is found, titleized version of key itself is used as somewhat
         
     | 
| 
      
 144 
     | 
    
         
            +
              # reasonable default. 
         
     | 
| 
       129 
145 
     | 
    
         
             
              def bento_sort_hash_for(engine)
         
     | 
| 
       130 
146 
     | 
    
         
             
                Hash[ 
         
     | 
| 
       131 
147 
     | 
    
         
             
                  engine.sort_definitions.keys.collect do |key|
         
     | 
| 
       132 
     | 
    
         
            -
                    [I18n.translate(key, :scope => "bento_search.sort_keys"), key]
         
     | 
| 
      
 148 
     | 
    
         
            +
                    [I18n.translate(key.to_s, :scope => "bento_search.sort_keys", :default => key.to_s.titleize), key.to_s]
         
     | 
| 
       133 
149 
     | 
    
         
             
                  end        
         
     | 
| 
       134 
150 
     | 
    
         
             
                ]    
         
     | 
| 
       135 
151 
     | 
    
         
             
              end
         
     | 
| 
         @@ -16,6 +16,8 @@ module BentoSearch 
     | 
|
| 
       16 
16 
     | 
    
         
             
              # In some cases nil can be returned, if no reasonable OpenURL can
         
     | 
| 
       17 
17 
     | 
    
         
             
              # be created from the ResultItem. 
         
     | 
| 
       18 
18 
     | 
    
         
             
              class OpenurlCreator
         
     | 
| 
      
 19 
     | 
    
         
            +
                include ActionView::Helpers::SanitizeHelper # for strip_tags
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
       19 
21 
     | 
    
         
             
                attr_accessor :result_item
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
23 
     | 
    
         
             
                def initialize(ri)
         
     | 
| 
         @@ -42,9 +44,9 @@ module BentoSearch 
     | 
|
| 
       42 
44 
     | 
    
         
             
                  r.set_metadata("genre", self.genre)
         
     | 
| 
       43 
45 
     | 
    
         | 
| 
       44 
46 
     | 
    
         
             
                  if result_item.authors.length > 0
         
     | 
| 
       45 
     | 
    
         
            -
                    r.set_metadata("aufirst", result_item.authors.first.first)
         
     | 
| 
       46 
     | 
    
         
            -
                    r.set_metadata("aulast", result_item.authors.first.last)
         
     | 
| 
       47 
     | 
    
         
            -
                    r.set_metadata("au", result_item.author_display(result_item.authors.first))
         
     | 
| 
      
 47 
     | 
    
         
            +
                    r.set_metadata("aufirst", ensure_no_tags(result_item.authors.first.first))
         
     | 
| 
      
 48 
     | 
    
         
            +
                    r.set_metadata("aulast", ensure_no_tags(result_item.authors.first.last))
         
     | 
| 
      
 49 
     | 
    
         
            +
                    r.set_metadata("au", result_item.author_display(ensure_no_tags result_item.authors.first))
         
     | 
| 
       48 
50 
     | 
    
         
             
                  end
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
       50 
52 
     | 
    
         
             
                  r.set_metadata("date",    result_item.year.to_s)
         
     | 
| 
         @@ -52,18 +54,18 @@ module BentoSearch 
     | 
|
| 
       52 
54 
     | 
    
         
             
                  r.set_metadata("issue",   result_item.issue.to_s)
         
     | 
| 
       53 
55 
     | 
    
         
             
                  r.set_metadata("spage",   result_item.start_page.to_s)
         
     | 
| 
       54 
56 
     | 
    
         
             
                  r.set_metadata("epage",   result_item.end_page.to_s)
         
     | 
| 
       55 
     | 
    
         
            -
                  r.set_metadata("jtitle",  result_item. 
     | 
| 
      
 57 
     | 
    
         
            +
                  r.set_metadata("jtitle",  ensure_no_tags(result_item.source_title))
         
     | 
| 
       56 
58 
     | 
    
         
             
                  r.set_metadata("issn",    result_item.issn)
         
     | 
| 
       57 
59 
     | 
    
         
             
                  r.set_metadata("isbn",    result_item.isbn)
         
     | 
| 
       58 
     | 
    
         
            -
                  r.set_metadata("pub",     result_item.publisher)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  r.set_metadata("pub",     ensure_no_tags(result_item.publisher))
         
     | 
| 
       59 
61 
     | 
    
         | 
| 
       60 
62 
     | 
    
         
             
                  case result_item.format
         
     | 
| 
       61 
63 
     | 
    
         
             
                  when "Book"
         
     | 
| 
       62 
     | 
    
         
            -
                    r.set_metadata("btitle", result_item.complete_title)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    r.set_metadata("btitle", ensure_no_tags(result_item.complete_title))
         
     | 
| 
       63 
65 
     | 
    
         
             
                  when "Article", :conference_paper
         
     | 
| 
       64 
     | 
    
         
            -
                    r.set_metadata("atitle", result_item.complete_title)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    r.set_metadata("atitle", ensure_no_tags(result_item.complete_title))
         
     | 
| 
       65 
67 
     | 
    
         
             
                  else
         
     | 
| 
       66 
     | 
    
         
            -
                    r.set_metadata("title", result_item.complete_title)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    r.set_metadata("title", ensure_no_tags(result_item.complete_title))
         
     | 
| 
       67 
69 
     | 
    
         
             
                  end
         
     | 
| 
       68 
70 
     | 
    
         | 
| 
       69 
71 
     | 
    
         
             
                  return context_object
         
     | 
| 
         @@ -124,5 +126,13 @@ module BentoSearch 
     | 
|
| 
       124 
126 
     | 
    
         
             
                end
         
     | 
| 
       125 
127 
     | 
    
         | 
| 
       126 
128 
     | 
    
         | 
| 
      
 129 
     | 
    
         
            +
                # If the input is not marked html_safe?, just return it. Otherwise
         
     | 
| 
      
 130 
     | 
    
         
            +
                # strip html tags from it.
         
     | 
| 
      
 131 
     | 
    
         
            +
                def ensure_no_tags(str)
         
     | 
| 
      
 132 
     | 
    
         
            +
                  return str unless str.html_safe?
         
     | 
| 
      
 133 
     | 
    
         
            +
                  
         
     | 
| 
      
 134 
     | 
    
         
            +
                  strip_tags(str)      
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
                
         
     | 
| 
       127 
137 
     | 
    
         
             
              end
         
     | 
| 
       128 
138 
     | 
    
         
             
            end
         
     | 
| 
         @@ -57,12 +57,8 @@ class BentoSearch::Registrar 
     | 
|
| 
       57 
57 
     | 
    
         
             
              # Turn a string into a constant/class object, lexical lookup
         
     | 
| 
       58 
58 
     | 
    
         
             
              # within BentoSearch module. Can use whatever would be legal
         
     | 
| 
       59 
59 
     | 
    
         
             
              # in ruby, "A", "A::B", "::A::B" (force top-level lookup), etc. 
         
     | 
| 
       60 
     | 
    
         
            -
              def constantize(klass_string) 
     | 
| 
       61 
     | 
    
         
            -
                 
     | 
| 
       62 
     | 
    
         
            -
                  raise NameError, "#{klass_string.inspect} is not a valid constant name!"
         
     | 
| 
       63 
     | 
    
         
            -
                end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                BentoSearch.module_eval(klass_string, __FILE__, __LINE__)
         
     | 
| 
      
 60 
     | 
    
         
            +
              def constantize(klass_string)     
         
     | 
| 
      
 61 
     | 
    
         
            +
                BentoSearch::Util.constantize(klass_string)    
         
     | 
| 
       66 
62 
     | 
    
         
             
              end
         
     | 
| 
       67 
63 
     | 
    
         | 
| 
       68 
64 
     | 
    
         | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'language_list'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module BentoSearch
         
     | 
| 
       2 
4 
     | 
    
         
             
              # Data object representing a single hit from a search, normalized
         
     | 
| 
       3 
5 
     | 
    
         
             
              # with common data fields. Usually held in a BentoSearch::Results object.
         
     | 
| 
         @@ -24,6 +26,10 @@ module BentoSearch 
     | 
|
| 
       24 
26 
     | 
    
         
             
                  self.custom_data ||= {}
         
     | 
| 
       25 
27 
     | 
    
         
             
                end
         
     | 
| 
       26 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
                # If set to true, item will refuse to generate an openurl,
         
     | 
| 
      
 30 
     | 
    
         
            +
                # returning nil from #to_openurl or #openurl_kev
         
     | 
| 
      
 31 
     | 
    
         
            +
                attr_accessor :openurl_disabled 
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
       27 
33 
     | 
    
         
             
                # Array (possibly empty) of BentoSearch::Link objects
         
     | 
| 
       28 
34 
     | 
    
         
             
                # representing additional links. Often SearchEngine's themselves
         
     | 
| 
       29 
35 
     | 
    
         
             
                # won't include any of these, but Decorators will be used
         
     | 
| 
         @@ -81,6 +87,29 @@ module BentoSearch 
     | 
|
| 
       81 
87 
     | 
    
         
             
                # format. 
         
     | 
| 
       82 
88 
     | 
    
         
             
                attr_accessor :format_str
         
     | 
| 
       83 
89 
     | 
    
         | 
| 
      
 90 
     | 
    
         
            +
                # Language of materials. Producer can set language_code to an ISO 639-1 (two
         
     | 
| 
      
 91 
     | 
    
         
            +
                # letter) or 639-3 (three letter) language code. If you do this, you don't
         
     | 
| 
      
 92 
     | 
    
         
            +
                # need to set language_str, it'll be automatically looked up. (Providing
         
     | 
| 
      
 93 
     | 
    
         
            +
                # language name in English at present, i18n later maybe). 
         
     | 
| 
      
 94 
     | 
    
         
            +
                #
         
     | 
| 
      
 95 
     | 
    
         
            +
                # Or, if you don't know the language code (or there isn't one?), you can set 
         
     | 
| 
      
 96 
     | 
    
         
            +
                # language_str manually to a presumably english user-displayable string.
         
     | 
| 
      
 97 
     | 
    
         
            +
                # Manually set language_str will over-ride display string calculated from
         
     | 
| 
      
 98 
     | 
    
         
            +
                # language_code. 
         
     | 
| 
      
 99 
     | 
    
         
            +
                # 
         
     | 
| 
      
 100 
     | 
    
         
            +
                # Consumers can look at language_code or language_str regardless (although
         
     | 
| 
      
 101 
     | 
    
         
            +
                # either or both may be nil). You can use language_list gem to normalize to a 
         
     | 
| 
      
 102 
     | 
    
         
            +
                # 2- or 3-letter from language_code that could be either. 
         
     | 
| 
      
 103 
     | 
    
         
            +
                attr_accessor :language_code
         
     | 
| 
      
 104 
     | 
    
         
            +
                attr_writer :language_str
         
     | 
| 
      
 105 
     | 
    
         
            +
                def language_str
         
     | 
| 
      
 106 
     | 
    
         
            +
                  @language_str || language_code.try do |code|
         
     | 
| 
      
 107 
     | 
    
         
            +
                    LanguageList::LanguageInfo.find(code).try do |lang_obj|
         
     | 
| 
      
 108 
     | 
    
         
            +
                      lang_obj.name
         
     | 
| 
      
 109 
     | 
    
         
            +
                    end
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
                
         
     | 
| 
       84 
113 
     | 
    
         
             
                # year published. a ruby int
         
     | 
| 
       85 
114 
     | 
    
         
             
                # PART of:. 
         
     | 
| 
       86 
115 
     | 
    
         
             
                # * schema.org CreativeWork "datePublished", year portion
         
     | 
| 
         @@ -93,9 +122,18 @@ module BentoSearch 
     | 
|
| 
       93 
122 
     | 
    
         
             
                attr_accessor :start_page
         
     | 
| 
       94 
123 
     | 
    
         
             
                attr_accessor :end_page
         
     | 
| 
       95 
124 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                 
     | 
| 
      
 125 
     | 
    
         
            +
                # source_title is often used for journal_title (and aliased
         
     | 
| 
      
 126 
     | 
    
         
            +
                # as #journal_title, although that may go away), but can
         
     | 
| 
      
 127 
     | 
    
         
            +
                # also be used for other 'container' titles. Book title for
         
     | 
| 
      
 128 
     | 
    
         
            +
                # a book chapter. Even web site or URL for a web page. 
         
     | 
| 
      
 129 
     | 
    
         
            +
                attr_accessor :source_title
         
     | 
| 
      
 130 
     | 
    
         
            +
                alias_method :journal_title, :source_title
         
     | 
| 
      
 131 
     | 
    
         
            +
                alias_method :'journal_title=',  :'source_title='
         
     | 
| 
      
 132 
     | 
    
         
            +
                
         
     | 
| 
      
 133 
     | 
    
         
            +
                
         
     | 
| 
       97 
134 
     | 
    
         
             
                attr_accessor :issn
         
     | 
| 
       98 
135 
     | 
    
         
             
                attr_accessor :isbn
         
     | 
| 
      
 136 
     | 
    
         
            +
                attr_accessor :oclcnum # OCLC accession number, WorldCat. 
         
     | 
| 
       99 
137 
     | 
    
         | 
| 
       100 
138 
     | 
    
         
             
                attr_accessor :doi
         
     | 
| 
       101 
139 
     | 
    
         | 
| 
         @@ -125,6 +163,8 @@ module BentoSearch 
     | 
|
| 
       125 
163 
     | 
    
         | 
| 
       126 
164 
     | 
    
         
             
                # Returns a ruby OpenURL::ContextObject (NISO Z39.88).     
         
     | 
| 
       127 
165 
     | 
    
         
             
                def to_openurl
         
     | 
| 
      
 166 
     | 
    
         
            +
                  return nil if openurl_disabled
         
     | 
| 
      
 167 
     | 
    
         
            +
                  
         
     | 
| 
       128 
168 
     | 
    
         
             
                  BentoSearch::OpenurlCreator.new(self).to_openurl
         
     | 
| 
       129 
169 
     | 
    
         
             
                end
         
     | 
| 
       130 
170 
     | 
    
         | 
| 
         @@ -180,9 +220,9 @@ module BentoSearch 
     | 
|
| 
       180 
220 
     | 
    
         
             
                    result_elements.push "<span class='year'>#{year}</span>"
         
     | 
| 
       181 
221 
     | 
    
         
             
                  end
         
     | 
| 
       182 
222 
     | 
    
         | 
| 
       183 
     | 
    
         
            -
                  result_elements.push( 
     | 
| 
      
 223 
     | 
    
         
            +
                  result_elements.push(source_title) unless source_title.blank?      
         
     | 
| 
       184 
224 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
                  if  
     | 
| 
      
 225 
     | 
    
         
            +
                  if source_title.blank? && ! publisher.blank?
         
     | 
| 
       186 
226 
     | 
    
         
             
                    result_elements.push html_escape publisher
         
     | 
| 
       187 
227 
     | 
    
         
             
                  end
         
     | 
| 
       188 
228 
     | 
    
         | 
| 
         @@ -11,6 +11,10 @@ module BentoSearch 
     | 
|
| 
       11 
11 
     | 
    
         
             
                attr_accessor :start
         
     | 
| 
       12 
12 
     | 
    
         
             
                # per_page setting, can be used for pagination. 
         
     | 
| 
       13 
13 
     | 
    
         
             
                attr_accessor :per_page
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                # simply copied over from search engine configuration :display key,
         
     | 
| 
      
 16 
     | 
    
         
            +
                # useful for making config available at display time in a DRY way. 
         
     | 
| 
      
 17 
     | 
    
         
            +
                attr_accessor :display_configuration
         
     | 
| 
       14 
18 
     | 
    
         | 
| 
       15 
19 
     | 
    
         
             
                # If error is non-nil, it's an error condition with no real results.
         
     | 
| 
       16 
20 
     | 
    
         
             
                # error should be a hash with these (and possibly other) keys, although
         
     | 
| 
         @@ -50,7 +50,8 @@ module BentoSearch 
     | 
|
| 
       50 
50 
     | 
    
         
             
              # framework:
         
     | 
| 
       51 
51 
     | 
    
         
             
              #
         
     | 
| 
       52 
52 
     | 
    
         
             
              #  [item_decorators]
         
     | 
| 
       53 
     | 
    
         
            -
              #      Array of Modules  
     | 
| 
      
 53 
     | 
    
         
            +
              #      Array of Modules (or strings specifying modules, helpful to keep
         
     | 
| 
      
 54 
     | 
    
         
            +
              #      config serializable) that will be decorated on to each individual search
         
     | 
| 
       54 
55 
     | 
    
         
             
              #      BentoSearch::ResultItem. These can be used to, via configuration, change
         
     | 
| 
       55 
56 
     | 
    
         
             
              #      the links associated with items, change certain item behaviors, or massage
         
     | 
| 
       56 
57 
     | 
    
         
             
              #      item metadata. (Needs more documentation). 
         
     | 
| 
         @@ -125,6 +126,14 @@ module BentoSearch 
     | 
|
| 
       125 
126 
     | 
    
         
             
                # handles configuration loading, mostly. Argument is a
         
     | 
| 
       126 
127 
     | 
    
         
             
                # Confstruct::Configuration or Hash. 
         
     | 
| 
       127 
128 
     | 
    
         
             
                def initialize(aConfiguration = Confstruct::Configuration.new)
         
     | 
| 
      
 129 
     | 
    
         
            +
                  # To work around weird confstruct bug, we need to change
         
     | 
| 
      
 130 
     | 
    
         
            +
                  # a hash to a Confstruct ourselves. 
         
     | 
| 
      
 131 
     | 
    
         
            +
                  # https://github.com/mbklein/confstruct/issues/14
         
     | 
| 
      
 132 
     | 
    
         
            +
                  unless aConfiguration.kind_of? Confstruct::Configuration
         
     | 
| 
      
 133 
     | 
    
         
            +
                    aConfiguration = Confstruct::Configuration.new aConfiguration
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
                    
         
     | 
| 
      
 136 
     | 
    
         
            +
                  
         
     | 
| 
       128 
137 
     | 
    
         
             
                  # init, from copy of default, or new      
         
     | 
| 
       129 
138 
     | 
    
         
             
                  if self.class.default_configuration
         
     | 
| 
       130 
139 
     | 
    
         
             
                    self.configuration = Confstruct::Configuration.new(self.class.default_configuration)
         
     | 
| 
         @@ -136,6 +145,7 @@ module BentoSearch 
     | 
|
| 
       136 
145 
     | 
    
         | 
| 
       137 
146 
     | 
    
         
             
                  # global defaults?
         
     | 
| 
       138 
147 
     | 
    
         
             
                  self.configuration[:item_decorators] ||= []
         
     | 
| 
      
 148 
     | 
    
         
            +
                  self.configuration[:for_display] ||= {}
         
     | 
| 
       139 
149 
     | 
    
         | 
| 
       140 
150 
     | 
    
         
             
                  # check for required keys -- have to be present, and not nil
         
     | 
| 
       141 
151 
     | 
    
         
             
                  if self.class.required_configuration
         
     | 
| 
         @@ -208,6 +218,8 @@ module BentoSearch 
     | 
|
| 
       208 
218 
     | 
    
         
             
                  results.engine_id     = configuration.id
         
     | 
| 
       209 
219 
     | 
    
         | 
| 
       210 
220 
     | 
    
         
             
                  results.timing = (Time.now - start_t)
         
     | 
| 
      
 221 
     | 
    
         
            +
                        
         
     | 
| 
      
 222 
     | 
    
         
            +
                  results.display_configuration = configuration.for_display
         
     | 
| 
       211 
223 
     | 
    
         | 
| 
       212 
224 
     | 
    
         
             
                  return results
         
     | 
| 
       213 
225 
     | 
    
         
             
                rescue *auto_rescue_exceptions => e
         
     | 
| 
         @@ -303,15 +315,26 @@ module BentoSearch 
     | 
|
| 
       303 
315 
     | 
    
         
             
                alias_method :parse_search_arguments, :normalized_search_arguments
         
     | 
| 
       304 
316 
     | 
    
         | 
| 
       305 
317 
     | 
    
         | 
| 
       306 
     | 
    
         
            -
                
         
     | 
| 
      
 318 
     | 
    
         
            +
                # Used mainly/only by the AJAX results loading. 
         
     | 
| 
      
 319 
     | 
    
         
            +
                # an array WHITELIST of attributes that can be sent as non-verified
         
     | 
| 
      
 320 
     | 
    
         
            +
                # request params and used to execute a search. For instance, 'auth' is
         
     | 
| 
      
 321 
     | 
    
         
            +
                # NOT on there, you can't trust a web request as to 'auth' status. 
         
     | 
| 
      
 322 
     | 
    
         
            +
                # individual engines may over-ride, call super, and add additional
         
     | 
| 
      
 323 
     | 
    
         
            +
                # engine-specific attributes. 
         
     | 
| 
      
 324 
     | 
    
         
            +
                def public_settable_search_args
         
     | 
| 
      
 325 
     | 
    
         
            +
                  [:query, :search_field, :semantic_search_field, :sort, :page, :start, :per_page]
         
     | 
| 
      
 326 
     | 
    
         
            +
                end
         
     | 
| 
       307 
327 
     | 
    
         | 
| 
       308 
328 
     | 
    
         | 
| 
       309 
329 
     | 
    
         
             
                protected
         
     | 
| 
       310 
330 
     | 
    
         | 
| 
       311 
331 
     | 
    
         
             
                # Extend each result with each specified decorator module
         
     | 
| 
      
 332 
     | 
    
         
            +
                # configuration.item_decorators is an array of either Module constants,
         
     | 
| 
      
 333 
     | 
    
         
            +
                # or strings specifying module constants. 
         
     | 
| 
       312 
334 
     | 
    
         
             
                def decorate(results)      
         
     | 
| 
       313 
335 
     | 
    
         
             
                  results.each do |result|
         
     | 
| 
       314 
336 
     | 
    
         
             
                    configuration.item_decorators.each do |decorator|
         
     | 
| 
      
 337 
     | 
    
         
            +
                      decorator = (decorator.kind_of? Module) ? decorator : BentoSearch::Util.constantize(decorator)          
         
     | 
| 
       315 
338 
     | 
    
         
             
                      result.extend decorator
         
     | 
| 
       316 
339 
     | 
    
         
             
                    end
         
     | 
| 
       317 
340 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -337,27 +360,6 @@ module BentoSearch 
     | 
|
| 
       337 
360 
     | 
    
         | 
| 
       338 
361 
     | 
    
         
             
                module ClassMethods
         
     | 
| 
       339 
362 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
                  # If support fielded search, over-ride to specify fields
         
     | 
| 
       341 
     | 
    
         
            -
                  # supported. Returns a hash, key is engine-specific internal
         
     | 
| 
       342 
     | 
    
         
            -
                  # search field, value is nil or a hash of metadata about
         
     | 
| 
       343 
     | 
    
         
            -
                  # the search field, including semantic mapping. 
         
     | 
| 
       344 
     | 
    
         
            -
                  #
         
     | 
| 
       345 
     | 
    
         
            -
                  # def search_field_definitions
         
     | 
| 
       346 
     | 
    
         
            -
                  #  { "intitle" => {:semantic => :title}}
         
     | 
| 
       347 
     | 
    
         
            -
                  # end
         
     | 
| 
       348 
     | 
    
         
            -
                  def search_field_definitions
         
     | 
| 
       349 
     | 
    
         
            -
                    {}
         
     | 
| 
       350 
     | 
    
         
            -
                  end
         
     | 
| 
       351 
     | 
    
         
            -
                  
         
     | 
| 
       352 
     | 
    
         
            -
                        
         
     | 
| 
       353 
     | 
    
         
            -
                  # Returns list of string internal search_field's that can
         
     | 
| 
       354 
     | 
    
         
            -
                  # be supplied to search(:search_field => x)
         
     | 
| 
       355 
     | 
    
         
            -
                  def search_keys        
         
     | 
| 
       356 
     | 
    
         
            -
                    return search_field_definitions.keys
         
     | 
| 
       357 
     | 
    
         
            -
                  end
         
     | 
| 
       358 
     | 
    
         
            -
                  
         
     | 
| 
       359 
     | 
    
         
            -
                  
         
     | 
| 
       360 
     | 
    
         
            -
                  
         
     | 
| 
       361 
363 
     | 
    
         
             
                  # Over-ride returning a hash or Confstruct with 
         
     | 
| 
       362 
364 
     | 
    
         
             
                  # any configuration values you want by default. 
         
     | 
| 
       363 
365 
     | 
    
         
             
                  # actual user-specified config values will be deep-merged
         
     |