bento_search 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/README.md +131 -74
  2. data/app/assets/javascripts/bento_search/ajax_load.js +12 -4
  3. data/app/assets/stylesheets/bento_search/suggested_styles.css +4 -4
  4. data/app/helpers/bento_search_helper.rb +114 -27
  5. data/app/item_decorators/bento_search/decorator_base.rb +53 -0
  6. data/app/item_decorators/bento_search/ebscohost/conditional_openurl_main_link.rb +36 -0
  7. data/app/item_decorators/bento_search/no_links.rb +3 -2
  8. data/app/item_decorators/bento_search/only_premade_openurl.rb +4 -0
  9. data/app/item_decorators/bento_search/openurl_add_other_link.rb +4 -0
  10. data/app/item_decorators/bento_search/openurl_main_link.rb +4 -0
  11. data/app/item_decorators/bento_search/standard_decorator.rb +122 -0
  12. data/app/models/bento_search/multi_searcher.rb +13 -6
  13. data/app/models/bento_search/openurl_creator.rb +25 -5
  14. data/app/models/bento_search/result_item.rb +25 -83
  15. data/app/models/bento_search/results/pagination.rb +8 -2
  16. data/app/models/bento_search/search_engine.rb +29 -23
  17. data/app/search_engines/bento_search/ebsco_host_engine.rb +161 -25
  18. data/app/search_engines/bento_search/eds_engine.rb +1 -44
  19. data/app/search_engines/bento_search/google_books_engine.rb +61 -14
  20. data/app/search_engines/bento_search/google_site_search_engine.rb +3 -1
  21. data/app/search_engines/bento_search/mock_engine.rb +4 -0
  22. data/app/search_engines/bento_search/primo_engine.rb +2 -3
  23. data/app/search_engines/bento_search/scopus_engine.rb +1 -0
  24. data/app/search_engines/bento_search/summon_engine.rb +5 -1
  25. data/app/search_engines/bento_search/worldcat_sru_dc_engine.rb +36 -8
  26. data/app/views/bento_search/_item_title.html.erb +29 -0
  27. data/app/views/bento_search/_no_results.html.erb +3 -0
  28. data/app/views/bento_search/_search_error.html.erb +19 -15
  29. data/app/views/bento_search/_std_item.html.erb +55 -30
  30. data/app/views/bento_search/search/search.html.erb +7 -0
  31. data/config/locales/en.yml +22 -0
  32. data/lib/bento_search/util.rb +63 -1
  33. data/lib/bento_search/version.rb +1 -1
  34. data/test/decorator/decorator_base_test.rb +72 -0
  35. data/test/decorator/standard_decorator_test.rb +55 -0
  36. data/test/dummy/db/development.sqlite3 +0 -0
  37. data/test/dummy/log/development.log +12 -0
  38. data/test/dummy/log/test.log +119757 -0
  39. data/test/functional/bento_search/search_controller_test.rb +28 -0
  40. data/test/helper/bento_search_helper_test.rb +71 -0
  41. data/test/helper/bento_truncate_helper_test.rb +71 -0
  42. data/test/unit/ebsco_host_engine_test.rb +110 -3
  43. data/test/unit/google_books_engine_test.rb +22 -14
  44. data/test/unit/google_site_search_test.rb +11 -4
  45. data/test/unit/item_decorators_test.rb +6 -65
  46. data/test/unit/openurl_creator_test.rb +87 -8
  47. data/test/unit/result_item_test.rb +1 -11
  48. data/test/unit/search_engine_base_test.rb +25 -2
  49. data/test/unit/search_engine_test.rb +16 -0
  50. data/test/unit/summon_engine_test.rb +3 -0
  51. data/test/vcr_cassettes/ebscohost/another_dissertation.yml +148 -0
  52. data/test/vcr_cassettes/ebscohost/dissertation_example.yml +218 -0
  53. data/test/vcr_cassettes/ebscohost/fulltext_info.yml +1306 -0
  54. data/test/vcr_cassettes/ebscohost/live_book_example.yml +130 -0
  55. data/test/vcr_cassettes/ebscohost/live_dissertation.yml +148 -0
  56. data/test/vcr_cassettes/ebscohost/live_pathological_book_item_example.yml +215 -0
  57. data/test/vcr_cassettes/google_site/gets_format_string.yml +232 -0
  58. data/test/vcr_cassettes/max_out_pagination.yml +155 -0
  59. data/test/vcr_cassettes/worldcat_sru_dc/max_out_pagination.yml +167 -0
  60. data/test/view/std_item_test.rb +25 -8
  61. metadata +45 -12
  62. data/test/unit/result_item_display_test.rb +0 -39
  63. data/test/unit/worldcat_sru_dc_engine_test.rb +0 -120
