class RDF::N3::List

Sub-class of RDF::List which uses a native representation of values and allows recursive lists.

Also serves as the vocabulary URI for expanding other methods

Constants

URI

Public Class Methods

method_missing(property, *args, &block) click to toggle source

Returns a vocubulary term

Calls superclass method
# File lib/rdf/n3/list.rb, line 13
def self.method_missing(property, *args, &block)
  property = RDF::Vocabulary.camelize(property.to_s)
  if args.empty? && !to_s.empty?
    RDF::Vocabulary::Term.intern("#{URI}#{property}", attributes: {})
  else
    super
  end
end
new(subject: nil, graph: nil, values: nil, &block) click to toggle source

Initializes a newly-constructed list.

Instantiates a new list based at ‘subject`, which must be an RDF::Node. List may be initialized using passed `values`.

@example add constructed list to existing graph

l = RDF::N3::List(values: (1, 2, 3))
g = RDF::Graph.new << l
g.count # => l.count

If values is not provided, but subject and graph are, then will attempt to recursively represent lists.

@param [RDF::Resource] subject (RDF.nil)

Subject should be an {RDF::Node}, not a {RDF::URI}. A list with an IRI head will not validate, but is commonly used to detect if a list is valid.

@param [RDF::Graph] graph (RDF::Graph.new) @param [Array<RDF::Term>] values

Any values which are not terms are coerced to `RDF::Literal`.

@yield [list] @yieldparam [RDF::List] list

# File lib/rdf/n3/list.rb, line 64
def initialize(subject: nil, graph: nil, values: nil, &block)
  @subject = subject || (Array(values).empty? ? RDF.nil : RDF::Node.new)
  @graph = graph
  @valid = true

  @values = case
  when values
    values.map do |v|
      # Convert values, as necessary.
      case v
      when RDF::Value then v.to_term
      when Symbol     then RDF::Node.intern(v)
      when Array      then RDF::N3::List.new(values: v)
      when nil        then RDF.nil
      else                 RDF::Literal.new(v)
      end
    end
  when subject && graph
    ln = RDF::List.new(subject: subject, graph: graph)
    @valid = ln.valid?
    ln.to_a.map {|li| self.class.try_list(li, graph)}
  else
    []
  end
end
to_uri() click to toggle source

Returns the base URI for this vocabulary.

@return [URI]

# File lib/rdf/n3/list.rb, line 26
def self.to_uri
  URI
end
try_list(subject, graph) click to toggle source

Attempts to create an RDF::N3::List from subject, or returns the node as is, if unable.

@param [RDF::Resource] subject @return [RDF::List, RDF::Resource] returns either the original resource, or a list based on that resource

# File lib/rdf/n3/list.rb, line 35
def self.try_list(subject, graph)
  return subject unless subject && (subject.node? || subject.uri? && subject == RDF.nil)
  ln = RDF::List.new(subject: subject, graph: graph)
  return subject unless ln.valid?

  # Return a new list, outside of this queryable, with any embedded lists also expanded
  values = ln.to_a.map {|li| try_list(li, graph)}
  RDF::N3::List.new(subject: subject, graph: graph, values: values)
end

Public Instance Methods

<<(value) click to toggle source

Appends an element to the tail of this list.

@example

RDF::List[] << 1 << 2 << 3              #=> RDF::List[1, 2, 3]

@param [RDF::Term] value @return [RDF::List] @see ruby-doc.org/core-2.2.2/Array.html#method-i-3C-3C

# File lib/rdf/n3/list.rb, line 240
def <<(value)
  value = normalize_value(value)
  @subject = nil
  @values << value
  self
end
==(other) click to toggle source

@see RDF::Value#==

# File lib/rdf/n3/list.rb, line 98
def ==(other)
  case other
  when Array, RDF::List then to_a == other.to_a
  else
    false
  end
end
[]=(*args) click to toggle source

Element Assignment — Sets the element at ‘index`, or replaces a subarray from the `start` index for `length` elements, or replaces a subarray specified by the `range` of indices.

