/
predicate_handler.rb
152 lines (131 loc) · 6.39 KB
/
predicate_handler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
# Copyright (c) 2010 Net7 SRL, <http://www.netseven.it/>
# This Software is released under the terms of the MIT License
# See LICENSE.TXT for the full text of the license.
module TaliaCore
module ActiveSourceParts
# Methods for ActiveSource objects for accessing and handling predicates, which are the
# properties connected to the source. When accessing a predicate/property of an ActiveSource,
# the system will return a SemanticCollectionWrapper.
#
# Once a predicate is loaded, the ActiveSource will cache the SemanticCollectionWrapper
# internally, and will re-use it on subsequent accesses.
#
# If the relations are prefetched (usually by providing the :prefetch_relations option to
# the find Method, see the Finders module), all wrappers for the source are loaded at once;
# the actual prefetching is done by the prefetch_relations_for method, which can also be
# used directly on a collection of sources.
module PredicateHandler
# Predicate-related class methods. See the PredicateHandler module for more
module ClassMethods
# Attempts to fetch all relations on the given sources at once, so that
# there is potentially only one.
#
# For safety reasons, there is a limit on the number of sources that is
# accepted. (For a web application, if you go over the default, you're
# probably doing it wrong).
#
# When prefetching, all the relations/properties for the given sources are
# loaded in a single request, and the data is injected in the internal cache
# of the sources.
#
# A source with prefetched relations will not cause database queries if you
# access its properties.
def prefetch_relations_for(sources, limit = 1024)
sources = [ sources ] if(sources.is_a?(ActiveSource))
raise(RangeError, "Too many sources for prefetching.") if(sources.size > limit)
src_hash = {}
sources.each { |src| src_hash[src.id] = src }
relations = SemanticRelation.find(:all, :conditions => { :subject_id => src_hash.keys }, :include => [:subject, :object])
relations.each do |rel|
src_hash[rel.subject_id].inject_predicate(rel)
end
# Set all as loaded
sources.each do |src|
src.each_cached_wrapper { |wrap| wrap.instance_variable_set(:'@loaded', true) }
src.instance_variable_set(:'@prefetched', true)
end
end
end # End class methods
# Gets the RDF types for the source. This is equivalent to accessing the
# rdf:type predicate.
def types
get_objects_on(N::RDF.type.to_s)
end
# Checks if the source has the given RDF type
def has_type?(type)
(self.types.include?(type))
end
# Returns the SemanticCollectionWrapper for the given predicate. The collection
# wrapper will be cached internally, so that subsequent calls will receive the
# same collection wrapper again.
#
# This also means that any modifications to the wrapper are preserved in the
# cache - if a wrapper is modified in memory, and accessed again _on the same
# source_, a subsequent access will return the modified wrapper.
#
# Modified wrappers are saved when the ActiveSource itself is saved (through
# save_wrappers, which is automatically called)
def get_wrapper_on(predicate)
@type_cache ||= {}
active_wrapper = @type_cache[predicate.to_s]
if(active_wrapper.nil?)
active_wrapper = SemanticCollectionWrapper.new(self, predicate)
# If this is a prefetched source we have everything, so we can
# initialize the wrapper without loading anything
active_wrapper.init_as_empty! if(@prefetched)
@type_cache[predicate.to_s] = active_wrapper
end
active_wrapper
end
# Returns the values for the given predicate. This will work like #get_wrapper_on
# _except_ if the predicate is declared as a :singular_property with ActiveSouce.property_options
# (or singular_property, or multi_property). In that case, it will
# return only a single value. However, if the predicate is declare singular and
# there already is more than one value, it will return the wrapper (and fail an assit).
# In general, a property that was defined singular and contains more than one value
# indicates a problem with the application.
def get_objects_on(predicate)
singular = property_options_for(predicate)[:singular_property].true?
wrapper = get_wrapper_on(predicate)
if(singular && wrapper.size <= 1)
wrapper.first
else
assit(!singular, 'Was expecting a single value (property is defined as singular). Got more than one.')
wrapper
end
end
# Goes through the existing SemanticCollectionWrappers in the cache, and
# saves any modifications that may exist.
#
# This is automatically called when the source is saved.
def save_wrappers
each_cached_wrapper do |wrap|
# Load unloaded if we're not rdf_autosaving. Quick hack since otherwise
# since the blanking of unloaded properties could cause problems with
# the rdf writing otherwise
wrap.send(:load!) unless(wrap.loaded? || autosave_rdf?)
wrap.save_items!
end
end
# Loops through the wrapper cache and passes each of the SemanticCollectionWrappers
# in the cache to the block given to this method
def each_cached_wrapper
return unless(@type_cache)
@type_cache.each_value { |wrap| yield(wrap) }
end
# Resets the internal cache of wrappers/properties. Any unsaved changes on
# the wrappers are lost, and get_object_on will have to reload all data when it is
# called again
def reset!
@type_cache = nil
end
# Injects a 'fat relation' into the cache/wrappter. A "fat" relation is a
# SemanticRelation which contains additional fields (e.g. the subject uri, all
# object information, etc.) - See also the SemanticCollectionWrapper documentation.
def inject_predicate(relation)
wrapper = get_wrapper_on(relation.predicate_uri)
wrapper.insert_item(relation)
end
end
end
end