summaryrefslogtreecommitdiff
path: root/lib/opentox.rb
diff options
context:
space:
mode:
authorChristoph Helma <helma@in-silico.ch>2013-03-26 10:56:04 +0100
committerChristoph Helma <helma@in-silico.ch>2013-03-26 10:56:04 +0100
commita54db46684680d98311631804eca367cc949a715 (patch)
tree283b8c5f256e8605131cbfeae2217a77d0288ca7 /lib/opentox.rb
parent4ba2cc9849473f97baf75195bb36c5057f1c58d4 (diff)
code cleanup and refactoring.
Diffstat (limited to 'lib/opentox.rb')
-rw-r--r--lib/opentox.rb225
1 files changed, 136 insertions, 89 deletions
diff --git a/lib/opentox.rb b/lib/opentox.rb
index 221a8dd..1251f33 100644
--- a/lib/opentox.rb
+++ b/lib/opentox.rb
@@ -3,109 +3,110 @@ $logger = OTLogger.new(STDERR)
$logger.level = Logger::DEBUG
module OpenTox
+ #include RDF CH: leads to namespace clashes with URI class
- attr_accessor :uri, :subjectid, :rdf
+ attr_reader :uri, :subjectid
+ attr_writer :metadata, :parameters
# Ruby interface
- # Create a new OpenTox object (does not load data from service)
+ # Create a new OpenTox object
# @param [optional,String] URI
# @param [optional,String] subjectid
# @return [OpenTox] OpenTox object
def initialize uri=nil, subjectid=nil
@rdf = RDF::Graph.new
- if uri
- @uri = uri.to_s.chomp
- else
- service = self.class.to_s.split('::').last.downcase
- service_uri = eval("$#{service}[:uri]")
- bad_request_error "$#{service}[:uri] variable not set. Please set $#{service}[:uri] or use an explicit uri as first constructor argument " unless service_uri
- @uri = File.join service_uri, SecureRandom.uuid
- end
- append RDF.type, eval("RDF::OT."+self.class.to_s.split('::').last)
- append RDF::DC.date, DateTime.now
@subjectid = subjectid
+ @metadata = {}
+ @parameters = []
+ uri ? @uri = uri.to_s.chomp : @uri = File.join(service_uri, SecureRandom.uuid)
end
- # Object metadata
+ # Object metadata (lazy loading)
# @return [Hash] Object metadata
- def metadata
- # return plain strings instead of RDF objects
- @rdf.to_hash[RDF::URI.new(@uri)].inject({}) { |h, (predicate, values)| h[predicate.to_s] = values.collect{|v| v.to_s}; h }
+ def metadata force_update=false
+ if (@metadata.empty? or force_update) and URI.accessible? @uri
+ get if @rdf.empty? or force_update
+ # return values as plain strings instead of RDF objects
+ @metadata = @rdf.to_hash[RDF::URI.new(@uri)].inject({}) { |h, (predicate, values)| h[predicate] = values.collect{|v| v.to_s}; h }
+ end
+ @metadata
end
# Metadata values
# @param [String] Predicate URI
# @return [Array, String] Predicate value(s)
def [](predicate)
- return nil if metadata[predicate.to_s].nil?
- metadata[predicate.to_s].size == 1 ? metadata[predicate.to_s].first : metadata[predicate.to_s]
+ return nil if metadata[predicate].nil?
+ metadata[predicate].size == 1 ? metadata[predicate].first : metadata[predicate]
end
- # Set object metadata
+ # Set a metadata entry
# @param [String] Predicate URI
# @param [Array, String] Predicate value(s)
def []=(predicate,values)
- @rdf.delete [RDF::URI.new(@uri.to_s),RDF::URI.new(predicate.to_s),nil]
- append predicate.to_s, values
- end
-
- def parameters
- params = {}
- query = RDF::Query.new({
- :parameter => {
- RDF.type => RDF::OT.Parameter,
- :property => :value,
- }
- })
- query.execute(@rdf).each do |solution|
- params[solution.parameter] = {} unless params[solution.parameter]
- params[solution.parameter][solution.property.to_s] = solution.value.to_s
- end
- params.values
- end
-
- def parameters=(parameters)
- parameters.each do |param|
- p_node = RDF::Node.new
- @rdf << [RDF::URI.new(@uri), RDF::OT.parameters, p_node]
- @rdf << [p_node, RDF.type, RDF::OT.Parameter]
- param.each{ |p,o| @rdf << [p_node, p, o] }
+ @metadata[predicate] = [values].flatten
+ end
+
+ def parameters force_update=false
+ if (@parameters.empty? or force_update) and URI.accessible? @uri
+ get if @rdf.empty? or force_update
+ params = {}
+ query = RDF::Query.new({
+ :parameter => {
+ RDF.type => RDF::OT.Parameter,
+ :property => :value,
+ }
+ })
+ query.execute(@rdf).each do |solution|
+ params[solution.parameter] = {} unless params[solution.parameter]
+ params[solution.parameter][solution.property] = solution.value
+ end
+ @parameters = params.values
end
+ @parameters
end
- # Append object metadata
- # @param [String] Predicate URI
- # @param [Array, String] Predicate value(s)
- def append(predicate,values)
- uri = RDF::URI.new @uri
- predicate = RDF::URI.new predicate
- [values].flatten.each { |value| @rdf << [uri, predicate, value] }
+ def parameter_value title
+ @parameters.collect{|p| p[RDF::OT.paramValue] if p[RDF::DC.title] == title}.compact.first
end
# Get object from webservice
def get mime_type="text/plain"
+ bad_request_error "Mime type #{mime_type} is not supported. Please use 'text/plain' (default) or 'application/rdf+xml'." unless mime_type == "text/plain" or mime_type == "application/rdf+xml"
response = RestClientWrapper.get(@uri,{},{:accept => mime_type, :subjectid => @subjectid})
if URI.task?(response)
- wait_for_task response
- response = RestClientWrapper.get(t.resultURI,{},{:accept => mime_type, :subjectid => @subjectid})
+ uri = wait_for_task response
+ response = RestClientWrapper.get(uri,{},{:accept => mime_type, :subjectid => @subjectid})
end
parse_ntriples response if mime_type == "text/plain"
parse_rdfxml response if mime_type == "application/rdf+xml"
end
- # Post object to webservice
- def post params=nil, wait=true
- # TODO: RDFXML
- uri = RestClientWrapper.post @uri.to_s, params, { :content_type => "text/plain", :subjectid => @subjectid}
- wait_for_task uri if wait
- end
-
- # Save object at webservice
- def put wait=true
- # TODO: RDFXML
- uri = RestClientWrapper.put @uri.to_s, self.to_ntriples, { :content_type => "text/plain", :subjectid => @subjectid}
- wait_for_task uri if wait
+ # Post object to webservice (append to object), rarely useful and deprecated
+ def post wait=true, mime_type="text/plain"
+ bad_request_error "Mime type #{mime_type} is not supported. Please use 'text/plain' (default) or 'application/rdf+xml'." unless mime_type == "text/plain" or mime_type == "application/rdf+xml"
+ case mime_type
+ when 'text/plain'
+ body = self.to_ntriples
+ when 'application/rdf+xml'
+ body = self.to_rdfxml
+ end
+ uri = RestClientWrapper.post @uri.to_s, body, { :content_type => mime_type, :subjectid => @subjectid}
+ wait ? wait_for_task(uri) : uri
+ end
+
+ # Save object at webservice (replace or create object)
+ def put wait=true, mime_type="text/plain"
+ bad_request_error "Mime type #{mime_type} is not supported. Please use 'text/plain' (default) or 'application/rdf+xml'." unless mime_type == "text/plain" or mime_type == "application/rdf+xml"
+ case mime_type
+ when 'text/plain'
+ body = self.to_ntriples
+ when 'application/rdf+xml'
+ body = self.to_rdfxml
+ end
+ uri = RestClientWrapper.put @uri.to_s, body, { :content_type => mime_type, :subjectid => @subjectid}
+ wait ? wait_for_task(uri) : uri
end
# Delete object at webservice
@@ -113,29 +114,30 @@ module OpenTox
RestClientWrapper.delete(@uri.to_s,nil,{:subjectid => @subjectid})
end
- def wait_for_task uri
- OpenTox.wait_for_task uri
+ def service_uri
+ self.class.service_uri
end
- def self.wait_for_task uri
- if URI.task?(uri)
- t = OpenTox::Task.new uri
- t.wait
- unless t.completed?
- method = RestClientWrapper.known_errors.select{|error| error[:code] == t.code}.first[:method]
- t.get
- Object.send(method,t.error_report[RDF::OT.message],t.uri)
- end
- uri = t.resultURI
+ def create_rdf
+ @rdf = RDF::Graph.new
+ @metadata[RDF.type] ||= eval("RDF::OT."+self.class.to_s.split('::').last)
+ @metadata[RDF::DC.date] ||= DateTime.now
+ @metadata.each do |predicate,values|
+ [values].flatten.each { |value| @rdf << [RDF::URI.new(@uri), predicate, value] }
+ end
+ @parameters.each do |parameter|
+ p_node = RDF::Node.new
+ @rdf << [RDF::URI.new(@uri), RDF::OT.parameters, p_node]
+ @rdf << [p_node, RDF.type, RDF::OT.Parameter]
+ parameter.each { |k,v| @rdf << [p_node, k, v] }
end
- uri
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,init=true|
- @rdf = RDF::Graph.new if init
+ 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
@@ -143,25 +145,27 @@ module OpenTox
# rdf serialization methods for all formats e.g. to_rdfxml
send :define_method, "to_#{format}".to_sym do
- RDF::Writer.for(format).buffer do |writer|
+ create_rdf
+ RDF::Writer.for(format).buffer(:encoding => Encoding::ASCII) do |writer|
@rdf.each{|statement| writer << statement}
end
end
end
- def to_turtle # redefine to use prefixes (not supported by RDF::Writer)
+ def to_turtle # redefined to use prefixes (not supported by RDF::Writer)
prefixes = {:rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#"}
['OT', 'DC', 'XSD', 'OLO'].each{|p| prefixes[p.downcase.to_sym] = eval("RDF::#{p}.to_s") }
+ create_rdf
RDF::N3::Writer.for(:turtle).buffer(:prefixes => prefixes) do |writer|
@rdf.each{|statement| writer << statement}
end
end
- {
- :title => RDF::DC.title,
- :dexcription => RDF::DC.description,
- :type => RDF.type
- }.each do |method,predicate|
+ def to_html
+ to_turtle.to_html
+ end
+
+ { :title => RDF::DC.title, :dexcription => RDF::DC.description, :type => RDF.type }.each do |method,predicate|
send :define_method, method do
self.[](predicate)
end
@@ -170,15 +174,58 @@ module OpenTox
end
end
- # create default OpenTox classes
+ # create default OpenTox classes with class methods
CLASSES.each do |klass|
c = Class.new do
include OpenTox
- def self.all service_uri, subjectid=nil
+ def self.all subjectid=nil
uris = RestClientWrapper.get(service_uri, {}, :accept => 'text/uri-list').split("\n").compact
uris.collect{|uri| self.new(uri, subjectid)}
end
+
+ def self.find uri, subjectid=nil
+ URI.accessible?(uri) ? self.new(uri, subjectid) : nil
+ end
+
+ def self.create metadata, subjectid=nil
+ object = self.new nil, subjectid
+ object.metadata = metadata
+ object.put
+ object
+ end
+
+ def self.find_or_create metadata, subjectid=nil
+ sparql = "SELECT DISTINCT ?s WHERE { "
+ metadata.each do |predicate,objects|
+ unless [RDF::DC.date,RDF::DC.modified,RDF::DC.description].include? predicate # remove dates and description (strange characters in description may lead to SPARQL errors)
+ if objects.is_a? String
+ URI.valid?(objects) ? o = "<#{objects}>" : o = "'''#{objects}'''"
+ sparql << "?s <#{predicate}> #{o}. "
+ elsif objects.is_a? Array
+ objects.each do |object|
+ URI.valid?(object) ? o = "<#{object}>" : o = "'#{object}'"
+ sparql << "?s <#{predicate}> #{o}. "
+ end
+ end
+ end
+ end
+ sparql << "}"
+ uris = RestClientWrapper.get(service_uri,{:query => sparql},{:accept => "text/uri-list", :subjectid => @subjectid}).split("\n")
+ if uris.empty?
+ self.create metadata, subjectid
+ else
+ self.new uris.first
+ end
+ end
+
+ def self.service_uri
+ service = self.to_s.split('::').last.downcase
+ eval("$#{service}[:uri]")
+ rescue
+ bad_request_error "$#{service}[:uri] variable not set. Please set $#{service}[:uri] or use an explicit uri as first constructor argument "
+ end
+
end
OpenTox.const_set klass,c
end