Class: TaliaCore::ActiveSource

Overview

This class encapsulate the basic “Source” behaviour for an element in the semantic store. This is the baseclass for all things that are represented as an “Resource” (with URL) in the semantic store.

If an object is modified but not saved, the ActiveSource does not guarantee that the RDF will always be in sync with the database. However, a subsequent saving of the object will re-sync the RDF.

An effort is made to treat RDF and database writes in the same way, so that they should usually be in sync:

  • Write operations are usually lazy, the data should only be saved once save! is called. However, ActiveRecord may still decide that the objects need to be saved if they are involved in other operations.

  • Operations that clear a predicate are immediate. That also means that using singular property setter, if used will immediately erase the old value. If the record is not saved, the property will be left empty (and not revert to the original value!)

Direct Known Subclasses

Source

Instance Method Summary collapse

Methods included from TaliaCore::ActiveSourceParts::ClassMethods

additional_rdf_types, create_from_xml, create_multi_from, create_source, exists?, expand_uri, new, paginate, props_to_destroy, rewrite, split_attribute_hash, update

Methods included from TaliaCore::ActiveSourceParts::Finders

count, find, find_by_partial_local, find_by_partial_uri, find_by_uri_token

Methods included from TaliaCore::ActiveSourceParts::SqlHelper

default_inv_joins, default_joins, props_join, sources_join

Methods included from TaliaCore::ActiveSourceParts::PredicateHandler::ClassMethods

prefetch_relations_for

Methods included from TaliaUtil::Progressable

progressor, progressor=, run_with_progress

Methods included from TaliaCore::ActiveSourceParts::RdfHandler

#autosave_rdf=, #autosave_rdf?, #create_rdf, #my_rdf

Methods included from TaliaCore::ActiveSourceParts::PredicateHandler

#each_cached_wrapper, #get_objects_on, #get_wrapper_on, #has_type?, #inject_predicate, #reset!, #save_wrappers, #types

Instance Method Details

#[](attribute) ⇒ Object Also known as: get_attribute

Works in the normal way for database attributes. If the value is not an attribute, it tries to find objects related to this source with the value as a predicate URL and returns a collection of those.

The assignment operator remains as it is for the ActiveRecord.



99
100
101
102
103
104
105
106
107
# File 'lib/talia_core/active_source.rb', line 99

def [](attribute)
  if(db_attr?(attribute))
    super(attribute)
  elsif(defined_property?(attribute))
    self.send(attribute)
  else
    get_objects_on(attribute)
  end
end

#[]=(attribute, value) ⇒ Object

Assignment to an attribute. This will overwrite all current triples.



111
112
113
114
115
116
117
118
119
# File 'lib/talia_core/active_source.rb', line 111

def []=(attribute, value)
  if(db_attr?(attribute))
    super(attribute, value)
  elsif(defined_property?(attribute))
    self.send("#{attribute}=", value)
  else
     get_wrapper_on(attribute).replace(value)
  end
end

#add_additional_rdf_typesObject

Add the additional types to the source that were configured in the class. Usually this will not need to be called directly, but will be automatically called during construction.

This will check the existing types to avoid duplication



341
342
343
344
345
346
347
348
349
350
351
# File 'lib/talia_core/active_source.rb', line 341

def add_additional_rdf_types
  # return if(self.class.additional_rdf_types.empty?)
  type_hash = {}
  self.types.each { |type| type_hash[type.respond_to?(:uri) ? type.uri.to_s : type.to_s] = true }
  # Add the "class" default type type (unless this is the source for the self type itself).0
  self.types << rdf_selftype unless(type_hash[rdf_selftype.to_s] || (rdf_selftype.to_s == self.uri.to_s))
  # Add the user-configured types
  self.class.additional_rdf_types.each do |type|
    self.types << type unless(type_hash[type.respond_to?(:uri) ? type.uri.to_s : type.to_s])
  end
end

#add_semantic_attributes(overwrite, attributes) ⇒ Object

