openurl 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/openurl.rb +10 -0
- data/lib/openurl/context_object.rb +530 -0
- data/lib/openurl/context_object_entity.rb +303 -0
- data/lib/openurl/transport.rb +172 -0
- metadata +50 -0
data/lib/openurl.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# A library to create and parse NISO Z39.88 OpenURLs
|
2
|
+
# Can also work with OpenURL 0.1, but your YMMV
|
3
|
+
# See: http://alcme.oclc.org/openurl/docs/implementation_guidelines/
|
4
|
+
# for more information on implementing NISO Z39.88
|
5
|
+
require 'date'
|
6
|
+
require 'rexml/document'
|
7
|
+
require 'cgi'
|
8
|
+
require 'openurl/context_object'
|
9
|
+
require 'openurl/context_object_entity'
|
10
|
+
require 'openurl/transport'
|
@@ -0,0 +1,530 @@
|
|
1
|
+
module OpenURL
|
2
|
+
|
3
|
+
# The ContextObject class is intended to both create new OpenURL 1.0 context
|
4
|
+
# objects or parse existing ones, either from Key-Encoded Values (KEVs) or XML.
|
5
|
+
# Usage:
|
6
|
+
# require 'openurl/context_object'
|
7
|
+
# include OpenURL
|
8
|
+
# ctx = ContextObject.new
|
9
|
+
# ctx.referent.set_format('journal')
|
10
|
+
# ctx.referent.add_identifier('info:doi/10.1016/j.ipm.2005.03.024')
|
11
|
+
# ctx.referent.set_metadata('issn', '0306-4573')
|
12
|
+
# ctx.referent.set_metadata('aulast', 'Bollen')
|
13
|
+
# ctx.referrer.add_identifier('info:sid/google')
|
14
|
+
# puts ctx.kev
|
15
|
+
# # url_ver=Z39.88-2004&ctx_tim=2007-10-29T12%3A18%3A53-0400&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&ctx_id=&rft.issn=0306-4573&rft.aulast=Bollen&rft_val_fmt=info%3Aofi%2Ffmt%3Axml%3Axsd%3Ajournal&rft_id=info%3Adoi%2F10.1016%2Fj.ipm.2005.03.024&rfr_id=info%3Asid%2Fgoogle
|
16
|
+
|
17
|
+
class ContextObject
|
18
|
+
|
19
|
+
attr_accessor(:referent, :referringEntity, :requestor, :referrer, :serviceType, :resolver, :custom)
|
20
|
+
attr_reader(:admin)
|
21
|
+
@@defined_entities = {"rft"=>"referent", "rfr"=>"referrer", "rfe"=>"referring-entity", "req"=>"requestor", "svc"=>"service-type", "res"=>"resolver"}
|
22
|
+
|
23
|
+
# Creates a new ContextObject object and initializes the ContextObjectEntities.
|
24
|
+
|
25
|
+
def initialize()
|
26
|
+
@referent = ReferentEntity.new()
|
27
|
+
@referringEntity = ReferringEntity.new()
|
28
|
+
@requestor = RequestorEntity.new()
|
29
|
+
@referrer = ReferrerEntity.new()
|
30
|
+
@serviceType = [ServiceTypeEntity.new()]
|
31
|
+
@resolver = [ResolverEntity.new()]
|
32
|
+
@custom = []
|
33
|
+
@admin = {"ctx_ver"=>{"label"=>"version", "value"=>"Z39.88-2004"}, "ctx_tim"=>{"label"=>"timestamp", "value"=>DateTime.now().to_s}, "ctx_id"=>{"label"=>"identifier", "value"=>""}, "ctx_enc"=>{"label"=>"encoding", "value"=>"info:ofi/enc:UTF-8"}}
|
34
|
+
end
|
35
|
+
|
36
|
+
def deep_copy
|
37
|
+
cloned = ContextObject.new
|
38
|
+
cloned.import_context_object( self )
|
39
|
+
return cloned
|
40
|
+
end
|
41
|
+
|
42
|
+
# Serialize the ContextObject to XML.
|
43
|
+
|
44
|
+
def xml
|
45
|
+
doc = REXML::Document.new()
|
46
|
+
coContainer = doc.add_element "ctx:context-objects"
|
47
|
+
coContainer.add_namespace("ctx","info:ofi/fmt:xml:xsd:ctx")
|
48
|
+
coContainer.add_namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
49
|
+
coContainer.add_attribute("xsi:schemaLocation", "info:ofi/fmt:xml:xsd:ctx http://www.openurl.info/registry/docs/info:ofi/fmt:xml:xsd:ctx")
|
50
|
+
co = coContainer.add_element "ctx:context-object"
|
51
|
+
@admin.each_key do |k|
|
52
|
+
co.add_attribute(@admin[k]["label"], @admin[k]["value"])
|
53
|
+
end
|
54
|
+
|
55
|
+
[@referent, @referringEntity, @requestor, @referrer].each do | ent |
|
56
|
+
ent.xml(co) unless ent.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
[@serviceType, @resolver, @custom].each do |entCont|
|
60
|
+
entCont.each do |ent|
|
61
|
+
ent.xml(co) unless ent.empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
return doc.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
# Alias for .xml
|
69
|
+
|
70
|
+
def sap2
|
71
|
+
return xml
|
72
|
+
end
|
73
|
+
|
74
|
+
# Output the ContextObject as a Key-encoded value string. Pass a boolean
|
75
|
+
# true argument if you do not want the ctx_tim key included.
|
76
|
+
|
77
|
+
def kev(no_date=false)
|
78
|
+
require 'cgi'
|
79
|
+
kevs = ["url_ver=Z39.88-2004"]
|
80
|
+
|
81
|
+
# Loop through the administrative metadata
|
82
|
+
@admin.each_key do |k|
|
83
|
+
next if k == "ctx_tim" && no_date
|
84
|
+
kevs.push(k+"="+CGI.escape(@admin[k]["value"].to_s)) if @admin[k]["value"]
|
85
|
+
end
|
86
|
+
|
87
|
+
[@referent, @referringEntity, @requestor, @referrer].each do | ent |
|
88
|
+
kevs.push(ent.kev) unless ent.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
[@serviceType, @resolver, @custom].each do |entCont|
|
92
|
+
entCont.each do |ent|
|
93
|
+
kevs.push(ent.kev) unless ent.empty?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
return kevs.join("&")
|
97
|
+
end
|
98
|
+
|
99
|
+
# Outputs the ContextObject as a ruby hash.
|
100
|
+
|
101
|
+
def to_hash
|
102
|
+
co_hash = {"url_ver"=>"Z39.88-2004"}
|
103
|
+
|
104
|
+
@admin.each_key do |k|
|
105
|
+
co_hash[k]=@admin[k]["value"] if @admin[k]["value"]
|
106
|
+
end
|
107
|
+
|
108
|
+
[@referent, @referringEntity, @requestor, @referrer].each do | ent |
|
109
|
+
co_hash.merge!(ent.to_hash) unless ent.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
[@serviceType, @resolver, @custom].each do |entCont|
|
113
|
+
entCont.each do |ent|
|
114
|
+
co_hash.merge!(ent.to_hash) unless ent.empty?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
return co_hash
|
118
|
+
end
|
119
|
+
|
120
|
+
# Alias for .kev
|
121
|
+
|
122
|
+
def sap1
|
123
|
+
return kev
|
124
|
+
end
|
125
|
+
|
126
|
+
# Outputs a COinS (ContextObject in SPANS) span tag for the ContextObject.
|
127
|
+
# Arguments are any other CSS classes you want included and the innerHTML
|
128
|
+
# content.
|
129
|
+
|
130
|
+
def coins (classnames=nil, innerHTML=nil)
|
131
|
+
return "<span class='Z3988 #{classnames}' title='"+CGI.escapeHTML(self.kev(true))+"'>#{innerHTML}</span>"
|
132
|
+
end
|
133
|
+
|
134
|
+
# Adds another ServiceType entity to the context object and returns the
|
135
|
+
# array index of the new object.
|
136
|
+
|
137
|
+
def add_service_type_entity
|
138
|
+
@serviceType << ServiceTypeEntity.new
|
139
|
+
return @serviceType.index(@serviceType.last)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Adds another Resolver entity to the context object and returns the
|
143
|
+
# array index of the new object.
|
144
|
+
|
145
|
+
def add_resolver_entity
|
146
|
+
@resolver << ResolverEntity.new
|
147
|
+
return @resolver.index(@resolver.last)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Adds a custom entity to the ContextObject and returns array index of the
|
151
|
+
# new object. Expects an abbreviation and label for KEV and XML output.
|
152
|
+
|
153
|
+
def add_custom_entity(abbr=nil, label=nil)
|
154
|
+
@custom << CustomEntity.new(abbr, label)
|
155
|
+
return @custom.index(@custom.last)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns the appropriate CustomEntity for the given entity abbreviation.
|
159
|
+
|
160
|
+
def custom_entity(abbr)
|
161
|
+
return @custom.find { |c| c.abbr == abbr }
|
162
|
+
end
|
163
|
+
|
164
|
+
# Sets a ContextObject administration field.
|
165
|
+
|
166
|
+
def set_administration_key(key, val)
|
167
|
+
raise ArgumentException, "#{key} is not a valid admin key!" unless @admin.keys.index(key)
|
168
|
+
@admin[key]["value"] = val
|
169
|
+
end
|
170
|
+
|
171
|
+
# Imports an existing Key-encoded value string and sets the appropriate
|
172
|
+
# entities.
|
173
|
+
|
174
|
+
def import_kev(kev)
|
175
|
+
co = CGI::parse(kev)
|
176
|
+
co2 = {}
|
177
|
+
co.each_key do |k|
|
178
|
+
# Only take the first value from the value array
|
179
|
+
co2[k] = co[k][0]
|
180
|
+
end
|
181
|
+
self.import_hash(co2)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Initialize a new ContextObject object from an existing KEV
|
185
|
+
|
186
|
+
def self.new_from_kev(kev)
|
187
|
+
co = self.new
|
188
|
+
co.import_kev(kev)
|
189
|
+
return co
|
190
|
+
end
|
191
|
+
|
192
|
+
# Imports an existing XML encoded context object and sets the appropriate
|
193
|
+
# entities
|
194
|
+
|
195
|
+
def import_xml(xml)
|
196
|
+
doc = REXML::Document.new xml
|
197
|
+
# Cut to the context object
|
198
|
+
ctx = REXML::XPath.first(doc, ".//ctx:context-object", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
|
199
|
+
ctx.attributes.each do |attr, val|
|
200
|
+
@admin.each do |adm, vals|
|
201
|
+
self.set_administration_key(adm, val) if vals["label"] == attr
|
202
|
+
end
|
203
|
+
end
|
204
|
+
ctx.to_a.each do | ent |
|
205
|
+
if @@defined_entities.value?(ent.name())
|
206
|
+
var = @@defined_entities.keys[@@defined_entities.values.index(ent.name())]
|
207
|
+
meth = "import_#{var}_node"
|
208
|
+
self.send(meth, ent)
|
209
|
+
else
|
210
|
+
self.import_custom_node(ent)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Initialize a new ContextObject object from an existing XML ContextObject
|
216
|
+
|
217
|
+
def self.new_from_xml(xml)
|
218
|
+
co = self.new
|
219
|
+
co.import_xml(xml)
|
220
|
+
return co
|
221
|
+
end
|
222
|
+
|
223
|
+
# Searches the Custom Entities for the key/value pair and returns an array
|
224
|
+
# of the @custom array keys of any matches.
|
225
|
+
|
226
|
+
def search_custom_entities(key, val)
|
227
|
+
matches = []
|
228
|
+
@custom.each do |cus|
|
229
|
+
begin
|
230
|
+
matches << @custom.index(cus) if cus.instance_variable_get('@'+key) == val
|
231
|
+
rescue NameError
|
232
|
+
next
|
233
|
+
end
|
234
|
+
end
|
235
|
+
return matches
|
236
|
+
end
|
237
|
+
|
238
|
+
# Imports an existing hash of ContextObject values and sets the appropriate
|
239
|
+
# entities.
|
240
|
+
|
241
|
+
def import_hash(hash)
|
242
|
+
ref = {}
|
243
|
+
openurl_keys = ["url_ver", "url_tim", "url_ctx_fmt"]
|
244
|
+
hash.each do |key, val|
|
245
|
+
if openurl_keys.include?(key)
|
246
|
+
next # None of these matter much for our purposes
|
247
|
+
elsif @admin.has_key?(key)
|
248
|
+
self.set_administration_key(key, val)
|
249
|
+
elsif key.downcase.match(/^[a-z]{3}_val_fmt$/)
|
250
|
+
# Realistically should only be rft or rfe: get the format
|
251
|
+
(entity, v, fmt) = key.split("_")
|
252
|
+
ent = self.translate_abbr(entity)
|
253
|
+
eval("@"+ent).set_format(val)
|
254
|
+
elsif key.match(/^[a-z]{3}_ref/)
|
255
|
+
# determines if we have a by-reference context object
|
256
|
+
(entity, v, fmt) = key.split("_")
|
257
|
+
ent = self.translate_abbr(entity)
|
258
|
+
# by-reference requires two fields, format and location, if this is
|
259
|
+
# the first field we've run across, set a place holder until we get
|
260
|
+
# the other value
|
261
|
+
unless ref[entity]
|
262
|
+
if fmt
|
263
|
+
ref_key = "format"
|
264
|
+
else
|
265
|
+
ref_key = "location"
|
266
|
+
end
|
267
|
+
ref[entity] = [ref_key, val]
|
268
|
+
else
|
269
|
+
if ref[entity][0] == "format"
|
270
|
+
eval("@"+ent).set_reference(val, ref[entity][1])
|
271
|
+
else
|
272
|
+
eval("@"+ent).set_reference(ref[entity][1], val)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
elsif key.match(/^[a-z]{3}_id$/)
|
276
|
+
# Get the entity identifier
|
277
|
+
(entity, v) = key.split("_")
|
278
|
+
ent = self.translate_abbr(entity)
|
279
|
+
eval("@"+ent).set_identifier(val)
|
280
|
+
elsif key.match(/^[a-z]{3}_dat$/)
|
281
|
+
# Get any private data
|
282
|
+
(entity, v) = key.split("_")
|
283
|
+
ent = self.translate_abbr(entity)
|
284
|
+
eval("@"+ent).set_private_data(val)
|
285
|
+
else
|
286
|
+
# collect the entity metadata
|
287
|
+
keyparts = key.split(".")
|
288
|
+
if keyparts.length > 1
|
289
|
+
# This is 1.0 OpenURL
|
290
|
+
ent = self.translate_abbr(keyparts[0])
|
291
|
+
eval("@"+ent).set_metadata(keyparts[1], val)
|
292
|
+
else
|
293
|
+
# This is a 0.1 OpenURL. Your mileage may vary on how accurately
|
294
|
+
# this maps.
|
295
|
+
if key == 'id'
|
296
|
+
@referent.set_identifier(val)
|
297
|
+
elsif key == 'sid'
|
298
|
+
@referrer.set_identifier("info:sid/"+val.to_s)
|
299
|
+
else
|
300
|
+
@referent.set_metadata(key, val)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Initialize a new ContextObject object from an existing key/value hash
|
307
|
+
|
308
|
+
def self.new_from_hash(hash)
|
309
|
+
co = self.new
|
310
|
+
co.import_hash(hash)
|
311
|
+
return co
|
312
|
+
end
|
313
|
+
|
314
|
+
# if we don't have a referent format (most likely because we have a 0.1
|
315
|
+
# OpenURL), try to determine something from the genre. If that doesn't
|
316
|
+
# exist, just call it a journal since most 0.1 OpenURLs would be one,
|
317
|
+
# anyway.
|
318
|
+
unless @referent.format
|
319
|
+
fmt = case @referent.metadata['genre']
|
320
|
+
when /article|journal|issue|proceeding|conference|preprint/ then 'journal'
|
321
|
+
when /book|bookitem|report|document/ then 'book'
|
322
|
+
else 'journal'
|
323
|
+
end
|
324
|
+
@referent.set_format(fmt)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Translates the abbreviated entity (rft, rfr, etc.) to the associated class
|
329
|
+
# name. For repeatable entities, uses the first object in the array. Returns
|
330
|
+
# a string of the object name which would then be eval'ed to call a method
|
331
|
+
# upon.
|
332
|
+
|
333
|
+
def translate_abbr(abbr)
|
334
|
+
if @@defined_entities.has_key?abbr
|
335
|
+
ent = @@defined_entities[abbr]
|
336
|
+
if ent == "service-type"
|
337
|
+
ent = "serviceType[0]"
|
338
|
+
elsif ent == "resolver"
|
339
|
+
ent = "resolver[0]"
|
340
|
+
elsif ent == "referring-entity"
|
341
|
+
ent = "referringEntity"
|
342
|
+
end
|
343
|
+
else
|
344
|
+
idx = self.search_custom_entities("abbr", abbr)
|
345
|
+
if idx.length == 0
|
346
|
+
self.add_custom_entity(abbr)
|
347
|
+
idx = self.search_custom_entities("abbr", abbr)
|
348
|
+
end
|
349
|
+
ent = "custom["+idx[0].to_s+"]"
|
350
|
+
end
|
351
|
+
return ent
|
352
|
+
end
|
353
|
+
|
354
|
+
# Imports an existing OpenURL::ContextObject object and sets the appropriate
|
355
|
+
# entity values.
|
356
|
+
|
357
|
+
def import_context_object(context_object)
|
358
|
+
@admin.each_key { |k|
|
359
|
+
self.set_administration_key(k, context_object.admin[k]["value"])
|
360
|
+
}
|
361
|
+
[context_object.referent, context_object.referringEntity, context_object.requestor, context_object.referrer].each {| ent |
|
362
|
+
unless ent.empty?
|
363
|
+
['identifier', 'format', 'private_data'].each { |var|
|
364
|
+
unless ent.send(var).nil?
|
365
|
+
unless ent.kind_of?(OpenURL::ReferringEntity)
|
366
|
+
eval("@"+ent.label.downcase).send('set_'+var,ent.send(var))
|
367
|
+
else
|
368
|
+
@referringEntity.send('set_'+var,ent.send(var))
|
369
|
+
end
|
370
|
+
end
|
371
|
+
}
|
372
|
+
unless ent.reference["format"].nil? or ent.reference["format"].nil?
|
373
|
+
unless ent.kind_of?(OpenURL::ReferringEntity)
|
374
|
+
eval("@"+ent.label.downcase).set_reference(ent.reference["location"], ent.reference["format"])
|
375
|
+
else
|
376
|
+
@referringEntity.set_referent(ent.reference["location"], ent.reference["format"])
|
377
|
+
end
|
378
|
+
end
|
379
|
+
ent.metadata.each_key { |k|
|
380
|
+
unless ent.metadata[k].nil?
|
381
|
+
unless ent.kind_of?(OpenURL::ReferringEntity)
|
382
|
+
eval("@"+ent.label.downcase).set_metadata(k, ent.metadata[k])
|
383
|
+
else
|
384
|
+
@referringEntity.set_metadata(k, ent.metadata[k])
|
385
|
+
end
|
386
|
+
end
|
387
|
+
}
|
388
|
+
end
|
389
|
+
}
|
390
|
+
context_object.serviceType.each { |svc|
|
391
|
+
if @serviceType[0].empty?
|
392
|
+
@serviceType[0] = svc
|
393
|
+
else
|
394
|
+
idx = self.add_service_type_entity
|
395
|
+
@serviceType[idx] = svc
|
396
|
+
end
|
397
|
+
|
398
|
+
}
|
399
|
+
context_object.resolver.each { |res|
|
400
|
+
if @resolver[0].empty?
|
401
|
+
@resolver[0] = res
|
402
|
+
else
|
403
|
+
idx = self.add_resolver_entity
|
404
|
+
@resolver[idx] = res
|
405
|
+
end
|
406
|
+
|
407
|
+
}
|
408
|
+
context_object.custom.each { |cus|
|
409
|
+
idx = self.add_custom_entity(cus.abbr, cus.label)
|
410
|
+
@custom[idx] = cus
|
411
|
+
}
|
412
|
+
end
|
413
|
+
|
414
|
+
# Initialize a new ContextObject object from an existing
|
415
|
+
# OpenURL::ContextObject
|
416
|
+
|
417
|
+
def self.new_from_context_object(context_object)
|
418
|
+
co = self.new
|
419
|
+
co.import_context_object(context_object)
|
420
|
+
return co
|
421
|
+
end
|
422
|
+
|
423
|
+
protected
|
424
|
+
|
425
|
+
def import_rft_node(node)
|
426
|
+
self.import_xml_common(@referent, node)
|
427
|
+
self.import_xml_mbv_ref(@referent, node)
|
428
|
+
end
|
429
|
+
|
430
|
+
def import_rfe_node(node)
|
431
|
+
self.import_xml_common(@referringEntity, node)
|
432
|
+
self.import_xml_mbv_ref(@referringEntity, node)
|
433
|
+
end
|
434
|
+
|
435
|
+
def import_rfr_node(node)
|
436
|
+
self.import_xml_common(@referrer, node)
|
437
|
+
self.import_xml_mbv(@referrer, node)
|
438
|
+
end
|
439
|
+
|
440
|
+
def import_req_node(node)
|
441
|
+
self.import_xml_common(@requestor, node)
|
442
|
+
self.import_xml_mbv(@requestor, node)
|
443
|
+
end
|
444
|
+
|
445
|
+
def import_svc_node(node)
|
446
|
+
if @serviceType[0].empty?
|
447
|
+
key = 0
|
448
|
+
else
|
449
|
+
key = self.add_service_type_entity
|
450
|
+
end
|
451
|
+
self.import_xml_common(@serviceType[key], node)
|
452
|
+
self.import_xml_mbv(@serviceType[key], node)
|
453
|
+
end
|
454
|
+
|
455
|
+
def import_custom_node(node)
|
456
|
+
key = self.add_custom_entity(node.name())
|
457
|
+
self.import_xml_commom(@custom[key], node)
|
458
|
+
self.import_xml_mbv(@custom[key], node)
|
459
|
+
end
|
460
|
+
|
461
|
+
def import_res_node(node)
|
462
|
+
if @resolver[0].empty?
|
463
|
+
key = 0
|
464
|
+
else
|
465
|
+
key = self.add_resolver_entity
|
466
|
+
end
|
467
|
+
self.import_xml_common(@resolver[key], node)
|
468
|
+
self.import_xml_mbv(@resolver[key], node)
|
469
|
+
end
|
470
|
+
|
471
|
+
# Parses the data that should apply to all XML context objects
|
472
|
+
|
473
|
+
def import_xml_common(ent, node)
|
474
|
+
fmt = REXML::XPath.first(node, ".//ctx:format", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
|
475
|
+
ent.set_format(fmt.get_text.value) if fmt and fmt.has_text
|
476
|
+
|
477
|
+
id = REXML::XPath.first(node, ".//ctx:identifier", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
|
478
|
+
ent.set_identifier(id.get_text.value) if id and id.has_text?
|
479
|
+
|
480
|
+
priv = REXML::XPath.first(node, ".//ctx:private-data", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
|
481
|
+
ent.set_private_data(priv.get_text.value) if priv and priv.has_text?
|
482
|
+
|
483
|
+
ref = REXML::XPath.first(node, ".//ctx:metadata-by-ref", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
|
484
|
+
if ref
|
485
|
+
ref.to_a.each do |r|
|
486
|
+
if r.name() == "format"
|
487
|
+
format = r.get_text.value
|
488
|
+
else
|
489
|
+
location = r.get_text.value
|
490
|
+
end
|
491
|
+
ent.set_reference(location, format)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Parses metadata-by-val data
|
497
|
+
|
498
|
+
def import_xml_mbv(ent, node)
|
499
|
+
mbv = REXML::XPath.first(node, ".//ctx:metadata-by-val", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
|
500
|
+
|
501
|
+
if mbv
|
502
|
+
mbv.to_a.each { |m|
|
503
|
+
ent.set_metadata(m.name(), m.get_text.value)
|
504
|
+
}
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
# Referent and ReferringEntities place their metadata-by-val inside
|
509
|
+
# the format element
|
510
|
+
|
511
|
+
def import_xml_mbv_ref(ent, node)
|
512
|
+
ns = "info:ofi/fmt:xml:xsd:"+ent.format
|
513
|
+
mbv = REXML::XPath.first(node, ".//fmt:"+ent.format, {"fmt"=>ns})
|
514
|
+
if mbv
|
515
|
+
mbv.to_a.each { |m|
|
516
|
+
if m.has_text?
|
517
|
+
ent.set_metadata(m.name(), m.get_text.value)
|
518
|
+
end
|
519
|
+
if m.has_elements?
|
520
|
+
m.to_a.each { | md |
|
521
|
+
if md.has_text?
|
522
|
+
ent.set_metadata(md.name(), md.get_text.value)
|
523
|
+
end
|
524
|
+
}
|
525
|
+
end
|
526
|
+
}
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
module OpenURL
|
2
|
+
|
3
|
+
# The ContextObjectEntity is a generic class to define an entity. It should
|
4
|
+
# not be initialized directly, only through one of its children:
|
5
|
+
# ReferentEntity, ReferrerEntity, ReferringEntity, ResolverEntity,
|
6
|
+
# ServiceTypeEntity, or CustomEntity
|
7
|
+
|
8
|
+
class ContextObjectEntity
|
9
|
+
# identifiers should always be an array, but it might be an empty one.
|
10
|
+
attr_reader(:identifiers, :reference, :format, :metadata, :private_data, :abbr, :label)
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@identifiers = []
|
14
|
+
@reference = {"format"=>nil, "location"=>nil}
|
15
|
+
@format = nil
|
16
|
+
@metadata = {}
|
17
|
+
@private_data = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets the location and format of a by-reference context object entity
|
21
|
+
|
22
|
+
def set_reference(loc, fmt)
|
23
|
+
@reference["location"] = loc
|
24
|
+
@reference["format"] = fmt
|
25
|
+
end
|
26
|
+
|
27
|
+
# Should really be called "add identifier", since we can have more
|
28
|
+
# than one. But for legacy, it's "set_identifier".
|
29
|
+
def add_identifier(val)
|
30
|
+
@identifiers.push( self.class.normalize_id(val) )
|
31
|
+
end
|
32
|
+
alias :set_identifier :add_identifier
|
33
|
+
|
34
|
+
# We can actually have more than one, but certain code calls this
|
35
|
+
# method as if there's only one. We return the first.
|
36
|
+
def identifier
|
37
|
+
return @identifiers[0]
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def set_private_data(val)
|
42
|
+
@private_data = val
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_metadata(key, val)
|
46
|
+
@metadata[key] = val
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_metadata(key)
|
50
|
+
return @metadata[key]
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_format(format)
|
54
|
+
@format = format
|
55
|
+
end
|
56
|
+
|
57
|
+
# Serializes the entity to XML and attaches it to the supplied REXML element.
|
58
|
+
|
59
|
+
def xml(co_elem)
|
60
|
+
meta = {"container"=>co_elem.add_element("ctx:"+@label)}
|
61
|
+
|
62
|
+
if @metadata.length > 0 or @format
|
63
|
+
meta["metadata-by-val"] = meta["container"].add_element("ctx:metadata-by-val")
|
64
|
+
if @format
|
65
|
+
meta["format"] = meta["container"].add_element("ctx:format")
|
66
|
+
meta["format"].text = "info:ofi/fmt:xml:xsd:"+@format
|
67
|
+
end
|
68
|
+
if @metadata.length > 0
|
69
|
+
meta["metadata"] = meta["metadata-by-val"].add_element("ctx:metadata")
|
70
|
+
@metadata.each do |k,v|
|
71
|
+
meta[k] = meta["metadata"].add_element("ctx:"+k)
|
72
|
+
meta[k].text = v
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
if @reference["format"]
|
77
|
+
meta["metadata-by-ref"] = meta["container"].add_element("ctx:metadata-by-ref")
|
78
|
+
meta["ref_format"] = meta["metadata-by-ref"].add_element("ctx:format")
|
79
|
+
meta["ref_format"].text = @reference["format"]
|
80
|
+
meta["ref_loc"] = meta["metadata-by-ref"].add_element("ctx:location")
|
81
|
+
meta["ref_loc"].text = @reference["location"]
|
82
|
+
end
|
83
|
+
|
84
|
+
@identifiers.each do |id|
|
85
|
+
# Yes, meta["identifier"] will get over-written if there's more than
|
86
|
+
# one identifier. But I dont' think this meta hash is used for much
|
87
|
+
# I don't think it's a problem. -JR
|
88
|
+
meta["identifier"] = meta["container"].add_element("ctx:identifier")
|
89
|
+
meta["identifier"].text = id
|
90
|
+
end
|
91
|
+
if @private_data
|
92
|
+
meta["private-data"] = meta["container"].add_element("ctx:private-data")
|
93
|
+
meta["private-data"].text = @private_data
|
94
|
+
end
|
95
|
+
return co_elem
|
96
|
+
end
|
97
|
+
|
98
|
+
# Outputs the entity as a KEV array
|
99
|
+
|
100
|
+
def kev
|
101
|
+
kevs = []
|
102
|
+
|
103
|
+
@metadata.each do |k,v|
|
104
|
+
kevs << "#{@abbr}.#{k}="+CGI.escape(v) if v
|
105
|
+
end
|
106
|
+
|
107
|
+
kevs << "#{@abbr}_val_fmt="+CGI.escape("info:ofi/fmt:xml:xsd:#{@format}") if @format
|
108
|
+
|
109
|
+
if @reference["format"]
|
110
|
+
kevs << "#{@abbr}_ref_fmt="+CGI.escape(@reference["format"])
|
111
|
+
kevs << "#{@abbr}_ref="+CGI.escape(@reference["location"])
|
112
|
+
end
|
113
|
+
|
114
|
+
@identifiers.each do |id|
|
115
|
+
kevs << "#{@abbr}_id="+CGI.escape(id)
|
116
|
+
end
|
117
|
+
|
118
|
+
kevs << "#{@abbr}_dat="+CGI.escape(@private_data) if @private_data
|
119
|
+
|
120
|
+
return kevs
|
121
|
+
end
|
122
|
+
|
123
|
+
# Outputs the entity as a hash
|
124
|
+
|
125
|
+
def to_hash
|
126
|
+
co_hash = {}
|
127
|
+
|
128
|
+
@metadata.each do |k,v|
|
129
|
+
co_hash["#{@abbr}.#{k}"]=v if v
|
130
|
+
end
|
131
|
+
|
132
|
+
co_hash["#{@abbr}_val_fmt"]="info:ofi/fmt:xml:xsd:#{@format}" if @format
|
133
|
+
|
134
|
+
if @reference["format"]
|
135
|
+
co_hash["#{@abbr}_ref_fmt"]=@reference["format"]
|
136
|
+
co_hash["#{@abbr}_ref"]=@reference["location"]
|
137
|
+
end
|
138
|
+
|
139
|
+
@identifiers.each do |id|
|
140
|
+
# Put em in a list.
|
141
|
+
co_hash["#{@abbr}_id"] ||= Array.new
|
142
|
+
co_hash["#{@abbr}_id"].push( id )
|
143
|
+
end
|
144
|
+
co_hash["#{@abbr}_dat"]=@private_data if @private_data
|
145
|
+
|
146
|
+
return co_hash
|
147
|
+
end
|
148
|
+
|
149
|
+
# Checks to see if the entity has any metadata set.
|
150
|
+
|
151
|
+
def empty?
|
152
|
+
return false if (@identifiers.length > 0 ) or @reference["format"] or @reference["location"] or @metadata.length > 0 or @format or @private_data
|
153
|
+
return true
|
154
|
+
end
|
155
|
+
|
156
|
+
# Serializes the metadata values for Referent and ReferringEntity entities
|
157
|
+
# since their schema is a little different.
|
158
|
+
|
159
|
+
def xml_for_ref_entity(co_elem)
|
160
|
+
meta = {"container"=>co_elem.add_element("ctx:"+@label)}
|
161
|
+
|
162
|
+
if @metadata.length > 0 or @format
|
163
|
+
meta["metadata-by-val"] = meta["container"].add_element("ctx:metadata-by-val")
|
164
|
+
if @format
|
165
|
+
meta["format"] = meta["metadata-by-val"].add_element("ctx:format")
|
166
|
+
meta["format"].text = "info:ofi/fmt:xml:xsd:"+@format
|
167
|
+
|
168
|
+
if @metadata.length > 0
|
169
|
+
meta["metadata"] = meta["metadata-by-val"].add_element("ctx:metadata")
|
170
|
+
meta["format_container"] = meta["metadata"].add_element(@format)
|
171
|
+
meta["format_container"].add_namespace(@abbr, meta["format"].text)
|
172
|
+
meta["format_container"].add_attribute("xsi:schemaLocation", meta["format"].text+" http://www.openurl.info/registry/docs/info:ofi/fmt:xml:xsd:"+@format)
|
173
|
+
@metadata.each do |k,v|
|
174
|
+
meta[k] = meta["format_container"].add_element(@abbr+":"+k)
|
175
|
+
meta[k].text = v
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
if @reference["format"]
|
181
|
+
meta["metadata-by-ref"] = meta["container"].add_element("ctx:metadata-by-ref")
|
182
|
+
meta["ref_format"] = meta["metadata-by-ref"].add_element("ctx:format")
|
183
|
+
meta["ref_format"].text = @reference["format"]
|
184
|
+
meta["ref_loc"] = meta["metadata-by-ref"].add_element("ctx:location")
|
185
|
+
meta["ref_loc"].text = @reference["location"]
|
186
|
+
end
|
187
|
+
|
188
|
+
@identifiers.each do |id|
|
189
|
+
# Yes, if there's more than one, meta["identifier"] will get
|
190
|
+
# overwritten with last. I don't think this is a problem, cause
|
191
|
+
# meta["identifier"] isn't used anywhere.
|
192
|
+
meta["identifier"] = meta["container"].add_element("ctx:identifier")
|
193
|
+
meta["identifier"].text = id
|
194
|
+
end
|
195
|
+
if @private_data
|
196
|
+
meta["private-data"] = meta["container"].add_element("ctx:private-data")
|
197
|
+
meta["private-data"].text = @private_data
|
198
|
+
end
|
199
|
+
return co_elem
|
200
|
+
end
|
201
|
+
|
202
|
+
# Switch old 0.1 style ids to new 1.0 style ids.
|
203
|
+
# Eg, turn << doi:[x] >> into << info:doi/[x] >>
|
204
|
+
def self.normalize_id(value)
|
205
|
+
# info, urn, and http are all good new style 1.0 ids.
|
206
|
+
# we assume anything else is not. Is this a valid assumption?
|
207
|
+
unless ( (value.slice(0,5) == 'info:') ||
|
208
|
+
(value.slice(0,4) == 'urn:') ||
|
209
|
+
(value.slice(0,5) == 'http:') )
|
210
|
+
value = value.sub(/^([a-z,A-Z]+)\:/, 'info:\1/')
|
211
|
+
end
|
212
|
+
|
213
|
+
return value
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
class ReferentEntity < ContextObjectEntity
|
219
|
+
def initialize
|
220
|
+
super()
|
221
|
+
@abbr = "rft"
|
222
|
+
@label = "referent"
|
223
|
+
end
|
224
|
+
def xml(co_elem)
|
225
|
+
return self.xml_for_ref_entity(co_elem)
|
226
|
+
end
|
227
|
+
def set_format(fmt)
|
228
|
+
if fmt.split(":").length > 1
|
229
|
+
@format = fmt.split(":").last
|
230
|
+
else
|
231
|
+
@format = fmt
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class ReferringEntity < ContextObjectEntity
|
237
|
+
def initialize
|
238
|
+
super()
|
239
|
+
@abbr = "rfe"
|
240
|
+
@label = "referring-entity"
|
241
|
+
end
|
242
|
+
def xml(co_elem)
|
243
|
+
return self.xml_for_ref_entity(co_elem)
|
244
|
+
end
|
245
|
+
def set_format(fmt)
|
246
|
+
if fmt.split(":").length > 1
|
247
|
+
@format = fmt.split(":").last
|
248
|
+
else
|
249
|
+
@format = fmt
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class ReferrerEntity < ContextObjectEntity
|
255
|
+
def initialize
|
256
|
+
super()
|
257
|
+
@abbr = "rfr"
|
258
|
+
@label = "referrer"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
class RequestorEntity < ContextObjectEntity
|
263
|
+
def initialize
|
264
|
+
super()
|
265
|
+
@abbr = "req"
|
266
|
+
@label = "requestor"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class ServiceTypeEntity < ContextObjectEntity
|
271
|
+
def initialize
|
272
|
+
super()
|
273
|
+
@abbr = "svc"
|
274
|
+
@label = "service-type"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
class ResolverEntity < ContextObjectEntity
|
278
|
+
def initialize
|
279
|
+
super()
|
280
|
+
@abbr = "res"
|
281
|
+
@label = "resolver"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
class CustomEntity < ContextObjectEntity
|
286
|
+
attr_accessor :abbr, :label
|
287
|
+
def initialize(abbr=nil, label=nil)
|
288
|
+
super()
|
289
|
+
unless abbr
|
290
|
+
@abbr = "cus"
|
291
|
+
else
|
292
|
+
@abbr = abbr
|
293
|
+
end
|
294
|
+
unless label
|
295
|
+
@label = @abbr
|
296
|
+
else
|
297
|
+
@abbr = label
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module OpenURL
|
5
|
+
# The Transport class is intended to be used to deliver ContextObject objects
|
6
|
+
# to an OpenURL enabled host. Currently only HTTP is supported.
|
7
|
+
# Usage:
|
8
|
+
# require 'openurl'
|
9
|
+
# include OpenURL
|
10
|
+
# context_object = ContextObject.new_from_kev('ctx_enc=info%3Aofi%2Fenc%3AUTF-8&ctx_ver=Z39.88-2004&rft.genre=article&rft_id=info%3Adoi%2F10.1016%2Fj.ipm.2005.03.024&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Aarticle&url_ctx_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Actx&url_ver=Z39.88-2004')
|
11
|
+
# transport = Transport.new('http://demo.exlibrisgroup.com:9003/lr_3', context_object)
|
12
|
+
# transport.get
|
13
|
+
# puts tranport.response
|
14
|
+
|
15
|
+
class Transport
|
16
|
+
attr_accessor(:extra_args, :ctx_id)
|
17
|
+
attr_reader(:response, :request_string, :context_objects, :code, :message)
|
18
|
+
|
19
|
+
# Creates the transport object which can be used to initiate
|
20
|
+
# subsequent requests. The contextobject argument can be an OpenURL
|
21
|
+
# ContextObject object, and array of ContextObjects or nil. http_arguments
|
22
|
+
# set the Net::HTTP attributes: {:open_timeout=>3, :read_timeout=>5}, etc.
|
23
|
+
|
24
|
+
def initialize(target_base_url, contextobject=nil, http_arguments={})
|
25
|
+
@uri = URI.parse(target_base_url)
|
26
|
+
@context_objects = []
|
27
|
+
self.add_context_object(contextobject) if contextobject
|
28
|
+
@url_ver = "Z39.88-2004"
|
29
|
+
@extra_args = {}
|
30
|
+
@client = Net::HTTP.new(@uri.host, @uri.port)
|
31
|
+
@client.open_timeout = (http_arguments[:open_timeout]||3)
|
32
|
+
@client.read_timeout = (http_arguments[:read_timeout]||5)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Can take either an OpenURL::ContextObject or an array of ContextObjects
|
36
|
+
# to send to the Transport target
|
37
|
+
|
38
|
+
def add_context_object(contextobject)
|
39
|
+
|
40
|
+
if contextobject.is_a?(OpenURL::ContextObject)
|
41
|
+
@context_objects << contextobject
|
42
|
+
elsif contextobject.is_a?(Array)
|
43
|
+
contextobject.each do | co |
|
44
|
+
raise ArgumentError, "Each element in array much be an OpenURL::ContextObject!" unless co.is_a?(OpenURL::ContextObject)
|
45
|
+
@context_objects << co
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise ArgumentError, "Argument must be a ContextObject or array of ContextObjects!, #{contextobject.class} sent."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Accepts either a ContextObject or array index to remove from array being
|
53
|
+
# sent to the Transport target
|
54
|
+
|
55
|
+
def remove_context_object(element)
|
56
|
+
idx = case element.class
|
57
|
+
when Fixnum then element
|
58
|
+
when OpenURL::ContextObject then @context_objects.index(element)
|
59
|
+
else raise ArgumentError, "Invalid argument for element"
|
60
|
+
end
|
61
|
+
@context_objects.delete_at(idx)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Perform an inline HTTP GET request. Only one context object can be sent
|
65
|
+
# via GET, so pass the index of the desired context object (defaults to the
|
66
|
+
# first)
|
67
|
+
|
68
|
+
def get(idx=0)
|
69
|
+
extra = ""
|
70
|
+
@extra_args.each do | key, val |
|
71
|
+
extra << "&#{key}=#{val}"
|
72
|
+
end
|
73
|
+
self.parse_response(@client.get("#{@uri.path}?#{@context_objects[idx].kev}#{extra}"))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sends an inline transport request. YOu can specify which HTTP method
|
77
|
+
# to use. Since you can only send one context object per inline request,
|
78
|
+
# the second argument is the index of the desired context object.
|
79
|
+
|
80
|
+
def transport_inline(method="GET", idx=0)
|
81
|
+
return(self.get(idx)) if method=="GET"
|
82
|
+
return(self.post({:inline=>true, :index=>idx})) if method=="POST"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Sends an by-value transport request. YOu can specify which HTTP method
|
86
|
+
# to use. Since a GET request is effectively the same as an inline request,
|
87
|
+
# the index of which context object must be specified (defaults to 0).
|
88
|
+
|
89
|
+
def transport_by_val(method="POST", idx=0)
|
90
|
+
return(self.get(idx)) if method=="GET"
|
91
|
+
return(self.post) if method=="POST"
|
92
|
+
end
|
93
|
+
|
94
|
+
# POSTs an HTTP request to the transport target. To send an inline request,
|
95
|
+
# include a hash that looks like: {:inline=>true, :index=>n} (:index defaults
|
96
|
+
# to 0. Transport.post must be used to send multiple context objects to a
|
97
|
+
# target.
|
98
|
+
|
99
|
+
def post(args={})
|
100
|
+
# Inline requests send the context object as a hash
|
101
|
+
if args[:inline]
|
102
|
+
self.parse_response(self.post_http(@context_objects[(args[:index]||0)].to_hash.merge(@extra_args.merge({"url_ctx_fmt"=>"info:ofi/fmt:kev:mtx:ctx"}))))
|
103
|
+
return
|
104
|
+
end
|
105
|
+
ctx_hash = {"url_ctx_fmt" => "info:ofi/fmt:xml:xsd:ctx"}
|
106
|
+
# If we're only sending one context object, use that, otherwise concatenate
|
107
|
+
# them.
|
108
|
+
if @context_objects.length == 1
|
109
|
+
ctx_hash["url_ctx_val"] = @context_objects[0].xml
|
110
|
+
else
|
111
|
+
ctx_hash["url_ctx_val"] = self.merge_context_objects
|
112
|
+
end
|
113
|
+
@context_objects[0].admin.each do | key, hsh |
|
114
|
+
ctx_hash[key] = hsh["value"]
|
115
|
+
end
|
116
|
+
|
117
|
+
self.parse_response(self.post_http(ctx_hash.merge(@extra_args)))
|
118
|
+
end
|
119
|
+
|
120
|
+
# For a multiple context object request, takes the first context object in
|
121
|
+
# the context_objects attribute, and adds the other context objects to it,
|
122
|
+
# under /ctx:context-objects/ctx:context-object and serializes it all as XML.
|
123
|
+
# Returns a string of the XML document
|
124
|
+
|
125
|
+
def merge_context_objects
|
126
|
+
ctx_doc = REXML::Document.new(@context_objects[0].xml)
|
127
|
+
root = ctx_doc.root
|
128
|
+
@context_objects.each do | ctx |
|
129
|
+
next if @context_objects.index(ctx) == 0
|
130
|
+
c_doc = REXML::Document.new(ctx.xml)
|
131
|
+
c_elm = c_doc.elements['ctx:context-objects/ctx:context-object']
|
132
|
+
root.add_element(c_elm)
|
133
|
+
end
|
134
|
+
return ctx_doc.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
# Deprecated. Set by-reference in OpenURL::ContextObject and use .get or
|
138
|
+
# .post
|
139
|
+
|
140
|
+
def transport_by_ref(fmt, ref, method="GET")
|
141
|
+
md = "url_ver=Z39.88-2004&url_ctx_fmt="+CGI.escape(fmt)+"&url_tim="+CGI.escape(DateTime.now().to_s)
|
142
|
+
if method == "GET"
|
143
|
+
parse.response(@client.get("#{@uri.path}?#{md}&url_ctx_ref="+CGI.escape(ref)))
|
144
|
+
else
|
145
|
+
args = {"url_ver"=>"Z39.88-2004",
|
146
|
+
"url_ctx_fmt"=>fmt,
|
147
|
+
"url_tim"=>DateTime.now().to_s,
|
148
|
+
"url_ctx_ref" => ref}
|
149
|
+
args = args.merge(@extra_args) unless @extra_args.empty?
|
150
|
+
|
151
|
+
self.parse_response(self.post_http(args))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
protected
|
156
|
+
|
157
|
+
# Reads the HTTP::Response object and sets the response, code and message
|
158
|
+
# attributes
|
159
|
+
|
160
|
+
def parse_response(response)
|
161
|
+
@response = response.body
|
162
|
+
@code = response.code
|
163
|
+
@message = response.message
|
164
|
+
end
|
165
|
+
|
166
|
+
# Sends the actual POST request.
|
167
|
+
|
168
|
+
def post_http(args)
|
169
|
+
return(Net::HTTP.post_form @uri, args)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: openurl
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2007-10-29 00:00:00 -04:00
|
8
|
+
summary: a Ruby library to create, parse and use NISO Z39.88 OpenURLs
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: rossfsinger@gmail.com
|
12
|
+
homepage: http://openurl.rubyforge.org/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: openurl
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Ross Singer
|
31
|
+
files:
|
32
|
+
- lib/openurl
|
33
|
+
- lib/openurl/context_object.rb
|
34
|
+
- lib/openurl/context_object_entity.rb
|
35
|
+
- lib/openurl/transport.rb
|
36
|
+
- lib/openurl.rb
|
37
|
+
test_files: []
|
38
|
+
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
executables: []
|
44
|
+
|
45
|
+
extensions: []
|
46
|
+
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
dependencies: []
|
50
|
+
|