openurl 0.0.1 → 0.1.0
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/README +47 -0
- data/lib/openurl.rb +7 -0
- data/lib/openurl/context_object.rb +364 -284
- data/lib/openurl/context_object_entity.rb +131 -124
- data/lib/openurl/metadata_formats/book.rb +36 -0
- data/lib/openurl/metadata_formats/dissertation.rb +36 -0
- data/lib/openurl/metadata_formats/dublin_core.rb +114 -0
- data/lib/openurl/metadata_formats/journal.rb +36 -0
- data/lib/openurl/metadata_formats/marc.rb +70 -0
- data/lib/openurl/metadata_formats/patent.rb +152 -0
- data/lib/openurl/metadata_formats/scholarly_common.rb +198 -0
- data/lib/openurl/metadata_formats/scholarly_service_type.rb +41 -0
- data/lib/openurl/transport.rb +9 -4
- data/test/context_object_entity_test.rb +41 -0
- data/test/context_object_test.rb +509 -0
- data/test/data/dc_ctx.xml +27 -0
- data/test/data/marc_ctx.xml +101 -0
- data/test/data/metalib_sap2_post_params.yml +1 -0
- data/test/data/scholarly_au_ctx.xml +1 -0
- data/test/data/yu.xml +50 -0
- data/test/test.yml +25 -0
- metadata +70 -35
@@ -0,0 +1,36 @@
|
|
1
|
+
#
|
2
|
+
# journal.rb
|
3
|
+
#
|
4
|
+
# Created on Nov 1, 2007, 10:35:28 AM
|
5
|
+
#
|
6
|
+
# To change this template, choose Tools | Templates
|
7
|
+
# and open the template in the editor.
|
8
|
+
|
9
|
+
require 'openurl/metadata_formats/scholarly_common'
|
10
|
+
module OpenURL
|
11
|
+
class Journal < ScholarlyCommon
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super()
|
15
|
+
@format = 'journal'
|
16
|
+
@metadata_keys = ['jtitle','atitle','title','stitle','place','pub','date','edition',
|
17
|
+
'spage','epage', 'pages','issn','eissn', 'isbn','sici','coden','chron',
|
18
|
+
'ssn','quarter','volume','part','issue','artnum'
|
19
|
+
]
|
20
|
+
@valid_genres = ["journal","issue","article", "conference","proceeding",
|
21
|
+
"preprint","unknown" ]
|
22
|
+
@xml_ns = "info:ofi/fmt:xml:xsd:journal"
|
23
|
+
@kev_ns = "info:ofi/fmt:kev:mtx:journal"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class JournalFactory < ContextObjectEntityFactory
|
28
|
+
@@identifiers = ["info:ofi/fmt:kev:mtx:journal","info:ofi/fmt:xml:xsd:journal"]
|
29
|
+
def self.identifiers
|
30
|
+
return @@identifiers
|
31
|
+
end
|
32
|
+
def self.create()
|
33
|
+
return OpenURL::Journal.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# marc.rb
|
3
|
+
#
|
4
|
+
# Created on Nov 12, 2007, 10:35:28 AM
|
5
|
+
#
|
6
|
+
# To change this template, choose Tools | Templates
|
7
|
+
# and open the template in the editor.
|
8
|
+
require 'rubygems'
|
9
|
+
require 'marc'
|
10
|
+
module OpenURL
|
11
|
+
class Marc < ContextObjectEntity
|
12
|
+
private :kev, :set_metadata
|
13
|
+
attr_accessor :marc
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@xml_ns = "info:ofi/fmt:xml:xsd:MARC21"
|
17
|
+
@format = @xml_ns
|
18
|
+
@marc = MARC::Record.new
|
19
|
+
@marc_ns = "http://www.loc.gov/MARC21/slim"
|
20
|
+
@metadata = @marc.fields
|
21
|
+
end
|
22
|
+
|
23
|
+
def serialize_metadata(elem, label)
|
24
|
+
metadata = elem.add_element("ctx:metadata")
|
25
|
+
if @marc.is_a?(Array)
|
26
|
+
container = metadata.add_elements("#{label}:collection")
|
27
|
+
container.add_namespace(label, @marc_ns)
|
28
|
+
@marc.each do | mrc |
|
29
|
+
rec = mrc.to_xml.root
|
30
|
+
mrc_elem = container.add_element rec
|
31
|
+
mrc_elem.name = "#{label}:#{mrc_elem.name}"
|
32
|
+
end
|
33
|
+
else
|
34
|
+
rec = @marc.to_xml.root
|
35
|
+
rec.add_namespace(label, @marc_ns)
|
36
|
+
rec.name = "#{label}:#{rec.name}"
|
37
|
+
mrc_elem = metadata.add_element rec
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def import_xml_metadata(node)
|
42
|
+
marcxml = REXML::XPath.first(node,"./ctx:metadata-by-val/ctx:metadata/fmt:collection | ./ctx:metadata-by-val/ctx:metadata/fmt:record",
|
43
|
+
{"ctx"=>"info:ofi/fmt:xml:xsd:ctx","fmt"=>@marc_ns})
|
44
|
+
if marcxml
|
45
|
+
marcxml.root.prefix = ''
|
46
|
+
records = []
|
47
|
+
MARC::XMLReader.new(StringIO.new(marcxml.to_s)).each do | record |
|
48
|
+
records << record
|
49
|
+
end
|
50
|
+
if records.length == 1
|
51
|
+
@marc = records[0]
|
52
|
+
else
|
53
|
+
@marc = records
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@metadata = @marc.fields
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
class MarcFactory < ContextObjectEntityFactory
|
62
|
+
@@identifiers = ["info:ofi/fmt:xml:xsd:MARC21"]
|
63
|
+
def self.identifiers
|
64
|
+
return @@identifiers
|
65
|
+
end
|
66
|
+
def self.create()
|
67
|
+
return OpenURL::Marc.new
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#
|
2
|
+
# patent.rb
|
3
|
+
#
|
4
|
+
# Created on Nov 1, 2007, 10:35:28 AM
|
5
|
+
#
|
6
|
+
# To change this template, choose Tools | Templates
|
7
|
+
# and open the template in the editor.
|
8
|
+
|
9
|
+
require 'openurl/metadata_formats/scholarly_common'
|
10
|
+
module OpenURL
|
11
|
+
class Patent < ContextObjectEntity
|
12
|
+
attr_reader :inventors
|
13
|
+
def initialize
|
14
|
+
super
|
15
|
+
@format = 'patent'
|
16
|
+
@inventors = [OpenURL::Inventor.new]
|
17
|
+
@metadata_keys = ['title','co','cc','kind','applcc','applnumber','number',
|
18
|
+
'date','applyear','appldate','assignee','pubdate','prioritydate'
|
19
|
+
]
|
20
|
+
@inventor_keys = ['inv', 'invlast', 'invfirst']
|
21
|
+
@xml_ns = "info:ofi/fmt:xml:xsd:patent"
|
22
|
+
@kev_ns = "info:ofi/fmt:kev:mtx:patent"
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(metadata, value=nil)
|
26
|
+
meta = metadata.to_s.sub(/=$/,'')
|
27
|
+
raise ArgumentError, "#{meta.to_s} is not a valid #{self.class} metadata field." unless (@inventor_keys+@metadata_keys).index(meta)
|
28
|
+
if metadata.to_s.match(/=$/)
|
29
|
+
self.set_metadata(meta, value)
|
30
|
+
if @inventor_keys.index(meta)
|
31
|
+
@inventors[0].instance_variable_set("@#{meta}", value)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
return self.metadata[meta]
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_metadata(key, val)
|
40
|
+
@metadata[key] = val
|
41
|
+
if @inventor_keys.index(key)
|
42
|
+
@inventors[0].instance_variable_set("@#{key}", val)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_inventor(inventor)
|
47
|
+
raise ArgumentError, "Argument must be an OpenURL::Author!" unless inventor.is_a?(OpenURL::Inventor)
|
48
|
+
@inventors << inventor
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove_inventor(inventor)
|
52
|
+
idx = inventor
|
53
|
+
idx = @inventors.index(inventor)
|
54
|
+
raise ArgumentError unless idx
|
55
|
+
@authors.delete_at(idx)
|
56
|
+
end
|
57
|
+
|
58
|
+
def serialize_metadata(elem, label)
|
59
|
+
meta = {}
|
60
|
+
metadata = elem.add_element("ctx:metadata")
|
61
|
+
meta["format_container"] = metadata.add_element("#{label}:#{@format}")
|
62
|
+
meta["format_container"].add_namespace(label, @xml_ns)
|
63
|
+
meta["format_container"].add_attribute("xsi:schemaLocation", "#{@xml_ns} http://www.openurl.info/registry/docs/info:ofi/fmt:xml:xsd:#{@format}")
|
64
|
+
@metadata.each do |k,v|
|
65
|
+
next if ['inv', 'invlast', 'invfirst'].index(k)
|
66
|
+
meta[k] = meta["format_container"].add_element("#{label}:#{k}")
|
67
|
+
meta[k].text = v
|
68
|
+
end
|
69
|
+
meta["inventor_container"] = meta["format_container"].add_element("#{label}:inventors")
|
70
|
+
@inventors.each do | inventor |
|
71
|
+
inventor.xml(meta["inventor_container"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
def import_xml_metadata(node)
|
75
|
+
mbv = REXML::XPath.first(node, "./ctx:metadata-by-val/ctx:metadata/fmt:#{@format}", {"fmt"=>@xml_ns})
|
76
|
+
if mbv
|
77
|
+
mbv.to_a.each do |m|
|
78
|
+
self.set_metadata(m.name(), m.get_text.value) if m.has_text?
|
79
|
+
if m.has_elements?
|
80
|
+
m.to_a.each do | md |
|
81
|
+
self.set_metadata(md.name(), md.get_text.value) if md.has_text?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
inv_num = 0
|
86
|
+
REXML::XPath.each(mbv, "fmt:inventors/fmt:inventor | fmt:inventor/fmt:inv", {"fmt"=>@xml_ns}) do | inventor |
|
87
|
+
empty_node = true
|
88
|
+
if inventor.name == "inventor"
|
89
|
+
inventor.elements.each do | inv_elem |
|
90
|
+
next unless @inventor_keys.index(inv_elem.name) and inv_elem.has_text?
|
91
|
+
empty_node = false
|
92
|
+
@inventors << OpenURL::Inventor.new unless @inventors[inv_num]
|
93
|
+
@inventors[inv_num].instance_variable_set("@#{inv_elem.name}".to_sym, inv_elem.get_text.value)
|
94
|
+
self.set_metadata(inv_elem.name, inv_elem.get_text.value) if inv_num == 0
|
95
|
+
end
|
96
|
+
elsif inventor.name.match(/^inv$/)
|
97
|
+
next unless inventor.has_text?
|
98
|
+
empty_node = false
|
99
|
+
@inventors << OpenURL::Inventor.new unless @inventors[inv_num]
|
100
|
+
@inventors[inv_num]["inv"] = inventor.get_text.value
|
101
|
+
self.set_metadata("inv", inventor.get_text.value) if inv_num == 0
|
102
|
+
end
|
103
|
+
inv_num += 1 unless empty_node
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Outputs the entity as a KEV array
|
109
|
+
|
110
|
+
def kev(abbr)
|
111
|
+
kevs = []
|
112
|
+
|
113
|
+
@metadata_keys.each do |key|
|
114
|
+
kevs << "#{abbr}.#{key}="+CGI.escape(@metadata[key]) if @metadata[key]
|
115
|
+
end
|
116
|
+
|
117
|
+
kevs << "#{abbr}_val_fmt="+CGI.escape(@kev_ns)
|
118
|
+
|
119
|
+
if @inventors[0] and not @inventors[0].empty?
|
120
|
+
@inventor_keys.each do | ikey |
|
121
|
+
key = ikey
|
122
|
+
key = "inventor" if ikey == "inv"
|
123
|
+
kevs << "#{abbr}.#{key}="+CGI.escape(@inventors[0].ikey) if @inventors[0].ikey
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
if @reference["format"]
|
129
|
+
kevs << "#{abbr}_ref_fmt="+CGI.escape(@reference["format"])
|
130
|
+
kevs << "#{abbr}_ref="+CGI.escape(@reference["location"])
|
131
|
+
end
|
132
|
+
|
133
|
+
@identifiers.each do |id|
|
134
|
+
kevs << "#{abbr}_id="+CGI.escape(id)
|
135
|
+
end
|
136
|
+
|
137
|
+
kevs << "#{abbr}_dat="+CGI.escape(@private_data) if @private_data
|
138
|
+
|
139
|
+
return kevs
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class PatentFactory < ContextObjectEntityFactory
|
144
|
+
@@identifiers = ["info:ofi/fmt:kev:mtx:patent","info:ofi/fmt:xml:xsd:patent"]
|
145
|
+
def self.identifiers
|
146
|
+
return @@identifiers
|
147
|
+
end
|
148
|
+
def self.create()
|
149
|
+
return OpenURL::Patent.new
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
#
|
2
|
+
# scholarly_common.rb
|
3
|
+
#
|
4
|
+
# Created on Nov 5, 2007, 3:24:35 PM
|
5
|
+
#
|
6
|
+
# To change this template, choose Tools | Templates
|
7
|
+
# and open the template in the editor.
|
8
|
+
|
9
|
+
|
10
|
+
module OpenURL
|
11
|
+
class ScholarlyCommon < ContextObjectEntity
|
12
|
+
attr_reader :authors
|
13
|
+
def initialize
|
14
|
+
super()
|
15
|
+
@authors = [OpenURL::Author.new]
|
16
|
+
@author_keys = ['aulast','aufirst','auinit','auinit1','auinitm','ausuffix',
|
17
|
+
'au', 'aucorp']
|
18
|
+
end
|
19
|
+
def method_missing(metadata, value=nil)
|
20
|
+
meta = metadata.to_s.sub(/=$/,'')
|
21
|
+
raise ArgumentError, "#{meta.to_s} is not a valid #{self.class} metadata field." unless (@author_keys+@metadata_keys).index(meta)
|
22
|
+
if metadata.to_s.match(/=$/)
|
23
|
+
self.set_metadata(meta, value)
|
24
|
+
if @author_keys.index(meta)
|
25
|
+
@authors[0].instance_variable_set("@#{meta}", value)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
return self.metadata[meta]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_metadata(key, val)
|
34
|
+
@metadata[key] = val.to_s
|
35
|
+
if @author_keys.index(key)
|
36
|
+
@authors[0].instance_variable_set("@#{key}", val)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def genre=(genre)
|
41
|
+
raise ArgumentError, "#{genre} is not a valid #{self.class} genre." unless @valid_genres.index(genre)
|
42
|
+
self.set_metadata('genre', genre)
|
43
|
+
end
|
44
|
+
|
45
|
+
def genre
|
46
|
+
return self.metadata["genre"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_author(author)
|
50
|
+
raise ArgumentError, "Argument must be an OpenURL::Author!" unless author.is_a?(OpenURL::Author)
|
51
|
+
@authors << author
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_author(author)
|
55
|
+
idx = author
|
56
|
+
idx = @authors.index(author)
|
57
|
+
raise ArgumentError unless idx
|
58
|
+
@authors.delete_at(idx)
|
59
|
+
end
|
60
|
+
|
61
|
+
def serialize_metadata(elem, label)
|
62
|
+
meta = {}
|
63
|
+
metadata = elem.add_element("ctx:metadata")
|
64
|
+
meta["format_container"] = metadata.add_element("#{label}:#{@format}")
|
65
|
+
meta["format_container"].add_namespace(label, @xml_ns)
|
66
|
+
meta["format_container"].add_attribute("xsi:schemaLocation", "#{@xml_ns} http://www.openurl.info/registry/docs/info:ofi/fmt:xml:xsd:#{@format}")
|
67
|
+
@metadata.each do |k,v|
|
68
|
+
next if ['au', 'aucorp', 'auinit', 'auinitm', 'aulast',
|
69
|
+
'aufirst', 'auinit1', 'ausuffix'].index(k)
|
70
|
+
meta[k] = meta["format_container"].add_element("#{label}:#{k}")
|
71
|
+
meta[k].text = v
|
72
|
+
end
|
73
|
+
meta["author_container"] = meta["format_container"].add_element("#{label}:authors")
|
74
|
+
@authors.each do | author |
|
75
|
+
author.xml(meta["author_container"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def import_xml_metadata(node)
|
80
|
+
mbv = REXML::XPath.first(node, "./ctx:metadata-by-val/ctx:metadata/fmt:#{@format}", {"ctx"=>"info:ofi/fmt:xml:xsd:ctx", "fmt"=>@xml_ns})
|
81
|
+
if mbv
|
82
|
+
mbv.to_a.each do |m|
|
83
|
+
self.set_metadata(m.name(), m.get_text.value) if m.has_text?
|
84
|
+
if m.has_elements?
|
85
|
+
m.to_a.each do | md |
|
86
|
+
self.set_metadata(md.name(), md.get_text.value) if md.has_text?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
auth_num = 0
|
91
|
+
REXML::XPath.each(mbv, "fmt:authors/fmt:author | fmt:authors/fmt:au | fmt:authors/fmt:aucorp", {"fmt"=>@xml_ns}) do | author |
|
92
|
+
empty_node = true
|
93
|
+
if author.name == "author"
|
94
|
+
author.elements.each do | auth_elem |
|
95
|
+
next unless @author_keys.index(auth_elem.name) and auth_elem.has_text?
|
96
|
+
empty_node = false
|
97
|
+
@authors << OpenURL::Author.new unless @authors[auth_num]
|
98
|
+
@authors[auth_num].instance_variable_set("@#{auth_elem.name}".to_sym, auth_elem.get_text.value)
|
99
|
+
self.set_metadata(auth_elem.name, auth_elem.get_text.value) if auth_num == 0
|
100
|
+
end
|
101
|
+
elsif author.name.match(/^au$|^aucorp$/)
|
102
|
+
next unless author.has_text?
|
103
|
+
empty_node = false
|
104
|
+
@authors << OpenURL::Author.new unless @authors[auth_num]
|
105
|
+
# This next line is causing an exception, replaced it with following line modeling from above clause. Don't entirely understand it.
|
106
|
+
# @authors[auth_num][author.name] = author.get_text.value
|
107
|
+
@authors[auth_num].instance_variable_set("@#{author.name}".to_sym, author.get_text.value)
|
108
|
+
self.set_metadata(author.name, author.get_text.value) if auth_num == 0
|
109
|
+
end
|
110
|
+
auth_num += 1 unless empty_node
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Author
|
117
|
+
attr_accessor :aulast, :aufirst, :auinit, :auinit1, :auinitm, :ausuffix,
|
118
|
+
:au, :aucorp
|
119
|
+
def initialize
|
120
|
+
end
|
121
|
+
|
122
|
+
def xml(elem)
|
123
|
+
if @au
|
124
|
+
au = elem.add_element("#{elem.prefix}:au")
|
125
|
+
au.text = @au
|
126
|
+
end
|
127
|
+
if @aucorp
|
128
|
+
aucorp = elem.add_element("#{elem.prefix}:aucorp")
|
129
|
+
aucorp.text = @aucorp
|
130
|
+
end
|
131
|
+
if @aulast || @aufirst || @auinit || @auinit1 || @auinitm || @ausuffix
|
132
|
+
author = elem.add_element("#{elem.prefix}:author")
|
133
|
+
if @aulast
|
134
|
+
aulast = author.add_element("#{elem.prefix}:aulast")
|
135
|
+
aulast.text = @aulast
|
136
|
+
end
|
137
|
+
if @aufirst
|
138
|
+
aufirst = author.add_element("#{elem.prefix}:aufirst")
|
139
|
+
aufirst.text = @aufirst
|
140
|
+
end
|
141
|
+
if @auinit
|
142
|
+
auinit = author.add_element("#{elem.prefix}:auinit")
|
143
|
+
auinit.text = @auinit
|
144
|
+
end
|
145
|
+
if @auinit1
|
146
|
+
auinit1 = author.add_element("#{elem.prefix}:auinit1")
|
147
|
+
auinit1.text = @auinit1
|
148
|
+
end
|
149
|
+
if @auinitm
|
150
|
+
auinitm = author.add_element("#{elem.prefix}:auinitm")
|
151
|
+
auinitm.text = @auinitm
|
152
|
+
end
|
153
|
+
if @ausuffix
|
154
|
+
ausuff = author.add_element("#{elem.prefix}:ausuffix")
|
155
|
+
ausuff.text = @ausuffix
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def empty?
|
161
|
+
self.instance_variables.each do | ivar |
|
162
|
+
return false if self.instance_variable_get(ivar)
|
163
|
+
end
|
164
|
+
return true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class Inventor
|
169
|
+
attr_accessor :invlast, :invfirst, :inv
|
170
|
+
def initialize
|
171
|
+
end
|
172
|
+
|
173
|
+
def xml(elem)
|
174
|
+
if @inv
|
175
|
+
inv = elem.add_element("#{elem.prefix}:inv")
|
176
|
+
inv.text = @inv
|
177
|
+
end
|
178
|
+
if @invlast || @invfirst
|
179
|
+
inventor = elem.add_element("#{elem.prefix}:inventor")
|
180
|
+
if @invlast
|
181
|
+
invlast = inventor.add_element("#{elem.prefix}:invlast")
|
182
|
+
invlast.text = @invlast
|
183
|
+
end
|
184
|
+
if @invfirst
|
185
|
+
invfirst = inventor.add_element("#{elem.prefix}:invfirst")
|
186
|
+
invfirst.text = @invfirst
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def empty?
|
192
|
+
self.instance_variables.each do | ivar |
|
193
|
+
return false if self.instance_variable_get(ivar)
|
194
|
+
end
|
195
|
+
return true
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|