Helper to update semantic attributes from the given hash. If there is a “<value>” string, it will be treated as a reference to an URI. Hash values may be arrays.

If overwrite is set to yes, the given attributes (and only those) are replaced with the values from the hash. Otherwise the attribute values will be added to the existing ones



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/talia_core/active_source.rb', line 209

def add_semantic_attributes(overwrite, attributes)
  attributes.each do |attr, value|
    if(defined_property?(attr))
      self[attr] = value
    else
      attr_wrap = self[attr]
      attr_wrap.remove if(overwrite)
      value.to_a.each { |val| self[attr] << target_for(val) }
    end
  end
end

#attach_files(files) ⇒ Object

Attaches files from the given hash. See the new method on ActiveSource for the details.

The call in this case should look like this:

attach_files([{ url => 'url_or_filename', :options => { .. }}, ...])

Have a look at the DataLoader module to see how the options work. You may also provide a single hash for :files (instead of an array) if you have just one file. Files will be saved immediately.



363
364
365
366
367
368
369
370
371
372
373
# File 'lib/talia_core/active_source.rb', line 363

def attach_files(files)
  files = [ files ] unless(files.is_a?(Array))
  files.each do |file|
    file.to_options!
    filename = file[:url]
    assit(filename)
    options = file[:options] || {}
    records = DataTypes::FileRecord.create_from_url(filename, options)
    records.each { |rec| self.data_records << rec }
  end
end

#data(type = nil, location = nil) ⇒ Object

This will return a list of DataRecord objects. Without parameters, this returns all data elements on the source. If a type is given, it will return only the elements of the given type. If both type and location are given, it will retrieve only the specified data element



379
380
381
382
383
384
385
386
# File 'lib/talia_core/active_source.rb', line 379

def data(type = nil, location= nil)
  find_type = location ? :first : :all # Find just one element if a location is given
  type = type.name if(type.is_a?(Class))
  options = {}
  options[:conditions] = [ "type = ?", type ] if(type && !location)
  options[:conditions] = [ "type = ? AND location = ?", type, location ] if(type && location)
  data_records.find(find_type, options)
end

#db_attr?(attribute) ⇒ Boolean

True if the given attribute is a database attribute

Returns:

  • (Boolean)


287
288
289
# File 'lib/talia_core/active_source.rb', line 287

def db_attr?(attribute)
  ActiveSource.db_attr?(attribute)
end

#defined_property?(prop_name) ⇒ Boolean

Returns:

  • (Boolean)


291
292
293
# File 'lib/talia_core/active_source.rb', line 291

def defined_property?(prop_name)
  self.class.defined_property?(prop_name)
end

#direct_predicatesObject

Gets the direct predicates (using the database)

Raises:

  • (ActiveRecord::RecordNotFound)


273
274
275
276
277
# File 'lib/talia_core/active_source.rb', line 273

def direct_predicates
  raise(ActiveRecord::RecordNotFound, "Cannot do this on unsaved record.") if(new_record?)
  rels = SemanticRelation.find_by_sql("SELECT DISTINCT predicate_uri FROM semantic_relations WHERE subject_id = #{self.id}")
  rels.collect { |rel| N::Predicate.new(rel.predicate_uri) }
end

#inverseObject

Returns a special object which collects the “inverse” properties of the Source - these are all RDF properties which have the current Source as the object.

The returned object supports the [] operator, which allows to fetch the “inverse” (the RDF subjects) for the given predicate.

Example: person.inverse[N::FOO::creator] would return a list of all the elements of which the current person is the creator.



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/talia_core/active_source.rb', line 230

def inverse
  inverseobj = Object.new
  inverseobj.instance_variable_set(:@assoc_source, self)
  
  class << inverseobj     
    
    def [](property)
      @assoc_source.subjects.find(:all, :conditions => { 'semantic_relations.predicate_uri' => property.to_s } )
    end
    
    private :type
  end
  
  inverseobj
end

#inverse_predicatesObject

Gets the inverse predicates

Raises:

  • (ActiveRecord::RecordNotFound)


