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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd71a0bca8dacc2bbb100e03944cce9dd2e9af90
4
- data.tar.gz: 02f77183e8e587d981c06c5c2c6fa2a999a08309
3
+ metadata.gz: 1b3e65d59c946950e4f441db258d4c909547ff9f
4
+ data.tar.gz: 25c864d3de32d04982061b81625a67765cc8113b
5
5
  SHA512:
6
- metadata.gz: 60b221078927b2628277e20215dafd4def6ab411c3204cb767da4e8b78e9b882bb9f9a0eb34d1464526e577db46bbd4239678821f370f69ccc7b33a2c01ddb4d
7
- data.tar.gz: 726176200c5fef15c51f42cda4fa880fc2e77655822628797ebdc7e5e64d1e0a29abbe04a000682c5277bb47c5efdc5d4479336247cacf9702d907c11da38f9a
6
+ metadata.gz: 17b3d9b7e5d0145c324f9fa50dbfdda2f5e44fcb5c18ff5ab3ecb1308648672019f9d4fe9aadb771eb68ce33535cb2ef0b95978fbf9e8426f2735a9a711309ae
7
+ data.tar.gz: ae0b881f1733c3e03afb6487cb92eeb1079d7a365e1b36d8a56a1593be3bdf4d3b2aa5e8b0037877d29e616161b52d8446dead59106a354356029d9e4e86a5f8
@@ -1,7 +1,7 @@
1
- Copyright (c) 2015-2016 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
-
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 (adjustable for single-line vs. multi-line )
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
- Here's a short example of output showing the power of proper wrapping:
22
-
23
- ~~~ json
24
- {
25
- "navigation.createroute.poi":[
26
- {"text":"Lay in a course to the Hilton","params":{"poi":"Hilton"}},
27
- {"text":"Take me to the airport","params":{"poi":"airport"}},
28
- {"text":"Let's go to IHOP","params":{"poi":"IHOP"}},
29
- {"text":"Show me how to get to The Med","params":{"poi":"The Med"}},
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
- * _Sorry, no NodeJS package yet._
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 option symbols to `neat_generate`:
154
-
155
- * `:wrap` — The maximum line width before wrapping. Use `false` to never wrap, or `true` (or `-1`) to always wrap. Default: `80`
156
- * `:indent` — Whitespace used to indent each level when wrapping. Default: `" "` (two spaces)
157
- * `:indent_last` — Indent the closing bracket/brace for arrays and objects? Default: `false`
158
- * `:short` — Keep the output 'short' when wrapping? This puts opening brackets on the same line as the first value, and closing brackets on the same line as the last. Default: `false`
159
- * _This causes the `:indent` and `:indent_last` options to be ignored, instead basing indentation on array and object padding._
160
- * `:sort` — Sort objects' keys in alphabetical order (`true`), or supply a lambda for custom sorting. Default: `false`
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
- * `:aligned` — When wrapping objects, line up the colons (per object)? Default: `false`
163
- * `:decimals` — Decimal precision to use for non-integer numbers; use `false` to keep float values precise. Default: `false`
164
- * `:array_padding` — Number of spaces to put inside brackets for arrays. Default: `0`
165
- * `:object_padding` — Number of spaces to put inside braces for objects. Default: `0`
166
- * `:padding` — Shorthand to set both `:array_padding` and `:object_padding`. Default: `0`
167
- * `:before_comma` — Number of spaces to put before commas (for both arrays and objects). Default: `0`
168
- * `:after_comma` — Number of spaces to put after commas (for both arrays and objects). Default: `0`
169
- * `:around_comma` — Shorthand to set both `:before_comma` and `:after_comma`. Default: `0`
170
- * `:before_colon_1` — Number of spaces before a colon when the object is on one line. Default: `0`
171
- * `:after_colon_1` — Number of spaces after a colon when the object is on one line. Default: `0`
172
- * `:before_colon_n` — Number of spaces before a colon when the object is on multiple lines. Default: `0`
173
- * `:after_colon_n` — Number of spaces after a colon when the object is on multiple lines. Default: `0`
174
- * `:before_colon` — Shorthand to set both `:before_colon_1` and `:before_colon_n`. Default: `0`
175
- * `:after_colon` — Shorthand to set both `:after_colon_1` and `:after_colon_n`. Default: `0`
176
- * `:around_colon` — Shorthand to set both `:before_colon` and `:after_colon`. Default: `0`
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,h){ return countByValue[v] } } ); // sort by count of same value
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–2016 by Gavin Kistner and is released under
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** - December 16th, 2016
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** - April 22nd, 2016
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** - April 21st, 2016
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** - April 14th, 2016
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** - April 6th, 2016
283
+ * **v0.7.1** April 6th, 2016
258
284
  * Fix Ruby library to work around bug in Opal.
259
285
 
260
- * **v0.7** - March 26th, 2016
286
+ * **v0.7** March 26th, 2016
261
287
  * Add `indentLast`/`indent_last` feature.
262
288
 
263
- * **v0.6.2** - February 8th, 2016
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** - October 12th, 2015
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** - April 26th, 2015
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** - April 19th, 2015
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** - April 18th, 2015
305
+ * **v0.4** April 18th, 2015
280
306
  * Add JavaScript version with online runner.
281
307
 
282
- * **v0.3.2** - April 16th, 2015
308
+ * **v0.3.2** April 16th, 2015
283
309
  * Force YARD to use Markdown for documentation.
284
310
 
285
- * **v0.3.1** - April 16th, 2015
311
+ * **v0.3.1** April 16th, 2015
286
312
  * Remove some debugging code accidentally left in.
287
313
 
288
- * **v0.3** - April 16th, 2015
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** - April 16th, 2015
317
+ * **v0.2** April 16th, 2015
292
318
  * Fix bug with `short:true` and wrapping values inside objects.
293
319
 
294
- * **v0.1** - April 15th, 2015
320
+ * **v0.1** April 15th, 2015
295
321
  * Initial release.
296
322
 
297
323
  [1]: https://github.com/Phrogz/NeatJSON/issues
@@ -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(&:first)
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