nestedtext 1.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42f5f6f0ac6182999d4036976103b740552796029ffe77749a8cdf5d0d8d0167
4
- data.tar.gz: c06dd5613b6c5296398a838e2419dd2addaf34b9712a1baec36ecd23e5d4c6f9
3
+ metadata.gz: bfafdf3e9638459777da2f7209954955473758103c72533b58a66cc23f4020e3
4
+ data.tar.gz: 400fd879811f4d09c193da219ffa0ff61853ee0e51baac386c58b7016dfbe354
5
5
  SHA512:
6
- metadata.gz: 0dc821033f7813441756fcfb04e9b6a442993130462ff2572c7401b634149b538be4a831a7deb5fdb91a432be5f795d37b473fc6ac4dad5eb1e8b9c88762a5f9
7
- data.tar.gz: ba7d69e4bfb3929c5dd5e0f037e88b7b13fac466aa011bccacf8095812f990c1e32b8c449df721514414cc8ce0b1c6f11723644cce13a5ee4330f549c793ee56
6
+ metadata.gz: 708025814b4038b1b5c6eea70378762e1155ae73e933cc902a17d26d4ce3efbb2a5b52f7d535c385818e34484fe12278f981c253469d80a875e97aa8f25b20c1
7
+ data.tar.gz: fe87505719301bf79a71cdef1c9d89d71f9058318f5f59f1305ac79b0b69007b79976d6e288a4846824c6cc89323021b4b29df56b8f14f534cd59906d55b0843
data/CHANGELOG.md CHANGED
@@ -6,6 +6,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.0.0] - 2022-01-27
10
+ ### Added
11
+ - API documentation generated with rdoc.
12
+
13
+ ### Fixed
14
+ - Removed leaked `NT_MIXIN` constant in core extensions.
15
+
16
+ ### Changed
17
+ - **Breaking change**: `#to_nt` on `String`, `Array` and `Hash` is no longer strict by default for consistency an unexpected surprises e.g. when having an array of CustomObjects and calling the method on the array.
18
+ - Internal clean-up and simplifications on helper classes and methods.
19
+
20
+ ## [2.1.0] - 2022-01-27
21
+ ### Changed
22
+ - Slim down Gem by using include instead of block list.
23
+
24
+ ## [2.0.1] - 2022-01-26
25
+ ### Fixed
26
+ - README issue with logo showing up on Rdoc (out-commented HTML).
27
+
28
+ ## [2.0.0] - 2022-01-26
29
+ ### Changed
30
+ - **Breaking change**: strict mode now defaults to false for both the `load` and `dump` methods.
31
+ - Internal rename of error classes to be more consistent.
32
+ - Internal simplification of argument passing.
33
+
9
34
  ## [1.2.0] - 2022-01-25
10
35
  ### Changed
11
36
  - Hide core extension `String.normalize_line_endings` from users.
data/CONTRIBUTING.md CHANGED
@@ -1,4 +1,4 @@
1
1
  # How to contribute
2
2
  Please use GitHub tooling (issues, PRs) to disucssion and code contributions!
3
3
 
4
- When you open an PR, Travis will build your code, run tests, liters and so on.
4
+ When you open an PR, GitHub Actions will build your code, run tests, liters and so on.
data/README.md CHANGED
@@ -1,43 +1,116 @@
1
1
  # NestedText Ruby Library [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=NestedText,%20the%20human%20friendly%20data%20format,%20has%20a%20now%20a%20ruby%20library%20for%20easy%20encoding%20and%20decoding&url=https://github.com/erikw/nestedtext-ruby&via=erik_westrup&hashtags=nestedtext,ruby,gem)
2
2
  [![Gem Version](https://badge.fury.io/rb/nestedtext.svg)](https://badge.fury.io/rb/nestedtext)
3
3
  [![Gem Downloads](https://ruby-gem-downloads-badge.herokuapp.com/nestedtext?color=brightgreen&type=total&label=gem%20downloads)](https://rubygems.org/gems/nestedtext)
4
+ [![Documentation](https://img.shields.io/badge/docs-API-informational?logo=readthedocs&logoColor=violet)](https://www.rubydoc.info/gems/nestedtext/)
4
5
  [![Data Format Version Supported](https://img.shields.io/badge/%F0%9F%84%BD%F0%9F%85%83%20Version%20Supported-3.2.1-blueviolet)](https://nestedtext.org/en/v3.2/)
5
- [![Official Tests](https://img.shields.io/badge/%F0%9F%8F%81%20Official%20Tests-Passing-success)](https://github.com/KenKundert/nestedtext_tests/tree/585e95a73d94ac1f48e71a154e2db0ab67cf30fa)
6
+ [![Official Tests](https://img.shields.io/badge/Official%20Tests-Passing-success?logo=cachet)](https://github.com/KenKundert/nestedtext_tests/tree/585e95a73d94ac1f48e71a154e2db0ab67cf30fa)
6
7
  [![GitHub Actions: Continuous Integration](https://github.com/erikw/nestedtext-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/erikw/nestedtext-ruby/actions/workflows/ci.yml)
7
8
  [![GitHub Actions: Continuous Deployment](https://github.com/erikw/nestedtext-ruby/actions/workflows/cd.yml/badge.svg)](https://github.com/erikw/nestedtext-ruby/actions/workflows/cd.yml)
8
9
  [![GitHub Actions: CodeQL Analysis](https://github.com/erikw/nestedtext-ruby/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/erikw/nestedtext-ruby/actions/workflows/codeql-analysis.yml)
9
10
  [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/8409b6cdc3dc62a33f6f/maintainability)](https://codeclimate.com/github/erikw/nestedtext-ruby/maintainability)
10
11
  [![Code Climate Test Coverage](https://api.codeclimate.com/v1/badges/8409b6cdc3dc62a33f6f/test_coverage)](https://codeclimate.com/github/erikw/nestedtext-ruby/test_coverage)
11
- [![SLOC](https://img.shields.io/tokei/lines/github/erikw/nestedtext-ruby)](#)
12
- [![License](https://img.shields.io/github/license/erikw/nestedtext-ruby)](LICENSE.txt)
12
+ [![SLOC](https://img.shields.io/tokei/lines/github/erikw/nestedtext-ruby?logo=codefactor&logoColor=lightgrey)](#)
13
+ [![License](https://img.shields.io/github/license/erikw/nestedtext-ruby?color=informational)](LICENSE.txt)
13
14
  [![OSS Lifecycle](https://img.shields.io/osslifecycle/erikw/nestedtext-ruby)](https://github.com/Netflix/osstracker)
14
15
 
15
16
 
16
- Inspired by the `JSON` and `YAML` modules.
17
+ A ruby library for the human friendly data format [NestedText](https://nestedtext.org/).
17
18
 
18
- This project will soon be released! :tada:
19
+ <a href="#" ><img src="https://raw.githubusercontent.com/erikw/nestedtext-ruby/main/img/logo.webp" align="right" width="420px" alt="Project logo" /></a>
19
20
 
20
- On-going development is at branch [**dev**](https://github.com/erikw/nestedtext-ruby/tree/dev).
21
+ Provided is support for decoding a NestedText file or string to Ruby data structures, as well as encoding Ruby objects to a NestedText file or string. Furthermore there is support for serialization and deserialization of custom classes. The supported language version of the data format can be see in the badge above. This implementation pass all the [official tests](https://github.com/KenKundert/nestedtext_tests).
22
+
23
+ This library is inspired Ruby stdlib modules `JSON` and `YAML` as well as the Python [reference implementation](https://github.com/KenKundert/nestedtext) of NestedText. Parsing is done with a LL(1) recursive descent parser and dumping with a recursive DFS traversal of the object references.
21
24
 
22
25
  # What is NestedText?
23
- TODO
26
+ Citing from the official [introduction](https://nestedtext.org/en/latest/index.html) page:
27
+ > NestedText is a file format for holding structured data to be entered, edited, or viewed by people. It organizes the data into a nested collection of dictionaries, lists, and strings without the need for quoting or escaping. A unique feature of this file format is that it only supports one scalar type: strings. While the decision to eschew integer, real, date, etc. types may seem counter intuitive, it leads to simpler data files and applications that are more robust.
28
+ >
29
+ > NestedText is convenient for configuration files, address books, account information, and the like. Because there is no need for quoting or escaping, it is particularly nice for holding code fragments.
24
30
 
25
- https://nestedtext.org/en/latest/alternatives.html
31
+ *"Why do we need another data format?"* is the right question to ask. The answer is that the current popular formats (JSON, YAML, TOML, INI etc.) all have shortcomings which NestedText [addresses](https://nestedtext.org/en/latest/alternatives.html).
26
32
 
27
- ## Examples
28
- TODO NT examples
33
+ ## Example
34
+ Here's a full-fledged example of an address book (from the official docs):
35
+ ```nestedtext
36
+ # Contact information for our officers
29
37
 
30
- # Usage
31
- TODO Link to lib docs
32
- TODO link to my test repo showin live usage. https://github.com/erikw/nestedtext-ruby-test
38
+ president:
39
+ name: Katheryn McDaniel
40
+ address:
41
+ > 138 Almond Street
42
+ > Topeka, Kansas 20697
43
+ phone:
44
+ cell: 1-210-555-5297
45
+ home: 1-210-555-8470
46
+ # Katheryn prefers that we always call her on her cell phone.
47
+ email: KateMcD@aol.com
48
+ additional roles:
49
+ - board member
50
+
51
+ vice president:
52
+ name: Margaret Hodge
53
+ ...
54
+ ```
33
55
 
56
+ See the [language introduction](https://nestedtext.org/en/latest/basic_syntax.html) for more details.
34
57
 
35
58
  # Usage
59
+ The full API documentation can be found at [**rubydocs.info**](https://www.rubydoc.info/gems/nestedtext/). A minimal & fully working example of a project using this library can be found at [erikw/nestedtext-ruby-test](https://github.com/erikw/nestedtext-ruby-test).
60
+
36
61
  ## Decoding (reading NT)
62
+ This is how you can decode NestedText from a string or directly from a file (`*.nt`) to Ruby object instances:
63
+
64
+ ### Any Top Level Type
65
+ ```ruby
66
+ require 'nestedtext'
67
+
68
+ ntstr = "- objitem1\n- list item 2"
69
+ obj1 = NestedText::load(ntstr)
70
+
71
+ obj2 = NestedText::load_file("path/to/data.nt")
72
+ ```
73
+
74
+ The type of the returned object depends on the top level type in the NestedText data and will be of corresponding native Ruby type. In the example above, `obj1` will be an `Array` and obj2 will be `Hash` if `data.nt` looks like e.g.
75
+
76
+ ```
77
+ key1: value1
78
+ key2: value2
79
+ ```
80
+
81
+ Thus you must know what you're parsing, or test what you decoded.
82
+
83
+ ### Explicit Top Level Type
84
+ If you already know what you expect to have, you can guarantee that this is what you will get by telling either function what the expected top type is. If not, an error will be raised.
85
+
86
+ ```ruby
87
+ require 'nestedtext'
88
+
89
+ ntstr = "- objitem1\n- list item 2"
90
+ array = NestedText::load(ntstr, top_class: Array)
91
+
92
+ hash = NestedText::load_file("path/to/data.nt", top_class: Hash)
93
+
94
+ # will raise NestedText::Error as we specify top level String but it will be Array.
95
+ NestedText::load(ntstr, top_class: String)
96
+ ```
37
97
 
38
98
  ## Encoding (writing NT)
99
+ This is how you can decode Ruby objects to a NestedText string or file:
100
+
101
+ ```ruby
102
+ require 'nestedtext'
103
+
104
+ data = ["i1", "i2"]
105
+
106
+ ntstr = NestedText::dump(data)
107
+
108
+ NestedText::dump_file(data, "path/to/data.nt")
109
+ ```
110
+
111
+ ### `#to_nt` Convenience
112
+ To make it more convenient, the Ruby Core is extended with a `#to_nt` method on the supported types that will dump a String of the data structure. Here's an IRB session showing how it works:
39
113
 
40
- `#to_nt` method:
41
114
  ```irb
42
115
  irb> require 'nestedtext'
43
116
  irb> puts "a\nstring".to_nt
@@ -47,30 +120,136 @@ irb> puts ["i1", "i2", "i3"].to_nt
47
120
  - i1
48
121
  - i2
49
122
  - i3
50
- irb> puts({"k1" => "v1", "multiline\nkey" => "v2", "k3" => "multiline\nvalue"}.to_nt)
123
+ irb> puts({"k1" => "v1", "multiline\nkey" => "v2", "k3" => ["a", "list"]}.to_nt)
51
124
  k1: v1
52
125
  : multiline
53
126
  : key
54
127
  > v2
55
128
  k3:
56
- > multiline
57
- > value
129
+ - a
130
+ - list
58
131
  ```
132
+
133
+ ## Types
134
+ Ruby classes maps like this to NestedText types:
135
+ Ruby | [NestedText](https://nestedtext.org/en/latest/basic_syntax.html)
136
+ ---|---
137
+ `String` |`String`
138
+ `Array` |`List`
139
+ `Hash` |`Dictionary`
140
+
141
+
142
+ ### Strict Mode
143
+ The strict mode determines how classes other than the basic types `String`, `Array` and `Hash` are handled during encoding and decoding. By **default** strict mode is turned **off**.
144
+
145
+ With `strict: true`
146
+ Ruby | NestedText | Comment
147
+ ---|---|---
148
+ `nil` |*empty* | (1.)
149
+ `Symbol` |`String` | Raises `NestedText::Error`
150
+ Other Class | -- | Raises `NestedText::Error`
151
+
152
+
153
+ With `strict: false`
154
+ Ruby | NestedText | Comment
155
+ ---|---|---
156
+ `nil` | *Custom Class Encoding* | (1.)
157
+ `Symbol` | `String` |
158
+ Custom Class | *Custom Class Encoding* | If the [Custom Class](#custom-classes-serialization) implements `#encode_nt_with` (2.)
159
+ Other Class | String | `#to_s` will be called if there is no `#encode_nt_with`
160
+
161
+
162
+ * (1.) How empty strings and nil are handled depends on where it is used. This library follows how the official implementation does it.
163
+
164
+
165
+
166
+
167
+
59
168
  ## Custom Classes Serialization
60
- This library has support for serialization/deserialization of custom classes as well.
61
- `strict: false` flag needed
169
+ This library has support for serialization/deserialization of custom classes as well. This is done by letting the objects tell NestedText what data should be used to represent the object instance with the `#encode_nt_with` method (inspired by `YAML`'s `#encode_with` method). All objects being recursively referenced from a root object being serialized must either implement this method or be one of the core supported NestedText data types from the table above.
170
+
171
+ A class implementing `#encode_nt_with` is refered to as `Custom Class` in this document.
172
+
173
+ ```ruby
174
+ class Apple
175
+ def initialize(type, weight)
176
+ @type = type
177
+ @weight = weight
178
+ end
179
+
180
+ def encode_nt_with
181
+ [@type, @weight]
182
+ end
183
+ end
184
+ ```
185
+
186
+ When an apple instance will be serialized e.g. by `apple.to_nt`, NestedText will call `Apple.encode_nt_with` if it exist and let the returned data be encoded to represent the instance.
187
+
188
+
189
+ To be able to get this instance back when deserializing the NestedText there must be a class method `Class.nt_create(data)`. When deserializing NestedText and the class `Apple` is detected, and the method `#nt_create` exist on the class, it will be called with the decoded data belonging to it. This method should create and return a new instance of the class. In the most simple case it's just translating this to a call to `#new`.
190
+
191
+ In full, the `Apple` class should look like:
192
+
193
+ ```ruby
194
+ class Apple
195
+ def self.nt_create(data)
196
+ new(*data)
197
+ end
198
+
199
+ def initialize(type, weight)
200
+ @type = type
201
+ @weight = weight
202
+ end
203
+
204
+ def encode_nt_with
205
+ [@type, @weight]
206
+ end
207
+ end
208
+ ```
209
+
210
+ An instance of this class would be encoded like this:
211
+
212
+ ```ruby
213
+ irb> puts NestedText::dump(Apple.new("granny smith", 12))
214
+ __nestedtext_class__: Apple
215
+ data:
216
+ - granny smith
217
+ - 12
218
+ ```
219
+
220
+ If you want to add some more super powers to your custom class, you can add the `#to_nt` shortcut by including the `NTEncodeMixin`:
221
+ ```ruby
222
+ class Apple
223
+ include NestedText::NTEncodeMixin
224
+ ...
225
+ end
226
+
227
+ Apple.new("granny smith", 12).to_nt
228
+ ```
229
+
230
+
231
+ **Important notes**:
232
+ * The special key to denote the class name is subject to change in future versions and you **must not** rely on it.
233
+ * Custom Classes **can not be a key** in a Hash. Trying to do this will raise an Error.
234
+ * When deserializing a custom class, this custom class must be available when calling the `#dump*` methods e.g.
235
+ ```ruby
236
+ require 'nestedtext'
237
+ require_relative 'apple' # This is needed if Apple is defined in apple.rb and not in this scope already.
238
+
239
+ NestedText::load_file('path/to/apple_dump.nt')
240
+ ```
241
+
62
242
  See [encode_custom_classes_test.rb](test/nestedtext/encode_custom_classes_test.rb) for more real working examples.
63
243
 
64
244
 
65
245
  # Installation
66
246
  1. Add this gem to your ruby project's Gemfile
67
- - Simply with `$ bundle add nestedtext` when standing in the project root
247
+ - Simply with `$ bundle add nestedtext` when standing inside your project
68
248
  - Or manually by adding to `Gemfile`
69
249
  ```ruby
70
250
  gem 'nestedtext'
71
251
  ```
72
252
  and then running `$ bundle install`.
73
- ```
74
253
  1. Require the library and start using it!
75
254
  ```ruby
76
255
  require 'nestedtext'
@@ -89,20 +268,20 @@ See [encode_custom_classes_test.rb](test/nestedtext/encode_custom_classes_test.r
89
268
  $ git clone https://github.com/erikw/nestedtext-ruby.git && cd $(basename "$_" .git)
90
269
  ```
91
270
  1. Install a supported ruby version (see .gemspec) with a ruby version manager e.g. [rbenv](https://github.com/rbenv/rbenv), [asdf](http://asdf-vm.com/) or [RVM](https://rvm.io/rvm/install)
92
- 1. run `$ script/setup` to install dependencies
93
- 1. run `$ script/test` to run the tests
271
+ 1. run `$ script/setup` or `$ bundle install` to install dependencies
272
+ 1. run `$ script/test` or `bundle exec rake test` to run the tests
94
273
  1. You can also run `$ script/console` for an interactive prompt that will allow you to experiment.
95
274
  1. For local testing, install the gem on local machine with: `$ bundle exec rake install`.
96
- * or manuall with `$ gem build *.gemscpec && gem install *.gem`
275
+ * or manually with `$ gem build *.gemscpec && gem install *.gem`
97
276
 
98
- Make sure that only intended constants and methods are exposed from the module `NestedText`. Check with
277
+ Make sure that only intended constants and methods are exposed publicly from the module `NestedText`. Check with
99
278
  ```
100
279
  irb> require 'nestedtext'
101
280
  irb> NestedText.constants
102
281
  irb> NestedText.methods(false)
103
282
  ```
104
283
 
105
- ## Releasing
284
+ # Releasing
106
285
  Instructions for releasing on rubygems.org below. Optionally make a GitHub [release](https://github.com/erikw/nestedtext-ruby/releases) after this for the pushed git tag.
107
286
 
108
287
  ## (manually) Using bundler/gem_tasks rake tasks
@@ -131,7 +310,7 @@ $ git commit -am "Prepare vX.Y.Z" && git push
131
310
  $ git tag x.y.z && git push --tags
132
311
  ```
133
312
 
134
- or combined with gem-release
313
+ or combined with gem-release:
135
314
  ```console
136
315
  $ vi CHANGELOG.md
137
316
  $ git commit -am "Update CHANGELOG.md" && git push
@@ -140,10 +319,11 @@ $ gem bump --version minor --tag --sign --push
140
319
 
141
320
 
142
321
  # Contributing
143
- Bug reports and pull requests are welcome on GitHub at [https://github.com/erikw/nestedtext-ruby](https://github.com/erikw/nestedtext-ruby).
322
+ Bug reports and pull requests are welcome on GitHub at [erikw/nestedtext-ruby](https://github.com/erikw/nestedtext-ruby).
144
323
 
145
324
  # License
146
325
  The gem is available as open source with the [License](./LICENSE.txt).
147
326
 
148
- # Acknowledgement & Thanks
149
- Thanks to the data format authors making it easier making new implementations by providing an [official test suite](https://github.com/KenKundert/nestedtext_tests).
327
+ # Acknowledgments
328
+ * Thanks to the data format authors making it easier making new implementations by providing an [official test suite](https://github.com/KenKundert/nestedtext_tests).
329
+ * Thanks to [pixteller](https://pixteller.com/) & [mp4.to](https://mp4.to/webp/) for offering the tools needed for creating an animated logo.
data/SECURITY.md CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  | Version | Supported |
6
6
  | ------- | ------------------ |
7
- | 1.x.x | :white_check_mark: |
8
- | < 1.1.1 | :x: |
7
+ | 2.x.x | :white_check_mark: |
8
+ | < 2.0.0 | :x: |
9
9
 
10
10
 
11
11
  ## Reporting a Vulnerability
@@ -1,17 +1,18 @@
1
1
  require "nestedtext/encode_helpers"
2
2
 
3
+ # Extension of Ruby core types with the NestedText::NTEncodeMixin.
4
+ #
3
5
  # TODO: add encoding of more Ruby native classes like Integer, Float etc plus commons like Set,....? Not covered in NestedText language.
4
6
  # Or leave this to a schema validator 3rd party plugin maybe? And replace my custom class decoding (and also encoding?)?
5
7
  # Or both: add encoding/decoding of more native classes, and allow decoding + applying a schema with 3rd party.
6
8
  # Or encourage using Marshal from core?
7
9
 
8
- NT_MIXIN = NestedText.const_get(:NTEncodeStrictMixin)
9
- class String include NT_MIXIN end
10
- class Array include NT_MIXIN end
11
- class Hash include NT_MIXIN end
10
+ class String include NestedText::NTEncodeMixin; end
11
+ class Array include NestedText::NTEncodeMixin; end
12
+ class Hash include NestedText::NTEncodeMixin; end
12
13
 
13
14
  class NilClass
14
- include NT_MIXIN
15
+ include NestedText::NTEncodeMixin
15
16
 
16
17
  def self.nt_create(_data) = nil
17
18
 
@@ -1,5 +1,5 @@
1
1
  module NestedText
2
- # Hiding extensions for Kernel here away from clients.
2
+ # Hiding extensions for Kernel here away from users.
3
3
  # Reference: https://ruby-doc.org/core-3.1.0/doc/syntax/refinements_rdoc.html
4
4
  module CoreExtInternal
5
5
  refine String do
@@ -1,19 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "nestedtext/parser"
4
- require "nestedtext/errors"
4
+ require "nestedtext/errors_internal"
5
5
 
6
6
  require "logger"
7
7
  require "stringio"
8
8
 
9
9
  module NestedText
10
- def self.load(ntstring, top_class: Object, strict: true)
10
+ # Decode a NestedText string to Ruby objects.
11
+ #
12
+ # [ntstring] The string containing NestedText to be decoded.
13
+ # [top_class] Force the top level returned object to be of this type. Supported values are +Object+, +Array+, +Hash+ and +String+. Default is +Object+.
14
+ # [strict] If strict mode should be used. +true+ or +false+. Default is +false+
15
+ #
16
+ # Returns the parsed object.
17
+ #
18
+ # Raises NestedText::Error if anything went wrong.
19
+ def self.load(ntstring, top_class: Object, strict: false)
11
20
  raise Errors::WrongInputTypeError.new([String], ntstring) unless ntstring.nil? || ntstring.is_a?(String)
12
21
 
13
22
  Parser.new(StringIO.new(ntstring), top_class, strict: strict).parse
14
23
  end
15
24
 
16
- def self.load_file(filename, top_class: Object, strict: true)
25
+ # Decode a NestedText stored in a given file.
26
+
27
+ # [filename] The file path to read NestedText to decode from.
28
+ # [top_class] Force the top level returned object to be of this type. Supported values are +Object+, +Array+, +Hash+ and +String+. Default is +Object+.
29
+ # [strict] If strict mode should be used. +true+ or +false+. Default is +false+
30
+ #
31
+ # Returns the parsed object.
32
+ #
33
+ # Raises NestedText::Error if anything went wrong.
34
+ #
35
+ # Raises +IOError+ on issue opening +filename+ for reading in text mode.
36
+ def self.load_file(filename, top_class: Object, strict: false)
17
37
  raise Errors::WrongInputTypeError.new([String], filename) unless !filename.nil? && filename.is_a?(String)
18
38
 
19
39
  # Open explicitly in text mode to detect \r as line ending.
@@ -3,17 +3,18 @@ require "nestedtext/core_ext_internal"
3
3
  module NestedText
4
4
  using NestedText.const_get(:CoreExtInternal)
5
5
 
6
+ # Dumping with recursive DFS traversal of the object references.
6
7
  class Dumper
7
- def initialize(opts = EncodeOptions.new)
8
- @indentation = opts.indentation
9
- @strict = opts.strict
10
- @trace_cycles = nil
11
- @trace_keys = nil
8
+ def initialize(indentation, strict)
9
+ @indentation = indentation
10
+ @strict = strict
11
+ @traced_cycles = nil
12
+ @traced_keys = nil
12
13
  end
13
14
 
14
15
  def dump(obj)
15
- @trace_cycles = []
16
- @trace_keys = []
16
+ @traced_cycles = []
17
+ @traced_keys = []
17
18
  dump_any obj
18
19
  end
19
20
 
@@ -47,7 +48,7 @@ module NestedText
47
48
  elsif !@strict
48
49
  key.to_s
49
50
  else
50
- raise Errors::DumpHashKeyStrictString, key
51
+ raise Errors::DumpHashKeyStrictStringError, key
51
52
  end
52
53
  end
53
54
 
@@ -57,26 +58,24 @@ module NestedText
57
58
  target.replace indented
58
59
  end
59
60
 
60
- # TODO: different name on method and instance var...
61
61
  def trace_cycles(obj)
62
- raise Errors::DumpCyclicReferencesDetected, traced_key if @trace_cycles.include?(obj)
62
+ raise Errors::DumpCyclicReferencesDetectedError, traced_key if @traced_cycles.include?(obj)
63
63
 
64
- @trace_cycles << obj
64
+ @traced_cycles << obj
65
65
  yield
66
66
  ensure
67
- @trace_cycles.pop
67
+ @traced_cycles.pop
68
68
  end
69
69
 
70
- # TODO: different name on method and instance var...
71
70
  def trace_keys(key)
72
- @trace_keys << key
71
+ @traced_keys << key
73
72
  yield
74
73
  ensure
75
- @trace_keys.pop
74
+ @traced_keys.pop
76
75
  end
77
76
 
78
77
  def traced_key
79
- @trace_keys.last
78
+ @traced_keys.last
80
79
  end
81
80
 
82
81
  def dump_any(obj, depth: 0, **kwargs)
@@ -93,7 +92,6 @@ module NestedText
93
92
  end
94
93
  end
95
94
 
96
- # TODO: document that @strict: false allows to_s on key object
97
95
  def dump_hash(obj, depth: 0, **kwargs)
98
96
  rep = if obj.empty?
99
97
  "{}"
@@ -1,32 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "nestedtext/errors"
4
- require "nestedtext/encode_helpers"
5
3
  require "nestedtext/dumper"
6
-
7
- # Model after JSON
8
- # NestedText.dump(obj, io=nil) => dumps to string, or to IO if given
9
- # NestedText.dump_file(obj, filename)
4
+ require "nestedtext/errors_internal"
10
5
 
11
6
  module NestedText
12
- # TODO: strict should maybe be false by default, as this is what ntpy does. If so, make the same for the load functions.
13
- def self.dump(obj, io: nil, indentation: 4, strict: true)
14
- # io - additionaly write the out result to IO and still return result.
15
-
16
- raise Errors::DumpBadIO, io unless io.nil? || io.respond_to?(:write) && io.respond_to?(:fsync)
7
+ # Encode a Ruby object to a NestedText string.
8
+ #
9
+ # [obj] The object to encode to NestedText.
10
+ # [io] Additionally write the output to this IO object. The caller is responsible for that the IO is closed after the call to this method.
11
+ # [indentation] The indentation of nested levels to use.
12
+ # [strict] If strict mode should be used. +true+ or +false+. Default is +false+
13
+ #
14
+ # Returns a String containing NestedText data.
15
+ #
16
+ # Raises NestedText::Error if anything went wrong.
17
+ #
18
+ # Raises whatever the passed +io+ can raise.
19
+ def self.dump(obj, io: nil, indentation: 4, strict: false)
20
+ raise Errors::DumpBadIOError, io unless io.nil? || io.respond_to?(:write) && io.respond_to?(:fsync)
17
21
 
18
- opts = EncodeOptions.new(indentation, strict)
19
- dumper = Dumper.new(opts)
22
+ dumper = Dumper.new(indentation, strict)
20
23
  result = dumper.dump obj
21
24
  unless io.nil?
22
25
  io.write(result)
23
26
  io.fsync
24
27
  end
25
- result
28
+ dumper.dump obj
26
29
  end
27
30
 
31
+ # Encode a Ruby object to a NestedText file.
32
+ #
33
+ # [filename] The file path to write the NestedText result to. The conventional file extension is +.nt+.
34
+ #
35
+ # Raises +IOError+ on issues opening the +filename+ for writing in text mode.
36
+ #
37
+ # Apart from +filename+, this method behaves exactly like dump (taking same arguments, returning and raising the same values).
28
38
  def self.dump_file(obj, filename, **kwargs)
29
- raise Errors::DumpFileBadPath, filename unless filename.is_a? String
39
+ raise Errors::DumpFileBadPathError, filename unless filename.is_a? String
30
40
 
31
41
  File.open(filename, mode = "wt") do |file|
32
42
  dump(obj, io: file, **kwargs)
@@ -1,26 +1,14 @@
1
1
  require "nestedtext/dumper"
2
2
 
3
3
  module NestedText
4
- module NTEncodeStrictMixin
5
- def to_nt(indentation: 4, strict: true)
6
- Dumper.new(EncodeOptions.new(indentation, strict)).dump self
7
- end
8
- end
9
- private_constant :NTEncodeStrictMixin
10
-
4
+ # A mixin for Custom Classes to get the to_nt shortcut.
5
+ # TODO rename to: ToNTMixin
11
6
  module NTEncodeMixin
12
- def to_nt(indentation: 4)
13
- Dumper.new(EncodeOptions.new(indentation, false)).dump self
14
- end
15
- end
16
-
17
- class EncodeOptions
18
- attr_reader :indentation, :strict
19
-
20
- def initialize(indentation = 4, strict = true)
21
- @indentation = indentation
22
- @strict = strict
7
+ # Encode this object to a NestedText string.
8
+ #
9
+ # This method takes the same arguments as NestedText::dump.
10
+ def to_nt(**kwargs)
11
+ NestedText.dump(self, strict: false, **kwargs)
23
12
  end
24
13
  end
25
- private_constant :EncodeOptions
26
14
  end
@@ -0,0 +1,8 @@
1
+ module NestedText
2
+ # Top level error to rescue on.
3
+ #
4
+ # +Error+ is a subclass of +StandardError+ and behaves as expected e.g. +#message+.
5
+ class Error < StandardError
6
+ private_class_method :new
7
+ end
8
+ end