Class: ShEx::Algebra::Schema

Inherits:
Operator show all
Defined in:
lib/shex/algebra/schema.rb

Constant Summary collapse

NAME =
:schema

Constants inherited from Operator

Operator::ARITY

Instance Attribute Summary collapse

Attributes inherited from Operator

#label, #logger, #operands, #options, #schema

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Operator

#base_uri, #closed?, #each_descendant, #eql?, #first_ancestor, #focus, #focus=, #inspect, iri, #iri, #json_type, #matched, #matched=, #message, #message=, #not_matched, #not_satisfied, #operand, #parent, #parent=, #satisfiable?, #satisfied, #satisfied=, #satisfy, #semact?, #semantic_actions, #serialize_value, #status, #structure_error, #to_h, #to_json, #to_sxp, #to_sxp_bin, #triple_expression?, #unmatched, #unmatched=, #unsatisfied, #unsatisfied=, #value, value

Constructor Details

#initialize(*operands) ⇒ Object #initialize(*operands, options) ⇒ Object

Initializes a new operator instance.

Overloads:

  • #initialize(*operands) ⇒ Object

    Parameters:

    • operands (Array<RDF::Term>)
  • #initialize(*operands, options) ⇒ Object

    Parameters:

    • operands (Array<RDF::Term>)
    • options (Hash{Symbol => Object})

      any additional options

    Options Hash (options):

    • :memoize (Boolean) — default: false

      whether to memoize results for particular operands

    • :label (RDF::Resource)

      Identifier of the operator

Raises:

  • (TypeError)

    if any operand is invalid



28
29
30
31
32
33
34
# File 'lib/shex/algebra/schema.rb', line 28

def initialize(*operands)
  super
  each_descendant do |op|
    # Set schema everywhere
    op.schema = self
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ShEx::Algebra::Operator

Instance Attribute Details

#extensionsHash{String => ShEx::Extension} (readonly)

Map of Semantic Action instances

Returns:



16
17
18
# File 'lib/shex/algebra/schema.rb', line 16

def extensions
  @extensions
end

#graphRDF::Queryable

Graph to validate

Returns:

  • (RDF::Queryable)


8
9
10
# File 'lib/shex/algebra/schema.rb', line 8

def graph
  @graph
end

#mapHash{RDF::Resource => RDF::Resource} (readonly)

Map of nodes to shapes

Returns:

  • (Hash{RDF::Resource => RDF::Resource})


12
13
14
# File 'lib/shex/algebra/schema.rb', line 12

def map
  @map
end

Class Method Details

.from_shexj(operator, options = {}) ⇒ Operator

Creates an operator instance from a parsed ShExJ representation

Returns:

Raises:

  • (ArgumentError)


22
23
24
25
# File 'lib/shex/algebra/schema.rb', line 22

def self.from_shexj(operator, options = {})
  raise ArgumentError unless operator.is_a?(Hash) && operator['type'] == "Schema"
  super
end

Instance Method Details

#enter_shape(label, node) {|shape,| ... } ⇒ Satisfiable

Indicate that a shape has been entered with a specific focus node. Any future attempt to enter the same shape with the same node raises an exception.

Parameters:

  • label (RDF::Resource)
  • node (RDF::Resource)

Yields:

  • :shape

