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

ruby-rdf / rdf / 5194667710

07 Jun 2023 12:30AM UTC coverage: 91.808% (+0.1%) from 91.682%
5194667710

push

github

gkellogg
More ruby warning fixes.

4886 of 5322 relevant lines covered (91.81%)

16981.21 hits per line

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

98.77
/lib/rdf/model/list.rb
1
# coding: utf-8
2
module RDF
2✔
3
  ##
4
  # An RDF list.
5
  #
6
  # @example Constructing a new list
7
  #   RDF::List[1, 2, 3]
8
  #
9
  # @since 0.2.3
10
  class RDF::List
2✔
11
    include RDF::Enumerable
2✔
12
    include RDF::Value
2✔
13
    include Comparable
2✔
14

15
    ##
16
    # Constructs a new list from the given `values`.
17
    #
18
    # The list will be identified by a new autogenerated blank node, and
19
    # backed by an initially empty in-memory graph.
20
    #
21
    # @example
22
    #     RDF::List[]
23
    #     RDF::List[*(1..10)]
24
    #     RDF::List[1, 2, 3]
25
    #     RDF::List["foo", "bar"]
26
    #     RDF::List["a", 1, "b", 2, "c", 3]
27
    #
28
    # @param  [Array<RDF::Term>] values
29
    # @return [RDF::List]
30
    def self.[](*values)
2✔
31
      self.new(subject: nil, graph: nil, values: values)
1,370✔
32
    end
33

34
    ##
35
    # Initializes a newly-constructed list.
36
    #
37
    # Instantiates a new list based at `subject`, which **should** be an RDF::Node. List may be initialized using passed `values`.
38
    #
39
    # If a `values` initializer is set with an empty list, `subject`
40
    # will be used as the first element in the list. Otherwise,
41
    # if the list is not empty, `subject` identifies the first element
42
    # of the list to which `values` are prepended yielding a new `subject`.
43
    # Otherwise, if there are no initial `values`, and `subject` does
44
    # not identify an existing list in `graph`, the list remains
45
    # identified by `subject`, but will be invalid.
46
    #
47
    # @example add constructed list to existing graph
48
    #     l = RDF::List(values: (1, 2, 3))
49
    #     g = RDF::Graph.new << l
50
    #     g.count # => l.count
51
    #
52
    # @example use a transaction for block initialization
53
    #     l = RDF::List(graph: graph, wrap_transaction: true) do |list|
54
    #       list << RDF::Literal(1)
55
    #       # list.graph.rollback will rollback all list changes within this block.
56
    #     end
57
    #     list.count #=> 1
58
    #
59
    # @param  [RDF::Resource]         subject (RDF.nil)
60
    #   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.
61
    # @param  [RDF::Graph]        graph (RDF::Graph.new)
62
    # @param  [Array<RDF::Term>]  values
63
    #   Any values which are not terms are coerced to `RDF::Literal`.
64
    # @param [Boolean] wrap_transaction (false)
65
    #   Wraps the callback in a transaction, and replaces the graph with that transaction for the duraction of the callback. This has the effect of allowing any list changes to be made atomically, or rolled back.
66
    # @yield  [list]
67
    # @yieldparam [RDF::List] list
68
    def initialize(subject: nil, graph: nil, values: nil, wrap_transaction: false, &block)
2✔
69
      @subject = subject || RDF.nil
2,478✔
70
      @graph   = graph   || RDF::Graph.new
2,478✔
71
      is_empty = @graph.query({subject: subject, predicate: RDF.first}).empty?
2,478✔
72

73
      if subject && is_empty
2,478✔
74
        # An empty list with explicit subject and value initializers
75
        @subject = RDF.nil
858✔
76
        first, *values = Array(values)
858✔
77
        if first || values.length > 0
858✔
78
          # Intantiate the list from values, and insert the first value using subject.
79
          values.reverse_each {|value| self.unshift(value)}
136✔
80
          @graph.insert RDF::Statement(subject, RDF.first, first || RDF.nil)
32✔
81
          @graph.insert RDF::Statement(subject, RDF.rest, @subject)
32✔
82
        end
83
        @subject = subject
858✔
84
      else
85
        # Otherwise, prepend any values, which resets @subject
86
        Array(values).reverse_each {|value| self.unshift(value)}
7,340✔
87
      end
88

89
      if block_given?
2,478✔
90
        if wrap_transaction
110✔
91
          old_graph = @graph
6✔
92
          begin
