File: table.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (141 lines) | stat: -rw-r--r-- 4,206 bytes parent folder | download
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# frozen_string_literal: true
module GraphQL
  class Backtrace
    # A class for turning a context into a human-readable table or array
    class Table
      MIN_COL_WIDTH = 4
      MAX_COL_WIDTH = 100
      HEADERS = [
        "Loc",
        "Field",
        "Object",
        "Arguments",
        "Result",
      ]

      def initialize(context, value:)
        @context = context
        @override_value = value
      end

      # @return [String] A table layout of backtrace with metadata
      def to_table
        @to_table ||= render_table(rows)
      end

      # @return [Array<String>] An array of position + field name entries
      def to_backtrace
        @to_backtrace ||= begin
          backtrace = rows.map { |r| "#{r[0]}: #{r[1]}" }
          # skip the header entry
          backtrace.shift
          backtrace
        end
      end

      private

      def rows
        @rows ||= build_rows(@context, rows: [HEADERS], top: true)
      end

      # @return [String]
      def render_table(rows)
        max = Array.new(HEADERS.length, MIN_COL_WIDTH)

        rows.each do |row|
          row.each_with_index do |col, idx|
            col_len = col.length
            max_len = max[idx]
            if col_len > max_len
              if col_len > MAX_COL_WIDTH
                max[idx] = MAX_COL_WIDTH
              else
                max[idx] = col_len
              end
            end
          end
        end

        table = "".dup
        last_col_idx = max.length - 1
        rows.each do |row|
          table << row.map.each_with_index do |col, idx|
            max_len = max[idx]
            if idx < last_col_idx
              col = col.ljust(max_len)
            end
            if col.length > max_len
              col = col[0, max_len - 3] + "..."
            end
            col
          end.join(" | ")
          table << "\n"
        end
        table
      end

      # @return [Array] 5 items for a backtrace table (not `key`)
      def build_rows(context_entry, rows:, top: false)
        case context_entry
        when Backtrace::Frame
          field_alias = context_entry.ast_node.respond_to?(:alias) && context_entry.ast_node.alias
          value = if top && @override_value
            @override_value
          else
            value_at(@context.query.context.namespace(:interpreter_runtime)[:runtime], context_entry.path)
          end
          rows << [
            "#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
            "#{context_entry.field.path}#{field_alias ? " as #{field_alias}" : ""}",
            "#{context_entry.object.object.inspect}",
            context_entry.arguments.to_h.inspect, # rubocop:disable Development/ContextIsPassedCop -- unrelated method
            Backtrace::InspectResult.inspect_result(value),
          ]
          if (parent = context_entry.parent_frame)
            build_rows(parent, rows: rows)
          else
            rows
          end
        when GraphQL::Query::Context
          query = context_entry.query
          op = query.selected_operation
          if op
            op_type = op.operation_type
            position = "#{op.line}:#{op.col}"
          else
            op_type = "query"
            position = "?:?"
          end
          op_name = query.selected_operation_name
          object = query.root_value
          if object.is_a?(GraphQL::Schema::Object)
            object = object.object
          end
          value = value_at(context_entry.namespace(:interpreter_runtime)[:runtime], [])
          rows << [
            "#{position}",
            "#{op_type}#{op_name ? " #{op_name}" : ""}",
            "#{object.inspect}",
            query.variables.to_h.inspect,
            Backtrace::InspectResult.inspect_result(value),
          ]
        else
          raise "Unexpected get_rows subject #{context_entry.class} (#{context_entry.inspect})"
        end
      end

      def value_at(runtime, path)
        response = runtime.final_result
        path.each do |key|
          if response && (response = response[key])
            next
          else
            break
          end
        end
        response
      end
    end
  end
end