talia_core 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. data/README.rdoc +41 -0
  2. data/bin/talia +33 -0
  3. data/lib/JXslt/jxslt.rb +60 -0
  4. data/lib/acts_as_roled.rb +11 -0
  5. data/lib/core_ext/platform.rb +9 -0
  6. data/lib/core_ext/string.rb +6 -0
  7. data/lib/core_ext.rb +1 -0
  8. data/lib/custom_template.rb +4 -0
  9. data/lib/loader_helper.rb +62 -0
  10. data/lib/mysql.rb +1214 -0
  11. data/lib/progressbar.rb +236 -0
  12. data/lib/role.rb +12 -0
  13. data/lib/talia_cl/command_line.rb +39 -0
  14. data/lib/talia_cl/commands/standalone/cl_options.rb +9 -0
  15. data/lib/talia_cl/commands/standalone/standalone_generate.rb +75 -0
  16. data/lib/talia_cl/commands/standalone.rb +25 -0
  17. data/lib/talia_cl/commands/talia_console/cl_options.rb +55 -0
  18. data/lib/talia_cl/commands/talia_console/console_commands.rb +37 -0
  19. data/lib/talia_cl/commands/talia_console/talia_commands.rb +131 -0
  20. data/lib/talia_cl/commands/talia_console.rb +47 -0
  21. data/lib/talia_cl/core_commands.rb +11 -0
  22. data/lib/talia_cl.rb +47 -0
  23. data/lib/talia_core/active_source.rb +372 -0
  24. data/lib/talia_core/active_source_parts/class_methods.rb +378 -0
  25. data/lib/talia_core/active_source_parts/predicate_handler.rb +89 -0
  26. data/lib/talia_core/active_source_parts/rdf.rb +131 -0
  27. data/lib/talia_core/active_source_parts/sql_helper.rb +36 -0
  28. data/lib/talia_core/active_source_parts/xml/base_builder.rb +47 -0
  29. data/lib/talia_core/active_source_parts/xml/generic_reader.rb +363 -0
  30. data/lib/talia_core/active_source_parts/xml/rdf_builder.rb +88 -0
  31. data/lib/talia_core/active_source_parts/xml/source_builder.rb +73 -0
  32. data/lib/talia_core/active_source_parts/xml/source_reader.rb +20 -0
  33. data/lib/talia_core/agent.rb +14 -0
  34. data/lib/talia_core/background_jobs/job.rb +82 -0
  35. data/lib/talia_core/background_jobs/progress_job.rb +68 -0
  36. data/lib/talia_core/collection.rb +13 -0
  37. data/lib/talia_core/data_types/data_loader.rb +92 -0
  38. data/lib/talia_core/data_types/data_record.rb +105 -0
  39. data/lib/talia_core/data_types/delayed_copier.rb +76 -0
  40. data/lib/talia_core/data_types/file_record.rb +59 -0
  41. data/lib/talia_core/data_types/file_store.rb +306 -0
  42. data/lib/talia_core/data_types/iip_data.rb +153 -0
  43. data/lib/talia_core/data_types/iip_loader.rb +127 -0
  44. data/lib/talia_core/data_types/image_data.rb +32 -0
  45. data/lib/talia_core/data_types/media_link.rb +19 -0
  46. data/lib/talia_core/data_types/mime_mapping.rb +45 -0
  47. data/lib/talia_core/data_types/path_helpers.rb +77 -0
  48. data/lib/talia_core/data_types/pdf_data.rb +42 -0
  49. data/lib/talia_core/data_types/simple_text.rb +36 -0
  50. data/lib/talia_core/data_types/temp_file_handling.rb +85 -0
  51. data/lib/talia_core/data_types/xml_data.rb +169 -0
  52. data/lib/talia_core/dc_resource.rb +20 -0
  53. data/lib/talia_core/dummy_handler.rb +34 -0
  54. data/lib/talia_core/dummy_source.rb +20 -0
  55. data/lib/talia_core/errors.rb +25 -0
  56. data/lib/talia_core/initializer.rb +427 -0
  57. data/lib/talia_core/ordered_source.rb +228 -0
  58. data/lib/talia_core/rails_ext/actionpack/action_controller/record_identifier.rb +13 -0
  59. data/lib/talia_core/rails_ext/actionpack/action_controller.rb +1 -0
  60. data/lib/talia_core/rails_ext/actionpack.rb +1 -0
  61. data/lib/talia_core/rails_ext.rb +1 -0
  62. data/lib/talia_core/rdf_import.rb +90 -0
  63. data/lib/talia_core/rdf_resource.rb +159 -0
  64. data/lib/talia_core/semantic_collection_item.rb +93 -0
  65. data/lib/talia_core/semantic_collection_wrapper.rb +324 -0
  66. data/lib/talia_core/semantic_property.rb +7 -0
  67. data/lib/talia_core/semantic_relation.rb +67 -0
  68. data/lib/talia_core/source.rb +323 -0
  69. data/lib/talia_core/source_transfer_object.rb +38 -0
  70. data/lib/talia_core/workflow/base.rb +15 -0
  71. data/lib/talia_core/workflow/publication_workflow.rb +62 -0
  72. data/lib/talia_core/workflow.rb +300 -0
  73. data/lib/talia_core.rb +9 -0
  74. data/lib/talia_dependencies.rb +12 -0
  75. data/lib/talia_util/bar_progressor.rb +15 -0
  76. data/lib/talia_util/configuration/config_file.rb +48 -0
  77. data/lib/talia_util/configuration/database_config.rb +40 -0
  78. data/lib/talia_util/configuration/mysql_database_setup.rb +104 -0
  79. data/lib/talia_util/data_import.rb +91 -0
  80. data/lib/talia_util/image_conversions.rb +82 -0
  81. data/lib/talia_util/import_job_helper.rb +132 -0
  82. data/lib/talia_util/io_helper.rb +54 -0
  83. data/lib/talia_util/progressable.rb +38 -0
  84. data/lib/talia_util/progressbar.rb +236 -0
  85. data/lib/talia_util/rdf_update.rb +80 -0
  86. data/lib/talia_util/some_sigla.xml +1960 -0
  87. data/lib/talia_util/test_helpers.rb +151 -0
  88. data/lib/talia_util/util.rb +226 -0
  89. data/lib/talia_util/yaml_import.rb +80 -0
  90. data/lib/talia_util.rb +13 -0
  91. data/lib/user.rb +116 -0
  92. data/lib/version.rb +15 -0
  93. data/test/core_ext/string_test.rb +11 -0
  94. data/test/custom_template_test.rb +8 -0
  95. data/test/talia_core/active_source_predicate_test.rb +54 -0
  96. data/test/talia_core/active_source_rdf_test.rb +89 -0
  97. data/test/talia_core/active_source_test.rb +631 -0
  98. data/test/talia_core/data_types/data_loader_test.rb +123 -0
  99. data/test/talia_core/data_types/data_record_test.rb +40 -0
  100. data/test/talia_core/data_types/file_record_test.rb +171 -0
  101. data/test/talia_core/data_types/iip_data_test.rb +130 -0
  102. data/test/talia_core/data_types/image_data_test.rb +88 -0
  103. data/test/talia_core/data_types/pdf_data_test.rb +68 -0
  104. data/test/talia_core/data_types/xml_data_test.rb +134 -0
  105. data/test/talia_core/generic_xml_test.rb +83 -0
  106. data/test/talia_core/initializer_test.rb +36 -0
  107. data/test/talia_core/ordered_source_test.rb +398 -0
  108. data/test/talia_core/rdf_resource_test.rb +115 -0
  109. data/test/talia_core/semantic_collection_item_test.rb +129 -0
  110. data/test/talia_core/source_reader_test.rb +33 -0
  111. data/test/talia_core/source_test.rb +484 -0
  112. data/test/talia_core/source_transfer_object_test.rb +24 -0
  113. data/test/talia_core/workflow/publication_workflow_test.rb +242 -0
  114. data/test/talia_core/workflow/user_class_for_workflow.rb +35 -0
  115. data/test/talia_core/workflow/workflow_base_test.rb +21 -0
  116. data/test/talia_core/workflow_test.rb +19 -0
  117. data/test/talia_util/import_job_helper_test.rb +46 -0
  118. data/test/test_helper.rb +68 -0
  119. metadata +262 -0
