tidy_json 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 595bfb60293652c3aec843420229bfe1c55c84a8387c7f60e5b4d5a62f47a9df
4
- data.tar.gz: 5ac9762a852f6753ff1bb8c52a2eac9224a55455bd14c9cf817377138fe21233
3
+ metadata.gz: 63e286aa3b914d96b71dc8e32904e0ca0514deb1159bf9a8ef40978f7b2a2ea0
4
+ data.tar.gz: f7f30ca7fab2e95b93b4aa385d5e2a4752899b273a160e8a64151c68248fb0d5
5
5
  SHA512:
6
- metadata.gz: 4e1bbd8d6caf301d83b83793188a0d4d8620876ed7930a4d38103aea5e5e9b79106762bb1b4caf0b03828d8e4713a0e1703bebe17dd12551f6d094d389865df3
7
- data.tar.gz: dfac6e1f21f516cd8311d3777497c0f2ab1563d4303b719c5faf2b3b7250d16ac4e4ffd429fe6abde5b67b78908dc1c3f6651854269253b747cb3b1ef10399b7
6
+ metadata.gz: 7a55fad57a8ffd7d303bb19cbd2f89665faad9089650d7324809fb7c4f1553c27b7783d6e608d0c284b3e91617801ae100a391bb11d142d023d3f037914ed8a0
7
+ data.tar.gz: 612e14c4480664a0ddb64cc8caac22915a60ca3709e3b1f5cae974eae050333104c5066ba513f70c1c5686dbc096f8c2663853126d4df2bd74d62860d4375ee3
data/.yardopts CHANGED
@@ -1 +1,3 @@
1
- --private lib/*.rb lib/tidy_json/version.rb - README.md LICENSE
1
+ --exclude lib/tidy_json/dedication.rb
2
+ --private lib/**/*.rb
3
+ --files README.md,LICENSE
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019-2020 Robert Di Pardo
3
+ Copyright (c) 2019-2021 Robert Di Pardo
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tidy_json
2
2
 
3
- [![Travis CI][travis_build_status_badge]][travis_build_status] [![Circle CI][cci_build_status_badge]][cci_build_status] [![codecov][codecov_badge]][codecov_status] [![Gem Version][gem_version_badge]][gem_version]
3
+ ![Gem Version][gem_version_badge] ![Downloads][gem_downloads] [![Travis CI][travis_build_status_badge]][travis_build_status] [![Circle CI][cci_build_status_badge]][cci_build_status] [![codecov][codecov_badge]][codecov_status]
4
4
 
5
5
  A mixin providing (recursive) JSON serialization and pretty printing.
6
6
 
@@ -19,6 +19,13 @@ gem 'tidy_json'
19
19
  # ...
20
20
  ```
21
21
 
22
+ ### Formatting Options
23
+
24
+ As of version [0.3.0][], most of the same options accepted by [`JSON.generate`][]
25
+ can be passed to `#write_json`, `#to_tidy_json`, or `TidyJson.tidy`.
26
+
27
+ See [the docs][] for a current list of options and their default values.
28
+
22
29
  ### Example
23
30
 
