bento_search 0.6.0 → 0.7.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 +131 -74
- data/app/assets/javascripts/bento_search/ajax_load.js +12 -4
- data/app/assets/stylesheets/bento_search/suggested_styles.css +4 -4
- data/app/helpers/bento_search_helper.rb +114 -27
- data/app/item_decorators/bento_search/decorator_base.rb +53 -0
- data/app/item_decorators/bento_search/ebscohost/conditional_openurl_main_link.rb +36 -0
- data/app/item_decorators/bento_search/no_links.rb +3 -2
- data/app/item_decorators/bento_search/only_premade_openurl.rb +4 -0
- data/app/item_decorators/bento_search/openurl_add_other_link.rb +4 -0
- data/app/item_decorators/bento_search/openurl_main_link.rb +4 -0
- data/app/item_decorators/bento_search/standard_decorator.rb +122 -0
- data/app/models/bento_search/multi_searcher.rb +13 -6
- data/app/models/bento_search/openurl_creator.rb +25 -5
- data/app/models/bento_search/result_item.rb +25 -83
- data/app/models/bento_search/results/pagination.rb +8 -2
- data/app/models/bento_search/search_engine.rb +29 -23
- data/app/search_engines/bento_search/ebsco_host_engine.rb +161 -25
- data/app/search_engines/bento_search/eds_engine.rb +1 -44
- data/app/search_engines/bento_search/google_books_engine.rb +61 -14
- data/app/search_engines/bento_search/google_site_search_engine.rb +3 -1
- data/app/search_engines/bento_search/mock_engine.rb +4 -0
- data/app/search_engines/bento_search/primo_engine.rb +2 -3
- data/app/search_engines/bento_search/scopus_engine.rb +1 -0
- data/app/search_engines/bento_search/summon_engine.rb +5 -1
- data/app/search_engines/bento_search/worldcat_sru_dc_engine.rb +36 -8
- data/app/views/bento_search/_item_title.html.erb +29 -0
- data/app/views/bento_search/_no_results.html.erb +3 -0
- data/app/views/bento_search/_search_error.html.erb +19 -15
- data/app/views/bento_search/_std_item.html.erb +55 -30
- data/app/views/bento_search/search/search.html.erb +7 -0
- data/config/locales/en.yml +22 -0
- data/lib/bento_search/util.rb +63 -1
- data/lib/bento_search/version.rb +1 -1
- data/test/decorator/decorator_base_test.rb +72 -0
- data/test/decorator/standard_decorator_test.rb +55 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +12 -0
- data/test/dummy/log/test.log +119757 -0
- data/test/functional/bento_search/search_controller_test.rb +28 -0
- data/test/helper/bento_search_helper_test.rb +71 -0
- data/test/helper/bento_truncate_helper_test.rb +71 -0
- data/test/unit/ebsco_host_engine_test.rb +110 -3
- data/test/unit/google_books_engine_test.rb +22 -14
- data/test/unit/google_site_search_test.rb +11 -4
- data/test/unit/item_decorators_test.rb +6 -65
- data/test/unit/openurl_creator_test.rb +87 -8
- data/test/unit/result_item_test.rb +1 -11
- data/test/unit/search_engine_base_test.rb +25 -2
- data/test/unit/search_engine_test.rb +16 -0
- data/test/unit/summon_engine_test.rb +3 -0
- data/test/vcr_cassettes/ebscohost/another_dissertation.yml +148 -0
- data/test/vcr_cassettes/ebscohost/dissertation_example.yml +218 -0
- data/test/vcr_cassettes/ebscohost/fulltext_info.yml +1306 -0
- data/test/vcr_cassettes/ebscohost/live_book_example.yml +130 -0
- data/test/vcr_cassettes/ebscohost/live_dissertation.yml +148 -0
- data/test/vcr_cassettes/ebscohost/live_pathological_book_item_example.yml +215 -0
- data/test/vcr_cassettes/google_site/gets_format_string.yml +232 -0
- data/test/vcr_cassettes/max_out_pagination.yml +155 -0
- data/test/vcr_cassettes/worldcat_sru_dc/max_out_pagination.yml +167 -0
- data/test/view/std_item_test.rb +25 -8
- metadata +45 -12
- data/test/unit/result_item_display_test.rb +0 -39
- data/test/unit/worldcat_sru_dc_engine_test.rb +0 -120
data/README.md
CHANGED
@@ -7,16 +7,46 @@ be some breaking api changes before 1.0, but probably not too many, it's
|
|
7
7
|
looking pretty good).
|
8
8
|
|
9
9
|
bento_search provides an abstraction/normalization layer for querying and
|
10
|
-
displaying results
|
10
|
+
displaying results from external search engines, in Ruby on Rails. Requires
|
11
11
|
Rails3 and tested only under ruby 1.9.3.
|
12
12
|
|
13
|
-
|
14
|
-
cases too. Initially, engine adapters are planned to be provided for:
|
15
|
-
Google Books, Scopus, SerialSolutions Summon, Ex Libris Primo,
|
16
|
-
EBSCO Discovery Service, EBSCO traditional 'EIT' api, Google Site Search. Most
|
17
|
-
of these search engines require a vendor license to use.
|
13
|
+
### Goals: To help you
|
18
14
|
|
19
|
-
*
|
15
|
+
* **Get up and running as quickly as possible** with searching and displaying
|
16
|
+
results from a third-party service. Solutions to idiosyncracies and
|
17
|
+
undocumented workarounds are encoded in a shared codebase, which abstracts
|
18
|
+
everything to a good, simple code API giving you building blocks to focus
|
19
|
+
on your needs, not the search service's problems.
|
20
|
+
* Let you switch out one search service for another in an already built
|
21
|
+
application with as little code rewriting as possible. **Avoid vendor lock-in**.
|
22
|
+
* Give you the harness to **write adapters for new search services**, without
|
23
|
+
having to rewrite common general functionality, just focus on the interface
|
24
|
+
with the new API you want to support.
|
25
|
+
|
26
|
+
bento_search is focused on use cases for academic libraries, which is mainly
|
27
|
+
evidenced by the search engine adapters currently included, and by the
|
28
|
+
generalized domain models including fields that matter in our domain (issn,
|
29
|
+
vol/issue/page, etc), and some targetted functionality (OpenURL generation).
|
30
|
+
But it ought to be useful for more general basic use
|
31
|
+
cases too (we include a google site search adapter for instance).
|
32
|
+
|
33
|
+
Adapters currently included in bento_search
|
34
|
+
|
35
|
+
* Google Books (requires free api key)
|
36
|
+
* Scopus (requires license)
|
37
|
+
* Serial Solution Summon (requires license)
|
38
|
+
* Ex Libris Primo (requires license)
|
39
|
+
* EBSCO Discovery Service (requires license)
|
40
|
+
* EBSCOHost 'traditional' API (requires license)
|
41
|
+
* WorldCat Search (requires OCLC membership to get api key)
|
42
|
+
* Google Site Search (requires sign-up for more than 100 searches/day)
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
### Scope of functionality
|
48
|
+
|
49
|
+
bento_search could be considered building blocks for a type of 'federated
|
20
50
|
search' functionality, but it does not and will never support merging results
|
21
51
|
from multiple engines into one result set. It is meant to support displaying the
|
22
52
|
first few results from multiple engines on one page, "bento box" style (as
|
@@ -24,18 +54,18 @@ named by Tito Sierra@NCSU), as well as more expanded single-search-on-a-page
|
|
24
54
|
uses.
|
25
55
|
|
26
56
|
* bento_search provides abstract functionality for pagination, sorting,
|
27
|
-
and single-field-specified queries. Faceting, limiting, and 'advanced'
|
28
|
-
multi-field searches are not yet supported, but
|
29
|
-
|
57
|
+
and single-field-specified queries. Faceting, generalized limiting, and 'advanced'
|
58
|
+
multi-field searches are not yet supported, but possibly will be built
|
59
|
+
out in the future.
|
60
|
+
|
61
|
+
Not all search engine adapters support all features. Some engines offer
|
62
|
+
engine-specific features, such as limiting. Search engine adapters can
|
30
63
|
declare search fields and sort options with 'semantics', so you can for
|
31
64
|
instance search or sort by 'title' across search engines without regard
|
32
65
|
to internal engine-specific field names.
|
33
66
|
|
34
67
|
bento_search is designed to allow code to be written agnostic of the search
|
35
|
-
provider, so you can switch out the search provider
|
36
|
-
code in your app that needs to be rewritten. As well as letting you get
|
37
|
-
started quick without reinventing the wheel and figuring out poorly
|
38
|
-
documented vendor API's yourself.
|
68
|
+
provider, so you can switch out the search provider.
|
39
69
|
|
40
70
|
See code-level api documentation for more details, especially at
|
41
71
|
BentoSearch::SearchEngine. http://rubydoc.info/gems/bento_search/frames/
|
@@ -53,9 +83,11 @@ are a few standard keys (see BentoSearch::SearchEngine), and others that
|
|
53
83
|
may be engine-specific. Some engine-specific keys (such as api auth keys)
|
54
84
|
may be required for certain engines.
|
55
85
|
|
86
|
+
~~~~ruby
|
56
87
|
engine = BentoSearch::GoogleBooksEngine.new(:api_key => "my_gbs_api_key")
|
57
88
|
results = engine.search("a query")
|
58
|
-
|
89
|
+
~~~~
|
90
|
+
|
59
91
|
`results` are a BentoSearch::Results object, which acts like an array of
|
60
92
|
BentoSearch::Item objects, along with some meta-information about the
|
61
93
|
search itself (pagination keys, etc). BentoSearch::Results and Item fields
|
@@ -72,16 +104,20 @@ required for certain functionality (like out-of-the-box AJAX loading).
|
|
72
104
|
|
73
105
|
In an initializer in your app, like say `./config/initializers/bento_search.rb`:
|
74
106
|
|
107
|
+
~~~~ruby
|
75
108
|
BentoSearch.register_engine("gbs") do |conf|
|
76
109
|
conf.engine = "BentoSearch::GoogleBooksEngine"
|
77
110
|
conf.api_key = "my_google_api_key"
|
78
111
|
# any other configuration
|
79
112
|
end
|
113
|
+
~~~~
|
80
114
|
|
81
115
|
Then you can refer to it, for instance in a controller, by the id you registered:
|
82
116
|
|
117
|
+
~~~~ruby
|
83
118
|
@results = BentoSearch.get_engine("gbs").search("my query")
|
84
|
-
|
119
|
+
~~~~
|
120
|
+
|
85
121
|
### Display results
|
86
122
|
|
87
123
|
You can of course write your own code to display a BentoSearch::Results object
|
@@ -89,47 +125,70 @@ however you like. But BentoSearch comes with a helper method for displaying
|
|
89
125
|
a list of BentoSearch::Results in a standard way, using the bento_search
|
90
126
|
helper method.
|
91
127
|
|
92
|
-
|
128
|
+
~~~~ruby
|
129
|
+
<%= bento_search @results %>
|
130
|
+
~~~~
|
131
|
+
|
132
|
+
See also the [Customizing Results Display wiki page](https://github.com/jrochkind/bento_search/wiki/Customizing-Results-Display).
|
93
133
|
|
94
134
|
### Fielded searching.
|
95
135
|
|
96
136
|
You can search by an internal engine-specific field name:
|
97
137
|
|
138
|
+
~~~~ruby
|
98
139
|
google_books_engine.search("smith", :search_field => "inauthor")
|
99
|
-
|
140
|
+
~~~~
|
141
|
+
|
100
142
|
Or, if the engine provides it, you can search by normalized semantic search
|
101
143
|
field type names:
|
102
144
|
|
103
|
-
|
104
|
-
|
105
|
-
|
145
|
+
~~~~ruby
|
146
|
+
google_books_engine.search("smith", :semantic_search_field => :title)
|
147
|
+
~~~~
|
148
|
+
|
106
149
|
You can find out what fields a particular engine supports.
|
107
150
|
|
151
|
+
~~~~ruby
|
108
152
|
google_books_engine.search_keys # => internal keys
|
109
153
|
google_books_engine.semantic_search_keys
|
154
|
+
~~~~
|
155
|
+
|
156
|
+
A helper method for generating an html select of search field options is
|
157
|
+
available in `bento_field_hash_for`, check it out.
|
110
158
|
|
111
159
|
You can also provide all arguments in a single hash when it's convenient
|
112
160
|
to do so:
|
113
161
|
|
162
|
+
~~~~ruby
|
114
163
|
google_books_engine.search(:query => "smith", :search_field => "inauthor")
|
164
|
+
~~~~
|
165
|
+
|
166
|
+
Search fields that are not recognized (semantic or internal) will normally
|
167
|
+
be ignored, but set `:unrecognized_search_field => :raise` in configuration
|
168
|
+
or search arg to get an ArgumentError instead.
|
115
169
|
|
116
170
|
### Sorting
|
117
171
|
|
118
172
|
An engine advertises what sort types it supports:
|
119
173
|
|
174
|
+
~~~~ruby
|
120
175
|
google_books_engine.sort_keys
|
121
|
-
|
176
|
+
~~~~
|
177
|
+
|
122
178
|
An array of sort identifiers, where possible
|
123
|
-
chosen from a standard list of semantics. (See list in config/i18n/en.yml
|
124
|
-
bento_search.sort_keys).
|
179
|
+
chosen from a standard list of semantics. (See list in `./config/i18n/en.yml`,
|
180
|
+
`bento_search.sort_keys`).
|
125
181
|
|
182
|
+
~~~~ruby
|
126
183
|
google_books_engine.search("my query", :sort => "date_desc")
|
127
|
-
|
128
|
-
For help creating your UI, you can use built-in helper method:
|
184
|
+
~~~~
|
129
185
|
|
130
|
-
|
131
|
-
|
132
|
-
|
186
|
+
For help creating your UI, you can use built-in helper method, perhaps with Rails helper
|
187
|
+
options_for_select:
|
188
|
+
|
189
|
+
~~~~ruby
|
190
|
+
<%= options_for_select( bento_sort_hash_for(engine), params[:sort] ) %>
|
191
|
+
~~~~
|
133
192
|
|
134
193
|
|
135
194
|
### Pagination
|
@@ -138,13 +197,17 @@ You can tell the search engine how many items you want per-page, and
|
|
138
197
|
use _either_ `:start` (0-based item offset) or `:page` (1-based page
|
139
198
|
offset) keys to paginate into the results.
|
140
199
|
|
200
|
+
~~~~ruby
|
141
201
|
results = google_books_engine.search("my query", :per_page => 20, :start => 40)
|
142
202
|
results = google_books_engine.search("my query", :per_page => 20, :page => 2) # means same as above
|
143
|
-
|
203
|
+
~~~~
|
204
|
+
|
144
205
|
An engine instance advertises it's maximum per-page values.
|
145
206
|
|
207
|
+
~~~~ruby
|
146
208
|
google_books_engine.max_per_page
|
147
|
-
|
209
|
+
~~~~
|
210
|
+
|
148
211
|
bento_search fixes the default per_page at 10.
|
149
212
|
|
150
213
|
For help creating your UI, you can ask a BentoSearch::Results for
|
@@ -153,8 +216,10 @@ object which should be suitable for passing to [kaminari](https://github.com/ama
|
|
153
216
|
`paginate`, or else have convenient methods for roll your own pagination UI.
|
154
217
|
Kaminari's paginate method:
|
155
218
|
|
219
|
+
~~~~ruby
|
156
220
|
<%= paginate results.pagination %>
|
157
|
-
|
221
|
+
~~~~
|
222
|
+
|
158
223
|
### Concurrent searching
|
159
224
|
|
160
225
|
If you're going to search 2 or more search engines at once, you'll want to execute
|
@@ -172,8 +237,8 @@ to help you do this easily. Say, in a controller:
|
|
172
237
|
# constructor takes id's registered with BentoSearch.register_engine
|
173
238
|
searcher = BentoSearch::MultiSearcher.new(:gbs, :scopus, :summon)
|
174
239
|
|
175
|
-
# Call '
|
176
|
-
searcher.
|
240
|
+
# Call 'search' with any parameters you would give to an_engine.search
|
241
|
+
searcher.search("my query", :semantic_search_field => :author, :sort => "title")
|
177
242
|
|
178
243
|
# At this point, all searches are executing asynchronously in seperate threads.
|
179
244
|
# To get the results, blocking until all complete:
|
@@ -189,8 +254,10 @@ in the main thread (like search a local store of some kind outside of
|
|
189
254
|
bento_search)
|
190
255
|
|
191
256
|
You will need to add the 'celluloid' gem to your app to use this feature,
|
192
|
-
BentoSearch doesn't automatically include the celluloid dependency
|
193
|
-
|
257
|
+
BentoSearch doesn't automatically include the celluloid dependency. Note
|
258
|
+
that Celluloid uses multi-threading in such a way that you might need
|
259
|
+
to turn Rails config.cache_classes=true even in development.
|
260
|
+
|
194
261
|
|
195
262
|
For more info, see BentoSearch::MultiSearcher.
|
196
263
|
|
@@ -199,38 +266,16 @@ For more info, see BentoSearch::MultiSearcher.
|
|
199
266
|
BentoSearch provides some basic support for initially displaying a placeholder
|
200
267
|
progress spinner, and having Javascript call back to get the actual results.
|
201
268
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
to get JS for ajax loading. (or require 'bento_search' to get all bento_search JS)
|
207
|
-
* **Note** that this is not a panacea for a very slow search engine -- if the
|
208
|
-
search results take 20 seconds to come in, when the AJAX call back happens,
|
209
|
-
your Rails process _will_ be blocked from serving any other requests for that 20
|
210
|
-
seconds. In fact, that makes this feature of very limited applicability in general,
|
211
|
-
think carefully about what this will do for you.
|
212
|
-
* **Beware** that there are some authorization considerations if your search
|
213
|
-
engine is not publically configurable, see BentoSearch::SearchController
|
214
|
-
for more details.
|
215
|
-
|
216
|
-
You have have registered a configured engine globally, and given it the special
|
217
|
-
`:allow_routable_results` key.
|
218
|
-
|
219
|
-
BentoSearch.register_engine("gbs") do |conf|
|
220
|
-
conf.api_key = "x"
|
221
|
-
conf.allow_routable_results = true
|
222
|
-
end
|
223
|
-
|
224
|
-
Now you can use the `bento_search` helper method with the registered id
|
225
|
-
and query, instead of with results as before, and with an option for
|
226
|
-
ajax auto-load.
|
227
|
-
|
228
|
-
<%= bento_search("gbs", :query => "my query",
|
229
|
-
:semantic_search_field => :title,
|
230
|
-
:load => :ajax_auto) %>
|
231
|
-
|
269
|
+
It's not a panacea for pathologically slow search results, and can be tricky
|
270
|
+
for results that need access controls. But it can be useful
|
271
|
+
in some situations, both for automatic on-page-load ajax loading, and triggered
|
272
|
+
ajax loading.
|
232
273
|
|
274
|
+
See the [wiki page](https://github.com/jrochkind/bento_search/wiki/AJAX-results-loading)
|
275
|
+
for more info.
|
233
276
|
|
277
|
+
|
278
|
+
|
234
279
|
### Item Decorators, and Links
|
235
280
|
|
236
281
|
You can configure Decorators, in the form of plain old ruby modules, to be
|
@@ -244,15 +289,27 @@ link resolver.
|
|
244
289
|
BentoSearch::Items can have a main link associated with them (generally
|
245
290
|
hyperlinked from title), as well as a list of additional links. Most engines
|
246
291
|
do not provide additional links by default, custom local Decorators would
|
247
|
-
be used to add them.
|
292
|
+
be used to add them. See wiki for more info on decorators, and BentoSearch::Link
|
293
|
+
for fields.
|
294
|
+
|
295
|
+
## OpenURL and metadata
|
248
296
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
297
|
+
Academic library uses often need openurl links from scholarly citations. One of
|
298
|
+
the design goals of bento_search is to produce standardized normalized BentoSearch::ResultItem
|
299
|
+
models, with sufficient semantics for translation to other formats.
|
300
|
+
|
301
|
+
See ResultItem#to_openurl_kev (string URL query encoding of OpenURL), and
|
302
|
+
ResultItem#to_openurl (a [ruby OpenURL gem](https://github.com/openurl/openurl) object).
|
253
303
|
|
254
|
-
|
255
|
-
|
304
|
+
Quality may vary, depending on how well the particular engine adapter captures semantics,
|
305
|
+
especially the format/type of results (See bento_search's internal format/type vocabulary
|
306
|
+
documented at ResultItem#format). As well as how well the #to_openurl routine
|
307
|
+
handles all edge cases (OpenURL can be weird). As edge cases are discovered, they
|
308
|
+
can be solved.
|
309
|
+
|
310
|
+
See `./app/item_decorators/bento_search/openurl_add_other_link.rb` for an example
|
311
|
+
of using item decorators to add a link to your openurl resover to an item when
|
312
|
+
displayed.
|
256
313
|
|
257
314
|
## Planned Features
|
258
315
|
|
@@ -267,7 +324,6 @@ Probably:
|
|
267
324
|
also may be supported by some engines that do not support facetting).
|
268
325
|
* Support for multi-field, multi-entry-box 'advanced search' UI's, in
|
269
326
|
a normalized cross-engine way.
|
270
|
-
* More mindless support for displaying pagination UI with kaminari.
|
271
327
|
|
272
328
|
Other needs or suggestions?
|
273
329
|
|
@@ -304,3 +360,4 @@ welcome. See more info on writing a BentoSearch::SearchEngine in the inline
|
|
304
360
|
docs in that file.
|
305
361
|
|
306
362
|
|
363
|
+
|
@@ -4,7 +4,7 @@ var BentoSearch = BentoSearch || {}
|
|
4
4
|
// Will AJAX load bento search results inside that node.
|
5
5
|
// optional second arg success callback function.
|
6
6
|
BentoSearch.ajax_load = function(node, success_callback) {
|
7
|
-
div = $(node);
|
7
|
+
var div = $(node);
|
8
8
|
|
9
9
|
if (div.length == 0) {
|
10
10
|
//we've got nothing
|
@@ -22,11 +22,19 @@ BentoSearch.ajax_load = function(node, success_callback) {
|
|
22
22
|
// Now load the actual external content from html5 data-bento-ajax-url
|
23
23
|
$.ajax({
|
24
24
|
url: div.data("bentoAjaxUrl"),
|
25
|
-
success: function(response, status, xhr) {
|
25
|
+
success: function(response, status, xhr) {
|
26
|
+
var do_replace = true;
|
26
27
|
if (success_callback) {
|
27
|
-
|
28
|
+
// We need to make the response into a DOM so the callback
|
29
|
+
// can deal with it better. Wrapped in a div, so it makes
|
30
|
+
// jquery happy even if there isn't a single parent element.
|
31
|
+
response = $("<div>" + response + "</div>");
|
32
|
+
|
33
|
+
do_replace = success_callback.apply(div, [response]);
|
28
34
|
}
|
29
|
-
|
35
|
+
if (do_replace != false) {
|
36
|
+
div.replaceWith(response);
|
37
|
+
}
|
30
38
|
},
|
31
39
|
error: function(xhr, status, errorThrown) {
|
32
40
|
var msg = "Sorry but there was an error: ";
|
@@ -3,10 +3,6 @@
|
|
3
3
|
or just use this as documentation for suggestions to implement yourself. */
|
4
4
|
|
5
5
|
|
6
|
-
/* year in bold in citations */
|
7
|
-
.bento_item_row.published_in .year {
|
8
|
-
font-weight: bold;
|
9
|
-
}
|
10
6
|
|
11
7
|
/* highlighted element in title is already in a <b> tag, but
|
12
8
|
title is likely to be bold already. italisize it too. */
|
@@ -29,3 +25,7 @@
|
|
29
25
|
.bento_item_title a .bento_search_highlight {
|
30
26
|
font-style: inherit;
|
31
27
|
}
|
28
|
+
|
29
|
+
.bento_item_body .source_title {
|
30
|
+
font-style: italic;
|
31
|
+
}
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
1
5
|
# Rails helper module provided by BentoSearch meant to be included
|
2
6
|
# in host app's helpers.
|
3
7
|
module BentoSearchHelper
|
@@ -25,6 +29,7 @@ module BentoSearchHelper
|
|
25
29
|
# bento_results("google_books", :query => "cancer", :load => :ajax_auto)
|
26
30
|
#
|
27
31
|
def bento_search(search_arg, options = {})
|
32
|
+
|
28
33
|
results = search_arg if search_arg.kind_of? BentoSearch::Results
|
29
34
|
|
30
35
|
load_mode = options.delete(:load)
|
@@ -35,6 +40,7 @@ module BentoSearchHelper
|
|
35
40
|
engine = (if search_arg.kind_of? BentoSearch::SearchEngine
|
36
41
|
search_arg
|
37
42
|
else
|
43
|
+
raise ArgumentError.new("Need Results, engine, or registered engine_id as first argument to #bento_search") unless search_arg
|
38
44
|
BentoSearch.get_engine(search_arg.to_s)
|
39
45
|
end)
|
40
46
|
|
@@ -66,16 +72,32 @@ module BentoSearchHelper
|
|
66
72
|
results = engine.search(options) unless results
|
67
73
|
|
68
74
|
if results.failed?
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
partial = (results.display_configuration.error_partial if results.display_configuration) || "bento_search/search_error"
|
76
|
+
render :partial => partial, :locals => {:results => results}
|
77
|
+
elsif results.length > 0
|
78
|
+
partial = (results.display_configuration.item_partial if results.display_configuration) || "bento_search/std_item"
|
79
|
+
render :partial => partial, :collection => results, :as => :item, :locals => {:results => results}
|
72
80
|
else
|
73
81
|
content_tag(:div, :class=> "bento_search_no_results") do
|
74
|
-
|
82
|
+
partial = (results.display_configuration.no_results_partial if results.display_configuration) || "bento_search/no_results"
|
83
|
+
render :partial => partial, :locals => {:results => results}
|
75
84
|
end
|
76
85
|
end
|
77
86
|
end
|
78
87
|
end
|
88
|
+
|
89
|
+
# Wrap a ResultItem in a decorator! For now hard-coded to
|
90
|
+
# BentoSearch::StandardDecorator
|
91
|
+
def bento_decorate(result_item)
|
92
|
+
# What decorator class? If specified as string in #decorator,
|
93
|
+
# look it up as a class object, else default.
|
94
|
+
decorator_class = result_item.decorator.try {|name| BentoSearch::Util.constantize(name) } || BentoSearch::StandardDecorator
|
95
|
+
|
96
|
+
# in a helper method, 'self' is a view_context already I think?
|
97
|
+
decorated = decorator_class.new(result_item, self)
|
98
|
+
yield(decorated) if block_given?
|
99
|
+
return decorated
|
100
|
+
end
|
79
101
|
|
80
102
|
|
81
103
|
##
|
@@ -89,33 +111,53 @@ module BentoSearchHelper
|
|
89
111
|
#
|
90
112
|
##
|
91
113
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
114
|
+
# Like rails truncate helper, and taking the same options, but html_safe.
|
115
|
+
#
|
116
|
+
# If input string is NOT marked html_safe?, simply passes to rails truncate helper.
|
117
|
+
# If a string IS marked html_safe?, uses nokogiri to parse it, and truncate
|
118
|
+
# actual displayed text to max_length, while keeping html structure valid.
|
119
|
+
#
|
120
|
+
# Default omission marker is unicode elipsis
|
121
|
+
#
|
122
|
+
# :length option will also default to 280, what we think is a good
|
123
|
+
# length for abstract/snippet display
|
124
|
+
def bento_truncate(str, options = {})
|
125
|
+
options.reverse_merge!(:omission => "…", :length => 280, :separator => ' ')
|
126
|
+
|
127
|
+
# works for non-html of course, but for html a quick check
|
128
|
+
# to avoid expensive nokogiri parse if the whole string, even
|
129
|
+
# with tags, is still less than max length.
|
130
|
+
return str if str.length < options[:length]
|
96
131
|
|
97
|
-
|
132
|
+
if str.html_safe?
|
133
|
+
noko = Nokogiri::HTML::DocumentFragment.parse(str)
|
134
|
+
BentoSearch::Util.nokogiri_truncate(noko, options[:length], options[:omission], options[:separator]).inner_html.html_safe
|
135
|
+
else
|
136
|
+
return truncate(str, options)
|
137
|
+
end
|
98
138
|
end
|
139
|
+
|
140
|
+
|
99
141
|
|
100
|
-
#
|
101
|
-
#
|
142
|
+
# Deprecated, made more sense to put this in a partial. Just
|
143
|
+
# call partial directly:
|
144
|
+
# <%= render :partial => "bento_search/item_title", :object => item, :as => 'item' %>
|
102
145
|
def bento_item_title(item)
|
103
|
-
|
104
|
-
safe_join([
|
105
|
-
link_to_unless( item.link.blank?, item.complete_title, item.link ),
|
106
|
-
if item.format.present? || item.format_str.present?
|
107
|
-
content_tag("small", :class => "bento_item_about") do
|
108
|
-
" (" +
|
109
|
-
if item.format_str
|
110
|
-
item.format_str
|
111
|
-
else
|
112
|
-
t(item.format, :scope => [:bento_search, :format], :default => item.format.to_s.titleize)
|
113
|
-
end + ")"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
], '')
|
117
|
-
end
|
146
|
+
render :partial => "bento_search/item_title", :object => item, :as => 'item'
|
118
147
|
end
|
148
|
+
deprecate :bento_item_title
|
149
|
+
|
150
|
+
# pass in 0-based rails current collection counter and a BentoSearch::Results,
|
151
|
+
# calculates a user-displayable result set index label.
|
152
|
+
#
|
153
|
+
# Only non-trivial thing is both inputs are allowed to be nil; if either
|
154
|
+
# is nil, nil is returned.
|
155
|
+
def bento_item_counter(counter, results)
|
156
|
+
return nil if counter.nil? || results.nil? || results.start.nil?
|
157
|
+
|
158
|
+
return counter + results.start + 1
|
159
|
+
end
|
160
|
+
|
119
161
|
|
120
162
|
# first 3 authors, each in a <span>, using item.author_display, seperated by
|
121
163
|
# semi-colons.
|
@@ -125,7 +167,7 @@ module BentoSearchHelper
|
|
125
167
|
first_three = item.authors.slice(0,3)
|
126
168
|
|
127
169
|
first_three.each_with_index do |author, index|
|
128
|
-
parts << content_tag("span", :class => "
|
170
|
+
parts << content_tag("span", :class => "authors") do
|
129
171
|
item.author_display(author)
|
130
172
|
end
|
131
173
|
if (index + 1) < first_three.length
|
@@ -133,6 +175,10 @@ module BentoSearchHelper
|
|
133
175
|
end
|
134
176
|
end
|
135
177
|
|
178
|
+
if item.authors.length > 3
|
179
|
+
parts << I18n.t("bento_search.authors_et_al")
|
180
|
+
end
|
181
|
+
|
136
182
|
return safe_join(parts, "")
|
137
183
|
end
|
138
184
|
|
@@ -150,5 +196,46 @@ module BentoSearchHelper
|
|
150
196
|
]
|
151
197
|
end
|
152
198
|
|
199
|
+
# Returns a hash of label => key suitable for passing to rails
|
200
|
+
# options_for_select. ONLY includes fields with :semantics set at
|
201
|
+
# present. Key will be _semantic_ key name.
|
202
|
+
# For engine-specific fields, you're on you're own, sorry!
|
203
|
+
#
|
204
|
+
# If first arg is an engine instance, will
|
205
|
+
# be search fields supported by that engine. If first arg is nil,
|
206
|
+
# will be any field in our i18n lists for search fields.
|
207
|
+
#
|
208
|
+
# Can pass in options :only or :except to customize list. Values
|
209
|
+
# in :only and :except will match on internal field names OR
|
210
|
+
# semantic field names (hopefully this convenience ambiguity
|
211
|
+
# won't cause problems)
|
212
|
+
#
|
213
|
+
# eg:
|
214
|
+
# <%= select_tag 'search_field', options_for_select(bento_field_hash_for(@engine), params[:search_field]) %>
|
215
|
+
def bento_field_hash_for(engine, options = {})
|
216
|
+
if engine.nil?
|
217
|
+
hash = I18n.t("bento_search.search_fields").invert
|
218
|
+
else
|
219
|
+
hash = Hash[ engine.search_field_definitions.collect do |k, defn|
|
220
|
+
if defn[:semantic] && (label = I18n.t(defn[:semantic], :scope => "bento_search.search_fields", :default => defn[:semantic].to_s.titlecase ))
|
221
|
+
[label, defn[:semantic].to_s]
|
222
|
+
end
|
223
|
+
end]
|
224
|
+
end
|
225
|
+
|
226
|
+
# :only/:except
|
227
|
+
if options[:only]
|
228
|
+
keys = [options[:only]].flatten.collect(&:to_s)
|
229
|
+
hash.delete_if {|key, value| ! keys.include?(value) }
|
230
|
+
end
|
231
|
+
|
232
|
+
if options[:except]
|
233
|
+
keys = [options[:except]].flatten.collect(&:to_s)
|
234
|
+
hash.delete_if {|key, value| keys.include?(value) }
|
235
|
+
end
|
236
|
+
|
237
|
+
return hash
|
238
|
+
end
|
239
|
+
|
153
240
|
|
154
241
|
end
|