• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ruby-rdf / rdf-rdfa / 11317674118

13 Oct 2024 09:03PM UTC coverage: 90.142%. Remained the same
11317674118

push

github

gkellogg
Remove duplicate datatype in writer lang option

1079 of 1197 relevant lines covered (90.14%)

1322.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.45
/lib/rdf/rdfa/expansion.rb
1
require 'rdf/aggregate_repo'
2✔
2

3
module RDF::RDFa
2✔
4
  ##
5
  # The Expansion module performs a subset of OWL entailment rules on the base class,
6
  # which implementes RDF::Readable.
7
  module Expansion
2✔
8

9
    ##
10
    # Perform vocabulary expansion on the resulting default graph.
11
    #
12
    #   Vocabulary expansion uses the built-in reasoner using included vocabularies from RDF.rb.
13
    #
14
    # @param [RDF::Repository] repository
15
    # @see [OWL2 PROFILES](https://www.w3.org/TR/2009/REC-owl2-profiles-20091027/#Reasoning_in_OWL_2_RL_and_RDF_Graphs_using_Rules)
16
    def expand(repository)
2✔
17
      add_debug("expand") {"Repository has #{repository.count} statements"}
54✔
18

19
      # Load missing vocabularies
20
      vocabs = repository.query({predicate: RDF::RDFA.usesVocabulary}).to_a.map(&:object)
34✔
21
      vocabs.map! do |vocab|
34✔
22
        begin
23
          # Create the name with a predictable name so that it is enumerated and can be found
24
          v = RDF::Vocabulary.find(vocab) || begin
18✔
25
            vg = RDF::Graph.load(vocab)
2✔
26
            RDF::Vocabulary.from_graph(vg, url: vocab, class_name: "D#{Digest::MD5.hexdigest vocab}") unless vg.empty?
2✔
27
          end
28
        rescue Exception => e
29
          # indicate the warning if the vocabulary fails to laod
30
          add_warning("expand", "Error loading vocabulary #{vocab}: #{e.message}", RDF::RDFA.UnresolvedVocabulary)
×
31
          nil
×
32
        end
33
      end.compact
34

35
      entailment(repository, vocabs)
34✔
36
      add_debug("expand") {"Repository now has #{repository.count} statements"}
54✔
37

38
    end
39

40
    ##
41
    # Perform property copying on the resulting default graph.
42
    #
43
    # For all objects of type rdfa:Pattern that are the target of an rdfa:copy property, load the IRI into a repository.
44
    #
45
    # Subsequently, remove reference rdfa:Pattern objects.
46
    #
47
    # @param [RDF::Repository] repository
48
    # @see [HTML+RDFa](https://www.w3.org/TR/rdfa-in-html/#rdfa-reference-folding)
49
    def copy_properties(repository)
2✔
50
      add_debug("expand") {"Repository has #{repository.size} statements"}
1,288✔
51
      fold(repository)
664✔
52
    end
53

54
    def rule(name, &block)
2✔
55
      Rule.new(name, block)
×
56
    end
57

58
    ##
59
    # An entailment rule
60
    #
61
    # Takes a list of antecedent patterns used to find solutions against a queryable
62
    # object. Yields each consequent with bindings from the solution
63
    class Rule
2✔
64
      # @!attribute [r] antecedents
65
      # @return [Array<RDF::Query::Pattern>]
66
      attr_reader :antecedents
2✔
67

68
      # @!attribute [r] consequents
69
      # @return [Array<RDF::Query::Pattern>]
70
      attr_reader :consequents
2✔
71

72
      # @!attribute [r] deletions
73
      # @return [Array<RDF::Query::Pattern>]
74
      attr_reader :deletions
2✔
75

76
      # @!attribute [r] name
77
      # @return [String]
78
      attr_reader :name
2✔
79

80
      ##
81
      # @example
82
      #   r = Rule.new("scm-spo") do
83
      #     antecedent :p1, RDF::RDFS.subPropertyOf, :p2
84
      #     antecedent :p2, RDF::RDFS.subPropertyOf, :p3
85
      #     consequent :p1, RDF::RDFS.subPropertyOf, :p3, "t-box"
86
      #   end
87
      #
88
      #   r.execute(queryable) {|statement| puts statement.inspect}
89
      #
90
      # @param [String] name
91
      def initialize(name, &block)
2✔
92
        @antecedents = []
16✔
93
        @consequents = []
16✔
94
        @name = name
16✔
95

96
        if block_given?
16✔
97
          case block.arity
16✔
98
            when 1 then block.call(self)
×
99
            else instance_eval(&block)
16✔
100
          end
101
        end
102
      end
103

104
      def antecedent(subject, prediate, object)
2✔
105
        antecedents << RDF::Query::Pattern.new(subject, prediate, object)
36✔
106
      end
107

108
      def consequent(subject, prediate, object)
2✔
109
        consequents << RDF::Query::Pattern.new(subject, prediate, object)
20✔
110
      end
111

112
      ##
113
      # Execute the rule against queryable, yielding each consequent with bindings
114
      #
115
      # @param [RDF::Queryable] queryable
116
      # @yield [statement]
117
      # @yieldparam [RDF::Statement] statement
118
      def execute(queryable)
2✔
119
        RDF::Query.new(antecedents).execute(queryable).each do |solution|
1,860✔
120
          nodes = {}
346✔
121
          consequents.each do |consequent|
346✔
122
            terms = {}
486✔
123
            [:subject, :predicate, :object].each do |r|
486✔
124
              terms[r] = case o = consequent.send(r)
1,458✔
125
              when RDF::Node            then nodes[o] ||= RDF::Node.new