@overload []=(index, term)

Replaces the element at `index` with `term`.
@param [Integer] index
@param [RDF::Term] term
  A non-RDF::Term is coerced to a Literal.
@return [RDF::Term]
@raise [IndexError]

@overload []=(start, length, value)

Replaces a subarray from the `start` index for `length` elements with `value`. Value is a {RDF::Term}, Array of {RDF::Term}, or {RDF::List}.
@param [Integer] start
@param [Integer] length
@param [RDF::Term, Array<RDF::Term>, RDF::List] value
  A non-RDF::Term is coerced to a Literal.
@return [RDF::Term, RDF::List]
@raise [IndexError]

@overload []=(range, value)

Replaces a subarray from the `start` index for `length` elements with `value`. Value is a {RDF::Term}, Array of {RDF::Term}, or {RDF::List}.
@param [Range] range
@param [RDF::Term, Array<RDF::Term>, RDF::List] value
  A non-RDF::Term is coerced to a Literal.
@return [RDF::Term, RDF::List]
@raise [IndexError]
# File lib/rdf/n3/list.rb, line 141
def []=(*args)
  value = case args.last
  when Array then args.last
  when RDF::List then args.last.to_a
  else [args.last]
  end.map do |v|
    # Convert values, as necessary.
    case v
    when RDF::Value then v.to_term
    when Symbol     then RDF::Node.intern(v)
    when Array      then RDF::N3::List.new(values: v)
    when nil        then RDF.nil
    else                 RDF::Literal.new(v)
    end
  end

  ret = case args.length
  when 3
    start, length = args[0], args[1]
    @subject = nil if start == 0
    @values[start, length] = value
  when 2
    case args.first
    when Integer
      raise ArgumentError, "Index form of []= takes a single term" if args.last.is_a?(Array)
      @values[args.first] = value.first
    when Range
      @values[args.first] = value
    else
      raise ArgumentError, "Index form of must use an integer or range"
    end
  else
    raise ArgumentError, "List []= takes one or two index values"
  end

  # Fill any nil entries in @values with rdf:nil
  @values.map! {|v| v || RDF.nil}

  @subject = RDF.nil if @values.empty?
  @subject ||= RDF::Node.new
  ret # Returns inserted values
end
at(index) click to toggle source