93
            Transaction.begin(@graph, graph_name: @graph.graph_name, mutable: @graph.mutable?) do |trans|
6✔
94
              @graph = trans
6✔
95
              case block.arity
6✔
96
                when 1 then block.call(self)
6✔
97
                else instance_eval(&block)
×
98
              end
99
              trans.execute if trans.mutated?
6✔
100
            end
101
          ensure
102
            @graph = old_graph
6✔
103
          end
104
        else
105
          case block.arity
104✔
106
            when 1 then block.call(self)
102✔
107
            else instance_eval(&block)
2✔
108
          end
109
        end
110
      end
111
    end
112

113
    UNSET = Object.new.freeze # @private
2✔
114

115
    # The canonical empty list.
116
    NIL = RDF::List.new(subject: RDF.nil).freeze
2✔
117

118
    ##
119
    # Is this a {RDF::List}?
120
    #
121
    # @return [Boolean]
122
    def list?
2✔
123
      true
104✔
124
    end
125

126
    ##
127
    # Validate the list ensuring that
128
    # * each node is referenced exactly once (except for the head, which may have no reference)
129
    # * rdf:rest values are all BNodes are nil
130
    # * each subject has exactly one value for `rdf:first` and
131
    #   `rdf:rest`.
132
    # * The value of `rdf:rest` must be either a BNode or `rdf:nil`.
133
    # * only the list head may have any other properties
134
    # @return [Boolean]
135
    def valid?
2✔
136
      li = subject
876✔
137
      list_nodes = []
876✔
138
      while li != RDF.nil do
876✔
139
        return false if list_nodes.include?(li)
932✔
140
        list_nodes << li
928✔
141
        rest = nil
928✔
142
        firsts = rests = 0
928✔
143
        @graph.query({subject: li}) do |st|
928✔
144
          return false unless st.subject.node?
682✔
145
          case st.predicate
678✔
146
          when RDF.first
147
            firsts += 1
242✔
148
          when RDF.rest
149
            rest = st.object
238✔
150
            return false unless rest.node? || rest == RDF.nil
238✔
151
            rests += 1
234✔
152
          when RDF.type
153
          else
154
            # It may have no other properties
155
            return false unless li == subject
134✔
156
          end
157
        end
158
        return false unless firsts == 1 && rests == 1
916✔
159
        li = rest
218✔
160
      end
161

162
      # All elements other than the head must be referenced exactly once
163
      return list_nodes.all? do |li|
162✔
164
        refs = @graph.query({object: li}).count
202✔
165
        case refs
202✔
166
        when 0 then li == subject
50✔
167
        when 1 then true
148✔
168
        else        false
4✔
169
        end
170
      end
171
    end
172

173
    # @!attribute [r] subject
174
    # @return [RDF::Resource] the subject term of this list.
175
    attr_reader :subject
2✔
176

177
    # @!attribute [r] graph
178
    # @return [RDF::Graph] the underlying graph storing the statements that constitute this list
179
    attr_reader :graph
2✔
180

181
    ##
182
    # @see RDF::Value#==
183
    def ==(other)
2✔
184
      return false if other.is_a?(RDF::Value) && !other.list?
118✔
185
      super
106✔
186
    end
187

188
    ##
189
    # Returns the set intersection of this list and `other`.
190
    #
191
    # The resulting list contains the elements common to both lists, with no
192
    # duplicates.
193
    #
194
    # @example
195
    #   RDF::List[1, 2] & RDF::List[1, 2]       #=> RDF::List[1, 2]
196
    #   RDF::List[1, 2] & RDF::List[2, 3]       #=> RDF::List[2]
197
    #   RDF::List[1, 2] & RDF::List[3, 4]       #=> RDF::List[]
198
    #
199
    # @param  [RDF::List] other
200
    # @return [RDF::List]
201
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-26
202
    def &(other)
2✔
203
      self.class.new(values: (to_a & other.to_a))
14✔
204
    end
205

206
    ##
207
    # Returns the set union of this list and `other`.
208
    #
209
    # The resulting list contains the elements from both lists, with no
210
    # duplicates.
211
    #
212
    # @example
213
    #   RDF::List[1, 2] | RDF::List[1, 2]       #=> RDF::List[1, 2]
214
    #   RDF::List[1, 2] | RDF::List[2, 3]       #=> RDF::List[1, 2, 3]
215
    #   RDF::List[1, 2] | RDF::List[3, 4]       #=> RDF::List[1, 2, 3, 4]
