Class: ActiveTriples::Relation

Inherits:
Object
  • Object
show all
Includes:
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 (Array) of RDF::Terms that are objects in the of source’s triples of the form:

<{#parent}> <{#predicate}> [term] .

Relations express a set of binary relationships (on a predicate) between the parent node and a term.

When the term is a URI or Blank Node, it is represented in the results Array as an RDFSource with a graph selected as a subgraph of the parent’s. The triples in this subgraph are: (a) those whose subject is the term; (b) …

See Also:

  • RDF::Term

Defined Under Namespace

Classes: ValueError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent_source, value_arguments) ⇒ Relation

Returns a new instance of Relation.

Parameters:

  • parent_source (ActiveTriples::RDFSource)
  • value_arguments (Array<Symbol, Hash>)

    if a Hash is passed as the final element, it is removed and set to ‘@rel_args`.



43
44
45
46
47
48
49
50
# File 'lib/active_triples/relation.rb', line 43

def initialize(parent_source, value_arguments)
  self.parent = parent_source
  @reflections = parent_source.reflections
  self.rel_args ||= {}
  self.rel_args = value_arguments.pop if value_arguments.is_a?(Array) &&
                                         value_arguments.last.is_a?(Hash)
  self.value_arguments = value_arguments
end

Instance Attribute Details

#parentRDFSource

Returns the resource that is the domain of this relation.

Returns:

  • (RDFSource)

    the resource that is the domain of this relation



33
34
35
# File 'lib/active_triples/relation.rb', line 33

def parent
  @parent
end

#reflectionsClass (readonly)

Returns:

  • (Class)


33
# File 'lib/active_triples/relation.rb', line 33

attr_accessor :parent, :value_arguments, :rel_args

#rel_argsHash

Returns:

  • (Hash)


33
# File 'lib/active_triples/relation.rb', line 33

attr_accessor :parent, :value_arguments, :rel_args

#value_argumentsArray<Object>

Returns:

  • (Array<Object>)


33
# File 'lib/active_triples/relation.rb', line 33

attr_accessor :parent, :value_arguments, :rel_args

Instance Method Details

#<<(values) ⇒ Relation Also known as: push

Adds values to the result set

Parameters:

  • values (Object, Array<Object>)

    values to add

Returns:

  • (Relation)

    a relation containing the set values; i.e. ‘self`



303
304
305
306
# File 'lib/active_triples/relation.rb', line 303

def <<(values)
  values = Array.wrap(result) | Array.wrap(values)
  self.set(values)
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.

Examples:

building an empty generic node

resource = ActiveTriples::Resource.new
resource.resource.get_values(RDF::Vocab::DC.relation).build
# => #<ActiveTriples::Resource:0x2b0(#<ActiveTriples::Resource:0x005>)>)

resource.dump :ttl
# => "\n [ <http://purl.org/dc/terms/relation> []] .\n"

building a node with attributes

class WithRelation
  include ActiveTriples::RDFSource
  property :relation, predicate:  RDF::Vocab::DC.relation,
    class_name: 'WithTitle'
end

class WithTitle
  include ActiveTriples::RDFSource
  property :title, predicate: RDF::Vocab::DC.title
end

resource = WithRelation.new
attributes = { id: 'http://ex.org/moomin', title: 'moomin' }

resource.get_values(:relation).build(attributes)
# => #<ActiveTriples::Resource:0x2b0(#<ActiveTriples::Resource:0x005>)>)

resource.dump :ttl
# => "\n<http://ex.org/moomin> <http://purl.org/dc/terms/title> \"moomin\" .\n\n [ <http://purl.org/dc/terms/relation> <http://ex.org/moomin>] .\n"

Parameters:

  • attributes (Hash) (defaults to: {})

    a hash of attribute names and values for the built node.

See Also:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/active_triples/relation.rb', line 182

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

#clearRelation

Empties the ‘Relation`, deleting any associated triples from `parent`.

Returns:

  • (Relation)

    self; a now empty relation



56
57
58
59
60
# File 'lib/active_triples/relation.rb', line 56

def clear
  parent.delete([rdf_subject, predicate, nil])

  self
end

#delete(value) ⇒ ActiveTriples::Relation

Note:

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.

Note:

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.

Examples:

deleting a value

resource = MySource.new
resource.title = ['moomin', 'valley']
resource.title.delete('moomin') # => ["valley"]
resource.title # => ['valley']

note the behavior of unmatched values

resource = MySource.new
resource.title = 'moomin'
resource.title.delete('valley') # => ["moomin"]
resource.title # => ['moomin']

Parameters:

  • value (Object)

    the value to delete from the relation

Returns:



225
226
227
228
229
230
# File 'lib/active_triples/relation.rb', line 225

def delete(value)
  value = RDF::Literal(value) if value.is_a? Symbol
  parent.delete([rdf_subject, predicate, value])

  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.

Parameters:

  • value (Object)

    the value to delete from the relation

Returns:

  • (Object, nil)

    ‘nil` if the value doesn’t exist; the value otherwise

See Also:



241
242
243
244
245
246
247
248
# File 'lib/active_triples/relation.rb', line 241

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

#first_or_create(attributes = {}) ⇒ Object

Returns the first result, if present; else a newly built node.

Returns:

  • (Object)

    the first result, if present; else a newly built node

See Also:



293
294
295
# File 'lib/active_triples/relation.rb', line 293

def first_or_create(attributes={})
  result.first || build(attributes)
end

#predicateRDF::Term?

Gives the predicate used by the Relation. Values of this object are those that match the pattern ‘<rdf_subject> <predicate> [value] .`

Returns:

  • (RDF::Term, nil)

    the predicate for this relation; nil if no predicate can be found

See Also:



336
337
338
339
# File 'lib/active_triples/relation.rb', line 336

def predicate
  return property if property.is_a?(RDF::Term)
  property_config[:predicate] if is_property?
end

#propertySymbol, RDF::URI

Returns the property for the Relation. This may be a registered property key or an RDF::URI.

Returns:

  • (Symbol, RDF::URI)

    the property for this Relation.

See Also:



324
325
326
# File 'lib/active_triples/relation.rb', line 324

def property
  value_arguments.last
end

#property_configHash<Symbol, ]

TODO:

find a way to simplify this?

Returns Hash<Symbol, ].

Returns:

  • (Hash<Symbol, ])

    Hash<Symbol, ]



312
313
314
315
316
# File 'lib/active_triples/relation.rb', line 312

def property_config
  return type_property if is_type?
  
  reflections.reflect_on_property(property)
end

#resultArray<Object>

Gives an Array containing the result set for the ActiveTriples::Relation.

By default, RDF::URI and RDF::Node results are cast to ‘RDFSource`. Literal results are given as their `#object` representations (e.g. String, Date.

When ‘cast?` is `false`, RDF::Resource values are left in their raw form. Similarly, when `#return_literals?` is `true`, literals are returned in their RDF::Literal form, preserving language tags, datatype, and value.

Examples:

results with default casting

parent << [parent.rdf_subject, predicate, 'my value']
parent << [parent.rdf_subject, predicate, Date.today]
parent << [parent.rdf_subject, predicate, RDF::URI('http://ex.org/#me')]
parent << [parent.rdf_subject, predicate, RDF::Node.new]
relation.result
# => ["my_value",
#     Fri, 25 Sep 2015,
#     #<ActiveTriples::Resource:0x3f8...>,
#     #<ActiveTriples::Resource:0x3f8...>]

results with ‘cast?` set to `false`

relation.result
# => ["my_value",
#     Fri, 25 Sep 2015,
#     #<RDF::URI:0x3f8... URI:http://ex.org/#me>,
#     #<RDF::Node:0x3f8...(_:g69843536054680)>]

results with ‘return_literals?` set to `true`

relation.result
# => [#<RDF::Literal:0x3f8...("my_value")>,
#     #<RDF::Literal::Date:0x3f8...("2015-09-25"^^<http://www.w3.org/2001/XMLSchema#date>)>,
#     #<ActiveTriples::Resource:0x3f8...>,
#     #<ActiveTriples::Resource:0x3f8...>]

Returns:

  • (Array<Object>)

    the result set



100
101
102
103
104
105
106
107
108
# File 'lib/active_triples/relation.rb', line 100

def result
  return [] if predicate.nil?
  statements = parent.query(:subject => rdf_subject,
                            :predicate => predicate)
  statements.each_with_object([]) do |x, collector|
    converted_object = convert_object(x.object)
    collector << converted_object unless converted_object.nil?
  end
end

#set(values) ⇒ Relation

Adds values to the relation

Parameters:

  • values (Array<RDF::Resource>, RDF::Resource)

    an array of values or a single value. If not an RDF::Resource, the values will be coerced to an RDF::Literal or RDF::Node by RDF::Statement

Returns:

  • (Relation)

    a relation containing the set values; i.e. ‘self`

Raises:

See Also:



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/active_triples/relation.rb', line 125

def set(values)
  raise UndefinedPropertyError.new(property, reflections) if predicate.nil?
  values = values.to_a if values.is_a? Relation
  values = [values].compact unless values.kind_of?(Array)

  clear
  values.each { |val| set_value(val) }

  parent.persist! if parent.persistence_strategy.is_a? ParentStrategy
  self
end

#subtract(enum) ⇒ Relation #subtract(*values) ⇒ Relation

Note:

This casts symbols to a literals, which gets us symmetric behavior with ‘#set(:sym)`.

Returns self.

Overloads:

  • #subtract(enum) ⇒ Relation

    Deletes objects in the enumerable from the relation

    Parameters:

    • values (Enumerable)

      an enumerable of objects to delete

  • #subtract(*values) ⇒ Relation

    Deletes each argument value from the relation

    Parameters:

    • *values (Array<Object>)

      the objects to delete

Returns:

See Also:



263
264
265
266
267
268
269
270
271
272
# File 'lib/active_triples/relation.rb', line 263

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)
  self
end

#swap(swap_out, swap_in) ⇒ Relation

Replaces the first argument with the second as a value within the relation.

Parameters:

  • swap_out (Object)

    the value to delete

  • swap_in (Object)

    the replacement value

Returns:



285
286
287
# File 'lib/active_triples/relation.rb', line 285

def swap(swap_out, swap_in)
  self.<<(swap_in) if delete?(swap_out)
end