| 1 | require 'active_rdf' |
|---|
| 2 | |
|---|
| 3 | # Constructs Ruby classes for RDFS classes (in the right namespace) |
|---|
| 4 | |
|---|
| 5 | class ObjectManager |
|---|
| 6 | # Constructs empty Ruby classes for all RDF types found in the data. Allows |
|---|
| 7 | # users to invoke methods on classes (e.g. FOAF::Person) without |
|---|
| 8 | # getting symbol undefined errors (because e.g. foaf:person wasnt encountered |
|---|
| 9 | # before so no class was created for it) |
|---|
| 10 | def self.construct_classes |
|---|
| 11 | # find all rdf:types and construct class for each of them |
|---|
| 12 | #q = Query.new.select(:t).where(:s,Namespace.lookup(:rdf,:type),:t) |
|---|
| 13 | |
|---|
| 14 | # find everything defined as rdfs:class or owl:class |
|---|
| 15 | type = Namespace.lookup(:rdf,:type) |
|---|
| 16 | rdfsklass = Namespace.lookup(:rdfs,:Class) |
|---|
| 17 | |
|---|
| 18 | # TODO: we should not do this, we should not support OWL |
|---|
| 19 | # instead, owl:Class is defined as subclass-of rdfs:Class, so if the |
|---|
| 20 | # reasoner has access to owl definition it should work out fine. |
|---|
| 21 | owlklass = Namespace.lookup(:owl,:Class) |
|---|
| 22 | |
|---|
| 23 | klasses = [] |
|---|
| 24 | klasses << Query.new.distinct(:s).where(:s,type,rdfsklass).execute |
|---|
| 25 | klasses << Query.new.distinct(:s).where(:s,type,owlklass).execute |
|---|
| 26 | |
|---|
| 27 | # flattening to get rid of nested arrays |
|---|
| 28 | # compacting array to get rid of nil (if one of these queries returned nil) |
|---|
| 29 | klasses = klasses.flatten.compact |
|---|
| 30 | $activerdflog.debug "ObjectManager: construct_classes: classes found: #{klasses}" |
|---|
| 31 | |
|---|
| 32 | # then we construct a Ruby class for each found rdfs:class |
|---|
| 33 | # and return the set of all constructed classes |
|---|
| 34 | klasses.collect { |t| construct_class(t) } |
|---|
| 35 | end |
|---|
| 36 | |
|---|
| 37 | # constructs Ruby class for the given resource (and puts it into the module as |
|---|
| 38 | # defined by the registered namespace abbreviations) |
|---|
| 39 | def self.construct_class(resource) |
|---|
| 40 | # get prefix abbreviation and localname from type |
|---|
| 41 | # e.g. :foaf and Person |
|---|
| 42 | localname = Namespace.localname(resource) |
|---|
| 43 | prefix = Namespace.prefix(resource) |
|---|
| 44 | |
|---|
| 45 | # find (ruby-acceptable) names for the module and class |
|---|
| 46 | # e.g. FOAF and Person |
|---|
| 47 | if prefix.nil? |
|---|
| 48 | # if the prefix is unknown, we create our own from the full URI |
|---|
| 49 | modulename = create_module_name(resource) |
|---|
| 50 | $activerdflog.debug "ObjectManager: construct_class: constructing modulename #{modulename} from URI #{resource}" |
|---|
| 51 | else |
|---|
| 52 | # otherwise we convert the registered prefix into a module name |
|---|
| 53 | modulename = prefix_to_module(prefix) |
|---|
| 54 | $activerdflog.debug "ObjectManager: construct_class: constructing modulename #{modulename} from registered prefix #{prefix}" |
|---|
| 55 | end |
|---|
| 56 | klassname = localname_to_class(localname) |
|---|
| 57 | |
|---|
| 58 | # look whether module defined |
|---|
| 59 | # else: create it |
|---|
| 60 | _module = if Object.const_defined?(modulename.to_sym) |
|---|
| 61 | $activerdflog.debug "ObjectManager: construct_class: module name #{modulename} previously defined" |
|---|
| 62 | Object.const_get(modulename.to_sym) |
|---|
| 63 | else |
|---|
| 64 | $activerdflog.debug "ObjectManager: construct_class: defining module name #{modulename} now" |
|---|
| 65 | Object.const_set(modulename, Module.new) |
|---|
| 66 | end |
|---|
| 67 | |
|---|
| 68 | # look whether class defined in that module |
|---|
| 69 | if _module.const_defined?(klassname.to_sym) |
|---|
| 70 | $activerdflog.debug "ObjectManager: construct_class: given class #{klassname} defined in the module" |
|---|
| 71 | # if so, return the existing class |
|---|
| 72 | _module.const_get(klassname.to_sym) |
|---|
| 73 | else |
|---|
| 74 | $activerdflog.debug "ObjectManager: construct_class: creating given class #{klassname}" |
|---|
| 75 | # otherwise: create it, inside that module, as subclass of RDFS::Resource |
|---|
| 76 | # (using toplevel Class.new to prevent RDFS::Class.new from being called) |
|---|
| 77 | klass = _module.module_eval("#{klassname} = Object::Class.new(RDFS::Resource)") |
|---|
| 78 | klass.class_uri = resource |
|---|
| 79 | klass |
|---|
| 80 | end |
|---|
| 81 | end |
|---|
| 82 | |
|---|
| 83 | def self.prefix_to_module(prefix) |
|---|
| 84 | # TODO: remove illegal characters |
|---|
| 85 | prefix.to_s.upcase |
|---|
| 86 | end |
|---|
| 87 | |
|---|
| 88 | def self.localname_to_class(localname) |
|---|
| 89 | # replace illegal characters inside the uri |
|---|
| 90 | # and capitalize the classname |
|---|
| 91 | replace_illegal_chars(localname).capitalize |
|---|
| 92 | end |
|---|
| 93 | |
|---|
| 94 | def self.create_module_name(resource) |
|---|
| 95 | # TODO: write unit test to verify replacement of all illegal characters |
|---|
| 96 | |
|---|
| 97 | # extract non-local part (including delimiter) |
|---|
| 98 | uri = resource.uri |
|---|
| 99 | delimiter = uri.rindex(/#|\//) |
|---|
| 100 | nonlocal = uri[0..delimiter] |
|---|
| 101 | |
|---|
| 102 | # remove illegal characters appearing at the end of the uri (e.g. trailing |
|---|
| 103 | # slash) |
|---|
| 104 | cleaned_non_local = nonlocal.gsub(/[^a-zA-Z0-9]+$/, '') |
|---|
| 105 | |
|---|
| 106 | # replace illegal chars within the uri |
|---|
| 107 | replace_illegal_chars(cleaned_non_local).upcase |
|---|
| 108 | end |
|---|
| 109 | |
|---|
| 110 | def self.replace_illegal_chars(name) |
|---|
| 111 | name.gsub(/[^a-zA-Z0-9]+/, '_') |
|---|
| 112 | end |
|---|
| 113 | |
|---|
| 114 | #declare the class level methods as private with these directives |
|---|
| 115 | private_class_method :prefix_to_module |
|---|
| 116 | private_class_method :localname_to_class |
|---|
| 117 | private_class_method :create_module_name |
|---|
| 118 | private_class_method :replace_illegal_chars |
|---|
| 119 | end |
|---|