@@ -8,6 +8,12 @@ module BentoSearch
8
8
  config.allow_routable_results = true
9
9
  end
10
10
 
11
+ BentoSearch.register_engine("failed_response") do |config|
12
+ config.engine = "MockEngine"
13
+ config.allow_routable_results = true
14
+ config.error = {:message => "faked error"}
15
+ end
16
+
11
17
  BentoSearch.register_engine("not_routable") do |config|
12
18
  config.engine = "MockEngine"
13
19
  # no allow_routable_results
@@ -37,8 +43,30 @@ module BentoSearch
37
43
  assert_not_nil assigns(:results)
38
44
 
39
45
  assert_template "bento_search/search"
46
+
47
+ # meta tag with count
48
+ assert_tag(:tag => "meta", :attributes => {"itemprop" => "total_items", "content" => /^\d+$/ })
49
+ end
50
+
51
+ test "failed search" do
52
+ get :search, {:engine_id => "failed_response", :query => "my search"}
53
+
54
+ # should this really be a success? Yes, I think so, we don't
55
+ # want to stop ajax from getting it, it'll just have an error
56
+ # message in the HTML. Should it maybe have an html5 meta microdata
57
+ # warning?
58
+ assert_response :success
59
+
60
+ assert_template "bento_search/search"
61
+ assert_template "bento_search/_search_error"
62
+
63
+ assert_no_tag(:tag => "meta", :attributes => {"itemprop" => "total_items"})
40
64
  end
41
65
 
66
+
67
+
68
+
69
+
42
70
  test "custom layout config" do
43
71
  get :search, {:engine_id => "with_layout_config", :query => "my search"}
44
72
 
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  # this seems to work? Rails view testing is a mess.
@@ -163,6 +165,75 @@ class BentoSearchHelperTest < ActionView::TestCase
163
165
 
164
166
  assert_equal "No Key Test", key
165
167
  end
168
+
169
+ def test_field_hash_for
170
+ # generic
171
+ hash = bento_field_hash_for(nil)
172
+
173
+ assert_equal I18n.t("bento_search.search_fields").invert, hash
174
+
175
+ # specific engine
176
+ engine = MockEngine.new(:search_field_definitions => {
177
+ :mytitle => {:semantic => :title},
178
+ :myauthor => {:semantic => :author},
179
+ :myissn => {:semantic => :issn},
180
+ :mycustom => {}
181
+ })
182
+ hash = bento_field_hash_for(engine)
183
+ expected = { I18n.t("bento_search.search_fields.title") => 'title',
184
+ I18n.t("bento_search.search_fields.author") => 'author',
185
+ I18n.t("bento_search.search_fields.issn") => 'issn',
186
+ }
187
+ assert_equal expected, hash
188
+
189
+ # only
190
+ hash = bento_field_hash_for(engine, :only => :author)
191
+ assert_equal( {"Author" => "author"}, hash )
192
+ hash = bento_field_hash_for(engine, :only => ["author", "title"])
193
+ assert_equal( {"Title" => "title", "Author" => "author"}, hash )
194
+
195
+ # except
196
+
197
+
198
+ end
199
+
200
+ def test_bento_decorate
201
+ item = BentoSearch::ResultItem.new(:title => "foo")
202
+
203
+ decorated = bento_decorate(item)
204
+
205
+ assert_kind_of BentoSearch::StandardDecorator, decorated
206
+
207
+ assert_equal "foo", decorated.title
208
+
209
+ assert decorated.send("_h").respond_to?(:url_for), "has ActionView helpers available internally"
210
+ end
211
+
212
+ def test_bento_decorate_with_yield
213
+ item = BentoSearch::ResultItem.new(:title => "foo")
214
+
215
+ got_here = false
216
+
217
+ bento_decorate(item) do |decorated|
218
+ got_here = true
219
+ assert_equal "foo", decorated.title
220
+ end
221
+
222
+ assert got_here, "Yielded block is called"
223
+
224
+ end
225
+
226
+ class SomeDecorator < BentoSearch::StandardDecorator ; end
227
+
228
+ def test_bento_decorate_with_custom_decorator
229
+ item = BentoSearch::ResultItem.new(:title => "foo", :decorator => "BentoSearchHelperTest::SomeDecorator")
230
+
231
+ decorated = bento_decorate(item)
232
+
233
+ assert_kind_of BentoSearchHelperTest::SomeDecorator, decorated
234
+ end
235
+
236
+
166
237
 
