bento_search 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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