Class: ActiveTriples::Relation
- Inherits:
-
Object
- Object
- ActiveTriples::Relation
- Includes:
- Comparable, Enumerable
- Defined in:
- lib/active_triples/relation.rb
Overview
A ‘Relation` represents the values of a specific property/predicate on an `RDFSource`. Each relation is a set (`Enumerable` of the `RDF::Term`s that are objects in the of source’s triples of the form:
<{#parent}> <{#predicate}> [term] .
Relations express a binary relationships (over a predicate) between the parent node and a set of terms.
When the term is a URI or Blank Node, it is represented in the results as an ‘RDFSource`. Literal values are cast to strings, Ruby native types, or remain as an `RDF::Literal` as documented in `#each`.
Defined Under Namespace
Classes: ValueError
Constant Summary collapse
- TYPE_PROPERTY =
{ predicate: RDF.type, cast: false }.freeze
Instance Attribute Summary collapse
-
#parent ⇒ RDFSource
The resource that is the domain of this relation.
- #reflections ⇒ Class readonly
- #rel_args ⇒ Hash readonly
- #value_arguments ⇒ Array<Object>
Instance Method Summary collapse
- #&(array) ⇒ Array
- #+(array) ⇒ Array
-
#<<(values) ⇒ Relation
(also: #push)
Adds values to the result set.
-
#<=>(other) ⇒ Object
Mimics ‘Set#<=>`, returning `0` when set membership is equivalent, and `nil` (as non-comparable) otherwise.
-
#build(attributes = {}) ⇒ Object
Builds a node with the given attributes, adding it to the relation.
-
#clear ⇒ Relation
Empties the ‘Relation`, deleting any associated triples from `parent`.
-
#delete(value) ⇒ ActiveTriples::Relation
Self.
-
#delete?(value) ⇒ Object?
A variation on ‘#delete`.
-
#each ⇒ Enumerator<Object>
Gives a result set for the ‘Relation`.
-
#empty? ⇒ Boolean
True if the results are empty.
-
#first_or_create(attributes = {}) ⇒ Object
deprecated
Deprecated.
for removal in 1.0.0. Use ‘first || build({})`, `build({}) if empty?` or similar logic.
-
#initialize(parent_source, value_arguments) ⇒ Relation
constructor
A new instance of Relation.
- #length ⇒ Integer
-
#predicate ⇒ RDF::Term?
Gives the predicate used by the Relation.
-
#property ⇒ Symbol, RDF::URI
Returns the property for the Relation.
-
#set(values) ⇒ Relation
Set the values of the Relation.
-
#subtract(*values) ⇒ Relation
Self.
-
#swap(swap_out, swap_in) ⇒ Relation
Replaces the first argument with the second as a value within the relation.
- #|(array) ⇒ Array
Constructor Details
#initialize(parent_source, value_arguments) ⇒ Relation
Returns a new instance of Relation.
43 44 45 46 47 48 49 50 51 |
# File 'lib/active_triples/relation.rb', line 43 def initialize(parent_source, value_arguments) self.parent = parent_source @reflections = parent_source.reflections @rel_args ||= {} @rel_args = value_arguments.pop if value_arguments.is_a?(Array) && value_arguments.last.is_a?(Hash) @value_arguments = value_arguments end |
Instance Attribute Details
#parent ⇒ RDFSource
Returns the resource that is the domain of this relation.
34 35 36 |
# File 'lib/active_triples/relation.rb', line 34 def parent @parent end |
#reflections ⇒ Class (readonly)
34 |
# File 'lib/active_triples/relation.rb', line 34 attr_accessor :parent, :value_arguments |
#rel_args ⇒ Hash (readonly)
34 |
# File 'lib/active_triples/relation.rb', line 34 attr_accessor :parent, :value_arguments |
#value_arguments ⇒ Array<Object>
34 |
# File 'lib/active_triples/relation.rb', line 34 attr_accessor :parent, :value_arguments |
Instance Method Details
#&(array) ⇒ Array
simply passes to ‘Array#&` unless argument is a `Relation`
60 61 62 63 64 65 |
# File 'lib/active_triples/relation.rb', line 60 def &(array) return to_a & array unless array.is_a? Relation (objects.to_a & array.objects.to_a) .map { |object| convert_object(object) } end |
#+(array) ⇒ Array
simply passes to ‘Array#+` unless argument is a `Relation`
88 89 90 91 92 93 |
# File 'lib/active_triples/relation.rb', line 88 def +(array) return to_a + array unless array.is_a? Relation (objects.to_a + array.objects.to_a) .map { |object| convert_object(object) } end |
#<<(values) ⇒ Relation Also known as: push
Adds values to the result set
142 143 144 145 |
# File 'lib/active_triples/relation.rb', line 142 def <<(values) values = prepare_relation(values) if values.is_a?(Relation) self.set(objects.to_a | Array.wrap(values)) end |
#<=>(other) ⇒ Object
Mimics ‘Set#<=>`, returning `0` when set membership is equivalent, and `nil` (as non-comparable) otherwise. Unlike `Set#<=>`, uses `#==` for member comparisons.
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 |
# File 'lib/active_triples/relation.rb', line 103 def <=>(other) return nil unless other.respond_to?(:each) # If we're empty, avoid calling `#to_a` on other. if empty? return 0 if other.each.first.nil? return nil end # We'll need to traverse `other` repeatedly, so we get a stable `Array` # representation. This avoids any repeated query cost if `other` is a # `Relation`. length = 0 other = other.to_a other_length = other.length this = each loop do begin current = this.next rescue StopIteration # If we die, we are equal to other so far, check length and walk away. return other_length == length ? 0 : nil end length += 1 # Return as not comparable if we have seen more terms than are in other, # or if other does not include the current term. return nil if other_length < length || !other.include?(current) end end |
#build(attributes = {}) ⇒ Object
Builds a node with the given attributes, adding it to the relation.
Nodes are built using the configured ‘class_name` for the relation. Attributes passed in the Hash argument are set on the new node through `RDFSource#attributes=`. If the attribute keys are not valid properties on the built node, we raise an error.
@todo: clarify class behavior; it is actually tied to type, in some cases.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/active_triples/relation.rb', line 193 def build(attributes={}) new_subject = attributes.fetch('id') { RDF::Node.new } make_node(new_subject).tap do |node| node.attributes = attributes.except('id') if parent.kind_of? List::ListResource parent.list << node elsif node.kind_of? RDF::List self.push node.rdf_subject else self.push node end end end |
#clear ⇒ Relation
Empties the ‘Relation`, deleting any associated triples from `parent`.
212 213 214 215 216 217 218 |
# File 'lib/active_triples/relation.rb', line 212 def clear return self if empty? parent.delete([rdf_subject, predicate, nil]) parent.notify_observers(property) self end |
#delete(value) ⇒ ActiveTriples::Relation
this method behaves somewhat differently from ‘Array#delete`. It succeeds on deletion of non-existing values, always returning `self` unless an error is raised. There is no option to pass a block to evaluate if the value is not present. This is because access for `value` depends on query time. i.e. the `Relation` set does not have an underlying efficient data structure allowing a reliably cheap existence check.
symbols are treated as RDF::Nodes by default in ‘RDF::Mutable#delete`, but may also represent tokens in statements. This casts symbols to a literals, which gets us symmetric behavior between `#set(:sym)` and `#delete(:sym)`.
Returns self.
248 249 250 251 252 253 254 255 256 |
# File 'lib/active_triples/relation.rb', line 248 def delete(value) value = RDF::Literal(value) if value.is_a? Symbol return self if parent.query([rdf_subject, predicate, value]).nil? parent.delete([rdf_subject, predicate, value]) parent.notify_observers(property) self end |
#delete?(value) ⇒ Object?
A variation on ‘#delete`. This queries the relation for matching values before running the deletion, returning `nil` if it does not exist.
267 268 269 270 271 272 273 274 |
# File 'lib/active_triples/relation.rb', line 267 def delete?(value) value = RDF::Literal(value) if value.is_a? Symbol return nil if parent.query([rdf_subject, predicate, value]).nil? delete(value) value end |
#each ⇒ Enumerator<Object>
Gives a result set for the ‘Relation`.
By default, ‘RDF::URI` and `RDF::Node` results are cast to `RDFSource`. When `cast?` is `false`, `RDF::Resource` values are left in their raw form.
‘Literal` results are cast as follows:
- Simple string literals are returned as `String`
- `rdf:langString` literals are always returned as raw `Literal` objects,
retaining their language tags.
- Typed literals are cast to their Ruby `#object` when their datatype
is associated with a `Literal` subclass.
317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/active_triples/relation.rb', line 317 def each return [].to_enum if predicate.nil? if block_given? objects do |object| converted_object = convert_object(object) yield converted_object unless converted_object.nil? end end to_enum end |
#empty? ⇒ Boolean
Returns true if the results are empty.
332 333 334 |
# File 'lib/active_triples/relation.rb', line 332 def empty? objects.empty? end |
#first_or_create(attributes = {}) ⇒ Object
for removal in 1.0.0. Use ‘first || build({})`, `build({}) if empty?` or similar logic.
Returns the first result, if present; else a newly built node.
343 344 345 346 |
# File 'lib/active_triples/relation.rb', line 343 def first_or_create(attributes={}) warn 'DEPRECATION: #first_or_create is deprecated for removal in 1.0.0.' first || build(attributes) end |
#length ⇒ Integer
350 351 352 |
# File 'lib/active_triples/relation.rb', line 350 def length objects.to_a.length end |
#predicate ⇒ RDF::Term?
Gives the predicate used by the Relation. Values of this object are those that match the pattern ‘<rdf_subject> <predicate> [value] .`
362 363 364 365 |
# File 'lib/active_triples/relation.rb', line 362 def predicate return property if property.is_a?(RDF::Term) property_config[:predicate] if is_property? end |
#property ⇒ Symbol, RDF::URI
Returns the property for the Relation. This may be a registered property key or an RDF::URI.
373 374 375 |
# File 'lib/active_triples/relation.rb', line 373 def property value_arguments.last end |
#set(values) ⇒ Relation
Set the values of the Relation
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/active_triples/relation.rb', line 392 def set(values) raise UndefinedPropertyError.new(property, reflections) if predicate.nil? values = prepare_relation(values) if values.is_a?(Relation) values = [values].compact unless values.kind_of?(Array) clear values.each { |val| set_value(val) } parent.notify_observers(property) parent.persist! if parent.persistence_strategy.respond_to?(:ancestors) && parent.persistence_strategy.ancestors.any? { |r| r.is_a?(ActiveTriples::List::ListResource) } self end |
#subtract(enum) ⇒ Relation #subtract(*values) ⇒ Relation
This casts symbols to a literals, which gets us symmetric behavior with ‘#set(:sym)`.
This method treats all calls as changes for the purpose of observer notifications
Returns self.
424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/active_triples/relation.rb', line 424 def subtract(*values) values = values.first if values.first.is_a? Enumerable statements = values.map do |value| value = RDF::Literal(value) if value.is_a? Symbol [rdf_subject, predicate, value] end parent.delete(*statements) parent.notify_observers(property) self end |
#swap(swap_out, swap_in) ⇒ Relation
Replaces the first argument with the second as a value within the relation.
444 445 446 |
# File 'lib/active_triples/relation.rb', line 444 def swap(swap_out, swap_in) self.<<(swap_in) if delete?(swap_out) end |
#|(array) ⇒ Array
simply passes to ‘Array#|` unless argument is a `Relation`
74 75 76 77 78 79 |
# File 'lib/active_triples/relation.rb', line 74 def |(array) return to_a | array unless array.is_a? Relation (objects.to_a | array.objects.to_a) .map { |object| convert_object(object) } end |