umlaut 4.0.3 → 4.1.0.pre.2
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.
- checksums.yaml +8 -8
- data/README.md +6 -0
- data/app/assets/javascripts/umlaut/update_html.js +81 -23
- data/app/assets/stylesheets/umlaut/_misc.scss +1 -2
- data/app/assets/stylesheets/umlaut/_resolve.scss +44 -10
- data/app/controllers/export_email_controller.rb +2 -2
- data/app/controllers/feedback_controller.rb +1 -1
- data/app/controllers/umlaut/controller_behavior.rb +14 -0
- data/app/controllers/umlaut_configurable.rb +52 -21
- data/app/helpers/open_search_helper.rb +1 -1
- data/app/helpers/resolve_helper.rb +36 -35
- data/app/helpers/umlaut/section_highlights.rb +85 -0
- data/app/mixin_logic/metadata_helper.rb +11 -5
- data/app/models/collection.rb +129 -96
- data/app/models/dispatched_service.rb +4 -0
- data/app/models/referent.rb +7 -1
- data/app/models/request.rb +21 -0
- data/app/models/service_store.rb +16 -3
- data/app/presentation/section_renderer.rb +16 -17
- data/app/service_adaptors/blacklight.rb +11 -4
- data/app/service_adaptors/internet_archive.rb +58 -25
- data/app/service_adaptors/isi.rb +14 -4
- data/app/service_adaptors/jcr.rb +13 -4
- data/app/views/resolve/_fulltext.html.erb +1 -1
- data/app/views/resolve/_section_display.html.erb +1 -1
- data/app/views/resolve/_service_errors.html.erb +3 -3
- data/app/views/search/books.html.erb +1 -1
- data/app/views/search/journal_search.html.erb +2 -1
- data/app/views/search/journals.html.erb +2 -1
- data/db/orig_fixed_data/service_type_values.yml +15 -37
- data/lib/aws_product_sign.rb +1 -1
- data/lib/generators/templates/umlaut_services.yml +5 -2
- data/lib/generators/umlaut/install_generator.rb +1 -0
- data/{app/models → lib}/service_type_value.rb +36 -22
- data/lib/umlaut/routes.rb +34 -5
- data/lib/umlaut/test_help.rb +159 -0
- data/lib/umlaut/version.rb +2 -2
- data/lib/umlaut.rb +44 -9
- metadata +10 -8
@@ -0,0 +1,85 @@
|
|
1
|
+
module Umlaut
|
2
|
+
# NOT Rails helper methods, but a helper class with logic to determine
|
3
|
+
# whether a given umlaut display section should be given the
|
4
|
+
# umlaut-section-highlighted class, used to mark recommended access
|
5
|
+
# methods. (For instance, fulltext if it's available, or maybe
|
6
|
+
# document_delivery if it's not, but it gets more complicated.)
|
7
|
+
class SectionHighlights
|
8
|
+
attr_reader :umlaut_request, :umlaut_config
|
9
|
+
|
10
|
+
# * First arg is the umlaut Request
|
11
|
+
# * Second optional is an UmlautConfiguration object, used for
|
12
|
+
# `section_highlights_filter` lambda -- will default to
|
13
|
+
# UmlautController.umlaut_config
|
14
|
+
def initialize(umlaut_request, umlaut_config = UmlautController.umlaut_config)
|
15
|
+
@umlaut_config = umlaut_config
|
16
|
+
@umlaut_request = umlaut_request
|
17
|
+
end
|
18
|
+
|
19
|
+
def should_highlight_section?(section_id)
|
20
|
+
highlighted_sections.include? section_id.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
# array of section div_id's that should be highlighted for
|
24
|
+
# the current request in it's current state. Calculated
|
25
|
+
# with calc_highlighted_sections!, then cached.
|
26
|
+
def highlighted_sections
|
27
|
+
@highlighted_sections ||= calc_highlighted_sections!
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns an array of zero or more sections to display with
|
31
|
+
# .umlaut-section-highlighted -- usually the recommended section,
|
32
|
+
# fulltext if we have it, etc.
|
33
|
+
#
|
34
|
+
# A bit hard to get exactly right for both technical and contextual
|
35
|
+
# policy issues, this is a basic starting point.
|
36
|
+
def calc_highlighted_sections!
|
37
|
+
sections = []
|
38
|
+
|
39
|
+
if umlaut_request.get_service_type("fulltext").present?
|
40
|
+
sections << "fulltext"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Highlight holdings if it's present AND:
|
44
|
+
# no fulltext is present OR it's a book (non-serial) type
|
45
|
+
# We think people want print for books more often.
|
46
|
+
if umlaut_request.get_service_type("holding").present? &&
|
47
|
+
( umlaut_request.get_service_type("fulltext").blank? || (! MetadataHelper.title_is_serial?(umlaut_request.referent)) )
|
48
|
+
sections << "holding"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Return document_delivery as highlighted only if
|
53
|
+
# fulltext and holdings are done being fetched. AND.
|
54
|
+
# If there's no fulltext or holdings, OR there's holdings, but
|
55
|
+
# it's a journal type thing, where we probably don't know if the
|
56
|
+
# particular volume/issue wanted is present. Ugh.
|
57
|
+
if ( umlaut_request.get_service_type("document_delivery").present? &&
|
58
|
+
umlaut_request.get_service_type("fulltext").empty? &&
|
59
|
+
(! umlaut_request.service_types_in_progress?(["fulltext", "holding"])) && (
|
60
|
+
umlaut_request.get_service_type("holding").empty? ||
|
61
|
+
umlaut_request.referent.format == "journal"
|
62
|
+
)
|
63
|
+
)
|
64
|
+
sections << "document_delivery"
|
65
|
+
end
|
66
|
+
|
67
|
+
sections = apply_filters!(sections)
|
68
|
+
|
69
|
+
return sections
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def apply_filters!(sections)
|
74
|
+
sections = sections.dup
|
75
|
+
|
76
|
+
(umlaut_config.section_highlights_filter || []).each do |filter|
|
77
|
+
# filters are expected to mutate 'sections' if they want
|
78
|
+
filter.call(umlaut_request, sections, self)
|
79
|
+
end
|
80
|
+
|
81
|
+
return sections
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -74,7 +74,7 @@ module MetadataHelper
|
|
74
74
|
title.gsub!(/\&/, ' and ') if options[:normalize_ampersand]
|
75
75
|
|
76
76
|
# remove non-alphanumeric, excluding apostrophe
|
77
|
-
title.gsub!(/[
|
77
|
+
title.gsub!(/[^[[:alnum:]][[:space:]]\']/, ' ') if options[:remove_punctuation]
|
78
78
|
|
79
79
|
# apostrophe not to space, just eat it.
|
80
80
|
title.gsub!(/[\']/, '') if options[:remove_punctuation] && ! options[:keep_apostrophes]
|
@@ -341,10 +341,16 @@ module MetadataHelper
|
|
341
341
|
# Look at weird bad OpenURLs, use heuristics to see if the 'title' probably
|
342
342
|
# represents a journal rather than a book. A guess at best, based on the bad
|
343
343
|
# data we've seen, sigh.
|
344
|
-
def title_is_serial?(rft)
|
345
|
-
rft.format != "book" &&
|
346
|
-
(
|
347
|
-
rft.metadata[
|
344
|
+
def title_is_serial?(rft)
|
345
|
+
( rft.format != "book" && rft.format != "dissertation") &&
|
346
|
+
( rft.metadata["btitle"].blank? ) &&
|
347
|
+
( %w{journal article}.include?(rft.metadata["genre"]) ||
|
348
|
+
rft.metadata['jtitle'].present? ||
|
349
|
+
(rft.metadata["genre"].blank? && rft.metadata["issn"].present?)
|
350
|
+
)
|
348
351
|
end
|
352
|
+
# Mark it a module function so it can be called as a utility as
|
353
|
+
# MetadataHelper.title_is_serial?(referent)
|
354
|
+
module_function :title_is_serial?
|
349
355
|
|
350
356
|
end
|
data/app/models/collection.rb
CHANGED
@@ -12,6 +12,8 @@ require 'confstruct'
|
|
12
12
|
# The Collection holds and executes the logic for running those services,
|
13
13
|
# foreground and background, making sure no service is run twice if it's
|
14
14
|
# already in progress, timing out expired services, etc.
|
15
|
+
#
|
16
|
+
# This code is a mess, sorry.
|
15
17
|
class Collection
|
16
18
|
attr_accessor :umlaut_request
|
17
19
|
attr_accessor :logger
|
@@ -66,26 +68,22 @@ class Collection
|
|
66
68
|
#
|
67
69
|
# Returns the Thread object used for dispatching background services
|
68
70
|
def dispatch_services!
|
69
|
-
|
70
|
-
|
71
|
-
|
71
|
+
freshen_dispatches!
|
72
|
+
mark_queued_if_empty!
|
73
|
+
|
74
|
+
dispatch_foreground!
|
72
75
|
|
73
76
|
# return main thread for background services.
|
74
|
-
return dispatch_background!
|
77
|
+
return dispatch_background!
|
75
78
|
end
|
76
79
|
|
77
|
-
# Call prepare_for_dispatch! first, the return value from that call
|
78
|
-
# is suitable as argument for this call: queued_service_ids, list of
|
79
|
-
# service id's already identified as suitable for running, and
|
80
|
-
# marked queued in the DispatchedService table.
|
81
|
-
#
|
82
80
|
# Will run such services in foreground priority waves. And then reload
|
83
81
|
# the UmlautRequest object in the current thread, to pick up any
|
84
82
|
# changes made in service threads.
|
85
|
-
def dispatch_foreground!
|
83
|
+
def dispatch_foreground!
|
86
84
|
# Foreground services
|
87
85
|
(0..9).each do | priority |
|
88
|
-
services_to_run = self.instantiate_services!(:level => priority, :ids =>
|
86
|
+
services_to_run = self.instantiate_services!(:level => priority, :ids => runnable_services_for_priority(priority))
|
89
87
|
next if services_to_run.empty?
|
90
88
|
ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
|
91
89
|
end
|
@@ -96,15 +94,13 @@ class Collection
|
|
96
94
|
umlaut_request.reload
|
97
95
|
end
|
98
96
|
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
# Will run such services in background priority waves.
|
97
|
+
# Will run such services in background priority waves. If some
|
98
|
+
# services are already running, will not run services in subsequent
|
99
|
+
# waves until they are done -- guard against multiple HTTP
|
100
|
+
# requests while services in progress.
|
105
101
|
#
|
106
102
|
# Returns the Thread object used for dispatching background services.
|
107
|
-
def dispatch_background!
|
103
|
+
def dispatch_background!
|
108
104
|
# Now we do some crazy magic, start a Thread to run our background
|
109
105
|
# services. We are NOT going to wait for this thread to join,
|
110
106
|
# we're going to let it keep doing it's thing in the background after
|
@@ -124,11 +120,22 @@ class Collection
|
|
124
120
|
# other stuff done before this thread.
|
125
121
|
Thread.pass
|
126
122
|
|
123
|
+
force_refresh = false
|
127
124
|
|
128
125
|
('a'..'z').each do | priority |
|
129
|
-
|
130
|
-
|
126
|
+
# force refresh only if we just ran some services, otherwise not enough
|
127
|
+
# time has gone by to be worthwhile.
|
128
|
+
runnable_ids = runnable_services_for_priority(priority, :refresh => force_refresh)
|
129
|
+
|
130
|
+
services_to_run = self.instantiate_services!(:level => priority, :ids => runnable_ids)
|
131
|
+
|
132
|
+
if services_to_run.empty?
|
133
|
+
force_refresh = false
|
134
|
+
next
|
135
|
+
end
|
136
|
+
|
131
137
|
ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
|
138
|
+
force_refresh = true
|
132
139
|
end
|
133
140
|
rescue Exception => e
|
134
141
|
# We are divorced from any HTTP request at this point, and may not
|
@@ -154,90 +161,116 @@ class Collection
|
|
154
161
|
end
|
155
162
|
end
|
156
163
|
|
157
|
-
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
(Time.now - ds.updated_at) > self.requeue_failedtemporary_services_in
|
199
|
-
)
|
200
|
-
)
|
201
|
-
|
202
|
-
# Need to expire. Delete all the service responses, and
|
203
|
-
# the DispatchedService record, and service will be automatically
|
204
|
-
# run again.
|
205
|
-
serv_id = ds.service_id
|
206
|
-
|
207
|
-
umlaut_request.service_responses.each do |response|
|
208
|
-
if response.service_id == serv_id
|
209
|
-
umlaut_request.service_responses.delete(response)
|
210
|
-
response.destroy
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
umlaut_request.dispatched_services.destroy(ds)
|
164
|
+
# Goes through existing DispatchedService objects, and freshens them up:
|
165
|
+
# * If a service is marked in progress longer than timeout, mark
|
166
|
+
# it failed temporary.
|
167
|
+
# * If an existing failed temporary is older than our resurrection time,
|
168
|
+
# delete the dispatch (and all it's responses), so it can be re-queued.
|
169
|
+
def freshen_dispatches!
|
170
|
+
umlaut_request.dispatched_services.each do | ds |
|
171
|
+
# go through dispatched_services and set still in progress but too long to failed temporary
|
172
|
+
if ( (ds.status == DispatchedService::InProgress ||
|
173
|
+
ds.status == DispatchedService::Queued ) &&
|
174
|
+
(Time.now - ds.updated_at) > self.background_service_timeout)
|
175
|
+
|
176
|
+
ds.store_exception( Exception.new("background service timed out (took longer than #{self.background_service_timeout} to run); thread assumed dead.")) unless ds.exception_info
|
177
|
+
# Fail it temporary, it'll be run again.
|
178
|
+
ds.status = DispatchedService::FailedTemporary
|
179
|
+
ds.save!
|
180
|
+
logger.warn("Background service timed out, thread assumed dead. #{umlaut_request.id} / #{ds.service_id}")
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
# go through dispatched_services and delete:
|
186
|
+
# 1) old completed dispatches, too old to use.
|
187
|
+
# 2) failedtemporary dispatches that are older than our resurrection time
|
188
|
+
# -> And all responses associated with those dispatches.
|
189
|
+
# After being deleted, they'll end up re-queued.
|
190
|
+
if ( (ds.completed? && completed_dispatch_expired?(ds) ) ||
|
191
|
+
( ds.status == DispatchedService::FailedTemporary &&
|
192
|
+
(Time.now - ds.updated_at) > self.requeue_failedtemporary_services_in
|
193
|
+
)
|
194
|
+
)
|
195
|
+
|
196
|
+
# Need to expire. Delete all the service responses, and
|
197
|
+
# the DispatchedService record, and service will be automatically
|
198
|
+
# run again.
|
199
|
+
serv_id = ds.service_id
|
200
|
+
|
201
|
+
umlaut_request.service_responses.each do |response|
|
202
|
+
if response.service_id == serv_id
|
203
|
+
umlaut_request.service_responses.delete(response)
|
204
|
+
response.destroy
|
215
205
|
end
|
206
|
+
end
|
207
|
+
|
208
|
+
umlaut_request.dispatched_services.destroy(ds)
|
216
209
|
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# For all configured services, if they have NO DispatchedService
|
214
|
+
# object, then create one with status Queued
|
215
|
+
def mark_queued_if_empty!
|
216
|
+
our_service_ids = self.get_service_definitions.collect {|d| d["service_id"]}
|
217
|
+
|
218
|
+
existing_dispatches = umlaut_request.dispatched_services.collect {|d| d.service_id}
|
219
|
+
|
220
|
+
not_yet_existing = our_service_ids - existing_dispatches
|
217
221
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
222
|
+
not_yet_existing.each do |service_id|
|
223
|
+
umlaut_request.new_dispatch_object!(service_id, DispatchedService::Queued).save!
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# All services for priority that are marked Queued, so long as
|
228
|
+
# no previous waves are still marked running.
|
229
|
+
#
|
230
|
+
# Pass `:refresh => true` as second argument to force trip
|
231
|
+
# to the database to get fresh DispatchedService objects.
|
232
|
+
#
|
233
|
+
# Returns array of service_id's, or empty array.
|
234
|
+
def runnable_services_for_priority(priority, options = {})
|
235
|
+
DispatchedService.connection_pool.with_connection do
|
236
|
+
service_definitions = self.get_service_definitions
|
237
|
+
|
238
|
+
# Make a hash where key is service id, and value is priority.to_s
|
239
|
+
service_to_priority = Hash[
|
240
|
+
service_definitions.collect do |d|
|
241
|
+
[ d["service_id"], d["priority"].to_s ]
|
234
242
|
end
|
243
|
+
]
|
244
|
+
|
245
|
+
if options[:refresh]
|
246
|
+
# force a refresh
|
247
|
+
umlaut_request.dispatched_services(true)
|
235
248
|
end
|
236
|
-
end
|
237
249
|
|
238
|
-
|
250
|
+
# If there is any service earlier than this wave still marked InProgress,
|
251
|
+
# we're not ready to run this wave, return empty array.
|
252
|
+
# Important to avoid race condition on HTTP requests, don't
|
253
|
+
# dispatch later background waves unless earlier are actually complete,
|
254
|
+
# even on an HTTP status check.
|
255
|
+
previous_waves_running = umlaut_request.dispatched_services.find do |ds|
|
256
|
+
ds.status == DispatchedService::InProgress &&
|
257
|
+
service_to_priority[ ds.service_id ] < priority.to_s
|
258
|
+
end.present?
|
259
|
+
return [] if previous_waves_running
|
260
|
+
|
261
|
+
# otherwise, the services for this priority are runnable if
|
262
|
+
# they are already marked Queued
|
263
|
+
# We use .to_a, we want to use the already in memory array, not
|
264
|
+
# go to the db here.
|
265
|
+
return umlaut_request.dispatched_services.to_a.find_all do |ds|
|
266
|
+
ds.status == DispatchedService::Queued &&
|
267
|
+
service_to_priority[ ds.service_id ] == priority.to_s
|
268
|
+
end.collect {|ds| ds.service_id}
|
269
|
+
end
|
239
270
|
end
|
240
271
|
|
272
|
+
|
273
|
+
|
241
274
|
def completed_dispatch_expired?(ds)
|
242
275
|
interval = self.response_expire_interval
|
243
276
|
crontab = self.response_expire_crontab_format
|
data/app/models/referent.rb
CHANGED
@@ -390,11 +390,17 @@ class Referent < ActiveRecord::Base
|
|
390
390
|
|
391
391
|
# options => { :overwrite => false } to only enhance if not already there
|
392
392
|
def enhance_referent(key, value, metadata=true, private_data=false, options = {})
|
393
|
+
|
394
|
+
|
393
395
|
ActiveRecord::Base.connection_pool.with_connection do
|
394
396
|
return if value.nil?
|
395
397
|
|
396
398
|
matches = self.referent_values.to_a.find_all do |rv|
|
397
|
-
|
399
|
+
# We ignore #metadata and #private_data matches in overwriting
|
400
|
+
# existing value. We used to take them into account, but it triggered
|
401
|
+
# a bug in Jruby, and pretty much isn't neccesary, those fields
|
402
|
+
# are pretty useless and mostly not used and should prob be removed.
|
403
|
+
(rv.key_name == key) # && (rv.metadata == metadata) && (rv.private_data == private_data)
|
398
404
|
end
|
399
405
|
|
400
406
|
matches.each do |rv|
|
data/app/models/request.rb
CHANGED
@@ -387,6 +387,25 @@ class Request < ActiveRecord::Base
|
|
387
387
|
self.dispatched_services << ds
|
388
388
|
return ds
|
389
389
|
end
|
390
|
+
|
391
|
+
# Returns an array of 0 or more ServiceDispatch objects matching
|
392
|
+
# specified conditions. Right now only one condition is supported:
|
393
|
+
#
|
394
|
+
# dispatch_objects_with(:service_type_values => values)
|
395
|
+
# values can be one or more string names of service types, returns
|
396
|
+
# DispatchedServices for services whose generated values include
|
397
|
+
# one or more of what you specified.
|
398
|
+
def dispatch_objects_with(options = {})
|
399
|
+
value_names = Array(options[:service_type_values])
|
400
|
+
|
401
|
+
raise ArgumentError.new("Need to supply a :service_type_values argument") unless value_names.present?
|
402
|
+
|
403
|
+
list = self.dispatched_services.to_a.find_all do |ds|
|
404
|
+
(value_names & ds.service.service_types_generated.collect(&:name)).present?
|
405
|
+
end
|
406
|
+
|
407
|
+
return list
|
408
|
+
end
|
390
409
|
|
391
410
|
protected
|
392
411
|
|
@@ -465,6 +484,8 @@ class Request < ActiveRecord::Base
|
|
465
484
|
return self.dispatched_services.where(:service_id => service.service_id).first
|
466
485
|
end
|
467
486
|
|
487
|
+
|
488
|
+
|
468
489
|
# Input is a CGI::parse style of HTTP params (array values)
|
469
490
|
# output is a string "fingerprint" canonically representing the input
|
470
491
|
# params, which can be stored in the db, so that when another request
|
data/app/models/service_store.rb
CHANGED
@@ -33,12 +33,19 @@ class ServiceStore
|
|
33
33
|
end
|
34
34
|
|
35
35
|
|
36
|
-
# Returns complete hash loaded from
|
36
|
+
# Returns complete hash loaded from config/umlaut_services.yml
|
37
|
+
# Passes through ERB first, allowing ERB in umlaut_services.yml
|
37
38
|
def config
|
38
39
|
# cache hash loaded from YAML, ensure it has the keys we expect.
|
39
40
|
unless defined? @services_config_list
|
40
41
|
yaml_path = File.expand_path("config/umlaut_services.yml", Rails.root)
|
41
|
-
|
42
|
+
|
43
|
+
@services_config_list = if File.exists? yaml_path
|
44
|
+
YAML::load(ERB.new(File.open(yaml_path).read).result)
|
45
|
+
else
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
|
42
49
|
@services_config_list["default"] ||= {}
|
43
50
|
@services_config_list["default"]["services"] ||= {}
|
44
51
|
end
|
@@ -120,13 +127,19 @@ class ServiceStore
|
|
120
127
|
# pass in string unique key OR a service definition hash,
|
121
128
|
# and a current UmlautRequest.
|
122
129
|
# get back instantiated Service object.
|
130
|
+
#
|
131
|
+
# If string service_id is passed in, but is not defined in application services,
|
132
|
+
# a ServiceStore::NoSuchService exception will be raised.
|
123
133
|
def instantiate_service!(service, request)
|
124
134
|
definition = service.kind_of?(Hash) ? service : service_definition_for(service.to_s)
|
125
|
-
raise "Service '#{service}'' does not exist in umlaut-services.yml" if definition.nil?
|
135
|
+
raise NoSuchService.new("Service '#{service}'' does not exist in umlaut-services.yml") if definition.nil?
|
126
136
|
className = definition["type"] || definition["service_id"]
|
127
137
|
classConst = Kernel.const_get(className)
|
128
138
|
service = classConst.new(definition)
|
129
139
|
service.request = request
|
130
140
|
return service
|
131
141
|
end
|
142
|
+
|
143
|
+
class NoSuchService < RuntimeError ; end
|
144
|
+
|
132
145
|
end
|
@@ -236,7 +236,6 @@
|
|
236
236
|
# {:div_id => "search_inside", :partial => "search_inside", :show_partial_only => true}
|
237
237
|
class SectionRenderer
|
238
238
|
include ActionView::Helpers::TagHelper
|
239
|
-
@@bg_update_sections = @@partial_update_sections = nil
|
240
239
|
|
241
240
|
# First argument is the current umlaut Request object.
|
242
241
|
# Second argument is a session description hash. See class overview
|
@@ -266,12 +265,12 @@ class SectionRenderer
|
|
266
265
|
def initialize(a_umlaut_request, section_def = {})
|
267
266
|
@umlaut_request = a_umlaut_request
|
268
267
|
|
269
|
-
@
|
270
|
-
raise Exception.new("SectionRenderer needs
|
268
|
+
@div_id = section_def[:div_id] || section_def[:id]
|
269
|
+
raise Exception.new("SectionRenderer needs a :div_id passed in arguments hash") unless @div_id
|
270
|
+
|
271
271
|
|
272
272
|
# Merge in default arguments for this section from config.
|
273
273
|
construct_options(section_def)
|
274
|
-
|
275
274
|
end
|
276
275
|
|
277
276
|
# Returns all ServiceTypeValue objects contained in this section, as
|
@@ -319,7 +318,7 @@ class SectionRenderer
|
|
319
318
|
end
|
320
319
|
|
321
320
|
def div_id
|
322
|
-
return @
|
321
|
+
return @div_id
|
323
322
|
end
|
324
323
|
|
325
324
|
def show_heading?
|
@@ -341,7 +340,7 @@ class SectionRenderer
|
|
341
340
|
|
342
341
|
{ :partial => "background_progress",
|
343
342
|
:locals =>{ :svc_types => service_type_values,
|
344
|
-
:div_id => "progress_#{
|
343
|
+
:div_id => "progress_#{self.div_id}",
|
345
344
|
:current_set_empty => responses_empty?,
|
346
345
|
:item_name => custom_item_name
|
347
346
|
}
|
@@ -404,7 +403,7 @@ class SectionRenderer
|
|
404
403
|
when :complete_with_responses
|
405
404
|
(! responses.empty?) && ! (services_in_progress?)
|
406
405
|
when Proc
|
407
|
-
# It's a lambda, which takes
|
406
|
+
# It's a lambda, which takes this SectionRenderer as an arg
|
408
407
|
@options[:visibility].call(self)
|
409
408
|
else true
|
410
409
|
end
|
@@ -504,20 +503,20 @@ class SectionRenderer
|
|
504
503
|
|
505
504
|
|
506
505
|
# service type value default to same name as section_id
|
507
|
-
@options[:service_type_values] ||= [
|
506
|
+
@options[:service_type_values] ||= [self.div_id]
|
508
507
|
|
509
508
|
# Partials to display. Default to _standard_response_item item partial.
|
510
509
|
if ( @options[:partial] == true)
|
511
|
-
@options[:partial] =
|
512
|
-
end
|
513
|
-
if (@options[:partial].blank?)
|
514
|
-
@options[:item_partial] =
|
515
|
-
case @options[:item_partial]
|
516
|
-
when true then @section_id + "_item"
|
517
|
-
when String then options[:item_partial]
|
518
|
-
else "standard_response_item"
|
519
|
-
end
|
510
|
+
@options[:partial] = self.div_id
|
520
511
|
end
|
512
|
+
|
513
|
+
@options[:item_partial] =
|
514
|
+
case @options[:item_partial]
|
515
|
+
when true then self.div_id + "_item"
|
516
|
+
when String then options[:item_partial]
|
517
|
+
else "standard_response_item"
|
518
|
+
end
|
519
|
+
|
521
520
|
|
522
521
|
# sanity check
|
523
522
|
if ( @options[:show_partial_only] && ! @options[:partial])
|
@@ -90,6 +90,7 @@ class Blacklight < Service
|
|
90
90
|
holdings_url = blacklight_precise_search_url( request, "dlf_expanded" )
|
91
91
|
holdings_added += add_holdings( holdings_url ) if holdings_url
|
92
92
|
end
|
93
|
+
|
93
94
|
#keyword search.
|
94
95
|
if (@keyword_search &&
|
95
96
|
url = blacklight_keyword_search_url(request))
|
@@ -210,8 +211,8 @@ class Blacklight < Service
|
|
210
211
|
# phrase search for title, just raw dismax for author
|
211
212
|
# Embed quotes inside the quoted value, need to backslash-quote for CQL,
|
212
213
|
# and backslash the backslashes for ruby literal.
|
213
|
-
clauses.push("#{@bl_fields["title"]} = \"\\\"#{
|
214
|
-
clauses.push("#{@bl_fields["author"]} = \"#{
|
214
|
+
clauses.push("#{@bl_fields["title"]} = \"\\\"#{escape_for_cql_double_quotes title}\\\"\"")
|
215
|
+
clauses.push("#{@bl_fields["author"]} = \"#{escape_for_cql_double_quotes author}\"") if author
|
215
216
|
|
216
217
|
url = base_url + "?search_field=#{@cql_search_field}&content_format=#{options[:content_format]}&q=#{CGI.escape(clauses.join(" AND "))}"
|
217
218
|
|
@@ -228,8 +229,14 @@ class Blacklight < Service
|
|
228
229
|
# error if we do nothing. Can we escape it somehow? CQL is really
|
229
230
|
# unclear, we're ALREADY backslash escaping the phrase quotes themselves!
|
230
231
|
# We just replace them with space, should work for our actual indexing.
|
231
|
-
|
232
|
-
|
232
|
+
#
|
233
|
+
# Single quotes (apostrophes) need to be escaped with an apostrophe itself,
|
234
|
+
# `''`, apparently. http://mail-archives.apache.org/mod_mbox/cassandra-user/201108.mbox/%3C20110803152250.294300@gmx.net%3E
|
235
|
+
def escape_for_cql_double_quotes(str)
|
236
|
+
str = str.gsub('"', " ")
|
237
|
+
str = str.gsub("'", "''")
|
238
|
+
|
239
|
+
return str
|
233
240
|
end
|
234
241
|
|
235
242
|
|