Class: RDFS::Resource

Inherits:
Object
  • Object
show all
Defined in:
lib/active_rdf/objectmanager/resource.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri) ⇒ Resource

creates new resource representing an RDF resource

Raises:



28
29
30
31
32
33
# File 'lib/active_rdf/objectmanager/resource.rb', line 28

def initialize uri
  raise ActiveRdfError, "creating resource <#{uri}>" unless uri.is_a?(String)

#      $log.debug "RDFS::Resource new: initializing new Resource with #{uri}"
  @uri = uri
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

manages invocations such as eyal.age

Raises:



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/active_rdf/objectmanager/resource.rb', line 132

def method_missing(method, *args)
    # possibilities:
    # 1. eyal.age is a property of eyal (triple exists <eyal> <age> "30")
    # evidence: eyal age ?a, ?a is not nil (only if value exists)
    # action: return ?a
    #
    # 2. eyal's class is in domain of age, but does not have value for eyal
    # explain: eyal is a person and some other person (not eyal) has an age
    # evidence: eyal type ?c, age domain ?c
    # action: return nil
    #
    # 3. eyal.age = 30 (setting a value for a property)
    # explain: eyal has (or could have) a value for age, and we update that value
    # complication: we need to find the full URI for age (by looking at
    # possible predicates to use
    # evidence: eyal age ?o  (eyal has a value for age now, we're updating it)
    # evidence: eyal type ?c, age domain ?c (eyal could have a value for age, we're setting it)
    # action: add triple (eyal, age, 30), return 30
    #
    # 4. eyal.age is a custom-written method in class Person
    # evidence: eyal type ?c, ?c.methods includes age
    # action: inject age into eyal and invoke

    # maybe change order in which to check these, checking (4) is probably
    # cheaper than (1)-(2) but (1) and (2) are probably more probable (getting
    # attribute values over executing custom methods)

    $log.debug "RDFS::Resource: method_missing on instance: called with method name #{method}"

    # are we doing an update or not?
    # checking if method ends with '='

    if method.to_s[-1..-1] == '='
      methodname = method.to_s[0..-2]
      update = true
    else
      methodname = method.to_s
      update = false
    end

    candidates = if update
                    class_level_predicates
                  else
                    direct_predicates
                  end
  
  # checking possibility (1) and (3)
  candidates.each do |pred|
    if Namespace.localname(pred) == methodname
      # found a property invocation of eyal: option 1) or 2)
      # query execution will return either the value for the predicate (1)
      # or nil (2)
      if update
        # TODO: delete old value if overwriting
        # FederiationManager.delete(self, pred, nil)

        # handling eyal.friends = [armin, andreas] --> expand array values
        args.each do |value|
          FederationManager.add(self, pred, value)
        end
        return args
      else
        # look into args, if it contains a hash with {:array => true} then
        # we should not flatten the query results
        return get_property_value(pred, args)
      end
    end
  end
  
  raise ActiveRdfError, "could not set #{methodname} to #{args}: no suitable predicate found. Maybe you are missing some shcema information?" if update

  # get/set attribute value did not succeed, so checking option (2) and (4)
  
  # checking possibility (2), it is not handled correctly above since we use
  # direct_predicates instead of class_level_predicates. If we didn't find
  # anything with direct_predicates, we need to try the
  # class_level_predicates. Only if we don't find either, we
  # throw "method_missing"
  candidates = class_level_predicates

  # if any of the class_level candidates fits the sought method, then we
  # found situation (2), so we return nil or [] depending on the {:array =>
  # true} value
  if candidates.any?{|c| Namespace.localname(c) == methodname}
    return_ary = args[0][:array] if args[0].is_a? Hash
    if return_ary
      return []
    else
      return nil

    end
  end

  # checking possibility (4)
  # TODO: implement search strategy to select in which class to invoke
  # e.g. if to_s defined in Resource and in Person we should use Person
  $log.debug "RDFS::Resource: method_missing on instance: branch selected: execution of custom class method"
  self.class.each do |klass|
    if klass.instance_methods.include?(method.to_s)
      _dup = klass.new(uri)
      return _dup.send(method,*args)
    end
  end

  # if none of the three possibilities work out, we don't know this method
  # invocation, but we don't want to throw NoMethodError, instead we return
  # nil, so that eyal.age does not raise error, but returns nil. (in RDFS,
  # we are never sure that eyal cannot have an age, we just dont know the
  # age right now)
  nil
end

Class Attribute Details

.class_uriObject

Returns the value of attribute class_uri.



21
22
23
# File 'lib/active_rdf/objectmanager/resource.rb', line 21

def class_uri
  @class_uri
end

Instance Attribute Details

#uriObject (readonly)

uri of the resource (for instances of this class: rdf resources)



25
26
27
# File 'lib/active_rdf/objectmanager/resource.rb', line 25

def uri
  @uri
end

Class Method Details

.find_allObject

returns array of all instances of this class (e.g. Person.find_all) (always returns collection)



116
117
118
119
120
121
122
123
124
125
# File 'lib/active_rdf/objectmanager/resource.rb', line 116

def Resource.find_all
  query = Query.new.distinct(:s).where(:s, Namespace.lookup(:rdf,:type), class_uri)
  if block_given?
    query.execute do |resource|
      yield resource
    end
  else
    query.execute(:flatten => false)
  end
end

.method_missing(method, *args) ⇒ Object

