umlaut 3.0.0alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (293) hide show
  1. data/LICENSE +7 -0
  2. data/README.md +49 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/images/error.gif +0 -0
  5. data/app/assets/images/export_bg_bot.gif +0 -0
  6. data/app/assets/images/export_bg_mid.gif +0 -0
  7. data/app/assets/images/export_bg_top.gif +0 -0
  8. data/app/assets/images/famfamfam/book_open.png +0 -0
  9. data/app/assets/images/famfamfam/cross.png +0 -0
  10. data/app/assets/images/famfamfam/page_sound.gif +0 -0
  11. data/app/assets/images/famfamfam/page_text.gif +0 -0
  12. data/app/assets/images/famfamfam/page_up.gif +0 -0
  13. data/app/assets/images/famfamfam/page_white.png +0 -0
  14. data/app/assets/images/famfamfam/readme.html +1495 -0
  15. data/app/assets/images/famfamfam/tiny_cross.png +0 -0
  16. data/app/assets/images/frame_remove.gif +0 -0
  17. data/app/assets/images/ico_go.gif +0 -0
  18. data/app/assets/images/jhu_findit.gif +0 -0
  19. data/app/assets/images/list_closed.png +0 -0
  20. data/app/assets/images/list_open.png +0 -0
  21. data/app/assets/images/more_info.gif +0 -0
  22. data/app/assets/images/rails.png +0 -0
  23. data/app/assets/images/request.gif +0 -0
  24. data/app/assets/images/spinner.gif +0 -0
  25. data/app/assets/javascripts/umlaut/ajax_windows.js +35 -0
  26. data/app/assets/javascripts/umlaut/ensure_window_size.js.erb +34 -0
  27. data/app/assets/javascripts/umlaut/expand_contract_toggle.js +25 -0
  28. data/app/assets/javascripts/umlaut/search_autocomplete.js +46 -0
  29. data/app/assets/javascripts/umlaut/simple_visible_toggle.js +8 -0
  30. data/app/assets/javascripts/umlaut/update_html.js +152 -0
  31. data/app/assets/javascripts/umlaut.js +17 -0
  32. data/app/assets/stylesheets/umlaut.css +857 -0
  33. data/app/controllers/application_controller.rb +14 -0
  34. data/app/controllers/export_email_controller.rb +123 -0
  35. data/app/controllers/js_helper_controller.rb +10 -0
  36. data/app/controllers/link_router_controller.rb +87 -0
  37. data/app/controllers/open_search_controller.rb +9 -0
  38. data/app/controllers/resolve_controller.rb +288 -0
  39. data/app/controllers/resource_controller.rb +83 -0
  40. data/app/controllers/search_controller.rb +328 -0
  41. data/app/controllers/search_methods/sfx3.rb +148 -0
  42. data/app/controllers/search_methods/sfx4.rb +257 -0
  43. data/app/controllers/search_methods/sfx_api.rb +47 -0
  44. data/app/controllers/store_controller.rb +64 -0
  45. data/app/controllers/umlaut/controller_behavior.rb +20 -0
  46. data/app/controllers/umlaut/controller_logic.rb +96 -0
  47. data/app/controllers/umlaut/error_handling.rb +48 -0
  48. data/app/controllers/umlaut_controller.rb +112 -0
  49. data/app/helpers/application_helper.rb +4 -0
  50. data/app/helpers/emailer_helper.rb +43 -0
  51. data/app/helpers/export_email_helper.rb +34 -0
  52. data/app/helpers/open_search_helper.rb +7 -0
  53. data/app/helpers/resolve_helper.rb +225 -0
  54. data/app/helpers/search_helper.rb +50 -0
  55. data/app/helpers/umlaut/footer_helper.rb +64 -0
  56. data/app/helpers/umlaut/helper.rb +62 -0
  57. data/app/helpers/umlaut/html_head_helper.rb +37 -0
  58. data/app/helpers/umlaut/url_generation.rb +77 -0
  59. data/app/mailers/emailer.rb +48 -0
  60. data/app/models/clickthrough.rb +2 -0
  61. data/app/models/collection.rb +259 -0
  62. data/app/models/crossref_lookup.rb +2 -0
  63. data/app/models/dispatched_service.rb +58 -0
  64. data/app/models/permalink.rb +29 -0
  65. data/app/models/referent.rb +473 -0
  66. data/app/models/referent_value.rb +14 -0
  67. data/app/models/request.rb +449 -0
  68. data/app/models/service_response.rb +179 -0
  69. data/app/models/service_store.rb +59 -0
  70. data/app/models/service_type_value.rb +58 -0
  71. data/app/models/service_wave.rb +150 -0
  72. data/app/models/sfx_db/az_additional_title.rb +11 -0
  73. data/app/models/sfx_db/az_letter_group.rb +11 -0
  74. data/app/models/sfx_db/az_title.rb +38 -0
  75. data/app/models/sfx_db/az_title_v2.rb +34 -0
  76. data/app/models/sfx_db/isbn.rb +12 -0
  77. data/app/models/sfx_db/issn.rb +12 -0
  78. data/app/models/sfx_db/object.rb +35 -0
  79. data/app/models/sfx_db/object_portfolio.rb +6 -0
  80. data/app/models/sfx_db/publisher.rb +10 -0
  81. data/app/models/sfx_db/sfx_db_base.rb +54 -0
  82. data/app/models/sfx_db/target.rb +9 -0
  83. data/app/models/sfx_db/target_service.rb +10 -0
  84. data/app/models/sfx_db/title.rb +10 -0
  85. data/app/models/sfx_db.rb +10 -0
  86. data/app/models/sfx_url.rb +35 -0
  87. data/app/views/emailer/citation.text.erb +28 -0
  88. data/app/views/emailer/short_citation.text.erb +8 -0
  89. data/app/views/export_email/_email.html.erb +25 -0
  90. data/app/views/export_email/_send_email.html.erb +3 -0
  91. data/app/views/export_email/_send_txt.html.erb +3 -0
  92. data/app/views/export_email/_txt.html.erb +62 -0
  93. data/app/views/export_email/email.html.erb +3 -0
  94. data/app/views/export_email/send_email.html.erb +1 -0
  95. data/app/views/export_email/send_txt.html.erb +1 -0
  96. data/app/views/export_email/txt.html.erb +3 -0
  97. data/app/views/js_helper/loader.erb.js +13 -0
  98. data/app/views/layouts/umlaut.html.erb +52 -0
  99. data/app/views/open_search/index.html.erb +9 -0
  100. data/app/views/resolve/_api_in_progress.xml.erb +21 -0
  101. data/app/views/resolve/_background_progress.html.erb +51 -0
  102. data/app/views/resolve/_background_updater.html.erb +38 -0
  103. data/app/views/resolve/_citation.html.erb +87 -0
  104. data/app/views/resolve/_coins.html.erb +1 -0
  105. data/app/views/resolve/_compact_citation.html.erb +33 -0
  106. data/app/views/resolve/_cover_image.html.erb +35 -0
  107. data/app/views/resolve/_fulltext.html.erb +55 -0
  108. data/app/views/resolve/_help.html.erb +17 -0
  109. data/app/views/resolve/_holding.html.erb +91 -0
  110. data/app/views/resolve/_related_items.html.erb +35 -0
  111. data/app/views/resolve/_search_inside.html.erb +62 -0
  112. data/app/views/resolve/_section_display.html.erb +49 -0
  113. data/app/views/resolve/_service_errors.html.erb +29 -0
  114. data/app/views/resolve/_standard_response_item.html.erb +89 -0
  115. data/app/views/resolve/api.xml.builder +72 -0
  116. data/app/views/resolve/background_status.html.erb +26 -0
  117. data/app/views/resolve/index.html.erb +73 -0
  118. data/app/views/resolve/partial_html_sections.xml.erb +30 -0
  119. data/app/views/search/_a_to_z.html.erb +6 -0
  120. data/app/views/search/_citation.html.erb +94 -0
  121. data/app/views/search/_pager.html.erb +60 -0
  122. data/app/views/search/books.html.erb +103 -0
  123. data/app/views/search/journal_search.html.erb +90 -0
  124. data/app/views/search/journals.html.erb +167 -0
  125. data/app/views/search/opensearch_description.rxml +10 -0
  126. data/app/views/testing/index.html.erb +1 -0
  127. data/app/views/umlaut/README +5 -0
  128. data/app/views/umlaut/error.html.erb +45 -0
  129. data/db/migrate/01_umlaut_init.rb +113 -0
  130. data/db/orig_fixed_data/service_type_values.yml +120 -0
  131. data/db/seeds.rb +7 -0
  132. data/lib/CronTab.rb +192 -0
  133. data/lib/aws_product_sign.rb +146 -0
  134. data/lib/exlibris/aleph/patron.rb +64 -0
  135. data/lib/exlibris/aleph/record.rb +54 -0
  136. data/lib/exlibris/aleph/rest_api.rb +29 -0
  137. data/lib/exlibris/primo/holding.rb +192 -0
  138. data/lib/exlibris/primo/rsrc.rb +17 -0
  139. data/lib/exlibris/primo/searcher.rb +276 -0
  140. data/lib/exlibris/primo/source/aleph.rb +46 -0
  141. data/lib/exlibris/primo/source/distribution/nyu_aleph.rb +323 -0
  142. data/lib/exlibris/primo/toc.rb +17 -0
  143. data/lib/exlibris/primo_ws.rb +140 -0
  144. data/lib/generators/templates/umlaut_services.yml +237 -0
  145. data/lib/generators/umlaut/asset_hooks_generator.rb +44 -0
  146. data/lib/generators/umlaut/install_generator.rb +110 -0
  147. data/lib/hip3/bib.rb +291 -0
  148. data/lib/hip3/bib_searcher.rb +302 -0
  149. data/lib/hip3/custom_field_lookup.rb +44 -0
  150. data/lib/hip3/holding.rb +50 -0
  151. data/lib/hip3/item.rb +65 -0
  152. data/lib/hip3/receipt.rb +7 -0
  153. data/lib/hip3/serial_copy.rb +82 -0
  154. data/lib/holding.rb +32 -0
  155. data/lib/marc_helper.rb +254 -0
  156. data/lib/metadata_helper.rb +312 -0
  157. data/lib/opensearch_feed.rb +398 -0
  158. data/lib/opensearch_query.rb +98 -0
  159. data/lib/referent_filter.rb +16 -0
  160. data/lib/referent_filters/dissertation_catch.rb +45 -0
  161. data/lib/section_renderer.rb +503 -0
  162. data/lib/service.rb +336 -0
  163. data/lib/service_adaptors/ajax_export.rb +37 -0
  164. data/lib/service_adaptors/amazon.rb +412 -0
  165. data/lib/service_adaptors/blacklight.rb +327 -0
  166. data/lib/service_adaptors/book_finder.rb +40 -0
  167. data/lib/service_adaptors/bx.rb +51 -0
  168. data/lib/service_adaptors/cover_thing.rb +73 -0
  169. data/lib/service_adaptors/elsevier_cover.rb +57 -0
  170. data/lib/service_adaptors/email_export.rb +10 -0
  171. data/lib/service_adaptors/ezproxy.rb +171 -0
  172. data/lib/service_adaptors/google_book_search.rb +442 -0
  173. data/lib/service_adaptors/gpo.rb +124 -0
  174. data/lib/service_adaptors/hathi_trust.rb +308 -0
  175. data/lib/service_adaptors/hip3_service.rb +150 -0
  176. data/lib/service_adaptors/hip_holding_search.rb +237 -0
  177. data/lib/service_adaptors/internet_archive.rb +488 -0
  178. data/lib/service_adaptors/isbn_db.rb +86 -0
  179. data/lib/service_adaptors/isi.rb +258 -0
  180. data/lib/service_adaptors/jcr.rb +146 -0
  181. data/lib/service_adaptors/opac.rb +351 -0
  182. data/lib/service_adaptors/open_library.rb +316 -0
  183. data/lib/service_adaptors/open_library_cover.rb +73 -0
  184. data/lib/service_adaptors/primo_service.rb +392 -0
  185. data/lib/service_adaptors/primo_source.rb +78 -0
  186. data/lib/service_adaptors/pubmed.rb +133 -0
  187. data/lib/service_adaptors/request_to_fixture.rb +68 -0
  188. data/lib/service_adaptors/scopus.rb +295 -0
  189. data/lib/service_adaptors/sfx-new.rb +557 -0
  190. data/lib/service_adaptors/sfx.rb +566 -0
  191. data/lib/service_adaptors/sfx_backchannel_record.rb +69 -0
  192. data/lib/service_adaptors/txt_holding_export.rb +32 -0
  193. data/lib/service_adaptors/ulrichs_cover.rb +57 -0
  194. data/lib/service_adaptors/ulrichs_link.rb +47 -0
  195. data/lib/service_adaptors/worldcat.rb +116 -0
  196. data/lib/service_adaptors/worldcat_identities.rb +591 -0
  197. data/lib/tasks/umlaut.rake +134 -0
  198. data/lib/umlaut/default_configuration.rb +5 -0
  199. data/lib/umlaut/routes.rb +136 -0
  200. data/lib/umlaut/version.rb +3 -0
  201. data/lib/umlaut.rb +37 -0
  202. data/lib/umlaut_configurable.rb +343 -0
  203. data/lib/umlaut_http.rb +100 -0
  204. data/lib/xml_schema_helper.rb +109 -0
  205. data/test/dummy/Rakefile +7 -0
  206. data/test/dummy/app/assets/javascripts/application.js +13 -0
  207. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  208. data/test/dummy/app/controllers/application_controller.rb +3 -0
  209. data/test/dummy/app/controllers/umlaut_controller.rb +112 -0
  210. data/test/dummy/app/helpers/application_helper.rb +2 -0
  211. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  212. data/test/dummy/config/application.rb +45 -0
  213. data/test/dummy/config/boot.rb +10 -0
  214. data/test/dummy/config/database-jhu.yml +44 -0
  215. data/test/dummy/config/database.yml +25 -0
  216. data/test/dummy/config/environment.rb +5 -0
  217. data/test/dummy/config/environments/development.rb +34 -0
  218. data/test/dummy/config/environments/production.rb +60 -0
  219. data/test/dummy/config/environments/test.rb +39 -0
  220. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  221. data/test/dummy/config/initializers/inflections.rb +10 -0
  222. data/test/dummy/config/initializers/mime_types.rb +5 -0
  223. data/test/dummy/config/initializers/secret_token.rb +7 -0
  224. data/test/dummy/config/initializers/session_store.rb +8 -0
  225. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  226. data/test/dummy/config/locales/en.yml +5 -0
  227. data/test/dummy/config/routes.rb +61 -0
  228. data/test/dummy/config/umlaut_services.yml +237 -0
  229. data/test/dummy/config.ru +4 -0
  230. data/test/dummy/db/migrate/20111228211210_umlaut_init.rb +113 -0
  231. data/test/dummy/db/schema.rb +124 -0
  232. data/test/dummy/log/development.log +12981 -0
  233. data/test/dummy/log/production.log +0 -0
  234. data/test/dummy/public/404.html +26 -0
  235. data/test/dummy/public/422.html +26 -0
  236. data/test/dummy/public/500.html +26 -0
  237. data/test/dummy/public/favicon.ico +0 -0
  238. data/test/dummy/script/rails +6 -0
  239. data/test/dummy/tmp/cache/assets/C5F/340/sprockets%2F99692920160b7a279b86a80415b79db7 +0 -0
  240. data/test/dummy/tmp/cache/assets/C70/4D0/sprockets%2F034ad2036e623081bd352800786dfe80 +0 -0
  241. data/test/dummy/tmp/cache/assets/C73/920/sprockets%2Fd371318f22900492fd180f17c5e2a504 +9268 -0
  242. data/test/dummy/tmp/cache/assets/C80/980/sprockets%2Fc94807409c1523d43e18d25f35d93c41 +0 -0
  243. data/test/dummy/tmp/cache/assets/C8F/780/sprockets%2Fe47e28558116fb5f8038754e60d1961d +11769 -0
  244. data/test/dummy/tmp/cache/assets/CAA/EB0/sprockets%2F1d179210e8b76f1ea63c802688a015e4 +9271 -0
  245. data/test/dummy/tmp/cache/assets/CBB/9C0/sprockets%2F706f28923fb754cad04b9107c89986a1 +0 -0
  246. data/test/dummy/tmp/cache/assets/CBF/B60/sprockets%2F08ca89671549936265dcb673bf02e36f +0 -0
  247. data/test/dummy/tmp/cache/assets/CC9/9F0/sprockets%2F306166316e2cafd13c15e62b51a2339d +0 -0
  248. data/test/dummy/tmp/cache/assets/CF6/F20/sprockets%2F5b2ffa1103079dfd555197838f87a99f +0 -0
  249. data/test/dummy/tmp/cache/assets/CF7/2B0/sprockets%2F25a7c73655bd3598173b39d9f98bcd46 +862 -0
  250. data/test/dummy/tmp/cache/assets/CFE/080/sprockets%2F37fe9f4255baddbd549a659914929398 +0 -0
  251. data/test/dummy/tmp/cache/assets/D22/060/sprockets%2F9aec77b768e91a802d284271c58e2f7e +21357 -0
  252. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  253. data/test/dummy/tmp/cache/assets/D33/6D0/sprockets%2F500129c57f1146e556ec3aacd6cd38c1 +0 -0
  254. data/test/dummy/tmp/cache/assets/D33/FD0/sprockets%2F2ba0b4e6334a77b923e5f770381bb2bf +0 -0
  255. data/test/dummy/tmp/cache/assets/D42/C20/sprockets%2Fbcf14e437b1582bf93b77670acf8e090 +21353 -0
  256. data/test/dummy/tmp/cache/assets/D50/A30/sprockets%2F7d8b294ac433db5d056538f8cf7c66b9 +0 -0
  257. data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +872 -0
  258. data/test/dummy/tmp/cache/assets/D65/590/sprockets%2Fc1bb92fc3406a126b7dd302edc96d629 +0 -0
  259. data/test/dummy/tmp/cache/assets/D71/6B0/sprockets%2Fde558b71b494cf09b1bf055c8dff0353 +0 -0
  260. data/test/dummy/tmp/cache/assets/D72/610/sprockets%2Fa8c708eeb30ef93de34d755d4f45d023 +859 -0
  261. data/test/dummy/tmp/cache/assets/D76/AD0/sprockets%2Fe2158cde93188cf5ab6457bc6d6602ec +0 -0
  262. data/test/dummy/tmp/cache/assets/D7A/E40/sprockets%2F9622ffcc499a57627cd1bb18fe31b8e4 +11772 -0
  263. data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
  264. data/test/dummy/tmp/cache/assets/D9B/770/sprockets%2F8aacf02eb7dbb0949704b28f27b87e0b +0 -0
  265. data/test/dummy/tmp/cache/assets/DA6/A80/sprockets%2F92e26d8e58d5bcc8b8f6c25d1b05b9c1 +0 -0
  266. data/test/dummy/tmp/cache/assets/DE8/790/sprockets%2Fd1333bde2b9aafcc712d11dd09ab35d8 +0 -0
  267. data/test/dummy/tmp/cache/assets/DF7/F30/sprockets%2F7bc16c4109b17fabe29f8ddbbf732d1c +374 -0
  268. data/test/dummy/tmp/cache/assets/E03/570/sprockets%2F493bdc0ac14cd4f57fdfe4253f992bde +0 -0
  269. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  270. data/test/dummy/tmp/cache/assets/E0B/4B0/sprockets%2F7988df51a61c81ce6ede4a2d4c8cce4f +377 -0
  271. data/test/dummy/tmp/cache/assets/E5F/960/sprockets%2Fdc007b6cad5c7ef08e33ec28cfff0ef6 +0 -0
  272. data/test/fixtures/dispatched_services.yml +5 -0
  273. data/test/fixtures/permalinks.yml +5 -0
  274. data/test/fixtures/referent_values.yml +1734 -0
  275. data/test/fixtures/referents.yml +156 -0
  276. data/test/fixtures/requests.yml +284 -0
  277. data/test/fixtures/service_responses.yml +5 -0
  278. data/test/fixtures/sfx_urls.yml +4 -0
  279. data/test/performance/browsing_test.rb +9 -0
  280. data/test/test_helper.rb +10 -0
  281. data/test/umlaut_test.rb +7 -0
  282. data/test/unit/aleph_patron_test.rb +39 -0
  283. data/test/unit/aleph_record_benchmarks.rb +28 -0
  284. data/test/unit/aleph_record_test.rb +30 -0
  285. data/test/unit/aws_product_sign_test.rb +93 -0
  286. data/test/unit/collection_test.rb +76 -0
  287. data/test/unit/google_book_search_test.rb +101 -0
  288. data/test/unit/primo_searcher_test.rb +403 -0
  289. data/test/unit/primo_service_test.rb +939 -0
  290. data/test/unit/primo_ws_test.rb +131 -0
  291. data/test/unit/service_response_test.rb +9 -0
  292. data/test/unit/service_test.rb +33 -0
  293. metadata +580 -0
