Module: JSON

Defined in:
lib/neatjson.rb

Class Method Summary collapse

Class Method Details

.neat_generate(object, opts = {}) ⇒ Object

The values returned for all keys must be all comparable, or an error will occur.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/neatjson.rb', line 35

def self.neat_generate(object,opts={})
	opts[:wrap] = 80 unless opts.key?(:wrap)
	opts[:wrap] = -1 if opts[:wrap]==true
	opts[:indent]         ||= "  "
	opts[:array_padding]  ||= opts[:padding]      || 0
	opts[:object_padding] ||= opts[:padding]      || 0
	opts[:after_comma]    ||= opts[:around_comma] || 0
	opts[:before_comma]   ||= opts[:around_comma] || 0
	opts[:before_colon]   ||= opts[:around_colon] || 0
	opts[:after_colon]    ||= opts[:around_colon] || 0
	opts[:before_colon_1] ||= opts[:around_colon_1] || opts[:before_colon] || 0
	opts[:after_colon_1]  ||= opts[:around_colon_1] || opts[:after_colon]  || 0
	opts[:before_colon_n] ||= opts[:around_colon_n] || opts[:before_colon] || 0
	opts[:after_colon_n]  ||= opts[:around_colon_n] || opts[:after_colon]  || 0
	raise ":indent option must only be whitespace" if opts[:indent]=~/\S/

	apad  = " " * opts[:array_padding]
	opad  = " " * opts[:object_padding]
	comma = "#{' '*opts[:before_comma]},#{' '*opts[:after_comma]}"
	colon1= "#{' '*opts[:before_colon_1]}:#{' '*opts[:after_colon_1]}"
	colonn= "#{' '*opts[:before_colon_n]}:#{' '*opts[:after_colon_n]}"

	memoizer = {}
	build = ->(o,indent) do
		memoizer[[o,indent]] ||= case o
			when String,Integer       then "#{indent}#{o.inspect}"
			when Symbol               then "#{indent}#{o.to_s.inspect}"
			when TrueClass,FalseClass then "#{indent}#{o}"
			when NilClass             then "#{indent}null"
			when Float
				if (o==o.to_i) && (o.to_s !~ /e/)
					build[o.to_i,indent]
				elsif opts[:decimals]
					"#{indent}%.#{opts[:decimals]}f" % o
				else
					"#{indent}#{o}"
				end

			when Array
				if o.empty?
					"#{indent}[]"
				else
					pieces = o.map{ |v| build[v,''] }
					one_line = "#{indent}[#{apad}#{pieces.join comma}#{apad}]"
					if !opts[:wrap] || (one_line.length <= opts[:wrap])
						one_line
					elsif opts[:short]
						indent2 = "#{indent} #{apad}"
						pieces = o.map{ |v| build[ v,indent2 ] }
						pieces[0].sub! indent2, "#{indent}[#{apad}"
						pieces.last << apad << "]"
						pieces.join ",\n"
					else
						indent2 = "#{indent}#{opts[:indent]}"
						"#{indent}[\n#{o.map{ |v| build[ v, indent2 ] }.join ",\n"}\n#{opts[:indent_last] ? indent2 : indent}]"
					end
				end

			when Hash
				if o.empty?
					"#{indent}{}"
				else
					case sort=(opts[:sorted] || opts[:sort])
						when true then o = o.sort_by(&:first)
						when Proc
							o = case sort.arity
							when 1 then o.sort_by{ |k,v| sort[k] }
							when 2 then o.sort_by{ |k,v| sort[k,v] }
							when 3 then o.sort_by{ |k,v| sort[k,v,o] }
							end
					end
					keyvals = o.map{ |k,v| [ k.to_s.inspect, build[v,''] ] }
					keyvals = keyvals.map{ |kv| kv.join(colon1) }.join(comma)
					one_line = "#{indent}{#{opad}#{keyvals}#{opad}}"
					if !opts[:wrap] || (one_line.length <= opts[:wrap])
						one_line
					else
						if opts[:short]
							keyvals = o.map{ |k,v| ["#{indent} #{opad}#{k.to_s.inspect}",v] }
							keyvals[0][0].sub! "#{indent} ", "#{indent}{"
							if opts[:aligned]
								longest = keyvals.map(&:first).map(&:length).max
								keyvals.each{ |k,v| k.replace( "%-#{longest}s" % k ) }
							end
							keyvals.map! do |k,v|
								indent2 = " "*"#{k}#{colonn}".length
								one_line = "#{k}#{colonn}#{build[v,'']}"
								if opts[:wrap] && (one_line.length > opts[:wrap]) && (v.is_a?(Array) || v.is_a?(Hash))
									"#{k}#{colonn}#{build[v,indent2].lstrip}"
								else
									one_line
								end
							end
							keyvals.join(",\n") << opad << "}"
						else
							keyvals = o.map{ |k,v| ["#{indent}#{opts[:indent]}#{k.to_s.inspect}",v] }
							keyvals = keyvals.sort_by(&:first) if opts[:sorted]
							if opts[:aligned]
								longest = keyvals.map(&:first).map(&:length).max
								keyvals.each{ |k,v| k.replace( "%-#{longest}s" % k ) }
							end
							indent2 = "#{indent}#{opts[:indent]}"
							keyvals.map! do |k,v|
								one_line = "#{k}#{colonn}#{build[v,'']}"
								if opts[:wrap] && (one_line.length > opts[:wrap]) && (v.is_a?(Array) || v.is_a?(Hash))
									"#{k}#{colonn}#{build[v,indent2].lstrip}"
								else
									one_line
								end
							end
							"#{indent}{\n#{keyvals.join(",\n")}\n#{opts[:indent_last] ? indent2 : indent}}"
						end
					end
				end

			else
				"#{indent}#{o.to_json(opts)}"
		end
	end

	build[object,'']
end