alexandria-zoom 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,268 @@
1
+ /*
2
+ * Copyright (C) 2005 Laurent Sansonetti <lrz@chopine.be>
3
+ *
4
+ * This library is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU Lesser General Public
6
+ * License as published by the Free Software Foundation; either
7
+ * version 2.1 of the License, or (at your option) any later version.
8
+ *
9
+ * This library is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ * Lesser General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU Lesser General Public
15
+ * License along with this library; if not, write to the Free Software
16
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ */
18
+
19
+ #include "rbzoom.h"
20
+
21
+ #ifdef MAKING_RDOC_HAPPY
22
+ mZoom = rb_define_module("ZOOM");
23
+ #endif
24
+
25
+ /* Class: ZOOM::ResultSet
26
+ * The result set object is a container for records returned from a target.
27
+ */
28
+ static VALUE cZoomResultSet;
29
+
30
+ VALUE
31
+ rbz_resultset_make (ZOOM_resultset resultset)
32
+ {
33
+ return resultset != NULL
34
+ ? Data_Wrap_Struct (cZoomResultSet,
35
+ NULL,
36
+ ZOOM_resultset_destroy,
37
+ resultset)
38
+ : Qnil;
39
+ }
40
+
41
+ static ZOOM_resultset
42
+ rbz_resultset_get (VALUE obj)
43
+ {
44
+ ZOOM_resultset resultset;
45
+
46
+ Data_Get_Struct (obj, struct ZOOM_resultset_p, resultset);
47
+ assert (resultset != NULL);
48
+
49
+ return resultset;
50
+ }
51
+
52
+ /*
53
+ * call-seq:
54
+ * set_option(key, value)
55
+ *
56
+ * key: the name of the option, as a string.
57
+ *
58
+ * value: the value of this option (as a string, integer or boolean).
59
+ *
60
+ * Sets an option on the result set.
61
+ *
62
+ * Returns: self.
63
+ */
64
+ static VALUE
65
+ rbz_resultset_set_option (VALUE self, VALUE key, VALUE val)
66
+ {
67
+ ZOOM_resultset_option_set (rbz_resultset_get (self),
68
+ RVAL2CSTR (key),
69
+ RVAL2CSTR (rb_obj_as_string (val)));
70
+
71
+ return self;
72
+ }
73
+
74
+ /*
75
+ * call-seq:
76
+ * get_option(key)
77
+ *
78
+ * key: the name of the option, as a string.
79
+ *
80
+ * Gets the value of a result set's option.
81
+ *
82
+ * Returns: the value of the given option, as a string, integer or boolean.
83
+ */
84
+ static VALUE
85
+ rbz_resultset_get_option (VALUE self, VALUE key)
86
+ {
87
+ const char *value;
88
+
89
+ value = ZOOM_resultset_option_get (rbz_resultset_get (self),
90
+ RVAL2CSTR (key));
91
+
92
+ return zoom_option_value_to_ruby_value (value);
93
+ }
94
+
95
+ /*
96
+ * Returns: the number of hits.
97
+ */
98
+ static VALUE
99
+ rbz_resultset_size (VALUE self)
100
+ {
101
+ return INT2NUM (ZOOM_resultset_size (rbz_resultset_get (self)));
102
+ }
103
+
104
+ /*
105
+ * call-seq:
106
+ * [](key)
107
+ *
108
+ * key: either an integer, a range or an interval of 2 integers.
109
+ *
110
+ * Retrieves one or many records from the result set, according to the given
111
+ * key.
112
+ *
113
+ * # Gets the first record.
114
+ * rset[0]
115
+ * # Gets the first, second and third records.
116
+ * rset[1..3]
117
+ * # Gets three records starting from the second one.
118
+ * rset[2, 3]
119
+ *
120
+ * Returns: one or many references to ZOOM::Record objects.
121
+ */
122
+ static VALUE
123
+ rbz_resultset_index (int argc, VALUE *argv, VALUE self)
124
+ {
125
+ ZOOM_record *records;
126
+ ZOOM_record record;
127
+ VALUE ary;
128
+ size_t begin;
129
+ size_t count;
130
+ size_t i;
131
+
132
+ if (argc == 1) {
133
+ VALUE arg = argv [0];
134
+
135
+ if (TYPE (arg) == T_FIXNUM || TYPE (arg) == T_BIGNUM) {
136
+ record = ZOOM_resultset_record (rbz_resultset_get (self),
137
+ NUM2LONG (arg));
138
+ return record != NULL
139
+ ? rbz_record_make (ZOOM_record_clone (record))
140
+ : Qnil;
141
+ }
142
+
143
+ if (CLASS_OF (arg) == rb_cRange) {
144
+ begin = NUM2LONG (rb_funcall (arg, rb_intern ("begin"), 0));
145
+ count = NUM2LONG (rb_funcall (arg, rb_intern ("end"), 0));
146
+ count -= begin;
147
+ }
148
+ else
149
+ rb_raise (rb_eArgError,
150
+ "Invalid argument of type %s (not Numeric or Range)",
151
+ rb_class2name (CLASS_OF (arg)));
152
+ }
153
+ else {
154
+ VALUE rb_begin;
155
+ VALUE rb_count;
156
+
157
+ rb_scan_args (argc, argv, "2", &rb_begin, &rb_count);
158
+
159
+ begin = NUM2LONG (rb_begin);
160
+ count = NUM2LONG (rb_count);
161
+ }
162
+
163
+ ary = rb_ary_new ();
164
+ if (count == 0)
165
+ return ary;
166
+
167
+ /* Allocate array */
168
+ records = ALLOC_N (ZOOM_record, count);
169
+
170
+ /* Download records in batches */
171
+ ZOOM_resultset_records (rbz_resultset_get (self), records, begin, count);
172
+
173
+ /* Test the first record in the set. If null, then fall back. If valid,
174
+ * generate the ruby array.
175
+ */
176
+
177
+ if (records[0]!=NULL) {
178
+ for (i = 0; i < count; i++)
179
+
180
+ /* We don't want any null records -- if there is on in the resultset,
181
+ * ignore it.
182
+ */
183
+
184
+ if (records[i]!=NULL)
185
+ rb_ary_push (ary, rbz_record_make (ZOOM_record_clone (records [i])));
186
+ } else {
187
+ /* This is our fallback function
188
+ * It exists for those anomalies where the server
189
+ * will not respect the batch request and will return just
190
+ * a null array (per change request 36 where Laurent Sansonetti notes
191
+ * Retrieves the record one by one using ZOOM_resultset_record instead
192
+ * of getting them all in once with ZOOM_resultset_records (for a strange
193
+ * reason sometimes the resultset was not empty but ZOOM_resultset_records
194
+ * used to return empty records).
195
+ */
196
+
197
+ for (i = 0; i < count; i++) {
198
+ record = ZOOM_resultset_record (rbz_resultset_get (self),
199
+ begin + i);
200
+ /* Ignore null records */
201
+ if (record != NULL)
202
+ rb_ary_push (ary, rbz_record_make (ZOOM_record_clone (record)));
203
+ }
204
+ }
205
+
206
+ return ary;
207
+ }
208
+
209
+ /*
210
+ * Lists the records inside the result set.
211
+ *
212
+ * Returns: an array of ZOOM::Record objects.
213
+ */
214
+ static VALUE
215
+ rbz_resultset_records (VALUE self)
216
+ {
217
+ VALUE argv [2];
218
+
219
+ argv [0] = INT2FIX (0);
220
+ argv [1] = rbz_resultset_size (self);
221
+
222
+ return rbz_resultset_index (2, argv, self);
223
+ }
224
+
225
+ /*
226
+ * call-seq:
227
+ * each_record { |record| ... }
228
+ *
229
+ * Parses the records inside the result set and call the given block for each
230
+ * record, passing a reference to a ZOOM::Record object as parameter.
231
+ *
232
+ * Returns: self.
233
+ */
234
+ static VALUE
235
+ rbz_resultset_each_record (VALUE self)
236
+ {
237
+ rb_ary_each (rbz_resultset_records (self));
238
+ return self;
239
+ }
240
+
241
+ void
242
+ Init_zoom_resultset (VALUE mZoom)
243
+ {
244
+ VALUE c;
245
+
246
+ c = rb_define_class_under (mZoom, "ResultSet", rb_cObject);
247
+ rb_undef_alloc_func(c);
248
+
249
+ rb_undef_method (CLASS_OF (c), "new");
250
+ rb_define_method (c, "set_option", rbz_resultset_set_option, 2);
251
+ rb_define_method (c, "get_option", rbz_resultset_get_option, 1);
252
+
253
+ define_zoom_option (c, "start");
254
+ define_zoom_option (c, "count");
255
+ define_zoom_option (c, "presentChunk");
256
+ define_zoom_option (c, "elementSetName");
257
+ define_zoom_option (c, "preferredRecordSyntax");
258
+ define_zoom_option (c, "schema");
259
+ define_zoom_option (c, "setname");
260
+
261
+ rb_define_method (c, "size", rbz_resultset_size, 0);
262
+ rb_define_alias (c, "length", "size");
263
+ rb_define_method (c, "records", rbz_resultset_records, 0);
264
+ rb_define_method (c, "each_record", rbz_resultset_each_record, 0);
265
+ rb_define_method (c, "[]", rbz_resultset_index, -1);
266
+
267
+ cZoomResultSet = c;
268
+ }
data/sample/hello.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'zoom'
2
+
3
+ ZOOM::Connection.open('z3950.loc.gov', 7090) do |conn|
4
+ conn.database_name = 'Voyager'
5
+ conn.preferred_record_syntax = 'USMARC'
6
+ rset = conn.search('@attr 1=7 0253333490')
7
+ p rset[0]
8
+ end
data/sample/needle.rb ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/ruby -w
2
+ #
3
+ # Given a list of ISBNs and Z39.50 servers, try to get a MARC record
4
+ # for each book. The servers listed here seem to be the best
5
+ # worldwide. If an ISBN cannot be found then we use the xisbn service
6
+ # to try different editions.
7
+ #
8
+ # TODO: If more then one record is found, pick the longest one.
9
+ #
10
+ # By Devin Bayer - Summer 2006 - Public Domain
11
+ #
12
+ #
13
+ require 'rubygems'
14
+ require 'zoom'
15
+ require 'net/http'
16
+ Net::HTTP.version_1_2
17
+ require 'rexml/document'
18
+
19
+ servers_source = [
20
+ # Server, Username, Password
21
+ [ 'z3950.loc.gov:7090/Voyager' ], # Library of Congress
22
+ [ 'amicus.nlc-bnc.ca/ANY', 'USERNAME', 'PASSWORD' ], # Canada
23
+ [ 'catnyp.nypl.org:210/INNOPAC' ], # New York Public
24
+ [ 'z3950.copac.ac.uk:2100/COPAC' ], # United Kingdom
25
+ [ 'z3950.btj.se:210/BURK' ], # Sweden
26
+ [ '195.249.206.204:210/Default' ], # DenMark
27
+ [ 'library.ox.ac.uk:210/ADVANCE' ], # Oxford
28
+ [ '216.16.224.199:210/INNOPAC' ], # Cambridge
29
+ [ 'prodorbis.library.yale.edu:7090/Voyager' ], # Yale
30
+ [ 'zsrv.library.northwestern.edu:11090/Voyager' ]] # NorthWestern University
31
+
32
+ #########################################
33
+ # Connect to each server
34
+ #########################################
35
+ @servers = Array.new
36
+
37
+ servers_source.each do |array|
38
+ tries = 0
39
+ $stderr.puts 'INFO: connecting to ' + array[0]
40
+ begin
41
+ con = ZOOM::Connection.new()
42
+ con.preferred_record_syntax = 'MARC21'
43
+ con.element_set_name = 'F'
44
+ if array[1] then
45
+ con.user = array[1]
46
+ con.password = array[2]
47
+ end
48
+ con.connect(array[0])
49
+ @servers << con
50
+ rescue RuntimeError
51
+ $stderr.puts 'ERROR: connecting to ' + array[0] + ': ' + $!
52
+ tries += 1
53
+ retry if tries < 3
54
+ $stderr.puts 'WARNING: giving up on ' + array[0]
55
+ end
56
+ end
57
+
58
+ #################################
59
+ # search for ISBN on each server
60
+ # return true if found
61
+ #################################
62
+ def search(isbn)
63
+ @servers.each do |con|
64
+ begin
65
+ rset = con.search("@attr 1=7 #{isbn}")
66
+ if rset.size > 0 and rset[0].to_s.chomp.length > 1 then
67
+ $stderr.puts con.host + ': ' + isbn + ': ' +
68
+ rset.size.to_s + ' records'
69
+ puts rset[0].raw('marc8')
70
+ return true
71
+ end
72
+ rescue RuntimeError
73
+ $stderr.puts 'WARNING: ' + con.host + ': ' +
74
+ isbn + ': ' + $!
75
+ end
76
+ end
77
+ return false
78
+ end
79
+
80
+ #########################################
81
+ # Lookup each ISBN
82
+ #########################################
83
+ failed_isbns = Array.new
84
+
85
+ xisbn = Net::HTTP.new('labs.oclc.org')
86
+
87
+ File.open('isbns').each_line do |isbn|
88
+ isbn = isbn.chomp.upcase
89
+ next if isbn.length < 1
90
+ if not search(isbn) then
91
+ tries = 0
92
+ # Try alternate editions
93
+ begin
94
+ xisbn.start if not xisbn.started?
95
+ xml = xisbn.get("/xisbn/#{isbn}").body
96
+ rescue SocketError, Errno::ECONNRESET, Errno::EPIPE, EOFError, Timeout::Error
97
+ xisbn.finish if xisbn.started?
98
+ if tries < 3 then
99
+ tries += 1
100
+ retry
101
+ else
102
+ $stderr.puts "ERROR: xisbn failure: " + $!
103
+ end
104
+ end
105
+ found = false
106
+ REXML::Document.new(xml).root.each_element do |elem|
107
+ alt = elem.texts.to_s.chomp.upcase
108
+ next if alt == isbn
109
+ if search(alt) then
110
+ $stderr.puts "INFO: alternate for #{isbn}: #{alt}"
111
+ found = true
112
+ break
113
+ end
114
+ end
115
+ if not found then
116
+ $stderr.puts "INFO: isbn #{isbn} not found"
117
+ failed_isbns << isbn
118
+ end
119
+ end
120
+ end
121
+
122
+ if failed_isbns.size > 0 then
123
+ $stderr.puts "ISBN's not found:"
124
+ failed_isbns.each { |isbn| $stderr.puts isbn }
125
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alexandria-zoom
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - Matijs van Zuijlen
8
+ - Laurent Sansonetti
9
+ - Ed Summers
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2023-04-28 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: pkg-config
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 1.5.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 1.5.1
29
+ - !ruby/object:Gem::Dependency
30
+ name: rake
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '13.0'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '13.0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rake-compiler
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.2'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.2'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rake-manifest
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 0.2.0
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 0.2.0
71
+ - !ruby/object:Gem::Dependency
72
+ name: rubocop
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1.41'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '1.41'
85
+ - !ruby/object:Gem::Dependency
86
+ name: test-unit
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '3.3'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '3.3'
99
+ description:
100
+ email:
101
+ executables: []
102
+ extensions:
103
+ - ext/zoom/extconf.rb
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ChangeLog
107
+ - LICENSE
108
+ - README.md
109
+ - ext/zoom/extconf.rb
110
+ - ext/zoom/rbzoom.c
111
+ - ext/zoom/rbzoom.h
112
+ - ext/zoom/rbzoomconnection.c
113
+ - ext/zoom/rbzoomoptions.c
114
+ - ext/zoom/rbzoompackage.c
115
+ - ext/zoom/rbzoomquery.c
116
+ - ext/zoom/rbzoomrecord.c
117
+ - ext/zoom/rbzoomresultset.c
118
+ - sample/hello.rb
119
+ - sample/needle.rb
120
+ homepage: https://github.com/mvz/alexandria-zoom
121
+ licenses:
122
+ - LGPL-2.1
123
+ metadata:
124
+ rubygems_mfa_required: 'true'
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: 2.7.0
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubygems_version: 3.4.12
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Ruby/ZOOM provides a Ruby binding to the Z39.50 Object-Orientation Model
144
+ (ZOOM), an abstract object-oriented programming interface to a subset of the services
145
+ specified by the Z39.50 standard, also known as the international standard ISO 23950.
146
+ test_files: []