bento_search 1.3.0 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -2
- data/app/controllers/bento_search/search_controller.rb +1 -1
- data/app/models/bento_search/author.rb +6 -4
- data/app/models/bento_search/link.rb +8 -5
- data/app/models/bento_search/multi_searcher.rb +1 -1
- data/app/models/bento_search/registrar.rb +2 -1
- data/app/models/bento_search/result_item.rb +37 -21
- data/app/models/bento_search/results/serialization.rb +136 -0
- data/app/models/bento_search/results.rb +46 -1
- data/app/models/bento_search/search_engine.rb +22 -17
- data/app/search_engines/bento_search/eds_engine.rb +1 -1
- data/app/search_engines/bento_search/journal_tocs_for_journal.rb +21 -3
- data/lib/bento_search/version.rb +1 -1
- data/lib/bento_search.rb +5 -2
- data/test/search_engines/journal_tocs_for_journal_test.rb +117 -93
- data/test/search_engines/search_engine_test.rb +1 -1
- data/test/unit/serialization_test.rb +249 -0
- data/test/vcr_cassettes/journal_tocs/empty_results_on_bad_ISSN.yml +19 -25
- data/test/vcr_cassettes/journal_tocs/error_on_bad_registered_email.yml +19 -26
- data/test/vcr_cassettes/journal_tocs/error_on_error_response.yml +33 -37
- data/test/vcr_cassettes/journal_tocs/fetch_xml_with_hits.yml +308 -305
- data/test/vcr_cassettes/journal_tocs/fills_out_metadata.yml +515 -377
- data/test/vcr_cassettes/journal_tocs/smoke_test.yml +308 -305
- data/test/vcr_cassettes/journal_tocs/sorts_by_date.yml +922 -0
- metadata +7 -6
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +0 -164426
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee7b23e2add7fc9cadf6223574615febac70af86
|
4
|
+
data.tar.gz: ebc2f96b07206c4b7a4e4de5083a8ef0a02576ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e7e9044ff5b1bdc115d828c05ddaa7af4190bed36ec28d3994cd8a4b7092f58d7086b1ed322183c32419d29fc2e311172722495fcde7b1b2afa5b4ec70324cf
|
7
|
+
data.tar.gz: 86adb80a03ef49a5a2d55d56447f92decb412084d246b092c54558c3ef215ede30517d73438fe9e2cce1c7e3ce3037b6a2e389707f32ef0f69951b685a044c29
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
bento_search provides an abstraction/normalization layer for querying and
|
7
7
|
displaying results from external search engines, in Ruby on Rails. Works with
|
8
|
-
Rails 3.x or 4.x.
|
8
|
+
Rails 3.x or 4.x. ruby 1.9.3+
|
9
9
|
|
10
10
|
### Goals: To help you
|
11
11
|
|
@@ -87,7 +87,7 @@ may be required for certain engines.
|
|
87
87
|
~~~~
|
88
88
|
|
89
89
|
`results` are a [BentoSearch::Results](./app/models/bento_search/results.rb) object, which acts like an array of
|
90
|
-
[BentoSearch::
|
90
|
+
[BentoSearch::ResultItem](./app/models/bento_search/result_item.rb) objects, along with some meta-information about the
|
91
91
|
search itself (pagination keys, etc). BentoSearch::Results and Item fields
|
92
92
|
are standardized accross engines. BentoSearch::Items provide semantic
|
93
93
|
values (title, author, etc.), as available from the particular engine.
|
@@ -350,6 +350,46 @@ end
|
|
350
350
|
There are additional details that might matter to you, for more info see the
|
351
351
|
[wiki page](https://github.com/jrochkind/bento_search/wiki/Machine-Readable-Serialization-With-Atom)
|
352
352
|
|
353
|
+
### Round-Trip Serialization to JSON
|
354
|
+
|
355
|
+
You can serialize BentoSearch::Results to a simple straightforward JSON structure, and de-serialize
|
356
|
+
them back into BentoSearch::Results.
|
357
|
+
|
358
|
+
~~~ruby
|
359
|
+
json_str = results.dump_to_json
|
360
|
+
copy_of_results = BentoSearch::Results.load_json(json_str)
|
361
|
+
~~~
|
362
|
+
|
363
|
+
Search context (query, start, per_page) are not serialized, and will be lost
|
364
|
+
on de-serialization.
|
365
|
+
|
366
|
+
Unlike the Atom serialization, **the JSON serialization is of internal data
|
367
|
+
state, without decoration.** Configuration context is not serialized.
|
368
|
+
|
369
|
+
However, the engine_id is included in serialization if present,
|
370
|
+
and configuration from the specified engine
|
371
|
+
will be re-assigned on de-serialization. This means if the configuration
|
372
|
+
changed between serialization and de-serialization, you get the new stuff
|
373
|
+
assigned on de-serialization.
|
374
|
+
|
375
|
+
The use case guiding JSON serialization is storage somewhere, and
|
376
|
+
round-trip de-serialization in the current app context.
|
377
|
+
|
378
|
+
If you want to take de-serialized results that did not have an engine_id,
|
379
|
+
or set configuration on them to a different engine (registered or not) you can:
|
380
|
+
|
381
|
+
~~~ruby
|
382
|
+
restored = BentoSearch::Results.load_json(json_str)
|
383
|
+
some_engine.fill_in_search_metadata_for(restored)
|
384
|
+
|
385
|
+
# restored Results will have configuration (engine_id, decorators, etc)
|
386
|
+
# set to those configured on some_engine
|
387
|
+
~~~
|
388
|
+
|
389
|
+
If you want a serialization to be consumed by something other than an
|
390
|
+
app using the bento_search gem, as an API, we recommend the [Atom serialization](https://github.com/jrochkind/bento_search/wiki/Machine-Readable-Serialization-With-Atom)
|
391
|
+
instead.
|
392
|
+
|
353
393
|
## Planned Features
|
354
394
|
|
355
395
|
I am trying to keep BentoSearch as simple as it can be to conveniently meet
|
@@ -32,7 +32,7 @@ module BentoSearch
|
|
32
32
|
# We may provide fancier/nicer API for this in the future, if there's
|
33
33
|
# demand.
|
34
34
|
class SearchController < BentoSearchController
|
35
|
-
class AccessDenied <
|
35
|
+
class AccessDenied < BentoSearch::Error ; end
|
36
36
|
|
37
37
|
|
38
38
|
rescue_from AccessDenied, :with => :deny_access
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module BentoSearch
|
2
2
|
class Author
|
3
|
+
include ::BentoSearch::Results::Serialization
|
4
|
+
|
3
5
|
def initialize(args ={})
|
4
6
|
args.each_pair do |key, value|
|
5
7
|
send("#{key}=", value)
|
@@ -7,15 +9,15 @@ module BentoSearch
|
|
7
9
|
end
|
8
10
|
|
9
11
|
# Can be first name or initial, whatever source provides
|
10
|
-
|
12
|
+
serializable_attr_accessor :first
|
11
13
|
# last name/surname/family name as provided by source
|
12
|
-
|
14
|
+
serializable_attr_accessor :last
|
13
15
|
# middle name or initial, as and if provided by source
|
14
|
-
|
16
|
+
serializable_attr_accessor :middle
|
15
17
|
|
16
18
|
# if source doens't provide seperate first/last,
|
17
19
|
# source may only be able to provide one big string, author_display
|
18
|
-
|
20
|
+
serializable_attr_accessor :display
|
19
21
|
|
20
22
|
def empty?
|
21
23
|
first.blank? && last.blank? && middle.blank? && display.blank?
|
@@ -3,27 +3,30 @@ module BentoSearch
|
|
3
3
|
#
|
4
4
|
# label, url, other metadata about the link.
|
5
5
|
class Link
|
6
|
+
include BentoSearch::Results::Serialization
|
7
|
+
|
8
|
+
serializable_attr_accessor :label
|
6
9
|
# url is normally a string, but can also be a Hash passed
|
7
10
|
# to url_for in the local app.
|
8
|
-
|
11
|
+
serializable_attr_accessor :url
|
9
12
|
|
10
13
|
# Used both for HTML links and possibly later Atom.
|
11
14
|
# Must be a string, EITHER a complete URL (representing
|
12
15
|
# vocab term), OR a legal short name from
|
13
16
|
# http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#linkTypes
|
14
|
-
|
17
|
+
serializable_attr_accessor :rel
|
15
18
|
|
16
19
|
# MIME content type may be used for both HMTL links and Atom
|
17
20
|
# link 'type' attribute
|
18
|
-
|
21
|
+
serializable_attr_accessor :type
|
19
22
|
|
20
23
|
# Array of strings, used for CSS classes on this link, possibly
|
21
24
|
# for custom styles/images etc. May be used in non-html link
|
22
25
|
# contexts too.
|
23
|
-
|
26
|
+
serializable_attr_accessor :style_classes
|
24
27
|
|
25
28
|
# Suggested `target` attribute to render link with as html <a>
|
26
|
-
|
29
|
+
serializable_attr_accessor :target
|
27
30
|
|
28
31
|
def initialize(hash = {})
|
29
32
|
self.style_classes = []
|
@@ -105,7 +105,7 @@ begin
|
|
105
105
|
def start(*search_args)
|
106
106
|
begin
|
107
107
|
@results = self.engine.search(*search_args)
|
108
|
-
rescue
|
108
|
+
rescue StandardError => e
|
109
109
|
Rails.logger.error("\nBentoSearch:MultiSearcher caught exception: #{e}\n#{e.backtrace.join(" \n")}")
|
110
110
|
# Make a fake results with caught exception.
|
111
111
|
@results = BentoSearch::Results.new
|
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'bento_search'
|
1
2
|
|
2
3
|
# Holds a list of registered search engines with configuration.
|
3
4
|
# There's one global one referened by BentoSearch module, but one
|
4
5
|
# might want to create multiple.
|
5
6
|
class BentoSearch::Registrar
|
6
|
-
class ::BentoSearch::NoSuchEngine <
|
7
|
+
class ::BentoSearch::NoSuchEngine < ::BentoSearch::Error ; end
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
@registered_engine_confs = {}
|
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'language_list'
|
2
|
+
require 'bento_search/author'
|
3
|
+
require 'bento_search/link'
|
4
|
+
|
2
5
|
|
3
6
|
module BentoSearch
|
4
7
|
# Data object representing a single hit from a search, normalized
|
@@ -14,6 +17,8 @@ module BentoSearch
|
|
14
17
|
include ERB::Util # for html_escape for our presentational stuff
|
15
18
|
include ActionView::Helpers::OutputSafetyHelper # for safe_join
|
16
19
|
|
20
|
+
include ::BentoSearch::Results::Serialization
|
21
|
+
|
17
22
|
# Can initialize with a hash of key/values
|
18
23
|
def initialize(args = {})
|
19
24
|
args.each_pair do |key, value|
|
@@ -30,21 +35,24 @@ module BentoSearch
|
|
30
35
|
# internal unique id for the document, from the particular
|
31
36
|
# search service it came from. May be alphanumeric. May be nil
|
32
37
|
# for engines that don't support it.
|
33
|
-
|
38
|
+
serializable_attr_accessor :unique_id
|
39
|
+
|
34
40
|
|
35
41
|
# If set to true, item will refuse to generate an openurl,
|
36
42
|
# returning nil from #to_openurl or #openurl_kev
|
37
|
-
|
43
|
+
serializable_attr_accessor :openurl_disabled
|
44
|
+
|
38
45
|
|
39
46
|
# Array (possibly empty) of BentoSearch::Link objects
|
40
47
|
# representing additional links. Often SearchEngine's themselves
|
41
48
|
# won't include any of these, but Decorators will be used
|
42
49
|
# to add them in.
|
43
50
|
attr_accessor :other_links
|
51
|
+
serializable_attr :other_links, :collection_of => "BentoSearch::Link"
|
44
52
|
|
45
53
|
# * dc.title
|
46
54
|
# * schema.org CreativeWork: 'name'
|
47
|
-
|
55
|
+
serializable_attr_accessor :title
|
48
56
|
# backwards compat, we used to have separate titles and subtitles
|
49
57
|
alias_method :complete_title, :title
|
50
58
|
|
@@ -52,6 +60,7 @@ module BentoSearch
|
|
52
60
|
# Can be changed in actual presentation with a Decorator.
|
53
61
|
# * schema.org CreativeWork: 'url'
|
54
62
|
attr_accessor :link
|
63
|
+
serializable_attr :link
|
55
64
|
|
56
65
|
# does the #link correspond to fulltext? true or false -- or nil
|
57
66
|
# for unknown/non-applicable. Not all engines will set.
|
@@ -61,6 +70,7 @@ module BentoSearch
|
|
61
70
|
def link_is_fulltext=(v)
|
62
71
|
@link_is_fulltext = v
|
63
72
|
end
|
73
|
+
serializable_attr :link_is_fulltext
|
64
74
|
|
65
75
|
# Our own INTERNAL controlled vocab for 'format'.
|
66
76
|
#
|
@@ -102,7 +112,7 @@ module BentoSearch
|
|
102
112
|
#
|
103
113
|
# Note: We're re-thinking this, might allow uncontrolled
|
104
114
|
# in here instead.
|
105
|
-
|
115
|
+
serializable_attr_accessor :format
|
106
116
|
|
107
117
|
# Translated from internal format vocab at #format. Outputs
|
108
118
|
# eg http://schema.org/Book
|
@@ -127,7 +137,7 @@ module BentoSearch
|
|
127
137
|
# uncontrolled presumably english-language format string.
|
128
138
|
# if supplied will be used in display in place of controlled
|
129
139
|
# format.
|
130
|
-
|
140
|
+
serializable_attr_accessor :format_str
|
131
141
|
|
132
142
|
# Language of materials. Producer can set language_code to an ISO 639-1 (two
|
133
143
|
# letter) or 639-3 (three letter) language code. If you do this, you don't
|
@@ -143,7 +153,7 @@ module BentoSearch
|
|
143
153
|
# #language_iso_639_2 (either may be null), or #language_str for uncontrolled
|
144
154
|
# string. If engine just sets one of these, internals take care of filling
|
145
155
|
# out the others. r
|
146
|
-
|
156
|
+
serializable_attr_accessor :language_code
|
147
157
|
attr_writer :language_str
|
148
158
|
def language_str
|
149
159
|
@language_str || language_code.try do |code|
|
@@ -152,6 +162,7 @@ module BentoSearch
|
|
152
162
|
end
|
153
163
|
end
|
154
164
|
end
|
165
|
+
serializable_attr :language_str
|
155
166
|
# Returns a LanguageList gem language object-- from #language_code
|
156
167
|
# if available, otherwise from direct language_str if available and
|
157
168
|
# possible.
|
@@ -176,45 +187,46 @@ module BentoSearch
|
|
176
187
|
# * prism:coverDate, year portion
|
177
188
|
#
|
178
189
|
# See also publication_date when you have a complete date
|
179
|
-
|
190
|
+
serializable_attr_accessor :year
|
180
191
|
# ruby stdlib Date object.
|
181
192
|
attr_accessor :publication_date
|
193
|
+
serializable_attr :publication_date, :serializer => "Date"
|
182
194
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
195
|
+
serializable_attr_accessor :volume
|
196
|
+
serializable_attr_accessor :issue
|
197
|
+
serializable_attr_accessor :start_page
|
198
|
+
serializable_attr_accessor :end_page
|
187
199
|
|
188
200
|
# source_title is often used for journal_title (and aliased
|
189
201
|
# as #journal_title, although that may go away), but can
|
190
202
|
# also be used for other 'container' titles. Book title for
|
191
203
|
# a book chapter. Even web site or URL for a web page.
|
192
|
-
|
204
|
+
serializable_attr_accessor :source_title
|
193
205
|
alias_method :journal_title, :source_title
|
194
206
|
alias_method :'journal_title=', :'source_title='
|
195
207
|
|
196
208
|
|
197
|
-
|
198
|
-
|
199
|
-
|
209
|
+
serializable_attr_accessor :issn
|
210
|
+
serializable_attr_accessor :isbn
|
211
|
+
serializable_attr_accessor :oclcnum # OCLC accession number, WorldCat.
|
200
212
|
|
201
|
-
|
202
|
-
|
213
|
+
serializable_attr_accessor :doi
|
214
|
+
serializable_attr_accessor :pmid
|
203
215
|
|
204
216
|
# usually used for books rather than articles
|
205
|
-
|
217
|
+
serializable_attr_accessor :publisher
|
206
218
|
|
207
219
|
# an openurl kev-encoded context object. optional,
|
208
220
|
# only if source provides one that may be better
|
209
221
|
# than can be constructed from individual elements above
|
210
|
-
|
222
|
+
serializable_attr_accessor :openurl_kev_co
|
211
223
|
|
212
224
|
# Short summary of item.
|
213
225
|
# Mark .html_safe if it includes html -- creator is responsible
|
214
226
|
# for making sure html is safely sanitizied and/or stripped,
|
215
227
|
# rails ActionView::Helpers::Sanistize #sanitize and #strip_tags
|
216
228
|
# may be helpful.
|
217
|
-
|
229
|
+
serializable_attr_accessor :abstract
|
218
230
|
|
219
231
|
# An ARRAY of string query-in-context snippets. Will usually
|
220
232
|
# have highlighting <b> tags in it. Creator is responsible
|
@@ -225,14 +237,16 @@ module BentoSearch
|
|
225
237
|
# with same content formatted differently (array of multiple vs
|
226
238
|
# one combined string), some engines they may be different.
|
227
239
|
attr_accessor :snippets
|
240
|
+
serializable_attr :snippets
|
228
241
|
|
229
242
|
# An array (order matters) of BentoSearch::Author objects
|
230
243
|
# add authors to it with results.authors << Author
|
231
244
|
attr_accessor :authors
|
245
|
+
serializable_attr :authors, :collection_of => "BentoSearch::Author"
|
232
246
|
|
233
247
|
# engine-specific data not suitable for abstract API, usually
|
234
248
|
# for internal use.
|
235
|
-
|
249
|
+
serializable_attr_accessor :custom_data
|
236
250
|
|
237
251
|
# Copied over from engine configuration usually, a string
|
238
252
|
# qualified name of a decorator class. Can be nil for default.
|
@@ -248,6 +262,8 @@ module BentoSearch
|
|
248
262
|
# into a backpointing reference instead? And user cover-methods
|
249
263
|
# for it? Nice thing about the configuration has instead is it's
|
250
264
|
# easily serializable, it's just data.
|
265
|
+
#
|
266
|
+
# Although we intentionally do NOT include these in JSON serialization, ha.
|
251
267
|
attr_accessor :display_configuration
|
252
268
|
attr_accessor :engine_id
|
253
269
|
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'bento_search/results'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'json'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
# Call #dump_to_json on a BentoSearch value object (such as BentoSearch::Result or ::Author)
|
7
|
+
# to get it in Json
|
8
|
+
#
|
9
|
+
# Values marked with serializable_attr in BentoSearch::Result are
|
10
|
+
# included in seralization.
|
11
|
+
#
|
12
|
+
# At present metadata and configuration are NOT serialized: #decorator, #display_configuration,
|
13
|
+
# and #engine_id are not included in the serialization, so when loaded from serialization,
|
14
|
+
# ResultItems will not have such things set.
|
15
|
+
#
|
16
|
+
# * Works by getting and setting instance variables directly, ignores getters/setters
|
17
|
+
#
|
18
|
+
# * This means decorated values are NOT included in serialization, the raw
|
19
|
+
# values are what is serialized. This is intended, we serialize internal
|
20
|
+
# state, not decoration which can be recreated. You should make sure the decorators you
|
21
|
+
# want are applied after de-serialization.
|
22
|
+
#
|
23
|
+
# * preserves html_safety status in serialization, by adding extra `_attr_htmlsafe: true` key/value
|
24
|
+
#
|
25
|
+
module BentoSearch::Results::Serialization
|
26
|
+
extend ActiveSupport::Concern
|
27
|
+
|
28
|
+
included do
|
29
|
+
class_attribute :_serializable_attrs, :_serializable_attr_options
|
30
|
+
self._serializable_attrs = []
|
31
|
+
self._serializable_attr_options = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
class_methods do
|
35
|
+
# Just a macro to mark a property name serializable -- the name is
|
36
|
+
# of an instance method that will be included in our serializations
|
37
|
+
# and de-serializations.
|
38
|
+
#
|
39
|
+
# Options:
|
40
|
+
# * collection_of: String fully qualified name of a class that is
|
41
|
+
# is also BentoSearch::Results::Serialization, the attribute
|
42
|
+
# is an array of these.
|
43
|
+
# * serializer: String fully qualified class name of a serializer
|
44
|
+
# class that has a `dump` and a `load` for individual values,
|
45
|
+
# we just use it for Date now, see BentoSearch::Results::Serialization::Date
|
46
|
+
def serializable_attr(symbol, options = nil)
|
47
|
+
symbol = symbol.to_s
|
48
|
+
self._serializable_attrs << symbol
|
49
|
+
if options
|
50
|
+
self._serializable_attr_options[symbol] = options
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# convenience macro to do attr_accessor AND mark it
|
55
|
+
# serializable
|
56
|
+
def serializable_attr_accessor(symbol)
|
57
|
+
self.send(:attr_accessor, symbol)
|
58
|
+
self.serializable_attr symbol
|
59
|
+
end
|
60
|
+
|
61
|
+
def from_internal_state_hash(hash)
|
62
|
+
o = self.new
|
63
|
+
hash.each_pair do |key, value|
|
64
|
+
key = key.to_s
|
65
|
+
|
66
|
+
next if key =~ /\A_.*_htmlsafe\Z/
|
67
|
+
|
68
|
+
|
69
|
+
if _serializable_attr_options[key] && _serializable_attr_options[key][:collection_of]
|
70
|
+
klass = qualified_const_get(_serializable_attr_options[key][:collection_of])
|
71
|
+
value = value.collect do |item|
|
72
|
+
klass.from_internal_state_hash(item)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if _serializable_attr_options[key] && _serializable_attr_options[key][:serializer]
|
77
|
+
klass = qualified_const_get(_serializable_attr_options[key][:serializer])
|
78
|
+
value = klass.load(value)
|
79
|
+
end
|
80
|
+
|
81
|
+
if hash["_#{key}_htmlsafe"] == true && value.respond_to?(:html_safe)
|
82
|
+
value = value.html_safe
|
83
|
+
end
|
84
|
+
|
85
|
+
o.instance_variable_set("@#{key}", value)
|
86
|
+
end
|
87
|
+
|
88
|
+
return o
|
89
|
+
end
|
90
|
+
|
91
|
+
def load_json(json_str)
|
92
|
+
self.from_internal_state_hash( JSON.parse! json_str )
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
def internal_state_hash
|
98
|
+
hash = {}
|
99
|
+
self._serializable_attrs.each do |accessor|
|
100
|
+
accessor = accessor.to_s
|
101
|
+
value = self.instance_variable_get("@#{accessor}")
|
102
|
+
|
103
|
+
next if value.blank?
|
104
|
+
|
105
|
+
if _serializable_attr_options[accessor] && _serializable_attr_options[accessor][:serializer]
|
106
|
+
klass = self.class.qualified_const_get(_serializable_attr_options[accessor][:serializer])
|
107
|
+
value = klass.dump(value)
|
108
|
+
elsif value.respond_to?(:to_ary)
|
109
|
+
value = value.to_ary.collect do |item|
|
110
|
+
item.respond_to?(:internal_state_hash) ? item.internal_state_hash : item
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
hash[accessor] = value
|
115
|
+
|
116
|
+
if value.respond_to?(:html_safe?) && value.html_safe?
|
117
|
+
hash["_#{accessor}_htmlsafe"] = true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
return hash
|
121
|
+
end
|
122
|
+
|
123
|
+
def dump_to_json
|
124
|
+
JSON.dump self.internal_state_hash
|
125
|
+
end
|
126
|
+
|
127
|
+
class Date
|
128
|
+
def self.dump(datetime)
|
129
|
+
datetime.iso8601
|
130
|
+
end
|
131
|
+
def self.load(str)
|
132
|
+
::Date.iso8601(str)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -1,10 +1,20 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module BentoSearch
|
2
4
|
# An array-like object (in fact it-subclasses Array) that holds
|
3
5
|
# a page of search results. But also has some meta-data about the
|
4
6
|
# search itself (query, paging, etc).
|
5
|
-
|
7
|
+
#
|
6
8
|
# If #error is non-nil, object may not have real results, but
|
7
9
|
# be an error. You can use failed? to see.
|
10
|
+
#
|
11
|
+
# Serializes as a Hash including ONLY the serialized results themselves,
|
12
|
+
# and the engine ID. Search context (total items, start, etc) are not
|
13
|
+
# included in serialization. Configuration context is also not serialized,
|
14
|
+
# although since the engineID is, it can be reconstructed on de-serialization.
|
15
|
+
#
|
16
|
+
# Serialization isn't actually implemented with the BentoSearch::Results::Serialization
|
17
|
+
# module, cause it didn't work out, just re-implemented here with same methods.
|
8
18
|
class Results < ::Array
|
9
19
|
attr_accessor :total_items
|
10
20
|
# 0-based index into total results, used for pagination
|
@@ -48,6 +58,7 @@ module BentoSearch
|
|
48
58
|
# Registered id of engine used to create these results,
|
49
59
|
# may be nil if used with an unregistered engine.
|
50
60
|
attr_accessor :engine_id
|
61
|
+
|
51
62
|
|
52
63
|
# Returns a BentoSearch::Results::Pagination, that should be suitable
|
53
64
|
# for passing right to Kaminari (although Kaminari isn't good about doc/specing
|
@@ -63,6 +74,40 @@ module BentoSearch
|
|
63
74
|
def inspect
|
64
75
|
"<BentoSearch::Results #{super} #{'FAILED' if self.failed?}>"
|
65
76
|
end
|
77
|
+
|
78
|
+
# Serialization
|
79
|
+
def internal_state_hash
|
80
|
+
{
|
81
|
+
"engine_id" => self.engine_id,
|
82
|
+
"result_items" => self.collect {|i| i.internal_state_hash}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
# Creates a Results object from an internal_state_hash, and restores
|
87
|
+
# it's configuration from engine_id
|
88
|
+
def self.from_internal_state_hash(hash)
|
89
|
+
results = BentoSearch::Results.new
|
90
|
+
results.engine_id = hash["engine_id"]
|
91
|
+
hash["result_items"].each do |item_hash|
|
92
|
+
results << BentoSearch::ResultItem.from_internal_state_hash(item_hash)
|
93
|
+
end
|
94
|
+
|
95
|
+
if results.engine_id
|
96
|
+
BentoSearch.get_engine(results.engine_id).fill_in_search_metadata_for(results, {})
|
97
|
+
end
|
98
|
+
|
99
|
+
return results
|
100
|
+
end
|
101
|
+
|
102
|
+
def dump_to_json
|
103
|
+
JSON.dump self.internal_state_hash
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.load_json(json_str)
|
107
|
+
from_internal_state_hash JSON.parse(json_str)
|
108
|
+
end
|
109
|
+
|
110
|
+
|
66
111
|
|
67
112
|
end
|
68
113
|
end
|
@@ -10,9 +10,13 @@ require 'nokogiri'
|
|
10
10
|
module BentoSearch
|
11
11
|
# Usually raised by #get on an engine, when result for specified identifier
|
12
12
|
# can't be found.
|
13
|
-
class NotFound <
|
13
|
+
class ::BentoSearch::NotFound < ::BentoSearch::Error ; end
|
14
14
|
# Usually raised by #get when identifier results in more than one record.
|
15
|
-
class TooManyFound <
|
15
|
+
class ::BentoSearch::TooManyFound < ::BentoSearch::Error ; end
|
16
|
+
# Raised for problem contacting or unexpected response from
|
17
|
+
# remote service. Not yet universally used.
|
18
|
+
class ::BentoSearch::FetchError < ::BentoSearch::Error ; end
|
19
|
+
|
16
20
|
|
17
21
|
# Module mix-in for bento_search search engines.
|
18
22
|
#
|
@@ -217,16 +221,7 @@ module BentoSearch
|
|
217
221
|
fill_in_search_metadata_for(results, arguments)
|
218
222
|
|
219
223
|
results.timing = (Time.now - start_t)
|
220
|
-
|
221
|
-
results.each do |item|
|
222
|
-
# We copy some configuraton info over to each Item, as a convenience
|
223
|
-
# to display logic that may have decide what to do given only an item,
|
224
|
-
# and may want to parameterize based on configuration.
|
225
|
-
item.engine_id = results.engine_id
|
226
|
-
item.decorator = configuration.lookup!("for_display.decorator")
|
227
|
-
item.display_configuration = configuration.for_display
|
228
|
-
end
|
229
|
-
|
224
|
+
|
230
225
|
return results
|
231
226
|
rescue *auto_rescue_exceptions => e
|
232
227
|
# Uncaught exception, log and turn into failed Results object. We
|
@@ -252,16 +247,26 @@ module BentoSearch
|
|
252
247
|
|
253
248
|
# SOME of the elements of Results to be returned that SearchEngine implementation
|
254
249
|
# fills in automatically post-search. Extracted into a method for DRY in
|
255
|
-
# error handling to try to fill these in even in errors.
|
256
|
-
#
|
257
|
-
|
258
|
-
def fill_in_search_metadata_for(results, normalized_arguments)
|
250
|
+
# error handling to try to fill these in even in errors. Also can be used
|
251
|
+
# as public method for de-serialized or mock results.
|
252
|
+
def fill_in_search_metadata_for(results, normalized_arguments = {})
|
259
253
|
results.search_args = normalized_arguments
|
260
254
|
results.start = normalized_arguments[:start] || 0
|
261
255
|
results.per_page = normalized_arguments[:per_page]
|
262
256
|
|
263
257
|
results.engine_id = configuration.id
|
264
|
-
results.display_configuration = configuration.for_display
|
258
|
+
results.display_configuration = configuration.for_display
|
259
|
+
|
260
|
+
# We copy some configuraton info over to each Item, as a convenience
|
261
|
+
# to display logic that may have decide what to do given only an item,
|
262
|
+
# and may want to parameterize based on configuration.
|
263
|
+
results.each do |item|
|
264
|
+
item.engine_id = configuration.id
|
265
|
+
item.decorator = configuration.lookup!("for_display.decorator")
|
266
|
+
item.display_configuration = configuration.for_display
|
267
|
+
end
|
268
|
+
|
269
|
+
results
|
265
270
|
end
|
266
271
|
|
267
272
|
|
@@ -539,7 +539,7 @@ class BentoSearch::EdsEngine
|
|
539
539
|
# there's a short reason in #message, but also
|
540
540
|
# possibly an http_status and http_body copied
|
541
541
|
# from error EDS response.
|
542
|
-
class EdsCommException <
|
542
|
+
class EdsCommException < ::BentoSearch::FetchError
|
543
543
|
attr_accessor :http_status, :http_body
|
544
544
|
def initialize(message, status = nil, body = nil)
|
545
545
|
super(message)
|