216
    #
217
    # @param  [RDF::List] other
218
    # @return [RDF::List]
219
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-7C
220
    def |(other)
2✔
221
      self.class.new(values: (to_a | other.to_a))
14✔
222
    end
223

224
    ##
225
    # Returns the concatenation of this list and `other`.
226
    #
227
    # @example
228
    #   RDF::List[1, 2] + RDF::List[3, 4]       #=> RDF::List[1, 2, 3, 4]
229
    #
230
    # @param  [RDF::List] other
231
    # @return [RDF::List]
232
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-2B
233
    def +(other)
2✔
234
      self.class.new(values: (to_a + other.to_a))
6✔
235
    end
236

237
    ##
238
    # Returns the difference between this list and `other`, removing any
239
    # elements that appear in both lists.
240
    #
241
    # @example
242
    #   RDF::List[1, 2, 2, 3] - RDF::List[2]    #=> RDF::List[1, 3]
243
    #
244
    # @param  [RDF::List] other
245
    # @return [RDF::List]
246
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-2D
247
    def -(other)
2✔
248
      self.class.new(values: (to_a - other.to_a))
6✔
249
    end
250

251
    ##
252
    # Returns either a repeated list or a string concatenation of the
253
    # elements in this list.
254
    #
255
    # @overload *(times)
256
    #   Returns a new list built of `times` repetitions of this list.
257
    #
258
    #   @example
259
    #     RDF::List[1, 2, 3] * 2                #=> RDF::List[1, 2, 3, 1, 2, 3]
260
    #
261
    #   @param  [Integer] times
262
    #   @return [RDF::List]
263
    #
264
    # @overload *(sep)
265
    #   Returns the string concatenation of the elements in this list
266
    #   separated by `sep`. Equivalent to `self.join(sep)`.
267
    #
268
    #   @example
269
    #     RDF::List[1, 2, 3] * ","              #=> "1,2,3"
270
    #
271
    #   @param  [String, #to_s] sep
272
    #   @return [RDF::List]
273
    #
274
    # @return [RDF::List]
275
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-2A
276
    def *(int_or_str)
2✔
277
      case int_or_str
10✔
278
        when Integer then self.class.new(values: (to_a * int_or_str))
6✔
279
        else join(int_or_str.to_s)
4✔
280
      end
281
    end
282

283
    ##
284
    # 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.
285
    #
286
    # If indices are greater than the current capacity of the array, the array grows automatically. Elements are inserted into the array at `start` if length is zero.
287
    #
288
    # Negative indices will count backward from the end of the array. For `start` and `range` cases the starting index is just before an element.
289
    #
290
    # An `IndexError` is raised if a negative index points past the beginning of the array.
291
    #
292
    # (see #unshift).
293
    #
294
    # @example
295
    #     a = RDF::List.new
296
    #     a[4] = "4";                 #=> [rdf:nil, rdf:nil, rdf:nil, rdf:nil, "4"]
297
    #     a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", rdf:nil, "4"]
298
    #     a[1..2] = [ 1, 2 ]          #=> ["a", 1, 2, rdf:nil, "4"]
299
    #     a[0, 2] = "?"               #=> ["?", 2, rdf:nil, "4"]
300
    #     a[0..2] = "A"               #=> ["A", "4"]
301
    #     a[-1]   = "Z"               #=> ["A", "Z"]
302
    #     a[1..-1] = nil              #=> ["A", rdf:nil]
303
    #     a[1..-1] = []               #=> ["A"]
304
    #     a[0, 0] = [ 1, 2 ]          #=> [1, 2, "A"]
305
    #     a[3, 0] = "B"               #=> [1, 2, "A", "B"]
306
    #
307
    # @overload []=(index, term)
308
    #   Replaces the element at `index` with `term`.
309
    #   @param [Integer] index
310
    #   @param [RDF::Term] term
311
    #     A non-RDF::Term is coerced to a Literal.
312
    #   @return [RDF::Term]
313
    #   @raise [IndexError]
314
    #
315
    # @overload []=(start, length, value)
316
    #   Replaces a subarray from the `start` index for `length` elements with `value`. Value is a {RDF::Term}, Array of {RDF::Term}, or {RDF::List}.
317
    #   @param [Integer] start
318
    #   @param [Integer] length
319
    #   @param [RDF::Term, Array<RDF::Term>, RDF::List] value
320
    #     A non-RDF::Term is coerced to a Literal.
321
    #   @return [RDF::Term, RDF::List]
322
    #   @raise [IndexError]
323
    #
324
    # @overload []=(range, value)
325
    #   Replaces a subarray from the `start` index for `length` elements with `value`. Value is a {RDF::Term}, Array of {RDF::Term}, or {RDF::List}.
326
    #   @param [Range] range
327
    #   @param [RDF::Term, Array<RDF::Term>, RDF::List] value
328
    #     A non-RDF::Term is coerced to a Literal.
329
    #   @return [RDF::Term, RDF::List]
330
    #   @raise [IndexError]
331
    # @since 1.1.15
332
    def []=(*args)
2✔
333
      start, length = 0, 0
38✔
334

335
      ary = self.to_a
38✔
336

337
      value = case args.last
38✔
338
      when Array then args.last
12✔
339
      when RDF::List then args.last.to_a
×
340
      else [args.last]
26✔
341
      end
342

343
      ret = case args.length
38✔
344
      when 3
345
        start, length = args[0], args[1]
16✔
346
        ary[start, length] = value
16✔
347
      when 2
348
        case args.first
18✔
349
        when Integer
350
          raise ArgumentError, "Index form of []= takes a single term" if args.last.is_a?(Array)
6✔
351
          ary[args.first] = args.last.is_a?(RDF::List) ? args.last.subject : args.last
6✔
352
        when Range
353
          ary[args.first] = value
10✔
354
        else
355
          raise ArgumentError, "Index form of must use an integer or range"
2✔
356
        end
357
      else
358
        raise ArgumentError, "List []= takes one or two index values"
4✔
359
      end
360

361
      # Clear the list and create a new list using the existing subject
362
      subject = @subject unless ary.empty? || @subject == RDF.nil
32✔
363
      self.clear
32✔
364
      new_list = RDF::List.new(subject: subject, graph: @graph, values: ary)
32✔
365
      @subject = new_list.subject
32✔
366
      ret # Returns inserted values
32✔
367
    end
368

369
    ##
370
    # Appends an element to the head of this list. Existing references are not updated, as the list subject changes as a side-effect.
371
    #
372
    # @example
373
    #   RDF::List[].unshift(1).unshift(2).unshift(3) #=> RDF::List[3, 2, 1]
374
    #
375
    # @param  [RDF::Term, Array<RDF::Term>, RDF::List] value
376
    #   A non-RDF::Term is coerced to a Literal
377
    # @return [RDF::List]
378
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-unshift
379
    #
380
    def unshift(value)
2✔
381
      value = normalize_value(value)
5,828✔
382

383
      new_subject, old_subject = RDF::Node.new, subject
5,828✔
384

385
      graph.insert([new_subject, RDF.first, value.is_a?(RDF::List) ? value.subject : value])
5,828✔
386
      graph.insert([new_subject, RDF.rest, old_subject])
5,828✔
387

388
      @subject = new_subject
5,828✔
389

390
      return self
5,828✔
391
    end
392

393
    ##
394
    # Removes and returns the element at the head of this list.
395
    #
396
    # @example
397
    #   RDF::List[1,2,3].shift              #=> 1
398
    #
399
    # @return [RDF::Term]
400
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-shift
401
    def shift
2✔
402
      return nil if empty?
154✔
403

404
      value = first
152✔
405
      old_subject, new_subject = subject, rest_subject
152✔
406
      graph.delete([old_subject, RDF.type, RDF.List])
152✔
407
      graph.delete([old_subject, RDF.first, value])
152✔
408
      graph.delete([old_subject, RDF.rest, new_subject])
152✔
409

410
      @subject = new_subject
152✔
411
      return value
152✔
412
    end
413

414
    ##
415
    # Empties this list
416
    #
417
    # @example
418
    #   RDF::List[1, 2, 2, 3].clear    #=> RDF::List[]
419
    #
420
    # @return [RDF::List]
421
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-clear
422
    def clear
2✔
423
      until empty?
34✔
424
        shift
148✔
425
      end
426
      return self
34✔
427
    end
428

429
    ##
430
    # Appends an element to the tail of this list.
431
    #
432
    # @example
433
    #   RDF::List[] << 1 << 2 << 3              #=> RDF::List[1, 2, 3]
434
    #
435
    # @param  [RDF::Term] value
436
    # @return [RDF::List]
437
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-3C-3C
438
    def <<(value)
2✔
439
      value = normalize_value(value)
122✔
440

441
      if empty?
