Skip to content

Instantly share code, notes, and snippets.

@elia
Last active February 21, 2017 01:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elia/435d1b7e7d840035d53b91d6583cdee3 to your computer and use it in GitHub Desktop.
Save elia/435d1b7e7d840035d53b91d6583cdee3 to your computer and use it in GitHub Desktop.
opal -I. main.rb
module InitConf
# how to add a new parameter
# 1. set the default here
# 2. maintain neatjson options to get it sorted here as well
# 3. update controller_command_defnitions to provide the add / edit commands
# 4. update conf_doc_source.rb to provide the documentation and help
# 5. update config-form.rb to attach a type
# 6. update user-interface.js to add the menu entries
def self.init_conf()
result =
{produce: [0],
abc_parser: 'ABC2SVG',
restposition: {default: :center, repeatstart: :next, repeatend: :default},
wrap: 60,
# here are values for object which occur multiple times
# such that there i no expolicit default in the configuration
defaults: {
notebound: {annotation: {pos: [5, -7]},
partname: {pos: [-4, -7]},
variantend: {pos: [-4, -7]},
tuplet: {
cp1: [5, 2], # first control point positive x: point is east of flowline, positive y: point is south of note
cp2: [5, -2], # second control point
shape: ['c'], # 'c' | 'l' => curve | line
show: true
}
}
},
# this is used to upddate / create new objects
templates: {
notes: {"pos" => [320, 6], "text" => "ENTER_NOTE", "style" => "large"}, # Seitenbeschriftung
lyrics: {verses: [1], pos: [350, 70]},
tuplet: {cp1: [5, 2], cp2: [5, -2], shape: ['c'], show: true},
annotations: {text: "_vorlage_", pos: [-5, -6]} # Notenbeschriftungsvorlage
},
# this is used to populate a QuickSettings menu
# in configuration editor
# needs to be handled in contrller_command_definiitions
presets: {
layout: {
layout_compact: {
LINE_MEDIUM: 0.2,
LINE_THICK: 0.3,
# all numbers in mm
ELLIPSE_SIZE: [3.5, 1.3], # radii of the largest Ellipse
REST_SIZE: [4, 1.5]
},
layout_large: {
LINE_MEDIUM: 0.3,
LINE_THICK: 0.7,
ELLIPSE_SIZE: [4, 2], # radii of the largest Ellipse
REST_SIZE: [4, 2]
}
},
notes: {
T01_number: {
value: {
pos: [394, 17],
text: "XXX-999-X",
style: "bold"
}},
T01_number_extract: {
value: {
pos: [411, 17],
text: "-X",
style: "bold"
}},
T01_number_extract_value: {
key: :T01_number_extract,
value: {
text: "-X",
}},
T02_copyright_music: {value: {pos: [372, 227], text: " ", style: "small"}},
T03_copyright_harpnotes: {
value: {
pos: [344, 208],
text: " ",
style: "small"
}},
T04_to_order: {
value: {
pos: [369, 224],
text: "",
style: "small"
}},
T99_do_not_copy: {
value: {
pos: [380, 284],
text: "Bitte nicht kopieren",
style: "small_bold"
}}
},
printer: {
printer_left: {
printer: {
a3_offset: [-10, 0],
a4_offset: [-5, 0],
show_border: false
},
layout: {limit_a3: false}
},
printer_centric: {
printer: {
a3_offset: [0, 0],
a4_offset: [5, 0],
show_border: false
},
layout: {limit_a3: true}
},
printer_right: {
printer: {
a3_offset: [10, 0],
a4_offset: [5, 0],
show_border: false
},
layout: {limit_a3: false}
}
}
},
# these are the builtin notebound annotations
annotations: {
vl: {text: "v", pos: [-5, -5]},
vt: {text: "v", pos: [2, -5]},
vr: {text: "v", pos: [-1, -5]}
}, # default for note based annotations
extract: {
"0" => {
title: "alle Stimmen",
filenamepart: 'alle-stimmen',
startpos: 15,
voices: [1, 2, 3, 4],
synchlines: [[1, 2], [3, 4]],
flowlines: [1, 3],
subflowlines: [2, 4],
jumplines: [1, 3],
repeatsigns: {voices: [],
left: {pos: [-7, -2], text: '|:', style: :bold},
right: {pos: [5, -2], text: ':|', style: :bold}
},
layoutlines: [1, 2, 3, 4],
legend: {spos: [320, 27], pos: [320, 20]},
lyrics: {},
#
# this denotes the layout parameters which are intended to bne configured
# by the regular user
layout: {limit_a3: true,
LINE_THIN: 0.1,
LINE_MEDIUM: 0.3,
LINE_THICK: 0.5,
# all numbers in mm
ELLIPSE_SIZE: [3.5, 1.7], # radii of the largest Ellipse
REST_SIZE: [4, 2],
packer: {
pack_method: 0,
pack_max_spreadfactor: 2,
pack_min_increment: 0.2
},
},
nonflowrest: false,
notes: {},
barnumbers: {
voices: [],
pos: [6, -4],
autopos: false,
style: "small_bold",
prefix: ""
},
countnotes: {voices: [], pos: [3, -2], autopos: false, style: "smaller"},
stringnames: {
text: "G G# A A# B C C# D D# E F F# G G# A A# B C C# D D# E F F# G G# A A# B C C# D D# E F F# G",
vpos: [],
style: :small,
marks: {vpos: [11], hpos: [43, 55, 79]}
},
printer: {
a3_offset: [0, 0],
a4_offset: [-5, 0],
show_border: true
}
},
"1" => {
title: "Sopran, Alt",
filenamepart: 'sopran-alt',
voices: [1, 2]
},
"2" => {
title: "Tenor, Bass",
filenamepart: 'tenor-bass',
voices: [3, 4]
},
"3" => {
title: "Melodie",
filenamepart: 'melodie',
voices: [1]
}
},
# this is the builtin default for layout
# it is somehow double maintained es
# extrat.0.layout defines a default as well.
# but the runtime layout has more parameters which
# are not intended to be configured by a regular user.
#
# nevertheless, an expert user could also change the
# other parameters
layout: {
grid: false,
packer: {
pack_method: 0,
pack_max_spreadfactor: 2,
pack_min_increment: 0.2
},
limit_a3: true,
SHOW_SLUR: false,
LINE_THIN: 0.1,
LINE_MEDIUM: 0.3,
LINE_THICK: 0.5,
# all numbers in mm
ELLIPSE_SIZE: [3.5, 1.7], # radii of the largest Ellipse
REST_SIZE: [4, 2], # radii of the largest Rest Glyph
# x-size of one step in a pitch. It is the horizontal
# distance between two strings of the harp
X_SPACING: 11.5, # Distance of strings
# X coordinate of the very first beat
X_OFFSET: 2.8, #ELLIPSE_SIZE.first,
Y_SCALE: 4, # 4 mm per minimal
DRAWING_AREA_SIZE: [400, 282], # Area in which Drawables can be placed
# this affects the performance of the harpnote renderer
# it also specifies the resolution of note starts
# in fact the shortest playable note is 1/16; to display dotted 16, we need 1/32
# in order to at least being able to handle triplets, we need to scale this up by 3
# todo:see if we can speed it up by using 16 ...
BEAT_RESOLUTION: 192, # SHORTEST_NOTE * BEAT_PER_DURATION, ## todo use if want to support 5 * 7 * 9 # Resolution of Beatmap
SHORTEST_NOTE: 64, # shortest possible note (1/64) do not change this
# in particular specifies the range of DURATION_TO_STYLE etc.
BEAT_PER_DURATION: 3, # BEAT_RESOLUTION / SHORTEST_NOTE,
# this is the negative of midi-pitch of the lowest plaayble note
# see http://computermusicresource.com/midikeys.html
PITCH_OFFSET: -43,
FONT_STYLE_DEF: {
bold: {text_color: [0, 0, 0], font_size: 12, font_style: "bold"},
italic: {text_color: [0, 0, 0], font_size: 12, font_style: "italic"},
large: {text_color: [0, 0, 0], font_size: 20, font_style: "bold"},
regular: {text_color: [0, 0, 0], font_size: 12, font_style: "normal"},
small_bold: {text_color: [0, 0, 0], font_size: 9, font_style: "bold"},
small_italic: {text_color: [0, 0, 0], font_size: 9, font_style: "italic"},
small: {text_color: [0, 0, 0], font_size: 9, font_style: "normal"},
smaller: {text_color: [0, 0, 0], font_size: 6, font_style: "normal"}
},
MM_PER_POINT: 0.3,
# This is a lookup table to map durations to giraphical representation
DURATION_TO_STYLE: {
#key size fill dot abc duration
:err => [2, :filled, FALSE], # 1 1
:d64 => [1, :empty, FALSE], # 1 1
:d48 => [0.75, :empty, TRUE], # 1/2 *
:d32 => [0.75, :empty, FALSE], # 1/2
:d24 => [0.75, :filled, TRUE], # 1/4 *
:d16 => [0.75, :filled, FALSE], # 1/4
:d12 => [0.5, :filled, TRUE], # 1/8 *
:d8 => [0.5, :filled, FALSE], # 1/8
:d6 => [0.3, :filled, TRUE], # 1/16 *
:d4 => [0.3, :filled, FALSE], # 1/16
:d3 => [0.1, :filled, TRUE], # 1/32 *
:d2 => [0.1, :filled, FALSE], # 1/32
:d1 => [0.05, :filled, FALSE] # 1/64
},
REST_TO_GLYPH: {
# this basically determines the white background rectangel
# [sizex, sizey], glyph, dot # note that sizex has no effect.
:err => [[2, 2], :rest_1, FALSE], # 1 1
:d64 => [[1, 0.8], :rest_1, FALSE], # 1 1 # make it a bit smaller than the note to improve visibility of barover
:d48 => [[0.5, 0.4], :rest_1, TRUE], # 1/2 * # make it a bit smaller than the note to improve visibility of barover
:d32 => [[0.5, 0.4], :rest_1, FALSE], # 1/2 # make it a bit smaller than the note to improve visibility of barover
:d24 => [[0.4, 0.75], :rest_4, TRUE], # 1/4 *
:d16 => [[0.4, 0.75], :rest_4, FALSE], # 1/4
:d12 => [[0.4, 0.5], :rest_8, TRUE], # 1/8 *
:d8 => [[0.4, 0.5], :rest_8, FALSE], # 1/8
:d6 => [[0.4, 0.3], :rest_16, TRUE], # 1/16 *
:d4 => [[0.3, 0.3], :rest_16, FALSE], # 1/16
:d3 => [[0.3, 0.5], :rest_32, TRUE], # 1/32 *
:d2 => [[0.3, 0.5], :rest_32, FALSE], # 1/32
:d1 => [[0.3, 0.5], :rest_64, FALSE] # 1/64
}
},
neatjson: {
wrap: 60, aligned: true, after_comma: 1, after_colon_1: 1, after_colon_n: 1, before_colon_n: 1, sorted: true,
decimals: 2,
explicit_sort: [[:produce, :annotations, :restposition, :default, :repeatstart, :repeatend, :extract,
:title, :filenamepart, :startpos, :voices, :flowlines, :subflowlines, :synchlines, :jumplines, :repeatsigns, :layoutlines, :barnumbers, :countnotes,
:legend, :nonflowrest, :lyrics, :notes, :tuplet, :layout, :printer,
#
:annotation, :partname, :variantend, :countnote, :stringnames, # sort within notebound
# sort within layout
:limit_a3, :LINE_THIN, :LINE_MEDIUM, :LINE_THICK, :ELLIPSE_SIZE, :REST_SIZE,
:DRAWING_AREA_SIZE,
:packer, :pack_method, :pack_max_spreadfactor, :pack_min_increment,
# sort within printer
:a3_offset, :a4_offset, # sort within laoyut
"0", "1", "2", "3", "4", "5", "6", :verses, # extracts
:cp1, :cp2, :shape, :pos, :hpos, :vpos, :spos, :autopos, :text, :style, :marks # tuplets annotations
],
[]],
}
}
result
end
end
require 'init_conf.rb'
require 'neatjson.rb'
require 'js'
if RUBY_ENGINE == 'opal'
# `console.log(process.pid)`
require 'native'
require 'neatJSON'
doc = JS.global.JS[:document]
doc.JS.write 'start...' if doc
end
neatJSON = $neatJSON
testdata = InitConf.init_conf
def bm(name, &block)
starttime = Time.now
100.times { block.call }
result = block.call
puts (Time.now - starttime).to_s + " #{name}"
result
end
output = bm :ruby do
JSON.neat_generate(
testdata,
wrap:40,
short:false,
aligned:true,
padding:1,
after_comma:1,
around_colon_n:1
)
end
if RUBY_ENGINE == 'opal'
inputjs = bm "convert ruby to js" do
%x{JSON.parse(JSON.stringify(#{testdata.to_n}))}
end
outputjs = bm :javascript do
out = %x{
neatJSON(JSON.parse(JSON.stringify(#{testdata.to_n})),{
wrap:40,
short:false,
aligned:true,
padding:1,
afterComma:1,
aroundColonN:1
})
}
out
# outputjs = %x{neatJSON(#{testdata.to_n},{
# wrap:40,
# short:false,
# aligned:true,
# sorted: true,
# padding:1,
# afterComma:1,
# aroundColonN:1
# })}
end
puts "result is equal: #{output === outputjs}"
end
if RUBY_ENGINE == 'opal'
doc.JS.write 'ok' if doc
end
function neatJSON(value,opts){
opts = opts || {}
if (!('wrap' in opts)) opts.wrap = 80;
if (opts.wrap==true) opts.wrap = -1;
if (!('indent' in opts)) opts.indent = ' ';
if (!('arrayPadding' in opts)) opts.arrayPadding = ('padding' in opts) ? opts.padding : 0;
if (!('objectPadding' in opts)) opts.objectPadding = ('padding' in opts) ? opts.padding : 0;
if (!('beforeComma' in opts)) opts.beforeComma = ('aroundComma' in opts) ? opts.aroundComma : 0;
if (!('afterComma' in opts)) opts.afterComma = ('aroundComma' in opts) ? opts.aroundComma : 0;
if (!('beforeColon' in opts)) opts.beforeColon = ('aroundColon' in opts) ? opts.aroundColon : 0;
if (!('afterColon' in opts)) opts.afterColon = ('aroundColon' in opts) ? opts.aroundColon : 0;
if (!('beforeColon1' in opts)) opts.beforeColon1 = ('aroundColon1' in opts) ? opts.aroundColon1 : ('beforeColon' in opts) ? opts.beforeColon : 0;
if (!('afterColon1' in opts)) opts.afterColon1 = ('aroundColon1' in opts) ? opts.aroundColon1 : ('afterColon' in opts) ? opts.afterColon : 0;
if (!('beforeColonN' in opts)) opts.beforeColonN = ('aroundColonN' in opts) ? opts.aroundColonN : ('beforeColon' in opts) ? opts.beforeColon : 0;
if (!('afterColonN' in opts)) opts.afterColonN = ('aroundColonN' in opts) ? opts.aroundColonN : ('afterColon' in opts) ? opts.afterColon : 0;
var apad = repeat(' ',opts.arrayPadding),
opad = repeat(' ',opts.objectPadding),
comma = repeat(' ',opts.beforeComma)+','+repeat(' ',opts.afterComma),
colon1 = repeat(' ',opts.beforeColon1)+':'+repeat(' ',opts.afterColon1),
colonN = repeat(' ',opts.beforeColonN)+':'+repeat(' ',opts.afterColonN);
var build = memoize();
return build(value,'');
function memoize(){
var memo = new Map;
return function(o,indent){
var byIndent=memo.get(o);
if (!byIndent) memo.set(o,byIndent={});
if (!byIndent[indent]) byIndent[indent] = rawBuild(o,indent);
return byIndent[indent];
}
}
function rawBuild(o,indent){
if (o===null || o===undefined) return indent+'null';
else{
if (typeof o==='number'){
var isFloat = (o === +o && o !== (o|0));
return indent + ((isFloat && ('decimals' in opts)) ? o.toFixed(opts.decimals) : (o+''));
}else if (o instanceof Array){
if (!o.length) return indent+"[]";
var pieces = o.map(function(v){ return build(v,'') });
var oneLine = indent+'['+apad+pieces.join(comma)+apad+']';
if (opts.wrap===false || oneLine.length<=opts.wrap) return oneLine;
if (opts.short){
var indent2 = indent+' '+apad;
pieces = o.map(function(v){ return build(v,indent2) });
pieces[0] = pieces[0].replace(indent2,indent+'['+apad);
pieces[pieces.length-1] = pieces[pieces.length-1]+apad+']';
return pieces.join(',\n');
}else{
var indent2 = indent+opts.indent;
return indent+'[\n'+o.map(function(v){ return build(v,indent2) }).join(',\n')+'\n'+(opts.indentLast?indent2:indent)+']';
}
}else if (o instanceof Object){
var sortedKV=[],i=0;
var sort = opts.sort || opts.sorted;
for (var k in o){
var kv = sortedKV[i++] = [k,o[k]];
if (sort===true) kv[2] = k;
else if (typeof sort==='function') kv[2]=sort(k,o[k],o);
}
if (!sortedKV.length) return indent+'{}';
if (sort) sortedKV = sortedKV.sort(function(a,b){ a=a[2]; b=b[2]; return a<b?-1:a>b?1:0 });
var keyvals=sortedKV.map(function(kv){ return [JSON.stringify(kv[0]), build(kv[1],'')] });
if (opts.sorted) keyvals = keyvals.sort(function(kv1,kv2){ kv1=kv1[0]; kv2=kv2[0]; return kv1<kv2?-1:kv1>kv2?1:0 });
keyvals = keyvals.map(function(kv){ return kv.join(colon1) }).join(comma);
var oneLine = indent+"{"+opad+keyvals+opad+"}";
if (opts.wrap===false || oneLine.length<opts.wrap) return oneLine;
if (opts.short){
keyvals = sortedKV.map(function(kv){ return [indent+' '+opad+JSON.stringify(kv[0]), kv[1]] });
keyvals[0][0] = keyvals[0][0].replace(indent+' ',indent+'{');
if (opts.aligned){
var longest = 0;
for (var i=keyvals.length;i--;) if (keyvals[i][0].length>longest) longest = keyvals[i][0].length;
var padding = repeat(' ',longest);
for (var i=keyvals.length;i--;) keyvals[i][0] = padRight(padding,keyvals[i][0]);
}
for (var i=keyvals.length;i--;){
var k=keyvals[i][0], v=keyvals[i][1];
var indent2 = repeat(' ',(k+colonN).length);
var oneLine = k+colonN+build(v,'');
keyvals[i] = (opts.wrap===false || oneLine.length<=opts.wrap || !v || typeof v!="object") ? oneLine : (k+colonN+build(v,indent2).replace(/^\s+/,''));
}
return keyvals.join(',\n') + opad + '}';
}else{
var keyvals=[],i=0;
for (var k in o) keyvals[i++] = [indent+opts.indent+JSON.stringify(k),o[k]];
if (sort) keyvals = keyvals.sort(function(kv1,kv2){ kv1=kv1[0]; kv2=kv2[0]; return kv1<kv2?-1:kv1>kv2?1:0 });
if (opts.aligned){
var longest = 0;
for (var i=keyvals.length;i--;) if (keyvals[i][0].length>longest) longest = keyvals[i][0].length;
var padding = repeat(' ',longest);
for (var i=keyvals.length;i--;) keyvals[i][0] = padRight(padding,keyvals[i][0]);
}
var indent2 = indent+opts.indent;
for (var i=keyvals.length;i--;){
var k=keyvals[i][0], v=keyvals[i][1];
var oneLine = k+colonN+build(v,'');
keyvals[i] = (opts.wrap===false || oneLine.length<=opts.wrap || !v || typeof v!="object") ? oneLine : (k+colonN+build(v,indent2).replace(/^\s+/,''));
}
return indent+'{\n'+keyvals.join(',\n')+'\n'+(opts.indentLast?indent2:indent)+'}'
}
}else{
return indent+JSON.stringify(o);
}
}
}
function repeat(str,times){ // http://stackoverflow.com/a/17800645/405017
var result = '';
while(true){
if (times & 1) result += str;
times >>= 1;
if (times) str += str;
else break;
}
return result;
}
function padRight(pad, str){
return (str + pad).substring(0, pad.length);
}
}
Opal.gvars.neatJSON = neatJSON;
require 'json'
module JSON
# Generate the JSON string representation for an object,
# with a variety of formatting options.
#
# @author Gavin Kistner <!@phrogz.net>
# @param object [Object] the object to serialize
# @param opts [Hash] the formatting options
# @option opts [Integer] :wrap (80) The maximum line width before wrapping. Use `false` to never wrap, or `true` to always wrap.
# @option opts [String] :indent (" ") Whitespace used to indent each level when wrapping (without the :short option).
# @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.
# @option opts [Boolean] :sorted (false) Sort the keys for objects to be in alphabetical order.
# @option opts [Boolean] :aligned (false) When wrapping objects, align the colons (only per object).
# @option opts [Integer] :decimals (null) Decimal precision to use for floats; omit to keep numberic values precise.
# @option opts [Integer] :padding (0) Number of spaces to put inside brackets/braces for both arrays and objects.
# @option opts [Integer] :array_padding (0) Number of spaces to put inside brackets for arrays. Overrides `:padding`.
# @option opts [Integer] :object_padding (0) Number of spaces to put inside braces for objects. Overrides `:padding`.
# @option opts [Integer] :around_comma (0) Number of spaces to put before/after commas (for both arrays and objects).
# @option opts [Integer] :before_comma (0) Number of spaces to put before commas (for both arrays and objects).
# @option opts [Integer] :after_comma (0) Number of spaces to put after commas (for both arrays and objects).
# @option opts [Integer] :around_colon (0) Number of spaces to put before/after colons (for objects).
# @option opts [Integer] :before_colon (0) Number of spaces to put before colons (for objects).
# @option opts [Integer] :after_colon (0) Number of spaces to put after colons (for objects).
# @option opts [Integer] :around_colon_1 (0) Number of spaces to put before/after colons for single-line objects.
# @option opts [Integer] :before_colon_1 (0) Number of spaces to put before colons for single-line objects.
# @option opts [Integer] :after_colon_1 (0) Number of spaces to put after colons for single-line objects.
# @option opts [Integer] :around_colon_n (0) Number of spaces to put before/after colons for multi-line objects.
# @option opts [Integer] :before_colon_n (0) Number of spaces to put before colons for multi-line objects.
# @option opts [Integer] :after_colon_n (0) Number of spaces to put after colons for multi-line objects.
# @return [String] the JSON representation of the object.
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
pieces = o.map{ |v| build[v,''] }
one_line = "#{indent}[#{apad}#{pieces.join comma}#{apad}]"
if o.empty?
"#{indent}[]"
elsif !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#{indent}]"
end
when Hash
keyvals = o.map{ |k,v| [ k.to_s.inspect, build[v,''] ] }
keyvals = keyvals.sort_by(&:first) if opts[:sorted]
keyvals = keyvals.map{ |kv| kv.join(colon1) }.join(comma)
one_line = "#{indent}{#{opad}#{keyvals}#{opad}}"
if o.empty?
"#{indent}{}"
elsif !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 = keyvals.sort_by(&:first) if opts[:sorted]
keyvals[0][0].sub! "#{indent} ", "#{indent}{"
if opts[:aligned]
longest = keyvals.map(&:first).map(&:length).max
keyvals = keyvals.map{|k, v| ["%-#{longest}s" % k, v] }
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 = keyvals.map{|k, v| ["%-#{longest}s" % k, v] }
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#{indent}}"
end
end
else
"#{indent}#{o.to_json(opts)}"
end
end
build[object,'']
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment