bento_search 0.0.1

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 (122) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +299 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/images/bento_search/large_loader.gif +0 -0
  5. data/app/assets/javascripts/bento_search.js +3 -0
  6. data/app/assets/javascripts/bento_search/ajax_load.js +22 -0
  7. data/app/assets/stylesheets/bento_search/bento.css +4 -0
  8. data/app/controllers/bento_search/bento_search_controller.rb +7 -0
  9. data/app/controllers/bento_search/search_controller.rb +72 -0
  10. data/app/helpers/bento_search_helper.rb +138 -0
  11. data/app/item_decorators/bento_search/only_premade_openurl.rb +16 -0
  12. data/app/item_decorators/bento_search/openurl_add_other_link.rb +35 -0
  13. data/app/item_decorators/bento_search/openurl_main_link.rb +30 -0
  14. data/app/models/bento_search/author.rb +25 -0
  15. data/app/models/bento_search/link.rb +30 -0
  16. data/app/models/bento_search/multi_searcher.rb +109 -0
  17. data/app/models/bento_search/openurl_creator.rb +128 -0
  18. data/app/models/bento_search/registrar.rb +70 -0
  19. data/app/models/bento_search/result_item.rb +203 -0
  20. data/app/models/bento_search/results.rb +54 -0
  21. data/app/models/bento_search/results/pagination.rb +67 -0
  22. data/app/models/bento_search/search_engine.rb +219 -0
  23. data/app/models/bento_search/search_engine/capabilities.rb +65 -0
  24. data/app/search_engines/bento_search/#Untitled-1# +11 -0
  25. data/app/search_engines/bento_search/ebsco_host_engine.rb +356 -0
  26. data/app/search_engines/bento_search/eds_engine.rb +557 -0
  27. data/app/search_engines/bento_search/google_books_engine.rb +184 -0
  28. data/app/search_engines/bento_search/primo_engine.rb +231 -0
  29. data/app/search_engines/bento_search/scopus_engine.rb +295 -0
  30. data/app/search_engines/bento_search/summon_engine.rb +398 -0
  31. data/app/search_engines/bento_search/xerxes_engine.rb +168 -0
  32. data/app/views/bento_search/_link.html.erb +4 -0
  33. data/app/views/bento_search/_search_error.html.erb +22 -0
  34. data/app/views/bento_search/_std_item.html.erb +39 -0
  35. data/app/views/bento_search/search/search.html.erb +1 -0
  36. data/config/locales/en.yml +25 -0
  37. data/lib/bento_search.rb +29 -0
  38. data/lib/bento_search/engine.rb +5 -0
  39. data/lib/bento_search/routes.rb +45 -0
  40. data/lib/bento_search/version.rb +3 -0
  41. data/lib/generators/bento_search/pull_ebsco_dbs_generator.rb +24 -0
  42. data/lib/generators/bento_search/templates/ebsco_global_var.erb +6 -0
  43. data/lib/http_client_patch/include_client.rb +86 -0
  44. data/lib/tasks/bento_search_tasks.rake +4 -0
  45. data/test/dummy/README.rdoc +261 -0
  46. data/test/dummy/Rakefile +7 -0
  47. data/test/dummy/app/assets/javascripts/application.js +15 -0
  48. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  49. data/test/dummy/app/controllers/application_controller.rb +3 -0
  50. data/test/dummy/app/helpers/application_helper.rb +2 -0
  51. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  52. data/test/dummy/config.ru +4 -0
  53. data/test/dummy/config/application.rb +56 -0
  54. data/test/dummy/config/boot.rb +10 -0
  55. data/test/dummy/config/database.yml +25 -0
  56. data/test/dummy/config/environment.rb +5 -0
  57. data/test/dummy/config/environments/development.rb +37 -0
  58. data/test/dummy/config/environments/production.rb +67 -0
  59. data/test/dummy/config/environments/test.rb +37 -0
  60. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  61. data/test/dummy/config/initializers/inflections.rb +15 -0
  62. data/test/dummy/config/initializers/mime_types.rb +5 -0
  63. data/test/dummy/config/initializers/secret_token.rb +7 -0
  64. data/test/dummy/config/initializers/session_store.rb +8 -0
  65. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  66. data/test/dummy/config/locales/en.yml +5 -0
  67. data/test/dummy/config/routes.rb +6 -0
  68. data/test/dummy/db/test.sqlite3 +0 -0
  69. data/test/dummy/log/test.log +3100 -0
  70. data/test/dummy/public/404.html +26 -0
  71. data/test/dummy/public/422.html +26 -0
  72. data/test/dummy/public/500.html +25 -0
  73. data/test/dummy/public/favicon.ico +0 -0
  74. data/test/dummy/script/rails +6 -0
  75. data/test/functional/bento_search/search_controller_test.rb +81 -0
  76. data/test/helper/bento_search_helper_test.rb +125 -0
  77. data/test/integration/navigation_test.rb +10 -0
  78. data/test/support/mock_engine.rb +23 -0
  79. data/test/support/test_with_cassette.rb +38 -0
  80. data/test/test_helper.rb +52 -0
  81. data/test/unit/#vcr_test.rb# +68 -0
  82. data/test/unit/ebsco_host_engine_test.rb +134 -0
  83. data/test/unit/eds_engine_test.rb +105 -0
  84. data/test/unit/google_books_engine_test.rb +93 -0
  85. data/test/unit/item_decorators_test.rb +66 -0
  86. data/test/unit/multi_searcher_test.rb +49 -0
  87. data/test/unit/openurl_creator_test.rb +111 -0
  88. data/test/unit/pagination_test.rb +59 -0
  89. data/test/unit/primo_engine_test.rb +37 -0
  90. data/test/unit/register_engine_test.rb +50 -0
  91. data/test/unit/result_item_display_test.rb +39 -0
  92. data/test/unit/result_item_test.rb +36 -0
  93. data/test/unit/scopus_engine_test.rb +130 -0
  94. data/test/unit/search_engine_base_test.rb +178 -0
  95. data/test/unit/search_engine_test.rb +95 -0
  96. data/test/unit/summon_engine_test.rb +161 -0
  97. data/test/unit/xerxes_engine_test.rb +70 -0
  98. data/test/vcr_cassettes/ebscohost/error_bad_db.yml +45 -0
  99. data/test/vcr_cassettes/ebscohost/error_bad_password.yml +45 -0
  100. data/test/vcr_cassettes/ebscohost/get_info.yml +3626 -0
  101. data/test/vcr_cassettes/ebscohost/live_search.yml +45 -0
  102. data/test/vcr_cassettes/ebscohost/live_search_smoke_test.yml +1311 -0
  103. data/test/vcr_cassettes/eds/basic_search_smoke_test.yml +1811 -0
  104. data/test/vcr_cassettes/eds/get_auth_token.yml +75 -0
  105. data/test/vcr_cassettes/eds/get_auth_token_failure.yml +39 -0
  106. data/test/vcr_cassettes/eds/get_with_auth.yml +243 -0
  107. data/test/vcr_cassettes/eds/get_with_auth_recovers_from_bad_auth.yml +368 -0
  108. data/test/vcr_cassettes/gbs/error_condition.yml +40 -0
  109. data/test/vcr_cassettes/gbs/pagination.yml +702 -0
  110. data/test/vcr_cassettes/gbs/search.yml +340 -0
  111. data/test/vcr_cassettes/primo/search_smoke_test.yml +1112 -0
  112. data/test/vcr_cassettes/scopus/bad_api_key_should_return_error_response.yml +60 -0
  113. data/test/vcr_cassettes/scopus/escaped_chars.yml +187 -0
  114. data/test/vcr_cassettes/scopus/fielded_search.yml +176 -0
  115. data/test/vcr_cassettes/scopus/simple_search.yml +227 -0
  116. data/test/vcr_cassettes/scopus/zero_results_search.yml +67 -0
  117. data/test/vcr_cassettes/summon/bad_auth.yml +54 -0
  118. data/test/vcr_cassettes/summon/proper_tags_for_snippets.yml +216 -0
  119. data/test/vcr_cassettes/summon/search.yml +242 -0
  120. data/test/vcr_cassettes/xerxes/live_search.yml +2580 -0
  121. data/test/view/std_item_test.rb +98 -0
  122. metadata +421 -0
@@ -0,0 +1,138 @@
1
+ # Rails helper module provided by BentoSearch meant to be included
2
+ # in host app's helpers.
3
+ module BentoSearchHelper
4
+
5
+ # Renders bento search results on page, or an AJAX loader, etc, as appropriate.
6
+ # Pass in:
7
+ # * BentoSearch::SearchResults => will render
8
+ # * an instantiated BentoSearch::SearchEngine => Will do search and render
9
+ # * an id that a search engine was registered under with
10
+ # BentoSearch.register_engine => will do search and render.
11
+ #
12
+ # Second arg options hash includes options for bento_search helper,
13
+ # as well as other options pased on to BentoSearch::Engine.search(options)
14
+ #
15
+ # == Options
16
+ #
17
+ # load: :ajax_auto, :immediate. :ajax_auto will put a spinner up there,
18
+ # and load actual results via AJAX request. :immediate preloads
19
+ # results.
20
+ #
21
+ # == Examples
22
+ #
23
+ # bento_results( results_obj )
24
+ # bento_results( engine_obj, :query => "cancer")
25
+ # bento_results("google_books", :query => "cancer", :load => :ajax_auto)
26
+ #
27
+ def bento_search(search_arg, options = {})
28
+ results = search_arg if search_arg.kind_of? BentoSearch::Results
29
+
30
+ load_mode = options.delete(:load)
31
+
32
+ engine = nil
33
+ unless results
34
+ # need to load an engine and do a search, or ajax, etc.
35
+ engine = (if search_arg.kind_of? BentoSearch::SearchEngine
36
+ search_arg
37
+ else
38
+ BentoSearch.get_engine(search_arg.to_s)
39
+ end)
40
+
41
+ end
42
+
43
+ if (!results && load_mode == :ajax_auto)
44
+ raise ArgumentError.new("`:load => :ajax` requires a registered engine with an id") unless engine.configuration.id
45
+ content_tag(:div, :class => "bento_search_ajax_wait",
46
+ :"data-bento-ajax-url" => to_bento_search_url( {:engine_id => engine.configuration.id}.merge(options) )) do
47
+ image_tag("bento_search/large_loader.gif", :alt => I18n.translate("bento_search.ajax_loading")) +
48
+ content_tag("noscript") do
49
+ "Can not load results without javascript"
50
+ end
51
+ end
52
+ else
53
+ results = engine.search(options) unless results
54
+
55
+ if results.failed?
56
+ render :partial => "bento_search/search_error", :locals => {:results => results}
57
+ elsif results.length > 0
58
+ render :partial => "bento_search/std_item", :collection => results
59
+ else
60
+ content_tag(:div, :class=> "bento_search_no_results") do
61
+ I18n.translate("bento_search.no_results")
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ ##
69
+ # More methods used by bento standard views, namespaced with bento_, sorry
70
+ # no great way to take logic out of views into helper methods without
71
+ # namespacey hack.
72
+ #
73
+ # You can use these methods in your own custom views, you also should be
74
+ # able to over-ride them (including calling super) in local helpers to
75
+ # change behavior in standard views.
76
+ #
77
+ ##
78
+
79
+ def bento_abstract_truncate(str)
80
+ # if it's html safe, we can't truncate it, we don't have an HTML-aware
81
+ # truncation routine right now, that avoids leaving tags open etc.
82
+ return str if str.html_safe?
83
+
84
+ truncate(str, :length => 280, :separator => " ")
85
+ end
86
+
87
+ # Prepare a title in an H4, with formats in parens in a <small> (for
88
+ # bootstrap), linked, etc.
89
+ def bento_item_title(item)
90
+ content_tag("h4", :class => "bento_item_title") do
91
+ safe_join([
92
+ link_to_unless( item.link.blank?, item.complete_title, item.link ),
93
+ if item.format.present? || item.format_str.present?
94
+ content_tag("small", :class => "bento_item_about") do
95
+ " (" +
96
+ if item.format_str
97
+ item.format_str
98
+ else
99
+ t(item.format, :scope => [:bento_search, :format], :default => item.format.to_s.titleize)
100
+ end + ")"
101
+ end
102
+ end
103
+ ], '')
104
+ end
105
+ end
106
+
107
+ # first 3 authors, each in a <span>, using item.author_display, seperated by
108
+ # semi-colons.
109
+ def bento_item_authors(item)
110
+ parts = []
111
+
112
+ first_three = item.authors.slice(0,3)
113
+
114
+ first_three.each_with_index do |author, index|
115
+ parts << content_tag("span", :class => "bento_item_author") do
116
+ item.author_display(author)
117
+ end
118
+ if (index + 1) < first_three.length
119
+ parts << "; "
120
+ end
121
+ end
122
+
123
+ return safe_join(parts, "")
124
+ end
125
+
126
+ # returns a hash of label => key suitable for passing to rails
127
+ # options_for_select. (Yes, it works backwards from how you'd expect).
128
+ # Label is looked up using I18n, at bento_search.sort_keys.*
129
+ def bento_sort_hash_for(engine)
130
+ Hash[
131
+ engine.sort_definitions.keys.collect do |key|
132
+ [I18n.translate(key, :scope => "bento_search.sort_keys"), key]
133
+ end
134
+ ]
135
+ end
136
+
137
+
138
+ end
@@ -0,0 +1,16 @@
1
+ require 'openurl'
2
+
3
+ # A Decorator that will make #to_openurl refuse to construct
4
+ # an openurl from individual elements, it'll use the #openurl_kev_co
5
+ # or nothing.
6
+ module BentoSearch::OnlyPremadeOpenurl
7
+
8
+ def to_openurl
9
+ if self.openurl_kev_co
10
+ return OpenURL::ContextObject.new_from_kev( self.openurl_kev_co )
11
+ else
12
+ return nil
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,35 @@
1
+ # Example of an Item Decorator that ADDs an 'other link' with an openurl.
2
+ #
3
+ # This example uses crazy metaprogramming to dynamically create
4
+ # a module configured with your base url etc. You don't need to use
5
+ # crazy method like that; just define your own local decorator doing
6
+ # exactly what you need, it's meant to be simple.
7
+ #
8
+ # config.item_decorators = [ BentoSearch::OpenurlAddOtherLink[:base_url => "http://resolve.somewhere.edu/foo", :extra_query => "&foo=bar"] ]
9
+ #
10
+ module BentoSearch::OpenurlAddOtherLink
11
+ def self.[](options)
12
+ base_url = options[:base_url]
13
+ extra_query = options[:extra_query] || ""
14
+ link_name = options[:link_name] || "Find It"
15
+ # overwrite: if true, overwrite previously existing other_links, if
16
+ # false add on to previously existing.
17
+ overwrite = options.has_key?(:overwrite) ? options[:overwrite] : false
18
+
19
+ Module.new do
20
+
21
+ define_method :other_links do
22
+ start = overwrite ? [] : super()
23
+ if (ou = to_openurl)
24
+ start + [BentoSearch::Link.new(:url => "#{base_url}?#{ou.kev}#{extra_query}", :label => link_name)]
25
+ else
26
+ start
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+
34
+
35
+ end
@@ -0,0 +1,30 @@
1
+ # Example of an Item Decorator that replaces the main 'link'
2
+ # with an openurl.
3
+ #
4
+ # This example uses crazy metaprogramming to dynamically create
5
+ # a module configured with your base url etc. You don't need to use
6
+ # crazy method like that; just define your own local decorator doing
7
+ # exactly what you need, it's meant to be simple.
8
+ #
9
+ # config.item_decorators = [ BentoSearch::OpenurlMainLink[:base_url => "http://resolve.somewhere.edu/foo", :extra_query => "&foo=bar"] ]
10
+ #
11
+ module BentoSearch::OpenurlMainLink
12
+ def self.[](options)
13
+ base_url = options[:base_url]
14
+ extra_query = options[:extra_query] || ""
15
+ Module.new do
16
+
17
+ define_method :link do
18
+ if (ou = to_openurl)
19
+ "#{base_url}?#{ou.kev}#{extra_query}"
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+
29
+
30
+ end
@@ -0,0 +1,25 @@
1
+ module BentoSearch
2
+ class Author
3
+ def initialize(args ={})
4
+ args.each_pair do |key, value|
5
+ send("#{key}=", value)
6
+ end
7
+ end
8
+
9
+ # Can be first name or initial, whatever source provides
10
+ attr_accessor :first
11
+ # last name/surname/family name as provided by source
12
+ attr_accessor :last
13
+ # middle name or initial, as and if provided by source
14
+ attr_accessor :middle
15
+
16
+ # if source doens't provide seperate first/last,
17
+ # source may only be able to provide one big string, author_display
18
+ attr_accessor :display
19
+
20
+ def empty?
21
+ first.blank? && last.blank? && middle.blank? && display.blank?
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module BentoSearch
2
+ # Represents an 'additional link' held in BentoSearch::ResultItem#other_links
3
+ #
4
+ # label, url, other metadata about the link.
5
+ class Link
6
+ # url is normally a string, but can also be a Hash passed
7
+ # to url_for in the local app.
8
+ attr_accessor :label, :url
9
+
10
+ # Used both for HTML links and possibly later Atom.
11
+ # Must be a string, EITHER a complete URL (representing
12
+ # vocab term), OR a legal short name from
13
+ # http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#linkTypes
14
+ attr_accessor :rel
15
+
16
+ # Array of strings, used for CSS classes on this link, possibly
17
+ # for custom styles/images etc. May be used in non-html link
18
+ # contexts too.
19
+ attr_accessor :style_classes
20
+
21
+ def initialize(hash = {})
22
+ self.style_classes = []
23
+
24
+ hash.each_pair do |key, value|
25
+ send("#{key}=", value)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,109 @@
1
+ require 'celluloid'
2
+
3
+ # Based on Celluloid, concurrently runs multiple searches in
4
+ # seperate threads. You must include 'celluloid' gem dependency
5
+ # into your local app to use this class.
6
+ #
7
+ # I am not an expert at use of Celluloid, it's possible there's a better
8
+ # way to do this all, but seems to work.
9
+ #
10
+ # ## Usage
11
+ #
12
+ # initialize with id's of registered engines:
13
+ # searcher = BentoBox::MultiSearcher.new(:gbs, :scopus)
14
+ #
15
+ # start the concurrent searches, params same as engine.search
16
+ # searcher.start( query_params )
17
+ #
18
+ # retrieve results, blocking until each is completed:
19
+ # searcher.results
20
+ #
21
+ # returns a Hash keyed by engine id, values BentoSearch::Results objects.
22
+ #
23
+ # Can only call #results once per #start, after that it'll return empty hash.
24
+ # (should we make it actually raise instead?). .
25
+ #
26
+ # important to call results at some point after calling start, in order
27
+ # to make sure Celluloid::Actors are properly terminated to avoid
28
+ # resource leakage.
29
+ #
30
+ # TODO: have a method that returns Futures instead of only supplying the blocking
31
+ # results method? Several tricks, including making sure to properly terminate actors.
32
+ class BentoSearch::MultiSearcher
33
+
34
+ def initialize(*engine_ids)
35
+ @engines = []
36
+ @actors = []
37
+ engine_ids.each do |id|
38
+ add_engine( BentoSearch.get_engine id )
39
+ end
40
+ end
41
+
42
+ # Adds an instantiated engine directly, rather than by id from global
43
+ # registry.
44
+ def add_engine(engine)
45
+ @engines << engine
46
+ end
47
+
48
+ # Starts all searches, returns self so you can chain method calls if you like.
49
+ def start(*search_args)
50
+ @engines.each do |engine|
51
+ a = Actor.new(engine)
52
+ @actors << a
53
+ a.start! *search_args
54
+ end
55
+ return self
56
+ end
57
+
58
+ # Call after #start. Blocks until each included engine is finished
59
+ # then returns a Hash keyed by engine registered id, value is a
60
+ # BentoSearch::Results object.
61
+ #
62
+ # Can only call _once_ per invocation of #start, after that it'll return
63
+ # an empty hash.
64
+ def results
65
+ results = {}
66
+
67
+ # we use #delete_if to get an iterator that deletes
68
+ # each item after iteration.
69
+ @actors.delete_if do |actor|
70
+ result_key = (actor.engine.configuration.id || actor.engine.class.name)
71
+ results[result_key] = actor.results
72
+ actor.terminate
73
+
74
+ true
75
+ end
76
+
77
+ return results
78
+ end
79
+
80
+
81
+ class Actor
82
+ include Celluloid
83
+
84
+ attr_accessor :engine
85
+
86
+ def initialize(a_engine)
87
+ self.engine = a_engine
88
+ end
89
+
90
+ # call as start! with bang, to invoke async.
91
+ def start(*search_args)
92
+ begin
93
+ @results = self.engine.search(*search_args)
94
+ rescue Exception => e
95
+ warn e
96
+ # Make a fake results with caught exception.
97
+ @results = BentoSearch::Results.new
98
+ @results.error ||= {}
99
+ @results.error["exception"] = e
100
+ end
101
+ end
102
+
103
+ def results
104
+ @results
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,128 @@
1
+ require 'openurl'
2
+
3
+ module BentoSearch
4
+
5
+ # Helper class used to take a ResultItem, and construct
6
+ # a ruby OpenURL::ContextObject out of it. That represents
7
+ # a NISO Z39.88 OpenURL context object, useful for using
8
+ # with linking software that expects such. http://en.wikipedia.org/wiki/OpenURL
9
+ #
10
+ # co = OpenurlCreator.new( result_item ).to_open_url
11
+ # # => ruby OpenURL::ContextObject object.
12
+ #
13
+ # co.kev
14
+ # # => context object serialized to KEV format (URL query string)
15
+ #
16
+ # In some cases nil can be returned, if no reasonable OpenURL can
17
+ # be created from the ResultItem.
18
+ class OpenurlCreator
19
+ attr_accessor :result_item
20
+
21
+ def initialize(ri)
22
+ self.result_item = ri
23
+ end
24
+
25
+ def to_openurl
26
+ # If we have a pre-constructed KEV, just use it.
27
+ if result_item.openurl_kev_co
28
+ return OpenURL::ContextObject.new_from_kev( result_item.openurl_kev_co )
29
+ end
30
+
31
+
32
+ context_object = OpenURL::ContextObject.new
33
+
34
+ r = context_object.referent
35
+
36
+ r.set_format( self.format )
37
+
38
+ if result_item.doi
39
+ r.add_identifier("info:doi:#{result_item.doi}")
40
+ end
41
+
42
+ r.set_metadata("genre", self.genre)
43
+
44
+ if result_item.authors.length > 0
45
+ r.set_metadata("aufirst", result_item.authors.first.first)
46
+ r.set_metadata("aulast", result_item.authors.first.last)
47
+ r.set_metadata("au", result_item.author_display(result_item.authors.first))
48
+ end
49
+
50
+ r.set_metadata("date", result_item.year.to_s)
51
+ r.set_metadata("volume", result_item.volume.to_s)
52
+ r.set_metadata("issue", result_item.issue.to_s)
53
+ r.set_metadata("spage", result_item.start_page.to_s)
54
+ r.set_metadata("epage", result_item.end_page.to_s)
55
+ r.set_metadata("jtitle", result_item.journal_title)
56
+ r.set_metadata("issn", result_item.issn)
57
+ r.set_metadata("isbn", result_item.isbn)
58
+ r.set_metadata("pub", result_item.publisher)
59
+
60
+ case result_item.format
61
+ when "Book"
62
+ r.set_metadata("btitle", result_item.complete_title)
63
+ when "Article", :conference_paper
64
+ r.set_metadata("atitle", result_item.complete_title)
65
+ else
66
+ r.set_metadata("title", result_item.complete_title)
67
+ end
68
+
69
+ return context_object
70
+ end
71
+
72
+
73
+ # rft.genre value. Yeah, the legal ones differ depending on openurl
74
+ # 'format', but we've given up trying to do things strictly legal,
75
+ # OpenURL is a bear, we do things as generally used and good enough.
76
+ #
77
+ # can be nil.
78
+ def genre
79
+ case result_item.format
80
+ when "Book"
81
+ "book"
82
+ when :book_item
83
+ "bookitem"
84
+ when :conference_paper
85
+ "proceeding"
86
+ when :conference_proceedings
87
+ "conference"
88
+ when :report
89
+ "report"
90
+ when :serial
91
+ "journal"
92
+ when "Article"
93
+ "article"
94
+ else
95
+ nil
96
+ end
97
+ end
98
+
99
+
100
+ # We need to map from our formats to which OpenURL 'format'
101
+ # we're going to create.
102
+ #
103
+ # We only pick from a limited set of standard scholarly citation formats,
104
+ # that's all any actual widespread software recognizes.
105
+ #
106
+ # Returns the last component of a valid format from:
107
+ # http://alcme.oclc.org/openurl/servlet/OAIHandler?verb=ListRecords&metadataPrefix=oai_dc&set=Core:Metadata+Formats
108
+ #
109
+ # Eg, "book", "journal", "dissertation".
110
+ #
111
+ # In fact, we only pick from one of those three -- by default, if we
112
+ # can't figure out exactly what it is or can't map it to a specific
113
+ # format, we'll return 'journal', never nil. 'journal' serves, in practice,
114
+ # with much actual software, as a neutral default.
115
+ def format
116
+ case result_item.format
117
+ when "Book"
118
+ "book"
119
+ when :dissertation
120
+ "dissertation"
121
+ else
122
+ "journal"
123
+ end
124
+ end
125
+
126
+
127
+ end
128
+ end