167
238
 
168
239
  end
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ # this seems to work? Rails view testing is a mess.
6
+ require 'sprockets/helpers/rails_helper'
7
+
8
+ class BentoSearchHelperTest < ActionView::TestCase
9
+ include BentoSearchHelper
10
+
11
+
12
+ def test_truncate_basic
13
+ # Basic test
14
+ output = bento_truncate("12345678901234567890", :length => 10)
15
+ assert_equal "123456789…", output
16
+ end
17
+
18
+ def test_truncate_tags
19
+ # With tags
20
+ html_input = "123456<p><b>78901234567</b>890</p>".html_safe
21
+ html_output = bento_truncate(html_input, :length => 10)
22
+ assert html_output.html_safe?, "truncated html_safe? is still html_safe?"
23
+ assert_equal "123456<p><b>789…</b></p>", html_output
24
+ end
25
+
26
+ def test_truncate_tag_boundary
27
+ # With break on tag boundary. Yes, there's an error not accounting
28
+ # for length of omission marker in this particular edge case,
29
+ # hard to fix, good enough for now.
30
+ html_input = "<p>1234567890<b>123456</b>7890</p>".html_safe
31
+ html_output = bento_truncate(html_input, :length => 10)
32
+ assert_equal "<p>1234567890…</p>", html_output
33
+ end
34
+
35
+ def test_truncate_boundary_edge_case
36
+ html_input = "12345<p>6789<b>0123456</b>7890</p>".html_safe
37
+ html_output = bento_truncate(html_input, :length => 10)
38
+ # yeah, weird elipses in <b> of their own, so it goes.
39
+ assert_equal "12345<p>6789<b>…</b></p>", html_output
40
+ end
41
+
42
+ def test_truncate_another_edge_case
43
+ html_input = "12345<p>67890<b>123456</b>7890</p>".html_safe
44
+ html_output = bento_truncate(html_input, :length => 10)
45
+ assert_equal "12345<p>67890…</p>", html_output
46
+ end
47
+
48
+ def test_truncate_html_with_separator
49
+ html_input = "12345<p>67 901234<b></p>".html_safe
50
+ html_output = bento_truncate(html_input, :length => 10, :separator => ' ')
51
+ assert_equal "12345<p>67…</p>", html_output
52
+ end
53
+
54
+ def test_truncate_html_with_separator_unavailable
55
+ html_input = "12345<p>678901234<b></p>".html_safe
56
+ html_output = bento_truncate(html_input, :length => 10, :separator => ' ')
57
+ assert_equal "12345<p>6789…</p>", html_output
58
+ end
59
+
60
+ def test_truncate_html_with_boundary_separator
61
+ # known edge case we dont' handle, sorry. If this test
62
+ # fails, that could be a good thing if you've fixed the edge case!
63
+ html_input = "12345<p>6 8<b>90123456</b>7890</p>".html_safe
64
+ html_output = bento_truncate(html_input, :length => 10, :separator => ' ')
65
+ assert_equal "12345<p>6 8<b>9…</b></p>", html_output
66
+ end
67
+
68
+
69
+
70
+
71
+ end
@@ -58,7 +58,7 @@ class EbscoHostEngineTest < ActiveSupport::TestCase
58
58
  assert_equal ["date"], query_params["sort"]