Returns the element at ‘index`.

@example

RDF::List[1, 2, 3].at(0)                #=> 1
RDF::List[1, 2, 3].at(4)                #=> nil

@return [RDF::Term, nil] @see ruby-doc.org/core-2.2.2/Array.html#method-i-at

# File lib/rdf/n3/list.rb, line 312
def at(index)
  @values.at(index)
end
clear() click to toggle source

Empties this list

@example

RDF::List[1, 2, 2, 3].clear    #=> RDF::List[]

@return [RDF::List] @see ruby-doc.org/core-2.2.2/Array.html#method-i-clear

# File lib/rdf/n3/list.rb, line 225
def clear
  @values.clear
  @subject = nil
  self
end
each(&block) click to toggle source

Yields each element in this list.

@example

RDF::List[1, 2, 3].each do |value|
  puts value.inspect
end

@return [Enumerator] @see ruby-doc.org/core-1.9/classes/Enumerable.html

# File lib/rdf/n3/list.rb, line 371
def each(&block)
  return to_enum unless block_given?

  @values.each(&block)
end
each_descendant(&block) click to toggle source

Enumerate via depth-first recursive descent over list members, yielding each member @yield term @yieldparam [RDF::Term] term @return [Enumerator]

# File lib/rdf/n3/list.rb, line 434
def each_descendant(&block)
  if block_given?
    each do |term|
      term.each_descendant(&block) if term.list?
      block.call(term)
    end
  end
  enum_for(:each_descendant)
end
each_statement(&block) click to toggle source

Yields each statement constituting this list. Uses actual statements if a graph was set, otherwise, the saved values.

This will recursively get statements for sub-lists as well.

@example

RDF::List[1, 2, 3].each_statement do |statement|
  puts statement.inspect
end

@return [Enumerator] @see RDF::Enumerable#each_statement

# File lib/rdf/n3/list.rb, line 389
def each_statement(&block)
  return enum_statement unless block_given?

  if graph
    RDF::List.new(subject: subject, graph: graph).each_statement(&block)
  elsif @values.length > 0
    # Create a subject for each entry based on the subject bnode
    subjects = (0..(@values.count-1)).map {|ndx| ndx > 0 ? RDF::Node.intern("#{subject.id}_#{ndx}") : subject}
    *values, last = @values
    while !values.empty?
      subj = subjects.shift
      value = values.shift
      block.call(RDF::Statement(subj, RDF.first, value.list? ? value.subject : value))
      block.call(RDF::Statement(subj, RDF.rest, subjects.first))
    end
    subj = subjects.shift
    block.call(RDF::Statement(subj, RDF.first, last.list? ? last.subject : last))
    block.call(RDF::Statement(subj, RDF.rest, RDF.nil))
  end

  # If a graph was used, also get statements from sub-lists
  @values.select(&:list?).each {|li| li.each_statement(&block)}
end
each_subject(&block) click to toggle source

Yields each subject term constituting this list along with sub-lists.

@example

RDF::List[1, 2, 3].each_subject do |subject|
  puts subject.inspect
end

@return [Enumerator] @see RDF::Enumerable#each

# File lib/rdf/n3/list.rb, line 423
def each_subject(&block)
  return enum_subject unless block_given?

  each_statement {|st| block.call(st.subject) if st.predicate == RDF.rest}
end
empty?() click to toggle source

Returns ‘true` if this list is empty.

@example

RDF::List[].empty?                      #=> true
RDF::List[1, 2, 3].empty?               #=> false

@return [Boolean] @see ruby-doc.org/core-2.2.2/Array.html#method-i-empty-3F

# File lib/rdf/n3/list.rb, line 256
def empty?
  @values.empty?
end
eql?(other) click to toggle source

Checks pattern equality against another list, considering nesting.

@param [List, Array] other @return [Boolean]

# File lib/rdf/n3/list.rb, line 485
def eql?(other)
  other = RDF::N3::List[*other] if other.is_a?(Array)
  return false if !other.is_a?(RDF::List) || count != other.count
  @values.each_with_index do |li, ndx|
    case li
    when RDF::Query::Pattern, RDF::N3::List
      return false unless li.eql?(other.at(ndx))
    else
      return false unless li == other.at(ndx)
    end
  end
  true
end
evaluate(bindings, formulae: {}, **options) click to toggle source

Evaluates the list using the given variable ‘bindings`.

@param [Hash{Symbol => RDF::Term}] bindings

a query solution containing zero or more variable bindings

@param [Hash{Symbol => Object}] options ({})

options passed from query

@return [RDF::N3::List] @see SPARQL::Algebra::Expression.evaluate

# File lib/rdf/n3/list.rb, line 559
def evaluate(bindings, formulae: {}, **options)
  # if values are constant, simply return ourselves
  return self if to_a.none? {|li| li.node? || li.variable?}
  bindings = bindings.to_h unless bindings.is_a?(Hash)
  # Create a new list subject using a combination of the current subject and a hash of the binding values
  subj = "#{subject.id}_#{bindings.values.sort.hash}"
  values = to_a.map do |o|
    o = o.evaluate(bindings, formulae: formulae, **options) || o
  end
  RDF::N3::List.new(subject: RDF::Node.intern(subj), values: values)
end
fetch(*args, &block) click to toggle source

Returns element at ‘index` with default.

@example

RDF::List[1, 2, 3].fetch(0)             #=> RDF::Literal(1)
RDF::List[1, 2, 3].fetch(4)             #=> IndexError
RDF::List[1, 2, 3].fetch(4, nil)        #=> nil
RDF::List[1, 2, 3].fetch(4) { |n| n*n } #=> 16

@return [RDF::Term, nil] @see ruby-doc.org/core-1.9/classes/Array.html#M000420

# File lib/rdf/n3/list.rb, line 299
def fetch(*args, &block)
  @values.fetch(*args, &block)
end
first() click to toggle source

Returns the first element in this list.

@example

RDF::List[*(1..10)].first               #=> RDF::Literal(1)

@return [RDF::Term]

# File lib/rdf/n3/list.rb, line 323
def first
  @values.first
end
has_nodes?() click to toggle source

Does this list, or any recusive list have any blank node members?

@return [Boolean]

# File lib/rdf/n3/list.rb, line 448
def has_nodes?
  @values.any? {|e| e.node? || e.list? && e.has_nodes?}
end
hash() click to toggle source

The list hash is the hash of it’s members.

@see RDF::Value#hash

# File lib/rdf/n3/list.rb, line 110
def hash
  to_a.hash
end
index(value) click to toggle source

Returns the index of the first element equal to ‘value`, or `nil` if no match was found.

@example

RDF::List['a', 'b', 'c'].index('a')     #=> 0
RDF::List['a', 'b', 'c'].index('d')     #=> nil

@param [RDF::Term] value @return [Integer] @see ruby-doc.org/core-2.2.2/Array.html#method-i-index

# File lib/rdf/n3/list.rb, line 284
def index(value)
  @values.index(value)
end
last() click to toggle source

Returns the last element in this list.

@example

RDF::List[*(1..10)].last                 #=> RDF::Literal(10)

@return [RDF::Term] @see ruby-doc.org/core-2.2.2/Array.html#method-i-last

# File lib/rdf/n3/list.rb, line 335
def last
  @values.last
end
length() click to toggle source

Returns the length of this list.

@example

RDF::List[].length                      #=> 0
RDF::List[1, 2, 3].length               #=> 3

@return [Integer] @see ruby-doc.org/core-2.2.2/Array.html#method-i-length

# File lib/rdf/n3/list.rb, line 269
def length
  @values.length
end
rest() click to toggle source

Returns a list containing all but the first element of this list.

@example

RDF::List[1, 2, 3].rest                 #=> RDF::List[2, 3]

@return [RDF::List]

# File lib/rdf/n3/list.rb, line 346
def rest
  self.class.new(values: @values[1..-1])
end
shift() click to toggle source

Removes and returns the element at the head of this list.

@example

RDF::List[1,2,3].shift              #=> 1

@return [RDF::Term] @see ruby-doc.org/core-2.2.2/Array.html#method-i-shift

# File lib/rdf/n3/list.rb, line 211
def shift
  return nil if empty?
  @subject = nil
  @values.shift
end
solution(list) click to toggle source

Returns a query solution constructed by binding any variables in this list with the corresponding terms in the given ‘list`.

@param [RDF::N3::List] list

a native list with patterns to bind.

@return [RDF::Query::Solution] @see RDF::Query::Pattern#solution

# File lib/rdf/n3/list.rb, line 578
def solution(list)
  RDF::Query::Solution.new do |solution|
    @values.each_with_index do |li, ndx|
      if li.respond_to?(:solution)
        solution.merge!(li.solution(list[ndx]))
      elsif li.is_a?(RDF::Query::Variable)
        solution[li.to_sym] = list[ndx]
      end
    end
  end
end
tail() click to toggle source

Returns a list containing the last element of this list.

@example

RDF::List[1, 2, 3].tail                 #=> RDF::List[3]

@return [RDF::List]

# File lib/rdf/n3/list.rb, line 357
def tail
  self.class.new(values: @values[-1..-1])
end
to_a() click to toggle source

Returns the elements in this list as an array.

@example

RDF::List[].to_a                        #=> []
RDF::List[1, 2, 3].to_a                 #=> [RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]

@return [Array]

# File lib/rdf/n3/list.rb, line 476
def to_a
  @values
end
to_base() click to toggle source