24
31
  ```ruby
@@ -28,26 +35,26 @@ class Jsonable
28
35
  attr_reader :a, :b
29
36
  def initialize
30
37
  @a = { a: 'uno', f: ['I', 'II', 'III', ['i.', 'ii.', 'iii.', { 'ichi': "\u{4e00}", 'ni': "\u{4e8c}", 'san': "\u{4e09}", 'yon': "\u{56db}" }]], c: {}, b: 'dos', e: [[]] }
31
- @b = { z: { iv: 4, ii: 'duos', iii: 3, i: 'one' }, b: ['two', 3, '<abbr title="four">IV</abbr>'], a: 1, g: [{ none: [] }], f: %w[x y z] }
38
+ @b = { z: { iv: 4, ii: 'dos', iii: 3, i: 'uno' }, b: ['deux', 3, '<abbr title="four">IV</abbr>'], a: 1, g: [{ none: [] }], f: %w[x y z] }
32
39
  end
33
40
  end
34
41
 
35
42
  my_jsonable = Jsonable.new
36
- # => #<Jsonable:0x0055b2aa0ff660 @a={:a=>"uno", :f=>["I", "II", "III", ["i.", "ii.", "iii.", {:ichi=>"一", :ni=>"二", :san=>"三", :yon=>"四"}]], :c=>{}, :b=>"dos", :e=>[[]]}, @b={:z=>{:iv=>4, :ii=>"duos", :iii=>3, :i=>"one"}, :b=>["two", 3, "<abbr title=\"four\">IV</abbr>"], :a=>1, :g=>[{:none=>[]}], :f=>["x", "y", "z"]}>
43
+ # => #<Jsonable:0x000055790c93e768 @a={:a=>"uno", :f=>["I", "II", "III", ["i.", "ii.", "iii.", {:ichi=>"一", :ni=>"二", :san=>"三", :yon=>"四"}]], :c=>{}, :b=>"dos", :e=>[[]]}, @b={:z=>{:iv=>4, :ii=>"dos", :iii=>3, :i=>"uno"}, :b=>["deux", 3, "<abbr title=\"four\">IV</abbr>"], :a=>1, :g=>[{:none=>[]}], :f=>["x", "y", "z"]}>
37
44
 
38
45
  JSON.parse my_jsonable.stringify
39
- # => {"class"=>"Jsonable", "a"=>{"a"=>"uno", "f"=>["I", "II", "III", ["i.", "ii.", "iii.", {"ichi"=>"", "ni"=>"", "san"=>"", "yon"=>""}]], "c"=>{}, "b"=>"dos", "e"=>[[]]}, "b"=>{"z"=>{"iv"=>4, "ii"=>"duos", "iii"=>3, "i"=>"one"}, "b"=>["two", 3, "<abbr title=\"four\">IV</abbr>"], "a"=>1, "g"=>[{"none"=>[]}], "f"=>["x", "y", "z"]}}
46
+ # => "{\"class\":\"Jsonable\",\"a\":{\"a\":\"uno\",\"f\":[\"I\",\"II\",\"III\",[\"i.\",\"ii.\",\"iii.\",{\"ichi\":\"一\",\"ni\":\"二\",\"san\":\"三\",\"yon\":\"四\"}]],\"c\":{},\"b\":\"dos\",\"e\":[[]]},\"b\":{\"z\":{\"iv\":4,\"ii\":\"dos\",\"iii\":3,\"i\":\"uno\"},\"b\":[\"deux\",3,\"<abbr title=\\\"four\\\">IV</abbr>\"],\"a\":1,\"g\":[{\"none\":[]}],\"f\":[\"x\",\"y\",\"z\"]}}"
40
47
 
41
- puts my_jsonable.to_tidy_json(indent: 4, sort: true)
48
+ puts my_jsonable.to_tidy_json(indent: 4, sort: true, space_before: 2, ascii_only: true)
42
49
  # {
43
- # "a": {
44
- # "a": "uno",
45
- # "b": "dos",
46
- # "c": {},
47
- # "e": [
50
+ # "a" : {
51
+ # "a" : "uno",
52
+ # "b" : "dos",
53
+ # "c" : {},
54
+ # "e" : [
48
55
  # []
49
56
  # ],
50
- # "f": [
57
+ # "f" : [
51
58
  # "I",
52
59
  # "II",
53
60
  # "III",
@@ -56,62 +63,58 @@ puts my_jsonable.to_tidy_json(indent: 4, sort: true)
56
63
  # "ii.",
57
64
  # "iii.",
58
65
  # {
59
- # "ichi": "",
60
- # "ni": "",
61
- # "san": "",
62
- # "yon": ""
66
+ # "ichi" : "\u4e00",
67
+ # "ni" : "\u4e8c",
68
+ # "san" : "\u4e09",
69
+ # "yon" : "\u56db"
63
70
  # }
64
71
  # ]
65
72
  # ]
66
73
  # },
67
- # "b": {
68
- # "a": 1,
69
- # "b": [
70
- # "two",
74
+ # "b" : {
75
+ # "a" : 1,
76
+ # "b" : [
77
+ # "deux",
71
78
  # 3,
72
79
  # "<abbr title=\"four\">IV</abbr>"
73
80
  # ],
74
- # "f": [
81
+ # "f" : [
75
82
  # "x",
76
83
  # "y",
77
84
  # "z"
78
85
  # ],
79
- # "g": [
86
+ # "g" : [
80
87
  # {
81
- # "none": []
88
+ # "none" : []
82
89
  # }
83
90
  # ],
84
- # "z": {
85
- # "i": "one",
86
- # "ii": "duos",
87
- # "iii": 3,
88
- # "iv": 4
91
+ # "z" : {
92
+ # "i" : "uno",
93
+ # "ii" : "dos",
94
+ # "iii" : 3,
95
+ # "iv" : 4
89
96
  # }
90
97
  # },
91
- # "class": "Jsonable"
98
+ # "class" : "Jsonable"
92
99
  # }
93
100
  # => nil
94
101
  ```