59
59
  end
60
60
 
61
- def fielded_construction
61
+ def test_fielded_construction
62
62
  url = @engine.query_url(:query => "cancer", :search_field => "SU")
63
63
 
64
64
  query_params = CGI.parse( URI.parse(url).query )
@@ -66,10 +66,33 @@ class EbscoHostEngineTest < ActiveSupport::TestCase
66
66
  assert_equal ["(SU cancer)"], query_params["query"]
67
67
  end
68
68
 
69
+ def test_peer_review_limit_construction
70
+ url = @engine.query_url(:query => "cancer", :search_field => "SU", :peer_reviewed_only => true)
71
+
72
+ query_params = CGI.parse( URI.parse(url).query )
73
+
74
+ assert_equal ["(SU cancer) AND (RV Y)"], query_params["query"]
75
+ end
76
+
77
+ def test_date_limit_construction
78
+ url = @engine.query_url(:query => "cancer", :pubyear_start => "1980", :pubyear_end => "1989")
79
+ query_params = CGI.parse( URI.parse(url).query )
80
+
81
+ assert_equal ["cancer AND (DT 1980-1989)"], query_params["query"]
82
+
83
+ # just one
84
+ url = @engine.query_url(:query => "cancer", :pubyear_start => "1980")
85
+ query_params = CGI.parse( URI.parse(url).query )
86
+
87
+ assert_equal ["cancer AND (DT 1980-)"], query_params["query"]
88
+
89
+ end
90
+
91
+
69
92
  def test_prepare_query
70
- query = @engine.ebsco_query_prepare('one :. ; two "three four" AND NOT OR five')
93
+ query = @engine.ebsco_query_prepare('one :. ; two "three four" and NOT OR five')
71
94
 
72
- assert_equal 'one AND two AND "three four" AND five', query
95
+ assert_equal 'one AND two AND "three four" AND "and" AND "NOT" AND "OR" AND five', query
73
96
  end
74
97
 
75
98
  def test_removes_paren_literals
@@ -145,5 +168,89 @@ class EbscoHostEngineTest < ActiveSupport::TestCase
145
168
  assert_present results.error[:error_info]
146
169
 
147
170
  end
171
+
172
+ test_with_cassette("fulltext info", :ebscohost) do
173
+ # We count on SOME records from first 10 for this query having fulltext,
174
+ # if you need to re-record VCR cassette and this query doesn't work
175
+ # for that anymore, then pick a different query.
176
+ results = @engine.search("cancer")
177
+
178
+ results_with_fulltext = results.find_all {|r| r.custom_data["fulltext_formats"] }
179
+
180
+ assert_present results_with_fulltext
181
+
182
+ results_with_fulltext.each do |record|
183
+ array = record.custom_data["fulltext_formats"]
184
+ # it's an array
185
+ assert_kind_of Array, array
186
+ # who's only legal values are P, T, and C, the EBSCO vocab for formats.
187
+ assert_equal array.length, array.find_all {|v| %w{P C T}.include?(v)}.length
188
+
189
+ assert record.link_is_fulltext?, "#link_is_fulltext set"
190
+ end
191
+
192
+ end
193
+
194
+ test_with_cassette("live book example", :ebscohost) do
195
+ # We keep adjusting the EBSCOHost heuristics for guessing format,
196
+ # and causing regressions, this test guards against them.
148
197
 
