Class: SHACL::Algebra::Operator Abstract

Inherits:
SPARQL::Algebra::Operator
  • Object
show all
Extended by:
JSON::LD::Utils
Includes:
RDF::Util::Logger
Defined in:
lib/shacl/algebra/operator.rb

Overview

This class is abstract.

The SHACL operator.

Constant Summary collapse

BUILTIN_KEYS =

All keys associated with shapes which are set in options

Returns:

  • (Array<Symbol>)
%i(
  id type label name comment description deactivated severity
  message
  order group defaultValue path
  targetNode targetClass targetSubjectsOf targetObjectsOf
  class datatype nodeKind
  minCount maxCount
  minExclusive minInclusive maxExclusive maxInclusive
  minLength maxLength
  languageIn uniqueLang
  equals disjoint lessThan lessThanOrEquals
  closed ignoredProperties hasValue in
  declare namespace prefix
).freeze
PARAMETERS =

Parameters to components.

{
  and: {class: :AndConstraintComponent},
  class: {
    class: :ClassConstraintComponent,
    nodeKind: :IRI,
  },
  closed: {
    class: :ClosedConstraintComponent,
    datatype: RDF::XSD.boolean,
  },
  datatype: {
    class: :DatatypeConstraintComponent,
    nodeKind: :IRI,
    maxCount: 1,
  },
  disjoint: {
    class: :DisjointConstraintComponent,
    nodeKind: :IRI,
  },
  equals: {
    class: :EqualsConstraintComponent,
    nodeKind: :IRI,
  },
  expression: {class: :ExpressionConstraintComponent},
  flags: {
    class: :PatternConstraintComponent,
    datatype: RDF::XSD.string,
    optional: true
  },
  hasValue: {
    class: :HasValueConstraintComponent,
    nodeKind: :IRIOrLiteral,
  },
  ignoredProperties: {
    class: :ClosedConstraintComponent,
    nodeKind: :IRI, # Added
    optional: true,
  },
  in: {
    class: :InConstraintComponent,
    nodeKind: :IRIOrLiteral,
    #maxCount: 1, # List internalized
  },
  languageIn: {
    class: :LanguageInConstraintComponent,
    datatype: RDF::XSD.string,  # Added
    #maxCount: 1, # List internalized
  },
  lessThan: {
    class: :LessThanConstraintComponent,
    nodeKind: :IRI,
  },
  lessThanOrEquals: {
    class: :LessThanOrEqualsConstraintComponent,
    nodeKind: :IRI,
  },
  maxCount: {
    class: :MaxCountConstraintComponent,
    datatype: RDF::XSD.integer,
    maxCount: 1,
  },
  maxExclusive: {
    class: :MaxExclusiveConstraintComponent,
    maxCount: 1,
    nodeKind: :Literal,
  },
  maxInclusive: {
    class: :MaxInclusiveConstraintComponent,
    maxCount: 1,
    nodeKind: :Literal,
  },
  maxLength: {
    class: :MaxLengthConstraintComponent,
    datatype: RDF::XSD.integer,
    maxCount: 1,
  },
  minCount: {
    class: :MinCountConstraintComponent,
    datatype: RDF::XSD.integer,
    maxCount: 1,
  },
  minExclusive: {
    class: :MinExclusiveConstraintComponent,
    maxCount: 1,
    nodeKind: :Literal,
  },
  minInclusive: {
    class: :MinInclusiveConstraintComponent,
    maxCount: 1,
    nodeKind: :Literal,
  },
  minLength: {
    class: :MinLengthConstraintComponent,
    datatype: RDF::XSD.integer,
    maxCount: 1,
  },
  node: {class: :NodeConstraintComponent},
  nodeKind: {
    class: :NodeKindConstraintComponent,
    in: %i(BlankNode IRI Literal BlankNodeOrIRI BlankNodeOrLiteral IRIOrLiteral),
    maxCount: 1,
  },
  not: {class: :NotConstraintComponent},
  or: {class: :OrConstraintComponent},
  pattern: {
    class: :PatternConstraintComponent,
    datatype: RDF::XSD.string,
  },
  property: {class: :PropertyConstraintComponent},
  qualifiedMaxCount: {
    class: :QualifiedValueConstraintComponent,
    datatype: RDF::XSD.integer,
  },
  qualifiedValueShape: {
    class: :QualifiedValueConstraintComponent,
  },
  qualifiedValueShapesDisjoint: {
    class: :QualifiedValueConstraintComponent,
    datatype: RDF::XSD.boolean,
    optional: true,
  },
  qualifiedMinCount: {
    class: :QualifiedValueConstraintComponent,
    datatype: RDF::XSD.integer
  },
  sparql: {class: :SPARQLConstraintComponent},
  uniqueLang: {
    class: :UniqueLangConstraintComponent,
    datatype: RDF::XSD.boolean,
    maxCount: 1,
  },
  xone: {class: :XoneConstraintComponent},
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#graphRDF::Queryable

Graph against which shapes are validated.

Returns:

  • (RDF::Queryable)


38
39
40
# File 'lib/shacl/algebra/operator.rb', line 38

def graph
  @graph
end

#optionsObject

Initialization options



34
35
36
# File 'lib/shacl/algebra/operator.rb', line 34

def options
  @options
end

#shapes_graphRDF::Graph

Graph from which original shapes were loaded.

Returns:

  • (RDF::Graph)


42
43
44
# File 'lib/shacl/algebra/operator.rb', line 42

def shapes_graph
  @shapes_graph
end

Class Method Details

.add_component(cls, parameters) ⇒ Object

Add parameters and class def from a SPARQL-based Constraint Component

Parameters:

  • cls (RDF::URI)

    The URI of the constraint component.

  • parameters (Hash{Symbol => Hash})

    Definitions of mandatory and optional parameters for this component.



185
186
187
188
189
190
191
# File 'lib/shacl/algebra/operator.rb', line 185

def add_component(cls, parameters)
  # Remember added paraemters.
  # FIXME: should merge parameters
  @added_parameters = (@added_parameters || {}).merge(parameters)
  # Rebuild
  @params = @component_params = nil
end

.apply_op(op, values) ⇒ Object

Recursively apply operand to sucessive values until the argument count which is expected is achieved



502
503
504
505
506
507
# File 'lib/shacl/algebra/operator.rb', line 502

def apply_op(op, values)
  if values.length > op.arity
    values = values.first, apply_op(op, values[1..-1])
  end
  op.new(*values)
end

.component_paramsHash{Symbol => Hash}

Constraint Component classes indexed to their mandatory and optional parameters, which may be supplemented by SPARQL-based Constraint Components.

Returns:

  • (Hash{Symbol => Hash})

    Returns a hash relating each component URI to its optional and mandatory parameters.



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/shacl/algebra/operator.rb', line 204

def component_params
  @component_params ||= params.inject({}) do |memo, (param, properties)|
    memo.merge(Array(properties[:class]).inject(memo) do |mem, cls|
      entry = mem.fetch(cls, {})
      param_type = properties[:optional] ? :optional : :mandatory
      entry[param_type] ||= []
      entry[param_type] << param
      mem.merge(cls => entry)
    end)
  end
end

.from_expanded_value(item, **options) ⇒ RDF::Term

Interpret a JSON-LD expanded value

Parameters:

  • item (Hash)

Returns:

  • (RDF::Term)


427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/shacl/algebra/operator.rb', line 427

def from_expanded_value(item, **options)
  if item['@value']
    value, datatype = item.fetch('@value'), item.fetch('type', nil)
    case value
    when TrueClass, FalseClass
      value = value.to_s
      datatype ||= RDF::XSD.boolean.to_s
    when Numeric
      # Don't serialize as double if there are no fractional bits
      as_double = value.ceil != value || value >= 1e21 || datatype == RDF::XSD.double
      lit = if as_double
        RDF::Literal::Double.new(value, canonicalize: true)
      else
        RDF::Literal.new(value.numerator, canonicalize: true)
      end

      datatype ||= lit.datatype
      value = lit.to_s.sub("E+", "E")
    else
      datatype ||= item.has_key?('@language') ? RDF.langString : RDF::XSD.string
    end
    datatype = iri(datatype) if datatype
    language = item.fetch('@language', nil) if datatype == RDF.langString
    RDF::Literal.new(value, datatype: datatype, language: language)
  elsif item['id']
    self.iri(item['id'], **options)
  else
    RDF::Node.new
  end
end

.from_json(operator, **options) ⇒ Operator

Creates an operator instance from a parsed SHACL representation

Parameters:

  • operator (Hash)
  • options (Hash)

    ({})

Options Hash (**options):

  • :prefixes (Hash{String => RDF::URI})

Returns:



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
# File 'lib/shacl/algebra/operator.rb', line 222

def from_json(operator, **options)
  operands = []

  # Node options used to instantiate the relevant class instance.
  node_opts = options.dup

  # Node Options and operands on shape or node, which are not Constraint Component Parameters
  operator.each do |k, v|
    k = k.to_sym
    next if v.nil? || params.include?(k)
    case k
    # List properties
    when :id                 then node_opts[:id] = iri(v, vocab: false, **options)
    when :path               then node_opts[:path] = parse_path(v, **options)
    when :property
      operands.push(*as_array(v).map {|vv| PropertyShape.from_json(vv, **options)})
    when :severity           then node_opts[:severity] = iri(v, **options)
    when :targetClass        then node_opts[:targetClass] = as_array(v).map {|vv| iri(vv, **options)}
    when :targetNode
      node_opts[:targetNode] = as_array(v).map do |vv|
        from_expanded_value(vv, **options)
      end
    when :targetObjectsOf    then node_opts[:targetObjectsOf] = as_array(v).map {|vv| iri(vv, **options)}
    when :targetSubjectsOf   then node_opts[:targetSubjectsOf] = as_array(v).map {|vv| iri(vv, **options)}
    when :type               then node_opts[:type] = as_array(v).map {|vv| iri(vv, **options)}
    else
      if BUILTIN_KEYS.include?(k)
        # Add as a plain option otherwise
        node_opts[k] = to_rdf(k, v, **options)
      end
    end
  end

  # Node Options and operands on shape or node, which are Constraint Component Parameters.
  # For constraints with a defined Ruby class, the primary parameter is the NAME from the constraint class. Other parameters are added as named operands to the component operator.
  used_components = {}
  operator.each do |k, v|
    k = k.to_sym
    next if v.nil? || !params.include?(k)
    param_props = params[k]
    param_classes = Array(param_props[:class])

    # Keep track of components which have been used.
    param_classes.each {|cls| used_components[cls] ||= {}}

    # Check parameter constraints
    v = as_array(v)
    if param_props[:maxCount] && v.length > param_props[:maxCount]
      raise SHACL::Error, "Property #{k} on #{self.const_get(:NAME)} has too many values: #{v.inspect}"
    end

    # If an optional parameter exists without corresponding mandatory parameters on a given shape, raise a SHACL::Error.
    #
    # Records any instances of components which are created to re-attach non-primary parameters after all operators are processed.
    instances = case k
    # List properties
    when :node
      as_array(v).map {|vv| NodeShape.from_json(vv, **options)}
    when :property
      as_array(v).map {|vv| PropertyShape.from_json(vv, **options)}
    when :sparql
      as_array(v).map {|vv| SPARQLConstraintComponent.from_json(vv, **options)}
    else
      # Process parameter values based on nodeKind, in, and datatype.
      elements = if param_props[:nodeKind]
        case param_props[:nodeKind]
        when :IRI
          v.map {|vv| iri(vv, **options)}
        when :Literal
          v.map do |vv|
            vv.is_a?(Hash) ?
              from_expanded_value(vv, **options) :
              RDF::Literal(vv)
          end
        when :IRIOrLiteral
          to_rdf(k, v, **options)
        end
      elsif param_props[:in]
        v.map do |vv|
          iri(vv, **options) if param_props[:in].include?(vv.to_sym)
        end
      elsif param_props[:datatype]
        v.map {|vv| RDF::Literal(vv, datatype: param_props[:datatype])}
      else
        v.map {|vv| SHACL::Algebra.from_json(vv, **options)}
      end

      # Builtins are added as options to the operator, otherwise, they are class instances of constraint components added as operators.
      if BUILTIN_KEYS.include?(k)
        node_opts[k] = elements
        [] # No instances created
      else
        klass = SHACL::Algebra.const_get(Array(param_props[:class]).first)

        name = klass.const_get(:NAME)
        # If the key `k` is the same as the NAME of the class, create the instance with the defined element values.
        if name == k
          param_classes.each do |cls|
            # Add `k` as a mandatory parameter fulfilled
            (used_components[cls][:mandatory_parameters] ||= []) << k
          end

          # Instantiate the compoent
          elements.map {|e| klass.new(*e, **options.dup)}
        else
          # Add non-primary parameters for subsequent insertion
          param_classes.each do |cls|
            # Add `k` as a mandatory parameter fulfilled if it is so defined
            (used_components[cls][:mandatory_parameters] ||= []) << k unless
              params[k][:optional]

            # Add parameter as S-Expression operand
            (used_components[cls][:parameters] ||= []) << elements.unshift(k)
          end
          [] # No instances created
        end
      end
    end

    # Record the instances created by class and its operands
    param_classes.each do |cls|
      used_components[cls][:instances] = instances
    end

    # FIXME: Only add instances when all mandatory parameters are present.
    operands.push(*instances)
  end

  # Append any parameters to the used components
  used_components.each do |cls, props|
    instances = props[:instances]
    next unless instances # BUILTINs

    parameters = props.fetch(:parameters, [])
    instances.each do |op|
      parameters.each do |param|
        # Note the potential that the parameter gets added twice, if there are multiple classes for both the primary and secondary paramters.
        op.operands << param
      end
    end
  end

  new(*operands, **node_opts)
end

.iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options) ⇒ RDF::Value