280
281
282
283
284
# File 'lib/talia_core/active_source.rb', line 280

def inverse_predicates
  raise(ActiveRecord::RecordNotFound, "Cannot do this on unsaved record.") if(new_record?)
  rels = SemanticRelation.find_by_sql("SELECT DISTINCT predicate_uri FROM semantic_relations WHERE object_id = #{self.id}")
  rels.collect { |rel| N::Predicate.new(rel.predicate_uri) }
end

#predicate(namespace, name) ⇒ Object

Accessor that allows to lookup a namespace/name combination. This works like the [] method: I will return an array-like object on predicates can be manipulated.



249
250
251
# File 'lib/talia_core/active_source.rb', line 249

def predicate(namespace, name)
  get_objects_on(get_namespace(namespace, name))
end

#predicate_replace(namespace, name, value) ⇒ Object

Replaces the given predicate with the value. Good for one-value predicates



266
267
268
269
270
# File 'lib/talia_core/active_source.rb', line 266

def predicate_replace(namespace, name, value)
  pred = predicate(namespace, name)
  pred.remove
  pred << value
end

#predicate_set(namespace, name, value) ⇒ Object

Setter method for predicates by namespace/name combination. This will *add a precdicate triple, not replace one!*



255
256
257
# File 'lib/talia_core/active_source.rb', line 255

def predicate_set(namespace, name, value)
  predicate(namespace, name) << value
end

#predicate_set_uniq(namespace, name, value) ⇒ Object

Setter method that will only add the value if it doesn’t exist already



260
261
262
263
# File 'lib/talia_core/active_source.rb', line 260

def predicate_set_uniq(namespace, name, value)
  pred = predicate(namespace, name)
  pred << value unless(pred.include?(value))
end

#property_options_for(property) ⇒ Object



295
296
297
# File 'lib/talia_core/active_source.rb', line 295

def property_options_for(property)
  self.class.property_options_for(property)
end

#rdf_selftypeObject

The RDF type that is used for objects of the current class



389
390
391
# File 'lib/talia_core/active_source.rb', line 389

def rdf_selftype
  (N::TALIA + self.class.name.demodulize)
end

#reloadObject



393
394
395
396
# File 'lib/talia_core/active_source.rb', line 393

def reload
  reset! # Clear the property cache
  super
end

#rewrite_attributes(attributes) {|_self| ... } ⇒ Object

Works like update_attributes, but will replace the semantic attributes rather than adding to them.

Yields:

  • (_self)

Yield Parameters:



191
192
193
194
# File 'lib/talia_core/active_source.rb', line 191

def rewrite_attributes(attributes)
  yield self if(block_given?)
  update_attributes_orig(process_attributes(true, attributes)) 
end

#rewrite_attributes!(attributes) {|_self| ... } ⇒ Object

Like rewrite_attributes, but calling save!

Yields:

  • (_self)

Yield Parameters:



197
198
199
200
# File 'lib/talia_core/active_source.rb', line 197

def rewrite_attributes!(attributes)
  yield self if(block_given?)
  update_attributes_orig!(process_attributes(true, attributes))
end

#short_uriObject

Uri in short notation



74
75
76
# File 'lib/talia_core/active_source.rb', line 74

def short_uri
  N::URI.new(self.uri).to_name_s
end

#to_rdfObject

Creates an RDF/XML resprentation of the source. The object is saved if this is a new record.



331
332
333
334
# File 'lib/talia_core/active_source.rb', line 331

def to_rdf
  save! if(new_record?)
  ActiveSourceParts::Xml::RdfBuilder.build_source(self) 
end

#to_sObject

To string: Just return the URI. Use to_xml if you need something more involved.



85
86
87
# File 'lib/talia_core/active_source.rb', line 85

def to_s
  self[:uri]
end

#to_uriObject

Create a new uri object



90
91
92
# File 'lib/talia_core/active_source.rb', line 90

def to_uri
  self[:uri].to_uri
end

#to_xmlObject

XML Representation of the source. The object is saved if this is a new record.



324
325
326
327
# File 'lib/talia_core/active_source.rb', line 324