122✔
442
        @subject = new_subject = RDF::Node.new
76✔
443
      else
444
        old_subject, new_subject = last_subject, RDF::Node.new
46✔
445
        graph.delete([old_subject, RDF.rest, RDF.nil])
46✔
446
        graph.insert([old_subject, RDF.rest, new_subject])
46✔
447
      end
448

449
      graph.insert([new_subject, RDF.first, value.is_a?(RDF::List) ? value.subject : value])
122✔
450
      graph.insert([new_subject, RDF.rest, RDF.nil])
122✔
451

452
      self
122✔
453
    end
454

455
    ##
456
    # Compares this list to `other` using eql? on each component.
457
    #
458
    # @example
459
    #   RDF::List[1, 2, 3].eql? RDF::List[1, 2, 3]  #=> true
460
    #   RDF::List[1, 2, 3].eql? [1, 2, 3]           #=> true
461
    #
462
    # @param  [RDF::List] other
463
    # @return [Integer]
464
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-3C-3D-3E
465
    def eql?(other)
2✔
466
      to_a.eql? Array(other)
4✔
467
    end
468

469
    ##
470
    # Compares this list to `other` for sorting purposes.
471
    #
472
    # @example
473
    #   RDF::List[1] <=> RDF::List[1]           #=> 0
474
    #   RDF::List[1] <=> RDF::List[2]           #=> -1
475
    #   RDF::List[2] <=> RDF::List[1]           #=> 1
476
    #
477
    # @param  [RDF::List] other
478
    # @return [Integer]
479
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-3C-3D-3E
480
    def <=>(other)
2✔
481
      to_a <=> Array(other)
108✔
482
    end
483

484
    ##
485
    # Returns `true` if this list is empty.
486
    #
487
    # @example
488
    #   RDF::List[].empty?                      #=> true
489
    #   RDF::List[1, 2, 3].empty?               #=> false
490
    #
491
    # @return [Boolean]
492
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-empty-3F
493
    def empty?
2✔
494
      graph.query({subject: subject, predicate: RDF.first}).empty?
476✔
495
    end
496

497
    ##
498
    # Returns the length of this list.
499
    #
500
    # @example
501
    #   RDF::List[].length                      #=> 0
502
    #   RDF::List[1, 2, 3].length               #=> 3
503
    #
504
    # @return [Integer]
505
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-length
506
    def length
2✔
507
      each.count
42✔
508
    end
509

510
    alias_method :size, :length
2✔
511

512
    ##
513
    # Returns the index of the first element equal to `value`, or `nil` if
514
    # no match was found.
515
    #
516
    # @example
517
    #   RDF::List['a', 'b', 'c'].index('a')     #=> 0
518
    #   RDF::List['a', 'b', 'c'].index('d')     #=> nil
519
    #
520
    # @param  [RDF::Term] value
521
    # @return [Integer]
522
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-index
523
    def index(value)
2✔
524
      each.with_index do |v, i|
6✔
525
        return i if v == value
28✔
526
      end
527
      return nil
528
    end
529

530
    ##
531
    # Returns a slice of a list.
532
    #
533
    # @example
534
    #     RDF::List[1, 2, 3].slice(0)    #=> RDF::Literal(1),
535
    #     RDF::List[1, 2, 3].slice(0, 2) #=> RDF::List[1, 2],
536
    #     RDF::List[1, 2, 3].slice(0..2) #=> RDF::List[1, 2, 3]
537
    #
538
    # @return [RDF::Term]
539
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-slice
540
    def slice(*args)
2✔
541
      case argc = args.size
34✔
542
        when 2 then slice_with_start_and_length(*args)
10✔
543
        when 1 then (arg = args.first).is_a?(Range) ? slice_with_range(arg) : at(arg)
22✔
544
        when 0 then raise ArgumentError, "wrong number of arguments (0 for 1)"
2✔
545
        else raise ArgumentError, "wrong number of arguments (#{argc} for 2)"
×
546
      end
547
    end
548
    alias :[] :slice
2✔
549

550
    ##
551
    # @private
552
    def slice_with_start_and_length(start, length)
2✔
553
      self.class.new(values: to_a.slice(start, length))
10✔
554
    end
555

556
    ##
557
    # @private
558
    def slice_with_range(range)
2✔
559
      self.class.new(values: to_a.slice(range))
6✔
560
    end
561

562
    protected :slice_with_start_and_length
2✔
563
    protected :slice_with_range
2✔
564

565
    ##