198
+ # This particular example from RILM is a book, but
199
+ # is getting listed as a book chapter, sort of.
200
+
201
+ engine = BentoSearch::EbscoHostEngine.new( @config.merge(:databases => ["rih"]) )
202
+
203
+ results = engine.search('"Funk: The music, the people, and the rhythm of the one"', :per_page => 1)
204
+
205
+ result = results.first
206
+
207
+ assert_equal "Book", result.format
208
+ assert_equal "St. Martin's Press", result.publisher
209
+ assert_equal "1996", result.year
210
+
211
+ assert_blank result.source_title
212
+ end
213
+
214
+ test_with_cassette("live pathological book_item example", :ebscohost) do
215
+ # this guy from RILM has really crappy metadata on EBSCO,
216
+ # but we still want to detect it as a book_item, not a book.
217
+
218
+ a = 'Heidegger and the management of the Haymarket Opera, 1713-1717'
219
+
220
+ engine = BentoSearch::EbscoHostEngine.new( @config.merge(:databases => ["rih"]) )
221
+ results = engine.search('"Heidegger and the management of the Haymarket Opera, 1713-1717"')
222
+ result = results.first
223
+
224
+ assert_equal :book_item, result.format
225
+
226
+
227
+ # for reasons I can't figure out, weird encoding in the hyphen makes us
228
+ # test start_with instead
229
+ assert result.title.starts_with?("Heidegger and the management of the Haymarket Opera, 1713")
230
+ assert result.source_title.starts_with?("Opera remade (1700")
231
+ end
232
+
233
+ test_with_cassette("dissertation example", :ebscohost) do
234
+ # yeah, all the weird ones are from RILM
235
+ engine = BentoSearch::EbscoHostEngine.new( @config.merge(:databases => ["rih"]) )
236
+
237
+ results = engine.search('"Research into free jazz in France, 1960-1975"')
238
+ result = results.first
239
+
240
+ assert_equal "Research into free jazz in France, 1960-1975", result.title
241
+ assert_equal :dissertation, result.format
242
+ end
243
+
244
+ test_with_cassette("another dissertation", :ebscohost) do
245
+ # yeah, all the weird edge cases that make good tests are from RILM, it's
246
+ # got weird data.
247
+
248
+ engine = BentoSearch::EbscoHostEngine.new( @config.merge(:databases => ["rih"]) )
249
+ results = engine.search('"Machine gun voices: Bandits, favelas, and utopia in Brazilian funk"')
250
+ result = results.first
251
+
252
+ assert_equal :dissertation, result.format
253
+ assert_equal "Machine gun voices: Bandits, favelas, and utopia in Brazilian funk", result.title
254
+ end
255
+
149
256
  end
@@ -27,18 +27,26 @@ class GoogleBooksEngineTest < ActiveSupport::TestCase
27
27
 
28
28
  assert_kind_of BentoSearch::ResultItem, first
29
29
 
30
- assert_not_empty first.title
31
- assert_not_empty first.publisher
32
- assert_not_empty first.link
33
- assert_not_empty first.format
34
- assert_not_nil first.year
35
- assert_not_empty first.abstract
36
- assert first.abstract.html_safe?
37
-
38
- assert_present first.language_code
39
-
40
- assert_not_empty first.authors
41
- assert_not_empty first.authors.first.display
30
+ assert_not_empty first.title
31
+ assert_not_empty first.publisher
32
+ assert_not_empty first.link
33
+ assert_not_empty first.format
34
+ assert_not_nil first.year
35
+ assert_not_empty first.abstract
36
+ assert first.abstract.html_safe?
37
+
38
+ assert_present first.language_code
39
+
40
+ assert_not_empty first.authors
41
+ assert_not_empty first.authors.first.display
42
+
43
+ # assume at least one thing in the result set has an ISBN to test
44
+ # our ISBN-setting code.
45
+ assert_present results.find {|r| r.isbn.present? }
46
+
47
+ assert_present first.custom_data[:viewability]
48
+
49
+ assert_not_nil first.link_is_fulltext?
42
50
  end
43
51
 
44
52
  test_with_cassette("pagination", :gbs) do
@@ -79,11 +87,11 @@ class GoogleBooksEngineTest < ActiveSupport::TestCase
79
87
  def test_sort_construction
80
88
  url = @engine.send(:args_to_search_url, :query => "cancer", :sort => "date_desc")
81
89
 
82
- assert_match '&sort=newest', url
90
+ assert_match '&orderBy=newest', url
83
91
 
84
92
  url = @engine.send(:args_to_search_url, :query => "cancer", :sort => "relevance")
85
93
 
86
- assert_not_match "&sort", url
94
+ assert_not_match "&orderBy", url
87
95
  end
88
96
 
89
97
  def test_fielded_search