×
126
              when RDF::Query::Variable then solution[o]
1,192✔
127
              else                           o
266✔
128
              end
129
            end
130

131
            yield RDF::Statement.from(terms)
486✔
132
          end
133
        end
134
      end
135
    end
136

137
  private
2✔
138

139
    RULES = [
140
      Rule.new("prp-spo1") do
2✔
141
        antecedent :p1, RDF::RDFS.subPropertyOf, :p2
2✔
142
        antecedent :x, :p1, :y
2✔
143
        consequent :x, :p2, :y
2✔
144
      end,
145
      Rule.new("prp-eqp1") do
146
        antecedent :p1, RDF::OWL.equivalentProperty, :p2
2✔
147
        antecedent :x, :p1, :y
2✔
148
        consequent :x, :p2, :y
2✔
149
      end,
150
      Rule.new("prp-eqp2") do
151
        antecedent :p1, RDF::OWL.equivalentProperty, :p2
2✔
152
        antecedent :x, :p2, :y
2✔
153
        consequent :x, :p1, :y
2✔
154
      end,
155
      Rule.new("cax-sco") do
156
        antecedent :c1, RDF::RDFS.subClassOf, :c2
2✔
157
        antecedent :x, RDF.type, :c1
2✔
158
        consequent :x, RDF.type, :c2
2✔
159
      end,
160
      Rule.new("cax-eqc1") do
161
        antecedent :c1, RDF::OWL.equivalentClass, :c2
2✔
162
        antecedent :x, RDF.type, :c1
2✔
163
        consequent :x, RDF.type, :c2
2✔
164
      end,
165
      Rule.new("cax-eqc2") do
166
        antecedent :c1, RDF::OWL.equivalentClass, :c2
2✔
167
        antecedent :x, RDF.type, :c2
2✔
168
        consequent :x, RDF.type, :c1
2✔
169
      end,
170
    ]
171

172
    FOLDING_RULES = [
173
      Rule.new("rdfa-ref") do
2✔
174
        antecedent :x, RDF::RDFA.copy, :PR
2✔
175
        antecedent :PR, RDF.type, RDF::RDFA.Pattern
2✔
176
        antecedent :PR, :p, :y
2✔
177
        consequent :x, :p, :y
2✔
178
      end,
179
    ]
180

181
    REMOVAL_RULES = [
182
      Rule.new("rdfa-ref-remove") do
2✔
183
        antecedent :x, RDF::RDFA.copy, :PR
2✔
184
        antecedent :PR, RDF.type, RDF::RDFA.Pattern
2✔
185
        antecedent :PR, :p, :y
2✔
186
        consequent :x, RDF::RDFA.copy, :PR
2✔
187
        consequent :x, RDF.type, RDF::RDFA.Pattern
2✔
188
        consequent :PR, :p, :y
2✔
189
      end,
190
    ]
191

192
    ##
193
    # Perform OWL entailment rules on repository
194
    # @param [RDF::Repository] repository
195
    # @return [RDF::Repository]
196
    def entailment(repository, vocabs)
2✔
197
      old_count = 0
50✔
198

199
      # Create an aggregate repo containing base repository and relevant entailment rules from the included vocabularies
200
      v_repo = RDF::Repository.new do |r|
50✔
201
        vocabs.each do |v|
50✔
202
          v.each_statement do |statement|
34✔
203
            r << statement if [
204
              RDF::OWL.equivalentProperty,
1,900✔
205
              RDF::OWL.equivalentClass,
206
              RDF::RDFS.subPropertyOf,
207
              RDF::RDFS.subClassOf
208
            ].include?(statement.predicate)
209
          end
210
        end
211
      end
212

213
      ag_repo = RDF::MergeGraph.new do
50✔
214
        source repository, false
50✔
215
        source v_repo, false
50✔
216
      end
217

218
      # Continue as long as new statements are added to repository
219
      while old_count < (count = repository.count)
186✔
220
        #add_debug("entailment") {"old: #{old_count} count: #{count}"}
221
        old_count = count
86✔
222
        to_add = []
86✔
223

224
        RULES.each do |rule|
86✔
225
          rule.execute(ag_repo) do |statement|
516✔
226
            #add_debug("entailment(#{rule.name})") {statement.inspect}
227
            to_add << statement
126✔
228
          end
229
        end
230

231
        repository.insert(*to_add)
86✔
232
      end
233
    end
234

235
    ##
236
    # Perform RDFa folding rules on repository
237
    # @param [RDF::Repository] repository
238
    def fold(repository)
2✔
239
      old_count = 0
664✔
240

241
      # Continue as long as new statements are added to repository
242
      while old_count < (count = repository.count)
2,008✔
243
        #add_debug("fold") {"old: #{old_count} count: #{count}"}
244
        old_count = count
680✔
245
        to_add = []
680✔
246

247
        FOLDING_RULES.each do |rule|
680✔
248
          rule.execute(repository) do |statement|
680✔
249
            #add_debug("fold(#{rule.name})") {statement.inspect}
250
            to_add << statement
150✔
251
          end
252
        end
253

254
        repository.insert(*to_add)
680✔
255
      end
256

257
      # Remove statements matched by removal rules
258
      to_remove = []
664✔
259
      REMOVAL_RULES.each do |rule|
664✔
260
        rule.execute(repository) do |statement|
664✔
261
          #add_debug("removal(#{rule.name})") {statement.inspect}
262
          to_remove << statement
210✔
263
        end
264
      end
265
      repository.delete(*to_remove)
664✔
266

267
      add_debug("fold", "final count: #{count}")
664✔
268
    end
269
  end
270
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc