Class: TidyJson::Formatter Private

Inherits:
Object show all
Defined in:
lib/tidy_json/formatter.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

A purpose-built JSON formatter.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Formatter

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Formatter.

Parameters:

  • opts (Hash) (defaults to: {})

    Formatting options.

Options Hash (opts):

  • :indent ([2,4,6,8,10,12]) — default: 2

    The number of spaces to indent each object member.

  • :space_before ([1..8]) — default: 0

    The number of spaces to put after property names.

  • :space ([1..8]) — default: 1

    The number of spaces to put before property values.

  • :object_nl (String) — default: "\n"

    A string of whitespace to delimit object members.

  • :array_nl (String) — default: "\n"

    A string of whitespace to delimit array elements.

  • :max_nesting (Numeric) — default: 100

    The maximum level of data structure nesting in the generated JSON. Disable depth checking by passing max_nesting: 0.

  • :escape_slash (Boolean) — default: false

    Whether or not a forward slash (/) should be escaped.

  • :ascii_only (Boolean) — default: false

    Whether or not only ASCII characters should be generated.

  • :allow_nan (Boolean) — default: false

    Whether or not to allow NaN, Infinity and -Infinity. If false, an exception is thrown if one of these values is encountered.

  • :sort (Boolean) — default: false

    Whether or not object members should be sorted by property name.

See Also:



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
# File 'lib/tidy_json/formatter.rb', line 40

def initialize(opts = {})
  # The number of times to reduce the left indent of a nested array's
  # opening bracket
  @left_bracket_offset = 0

  # True if printing a nested array
  @need_offset = false

  valid_indent = (2..12).step(2).include?(opts[:indent])
  valid_space_before = (1..8).include?(opts[:space_before])
  valid_space_after = (1..8).include?(opts[:space])
  # don't test for the more explicit :integer? method because it's defined
  # for floating point numbers also
  valid_depth = opts[:max_nesting] >= 0 \
                if opts[:max_nesting].respond_to?(:times)
  valid_newline = ->(str) { str.respond_to?(:strip) && str.strip.empty? }
  @format = {
    indent: "\s" * (valid_indent ? opts[:indent] : 2),
    space_before: "\s" * (valid_space_before ? opts[:space_before] : 0),
    space: "\s" * (valid_space_after ? opts[:space] : 1),
    object_nl: (valid_newline.call(opts[:object_nl]) ? opts[:object_nl] : "\n"),
    array_nl: (valid_newline.call(opts[:array_nl]) ? opts[:array_nl] : "\n"),
    max_nesting: valid_depth ? opts[:max_nesting] : 100,
    escape_slash: opts[:escape_slash] || false,
    ascii_only: opts[:ascii_only] || false,
    allow_nan: opts[:allow_nan] || false,
    sorted: opts[:sort] || false
  }
end

Instance Attribute Details

#formatHash (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the JSON format options specified by this Formatter instance.

Returns:

  • (Hash)

    the JSON format options specified by this Formatter instance.



11
12
13
# File 'lib/tidy_json/formatter.rb', line 11

def format
  @format
end

Instance Method Details

#format_node(node, obj) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the given node as pretty-printed JSON.