def to_xml
  save! if(new_record?)
  ActiveSourceParts::Xml::SourceBuilder.build_source(self)
end

#update_attributes(attributes) {|_self| ... } ⇒ Object

Updates all attributes of this source. For the database attributes, this works exactly like ActiveRecord::Base#update_attributes

If semantic attributes are present, they will be updated on the semantic store.

After the update, the source will be saved.

Yields:

  • (_self)

Yield Parameters:



178
179
180
181
# File 'lib/talia_core/active_source.rb', line 178

def update_attributes(attributes)
  yield self if(block_given?)
  super(process_attributes(false, attributes))
end

#update_attributes!(attributes) {|_self| ... } ⇒ Object

As update_attributes, but uses save! to save the source

Yields:

  • (_self)

Yield Parameters:



184
185
186
187
# File 'lib/talia_core/active_source.rb', line 184

def update_attributes!(attributes)
  yield self if(block_given?)
  super(process_attributes(false, attributes))
end

#update_attributes_origObject

Make aliases for the original updating methods



122
# File 'lib/talia_core/active_source.rb', line 122

alias :update_attributes_orig :update_attributes

#update_attributes_orig!Object



123
# File 'lib/talia_core/active_source.rb', line 123

alias :update_attributes_orig! :update_attributes!

#update_source(properties, mode) ⇒ Object

Updates the source with the given properties. The ‘mode’ field indicates if and how the update will be performed. See the ImportJobHelper class for the different modes.

As opposed to the *_attributes method, this will also handle file elements. The default mode is :skip (do nothing)



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
# File 'lib/talia_core/active_source.rb', line 132

def update_source(properties, mode)
  properties.to_options!
  mode = :update if(self.is_a?(SourceTypes::DummySource)) # Dummy sources are always updated
  mode ||= :skip
  mode = mode.to_sym
  return self if(mode == :skip) # If we're told to ignore updates
  
  # Deal with already existing sources
  files = properties.delete(:files)
  
  if(mode == :overwrite)
    # If we are to overwrite, delete all relations and update normally
    self.semantic_relations.destroy_all
    self.data_records.destroy_all
    mode = :update
  elsif(mode == :update && files && self.data_records.size > 0)
    # On updating we should only remove the files if there are new ones
    self.data_records.destroy_all
  end
  
  # Add any files
  attach_files(files) if(files)
  
  # Rewrite the type, if neccessary
  type = properties[:type]
  switch_type = type && (self.type != type)
  # Warn to the log if we have a problematic type change
  TaliaCore.logger.warn("WARNING: Type change from #{self.type} to #{type}") if(switch_type && !self.is_a?(SourceTypes::DummySource))
  self.type = type if(switch_type)
  
  # Now we should either be adding or updating
  assit(mode == :update || mode == :add)
  update = (mode == :update)
  
  # Overwrite with or add the imported attributes
  update ? rewrite_attributes(properties) : update_attributes(properties)
  
  self
end

#value_for(thing) ⇒ Object

Helper



79
80
81
# File 'lib/talia_core/active_source.rb', line 79

def value_for(thing)
  self.class.value_for(thing)
end

#write_predicate_direct(predicate, value) ⇒ Object

Writes the predicate directly to the database and the rdf store. The Source does not need to be saved and no data is loaded from the database. This is faster than adding the data normally and doing a full save, at least if only one or two predicates are written.



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/talia_core/active_source.rb', line 303

def write_predicate_direct(predicate, value)
  autosave = self.autosave_rdf?
  value.save! if(value.is_a?(ActiveSource) && value.new_record?)
  self.autosave_rdf = false
  self[predicate] << value
  uri_res = N::URI.new(predicate)
  # Now add the RDF data by hand
  if(value.kind_of?(Array))
    value.each do |v|
      my_rdf.direct_write_predicate(uri_res, v)
    end
  else
    my_rdf.direct_write_predicate(uri_res, value)
  end
  save! # Save without RDF save
  self.autosave_rdf = autosave
end