Create URIs

Parameters:

  • value (RDF::Value, String)
  • base (RDF::URI) (defaults to: RDF::Vocab::SHACL.to_uri)

    Base IRI used for resolving relative values (RDF::Vocab::SHACL.to_uri).

  • vocab (Boolean) (defaults to: true)

    resolve vocabulary relative to the builtin context.

  • options (Hash{Symbol => Object})

Returns:

  • (RDF::Value)


371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/shacl/algebra/operator.rb', line 371

def iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options)
  # Context will have been pre-loaded
  @context ||= JSON::LD::Context.parse("http://github.com/ruby-rdf/shacl/")

  value = value['id'] || value['@id'] if value.is_a?(Hash)
  result = @context.expand_iri(value, base: base, vocab: vocab)
  result = RDF::URI(result) if result.is_a?(String)
  if result.respond_to?(:qname) && result.qname
    result = RDF::URI.new(result.to_s) if result.frozen?
    result.lexical = result.qname.join(':')
  end
  result
end

.paramsHash{Symbol => Hash}

Defined parameters for components, which may be supplemented by SPARQL-based Constraint Components. A parameter may be mapped to more than one component class.

Returns:

  • (Hash{Symbol => Hash})

    Returns each parameter referencing the component classes it is used in, and the property validators for values of that parameter.



