talia_core 0.5.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +2 -2
- data/config/talia_core.yml.example +37 -35
- data/generators/talia_admin/templates/app/models/fake_source.rb +93 -0
- data/generators/talia_admin/templates/app/models/talia_collection.rb +13 -37
- data/generators/talia_base/talia_base_generator.rb +0 -1
- data/generators/talia_base/templates/app/controllers/custom_templates_controller.rb +2 -1
- data/generators/talia_base/templates/app/controllers/sources_controller.rb +1 -1
- data/generators/talia_base/templates/script/configure_talia +56 -73
- data/generators/talia_swicky/talia_swicky_generator.rb +18 -0
- data/generators/talia_swicky/templates/app/controllers/swicky_notebooks_controller.rb +111 -0
- data/generators/talia_swicky/templates/app/helpers/swicky_notebooks_helper.rb +29 -0
- data/generators/talia_swicky/templates/app/views/swicky_notebooks/index.builder +6 -0
- data/generators/talia_swicky/templates/app/views/swicky_notebooks/index.html.erb +10 -0
- data/generators/talia_swicky/templates/app/views/swicky_notebooks/show.html.erb +11 -0
- data/generators/talia_swicky/templates/test/fixtures/notebook.rdf +862 -0
- data/generators/talia_swicky/templates/test/functional/swicky_notebooks_controller_test.rb +44 -0
- data/lib/core_ext/boolean.rb +23 -0
- data/lib/core_ext/jdbc_rake_monkeypatch.rb +22 -0
- data/lib/core_ext/nil_class.rb +11 -0
- data/lib/core_ext/object.rb +34 -0
- data/lib/core_ext/string.rb +15 -0
- data/lib/custom_template.rb +3 -1
- data/lib/loader_helper.rb +16 -3
- data/lib/mysql.rb +7 -7
- data/lib/progressbar.rb +2 -2
- data/lib/swicky/exhibit_json/item.rb +129 -0
- data/lib/swicky/exhibit_json/item_collection.rb +129 -0
- data/lib/swicky/fragment.rb +0 -0
- data/lib/swicky/note.rb +7 -0
- data/lib/swicky/notebook.rb +78 -12
- data/lib/talia_core/active_source.rb +45 -13
- data/lib/talia_core/active_source_parts/class_methods.rb +154 -26
- data/lib/talia_core/active_source_parts/finders.rb +49 -26
- data/lib/talia_core/active_source_parts/predicate_handler.rb +71 -23
- data/lib/talia_core/active_source_parts/rdf/ntriples_reader.rb +13 -0
- data/lib/talia_core/active_source_parts/rdf/rdf_reader.rb +99 -0
- data/lib/talia_core/active_source_parts/rdf/rdfxml_reader.rb +12 -0
- data/lib/talia_core/active_source_parts/{rdf.rb → rdf_handler.rb} +52 -19
- data/lib/talia_core/active_source_parts/xml/generic_reader.rb +151 -260
- data/lib/talia_core/active_source_parts/xml/generic_reader_add_statements.rb +97 -0
- data/lib/talia_core/active_source_parts/xml/generic_reader_helpers.rb +88 -0
- data/lib/talia_core/active_source_parts/xml/generic_reader_import_statements.rb +239 -0
- data/lib/talia_core/active_source_parts/xml/rdf_builder.rb +14 -7
- data/lib/talia_core/active_source_parts/xml/source_builder.rb +7 -3
- data/lib/talia_core/active_source_parts/xml/source_reader.rb +17 -2
- data/lib/talia_core/collection.rb +192 -1
- data/lib/talia_core/data_types/data_loader.rb +88 -18
- data/lib/talia_core/data_types/data_record.rb +24 -2
- data/lib/talia_core/data_types/delayed_copier.rb +13 -3
- data/lib/talia_core/data_types/file_record.rb +24 -13
- data/lib/talia_core/data_types/file_store.rb +111 -94
- data/lib/talia_core/data_types/iip_data.rb +104 -23
- data/lib/talia_core/data_types/iip_loader.rb +102 -56
- data/lib/talia_core/data_types/image_data.rb +3 -1
- data/lib/talia_core/data_types/media_link.rb +4 -1
- data/lib/talia_core/data_types/mime_mapping.rb +65 -38
- data/lib/talia_core/data_types/path_helpers.rb +23 -17
- data/lib/talia_core/data_types/pdf_data.rb +9 -6
- data/lib/talia_core/data_types/simple_text.rb +5 -4
- data/lib/talia_core/data_types/xml_data.rb +53 -25
- data/lib/talia_core/dummy_handler.rb +3 -2
- data/lib/talia_core/errors.rb +13 -27
- data/lib/talia_core/initializer.rb +44 -4
- data/lib/talia_core/oai/active_source_model.rb +13 -6
- data/lib/talia_core/oai/active_source_oai_adapter.rb +13 -12
- data/lib/talia_core/rdf_import.rb +1 -1
- data/lib/talia_core/rdf_resource.rb +2 -1
- data/lib/talia_core/semantic_collection_wrapper.rb +143 -151
- data/lib/talia_core/semantic_property.rb +4 -0
- data/lib/talia_core/semantic_relation.rb +84 -33
- data/lib/talia_core/source.rb +45 -25
- data/lib/talia_core/source_fragment.rb +7 -0
- data/lib/talia_core/source_transfer_object.rb +3 -1
- data/lib/talia_core/source_types/agent.rb +16 -0
- data/lib/talia_core/source_types/dc_resource.rb +3 -3
- data/lib/talia_core/source_types/marcont_resource.rb +15 -0
- data/lib/talia_core/source_types/skos_concept.rb +17 -0
- data/lib/talia_dependencies.rb +1 -1
- data/lib/talia_util.rb +1 -1
- data/lib/talia_util/bar_progressor.rb +1 -1
- data/lib/talia_util/image_conversions.rb +8 -2
- data/lib/talia_util/import_job_helper.rb +40 -3
- data/lib/talia_util/io_helper.rb +15 -4
- data/lib/talia_util/progressable.rb +50 -1
- data/lib/talia_util/rake_tasks.rb +3 -21
- data/lib/talia_util/test_helpers.rb +6 -1
- data/lib/talia_util/util.rb +108 -27
- data/lib/talia_util/xml/base_builder.rb +28 -1
- data/lib/talia_util/xml/rdf_builder.rb +81 -5
- data/lib/tasks/talia_core_tasks.rake +2 -0
- data/test/core_ext/boolean_test.rb +26 -0
- data/test/core_ext/nil_class_test.rb +14 -0
- data/test/core_ext/object_test.rb +26 -0
- data/test/core_ext/string_test.rb +11 -0
- data/test/swicky/json_encoder_test.rb +51 -42
- data/test/swicky/notebook_test.rb +13 -6
- data/test/talia_core/active_source_finder_interface_test.rb +30 -0
- data/test/talia_core/active_source_test.rb +445 -34
- data/test/talia_core/collection_test.rb +332 -0
- data/test/talia_core/data_types/file_record_test.rb +2 -23
- data/test/talia_core/ntriples_reader_test.rb +49 -0
- data/test/talia_core/rdfxml_reader_test.rb +51 -0
- data/test/talia_core/source_test.rb +12 -0
- data/test/talia_util/import_job_helper_test.rb +19 -12
- metadata +190 -90
- data/config/database.yml +0 -19
- data/config/rdfstore.yml +0 -13
- data/config/talia_core.yml +0 -24
- data/generators/talia_base/templates/migrations/bj_migration.rb +0 -10
- data/lib/JXslt/jxslt.rb +0 -60
- data/lib/swicky/json_encoder.rb +0 -179
- data/lib/talia_core/agent.rb +0 -14
- data/lib/talia_core/background_jobs/job.rb +0 -82
- data/lib/talia_core/background_jobs/progress_job.rb +0 -68
- data/lib/talia_core/data_types/temp_file_handling.rb +0 -85
- data/lib/talia_core/ordered_source.rb +0 -228
- data/lib/talia_core/semantic_collection_item.rb +0 -94
- data/lib/talia_core/source_types/collection.rb +0 -15
- data/lib/talia_util/progressbar.rb +0 -236
- data/tasks/talia_core_tasks.rake +0 -2
- data/test/talia_core/ordered_source_test.rb +0 -394
- data/test/talia_core/semantic_collection_item_test.rb +0 -125
File without changes
|
data/lib/swicky/note.rb
ADDED
data/lib/swicky/notebook.rb
CHANGED
@@ -12,13 +12,20 @@ module Swicky
|
|
12
12
|
class Notebook
|
13
13
|
|
14
14
|
include TaliaUtil::UriHelper
|
15
|
+
include ActiveRDF::ResourceLike
|
15
16
|
extend TaliaUtil::UriHelper
|
16
17
|
|
17
18
|
attr_reader :user_url, :url
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
alias :uri :url
|
21
|
+
|
22
|
+
def initialize(user_name_or_uri, notebook_name = nil)
|
23
|
+
if(notebook_name)
|
24
|
+
@user_url = self.class.user_url(user_name_or_uri)
|
25
|
+
@url = self.class.notebook_url(user_name_or_uri, notebook_name)
|
26
|
+
else
|
27
|
+
@url = sanitize_sparql(user_name_or_uri).to_uri
|
28
|
+
end
|
22
29
|
end
|
23
30
|
|
24
31
|
def data
|
@@ -62,36 +69,79 @@ module Swicky
|
|
62
69
|
ActiveRDF::Query.new(N::URI).select(:user).where(:user, N::TALIA.hasSwickyNotebook, url).execute.size > 0
|
63
70
|
end
|
64
71
|
|
72
|
+
def to_uri
|
73
|
+
N::URI.new(uri)
|
74
|
+
end
|
75
|
+
|
76
|
+
def ==(value)
|
77
|
+
(value.class == self.class) && (value.uri == self.uri)
|
78
|
+
end
|
79
|
+
|
65
80
|
class << self
|
81
|
+
|
82
|
+
# Find all notebooks for the given user
|
66
83
|
def find_all(user_name = nil)
|
67
|
-
nb_query = ActiveRDF::Query.new(
|
84
|
+
nb_query = ActiveRDF::Query.new(Notebook).select(:notebook).distinct
|
68
85
|
nb_query.where(:notebook, N::RDF.type, N::TALIA.SwickyNotebook)
|
69
86
|
nb_query.where(user_url(user_name), N::TALIA.hasSwickyNotebook, :notebook) if(user_name)
|
70
87
|
nb_query.execute
|
71
88
|
end
|
72
89
|
|
90
|
+
# Construct the "user" url for the given user name
|
73
91
|
def user_url(user_name)
|
74
92
|
sanitize_sparql(N::LOCAL + "users/#{user_name}").to_uri
|
75
93
|
end
|
76
94
|
|
95
|
+
# Construct the URL for a notebook from the user and notebook name
|
77
96
|
def notebook_url(user_name, notebook_name)
|
78
97
|
sanitize_sparql(user_url(user_name) + '/swicky_notebooks/' + notebook_name).to_uri
|
79
98
|
end
|
80
99
|
|
100
|
+
# Get the "coordinates" (an xpointer in the case of HTML fragments) for all the
|
101
|
+
# fragments that are part of the element with the given url.
|
81
102
|
def coordinates_for(url)
|
103
|
+
result = []
|
82
104
|
url = sanitize_sparql(url).to_uri
|
83
|
-
frag_qry = ActiveRDF::Query.new(N::URI).select(:coordinates).distinct
|
105
|
+
frag_qry = ActiveRDF::Query.new(N::URI).select(:fragment, :coordinates).distinct
|
84
106
|
frag_qry.where(:fragment, N::DISCOVERY.isPartOf, url)
|
85
107
|
frag_qry.where(:fragment, N::SWICKY.hasCoordinates, :coordinates)
|
86
108
|
frag_qry.where(:note, N::SWICKY.refersTo, :fragment)
|
87
|
-
frag_qry.execute.
|
109
|
+
frag_qry.execute.each do |fragment, coordinates|
|
110
|
+
result << {'fragment' => fragment.to_s, 'coordinates' => coordinates.to_s}
|
111
|
+
end
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
115
|
+
def annotation_list_for_url(url)
|
116
|
+
qry = ActiveRDF::Query.new(N::URI).distinct.select(:note).where(:fragment, N::DISCOVERY.isPartOf, url.to_uri).where(:note, N::SWICKY.refersTo, :fragment).execute
|
88
117
|
end
|
89
118
|
|
119
|
+
# Select all the triples for all the annotations (notes) that refer to the given
|
120
|
+
# URL
|
90
121
|
def annotations_for_url(url)
|
91
122
|
url = sanitize_sparql(url).to_uri
|
92
123
|
select_annotations([:note, N::SWICKY.refersTo, url])
|
93
124
|
end
|
94
|
-
|
125
|
+
|
126
|
+
def annotations_for_image(url)
|
127
|
+
url = sanitize_sparql(url).to_uri
|
128
|
+
select_annotations([:note, N::SWICKY.refersTo, :fragment], [:fragment, N::DISCOVERY.isPartOf, url])
|
129
|
+
# result = []
|
130
|
+
# url = sanitize_sparql(url).to_uri
|
131
|
+
# q = ActiveRDF::Query.new(N::URI).select(:fragment).distinct.where(:fragment, N::DISCOVERY.isPartOf, url)
|
132
|
+
# q.execute.each do |fragment|
|
133
|
+
# result = {fragment.uri.to_s => {}}
|
134
|
+
# q2 = ActiveRDF::Query.new(N::URI).select(:predicate, :object).distinct
|
135
|
+
# q2.where fragment, :predicate, :object
|
136
|
+
# q2.execute.each do |predicate, object|
|
137
|
+
# result[fragment.uri.to_s][predicate.to_s] = object.to_s
|
138
|
+
# end
|
139
|
+
# end
|
140
|
+
# result
|
141
|
+
end
|
142
|
+
|
143
|
+
# Select all the annotations on the note that uses the fragment identified by the given XPOINTER
|
144
|
+
# string
|
95
145
|
def annotations_for_xpointer(xpointer)
|
96
146
|
xpointer = sanitize_sparql(xpointer).to_uri
|
97
147
|
select_annotations([:note, N::SWICKY.refersTo, :fragment], [:fragment, N::SWICKY.hasCoordinates, xpointer])
|
@@ -99,16 +149,33 @@ module Swicky
|
|
99
149
|
|
100
150
|
private
|
101
151
|
|
152
|
+
# Select annotation triples. This expects an array of "where" conditions (that is, arrays with a
|
153
|
+
# subject-predicate-object pattern). One of the conditions must match a :note variable.
|
154
|
+
#
|
155
|
+
# This will return all triples where:
|
156
|
+
#
|
157
|
+
# * :note is the subject of the triple
|
158
|
+
# * That have :statement as their subject, and :statement is a statement on one of the notes above
|
159
|
+
# * That have any of the predicates or objects of the results above as their subject
|
102
160
|
def select_annotations(*note_matching)
|
103
161
|
# Select all triples on the notes
|
104
162
|
note_triples_qry = ActiveRDF::Query.new(N::URI).select(:note, :predicate, :object).distinct
|
105
163
|
note_matching.each { |conditions| note_triples_qry.where(*conditions) }
|
106
|
-
|
164
|
+
result_triples = note_triples_qry.where(:note, :predicate, :object).execute
|
165
|
+
# Select all on the fragments of the note
|
166
|
+
fragment_triples_qry = ActiveRDF::Query.new(N::URI).select(:fragment, :predicate, :object).distinct
|
167
|
+
note_matching.each { |conditions| fragment_triples_qry.where(*conditions) }
|
168
|
+
fragment_triples_qry.where(:note, N::SWICKY.refersTo, :fragment)
|
169
|
+
fragment_triples_qry.where(:fragment, :predicate, :object)
|
170
|
+
result_triples += fragment_triples_qry.execute
|
107
171
|
# Select all statements on the triples
|
108
172
|
statement_triples_qry = ActiveRDF::Query.new(N::URI).select(:statement, :predicate, :object).distinct
|
109
173
|
note_matching.each { |conditions| statement_triples_qry.where(*conditions) }
|
110
|
-
statement_triples_qry.where(:note, N::SWICKY.
|
111
|
-
|
174
|
+
statement_triples_qry.where(:note, N::SWICKY.refersTo, :fragment)
|
175
|
+
statement_triples_qry.where(:fragment, N::SWICKY.hasStatement, :statement)
|
176
|
+
statement_triples_qry.where(:statement, :predicate, :object)
|
177
|
+
|
178
|
+
result_triples += statement_triples_qry.execute
|
112
179
|
# TODO: Fix this to better query once available in ActiveRDF
|
113
180
|
additional_triples = []
|
114
181
|
result_triples.each do |trip|
|
@@ -117,7 +184,6 @@ module Swicky
|
|
117
184
|
additional_triples += ActiveRDF::Query.new(N::URI).select(:predicate, :object).distinct.where(trip.last, :predicate, :object).execute.collect { |result| [trip.last] + result }
|
118
185
|
end
|
119
186
|
end
|
120
|
-
|
121
187
|
# Return all results
|
122
188
|
result_triples + additional_triples
|
123
189
|
end
|
@@ -125,4 +191,4 @@ module Swicky
|
|
125
191
|
end
|
126
192
|
|
127
193
|
end
|
128
|
-
end
|
194
|
+
end
|
@@ -28,7 +28,7 @@ module TaliaCore
|
|
28
28
|
extend ActiveSourceParts::SqlHelper
|
29
29
|
include ActiveSourceParts::PredicateHandler
|
30
30
|
extend ActiveSourceParts::PredicateHandler::ClassMethods
|
31
|
-
include ActiveSourceParts::
|
31
|
+
include ActiveSourceParts::RdfHandler
|
32
32
|
extend TaliaUtil::Progressable # Progress for import methods on class
|
33
33
|
|
34
34
|
# Set the handlers for the callbacks defined in the other modules. The
|
@@ -36,6 +36,8 @@ module TaliaCore
|
|
36
36
|
after_update :auto_update_rdf
|
37
37
|
after_create :auto_create_rdf
|
38
38
|
after_save :save_wrappers # Save the cache wrappers
|
39
|
+
before_destroy :destroy_dependent_props
|
40
|
+
after_destroy :clear_rdf
|
39
41
|
|
40
42
|
|
41
43
|
# Relations where this source is the subject of the triple
|
@@ -97,6 +99,8 @@ module TaliaCore
|
|
97
99
|
def [](attribute)
|
98
100
|
if(db_attr?(attribute))
|
99
101
|
super(attribute)
|
102
|
+
elsif(defined_property?(attribute))
|
103
|
+
self.send(attribute)
|
100
104
|
else
|
101
105
|
get_objects_on(attribute)
|
102
106
|
end
|
@@ -107,10 +111,10 @@ module TaliaCore
|
|
107
111
|
def []=(attribute, value)
|
108
112
|
if(db_attr?(attribute))
|
109
113
|
super(attribute, value)
|
114
|
+
elsif(defined_property?(attribute))
|
115
|
+
self.send("#{attribute}=", value)
|
110
116
|
else
|
111
|
-
|
112
|
-
pred.remove
|
113
|
-
pred << value
|
117
|
+
get_wrapper_on(attribute).replace(value)
|
114
118
|
end
|
115
119
|
end
|
116
120
|
|
@@ -176,7 +180,7 @@ module TaliaCore
|
|
176
180
|
super(process_attributes(false, attributes))
|
177
181
|
end
|
178
182
|
|
179
|
-
# As update_attributes, but uses save! to save the source
|
183
|
+
# As update_attributes, but uses save! to save the source
|
180
184
|
def update_attributes!(attributes)
|
181
185
|
yield self if(block_given?)
|
182
186
|
super(process_attributes(false, attributes))
|
@@ -204,10 +208,13 @@ module TaliaCore
|
|
204
208
|
# the attribute values will be added to the existing ones
|
205
209
|
def add_semantic_attributes(overwrite, attributes)
|
206
210
|
attributes.each do |attr, value|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
+
if(defined_property?(attr))
|
212
|
+
self[attr] = value
|
213
|
+
else
|
214
|
+
attr_wrap = self[attr]
|
215
|
+
attr_wrap.remove if(overwrite)
|
216
|
+
value.to_a.each { |val| self[attr] << target_for(val) }
|
217
|
+
end
|
211
218
|
end
|
212
219
|
end
|
213
220
|
|
@@ -280,6 +287,14 @@ module TaliaCore
|
|
280
287
|
def db_attr?(attribute)
|
281
288
|
ActiveSource.db_attr?(attribute)
|
282
289
|
end
|
290
|
+
|
291
|
+
def defined_property?(prop_name)
|
292
|
+
self.class.defined_property?(prop_name)
|
293
|
+
end
|
294
|
+
|
295
|
+
def property_options_for(property)
|
296
|
+
self.class.property_options_for(property)
|
297
|
+
end
|
283
298
|
|
284
299
|
# Writes the predicate directly to the database and the rdf store. The
|
285
300
|
# Source does not need to be saved and no data is loaded from the database.
|
@@ -348,9 +363,10 @@ module TaliaCore
|
|
348
363
|
def attach_files(files)
|
349
364
|
files = [ files ] unless(files.is_a?(Array))
|
350
365
|
files.each do |file|
|
351
|
-
|
366
|
+
file.to_options!
|
367
|
+
filename = file[:url]
|
352
368
|
assit(filename)
|
353
|
-
options = file[:options] ||
|
369
|
+
options = file[:options] || {}
|
354
370
|
records = DataTypes::FileRecord.create_from_url(filename, options)
|
355
371
|
records.each { |rec| self.data_records << rec }
|
356
372
|
end
|
@@ -373,14 +389,30 @@ module TaliaCore
|
|
373
389
|
def rdf_selftype
|
374
390
|
(N::TALIA + self.class.name.demodulize)
|
375
391
|
end
|
376
|
-
|
392
|
+
|
393
|
+
def reload
|
394
|
+
reset! # Clear the property cache
|
395
|
+
super
|
396
|
+
end
|
397
|
+
|
377
398
|
private
|
378
399
|
|
400
|
+
# Removes dependent properties
|
401
|
+
def destroy_dependent_props
|
402
|
+
self.class.props_to_destroy.each do |prop|
|
403
|
+
values = self[prop]
|
404
|
+
values = [values] unless(values.is_a?(SemanticCollectionWrapper))
|
405
|
+
values.each do |val|
|
406
|
+
val.destroy if(val.is_a?(TaliaCore::ActiveSource))
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
379
411
|
# Extracts the semantic attributes from the attribute hash and passes them
|
380
412
|
# to add_semantic_attributes with the given overwrite flag.
|
381
413
|
# The database attributes are returned by the method
|
382
414
|
def process_attributes(overwrite, attributes)
|
383
|
-
attributes =
|
415
|
+
attributes = self.class.split_attribute_hash(attributes)
|
384
416
|
add_semantic_attributes(overwrite, attributes[:semantic_attributes])
|
385
417
|
attributes[:db_attributes]
|
386
418
|
end
|
@@ -1,8 +1,16 @@
|
|
1
1
|
module TaliaCore
|
2
2
|
module ActiveSourceParts
|
3
|
+
|
4
|
+
# Class methods for ActiveSource:
|
5
|
+
#
|
6
|
+
# * Property definitions for source classes (singular_property, multi_property, manual_property)
|
7
|
+
# * Logic for the creation of new sources, and things like exists?
|
8
|
+
# * "Import" methods for the class: create_from_xml, create_multi_from
|
9
|
+
# * autofill_uri logic
|
10
|
+
# * Various utility method
|
3
11
|
module ClassMethods
|
4
12
|
|
5
|
-
# Accessor for
|
13
|
+
# Accessor for additional rdf types that will automatically be added to each
|
6
14
|
# object of that Source class
|
7
15
|
def additional_rdf_types
|
8
16
|
@additional_rdf_types ||= []
|
@@ -24,7 +32,12 @@ module TaliaCore
|
|
24
32
|
|
25
33
|
# We have an option hash to init the source
|
26
34
|
files = options.delete(:files)
|
27
|
-
options[:uri] = uri_string_for(options[:uri])
|
35
|
+
options[:uri] = uri_string_for(options[:uri], false)
|
36
|
+
if(autofill_overwrites?)
|
37
|
+
options[:uri] = auto_uri
|
38
|
+
elsif(autofill_uri?)
|
39
|
+
options[:uri] ||= auto_uri
|
40
|
+
end
|
28
41
|
attributes = split_attribute_hash(options)
|
29
42
|
the_source = super(attributes[:db_attributes])
|
30
43
|
the_source.add_semantic_attributes(false, attributes[:semantic_attributes])
|
@@ -33,6 +46,10 @@ module TaliaCore
|
|
33
46
|
elsif(args.size == 1 && ( uri_s = uri_string_for(args[0]))) # One string argument should be the uri
|
34
47
|
# Either the current object from the db, or a new one if it doesn't exist in the db
|
35
48
|
find(:first, :conditions => { :uri => uri_s } ) || super(:uri => uri_s)
|
49
|
+
elsif(args.size == 0 && autofill_uri?)
|
50
|
+
auto = auto_uri
|
51
|
+
raise(ArgumentError, "Record already exists #{auto}") if(ActiveSource.exists?(auto))
|
52
|
+
super(:uri => auto)
|
36
53
|
else
|
37
54
|
# In this case, it's a generic "new" call
|
38
55
|
super
|
@@ -41,6 +58,7 @@ module TaliaCore
|
|
41
58
|
the_source
|
42
59
|
end
|
43
60
|
|
61
|
+
|
44
62
|
# Retrieves a new source with the given type. This gets a propety hash
|
45
63
|
# like #new, but it will correctly initialize a source of the type given
|
46
64
|
# in the hash. If no type is given, this will create a plain ActiveSource.
|
@@ -97,7 +115,7 @@ module TaliaCore
|
|
97
115
|
props.to_options!
|
98
116
|
src = nil
|
99
117
|
begin
|
100
|
-
props[:uri] = uri_string_for(props[:uri])
|
118
|
+
props[:uri] = uri_string_for(props[:uri], false)
|
101
119
|
assit(props[:uri], "Must have a valid uri at this step")
|
102
120
|
if(src = ActiveSource.find(:first, :conditions => { :uri => props[:uri] }))
|
103
121
|
src.update_source(props, options[:duplicates])
|
@@ -107,7 +125,7 @@ module TaliaCore
|
|
107
125
|
src.save!
|
108
126
|
rescue Exception => e
|
109
127
|
if(options[:errors])
|
110
|
-
err = ImportError.new("ERROR during import of #{props[:uri]}: #{e.message}")
|
128
|
+
err = Errors::ImportError.new("ERROR during import of #{props[:uri]}: #{e.message}")
|
111
129
|
err.set_backtrace(e.backtrace)
|
112
130
|
options[:errors] << err
|
113
131
|
TaliaCore.logger.warn("Problems importing #{props[:uri]} (logged): #{e.message}")
|
@@ -142,7 +160,7 @@ module TaliaCore
|
|
142
160
|
end
|
143
161
|
|
144
162
|
# Like update, only that it will overwrite the given attributes instead
|
145
|
-
# of adding to
|
163
|
+
# of adding to themƒ
|
146
164
|
def rewrite(id, attributes)
|
147
165
|
record = find(id)
|
148
166
|
raise(ActiveRecord::RecordNotFound) unless(record)
|
@@ -200,6 +218,8 @@ module TaliaCore
|
|
200
218
|
attributes.each do |field, value|
|
201
219
|
if(db_attr?(field))
|
202
220
|
db_attributes[field] = value
|
221
|
+
elsif(defined_property?(field))
|
222
|
+
semantic_attributes[field] = value
|
203
223
|
else
|
204
224
|
semantic_attributes[expand_uri(field)] = value
|
205
225
|
end
|
@@ -207,8 +227,37 @@ module TaliaCore
|
|
207
227
|
{ :semantic_attributes => semantic_attributes, :db_attributes => db_attributes }
|
208
228
|
end
|
209
229
|
|
230
|
+
def property_options_for(property)
|
231
|
+
property = defined_props[property.to_s] if(defined_props[property.to_s])
|
232
|
+
this_options = my_property_options[property.to_s]
|
233
|
+
parent_options = superclass.try_call.property_options_for(property)
|
234
|
+
if(this_options && parent_options)
|
235
|
+
parent_options.merge(this_options)
|
236
|
+
else
|
237
|
+
this_options || parent_options || {}
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def defined_property?(prop_name)
|
242
|
+
defined_props.include?(prop_name.to_s) || superclass.try_call.defined_property?(prop_name.to_s)
|
243
|
+
end
|
244
|
+
|
245
|
+
# All the options that should be destroy for :dependent => :destroy settings
|
246
|
+
def props_to_destroy
|
247
|
+
to_destroy = (superclass.try_call.props_to_destroy || [])
|
248
|
+
my_property_options.each do |prop, options|
|
249
|
+
to_destroy << prop if(options[:dependent] == :destroy)
|
250
|
+
end
|
251
|
+
to_destroy
|
252
|
+
end
|
253
|
+
|
210
254
|
private
|
211
255
|
|
256
|
+
# Make URL for autofilling
|
257
|
+
def auto_uri
|
258
|
+
(N::LOCAL + self.name.tableize + "/#{rand Time.now.to_i}").to_s
|
259
|
+
end
|
260
|
+
|
212
261
|
# The attributes stored in the database
|
213
262
|
def db_attributes
|
214
263
|
@db_attributes ||= (ActiveSource.new.attribute_names << 'id')
|
@@ -221,33 +270,73 @@ module TaliaCore
|
|
221
270
|
types.each { |t| @additional_rdf_types << t.to_s }
|
222
271
|
end
|
223
272
|
|
273
|
+
# Class helper to declare that this Source model is allowed to automatically
|
274
|
+
# create uri values for new elements. In that case, the model will
|
275
|
+
# automatically assign a URL to all new records to which no url value has
|
276
|
+
# been passed.
|
277
|
+
#
|
278
|
+
# If the :force option is set, the autofill will overwrite an existing uri that
|
279
|
+
# is passed in during creation.
|
280
|
+
def autofill_uri(options = {})
|
281
|
+
options.to_options!
|
282
|
+
options.assert_valid_keys(:force)
|
283
|
+
@can_autofill = true
|
284
|
+
@autofill_overwrites = options[:force]
|
285
|
+
end
|
286
|
+
|
287
|
+
def autofill_uri?
|
288
|
+
@can_autofill
|
289
|
+
end
|
290
|
+
|
291
|
+
def autofill_overwrites?
|
292
|
+
@autofill_overwrites
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
def singular_property(prop_name, property, options = {})
|
297
|
+
define_property(prop_name, property, options.merge(:singular_property => true))
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
# Defines a multi-value property in the same way as #singular_property
|
302
|
+
def multi_property(prop_name, property, options = {})
|
303
|
+
define_property(prop_name, property, options.merge(:singular_property => false))
|
304
|
+
end
|
305
|
+
|
306
|
+
# Defines a "manual" property. This means that getters and setters are provided
|
307
|
+
# by the user and this statement only declares that the system may autoassign to
|
308
|
+
# that property
|
309
|
+
def manual_property(prop_name)
|
310
|
+
defined_props[prop_name.to_s] = :manual
|
311
|
+
end
|
312
|
+
|
224
313
|
# Helper to define a "singular accessor" for something (e.g. siglum, catalog)
|
225
314
|
# This accessor will provide an "accessor" method that returns the
|
226
315
|
# single property value directly and an assignment method that replaces
|
227
316
|
# the property with the value.
|
228
317
|
#
|
318
|
+
# A find_by_<property> finder method is also created.
|
319
|
+
#
|
229
320
|
# The Source will cache newly set singular properties internally, so that
|
230
321
|
# the new value is immediately reflected on the object. However, the
|
231
322
|
# change will only be made permanent on #save! - and saving will also clear
|
232
323
|
# the cache
|
233
|
-
|
324
|
+
#
|
325
|
+
# [*:dependent*] You may pass :dependend => :destroy as for ActiveRecord relations
|
326
|
+
def define_property(prop_name, property, options = {})
|
234
327
|
prop_name = prop_name.to_s
|
235
|
-
|
236
|
-
|
328
|
+
property_options(property, options) # Save options for the current property
|
329
|
+
|
330
|
+
return if(defined_props.include?(prop_name))
|
237
331
|
raise(ArgumentError, "Cannot overwrite method #{prop_name}") if(self.instance_methods.include?(prop_name) || self.instance_methods.include?("#{prop_name}="))
|
332
|
+
|
238
333
|
# define the accessor
|
239
334
|
define_method(prop_name) do
|
240
|
-
|
241
|
-
assit_block { |err| (prop.size > 1) ? err << "Must have at most 1 value for singular property #{prop_name} on #{self.uri}. Values #{self[property]}" : true }
|
242
|
-
prop.size > 0 ? prop[0] : nil
|
335
|
+
self[property]
|
243
336
|
end
|
244
337
|
|
245
338
|
# define the writer
|
246
|
-
|
247
|
-
prop = self[property]
|
248
|
-
prop.remove
|
249
|
-
prop << value
|
250
|
-
end
|
339
|
+
define_writer(prop_name, property)
|
251
340
|
|
252
341
|
# define the finder
|
253
342
|
(class << self ; self; end).module_eval do
|
@@ -258,31 +347,70 @@ module TaliaCore
|
|
258
347
|
find(:all, finder)
|
259
348
|
end
|
260
349
|
end
|
350
|
+
defined_props[prop_name] = property
|
351
|
+
end
|
261
352
|
|
262
|
-
|
263
|
-
|
353
|
+
# Helper to dynamically define the singular or multi-value assignment accessor
|
354
|
+
def define_writer(prop_name, property)
|
355
|
+
define_method("#{prop_name}=") do |values|
|
356
|
+
self[property] = values
|
357
|
+
end
|
264
358
|
end
|
265
359
|
|
266
|
-
#
|
267
|
-
#
|
268
|
-
def
|
269
|
-
|
270
|
-
|
360
|
+
# The hash containing the mapping between defined property names and the
|
361
|
+
# RDF properties on which they are defined.
|
362
|
+
def defined_props
|
363
|
+
@defined_props ||= {}
|
364
|
+
end
|
365
|
+
|
366
|
+
# Hash that contains all options that are defined for the properties
|
367
|
+
def my_property_options
|
368
|
+
@my_property_options ||= {}
|
369
|
+
end
|
370
|
+
|
371
|
+
# Sets the options for handling semantic relations/properties with the predicate
|
372
|
+
# #property. The options are:
|
373
|
+
#
|
374
|
+
# [*force_relation*] Forces the the values to be relations. This means that
|
375
|
+
# each and every value passed to the generated accessors
|
376
|
+
# will be interpreted as a URL. *DEPRECATED*, use
|
377
|
+
# `:type => TaliaCore::ActiveSource` instead
|
378
|
+
# [*type*] Declare that the values of this property should be of the given
|
379
|
+
# type, which should be a Ruby runtime class or a symbol corresponding
|
380
|
+
# to an ActiveRecord field type. If this is an ActiveSource subclass,
|
381
|
+
# this will force all values that are passed to this property
|
382
|
+
# to be interpreted as the URI of an ActiveSource (if the value is not
|
383
|
+
# an ActiveSource already)
|
384
|
+
# [*singular_property*]
|
385
|
+
# be used to force each value passed to the accessors, #new and
|
386
|
+
# #update* will be interpreted as the uri of a source of the given type,
|
387
|
+
#
|
388
|
+
def property_options(property, options)
|
389
|
+
options.to_options!
|
390
|
+
options.assert_valid_keys(:force_relation, :dependent, :type, :singular_property)
|
391
|
+
if(force = options.delete(:force_relation).true?)
|
392
|
+
warn("Deprecation Warning: :force_relation is deprecated - use ':type => TaliaCore::ActiveSource' instead")
|
393
|
+
options[:type] ||= ActiveSource
|
271
394
|
end
|
395
|
+
my_property_options[property.to_s] ||= {}
|
396
|
+
my_property_options[property.to_s].merge!(options)
|
272
397
|
end
|
273
398
|
|
274
399
|
# This gets the URI string from the given value. This will just return
|
275
400
|
# the value if it's a string. It will return the result of value.uri, if
|
276
401
|
# that method exists; otherwise it'll return nil
|
277
|
-
|
402
|
+
#
|
403
|
+
# If the id_aware flag is set this will return nil for any uri string that
|
404
|
+
# appears to be a numeric id.
|
405
|
+
def uri_string_for(value, id_aware = true)
|
278
406
|
result = if value.is_a? String
|
279
|
-
return nil if(value =~ /\A\d+(-.*)?\Z/) # This looks like a record id or record param, encoded as a string
|
407
|
+
return nil if((value =~ /\A\d+(-.*)?\Z/) && id_aware) # This looks like a record id or record param, encoded as a string
|
280
408
|
# if this is a local name, prepend the local namespace
|
281
409
|
(value =~ /:/) ? value : (N::LOCAL + value).uri
|
282
410
|
elsif(value.respond_to?(:uri))
|
283
411
|
value.uri
|
284
412
|
else
|
285
|
-
nil
|
413
|
+
id_aware ? nil : value
|
286
414
|
end
|
287
415
|
result = result.to_s if result
|
288
416
|
result
|