Yield Parameters:

  • shape, (Satisfiable)

    or ‘nil` if shape already entered

Returns:



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/shex/algebra/schema.rb', line 139

def enter_shape(label, node, &block)
  shape = shapes.detect {|s| s.label == label}
  structure_error("No shape found for #{label}") unless shape
  @shapes_entered[label] ||= {}
  if @shapes_entered[label][node]
    block.call(false)
  else
    @shapes_entered[label][node] = self
    begin
      block.call(shape)
    ensure
      @shapes_entered[label].delete(node)
    end
  end
end

#execute(focus, graph, map, shapeExterns: [], depth: 0, **options) ⇒ Operand

Match on schema. Finds appropriate shape for node, and matches that shape.

Parameters:

  • focus (RDF::Term)
  • graph (RDF::Queryable)
  • map (Hash{RDF::Resource => RDF::Resource})
  • shapeExterns (Array<Schema, String>) (defaults to: [])

    ([]) One or more schemas, or paths to ShEx schema resources used for finding external shapes.

  • options (Hash{Symbol => Object})

Options Hash (**options):

  • :base_uri (String)

Returns:

  • (Operand)

    Returns operand graph annotated with satisfied and unsatisfied operations.

Raises:



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/shex/algebra/schema.rb', line 48

def execute(focus, graph, map, shapeExterns: [], depth: 0, **options)
  @graph, @shapes_entered = graph, {}
  @external_schemas = shapeExterns
  focus = value(focus)

  logger = options[:logger] || @options[:logger]
  each_descendant do |op|
    # Set logging everywhere
    op.logger = logger
  end

  # Initialize Extensions
  @extensions = {}
  each_descendant do |op|
    next unless op.is_a?(SemAct)
    name = op.operands.first.to_s
    if ext_class = ShEx::Extension.find(name)
      @extensions[name] ||= ext_class.new(schema: self, depth: depth, **options)
    end
  end

  # If `n` is a Blank Node, we won't find it through normal matching, find an equivalent node in the graph having the same label
  graph_focus = graph.enum_term.detect {|t| t.node? && t.id == focus.id} if focus.is_a?(RDF::Node)
  graph_focus ||= focus

  # Make sure they're URIs
  @map = (map || {}).inject({}) {|memo, (k,v)| memo.merge(value(k) => iri(v))}

  # First, evaluate semantic acts
  semantic_actions.all? do |op|
    op.satisfies?([], depth: depth + 1)
  end

  # Keep a new Schema, specifically for recording actions
  satisfied_schema = Schema.new
  # Next run any start expression
  if start
    satisfied_schema.operands << start.satisfies?(focus, depth: depth + 1)
  end

  # Add shape result(s)
  satisfied_shapes = {}
  satisfied_schema.operands << [:shapes, satisfied_shapes] unless shapes.empty?

  # Match against all shapes associated with the labels for focus
  Array(@map[focus]).each do |label|
    enter_shape(label, focus) do |shape|
      satisfied_shapes[label] = shape.satisfies?(graph_focus, depth: depth + 1)
    end
  end
  status "schema satisfied", depth: depth
  satisfied_schema
ensure
  # Close Semantic Action extensions
  @extensions.values.each {|ext| ext.close(schema: self, depth: depth, **options)}
end

#external_schemasArray<Schema>

Externally loaded schemas, lazily evaluated

Returns:



158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/shex/algebra/schema.rb', line 158

def external_schemas
  @external_schemas = Array(@external_schemas).map do |extern|
    schema = case extern
    when Schema then extern
    else
      status "Load extern #{extern}"
      ShEx.open(extern, logger: options[:logger])
    end
    schema.graph = graph
    schema
  end
end

#satisfies?(focus, graph, map, shapeExterns: [], **options) ⇒ Boolean

Match on schema. Finds appropriate shape for node, and matches that shape.

Parameters:

  • focus (RDF::Resource)
  • graph (RDF::Queryable)
  • map (Hash{RDF::Resource => RDF::Resource})
  • shapeExterns (Array<Schema, String>) (defaults to: [])

    ([]) One or more schemas, or paths to ShEx schema resources used for finding external shapes.

  • options (Hash{Symbol => Object})

Options Hash (**options):

  • :base_uri (String)

Returns:

  • (Boolean)


116
117
118
119
120
# File 'lib/shex/algebra/schema.rb', line 116

def satisfies?(focus, graph, map, shapeExterns: [], **options)
  execute(focus, graph, map, options.merge(shapeExterns: shapeExterns))
rescue ShEx::NotSatisfied
  false
end

#shapesArray<Operator>

Shapes as a hash

Returns:



125
126
127
128
129
130
# File 'lib/shex/algebra/schema.rb', line 125

def shapes
  @shapes ||= begin
    shapes = Array(operands.detect {|op| op.is_a?(Array) && op.first == :shapes})
    Array(shapes[1..-1])
  end
end

#startObject

Start action, if any



173
174
175
# File 'lib/shex/algebra/schema.rb', line 173

def start
  @start ||= operands.detect {|op| op.is_a?(Start)}
end

#validate!SPARQL::Algebra::Expression

Validate shapes, in addition to other operands

Returns:

  • (SPARQL::Algebra::Expression)

    ‘self`

Raises:

  • (ArgumentError)

    if the value is invalid



181
182
183
184
# File 'lib/shex/algebra/schema.rb', line 181

def validate!
  shapes.each {|op| op.validate! if op.respond_to?(:validate!)}
  super
end