Returns the base representation of this term.

@return [Sring]

# File lib/rdf/n3/list.rb, line 594
def to_base
  "(#{@values.map(&:to_base).join(' ')})"
end
to_ndvar(scope) click to toggle source

Substitutes blank node members with existential variables, recusively.

@param [RDF::Node] scope @return [RDF::N3::List]

# File lib/rdf/n3/list.rb, line 457
def to_ndvar(scope)
  values = @values.map do |e|
    case e
    when RDF::Node     then e.to_ndvar(scope)
    when RDF::N3::List then e.to_ndvar(scope)
    else                    e
    end
  end
  RDF::N3::List.new(values: values)
end
to_sxp_bin() click to toggle source

Transform Statement into an SXP @return [Array]

# File lib/rdf/n3/list.rb, line 600
def to_sxp_bin
  to_a.to_sxp_bin
end
transform(&block) click to toggle source

Creates a new list by recusively mapping the values of the list

@return [RDF::N3::list]

# File lib/rdf/n3/list.rb, line 608
def transform(&block)
  values = self.to_a.map {|v| v.list? ? v.map(&block) : block.call(v)}
  RDF::N3::List.new(values: values)
end
unshift(value) click to toggle source

Appends an element to the head of this list. Existing references are not updated, as the list subject changes as a side-effect.

@example

RDF::List[].unshift(1).unshift(2).unshift(3) #=> RDF::List[3, 2, 1]

@param [RDF::Term, Array<RDF::Term>, RDF::List] value

A non-RDF::Term is coerced to a Literal

@return [RDF::List] @see ruby-doc.org/core-2.2.2/Array.html#method-i-unshift

# File lib/rdf/n3/list.rb, line 195
def unshift(value)
  value = normalize_value(value)
  @values.unshift(value)
  @subject = nil

  return self
end
valid?() click to toggle source

Lists are valid, unless established via RDF::List, in which case they are only valid if the RDF::List is valid.

@return [Boolean]

# File lib/rdf/n3/list.rb, line 94
def valid?; @valid; end
var_values(var, list) click to toggle source

Returns all values the list in the same pattern position

@param [Symbol] var @param [RDF::N3::List] list @return [Array<RDF::Term>]

# File lib/rdf/n3/list.rb, line 540
def var_values(var, list)
  results = []
  @values.each_index do |ndx|
    maybe_var = @values[ndx]
    next unless maybe_var.respond_to?(:var_values)
    results.push(*Array(maybe_var.var_values(var, list.at(ndx))))
  end
  results.flatten.compact
end
variable?() click to toggle source

A list is variable if any of its members are variable?

@return [Boolean]

# File lib/rdf/n3/list.rb, line 503
def variable?
  @values.any?(&:variable?)
end
variable_count() click to toggle source

Returns the number of variables in this list, recursively.

@return [Integer]

# File lib/rdf/n3/list.rb, line 530
def variable_count
  variables.length
end
variables() click to toggle source

Returns all variables in this list.

Note: this returns a hash containing distinct variables only.

@return [Hash{Symbol => Variable}]

# File lib/rdf/n3/list.rb, line 520
def variables
  @values.inject({}) do |hash, li|
    li.respond_to?(:variables) ? hash.merge(li.variables) : hash
  end
end
vars() click to toggle source

Return the variables contained this list @return [Array<RDF::Query::Variable>]

# File lib/rdf/n3/list.rb, line 510
def vars
  @values.vars
end

Private Instance Methods

normalize_value(value) click to toggle source

Normalizes ‘Array` to `RDF::List` and `nil` to `RDF.nil`.

@param value [Object] @return [RDF::Value, Object] normalized value

# File lib/rdf/n3/list.rb, line 620
def normalize_value(value)
  case value
  when RDF::Value then value.to_term
  when Array      then RDF::N3::List.new(values: value)
  when Symbol     then RDF::Node.intern(value)
  when nil        then RDF.nil
  else                 RDF::Literal.new(value)
  end
end