openurl 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NWViZjIwOWQ1ODhmMzYzYzc1NTg5ZDI1MDQwODc2Y2FmNGNjMDk4Ng==
5
+ data.tar.gz: !binary |-
6
+ YmU1ODM5YTA4NzM1ZDk3MmUxMmJlYjBkZTg2YmQzM2ZhNTJhZTRjYQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YzVhZDBiNzAwNGYyYThiZGM2NTk0NmU0NWExNjJiYjNhYTRhNmRmNWYxYzFm
10
+ ZGRjNjdlNjRlOTgwMWY0ODA1ZjFjMjY3MmJlMjRkY2M0NzFkNTZkNDE5ZmEx
11
+ YjlmYzgwMGMzY2Q0MDFlODI3M2JkOTc2OGQyNTFmMDVmOWM2ZDc=
12
+ data.tar.gz: !binary |-
13
+ YjRjNDE2MjJmYzI1MDMyNDkyOGM2YzE3MDc5ZjczYTk2ZWMwNTY4YzNiYjc5
14
+ YWVkMGRmMWUwMTYyNDAwYWM0MWFmMzk2MTU4NTU2YTM2OTJkZTYxYTA4ZWEw
15
+ YzI2OWRlNzEwNmZkM2UxOTNiZjk2NzQ4MTBkZWEwOTFkYTBkYzI=
@@ -352,7 +352,7 @@ module OpenURL
352
352
  elsif key.match(/^[a-z]{3}_ref/)
353
353
  # determines if we have a by-reference context object
354
354
  (entity, v, fmt) = key.split("_")
355
- ent = self.translate_abbr(entity)
355
+ ent = self.get_entity_obj(entity)
356
356
  unless ent
357
357
  self.foreign_keys[key] = val
358
358
  next
@@ -368,45 +368,45 @@ module OpenURL
368
368
  end
369
369
  ref[entity] = [ref_key, val]
370
370
  else
371
- if ref[entity][0] == "format"
372
- eval("@"+ent).set_reference(val, ref[entity][1])
371
+ if ref[entity][0] == "format"
372
+ instance_variable_get("@#{ent}").set_reference(val, ref[entity][1])
373
373
  else
374
- eval("@"+ent).set_reference(ref[entity][1], val)
374
+ instance_variable_get("@#{ent}").set_reference(ref[entity][1], val)
375
375
  end
376
376
  end
377
377
  elsif key.match(/^[a-z]{3}_id$/)
378
378
  # Get the entity identifier
379
379
  (entity, v) = key.split("_")
380
- ent = self.translate_abbr(entity)
380
+ ent = self.get_entity_obj(entity)
381
381
  unless ent
382
382
  self.foreign_keys[key] = val
383
383
  next
384
384
  end
385
385
  # May or may not be an array, turn it into one.
386
386
  [value].flatten.each do | id |
387
- eval("@"+ent).add_identifier(id)
387
+ ent.add_identifier(id)
388
388
  end
389
389
 
390
390
  elsif key.match(/^[a-z]{3}_dat$/)
391
391
  # Get any private data
392
392
  (entity, v) = key.split("_")
393
- ent = self.translate_abbr(entity)
393
+ ent = self.get_entity_obj(entity)
394
394
  unless ent
395
395
  self.foreign_keys[key] = val
396
396
  next
397
397
  end
398
- eval("@"+ent).set_private_data(val)
398
+ ent.set_private_data(val)
399
399
  else
400
400
  # collect the entity metadata
401
401
  keyparts = key.split(".")
402
402
  if keyparts.length > 1
403
403
  # This is 1.0 OpenURL
404
- ent = self.translate_abbr(keyparts[0])
404
+ ent = self.get_entity_obj(keyparts[0])
405
405
  unless ent
406
406
  self.foreign_keys[key] = val
407
407
  next
408
408
  end
409
- eval("@"+ent).set_metadata(keyparts[1], val)
409
+ ent.set_metadata(keyparts[1], val)
410
410
  else
411
411
  # This is a 0.1 OpenURL. Your mileage may vary on how accurately
412
412
  # this maps.
@@ -452,26 +452,29 @@ module OpenURL
452
452
  end
453
453
  end
454
454
 
455
- # Translates the abbreviated entity (rft, rfr, etc.) to the associated class
456
- # name. For repeatable entities, uses the first object in the array. Returns
457
- # a string of the object name which would then be eval'ed to call a method
458
- # upon.
459
-
460
- def translate_abbr(abbr)
461
- if @@defined_entities.has_key?(abbr)
462
- ent = @@defined_entities[abbr]
463
- if ent == "service-type"
464
- ent = "serviceType[0]"
465
- elsif ent == "resolver"
466
- ent = "resolver[0]"
467
- elsif ent == "referring-entity"
468
- ent = "referringEntity"
455
+ # Takes a string abbreviated entity ('rfr', 'rft', etc.), returns
456
+ # the appropriate ContextObjectEntity object for this ContextObject,
457
+ # in some cases lazily creating and assigning it.
458
+ # @@defined_entities = {"rft"=>"referent", "rfr"=>"referrer", "rfe"=>"referring-entity", "req"=>"requestor", "svc"=>"service-type", "res"=>"resolver"}
459
+ def get_entity_obj(abbr)
460
+ ivar_name = @@defined_entities[abbr]
461
+
462
+ return nil unless ivar_name
463
+
464
+ return case ivar_name
465
+ when "service-type"
466
+ @serviceType << ContextObjectEntity.new if @serviceType.empty?
467
+ @serviceType.first
468
+ when "resolver"
469
+ @resolver << ContextObjectEntity.new if @resolver.empty?
470
+ @resolver.first
471
+ when "referring-entity"
472
+ instance_variable_get("@referringEntity")
473
+ else
474
+ instance_variable_get("@#{ivar_name}")
469
475
  end
470
- else
471
- return nil
472
- end
473
- return ent
474
476
  end
477
+
475
478
 
476
479
  def self.entities(term)
477
480
  return @@defined_entities[term] if @@defined_entities.keys.index(term)
@@ -371,6 +371,11 @@ class ContextObjectTest < Test::Unit::TestCase
371
371
  assert_match(/ctx_ver=&/, kev)
372
372
  end
373
373
 
374
+ def test_kev_res_id
375
+ kev = "url_ver=Z39.88-2004&url_ctx_fmt=info:ofi/fmt:kev:mtx:ctx&rfr_id=info:sid/aph&res_id=http://openurl.ac.uk/ukfed:dur.ac.uk&rft_val_fmt=info:ofi/fmt:kev:mtx:journal&rft.aulast=Rance&rft.aufirst=Philip&rft.atitle=De%20militari%20scientia »%20or%20Müller%20Fragment%20as%20a%20philological%20resource&rft.jtitle=Glotta&rft.stitle=Glotta&rft.date=2010&rft.volume=86&rft.spage=63&rft.epage=92&rft.issn=0017-1298&rft_id=info:oclcnum/1714662&rft.genre=article&sid=default:none"
376
+ ctx = OpenURL::ContextObject.new_from_kev(kev)
377
+ assert_equal("http://openurl.ac.uk/ukfed:dur.ac.uk", ctx.resolver.first.identifier)
378
+ end
374
379
  protected
375
380
 
376
381
  # Make sure ctx1 and ctx2 don't share the same data objects.
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openurl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
5
- prerelease:
4
+ version: 0.4.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jonathan Rochkind
@@ -10,12 +9,11 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2012-07-25 00:00:00.000000000 Z
12
+ date: 2013-04-17 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: marc
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
18
  - - ! '>='
21
19
  - !ruby/object:Gem::Version
@@ -23,7 +21,6 @@ dependencies:
23
21
  type: :runtime
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
25
  - - ! '>='
29
26
  - !ruby/object:Gem::Version
@@ -31,7 +28,6 @@ dependencies:
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: ensure_valid_encoding
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
32
  - - ! '>='
37
33
  - !ruby/object:Gem::Version
@@ -39,7 +35,6 @@ dependencies:
39
35
  type: :runtime
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
39
  - - ! '>='
45
40
  - !ruby/object:Gem::Version
@@ -63,7 +58,6 @@ files:
63
58
  - lib/openurl/metadata_formats/marc.rb
64
59
  - lib/openurl/metadata_formats/patent.rb
65
60
  - lib/openurl/metadata_formats/dissertation.rb
66
- - lib/openurl/context_object-SAVED
67
61
  - lib/openurl/transport.rb
68
62
  - lib/openurl/context_object.rb
69
63
  - test/data/metalib_sap2_post_params.yml
@@ -79,27 +73,26 @@ files:
79
73
  - README.md
80
74
  homepage: https://github.com/openurl/openurl
81
75
  licenses: []
76
+ metadata: {}
82
77
  post_install_message:
83
78
  rdoc_options: []
84
79
  require_paths:
85
80
  - lib
86
81
  required_ruby_version: !ruby/object:Gem::Requirement
87
- none: false
88
82
  requirements:
89
83
  - - ! '>='
90
84
  - !ruby/object:Gem::Version
91
85
  version: '0'
92
86
  required_rubygems_version: !ruby/object:Gem::Requirement
93
- none: false
94
87
  requirements:
95
88
  - - ! '>='
96
89
  - !ruby/object:Gem::Version
97
90
  version: '0'
98
91
  requirements: []
99
92
  rubyforge_project:
100
- rubygems_version: 1.8.24
93
+ rubygems_version: 2.0.3
101
94
  signing_key:
102
- specification_version: 3
95
+ specification_version: 4
103
96
  summary: a Ruby library to create, parse and use NISO Z39.88 OpenURLs
104
97
  test_files:
105
98
  - test/data/metalib_sap2_post_params.yml
@@ -112,3 +105,4 @@ test_files:
112
105
  - test/context_object_test.rb
113
106
  - test/encoding_test.rb
114
107
  - test/scholarly_common_test.rb
108
+ has_rdoc:
@@ -1,663 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'ensure_valid_encoding'
4
-
5
- module OpenURL
6
-
7
- if RUBY_VERSION < '1.9'
8
- require 'jcode'
9
- $KCODE='UTF-8'
10
- end
11
-
12
- ##
13
- # The ContextObject class is intended to both create new OpenURL 1.0 context
14
- # objects or parse existing ones, either from Key-Encoded Values (KEVs) or
15
- # XML.
16
- # == Create a new ContextObject programmatically
17
- # require 'openurl/context_object'
18
- # include OpenURL
19
- #
20
- # ctx = ContextObject.new
21
- # ctx.referent.set_format('journal') # important to do this FIRST.
22
- #
23
- # ctx.referent.add_identifier('info:doi/10.1016/j.ipm.2005.03.024')
24
- # ctx.referent.set_metadata('issn', '0306-4573')
25
- # ctx.referent.set_metadata('aulast', 'Bollen')
26
- # ctx.referrer.add_identifier('info:sid/google')
27
- # puts ctx.kev
28
- # # 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
29
- #
30
- # == Create a new ContextObject from an existing kev or XML serialization:
31
- #
32
- # ContextObject.new_from_kev( kev_context_object )
33
- # ContextObject.new_from_xml( xml_context_object ) # Can be String or REXML::Document
34
- #
35
- # == Serialize a ContextObject to kev or XML :
36
- # ctx.kev
37
- # ctx.xml
38
- class ContextObject
39
- include EnsureValidEncoding
40
-
41
- attr_reader :admin, :referent, :referringEntity, :requestor, :referrer,
42
- :serviceType, :resolver
43
- attr_accessor :foreign_keys, :openurl_ver
44
-
45
- @@defined_entities = {"rft"=>"referent", "rfr"=>"referrer", "rfe"=>"referring-entity", "req"=>"requestor", "svc"=>"service-type", "res"=>"resolver"}
46
-
47
- # Creates a new ContextObject object and initializes the ContextObjectEntities.
48
-
49
- def initialize()
50
- @referent = ContextObjectEntity.new
51
- @referrer = ContextObjectEntity.new
52
- @referringEntity = ContextObjectEntity.new
53
- @requestor = ContextObjectEntity.new
54
- @serviceType = []
55
- @resolver = []
56
- @foreign_keys = {}
57
- @openurl_ver = "Z39.88-2004"
58
- @admin = {"ctx_ver"=>{"label"=>"version", "value"=>@openurl_ver}, "ctx_tim"=>{"label"=>"timestamp", "value"=>DateTime.now().to_s}, "ctx_id"=>{"label"=>"identifier", "value"=>""}, "ctx_enc"=>{"label"=>"encoding", "value"=>"info:ofi/enc:UTF-8"}}
59
- end
60
-
61
- # Any legal OpenURL 1.0 sends url_ver=Z39.88-2004, and usually
62
- # ctx_ver=Z39.88-2004 too. However, sometimes we need to send
63
- # an illegal OpenURL with a different openurl ver string, to deal
64
- # with weird agents, for instance to trick SFX into doing the right thing.
65
- def openurl_ver=(ver)
66
- @openurl_ver = ver
67
- @admin["ctx_ver"]["value"] = ver
68
- end
69
-
70
- def deep_copy
71
- cloned = ContextObject.new
72
- cloned.import_context_object( self )
73
- return cloned
74
- end
75
-
76
- # Serialize the ContextObject to XML.
77
-
78
- def xml
79
- doc = REXML::Document.new()
80
- coContainer = doc.add_element "ctx:context-objects"
81
- coContainer.add_namespace("ctx","info:ofi/fmt:xml:xsd:ctx")
82
- coContainer.add_namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
83
- coContainer.add_attribute("xsi:schemaLocation", "info:ofi/fmt:xml:xsd:ctx http://www.openurl.info/registry/docs/info:ofi/fmt:xml:xsd:ctx")
84
- co = coContainer.add_element "ctx:context-object"
85
- @admin.each_key do |k|
86
- next if k == "ctx_enc"
87
- co.add_attribute(@admin[k]["label"], @admin[k]["value"])
88
- end
89
-
90
- [{@referent=>"rft"},
91
- {@referringEntity=>"rfe"}, {@requestor=>"req"},
92
- {@referrer=>"rfr"}].each do | entity |
93
-
94
- entity.each do | ent, label |
95
- ent.xml(co, label) unless ent.empty?
96
- end
97
- end
98
-
99
- [{@serviceType=>"svc"}, {@resolver=>"res"}].each do |entity|
100
- entity.each do | entCont, label |
101
- entCont.each do |ent|
102
- ent.xml(co, label) unless ent.empty?
103
- end
104
- end
105
- end
106
-
107
- return doc.to_s
108
- end
109
-
110
-
111
- # Output the ContextObject as a Key-encoded value string. Pass a boolean
112
- # true argument if you do not want the ctx_tim key included.
113
-
114
- def kev(no_date=false)
115
- kevs = ["url_ver=#{self.openurl_ver}", "url_ctx_fmt=#{CGI.escape("info:ofi/fmt:kev:mtx:ctx")}"]
116
-
117
- # Loop through the administrative metadata
118
- @admin.each_key do |k|
119
- next if k == "ctx_tim" && no_date
120
- kevs.push(k+"="+CGI.escape(@admin[k]["value"].to_s)) if @admin[k]["value"]
121
- end
122
-
123
- {@referent=>"rft", @referringEntity=>"rfe", @requestor=>"req", @referrer=>"rfr"}.each do | ent, abbr |
124
- kevs.push(ent.kev(abbr)) unless ent.empty?
125
- end
126
-
127
- {@serviceType=>"svc", @resolver=>"res"}.each do |entCont, abbr|
128
- entCont.each do |ent|
129
- next if ent.empty?
130
- kevs.push(ent.kev(abbr))
131
- end
132
- end
133
- return kevs.join("&")
134
- end
135
-
136
- # Outputs the ContextObject as a ruby hash---hash version of the kev format.
137
- # Outputting a context object as a hash
138
- # is imperfect, because context objects can have multiple elements
139
- # with the same key--and because some keys depend on SAP1 vs SAP2.
140
- # So this function is really deprecated, but here because we have so much
141
- # code dependent on it.
142
- def to_hash
143
- co_hash = {"url_ver"=>self.openurl_ver, "url_ctx_fmt"=>"info:ofi/fmt:kev:mtx:ctx"}
144
-
145
- @admin.each_key do |k|
146
- co_hash[k]=@admin[k]["value"] if @admin[k]["value"]
147
- end
148
-
149
- {@referent=>"rft", @referringEntity=>"rfe", @requestor=>"req", @referrer=>"rfr"}.each do | ent, abbr |
150
- co_hash.merge!(ent.to_hash(abbr)) unless ent.empty?
151
- end
152
-
153
- # svc and res are arrays of ContextObjectEntity
154
- {@serviceType=>"svc", @resolver=>"res"}.each do |ent_list, abbr|
155
- ent_list.each do |ent|
156
- co_hash.merge!(ent.to_hash(abbr)) unless ent.empty?
157
- end
158
- end
159
- return co_hash
160
- end
161
-
162
-
163
- # Outputs a COinS (ContextObject in SPANS) span tag for the ContextObject.
164
- # Arguments are any other CSS classes you want included and the innerHTML
165
- # content.
166
-
167
- def coins (classnames=nil, innerHTML=nil)
168
- return "<span class='Z3988 #{classnames}' title='"+CGI.escapeHTML(self.kev(true))+"'>#{innerHTML}</span>"
169
- end
170
-
171
-
172
- # Sets a ContextObject administration field.
173
-
174
- def set_administration_key(key, val)
175
- raise ArgumentException, "#{key} is not a valid admin key!" unless @admin.keys.index(key)
176
- @admin[key]["value"] = val
177
- end
178
-
179
- # Imports an existing Key-encoded value string and sets the appropriate
180
- # entities.
181
-
182
- def import_kev(kev)
183
- co = CGI::parse(kev)
184
- co2 = {}
185
- co.each do |key, val|
186
- if val.is_a?(Array)
187
- if val.length == 1
188
- co2[key] = val[0]
189
- else
190
- co2[key] = val
191
- end
192
- end
193
- end
194
- self.import_hash(co2)
195
- end
196
-
197
- # Initialize a new ContextObject object from an existing KEV
198
-
199
- def self.new_from_kev(kev)
200
- co = self.new
201
- co.import_kev(kev)
202
- return co
203
- end
204
-
205
- # Initialize a new ContextObject object from a CGI.params style hash
206
- # Expects a hash with default value being nil though, not [] as CGI.params
207
- # actually returns, beware. Can also accept a Rails-style params hash
208
- # (single string values, not array values), although this may lose
209
- # some context object information.
210
- def self.new_from_form_vars(params)
211
- co = self.new
212
- if ctx_val = (params[:url_ctx_val]||params["url_ctx_val"]) and not ctx_val.empty? # this is where the context object stuff will be
213
- co.admin.keys.each do | adm |
214
- if params[adm.to_s]
215
- if params[adm.to_s].is_a?(Array)
216
- co.set_administration_key(adm, params[adm.to_s].first)
217
- else
218
- co.set_administration_key(adm, params[adm.to_s])
219
- end
220
- end
221
- end
222
-
223
- if ctx_format = (params["url_ctx_fmt"]||params[:url_ctx_fmt])
224
- ctx_format = ctx_format.first if ctx_format.is_a?(Array)
225
- ctx_val = ctx_val.first if ctx_val.is_a?(Array)
226
- if ctx_format == "info:ofi/fmt:xml:xsd:ctx"
227
- co.import_xml(ctx_val)
228
- elsif ctx_format == "info:ofi/fmt:kev:mtx:ctx"
229
- co.import_kev(ctx_val)
230
- end
231
- end
232
- else # we'll assume this is standard inline kev
233
- co.import_hash(params)
234
- end
235
- return co
236
- end
237
-
238
- # Imports an existing XML encoded context object and sets the appropriate
239
- # entities
240
-
241
- def import_xml(xml)
242
- if xml.is_a?(String)
243
- xml.force_encoding("UTF-8")
244
- ensure_valid_encoding!(xml, :invalid => :replace)
245
- doc = REXML::Document.new xml.gsub(/>[\s\t]*\n*[\s\t]*</, '><').strip
246
- elsif xml.is_a?(REXML::Document)
247
- doc = xml
248
- else
249
- raise ArgumentError, "Argument must be an REXML::Document or well-formed XML string"
250
- end
251
-
252
- # Cut to the context object
253
- ctx = REXML::XPath.first(doc, ".//ctx:context-object", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
254
-
255
-
256
-
257
-
258
-
259
-
260
- ctx.attributes.each do |attr, val|
261
- @admin.each do |adm, vals|
262
- self.set_administration_key(adm, val) if vals["label"] == attr
263
- end
264
- end
265
- ctx.to_a.each do | ent |
266
- if @@defined_entities.value?(ent.name())
267
- self.import_entity(ent)
268
- else
269
- self.import_custom_node(ent)
270
- end
271
- end
272
- end
273
-
274
- # Initialize a new ContextObject object from an existing XML ContextObject
275
-
276
- def self.new_from_xml(xml)
277
- co = self.new
278
- co.import_xml(xml)
279
- return co
280
- end
281
-
282
-
283
- # Takes a hash of openurl key/values, as output by CGI.parse
284
- # from a query string for example (values can be strings or arrays
285
- # of string). Mutates hash in place.
286
- #
287
- # Force encodes to UTF8 or 8859-1, depending on ctx_enc
288
- # presence and value.
289
- #
290
- # Replaces any illegal bytes with replacement chars,
291
- # transcodes to UTF-8 if needed to ensure UTF8 on way out.
292
- def clean_char_encoding!(hash)
293
- source_encoding = if hash["ctx_ver"] == "info:ofi/enc:ISO-8859-1"
294
- "ISO-8859-1"
295
- else
296
- "UTF-8"
297
- end
298
-
299
- hash.each_pair do |key, values|
300
- # get a list of all terminal values, whether wrapped
301
- # in arrays or not. We're going to mutate them.
302
- [values].flatten.each do | v |
303
- v.force_encoding(source_encoding)
304
- if source_encoding == "UTF-8"
305
- ensure_valid_encoding!(v, :invalid => :replace)
306
- else
307
- # transcode, replacing any bad chars.
308
- v.encode!("UTF-8", :invalid => :replace)
309
- end
310
- end
311
- end
312
-
313
- end
314
-
315
- # Imports an existing hash of ContextObject values and sets the appropriate
316
- # entities.
317
- def import_hash(hash)
318
- clean_char_encoding!(hash)
319
-
320
- ref = {}
321
- {"@referent"=>"rft", "@referrer"=>"rfr", "@referringEntity"=>"rfe",
322
- "@requestor"=>"req"}.each do | ent, abbr |
323
- next unless hash["#{abbr}_val_fmt"]
324
- val = hash["#{abbr}_val_fmt"]
325
- val = val[0] if val.is_a?(Array)
326
- self.instance_variable_set(ent.to_sym, ContextObjectEntityFactory.format(val))
327
- end
328
-
329
- {"@serviceType"=>"svc","@resolver"=>"res"}.each do | ent, abbr |
330
- next unless hash["#{abbr}_val_fmt"]
331
- val = hash["#{abbr}_val_fmt"]
332
- val = val[0] if val.is_a?(Array)
333
- self.instance_variable_set(ent.to_sym, [ContextObjectEntityFactory.format(val)])
334
- end
335
-
336
- openurl_keys = ["url_ver", "url_tim", "url_ctx_fmt"]
337
- hash.each do |key, value|
338
- val = value
339
- val = value[0] if value.is_a?(Array)
340
-
341
- next if value.nil? || value.empty?
342
-
343
- if openurl_keys.include?(key)
344
- next # None of these matter much for our purposes
345
- elsif @admin.has_key?(key)
346
- self.set_administration_key(key, val)
347
- elsif key.match(/^[a-z]{3}_val_fmt/)
348
- next
349
- elsif key.match(/^[a-z]{3}_ref/)
350
- # determines if we have a by-reference context object
351
- (entity, v, fmt) = key.split("_")
352
- ent = self.translate_abbr(entity)
353
- unless ent
354
- self.foreign_keys[key] = val
355
- next
356
- end
357
- # by-reference requires two fields, format and location, if this is
358
- # the first field we've run across, set a place holder until we get
359
- # the other value
360
- unless ref[entity]
361
- if fmt
362
- ref_key = "format"
363
- else
364
- ref_key = "location"
365
- end
366
- ref[entity] = [ref_key, val]
367
- else
368
- if ref[entity][0] == "format"
369
- eval("@"+ent).set_reference(val, ref[entity][1])
370
- else
371
- eval("@"+ent).set_reference(ref[entity][1], val)
372
- end
373
- end
374
- elsif key.match(/^[a-z]{3}_id$/)
375
- # Get the entity identifier
376
- (entity, v) = key.split("_")
377
- ent = self.translate_abbr(entity)
378
- unless ent
379
- self.foreign_keys[key] = val
380
- next
381
- end
382
- # May or may not be an array, turn it into one.
383
- [value].flatten.each do | id |
384
- eval("@"+ent).add_identifier(id)
385
- end
386
-
387
- elsif key.match(/^[a-z]{3}_dat$/)
388
- # Get any private data
389
- (entity, v) = key.split("_")
390
- ent = self.translate_abbr(entity)
391
- unless ent
392
- self.foreign_keys[key] = val
393
- next
394
- end
395
- eval("@"+ent).set_private_data(val)
396
- else
397
- # collect the entity metadata
398
- keyparts = key.split(".")
399
- if keyparts.length > 1
400
- # This is 1.0 OpenURL
401
- ent = self.translate_abbr(keyparts[0])
402
- unless ent
403
- self.foreign_keys[key] = val
404
- next
405
- end
406
- eval("@"+ent).set_metadata(keyparts[1], val)
407
- else
408
- # This is a 0.1 OpenURL. Your mileage may vary on how accurately
409
- # this maps.
410
- if key == 'id'
411
- if value.is_a?(Array)
412
- value.each do | id |
413
- @referent.add_identifier(id)
414
- end
415
- else
416
- @referent.add_identifier(val)
417
- end
418
- elsif key == 'sid'
419
- @referrer.set_identifier("info:sid/"+val.to_s)
420
- elsif key == 'pid'
421
- @referent.set_private_data(val.to_s)
422
- else
423
- @referent.set_metadata(key, val)
424
- end
425
- end
426
- end
427
- end
428
-
429
-
430
-
431
- # Initialize a new ContextObject object from an existing key/value hash
432
- co = self.new
433
- co.import_hash(hash)
434
- return co
435
- end
436
-
437
- # if we don't have a referent format (most likely because we have a 0.1
438
- # OpenURL), try to determine something from the genre. If that doesn't
439
- # exist, just call it a journal since most 0.1 OpenURLs would be one,
440
- # anyway.
441
- unless @referent.format
442
- fmt = case @referent.metadata['genre']
443
- when /article|journal|issue|proceeding|conference|preprint/ then 'journal'
444
- when /book|bookitem|report|document/ then 'book'
445
- else 'journal'
446
- end
447
- @referent.set_format(fmt)
448
- end
449
- end
450
-
451
- # Translates the abbreviated entity (rft, rfr, etc.) to the associated class
452
- # name. For repeatable entities, uses the first object in the array. Returns
453
- # a string of the object name which would then be eval'ed to call a method
454
- # upon.
455
-
456
- def translate_abbr(abbr)
457
- if @@defined_entities.has_key?(abbr)
458
- ent = @@defined_entities[abbr]
459
- if ent == "service-type"
460
- ent = "serviceType[0]"
461
- elsif ent == "resolver"
462
- ent = "resolver[0]"
463
- elsif ent == "referring-entity"
464
- ent = "referringEntity"
465
- end
466
- else
467
- return nil
468
- end
469
- return ent
470
- end
471
-
472
- def self.entities(term)
473
- return @@defined_entities[term] if @@defined_entities.keys.index(term)
474
- return @@defined_entities[@@defined_entities.values.index(term)] if @@defined_entities.values.index(term)
475
- return nil
476
-
477
- end
478
-
479
- # Imports an existing OpenURL::ContextObject object and sets the appropriate
480
- # entity values.
481
-
482
- def import_context_object(context_object)
483
- @admin.each_key { |k|
484
- self.set_administration_key(k, context_object.admin[k]["value"])
485
- }
486
- ["@referent", "@referringEntity", "@requestor", "@referrer"].each do | ent |
487
- self.instance_variable_set(ent.to_sym, Marshal::load(Marshal.dump(context_object.instance_variable_get(ent.to_sym))))
488
- end
489
- context_object.serviceType.each { |svc|
490
- @serviceType << Marshal::load(Marshal.dump(svc))
491
- }
492
- context_object.resolver.each { |res|
493
- @resolver << Marshal::load(Marshal.dump(res))
494
- }
495
- context_object.foreign_keys.each do | key, val |
496
- self.foreign_keys[key] = val
497
- end
498
- end
499
-
500
- # Initialize a new ContextObject object from an existing
501
- # OpenURL::ContextObject
502
-
503
- def self.new_from_context_object(context_object)
504
- co = self.new
505
- co.import_context_object(context_object)
506
- return co
507
- end
508
-
509
- def referent=(entity)
510
- raise ArgumentError, "Referent must be an OpenURL::ContextObjectEntity" unless entity.is_a?(OpenURL::ContextObjectEntity)
511
- @referent=entity
512
- end
513
-
514
- def referrer=(entity)
515
- raise ArgumentError, "Referrer must be an OpenURL::ContextObjectEntity" unless entity.is_a?(OpenURL::ContextObjectEntity)
516
- @referrer=entity
517
- end
518
-
519
- def referringEntity=(entity)
520
- raise ArgumentError, "Referring-Entity must be an OpenURL::ContextObjectEntity" unless entity.is_a?(OpenURL::ContextObjectEntity)
521
- @referringEntity=entity
522
- end
523
-
524
- def requestor=(entity)
525
- raise ArgumentError, "Requestor must be an OpenURL::ContextObjectEntity" unless entity.is_a?(OpenURL::ContextObjectEntity)
526
- @requestor=entity
527
- end
528
-
529
- protected
530
-
531
- def import_entity(node)
532
- entities = {"rft"=>:@referent, "rfr"=>:@referrer, "rfe"=>:@referringEntity,"req"=>:@requestor,
533
- "svc"=>:@serviceType,"res"=>:@resolver}
534
-
535
- ent = @@defined_entities.keys[@@defined_entities.values.index(node.name())]
536
-
537
-
538
- metalib_workaround(node)
539
-
540
- unless ["svc","res"].index(ent)
541
- self.instance_variable_set(entities[ent], self.set_typed_entity(node))
542
- entity = self.instance_variable_get(entities[ent])
543
-
544
-
545
-
546
- self.import_xml_common(entity, node)
547
- entity.import_xml_metadata(node)
548
- end
549
- end
550
-
551
- def import_svc_node(node)
552
- if @serviceType[0].empty?
553
- key = 0
554
- else
555
- key = self.add_service_type_entity
556
- end
557
- self.import_xml_common(@serviceType[key], node)
558
- self.import_xml_mbv(@serviceType[key], node)
559
- end
560
-
561
- def import_res_node(node)
562
- if @resolver[0].empty?
563
- key = 0
564
- else
565
- key = self.add_resolver_entity
566
- end
567
- self.import_xml_common(@resolver[key], node)
568
- self.import_xml_mbv(@resolver[key], node)
569
- end
570
-
571
- # Determines the proper subclass of ContextObjectEntity to use
572
- # for given format. Input is an REXML node representing a ctx:referent.
573
- # Returns ContextObjectEntity.
574
- def set_typed_entity(node)
575
- fmt = REXML::XPath.first(node, "./ctx:metadata-by-val/ctx:format", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
576
-
577
- fmt_val = fmt.get_text.value if fmt && fmt.has_text?
578
-
579
- # Special weird workaround for info sent from metalib.
580
- # "info:ofi/fmt:xml:xsd" is not actually a legal format
581
- # identifier, it should have more on the end.
582
- # XPath should really end in "rft:*" for maximal generality, but
583
- # REXML doesn't like that.
584
- if (false && fmt_val && fmt_val == "info:ofi/fmt:xml:xsd")
585
- metalib_evidence = REXML::XPath.first( node, "./ctx:metadata-by-val/ctx:metadata/rft:journal", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx", "rft"=>"info:ofi/fmt:xml:xsd:journal"})
586
-
587
- # Okay, even if we don't have that one, do we have a REALLY bad one
588
- # where Metalib puts an illegal namespace identifier in too?
589
- metalib_evidence = REXML::XPath.first( node, "./ctx:metadata-by-val/ctx:metadata/rft:journal", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx", "rft"=>"info:ofi/fmt:xml:xsd"}) unless metalib_evidence
590
-
591
- # metalib didn't advertise it properly, but it's really
592
- # journal format.
593
- fmt_val = "info:ofi/fmt:xml:xsd:journal" if metalib_evidence
594
- end
595
-
596
- if fmt_val
597
- return OpenURL::ContextObjectEntityFactory.format(fmt_val)
598
- else
599
- return OpenURL::ContextObjectEntity.new
600
- end
601
- end
602
-
603
- # Parses the data that should apply to all XML context objects
604
- def import_xml_common(ent, node)
605
-
606
- REXML::XPath.each(node, "./ctx:identifier", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"}) do | id |
607
- ent.add_identifier(id.get_text.value) if id and id.has_text?
608
- end
609
-
610
- priv = REXML::XPath.first(node, "./ctx:private-data", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
611
- ent.set_private_data(priv.get_text.value) if priv and priv.has_text?
612
-
613
- ref = REXML::XPath.first(node, "./ctx:metadata-by-ref", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
614
- if ref
615
- reference = {}
616
- ref.to_a.each do |r|
617
- if r.name() == "format"
618
- reference[:format] = r.get_text.value if r.get_text
619
- else
620
- reference[:location] = r.get_text.value
621
- end
622
- end
623
- ent.set_reference(reference[:location], reference[:format])
624
- end
625
- end
626
-
627
- # Pass in a REXML element representing an entity.
628
- # Special weird workaround for info sent from metalib.
629
- # Metalib uses "info:ofi/fmt:xml:xsd" as a format identifier, and
630
- # sometimes even as a namespace identifier for a <journal> element.
631
- # It's not legal for either. It messes up our parsing. The identifier
632
- # should have something else on the end ":journal", ":book", etc.
633
- # We tack ":journal" on the end if we find this unspecified
634
- # but it contains a <journal> element.
635
- # XPath should really end in "rft:*" for maximal generality, but
636
- # REXML doesn't like that.
637
- def metalib_workaround(node)
638
- # Metalib fix
639
- # Fix awful illegal Metalib XML
640
- fmt = REXML::XPath.first(node, "./ctx:metadata-by-val/ctx:format", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx"})
641
- if ( fmt && fmt.text == "info:ofi/fmt:xml:xsd")
642
- metadata_by_val = node.children.find {|e| e.respond_to?(:name) && e.name == 'metadata-by-val' }
643
-
644
- # Find a "journal" element to make sure forcing to ":journal" is a good
645
- # idea, and to later
646
- # fix the journal namespace if needed
647
- metadata = metadata_by_val.children.find {|e| e.respond_to?(:name) && e.name == 'metadata' } if metadata_by_val
648
- journal = metadata.find {|e| e.respond_to?(:name) && e.name == 'journal' } if metadata
649
-
650
- # Fix the format only if there's a <journal> element in there.
651
- fmt = metadata_by_val.children.find {|e| e.respond_to?(:name) && e.name == 'format' } if metadata_by_val && journal
652
- fmt.text = "info:ofi/fmt:xml:xsd:journal" if fmt
653
-
654
- if (journal && journal.namespace == "info:ofi/fmt:xml:xsd")
655
- journal.add_namespace("xmlns:rft", "info:ofi/fmt:xml:xsd:journal")
656
- end
657
- end
658
- end
659
-
660
- end
661
-
662
-
663
- end