566
    # Returns element at `index` with default.
567
    #
568
    # @example
569
    #   RDF::List[1, 2, 3].fetch(0)             #=> RDF::Literal(1)
570
    #   RDF::List[1, 2, 3].fetch(4)             #=> IndexError
571
    #   RDF::List[1, 2, 3].fetch(4, nil)        #=> nil
572
    #   RDF::List[1, 2, 3].fetch(4) { |n| n*n } #=> 16
573
    #
574
    # @return [RDF::Term, nil]
575
    # @see    http://ruby-doc.org/core-1.9/classes/Array.html#M000420
576
    def fetch(index, default = UNSET)
2✔
577
      val = at(index)
26✔
578
      return val unless val.nil?
26✔
579

580
      case
581
        when block_given?         then yield index
20✔
582
        when !default.eql?(UNSET) then default
6✔
583
        else raise IndexError, "index #{index} not in the list #{self.inspect}"
2✔
584
      end
585
    end
586

587
    ##
588
    # Returns the element at `index`.
589
    #
590
    # @example
591
    #   RDF::List[1, 2, 3].at(0)                #=> 1
592
    #   RDF::List[1, 2, 3].at(4)                #=> nil
593
    #
594
    # @return [RDF::Term, nil]
595
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-at
596
    def at(index)
2✔
597
      each.with_index { |v, i| return v if i == index }
1,030✔
598
      return nil
599
    end
600

601
    alias_method :nth, :at
2✔
602

603
    ##
604
    # Returns the first element in this list.
605
    #
606
    # @example
607
    #   RDF::List[*(1..10)].first               #=> RDF::Literal(1)
608
    #
609
    # @return [RDF::Term]
610
    def first
2✔
611
      graph.first_object(subject: first_subject, predicate: RDF.first)
174✔
612
    end
613

614
    ##
615
    # Returns the second element in this list.
616
    #
617
    # @example
618
    #   RDF::List[*(1..10)].second              #=> RDF::Literal(2)
619
    #
620
    # @return [RDF::Term]
621
    def second
2✔
622
      at(1)
8✔
623
    end
624

625
    ##
626
    # Returns the third element in this list.
627
    #
628
    # @example
629
    #   RDF::List[*(1..10)].third               #=> RDF::Literal(4)
630
    #
631
    # @return [RDF::Term]
632
    def third
2✔
633
      at(2)
8✔
634
    end
635

636
    ##
637
    # Returns the fourth element in this list.
638
    #
639
    # @example
640
    #   RDF::List[*(1..10)].fourth              #=> RDF::Literal(4)
641
    #
642
    # @return [RDF::Term]
643
    def fourth
2✔
644
      at(3)
8✔
645
    end
646

647
    ##
648
    # Returns the fifth element in this list.
649
    #
650
    # @example
651
    #   RDF::List[*(1..10)].fifth               #=> RDF::Literal(5)
652
    #
653
    # @return [RDF::Term]
654
    def fifth
2✔
655
      at(4)
8✔
656
    end
657

658
    ##
659
    # Returns the sixth element in this list.
660
    #
661
    # @example
662
    #   RDF::List[*(1..10)].sixth               #=> RDF::Literal(6)
663
    #
664
    # @return [RDF::Term]
665
    def sixth
2✔
666
      at(5)
8✔
667
    end
668

669
    ##
670
    # Returns the seventh element in this list.
671
    #
672
    # @example
673
    #   RDF::List[*(1..10)].seventh             #=> RDF::Literal(7)
674
    #
675
    # @return [RDF::Term]
676
    def seventh
2✔
677
      at(6)
8✔
678
    end
679

680
    ##
681
    # Returns the eighth element in this list.
682
    #
683
    # @example
684
    #   RDF::List[*(1..10)].eighth              #=> RDF::Literal(8)
685
    #
686
    # @return [RDF::Term]
687
    def eighth
2✔
688
      at(7)
8✔
689
    end
690

691
    ##
692
    # Returns the ninth element in this list.
693
    #
694
    # @example
695
    #   RDF::List[*(1..10)].ninth               #=> RDF::Literal(9)
696
    #
697
    # @return [RDF::Term]
698
    def ninth
2✔
699
      at(8)
8✔
700
    end
701

702
    ##
703
    # Returns the tenth element in this list.
704
    #
705
    # @example
706
    #   RDF::List[*(1..10)].tenth               #=> RDF::Literal(10)
707
    #
708
    # @return [RDF::Term]
