umlaut 3.0.0alpha1

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.
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,16 @@
1
+ # Kind of analagous to SFX "source parser". Takes ContextObjects
2
+ # passed in, and filters/mutates them.
3
+ #
4
+ # specific subclasses in lib/context_object_filters
5
+ #
6
+ # configured to apply in environment.rb
7
+
8
+ class ReferentFilter
9
+
10
+ # input: Referent object
11
+ # will mutate/modify it.
12
+ def filter(referent)
13
+ # implement in subclass
14
+ end
15
+
16
+ end
@@ -0,0 +1,45 @@
1
+ class DissertationCatch < ReferentFilter
2
+ include MetadataHelper
3
+
4
+ @@da_issns = ['00959154', '00993123', '04194209', '04194217', '0420073X', '00993123', '10427279', '03076075']
5
+
6
+ # input: ropenurl ContextObject
7
+ # Is this a citation to a Dissertation Abstracts
8
+ # issn, or do we otherwise think it's a dissertation citation? Then change
9
+ # it to a dissertation citation.
10
+ def filter(referent)
11
+ issn = get_identifier(:urn, "issn", referent)
12
+
13
+ return unless issn
14
+
15
+ # normalize removing hyphen
16
+ issn.gsub!('-', '')
17
+
18
+ if ( @@da_issns.find { |i| i == issn } )
19
+ # || lc($jtitle) =~ /dissertation/i || lc($jtitle2) =~ /dissertation/i)
20
+
21
+ referent.enhance_referent("genre", "dissertation")
22
+
23
+ metadata = referent.metadata
24
+ # Reset it's title to the dissertation title
25
+ title = metadata['atitle'] || metadata['title']
26
+ referent.enhance_referent("btitle", title)
27
+ referent.enhance_referent("title", title, true, false, :overwrite => true)
28
+ # Now erase titles that do not apply
29
+ referent.remove_value("atitle")
30
+ referent.remove_value("jtitle")
31
+ referent.remove_value("stitle")
32
+ # issn or isbn are wrong, probably point to Dissertation Abstracts
33
+ referent.remove_value("issn")
34
+ referent.remove_value("isbn")
35
+ # Same with all article level metadata
36
+ referent.remove_value("volume")
37
+ referent.remove_value("issue")
38
+ referent.remove_value("issue_start")
39
+ referent.remove_value("spage")
40
+ referent.remove_value("epage")
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,503 @@
1
+ # = The Section Architecture
2
+ #
3
+ # Umlaut has what could be considered a 'domain specific language' for
4
+ # describing the display individual sections of content on the resolve menu
5
+ # page. These sections often correspond to a ServiceTypeValue, like "fulltext".
6
+ # But sometimes may include multiple ServiceTypeValues (eg related_items section
7
+ # includes cited_by and similar_items), or no ServiceTypeValue at all (eg
8
+ # section to display a COinS).
9
+ #
10
+ # A description of a section is simply a hash with certain conventional
11
+ # keys describing various aspects of the contents and display of that section.
12
+ # These hashes are listed in the resolve_sections application configuration
13
+ # variable, initialized in the resolve_views.rb initializer, and customized
14
+ # or over-ridden in the local resolve_views.rb initializer.
15
+ #
16
+ # One benefit of describing a section through configuration is that section
17
+ # display can often by changed at configure time without requiring a code
18
+ # time. Another is that the description of the section can be used not
19
+ # only to generate the initial HTML page; but also by the javascript that
20
+ # update the sections with new background content as available; and by the
21
+ # partial_html_sections api that delivers HTML fragments for sections in an
22
+ # XML or JSON container.
23
+ #
24
+ # A description of a section is simply a hash, suitable for passing to
25
+ # SectionRenderer.new, detailed below. Plus some additional variables
26
+ # specifying _where_ to display the section, documented in the resolve_views.rb
27
+ # initializer.
28
+ #
29
+ # = The SectionRenderer
30
+ # A SectionRenderer object provides logic for displaying a specific section
31
+ # on the Umlaut resolve menu page. It is initialized with a hash describing
32
+ # the details -- or significantly, with simply a pointer to such a hash
33
+ # already existing in the resolve_sections config variable.
34
+ #
35
+ # A SectionRenderer is typically created by the ResolveHelper#render_section
36
+ # method, which then passes the SectionRender object to the
37
+ # _section_display.erb.html that does the actual rendering, using
38
+ # the SectionRenderer for logic and hashes to pass to render calls in
39
+ # the partial.
40
+ #
41
+ #
42
+ # == Section Options
43
+ #
44
+ # Section options are typically configured in hashes in the application
45
+ # config variable resolve_sections, which is expected to be a list of hashes.
46
+ # That hash is suitable to be passed to a SectionRenderer.new() as configuration
47
+ # options for the section. The various ways these options can be used
48
+ # is documented below.
49
+ #
50
+ # === Simplest Case, Defaults
51
+ #
52
+ # As is common in ruby, SectionRenderer will make a lot of conventional
53
+ # assumptions, allowing you to be very concise for the basic simple case:
54
+ #
55
+ # { :div_id => "fulltext", :html_area => :main }
56
+ #
57
+ # This means that:
58
+ # * this section is assumed to be contained within a <div id="fulltext">. The
59
+ # div won't be automatically rendered, it's the containing pages
60
+ # responsibility to put in a div with this id.
61
+ #
62
+ # * this section is assumed to contain responses of type
63
+ # ServiceTypeValue["fulltext"]
64
+ #
65
+ # * The section will be displayed with stock heading block including a title
66
+ # constructed from the display_name of ServiceTypeValue["fulltext"], or
67
+ # in general the display_name of the first ServiceTypeValue included
68
+ # in this section.
69
+ #
70
+ # * The section will include a stock 'spinner' if there are potential background
71
+ # results being gathered for the ServiceTypeValue(s) contained.
72
+ #
73
+ # * The actual ServiceResponses collected for the ServiceTypeValue included
74
+ # will be rendered with a _standard_response_item
75
+ # partial, using render :collection.
76
+ #
77
+ # * The section will be displayed whether or not there are any actual
78
+ # responses included. If there are no responses, a message will be displayed
79
+ # to that effect.
80
+ #
81
+ # The display of a section can be customized via configuration parameters to
82
+ # a large degree, including supplying your own partial to take over almost
83
+ # all display of the section.
84
+ #
85
+ # === Customizing ServiceTypeValues
86
+ #
87
+ # You can specifically supply the ServiceTypeValues contained in this
88
+ # section, to a different type than would be guessed from the div_id:
89
+ #
90
+ # {:div_id => "my_area", :service_type_values => ["fulltext"]}
91
+ #
92
+ # Or specify multiple types included in one section:
93
+ #
94
+ # {:div_id => "related_items", :service_type_values => ['cited_by', 'similar]}
95
+ #
96
+ # Or a section that isn't used for displaying service responses at all,
97
+ # and has no service type:
98
+ #
99
+ # {:div_id => "coins", :partial => "coins", :service_type_values => []}
100
+ #
101
+ # Note that a custom partial needs to be supplied if there are no service_type_values supplied.
102
+ #
103
+ # === Customizing heading display
104
+ #
105
+ # You can supply a title for the section that's different than what would
106
+ # be guessed from it's ServiceTypeValues. You can also supply a prompt.
107
+ #
108
+ # {:div_id =>"excerpts", :section_title=>"Lots of good stuff", :section_prompt => "Limited previes and excerpts."}
109
+ #
110
+ # You can also suppress display of the stock section heading at all:
111
+ # {:show_heading => false, ...}
112
+ #
113
+ # This may be becuase you don't want a heading, or because you are supplying
114
+ # a custom partial that will take care of the heading in a custom way.
115
+ #
116
+ # === Customizing spinner display
117
+ #
118
+ # You can also suppress display of the stock spinner, because you don't
119
+ # want a spinner, or because your custom partial will be taking care of it.
120
+ # {:show_spinner => false, ...}
121
+ #
122
+ # By default, the spinner displays what type of thing it's waiting on, guessing
123
+ # from the ServiceTypeValue configured. If you want to specify this item name:
124
+ # {:item_name_plural => "Related Items", ...}
125
+ #
126
+ # === Customizing visibility of section
127
+ #
128
+ # By default, a section will simply be displayed regardless of whether
129
+ # there are any actual responses to display. However, the 'visibility'
130
+ # argument can be used to customize this in many ways.
131
+ # visibilty:
132
+ # [*true*]
133
+ # Default, always show section.
134
+ # [*false*]
135
+ # Never show section. (Not sure why you'd want this).
136
+ # [<b>:any_services</b>]
137
+ # Show section if and only if there are any configured
138
+ # services that generate the ServiceTypeValues included
139
+ # in this section, regardless of whether in this case
140
+ # they have or not.
141
+ # [<b>:in_progress</b>]
142
+ # Show the section if responses exist, OR if any services
143
+ # are currently in progress that are capable of generating
144
+ # responses of the right type for this section.
145
+ # [<b>:responses_exist</b>]
146
+ # Show the section if and only if some responses
147
+ # have actually been generated of the types contained
148
+ # in this section.
149
+ # [<b>:complete_with_responses</b>]
150
+ # Show the section only if there are responses
151
+ # generated, AND all services supplying
152
+ # responses of the type contained in section
153
+ # have completed, no more responses are possible.
154
+ # [<b>(lambda object)</b>]
155
+ # Most flexibly of all, you can supply your own lambda
156
+ # supplying custom logic to determine whether to show
157
+ # the section, based on current context. The lambda
158
+ # will be passed the SectionRenderer object as an argument,
159
+ # providing access to the Umlaut Request with context.
160
+ # eg:
161
+ # :visibility => lambda do |renderer|
162
+ # renderer.request.something == something
163
+ # end
164
+ #
165
+ # === List with limit
166
+ #
167
+ # You can have the section automatically use the ResolveHelper#list_with_limit
168
+ # helper to limit the number of items initially displayed, with the rest behind
169
+ # a 'more' expand/contract widget.
170
+ #
171
+ # { :div_id => "highlighted_link",
172
+ # :list_visible_limit => 1,
173
+ # :visibility => :in_progress, ... }
174
+ #
175
+ # === Custom partial display
176
+ #
177
+ # By default, the SectionRenderer assumes that all the ServiceResposnes included
178
+ # are capable of being displayed by the standard_item_response, and displays
179
+ # them simply by render standard_item_response with a \:colection. Sometimes
180
+ # this assumption isn't true, or you want custom display for other reasons.
181
+ # You can supply your own partial that the renderer will use to display
182
+ # the content.
183
+ #
184
+ # { :div_id => "my_div", :partial => "my_partial", ... }
185
+ #
186
+ # The partial so supplied should live in resolve/_my_partial.html.erb
187
+ #
188
+ # When this partial is called, it will have local variables set
189
+ # to give it the data it needs in order to create a display:
190
+ #
191
+ # [*responses_by_type*]
192
+ # a hash keyed by ServiceTypeValue name, with the
193
+ # the value being an array of the respective ServiceType
194
+ # objects.
195
+ # [*responses*] a flattened list of all ServiceTypes included in
196
+ # this section, of varying ServiceTypeValues. Most
197
+ # useful when the section only includes one
198
+ # ServiceTypeValue
199
+ # [*renderer*] The SectionRenderer object itself, from which
200
+ # the current umlaut request can be obtained,
201
+ # among other things.
202
+ #
203
+ # You can supply additional static local arguments to the partial
204
+ # in the SectionRenderer setup:
205
+ #
206
+ # {:div_id=> "foo", :partial=>"my_partial", :partial_locals => {:mode => "big"}, ... }
207
+ #
208
+ # the :partial_locals argument can be used with the standard_response_item
209
+ # too:
210
+ # {:div_id => "highlighted_link", :partial_locals => {:show_source => true}}
211
+ #
212
+ # Note that your custom partial will still be displayed with stock
213
+ # header and possibly spinner surrounding it. You can suppress these elements:
214
+ #
215
+ # {:div_id => "cover_image", :partial => "cover_image", :show_heading => false, :show_spinner => false}
216
+ #
217
+ # But even so, some 'wrapping' html is rendered surrounding your partial.
218
+ # If you want to disable even this, becuase your partial will take care of it
219
+ # itself, you can do so with \:show_partial_only => true
220
+ # {:div_id => "search_inside", :partial => "search_inside", :show_partial_only => true}
221
+ class SectionRenderer
222
+ include ActionView::Helpers::TagHelper
223
+ @@bg_update_sections = @@partial_update_sections = nil
224
+
225
+ # First argument is the current umlaut Request object.
226
+ # Second argument is a session description hash. See class overview
227
+ # for an overview. Recognized keys of session description hash:
228
+ # * [id] SessionRenderer will look up session description hash in
229
+ # resolve_views finding one with :div_id == id
230
+ # * [div_id] The id of the <div> the section lives in. Also used
231
+ # generally as unique ID for the section.
232
+ # * [service_type_values] ServiceTypeValue's that this section contains.
233
+ # defaults to [ServiceTypeValue[div_id]]
234
+ # * [section_title] Title for the section. Defaults to
235
+ # service_type_values.first.display_name
236
+ # * [section_prompt] Prompt. Default nil.
237
+ # * [show_heading] Show the heading section at all. Default true.
238
+ # * [show_spinner] Show a stock spinner for bg action for service_type_values.
239
+ # default true.
240
+ # * [item_name_plural] Pluralized name of the objects included, used in
241
+ # spinner message. Default
242
+ # service_type_values.first.display_name_pluralize
243
+ # * [visibilty] What logic to use to decide whether to show the section at
244
+ # all. true|false|:any_services|:in_progress|:responses_exist|:complete_with_responses|(lambda object)
245
+ # * [list_visible_limit] Use list_with_limit to limit initially displayed
246
+ # items to value. Default nil, meaning don't use
247
+ # list_with_limit.
248
+ # * [partial] Use a custom partial to display this section, instead of
249
+ # using render("standard_response_item", :collection => [all responses]) as default.
250
+ # * [show_partial_only] Display custom partial without any of the usual
251
+ # standardized wrapping HTML. Custom partial will
252
+ # take care of it itself.
253
+ def initialize(a_umlaut_request, section_def = {})
254
+ @umlaut_request = a_umlaut_request
255
+
256
+ @section_id = section_def[:id] || section_def[:div_id]
257
+ raise Exception.new("SectionRenderer needs an :id passed in arguments hash") unless @section_id
258
+
259
+ # Merge in default arguments for this section from config.
260
+ construct_options(section_def)
261
+
262
+ end
263
+
264
+ # Returns all ServiceTypeValue objects contained in this section, as
265
+ # configured. Lazy caches result for perfomance.
266
+ def service_type_values
267
+ @service_type_values ||=
268
+ @options[:service_type_values].collect do |s|
269
+ s.kind_of?(ServiceTypeValue)? s : ServiceTypeValue[s]
270
+ end
271
+ end
272
+
273
+ # Whether any services that generate #service_type_values are
274
+ # currently in progress. Lazy caches result for efficiency.
275
+ def services_in_progress?
276
+ # cache for efficiency
277
+ @services_in_progress ||=
278
+ @umlaut_request.service_types_in_progress?(service_type_values)
279
+ end
280
+
281
+ # Hash of ServiceType objects (join obj
282
+ # representing individual reponse data) included in this
283
+ # section. Keyed by string ServiceTypeValue id, value is array
284
+ # of ServiceTypes
285
+ def responses
286
+ unless (@responses)
287
+ @responses = {}
288
+ service_type_values.each do |st|
289
+ @responses[st.name] = @umlaut_request.get_service_type(st)
290
+ end
291
+ end
292
+ @responses
293
+ end
294
+
295
+ # All the values from #responses, flattened into a simple Array.
296
+ def responses_list
297
+ responses.values.flatten
298
+ end
299
+
300
+ def responses_empty?
301
+ responses_list.empty?
302
+ end
303
+
304
+ def request
305
+ return @umlaut_request
306
+ end
307
+
308
+ def div_id
309
+ return @section_id
310
+ end
311
+
312
+ def show_heading?
313
+ (! show_partial_only?) && @options[:show_heading]
314
+ end
315
+
316
+ def render_heading
317
+ content_tag(:div, :class=>"section_heading")
318
+
319
+ output = ''
320
+
321
+ output <<= '<div class="section_heading">'
322
+ (output <<= '<h3>' << CGI::escapeHTML(section_title) << '</h3>') if section_title
323
+ (output <<= '<p class="section_prompt">' << CGI::escapeHTML(section_prompt) << '</p>') if section_prompt
324
+ output <<= '</div>'
325
+
326
+ output.html_safe
327
+ end
328
+
329
+ def show_spinner?
330
+ (! show_partial_only?) && @options[:show_spinner] &&
331
+ @umlaut_request.service_types_in_progress?(service_type_values)
332
+ end
333
+
334
+ # A hash suitable to be passed to Rails render(), to render
335
+ # a spinner for this section. Called by section_display partial,
336
+ # nobody else should need to call it.
337
+ def spinner_render_hash
338
+ { :partial => "background_progress",
339
+ :locals =>{ :svc_types => service_type_values,
340
+ :div_id => "progress_#{@section_id}",
341
+ :current_set_empty => responses_empty?,
342
+ :item_name => @options[:item_name_plural]}
343
+ }
344
+ end
345
+
346
+ def show_partial_only?
347
+ @options[:show_partial_only]
348
+ end
349
+
350
+ def custom_partial?
351
+ ! @options[:partial].nil?
352
+ end
353
+
354
+ # A hash suitable to be passed to Rails render() to render the
355
+ # inner content portion of the section. Called by the section_display
356
+ # partial, nobody else should need to call this. You may be looking
357
+ # for ResolveHelper#render_section instead.
358
+ def content_render_hash
359
+ if custom_partial?
360
+ {:partial => @options[:partial].to_s,
361
+ :object => responses_list,
362
+ :locals => @options[:partial_locals].merge(
363
+ {:responses_by_type => responses,
364
+ :responses => responses_list,
365
+ :umlaut_request => request,
366
+ :renderer => self})}
367
+ else
368
+ {:partial => @options[:item_partial].to_s,
369
+ :collection => responses_list,
370
+ :locals => @options[:partial_locals].clone}
371
+ end
372
+ end
373
+
374
+ # used only with with list_with_limit functionality in section_display
375
+ # partial.
376
+ def item_render_hash(item)
377
+ # need to clone @options[:partial_locals], because
378
+ # Rails will modify it to add the 'object' to it. Bah!
379
+ {:partial => @options[:item_partial],
380
+ :object => item,
381
+ :locals => @options[:partial_locals].clone}
382
+ end
383
+
384
+ # Is the section visible according to it's settings calculated in current
385
+ # context?
386
+ def visible?
387
+ case @options[:visibility]
388
+ when true, false
389
+ @options[:visibility]
390
+ when :any_services
391
+ # do any services exist which even potentially generate our types, even
392
+ # if they've completed without doing so?.
393
+ nil != @umlaut_request.dispatched_services.to_a.find do |ds|
394
+ ! (service_type_values & ds.service.service_types_generated ).empty?
395
+ end
396
+ when :in_progress
397
+ # Do we have any of our types generated, or any services in progress
398
+ # that might generate them?
399
+ (! responses_empty?) || services_in_progress?
400
+ when :responses_exist
401
+ # Have any responses of our type actually been generated?
402
+ ! responses_empty?
403
+ when :complete_with_responses
404
+ (! responses.empty?) && ! (services_in_progress?)
405
+ when Proc
406
+ # It's a lambda, which takes @umlaut_request as an arg
407
+ @options[:visibility].call(self)
408
+ else true
409
+ end
410
+ end
411
+
412
+ def list_visible_limit
413
+ @options[:list_visible_limit]
414
+ end
415
+
416
+ def section_title
417
+ @options[:section_title]
418
+ end
419
+
420
+ def section_prompt
421
+ @options[:section_prompt]
422
+ end
423
+
424
+
425
+ # Convenience method for re-ordering sections in local resolve_views
426
+ # initializer.
427
+ # Swaps elements if necessary to ensure they are in the specified order.
428
+ # For example, make sure holding comes before document_delivery:
429
+ # SectionRenderer.ensureOrder("holding", "document_delivery")
430
+ # Maybe in the future we'll expand this to take variable arguments.
431
+ def self.swap_if_needed!(first, second)
432
+
433
+ list = AppConfig.param("resolve_sections")
434
+ return unless list
435
+
436
+ index1 = find_index(list) {|s| s[:div_id] == first}
437
+ index2 = find_index(list) {|s| s[:div_id] == second}
438
+
439
+ (list[index1], list[index2] = list[index2], list[index1]) if index1 && index2 && (index1 > index2)
440
+
441
+ list
442
+ end
443
+
444
+ # helper for swap_if_needed! and ensure_order!
445
+ def self.find_index(array, &block)
446
+ array.each_with_index do |value, index|
447
+ return index if block.call(value)
448
+ end
449
+ return nil
450
+ end
451
+
452
+
453
+ protected
454
+
455
+ def construct_options(arguments)
456
+
457
+ # Fill in static defaults
458
+ @options = {:show_spinner => true,
459
+ :show_heading => true,
460
+ :visibility => true,
461
+ :show_partial_only => false,
462
+ :partial_locals => {}}.merge!(arguments)
463
+
464
+
465
+ # service type value default to same name as section_id
466
+ @options[:service_type_values] ||= [@section_id]
467
+
468
+
469
+ # Fill in calculatable-defaults
470
+ if (service_type_values.length > 0)
471
+ @options = {:section_title =>
472
+ service_type_values.first.display_name
473
+ }.merge(@options)
474
+ end
475
+
476
+ # Partials to display. Default to _standard_response_item item partial.
477
+ if ( @options[:partial] == true)
478
+ @options[:partial] = @section_id
479
+ end
480
+ if (@options[:partial].blank?)
481
+ @options[:item_partial] =
482
+ case @options[:item_partial]
483
+ when true then @section_id + "_item"
484
+ when String then options[:item_partial]
485
+ else "standard_response_item"
486
+ end
487
+ end
488
+
489
+ # sanity check
490
+ if ( @options[:show_partial_only] && ! @options[:partial])
491
+ raise Exception.new("SectionRenderer: You must supply a :partial argument if :show_partial_only is set true")
492
+ end
493
+
494
+ return @options
495
+ end
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+ end