neatjson 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +6 -6
- data/README.md +106 -80
- data/lib/neatjson.rb +159 -159
- data/neatjson.gemspec +2 -2
- data/test/large.json +401 -401
- data/test/test_neatjson.rb +28 -28
- data/test/tests.js +183 -183
- data/test/tests.rb +10 -1
- metadata +4 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b3e65d59c946950e4f441db258d4c909547ff9f
|
4
|
+
data.tar.gz: 25c864d3de32d04982061b81625a67765cc8113b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17b3d9b7e5d0145c324f9fa50dbfdda2f5e44fcb5c18ff5ab3ecb1308648672019f9d4fe9aadb771eb68ce33535cb2ef0b95978fbf9e8426f2735a9a711309ae
|
7
|
+
data.tar.gz: ae0b881f1733c3e03afb6487cb92eeb1079d7a365e1b36d8a56a1593be3bdf4d3b2aa5e8b0037877d29e616161b52d8446dead59106a354356029d9e4e86a5f8
|
data/LICENSE.txt
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
Copyright (c) 2015-
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
-
|
5
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
-
|
1
|
+
Copyright (c) 2015-2018 Gavin Kistner
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
7
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -14,63 +14,72 @@ Pretty-print your JSON in Ruby or JavaScript with more power than is provided by
|
|
14
14
|
* "Short" wrapping uses fewer lines, indentation based on values. (See last example below.)
|
15
15
|
* Indent final closing bracket/brace for each array/object.
|
16
16
|
* Adjust number of spaces inside array/object braces.
|
17
|
-
* Adjust number of spaces before/after commas and colons (
|
17
|
+
* Adjust number of spaces before/after commas and colons (both for single- vs. multi-line).
|
18
18
|
* Line up the values for an object across lines.
|
19
19
|
* [Online webpage](http://phrogz.net/JS/NeatJSON) for conversions and experimenting with options.
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
{"text":"Create a route to Arby's","params":{"poi":"Arby's"}},
|
31
|
-
{
|
32
|
-
"text":"Go to the Hilton by the Airport",
|
33
|
-
"params":{"poi":"Hilton","location":"Airport"}
|
34
|
-
},
|
35
|
-
{
|
36
|
-
"text":"Take me to the Fry's in Fresno",
|
37
|
-
"params":{"poi":"Fry's","location":"Fresno"}
|
38
|
-
}
|
39
|
-
],
|
40
|
-
"navigation.eta":[
|
41
|
-
{"text":"When will we get there?"},
|
42
|
-
{"text":"When will I arrive?"},
|
43
|
-
{"text":"What time will I get to the destination?"},
|
44
|
-
{"text":"What time will I reach the destination?"},
|
45
|
-
{"text":"What time will it be when I arrive?"}
|
46
|
-
]
|
47
|
-
}
|
48
|
-
~~~
|
21
|
+
## Table of Contents
|
22
|
+
|
23
|
+
* [Installation](#installation)
|
24
|
+
* [Usage](#usage)
|
25
|
+
* [Examples](#examples)
|
26
|
+
* [Options](#options)
|
27
|
+
* [License & Contact](#license--contact)
|
28
|
+
* [TODO/Known Limitations](#todo-aka-known-limitations)
|
29
|
+
* [History](#history)
|
49
30
|
|
50
31
|
## Installation
|
51
32
|
|
52
33
|
* Ruby: `gem install neatjson`
|
53
|
-
* JavaScript: Clone the GitHub repo and copy `javascript/neatjson.js`
|
54
|
-
|
34
|
+
* JavaScript (web): Clone the GitHub repo and copy `javascript/neatjson.js`
|
35
|
+
* Node.js: `npm install neatjson`
|
36
|
+
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
**Ruby**:
|
41
|
+
|
42
|
+
~~~ ruby
|
43
|
+
require 'neatjson'
|
44
|
+
json = JSON.neat_generate( value, options )
|
45
|
+
~~~
|
46
|
+
|
47
|
+
**JavaScript (web)**:
|
48
|
+
|
49
|
+
~~~ html
|
50
|
+
<script type="text/javascript" src="neatjson.js"></script>
|
51
|
+
<script type="text/javascript">
|
52
|
+
var json = neatJSON( value, options );
|
53
|
+
</script>
|
54
|
+
~~~
|
55
|
+
|
56
|
+
|
57
|
+
**Node.js**:
|
58
|
+
|
59
|
+
~~~ js
|
60
|
+
const { neatJSON } = require('neatjson');
|
61
|
+
var json = neatJSON( value, options );
|
62
|
+
~~~
|
55
63
|
|
56
64
|
## Examples
|
57
65
|
|
66
|
+
_The following are all in Ruby, but similar options apply in JavaScript._
|
67
|
+
|
58
68
|
~~~ ruby
|
59
69
|
require 'neatjson'
|
60
70
|
|
61
|
-
o = { b:42.005, a:[42,17], longer:true, str:"yes
|
62
|
-
please" }
|
71
|
+
o = { b:42.005, a:[42,17], longer:true, str:"yes\nplease" }
|
63
72
|
|
64
73
|
puts JSON.neat_generate(o)
|
65
74
|
#=> {"b":42.005,"a":[42,17],"longer":true,"str":"yes\nplease"}
|
66
75
|
|
67
|
-
puts JSON.neat_generate(o,sort:true)
|
76
|
+
puts JSON.neat_generate(o, sort:true)
|
68
77
|
#=> {"a":[42,17],"b":42.005,"longer":true,"str":"yes\nplease"}
|
69
78
|
|
70
79
|
puts JSON.neat_generate(o,sort:true,padding:1,after_comma:1)
|
71
80
|
#=> { "a":[ 42, 17 ], "b":42.005, "longer":true, "str":"yes\nplease" }
|
72
81
|
|
73
|
-
puts JSON.neat_generate(o,sort:true,wrap:40)
|
82
|
+
puts JSON.neat_generate(o, sort:true, wrap:40)
|
74
83
|
#=> {
|
75
84
|
#=> "a":[42,17],
|
76
85
|
#=> "b":42.005,
|
@@ -78,7 +87,7 @@ puts JSON.neat_generate(o,sort:true,wrap:40)
|
|
78
87
|
#=> "str":"yes\nplease"
|
79
88
|
#=> }
|
80
89
|
|
81
|
-
puts JSON.neat_generate(o,sort:true,wrap:40,decimals:2)
|
90
|
+
puts JSON.neat_generate(o, sort:true, wrap:40, decimals:2)
|
82
91
|
#=> {
|
83
92
|
#=> "a":[42,17],
|
84
93
|
#=> "b":42.01,
|
@@ -86,7 +95,7 @@ puts JSON.neat_generate(o,sort:true,wrap:40,decimals:2)
|
|
86
95
|
#=> "str":"yes\nplease"
|
87
96
|
#=> }
|
88
97
|
|
89
|
-
puts JSON.neat_generate(o,sort:->(k){ k.length },wrap:40,aligned:true)
|
98
|
+
puts JSON.neat_generate(o, sort:->(k){ k.length }, wrap:40, aligned:true)
|
90
99
|
#=> {
|
91
100
|
#=> "a" :[42,17],
|
92
101
|
#=> "b" :42.005,
|
@@ -94,7 +103,7 @@ puts JSON.neat_generate(o,sort:->(k){ k.length },wrap:40,aligned:true)
|
|
94
103
|
#=> "longer":true
|
95
104
|
#=> }
|
96
105
|
|
97
|
-
puts JSON.neat_generate(o,sort:true,wrap:40,aligned:true,around_colon:1)
|
106
|
+
puts JSON.neat_generate(o, sort:true, wrap:40, aligned:true, around_colon:1)
|
98
107
|
#=> {
|
99
108
|
#=> "a" : [42,17],
|
100
109
|
#=> "b" : 42.005,
|
@@ -102,7 +111,7 @@ puts JSON.neat_generate(o,sort:true,wrap:40,aligned:true,around_colon:1)
|
|
102
111
|
#=> "str" : "yes\nplease"
|
103
112
|
#=> }
|
104
113
|
|
105
|
-
puts JSON.neat_generate(o,sort:true,wrap:40,aligned:true,around_colon:1,short:true)
|
114
|
+
puts JSON.neat_generate(o, sort:true, wrap:40, aligned:true, around_colon:1, short:true)
|
106
115
|
#=> {"a" : [42,17],
|
107
116
|
#=> "b" : 42.005,
|
108
117
|
#=> "longer" : true,
|
@@ -149,31 +158,42 @@ puts JSON.neat_generate( data, opts )
|
|
149
158
|
#=> "piggies" : { "color":"pink", "tasty":true } } ]
|
150
159
|
~~~
|
151
160
|
|
161
|
+
|
152
162
|
## Options
|
153
|
-
You may pass any of the following
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
163
|
+
You may pass any of the following options to `neat_generate` (Ruby) or `neatJSON` (JavaScript). **Note**: option names with underscores below use camelCase in JavaScript. For example:
|
164
|
+
|
165
|
+
~~~ ruby
|
166
|
+
# Ruby
|
167
|
+
json = JSON.neat_generate my_value, array_padding:1, after_comma:1, before_colon_n:2, indent_last:true
|
168
|
+
~~~
|
169
|
+
|
170
|
+
~~~ js
|
171
|
+
// JavaScript
|
172
|
+
var json = neatJSON( myValue, { arrayPadding:1, afterComma:1, beforeColonN:2, indentLast:true } );
|
173
|
+
~~~
|
174
|
+
|
175
|
+
* `wrap` — Maximum line width before wrapping. Use `false` to never wrap, `true` to always wrap. default:`80`
|
176
|
+
* `indent` — Whitespace used to indent each level when wrapping. default:`" "` (two spaces)
|
177
|
+
* `indent_last` — Indent the closing bracket/brace for arrays and objects? default:`false`
|
178
|
+
* `short` — Put opening brackets on the same line as the first value, closing brackets on the same line as the last? default:`false`
|
179
|
+
* _This causes the `indent` and `indent_last` options to be ignored, instead basing indentation on array and object padding._
|
180
|
+
* `sort` — Sort objects' keys in alphabetical order (`true`), or supply a lambda for custom sorting. default:`false`
|
161
181
|
* If you supply a lambda to the `sort` option, it will be passed three values: the (string) name of the key, the associated value, and the object being sorted, e.g. `{ sort:->(key,value,hash){ Float(value) rescue Float::MAX } }`
|
162
|
-
*
|
163
|
-
*
|
164
|
-
*
|
165
|
-
*
|
166
|
-
*
|
167
|
-
*
|
168
|
-
*
|
169
|
-
*
|
170
|
-
*
|
171
|
-
*
|
172
|
-
*
|
173
|
-
*
|
174
|
-
*
|
175
|
-
*
|
176
|
-
*
|
182
|
+
* `aligned` — When wrapping objects, line up the colons (per object)? default:`false`
|
183
|
+
* `decimals` — Decimal precision for non-integer numbers; use `false` to keep values precise. default:`false`
|
184
|
+
* `array_padding` — Number of spaces to put inside brackets for arrays. default:`0`
|
185
|
+
* `object_padding` — Number of spaces to put inside braces for objects. default:`0`
|
186
|
+
* `padding` — Shorthand to set both `array_padding` and `object_padding`. default:`0`
|
187
|
+
* `before_comma` — Number of spaces to put before commas (for both arrays and objects). default:`0`
|
188
|
+
* `after_comma` — Number of spaces to put after commas (for both arrays and objects). default:`0`
|
189
|
+
* `around_comma` — Shorthand to set both `before_comma` and `after_comma`. default:`0`
|
190
|
+
* `before_colon_1` — Number of spaces before a colon when the object is on one line. default:`0`
|
191
|
+
* `after_colon_1` — Number of spaces after a colon when the object is on one line. default:`0`
|
192
|
+
* `before_colon_n` — Number of spaces before a colon when the object is on multiple lines. default:`0`
|
193
|
+
* `after_colon_n` — Number of spaces after a colon when the object is on multiple lines. default:`0`
|
194
|
+
* `before_colon` — Shorthand to set both `before_colon_1` and `before_colon_n`. default:`0`
|
195
|
+
* `after_colon` — Shorthand to set both `after_colon_1` and `after_colon_n`. default:`0`
|
196
|
+
* `around_colon` — Shorthand to set both `before_colon` and `after_colon`. default:`0`
|
177
197
|
|
178
198
|
You may omit the 'value' and/or 'object' parameters in your `sort` lambda if desired. For example:
|
179
199
|
|
@@ -209,7 +229,7 @@ neatJSON( obj, { sort:function(k,v){ return -v }} ); // so
|
|
209
229
|
|
210
230
|
var countByValue = {};
|
211
231
|
for (var k in obj) countByValue[obj[k]] = (countByValue[obj[k]]||0) + 1;
|
212
|
-
neatJSON( obj, { sort:function(k,v
|
232
|
+
neatJSON( obj, { sort:function(k,v){ return countByValue[v] } } ); // sort by count of same value
|
213
233
|
// {"d":1,"a":2,"b":2,"e":3,"c":3,"f":3}
|
214
234
|
~~~
|
215
235
|
|
@@ -218,13 +238,14 @@ _Note that the JavaScript version of NeatJSON does not provide a mechanism for c
|
|
218
238
|
|
219
239
|
## License & Contact
|
220
240
|
|
221
|
-
NeatJSON is copyright ©2015–
|
241
|
+
NeatJSON is copyright ©2015–2017 by Gavin Kistner and is released under
|
222
242
|
the [MIT License](http://www.opensource.org/licenses/mit-license.php).
|
223
243
|
See the LICENSE.txt file for more details.
|
224
244
|
|
225
245
|
For bugs or feature requests please open [issues on GitHub][1].
|
226
246
|
For other communication you can [email the author directly](mailto:!@phrogz.net?subject=NeatJSON).
|
227
247
|
|
248
|
+
|
228
249
|
## TODO (aka Known Limitations)
|
229
250
|
|
230
251
|
* Figure out the best way to play with custom objects that use `to_json` for their representation.
|
@@ -232,66 +253,71 @@ For other communication you can [email the author directly](mailto:!@phrogz.net?
|
|
232
253
|
* Possibly allow illegal JSON values like `NaN` or `Infinity`.
|
233
254
|
* Possibly allow "JSON5" output (legal identifiers unquoted, etc.)
|
234
255
|
|
256
|
+
|
235
257
|
## HISTORY
|
236
258
|
|
259
|
+
* **v0.8.4** — May 3, 2018
|
260
|
+
* Fix issue #27: Default sorting fails with on objects with mixed keys [Ruby only]
|
261
|
+
* _Thanks Reid Beels_
|
262
|
+
|
237
263
|
* **v0.8.3** — February 20, 2017
|
238
264
|
* Fix issue #25: Sorting keys on multi-line object **using function** does not work without "short" [JS only]
|
239
265
|
* _Thanks Bernhard Weichel_
|
240
266
|
|
241
|
-
* **v0.8.2**
|
267
|
+
* **v0.8.2** — December 16th, 2016
|
242
268
|
* Fix issue #22: Sorting keys on multi-line object does not work without "short" [JS only]
|
243
269
|
* Update online interface to support tabs as well as spaces.
|
244
270
|
* Update online interface to use a textarea for the output (easier to select and copy).
|
245
271
|
* Update online interface turn off spell checking for input and output.
|
246
272
|
|
247
|
-
* **v0.8.1**
|
273
|
+
* **v0.8.1** — April 22nd, 2016
|
248
274
|
* Make NeatJSON work with [Opal](http://opalrb.org) (by removing all in-place string mutations)
|
249
275
|
|
250
|
-
* **v0.8**
|
276
|
+
* **v0.8** — April 21st, 2016
|
251
277
|
* Allow `sort` to take a lambda for customized sorting of object key/values.
|
252
278
|
|
253
|
-
* **v0.7.2**
|
279
|
+
* **v0.7.2** — April 14th, 2016
|
254
280
|
* Fix JavaScript library to support objects without an `Object` constructor (e.g. `location`).
|
255
281
|
* Online HTML converter accepts arbitrary JavaScript values as input in addition to JSON.
|
256
282
|
|
257
|
-
* **v0.7.1**
|
283
|
+
* **v0.7.1** — April 6th, 2016
|
258
284
|
* Fix Ruby library to work around bug in Opal.
|
259
285
|
|
260
|
-
* **v0.7**
|
286
|
+
* **v0.7** — March 26th, 2016
|
261
287
|
* Add `indentLast`/`indent_last` feature.
|
262
288
|
|
263
|
-
* **v0.6.2**
|
289
|
+
* **v0.6.2** — February 8th, 2016
|
264
290
|
* Use memoization to avoid performance stalls when wrapping deeply-nested objects/arrays.
|
265
291
|
_Thanks @chroche_
|
266
292
|
|
267
|
-
* **v0.6.1**
|
293
|
+
* **v0.6.1** — October 12th, 2015
|
268
294
|
* Fix handling of nested empty objects and arrays. (Would cause a runtime error in many cases.)
|
269
295
|
* _This change causes empty arrays in a tight wrapping scenario to appear on a single line where they would previously take up three lines._
|
270
296
|
|
271
|
-
* **v0.6**
|
297
|
+
* **v0.6** — April 26th, 2015
|
272
298
|
* Added `before_colon_1` and `before_colon_n` to distinguish between single-line and multi-line objects.
|
273
299
|
|
274
|
-
* **v0.5**
|
300
|
+
* **v0.5** — April 19th, 2015
|
275
301
|
* Do not format integers (or floats that equal their integer) using `decimals` option.
|
276
302
|
* Make `neatJSON()` JavaScript available to Node.js as well as web browsers.
|
277
303
|
* Add (Node-based) testing for the JavaScript version.
|
278
304
|
|
279
|
-
* **v0.4**
|
305
|
+
* **v0.4** — April 18th, 2015
|
280
306
|
* Add JavaScript version with online runner.
|
281
307
|
|
282
|
-
* **v0.3.2**
|
308
|
+
* **v0.3.2** — April 16th, 2015
|
283
309
|
* Force YARD to use Markdown for documentation.
|
284
310
|
|
285
|
-
* **v0.3.1**
|
311
|
+
* **v0.3.1** — April 16th, 2015
|
286
312
|
* Remove some debugging code accidentally left in.
|
287
313
|
|
288
|
-
* **v0.3**
|
314
|
+
* **v0.3** — April 16th, 2015
|
289
315
|
* Fix another bug with `short:true` and wrapping array values inside objects.
|
290
316
|
|
291
|
-
* **v0.2**
|
317
|
+
* **v0.2** — April 16th, 2015
|
292
318
|
* Fix bug with `short:true` and wrapping values inside objects.
|
293
319
|
|
294
|
-
* **v0.1**
|
320
|
+
* **v0.1** — April 15th, 2015
|
295
321
|
* Initial release.
|
296
322
|
|
297
323
|
[1]: https://github.com/Phrogz/NeatJSON/issues
|
data/lib/neatjson.rb
CHANGED
@@ -1,159 +1,159 @@
|
|
1
|
-
require 'json'
|
2
|
-
module JSON
|
3
|
-
# Generate the JSON string representation for an object,
|
4
|
-
# with a variety of formatting options.
|
5
|
-
#
|
6
|
-
# @author Gavin Kistner <!@phrogz.net>
|
7
|
-
# @param object [Object] the object to serialize
|
8
|
-
# @param opts [Hash] the formatting options
|
9
|
-
# @option opts [Integer] :wrap (80) The maximum line width before wrapping. Use `false` to never wrap, or `true` to always wrap.
|
10
|
-
# @option opts [String] :indent (" ") Whitespace used to indent each level when wrapping (without the :short option).
|
11
|
-
# @option opts [Boolean] :indent_last (false) Indent the closing bracket for arrays and objects (without the :short option).
|
12
|
-
# @option opts [Boolean] :short (false) Keep the output 'short' when wrapping, putting opening brackets on the same line as the first value, and closing brackets on the same line as the last item.
|
13
|
-
# @option opts [Boolean] :sort (false) Sort the keys for objects to be in alphabetical order (`true`), or supply a lambda to determine ordering.
|
14
|
-
# @option opts [Boolean] :aligned (false) When wrapping objects, align the colons (only per object).
|
15
|
-
# @option opts [Integer] :decimals (null) Decimal precision to use for floats; omit to keep numberic values precise.
|
16
|
-
# @option opts [Integer] :padding (0) Number of spaces to put inside brackets/braces for both arrays and objects.
|
17
|
-
# @option opts [Integer] :array_padding (0) Number of spaces to put inside brackets for arrays. Overrides `:padding`.
|
18
|
-
# @option opts [Integer] :object_padding (0) Number of spaces to put inside braces for objects. Overrides `:padding`.
|
19
|
-
# @option opts [Integer] :around_comma (0) Number of spaces to put before/after commas (for both arrays and objects).
|
20
|
-
# @option opts [Integer] :before_comma (0) Number of spaces to put before commas (for both arrays and objects).
|
21
|
-
# @option opts [Integer] :after_comma (0) Number of spaces to put after commas (for both arrays and objects).
|
22
|
-
# @option opts [Integer] :around_colon (0) Number of spaces to put before/after colons (for objects).
|
23
|
-
# @option opts [Integer] :before_colon (0) Number of spaces to put before colons (for objects).
|
24
|
-
# @option opts [Integer] :after_colon (0) Number of spaces to put after colons (for objects).
|
25
|
-
# @option opts [Integer] :around_colon_1 (0) Number of spaces to put before/after colons for single-line objects.
|
26
|
-
# @option opts [Integer] :before_colon_1 (0) Number of spaces to put before colons for single-line objects.
|
27
|
-
# @option opts [Integer] :after_colon_1 (0) Number of spaces to put after colons for single-line objects.
|
28
|
-
# @option opts [Integer] :aroun_n (0) Number of spaces to put before/after colons for multi-line objects.
|
29
|
-
# @option opts [Integer] :before_colon_n (0) Number of spaces to put before colons for multi-line objects.
|
30
|
-
# @option opts [Integer] :after_colon_n (0) Number of spaces to put after colons for multi-line objects.
|
31
|
-
# @return [String] the JSON representation of the object.
|
32
|
-
#
|
33
|
-
# The lambda for the `sort` option will be passed the string name of the key, the value, and the hash for the object being sorted.
|
34
|
-
# The values returned for all keys must be all comparable, or an error will occur.
|
35
|
-
def self.neat_generate(object,opts={})
|
36
|
-
opts ||= {}
|
37
|
-
opts[:wrap] = 80 unless opts.key?(:wrap)
|
38
|
-
opts[:wrap] = -1 if opts[:wrap]==true
|
39
|
-
opts[:indent] ||= " "
|
40
|
-
opts[:array_padding] ||= opts[:padding] || 0
|
41
|
-
opts[:object_padding] ||= opts[:padding] || 0
|
42
|
-
opts[:after_comma] ||= opts[:around_comma] || 0
|
43
|
-
opts[:before_comma] ||= opts[:around_comma] || 0
|
44
|
-
opts[:before_colon] ||= opts[:around_colon] || 0
|
45
|
-
opts[:after_colon] ||= opts[:around_colon] || 0
|
46
|
-
opts[:before_colon_1] ||= opts[:around_colon_1] || opts[:before_colon] || 0
|
47
|
-
opts[:after_colon_1] ||= opts[:around_colon_1] || opts[:after_colon] || 0
|
48
|
-
opts[:before_colon_n] ||= opts[:around_colon_n] || opts[:before_colon] || 0
|
49
|
-
opts[:after_colon_n] ||= opts[:around_colon_n] || opts[:after_colon] || 0
|
50
|
-
raise ":indent option must only be whitespace" if opts[:indent]=~/\S/
|
51
|
-
|
52
|
-
apad = " " * opts[:array_padding]
|
53
|
-
opad = " " * opts[:object_padding]
|
54
|
-
comma = "#{' '*opts[:before_comma]},#{' '*opts[:after_comma]}"
|
55
|
-
colon1= "#{' '*opts[:before_colon_1]}:#{' '*opts[:after_colon_1]}"
|
56
|
-
colonn= "#{' '*opts[:before_colon_n]}:#{' '*opts[:after_colon_n]}"
|
57
|
-
|
58
|
-
memoizer = {}
|
59
|
-
build = ->(o,indent) do
|
60
|
-
memoizer[[o,indent]] ||= case o
|
61
|
-
when String,Integer then "#{indent}#{o.inspect}"
|
62
|
-
when Symbol then "#{indent}#{o.to_s.inspect}"
|
63
|
-
when TrueClass,FalseClass then "#{indent}#{o}"
|
64
|
-
when NilClass then "#{indent}null"
|
65
|
-
when Float
|
66
|
-
if (o==o.to_i) && (o.to_s !~ /e/)
|
67
|
-
build[o.to_i,indent]
|
68
|
-
elsif opts[:decimals]
|
69
|
-
"#{indent}%.#{opts[:decimals]}f" % o
|
70
|
-
else
|
71
|
-
"#{indent}#{o}"
|
72
|
-
end
|
73
|
-
|
74
|
-
when Array
|
75
|
-
if o.empty?
|
76
|
-
"#{indent}[]"
|
77
|
-
else
|
78
|
-
pieces = o.map{ |v| build[v,''] }
|
79
|
-
one_line = "#{indent}[#{apad}#{pieces.join comma}#{apad}]"
|
80
|
-
if !opts[:wrap] || (one_line.length <= opts[:wrap])
|
81
|
-
one_line
|
82
|
-
elsif opts[:short]
|
83
|
-
indent2 = "#{indent} #{apad}"
|
84
|
-
pieces = o.map{ |v| build[ v,indent2 ] }
|
85
|
-
pieces[0] = pieces[0].sub indent2, "#{indent}[#{apad}"
|
86
|
-
pieces[pieces.length-1] = "#{pieces.last}#{apad}]"
|
87
|
-
pieces.join ",\n"
|
88
|
-
else
|
89
|
-
indent2 = "#{indent}#{opts[:indent]}"
|
90
|
-
"#{indent}[\n#{o.map{ |v| build[ v, indent2 ] }.join ",\n"}\n#{opts[:indent_last] ? indent2 : indent}]"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
when Hash
|
95
|
-
if o.empty?
|
96
|
-
"#{indent}{}"
|
97
|
-
else
|
98
|
-
case sort=(opts[:sorted] || opts[:sort])
|
99
|
-
when true then o = o.sort_by
|
100
|
-
when Proc
|
101
|
-
o = case sort.arity
|
102
|
-
when 1 then o.sort_by{ |k,v| sort[k] }
|
103
|
-
when 2 then o.sort_by{ |k,v| sort[k,v] }
|
104
|
-
when 3 then o.sort_by{ |k,v| sort[k,v,o] }
|
105
|
-
end
|
106
|
-
end
|
107
|
-
keyvals = o.map{ |k,v| [ k.to_s.inspect, build[v,''] ] }
|
108
|
-
keyvals = keyvals.map{ |kv| kv.join(colon1) }.join(comma)
|
109
|
-
one_line = "#{indent}{#{opad}#{keyvals}#{opad}}"
|
110
|
-
if !opts[:wrap] || (one_line.length <= opts[:wrap])
|
111
|
-
one_line
|
112
|
-
else
|
113
|
-
if opts[:short]
|
114
|
-
keyvals = o.map{ |k,v| ["#{indent} #{opad}#{k.to_s.inspect}",v] }
|
115
|
-
keyvals[0][0] = keyvals[0][0].sub "#{indent} ", "#{indent}{"
|
116
|
-
if opts[:aligned]
|
117
|
-
longest = keyvals.map(&:first).map(&:length).max
|
118
|
-
formatk = "%-#{longest}s"
|
119
|
-
keyvals.map!{ |k,v| [ formatk % k,v] }
|
120
|
-
end
|
121
|
-
keyvals.map! do |k,v|
|
122
|
-
indent2 = " "*"#{k}#{colonn}".length
|
123
|
-
one_line = "#{k}#{colonn}#{build[v,'']}"
|
124
|
-
if opts[:wrap] && (one_line.length > opts[:wrap]) && (v.is_a?(Array) || v.is_a?(Hash))
|
125
|
-
"#{k}#{colonn}#{build[v,indent2].lstrip}"
|
126
|
-
else
|
127
|
-
one_line
|
128
|
-
end
|
129
|
-
end
|
130
|
-
"#{keyvals.join(",\n")}#{opad}}"
|
131
|
-
else
|
132
|
-
keyvals = o.map{ |k,v| ["#{indent}#{opts[:indent]}#{k.to_s.inspect}",v] }
|
133
|
-
if opts[:aligned]
|
134
|
-
longest = keyvals.map(&:first).map(&:length).max
|
135
|
-
formatk = "%-#{longest}s"
|
136
|
-
keyvals.map!{ |k,v| [ formatk % k,v] }
|
137
|
-
end
|
138
|
-
indent2 = "#{indent}#{opts[:indent]}"
|
139
|
-
keyvals.map! do |k,v|
|
140
|
-
one_line = "#{k}#{colonn}#{build[v,'']}"
|
141
|
-
if opts[:wrap] && (one_line.length > opts[:wrap]) && (v.is_a?(Array) || v.is_a?(Hash))
|
142
|
-
"#{k}#{colonn}#{build[v,indent2].lstrip}"
|
143
|
-
else
|
144
|
-
one_line
|
145
|
-
end
|
146
|
-
end
|
147
|
-
"#{indent}{\n#{keyvals.join(",\n")}\n#{opts[:indent_last] ? indent2 : indent}}"
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
else
|
153
|
-
"#{indent}#{o.to_json(opts)}"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
build[object,'']
|
158
|
-
end
|
159
|
-
end
|
1
|
+
require 'json'
|
2
|
+
module JSON
|
3
|
+
# Generate the JSON string representation for an object,
|
4
|
+
# with a variety of formatting options.
|
5
|
+
#
|
6
|
+
# @author Gavin Kistner <!@phrogz.net>
|
7
|
+
# @param object [Object] the object to serialize
|
8
|
+
# @param opts [Hash] the formatting options
|
9
|
+
# @option opts [Integer] :wrap (80) The maximum line width before wrapping. Use `false` to never wrap, or `true` to always wrap.
|
10
|
+
# @option opts [String] :indent (" ") Whitespace used to indent each level when wrapping (without the :short option).
|
11
|
+
# @option opts [Boolean] :indent_last (false) Indent the closing bracket for arrays and objects (without the :short option).
|
12
|
+
# @option opts [Boolean] :short (false) Keep the output 'short' when wrapping, putting opening brackets on the same line as the first value, and closing brackets on the same line as the last item.
|
13
|
+
# @option opts [Boolean] :sort (false) Sort the keys for objects to be in alphabetical order (`true`), or supply a lambda to determine ordering.
|
14
|
+
# @option opts [Boolean] :aligned (false) When wrapping objects, align the colons (only per object).
|
15
|
+
# @option opts [Integer] :decimals (null) Decimal precision to use for floats; omit to keep numberic values precise.
|
16
|
+
# @option opts [Integer] :padding (0) Number of spaces to put inside brackets/braces for both arrays and objects.
|
17
|
+
# @option opts [Integer] :array_padding (0) Number of spaces to put inside brackets for arrays. Overrides `:padding`.
|
18
|
+
# @option opts [Integer] :object_padding (0) Number of spaces to put inside braces for objects. Overrides `:padding`.
|
19
|
+
# @option opts [Integer] :around_comma (0) Number of spaces to put before/after commas (for both arrays and objects).
|
20
|
+
# @option opts [Integer] :before_comma (0) Number of spaces to put before commas (for both arrays and objects).
|
21
|
+
# @option opts [Integer] :after_comma (0) Number of spaces to put after commas (for both arrays and objects).
|
22
|
+
# @option opts [Integer] :around_colon (0) Number of spaces to put before/after colons (for objects).
|
23
|
+
# @option opts [Integer] :before_colon (0) Number of spaces to put before colons (for objects).
|
24
|
+
# @option opts [Integer] :after_colon (0) Number of spaces to put after colons (for objects).
|
25
|
+
# @option opts [Integer] :around_colon_1 (0) Number of spaces to put before/after colons for single-line objects.
|
26
|
+
# @option opts [Integer] :before_colon_1 (0) Number of spaces to put before colons for single-line objects.
|
27
|
+
# @option opts [Integer] :after_colon_1 (0) Number of spaces to put after colons for single-line objects.
|
28
|
+
# @option opts [Integer] :aroun_n (0) Number of spaces to put before/after colons for multi-line objects.
|
29
|
+
# @option opts [Integer] :before_colon_n (0) Number of spaces to put before colons for multi-line objects.
|
30
|
+
# @option opts [Integer] :after_colon_n (0) Number of spaces to put after colons for multi-line objects.
|
31
|
+
# @return [String] the JSON representation of the object.
|
32
|
+
#
|
33
|
+
# The lambda for the `sort` option will be passed the string name of the key, the value, and the hash for the object being sorted.
|
34
|
+
# The values returned for all keys must be all comparable, or an error will occur.
|
35
|
+
def self.neat_generate(object,opts={})
|
36
|
+
opts ||= {}
|
37
|
+
opts[:wrap] = 80 unless opts.key?(:wrap)
|
38
|
+
opts[:wrap] = -1 if opts[:wrap]==true
|
39
|
+
opts[:indent] ||= " "
|
40
|
+
opts[:array_padding] ||= opts[:padding] || 0
|
41
|
+
opts[:object_padding] ||= opts[:padding] || 0
|
42
|
+
opts[:after_comma] ||= opts[:around_comma] || 0
|
43
|
+
opts[:before_comma] ||= opts[:around_comma] || 0
|
44
|
+
opts[:before_colon] ||= opts[:around_colon] || 0
|
45
|
+
opts[:after_colon] ||= opts[:around_colon] || 0
|
46
|
+
opts[:before_colon_1] ||= opts[:around_colon_1] || opts[:before_colon] || 0
|
47
|
+
opts[:after_colon_1] ||= opts[:around_colon_1] || opts[:after_colon] || 0
|
48
|
+
opts[:before_colon_n] ||= opts[:around_colon_n] || opts[:before_colon] || 0
|
49
|
+
opts[:after_colon_n] ||= opts[:around_colon_n] || opts[:after_colon] || 0
|
50
|
+
raise ":indent option must only be whitespace" if opts[:indent]=~/\S/
|
51
|
+
|
52
|
+
apad = " " * opts[:array_padding]
|
53
|
+
opad = " " * opts[:object_padding]
|
54
|
+
comma = "#{' '*opts[:before_comma]},#{' '*opts[:after_comma]}"
|
55
|
+
colon1= "#{' '*opts[:before_colon_1]}:#{' '*opts[:after_colon_1]}"
|
56
|
+
colonn= "#{' '*opts[:before_colon_n]}:#{' '*opts[:after_colon_n]}"
|
57
|
+
|
58
|
+
memoizer = {}
|
59
|
+
build = ->(o,indent) do
|
60
|
+
memoizer[[o,indent]] ||= case o
|
61
|
+
when String,Integer then "#{indent}#{o.inspect}"
|
62
|
+
when Symbol then "#{indent}#{o.to_s.inspect}"
|
63
|
+
when TrueClass,FalseClass then "#{indent}#{o}"
|
64
|
+
when NilClass then "#{indent}null"
|
65
|
+
when Float
|
66
|
+
if (o==o.to_i) && (o.to_s !~ /e/)
|
67
|
+
build[o.to_i,indent]
|
68
|
+
elsif opts[:decimals]
|
69
|
+
"#{indent}%.#{opts[:decimals]}f" % o
|
70
|
+
else
|
71
|
+
"#{indent}#{o}"
|
72
|
+
end
|
73
|
+
|
74
|
+
when Array
|
75
|
+
if o.empty?
|
76
|
+
"#{indent}[]"
|
77
|
+
else
|
78
|
+
pieces = o.map{ |v| build[v,''] }
|
79
|
+
one_line = "#{indent}[#{apad}#{pieces.join comma}#{apad}]"
|
80
|
+
if !opts[:wrap] || (one_line.length <= opts[:wrap])
|
81
|
+
one_line
|
82
|
+
elsif opts[:short]
|
83
|
+
indent2 = "#{indent} #{apad}"
|
84
|
+
pieces = o.map{ |v| build[ v,indent2 ] }
|
85
|
+
pieces[0] = pieces[0].sub indent2, "#{indent}[#{apad}"
|
86
|
+
pieces[pieces.length-1] = "#{pieces.last}#{apad}]"
|
87
|
+
pieces.join ",\n"
|
88
|
+
else
|
89
|
+
indent2 = "#{indent}#{opts[:indent]}"
|
90
|
+
"#{indent}[\n#{o.map{ |v| build[ v, indent2 ] }.join ",\n"}\n#{opts[:indent_last] ? indent2 : indent}]"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
when Hash
|
95
|
+
if o.empty?
|
96
|
+
"#{indent}{}"
|
97
|
+
else
|
98
|
+
case sort=(opts[:sorted] || opts[:sort])
|
99
|
+
when true then o = o.sort_by{|k,v| k.to_s }
|
100
|
+
when Proc
|
101
|
+
o = case sort.arity
|
102
|
+
when 1 then o.sort_by{ |k,v| sort[k] }
|
103
|
+
when 2 then o.sort_by{ |k,v| sort[k,v] }
|
104
|
+
when 3 then o.sort_by{ |k,v| sort[k,v,o] }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
keyvals = o.map{ |k,v| [ k.to_s.inspect, build[v,''] ] }
|
108
|
+
keyvals = keyvals.map{ |kv| kv.join(colon1) }.join(comma)
|
109
|
+
one_line = "#{indent}{#{opad}#{keyvals}#{opad}}"
|
110
|
+
if !opts[:wrap] || (one_line.length <= opts[:wrap])
|
111
|
+
one_line
|
112
|
+
else
|
113
|
+
if opts[:short]
|
114
|
+
keyvals = o.map{ |k,v| ["#{indent} #{opad}#{k.to_s.inspect}",v] }
|
115
|
+
keyvals[0][0] = keyvals[0][0].sub "#{indent} ", "#{indent}{"
|
116
|
+
if opts[:aligned]
|
117
|
+
longest = keyvals.map(&:first).map(&:length).max
|
118
|
+
formatk = "%-#{longest}s"
|
119
|
+
keyvals.map!{ |k,v| [ formatk % k,v] }
|
120
|
+
end
|
121
|
+
keyvals.map! do |k,v|
|
122
|
+
indent2 = " "*"#{k}#{colonn}".length
|
123
|
+
one_line = "#{k}#{colonn}#{build[v,'']}"
|
124
|
+
if opts[:wrap] && (one_line.length > opts[:wrap]) && (v.is_a?(Array) || v.is_a?(Hash))
|
125
|
+
"#{k}#{colonn}#{build[v,indent2].lstrip}"
|
126
|
+
else
|
127
|
+
one_line
|
128
|
+
end
|
129
|
+
end
|
130
|
+
"#{keyvals.join(",\n")}#{opad}}"
|
131
|
+
else
|
132
|
+
keyvals = o.map{ |k,v| ["#{indent}#{opts[:indent]}#{k.to_s.inspect}",v] }
|
133
|
+
if opts[:aligned]
|
134
|
+
longest = keyvals.map(&:first).map(&:length).max
|
135
|
+
formatk = "%-#{longest}s"
|
136
|
+
keyvals.map!{ |k,v| [ formatk % k,v] }
|
137
|
+
end
|
138
|
+
indent2 = "#{indent}#{opts[:indent]}"
|
139
|
+
keyvals.map! do |k,v|
|
140
|
+
one_line = "#{k}#{colonn}#{build[v,'']}"
|
141
|
+
if opts[:wrap] && (one_line.length > opts[:wrap]) && (v.is_a?(Array) || v.is_a?(Hash))
|
142
|
+
"#{k}#{colonn}#{build[v,indent2].lstrip}"
|
143
|
+
else
|
144
|
+
one_line
|
145
|
+
end
|
146
|
+
end
|
147
|
+
"#{indent}{\n#{keyvals.join(",\n")}\n#{opts[:indent_last] ? indent2 : indent}}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
else
|
153
|
+
"#{indent}#{o.to_json(opts)}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
build[object,'']
|
158
|
+
end
|
159
|
+
end
|