1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
# defaults to stderr, may be changed to file output (e.g in opentox-service)
$logger = OTLogger.new(STDERR)
$logger.level = Logger::DEBUG
module OpenTox
attr_accessor :uri, :subjectid, :rdf, :response
# Ruby interface
# Create a new OpenTox object (does not load data from service)
# @param [optional,String] URI
# @param [optional,String] subjectid
# @return [OpenTox] OpenTox object
def initialize uri=nil, subjectid=nil
@uri = uri.to_s.chomp
@subjectid = subjectid
@rdf = RDF::Graph.new
end
# Load metadata from service
def pull
kind_of?(OpenTox::Dataset) ? uri = File.join(@uri,"metadata") : uri = @uri
# TODO generic method for all formats
parse_rdfxml RestClientWrapper.get(uri,{},{:accept => $default_rdf, :subjectid => @subjectid})
end
# Get object metadata
# @return [Hash] Metadata
def metadata
pull if @rdf.empty?
metadata = {}
@rdf.query([RDF::URI.new(@uri),nil,nil]).collect do |statement|
metadata[statement.predicate.to_s] ||= []
metadata[statement.predicate.to_s] << statement.object.to_s
end
metadata
end
# Get metadata values
# @param [RDF] Key from RDF Vocabularies
# @return [Array] Values for supplied key
def [](key)
pull if @rdf.empty?
result = @rdf.query([RDF::URI.new(@uri),key,nil]).collect{|statement| statement.object.to_s}
result.size == 1 ? result.first : result
end
# Save object at service
def save
#TODO: dynamic assignment
post self.to_rdfxml, { :content_type => $default_rdf}
end
RDF_FORMATS.each do |format|
# rdf parse methods for all formats e.g. parse_rdfxml
send :define_method, "parse_#{format}".to_sym do |rdf|
@rdf = RDF::Graph.new
RDF::Reader.for(format).new(rdf) do |reader|
reader.each_statement{ |statement| @rdf << statement }
end
end
# rdf serialization methods for all formats e.g. to_rdfxml
send :define_method, "to_#{format}".to_sym do
rdf = RDF::Writer.for(format).buffer do |writer|
@rdf.each{|statement| writer << statement}
end
rdf
end
end
# REST API
def get headers={}
headers[:subjectid] ||= @subjectid
headers[:accept] ||= 'application/rdf+xml'
@response = RestClientWrapper.get @uri, {}, headers
end
def post payload={}, headers={}
headers[:subjectid] ||= @subjectid
headers[:accept] ||= 'application/rdf+xml'
@response = RestClientWrapper.post(@uri.to_s, payload, headers)
end
def put payload={}, headers={}
headers[:subjectid] ||= @subjectid
headers[:accept] ||= 'application/rdf+xml'
@response = RestClientWrapper.put(@uri.to_s, payload, headers)
end
def delete headers={}
headers[:subjectid] ||= @subjectid
@response = RestClientWrapper.delete(@uri.to_s,nil,headers)
end
# class methods
module ClassMethods
def all service_uri, subjectid=nil
uris = RestClientWrapper.get(service_uri, {}, :accept => 'text/uri-list').split("\n").compact
uris.collect{|uri| URI.task?(service_uri) ? from_uri(uri, subjectid, false) : from_uri(uri, subjectid)}
end
def create service_uri, subjectid=nil
uri = RestClientWrapper.post(service_uri, {}, {:accept => 'text/uri-list', :subjectid => subjectid})
URI.task?(service_uri) ? from_uri(uri, subjectid, false) : from_uri(uri, subjectid)
end
def from_file service_uri, filename, subjectid=nil
file = File.new filename
from_uri RestClientWrapper.post(service_uri, {:file => file}, {:subjectid => subjectid, :content_type => file.mime_type, :accept => "text/uri-list"}), subjectid
end
private
def from_uri uri, subjectid=nil, wait=true
uri.chomp!
# TODO add waiting task
if URI.task? uri and wait
t = OpenTox::Task.new(uri)
t.wait
uri = t.resultURI
end
# guess class from uri, this is potentially unsafe, but polling metadata from large uri lists is way too slow (and not all service provide RDF.type in their metadata)
result = CLASSES.collect{|s| s if uri =~ /#{s.downcase}/}.compact
internal_server_error "Cannot determine class from URI: '#{uri}.\nCandidate classes are #{result.inspect}" unless result.size == 1
klass = result.first
# initialize with/without subjectid
subjectid ? eval("#{self}.new(\"#{uri}\", #{subjectid})") : eval("#{self}.new(\"#{uri}\")")
end
end
# create default OpenTox classes
CLASSES.each do |klass|
c = Class.new do
include OpenTox
extend OpenTox::ClassMethods
end
OpenTox.const_set klass,c
end
end
|