709
    def tenth
2✔
710
      at(9)
8✔
711
    end
712

713
    ##
714
    # Returns the last element in this list.
715
    #
716
    # @example
717
    #   RDF::List[*(1..10)].last                 #=> RDF::Literal(10)
718
    #
719
    # @return [RDF::Term]
720
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-last
721
    def last
2✔
722
      graph.first_object(subject: last_subject, predicate: RDF.first)
10✔
723
    end
724

725
    ##
726
    # Returns a list containing all but the first element of this list.
727
    #
728
    # @example
729
    #   RDF::List[1, 2, 3].rest                 #=> RDF::List[2, 3]
730
    #
731
    # @return [RDF::List]
732
    def rest
2✔
733
      (subject = rest_subject).eql?(RDF.nil) ? nil : self.class.new(subject: subject, graph: graph)
4✔
734
    end
735

736
    ##
737
    # Returns a list containing the last element of this list.
738
    #
739
    # @example
740
    #   RDF::List[1, 2, 3].tail                 #=> RDF::List[3]
741
    #
742
    # @return [RDF::List]
743
    def tail
2✔
744
      (subject = last_subject).eql?(RDF.nil) ? nil : self.class.new(subject: subject, graph: graph)
4✔
745
    end
746

747
    ##
748
    # Returns the first subject term constituting this list.
749
    #
750
    # This is equivalent to `subject`.
751
    #
752
    # @example
753
    #   RDF::List[1, 2, 3].first_subject        #=> RDF::Node(...)
754
    #
755
    # @return [RDF::Resource]
756
    def first_subject
2✔
757
      subject
178✔
758
    end
759

760
    ##
761
    # @example
762
    #   RDF::List[1, 2, 3].rest_subject         #=> RDF::Node(...)
763
    #
764
    # @return [RDF::Resource]
765
    def rest_subject
2✔
766
      graph.first_object(subject: subject, predicate: RDF.rest)
160✔
767
    end
768

769
    ##
770
    # Returns the last subject term constituting this list.
771
    #
772
    # @example
773
    #   RDF::List[1, 2, 3].last_subject         #=> RDF::Node(...)
774
    #
775
    # @return [RDF::Resource]
776
    def last_subject
2✔
777
      each_subject.to_a.last # TODO: optimize this
66✔
778
    end
779

780
    ##
781
    # Yields each subject term constituting this list.
782
    #
783
    # @example
784
    #   RDF::List[1, 2, 3].each_subject do |subject|
785
    #     puts subject.inspect
786
    #   end
787
    #
788
    # @return [Enumerator]
789
    # @see    RDF::Enumerable#each
790
    def each_subject
2✔
791
      return enum_subject unless block_given?
1,022✔
792

793
      subject = self.subject
948✔
794
      yield subject
948✔
795

796
      loop do
922✔
797
        rest = graph.first_object(subject: subject, predicate: RDF.rest)
3,506✔
798
        break if rest.nil? || rest.eql?(RDF.nil)
3,506✔
799
        yield subject = rest
2,694✔
800
      end
801
    end
802

803
    ##
804
    # Yields each element in this list.
805
    #
806
    # @example
807
    #   RDF::List[1, 2, 3].each do |value|
808
    #     puts value.inspect
809
    #   end
810
    #
811
    # @return [Enumerator]
812
    # @see    http://ruby-doc.org/core-1.9/classes/Enumerable.html
813
    def each
2✔
814
      return to_enum unless block_given?
1,464✔
815

816
      each_subject do |subject|
826✔
817
        if value = graph.first_object(subject: subject, predicate: RDF.first)
3,128✔
818
          yield value # FIXME
2,982✔
819
        end
820
      end
821
    end
822

823
    ##
824
    # Yields each statement constituting this list.
825
    #
826
    # @example
827
    #   RDF::List[1, 2, 3].each_statement do |statement|
828
    #     puts statement.inspect
829
    #   end
830
    #
831
    # @return [Enumerator]
832
    # @see    RDF::Enumerable#each_statement
833
    def each_statement(&block)
2✔
834
      return enum_statement unless block_given?
64✔
835

836
      each_subject do |subject|
50✔
837
        graph.query({subject: subject}, &block)
200✔
838
      end
839
    end
840
    alias_method :to_rdf, :each_statement
2✔
841

842
    ##
843
    # Returns a string created by converting each element of this list into
844
    # a string, separated by `sep`.
845
    #
846
    # @example
