sru 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.
- 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: []
|