bento_search 0.6.0 → 0.7.0
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.
- data/README.md +131 -74
- data/app/assets/javascripts/bento_search/ajax_load.js +12 -4
- data/app/assets/stylesheets/bento_search/suggested_styles.css +4 -4
- data/app/helpers/bento_search_helper.rb +114 -27
- data/app/item_decorators/bento_search/decorator_base.rb +53 -0
- data/app/item_decorators/bento_search/ebscohost/conditional_openurl_main_link.rb +36 -0
- data/app/item_decorators/bento_search/no_links.rb +3 -2
- data/app/item_decorators/bento_search/only_premade_openurl.rb +4 -0
- data/app/item_decorators/bento_search/openurl_add_other_link.rb +4 -0
- data/app/item_decorators/bento_search/openurl_main_link.rb +4 -0
- data/app/item_decorators/bento_search/standard_decorator.rb +122 -0
- data/app/models/bento_search/multi_searcher.rb +13 -6
- data/app/models/bento_search/openurl_creator.rb +25 -5
- data/app/models/bento_search/result_item.rb +25 -83
- data/app/models/bento_search/results/pagination.rb +8 -2
- data/app/models/bento_search/search_engine.rb +29 -23
- data/app/search_engines/bento_search/ebsco_host_engine.rb +161 -25
- data/app/search_engines/bento_search/eds_engine.rb +1 -44
- data/app/search_engines/bento_search/google_books_engine.rb +61 -14
- data/app/search_engines/bento_search/google_site_search_engine.rb +3 -1
- data/app/search_engines/bento_search/mock_engine.rb +4 -0
- data/app/search_engines/bento_search/primo_engine.rb +2 -3
- data/app/search_engines/bento_search/scopus_engine.rb +1 -0
- data/app/search_engines/bento_search/summon_engine.rb +5 -1
- data/app/search_engines/bento_search/worldcat_sru_dc_engine.rb +36 -8
- data/app/views/bento_search/_item_title.html.erb +29 -0
- data/app/views/bento_search/_no_results.html.erb +3 -0
- data/app/views/bento_search/_search_error.html.erb +19 -15
- data/app/views/bento_search/_std_item.html.erb +55 -30
- data/app/views/bento_search/search/search.html.erb +7 -0
- data/config/locales/en.yml +22 -0
- data/lib/bento_search/util.rb +63 -1
- data/lib/bento_search/version.rb +1 -1
- data/test/decorator/decorator_base_test.rb +72 -0
- data/test/decorator/standard_decorator_test.rb +55 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +12 -0
- data/test/dummy/log/test.log +119757 -0
- data/test/functional/bento_search/search_controller_test.rb +28 -0
- data/test/helper/bento_search_helper_test.rb +71 -0
- data/test/helper/bento_truncate_helper_test.rb +71 -0
- data/test/unit/ebsco_host_engine_test.rb +110 -3
- data/test/unit/google_books_engine_test.rb +22 -14
- data/test/unit/google_site_search_test.rb +11 -4
- data/test/unit/item_decorators_test.rb +6 -65
- data/test/unit/openurl_creator_test.rb +87 -8
- data/test/unit/result_item_test.rb +1 -11
- data/test/unit/search_engine_base_test.rb +25 -2
- data/test/unit/search_engine_test.rb +16 -0
- data/test/unit/summon_engine_test.rb +3 -0
- data/test/vcr_cassettes/ebscohost/another_dissertation.yml +148 -0
- data/test/vcr_cassettes/ebscohost/dissertation_example.yml +218 -0
- data/test/vcr_cassettes/ebscohost/fulltext_info.yml +1306 -0
- data/test/vcr_cassettes/ebscohost/live_book_example.yml +130 -0
- data/test/vcr_cassettes/ebscohost/live_dissertation.yml +148 -0
- data/test/vcr_cassettes/ebscohost/live_pathological_book_item_example.yml +215 -0
- data/test/vcr_cassettes/google_site/gets_format_string.yml +232 -0
- data/test/vcr_cassettes/max_out_pagination.yml +155 -0
- data/test/vcr_cassettes/worldcat_sru_dc/max_out_pagination.yml +167 -0
- data/test/view/std_item_test.rb +25 -8
- metadata +45 -12
- data/test/unit/result_item_display_test.rb +0 -39
- data/test/unit/worldcat_sru_dc_engine_test.rb +0 -120
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module BentoSearch
|
4
|
+
# A delegator with an ActionView context.
|
5
|
+
# You can access the ActionView context at _h , to call Rails helper
|
6
|
+
# methods (framework or app specific, whatever should be avail at
|
7
|
+
# given context)
|
8
|
+
#
|
9
|
+
# inside a method in a decorator, `_h.content_tag` or `_h.html_escape`
|
10
|
+
# or `_h.url_for` etc.
|
11
|
+
#
|
12
|
+
# (Except you can't call html_escape that way becuase Rails makes it
|
13
|
+
# private for some reason, wtf. We provide an html_escape)
|
14
|
+
#
|
15
|
+
# Inside a decorator, access #_base to get undecorated base model.
|
16
|
+
class DecoratorBase < SimpleDelegator
|
17
|
+
|
18
|
+
def initialize(base, view_context)
|
19
|
+
super(base)
|
20
|
+
|
21
|
+
|
22
|
+
# This worked to make html_escape avail at _h.html_escape, but
|
23
|
+
# yfeldblum warned it messes up the method lookup cache, so
|
24
|
+
# we just provide a straight #html_escape instead.
|
25
|
+
#if view_context.respond_to?(:html_escape, true)
|
26
|
+
# thanks yfeldblum in #rails for this simple way to make
|
27
|
+
# html_escape public, which I don't entirely understand myself. :)
|
28
|
+
|
29
|
+
# class << view_context
|
30
|
+
# public :html_escape
|
31
|
+
# end
|
32
|
+
#end
|
33
|
+
|
34
|
+
@view_context = view_context
|
35
|
+
@base = base
|
36
|
+
end
|
37
|
+
|
38
|
+
def _h
|
39
|
+
@view_context
|
40
|
+
end
|
41
|
+
|
42
|
+
def _base
|
43
|
+
@base
|
44
|
+
end
|
45
|
+
|
46
|
+
# _h.html_escape won't work because Rails makes html_escape
|
47
|
+
# private for some weird reason. We provide our own here instead.
|
48
|
+
def html_escape(*args, &block)
|
49
|
+
ERB::Util.html_escape(*args, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# DEPRECATED. Just write the logic into a custom Decorator yourself.
|
2
|
+
# See wiki on decorators.
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# For ebscohost connector, example of an Item Decorator that replaces the main
|
6
|
+
# 'link' with an openurl ONLY if there is NOT fulltext avail from EBSCO.
|
7
|
+
#
|
8
|
+
# This example uses crazy metaprogramming to dynamically create
|
9
|
+
# a module configured with your base url etc. You don't need to use
|
10
|
+
# crazy method like that; just define your own local decorator doing
|
11
|
+
# exactly what you need, it's meant to be simple.
|
12
|
+
#
|
13
|
+
# config.item_decorators = [ BentoSearch::Ebscohost::ConditionalOpenurlMainLink[:base_url => "http://resolve.somewhere.edu/foo", :extra_query => "&foo=bar"] ]
|
14
|
+
#
|
15
|
+
module BentoSearch::Ebscohost::ConditionalOpenurlMainLink
|
16
|
+
def self.[](options)
|
17
|
+
base_url = options[:base_url]
|
18
|
+
extra_query = options[:extra_query] || ""
|
19
|
+
Module.new do
|
20
|
+
|
21
|
+
define_method :link do
|
22
|
+
if custom_data["fulltext_formats"]
|
23
|
+
super()
|
24
|
+
elsif (ou = to_openurl)
|
25
|
+
"#{base_url}?#{ou.kev}#{extra_query}"
|
26
|
+
else
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
# An item decorator that just erases all links, main link and other
|
2
|
-
# links.
|
1
|
+
# An item decorator mix-in that just erases all links, main link and other
|
2
|
+
# links. May be convenient to `include BentoSearch::NoLinks` in your
|
3
|
+
# custom decorator, although you could always just write this yourself too.
|
3
4
|
module BentoSearch::NoLinks
|
4
5
|
|
5
6
|
def link
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module BentoSearch
|
2
|
+
class StandardDecorator < DecoratorBase
|
3
|
+
|
4
|
+
|
5
|
+
# How to display a BentoSearch::Author object as a name
|
6
|
+
def author_display(author)
|
7
|
+
if (author.first && author.last)
|
8
|
+
"#{author.last}, #{author.first.slice(0,1)}"
|
9
|
+
elsif author.display
|
10
|
+
author.display
|
11
|
+
elsif author.last
|
12
|
+
author.last
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Put together title and subtitle if neccesary.
|
19
|
+
def complete_title
|
20
|
+
t = self.title
|
21
|
+
if self.subtitle
|
22
|
+
t = safe_join([t, ": ", self.subtitle], "")
|
23
|
+
end
|
24
|
+
|
25
|
+
if t.blank?
|
26
|
+
t = I18n.translate("bento_search.missing_title")
|
27
|
+
end
|
28
|
+
|
29
|
+
return t
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
# A simple user-displayable citation, _without_ author/title.
|
37
|
+
# the journal, year, vol, iss, page; or publisher and year; etc.
|
38
|
+
# Constructed from individual details. Not formal APA or MLA or anything,
|
39
|
+
# just a rough and ready display.
|
40
|
+
#
|
41
|
+
# TODO: Should this be moved to a rails helper method? Not sure.
|
42
|
+
def published_in
|
43
|
+
result_elements = []
|
44
|
+
|
45
|
+
result_elements.push("<span class='source_label'>#{I18n.t("bento_search.published_in")}</span><span class='source_title'>#{html_escape source_title}</span>".html_safe) unless source_title.blank?
|
46
|
+
|
47
|
+
if source_title.blank? && ! publisher.blank?
|
48
|
+
result_elements.push html_escape publisher
|
49
|
+
end
|
50
|
+
|
51
|
+
result_elements.push("#{I18n.t('bento_search.volume')} #{volume}") if volume.present?
|
52
|
+
|
53
|
+
result_elements.push("#{I18n.t('bento_search.issue')} #{issue}") if issue.present?
|
54
|
+
|
55
|
+
if (! start_page.blank?) && (! end_page.blank?)
|
56
|
+
result_elements.push html_escape "#{I18n.t('bento_search.pages')} #{start_page}-#{end_page}"
|
57
|
+
elsif ! start_page.blank?
|
58
|
+
result_elements.push html_escape "#{I18n.t('bento_search.page')} #{start_page}"
|
59
|
+
end
|
60
|
+
|
61
|
+
return nil if result_elements.empty?
|
62
|
+
|
63
|
+
return result_elements.join(", ").html_safe
|
64
|
+
end
|
65
|
+
|
66
|
+
# A display method, this is like #langauge_str, but will be nil if
|
67
|
+
# the language_code matches the current default locale, used
|
68
|
+
# for printing language only when not "English" normally.
|
69
|
+
#
|
70
|
+
#(Sorry, will be 'Spanish' never 'Espa~nol", we don't
|
71
|
+
# have a data source for language names in other languages right now. )
|
72
|
+
def display_language
|
73
|
+
return nil unless self.language_code
|
74
|
+
|
75
|
+
default = I18n.locale.try {|l| l.to_s.gsub(/\-.*$/, '')} || "en"
|
76
|
+
|
77
|
+
this_doc = self.language_obj.try(:iso_639_1)
|
78
|
+
|
79
|
+
return nil if this_doc == default
|
80
|
+
|
81
|
+
self.language_str
|
82
|
+
end
|
83
|
+
|
84
|
+
# format string to display to user. Uses #format_str if present,
|
85
|
+
# otherwise finds an i18n label from #format. Returns nil if none
|
86
|
+
# available.
|
87
|
+
def display_format
|
88
|
+
value = self.format_str ||
|
89
|
+
I18n.t(self.format, :scope => [:bento_search, :format], :default => self.format.to_s.titleize)
|
90
|
+
|
91
|
+
return value.blank? ? nil : value
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
###################
|
96
|
+
# turn into a representative OpenURL
|
97
|
+
#
|
98
|
+
# use to_openurl_kev to go straight there,
|
99
|
+
# or to_openurl to get a ruby OpenURL object.
|
100
|
+
###################
|
101
|
+
|
102
|
+
|
103
|
+
# Returns a ruby OpenURL::ContextObject (NISO Z39.88).
|
104
|
+
# or nil if none avail.
|
105
|
+
def to_openurl
|
106
|
+
return nil if openurl_disabled
|
107
|
+
|
108
|
+
BentoSearch::OpenurlCreator.new(self).to_openurl
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a kev encoded openurl, that is a URL query string representing
|
112
|
+
# openurl. Or nil if none available.
|
113
|
+
#
|
114
|
+
# Right now just calls #to_openurl.kev, can conceivably
|
115
|
+
# be modified to do things more efficient, without a ruby openurl
|
116
|
+
# obj. Law of demeter, represent.
|
117
|
+
def to_openurl_kev
|
118
|
+
to_openurl.try(:kev)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -2,7 +2,8 @@ require 'celluloid'
|
|
2
2
|
|
3
3
|
# Based on Celluloid, concurrently runs multiple searches in
|
4
4
|
# seperate threads. You must include 'celluloid' gem dependency
|
5
|
-
# into your local app to use this class.
|
5
|
+
# into your local app to use this class. Requires celluloid 0.12.0
|
6
|
+
# or above (for new preferred async syntax).
|
6
7
|
#
|
7
8
|
# I am not an expert at use of Celluloid, it's possible there's a better
|
8
9
|
# way to do this all, but seems to work.
|
@@ -13,7 +14,7 @@ require 'celluloid'
|
|
13
14
|
# searcher = BentoBox::MultiSearcher.new(:gbs, :scopus)
|
14
15
|
#
|
15
16
|
# start the concurrent searches, params same as engine.search
|
16
|
-
# searcher.
|
17
|
+
# searcher.search( query_params )
|
17
18
|
#
|
18
19
|
# retrieve results, blocking until each is completed:
|
19
20
|
# searcher.results
|
@@ -25,7 +26,12 @@ require 'celluloid'
|
|
25
26
|
#
|
26
27
|
# important to call results at some point after calling start, in order
|
27
28
|
# to make sure Celluloid::Actors are properly terminated to avoid
|
28
|
-
# resource leakage.
|
29
|
+
# resource leakage. May want to do it in an ensure block.
|
30
|
+
#
|
31
|
+
# Note that celluloid uses multi-threading in such a way that you
|
32
|
+
# may have to set config.cache_classes=true even in development
|
33
|
+
# to avoid problems. Rails class reloading is not thread-safe.
|
34
|
+
#
|
29
35
|
#
|
30
36
|
# TODO: have a method that returns Futures instead of only supplying the blocking
|
31
37
|
# results method? Several tricks, including making sure to properly terminate actors.
|
@@ -46,14 +52,15 @@ class BentoSearch::MultiSearcher
|
|
46
52
|
end
|
47
53
|
|
48
54
|
# Starts all searches, returns self so you can chain method calls if you like.
|
49
|
-
def
|
55
|
+
def search(*search_args)
|
50
56
|
@engines.each do |engine|
|
51
57
|
a = Actor.new(engine)
|
52
58
|
@actors << a
|
53
|
-
a.start
|
59
|
+
a.async.start *search_args
|
54
60
|
end
|
55
61
|
return self
|
56
62
|
end
|
63
|
+
alias_method :start, :search # backwards compat
|
57
64
|
|
58
65
|
# Call after #start. Blocks until each included engine is finished
|
59
66
|
# then returns a Hash keyed by engine registered id, value is a
|
@@ -87,7 +94,7 @@ class BentoSearch::MultiSearcher
|
|
87
94
|
self.engine = a_engine
|
88
95
|
end
|
89
96
|
|
90
|
-
# call as start
|
97
|
+
# call as .async.start, to invoke async.
|
91
98
|
def start(*search_args)
|
92
99
|
begin
|
93
100
|
@results = self.engine.search(*search_args)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'openurl'
|
2
|
+
require 'cgi'
|
2
3
|
|
3
4
|
module BentoSearch
|
4
5
|
|
@@ -7,7 +8,7 @@ module BentoSearch
|
|
7
8
|
# a NISO Z39.88 OpenURL context object, useful for using
|
8
9
|
# with linking software that expects such. http://en.wikipedia.org/wiki/OpenURL
|
9
10
|
#
|
10
|
-
# co = OpenurlCreator.new(
|
11
|
+
# co = OpenurlCreator.new( decorated_result_item ).to_open_url
|
11
12
|
# # => ruby OpenURL::ContextObject object.
|
12
13
|
#
|
13
14
|
# co.kev
|
@@ -20,6 +21,9 @@ module BentoSearch
|
|
20
21
|
|
21
22
|
attr_accessor :result_item
|
22
23
|
|
24
|
+
# Pass in a DECORATED result_item, eg StandardDecorator.new(result_item, nil)
|
25
|
+
# Need the display logic methods in the decorator, not just a raw
|
26
|
+
# result_item.
|
23
27
|
def initialize(ri)
|
24
28
|
self.result_item = ri
|
25
29
|
end
|
@@ -38,7 +42,13 @@ module BentoSearch
|
|
38
42
|
r.set_format( self.format )
|
39
43
|
|
40
44
|
if result_item.doi
|
41
|
-
r.add_identifier("info:doi
|
45
|
+
r.add_identifier("info:doi/#{result_item.doi}")
|
46
|
+
end
|
47
|
+
|
48
|
+
if result_item.oclcnum
|
49
|
+
r.add_identifier("info:oclcnum/#{result_item.oclcnum}")
|
50
|
+
# and do the one that's not actually legal practice, but is common
|
51
|
+
r.set_metadata("oclcnum", result_item.oclcnum)
|
42
52
|
end
|
43
53
|
|
44
54
|
r.set_metadata("genre", self.genre)
|
@@ -49,7 +59,13 @@ module BentoSearch
|
|
49
59
|
r.set_metadata("au", result_item.author_display(ensure_no_tags result_item.authors.first))
|
50
60
|
end
|
51
61
|
|
52
|
-
|
62
|
+
if result_item.publication_date
|
63
|
+
r.set_metadata("date", result_item.publication_date.iso8601)
|
64
|
+
else
|
65
|
+
r.set_metadata("date", result_item.year.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
53
69
|
r.set_metadata("volume", result_item.volume.to_s)
|
54
70
|
r.set_metadata("issue", result_item.issue.to_s)
|
55
71
|
r.set_metadata("spage", result_item.start_page.to_s)
|
@@ -59,9 +75,13 @@ module BentoSearch
|
|
59
75
|
r.set_metadata("isbn", result_item.isbn)
|
60
76
|
r.set_metadata("pub", ensure_no_tags(result_item.publisher))
|
61
77
|
|
78
|
+
|
62
79
|
case result_item.format
|
63
80
|
when "Book"
|
64
81
|
r.set_metadata("btitle", ensure_no_tags(result_item.complete_title))
|
82
|
+
when :book_item
|
83
|
+
r.set_metadata("btitle", result_item.source_title)
|
84
|
+
r.set_metadata("atitle", result_item.title)
|
65
85
|
when "Article", :conference_paper
|
66
86
|
r.set_metadata("atitle", ensure_no_tags(result_item.complete_title))
|
67
87
|
else
|
@@ -116,8 +136,8 @@ module BentoSearch
|
|
116
136
|
# with much actual software, as a neutral default.
|
117
137
|
def format
|
118
138
|
case result_item.format
|
119
|
-
when "Book"
|
120
|
-
"book"
|
139
|
+
when "Book", :book_item
|
140
|
+
"book"
|
121
141
|
when :dissertation
|
122
142
|
"dissertation"
|
123
143
|
else
|
@@ -51,6 +51,15 @@ module BentoSearch
|
|
51
51
|
# * schema.org CreativeWork: 'url'
|
52
52
|
attr_accessor :link
|
53
53
|
|
54
|
+
# does the #link correspond to fulltext? true or false -- or nil
|
55
|
+
# for unknown/non-applicable. Not all engines will set.
|
56
|
+
def link_is_fulltext?
|
57
|
+
@link_is_fulltext
|
58
|
+
end
|
59
|
+
def link_is_fulltext=(v)
|
60
|
+
@link_is_fulltext = v
|
61
|
+
end
|
62
|
+
|
54
63
|
# normalized controlled vocab title, important this is supplied
|
55
64
|
# if possible for OpenURL generation and other features.
|
56
65
|
#
|
@@ -98,7 +107,8 @@ module BentoSearch
|
|
98
107
|
# language_code.
|
99
108
|
#
|
100
109
|
# Consumers can look at language_code or language_str regardless (although
|
101
|
-
# either or both may be nil). You can
|
110
|
+
# either or both may be nil). You can get a language_list gem obj from
|
111
|
+
# language_obj, and use to normalize to a
|
102
112
|
# 2- or 3-letter from language_code that could be either.
|
103
113
|
attr_accessor :language_code
|
104
114
|
attr_writer :language_str
|
@@ -109,13 +119,24 @@ module BentoSearch
|
|
109
119
|
end
|
110
120
|
end
|
111
121
|
end
|
122
|
+
# Returns a LanguageList gem language object, from #language_code,
|
123
|
+
# convenience
|
124
|
+
def language_obj
|
125
|
+
return nil unless self.language_code
|
126
|
+
|
127
|
+
@language_obj ||= LanguageList::LanguageInfo.find( self.language_code )
|
128
|
+
end
|
112
129
|
|
113
130
|
# year published. a ruby int
|
114
131
|
# PART of:.
|
115
132
|
# * schema.org CreativeWork "datePublished", year portion
|
116
133
|
# * dcterms.issued, year portion
|
117
134
|
# * prism:coverDate, year portion
|
135
|
+
#
|
136
|
+
# See also publication_date when you have a complete date
|
118
137
|
attr_accessor :year
|
138
|
+
# ruby stdlib Date object.
|
139
|
+
attr_accessor :publication_date
|
119
140
|
|
120
141
|
attr_accessor :volume
|
121
142
|
attr_accessor :issue
|
@@ -160,89 +181,10 @@ module BentoSearch
|
|
160
181
|
# for internal use.
|
161
182
|
attr_accessor :custom_data
|
162
183
|
|
184
|
+
# Copied over from engine configuration usually, a string
|
185
|
+
# qualified name of a decorator class. Can be nil for default.
|
186
|
+
attr_accessor :decorator
|
163
187
|
|
164
|
-
# Returns a ruby OpenURL::ContextObject (NISO Z39.88).
|
165
|
-
def to_openurl
|
166
|
-
return nil if openurl_disabled
|
167
|
-
|
168
|
-
BentoSearch::OpenurlCreator.new(self).to_openurl
|
169
|
-
end
|
170
|
-
|
171
|
-
##################
|
172
|
-
# Presentation related methods.
|
173
|
-
# yes, it really makes sense to include them here, they can be overridden
|
174
|
-
# by decorators.
|
175
|
-
# May extract these to their own base decorator module at some point,
|
176
|
-
# but the OO hieararchy would be basically the same either way.
|
177
|
-
#######################
|
178
|
-
|
179
|
-
|
180
|
-
# How to display a BentoSearch::Author object as a name
|
181
|
-
def author_display(author)
|
182
|
-
if (author.first && author.last)
|
183
|
-
"#{author.last}, #{author.first.slice(0,1)}"
|
184
|
-
elsif author.display
|
185
|
-
author.display
|
186
|
-
elsif author.last
|
187
|
-
author.last
|
188
|
-
else
|
189
|
-
nil
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# Put together title and subtitle if neccesary.
|
194
|
-
def complete_title
|
195
|
-
t = self.title
|
196
|
-
if self.subtitle
|
197
|
-
t = safe_join([t, ": ", self.subtitle], "")
|
198
|
-
end
|
199
|
-
|
200
|
-
if t.blank?
|
201
|
-
t = I18n.translate("bento_search.missing_title")
|
202
|
-
end
|
203
|
-
|
204
|
-
return t
|
205
|
-
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
# A simple user-displayable citation, _without_ author/title.
|
210
|
-
# the journal, year, vol, iss, page; or publisher and year; etc.
|
211
|
-
# Constructed from individual details. Not formal APA or MLA or anything,
|
212
|
-
# just a rough and ready display.
|
213
|
-
#
|
214
|
-
# TODO: Should this be moved to a rails helper method? Not sure.
|
215
|
-
def published_in
|
216
|
-
result_elements = []
|
217
|
-
|
218
|
-
unless year.blank?
|
219
|
-
# wrap year in a span so we can bold it.
|
220
|
-
result_elements.push "<span class='year'>#{year}</span>"
|
221
|
-
end
|
222
|
-
|
223
|
-
result_elements.push(source_title) unless source_title.blank?
|
224
|
-
|
225
|
-
if source_title.blank? && ! publisher.blank?
|
226
|
-
result_elements.push html_escape publisher
|
227
|
-
end
|
228
|
-
|
229
|
-
if (! volume.blank?) && (! issue.blank?)
|
230
|
-
result_elements.push html_escape "#{volume}(#{issue})"
|
231
|
-
else
|
232
|
-
result_elements.push html_escape volume unless volume.blank?
|
233
|
-
result_elements.push html_escape issue unless issue.blank?
|
234
|
-
end
|
235
|
-
|
236
|
-
if (! start_page.blank?) && (! end_page.blank?)
|
237
|
-
result_elements.push html_escape "pp. #{start_page}-#{end_page}"
|
238
|
-
elsif ! start_page.blank?
|
239
|
-
result_elements.push html_escape "p. #{start_page}"
|
240
|
-
end
|
241
|
-
|
242
|
-
return nil if result_elements.empty?
|
243
|
-
|
244
|
-
return result_elements.join(", ").html_safe
|
245
|
-
end
|
246
188
|
|
247
189
|
end
|
248
190
|
end
|