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

OrderedSource, 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, 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::Rdf

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

Methods included from TaliaCore::ActiveSourceParts::PredicateHandler

#each_cached_wrapper, #get_objects_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.



97
98
99
100
101
102
103
# File 'lib/talia_core/active_source.rb', line 97

def [](attribute)
  if(db_attr?(attribute))
    super(attribute)
  else
    get_objects_on(attribute)
  end
end

#[]=(attribute, value) ⇒ Object

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



107
108
109
110
111
112
113
114
115
# File 'lib/talia_core/active_source.rb', line 107

def []=(attribute, value)
  if(db_attr?(attribute))
    super(attribute, value)
  else
    pred = get_attribute(attribute)
    pred.remove
    pred << 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



326
327
328
329
330
331
332
333
334
335
336
# File 'lib/talia_core/active_source.rb', line 326

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



205
206
207
208
209
210
211
212
# File 'lib/talia_core/active_source.rb', line 205

def add_semantic_attributes(overwrite, attributes)
  attributes.each do |attr, value|
    value = [ value ] unless(value.is_a?(Array))
    attr_wrap = self[attr]
    attr_wrap.remove if(overwrite)
    value.each { |val |self[attr] << target_for(val) }
  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.



348
349
350
351
352
353
354
355
356
357
# File 'lib/talia_core/active_source.rb', line 348

def attach_files(files)
  files = [ files ] unless(files.is_a?(Array))
  files.each do |file|
    filename = file[:url] || file['url']
    assit(filename)
    options = file[: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



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

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)


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

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

#direct_predicatesObject

Gets the direct predicates (using the database)

Raises:

  • (ActiveRecord::RecordNotFound)


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

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.



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/talia_core/active_source.rb', line 223

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)


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

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.



242
243
244
# File 'lib/talia_core/active_source.rb', line 242

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



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

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!*



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

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



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

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

#rdf_selftypeObject

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



373
374
375
# File 'lib/talia_core/active_source.rb', line 373

def rdf_selftype
  (N::TALIA + self.class.name.demodulize)
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:



187
188
189
190
# File 'lib/talia_core/active_source.rb', line 187

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:



193
194
195
196
# File 'lib/talia_core/active_source.rb', line 193

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

#short_uriObject

Uri in short notation



72
73
74
# File 'lib/talia_core/active_source.rb', line 72

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.



316
317
318
319
# File 'lib/talia_core/active_source.rb', line 316

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.



83
84
85
# File 'lib/talia_core/active_source.rb', line 83

def to_s
  self[:uri]
end

#to_uriObject

Create a new uri object



88
89
90
# File 'lib/talia_core/active_source.rb', line 88

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.



309
310
311
312
# File 'lib/talia_core/active_source.rb', line 309

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:



174
175
176
177
# File 'lib/talia_core/active_source.rb', line 174

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:



180
181
182
183
# File 'lib/talia_core/active_source.rb', line 180

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



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

alias :update_attributes_orig :update_attributes

#update_attributes_orig!Object



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

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)



128
129
130
131
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
# File 'lib/talia_core/active_source.rb', line 128

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



77
78
79
# File 'lib/talia_core/active_source.rb', line 77

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.



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/talia_core/active_source.rb', line 288

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