196
197
198
# File 'lib/shacl/algebra/operator.rb', line 196

def params
  @params ||= PARAMETERS.merge(@added_parameters || {})
end

.parse_path(path, **options) ⇒ RDF::URI, SPARQL::Algebra::Expression

Parse the “path” attribute into a SPARQL Property Path and evaluate to find related nodes.

Parameters:

  • path (Object)

Returns:

  • (RDF::URI, SPARQL::Algebra::Expression)


463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/shacl/algebra/operator.rb', line 463

def parse_path(path, **options)
  case path
  when RDF::URI then path
  when String then iri(path)
  when Hash
    # Creates a SPARQL S-Expression resulting in a query which can be used to find corresponding
    {
      alternativePath: :alt,
      inversePath: :reverse,
      oneOrMorePath: :"path+",
      "@list": :seq,
      zeroOrMorePath: :"path*",
      zeroOrOnePath: :"path?",
    }.each do |prop, op_sym|
      if path[prop.to_s]
        value = path[prop.to_s]
        value = value['@list'] if value.is_a?(Hash) && value.key?('@list')
        value = [value] if !value.is_a?(Array)
        value = value.map {|e| parse_path(e, **options)}
        op = SPARQL::Algebra::Operator.for(op_sym)
        if value.length > op.arity
          # Divide into the first operand followed by the operator re-applied to the reamining operands
          value = value.first, apply_op(op, value[1..-1])
        end
        return op.new(*value)
      end
    end

    if path['id']
      iri(path['id'])
    else
      log_error('PropertyPath', "Can't handle path", **options) {path.to_sxp}
    end
  else
    log_error('PropertyPath', "Can't handle path", **options) {path.to_sxp}
  end
