ruby-json-toon 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -1
- data/lib/json_to_toon/encoder.rb +25 -7
- data/lib/json_to_toon.rb +0 -1
- data/lib/ruby_json_toon/version.rb +5 -0
- data/lib/ruby_json_toon.rb +15 -0
- data/lib/toon_to_json/decoder.rb +88 -21
- metadata +4 -4
- data/lib/json_to_toon/version.rb +0 -5
- data/lib/toon_to_json/version.rb +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e84a5a72ebd25a0ce5a32c395a1bdc881768e0f7ca153fc7aa8d082b05964731
|
|
4
|
+
data.tar.gz: 95ced809c09555d02369300a0fcfdbf7998c22ca39dbaec4f8bb0e787c2969c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06e08a7eccb8b565901c3c0c9e07718d742d4709a37cffc2513f7d0f7b0094065c10b0788275d7e82aa6ef5ea477ae756a93cf1b2b07483202612c31d4a066f3
|
|
7
|
+
data.tar.gz: 57170a59a1b8dd083b88df2fd5e29921c6ab179ba49264c64685d100232d82a33851ba0665d9a09c3f14e11f96a65cf749952537adea5c03b413e54fba8dcc03
|
data/CHANGELOG.md
CHANGED
|
@@ -30,5 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
30
30
|
- Added decoder implementation
|
|
31
31
|
- Added ability to seperately require decoder using: require "toon_to_json"
|
|
32
32
|
|
|
33
|
+
## [1.0.0] - Complete JSON to TOON roundtrip conversion
|
|
33
34
|
### Added
|
|
34
|
-
-
|
|
35
|
+
- RubyJsonToon wrapper to encapsulate all logic
|
|
36
|
+
- Simple 2 method implementation
|
|
37
|
+
- Fixed known issues
|
data/lib/json_to_toon/encoder.rb
CHANGED
|
@@ -253,24 +253,42 @@ module JsonToToon
|
|
|
253
253
|
end
|
|
254
254
|
|
|
255
255
|
# COMPLEX: Handle list item where first field is an array
|
|
256
|
+
# Implementation: Handle list item where first field is an array
|
|
256
257
|
def encode_list_item_with_array_first(item, depth)
|
|
257
|
-
# This is a complex edge case from the spec
|
|
258
|
-
# TODO: Implement proper handling per spec
|
|
259
258
|
keys = item.keys
|
|
260
259
|
first_key = keys.first
|
|
260
|
+
first_val = item[first_key]
|
|
261
261
|
|
|
262
262
|
base_indent = indent(depth)
|
|
263
263
|
hyphen_line = "#{base_indent}- "
|
|
264
264
|
field_indent = "#{base_indent} "
|
|
265
265
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
# Create the compact header: tags[2]:
|
|
267
|
+
length_str = "#{length_prefix}#{first_val.length}"
|
|
268
|
+
marker = delimiter_marker
|
|
269
|
+
array_header = "#{format_key(first_key)}[#{length_str}#{marker}]:"
|
|
270
|
+
|
|
271
|
+
# JOIN the values on the same line as the hyphen
|
|
272
|
+
if all_primitives?(first_val)
|
|
273
|
+
values_str = first_val.map { |v| format_value(v) }.join(@delimiter)
|
|
274
|
+
emit_line("#{hyphen_line}#{array_header} #{values_str}")
|
|
275
|
+
else
|
|
276
|
+
# Fallback for complex arrays (keep as is)
|
|
277
|
+
emit_line("#{hyphen_line}#{array_header}")
|
|
278
|
+
first_val.each { |sub| encode_list_item(sub, depth + 1) }
|
|
279
|
+
end
|
|
269
280
|
|
|
281
|
+
# Encode remaining fields (id, etc.)
|
|
270
282
|
keys[1..].each do |k|
|
|
283
|
+
v = item[k]
|
|
271
284
|
fk = format_key(k)
|
|
272
|
-
|
|
273
|
-
|
|
285
|
+
# NOTE: Use field_indent to align with the start of the keys
|
|
286
|
+
if primitive?(v)
|
|
287
|
+
emit_line("#{field_indent}#{fk}: #{format_value(v)}")
|
|
288
|
+
else
|
|
289
|
+
emit_line("#{field_indent}#{fk}:")
|
|
290
|
+
encode_value(v, depth + 1)
|
|
291
|
+
end
|
|
274
292
|
end
|
|
275
293
|
end
|
|
276
294
|
|
data/lib/json_to_toon.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'ruby_json_toon/version'
|
|
4
|
+
require_relative 'json_to_toon'
|
|
5
|
+
require_relative 'toon_to_json'
|
|
6
|
+
|
|
7
|
+
module RubyJsonToon
|
|
8
|
+
def self.encode(value, options = {})
|
|
9
|
+
JsonToToon.encode(value, options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.decode(toon_string)
|
|
13
|
+
ToonToJson.decode(toon_string)
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/toon_to_json/decoder.rb
CHANGED
|
@@ -12,15 +12,9 @@ module ToonToJson
|
|
|
12
12
|
if lines.length == 1
|
|
13
13
|
line = lines.first.strip
|
|
14
14
|
|
|
15
|
-
# Check if it's a primitive (not a TOON structure)
|
|
16
|
-
# TOON structures have:
|
|
17
|
-
# - Colons (key:value or key:)
|
|
18
|
-
# - List items (starts with "- " - note the space!)
|
|
19
|
-
# - Array headers (starts with [)
|
|
20
|
-
|
|
21
15
|
is_structure = line.include?(':') ||
|
|
22
|
-
line.match?(/\A-\s/) ||
|
|
23
|
-
line.match?(/\A\[/)
|
|
16
|
+
line.match?(/\A-\s/) ||
|
|
17
|
+
line.match?(/\A\[/)
|
|
24
18
|
|
|
25
19
|
return primitive_to_json(line) unless is_structure
|
|
26
20
|
end
|
|
@@ -54,19 +48,16 @@ module ToonToJson
|
|
|
54
48
|
def parse_block(min_indent)
|
|
55
49
|
return parse_object_block(min_indent) if @i >= @lines.length
|
|
56
50
|
|
|
57
|
-
# Check if first line is array header
|
|
58
51
|
first_line = @lines[@i][:text]
|
|
59
52
|
if first_line.match?(/\A\[/)
|
|
60
53
|
array_header = parse_array_header(first_line)
|
|
61
54
|
if array_header && array_header[:key].nil?
|
|
62
|
-
# Root-level array
|
|
63
55
|
@i += 1
|
|
64
56
|
parse_array_body(array_header, @lines[@i - 1][:indent] + @indent_unit)
|
|
65
57
|
return
|
|
66
58
|
end
|
|
67
59
|
end
|
|
68
60
|
|
|
69
|
-
# Check if list format (starts with "- " with space)
|
|
70
61
|
start_i = @i
|
|
71
62
|
while start_i < @lines.length
|
|
72
63
|
ln = @lines[start_i]
|
|
@@ -92,7 +83,6 @@ module ToonToJson
|
|
|
92
83
|
break if ln[:raw].strip.empty?
|
|
93
84
|
break if ln[:indent] < min_indent
|
|
94
85
|
|
|
95
|
-
# Array header
|
|
96
86
|
if (array_header = parse_array_header(ln[:text]))
|
|
97
87
|
@output << ',' unless first
|
|
98
88
|
first = false
|
|
@@ -109,7 +99,6 @@ module ToonToJson
|
|
|
109
99
|
next
|
|
110
100
|
end
|
|
111
101
|
|
|
112
|
-
# Key-value pair
|
|
113
102
|
if (kv = parse_key_value_line(ln[:text]))
|
|
114
103
|
@output << ',' unless first
|
|
115
104
|
first = false
|
|
@@ -121,7 +110,6 @@ module ToonToJson
|
|
|
121
110
|
next
|
|
122
111
|
end
|
|
123
112
|
|
|
124
|
-
# Key-only (nested object)
|
|
125
113
|
if (key = parse_key_only(ln[:text]))
|
|
126
114
|
@output << ',' unless first
|
|
127
115
|
first = false
|
|
@@ -158,7 +146,6 @@ module ToonToJson
|
|
|
158
146
|
def parse_array_body(header, child_indent)
|
|
159
147
|
@output << '['
|
|
160
148
|
|
|
161
|
-
# Inline values
|
|
162
149
|
if header[:inline] && !header[:inline].strip.empty?
|
|
163
150
|
delim = detect_delimiter(header[:marker], header[:fields])
|
|
164
151
|
values = split_with_quotes(header[:inline], delim)
|
|
@@ -172,7 +159,6 @@ module ToonToJson
|
|
|
172
159
|
return
|
|
173
160
|
end
|
|
174
161
|
|
|
175
|
-
# Tabular format
|
|
176
162
|
if header[:fields]
|
|
177
163
|
delim = detect_delimiter(header[:marker], header[:fields])
|
|
178
164
|
fields = split_with_quotes(header[:fields], delim)
|
|
@@ -213,7 +199,6 @@ module ToonToJson
|
|
|
213
199
|
end
|
|
214
200
|
end
|
|
215
201
|
|
|
216
|
-
# List format - parse items directly
|
|
217
202
|
if @i < @lines.length && @lines[@i][:indent] >= child_indent &&
|
|
218
203
|
@lines[@i][:text].match?(/\A-\s/)
|
|
219
204
|
first = true
|
|
@@ -238,6 +223,73 @@ module ToonToJson
|
|
|
238
223
|
next
|
|
239
224
|
end
|
|
240
225
|
|
|
226
|
+
# FIXED: Check if it's an array header first (like "access[2]: read,write")
|
|
227
|
+
if (inline_array = parse_array_header(after))
|
|
228
|
+
@output << '{'
|
|
229
|
+
@output << json_string(inline_array[:key])
|
|
230
|
+
@output << ':'
|
|
231
|
+
|
|
232
|
+
# Parse the inline array directly
|
|
233
|
+
@output << '['
|
|
234
|
+
if inline_array[:inline] && !inline_array[:inline].strip.empty?
|
|
235
|
+
delim = detect_delimiter(inline_array[:marker], inline_array[:fields])
|
|
236
|
+
values = split_with_quotes(inline_array[:inline], delim)
|
|
237
|
+
values.each_with_index do |v, idx|
|
|
238
|
+
@output << ',' if idx > 0
|
|
239
|
+
@output << value_to_json(v.strip)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
@output << ']'
|
|
243
|
+
|
|
244
|
+
@i += 1
|
|
245
|
+
|
|
246
|
+
# Handle remaining fields
|
|
247
|
+
if @i < @lines.length && @lines[@i][:indent] > ln[:indent]
|
|
248
|
+
child_ind = @lines[@i][:indent]
|
|
249
|
+
|
|
250
|
+
while @i < @lines.length && @lines[@i][:indent] >= child_ind
|
|
251
|
+
field_ln = @lines[@i]
|
|
252
|
+
break if field_ln[:text].match?(/\A-\s/)
|
|
253
|
+
|
|
254
|
+
# Check if field is an array header
|
|
255
|
+
if (field_array = parse_array_header(field_ln[:text]))
|
|
256
|
+
@output << ','
|
|
257
|
+
@output << json_string(field_array[:key])
|
|
258
|
+
@output << ':'
|
|
259
|
+
|
|
260
|
+
@output << '['
|
|
261
|
+
if field_array[:inline] && !field_array[:inline].strip.empty?
|
|
262
|
+
delim = detect_delimiter(field_array[:marker], field_array[:fields])
|
|
263
|
+
vals = split_with_quotes(field_array[:inline], delim)
|
|
264
|
+
vals.each_with_index do |v, idx|
|
|
265
|
+
@output << ',' if idx > 0
|
|
266
|
+
@output << value_to_json(v.strip)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
@output << ']'
|
|
270
|
+
@i += 1
|
|
271
|
+
elsif field_kv = parse_key_value_line(field_ln[:text])
|
|
272
|
+
@output << ','
|
|
273
|
+
@output << json_string(field_kv[:key])
|
|
274
|
+
@output << ':'
|
|
275
|
+
@output << field_kv[:value]
|
|
276
|
+
@i += 1
|
|
277
|
+
elsif field_key = parse_key_only(field_ln[:text])
|
|
278
|
+
@output << ','
|
|
279
|
+
@output << json_string(field_key)
|
|
280
|
+
@output << ':'
|
|
281
|
+
@i += 1
|
|
282
|
+
parse_block(field_ln[:indent] + @indent_unit)
|
|
283
|
+
else
|
|
284
|
+
break
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
@output << '}'
|
|
290
|
+
next
|
|
291
|
+
end
|
|
292
|
+
|
|
241
293
|
if (kv = parse_key_value_line(after))
|
|
242
294
|
@output << '{'
|
|
243
295
|
@output << json_string(kv[:key])
|
|
@@ -253,7 +305,24 @@ module ToonToJson
|
|
|
253
305
|
field_ln = @lines[@i]
|
|
254
306
|
break if field_ln[:text].match?(/\A-\s/)
|
|
255
307
|
|
|
256
|
-
if
|
|
308
|
+
# FIXED: Check if field is an array header
|
|
309
|
+
if (field_array = parse_array_header(field_ln[:text]))
|
|
310
|
+
@output << ','
|
|
311
|
+
@output << json_string(field_array[:key])
|
|
312
|
+
@output << ':'
|
|
313
|
+
|
|
314
|
+
@output << '['
|
|
315
|
+
if field_array[:inline] && !field_array[:inline].strip.empty?
|
|
316
|
+
delim = detect_delimiter(field_array[:marker], field_array[:fields])
|
|
317
|
+
vals = split_with_quotes(field_array[:inline], delim)
|
|
318
|
+
vals.each_with_index do |v, idx|
|
|
319
|
+
@output << ',' if idx > 0
|
|
320
|
+
@output << value_to_json(v.strip)
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
@output << ']'
|
|
324
|
+
@i += 1
|
|
325
|
+
elsif field_kv = parse_key_value_line(field_ln[:text])
|
|
257
326
|
@output << ','
|
|
258
327
|
@output << json_string(field_kv[:key])
|
|
259
328
|
@output << ':'
|
|
@@ -301,7 +370,6 @@ module ToonToJson
|
|
|
301
370
|
return
|
|
302
371
|
end
|
|
303
372
|
|
|
304
|
-
# Empty array
|
|
305
373
|
@output << ']'
|
|
306
374
|
end
|
|
307
375
|
|
|
@@ -313,7 +381,7 @@ module ToonToJson
|
|
|
313
381
|
break if child_text.strip.empty?
|
|
314
382
|
|
|
315
383
|
child_header = parse_array_header(child_text)
|
|
316
|
-
break unless child_header && child_header[:key].nil?
|
|
384
|
+
break unless child_header && child_header[:key].nil?
|
|
317
385
|
|
|
318
386
|
@output << ',' unless first_item
|
|
319
387
|
first_item = false
|
|
@@ -442,7 +510,6 @@ module ToonToJson
|
|
|
442
510
|
return 'false' if t.casecmp('false').zero?
|
|
443
511
|
return 'null' if t.casecmp('null').zero?
|
|
444
512
|
|
|
445
|
-
# Numbers
|
|
446
513
|
return t if t.match?(/\A-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\z/)
|
|
447
514
|
|
|
448
515
|
json_string(t)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-json-toon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jitendra Neema
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: benchmark-ips
|
|
@@ -121,10 +121,10 @@ files:
|
|
|
121
121
|
- README.md
|
|
122
122
|
- lib/json_to_toon.rb
|
|
123
123
|
- lib/json_to_toon/encoder.rb
|
|
124
|
-
- lib/
|
|
124
|
+
- lib/ruby_json_toon.rb
|
|
125
|
+
- lib/ruby_json_toon/version.rb
|
|
125
126
|
- lib/toon_to_json.rb
|
|
126
127
|
- lib/toon_to_json/decoder.rb
|
|
127
|
-
- lib/toon_to_json/version.rb
|
|
128
128
|
homepage: https://github.com/jitendra-neema/ruby-json-toon
|
|
129
129
|
licenses:
|
|
130
130
|
- MIT
|
data/lib/json_to_toon/version.rb
DELETED