toon-parser 0.1.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: 146ba580e5b40ed0c57aca735d092c02660125aad2e0ca06643da9336e18d49d
4
+ data.tar.gz: 7e86260004907ad3d34769751964928a88e7712a478e6d0dc628affc9e883ea8
5
+ SHA512:
6
+ metadata.gz: e1bacf72b9ba9bcd27934039294b5a1fe16975a75b28e0b70cb7eb7ce8787292fa55247f5a3b2ab9a178d77ca7ab2d7c62cd68f7b148b6567591a38b5a9b5484
7
+ data.tar.gz: ea087bc89093ced0c234f1a76acf962eb1ed934a31a7e7882b053f9dc3eface801692f24a74dc95e8574faba4f0d8c74a73c57346c3d8b1d0c50f19a188b4dcf
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
4
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
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
+ ## [0.1.0] - 2024-01-XX
11
+
12
+ ### Added
13
+ - Initial release of ToonParser gem
14
+ - Core parsing functionality for TOON format
15
+ - Serialization of Ruby objects to TOON format
16
+ - File operations (`parse_file`, `serialize_to_file`)
17
+ - Rails integration with Railtie
18
+ - Controller helpers for TOON responses
19
+ - Automatic MIME type registration (`application/toon`)
20
+ - Parameter parser for TOON request bodies
21
+ - API client helpers for TOON-formatted HTTP requests
22
+ - Support for nested objects
23
+ - Support for arrays of objects with schemas
24
+ - Support for simple arrays
25
+ - Type inference for numbers, booleans, and null values
26
+ - Comprehensive test suite
27
+ - Documentation and examples
28
+
29
+ ### Features
30
+ - Parse TOON strings to Ruby objects (Hash/Array)
31
+ - Serialize Ruby objects to TOON format
32
+ - Rails controller integration with `render toon: data`
33
+ - Automatic request body parsing for TOON format
34
+ - HTTP client for TOON APIs
35
+ - Round-trip conversion support
36
+
37
+ [Unreleased]: https://github.com/afshmini/toon-parser/compare/v0.1.0...HEAD
38
+ [0.1.0]: https://github.com/afshmini/toon-parser/releases/tag/v0.1.0
39
+
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,77 @@
1
+ # Contributing to ToonParser
2
+
3
+ Thank you for your interest in contributing to ToonParser! This document provides guidelines and instructions for contributing.
4
+
5
+ ## How to Contribute
6
+
7
+ ### Reporting Bugs
8
+
9
+ If you find a bug, please open an issue on GitHub with:
10
+
11
+ - A clear, descriptive title
12
+ - Steps to reproduce the issue
13
+ - Expected behavior
14
+ - Actual behavior
15
+ - Ruby version and gem version
16
+ - Any relevant code examples
17
+
18
+ ### Suggesting Features
19
+
20
+ Feature suggestions are welcome! Please open an issue with:
21
+
22
+ - A clear description of the feature
23
+ - Use cases and examples
24
+ - Any potential implementation ideas
25
+
26
+ ### Pull Requests
27
+
28
+ 1. **Fork the repository** and create your branch from `main`
29
+ 2. **Make your changes** following the coding standards
30
+ 3. **Add tests** for new functionality
31
+ 4. **Ensure all tests pass** (`bundle exec rspec`)
32
+ 5. **Update documentation** if needed
33
+ 6. **Submit a pull request** with a clear description
34
+
35
+ ## Development Setup
36
+
37
+ ```bash
38
+ # Clone your fork
39
+ git clone https://github.com/YOUR_USERNAME/toon-parser.git
40
+ cd toon-parser
41
+
42
+ # Install dependencies
43
+ bundle install
44
+
45
+ # Run tests
46
+ bundle exec rspec
47
+
48
+ # Run linter
49
+ bundle exec rubocop
50
+ ```
51
+
52
+ ## Coding Standards
53
+
54
+ - Follow Ruby style guidelines
55
+ - Write tests for new features
56
+ - Keep code simple and readable
57
+ - Add documentation for public methods
58
+ - Use meaningful variable and method names
59
+
60
+ ## Testing
61
+
62
+ - Write tests for all new features
63
+ - Ensure existing tests still pass
64
+ - Aim for good test coverage
65
+ - Test edge cases
66
+
67
+ ## Documentation
68
+
69
+ - Update README.md for user-facing changes
70
+ - Add code comments for complex logic
71
+ - Update examples if API changes
72
+ - Keep CHANGELOG.md updated
73
+
74
+ ## Questions?
75
+
76
+ Feel free to open an issue for any questions about contributing!
77
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 afshmini
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.
22
+
data/README.md ADDED
@@ -0,0 +1,437 @@
1
+ # ToonParser
2
+
3
+ [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%202.7.0-red.svg)](https://www.ruby-lang.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.txt)
5
+ [![GitHub](https://img.shields.io/github/stars/afshmini/toon-parser?style=social)](https://github.com/afshmini/toon-parser)
6
+
7
+ A Ruby gem for parsing and serializing **TOON** (Token-Oriented Object Notation) format. TOON is a compact, human-readable data format designed to reduce token usage in LLM applications by **30-60%** compared to JSON.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Installation](#installation)
13
+ - [Quick Start](#quick-start)
14
+ - [Usage](#usage)
15
+ - [Basic Parsing](#basic-parsing)
16
+ - [Basic Serialization](#basic-serialization)
17
+ - [File Operations](#file-operations)
18
+ - [Rails Controller Integration](#rails-controller-integration)
19
+ - [API Integration](#api-integration)
20
+ - [Advanced Usage](#advanced-usage)
21
+ - [TOON Format Overview](#toon-format-overview)
22
+ - [Examples](#examples)
23
+ - [Development](#development)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
26
+
27
+ ## Features
28
+
29
+ ✨ **Key Features:**
30
+
31
+ - 🚀 **Fast parsing and serialization** of TOON format
32
+ - 🎯 **Rails integration** - Use TOON in controllers instead of JSON
33
+ - 📦 **File operations** - Read and write TOON files
34
+ - 🌐 **API helpers** - Built-in HTTP client for TOON APIs
35
+ - 🔄 **Round-trip conversion** - Parse and serialize without data loss
36
+ - 🎨 **Type inference** - Automatic detection of numbers, booleans, and null
37
+ - 📝 **Human-readable** - Compact yet readable format
38
+ - 🔌 **Optional Rails dependency** - Works standalone or with Rails
39
+
40
+ ## Installation
41
+
42
+ Add this line to your application's Gemfile:
43
+
44
+ ```ruby
45
+ gem 'toon-parser'
46
+ ```
47
+
48
+ And then execute:
49
+
50
+ ```bash
51
+ $ bundle install
52
+ ```
53
+
54
+ Or install it yourself as:
55
+
56
+ ```bash
57
+ $ gem install toon-parser
58
+ ```
59
+
60
+ ## Quick Start
61
+
62
+ ```ruby
63
+ require 'toon-parser'
64
+
65
+ # Parse TOON string
66
+ toon = "users[2]{id,name}:\n 1,Alice\n 2,Bob"
67
+ data = ToonParser.parse(toon)
68
+ # => {"users" => [{"id" => 1, "name" => "Alice"}, {"id" => 2, "name" => "Bob"}]}
69
+
70
+ # Serialize to TOON
71
+ data = { "users" => [{ "id" => 1, "name" => "Alice" }] }
72
+ toon = ToonParser.serialize(data)
73
+ # => "users[1]{id,name}:\n 1,Alice"
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ ### Basic Parsing
79
+
80
+ Parse a TOON string into Ruby objects:
81
+
82
+ ```ruby
83
+ require 'toon-parser'
84
+
85
+ toon = <<~TOON
86
+ users[2]{id,name}:
87
+ 1,Alice
88
+ 2,Bob
89
+ TOON
90
+
91
+ data = ToonParser.parse(toon)
92
+ # => {"users" => [{"id" => 1, "name" => "Alice"}, {"id" => 2, "name" => "Bob"}]}
93
+ ```
94
+
95
+ ### Basic Serialization
96
+
97
+ Convert Ruby objects to TOON format:
98
+
99
+ ```ruby
100
+ data = {
101
+ "users" => [
102
+ { "id" => 1, "name" => "Alice" },
103
+ { "id" => 2, "name" => "Bob" }
104
+ ]
105
+ }
106
+
107
+ toon = ToonParser.serialize(data)
108
+ # => "users[2]{id,name}:\n 1,Alice\n 2,Bob"
109
+ ```
110
+
111
+ ### File Operations
112
+
113
+ Parse a TOON file:
114
+
115
+ ```ruby
116
+ data = ToonParser.parse_file("data.toon")
117
+ ```
118
+
119
+ Serialize to a TOON file:
120
+
121
+ ```ruby
122
+ ToonParser.serialize_to_file(data, "output.toon")
123
+ ```
124
+
125
+ ### Rails Controller Integration
126
+
127
+ Use TOON format in your Rails controllers instead of JSON. The gem automatically integrates with Rails when available.
128
+
129
+ #### Basic Controller Usage
130
+
131
+ ```ruby
132
+ # app/controllers/api/users_controller.rb
133
+ class Api::UsersController < ApplicationController
134
+ before_action :set_toon_format
135
+
136
+ def index
137
+ @users = User.all
138
+ render toon: {
139
+ users: @users.map { |u| { id: u.id, name: u.name, email: u.email } }
140
+ }
141
+ end
142
+
143
+ def show
144
+ @user = User.find(params[:id])
145
+ render toon: {
146
+ user: { id: @user.id, name: @user.name, email: @user.email }
147
+ }
148
+ end
149
+
150
+ def create
151
+ # TOON request body is automatically parsed if Content-Type is application/toon
152
+ user_data = parse_toon_request
153
+ @user = User.create(user_data)
154
+
155
+ if @user.persisted?
156
+ render toon: { user: @user }, status: :created
157
+ else
158
+ render toon: { errors: @user.errors.full_messages }, status: :unprocessable_entity
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def set_toon_format
165
+ request.format = :toon if request.format == :html
166
+ end
167
+ end
168
+ ```
169
+
170
+ #### Setting Default Format
171
+
172
+ Set TOON as the default format for API controllers:
173
+
174
+ ```ruby
175
+ # app/controllers/api/base_controller.rb
176
+ class Api::BaseController < ApplicationController
177
+ before_action :set_toon_format
178
+
179
+ private
180
+
181
+ def set_toon_format
182
+ request.format = :toon if request.format == :html
183
+ end
184
+ end
185
+ ```
186
+
187
+ #### Responding to Multiple Formats
188
+
189
+ Support both TOON and JSON:
190
+
191
+ ```ruby
192
+ class Api::UsersController < ApplicationController
193
+ def index
194
+ @users = User.all
195
+ data = { users: @users.map { |u| { id: u.id, name: u.name } } }
196
+
197
+ respond_to do |format|
198
+ format.toon { render toon: data }
199
+ format.json { render json: data }
200
+ end
201
+ end
202
+ end
203
+ ```
204
+
205
+ #### Parsing TOON Request Bodies
206
+
207
+ The gem automatically registers a parameter parser for TOON request bodies. When a request has `Content-Type: application/toon`, Rails will automatically parse the body. You can also manually parse:
208
+
209
+ ```ruby
210
+ class Api::UsersController < ApplicationController
211
+ def create
212
+ # Option 1: Automatic parsing (if Content-Type is application/toon)
213
+ # The body is automatically parsed into params
214
+
215
+ # Option 2: Manual parsing
216
+ user_data = parse_toon_request
217
+ @user = User.create(user_data)
218
+ render toon: { user: @user }
219
+ end
220
+ end
221
+ ```
222
+
223
+ **Note:** The automatic parameter parser only works if the request has `Content-Type: application/toon` header. For manual parsing, use the `parse_toon_request` helper method.
224
+
225
+ #### Routes Configuration
226
+
227
+ Configure routes to accept TOON format:
228
+
229
+ ```ruby
230
+ # config/routes.rb
231
+ Rails.application.routes.draw do
232
+ namespace :api do
233
+ resources :users, defaults: { format: :toon }
234
+ end
235
+ end
236
+ ```
237
+
238
+ #### Available Controller Helpers
239
+
240
+ - `render_toon(data, options = {})` - Render TOON response
241
+ - `parse_toon_request` - Parse TOON request body
242
+ - `set_toon_format` - Set TOON as default format
243
+ - `respond_to_toon(data, options = {})` - Support multiple formats
244
+
245
+ ### API Integration
246
+
247
+ Use the API helper for TOON-formatted API requests and responses:
248
+
249
+ ```ruby
250
+ # Create an API client
251
+ api = ToonParser.api("https://api.example.com", headers: { "Authorization" => "Bearer token" })
252
+
253
+ # GET request
254
+ users = api.get("/users")
255
+
256
+ # POST request with TOON body
257
+ new_user = api.post("/users", data: { "name" => "Alice", "email" => "alice@example.com" })
258
+
259
+ # PUT request
260
+ updated = api.put("/users/1", data: { "name" => "Alice Updated" })
261
+
262
+ # PATCH request
263
+ patched = api.patch("/users/1", data: { "name" => "Alice Patched" })
264
+
265
+ # DELETE request
266
+ api.delete("/users/1")
267
+ ```
268
+
269
+ ### Advanced Usage
270
+
271
+ #### Nested Objects
272
+
273
+ ```ruby
274
+ toon = <<~TOON
275
+ user:
276
+ id: 1
277
+ name: Alice
278
+ profile:
279
+ age: 30
280
+ city: New York
281
+ TOON
282
+
283
+ data = ToonParser.parse(toon)
284
+ # => {"user" => {"id" => 1, "name" => "Alice", "profile" => {"age" => 30, "city" => "New York"}}}
285
+ ```
286
+
287
+ #### Arrays of Primitives
288
+
289
+ ```ruby
290
+ toon = <<~TOON
291
+ numbers[3]:
292
+ 1
293
+ 2
294
+ 3
295
+ TOON
296
+
297
+ data = ToonParser.parse(toon)
298
+ # => {"numbers" => [1, 2, 3]}
299
+ ```
300
+
301
+ #### Boolean and Null Values
302
+
303
+ ```ruby
304
+ toon = <<~TOON
305
+ data:
306
+ active: true
307
+ deleted: false
308
+ value: null
309
+ TOON
310
+
311
+ data = ToonParser.parse(toon)
312
+ # => {"data" => {"active" => true, "deleted" => false, "value" => nil}}
313
+ ```
314
+
315
+ #### Floating Point Numbers
316
+
317
+ ```ruby
318
+ toon = <<~TOON
319
+ prices[2]:
320
+ 19.99
321
+ 29.50
322
+ TOON
323
+
324
+ data = ToonParser.parse(toon)
325
+ # => {"prices" => [19.99, 29.50]}
326
+ ```
327
+
328
+ ## TOON Format Overview
329
+
330
+ TOON uses a compact syntax that eliminates unnecessary brackets, quotes, and whitespace:
331
+
332
+ **JSON:**
333
+ ```json
334
+ {
335
+ "users": [
336
+ { "id": 1, "name": "Alice" },
337
+ { "id": 2, "name": "Bob" }
338
+ ]
339
+ }
340
+ ```
341
+
342
+ **TOON:**
343
+ ```
344
+ users[2]{id,name}:
345
+ 1,Alice
346
+ 2,Bob
347
+ ```
348
+
349
+ ### Key Features
350
+
351
+ - **Array notation**: `[size]` indicates array length
352
+ - **Object schema**: `{field1,field2}` defines object structure
353
+ - **Tabular data**: Rows are comma-separated values
354
+ - **Type inference**: Numbers, booleans, and null are automatically detected
355
+ - **Indentation**: Used for nesting and structure
356
+ - **Compact**: 30-60% smaller than JSON for typical data structures
357
+
358
+ ## Examples
359
+
360
+ See the [examples directory](examples/) for more usage examples:
361
+
362
+ - [Rails Controller Example](examples/rails_controller_example.rb) - Complete Rails controller implementation
363
+
364
+ ## Development
365
+
366
+ After checking out the repo, run:
367
+
368
+ ```bash
369
+ $ bundle install
370
+ ```
371
+
372
+ To run the test suite:
373
+
374
+ ```bash
375
+ $ bundle exec rspec
376
+ ```
377
+
378
+ Or run tests directly:
379
+
380
+ ```bash
381
+ $ rspec spec/
382
+ ```
383
+
384
+ To install this gem onto your local machine, run:
385
+
386
+ ```bash
387
+ $ bundle exec rake install
388
+ ```
389
+
390
+ To release a new version:
391
+
392
+ 1. Update the version number in `lib/toon-parser/version.rb`
393
+ 2. Run `bundle exec rake release`
394
+
395
+ This will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
396
+
397
+ ## Contributing
398
+
399
+ Contributions are welcome! Please feel free to submit a Pull Request.
400
+
401
+ 1. Fork the repository
402
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
403
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
404
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
405
+ 5. Open a Pull Request
406
+
407
+ ### Development Setup
408
+
409
+ ```bash
410
+ # Clone the repository
411
+ git clone https://github.com/afshmini/toon-parser.git
412
+ cd toon-parser
413
+
414
+ # Install dependencies
415
+ bundle install
416
+
417
+ # Run tests
418
+ bundle exec rspec
419
+
420
+ # Run linter
421
+ bundle exec rubocop
422
+ ```
423
+
424
+ ## License
425
+
426
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
427
+
428
+ ## Links
429
+
430
+ - [GitHub Repository](https://github.com/afshmini/toon-parser)
431
+ - [Issue Tracker](https://github.com/afshmini/toon-parser/issues)
432
+ - [RubyGems](https://rubygems.org/gems/toon-parser) (when published)
433
+
434
+ ## Acknowledgments
435
+
436
+ - Inspired by the [TOON format specification](https://github.com/toon-format/spec)
437
+ - Designed to reduce token usage in LLM applications
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
9
+
@@ -0,0 +1,89 @@
1
+ # Example Rails Controller using TOON format
2
+ # Place this in app/controllers/api/users_controller.rb
3
+
4
+ class Api::UsersController < ApplicationController
5
+ # Set TOON as default format for this controller
6
+ before_action :set_toon_format
7
+
8
+ def index
9
+ @users = User.all
10
+ # Render as TOON format
11
+ render toon: {
12
+ users: @users.map do |user|
13
+ {
14
+ id: user.id,
15
+ name: user.name,
16
+ email: user.email,
17
+ created_at: user.created_at
18
+ }
19
+ end
20
+ }
21
+ end
22
+
23
+ def show
24
+ @user = User.find(params[:id])
25
+ render toon: {
26
+ user: {
27
+ id: @user.id,
28
+ name: @user.name,
29
+ email: @user.email,
30
+ profile: {
31
+ bio: @user.bio,
32
+ avatar: @user.avatar_url
33
+ }
34
+ }
35
+ }
36
+ end
37
+
38
+ def create
39
+ # Parse TOON request body
40
+ user_data = parse_toon_request
41
+
42
+ @user = User.new(user_data)
43
+ if @user.save
44
+ render toon: { user: @user }, status: :created
45
+ else
46
+ render toon: { errors: @user.errors.full_messages }, status: :unprocessable_entity
47
+ end
48
+ end
49
+
50
+ def update
51
+ @user = User.find(params[:id])
52
+ user_data = parse_toon_request
53
+
54
+ if @user.update(user_data)
55
+ render toon: { user: @user }
56
+ else
57
+ render toon: { errors: @user.errors.full_messages }, status: :unprocessable_entity
58
+ end
59
+ end
60
+
61
+ def destroy
62
+ @user = User.find(params[:id])
63
+ @user.destroy
64
+ render toon: { message: "User deleted successfully" }, status: :ok
65
+ end
66
+
67
+ private
68
+
69
+ def set_toon_format
70
+ # Set TOON as default format if not specified
71
+ request.format = :toon if request.format == :html
72
+ end
73
+ end
74
+
75
+ # Example: Support both TOON and JSON
76
+ class Api::ProductsController < ApplicationController
77
+ def index
78
+ @products = Product.all
79
+ data = {
80
+ products: @products.map { |p| { id: p.id, name: p.name, price: p.price } }
81
+ }
82
+
83
+ respond_to do |format|
84
+ format.toon { render toon: data }
85
+ format.json { render json: data }
86
+ end
87
+ end
88
+ end
89
+