end

.to_rdf(term, item, **options) ⇒ Object

Turn a JSON-LD value into its RDF representation

Parameters:

  • term (Symbol)
  • item (Object)

Returns:

  • RDF::Term

See Also:

  • JSON::LD::ToRDF.item_to_rdf


390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/shacl/algebra/operator.rb', line 390

def to_rdf(term, item, **options)
  @context ||= JSON::LD::Context.parse("http://github.com/ruby-rdf/shacl/")

  return item.map {|v| to_rdf(term, v, **options)} if item.is_a?(Array)

  case
  when item.is_a?(TrueClass) || item.is_a?(FalseClass) || item.is_a?(Numeric)
    return RDF::Literal(item)
  when value?(item)
    value, datatype = item.fetch('@value'), item.fetch('type', nil)
    case value
    when TrueClass, FalseClass, Numeric
      return RDF::Literal(value)
    else
      datatype ||= item.has_key?('@direction') ?
        RDF::URI("https://www.w3.org/ns/i18n##{item.fetch('@language', '').downcase}_#{item['@direction']}") :
        (item.has_key?('@language') ? RDF.langString : RDF::XSD.string)
    end
    datatype = iri(datatype) if datatype
            
    # Initialize literal as an RDF literal using value and datatype. If element has the key @language and datatype is xsd:string, then add the value associated with the @language key as the language of the object.
    language = item.fetch('@language', nil) if datatype == RDF.langString
    return RDF::Literal.new(value, datatype: datatype, language: language)
  when node?(item)
    return iri(item, **options)
  when list?(item)
    RDF::List(*item['@list'].map {|v| to_rdf(term, v, **options)})
  when item.is_a?(String)
    RDF::Literal(item)
  else
    raise "Can't transform #{item.inspect} to RDF on property #{term}"
  end
end

Instance Method Details

#commentRDF::Literal

Any comment associated with this operator

Returns:

  • (RDF::Literal)


529
# File 'lib/shacl/algebra/operator.rb', line 529

def comment; @options[:comment]; end

#conforms(node, depth: 0, **options) ⇒ Array<ValidationResult>

Validates the specified node within graph, a list of ValidationResult.

A node conforms if it is not deactivated and all of its operands conform.

Parameters:

  • node (RDF::Term)
  • options (Hash{Symbol => Object})

Returns:

Raises:

  • (NotImplemented)


548
549
550
# File 'lib/shacl/algebra/operator.rb', line 548

def conforms(node, depth: 0, **options)
  raise NotImplemented
end

#deactivated?Boolean

Is this shape deactivated?

Returns:

  • (Boolean)


525
# File 'lib/shacl/algebra/operator.rb', line 525

def deactivated?; @options[:deactivated] == RDF::Literal::TRUE; end

#idRDF::Resource

The ID of this operator

Returns:

  • (RDF::Resource)


513
# File 'lib/shacl/algebra/operator.rb', line 513

def id; @options[:id]; end

#iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options) ⇒ RDF::Value

Create URIs

Parameters:

  • value (RDF::Value, String)
  • base (RDF::URI) (defaults to: RDF::Vocab::SHACL.to_uri)

    Base IRI used for resolving relative values (RDF::Vocab::SHACL.to_uri).

  • vocab (Boolean) (defaults to: true)

    resolve vocabulary relative to the builtin context.

  • options (Hash{Symbol => Object})