95
102
 
96
- ### Dependencies
97
-
98
- #### Runtime
99
- - [json](https://rubygems.org/gems/json) ~> 2.2
100
-
101
- #### Building
102
- - [test-unit](https://rubygems.org/gems/test-unit) ~> 3.3
103
- - [yard](https://rubygems.org/gems/yard) ~> 0.9
104
-
105
103
  ### License
106
- [MIT](https://github.com/rdipardo/tidy_json/blob/master/LICENSE)
104
+ Distributed under the terms of the [MIT License][].
107
105
 
108
106
 
109
107
  [travis_build_status]: https://travis-ci.com/rdipardo/tidy_json
110
- [travis_build_status_badge]: https://travis-ci.com/rdipardo/tidy_json.svg?branch=master
111
108
  [cci_build_status]: https://circleci.com/gh/rdipardo/tidy_json/tree/master
112
109
  [cci_build_status_badge]: https://circleci.com/gh/rdipardo/tidy_json.svg?style=svg
113
- [codecov_status]: https://codecov.io/gh/rdipardo/tidy_json
114
- [codecov_badge]: https://codecov.io/gh/rdipardo/tidy_json/badge.svg
115
- [gem_version]: https://badge.fury.io/rb/tidy_json
116
- [gem_version_badge]: https://badge.fury.io/rb/tidy_json.svg
117
-
110
+ [travis_build_status_badge]: https://travis-ci.com/rdipardo/tidy_json.svg?branch=master
111
+ [codecov_status]: https://codecov.io/gh/rdipardo/tidy_json/branch/master
112
+ [codecov_badge]: https://codecov.io/gh/rdipardo/tidy_json/branch/master/graph/badge.svg
113
+ [gem_version_badge]: https://img.shields.io/gem/v/tidy_json?color=%234ec820&label=gem%20version&logo=ruby&logoColor=%23e9573f
114
+ [gem_downloads]: https://img.shields.io/gem/dt/tidy_json?logo=ruby&logoColor=%23e9573f
115
+ [MIT License]: https://github.com/rdipardo/tidy_json/blob/master/LICENSE
116
+
117
+ <!-- API spec -->
118
+ [`JSON.generate`]: https://github.com/flori/json/blob/d49c5de49e54a5ad3f6fcf587f98d63266ef9439/lib/json/pure/generator.rb#L111
119
+ [the docs]: https://rubydoc.org/github/rdipardo/tidy_json/TidyJson/Formatter#initialize-instance_method
120
+ [0.3.0]: https://github.com/rdipardo/tidy_json/releases/tag/v0.3.0
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  require 'json'
4
+ require_relative 'tidy_json/serializer'
5
+ require_relative 'tidy_json/formatter'
4
6
  require_relative 'tidy_json/version'
5
7
 
6
8
  ##
@@ -16,31 +18,32 @@ module TidyJson
16
18
  # @return [String] A pretty-printed JSON string.
17
19
  def self.tidy(obj = {}, opts = {})
18
20
  formatter = Formatter.new(opts)
19
- obj = sort_keys(obj) if formatter.sorted
20
- str = ''
21
+ json = ''
21
22
 
22
- if obj.instance_of?(Hash)
23
- str << "{\n"
24
-
25
- obj.each do |k, v|
26
- str << formatter.indent << "\"#{k}\": "
27
- str << formatter.format_node(v, obj)
28
- end
29
-
30
- str << "}\n"
23
+ begin
24
+ if obj.instance_variables.empty?
25
+ obj = sort_keys(obj) if formatter.format[:sorted]
26
+ json = JSON.generate(obj, formatter.format)
27
+ else
28
+ str = "{\n"
29
+ obj = JSON.parse(obj.stringify)
30
+ obj = sort_keys(obj) if formatter.format[:sorted]
31
31
 
32
- elsif obj.instance_of?(Array)
33
- str << "[\n"
32
+ obj.each do |k, v|
33
+ str << formatter.format[:indent] << "\"#{k}\": "
34
+ str << formatter.format_node(v, obj)
35
+ end
34
36
 
35
- obj.each do |v|
36
- str << formatter.indent
37
- str << formatter.format_node(v, obj)
37
+ str << "}\n"
38
+ json = JSON.generate(JSON.parse(formatter.trim(str)), formatter.format)
38
39
  end
39
40
 
40
- str << "]\n"
41
+ json.gsub(/[\n\r]{2,}/, "\n")
42
+ .gsub(/\[\s+\]/, '[]')
43
+ .gsub(/{\s+}/, '{}') << "\n"
44
+ rescue JSON::JSONError => e
45
+ warn "#{__FILE__}.#{__LINE__}: #{e.message}"
41
46
  end
42
-
43
- formatter.trim str
44
47
  end
45
48
 
46
49
  ##
@@ -80,7 +83,7 @@ module TidyJson
80
83
  temp[idx] = sorter.call(h, {})
81
84
  end
82
85
 
83
- temp.keys.each { |k| sorted << temp[k] }
86
+ temp.each_key { |k| sorted << temp[k] }
84
87
  else
85
88
  sorted = sorter.call(obj, {})
86
89
  end
@@ -95,11 +98,7 @@ module TidyJson
95
98
  # @option (see Formatter#initialize)
96
99
  # @return [String] A pretty-printed JSON string.
97
100
  def to_tidy_json(opts = {})
98
- if instance_variables.empty?
99
- TidyJson.tidy(self, opts)
100
- else
101
- TidyJson.tidy(JSON.parse(stringify), opts)
102
- end
101
+ TidyJson.tidy(self, opts)
103
102
  end
104
103
 
105
104
  ##
@@ -144,356 +143,7 @@ module TidyJson
144
143
  rescue Errno::ENOENT, Errno::EACCES, IOError, RuntimeError, NoMethodError => e
145
144
  warn "#{__FILE__}.#{__LINE__}: #{e.message}"
146
145
  end
147
-
148
- ##
149
- # A purpose-built JSON generator.
150
- #
151
- # @api private
152
- class Serializer
153
- ##
154
- # Searches +obj+ to a maximum depth of 2 for readable attributes, storing
155
- # them as key-value pairs in +json_hash+.
156
- #
157
- # @param obj [Object] A Ruby object that can be parsed as JSON.
158
- # @param json_hash [{String,Symbol => #to_s}] Accumulator.
159
- # @return [{String => #to_s}] A hash mapping of +obj+'s visible attributes.
160
- def self.serialize(obj, json_hash)
161
- obj.instance_variables.each do |m|
162
- key = m.to_s[/[^\@]\w*/].to_sym
163
-
164
- next unless key && !key.eql?('')
165
-
166
- begin
167
- val = obj.send(key) # assuming readable attributes . . .
168
- rescue NoMethodError # . . . which may not be always be the case !
169
- json_hash[key] = nil
170
- end
171
-
172
- begin
173
- # process class members of Hash type
174
- if val.instance_of?(Hash)
175
- nested_key = ''
176
- nested = nil
177
-
178
- val.each.any? do |k, v|
179
- unless v.instance_variables.empty?
180
- nested_key = k
181
- nested = v
182
- end
183
- end
184
-
185
- json_hash[key] = val
186
-
187
- if nested
188
- pos = val.keys.select { |k| k === nested_key }.first.to_sym
189
- nested.instance_variables.each do
190
- json_hash[key][pos] = serialize(nested,
191
- class: nested.class.name)
192
- end
193
- end
194
-
195
- # process class members of Array type
196
- elsif val.instance_of?(Array)
197
- json_hash[key] = []
198
-
199
- val.each do |elem|
200
- i = val.index(elem)
201
-
202
- # member is a multi-dimensional collection
203
- if elem.respond_to?(:each)
204
- nested = []
205
- elem.each do |e|
206
- j = if elem.respond_to?(:key)
207
- elem.key(e)
208
- else elem.index(e)
209
- end
210
-
211
- # nested element is a class object
212
- if !e.instance_variables.empty?
213
- json_hash[key][j] = { class: e.class.name }
214
-
215
- # recur over the contained object
216
- serialize(e, json_hash[key][j])
217
-
218
- # some kind of collection?
219
- elsif e.respond_to?(:each)
220
- temp = []
221
- e.each do |el|
222
- temp << if el.instance_variables.empty? then el
223
- else JSON.parse(el.stringify)
224
- end
225
- end
226
-
227
- nested << temp
228
-
229
- # scalar type
230
- else nested << e
231
- end
232
- end
233
- # ~iteration of nested array elements
234
-
235
- json_hash[key] << nested
236
-
237
- # member is a flat array
238
- elsif !elem.instance_variables.empty? # class object?
239
- json_hash[key] << { class: elem.class.name }
240
- serialize(elem, json_hash[key][i])
241
-
242
- # scalar type
243
- else json_hash[key] << elem
244
- end
245
- end
246
- # ~iteration of top-level array elements
247
-
248
- # process any nested class members, i.e., handle a recursive call
249
- # to Serializer.serialize
250
- elsif obj.index(val) || json_hash.key?(key)
251
- if !val.instance_variables.empty?
252
- class_elem = { class: val.class.name }
253
- json_hash[key] << class_elem
254
- k = json_hash[key].index(class_elem)
255
- serialize(val, json_hash[key][k])
256
- else
257
- json_hash[key] << val
258
- end
259
-
260
- # process uncollected class members
261
- elsif !val.instance_variables.empty? # member is a class object
262
- json_hash[key] = { class: val.class.name }
263
- serialize(val, json_hash[key])
264
-
265
- # member belongs to a contained object
266
- elsif json_hash.key?(key) &&
267
- !json_hash[key].has_val?(val) &&
268
- json_hash[key].instance_of?(Hash)
269
-
270
- json_hash[key][key] = val
271
-
272
- # scalar member
273
- else json_hash[key] = val
274
- end
275
- rescue NoMethodError
276
- # we expected an array to behave like a hash, or vice-versa
277
- json_hash.store(key, val) # a shallow copy is better than nothing
278
- end
279
- end
280
- # ~iteration of instance variables
281
-
282
- json_hash
283
- end
284
- # ~Serializer.serialize
285
- end
286
- # ~Serializer
287
-
288
- ##
289
- # A purpose-built JSON formatter.
290
- #
291
- # @api private
292
- class Formatter
293
- attr_reader :indent, :sorted
294
- # @!attribute indent
295
- # @return [String] the string of white space used by this +Formatter+ to
296
- # indent object members.
297
-
298
- # @!attribute sorted
299
- # @return [Boolean] whether or not this +Formatter+ will sort object
300
- # members by key name.
301
-
302
- ##
303
- # @param opts [Hash] Formatting options.
304
- # @option opts [[2,4,6,8,10,12]] :indent (2) An even number of white spaces
305
- # to indent each object member.
306
- # @option opts [Boolean] :sort (false) Whether or not object members should
307
- # be sorted by key.
308
- def initialize(opts = {})
309
- # The number of times to reduce the left indent of a nested array's
310
- # opening bracket
311
- @left_bracket_offset = 0
312
-
313
- # True if printing a nested array
314
- @need_offset = false
315
-
316
- # don't test for the more explicit :integer? method because it's defined
317
- # for floating point numbers also
318
- valid_width = opts[:indent].positive? \
319
- if opts[:indent].respond_to?(:times) &&
320
- (2..12).step(2).include?(opts[:indent])
321
- @indent = "\s" * (valid_width ? opts[:indent] : 2)
322
- @sorted = opts[:sort] || false
323
- end
324
- # ~Formatter#initialize
325
-
326
- ##
327
- # Returns the given +node+ as pretty-printed JSON.
328
- #
329
- # @param node [#to_s] A visible attribute of +obj+.
330
- # @param obj [{Object => #to_s}, <#to_s>] The enumerable object
331
- # containing +node+.
332
- # @return [String] A formatted string representation of +node+.
333
- def format_node(node, obj)
334
- str = ''
335
- indent = @indent
336
-
337
- is_last = (obj.length <= 1) ||
338
- (obj.length > 1 &&
339
- (obj.instance_of?(Array) &&
340
- !(node === obj.first) &&
341
- (obj.size.pred == obj.rindex(node))))
342
-
343
- if node.instance_of?(Array)
344
- str << '['
345
- str << "\n" unless node.empty?
346
-
347
- # format array elements
348
- node.each do |elem|
349
- if elem.instance_of?(Hash)
350
- str << "#{indent * 2}{"
351
- str << "\n" unless elem.empty?
352
-
353
- elem.each_with_index do |inner_h, h_idx|
354
- str << "#{indent * 3}\"#{inner_h.first}\": "
355
- str << node_to_str(inner_h.last, 4)
356
- str << ', ' unless h_idx == elem.to_a.length.pred
357
- str << "\n"
358
- end
359
-
360
- str << (indent * 2).to_s unless elem.empty?
361
- str << '}'
362
- str << ',' unless node.index(elem) == node.length.pred
363
- str << "\n" unless node.index(elem) == node.length.pred
364
-
365
- # element a scalar, or a nested array
366
- else
367
- is_nested_array = elem.instance_of?(Array) &&
368
- elem.any? { |e| e.instance_of?(Array) }
369
- if is_nested_array
370
- @left_bracket_offset = \
371
- elem.take_while { |e| e.instance_of?(Array) }.size
372
- end
373
-
374
- str << (indent * 2) << node_to_str(elem)
375
- str << ",\n" unless node.index(elem) == node.length.pred
376
- end
377
- end
378
-
379
- str << "\n#{indent}" unless node.empty?
380
- str << "]\n"
381
-
382
- elsif node.instance_of?(Hash)
383
- str << '{'
384
- str << "\n" unless node.empty?
385
-
386
- # format elements as key-value pairs
387
- node.each_with_index do |h, idx|
388
- # format values which are hashes themselves
389
- if h.last.instance_of?(Hash)
390
- key = if h.first.eql? ''
391
- "#{indent * 2}\"<##{h.last.class.name.downcase}>\": "
392
- else
393
- "#{indent * 2}\"#{h.first}\": "
394
- end
395
-
396
- str << key << '{'
397
- str << "\n" unless h.last.empty?
398
-
399
- h.last.each_with_index do |inner_h, inner_h_idx|
400
- str << "#{indent * 3}\"#{inner_h.first}\": "
401
- str << node_to_str(inner_h.last, 4)
402
- str << ",\n" unless inner_h_idx == h.last.to_a.length.pred
403
- end
404
-
405
- str << "\n#{indent * 2}" unless h.last.empty?
406
- str << '}'
407
-
408
- # format scalar values
409
- else
410
- str << "#{indent * 2}\"#{h.first}\": " << node_to_str(h.last)
411
- end
412
-
413
- str << ",\n" unless idx == node.to_a.length.pred
414
- end
415
-
416
- str << "\n#{indent}" unless node.empty?
417
- str << '}'
418
- str << ', ' unless is_last
419
- str << "\n"
420
-
421
- # scalars
422
- else
423
- str << node_to_str(node)
424
- str << ', ' unless is_last
425
- str << "\n"
426
- end
427
-
428
- trim str.gsub(/(#{indent})+[\n\r]+/, '')
429
- .gsub(/\}\,+/, '},')
430
- .gsub(/\]\,+/, '],')
431
- end
432
- # ~Formatter#format_node
433
-
434
- ##
435
- # Returns a JSON-appropriate string representation of +node+.
436
- #
437
- # @param node [#to_s] A visible attribute of a Ruby object.
438
- # @param tabs [Integer] Tab width at which to start printing this node.
439
- # @return [String] A formatted string representation of +node+.
440
- def node_to_str(node, tabs = 0)
441
- graft = ''
442
- tabs += 2 if tabs.zero?
443
-
444
- if @need_offset
445
- tabs -= 1
446
- @left_bracket_offset -= 1
447
- end
448
-
449
- indent = @indent * (tabs / 2)
450
-
451
- if node.nil? then graft << 'null'
452
-
453
- elsif node.instance_of?(Hash)
454
-
455
- format_node(node, node).scan(/.*$/) do |n|
456
- graft << "\n" << indent << n
457
- end
458
-
459
- elsif node.instance_of?(Array)
460
- @need_offset = @left_bracket_offset.positive?
461
-
462
- format_node(node, {}).scan(/.*$/) do |n|
463
- graft << "\n" << indent << n
464
- end
465
-
466
- elsif !node.instance_of?(String) then graft << node.to_s
467
-
468
- else graft << "\"#{node.gsub(/\"/, '\\"')}\""
469
- end
470
-
471
- graft.strip
472
- end
473
- # ~Formatter#node_to_str
474
-
475
- ##
476
- # Removes any trailing comma from serialized object members.
477
- #
478
- # @param node [String] A serialized object member.
479
- # @return [String] A copy of +node+ without a trailing comma.
480
- def trim(node)
481
- if (extra_comma = /(?<trail>,\s*[\]\}])$/.match(node))
482
- node.sub(extra_comma[:trail],
483
- extra_comma[:trail]
484
- .slice(1, node.length.pred)
485
- .sub(/^\s/, ''))
486
- else node
487
- end
488
- end
489
- # ~Formatter#trim
490
- end
491
- # ~Formatter
492
-
493
- private_constant :Serializer
494
- private_constant :Formatter
495
146
  end
496
- # ~TidyJson
497
147
 
498
148
  ##
499
149
  # Includes +TidyJson+ in every Ruby class.