bento_search 0.9.0 → 1.0.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 +47 -14
- data/app/item_decorators/bento_search/standard_decorator.rb +30 -12
- data/app/models/bento_search/link.rb +4 -0
- data/app/models/bento_search/multi_searcher.rb +3 -1
- data/app/models/bento_search/registrar.rb +31 -2
- data/app/models/bento_search/result_item.rb +52 -14
- data/app/models/bento_search/search_engine.rb +18 -12
- data/app/search_engines/bento_search/google_books_engine.rb +9 -2
- data/app/search_engines/bento_search/google_site_search_engine.rb +1 -1
- data/app/search_engines/bento_search/summon_engine.rb +13 -5
- data/app/search_engines/bento_search/worldcat_sru_dc_engine.rb +1 -1
- data/app/views/bento_search/_atom_item.atom.builder +135 -0
- data/app/views/bento_search/atom_results.atom.builder +61 -0
- data/lib/bento_search.rb +4 -2
- data/lib/bento_search/version.rb +1 -1
- data/test/dummy/config/routes.rb +3 -1
- data/test/dummy/log/test.log +45768 -0
- data/test/support/atom.xsd.xml +240 -0
- data/test/unit/openurl_creator_test.rb +2 -4
- data/test/unit/register_engine_test.rb +59 -0
- data/test/unit/summon_engine_test.rb +1 -5
- data/test/view/atom_results_test.rb +281 -0
- metadata +11 -5
@@ -0,0 +1,240 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<xs:schema targetNamespace="http://www.w3.org/2005/Atom" elementFormDefault="qualified"
|
3
|
+
attributeFormDefault="unqualified"
|
4
|
+
xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
5
|
+
<xs:annotation>
|
6
|
+
<xs:documentation>
|
7
|
+
This version of the Atom schema is based on version 1.0 of the format specifications,
|
8
|
+
found here http://www.atomenabled.org/developers/syndication/atom-format-spec.php.
|
9
|
+
</xs:documentation>
|
10
|
+
</xs:annotation>
|
11
|
+
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/03/xml.xsd" />
|
12
|
+
<xs:annotation>
|
13
|
+
<xs:documentation>
|
14
|
+
An Atom document may have two root elements, feed and entry, as defined in section 2.
|
15
|
+
</xs:documentation>
|
16
|
+
</xs:annotation>
|
17
|
+
<xs:element name="feed" type="atom:feedType"/>
|
18
|
+
<xs:element name="entry" type="atom:entryType"/>
|
19
|
+
<xs:complexType name="textType" mixed="true">
|
20
|
+
<xs:annotation>
|
21
|
+
<xs:documentation>
|
22
|
+
The Atom text construct is defined in section 3.1 of the format spec.
|
23
|
+
</xs:documentation>
|
24
|
+
</xs:annotation>
|
25
|
+
<xs:sequence>
|
26
|
+
<xs:any namespace="http://www.w3.org/1999/xhtml" minOccurs="0"/>
|
27
|
+
</xs:sequence>
|
28
|
+
<xs:attribute name="type" >
|
29
|
+
<xs:simpleType>
|
30
|
+
<xs:restriction base="xs:token">
|
31
|
+
<xs:enumeration value="text"/>
|
32
|
+
<xs:enumeration value="html"/>
|
33
|
+
<xs:enumeration value="xhtml"/>
|
34
|
+
</xs:restriction>
|
35
|
+
</xs:simpleType>
|
36
|
+
</xs:attribute>
|
37
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
38
|
+
</xs:complexType>
|
39
|
+
<xs:complexType name="personType">
|
40
|
+
<xs:annotation>
|
41
|
+
<xs:documentation>
|
42
|
+
The Atom person construct is defined in section 3.2 of the format spec.
|
43
|
+
</xs:documentation>
|
44
|
+
</xs:annotation>
|
45
|
+
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
46
|
+
<xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
|
47
|
+
<xs:element name="uri" type="atom:uriType" minOccurs="0" maxOccurs="1" />
|
48
|
+
<xs:element name="email" type="atom:emailType" minOccurs="0" maxOccurs="1" />
|
49
|
+
<xs:any namespace="##other"/>
|
50
|
+
</xs:choice>
|
51
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
52
|
+
</xs:complexType>
|
53
|
+
<xs:simpleType name="emailType">
|
54
|
+
<xs:annotation>
|
55
|
+
<xs:documentation>
|
56
|
+
Schema definition for an email address.
|
57
|
+
</xs:documentation>
|
58
|
+
</xs:annotation>
|
59
|
+
<xs:restriction base="xs:normalizedString">
|
60
|
+
<xs:pattern value="\w+@(\w+\.)+\w+" />
|
61
|
+
</xs:restriction>
|
62
|
+
</xs:simpleType>
|
63
|
+
<xs:complexType name="feedType">
|
64
|
+
<xs:annotation>
|
65
|
+
<xs:documentation>
|
66
|
+
The Atom feed construct is defined in section 4.1.1 of the format spec.
|
67
|
+
</xs:documentation>
|
68
|
+
</xs:annotation>
|
69
|
+
<xs:choice minOccurs="3" maxOccurs="unbounded">
|
70
|
+
<xs:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
|
71
|
+
<xs:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded" />
|
72
|
+
<xs:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
|
73
|
+
<xs:element name="generator" type="atom:generatorType" minOccurs="0" maxOccurs="1" />
|
74
|
+
<xs:element name="icon" type="atom:iconType" minOccurs="0" maxOccurs="1" />
|
75
|
+
<xs:element name="id" type="atom:idType" minOccurs="1" maxOccurs="1" />
|
76
|
+
<xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded" />
|
77
|
+
<xs:element name="logo" type="atom:logoType" minOccurs="0" maxOccurs="1" />
|
78
|
+
<xs:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1" />
|
79
|
+
<xs:element name="subtitle" type="atom:textType" minOccurs="0" maxOccurs="1" />
|
80
|
+
<xs:element name="title" type="atom:textType" minOccurs="1" maxOccurs="1" />
|
81
|
+
<xs:element name="updated" type="atom:dateTimeType" minOccurs="1" maxOccurs="1" />
|
82
|
+
<xs:element name="entry" type="atom:entryType" minOccurs="0" maxOccurs="unbounded" />
|
83
|
+
<xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" />
|
84
|
+
</xs:choice>
|
85
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
86
|
+
</xs:complexType>
|
87
|
+
<xs:complexType name="entryType">
|
88
|
+
<xs:annotation>
|
89
|
+
<xs:documentation>
|
90
|
+
The Atom entry construct is defined in section 4.1.2 of the format spec.
|
91
|
+
</xs:documentation>
|
92
|
+
</xs:annotation>
|
93
|
+
<xs:choice maxOccurs="unbounded">
|
94
|
+
<xs:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
|
95
|
+
<xs:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded" />
|
96
|
+
<xs:element name="content" type="atom:contentType" minOccurs="0" maxOccurs="1" />
|
97
|
+
<xs:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
|
98
|
+
<xs:element name="id" type="atom:idType" minOccurs="1" maxOccurs="1" />
|
99
|
+
<xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded" />
|
100
|
+
<xs:element name="published" type="atom:dateTimeType" minOccurs="0" maxOccurs="1" />
|
101
|
+
<xs:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1" />
|
102
|
+
<xs:element name="source" type="atom:textType" minOccurs="0" maxOccurs="1" />
|
103
|
+
<xs:element name="summary" type="atom:textType" minOccurs="0" maxOccurs="1" />
|
104
|
+
<xs:element name="title" type="atom:textType" minOccurs="1" maxOccurs="1" />
|
105
|
+
<xs:element name="updated" type="atom:dateTimeType" minOccurs="1" maxOccurs="1" />
|
106
|
+
<xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
|
107
|
+
</xs:choice>
|
108
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
109
|
+
</xs:complexType>
|
110
|
+
<xs:complexType name="contentType" mixed="true">
|
111
|
+
<xs:annotation>
|
112
|
+
<xs:documentation>
|
113
|
+
The Atom content construct is defined in section 4.1.3 of the format spec.
|
114
|
+
</xs:documentation>
|
115
|
+
</xs:annotation>
|
116
|
+
<xs:sequence>
|
117
|
+
<xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" />
|
118
|
+
</xs:sequence>
|
119
|
+
<xs:attribute name="type" type="xs:string"/>
|
120
|
+
<xs:attribute name="src" type="xs:anyURI"/>
|
121
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
122
|
+
</xs:complexType>
|
123
|
+
<xs:complexType name="categoryType">
|
124
|
+
<xs:annotation>
|
125
|
+
<xs:documentation>
|
126
|
+
The Atom cagegory construct is defined in section 4.2.2 of the format spec.
|
127
|
+
</xs:documentation>
|
128
|
+
</xs:annotation>
|
129
|
+
<xs:attribute name="term" type="xs:string" use="required"/>
|
130
|
+
<xs:attribute name="scheme" type="xs:anyURI" use="optional"/>
|
131
|
+
<xs:attribute name="label" type="xs:string" use="optional"/>
|
132
|
+
<xs:attributeGroup ref="atom:commonAttributes" />
|
133
|
+
</xs:complexType>
|
134
|
+
<xs:complexType name="generatorType">
|
135
|
+
<xs:annotation>
|
136
|
+
<xs:documentation>
|
137
|
+
The Atom generator element is defined in section 4.2.4 of the format spec.
|
138
|
+
</xs:documentation>
|
139
|
+
</xs:annotation>
|
140
|
+
<xs:simpleContent>
|
141
|
+
<xs:extension base="xs:string">
|
142
|
+
<xs:attribute name="uri" use="optional" type="xs:anyURI" />
|
143
|
+
<xs:attribute name="version" use="optional" type="xs:string" />
|
144
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
145
|
+
</xs:extension>
|
146
|
+
</xs:simpleContent>
|
147
|
+
</xs:complexType>
|
148
|
+
<xs:complexType name="iconType">
|
149
|
+
<xs:annotation>
|
150
|
+
<xs:documentation>
|
151
|
+
The Atom icon construct is defined in section 4.2.5 of the format spec.
|
152
|
+
</xs:documentation>
|
153
|
+
</xs:annotation>
|
154
|
+
<xs:simpleContent>
|
155
|
+
<xs:extension base="xs:anyURI">
|
156
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
157
|
+
</xs:extension>
|
158
|
+
</xs:simpleContent>
|
159
|
+
</xs:complexType>
|
160
|
+
<xs:complexType name="idType">
|
161
|
+
<xs:annotation>
|
162
|
+
<xs:documentation>
|
163
|
+
The Atom id construct is defined in section 4.2.6 of the format spec.
|
164
|
+
</xs:documentation>
|
165
|
+
</xs:annotation>
|
166
|
+
<xs:simpleContent>
|
167
|
+
<xs:extension base="xs:anyURI">
|
168
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
169
|
+
</xs:extension>
|
170
|
+
</xs:simpleContent>
|
171
|
+
</xs:complexType>
|
172
|
+
<xs:complexType name="linkType" mixed="true">
|
173
|
+
<xs:annotation>
|
174
|
+
<xs:documentation>
|
175
|
+
The Atom link construct is defined in section 3.4 of the format spec.
|
176
|
+
</xs:documentation>
|
177
|
+
</xs:annotation>
|
178
|
+
<xs:attribute name="href" use="required" type="xs:anyURI" />
|
179
|
+
<xs:attribute name="rel" type="xs:string" use="optional"/>
|
180
|
+
<xs:attribute name="type" use="optional" type="xs:string" />
|
181
|
+
<xs:attribute name="hreflang" use="optional" type="xs:NMTOKEN" />
|
182
|
+
<xs:attribute name="title" use="optional" type="xs:string" />
|
183
|
+
<xs:attribute name="length" use="optional" type="xs:positiveInteger" />
|
184
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
185
|
+
</xs:complexType>
|
186
|
+
<xs:complexType name="logoType">
|
187
|
+
<xs:annotation>
|
188
|
+
<xs:documentation>
|
189
|
+
The Atom logo construct is defined in section 4.2.8 of the format spec.
|
190
|
+
</xs:documentation>
|
191
|
+
</xs:annotation>
|
192
|
+
<xs:simpleContent>
|
193
|
+
<xs:extension base="xs:anyURI">
|
194
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
195
|
+
</xs:extension>
|
196
|
+
</xs:simpleContent>
|
197
|
+
</xs:complexType>
|
198
|
+
<xs:complexType name="sourceType">
|
199
|
+
<xs:annotation>
|
200
|
+
<xs:documentation>
|
201
|
+
The Atom source construct is defined in section 4.2.11 of the format spec.
|
202
|
+
</xs:documentation>
|
203
|
+
</xs:annotation>
|
204
|
+
<xs:choice maxOccurs="unbounded">
|
205
|
+
<xs:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded"/>
|
206
|
+
<xs:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded"/>
|
207
|
+
<xs:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded"/>
|
208
|
+
<xs:element name="generator" type="atom:generatorType" minOccurs="0" maxOccurs="1"/>
|
209
|
+
<xs:element name="icon" type="atom:iconType" minOccurs="0" maxOccurs="1"/>
|
210
|
+
<xs:element name="id" type="atom:idType" minOccurs="0" maxOccurs="1"/>
|
211
|
+
<xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded"/>
|
212
|
+
<xs:element name="logo" type="atom:logoType" minOccurs="0" maxOccurs="1"/>
|
213
|
+
<xs:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1"/>
|
214
|
+
<xs:element name="subtitle" type="atom:textType" minOccurs="0" maxOccurs="1"/>
|
215
|
+
<xs:element name="title" type="atom:textType" minOccurs="0" maxOccurs="1"/>
|
216
|
+
<xs:element name="updated" type="atom:dateTimeType" minOccurs="0" maxOccurs="1"/>
|
217
|
+
<xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
|
218
|
+
</xs:choice>
|
219
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
220
|
+
</xs:complexType>
|
221
|
+
<xs:complexType name="uriType">
|
222
|
+
<xs:simpleContent>
|
223
|
+
<xs:extension base="xs:anyURI">
|
224
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
225
|
+
</xs:extension>
|
226
|
+
</xs:simpleContent>
|
227
|
+
</xs:complexType>
|
228
|
+
<xs:complexType name="dateTimeType">
|
229
|
+
<xs:simpleContent>
|
230
|
+
<xs:extension base="xs:dateTime">
|
231
|
+
<xs:attributeGroup ref="atom:commonAttributes"/>
|
232
|
+
</xs:extension>
|
233
|
+
</xs:simpleContent>
|
234
|
+
</xs:complexType>
|
235
|
+
<xs:attributeGroup name="commonAttributes">
|
236
|
+
<xs:attribute ref="xml:base" />
|
237
|
+
<xs:attribute ref="xml:lang" />
|
238
|
+
<xs:anyAttribute namespace="##other"/>
|
239
|
+
</xs:attributeGroup>
|
240
|
+
</xs:schema>
|
@@ -14,8 +14,7 @@ class OpenurlCreatorTest < ActiveSupport::TestCase
|
|
14
14
|
def test_create_article
|
15
15
|
item = decorated_item(
|
16
16
|
:format => "Article",
|
17
|
-
:title => "My Title",
|
18
|
-
:subtitle => "A Nice One",
|
17
|
+
:title => "My Title: A Nice One",
|
19
18
|
:year => 2012,
|
20
19
|
:volume => "10",
|
21
20
|
:issue => "1",
|
@@ -53,8 +52,7 @@ class OpenurlCreatorTest < ActiveSupport::TestCase
|
|
53
52
|
def test_numeric_conversion
|
54
53
|
item = decorated_item(
|
55
54
|
:format => "Article",
|
56
|
-
:title => "My Title",
|
57
|
-
:subtitle => "A Nice One",
|
55
|
+
:title => "My Title: A Nice One",
|
58
56
|
:year => 2012,
|
59
57
|
:volume => 10,
|
60
58
|
:issue => 1,
|
@@ -46,5 +46,64 @@ class RegisterEngineTest < ActiveSupport::TestCase
|
|
46
46
|
assert_raise(BentoSearch::NoSuchEngine) { BentoSearch.get_engine("not_registered")}
|
47
47
|
end
|
48
48
|
|
49
|
+
test "returns configuration" do
|
50
|
+
returned_configuration = BentoSearch.register_engine("test_engine") do |conf|
|
51
|
+
conf.engine = "DummyEngine"
|
52
|
+
conf.api_key = "dummy"
|
53
|
+
end
|
54
|
+
|
55
|
+
assert_kind_of Confstruct::Configuration, returned_configuration
|
56
|
+
end
|
57
|
+
|
58
|
+
test "can take data argument instead of block" do
|
59
|
+
BentoSearch.register_engine("test_engine",
|
60
|
+
{:engine => "DummyEngine", :api_key => "dummy"}
|
61
|
+
)
|
62
|
+
|
63
|
+
engine = BentoSearch.get_engine("test_engine")
|
64
|
+
|
65
|
+
assert_kind_of BentoSearch::DummyEngine, engine
|
66
|
+
assert_equal "dummy", engine.configuration.api_key
|
67
|
+
end
|
68
|
+
|
69
|
+
test "block over-rides data argument" do
|
70
|
+
args = {:engine => "DummyEngine", :api_key => "dummy", :other_thing => "other_thing"}
|
71
|
+
|
72
|
+
BentoSearch.register_engine("test_engine", args) do |conf|
|
73
|
+
conf.api_key = "new_api_key"
|
74
|
+
end
|
75
|
+
|
76
|
+
engine = BentoSearch.get_engine("test_engine")
|
77
|
+
|
78
|
+
|
79
|
+
assert_equal "new_api_key", engine.configuration.api_key
|
80
|
+
assert_equal "other_thing", engine.configuration.other_thing
|
81
|
+
end
|
82
|
+
|
83
|
+
test "use one config as base for another" do
|
84
|
+
source_configuration = BentoSearch.register_engine("source_engine") do |conf|
|
85
|
+
conf.engine = "DummyEngine"
|
86
|
+
conf.api_key = "api_key"
|
87
|
+
conf.for_display do |display|
|
88
|
+
display.title = "source_title"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
BentoSearch.register_engine("derived_engine", source_configuration) do |conf|
|
93
|
+
conf.for_display do |display|
|
94
|
+
display.title = "derived_title"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
source_engine = BentoSearch.get_engine("source_engine")
|
99
|
+
derived_engine = BentoSearch.get_engine("derived_engine")
|
100
|
+
|
101
|
+
assert_equal "api_key", derived_engine.configuration.api_key
|
102
|
+
assert_equal "derived_title", derived_engine.configuration.for_display.title
|
103
|
+
|
104
|
+
assert_equal "source_title", source_engine.configuration.for_display.title
|
105
|
+
|
106
|
+
end
|
107
|
+
|
49
108
|
end
|
50
109
|
|
@@ -169,11 +169,7 @@ class SummonEngineTest < ActiveSupport::TestCase
|
|
169
169
|
|
170
170
|
assert_include first.title, '<b class="bento_search_highlight">'
|
171
171
|
|
172
|
-
assert first.title.html_safe?, "title is HTML safe"
|
173
|
-
|
174
|
-
assert_present first.custom_data['raw_title']
|
175
|
-
assert_not_include first.custom_data['raw_title'], '<b class="bento_search_highlight">'
|
176
|
-
|
172
|
+
assert first.title.html_safe?, "title is HTML safe"
|
177
173
|
end
|
178
174
|
|
179
175
|
test_with_cassette("live #get(id)", :summon) do
|
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
class AtomResultsTest < ActionView::TestCase
|
6
|
+
include ActionView::Helpers::UrlHelper
|
7
|
+
|
8
|
+
@@namespaces = {
|
9
|
+
"atom" => "http://www.w3.org/2005/Atom",
|
10
|
+
"opensearch" => "http://a9.com/-/spec/opensearch/1.1/",
|
11
|
+
"prism" => "http://prismstandard.org/namespaces/basic/2.1/",
|
12
|
+
"dcterms" => "http://purl.org/dc/terms/",
|
13
|
+
"bibo" => "http://purl.org/ontology/bibo/"
|
14
|
+
}
|
15
|
+
|
16
|
+
# Instead of using assert_select, we do it ourselves with nokogiri
|
17
|
+
# for better namespace control.
|
18
|
+
#
|
19
|
+
# xml = Nokogiri::XML( rendered )
|
20
|
+
# assert_node(xml, "atom:entry") do |matched_nodes|
|
21
|
+
# assert matched_node.first["attribute"] == "foo"
|
22
|
+
# end
|
23
|
+
def assert_node(xml, xpath, options = {})
|
24
|
+
result = xml.xpath(xpath, @@namespaces)
|
25
|
+
|
26
|
+
assert result.length > 0, "Expected xpath '#{xpath}' to match in #{xml.to_s[0..200]}..."
|
27
|
+
|
28
|
+
if options[:text]
|
29
|
+
assert_equal options[:text], result.text.strip, "Expected #{options[:text]} as content of #{result.to_s[0..200]}"
|
30
|
+
end
|
31
|
+
|
32
|
+
yield result if block_given?
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup
|
36
|
+
@total_items = 1000
|
37
|
+
@start = 6
|
38
|
+
@per_page = 15
|
39
|
+
|
40
|
+
|
41
|
+
@engine = BentoSearch::MockEngine.new(:total_items => @total_items)
|
42
|
+
@results = @engine.search("some query", :start => @start, :per_page => @per_page)
|
43
|
+
|
44
|
+
# but fill the first result elements with some non-blank data to test
|
45
|
+
@article = BentoSearch::ResultItem.new(
|
46
|
+
:title => "An Article", #
|
47
|
+
:link => "http://example.org/main_link", #
|
48
|
+
:unique_id => "UNIQUE_ID",
|
49
|
+
:format => "Article", #
|
50
|
+
:format_str => "Uncontrolled format", #
|
51
|
+
:language_code => "en", #
|
52
|
+
:year => "2004", #
|
53
|
+
:volume => "10", #
|
54
|
+
:issue => "1", #
|
55
|
+
:start_page => "101", #
|
56
|
+
:end_page => "110", #
|
57
|
+
:source_title => "Journal of Something", #
|
58
|
+
:issn => "12345678", #
|
59
|
+
:doi => "10.1000/182", #
|
60
|
+
:abstract => "This is an abstract with escaped > parts < ", #
|
61
|
+
:authors => [ #
|
62
|
+
BentoSearch::Author.new(:first => "John", :last => "Smith"),
|
63
|
+
BentoSearch::Author.new(:display => "Jones, Jane")
|
64
|
+
],
|
65
|
+
:other_links => [ #
|
66
|
+
BentoSearch::Link.new(:url => "http://example.org/bare_link"),
|
67
|
+
BentoSearch::Link.new(
|
68
|
+
:url => "http://example.org/label_and_type",
|
69
|
+
:label => "A link somewhere",
|
70
|
+
:type => "application/pdf"
|
71
|
+
),
|
72
|
+
BentoSearch::Link.new(
|
73
|
+
:url => "http://example.org/rel",
|
74
|
+
:rel => "something"
|
75
|
+
)
|
76
|
+
]
|
77
|
+
)
|
78
|
+
@article_with_html_abstract = BentoSearch::ResultItem.new(
|
79
|
+
:title => "foo",
|
80
|
+
:format => "Article",
|
81
|
+
:abstract => "This is <b>html</b>".html_safe
|
82
|
+
)
|
83
|
+
@article_with_full_date = BentoSearch::ResultItem.new(
|
84
|
+
:title => "foo",
|
85
|
+
:format => "Article",
|
86
|
+
:publication_date => Date.new(2011, 5, 6)
|
87
|
+
)
|
88
|
+
@book = BentoSearch::ResultItem.new(
|
89
|
+
:title => "A Book",
|
90
|
+
:format => "Book",
|
91
|
+
:publisher => "Some publisher",
|
92
|
+
:isbn => "123456789X",
|
93
|
+
:oclcnum => "12124345",
|
94
|
+
:year => "2004"
|
95
|
+
)
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
@results[0] = @article
|
100
|
+
@results[1] = @article_with_html_abstract
|
101
|
+
@results[3] = @article_with_full_date
|
102
|
+
@results[4] = @book
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_smoke_atom_validate
|
106
|
+
# Validate under Atom schema. Should we validate under prism and dc schemas
|
107
|
+
# too? Not sure if it makes sense, or if there's even a relevant schema
|
108
|
+
# for how we're using em. So of just basic 'smoke' value.
|
109
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
110
|
+
xml_response = Nokogiri::XML( rendered ) { |config| config.strict }
|
111
|
+
|
112
|
+
atom_xsd_filepath = File.expand_path("../../support/atom.xsd.xml", __FILE__)
|
113
|
+
schema_xml = Nokogiri::XML(File.read(atom_xsd_filepath))
|
114
|
+
# modify to add processContents lax so it'll let us include elements from
|
115
|
+
# external namespaces.
|
116
|
+
schema_xml.xpath("//xs:any[@namespace='##other']", {"xs" => "http://www.w3.org/2001/XMLSchema"}).each do |node|
|
117
|
+
node["processContents"] = "lax"
|
118
|
+
end
|
119
|
+
|
120
|
+
schema = Nokogiri::XML::Schema.from_document( schema_xml )
|
121
|
+
|
122
|
+
assert_empty schema.validate(xml_response), "Validates with atom XSD schema"
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def test_feed_metadata
|
127
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
128
|
+
xml_response = Nokogiri::XML( rendered )
|
129
|
+
|
130
|
+
assert_node(xml_response, "atom:feed") do |feed|
|
131
|
+
assert_node(feed, "atom:title")
|
132
|
+
assert_node(feed, "atom:author")
|
133
|
+
assert_node(feed, "atom:updated")
|
134
|
+
|
135
|
+
assert_node(feed, "opensearch:totalResults", :text => @total_items.to_s)
|
136
|
+
assert_node(feed, "opensearch:startIndex", :text => @start.to_s)
|
137
|
+
assert_node(feed, "opensearch:itemsPerPage", :text => @per_page.to_s)
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_article_entry_example
|
143
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
144
|
+
xml_response = Nokogiri::XML( rendered )
|
145
|
+
|
146
|
+
assert_node(xml_response, "./atom:feed/atom:entry[1]") do |article|
|
147
|
+
assert_node(article, "atom:title", :text => @article.title)
|
148
|
+
assert_node(article, "prism:coverDate", :text => @article.year)
|
149
|
+
|
150
|
+
assert_node(article, "prism:issn", :text => @article.issn)
|
151
|
+
assert_node(article, "prism:doi", :text => @article.doi)
|
152
|
+
|
153
|
+
assert_node(article, "prism:volume", :text => @article.volume)
|
154
|
+
assert_node(article, "prism:number", :text => @article.issue)
|
155
|
+
|
156
|
+
assert_node(article, "prism:startingPage", :text => @article.start_page)
|
157
|
+
assert_node(article, "prism:endingPage", :text => @article.end_page)
|
158
|
+
|
159
|
+
assert_node(article, "prism:publicationName", :text => @article.source_title)
|
160
|
+
|
161
|
+
abstract = article.at_xpath("atom:summary", @@namespaces)
|
162
|
+
assert_present abstract, "Has an abstract"
|
163
|
+
assert_equal "text", abstract["type"], "Abstract type text"
|
164
|
+
assert_equal @article.abstract, abstract.text
|
165
|
+
|
166
|
+
assert_node(article, "dcterms:language[@vocabulary='http://dbpedia.org/resource/ISO_639-1']", :text => @article.language_iso_639_1)
|
167
|
+
assert_node(article, "dcterms:language[@vocabulary='http://dbpedia.org/resource/ISO_639-3']", :text => @article.language_iso_639_3)
|
168
|
+
assert_node(article, "dcterms:language[not(@vocabulary)]", :text => @article.language_str)
|
169
|
+
|
170
|
+
assert_node(article, "dcterms:type[not(@vocabulary)]", :text => @article.format_str)
|
171
|
+
|
172
|
+
assert_node(article, "dcterms:type[@vocabulary='http://schema.org/']", :text => @article.schema_org_type_url)
|
173
|
+
assert_node(article, "dcterms:type[@vocabulary='http://purl.org/NET/bento_search/ontology']", :text => @article.format)
|
174
|
+
|
175
|
+
# Just make sure right number of author elements, with right structure.
|
176
|
+
assert_node(article, "atom:author/atom:name") do |authors|
|
177
|
+
assert_equal @article.authors.length, authors.length, "right number of author elements"
|
178
|
+
end
|
179
|
+
|
180
|
+
# Links. Main link is just rel=alternate
|
181
|
+
assert_node(article,
|
182
|
+
"atom:link[@rel='alternate'][@href='#{@article.link}']")
|
183
|
+
|
184
|
+
# other links also there, default rel=related
|
185
|
+
assert_node(article,
|
186
|
+
"atom:link[@rel='related'][@type='application/pdf'][@title='A link somewhere'][@href='http://example.org/label_and_type']")
|
187
|
+
assert_node(article,
|
188
|
+
"atom:link[@rel='something'][@href='http://example.org/rel']")
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def test_with_unique_id
|
195
|
+
@results = @engine.search("find")
|
196
|
+
@results[0] = BentoSearch::ResultItem.new(
|
197
|
+
:title => "Something",
|
198
|
+
:unique_id => "a000:/01",
|
199
|
+
:engine_id => "some_engine"
|
200
|
+
)
|
201
|
+
|
202
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
203
|
+
xml_response = Nokogiri::XML( rendered )
|
204
|
+
|
205
|
+
with_unique_id = xml_response.xpath("./atom:feed/atom:entry", @@namespaces)[0]
|
206
|
+
|
207
|
+
assert_node(with_unique_id, "atom:id") do |id|
|
208
|
+
# based off of engine_id and unique_id
|
209
|
+
assert_include id.text, "some_engine"
|
210
|
+
assert_include id.text, "a000%3A%2F01"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_with_html_abstract
|
215
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
216
|
+
xml_response = Nokogiri::XML( rendered )
|
217
|
+
|
218
|
+
with_html_abstract = xml_response.xpath("./atom:feed/atom:entry", @@namespaces)[1]
|
219
|
+
|
220
|
+
assert_node(with_html_abstract, "atom:summary[@type='html']", :text => @article_with_html_abstract.abstract.to_s)
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_book
|
224
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
225
|
+
xml_response = Nokogiri::XML( rendered )
|
226
|
+
|
227
|
+
book = xml_response.xpath("./atom:feed/atom:entry", @@namespaces)[4]
|
228
|
+
|
229
|
+
assert_node(book, "dcterms:type[@vocabulary='http://purl.org/NET/bento_search/ontology']", :text => "Book")
|
230
|
+
assert_node(book, "dcterms:type[@vocabulary='http://schema.org/']", :text => "http://schema.org/Book")
|
231
|
+
|
232
|
+
assert_node(book, "dcterms:publisher", :text => @book.publisher)
|
233
|
+
|
234
|
+
assert_node(book, "prism:isbn", :text => @book.isbn)
|
235
|
+
|
236
|
+
assert_node(book, "bibo:oclcnum", :text => @book.oclcnum)
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_with_full_date
|
240
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => @results}
|
241
|
+
xml_response = Nokogiri::XML( rendered )
|
242
|
+
|
243
|
+
with_full_date = xml_response.at_xpath("./atom:feed/atom:entry[4]", @@namespaces)
|
244
|
+
|
245
|
+
assert_node(with_full_date, "prism:coverDate", :text => "2011-05-06")
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_nil_results
|
249
|
+
# should render a more or less empty atom response for
|
250
|
+
# nil results, convenient to not raise on nil
|
251
|
+
render :template => "bento_search/atom_results", :locals => {:atom_results => nil}
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_locals_for_feed_name_and_author
|
255
|
+
render( :template => "bento_search/atom_results",
|
256
|
+
:locals => {:atom_results => @results,
|
257
|
+
:feed_name => "My Feed",
|
258
|
+
:feed_author_name => "ACME Seed And Feed Products"}
|
259
|
+
)
|
260
|
+
|
261
|
+
xml_response = Nokogiri::XML( rendered )
|
262
|
+
|
263
|
+
assert_node(xml_response, "./atom:feed/atom:title", :text => "My Feed")
|
264
|
+
assert_node(xml_response, "./atom:feed/atom:author/atom:name", :text => "ACME Seed And Feed Products")
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_html_in_title_stripped
|
268
|
+
results = BentoSearch::Results.new
|
269
|
+
results << BentoSearch::ResultItem.new(
|
270
|
+
:title => "html <b>title</b>".html_safe
|
271
|
+
)
|
272
|
+
|
273
|
+
render(:template => "bento_search/atom_results", :locals => {:atom_results => results})
|
274
|
+
xml_response = Nokogiri::XML( rendered )
|
275
|
+
|
276
|
+
assert_node(xml_response, "./atom:feed/atom:entry[1]/atom:title", :text => "html title")
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
end
|