Returns:

  • (RDF::Value)


537
538
539
# File 'lib/shacl/algebra/operator.rb', line 537

def iri(value, base: RDF::Vocab::SHACL.to_uri, vocab: true, **options)
  self.class.iri(value, base: base, vocab: vocab, **options)
end

#labelRDF::Literal

Any label associated with this operator

Returns:

  • (RDF::Literal)


521
# File 'lib/shacl/algebra/operator.rb', line 521

def label; @options[:label]; end

#not_satisfied(focus:, shape:, component:, resultSeverity: RDF::Vocab::SHACL.Violation, path: nil, value: nil, details: nil, message: nil, **options) ⇒ Array<SHACL::ValidationResult>

Create a result that does not satisfies the shape.

Parameters:

  • focus (RDF::Term)
  • shape (RDF::Resource)
  • component (RDF::URI)
  • resultSeverity (RDF::URI) (defaults to: RDF::Vocab::SHACL.Violation)

    (RDF:::Vocab::SHACL.Violation)

  • path (Array<RDF::URI>) (defaults to: nil)

    (nil)

  • value (RDF::Term) (defaults to: nil)

    (nil)

  • details (RDF::Term) (defaults to: nil)

    (nil)

  • message (String) (defaults to: nil)

    (nil)

Returns:



591
592
593
594
595
# File 'lib/shacl/algebra/operator.rb', line 591

def not_satisfied(focus:, shape:, component:, resultSeverity: RDF::Vocab::SHACL.Violation, path: nil, value: nil, details: nil, message: nil, **options)
  log_info(self.class.const_get(:NAME), "not satisfied #{value.to_sxp if value}#{': ' + message if message}", **options)
  [SHACL::ValidationResult.new(focus, path, shape, resultSeverity, component,
                               details, value, message)]
end

#satisfy(focus:, shape:, component:, resultSeverity: nil, path: nil, value: nil, details: nil, message: nil, **options) ⇒ Array<SHACL::ValidationResult>

Create a result that satisfies the shape.

Parameters:

  • focus (RDF::Term)
  • shape (RDF::Resource)
  • component (RDF::URI)
  • resultSeverity (RDF::URI) (defaults to: nil)

    (nil)

  • path (Array<RDF::URI>) (defaults to: nil)

    (nil)

  • value (RDF::Term) (defaults to: nil)

    (nil)

  • details (RDF::Term) (defaults to: nil)

    (nil)

  • message (String) (defaults to: nil)

    (nil)

Returns:



573
574
575
576
577
# File 'lib/shacl/algebra/operator.rb', line 573

def satisfy(focus:, shape:, component:, resultSeverity: nil, path: nil, value: nil, details: nil, message: nil, **options)
  log_debug(self.class.const_get(:NAME), "#{'not ' if resultSeverity}satisfied #{value.to_sxp if value}#{': ' + message if message}", **options)
  [SHACL::ValidationResult.new(focus, path, shape, resultSeverity, component,
                               details, value, message)]
end

#to_sxp_binObject

Create structure for serializing this component/shape, beginning with its cononical name.



553
554
555
556
557
558
559
# File 'lib/shacl/algebra/operator.rb', line 553

def to_sxp_bin
  expressions = BUILTIN_KEYS.inject([self.class.const_get(:NAME)]) do |memo, sym|
    @options[sym] ? memo.push([sym, *@options[sym]]) : memo
  end + operands

  expressions.to_sxp_bin
end

#typeArray<RDF::URI>

The types associated with this operator

Returns:

  • (Array<RDF::URI>)


517
# File 'lib/shacl/algebra/operator.rb', line 517

def type; @options[:type]; end