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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 239c3f74f2d761583b30c69d05882a1072d02c423b84446bbae9e7d241b09a2b
4
- data.tar.gz: 4a9b92b5663e92fa1f99d3f61f9b80be0ad2b0fef1a5ebf16564573b3338e7b7
3
+ metadata.gz: e84a5a72ebd25a0ce5a32c395a1bdc881768e0f7ca153fc7aa8d082b05964731
4
+ data.tar.gz: 95ced809c09555d02369300a0fcfdbf7998c22ca39dbaec4f8bb0e787c2969c6
5
5
  SHA512:
6
- metadata.gz: c54ca35838a6697763813f709ee382162d5efdefae022ed00cba6849a63f3135e06af366b217552ea2c6a941941db82cf0069fe03a960f178ff7130fff8f470e
7
- data.tar.gz: ed409049fb431d60f786edc49ace9dd827429f59b67f01e0b08c238e8cc2619cb1b704dbea26888e392a48e8894d2bb8e1e41ed7ef2c938460908cfd34e79591
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
- - Initial release
35
+ - RubyJsonToon wrapper to encapsulate all logic
36
+ - Simple 2 method implementation
37
+ - Fixed known issues
@@ -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
- formatted_key = format_key(first_key)
267
- emit_line("#{hyphen_line}#{formatted_key}:")
268
- encode_value(item[first_key], depth + 1, nil)
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
- fv = format_value(item[k])
273
- emit_line("#{field_indent}#{fk}: #{fv}")
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
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'json_to_toon/version'
4
3
  require_relative 'json_to_toon/encoder'
5
4
 
6
5
  module JsonToToon
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJsonToon
4
+ VERSION = '1.0.0'
5
+ end
@@ -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
@@ -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/) || # "- " with space (list item)
23
- line.match?(/\A\[/) # Array header
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 field_kv = parse_key_value_line(field_ln[:text])
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? # Must be headerless array
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.3.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-09 00:00:00.000000000 Z
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/json_to_toon/version.rb
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
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JsonToToon
4
- VERSION = '0.3.0'
5
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ToonToJson
4
- VERSION = '0.3.0'
5
- end