File: evaluator.rb

package info (click to toggle)
ruby-brandur-json-schema 0.19.1-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 376 kB
  • sloc: ruby: 3,764; makefile: 6
file content (80 lines) | stat: -rw-r--r-- 2,202 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
module JsonPointer
  # Evaluates a JSON pointer within a JSON document.
  #
  # Note that this class is designed to evaluate references across a plain JSON
  # data object _or_ an instance of `JsonSchema::Schema`, so the constructor's
  # `data` argument can be of either type.
  class Evaluator
    def initialize(data)
      @data = data
    end

    def evaluate(original_path)
      path = original_path

      # the leading # can either be included or not
      path = path[1..-1] if path[0] == "#"

      # special case on "" or presumably "#"
      if path.empty?
        return @data
      end

      if path[0] != "/"
        raise ArgumentError, %{Path must begin with a leading "/": #{original_path}.}
      end

      path_parts = split(path)
      evaluate_segment(@data, path_parts)
    end

    private

    def evaluate_segment(data, path_parts)
      if path_parts.empty?
        data
      elsif data == nil
        # spec doesn't define how to handle this, so we'll return `nil`
        nil
      else
        key = transform_key(path_parts.shift)
        if data.is_a?(Array)
          unless key =~ /^\d+$/
            raise ArgumentError, %{Key operating on an array must be a digit or "-": #{key}.}
          end
          evaluate_segment(data[key.to_i], path_parts)
        else
          evaluate_segment(data[key], path_parts)
        end
      end
    end

    # custom split method to account for blank segments
    def split(path)
      parts = []
      last_index = 0
      while index = path.index("/", last_index)
        if index == last_index
          parts << ""
        else
          parts << path[last_index...index]
        end
        last_index = index + 1
      end
      # and also get that last segment
      parts << path[last_index..-1]
      # it should begin with a blank segment from the leading "/"; kill that
      parts.shift
      parts
    end

    def transform_key(key)
      # ~ has special meaning to JSON pointer to allow keys containing "/", so
      # perform some transformations first as defined by the spec
      # first as defined by the spec
      key = key.gsub('~1', '/')
      key = key.gsub('~0', '~')
      key
    end
  end
end