@@ -81,7 +81,7 @@ class GoogleSiteSearchTest < ActiveSupport::TestCase
81
81
  assert_present first.journal_title # used as source_title for display url
82
82
 
83
83
  # no openurls for google, we decided
84
- assert_nil first.to_openurl
84
+ assert_nil BentoSearch::StandardDecorator.new(first, nil).to_openurl
85
85
  end
86
86
 
87
87
  test_with_cassette("with highlighting", :google_site) do
@@ -93,9 +93,7 @@ class GoogleSiteSearchTest < ActiveSupport::TestCase
93
93
 
94
94
  assert first.title.html_safe?
95
95
  assert first.abstract.html_safe?
96
- assert first.journal_title.html_safe?
97
-
98
- assert first.published_in.html_safe?
96
+ assert first.journal_title.html_safe?
99
97
  end
100
98
 
101
99
  test_with_cassette("without highlighting", :google_site) do
@@ -119,4 +117,13 @@ class GoogleSiteSearchTest < ActiveSupport::TestCase
119
117
  assert results.empty?
120
118
  end
121
119
 
120
+ test_with_cassette("gets format string", :google_site) do
121
+ results = @engine.search("Adobe PDF")
122
+
123
+ # assume at least one result had a PDF format, which it does
124
+ # in our current VCR capture. For a new one, find a search where
125
+ # it does.
126
+ assert_present(results.find_all {|i| i.format_str =~ /PDF/ }, "At least one result has PDF in format_str")
127
+ end
128
+
122
129
  end
@@ -3,76 +3,17 @@ require 'test_helper'
3
3
  class ItemDecoratorsTest < ActiveSupport::TestCase
4
4
  MockEngine = BentoSearch::MockEngine
5
5
 
6
- # simple decorator that replaces main link
7
- module Decorator
8
- def link
9
- "http://newlink.com"
10
- end
11
-
12
- def other_links
13
- super + [ BentoSearch::Link.new(:label => "One", :url => "http://one.com") ]
14
- end
15
- end
16
-
17
- setup do
18
- @engine = MockEngine.new(:item_decorators => [Decorator])
19
- end
20
-
21
- test "decorators" do
22
- results = @engine.search(:query => "Query")
23
-
24
- assert_present results
25
-
26
- results.each do |result|
27
- assert_kind_of Decorator, result
28
-
29
- assert_equal "http://newlink.com", result.link
30
-
31
- assert_present result.other_links
32
-
33
- assert_equal "One", result.other_links.first.label
34
- assert_equal "http://one.com", result.other_links.first.url
35
- end
36
- end
37
-
38
- test "decorator as string" do
39
- @engine = MockEngine.new(:item_decorators => ["::ItemDecoratorsTest::Decorator"])
40
-
41
- results = @engine.search(:query => "Query")
42
-
43
- results.each do |result|
44
- assert_kind_of Decorator, result
45
- end
46
6
 
47
- end
7
+ ################
8
+ # ABove here, old style decorators on their way out. Below, new:
9
+ ###########
48
10
 
49
- # Is it a good idea to have a decorator that mutates on 'extend'?
50
- # I'm not sure, I think probably not, but it is possible.
51
- # Here we'll use it to move an original link to other links
52
- module MutatingDecorator
53
- def self.extended(item)
54
- orig_link = item.link
55
-
56
- item.link = nil
57
-
58
- item.other_links << BentoSearch::Link.new(:label => "Some Other", :url => orig_link)
59
- end
60
- end
61
-
62
- test "mutating decorator" do
63
- @engine = MockEngine.new(:item_decorators => [MutatingDecorator], :link => "http://example2.org")
11
+ test "decorator specified in configuration" do
12
+ @engine = MockEngine.new(:for_display => {:decorator => "TestDecorator"})
64
13
  results = @engine.search("query")
65
14
 
66
- assert_present results
67
-
68
- results.each do |result|
69
- assert_blank result.link
70
- assert_equal "http://example2.org", result.other_links.first.url
71
- assert_equal "Some Other", result.other_links.first.label
72
- end
73
-
15
+ assert_equal "TestDecorator", results.first.decorator
74
16
  end
75
-
76
17
 
77
18
 
78
19