data/lib/talia_cl.rb ADDED
@@ -0,0 +1,47 @@
1
+ # This is the loader file for the Talia command line tool.
2
+ #
3
+ # The Talia command line works as follows: This file will load some helper
4
+ # methods that can be used to register subcommands for the command line tool.
5
+ #
6
+ # Each subcommand is registered with a name (as symbol), a description string
7
+ # and a block that will be run if the command is selected.
8
+ #
9
+ # If the command line processing is started, the first command line argument
10
+ # will be taken as the name of the command to be run. The argument will be
11
+ # removed from ARGV, and then the block belonging to the command will be
12
+ # executed.
13
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), "talia_cl")
14
+
15
+ # require basic stuff
16
+ require "talia_core"
17
+ require "talia_util"
18
+
19
+ include TaliaUtil
20
+
21
+ require 'command_line'
22
+ require 'core_commands'
23
+
24
+ # Quick and dirty: Try to load the (Rails) add-on commands. If not found, ignore.
25
+ begin
26
+ require 'addon_commands'
27
+ puts "Additional talia commands loaded"
28
+ rescue LoadError
29
+ puts "No additional talia commands found or loaded."
30
+ end
31
+
32
+
33
+ # Runs the command line
34
+ def run_command_line
35
+ Util::title
36
+
37
+ command = ARGV.shift
38
+
39
+ if(TaliaCommandLine::command?(command))
40
+ TaliaCommandLine::run_command(command)
41
+ else
42
+ puts "Use talia <command> - possible commands:"
43
+ TaliaCommandLine::each do |command, description|
44
+ puts "#{command}\t- #{description}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,372 @@
1
+ module TaliaCore
2
+
3
+ # This class encapsulate the basic "Source" behaviour for an element in the
4
+ # semantic store. This is the baseclass for all things that are represented
5
+ # as an "Resource" (with URL) in the semantic store.
6
+ #
7
+ # If an object is modified but <b>not</b> saved, the ActiveSource does <b>not</b>
8
+ # guarantee that the RDF will always be in sync with the database. However,
9
+ # a subsequent saving of the object will re-sync the RDF.
10
+ #
11
+ # An effort is made to treat RDF and database writes in the same way, so that
12
+ # they should <i>usually</i> be in sync:
13
+ #
14
+ # * Write operations are usually lazy, the data should only be saved once
15
+ # save! is called. However, ActiveRecord may still decide that the objects
16
+ # need to be saved if they are involved in other operations.
17
+ # * Operations that clear a predicate are <b>immediate</b>. That also means
18
+ # that using singular property setter, if
19
+ # used will immediately erase the old value. If the record is not saved,
20
+ # the property will be left empty (and not revert to the original value!)
21
+ class ActiveSource < ActiveRecord::Base
22
+
23
+ # Act like an ActiveRdfResource
24
+ include RDFS::ResourceLike
25
+
26
+ extend ActiveSourceParts::ClassMethods
27
+ extend ActiveSourceParts::SqlHelper
28
+ include ActiveSourceParts::PredicateHandler
29
+ extend ActiveSourceParts::PredicateHandler::ClassMethods
30
+ include ActiveSourceParts::Rdf
31
+ extend TaliaUtil::Progressable # Progress for import methods on class
32
+
33
+ # Set the handlers for the callbacks defined in the other modules. The
34
+ # order matters here.
35
+ after_update :auto_update_rdf
36
+ after_create :auto_create_rdf
37
+ after_save :save_wrappers # Save the cache wrappers
38
+
39
+
40
+ # Relations where this source is the subject of the triple
41
+ has_many :semantic_relations, :foreign_key => 'subject_id', :class_name => 'TaliaCore::SemanticRelation', :dependent => :destroy
42
+
43
+ # Data records attached to the active source
44
+ has_many :data_records, :class_name => 'TaliaCore::DataTypes::DataRecord', :dependent => :destroy, :foreign_key => 'source_id'
45
+
46
+ # Relations where this source is the object of the relation
47
+ has_many :related_subjects,
48
+ :foreign_key => 'object_id',
49
+ :class_name => 'TaliaCore::SemanticRelation'
50
+ has_many :subjects, :through => :related_subjects
51
+
52
+ validates_format_of :uri, :with => /\A\S*:.*\Z/
53
+ validates_uniqueness_of :uri
54
+
55
+ before_destroy :remove_inverse_properties # Remove inverse properties when destroying an element
56
+
57
+ # We may wish to use the following Regexp.
58
+ # It matches:
59
+ # http://discovery-project.eu
60
+ # http://www.discovery-project.eu
61
+ # http://trac.talia.discovery-project.eu
62
+ # http://trac.talia.discovery-project.eu/source
63
+ # http://trac.talia.discovery-project.eu/namespace#predicate
64
+ #
65
+ # validates_format_of :uri,
66
+ # :with => /^(http|https):\/\/[a-z0-9\_\-\.]*[a-z0-9_-]{1,}\.[a-z]{2,4}[\/\w\d\_\-\.\?\&\#]*$/i
67
+
68
+ validate :check_uri
69
+
70
+ # Helper
71
+ def value_for(thing)
72
+ self.class.value_for(thing)
73
+ end
74
+
75
+ # To string: Just return the URI. Use to_xml if you need something more
76
+ # involved.
77
+ def to_s
78
+ self[:uri]
79
+ end
80
+
81
+ # Works in the normal way for database attributes. If the value
82
+ # is not an attribute, it tries to find objects related to this source
83
+ # with the value as a predicate URL and returns a collection of those.
84
+ #
85
+ # The assignment operator remains as it is for the ActiveRecord.
86
+ def [](attribute)
87
+ if(db_attr?(attribute))
88
+ super(attribute)
89
+ else
90
+ get_objects_on(attribute)
91
+ end
92
+ end
93
+ alias :get_attribute :[]
94
+
95
+ # Assignment to an attribute. This will overwrite all current triples.
96
+ def []=(attribute, value)
97
+ if(db_attr?(attribute))
98
+ super(attribute, value)
99
+ else
100
+ pred = get_attribute(attribute)
101
+ pred.remove
102
+ pred << value
103
+ end
104
+ end
105
+
106
+ # Make aliases for the original updating methods
107
+ alias :update_attributes_orig :update_attributes
108
+ alias :update_attributes_orig! :update_attributes!
109
+
110
+ # Updates *all* attributes of this source. For the database attributes, this works
111
+ # exactly like ActiveRecord::Base#update_attributes
112
+ #
113
+ # If semantic attributes are present, they will be updated on the semantic store.
114
+ #
115
+ # After the update, the source will be saved.
116
+ def update_attributes(attributes)
117
+ yield self if(block_given?)
118
+ super(process_attributes(false, attributes))
119
+ end
120
+
121
+ # As update_attributes, but uses save! to save the source
122
+ def update_attributes!(attributes)
123
+ yield self if(block_given?)
124
+ super(process_attributes(false, attributes))
125
+ end
126
+
127
+ # Works like update_attributes, but will replace the semantic attributes
128
+ # rather than adding to them.
129
+ def rewrite_attributes(attributes)
130
+ yield self if(block_given?)
131
+ update_attributes_orig(process_attributes(true, attributes))
132
+ end
133
+
134
+ # Like rewrite_attributes, but calling save!
135
+ def rewrite_attributes!(attributes)
136
+ yield self if(block_given?)
137
+ update_attributes_orig!(process_attributes(true, attributes))
138
+ end
139
+
140
+ # Helper to update semantic attributes from the given hash. If there is a
141
+ # "<value>" string, it will be treated as a reference to an URI. Hash
142
+ # values may be arrays.
143
+ #
144
+ # If overwrite is set to yes, the given attributes (and only those)
145
+ # are replaced with the values from the hash. Otherwise
146
+ # the attribute values will be added to the existing ones
147
+ def add_semantic_attributes(overwrite, attributes)
148
+ attributes.each do |attr, value|
149
+ value = [ value ] unless(value.is_a?(Array))
150
+ attr_wrap = self[attr]
151
+ attr_wrap.remove if(overwrite)
152
+ value.each { |val |self[attr] << target_for(val) }
153
+ end
154
+ end
155
+
156
+ # Returns a special object which collects the "inverse" properties
157
+ # of the Source - these are all RDF properties which have the current
158
+ # Source as the object.
159
+ #
160
+ # The returned object supports the [] operator, which allows to fetch the
161
+ # "inverse" (the RDF subjects) for the given predicate.
162
+ #
163
+ # Example: <tt>person.inverse[N::FOO::creator]</tt> would return a list of
164
+ # all the elements of which the current person is the creator.
165
+ def inverse
166
+ inverseobj = Object.new
167
+ inverseobj.instance_variable_set(:@assoc_source, self)
168
+
169
+ class << inverseobj
170
+
171
+ def [](property)
172
+ @assoc_source.subjects.find(:all, :conditions => { 'semantic_relations.predicate_uri' => property.to_s } )
173
+ end
174
+
175
+ private :type
176
+ end
177
+
178
+ inverseobj
179
+ end
180
+
181
+ # Accessor that allows to lookup a namespace/name combination. This works like
182
+ # the [] method: I will return an array-like object on predicates can be
183
+ # manipulated.
184
+ def predicate(namespace, name)
185
+ get_objects_on(get_namespace(namespace, name))
186
+ end
187
+
188
+ # Setter method for predicates by namespace/name combination. This will
189
+ # *add a precdicate triple, not replace one!*
190
+ def predicate_set(namespace, name, value)
191
+ predicate(namespace, name) << value
192
+ end
193
+
194
+ # Setter method that will only add the value if it doesn't exist already
195
+ def predicate_set_uniq(namespace, name, value)
196
+ pred = predicate(namespace, name)
197
+ pred << value unless(pred.include?(value))
198
+ end
199
+
200
+ # Replaces the given predicate with the value. Good for one-value predicates
201
+ def predicate_replace(namespace, name, value)
202
+ pred = predicate(namespace, name)
203
+ pred.remove
204
+ pred << value
205
+ end
206
+
207
+ # Gets the direct predicates (using the database)
208
+ def direct_predicates
209
+ raise(ActiveRecord::RecordNotFound, "Cannot do this on unsaved record.") if(new_record?)
210
+ rels = SemanticRelation.find_by_sql("SELECT DISTINCT predicate_uri FROM semantic_relations WHERE subject_id = #{self.id}")
211
+ rels.collect { |rel| N::Predicate.new(rel.predicate_uri) }
212
+ end
213
+
214
+ # Gets the inverse predicates
215
+ def inverse_predicates
216
+ raise(ActiveRecord::RecordNotFound, "Cannot do this on unsaved record.") if(new_record?)
217
+ rels = SemanticRelation.find_by_sql("SELECT DISTINCT predicate_uri FROM semantic_relations WHERE object_id = #{self.id}")
218
+ rels.collect { |rel| N::Predicate.new(rel.predicate_uri) }
219
+ end
220
+
221
+ # Returns if the Facsimile is of the given type
222
+ def has_type?(type)
223
+ (self.types.include?(type))
224
+ end
225
+
226
+ # True if the given attribute is a database attribute
227
+ def db_attr?(attribute)
228
+ ActiveSource.db_attr?(attribute)
229
+ end
230
+
231
+ # Writes the predicate directly to the database and the rdf store. The
232
+ # Source does not need to be saved and no data is loaded from the database.
233
+ # This is faster than adding the data normally and doing a full save,
234
+ # at least if only one or two predicates are written.
235
+ def write_predicate_direct(predicate, value)
236
+ autosave = self.autosave_rdf?
237
+ value.save! if(value.is_a?(ActiveSource) && value.new_record?)
238
+ self.autosave_rdf = false
239
+ self[predicate] << value
240
+ uri_res = N::URI.new(predicate)
241
+ # Now add the RDF data by hand
242
+ if(value.kind_of?(Array))
243
+ value.each do |v|
244
+ my_rdf.direct_write_predicate(uri_res, v)
245
+ end
246
+ else
247
+ my_rdf.direct_write_predicate(uri_res, value)
248
+ end
249
+ save! # Save without RDF save
250
+ self.autosave_rdf = autosave
251
+ end
252
+
253
+
254
+ # XML Representation of the source. The object is saved if this is a new
255
+ # record.
256
+ def to_xml
257
+ save! if(new_record?)
258
+ ActiveSourceParts::Xml::SourceBuilder.build_source(self)
259
+ end
260
+
261
+ # Creates an RDF/XML resprentation of the source. The object is saved if
262
+ # this is a new record.
263
+ def to_rdf
264
+ save! if(new_record?)
265
+ ActiveSourceParts::Xml::RdfBuilder.build_source(self)
266
+ end
267
+
268
+ # Add the additional types to the source that were configured in the class.
269
+ # Usually this will not need to be called directly, but will be automatically
270
+ # called during construction.
271
+ #
272
+ # This will check the existing types to avoid duplication
273
+ def add_additional_rdf_types
274
+ return if(self.class.additional_rdf_types.size == 0) # Avoid database access if there's nothing to do
275
+ type_hash = {}
276
+ self.types.each { |type| type_hash[type.respond_to?(:uri) ? type.uri.to_s : type.to_s] = true }
277
+ self.class.additional_rdf_types.each do |type|
278
+ self.types << type unless(type_hash[type.respond_to?(:uri) ? type.uri.to_s : type.to_s])
279
+ end
280
+ end
281
+
282
+ # Attaches files from the given hash. See the new method on ActiveSource for the
283
+ # details.
284
+ #
285
+ # The call in this case should look like this:
286
+ #
287
+ # attach_files([{ url => 'url_or_filename', :options => { .. }}, ...])
288
+ #
289
+ # Have a look at the DataLoader module to see how the options work. You may also provide
290
+ # a single hash for :files (instead of an array) if you have just one file. Files will
291
+ # be saved immediately.
292
+ def attach_files(files)
293
+ files = [ files ] unless(files.is_a?(Array))
294
+ files.each do |file|
295
+ filename = file[:url] || file['url']
296
+ assit(filename)
297
+ options = file[:options] || file['options'] || {}
298
+ records = DataTypes::FileRecord.create_from_url(filename, options)
299
+ records.each { |rec| self.data_records << rec }
300
+ end
301
+ end
302
+
303
+ # This will return a list of DataRecord objects. Without parameters, this
304
+ # returns all data elements on the source. If a type is given, it will
305
+ # return only the elements of the given type. If both type and location are
306
+ # given, it will retrieve only the specified data element
307
+ def data(type = nil, location= nil)
308
+ find_type = location ? :first : :all # Find just one element if a location is given
309
+ options = {}
310
+ options[:conditions] = [ "type = ?", type ] if(type && !location)
311
+ options[:conditions] = [ "type = ? AND location = ?", type, location ] if(type && location)
312
+ data_records.find(find_type, options)
313
+ end
314
+
315
+ private
316
+
317
+ # Extracts the semantic attributes from the attribute hash and passes them
318
+ # to add_semantic_attributes with the given overwrite flag.
319
+ # The database attributes are returned by the method
320
+ def process_attributes(overwrite, attributes)
321
+ attributes = ActiveSource.split_attribute_hash(attributes)
322
+ add_semantic_attributes(overwrite, attributes[:semantic_attributes])
323
+ attributes[:db_attributes]
324
+ end
325
+
326
+ # Creates the target for the given attribute. If the value
327
+ # has the format <...>, it will be returned as an URI object, if it's a normal
328
+ # string it will be returned as a string.
329
+ def target_for(value)
330
+ return value if(value.kind_of?(N::URI) || value.kind_of?(ActiveSource))
331
+ assit_kind_of(String, value)
332
+ value.strip!
333
+ if((value[0..0] == '<') && (value[-1..-1] == '>'))
334
+ value = ActiveSource.expand_uri(value [1..-2])
335
+ val_src = ActiveSource.find(:first, :conditions => { :uri => value })
336
+ if(!val_src)
337
+ value = DummySource.new(value)
338
+ value.save!
339
+ else
340
+ value = val_src
341
+ end
342
+ end
343
+ value
344
+ end
345
+
346
+ # Get the namespace URI object for the given namespace
347
+ def get_namespace(namespace, name = '')
348
+ namesp_uri = N::Namespace[namespace]
349
+ raise(ArgumentError, "Illegal namespace given #{namespace}") unless(namesp_uri)
350
+ namesp_uri + name.to_s
351
+ end
352
+
353
+ # Takes over the validation for ActiveSources
354
+ def validate
355
+ self.class
356
+ end
357
+
358
+ # Check the uri should be different than N::LOCAL, this could happen when an user
359
+ # leaves the input text blank.
360
+ def check_uri
361
+ self.errors.add(:uri, "Cannot be blank") if self.uri == N::LOCAL.to_s
362
+ end
363
+
364
+ # Remove the inverse properties, to be called before destroy. Doing this
365
+ # through the polymorphic relation automatically could be a bit fishy...
366
+ def remove_inverse_properties
367
+ SemanticRelation.delete_all(["object_type = 'TaliaCore::ActiveSource' AND object_id = ?", self.id])
368
+ end
369
+
370
+ end
371
+
372
+ end