tidy_json 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.