Class: Roby::Relations::Space
- Defined in:
- lib/roby/relations/space.rb
Overview
A relation space is a module which handles a list of relations (Relations::Graph instances) and applies them to a set of classes. For instance, the TaskStructure relation space is defined by
TaskStructure = Space(Task)
See the files in roby/relations to see example definitions of new relations
Use Space#relation allow to define a new relation in a given space. For instance, one can either do
TaskStructure.relation :NewRelation
or
module TaskStructure
relation :NewRelation
end
This relation can then be referenced by TaskStructure::NewRelation
Instance Attribute Summary collapse
-
#applied ⇒ Object
readonly
The set of classes on which the relations have been applied.
-
#default_graph_class ⇒ Object
The default graph class to be used for new relations.
-
#relations ⇒ Object
readonly
The set of relations included in this relation space.
Class Method Summary collapse
Instance Method Summary collapse
- #add_relation(rel) ⇒ Object
-
#apply_on(klass) ⇒ Object
This relation applies on
klass
. -
#each_relation(&block) ⇒ Object
Yields the relations that are defined on this space.
-
#each_root_relation ⇒ Object
Yields the root relations that are defined on this space.
-
#initialize ⇒ Space
constructor
:nodoc:.
-
#instanciate(observer: nil) ⇒ Hash<Models<Graph>,Graph>
Instanciate this space’s relation graphs.
-
#relation(relation_name, child_name: relation_name.to_s.snakecase, const_name: relation_name, parent_name: nil, graph: default_graph_class, single_child: false, distribute: true, dag: true, weak: false, strong: false, copy_on_replace: false, noinfo: false, subsets: Set.new, **submodel_options) ⇒ Object
Defines a relation in this relation space.
-
#remove_relation(rel) ⇒ Object
Remove
rel
from the set of relations managed in this space.
Methods inherited from Module
#action_library, #droby_dump, #droby_marshallable?, #each_fullfilled_model, #private_model?, #proxy_for
Methods included from Models::TaskServiceDefinitionDSL
Constructor Details
Instance Attribute Details
#applied ⇒ Object (readonly)
The set of classes on which the relations have been applied
30 31 32 |
# File 'lib/roby/relations/space.rb', line 30 def applied @applied end |
#default_graph_class ⇒ Object
The default graph class to be used for new relations. Defaults to Relations::Graph
33 34 35 |
# File 'lib/roby/relations/space.rb', line 33 def default_graph_class @default_graph_class end |
#relations ⇒ Object (readonly)
The set of relations included in this relation space
28 29 30 |
# File 'lib/roby/relations/space.rb', line 28 def relations @relations end |
Class Method Details
.new_relation_graph_mapping ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/roby/relations/space.rb', line 42 def self.new_relation_graph_mapping Hash.new do |h, k| if k if k.kind_of?(Class) known_relations = h.each_key.find_all { |rel| rel.kind_of?(Class) } .map { |o| o.name.to_s }.join(", ") raise ArgumentError, "#{k} is not a known relation (known relations "\ "are #{known_relations})" elsif known_graph = h.fetch(k.class, nil) raise ArgumentError, "it seems that you're trying to use the relation API "\ "to access a graph that is not part of this "\ "object's current plan. Given graph was "\ "#{k.object_id}, and the current graph for "\ "#{k.class} is #{known_graph.object_id}" else raise ArgumentError, "graph object #{known_graph} is not a known "\ "relation graph" end end end end |
Instance Method Details
#add_relation(rel) ⇒ Object
379 380 381 382 |
# File 'lib/roby/relations/space.rb', line 379 def add_relation(rel) relations << rel Relations.add_relation(rel) end |
#apply_on(klass) ⇒ Object
This relation applies on klass
. It mainly means that a relation defined on this Space will define the relation-access methods and include its support module (if any) in klass
. Note that the DirectedRelationSupport module is automatically included in klass
as well.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/roby/relations/space.rb', line 93 def apply_on(klass) klass.include DirectedRelationSupport klass.relation_spaces << self each_relation do |graph| klass.include graph::Extension end applied << klass while klass if klass.respond_to?(:all_relation_spaces) klass.all_relation_spaces << self end klass = if klass.respond_to?(:supermodel) then klass.supermodel end end end |
#each_relation(&block) ⇒ Object
Yields the relations that are defined on this space
111 112 113 114 115 |
# File 'lib/roby/relations/space.rb', line 111 def each_relation(&block) return enum_for(__method__) unless block_given? relations.each(&block) end |
#each_root_relation ⇒ Object
Yields the root relations that are defined on this space. A relation is a root relation when it has no parent relation (i.e. it is the subset of no other relations).
120 121 122 123 124 125 126 |
# File 'lib/roby/relations/space.rb', line 120 def each_root_relation return enum_for(__method__) unless block_given? relations.each do |rel| yield(rel) unless rel.parent end end |
#instanciate(observer: nil) ⇒ Hash<Models<Graph>,Graph>
Instanciate this space’s relation graphs
It instanciates a graph per relation defined on self, and sets their subset/superset relationships accordingly
74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/roby/relations/space.rb', line 74 def instanciate(observer: nil) graphs = self.class.new_relation_graph_mapping relations.each do |rel| g = rel.new(observer: observer) graphs[g] = graphs[rel] = g end relations.each do |rel| # rubocop:disable Style/CombinableLoops rel.subsets.each do |subset_rel| graphs[rel].superset_of(graphs[subset_rel]) end end graphs end |
#relation(relation_name, child_name: relation_name.to_s.snakecase, const_name: relation_name, parent_name: nil, graph: default_graph_class, single_child: false, distribute: true, dag: true, weak: false, strong: false, copy_on_replace: false, noinfo: false, subsets: Set.new, **submodel_options) ⇒ Object
Defines a relation in this relation space. This defines a relation graph, and various iteration methods on the vertices. If a block is given, it defines a set of functions which should additionally be defined on the vertex objects.
The valid options are:
- child_name
-
define a
each_#{child_name}
method to iterate on the vertex children. Uses the relation name by default (a Child relation would define aeach_child
method) - parent_name
-
define a
each_#{parent_name}
method to iterate on the parent vertices. If none is given, no method is defined. - subsets
-
a list of subgraphs. See Relations::Graph#superset_of [empty set by default]
- noinfo
-
wether the relation embeds some additional information. If false, the child iterator method (
each_#{child_name}
) will yield (child, info) instead of only child [false by default] - graph
-
the relation graph class [Relations::Graph by default]
- distribute
-
if true, the relation can be seen by remote peers [true by default]
- single_child
-
if the relations accepts only one child per vertex. If this option is set, defines a
#{child_name}
method which returns the only child (or nil if there is no child at all) [false by default] - dag
-
if true, CycleFoundError will be raised if a new vertex would create a cycle in this relation [true by default]
- weak
-
marks that this relation might be broken by the plan manager if needs be. This is currently only used in the garbage collection phase to decide in which order to GC the tasks. I.e. if a cycle is found, the weak relations will be broken to resolve it.
- strong
-
marks that the tasks that are linked by this relation should not be torn apart. This is for instance used in the replacement operation, which will never “move” a relation from the original task to the replaced one.
For instance,
relation :Children
defines an instance of Relations::Graph which is a DAG, defining the following methods on its vertices:
each_children { |v, info| ... } => graph
find_children { |v, info| ... } => object or nil
add_children(v, info = nil) => graph
remove_children(v) => graph
and
relation :Children, child_name: :child
would define
each_child { |v, info| ... } => graph
find_child { |v, info| ... } => object or nil
add_child(v, info = nil) => graph
remove_child(v) => graph
-
the DirectedRelationSupport module gets included in the vertex classes at the construction of the Space instance. See #apply_on.
-
the
:noinfo
option would then remove the ‘info’ parameter to the various blocks. -
if
:single_child
is set to true, then an additional method is defined:child => object or nil
-
and finally if the following is used
relation :Children, child_name: :child, parent_name: :parent
then the following method is additionally defined
each_parent { |v| ... }
Finally, if a block is given, it gets included in the target class (i.e. for a TaskStructure relation, Roby::Task)
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/roby/relations/space.rb', line 201 def relation(relation_name, child_name: relation_name.to_s.snakecase, const_name: relation_name, parent_name: nil, graph: default_graph_class, single_child: false, distribute: true, dag: true, weak: false, strong: false, copy_on_replace: false, noinfo: false, subsets: Set.new, **) if block_given? raise ArgumentError, "calling relation with a block is not supported anymore. Reopen #{const_name}::Extension after the relation call to add helper methods" elsif strong && weak raise ArgumentError, "a relation cannot be both strong and weak" end # Check if this relation is already defined. If it is the case, reuse it. # This is needed mostly by the reloading code graph_class = define_or_reuse(const_name) do klass = graph.new_submodel( distribute: distribute, dag: dag, weak: weak, strong: strong, copy_on_replace: copy_on_replace, noinfo: noinfo, subsets: subsets, child_name: child_name, **) synthetized_methods = Module.new do define_method("__r_#{relation_name}__") { self.relation_graphs[klass] } end extension = Module.new class_extension = Module.new klass.const_set("SynthetizedMethods", synthetized_methods) klass.const_set("Extension", extension) klass.const_set("ModelExtension", class_extension) extension.const_set("ClassExtension", class_extension) klass end subsets.each do |subset_rel| graph_class.superset_of(subset_rel) end synthetized_methods = graph_class::SynthetizedMethods extension = graph_class::Extension applied.each do |klass| klass.include synthetized_methods klass.include extension end if parent_name synthetized_methods.class_eval <<-DEF, __FILE__, __LINE__ + 1 def each_#{parent_name}(&iterator) return enum_for(__method__) if !iterator self.each_parent_object(__r_#{relation_name}__, &iterator) end DEF end if noinfo synthetized_methods.class_eval <<-DEF, __FILE__, __LINE__ + 1 def each_#{child_name}(&iterator) return enum_for(__method__) if !iterator each_child_object(__r_#{relation_name}__, &iterator) end def find_#{child_name}(&block) each_child_object(__r_#{relation_name}__).find(&block) end DEF else synthetized_methods.class_eval <<-DEF, __FILE__, __LINE__ + 1 def enum_#{child_name} Roby.warn_deprecated "enum_#{child_name} is deprecated, use each_#{child_name} instead" each_#{child_name} end def each_#{child_name}(with_info = true, &block) return enum_for(__method__, with_info) unless block if with_info each_child_object(__r_#{relation_name}__) do |child| yield(child, self[child, __r_#{relation_name}__]) end else each_child_object(__r_#{relation_name}__, &block) end end def find_#{child_name}(with_info = true, &block) if with_info each_child_object(__r_#{relation_name}__) do |child| return child if yield(child, self[child, __r_#{relation_name}__]) end else each_child_object(__r_#{relation_name}__).find(&block) end nil end DEF end synthetized_methods.class_eval <<-DEF, __FILE__, __LINE__ + 1 def add_#{child_name}(to, info = nil) add_child_object(to, __r_#{relation_name}__, info) self end def remove_#{child_name}(to) remove_child_object(to, __r_#{relation_name}__) self end def adding_#{child_name}_parent(parent, info) end def added_#{child_name}_parent(parent, info) end def removing_#{child_name}_parent(parent) end def removed_#{child_name}_parent(parent) end def updating_#{child_name}_parent(parent, info) end def updated_#{child_name}_parent(parent, info) end def adding_#{child_name}(child, info) end def added_#{child_name}(child, info) end def removing_#{child_name}(child) end def removed_#{child_name}(child) end def updating_#{child_name}(child, info) end def updated_#{child_name}(child, info) end DEF if single_child synthetized_methods.class_eval do define_method child_name do if task = instance_variable_get("@#{child_name}") plan[task] end end end graph_class.class_eval do attr_reader :single_child_accessor def add_edge(parent, child, info) super parent.instance_variable_set single_child_accessor, child end def update_single_child_accessor(object, expected_object) current_object = object.instance_variable_get single_child_accessor if current_object == expected_object object.instance_variable_set single_child_accessor, each_out_neighbour(object).first end end def remove_edge(parent, child) super update_single_child_accessor(parent, child) end def remove_vertex(object) parents = in_neighbours(object) super parents.each do |parent| update_single_child_accessor(parent, object) end end end end add_relation(graph_class) graph_class end |
#remove_relation(rel) ⇒ Object
Remove rel
from the set of relations managed in this space
385 386 387 388 |
# File 'lib/roby/relations/space.rb', line 385 def remove_relation(rel) relations.delete(rel) Relations.remove_relation(rel) end |