847
    #   RDF::List[1, 2, 3].join                 #=> "123"
848
    #   RDF::List[1, 2, 3].join(", ")           #=> "1, 2, 3"
849
    #
850
    # @param  [String] sep
851
    # @return [String]
852
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-join
853
    def join(sep = $,)
2✔
854
      map(&:to_s).join(sep)
50✔
855
    end
856

857
    ##
858
    # Returns the elements in this list in reversed order.
859
    #
860
    # @example
861
    #   RDF::List[1, 2, 3].reverse              #=> RDF::List[3, 2, 1]
862
    #
863
    # @return [RDF::List]
864
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-reverse
865
    def reverse
2✔
866
      self.class.new(values: to_a.reverse)
8✔
867
    end
868

869
    ##
870
    # Returns the elements in this list in sorted order.
871
    #
872
    # @example
873
    #   RDF::List[2, 3, 1].sort                 #=> RDF::List[1, 2, 3]
874
    #
875
    # @return [RDF::List]
876
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-sort
877
    def sort(&block)
2✔
878
      self.class.new(values: super)
10✔
879
    end
880

881
    ##
882
    # Returns the elements in this list in sorted order.
883
    #
884
    # @example
885
    #   RDF::List[2, 3, 1].sort_by(&:to_i)      #=> RDF::List[1, 2, 3]
886
    #
887
    # @return [RDF::List]
888
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-sort_by
889
    def sort_by(&block)
2✔
890
      self.class.new(values: super)
6✔
891
    end
892

893
    ##
894
    # Returns a new list with the duplicates in this list removed.
895
    #
896
    # @example
897
    #   RDF::List[1, 2, 2, 3].uniq              #=> RDF::List[1, 2, 3]
898
    #
899
    # @return [RDF::List]
900
    # @see    http://ruby-doc.org/core-2.2.2/Array.html#method-i-uniq
901
    def uniq
2✔
902
      self.class.new(values: to_a.uniq)
8✔
903
    end
904

905
    ##
906
    # Returns the elements in this list as an array.
907
    #
908
    # @example
909
    #   RDF::List[].to_a                        #=> []
910
    #   RDF::List[1, 2, 3].to_a                 #=> [RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]
911
    #
912
    # @return [Array]
913
    def to_a
2✔
914
      each.to_a
410✔
915
    end
916

917
    ##
918
    # Returns the elements in this list as a set.
919
    #
920
    # @example
921
    #   RDF::List[1, 2, 3].to_set               #=> Set[RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]
922
    #
923
    # @return [Set]
924
    def to_set
2✔
925
      require 'set' unless defined?(::Set)
14✔
926
      each.to_set
14✔
927
    end
928

929
    ##
930
    # Returns the subject of the list.
931
    #
932
    # @example
933
    #   RDF::List[].to_term                     #=> "RDF[:nil]"
934
    #   RDF::List[1, 2, 3].to_term              #=> "RDF::Node"
935
    #
936
    # @return [RDF::Resource]
937
    def to_term
2✔
938
      subject
20✔
939
    end
940

941
    ##
942
    # Returns a string representation of this list.
943
    #
944
    # @example
945
    #   RDF::List[].to_s                        #=> "RDF::List[]"
946
    #   RDF::List[1, 2, 3].to_s                 #=> "RDF::List[1, 2, 3]"
947
    #
948
    # @return [String]
949
    def to_s
2✔
950
      'RDF::List[' + join(', ') + ']'
12✔
951
    end
952

953
    ##
954
    # Returns a developer-friendly representation of this list.
955
    #
956
    # @example
957
    #   RDF::List[].inspect                     #=> "#<RDF::List(_:g2163790380)>"
958
    #
959
    # @return [String]
960
    def inspect
2✔
961
      if self.equal?(NIL)
26✔
962
        'RDF::List::NIL'
6✔
963
      else
964
        sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, join(', '))
20✔
965
      end
966
    end
967

968
    private
2✔
969

970
    ##
971
    # Normalizes `Array` to `RDF::List` and `nil` to `RDF.nil`.
972
    #
973
    # @param value [Object]
974
    # @return [RDF::Value, Object] normalized value
975
    def normalize_value(value)
2✔
976
      case value
5,950✔
977
        when nil         then RDF.nil
44✔
978
        when RDF::Value  then value
2,762✔
979
        when Array       then self.class.new(subject: nil, graph: graph, values: value)
4✔
980
        else value
3,140✔
981
      end
982
    end
983
  end
984
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