openurl 0.4.2 → 0.4.3

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.
@@ -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