ruby-json-toon 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2eda6d64edfc1cc2a7f956141060c3705bf4a66e668ce10622f42c30ba2ff92b
4
+ data.tar.gz: f8dc21d8f08cc1751463d3b664c00c27f5a83720b715d0894be5e71c29ea0a12
5
+ SHA512:
6
+ metadata.gz: 435a729daee86264c5eab4917f6191fe0fc70646c7c49574a85a74c34b19ecebd58fd1e04fff136a36667346a2aa511f3a81c8f8732c4bcf6621ae2ccbc6ffd7
7
+ data.tar.gz: 00ae26b0633543fb121c7910efe3ab5d79b699d1143bd963e659fc2b3f94f8cbc9a1278ca81bbb65343476a05a44fb8d216aedd892ed108998451c9491d729a1
data/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Initial implementation of JSON to TOON converter
12
+ - Support for all TOON format types (objects, arrays, primitives)
13
+ - Configurable delimiters (comma, tab, pipe)
14
+ - Configurable indentation
15
+ - Optional length markers
16
+ - Comprehensive test suite
17
+ - Full TOON specification compliance
18
+
19
+ ## [0.1.0] - Initial publish
20
+ ### Added
21
+ - Initial Encoder class implementation
22
+ - Tests for JSON to TOON conversion
23
+
24
+ ##[0.2.0] - First tagged release
25
+ ### Added
26
+ - Updated to run release workflow on tag pushes
27
+
28
+ ## [1.0.0] - TBD
29
+
30
+ ### Added
31
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 [Jitendra Neema]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # JSON to TOON
2
+
3
+ A lightweight, zero-dependency Ruby library for converting JSON data to TOON (Token-Oriented Object Notation) format, achieving 30-60% token reduction for LLM applications.
4
+
5
+ ## What is TOON?
6
+
7
+ TOON (Token-Oriented Object Notation) is a compact, indentation-based data format optimized for LLM token efficiency. It uses 30-60% fewer tokens than JSON while remaining human-readable.
8
+
9
+ ### Comparison
10
+
11
+ **JSON (87 tokens):**
12
+ ```json
13
+ {
14
+ "users": [
15
+ {"id": 1, "name": "Alice", "role": "admin"},
16
+ {"id": 2, "name": "Bob", "role": "user"}
17
+ ]
18
+ }
19
+ ```
20
+
21
+ **TOON (31 tokens):**
22
+ ```
23
+ users[2]{id,name,role}:
24
+ 1,Alice,admin
25
+ 2,Bob,user
26
+ ```
27
+
28
+ ## Installation
29
+
30
+ Add to your Gemfile:
31
+
32
+ ```ruby
33
+ gem 'json_to_toon'
34
+ ```
35
+
36
+ Or install directly:
37
+
38
+ ```bash
39
+ gem install json_to_toon
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ```ruby
45
+ require 'json_to_toon'
46
+
47
+ # Convert Ruby hash to TOON
48
+ data = { name: 'Ada', role: 'admin', active: true }
49
+ toon = JsonToToon.encode(data)
50
+ # Output:
51
+ # name: Ada
52
+ # role: admin
53
+ # active: true
54
+
55
+ # Convert JSON string to TOON
56
+ json_data = JSON.parse('{"users":[{"id":1,"name":"Alice"}]}')
57
+ toon = JsonToToon.encode(json_data)
58
+ ```
59
+
60
+ ## Documentation
61
+
62
+ See full documentation at [rubydoc.info](https://rubydoc.info/gems/json_to_toon)
63
+
64
+ ## Options
65
+
66
+ ```ruby
67
+ JsonToToon.encode(data,
68
+ indent: 2, # Spaces per indentation level (default: 2)
69
+ delimiter: ',', # Delimiter: ',' (default), "\t", or '|'
70
+ length_marker: '#' # Length marker or false (default: false)
71
+ )
72
+ ```
73
+
74
+ ## Development
75
+
76
+ ```bash
77
+ # Install dependencies
78
+ bundle install
79
+
80
+ # Run tests
81
+ bundle exec rspec
82
+
83
+ # Run linter
84
+ bundle exec rubocop
85
+
86
+ # Build gem
87
+ gem build json_to_toon.gemspec
88
+ ```
89
+
90
+ ## License
91
+
92
+ MIT License - see LICENSE file for details
93
+
94
+ ## Links
95
+
96
+ - [TOON Specification](https://toonformat.dev)
97
+ - [GitHub Repository](https://github.com/jitendra-neema/json_to_toon)
98
+ - [Bug Tracker](https://github.com/jitendra-neema/json_to_toon/issues)
@@ -0,0 +1,399 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module JsonToToon
6
+ class Encoder
7
+ DEFAULT_INDENT = 2
8
+ DELIMITER_COMMA = ','
9
+ DELIMITER_TAB = "\t"
10
+ DELIMITER_PIPE = '|'
11
+ VALID_DELIMITERS = [DELIMITER_COMMA, DELIMITER_TAB, DELIMITER_PIPE].freeze
12
+
13
+ attr_reader :indent_size, :delimiter, :length_marker
14
+
15
+ def initialize(options = {})
16
+ @indent_size = options[:indent] || DEFAULT_INDENT
17
+ @delimiter = options[:delimiter] || DELIMITER_COMMA
18
+ @length_marker = options[:length_marker] || false
19
+
20
+ validate_options!
21
+
22
+ @output = []
23
+ @visited = {}
24
+ end
25
+
26
+ def encode(value)
27
+ @output = []
28
+ @visited = {}
29
+ result = encode_value(value, 0)
30
+
31
+ # If encoding a primitive value at root, encode_value returns the
32
+ # formatted string but nothing is emitted to @output. In that case
33
+ # return the direct result.
34
+ return result.to_s if @output.empty? && result
35
+
36
+ @output.join("\n")
37
+ end
38
+
39
+ def delimiter_marker
40
+ case @delimiter
41
+ when DELIMITER_COMMA then ''
42
+ when DELIMITER_TAB then "\t"
43
+ when DELIMITER_PIPE then '|'
44
+ end
45
+ end
46
+
47
+ def length_prefix
48
+ @length_marker == '#' ? '#' : ''
49
+ end
50
+
51
+ private
52
+
53
+ def validate_options!
54
+ unless @indent_size.is_a?(Integer) && @indent_size.positive?
55
+ raise InvalidOptionError, "indent must be a positive integer, got: #{@indent_size.inspect}"
56
+ end
57
+
58
+ unless VALID_DELIMITERS.include?(@delimiter)
59
+ raise InvalidOptionError,
60
+ "delimiter must be one of #{VALID_DELIMITERS.map(&:inspect).join(', ')}, got: #{@delimiter.inspect}"
61
+ end
62
+
63
+ return if @length_marker == '#' || @length_marker == false
64
+
65
+ raise InvalidOptionError, "length_marker must be '#' or false, got: #{@length_marker.inspect}"
66
+ end
67
+
68
+ def encode_value(value, depth, key = nil)
69
+ check_circular_reference(value)
70
+
71
+ result = case value
72
+ when Hash then encode_hash(value, depth)
73
+ when Array then encode_array_with_key(value, depth, key)
74
+ else format_value(value)
75
+ end
76
+
77
+ unmark_visited(value)
78
+ result
79
+ end
80
+
81
+ def check_circular_reference(value)
82
+ return unless value.is_a?(Hash) || value.is_a?(Array)
83
+
84
+ object_id = value.object_id
85
+ raise CircularReferenceError, 'Circular reference detected' if @visited[object_id]
86
+
87
+ @visited[object_id] = true
88
+ end
89
+
90
+ def unmark_visited(value)
91
+ @visited.delete(value.object_id) if value.is_a?(Hash) || value.is_a?(Array)
92
+ end
93
+
94
+ def encode_hash(hash, depth)
95
+ return if hash.empty? && depth.zero?
96
+
97
+ hash.each do |key, value|
98
+ formatted_key = format_key(key)
99
+
100
+ if primitive?(value)
101
+ formatted_value = format_value(value)
102
+ emit_line(indent(depth) + "#{formatted_key}: #{formatted_value}")
103
+ elsif value.is_a?(Hash)
104
+ emit_line(indent(depth) + "#{formatted_key}:")
105
+ encode_value(value, depth + 1) unless value.empty?
106
+ elsif value.is_a?(Array)
107
+ encode_value(value, depth, formatted_key)
108
+ end
109
+ end
110
+ end
111
+
112
+ def encode_array_with_key(array, depth, key)
113
+ if array.empty?
114
+ header = key ? "#{key}[#{length_prefix}0]:" : "[#{length_prefix}0]:"
115
+ emit_line(indent(depth) + header)
116
+ return
117
+ end
118
+
119
+ if tabular_eligible?(array)
120
+ encode_tabular(array, depth, key)
121
+ elsif all_primitives?(array)
122
+ encode_inline(array, depth, key)
123
+ else
124
+ encode_list(array, depth, key)
125
+ end
126
+ end
127
+
128
+ # FIXED: Tabular eligibility with proper key checking
129
+ def tabular_eligible?(array)
130
+ return false if array.empty?
131
+ return false unless array.all? { |item| item.is_a?(Hash) && !item.empty? }
132
+
133
+ # Get first object's key set (sorted for comparison)
134
+ first_keys = array.first.keys.sort
135
+
136
+ # All objects must have exactly the same keys
137
+ return false unless array.all? { |item| item.keys.sort == first_keys }
138
+
139
+ # All values must be primitives
140
+ array.all? { |item| item.values.all? { |v| primitive?(v) } }
141
+ end
142
+
143
+ def all_primitives?(array)
144
+ array.all? { |item| primitive?(item) }
145
+ end
146
+
147
+ # FIXED: Tabular encoding with correct field order from first object
148
+ def encode_tabular(array, depth, key)
149
+ # Use first object's key order for header
150
+ field_order = array.first.keys
151
+ field_header = field_order.map { |k| format_key(k) }.join(delimiter)
152
+
153
+ length_str = "#{length_prefix}#{array.length}"
154
+ marker = delimiter_marker
155
+ header = if key
156
+ "#{key}[#{length_str}#{marker}]{#{field_header}}:"
157
+ else
158
+ "[#{length_str}#{marker}]{#{field_header}}:"
159
+ end
160
+
161
+ emit_line(indent(depth) + header)
162
+
163
+ array.each do |item|
164
+ # CRITICAL: Use field_order, not item's key order!
165
+ values = field_order.map { |k| format_value(item[k]) }
166
+ row = values.join(@delimiter)
167
+ emit_line(indent(depth + 1) + row)
168
+ end
169
+ end
170
+
171
+ def encode_inline(array, depth, key)
172
+ length_str = "#{length_prefix}#{array.length}"
173
+ marker = delimiter_marker
174
+ header = key ? "#{key}[#{length_str}#{marker}]:" : "[#{length_str}#{marker}]:"
175
+
176
+ values = array.map { |item| format_value(item) }
177
+ line = "#{indent(depth)}#{header} #{values.join(@delimiter)}"
178
+
179
+ emit_line(line)
180
+ end
181
+
182
+ # FIXED: List encoding with proper indentation
183
+ def encode_list(array, depth, key)
184
+ length_str = "#{length_prefix}#{array.length}"
185
+ header = key ? "#{key}[#{length_str}]:" : "[#{length_str}]:"
186
+ emit_line(indent(depth) + header)
187
+
188
+ array.each do |item|
189
+ encode_list_item(item, depth + 1)
190
+ end
191
+ end
192
+
193
+ # FIXED: Proper list item indentation
194
+ def encode_list_item(item, depth)
195
+ base_indent = indent(depth)
196
+ hyphen_line = "#{base_indent}- "
197
+ field_indent = "#{base_indent} "
198
+
199
+ case item
200
+ when Hash
201
+ if item.empty?
202
+ emit_line(hyphen_line)
203
+ else
204
+ keys = item.keys
205
+ first_key = keys.first
206
+ formatted_key = format_key(first_key)
207
+
208
+ # Check if first value is an array (special case)
209
+ if item[first_key].is_a?(Array)
210
+ # TODO: Handle nested array as first field
211
+ # This is complex - needs special handling
212
+ encode_list_item_with_array_first(item, depth)
213
+ elsif item[first_key].is_a?(Hash)
214
+ # First field is nested object
215
+ emit_line("#{hyphen_line}#{formatted_key}:")
216
+ encode_value(item[first_key], depth + 1)
217
+
218
+ # Remaining fields
219
+ keys[1..].each do |k|
220
+ fk = format_key(k)
221
+ fv = format_value(item[k])
222
+ emit_line("#{field_indent}#{fk}: #{fv}")
223
+ end
224
+ else
225
+ # First field is primitive
226
+ fv = format_value(item[first_key])
227
+ emit_line("#{hyphen_line}#{formatted_key}: #{fv}")
228
+
229
+ # Remaining fields at same indent level
230
+ keys[1..].each do |k|
231
+ fk = format_key(k)
232
+ v = item[k]
233
+
234
+ if v.is_a?(Hash)
235
+ emit_line("#{field_indent}#{fk}:")
236
+ encode_value(v, depth + 2)
237
+ elsif v.is_a?(Array)
238
+ encode_value(v, depth + 1, fk)
239
+ else
240
+ fv = format_value(v)
241
+ emit_line("#{field_indent}#{fk}: #{fv}")
242
+ end
243
+ end
244
+ end
245
+ end
246
+ when Array
247
+ # Nested array in list - go through encode_value for circular checks
248
+ encode_value(item, depth, nil)
249
+ else
250
+ # Primitive
251
+ emit_line("#{hyphen_line}#{format_value(item)}")
252
+ end
253
+ end
254
+
255
+ # COMPLEX: Handle list item where first field is an array
256
+ 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
+ keys = item.keys
260
+ first_key = keys.first
261
+
262
+ base_indent = indent(depth)
263
+ hyphen_line = "#{base_indent}- "
264
+ field_indent = "#{base_indent} "
265
+
266
+ formatted_key = format_key(first_key)
267
+ emit_line("#{hyphen_line}#{formatted_key}:")
268
+ encode_value(item[first_key], depth + 1, nil)
269
+
270
+ keys[1..].each do |k|
271
+ fk = format_key(k)
272
+ fv = format_value(item[k])
273
+ emit_line("#{field_indent}#{fk}: #{fv}")
274
+ end
275
+ end
276
+
277
+ def primitive?(value)
278
+ value.nil? ||
279
+ value.is_a?(String) ||
280
+ value.is_a?(Numeric) ||
281
+ value == true ||
282
+ value == false ||
283
+ value.is_a?(Symbol)
284
+ end
285
+
286
+ def format_key(key)
287
+ key_str = key.to_s
288
+ needs_key_quotes?(key_str) ? quote_string(key_str) : key_str
289
+ end
290
+
291
+ def needs_key_quotes?(str)
292
+ return true if str.empty?
293
+ return true if str.start_with?('-')
294
+ return true if str.match?(/\A\d+\z/)
295
+ return true if str.match?(/[\s,:"\[\]{}\\]/)
296
+ return true if str.match?(/[\n\r\t]/)
297
+
298
+ false
299
+ end
300
+
301
+ def format_value(value)
302
+ case value
303
+ when nil then 'null'
304
+ when true then 'true'
305
+ when false then 'false'
306
+ when Integer then value.to_s
307
+ when Float then format_float(value)
308
+ when String then format_string(value)
309
+ when Symbol then format_string(value.to_s)
310
+ when Time, DateTime then quote_string(value.iso8601(3))
311
+ else 'null'
312
+ end
313
+ end
314
+
315
+ # FIXED: Better float formatting
316
+ def format_float(float)
317
+ return 'null' if float.nan? || float.infinite?
318
+ return '0' if float.zero?
319
+
320
+ # Handle scientific notation and very small/large numbers
321
+ str = if float.abs < 1e-10 || float.abs >= 1e15
322
+ BigDecimal(float, 10).to_s('F')
323
+ else
324
+ float.to_s
325
+ end
326
+
327
+ # Remove scientific notation if present
328
+ str = BigDecimal(str).to_s('F') if str.include?('e') || str.include?('E')
329
+
330
+ # Remove trailing zeros after decimal point
331
+ str = str.sub(/\.?0+\z/, '') if str.include?('.')
332
+
333
+ # Ensure no lone decimal point
334
+ str = str.sub(/\.\z/, '')
335
+
336
+ # Final -0 check
337
+ str = '0' if ['-0', '-0.0'].include?(str)
338
+
339
+ str
340
+ end
341
+
342
+ # FIXED: Correct string quoting - only leading/trailing spaces!
343
+ def format_string(str)
344
+ needs_string_quotes?(str) ? quote_string(str) : str
345
+ end
346
+
347
+ def needs_string_quotes?(str)
348
+ return true if str.empty?
349
+ return true if str.include?(@delimiter)
350
+ return true if str.include?(':')
351
+ return true if str.include?('"') || str.include?('\\')
352
+ return true if str.match?(/[\n\r\t]/)
353
+
354
+ # Quote strings that contain spaces (inner or leading/trailing)
355
+ return true if str.include?(' ')
356
+
357
+ return true if str == '-' || str.start_with?('- ')
358
+ return true if looks_like_boolean?(str)
359
+ return true if looks_like_null?(str)
360
+ return true if looks_like_number?(str)
361
+ return true if looks_like_structural?(str)
362
+
363
+ false
364
+ end
365
+
366
+ def looks_like_boolean?(str)
367
+ str.match?(/\A(true|false)\z/i)
368
+ end
369
+
370
+ def looks_like_null?(str)
371
+ str.match?(/\Anull\z/i)
372
+ end
373
+
374
+ def looks_like_number?(str)
375
+ str.match?(/\A-?\d+(\.\d+)?([eE][+-]?\d+)?\z/)
376
+ end
377
+
378
+ def looks_like_structural?(str)
379
+ str.match?(/\A\[.*\]\z/) || str.match?(/\A\{.*\}\z/)
380
+ end
381
+
382
+ def quote_string(str)
383
+ escaped = str.gsub('\\', '\\\\\\\\')
384
+ .gsub('"', '\"')
385
+ .gsub("\n", '\n')
386
+ .gsub("\r", '\r')
387
+ .gsub("\t", '\t')
388
+ "\"#{escaped}\""
389
+ end
390
+
391
+ def indent(depth)
392
+ ' ' * (depth * @indent_size)
393
+ end
394
+
395
+ def emit_line(content)
396
+ @output << content.rstrip
397
+ end
398
+ end
399
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonToToon
4
+ VERSION = '0.2.0'
5
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'json_to_toon/version'
4
+ require_relative 'json_to_toon/encoder'
5
+
6
+ module JsonToToon
7
+ class Error < StandardError; end
8
+ class CircularReferenceError < Error; end
9
+ class InvalidOptionError < Error; end
10
+
11
+ # Convert a Ruby object to TOON format
12
+ #
13
+ # @param value [Object] The Ruby object to encode (Hash, Array, or primitive)
14
+ # @param options [Hash] Encoding options
15
+ # @option options [Integer] :indent Number of spaces per indentation level (default: 2)
16
+ # @option options [String] :delimiter Delimiter for arrays: ',' (default), "\t", or '|'
17
+ # @option options [String, false] :length_marker Length marker character or false (default: false)
18
+ # @return [String] TOON-formatted string with no trailing newline
19
+ # @raise [InvalidOptionError] If options are invalid
20
+ # @raise [CircularReferenceError] If circular references detected
21
+ #
22
+ # @example Basic usage
23
+ # JsonToToon.encode({name: 'Ada', age: 30})
24
+ # # => "name: Ada\nage: 30"
25
+ #
26
+ # @example With tab delimiter
27
+ # JsonToToon.encode({tags: ['a', 'b']}, delimiter: "\t")
28
+ # # => "tags[2\t]: a\tb"
29
+ def self.encode(value, options = {})
30
+ Encoder.new(options).encode(value)
31
+ end
32
+
33
+ # Alias for encode
34
+ def self.convert(value, options = {})
35
+ encode(value, options)
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-json-toon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Jitendra Neema
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: benchmark-ips
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: memory_profiler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.50'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.50'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.22'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.22'
125
+ description: Lightweight Ruby library for converting JSON data to TOON format, achieving
126
+ 30-60% token reduction for LLM applications
127
+ email:
128
+ - jitenra.neema.8@gmail.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - CHANGELOG.md
134
+ - LICENSE
135
+ - README.md
136
+ - lib/json_to_toon.rb
137
+ - lib/json_to_toon/encoder.rb
138
+ - lib/json_to_toon/version.rb
139
+ homepage: https://github.com/jitendra-neema/ruby-json-toon
140
+ licenses:
141
+ - MIT
142
+ metadata:
143
+ homepage_uri: https://github.com/jitendra-neema/ruby-json-toon
144
+ source_code_uri: https://github.com/jitendra-neema/ruby-json-toon
145
+ changelog_uri: https://github.com/jitendra-neema/ruby-json-toon/blob/main/CHANGELOG.md
146
+ bug_tracker_uri: https://github.com/jitendra-neema/ruby-json-toon/issues
147
+ documentation_uri: https://rubydoc.info/gems/ruby-json-toon
148
+ rubygems_mfa_required: 'true'
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: 2.7.0
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubygems_version: 3.4.19
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: Convert JSON to TOON (Token-Oriented Object Notation)
168
+ test_files: []