@@ -0,0 +1,179 @@
1
+ =begin rdoc
2
+
3
+ A ServiceResponse is a piece of data generated by a Service. It usually will
4
+ be displayed on the resolve menu.
5
+
6
+ ServiceResponses have a service type, represented by a string. When displaying, ServiceResponses are typically grouped into lists by service type. ServiceResponses are tied to the Service that created them, with the #service accessor.
7
+
8
+ ServiceResponses have a few basic attributes stored in columns in the db: 'display_text' is the text to put in the hyperlink. 'notes' is available for longer explanatory text (\n in notes will be converted to <br> by view). 'url' can be used to store the url to link to (but see below on linking mechanism).
9
+
10
+ [The legacy columns response_key, value_string, value_alt_string and value_text are deprecated and should not be used, but some legacy Services still use them, so they're still there for now].
11
+
12
+ In addition, there's a Hash (automatically serialized by ActiveRecord) that's stored in service_data, for arbitrary additional data that a Service can store--whatever you want, just put it in. However, there are conventions that Views expect, see below. You can access ALL the arbitrary key/values in a ServiceResponse, built-in in attributes or from the serialized Hash, by the proxy object returned from #data_values.
13
+
14
+ ServiceResponse is connected to a Request via the ServiceType join table. The data architecture allows a ServiceResponse to be tied to multiple requests, perhaps to support some kind of cacheing re-use in the future. But at present, the code doesn't do this, a ServiceResponse will really only be related to one request. However, a ServiceResponse can be related to a single Request more than once--once per each type of service response. ServiceType is really a three way join, representing a ServiceResponse, attached to a particular Request, with a particular ServiceTypeValue.
15
+
16
+
17
+ == View Display of ServiceResponse
18
+
19
+ The resolve menu View expects a Hash (or Hash-like) object with certain conventional keys, to display a particular ServiceResponse. You can provide code in your Service to translate a ServiceResponse to a Hash. But you often don't need to, you can use the proxy object returned by #data_values instead, which provides hash-like access to all arbitrary key/values stored in ServiceResponse. If the Service stores properties in there using conventional keys (see below), no further translation is needed.
20
+
21
+ However, if you need to do further translation you can implement methods on the Service, of the form: "to_[service type string](response)", for instance "to_fulltext". Umlaut will give it a ServiceResponse object, method should return a hash (or hash-like obj). Service can also implement a method response_to_view_data(response), as a 'default' translation. This mechanism of various possible 'translations' is implemented by Service#view_data_from_service_type.
22
+
23
+ == Url generation
24
+
25
+ At the point the user clicks on a ServiceResponse, Umlaut will attempt to find a url for the ServiceResponse, by calling response_url(response) on the relevant Service. The default implementation in service.rb just returns service_response['url'], so the easiest way to do this is just to put the url in service_response['url']. However, your Service can over-ride this method to provide it's own implementation to generate to generate the url on demand in any way it wants. If it does this, technically service_response['url'] doesn't need to include anything. But if you have a URL, you may still want to put it there, for Umlaut to use in guessing something about the destination, for de-duplication and possibly other future purposes.
26
+
27
+ = Conventional keys:
28
+
29
+ Absolute minimum:
30
+ [:display_text] Text that will be used
31
+
32
+ Basic set (used by fulltext and often others)
33
+ [:display_text]
34
+ [:notes] (newlines converted to <br>)
35
+ [:coverage]
36
+ [:authentication]
37
+ [:match_reliability] => One of MatchExact or MatchUnsure (maybe more later), for whether there's a chance this is an alternate Edition or the wrong work entirely. These are fuzzy of neccisity -- if it MIGHT be an alt edition, use MatchAltEdition even if you can't be sure it's NOT an exact match.
38
+ :edition_str => String statement of edition or work to let the user disambiguate and see if it's what they want. Can be taken for instance from Marc 260. Generally only displayed when match_reliabilty is not MatchExact. If no value, Umlaut treats as MatchExact.
39
+
40
+ == Full text specific
41
+ These are applicable only when the incoming OpenURL is an article-level citation. Umlaut uses Request#title_level_citation? to estimate this.
42
+
43
+ [:coverage_checked] boolean, default true. False for links from, eg, the catalog, where we weren't able to pre-check if the particular citation is included at this link.
44
+ [:can_link_to_article] boolean, default true. False if the links is _known_ not to deliver user to actual article requested, but just to a title-level page. Even though SFX links sometimes incorrectly do this, they are still not set to false here.
45
+
46
+ == highlighted_link (see also)
47
+ [:source] (optional, otherwise service's display_name is used)
48
+
49
+ == Holdings set adds:
50
+ [:source_name]
51
+ [:call_number]
52
+ [:status]
53
+ [:request_url] a url to request the item. optional.
54
+ [:coverage_array] (Array of coverage strings.)
55
+ [:due_date]
56
+ [:collection_str]
57
+ [:location_str]
58
+
59
+ == search_inside
60
+ Has no additional conventional keys, but when calling it's url handling functionality, send it a url param query= with the users query. In the API, this means using the umlaut_passthrough_url, but adding a url parameter query on to it. This will redirect to the search results.
61
+
62
+ == Cover images:
63
+ [:display_text] set to desired alt text
64
+ [:url] src url to img
65
+ [:size] => 'small', 'medium', 'large' or 'extra-large'. Also set in :key
66
+
67
+ == Anything from amazon:
68
+ [:asin]
69
+
70
+ == Abstracts/Tocs:
71
+ Can be a link to, or actual content. Either way, should be set
72
+ up to link to source of content if possible. Basic set, plus:
73
+ [:content] actual content, if available.
74
+
75
+ =end
76
+ class ServiceResponse < ActiveRecord::Base
77
+ @@built_in_fields = [:display_text, :url, :notes, :response_key, :value_string, :value_alt_string, :value_text]
78
+ belongs_to :request
79
+ serialize :service_data
80
+ # This value is not stored in db, but is set temporarily so
81
+ # the http request params can easily be passed around with a response
82
+ # object.
83
+ attr_accessor :http_request_params
84
+
85
+ # Constants for 'match_reliability' value.
86
+ MatchExact = 'exact'
87
+ MatchUnsure = 'unsure'
88
+ #MatchAltEdition = 'edition'
89
+ #MatchAltWork = 'work'
90
+
91
+ def initialize(params = nil)
92
+ super(params)
93
+ self.service_data = {} unless self.service_data
94
+ end
95
+
96
+ # Instantiates and returns a new Service associated with this response.
97
+ def service
98
+ @service ||= ServiceStore.instantiate_service!( self.service_id, nil )
99
+ end
100
+
101
+ # Returns a hash or hash-like object with properties for the service response.
102
+ def view_data
103
+ self.service.view_data_from_service_type(self)
104
+ end
105
+
106
+ # Should take a ServiceTypeValue object, or symbol name of
107
+ # ServiceTypeValue object.
108
+ def service_type_value=(value)
109
+ value = ServiceTypeValue[value] unless value.kind_of?(ServiceTypeValue)
110
+ self.service_type_value_name = value.name
111
+ end
112
+ def service_type_value
113
+ ServiceTypeValue[self.service_type_value_name]
114
+ end
115
+
116
+
117
+
118
+ def take_key_values(hash)
119
+ # copy it, cause we're gonna modify it
120
+ hash = hash.clone
121
+ hash.each_pair do |key, value|
122
+ if ( self.class.built_in_fields.include?(key))
123
+ self.send(key.to_s + '=', value)
124
+ hash.delete(key)
125
+ end
126
+ end
127
+ # What's left is arbitrary key/values that go in service_data
128
+ init_service_data(hash)
129
+ end
130
+
131
+ def init_service_data(hash)
132
+ hash.each {|key, value| data_values[key] = value} if hash
133
+ end
134
+
135
+ def data_values
136
+ # Lazy load, and store a reference. Don't worry, ruby
137
+ # GC handles circular references no problem.
138
+ unless (@data_values_proxy)
139
+ @data_values_proxy = ServiceResponseDataValues.new(self)
140
+ end
141
+ return @data_values_proxy;
142
+ end
143
+
144
+ def self.built_in_fields
145
+ @@built_in_fields
146
+ end
147
+
148
+
149
+
150
+ end
151
+
152
+ # A proxy-like class, to provide hash-like access to all arbitrary
153
+ # key/value pairs stored in a ServiceResponse, whether they key/value
154
+ # is stored in an ActiveRecord attribute (#built_in_fields) or in
155
+ # the serialized hash in the service_data attribute.
156
+ # Symbols passed in will be 'normalized' to strings before being used as keys.
157
+ # So symbols and strings are interchangeable. Normally, keys should be symbols.
158
+ class ServiceResponseDataValues
159
+ def initialize(arg_service_response)
160
+ @service_response = arg_service_response
161
+ end
162
+
163
+ def [](key)
164
+ if ServiceResponse.built_in_fields.include?(key)
165
+ return @service_response.send(key)
166
+ else
167
+ return @service_response.service_data[key]
168
+ end
169
+ end
170
+
171
+ def []=(key, value)
172
+ if(ServiceResponse.built_in_fields.include?(key))
173
+ @service_response.send(key.to_s+'=', value)
174
+ else
175
+ @service_response.service_data[key] = value
176
+ end
177
+ end
178
+
179
+ end
@@ -0,0 +1,59 @@
1
+ # Loads Service definitions from Rails.root/config/umlaut_services.yml
2
+ # instantiates services from definitions, by id.
3
+ #
4
+ # It's terrible we need to do this globally like this, but
5
+ # too hard to back out of legacy design now.
6
+ class ServiceStore
7
+
8
+ # Returns complete hash loaded from services.yml
9
+ def self.config
10
+ # cache hash loaded from YAML, ensure it has the keys we expect.
11
+ unless defined? @@services_config_list
12
+ yaml_path = File.expand_path("config/umlaut_services.yml", Rails.root)
13
+ if File.exists? yaml_path
14
+ @@services_config_list = YAML::load(File.open( yaml_path ))
15
+ else
16
+ @@services_config_list = {}
17
+ end
18
+ @@services_config_list["default"] ||= {}
19
+ @@services_config_list["default"]["services"] ||= {}
20
+ end
21
+ return @@services_config_list
22
+ end
23
+
24
+ # Returns hash keyed by unique service name, value service definition
25
+ # hash.
26
+ def self.service_definitions
27
+ unless defined? @@service_definitions
28
+ @@service_definitions = {}
29
+ config.each_pair do |group_name, group|
30
+ @@service_definitions.merge!( group["services"] ) if group["services"]
31
+ end
32
+ end
33
+ return @@service_definitions
34
+ end
35
+
36
+ def self.service_definition_for(service_id)
37
+ return service_definitions[service_id]
38
+ end
39
+
40
+ # pass in string unique key OR a service definition hash,
41
+ # and a current UmlautRequest.
42
+ # get back instantiated Service object.
43
+ def self.instantiate_service!(service, request)
44
+ definition = service.kind_of?(Hash) ? service : service_definition_for(service.to_s)
45
+
46
+ if definition.nil?
47
+ raise "Service '#{service}'' does not exist in umlaut-services.yml"
48
+ end
49
+
50
+ className = definition["type"] || definition["service_id"]
51
+ classConst = Kernel.const_get(className)
52
+ service = classConst.new(definition)
53
+ service.request = request
54
+
55
+ return service
56
+ end
57
+
58
+
59
+ end
@@ -0,0 +1,58 @@
1
+ require 'yaml'
2
+
3
+
4
+ # This model is the actual list of valid service types. Not ActiveRecord,
5
+ # just load from config files into memory (loaded on file load).
6
+ #
7
+ # ServiceTypeValue's also have displable strings, stored in the
8
+ # display_name attribute, and possibly custom display_name_plural
9
+ class ServiceTypeValue
10
+ attr_accessor :name, :id, :display_name, :display_name_plural
11
+
12
+ class << self; attr_accessor :values; end
13
+
14
+ def initialize(hash)
15
+ hash.each_pair do |key, value|
16
+ self.send(key.to_s+"=", value)
17
+ end
18
+ end
19
+
20
+ def self.find(name)
21
+ load_values! if values.nil?
22
+ values[name.to_sym] or raise ArgumentError.new("No ServiceTypeValue found for #{name}")
23
+ end
24
+ def self.[](name)
25
+ find(name)
26
+ end
27
+
28
+ def display_name_pluralize
29
+ return self.display_name_plural || self.display_name.pluralize
30
+ end
31
+
32
+ @@distro_conf_file = File.join(Umlaut::Engine.root, "db", "orig_fixed_data", "service_type_values.yml")
33
+ @@local_conf_file = File.join(Rails.root, "config", "umlaut_service_type_values.yml")
34
+
35
+
36
+ # Loads from config files, distro and local, into memory.
37
+ def self.load_values!
38
+ # Load in starting set of ServiceTypeValue, merge in local defines.
39
+
40
+ service_type_values = YAML.load_file( @@distro_conf_file )
41
+ local_overrides = File.exists?( @@local_conf_file ) ?
42
+ YAML.load_file(@@local_conf_file) :
43
+ nil
44
+ # Merge in the params for each service type with possible
45
+ # existing params.
46
+ if ( local_overrides )
47
+ local_overrides.each do |name, params|
48
+ service_type_values[name] ||= {}
49
+ service_type_values[name].merge!( params )
50
+ end
51
+ end
52
+
53
+ self.values = {}
54
+ service_type_values.each_pair do |name, hash|
55
+ self.values[name.to_sym] = ServiceTypeValue.new(hash.merge(:name => name))
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,150 @@
1
+ # ServiceWave is basically responsible for multi-threaded execution of
2
+ # a list of services, all in the same priority. Generally it's only
3
+ # called by Collection, nobody else needs it directly.
4
+ #
5
+ class ServiceWave
6
+ attr_accessor :services
7
+ attr_accessor :priority_level
8
+
9
+ # Priority level is purely information, used for debug output.
10
+ def initialize(service_objects, priority_level = nil, config = UmlautController.umlaut_config)
11
+ @services = service_objects
12
+ @priority_level = priority_level
13
+
14
+ @log_timing = config.lookup!("log_service_timing", true)
15
+
16
+ # Don't forward exceptions, that'll interrupt other service processing.
17
+ # Catch the exception, record it in the dispatch table, done. May want
18
+ # to set this to true for debugging/development, but NOT for production.
19
+ @forward_exceptions = false
20
+ end
21
+
22
+ # Safe to call in a thread. Returns true or false depending on
23
+ # whether dispatch should proceed.
24
+ def prepare_dispatch!(request, service, session_id)
25
+ can_dispatch = false
26
+
27
+ ActiveRecord::Base.connection_pool.with_connection do
28
+ if request.can_dispatch?( service)
29
+ # Mark this service as in progress in the dispatch table.
30
+ request.dispatched( service, DispatchedService::InProgress )
31
+ # remember the rails session id.
32
+ service.session_id = session_id
33
+
34
+ can_dispatch = true
35
+ end
36
+ end
37
+ return can_dispatch
38
+ end
39
+
40
+ def handle(request, session_id)
41
+ return if (@services.nil? || @services.empty?)
42
+
43
+ bundle_start = Time.now
44
+ Rails.logger.info("Launching servicelevel #{@priority_level}, request #{request.id}") if @log_timing
45
+
46
+
47
+ threads = []
48
+ some_service_executed = false
49
+ @services.each do | service |
50
+ some_service_executed = true
51
+ local_request = nil
52
+
53
+ threads << Thread.new(request.id, service.clone) do | request_id, local_service |
54
+ # Deal with ruby's brain dead thread scheduling by setting
55
+ # bg threads to a lower priority so they don't interfere with fg
56
+ # threads.
57
+ Thread.current.priority = -1
58
+
59
+ # Save some things in thread local hash useful for debugging
60
+ Thread.current[:debug_name] = local_service.class.name
61
+ Thread.current[:service] = service
62
+
63
+ # Tell our AR extension not to allow implicit checkouts
64
+ ActiveRecord::Base.forbid_implicit_checkout_for_thread! if ActiveRecord::Base.respond_to?("forbid_implicit_checkout_for_thread!")
65
+ begin
66
+ service_start = Time.now
67
+
68
+ local_request = ActiveRecord::Base.connection_pool.with_connection do
69
+ # pre-load all relationships so no ActiveRecord activity will be
70
+ # needed later to see em.
71
+ Request.includes(:referent, :service_responses, :dispatched_services).find(request_id)
72
+ end
73
+
74
+
75
+ if prepare_dispatch!(local_request, local_service, session_id)
76
+ local_service.handle_wrapper(local_request)
77
+ else
78
+ Rails.logger.info("NOT launching service #{local_service.service_id}, level #{@priority_level}, request #{local_request.id}: not in runnable state") if @log_timing
79
+ end
80
+
81
+
82
+ rescue Exception => e
83
+ # We may not be able to access ActiveRecord because it may
84
+ # have been an AR connection error, perhaps out of connections
85
+ # in the pool. So log and record in non-AR ways.
86
+ # the code waiting on our thread will see exception
87
+ # reported in Thread local var, and log it AR if possible.
88
+
89
+
90
+ # Log it too, although experience shows it may never make it to the
91
+ # log for mysterious reasons.
92
+ Rails.logger.error("Threaded service raised exception. Service: #{service.service_id}, #{e}\n #{clean_backtrace(e).join("\n ")}")
93
+
94
+ # And stick it in a thread variable too
95
+ Thread.current[:exception] = e
96
+ ensure
97
+ Rails.logger.info("Completed service #{local_service.service_id}, level #{@priority_level}, request #{local_request && local_request.id}: in #{Time.now - service_start}.") if @log_timing
98
+ end
99
+ end
100
+ end
101
+
102
+ # Wait for all the threads to complete, if any.
103
+ threads.each do |aThread|
104
+ aThread.join
105
+ #aThread.kill # shouldn't be neccesary, but I'm paranoid
106
+
107
+
108
+
109
+ if aThread[:exception]
110
+ debugger
111
+ # mark it finished in the db, we should have a connection already, we're
112
+ # in main request thread.
113
+ begin
114
+ request.dispatched(aThread[:service], DispatchedService::FailedFatal, aThread[:exception])
115
+ rescue Exception => e
116
+ debugger
117
+ raise e
118
+ end
119
+ end
120
+
121
+ # Okay, raise if exception, if desired.
122
+ if ( aThread[:exception] && self.forward_exceptions? )
123
+ raise aThread[:exception]
124
+ end
125
+ end
126
+
127
+ threads.clear # more paranoia
128
+
129
+ # Clean up any leftover connections left open by threads.
130
+ #ActiveRecord::Base.clear_active_connections!
131
+ ActiveRecord::Base.connection_pool.clear_stale_cached_connections!
132
+ Rails.logger.info("Completed services level #{@priority_level}, request #{request.id}: in #{Time.now - bundle_start}") if some_service_executed && @log_timing
133
+ end
134
+
135
+ def forward_exceptions?
136
+ return @forward_exceptions
137
+ end
138
+ def forward_exceptions=(f)
139
+ @foward_excpetions = f
140
+ end
141
+
142
+ protected
143
+ def clean_backtrace(exception, *args)
144
+ defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
145
+ Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
146
+ exception.backtrace
147
+ end
148
+
149
+
150
+ end