sru 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sru.rb +8 -0
- data/lib/sru/client.rb +95 -0
- data/lib/sru/exception.rb +5 -0
- data/lib/sru/explain.rb +16 -0
- data/lib/sru/response.rb +38 -0
- data/lib/sru/scan.rb +16 -0
- data/lib/sru/search_retrieve.rb +30 -0
- data/lib/sru/term.rb +17 -0
- data/test.rb +49 -0
- metadata +48 -0
data/lib/sru.rb
ADDED
data/lib/sru/client.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'cgi'
|
3
|
+
require 'net/http'
|
4
|
+
require 'rexml/document'
|
5
|
+
|
6
|
+
module SRU
|
7
|
+
|
8
|
+
# A client for issuing requests to a particular SRU server.
|
9
|
+
# SRU is a RESTlike information retrieval protocol detailed at
|
10
|
+
# http://www.loc.gov/standards/sru/
|
11
|
+
|
12
|
+
class Client
|
13
|
+
|
14
|
+
# creates a client object which will automatically perform an
|
15
|
+
# explain request to determine the version to be used in
|
16
|
+
# subsequent requests.
|
17
|
+
|
18
|
+
def initialize(base)
|
19
|
+
@server = URI.parse base
|
20
|
+
|
21
|
+
# stash this away for future requests
|
22
|
+
@version = self.explain.version
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Send an explain request to the SRU server and return a
|
27
|
+
# SRU::ExplainResponse object.
|
28
|
+
#
|
29
|
+
# client = SRU::Client.new 'http://sru.example.com'
|
30
|
+
# explain = client.explain
|
31
|
+
|
32
|
+
def explain
|
33
|
+
doc = get_doc(:operation => 'explain')
|
34
|
+
return ExplainResponse.new(doc)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Send a searchRetrieve request to the SRU server and return
|
39
|
+
# a SRU::SearchResponse object. The first argument is the required
|
40
|
+
# query option. Any remaining searchRetrieve options can be passed
|
41
|
+
# as an optional second argument.
|
42
|
+
#
|
43
|
+
# client = SRU::Client.new 'http://example.com/sru'
|
44
|
+
# response = client.search_retrieve('mark twain', maximumRecords => 1)
|
45
|
+
|
46
|
+
def search_retrieve(query, options={})
|
47
|
+
options['query'] = query
|
48
|
+
options['operation'] = 'searchRetrieve'
|
49
|
+
doc = get_doc(options)
|
50
|
+
return SearchResponse.new(doc)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Send a scan request to the SRU server and return a SRU::ScanResponse
|
55
|
+
# object. You must supply the first parameter which is the searchClause.
|
56
|
+
# Other SRU options can be sent in a hash as the seond argument.
|
57
|
+
#
|
58
|
+
# scan_response = client.scan 'title
|
59
|
+
|
60
|
+
def scan(clause, options={})
|
61
|
+
options['scanClause'] = clause
|
62
|
+
options['operation'] = 'scan'
|
63
|
+
doc = get_doc(options)
|
64
|
+
return ScanResponse.new(doc)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# helper to fetch xml responses from the sru server
|
70
|
+
# given a set of options
|
71
|
+
|
72
|
+
def get_doc(hash)
|
73
|
+
# all requests get a version
|
74
|
+
hash['version'] = @version
|
75
|
+
|
76
|
+
# don't want to monkey with the original
|
77
|
+
uri = @server.clone
|
78
|
+
|
79
|
+
# no ruby class for building a query string!?!?
|
80
|
+
# probably just wasn't looking in the right place
|
81
|
+
parts = hash.entries.map { |entry|
|
82
|
+
"#{entry[0]}=#{CGI.escape(entry[1].to_s)}"
|
83
|
+
}
|
84
|
+
uri.query = parts.join('&')
|
85
|
+
|
86
|
+
# fetch the xml and build/return a document object from it
|
87
|
+
begin
|
88
|
+
xml = Net::HTTP.get(uri)
|
89
|
+
return REXML::Document.new(xml)
|
90
|
+
rescue
|
91
|
+
raise SRU::Exception, "exception during SRU operation", caller
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/sru/explain.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'sru/response'
|
2
|
+
|
3
|
+
module SRU
|
4
|
+
class ExplainResponse < Response
|
5
|
+
def version
|
6
|
+
version = xpath('.//zs:explainResponse/zs:version')
|
7
|
+
return version if version
|
8
|
+
|
9
|
+
# also look here
|
10
|
+
info = xpath_first('.//serverInfo')
|
11
|
+
return info.attributes['version'] if info
|
12
|
+
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/sru/response.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rexml/xpath'
|
2
|
+
|
3
|
+
module SRU
|
4
|
+
|
5
|
+
# base class for all SRU responses
|
6
|
+
class Response
|
7
|
+
attr_reader :doc
|
8
|
+
|
9
|
+
# namespaces for use in xpath queries
|
10
|
+
@@namespaces = {'zs' => 'http://www.loc.gov/zing/srw/'}
|
11
|
+
|
12
|
+
def initialize(doc)
|
13
|
+
@doc = doc
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# get all nodes that match an xpath
|
19
|
+
def xpath_all(path)
|
20
|
+
return REXML::XPath.match(@doc, path, @@namespaces)
|
21
|
+
end
|
22
|
+
|
23
|
+
# get the first node that matches an xpath
|
24
|
+
def xpath_first(path)
|
25
|
+
elements = xpath_all(path)
|
26
|
+
return elements[0] if elements != nil
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# get the text inside the first node that matches the xpath
|
31
|
+
def xpath(path)
|
32
|
+
e = xpath_first(path)
|
33
|
+
return e.text if e != nil
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/sru/scan.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'sru/response'
|
2
|
+
|
3
|
+
module SRU
|
4
|
+
|
5
|
+
# A wrapper for the scan response from a SRU server.
|
6
|
+
class ScanResponse < Response
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def each
|
10
|
+
for term_node in xpath_all('.//zs:term')
|
11
|
+
yield Term.new(term_node)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'sru/response'
|
2
|
+
|
3
|
+
module SRU
|
4
|
+
|
5
|
+
# An iterator for search results which allows you to do stuff like:
|
6
|
+
#
|
7
|
+
# client = SRU::Client.new 'http://sru.example.com'
|
8
|
+
# for record in client.search_retrieve('Mark Twain')
|
9
|
+
# puts record
|
10
|
+
# end
|
11
|
+
|
12
|
+
class SearchResponse < Response
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
def number_of_records
|
16
|
+
return Integer(xpath('.//zs:numberOfRecords'))
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the contents of each recordData element in a
|
20
|
+
# SRU searchRetrieve response.
|
21
|
+
|
22
|
+
def each
|
23
|
+
for record_data in xpath_all('.//zs:recordData')
|
24
|
+
if record_data.elements.size > 0
|
25
|
+
yield record_data.elements[1]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/sru/term.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module SRU
|
2
|
+
|
3
|
+
# a class for representing a term in the response from a sru server
|
4
|
+
class Term < Response
|
5
|
+
attr_accessor :value, :number_of_records, :displayTerm, :whereInList,
|
6
|
+
:extraTermData
|
7
|
+
|
8
|
+
def initialize(element)
|
9
|
+
super element
|
10
|
+
@value = xpath('value')
|
11
|
+
@number_of_records = xpath('numberOfRecords')
|
12
|
+
@display_term = xpath('displayTerm')
|
13
|
+
@whereInList = xpath('whereInList')
|
14
|
+
@extraTermData = xpath_first('extraTermData')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/test.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift 'lib'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'sru'
|
6
|
+
|
7
|
+
class ClientTests < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def test_explain
|
10
|
+
client = SRU::Client.new 'http://z3950.loc.gov:7090/voyager'
|
11
|
+
explain = client.explain
|
12
|
+
assert_equal SRU::ExplainResponse, explain.class
|
13
|
+
assert_equal '1.1', explain.version
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_search_retrieve
|
17
|
+
client = SRU::Client.new 'http://z3950.loc.gov:7090/voyager'
|
18
|
+
results = client.search_retrieve 'twain', :maximumRecords => 5
|
19
|
+
assert_equal 5, results.entries.size
|
20
|
+
assert results.number_of_records > 2000
|
21
|
+
assert_equal REXML::Element, results.entries[0].class
|
22
|
+
assert_equal 'record', results.entries[0].name
|
23
|
+
|
24
|
+
# hopefully there isn't a document that matches this :)
|
25
|
+
results = client.search_retrieve 'fidkidkdiejfl'
|
26
|
+
assert_equal 0, results.entries.size
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_scan
|
30
|
+
# this scan response appears to be canned might need to change
|
31
|
+
client = SRU::Client.new 'http://tweed.lib.ed.ac.uk:8080/elf/search/copac'
|
32
|
+
scan = client.scan('foobar')
|
33
|
+
assert scan.entries.size > 0
|
34
|
+
assert_equal SRU::Term, scan.entries[0].class
|
35
|
+
assert_equal 'low', scan.entries[0].value
|
36
|
+
assert_equal '1', scan.entries[0].number_of_records
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_xml_exception
|
40
|
+
assert_raise(SRU::Exception) {SRU::Client.new 'http://www.google.com'}
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_http_exception
|
44
|
+
assert_raise(SRU::Exception) {SRU::Client.new 'http://example.com'}
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: sru
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2006-04-16 00:00:00 -05:00
|
8
|
+
summary: talk to sru servers with ruby
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: ehs@pobox.com
|
12
|
+
homepage: http://www.textualize.com/sruby
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: sru
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
authors:
|
30
|
+
- Ed Summers
|
31
|
+
files:
|
32
|
+
- lib/sru
|
33
|
+
- lib/sru.rb
|
34
|
+
- lib/sru/client.rb
|
35
|
+
- lib/sru/exception.rb
|
36
|
+
- lib/sru/explain.rb
|
37
|
+
- lib/sru/response.rb
|
38
|
+
- lib/sru/scan.rb
|
39
|
+
- lib/sru/search_retrieve.rb
|
40
|
+
- lib/sru/term.rb
|
41
|
+
test_files:
|
42
|
+
- test.rb
|
43
|
+
rdoc_options: []
|
44
|
+
extra_rdoc_files: []
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
requirements: []
|
48
|
+
dependencies: []
|