manages invocations such as Person.find_by_name



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
# File 'lib/active_rdf/objectmanager/resource.rb', line 77

def Resource.method_missing(method, *args)
  method_name = method.to_s

  $log.debug "RDFS::Resource: method_missing on class: called with method name #{method}"

  # extract predicates on which to match
  # e.g. find_by_name, find_by_name_and_age
  if match = /find_by_(.+)/.match(method_name)
    # find searched attributes, e.g. name, age
    attributes = match[1].split('_and_')

    # get list of possible predicates for this class
    possible_predicates = predicates

    # build query looking for all resources with the given parameters
    query = Query.new.distinct(:s)

    # add where clause for each attribute-value pair,
    # looking into possible_predicates to figure out
    # which full-URI to use for each given parameter (heuristic)

    attributes.each_with_index do |atr,i|
      possible_predicates.each do |pred|
        query.where(:s, pred, args[i]) if Namespace.localname(pred) == atr
      end
    end

    # execute query
    $log.debug "RDFS::Resource: method_missing on class: executing query: #{query}"
    return query.execute
  end

  # otherwise, if no match found, raise NoMethodError (in superclass)
  $log.warn 'RDFS::Resource: method_missing on class: method not matching find_by_*'
  super
end

.predicatesObject

returns the predicates that have this resource as their domain (applicable predicates for this resource)



71
72
73
74
# File 'lib/active_rdf/objectmanager/resource.rb', line 71

def Resource.predicates
  domain = Namespace.lookup(:rdfs, :domain)
  Query.new.distinct(:p).where(:p, domain, class_uri).execute || []
end

Instance Method Details

#<=>(other) ⇒ Object

overriding sort based on uri



61
62
63
# File 'lib/active_rdf/objectmanager/resource.rb', line 61

def <=>(other)
  uri <=> other.uri
end

#==(other) ⇒ Object Also known as: eql?, include?

a resource is same as another if they both represent the same uri



45
46
47
48
49
50
51
# File 'lib/active_rdf/objectmanager/resource.rb', line 45

def ==(other)
  if other.respond_to?(:uri)
    other.uri == self.uri
  else
    false
  end
end

#classObject

returns classes to which this resource belongs (according to rdf:type)



245
246
247
248
249
# File 'lib/active_rdf/objectmanager/resource.rb', line 245

def class
  types.collect do |type|
    ObjectManager.construct_class(type)
  end
end

#class_level_predicatesObject

returns all predicates that fall into the domain of the rdf:type of this resource



262
263
264
265
266
# File 'lib/active_rdf/objectmanager/resource.rb', line 262

def class_level_predicates
  type = Namespace.lookup(:rdf, 'type')
  domain = Namespace.lookup(:rdfs, 'domain')
  Query.new.distinct(:p).where(self,type,:t).where(:p, domain, :t).execute || []
end

#direct_predicates(distinct = true) ⇒ Object

returns all predicates that are directly defined for this resource



269
270
271
272
273
274
275
276
# File 'lib/active_rdf/objectmanager/resource.rb', line 269

def direct_predicates(distinct = true)
  if distinct
    q = Query.new.distinct(:p)
  else
    q = Query.new.select(:p)
  end
  q.where(self,:p, :o).execute(:flatten => false) || []
end

#hashObject

overriding hash to use uri.hash needed for array.uniq



56
57
58
# File 'lib/active_rdf/objectmanager/resource.rb', line 56

def hash
  uri.hash
end

#instance_of?(klass) ⇒ Boolean

overrides built-in instance_of? to use rdf:type definitions

Returns:

  • (Boolean)


256
257
258
# File 'lib/active_rdf/objectmanager/resource.rb', line 256

def instance_of?(klass)
  self.class.include?(klass)
end

#label(*args) ⇒ Object



306
307
308
309
310
311
312
313
314
315
316
# File 'lib/active_rdf/objectmanager/resource.rb', line 306

def label(*args)
  label = get_property_value(Namespace.lookup(:rdfs,:label)) || Namespace.localname(self)

# empty labels are not useful: replace them by localname
label = Namespace.localname(self) if label.empty?

# if we have no localname, use full uri
label = uri if label.empty?

label
end

#property_accessorsObject



278
279
280
# File 'lib/active_rdf/objectmanager/resource.rb', line 278

def property_accessors
  direct_predicates.collect {|pred| Namespace.localname(pred) }
end

#to_sObject

returns uri of resource, can be overridden in subclasses



302
303
304
# File 'lib/active_rdf/objectmanager/resource.rb', line 302

def to_s
  "resource: #{uri}"
end

#typeObject



251
252
253
# File 'lib/active_rdf/objectmanager/resource.rb', line 251

def type
  get_property_value(Namespace.lookup(:rdf,:type))
end

#typesObject

returns all rdf:types of this resource



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/active_rdf/objectmanager/resource.rb', line 283

def types
  type = Namespace.lookup(:rdf, :type)

  # we lookup the type in the database
  types = Query.new.distinct(:t).where(self,type,:t).execute(:flatten => false)

  # if we dont know it, we return Resource (as toplevel)
  # this should in theory actually never happen (since any node is a rdfs:Resource)
  # but could happen if the subject is unknown to the database
  # or if the database does not support RDFS inferencing
  return [Namespace.lookup(:rdfs,"Resource")] if types.empty?
  return types
end