Parameters:

  • node (#to_s)

    A visible attribute of obj.

  • obj ({Object => #to_s}, <#to_s>)

    The enumerable object containing node.

Returns:

  • (String)

    A formatted string representation of node.



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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/tidy_json/formatter.rb', line 78

def format_node(node, obj)
  str = ''
  indent = @format[:indent]

  is_last = (obj.length <= 1) ||
            (obj.instance_of?(Array) &&
              (obj.size.pred == obj.rindex(node)))

  if node.instance_of?(Array)
    str << '['
    str << "\n" unless node.empty?

    # format array elements
    node.each do |elem|
      if elem.instance_of?(Hash)
        str << "#{indent * 2}{"
        str << "\n" unless elem.empty?

        elem.each_with_index do |inner_h, h_idx|
          str << "#{indent * 3}\"#{inner_h.first}\": "
          str << node_to_str(inner_h.last, 4)
          str << ',' unless h_idx == elem.to_a.length.pred
          str << "\n"
        end

        str << (indent * 2).to_s unless elem.empty?
        str << '}'

      # element a scalar, or a nested array
      else
        is_nested_array = elem.instance_of?(Array) &&
                          elem.any? { |e| e.instance_of?(Array) }
        if is_nested_array
          @left_bracket_offset = \
            elem.take_while { |e| e.instance_of?(Array) }.size
        end

        str << (indent * 2) << node_to_str(elem)
      end

      str << ",\n" unless node.index(elem) == node.length.pred
    end

    str << "\n#{indent}" unless node.empty?
    str << ']'
    str << ",\n" unless is_last

  elsif node.instance_of?(Hash)
    str << '{'
    str << "\n" unless node.empty?

    # format elements as key-value pairs
    node.each_with_index do |h, idx|
      # format values which are hashes themselves
      if h.last.instance_of?(Hash)
        key = "#{indent * 2}\"#{h.first || "<##{h.last.class.name.downcase}>"}\": "
        str << key << '{'
        str << "\n" unless h.last.empty?

        h.last.each_with_index do |inner_h, inner_h_idx|
          str << "#{indent * 3}\"#{inner_h.first}\": "
          str << node_to_str(inner_h.last, 4)
          str << ",\n" unless inner_h_idx == h.last.to_a.length.pred
        end

        str << "\n#{indent * 2}" unless h.last.empty?
        str << '}'

      # format scalar values
      else
        str << "#{indent * 2}\"#{h.first}\": " << node_to_str(h.last)
      end

      str << ",\n" unless idx == node.to_a.length.pred
    end

    str << "\n#{indent}" unless node.empty?
    str << '}'
    str << ',' unless is_last
    str << "\n"

  # scalars
  else
    str << node_to_str(node)
    str << ',' unless is_last
    str << "\n"
  end

  trim str.gsub(/(#{indent})+[\n\r]+/, '')
          .gsub(/\},+/, '},')
          .gsub(/\],+/, '],')
end

#node_to_str(node, tabs = 0) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a JSON-appropriate string representation of node.

Parameters:

  • node (#to_s)

    A visible attribute of a Ruby object.

  • tabs (Integer) (defaults to: 0)

    Tab width at which to start printing this node.

Returns:

  • (String)

    A formatted string representation of node.



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/tidy_json/formatter.rb', line 178

def node_to_str(node, tabs = 0)
  graft = ''
  tabs += 2 if tabs.zero?

  if @need_offset
    tabs -= 1
    @left_bracket_offset -= 1
  end

  indent = @format[:indent] * (tabs / 2)

  if node.nil? then graft << 'null'

  elsif node.instance_of?(Hash)

    format_node(node, node).scan(/.*$/) do |n|
      graft << "\n" << indent << n
    end

  elsif node.instance_of?(Array)
    @need_offset = @left_bracket_offset.positive?

    format_node(node, {}).scan(/.*$/) do |n|
      graft << "\n" << indent << n
    end

  elsif !node.instance_of?(String) then graft << node.to_s

  else graft << "\"#{node.gsub('"', '\\"')}\""
  end

  graft.strip
end

#trim(node) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Removes any trailing comma from serialized object members.

Parameters:

  • node (String)

    A serialized object member.

Returns:

  • (String)

    A copy of node without a trailing comma.



218
219
220
221
222
223
224
225
226
# File 'lib/tidy_json/formatter.rb', line 218

def trim(node)
  if (extra_comma = /(?<trail>,\s*[\]}]\s*)$/.match(node))
    node.sub(extra_comma[:trail],
             extra_comma[:trail]
             .slice(1, node.length.pred)
             .sub(/^\s/, "\n"))
  else node
  end
end